Ship the four upstream changes required by AgentWorkforce/workforce#338 so that PR's lazy dependency boundary can be removed on the next workforce version bump.
Full spec: SPEC-relay.md on the relayflows repo (committed alongside this issue).
Scope
Narrow — do NOT refactor unrelated fleet code, do NOT rename anything outside these four contracts, do NOT touch the CLI's attach command UX beyond an internal re-export.
The four changes
-
Extract startFleetNodeAttachProxy to a public SDK subpath.
Move the attach-proxy logic (server binding, loopback WebSocket adapter, Relaycast terminal-session mapping) from packages/cli/src/cli/lib/attach-fleet-node.ts into a new packages/sdk/src/attach.ts. Add @agent-relay/sdk/attach to the SDK exports map (types + import + require). Leave the CLI file as a thin re-export so nothing that imports from the old path breaks.
-
Reshape FleetNodeAttachProxy to the consumer contract.
Today (packages/cli/src/cli/lib/attach-fleet-node.ts:101-105):
```ts
export interface FleetNodeAttachProxy {
brokerUrl: string;
apiKey: string;
requestTimeoutMs: number;
// ...
}
```
Should be:
```ts
export interface FleetNodeAttachProxy {
socketPath: string; // local UNIX socket the caller pipes stdio to
finished: Promise; // resolves with the remote harness's exit code
close(): Promise;
}
```
Keep the existing brokerUrl/apiKey/requestTimeoutMs plumbing but under a distinct internal type (`FleetNodeAttachTransport` or similar). The exported `FleetNodeAttachProxy` contract is the three fields above — that's what workforce#338 already imports.
-
Publish spawnFleetSandbox on @agent-relay/sdk/fleet.
Internal fleet-sandbox ensure/spawn already exists (called by `agent-relay fleet spawn --sandbox`). Add `packages/sdk/src/fleet.ts` exporting a typed `spawnFleetSandbox({...}): Promise` — payload shape and handle shape spelled out in SPEC-relay.md §3. Add `@agent-relay/sdk/fleet` to the SDK `exports` map. Update the CLI's `fleet spawn --sandbox` to import from this new SDK entry, proving the extraction works.
-
Extend `/api/v1/fleet/nodes/sandbox/ensure` with `readonlyPaths`.
Cloud already understands the concept (`packages/cloud/src/compiler.ts:215+`, `permissions.ts:142`). Add `readonlyPaths?: string[]` to the ensure request schema, thread it into the sandbox-mount build so Cloud materializes those paths with chmod-444, and extend `tests/relayflows/cases/1630-scoped-relayfile-sandbox-mount/run.mjs` to assert a file under a `readonlyPaths` path is mode 444 from inside the sandbox and that a write attempt fails.
Definition of done
- `npm run typecheck` / `build` green
- `npm --prefix packages/{sdk,cloud,cli,fleet} test` green
- Extended 1630 case runs green end-to-end against a live Daytona sandbox and asserts chmod-444
- `agent-relay fleet spawn --sandbox` and `agent-relay node agent attach` continue to work identically
- `@agent-relay/sdk/fleet` and `@agent-relay/sdk/attach` are new first-class public entries in the SDK's `exports` map (with `types`, `import`, `require`)
- No fabricated version bumps
- PR opened as a draft so workforce#338 can pin the exact commit once merged
Context
This work was originally scheduled to run as a v1 relayflow (planner → codex → runtime tests → adversary → draft PR) alongside PR #338's flow. The workforce-side flow ran end-to-end for 52 min and demonstrated the discipline (real test failure gated closed, SDK auto-triggered repair pass). The relay-side flow started twice on the same host and hit macOS memory-pressure kills within 90 seconds — the machine can't run a second full flow concurrent with codex/pnpm workloads. Filing this as an issue for a future session on a fresh host, or for direct manual implementation.
Once this merges
Update workforce#338: bump `@agent-relay/sdk` to the newly-published version, delete the lazy dependency boundary in `packages/deploy/src/modes/sandbox-interactive.ts`, flip that PR from draft to ready-for-review.
/cc @khaliqgant
Ship the four upstream changes required by AgentWorkforce/workforce#338 so that PR's lazy dependency boundary can be removed on the next workforce version bump.
Full spec:
SPEC-relay.mdon the relayflows repo (committed alongside this issue).Scope
Narrow — do NOT refactor unrelated fleet code, do NOT rename anything outside these four contracts, do NOT touch the CLI's
attachcommand UX beyond an internal re-export.The four changes
Extract
startFleetNodeAttachProxyto a public SDK subpath.Move the attach-proxy logic (server binding, loopback WebSocket adapter, Relaycast terminal-session mapping) from
packages/cli/src/cli/lib/attach-fleet-node.tsinto a newpackages/sdk/src/attach.ts. Add@agent-relay/sdk/attachto the SDKexportsmap (types + import + require). Leave the CLI file as a thin re-export so nothing that imports from the old path breaks.Reshape
FleetNodeAttachProxyto the consumer contract.Today (
packages/cli/src/cli/lib/attach-fleet-node.ts:101-105):```ts
export interface FleetNodeAttachProxy {
brokerUrl: string;
apiKey: string;
requestTimeoutMs: number;
// ...
}
```
Should be:
```ts
export interface FleetNodeAttachProxy {
socketPath: string; // local UNIX socket the caller pipes stdio to
finished: Promise; // resolves with the remote harness's exit code
close(): Promise;
}
```
Keep the existing brokerUrl/apiKey/requestTimeoutMs plumbing but under a distinct internal type (`FleetNodeAttachTransport` or similar). The exported `FleetNodeAttachProxy` contract is the three fields above — that's what workforce#338 already imports.
Publish
spawnFleetSandboxon@agent-relay/sdk/fleet.Internal fleet-sandbox ensure/spawn already exists (called by `agent-relay fleet spawn --sandbox`). Add `packages/sdk/src/fleet.ts` exporting a typed `spawnFleetSandbox({...}): Promise` — payload shape and handle shape spelled out in SPEC-relay.md §3. Add `@agent-relay/sdk/fleet` to the SDK `exports` map. Update the CLI's `fleet spawn --sandbox` to import from this new SDK entry, proving the extraction works.
Extend `/api/v1/fleet/nodes/sandbox/ensure` with `readonlyPaths`.
Cloud already understands the concept (`packages/cloud/src/compiler.ts:215+`, `permissions.ts:142`). Add `readonlyPaths?: string[]` to the ensure request schema, thread it into the sandbox-mount build so Cloud materializes those paths with chmod-444, and extend `tests/relayflows/cases/1630-scoped-relayfile-sandbox-mount/run.mjs` to assert a file under a `readonlyPaths` path is mode 444 from inside the sandbox and that a write attempt fails.
Definition of done
Context
This work was originally scheduled to run as a v1 relayflow (planner → codex → runtime tests → adversary → draft PR) alongside PR #338's flow. The workforce-side flow ran end-to-end for 52 min and demonstrated the discipline (real test failure gated closed, SDK auto-triggered repair pass). The relay-side flow started twice on the same host and hit macOS memory-pressure kills within 90 seconds — the machine can't run a second full flow concurrent with codex/pnpm workloads. Filing this as an issue for a future session on a fresh host, or for direct manual implementation.
Once this merges
Update workforce#338: bump `@agent-relay/sdk` to the newly-published version, delete the lazy dependency boundary in `packages/deploy/src/modes/sandbox-interactive.ts`, flip that PR from draft to ready-for-review.
/cc @khaliqgant