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,34 @@
# Trajectory: Fix @agent-relay/fleet exports dropped to undefined in bun --compile standalone binary (local up Fleet local node skipped)

> **Status:** ✅ Completed
> **Confidence:** 90%
> **Started:** June 22, 2026 at 09:44 AM
> **Completed:** June 22, 2026 at 10:06 AM

---

## Summary

Fixed bun-compiled CLI dropping @agent-relay/_ workspace exports to undefined (Fleet local node skipped / 'is not a function' on local up). Root cause: bun & esbuild honor tsconfig paths that map workspace packages to their .d.ts (no runtime exports); paths resolve per nearest tsconfig to the importing file, so transitive imports broke too. Final fix (after a fresh-eyes review pivoted away from an earlier build-script workaround): removed the redundant @agent-relay/_ paths from the root and packages/cli tsconfig.json — they duplicated the npm workspace node_modules symlinks, so tsc still resolves .d.ts (via package exports.types) and bundlers resolve .js (via exports.import). Also reverted the now-obsolete namespace-import workarounds in fleet-sidecar.ts and fleet/src/index.ts to plain named imports. tsc type-checking untouched. Verified: build:core green (0 type errors), built binary, local up starts implicit fleet node with fleet-node.json connected:true + spawn:claude/codex/gemini.

**Approach:** Standard approach

---

## Key Decisions

### Fix at the bundler layer via empty-paths tsconfig next to the build entry

- **Chose:** Fix at the bundler layer via empty-paths tsconfig next to the build entry
- **Reasoning:** Root cause: packages/cli/tsconfig.json paths map @agent-relay/\* to dist/index.d.ts; bun/esbuild honor tsconfig paths and bundle the declaration files (no runtime exports) -> undefined. fleet breaks because it is only ever namespace-imported. Build-script override is surgical and leaves tsc type-checking untouched, vs changing shared root paths.

---

## Chapters

### 1. Work

_Agent: default_

- Fix at the bundler layer via empty-paths tsconfig next to the build entry: Fix at the bundler layer via empty-paths tsconfig next to the build entry
- Root cause is bun/esbuild honoring tsconfig paths (->.d.ts, no runtime exports) per-importing-file; fix needs empty-paths tsconfig in EVERY bundled package dist, not just the entry, to cover transitive workspace imports. Verified end-to-end: fleet-node.json connected:true with all spawn handlers.
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{
"id": "traj_d88jruejk2zk",
"version": 1,
"task": {
"title": "Fix @agent-relay/fleet exports dropped to undefined in bun --compile standalone binary (local up Fleet local node skipped)"
},
"status": "completed",
"startedAt": "2026-06-22T07:44:17.272Z",
"completedAt": "2026-06-22T08:06:29.530Z",
"agents": [
{
"name": "default",
"role": "lead",
"joinedAt": "2026-06-22T07:53:58.239Z"
}
],
"chapters": [
{
"id": "chap_fmr96gta9rp3",
"title": "Work",
"agentName": "default",
"startedAt": "2026-06-22T07:53:58.239Z",
"endedAt": "2026-06-22T08:06:29.530Z",
"events": [
{
"ts": 1782114838239,
"type": "decision",
"content": "Fix at the bundler layer via empty-paths tsconfig next to the build entry: Fix at the bundler layer via empty-paths tsconfig next to the build entry",
"raw": {
"question": "Fix at the bundler layer via empty-paths tsconfig next to the build entry",
"chosen": "Fix at the bundler layer via empty-paths tsconfig next to the build entry",
"alternatives": [],
"reasoning": "Root cause: packages/cli/tsconfig.json paths map @agent-relay/* to dist/index.d.ts; bun/esbuild honor tsconfig paths and bundle the declaration files (no runtime exports) -> undefined. fleet breaks because it is only ever namespace-imported. Build-script override is surgical and leaves tsc type-checking untouched, vs changing shared root paths."
},
"significance": "high"
},
{
"ts": 1782115588773,
"type": "reflection",
"content": "Root cause is bun/esbuild honoring tsconfig paths (->.d.ts, no runtime exports) per-importing-file; fix needs empty-paths tsconfig in EVERY bundled package dist, not just the entry, to cover transitive workspace imports. Verified end-to-end: fleet-node.json connected:true with all spawn handlers.",
"raw": {
"confidence": 0.9
},
"significance": "high",
"tags": ["confidence:0.9"]
}
]
}
],
"retrospective": {
"summary": "Fixed bun-compiled CLI dropping @agent-relay/* workspace exports to undefined (Fleet local node skipped / 'is not a function' on local up). Root cause: bun & esbuild honor tsconfig paths that map workspace packages to their .d.ts (no runtime exports); paths resolve per nearest tsconfig to the importing file, so transitive imports broke too. Fix: build-bun.sh & build-standalone.sh write an empty-paths tsconfig into every packages/*/dist before bundling, forcing node_modules (.js) resolution. tsc type-checking untouched. Verified: rebuilt core from source, built binary, local up starts implicit fleet node with fleet-node.json connected:true + spawn:claude/codex/gemini.",
"approach": "Standard approach",
"confidence": 0.9
},
"commits": [],
"filesChanged": [],
"projectId": "AgentWorkforce/relay",
"tags": [],
"_trace": {
"startRef": "c7380efd69238db002f4cba087cebef593bf9fcd",
"endRef": "c7380efd69238db002f4cba087cebef593bf9fcd"
}
}
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

- The Bun-compiled `agent-relay` standalone binary now bundles workspace packages from their compiled JS instead of their `.d.ts`, so `local up` starts the implicit Fleet local node instead of failing with `Fleet local node skipped: … is not a function`. The `tsconfig` `paths` that mapped `@agent-relay/*` to declaration files (no runtime exports) were redundant with the npm workspace symlinks and have been removed.

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.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

CHANGELOG entry includes implementation details; focus on user-visible impact only.

Per coding guidelines for CHANGELOG.md, entries should be "concise and impact-first, with one short bullet per user-visible change. Omit... internal notes, and implementation details."

The current entry includes technical implementation details (tsconfig paths, .d.ts vs. compiled JS, npm workspace symlinks, "no runtime exports") that users don't need to understand. The user-visible change is singular: local up now works instead of failing.

Suggested revision (concise, impact-first, omits implementation):

agent-relay local up now starts the implicit Fleet local node instead of failing with "Fleet local node skipped: … is not a function" in the Bun-compiled standalone binary.

🤖 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 `@CHANGELOG.md` at line 12, The CHANGELOG entry contains excessive
implementation details (tsconfig paths, .d.ts versus compiled JS, npm workspace
symlinks) that distract from the user-visible impact. Simplify the entry to
focus only on what users care about: that `agent-relay local up` now
successfully starts the implicit Fleet local node instead of failing with the
"is not a function" error. Remove all technical details about how the fix was
implemented (tsconfig changes, workspace symlinks, declaration files) and keep
the entry concise and impact-focused as per CHANGELOG guidelines.

Source: Coding guidelines

- `agent-relay` and `@agent-relay/sdk` require `@relaycast/sdk` `^4.1.2`, whose matching `@relaycast/types` package is now published, so publish installs resolve cleanly without pinning.
- `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.
Expand Down
12 changes: 7 additions & 5 deletions packages/cli/src/cli/lib/fleet-sidecar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import type {
FleetRelaySendMessageInput,
FleetSpawnAgentInput,
} from '@agent-relay/fleet';
// bun's standalone `--compile` bundler mis-validates named value imports from
// this workspace package against its generated .d.ts ("No matching export").
// A namespace import + destructure sidesteps it while keeping call sites unchanged.
import * as fleetSdk from '@agent-relay/fleet';
const { defineDefaultLocalNode, invokeNodeHandler, nodeInfo, nodeManifest, triggerSyncInputs } = fleetSdk;
import {
defineDefaultLocalNode,
invokeNodeHandler,
nodeInfo,
nodeManifest,
triggerSyncInputs,
} from '@agent-relay/fleet';
import {
PROTOCOL_VERSION,
type BrokerToSdk,
Expand Down
26 changes: 6 additions & 20 deletions packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,12 @@
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@agent-relay/config": ["../config/dist/index.d.ts"],
"@agent-relay/config/*": ["../config/dist/*"],
"@agent-relay/policy": ["../policy/dist/index.d.ts"],
"@agent-relay/policy/*": ["../policy/dist/*"],
"@agent-relay/sdk": ["../sdk/dist/index.d.ts"],
"@agent-relay/sdk/*": ["../sdk/dist/*"],
"@agent-relay/harness-driver": ["../harness-driver/dist/index.d.ts"],
"@agent-relay/harness-driver/*": ["../harness-driver/dist/*"],
"@agent-relay/harnesses": ["../harnesses/dist/index.d.ts"],
"@agent-relay/harnesses/*": ["../harnesses/dist/*"],
"@agent-relay/fleet": ["../fleet/dist/index.d.ts"],
"@agent-relay/fleet/*": ["../fleet/dist/*"],
"@agent-relay/utils": ["../utils/dist/index.d.ts"],
"@agent-relay/utils/*": ["../utils/dist/*"],
"@agent-relay/cloud": ["../cloud/dist/index.d.ts"],
"@agent-relay/cloud/*": ["../cloud/dist/*"]
}
"sourceMap": true
// No `paths` for @agent-relay/* on purpose: the npm workspace symlinks them
// into node_modules, so tsc resolves their `.d.ts` and bundlers (bun,
// esbuild) resolve their `.js`. Mapping them to `dist/index.d.ts` here made
// the compiled CLI inline the declaration files and lose every workspace
// export at runtime (e.g. `local up` → "Fleet local node skipped").
},
"include": ["src/index.ts", "src/cli/index.ts", "src/cli/bootstrap.ts", "src/cli/agent-relay-mcp.ts"],
"exclude": ["node_modules", "dist", "src/**/*.test.ts"]
Expand Down
6 changes: 1 addition & 5 deletions packages/fleet/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { z } from 'zod';
import type { PtyHarness } from '@agent-relay/harnesses';
// Namespace import sidesteps bun --compile's named-import validation against the
// package .d.ts (see packages/cli .../fleet-sidecar.ts).
import * as harnessDefs from '@agent-relay/harnesses';
const { claude, codex, definePtyHarness, gemini } = harnessDefs;
import { claude, codex, definePtyHarness, gemini, type PtyHarness } from '@agent-relay/harnesses';
import { resolveStaticHarnessConfig, type StaticPtyHarnessDefinition } from '@agent-relay/harness-driver';
import type {
AgentSpec,
Expand Down
28 changes: 6 additions & 22 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,12 @@
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@agent-relay/config": ["packages/config/dist/index.d.ts"],
"@agent-relay/config/*": ["packages/config/dist/*"],
"@agent-relay/policy": ["packages/policy/dist/index.d.ts"],
"@agent-relay/policy/*": ["packages/policy/dist/*"],
"@agent-relay/sdk": ["packages/sdk/dist/index.d.ts"],
"@agent-relay/sdk/*": ["packages/sdk/dist/*"],
"@agent-relay/harness-driver": ["packages/harness-driver/dist/index.d.ts"],
"@agent-relay/harness-driver/*": ["packages/harness-driver/dist/*"],
"@agent-relay/harnesses": ["packages/harnesses/dist/index.d.ts"],
"@agent-relay/harnesses/*": ["packages/harnesses/dist/*"],
"@agent-relay/integration-prompts": ["packages/integration-prompts/dist/index.d.ts"],
"@agent-relay/integration-prompts/*": ["packages/integration-prompts/dist/*"],
"@agent-relay/fleet": ["packages/fleet/dist/index.d.ts"],
"@agent-relay/fleet/*": ["packages/fleet/dist/*"],
"@agent-relay/utils": ["packages/utils/dist/index.d.ts"],
"@agent-relay/utils/*": ["packages/utils/dist/*"],
"@agent-relay/cloud": ["packages/cloud/dist/index.d.ts"],
"@agent-relay/cloud/*": ["packages/cloud/dist/*"]
}
"sourceMap": true
// No `paths` for @agent-relay/* on purpose: the npm workspace symlinks them
// into node_modules, so tsc resolves their `.d.ts` and bundlers (bun,
// esbuild) resolve their `.js`. Mapping them to `dist/index.d.ts` here made
// bundlers inline the declaration files (no runtime exports), so the
// compiled CLI lost every workspace export at runtime.
},
"files": [],
"include": []
Expand Down
Loading