Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Trajectory: Fix GitHub issue 1169

> **Status:** ✅ Completed
> **Confidence:** 92%
> **Started:** June 19, 2026 at 01:30 PM
> **Completed:** June 19, 2026 at 01:34 PM

---

## Summary

Fixed fleet serve node-definition loading for Bun-compiled CLI by using native import for JavaScript node files, lazy-loading jiti only for TypeScript sources, and validating the fleet marker locally to avoid compiled runtime import failures.

**Approach:** Standard approach

---

## Key Decisions

### Use native dynamic import for JS fleet node definitions

- **Chose:** Use native dynamic import for JS fleet node definitions
- **Reasoning:** The Bun-compiled CLI fails when jiti constructs its transform backend; compiled JS can be loaded by the runtime directly, so jiti should only be loaded for TypeScript-like sources that need transpilation.

### Validate loaded fleet node definitions locally in the CLI

- **Chose:** Validate loaded fleet node definitions locally in the CLI
- **Reasoning:** The Bun-compiled smoke showed the fleet SDK validator import can be undefined in the standalone binary; the public validator only checks the \_\_agentRelayFleetNode marker, so a local marker check avoids another runtime import failure without changing validation behavior.

---

## Chapters

### 1. Work

_Agent: default_

- Use native dynamic import for JS fleet node definitions: Use native dynamic import for JS fleet node definitions
- Validate loaded fleet node definitions locally in the CLI: Validate loaded fleet node definitions locally in the CLI
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"id": "traj_t83sls93qtdp",
"version": 1,
"task": {
"title": "Fix GitHub issue 1169"
},
"status": "completed",
"startedAt": "2026-06-19T11:30:05.358Z",
"completedAt": "2026-06-19T11:34:06.470Z",
"agents": [
{
"name": "default",
"role": "lead",
"joinedAt": "2026-06-19T11:31:29.687Z"
}
],
"chapters": [
{
"id": "chap_h8ukvp6tcyks",
"title": "Work",
"agentName": "default",
"startedAt": "2026-06-19T11:31:29.687Z",
"endedAt": "2026-06-19T11:34:06.470Z",
"events": [
{
"ts": 1781868689689,
"type": "decision",
"content": "Use native dynamic import for JS fleet node definitions: Use native dynamic import for JS fleet node definitions",
"raw": {
"question": "Use native dynamic import for JS fleet node definitions",
"chosen": "Use native dynamic import for JS fleet node definitions",
"alternatives": [],
"reasoning": "The Bun-compiled CLI fails when jiti constructs its transform backend; compiled JS can be loaded by the runtime directly, so jiti should only be loaded for TypeScript-like sources that need transpilation."
},
"significance": "high"
},
{
"ts": 1781868801483,
"type": "decision",
"content": "Validate loaded fleet node definitions locally in the CLI: Validate loaded fleet node definitions locally in the CLI",
"raw": {
"question": "Validate loaded fleet node definitions locally in the CLI",
"chosen": "Validate loaded fleet node definitions locally in the CLI",
"alternatives": [],
"reasoning": "The Bun-compiled smoke showed the fleet SDK validator import can be undefined in the standalone binary; the public validator only checks the __agentRelayFleetNode marker, so a local marker check avoids another runtime import failure without changing validation behavior."
},
"significance": "high"
}
]
}
],
"retrospective": {
"summary": "Fixed fleet serve node-definition loading for Bun-compiled CLI by using native import for JavaScript node files, lazy-loading jiti only for TypeScript sources, and validating the fleet marker locally to avoid compiled runtime import failures.",
"approach": "Standard approach",
"confidence": 0.92
},
"commits": [],
"filesChanged": [],
"projectId": "AgentWorkforce/relay",
"tags": [],
"_trace": {
"startRef": "66f972f23cafb958fa1956d3654710423e2391b5",
"endRef": "66f972f23cafb958fa1956d3654710423e2391b5"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Trajectory: Address PR 1170 bot feedback

> **Status:** ✅ Completed
> **Confidence:** 90%
> **Started:** June 19, 2026 at 01:41 PM
> **Completed:** June 19, 2026 at 01:42 PM

---

## Summary

Addressed PR 1170 automated review feedback by adding marker-aware default unwrapping for CommonJS default wrappers and a Node-only syntax-error fallback to jiti for ESM-syntax .js files in CommonJS projects.

**Approach:** Standard approach

---

## Key Decisions

### Preserve Node.js JS interop while keeping Bun standalone off the jiti fallback

- **Chose:** Preserve Node.js JS interop while keeping Bun standalone off the jiti fallback
- **Reasoning:** Native import fixes compiled JavaScript node definitions in the Bun binary. Node.js can still hit SyntaxError for ESM-syntax .js files in CommonJS projects, so the loader falls back to jiti only for syntax failures outside Bun and unwraps nested CommonJS default wrappers.

---

## Chapters

### 1. Work

_Agent: default_

- Preserve Node.js JS interop while keeping Bun standalone off the jiti fallback: Preserve Node.js JS interop while keeping Bun standalone off the jiti fallback
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"id": "traj_upshg9a9y5tx",
"version": 1,
"task": {
"title": "Address PR 1170 bot feedback"
},
"status": "completed",
"startedAt": "2026-06-19T11:41:01.857Z",
"completedAt": "2026-06-19T11:42:49.228Z",
"agents": [
{
"name": "default",
"role": "lead",
"joinedAt": "2026-06-19T11:42:15.525Z"
}
],
"chapters": [
{
"id": "chap_tvtdh06tamro",
"title": "Work",
"agentName": "default",
"startedAt": "2026-06-19T11:42:15.525Z",
"endedAt": "2026-06-19T11:42:49.228Z",
"events": [
{
"ts": 1781869335526,
"type": "decision",
"content": "Preserve Node.js JS interop while keeping Bun standalone off the jiti fallback: Preserve Node.js JS interop while keeping Bun standalone off the jiti fallback",
"raw": {
"question": "Preserve Node.js JS interop while keeping Bun standalone off the jiti fallback",
"chosen": "Preserve Node.js JS interop while keeping Bun standalone off the jiti fallback",
"alternatives": [],
"reasoning": "Native import fixes compiled JavaScript node definitions in the Bun binary. Node.js can still hit SyntaxError for ESM-syntax .js files in CommonJS projects, so the loader falls back to jiti only for syntax failures outside Bun and unwraps nested CommonJS default wrappers."
},
"significance": "high"
}
]
}
],
"retrospective": {
"summary": "Addressed PR 1170 automated review feedback by adding marker-aware default unwrapping for CommonJS default wrappers and a Node-only syntax-error fallback to jiti for ESM-syntax .js files in CommonJS projects.",
"approach": "Standard approach",
"confidence": 0.9
},
"commits": [],
"filesChanged": [],
"projectId": "AgentWorkforce/relay",
"tags": [],
"_trace": {
"startRef": "29a9c0361c067786c9771b87c5837d6d261b093e",
"endRef": "29a9c0361c067786c9771b87c5837d6d261b093e"
}
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- `agent-relay fleet serve <node-def>` loads plain JavaScript node definitions without `jiti`, so the published Bun-compiled CLI can serve compiled JS node files.
- Spawned opencode worker agents no longer pause for interactive tool-approval prompts; the broker injects a wildcard allow-all permission block into every generated `opencode.json`, augmenting existing partial permission objects rather than replacing them.

### Added
Expand Down
76 changes: 76 additions & 0 deletions packages/cli/src/cli/commands/fleet.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { once } from 'node:events';
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';

import { Command } from 'commander';
Expand Down Expand Up @@ -70,6 +72,80 @@ describe('fleet command support', () => {
expect(Object.keys(node.capabilities)).toContain('spawn:codex');
});

it('loads a plain JS node file through native import', async () => {
const dir = await mkdtemp(path.join(os.tmpdir(), 'relay-node-def-'));
try {
await writeFile(path.join(dir, 'package.json'), JSON.stringify({ type: 'module' }));
const file = path.join(dir, 'node-def.js');
await writeFile(
file,
[
'export default {',
' __agentRelayFleetNode: true,',
' name: "plain-js-node",',
' capabilities: {},',
' triggers: [],',
'};',
].join('\n')
);

const node = await loadNodeDefinition(file);

expect(node.name).toBe('plain-js-node');
} finally {
await rm(dir, { recursive: true, force: true });
}
});

it('loads CommonJS compiled JS node files that export default wrappers', async () => {
const dir = await mkdtemp(path.join(os.tmpdir(), 'relay-node-def-'));
try {
const file = path.join(dir, 'node-def.js');
await writeFile(
file,
[
'exports.default = {',
' __agentRelayFleetNode: true,',
' name: "compiled-cjs-node",',
' capabilities: {},',
' triggers: [],',
'};',
].join('\n')
);

const node = await loadNodeDefinition(file);

expect(node.name).toBe('compiled-cjs-node');
} finally {
await rm(dir, { recursive: true, force: true });
}
});
Comment on lines +100 to +122

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Add package.json to ensure CJS module context.

The test writes exports.default = { ... } (CommonJS syntax) but does not create a package.json to explicitly set type: "commonjs". This means the module type is inherited from parent directories. If the project root has type: "module", the test file will be interpreted as ESM, where exports is undefined, causing a ReferenceError during native import().

Depending on shouldFallbackToJiti behavior, this could either cause the test to unintentionally fall back to jiti (not exercising the native-import code path it's meant to test) or fail outright in ESM-configured projects.

To ensure the test reliably exercises the native-import path for CommonJS-compiled wrappers, write a package.json with type: "commonjs" in the temp directory, matching the pattern used in the test at lines 124–147.

📦 Recommended fix: add package.json
   it('loads CommonJS compiled JS node files that export default wrappers', async () => {
     const dir = await mkdtemp(path.join(os.tmpdir(), 'relay-node-def-'));
     try {
+      await writeFile(path.join(dir, 'package.json'), JSON.stringify({ type: 'commonjs' }));
       const file = path.join(dir, 'node-def.js');
       await writeFile(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/cli/commands/fleet.test.ts` around lines 100 - 122, The test
'loads CommonJS compiled JS node files that export default wrappers' creates a
temporary directory and writes a CommonJS node-def.js file, but does not create
a package.json to explicitly declare the module type as "commonjs". This can
cause the test to inherit the module type from parent directories, potentially
interpreting the CommonJS exports syntax as ESM and causing failures. After
creating the temporary directory with mkdtemp and before writing the node-def.js
file, add code to write a package.json file to the temp directory with the
content containing type set to "commonjs", matching the pattern already
demonstrated elsewhere in the test file.


it('falls back to jiti for ESM-syntax JS node files in CommonJS projects', async () => {
const dir = await mkdtemp(path.join(os.tmpdir(), 'relay-node-def-'));
try {
await writeFile(path.join(dir, 'package.json'), JSON.stringify({ type: 'commonjs' }));
const file = path.join(dir, 'node-def.js');
await writeFile(
file,
[
'export default {',
' __agentRelayFleetNode: true,',
' name: "commonjs-project-esm-node",',
' capabilities: {},',
' triggers: [],',
'};',
].join('\n')
);

const node = await loadNodeDefinition(file);

expect(node.name).toBe('commonjs-project-esm-node');
} finally {
await rm(dir, { recursive: true, force: true });
}
});

it('registers a served node and dispatches invoke_handler over a stub broker', async () => {
const node = defineNode({
name: 'stub-node',
Expand Down
Loading
Loading