From f8f3ee043a3951aae8e570636d395b40df86022b Mon Sep 17 00:00:00 2001 From: Will Washburn Date: Thu, 18 Jun 2026 13:47:36 -0400 Subject: [PATCH 1/8] Expose workspace fleet flag in Relay --- .../2026-06/traj_ws73z8bvitw0/summary.md | 31 +++ .../2026-06/traj_ws73z8bvitw0/trajectory.json | 53 ++++++ CHANGELOG.md | 1 + package-lock.json | 179 +++++++++++------- packages/cli/package.json | 2 +- packages/cli/src/cli/commands/fleet.test.ts | 52 +++++ packages/cli/src/cli/commands/fleet.ts | 36 ++++ packages/sdk/package.json | 2 +- packages/sdk/src/__tests__/facade.test.ts | 37 +++- packages/sdk/src/__tests__/messaging.test.ts | 94 +++++++++ packages/sdk/src/facade.ts | 7 + packages/sdk/src/messaging/relaycast.ts | 83 ++++++++ packages/sdk/src/messaging/types.ts | 11 ++ web/content/docs/fleets.mdx | 4 +- 14 files changed, 513 insertions(+), 79 deletions(-) create mode 100644 .agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md create mode 100644 .agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md new file mode 100644 index 000000000..57a997e73 --- /dev/null +++ b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md @@ -0,0 +1,31 @@ +# Trajectory: Expose workspace fleet node flag in Relay SDK and CLI + +> **Status:** ✅ Completed +> **Confidence:** 82% +> **Started:** June 18, 2026 at 01:47 PM +> **Completed:** June 18, 2026 at 01:47 PM + +--- + +## Summary + +Added Relay SDK workspace.fleetNodes get/set/inherit, CLI fleet config/enable/disable/inherit commands, docs and changelog entries, and updated Relay workspaces to @relaycast/sdk 4.1.0 with a REST fallback for the missing convenience method. SDK tests/build and focused fleet CLI tests pass; full CLI build is blocked by existing current-main cloud auth StoredAuth type errors. + +**Approach:** Standard approach + +--- + +## Key Decisions + +### Use REST fallback for workspace fleet config +- **Chose:** Use REST fallback for workspace fleet config +- **Reasoning:** @relaycast/sdk 4.1.0 is published and installed, but its dist types do not expose workspace.fleetNodes, so Relay should call /v1/workspace/fleet-nodes directly when the SDK convenience method is absent. + +--- + +## Chapters + +### 1. Work +*Agent: default* + +- Use REST fallback for workspace fleet config: Use REST fallback for workspace fleet config diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json new file mode 100644 index 000000000..b25341303 --- /dev/null +++ b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json @@ -0,0 +1,53 @@ +{ + "id": "traj_ws73z8bvitw0", + "version": 1, + "task": { + "title": "Expose workspace fleet node flag in Relay SDK and CLI" + }, + "status": "completed", + "startedAt": "2026-06-18T17:47:07.114Z", + "completedAt": "2026-06-18T17:47:15.340Z", + "agents": [ + { + "name": "default", + "role": "lead", + "joinedAt": "2026-06-18T17:47:11.173Z" + } + ], + "chapters": [ + { + "id": "chap_a3bv93ofiw3n", + "title": "Work", + "agentName": "default", + "startedAt": "2026-06-18T17:47:11.173Z", + "endedAt": "2026-06-18T17:47:15.340Z", + "events": [ + { + "ts": 1781804831174, + "type": "decision", + "content": "Use REST fallback for workspace fleet config: Use REST fallback for workspace fleet config", + "raw": { + "question": "Use REST fallback for workspace fleet config", + "chosen": "Use REST fallback for workspace fleet config", + "alternatives": [], + "reasoning": "@relaycast/sdk 4.1.0 is published and installed, but its dist types do not expose workspace.fleetNodes, so Relay should call /v1/workspace/fleet-nodes directly when the SDK convenience method is absent." + }, + "significance": "high" + } + ] + } + ], + "retrospective": { + "summary": "Added Relay SDK workspace.fleetNodes get/set/inherit, CLI fleet config/enable/disable/inherit commands, docs and changelog entries, and updated Relay workspaces to @relaycast/sdk 4.1.0 with a REST fallback for the missing convenience method. SDK tests/build and focused fleet CLI tests pass; full CLI build is blocked by existing current-main cloud auth StoredAuth type errors.", + "approach": "Standard approach", + "confidence": 0.82 + }, + "commits": [], + "filesChanged": [], + "projectId": "AgentWorkforce/relay", + "tags": [], + "_trace": { + "startRef": "e4cbf8385499b9da734a2a36e99acc2dbc4f9bf5", + "endRef": "e4cbf8385499b9da734a2a36e99acc2dbc4f9bf5" + } +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d1cf1a55..041c13075 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `@agent-relay/config` `CLI_AUTH_CONFIG` adds an `xai` provider (Grok CLI): `grok login --device-auth` device-code connect, `~/.grok/auth.json` credential capture, and the official x.ai installer as the sandbox fallback — so cloud sandboxes can authenticate the `grok` harness from a connected account instead of an API key. - `@agent-relay/sdk` wires the durable delivery surface to the Relaycast backend: `inbox.list`, `inbox.subscribe`, `inbox.ack/fail/defer`, and `deliveries.ack/fail/defer` now use the hosted delivery ledger, agent-scoped capabilities report `serverDeliveryState: true`, and `DeliveryRunner` works against Relaycast-backed inbox items. - `@agent-relay/sdk` adds `placement.spawn({ capability, node?, repo? })` — node-targeted/`self`/least-eligible placement that gates on advertised capability and repo-key map, queues with a bounded TTL until an eligible live node appears, and surfaces queue/fail visibility through `onReconcile` events. A `spawn:` capability pins the broker harness — a mismatched `input.cli` is rejected — and the exported `RelayPlacementError` reports `capability_mismatch` / `placement_queue_full` / `placement_ttl_expired` / `unmapped_repo`. +- `agent-relay fleet config|enable|disable|inherit` and `@agent-relay/sdk` `workspace.fleetNodes` expose the per-workspace fleet node rollout flag. - Two-node fleet E2E (`tests/e2e/fleet`, `npm run test:e2e`, `Fleet E2E` CI workflow): boots a real relaycast engine plus two `agent-relay fleet serve` nodes (real Rust broker + sidecar each) and asserts the live control wire — boot/register (real broker `Authorization: Bearer` node auth), negative auth, capability-filtered roster, cross-node action dispatch + ack, declarative trigger fire-once with loop guard, end-to-end spawn completion (token mint+inject), capability-routed + least-loaded + resume placement, `capability_mismatch` failure, in-flight reschedule on node death + restart reconcile, and bounded-mailbox TTL dead-letter. ### Changed diff --git a/package-lock.json b/package-lock.json index 5d30e293d..4296ee410 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@agent-relay/monorepo", - "version": "8.8.3", + "version": "8.8.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@agent-relay/monorepo", - "version": "8.8.3", + "version": "8.8.5", "license": "Apache-2.0", "workspaces": [ "packages/*", @@ -2104,6 +2104,7 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", + "extraneous": true, "inBundle": true, "license": "MIT", "engines": { @@ -2538,7 +2539,6 @@ "os": [ "aix" ], - "peer": true, "engines": { "node": ">=18" } @@ -2556,7 +2556,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -2574,7 +2573,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -2592,7 +2590,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -2610,7 +2607,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=18" } @@ -2628,7 +2624,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=18" } @@ -2646,7 +2641,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -2664,7 +2658,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -2682,7 +2675,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2700,7 +2692,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2718,7 +2709,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2736,7 +2726,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2754,7 +2743,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2772,7 +2760,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2790,7 +2777,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2808,7 +2794,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2826,7 +2811,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2844,7 +2828,6 @@ "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -2862,7 +2845,6 @@ "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -2880,7 +2862,6 @@ "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -2898,7 +2879,6 @@ "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -2916,7 +2896,6 @@ "os": [ "openharmony" ], - "peer": true, "engines": { "node": ">=18" } @@ -2934,7 +2913,6 @@ "os": [ "sunos" ], - "peer": true, "engines": { "node": ">=18" } @@ -2952,7 +2930,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -2970,7 +2947,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -2988,7 +2964,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -9417,7 +9392,6 @@ "version": "0.0.7", "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz", "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==", - "dev": true, "optional": true, "engines": { "node": ">=10.0.0" @@ -9941,7 +9915,6 @@ "version": "0.0.10", "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", - "dev": true, "hasInstallScript": true, "optional": true, "dependencies": { @@ -15301,7 +15274,6 @@ "version": "2.27.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.27.0.tgz", "integrity": "sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ==", - "dev": true, "license": "MIT", "optional": true }, @@ -19633,46 +19605,46 @@ }, "packages/brand": { "name": "@agent-relay/brand", - "version": "8.8.3" + "version": "8.8.5" }, "packages/broker-darwin-arm64": { "name": "@agent-relay/broker-darwin-arm64", - "version": "8.8.3", + "version": "8.8.5", "license": "MIT" }, "packages/broker-darwin-x64": { "name": "@agent-relay/broker-darwin-x64", - "version": "8.8.3", + "version": "8.8.5", "license": "MIT" }, "packages/broker-linux-arm64": { "name": "@agent-relay/broker-linux-arm64", - "version": "8.8.3", + "version": "8.8.5", "license": "MIT" }, "packages/broker-linux-x64": { "name": "@agent-relay/broker-linux-x64", - "version": "8.8.3", + "version": "8.8.5", "license": "MIT" }, "packages/broker-win32-x64": { "name": "@agent-relay/broker-win32-x64", - "version": "8.8.3", + "version": "8.8.5", "license": "MIT" }, "packages/cli": { "name": "agent-relay", - "version": "8.8.3", + "version": "8.8.5", "license": "Apache-2.0", "dependencies": { - "@agent-relay/cloud": "8.8.3", - "@agent-relay/config": "8.8.3", - "@agent-relay/fleet": "8.8.3", - "@agent-relay/harness-driver": "8.8.3", - "@agent-relay/sdk": "8.8.3", - "@agent-relay/utils": "8.8.3", + "@agent-relay/cloud": "8.8.5", + "@agent-relay/config": "8.8.5", + "@agent-relay/fleet": "8.8.5", + "@agent-relay/harness-driver": "8.8.5", + "@agent-relay/sdk": "8.8.5", + "@agent-relay/utils": "8.8.5", "@modelcontextprotocol/sdk": "^1.0.0", - "@relaycast/sdk": "^4.0.0", + "@relaycast/sdk": "^4.1.0", "@relayflows/cli": "^1.0.1", "@xterm/headless": "^6.0.0", "commander": "^12.1.0", @@ -19693,11 +19665,46 @@ "node": ">=20.9.0" } }, + "packages/cli/node_modules/@relaycast/sdk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@relaycast/sdk/-/sdk-4.1.0.tgz", + "integrity": "sha512-4NXu/hZstJOFWrYIy3IuzD0yfQwrb9qnJTFlRs95R2CFE8Ml4fArIItDLV+cOMqUTkY3o+T8MQG3bq+LHOrsaw==", + "dependencies": { + "@relaycast/types": "4.1.0", + "zod": "^4.3.6" + } + }, + "packages/cli/node_modules/@relaycast/sdk/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "packages/cli/node_modules/@relaycast/types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@relaycast/types/-/types-4.1.0.tgz", + "integrity": "sha512-A1dEjGBzUBndNbkfRnDDAgwDWL2L+eY7An4ifkceC6F+mwfWopoGCNf+FYCQoCGDbBkz2tRK0wzLiawCl74P8A==", + "dependencies": { + "zod": "^4.3.6" + } + }, + "packages/cli/node_modules/@relaycast/types/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "packages/cloud": { "name": "@agent-relay/cloud", - "version": "8.8.3", + "version": "8.8.5", "dependencies": { - "@agent-relay/config": "8.8.3", + "@agent-relay/config": "8.8.5", "@aws-sdk/client-s3": "3.1020.0", "ignore": "^7.0.5", "tar": "^7.5.10" @@ -19713,7 +19720,7 @@ }, "packages/config": { "name": "@agent-relay/config", - "version": "8.8.3", + "version": "8.8.5", "dependencies": { "zod": "^3.23.8", "zod-to-json-schema": "^3.23.1" @@ -19726,60 +19733,60 @@ }, "packages/evals": { "name": "@agent-relay/evals", - "version": "8.8.3", + "version": "8.8.5", "license": "Apache-2.0", "dependencies": { - "@agent-relay/harness-driver": "8.8.3", - "@agent-relay/integration-prompts": "8.8.3" + "@agent-relay/harness-driver": "8.8.5", + "@agent-relay/integration-prompts": "8.8.5" } }, "packages/fleet": { "name": "@agent-relay/fleet", - "version": "8.8.3", + "version": "8.8.5", "license": "Apache-2.0", "dependencies": { - "@agent-relay/harness-driver": "8.8.3", - "@agent-relay/harnesses": "8.8.3", - "@agent-relay/sdk": "8.8.3", + "@agent-relay/harness-driver": "8.8.5", + "@agent-relay/harnesses": "8.8.5", + "@agent-relay/sdk": "8.8.5", "zod": "^3.23.8" } }, "packages/harness-driver": { "name": "@agent-relay/harness-driver", - "version": "8.8.3", + "version": "8.8.5", "license": "Apache-2.0", "dependencies": { - "@agent-relay/sdk": "8.8.3", + "@agent-relay/sdk": "8.8.5", "ws": "^8.18.3", "zod": "^3.23.8" }, "optionalDependencies": { - "@agent-relay/broker-darwin-arm64": "8.8.3", - "@agent-relay/broker-darwin-x64": "8.8.3", - "@agent-relay/broker-linux-arm64": "8.8.3", - "@agent-relay/broker-linux-x64": "8.8.3", - "@agent-relay/broker-win32-x64": "8.8.3" + "@agent-relay/broker-darwin-arm64": "8.8.5", + "@agent-relay/broker-darwin-x64": "8.8.5", + "@agent-relay/broker-linux-arm64": "8.8.5", + "@agent-relay/broker-linux-x64": "8.8.5", + "@agent-relay/broker-win32-x64": "8.8.5" } }, "packages/harnesses": { "name": "@agent-relay/harnesses", - "version": "8.8.3", + "version": "8.8.5", "license": "Apache-2.0", "dependencies": { - "@agent-relay/harness-driver": "8.8.3", - "@agent-relay/sdk": "8.8.3" + "@agent-relay/harness-driver": "8.8.5", + "@agent-relay/sdk": "8.8.5" } }, "packages/integration-prompts": { "name": "@agent-relay/integration-prompts", - "version": "8.8.3", + "version": "8.8.5", "license": "Apache-2.0" }, "packages/policy": { "name": "@agent-relay/policy", - "version": "8.8.3", + "version": "8.8.5", "dependencies": { - "@agent-relay/config": "8.8.3" + "@agent-relay/config": "8.8.5" }, "devDependencies": { "@types/node": "^22.19.3", @@ -19788,24 +19795,50 @@ }, "packages/sdk": { "name": "@agent-relay/sdk", - "version": "8.8.3", + "version": "8.8.5", "dependencies": { - "@relaycast/sdk": "^4.0.0" + "@relaycast/sdk": "^4.1.0" }, "devDependencies": { "@types/node": "^22.13.10" } }, + "packages/sdk/node_modules/@relaycast/sdk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@relaycast/sdk/-/sdk-4.1.0.tgz", + "integrity": "sha512-4NXu/hZstJOFWrYIy3IuzD0yfQwrb9qnJTFlRs95R2CFE8Ml4fArIItDLV+cOMqUTkY3o+T8MQG3bq+LHOrsaw==", + "dependencies": { + "@relaycast/types": "4.1.0", + "zod": "^4.3.6" + } + }, + "packages/sdk/node_modules/@relaycast/types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@relaycast/types/-/types-4.1.0.tgz", + "integrity": "sha512-A1dEjGBzUBndNbkfRnDDAgwDWL2L+eY7An4ifkceC6F+mwfWopoGCNf+FYCQoCGDbBkz2tRK0wzLiawCl74P8A==", + "dependencies": { + "zod": "^4.3.6" + } + }, + "packages/sdk/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "packages/telemetry": { "name": "@agent-relay/telemetry", - "version": "8.8.3", + "version": "8.8.5", "deprecated": "@agent-relay/telemetry is deprecated. Telemetry is now internal to the agent-relay CLI." }, "packages/utils": { "name": "@agent-relay/utils", - "version": "8.8.3", + "version": "8.8.5", "dependencies": { - "@agent-relay/config": "8.8.3", + "@agent-relay/config": "8.8.5", "compare-versions": "^6.1.1" }, "devDependencies": { diff --git a/packages/cli/package.json b/packages/cli/package.json index e4a59dfd9..bbeb5874e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -50,7 +50,7 @@ "@agent-relay/sdk": "8.8.5", "@agent-relay/utils": "8.8.5", "@modelcontextprotocol/sdk": "^1.0.0", - "@relaycast/sdk": "^4.0.0", + "@relaycast/sdk": "^4.1.0", "@relayflows/cli": "^1.0.1", "@xterm/headless": "^6.0.0", "commander": "^12.1.0", diff --git a/packages/cli/src/cli/commands/fleet.test.ts b/packages/cli/src/cli/commands/fleet.test.ts index 827423bf7..2e4fb33ad 100644 --- a/packages/cli/src/cli/commands/fleet.test.ts +++ b/packages/cli/src/cli/commands/fleet.test.ts @@ -11,6 +11,58 @@ import { loadNodeDefinition, registerFleetCommands, stripEnrollmentFlags } from import { startFleetSidecar, serveFleetSidecar } from '../lib/fleet-sidecar.js'; describe('fleet command support', () => { + it.each([ + ['config', 'get', undefined], + ['enable', 'set', true], + ['disable', 'set', false], + ['inherit', 'inherit', undefined], + ] as const)('fleet %s delegates to workspace fleet node config API', async (command, method, value) => { + const fleetNodes = { + get: vi.fn(async () => ({ enabled: false, defaultEnabled: false, override: null })), + set: vi.fn(async (enabled: boolean) => ({ enabled, defaultEnabled: false, override: enabled })), + inherit: vi.fn(async () => ({ enabled: false, defaultEnabled: false, override: null })), + }; + const createWorkspaceRelay = vi.fn(() => ({ workspace: { fleetNodes } })); + const logs: string[] = []; + const program = new Command(); + program.exitOverride(); + registerFleetCommands(program, { + sdk: { + createAgentRelay: vi.fn() as never, + createWorkspaceRelay: createWorkspaceRelay as never, + createWorkspace: vi.fn() as never, + log: (message: unknown) => logs.push(String(message)), + error: vi.fn(), + exit: vi.fn(() => { + throw new Error('__exit__'); + }) as never, + }, + log: () => undefined, + warn: () => undefined, + error: () => undefined, + }); + + await program.parseAsync( + ['fleet', command, '--workspace-key', 'rk_live_test', '--base-url', 'https://relay.example'], + { from: 'user' } + ); + + expect(createWorkspaceRelay).toHaveBeenCalledWith({ + workspaceKey: 'rk_live_test', + token: undefined, + baseUrl: 'https://relay.example', + }); + if (method === 'set') { + expect(fleetNodes.set).toHaveBeenCalledWith(value); + } else { + expect(fleetNodes[method]).toHaveBeenCalledTimes(1); + } + expect(JSON.parse(logs[0]!)).toMatchObject({ + enabled: method === 'set' ? value : false, + defaultEnabled: false, + }); + }); + it('loads the example TS node file', async () => { const node = await loadNodeDefinition(path.resolve(process.cwd(), 'examples/relay-node.ts')); diff --git a/packages/cli/src/cli/commands/fleet.ts b/packages/cli/src/cli/commands/fleet.ts index a0d77a9fb..29db09548 100644 --- a/packages/cli/src/cli/commands/fleet.ts +++ b/packages/cli/src/cli/commands/fleet.ts @@ -107,6 +107,42 @@ export function registerFleetCommands( }); }); + addSdkOptions(group.command('config').description('Show workspace fleet node configuration')).action( + async (options: Record) => { + await runSdk(deps.sdk, async () => { + const relay = deps.sdk.createWorkspaceRelay(sdkOptionsFromOpts(options)); + printJson(deps.sdk, await relay.workspace.fleetNodes.get()); + }); + } + ); + + addSdkOptions(group.command('enable').description('Enable fleet nodes for the workspace')).action( + async (options: Record) => { + await runSdk(deps.sdk, async () => { + const relay = deps.sdk.createWorkspaceRelay(sdkOptionsFromOpts(options)); + printJson(deps.sdk, await relay.workspace.fleetNodes.set(true)); + }); + } + ); + + addSdkOptions(group.command('disable').description('Disable fleet nodes for the workspace')).action( + async (options: Record) => { + await runSdk(deps.sdk, async () => { + const relay = deps.sdk.createWorkspaceRelay(sdkOptionsFromOpts(options)); + printJson(deps.sdk, await relay.workspace.fleetNodes.set(false)); + }); + } + ); + + addSdkOptions(group.command('inherit').description('Use the deployment default for workspace fleet nodes')).action( + async (options: Record) => { + await runSdk(deps.sdk, async () => { + const relay = deps.sdk.createWorkspaceRelay(sdkOptionsFromOpts(options)); + printJson(deps.sdk, await relay.workspace.fleetNodes.inherit()); + }); + } + ); + group .command('status') .description('Show local fleet broker and sidecar status') diff --git a/packages/sdk/package.json b/packages/sdk/package.json index b9a987aff..8a73077c2 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -59,6 +59,6 @@ "@types/node": "^22.13.10" }, "dependencies": { - "@relaycast/sdk": "^4.0.0" + "@relaycast/sdk": "^4.1.0" } } diff --git a/packages/sdk/src/__tests__/facade.test.ts b/packages/sdk/src/__tests__/facade.test.ts index 8c4ba4f1a..cc2da2cf8 100644 --- a/packages/sdk/src/__tests__/facade.test.ts +++ b/packages/sdk/src/__tests__/facade.test.ts @@ -32,8 +32,16 @@ function createMessagingMock() { channels: [], })), }; - const messaging = { messages, agents, events: {} } as unknown as RelayMessaging; - return { messaging, messages, agents }; + const workspace = { + info: vi.fn(async () => ({ id: 'ws_1', name: 'Ops' })), + fleetNodes: { + get: vi.fn(async () => ({ enabled: false, defaultEnabled: false, override: null })), + set: vi.fn(async (enabled: boolean) => ({ enabled, defaultEnabled: false, override: enabled })), + inherit: vi.fn(async () => ({ enabled: false, defaultEnabled: false, override: null })), + }, + }; + const messaging = { messages, agents, workspace, events: {} } as unknown as RelayMessaging; + return { messaging, messages, agents, workspace }; } describe('AgentRelay facade (Phase A)', () => { @@ -72,6 +80,31 @@ describe('AgentRelay facade (Phase A)', () => { expect(client.token).toBe('tok-self'); }); + it('workspace.fleetNodes delegates fleet node config calls', async () => { + const { messaging, workspace } = createMessagingMock(); + const relay = new AgentRelay({ messaging }); + + await expect(relay.workspace.fleetNodes.get()).resolves.toEqual({ + enabled: false, + defaultEnabled: false, + override: null, + }); + await expect(relay.workspace.fleetNodes.set(true)).resolves.toEqual({ + enabled: true, + defaultEnabled: false, + override: true, + }); + await expect(relay.workspace.fleetNodes.inherit()).resolves.toEqual({ + enabled: false, + defaultEnabled: false, + override: null, + }); + + expect(workspace.fleetNodes.get).toHaveBeenCalledTimes(1); + expect(workspace.fleetNodes.set).toHaveBeenCalledWith(true); + expect(workspace.fleetNodes.inherit).toHaveBeenCalledTimes(1); + }); + it('sendMessage routes #channel to messages.send and a bare name to direct', async () => { const { messaging, messages } = createMessagingMock(); const relay = new AgentRelay({ messaging, createAgentMessaging: () => messaging }); diff --git a/packages/sdk/src/__tests__/messaging.test.ts b/packages/sdk/src/__tests__/messaging.test.ts index 48d7d7b00..bb42d8311 100644 --- a/packages/sdk/src/__tests__/messaging.test.ts +++ b/packages/sdk/src/__tests__/messaging.test.ts @@ -143,6 +143,14 @@ function createWorkspace() { load: 0, })), }, + workspace: { + info: vi.fn(async () => ({ id: 'ws_1', name: 'Ops' })), + fleetNodes: { + get: vi.fn(async () => ({ enabled: true, default_enabled: false, override: true })), + set: vi.fn(async (enabled: boolean) => ({ enabled, default_enabled: false, override: enabled })), + inherit: vi.fn(async () => ({ enabled: false, default_enabled: false, override: null })), + }, + }, }; } @@ -359,6 +367,92 @@ describe('RelaycastMessagingClient', () => { }); }); + it('delegates workspace fleet node config calls to Relaycast', async () => { + const workspace = createWorkspace(); + const client = new RelaycastMessagingClient({ relaycast: workspace }); + + await expect(client.workspace.fleetNodes.get()).resolves.toEqual({ + enabled: true, + defaultEnabled: false, + override: true, + }); + await expect(client.workspace.fleetNodes.set(false)).resolves.toEqual({ + enabled: false, + defaultEnabled: false, + override: false, + }); + await expect(client.workspace.fleetNodes.inherit()).resolves.toEqual({ + enabled: false, + defaultEnabled: false, + override: null, + }); + + expect(workspace.workspace.fleetNodes.get).toHaveBeenCalledTimes(1); + expect(workspace.workspace.fleetNodes.set).toHaveBeenCalledWith(false); + expect(workspace.workspace.fleetNodes.inherit).toHaveBeenCalledTimes(1); + }); + + it('falls back to the REST workspace fleet node config endpoint when the Relaycast SDK method is unavailable', async () => { + const workspace = createWorkspace(); + delete (workspace.workspace as { fleetNodes?: unknown }).fleetNodes; + const fetchMock = vi.fn(async () => + new Response(JSON.stringify({ + ok: true, + data: { + enabled: true, + default_enabled: false, + override: true, + }, + }), { + status: 200, + headers: { 'content-type': 'application/json' }, + }) + ); + vi.stubGlobal('fetch', fetchMock); + + try { + const client = new RelaycastMessagingClient({ + relaycast: workspace, + workspaceKey: 'rk_live_test', + baseUrl: 'https://gateway.example.test', + }); + + await expect(client.workspace.fleetNodes.get()).resolves.toEqual({ + enabled: true, + defaultEnabled: false, + override: true, + }); + await expect(client.workspace.fleetNodes.set(false)).resolves.toEqual({ + enabled: true, + defaultEnabled: false, + override: true, + }); + await expect(client.workspace.fleetNodes.inherit()).resolves.toEqual({ + enabled: true, + defaultEnabled: false, + override: true, + }); + + expect(fetchMock).toHaveBeenCalledTimes(3); + expect(fetchMock.mock.calls[0]?.[0]).toBe('https://gateway.example.test/v1/workspace/fleet-nodes'); + expect(fetchMock.mock.calls[0]?.[1]).toMatchObject({ + method: 'GET', + headers: expect.objectContaining({ Authorization: 'Bearer rk_live_test' }), + }); + expect(fetchMock.mock.calls[1]?.[1]).toMatchObject({ + method: 'PUT', + headers: expect.objectContaining({ + Authorization: 'Bearer rk_live_test', + 'Content-Type': 'application/json', + }), + }); + expect(JSON.parse(fetchMock.mock.calls[1]?.[1]?.body as string)).toEqual({ enabled: false }); + expect(JSON.parse(fetchMock.mock.calls[2]?.[1]?.body as string)).toEqual({ mode: 'inherit' }); + } finally { + vi.unstubAllGlobals(); + } + }); + it('normalizes fleet node roster fields and passes node query options through', async () => { const workspace = createWorkspace(); const client = new RelaycastMessagingClient({ relaycast: workspace }); diff --git a/packages/sdk/src/facade.ts b/packages/sdk/src/facade.ts index 145ea019c..88cb24f37 100644 --- a/packages/sdk/src/facade.ts +++ b/packages/sdk/src/facade.ts @@ -11,6 +11,7 @@ import type { RelayMessageMode, RelaySendChannelMessageInput, RelayWorkspaceInfo, + RelayWorkspaceFleetNodesConfig, } from './messaging/index.js'; import { actionSchemaToJsonSchema, @@ -196,6 +197,11 @@ export interface RelayWorkspace { ): Promise; reconnect(input: { apiToken: string }): Promise; info(): Promise; + fleetNodes: { + get(): Promise; + set(enabled: boolean): Promise; + inherit(): Promise; + }; } export interface NotifyOptions { @@ -353,6 +359,7 @@ export function createWorkspaceFacade(messaging: RelayMessaging, deps?: Workspac return { info: () => messaging.workspace.info(), + fleetNodes: messaging.workspace.fleetNodes, register: register as RelayWorkspace['register'], reconnect: async ({ apiToken }) => { if (!deps) { diff --git a/packages/sdk/src/messaging/relaycast.ts b/packages/sdk/src/messaging/relaycast.ts index d82bf3748..38dc32aea 100644 --- a/packages/sdk/src/messaging/relaycast.ts +++ b/packages/sdk/src/messaging/relaycast.ts @@ -47,6 +47,7 @@ import type { RelayRegisterCapabilityInput, RelayWebhook, RelayWorkspaceInfo, + RelayWorkspaceFleetNodesConfig, InboxAckInput, InboxDeferInput, InboxFailInput, @@ -243,6 +244,15 @@ function readRecord(record: Record, ...keys: string[]): Record< return undefined; } +function toRelayWorkspaceFleetNodesConfig(raw: unknown): RelayWorkspaceFleetNodesConfig { + const record = asRecord(raw); + return { + enabled: readBoolean(record, 'enabled') ?? false, + defaultEnabled: readBoolean(record, 'defaultEnabled', 'default_enabled') ?? false, + override: readBoolean(record, 'override') ?? null, + }; +} + type PlacementReconcileReason = 'no_eligible_node' | 'target_offline' | 'unmapped_repo'; type PlacementSelection = @@ -466,6 +476,11 @@ type RelaycastWorkspaceLike = { }; workspace?: { info(): Promise; + fleetNodes?: { + get(): Promise; + set(enabled: boolean): Promise; + inherit(): Promise; + }; }; as?: (agentToken: string, options?: AgentClientOptions) => RelaycastAgentLike; // Workspace-scoped realtime stream (relaycast 2.5+): lets a workspace-key @@ -482,6 +497,8 @@ type RelaycastAgentDeliverySurface = Required< Pick >; +const DEFAULT_RELAYCAST_BASE_URL = 'https://gateway.relaycast.dev'; + type RelaycastAgentLike = { me(): Promise; connect(): void; @@ -639,6 +656,8 @@ export class RelaycastMessagingClient implements RelayMessagingClient { private readonly relaycast: RelaycastWorkspaceLike; private readonly agentClient?: RelaycastAgentLike; + private readonly workspaceKey?: string; + private readonly baseUrl: string; private readonly selfNodeName?: string; private readonly placementTtlMs: number; private readonly maxQueuedPlacements: number; @@ -655,6 +674,8 @@ export class RelaycastMessagingClient implements RelayMessagingClient { this.agentClient = options.agentClient ?? (options.agentToken ? this.relaycast.as?.(options.agentToken, options.agentClientOptions) : undefined); + this.workspaceKey = options.workspaceKey ?? options.apiKey; + this.baseUrl = options.baseUrl ?? DEFAULT_RELAYCAST_BASE_URL; this.selfNodeName = options.selfNodeName; this.placementTtlMs = options.placementTtlMs ?? 60 * 60 * 1000; this.maxQueuedPlacements = options.maxQueuedPlacements ?? 100; @@ -1250,8 +1271,70 @@ export class RelaycastMessagingClient implements RelayMessagingClient { } return (await this.relaycast.workspace.info()) as RelayWorkspaceInfo; }, + fleetNodes: { + get: async (): Promise => { + const api = this.relaycast.workspace?.fleetNodes; + return api + ? toRelayWorkspaceFleetNodesConfig(await api.get()) + : await this.requestWorkspaceFleetNodesConfig('GET'); + }, + set: async (enabled: boolean): Promise => { + const api = this.relaycast.workspace?.fleetNodes; + return api + ? toRelayWorkspaceFleetNodesConfig(await api.set(enabled)) + : await this.requestWorkspaceFleetNodesConfig('PUT', { enabled }); + }, + inherit: async (): Promise => { + const api = this.relaycast.workspace?.fleetNodes; + return api + ? toRelayWorkspaceFleetNodesConfig(await api.inherit()) + : await this.requestWorkspaceFleetNodesConfig('PUT', { mode: 'inherit' }); + }, + }, }; + private async requestWorkspaceFleetNodesConfig( + method: 'GET' | 'PUT', + body?: { enabled: boolean } | { mode: 'inherit' } + ): Promise { + if (!this.workspaceKey) { + throw new Error( + 'RelaycastMessagingClient.workspace.fleetNodes requires @relaycast/sdk with the workspace fleet nodes API or a workspaceKey for the REST fallback.' + ); + } + + const url = new URL('/v1/workspace/fleet-nodes', this.baseUrl); + const response = await fetch(url.toString(), { + method, + headers: { + Accept: 'application/json', + Authorization: `Bearer ${this.workspaceKey}`, + ...(body ? { 'Content-Type': 'application/json' } : {}), + }, + ...(body ? { body: JSON.stringify(body) } : {}), + }); + + let payload: unknown; + try { + payload = await response.json(); + } catch (error) { + throw new Error( + `RelaycastMessagingClient.workspace.fleetNodes failed to parse Relaycast response: ${ + error instanceof Error ? error.message : String(error) + }` + ); + } + + const envelope = asRecord(payload); + if (!response.ok || envelope.ok === false) { + const error = asRecord(envelope.error); + const message = readStr(error, 'message') ?? `Relaycast fleet node config request failed (${response.status})`; + throw new Error(message); + } + + return toRelayWorkspaceFleetNodesConfig(envelope.data ?? payload); + } + private resolvePlacementNode(node: string | 'self' | undefined, selfNodeName?: string): string | undefined { if (!node) return undefined; if (node !== 'self') return nonEmptyPlacement(node, 'placement node'); diff --git a/packages/sdk/src/messaging/types.ts b/packages/sdk/src/messaging/types.ts index 516183691..b0976baaa 100644 --- a/packages/sdk/src/messaging/types.ts +++ b/packages/sdk/src/messaging/types.ts @@ -593,6 +593,12 @@ export interface RelayWorkspaceInfo { [key: string]: unknown; } +export interface RelayWorkspaceFleetNodesConfig { + enabled: boolean; + defaultEnabled: boolean; + override: boolean | null; +} + export type InboxItemState = 'queued' | 'delivered' | 'failed' | 'deferred' | 'read'; export interface InboxItem { @@ -917,6 +923,11 @@ export interface RelayMessagingClient { }; readonly workspace: { info(): Promise; + fleetNodes: { + get(): Promise; + set(enabled: boolean): Promise; + inherit(): Promise; + }; }; } diff --git a/web/content/docs/fleets.mdx b/web/content/docs/fleets.mdx index 635651fa4..7fd0e21fb 100644 --- a/web/content/docs/fleets.mdx +++ b/web/content/docs/fleets.mdx @@ -13,8 +13,8 @@ One `fleet serve` = one node. A node advertises which harnesses it can run; the nodes that have the harness you asked for. - Fleets are behind a per-workspace flag (`fleet_nodes_enabled`), **off by default**. Ask your - operator to enable it before serving a node. + Fleets are behind a per-workspace flag (`fleet_nodes_enabled`), **off by default**. Enable it with + `agent-relay fleet enable --workspace-key "$RELAY_WORKSPACE_KEY"` before serving a node. ## Set up a node From 54793428d93d225ea056675e16bdf6f80b980162 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 18 Jun 2026 17:49:04 +0000 Subject: [PATCH 2/8] style: auto-format with Prettier --- .../2026-06/traj_ws73z8bvitw0/summary.md | 4 ++- .../2026-06/traj_ws73z8bvitw0/trajectory.json | 2 +- packages/cli/src/cli/commands/fleet.ts | 16 +++++------ packages/sdk/src/__tests__/messaging.test.ts | 28 +++++++++++-------- packages/sdk/src/messaging/relaycast.ts | 3 +- 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md index 57a997e73..0fe6f9c9c 100644 --- a/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md +++ b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md @@ -18,6 +18,7 @@ Added Relay SDK workspace.fleetNodes get/set/inherit, CLI fleet config/enable/di ## Key Decisions ### Use REST fallback for workspace fleet config + - **Chose:** Use REST fallback for workspace fleet config - **Reasoning:** @relaycast/sdk 4.1.0 is published and installed, but its dist types do not expose workspace.fleetNodes, so Relay should call /v1/workspace/fleet-nodes directly when the SDK convenience method is absent. @@ -26,6 +27,7 @@ Added Relay SDK workspace.fleetNodes get/set/inherit, CLI fleet config/enable/di ## Chapters ### 1. Work -*Agent: default* + +_Agent: default_ - Use REST fallback for workspace fleet config: Use REST fallback for workspace fleet config diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json index b25341303..58e82ba73 100644 --- a/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json +++ b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json @@ -50,4 +50,4 @@ "startRef": "e4cbf8385499b9da734a2a36e99acc2dbc4f9bf5", "endRef": "e4cbf8385499b9da734a2a36e99acc2dbc4f9bf5" } -} \ No newline at end of file +} diff --git a/packages/cli/src/cli/commands/fleet.ts b/packages/cli/src/cli/commands/fleet.ts index 29db09548..b5747b8b0 100644 --- a/packages/cli/src/cli/commands/fleet.ts +++ b/packages/cli/src/cli/commands/fleet.ts @@ -134,14 +134,14 @@ export function registerFleetCommands( } ); - addSdkOptions(group.command('inherit').description('Use the deployment default for workspace fleet nodes')).action( - async (options: Record) => { - await runSdk(deps.sdk, async () => { - const relay = deps.sdk.createWorkspaceRelay(sdkOptionsFromOpts(options)); - printJson(deps.sdk, await relay.workspace.fleetNodes.inherit()); - }); - } - ); + addSdkOptions( + group.command('inherit').description('Use the deployment default for workspace fleet nodes') + ).action(async (options: Record) => { + await runSdk(deps.sdk, async () => { + const relay = deps.sdk.createWorkspaceRelay(sdkOptionsFromOpts(options)); + printJson(deps.sdk, await relay.workspace.fleetNodes.inherit()); + }); + }); group .command('status') diff --git a/packages/sdk/src/__tests__/messaging.test.ts b/packages/sdk/src/__tests__/messaging.test.ts index bb42d8311..570b345bc 100644 --- a/packages/sdk/src/__tests__/messaging.test.ts +++ b/packages/sdk/src/__tests__/messaging.test.ts @@ -395,18 +395,22 @@ describe('RelaycastMessagingClient', () => { it('falls back to the REST workspace fleet node config endpoint when the Relaycast SDK method is unavailable', async () => { const workspace = createWorkspace(); delete (workspace.workspace as { fleetNodes?: unknown }).fleetNodes; - const fetchMock = vi.fn(async () => - new Response(JSON.stringify({ - ok: true, - data: { - enabled: true, - default_enabled: false, - override: true, - }, - }), { - status: 200, - headers: { 'content-type': 'application/json' }, - }) + const fetchMock = vi.fn( + async () => + new Response( + JSON.stringify({ + ok: true, + data: { + enabled: true, + default_enabled: false, + override: true, + }, + }), + { + status: 200, + headers: { 'content-type': 'application/json' }, + } + ) ); vi.stubGlobal('fetch', fetchMock); diff --git a/packages/sdk/src/messaging/relaycast.ts b/packages/sdk/src/messaging/relaycast.ts index 38dc32aea..5ea5d6f71 100644 --- a/packages/sdk/src/messaging/relaycast.ts +++ b/packages/sdk/src/messaging/relaycast.ts @@ -1328,7 +1328,8 @@ export class RelaycastMessagingClient implements RelayMessagingClient { const envelope = asRecord(payload); if (!response.ok || envelope.ok === false) { const error = asRecord(envelope.error); - const message = readStr(error, 'message') ?? `Relaycast fleet node config request failed (${response.status})`; + const message = + readStr(error, 'message') ?? `Relaycast fleet node config request failed (${response.status})`; throw new Error(message); } From f8b1dac0b42501400a07c81cb5505ede2a813b5e Mon Sep 17 00:00:00 2001 From: Will Washburn Date: Thu, 18 Jun 2026 14:07:40 -0400 Subject: [PATCH 3/8] Remove fleet config REST fallback --- .../2026-06/traj_ws73z8bvitw0/summary.md | 10 +-- .../2026-06/traj_ws73z8bvitw0/trajectory.json | 10 +-- packages/sdk/src/__tests__/messaging.test.ts | 65 ----------------- packages/sdk/src/messaging/relaycast.ts | 73 +++---------------- 4 files changed, 22 insertions(+), 136 deletions(-) diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md index 0fe6f9c9c..dcf324f13 100644 --- a/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md +++ b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md @@ -9,7 +9,7 @@ ## Summary -Added Relay SDK workspace.fleetNodes get/set/inherit, CLI fleet config/enable/disable/inherit commands, docs and changelog entries, and updated Relay workspaces to @relaycast/sdk 4.1.0 with a REST fallback for the missing convenience method. SDK tests/build and focused fleet CLI tests pass; full CLI build is blocked by existing current-main cloud auth StoredAuth type errors. +Added Relay SDK workspace.fleetNodes get/set/inherit, CLI fleet config/enable/disable/inherit commands, docs and changelog entries, and updated Relay workspaces to @relaycast/sdk 4.1.0. Removed the direct REST fallback so Relay requires the Relaycast SDK workspace fleet-node method shipped by the follow-up Relaycast SDK fix. SDK tests/build and focused fleet CLI tests pass; full CLI build is blocked by existing current-main cloud auth StoredAuth type errors. **Approach:** Standard approach @@ -17,10 +17,10 @@ Added Relay SDK workspace.fleetNodes get/set/inherit, CLI fleet config/enable/di ## Key Decisions -### Use REST fallback for workspace fleet config +### Require Relaycast SDK workspace fleet config API -- **Chose:** Use REST fallback for workspace fleet config -- **Reasoning:** @relaycast/sdk 4.1.0 is published and installed, but its dist types do not expose workspace.fleetNodes, so Relay should call /v1/workspace/fleet-nodes directly when the SDK convenience method is absent. +- **Chose:** Require Relaycast SDK workspace fleet config API +- **Reasoning:** Relay should not duplicate the Relaycast REST call. The missing workspace.fleetNodes method belongs in @relaycast/sdk, so Relay delegates to the SDK surface and the Relaycast fix is tracked separately. --- @@ -30,4 +30,4 @@ Added Relay SDK workspace.fleetNodes get/set/inherit, CLI fleet config/enable/di _Agent: default_ -- Use REST fallback for workspace fleet config: Use REST fallback for workspace fleet config +- Require Relaycast SDK workspace fleet config API: Require Relaycast SDK workspace fleet config API diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json index 58e82ba73..65675bf58 100644 --- a/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json +++ b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json @@ -25,12 +25,12 @@ { "ts": 1781804831174, "type": "decision", - "content": "Use REST fallback for workspace fleet config: Use REST fallback for workspace fleet config", + "content": "Require Relaycast SDK workspace fleet config API: Require Relaycast SDK workspace fleet config API", "raw": { - "question": "Use REST fallback for workspace fleet config", - "chosen": "Use REST fallback for workspace fleet config", + "question": "Require Relaycast SDK workspace fleet config API", + "chosen": "Require Relaycast SDK workspace fleet config API", "alternatives": [], - "reasoning": "@relaycast/sdk 4.1.0 is published and installed, but its dist types do not expose workspace.fleetNodes, so Relay should call /v1/workspace/fleet-nodes directly when the SDK convenience method is absent." + "reasoning": "Relay should not duplicate the Relaycast REST call. The missing workspace.fleetNodes method belongs in @relaycast/sdk, so Relay delegates to the SDK surface and the Relaycast fix is tracked separately." }, "significance": "high" } @@ -38,7 +38,7 @@ } ], "retrospective": { - "summary": "Added Relay SDK workspace.fleetNodes get/set/inherit, CLI fleet config/enable/disable/inherit commands, docs and changelog entries, and updated Relay workspaces to @relaycast/sdk 4.1.0 with a REST fallback for the missing convenience method. SDK tests/build and focused fleet CLI tests pass; full CLI build is blocked by existing current-main cloud auth StoredAuth type errors.", + "summary": "Added Relay SDK workspace.fleetNodes get/set/inherit, CLI fleet config/enable/disable/inherit commands, docs and changelog entries, and updated Relay workspaces to @relaycast/sdk 4.1.0. Removed the direct REST fallback so Relay requires the Relaycast SDK workspace fleet-node method shipped by the follow-up Relaycast SDK fix. SDK tests/build and focused fleet CLI tests pass; full CLI build is blocked by existing current-main cloud auth StoredAuth type errors.", "approach": "Standard approach", "confidence": 0.82 }, diff --git a/packages/sdk/src/__tests__/messaging.test.ts b/packages/sdk/src/__tests__/messaging.test.ts index 570b345bc..5e8705939 100644 --- a/packages/sdk/src/__tests__/messaging.test.ts +++ b/packages/sdk/src/__tests__/messaging.test.ts @@ -392,71 +392,6 @@ describe('RelaycastMessagingClient', () => { expect(workspace.workspace.fleetNodes.inherit).toHaveBeenCalledTimes(1); }); - it('falls back to the REST workspace fleet node config endpoint when the Relaycast SDK method is unavailable', async () => { - const workspace = createWorkspace(); - delete (workspace.workspace as { fleetNodes?: unknown }).fleetNodes; - const fetchMock = vi.fn( - async () => - new Response( - JSON.stringify({ - ok: true, - data: { - enabled: true, - default_enabled: false, - override: true, - }, - }), - { - status: 200, - headers: { 'content-type': 'application/json' }, - } - ) - ); - vi.stubGlobal('fetch', fetchMock); - - try { - const client = new RelaycastMessagingClient({ - relaycast: workspace, - workspaceKey: 'rk_live_test', - baseUrl: 'https://gateway.example.test', - }); - - await expect(client.workspace.fleetNodes.get()).resolves.toEqual({ - enabled: true, - defaultEnabled: false, - override: true, - }); - await expect(client.workspace.fleetNodes.set(false)).resolves.toEqual({ - enabled: true, - defaultEnabled: false, - override: true, - }); - await expect(client.workspace.fleetNodes.inherit()).resolves.toEqual({ - enabled: true, - defaultEnabled: false, - override: true, - }); - - expect(fetchMock).toHaveBeenCalledTimes(3); - expect(fetchMock.mock.calls[0]?.[0]).toBe('https://gateway.example.test/v1/workspace/fleet-nodes'); - expect(fetchMock.mock.calls[0]?.[1]).toMatchObject({ - method: 'GET', - headers: expect.objectContaining({ Authorization: 'Bearer rk_live_test' }), - }); - expect(fetchMock.mock.calls[1]?.[1]).toMatchObject({ - method: 'PUT', - headers: expect.objectContaining({ - Authorization: 'Bearer rk_live_test', - 'Content-Type': 'application/json', - }), - }); - expect(JSON.parse(fetchMock.mock.calls[1]?.[1]?.body as string)).toEqual({ enabled: false }); - expect(JSON.parse(fetchMock.mock.calls[2]?.[1]?.body as string)).toEqual({ mode: 'inherit' }); - } finally { - vi.unstubAllGlobals(); - } - }); - it('normalizes fleet node roster fields and passes node query options through', async () => { const workspace = createWorkspace(); const client = new RelaycastMessagingClient({ relaycast: workspace }); diff --git a/packages/sdk/src/messaging/relaycast.ts b/packages/sdk/src/messaging/relaycast.ts index 5ea5d6f71..51e931d78 100644 --- a/packages/sdk/src/messaging/relaycast.ts +++ b/packages/sdk/src/messaging/relaycast.ts @@ -497,8 +497,6 @@ type RelaycastAgentDeliverySurface = Required< Pick >; -const DEFAULT_RELAYCAST_BASE_URL = 'https://gateway.relaycast.dev'; - type RelaycastAgentLike = { me(): Promise; connect(): void; @@ -656,8 +654,6 @@ export class RelaycastMessagingClient implements RelayMessagingClient { private readonly relaycast: RelaycastWorkspaceLike; private readonly agentClient?: RelaycastAgentLike; - private readonly workspaceKey?: string; - private readonly baseUrl: string; private readonly selfNodeName?: string; private readonly placementTtlMs: number; private readonly maxQueuedPlacements: number; @@ -674,8 +670,6 @@ export class RelaycastMessagingClient implements RelayMessagingClient { this.agentClient = options.agentClient ?? (options.agentToken ? this.relaycast.as?.(options.agentToken, options.agentClientOptions) : undefined); - this.workspaceKey = options.workspaceKey ?? options.apiKey; - this.baseUrl = options.baseUrl ?? DEFAULT_RELAYCAST_BASE_URL; this.selfNodeName = options.selfNodeName; this.placementTtlMs = options.placementTtlMs ?? 60 * 60 * 1000; this.maxQueuedPlacements = options.maxQueuedPlacements ?? 100; @@ -1273,69 +1267,17 @@ export class RelaycastMessagingClient implements RelayMessagingClient { }, fleetNodes: { get: async (): Promise => { - const api = this.relaycast.workspace?.fleetNodes; - return api - ? toRelayWorkspaceFleetNodesConfig(await api.get()) - : await this.requestWorkspaceFleetNodesConfig('GET'); + return toRelayWorkspaceFleetNodesConfig(await this.requireWorkspaceFleetNodes().get()); }, set: async (enabled: boolean): Promise => { - const api = this.relaycast.workspace?.fleetNodes; - return api - ? toRelayWorkspaceFleetNodesConfig(await api.set(enabled)) - : await this.requestWorkspaceFleetNodesConfig('PUT', { enabled }); + return toRelayWorkspaceFleetNodesConfig(await this.requireWorkspaceFleetNodes().set(enabled)); }, inherit: async (): Promise => { - const api = this.relaycast.workspace?.fleetNodes; - return api - ? toRelayWorkspaceFleetNodesConfig(await api.inherit()) - : await this.requestWorkspaceFleetNodesConfig('PUT', { mode: 'inherit' }); + return toRelayWorkspaceFleetNodesConfig(await this.requireWorkspaceFleetNodes().inherit()); }, }, }; - private async requestWorkspaceFleetNodesConfig( - method: 'GET' | 'PUT', - body?: { enabled: boolean } | { mode: 'inherit' } - ): Promise { - if (!this.workspaceKey) { - throw new Error( - 'RelaycastMessagingClient.workspace.fleetNodes requires @relaycast/sdk with the workspace fleet nodes API or a workspaceKey for the REST fallback.' - ); - } - - const url = new URL('/v1/workspace/fleet-nodes', this.baseUrl); - const response = await fetch(url.toString(), { - method, - headers: { - Accept: 'application/json', - Authorization: `Bearer ${this.workspaceKey}`, - ...(body ? { 'Content-Type': 'application/json' } : {}), - }, - ...(body ? { body: JSON.stringify(body) } : {}), - }); - - let payload: unknown; - try { - payload = await response.json(); - } catch (error) { - throw new Error( - `RelaycastMessagingClient.workspace.fleetNodes failed to parse Relaycast response: ${ - error instanceof Error ? error.message : String(error) - }` - ); - } - - const envelope = asRecord(payload); - if (!response.ok || envelope.ok === false) { - const error = asRecord(envelope.error); - const message = - readStr(error, 'message') ?? `Relaycast fleet node config request failed (${response.status})`; - throw new Error(message); - } - - return toRelayWorkspaceFleetNodesConfig(envelope.data ?? payload); - } - private resolvePlacementNode(node: string | 'self' | undefined, selfNodeName?: string): string | undefined { if (!node) return undefined; if (node !== 'self') return nonEmptyPlacement(node, 'placement node'); @@ -1481,6 +1423,15 @@ export class RelaycastMessagingClient implements RelayMessagingClient { return this.relaycast.triggers; } + private requireWorkspaceFleetNodes(): NonNullable['fleetNodes']> { + if (!this.relaycast.workspace?.fleetNodes) { + throw new Error( + 'RelaycastMessagingClient.workspace.fleetNodes requires @relaycast/sdk with the workspace fleet nodes API.' + ); + } + return this.relaycast.workspace.fleetNodes; + } + private requireAgentActions(operation: string): NonNullable { const actions = this.agentClient?.actions; if (!actions) { From 27e39cf01b9f1559c121cf7a25a534aef35169e4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 18 Jun 2026 18:09:30 +0000 Subject: [PATCH 4/8] style: auto-format with Prettier --- packages/sdk/src/messaging/relaycast.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/sdk/src/messaging/relaycast.ts b/packages/sdk/src/messaging/relaycast.ts index 51e931d78..f95ecb81d 100644 --- a/packages/sdk/src/messaging/relaycast.ts +++ b/packages/sdk/src/messaging/relaycast.ts @@ -1423,7 +1423,9 @@ export class RelaycastMessagingClient implements RelayMessagingClient { return this.relaycast.triggers; } - private requireWorkspaceFleetNodes(): NonNullable['fleetNodes']> { + private requireWorkspaceFleetNodes(): NonNullable< + NonNullable['fleetNodes'] + > { if (!this.relaycast.workspace?.fleetNodes) { throw new Error( 'RelaycastMessagingClient.workspace.fleetNodes requires @relaycast/sdk with the workspace fleet nodes API.' From 6399193c4713e29ae6a9c280720c7d4d4edb4ee1 Mon Sep 17 00:00:00 2001 From: Will Washburn Date: Thu, 18 Jun 2026 16:09:17 -0400 Subject: [PATCH 5/8] Update Relaycast SDK to 4.1.1 --- .../2026-06/traj_ws73z8bvitw0/summary.md | 2 +- .../2026-06/traj_ws73z8bvitw0/trajectory.json | 2 +- package-lock.json | 32 +++++++++---------- packages/cli/package.json | 2 +- packages/sdk/package.json | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md index dcf324f13..439605c09 100644 --- a/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md +++ b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/summary.md @@ -9,7 +9,7 @@ ## Summary -Added Relay SDK workspace.fleetNodes get/set/inherit, CLI fleet config/enable/disable/inherit commands, docs and changelog entries, and updated Relay workspaces to @relaycast/sdk 4.1.0. Removed the direct REST fallback so Relay requires the Relaycast SDK workspace fleet-node method shipped by the follow-up Relaycast SDK fix. SDK tests/build and focused fleet CLI tests pass; full CLI build is blocked by existing current-main cloud auth StoredAuth type errors. +Added Relay SDK workspace.fleetNodes get/set/inherit, CLI fleet config/enable/disable/inherit commands, docs and changelog entries, and updated Relay workspaces to @relaycast/sdk 4.1.1, which ships the Relaycast SDK workspace fleet-node method. SDK tests/build and focused fleet CLI tests pass; full CLI build is blocked by existing current-main cloud auth StoredAuth type errors. **Approach:** Standard approach diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json index 65675bf58..d0bf8ed0f 100644 --- a/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json +++ b/.agentworkforce/trajectories/completed/2026-06/traj_ws73z8bvitw0/trajectory.json @@ -38,7 +38,7 @@ } ], "retrospective": { - "summary": "Added Relay SDK workspace.fleetNodes get/set/inherit, CLI fleet config/enable/disable/inherit commands, docs and changelog entries, and updated Relay workspaces to @relaycast/sdk 4.1.0. Removed the direct REST fallback so Relay requires the Relaycast SDK workspace fleet-node method shipped by the follow-up Relaycast SDK fix. SDK tests/build and focused fleet CLI tests pass; full CLI build is blocked by existing current-main cloud auth StoredAuth type errors.", + "summary": "Added Relay SDK workspace.fleetNodes get/set/inherit, CLI fleet config/enable/disable/inherit commands, docs and changelog entries, and updated Relay workspaces to @relaycast/sdk 4.1.1, which ships the Relaycast SDK workspace fleet-node method. SDK tests/build and focused fleet CLI tests pass; full CLI build is blocked by existing current-main cloud auth StoredAuth type errors.", "approach": "Standard approach", "confidence": 0.82 }, diff --git a/package-lock.json b/package-lock.json index 4296ee410..ef3ed990c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19644,7 +19644,7 @@ "@agent-relay/sdk": "8.8.5", "@agent-relay/utils": "8.8.5", "@modelcontextprotocol/sdk": "^1.0.0", - "@relaycast/sdk": "^4.1.0", + "@relaycast/sdk": "^4.1.1", "@relayflows/cli": "^1.0.1", "@xterm/headless": "^6.0.0", "commander": "^12.1.0", @@ -19666,11 +19666,11 @@ } }, "packages/cli/node_modules/@relaycast/sdk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@relaycast/sdk/-/sdk-4.1.0.tgz", - "integrity": "sha512-4NXu/hZstJOFWrYIy3IuzD0yfQwrb9qnJTFlRs95R2CFE8Ml4fArIItDLV+cOMqUTkY3o+T8MQG3bq+LHOrsaw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@relaycast/sdk/-/sdk-4.1.1.tgz", + "integrity": "sha512-+9c21F4nRpmOtP4pYTUVJ60Vqju2dA9ADuCHY5D+Q/AtZCmG0zGZLmCXqsdStC7FJHo8e+SfiObvVxcFao+DEg==", "dependencies": { - "@relaycast/types": "4.1.0", + "@relaycast/types": "4.1.1", "zod": "^4.3.6" } }, @@ -19684,9 +19684,9 @@ } }, "packages/cli/node_modules/@relaycast/types": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@relaycast/types/-/types-4.1.0.tgz", - "integrity": "sha512-A1dEjGBzUBndNbkfRnDDAgwDWL2L+eY7An4ifkceC6F+mwfWopoGCNf+FYCQoCGDbBkz2tRK0wzLiawCl74P8A==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@relaycast/types/-/types-4.1.1.tgz", + "integrity": "sha512-99K+pGTzuW8exa2gu0p3a9EpVywK6SHuDSnGUqmXdIbxoTgF0r73QkP+qBhZ+2qPacwcWo2Cjn0IW5RKuzsIwA==", "dependencies": { "zod": "^4.3.6" } @@ -19797,25 +19797,25 @@ "name": "@agent-relay/sdk", "version": "8.8.5", "dependencies": { - "@relaycast/sdk": "^4.1.0" + "@relaycast/sdk": "^4.1.1" }, "devDependencies": { "@types/node": "^22.13.10" } }, "packages/sdk/node_modules/@relaycast/sdk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@relaycast/sdk/-/sdk-4.1.0.tgz", - "integrity": "sha512-4NXu/hZstJOFWrYIy3IuzD0yfQwrb9qnJTFlRs95R2CFE8Ml4fArIItDLV+cOMqUTkY3o+T8MQG3bq+LHOrsaw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@relaycast/sdk/-/sdk-4.1.1.tgz", + "integrity": "sha512-+9c21F4nRpmOtP4pYTUVJ60Vqju2dA9ADuCHY5D+Q/AtZCmG0zGZLmCXqsdStC7FJHo8e+SfiObvVxcFao+DEg==", "dependencies": { - "@relaycast/types": "4.1.0", + "@relaycast/types": "4.1.1", "zod": "^4.3.6" } }, "packages/sdk/node_modules/@relaycast/types": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@relaycast/types/-/types-4.1.0.tgz", - "integrity": "sha512-A1dEjGBzUBndNbkfRnDDAgwDWL2L+eY7An4ifkceC6F+mwfWopoGCNf+FYCQoCGDbBkz2tRK0wzLiawCl74P8A==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@relaycast/types/-/types-4.1.1.tgz", + "integrity": "sha512-99K+pGTzuW8exa2gu0p3a9EpVywK6SHuDSnGUqmXdIbxoTgF0r73QkP+qBhZ+2qPacwcWo2Cjn0IW5RKuzsIwA==", "dependencies": { "zod": "^4.3.6" } diff --git a/packages/cli/package.json b/packages/cli/package.json index bbeb5874e..5f845f1bf 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -50,7 +50,7 @@ "@agent-relay/sdk": "8.8.5", "@agent-relay/utils": "8.8.5", "@modelcontextprotocol/sdk": "^1.0.0", - "@relaycast/sdk": "^4.1.0", + "@relaycast/sdk": "^4.1.1", "@relayflows/cli": "^1.0.1", "@xterm/headless": "^6.0.0", "commander": "^12.1.0", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 8a73077c2..b543dc1a6 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -59,6 +59,6 @@ "@types/node": "^22.13.10" }, "dependencies": { - "@relaycast/sdk": "^4.1.0" + "@relaycast/sdk": "^4.1.1" } } From d2e764c30ccd32fd7518506d5d04a33c4e51ddc6 Mon Sep 17 00:00:00 2001 From: Will Washburn Date: Thu, 18 Jun 2026 17:13:25 -0400 Subject: [PATCH 6/8] Fix fleet CLI bootstrap test --- .../2026-06/traj_m2ldjnpmev99/summary.md | 31 +++++++++++ .../2026-06/traj_m2ldjnpmev99/trajectory.json | 53 +++++++++++++++++++ packages/cli/src/cli/bootstrap.test.ts | 4 ++ 3 files changed, 88 insertions(+) create mode 100644 .agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/summary.md create mode 100644 .agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/trajectory.json diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/summary.md b/.agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/summary.md new file mode 100644 index 000000000..2ad7a5262 --- /dev/null +++ b/.agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/summary.md @@ -0,0 +1,31 @@ +# Trajectory: Fix PR CI command snapshot + +> **Status:** ✅ Completed +> **Confidence:** 95% +> **Started:** June 18, 2026 at 05:12 PM +> **Completed:** June 18, 2026 at 05:13 PM + +--- + +## Summary + +Fixed PR CI by adding the newly registered fleet workspace CLI commands to the bootstrap expected command list. + +**Approach:** Standard approach + +--- + +## Key Decisions + +### Updated bootstrap expected CLI commands for fleet workspace controls +- **Chose:** Updated bootstrap expected CLI commands for fleet workspace controls +- **Reasoning:** CI failed because createProgram now registers fleet config/enable/disable/inherit, but the bootstrap test expected list still omitted them. + +--- + +## Chapters + +### 1. Work +*Agent: default* + +- Updated bootstrap expected CLI commands for fleet workspace controls: Updated bootstrap expected CLI commands for fleet workspace controls diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/trajectory.json b/.agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/trajectory.json new file mode 100644 index 000000000..ddeb7d41d --- /dev/null +++ b/.agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/trajectory.json @@ -0,0 +1,53 @@ +{ + "id": "traj_m2ldjnpmev99", + "version": 1, + "task": { + "title": "Fix PR CI command snapshot" + }, + "status": "completed", + "startedAt": "2026-06-18T21:12:34.284Z", + "completedAt": "2026-06-18T21:13:09.669Z", + "agents": [ + { + "name": "default", + "role": "lead", + "joinedAt": "2026-06-18T21:13:06.028Z" + } + ], + "chapters": [ + { + "id": "chap_4j4606o10yjt", + "title": "Work", + "agentName": "default", + "startedAt": "2026-06-18T21:13:06.028Z", + "endedAt": "2026-06-18T21:13:09.669Z", + "events": [ + { + "ts": 1781817186029, + "type": "decision", + "content": "Updated bootstrap expected CLI commands for fleet workspace controls: Updated bootstrap expected CLI commands for fleet workspace controls", + "raw": { + "question": "Updated bootstrap expected CLI commands for fleet workspace controls", + "chosen": "Updated bootstrap expected CLI commands for fleet workspace controls", + "alternatives": [], + "reasoning": "CI failed because createProgram now registers fleet config/enable/disable/inherit, but the bootstrap test expected list still omitted them." + }, + "significance": "high" + } + ] + } + ], + "retrospective": { + "summary": "Fixed PR CI by adding the newly registered fleet workspace CLI commands to the bootstrap expected command list.", + "approach": "Standard approach", + "confidence": 0.95 + }, + "commits": [], + "filesChanged": [], + "projectId": "AgentWorkforce/relay", + "tags": [], + "_trace": { + "startRef": "6399193c4713e29ae6a9c280720c7d4d4edb4ee1", + "endRef": "6399193c4713e29ae6a9c280720c7d4d4edb4ee1" + } +} \ No newline at end of file diff --git a/packages/cli/src/cli/bootstrap.test.ts b/packages/cli/src/cli/bootstrap.test.ts index 50712c212..b09bdf43d 100644 --- a/packages/cli/src/cli/bootstrap.test.ts +++ b/packages/cli/src/cli/bootstrap.test.ts @@ -30,6 +30,10 @@ const expectedLeafCommands = [ 'telemetry', 'mcp', // fleet + 'fleet config', + 'fleet disable', + 'fleet enable', + 'fleet inherit', 'fleet serve', 'fleet nodes', 'fleet status', From fc355dfcdf25ef93a142493dc1888b70c0a148bc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 18 Jun 2026 21:14:52 +0000 Subject: [PATCH 7/8] style: auto-format with Prettier --- .../completed/2026-06/traj_m2ldjnpmev99/summary.md | 4 +++- .../completed/2026-06/traj_m2ldjnpmev99/trajectory.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/summary.md b/.agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/summary.md index 2ad7a5262..a825b0b93 100644 --- a/.agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/summary.md +++ b/.agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/summary.md @@ -18,6 +18,7 @@ Fixed PR CI by adding the newly registered fleet workspace CLI commands to the b ## Key Decisions ### Updated bootstrap expected CLI commands for fleet workspace controls + - **Chose:** Updated bootstrap expected CLI commands for fleet workspace controls - **Reasoning:** CI failed because createProgram now registers fleet config/enable/disable/inherit, but the bootstrap test expected list still omitted them. @@ -26,6 +27,7 @@ Fixed PR CI by adding the newly registered fleet workspace CLI commands to the b ## Chapters ### 1. Work -*Agent: default* + +_Agent: default_ - Updated bootstrap expected CLI commands for fleet workspace controls: Updated bootstrap expected CLI commands for fleet workspace controls diff --git a/.agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/trajectory.json b/.agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/trajectory.json index ddeb7d41d..7c4a50b8e 100644 --- a/.agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/trajectory.json +++ b/.agentworkforce/trajectories/completed/2026-06/traj_m2ldjnpmev99/trajectory.json @@ -50,4 +50,4 @@ "startRef": "6399193c4713e29ae6a9c280720c7d4d4edb4ee1", "endRef": "6399193c4713e29ae6a9c280720c7d4d4edb4ee1" } -} \ No newline at end of file +} From 44b6256a7f8459dcaeee61403e3056bf307be3ff Mon Sep 17 00:00:00 2001 From: Will Washburn Date: Thu, 18 Jun 2026 17:15:59 -0400 Subject: [PATCH 8/8] Trigger CI after formatting