agent-relay 11.4.0 → 11.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +73 -0
  2. package/dist/cli/agent-relay-mcp.d.ts.map +1 -1
  3. package/dist/cli/agent-relay-mcp.js +28 -12
  4. package/dist/cli/agent-relay-mcp.js.map +1 -1
  5. package/dist/cli/bootstrap.d.ts.map +1 -1
  6. package/dist/cli/bootstrap.js +5 -2
  7. package/dist/cli/bootstrap.js.map +1 -1
  8. package/dist/cli/commands/cloud.d.ts +4 -0
  9. package/dist/cli/commands/cloud.d.ts.map +1 -1
  10. package/dist/cli/commands/cloud.js +45 -3
  11. package/dist/cli/commands/cloud.js.map +1 -1
  12. package/dist/cli/commands/core.d.ts +5 -0
  13. package/dist/cli/commands/core.d.ts.map +1 -1
  14. package/dist/cli/commands/core.js +6 -1
  15. package/dist/cli/commands/core.js.map +1 -1
  16. package/dist/cli/commands/node.d.ts +3 -1
  17. package/dist/cli/commands/node.d.ts.map +1 -1
  18. package/dist/cli/commands/node.js +75 -12
  19. package/dist/cli/commands/node.js.map +1 -1
  20. package/dist/cli/commands/workspace.d.ts.map +1 -1
  21. package/dist/cli/commands/workspace.js +72 -6
  22. package/dist/cli/commands/workspace.js.map +1 -1
  23. package/dist/cli/index.d.ts.map +1 -1
  24. package/dist/cli/index.js +6 -2
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/cli/lib/broker-lifecycle.d.ts +7 -0
  27. package/dist/cli/lib/broker-lifecycle.d.ts.map +1 -1
  28. package/dist/cli/lib/broker-lifecycle.js +203 -39
  29. package/dist/cli/lib/broker-lifecycle.js.map +1 -1
  30. package/dist/cli/lib/enrollment-pin.d.ts +50 -0
  31. package/dist/cli/lib/enrollment-pin.d.ts.map +1 -0
  32. package/dist/cli/lib/enrollment-pin.js +38 -0
  33. package/dist/cli/lib/enrollment-pin.js.map +1 -0
  34. package/dist/cli/lib/exit.d.ts.map +1 -1
  35. package/dist/cli/lib/exit.js +3 -2
  36. package/dist/cli/lib/exit.js.map +1 -1
  37. package/dist/cli/lib/flush-stdio.d.ts +52 -0
  38. package/dist/cli/lib/flush-stdio.d.ts.map +1 -0
  39. package/dist/cli/lib/flush-stdio.js +68 -0
  40. package/dist/cli/lib/flush-stdio.js.map +1 -0
  41. package/dist/cli/lib/project-workspace-key.d.ts +1 -1
  42. package/dist/cli/lib/project-workspace-key.d.ts.map +1 -1
  43. package/dist/cli/lib/project-workspace-key.js +1 -1
  44. package/dist/cli/lib/project-workspace-key.js.map +1 -1
  45. package/dist/cli/lib/workspace-session.d.ts +29 -1
  46. package/dist/cli/lib/workspace-session.d.ts.map +1 -1
  47. package/dist/cli/lib/workspace-session.js +57 -2
  48. package/dist/cli/lib/workspace-session.js.map +1 -1
  49. package/dist/cli/mcp/action-tools.d.ts.map +1 -1
  50. package/dist/cli/mcp/action-tools.js +2 -1
  51. package/dist/cli/mcp/action-tools.js.map +1 -1
  52. package/dist/cli/mcp/messaging-tools.d.ts.map +1 -1
  53. package/dist/cli/mcp/messaging-tools.js +40 -20
  54. package/dist/cli/mcp/messaging-tools.js.map +1 -1
  55. package/dist/cli/telemetry/client.d.ts.map +1 -1
  56. package/dist/cli/telemetry/client.js +7 -5
  57. package/dist/cli/telemetry/client.js.map +1 -1
  58. package/dist/cli/telemetry/events.d.ts +2 -0
  59. package/dist/cli/telemetry/events.d.ts.map +1 -1
  60. package/dist/index.cjs +1 -1
  61. package/package.json +9 -9
@@ -3,7 +3,17 @@ import { resolveActiveWorkspace } from '@agent-relay/cloud';
3
3
  import { maskSecret } from '../lib/redact.js';
4
4
  import { printJson, runSdk, withSdkDefaults } from '../lib/sdk-command.js';
5
5
  import { readWorkspaceStore, setWorkspaceKey } from '../lib/workspace-store.js';
6
- import { persistWorkspaceSession, validateWorkspaceSessionName } from '../lib/workspace-session.js';
6
+ import { describeClearedEnrollment, persistWorkspaceSession, pinProjectWorkspaceSession, validateWorkspaceSessionName, } from '../lib/workspace-session.js';
7
+ /**
8
+ * Say so when moving workspaces drops this project's enrolled fleet node.
9
+ * Leaving it silent is what produced the split rosters in #1432.
10
+ */
11
+ function reportClearedEnrollment(result, deps) {
12
+ const message = describeClearedEnrollment(result);
13
+ if (message) {
14
+ deps.log(message);
15
+ }
16
+ }
7
17
  function parsePositiveInteger(value) {
8
18
  const parsed = Number.parseInt(value, 10);
9
19
  if (!Number.isInteger(parsed) || parsed <= 0) {
@@ -56,14 +66,27 @@ export function registerWorkspaceCommands(program, overrides = {}) {
56
66
  await runSdk(deps, async () => {
57
67
  const workspaceName = validateWorkspaceSessionName(name);
58
68
  const relay = await deps.createWorkspace(workspaceName, o.baseUrl);
59
- if (relay.workspaceKey) {
60
- persistWorkspaceSession({ name: workspaceName, workspaceKey: relay.workspaceKey });
69
+ const previousActive = readWorkspaceStore().active;
70
+ const persisted = relay.workspaceKey
71
+ ? persistWorkspaceSession({ name: workspaceName, workspaceKey: relay.workspaceKey })
72
+ : {};
73
+ if (relay.workspaceKey && previousActive && previousActive !== workspaceName) {
74
+ deps.error(`⚠ Active workspace changed: ${previousActive} → ${workspaceName}`);
75
+ deps.error(' Restore with: agent-relay workspace restore');
61
76
  }
62
77
  // The key is persisted to the workspace store either way; the output
63
- // masks it unless the caller explicitly asks for the raw value.
78
+ // masks it unless the caller explicitly asks for the raw value. A
79
+ // dropped enrollment rides in the JSON rather than a log line so the
80
+ // output stays parseable.
64
81
  printJson(deps, {
65
82
  name: workspaceName,
66
83
  workspaceKey: relay.workspaceKey && !o.revealSecrets ? maskSecret(relay.workspaceKey) : relay.workspaceKey,
84
+ ...(persisted.clearedEnrolledNodeId
85
+ ? {
86
+ clearedEnrolledNodeId: persisted.clearedEnrolledNodeId,
87
+ warning: describeClearedEnrollment(persisted),
88
+ }
89
+ : {}),
67
90
  });
68
91
  });
69
92
  });
@@ -75,6 +98,7 @@ export function registerWorkspaceCommands(program, overrides = {}) {
75
98
  const store = readWorkspaceStore();
76
99
  printJson(deps, {
77
100
  active: store.active,
101
+ previous: store.previous,
78
102
  workspaces: Object.keys(store.workspaces),
79
103
  });
80
104
  });
@@ -116,8 +140,9 @@ export function registerWorkspaceCommands(program, overrides = {}) {
116
140
  .argument('<key>', 'Workspace key')
117
141
  .action(async (name, key) => {
118
142
  await runSdk(deps, async () => {
119
- persistWorkspaceSession({ name, workspaceKey: key });
143
+ const result = persistWorkspaceSession({ name, workspaceKey: key });
120
144
  deps.log(`Joined and switched to workspace "${name}".`);
145
+ reportClearedEnrollment(result, deps);
121
146
  });
122
147
  });
123
148
  group
@@ -131,8 +156,49 @@ export function registerWorkspaceCommands(program, overrides = {}) {
131
156
  if (!workspace) {
132
157
  throw new Error(`Unknown workspace "${name}". Add it with \`relay workspace set_key ${name} <key>\`.`);
133
158
  }
134
- persistWorkspaceSession({ name, workspaceKey: workspace.key });
159
+ const result = persistWorkspaceSession({ name, workspaceKey: workspace.key });
135
160
  deps.log(`Switched to workspace "${name}".`);
161
+ reportClearedEnrollment(result, deps);
162
+ });
163
+ });
164
+ group
165
+ .command('restore')
166
+ .description('Switch back to the previously active workspace')
167
+ .action(async () => {
168
+ await runSdk(deps, async () => {
169
+ const store = readWorkspaceStore();
170
+ const previous = store.previous;
171
+ if (!previous) {
172
+ throw new Error('No previous workspace is recorded.');
173
+ }
174
+ if (previous === store.active) {
175
+ throw new Error(`The recorded previous workspace "${previous}" is already active.`);
176
+ }
177
+ const workspace = Object.hasOwn(store.workspaces, previous) ? store.workspaces[previous] : undefined;
178
+ if (!workspace) {
179
+ throw new Error(`The recorded previous workspace "${previous}" no longer exists.`);
180
+ }
181
+ const current = store.active;
182
+ persistWorkspaceSession({ name: previous, workspaceKey: workspace.key });
183
+ deps.log(`Switched to workspace "${previous}" (was ${current ?? 'none'}).`);
184
+ });
185
+ });
186
+ group
187
+ .command('rebind')
188
+ .description("Pin this project's broker to a stored workspace")
189
+ .argument('<name>', 'Stored workspace name')
190
+ .action(async (name) => {
191
+ await runSdk(deps, async () => {
192
+ const workspaceName = validateWorkspaceSessionName(name);
193
+ const store = readWorkspaceStore();
194
+ const workspace = Object.hasOwn(store.workspaces, workspaceName)
195
+ ? store.workspaces[workspaceName]
196
+ : undefined;
197
+ if (!workspace) {
198
+ throw new Error(`Unknown workspace "${workspaceName}". Add it with \`relay workspace set_key ${workspaceName} <key>\`.`);
199
+ }
200
+ pinProjectWorkspaceSession({ workspaceKey: workspace.key });
201
+ deps.log(`Rebound this project's broker to workspace "${workspaceName}". Restart the broker to apply it.`);
136
202
  });
137
203
  });
138
204
  }
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../src/cli/commands/workspace.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../src/cli/commands/workspace.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,EAAuB,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACvB,0BAA0B,EAC1B,4BAA4B,GAE7B,MAAM,6BAA6B,CAAC;AAIrC;;;GAGG;AACH,SAAS,uBAAuB,CAC9B,MAAqC,EACrC,IAAkC;IAElC,MAAM,OAAO,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;IAClD,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAa;IACzC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,oBAAoB,CAAC,8BAA8B,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,OAAgB,EAChB,YAAmD,EAAE;IAErD,MAAM,IAAI,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,WAAW,CAAC,sCAAsC,CAAC,CAAC;IAE/F,KAAK;SACF,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,2CAA2C,CAAC;SACxD,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC;SAC/C,MAAM,CAAC,QAAQ,EAAE,2EAA2E,CAAC;SAC7F,MAAM,CAAC,kBAAkB,EAAE,6CAA6C,CAAC;SACzE,MAAM,CACL,kCAAkC,EAClC,0CAA0C,EAC1C,oBAAoB,CACrB;SACA,MAAM,CACL,KAAK,EAAE,OAKN,EAAE,EAAE;QACH,MAAM,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YAC5B,MAAM,SAAS,GAAG,MAAM,sBAAsB,CAAC;gBAC7C,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,WAAW,EAAE,KAAK;gBAClB,gBAAgB,EAAE,OAAO,CAAC,cAAc;aACzC,CAAC,CAAC;YAEH,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBACjB,SAAS,CACP,IAAI,EACJ,OAAO,CAAC,aAAa;oBACnB,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC;wBACE,GAAG,SAAS;wBACZ,GAAG,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC;wBAC9B,GAAG,CAAC,SAAS,CAAC,eAAe;4BAC3B,CAAC,CAAC,EAAE,eAAe,EAAE,UAAU,CAAC,SAAS,CAAC,eAAe,CAAC,EAAE;4BAC5D,CAAC,CAAC,EAAE,CAAC;qBACR,CACN,CAAC;gBACF,OAAO;YACT,CAAC;YAED,IAAI,CAAC,GAAG,CAAC,cAAc,SAAS,CAAC,IAAI,IAAI,SAAS,CAAC,gBAAgB,EAAE,CAAC,CAAC;YACvE,IAAI,CAAC,GAAG,CAAC,uBAAuB,SAAS,CAAC,gBAAgB,EAAE,CAAC,CAAC;YAC9D,IAAI,CAAC,GAAG,CAAC,2BAA2B,SAAS,CAAC,oBAAoB,EAAE,CAAC,CAAC;YACtE,IAAI,CAAC,GAAG,CAAC,2BAA2B,SAAS,CAAC,oBAAoB,EAAE,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;IACL,CAAC,CACF,CAAC;IAEJ,KAAK;SACF,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,0CAA0C,CAAC;SACvD,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SACpC,MAAM,CAAC,kBAAkB,EAAE,2BAA2B,CAAC;SACvD,MAAM,CAAC,kBAAkB,EAAE,6CAA6C,CAAC;SACzE,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,CAA0B,EAAE,EAAE;QACzD,MAAM,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YAC5B,MAAM,aAAa,GAAG,4BAA4B,CAAC,IAAI,CAAC,CAAC;YACzD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,CAAC,CAAC,OAA6B,CAAC,CAAC;YACzF,MAAM,cAAc,GAAG,kBAAkB,EAAE,CAAC,MAAM,CAAC;YACnD,MAAM,SAAS,GAAG,KAAK,CAAC,YAAY;gBAClC,CAAC,CAAC,uBAAuB,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC;gBACpF,CAAC,CAAC,EAAE,CAAC;YACP,IAAI,KAAK,CAAC,YAAY,IAAI,cAAc,IAAI,cAAc,KAAK,aAAa,EAAE,CAAC;gBAC7E,IAAI,CAAC,KAAK,CAAC,gCAAgC,cAAc,MAAM,aAAa,EAAE,CAAC,CAAC;gBAChF,IAAI,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;YAC/D,CAAC;YACD,qEAAqE;YACrE,kEAAkE;YAClE,qEAAqE;YACrE,0BAA0B;YAC1B,SAAS,CAAC,IAAI,EAAE;gBACd,IAAI,EAAE,aAAa;gBACnB,YAAY,EACV,KAAK,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY;gBAC9F,GAAG,CAAC,SAAS,CAAC,qBAAqB;oBACjC,CAAC,CAAC;wBACE,qBAAqB,EAAE,SAAS,CAAC,qBAAqB;wBACtD,OAAO,EAAE,yBAAyB,CAAC,SAAS,CAAC;qBAC9C;oBACH,CAAC,CAAC,EAAE,CAAC;aACR,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,KAAK;SACF,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,wBAAwB,CAAC;SACrC,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YAC5B,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;YACnC,SAAS,CAAC,IAAI,EAAE;gBACd,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;aAC1C,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,KAAK;SACF,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,oFAAoF,CAAC;SACjG,QAAQ,CAAC,QAAQ,EAAE,mDAAmD,CAAC;SACvE,MAAM,CAAC,kBAAkB,EAAE,mBAAmB,CAAC;SAC/C,MAAM,CAAC,KAAK,EAAE,IAAwB,EAAE,CAA0B,EAAE,EAAE;QACrE,MAAM,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YAC5B,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,IAAI,KAAK,CAAC,MAAM,CAAC;YAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;YACrE,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC9F,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,gCAAgC,MAAM,IAAI,CAAC,CAAC;YAC9D,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,KAAK;SACF,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,oCAAoC,CAAC;SACjD,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SACpC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;SAClC,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,GAAW,EAAE,EAAE;QAC1C,MAAM,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YAC5B,eAAe,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC3B,IAAI,CAAC,GAAG,CAAC,6BAA6B,IAAI,IAAI,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,KAAK;SACF,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,4CAA4C,CAAC;SACzD,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SACpC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;SAClC,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,GAAW,EAAE,EAAE;QAC1C,MAAM,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YAC5B,MAAM,MAAM,GAAG,uBAAuB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC;YACpE,IAAI,CAAC,GAAG,CAAC,qCAAqC,IAAI,IAAI,CAAC,CAAC;YACxD,uBAAuB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,KAAK;SACF,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,6BAA6B,CAAC;SAC1C,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SACpC,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;QAC7B,MAAM,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YAC5B,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;YACnC,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,MAAM,IAAI,KAAK,CACb,sBAAsB,IAAI,4CAA4C,IAAI,WAAW,CACtF,CAAC;YACJ,CAAC;YACD,MAAM,MAAM,GAAG,uBAAuB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,CAAC,GAAG,EAAE,CAAC,CAAC;YAC9E,IAAI,CAAC,GAAG,CAAC,0BAA0B,IAAI,IAAI,CAAC,CAAC;YAC7C,uBAAuB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,KAAK;SACF,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,gDAAgD,CAAC;SAC7D,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YAC5B,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;YACnC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;YAChC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;YACxD,CAAC;YACD,IAAI,QAAQ,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC9B,MAAM,IAAI,KAAK,CAAC,oCAAoC,QAAQ,sBAAsB,CAAC,CAAC;YACtF,CAAC;YACD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACrG,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,MAAM,IAAI,KAAK,CAAC,oCAAoC,QAAQ,qBAAqB,CAAC,CAAC;YACrF,CAAC;YACD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC;YAC7B,uBAAuB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,CAAC,GAAG,EAAE,CAAC,CAAC;YACzE,IAAI,CAAC,GAAG,CAAC,0BAA0B,QAAQ,UAAU,OAAO,IAAI,MAAM,IAAI,CAAC,CAAC;QAC9E,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,KAAK;SACF,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,iDAAiD,CAAC;SAC9D,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;SAC3C,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;QAC7B,MAAM,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;YAC5B,MAAM,aAAa,GAAG,4BAA4B,CAAC,IAAI,CAAC,CAAC;YACzD,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;YACnC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,aAAa,CAAC;gBAC9D,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC;gBACjC,CAAC,CAAC,SAAS,CAAC;YACd,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,MAAM,IAAI,KAAK,CACb,sBAAsB,aAAa,4CAA4C,aAAa,WAAW,CACxG,CAAC;YACJ,CAAC;YACD,0BAA0B,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,GAAG,EAAE,CAAC,CAAC;YAC5D,IAAI,CAAC,GAAG,CACN,+CAA+C,aAAa,oCAAoC,CACjG,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAWA,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAYA,cAAc,gBAAgB,CAAC"}
package/dist/cli/index.js CHANGED
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'node:url';
5
5
  import { runAiSdkSidecarMain } from '@agent-relay/harnesses';
6
6
  import { runCli } from './bootstrap.js';
7
7
  import { describeError } from './lib/describe-error.js';
8
+ import { exitAfterFlush } from './lib/flush-stdio.js';
8
9
  export * from './bootstrap.js';
9
10
  function isEntrypoint() {
10
11
  const invocationPath = process.argv[1];
@@ -19,13 +20,16 @@ function isEntrypoint() {
19
20
  }
20
21
  if (isEntrypoint()) {
21
22
  const main = process.argv[2] === '__ai-sdk-sidecar' ? runAiSdkSidecarMain(process.argv.slice(3)) : runCli();
22
- main.catch((err) => {
23
+ main.catch(async (err) => {
23
24
  // Commander will have already printed a helpful message for parse errors.
24
25
  // For other top-level failures, surface them to stderr and exit non-zero.
25
26
  // `describeError` keeps a non-Error rejection (e.g. `{ status: 401 }` from
26
27
  // a broker client) readable instead of collapsing it to `[object Object]`.
27
28
  process.stderr.write(`${describeError(err)}\n`);
28
- process.exit(1);
29
+ // Flush first: on macOS a piped stderr is asynchronous, so exiting in this
30
+ // tick would discard the message we just wrote and leave the caller with a
31
+ // silent non-zero exit.
32
+ await exitAfterFlush(1);
29
33
  });
30
34
  }
31
35
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,cAAc,gBAAgB,CAAC;AAE/B,SAAS,YAAY;IACnB,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvC,IAAI,CAAC,cAAc;QAAE,OAAO,KAAK,CAAC;IAClC,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED,IAAI,YAAY,EAAE,EAAE,CAAC;IACnB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,kBAAkB,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC5G,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACjB,0EAA0E;QAC1E,0EAA0E;QAC1E,2EAA2E;QAC3E,2EAA2E;QAC3E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,cAAc,gBAAgB,CAAC;AAE/B,SAAS,YAAY;IACnB,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvC,IAAI,CAAC,cAAc;QAAE,OAAO,KAAK,CAAC;IAClC,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED,IAAI,YAAY,EAAE,EAAE,CAAC;IACnB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,kBAAkB,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC5G,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACvB,0EAA0E;QAC1E,0EAA0E;QAC1E,2EAA2E;QAC3E,2EAA2E;QAC3E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChD,2EAA2E;QAC3E,2EAA2E;QAC3E,wBAAwB;QACxB,MAAM,cAAc,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -1,9 +1,12 @@
1
1
  import { type FleetNodeDefinition } from '@agent-relay/fleet';
2
2
  import type { CoreDependencies, CoreProjectPaths, CoreRelay } from '../commands/core.js';
3
3
  import { type NodeDefinitionDescriptor } from './node-provider-child.js';
4
+ import { type WorkspaceSelection } from './project-workspace-key.js';
4
5
  type UpOptions = {
5
6
  spawn?: boolean;
6
7
  background?: boolean;
8
+ /** Internal marker set only on the detached child re-exec. */
9
+ backgroundChild?: boolean;
7
10
  verbose?: boolean;
8
11
  workspaceKey?: string;
9
12
  stateDir?: string;
@@ -31,11 +34,15 @@ type DownOptions = {
31
34
  timeout?: string;
32
35
  stateDir?: string;
33
36
  };
37
+ export declare const WORKSPACE_BINDING_SOURCE_ENV = "AGENT_RELAY_WORKSPACE_SOURCE";
38
+ export type WorkspaceBindingSource = WorkspaceSelection['source'] | 'created' | 'multi-workspace';
34
39
  export interface BrokerConnection {
35
40
  url: string;
36
41
  port: number;
37
42
  api_key: string;
38
43
  pid: number;
44
+ /** Non-secret provenance recorded by the CLI after the broker handshake. */
45
+ workspace_source?: WorkspaceBindingSource;
39
46
  }
40
47
  type NodeDeliveryStatus = {
41
48
  tokenPresent: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"broker-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/broker-lifecycle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"broker-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/broker-lifecycle.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,10 @@
1
+ import { randomUUID } from 'node:crypto';
1
2
  import fs from 'node:fs';
2
3
  import path from 'node:path';
3
4
  import { HarnessDriverClient } from '@agent-relay/harness-driver';
4
5
  import { startServeNode } from '@agent-relay/fleet';
5
6
  import { createLogger } from '@agent-relay/utils';
7
+ import { redactCredentialValues } from '@agent-relay/cloud/redact';
6
8
  import { track } from '../telemetry/index.js';
7
9
  import { buildBundledAgentRelayMcpCommand } from './agent-relay-mcp-command.js';
8
10
  import { errorClassName } from './telemetry-helpers.js';
@@ -12,13 +14,14 @@ import { describeNodeDefinitionViaNode, descriptorCapacitySource, startNodeJsNod
12
14
  import { describeError } from './describe-error.js';
13
15
  import { maskSecret } from './redact.js';
14
16
  import { startReflexCapture } from './reflex-capture.js';
15
- import { projectWorkspaceKeyPath, writeProjectWorkspaceKey } from './project-workspace-key.js';
16
- import { promoteWorkspaceKeyEnvAlias } from './workspace-env.js';
17
+ import { readProjectWorkspaceSession, resolveWorkspaceSelection, writeProjectWorkspaceKey, } from './project-workspace-key.js';
17
18
  const MAX_API_PORT_ATTEMPTS = 25;
18
19
  const MAX_PORT = 65535;
19
20
  const DEFAULT_BROKER_BASE_PORT = 3888;
20
21
  /** The broker writes this file with URL, port, API key, and PID. */
21
22
  const CONNECTION_FILENAME = 'connection.json';
23
+ const BACKGROUND_START_ERROR_FILENAME = 'background-start-error.log';
24
+ export const WORKSPACE_BINDING_SOURCE_ENV = 'AGENT_RELAY_WORKSPACE_SOURCE';
22
25
  const STATUS_POLL_INTERVAL_MS = 500;
23
26
  const DETACHED_START_READY_TIMEOUT_MS = 10_000;
24
27
  const NODE_DELIVERY_READY_TIMEOUT_MS = 10_000;
@@ -188,7 +191,7 @@ export function describeErrorWithCause(err) {
188
191
  parts.push(detail);
189
192
  if (codes.length > 0)
190
193
  parts.push(`[${codes.join(', ')}]`);
191
- return parts.join(' — ');
194
+ return redactCredentialValues(parts.join(' — '));
192
195
  }
193
196
  /**
194
197
  * Pick the best `error_class` for telemetry. Prefer a network-style code from
@@ -534,6 +537,77 @@ function safeUnlink(filePath, deps) {
534
537
  // Best-effort cleanup.
535
538
  }
536
539
  }
540
+ function workspaceBindingSource(value) {
541
+ return value === 'flag' ||
542
+ value === 'env' ||
543
+ value === 'project' ||
544
+ value === 'store' ||
545
+ value === 'created' ||
546
+ value === 'multi-workspace'
547
+ ? value
548
+ : undefined;
549
+ }
550
+ function workspaceBindingSourceLabel(source) {
551
+ switch (source) {
552
+ case 'flag':
553
+ return 'command-line flag (--workspace-key / --wk)';
554
+ case 'env':
555
+ return 'environment (RELAY_WORKSPACE_KEY > AGENT_RELAY_WORKSPACE_KEY > RELAY_API_KEY)';
556
+ case 'project':
557
+ return 'repository pin (.agentworkforce/relay/workspace-key.json)';
558
+ case 'store':
559
+ return 'machine-global active workspace (~/.agentworkforce/relay/workspaces.json)';
560
+ case 'created':
561
+ return 'created (no configured workspace resolved)';
562
+ case 'multi-workspace':
563
+ return 'multi-workspace session ($RELAY_WORKSPACES_JSON)';
564
+ }
565
+ }
566
+ /** True when `RELAY_WORKSPACES_JSON` carries at least one membership. The broker's
567
+ * `startup_session_set_with_options` checks this env var before any single
568
+ * workspace key (flag, env, repository pin, or machine-global store), so the
569
+ * CLI's precedence ladder must defer to it for provenance too — otherwise
570
+ * `node up` / `node status` can report a source the broker never used. */
571
+ function usesMultiWorkspaceEnv(env) {
572
+ return Boolean(env.RELAY_WORKSPACES_JSON?.trim());
573
+ }
574
+ function writeBrokerBindingSource(dataDir, source, deps) {
575
+ const connectionPath = path.join(dataDir, CONNECTION_FILENAME);
576
+ const connection = readBrokerConnectionFromFs(deps.fs, dataDir);
577
+ if (!connection)
578
+ return;
579
+ // Every CLI invocation resolves the broker through this file, so a
580
+ // concurrent writer must never observe a partial or clobbered write.
581
+ // Write to a private tmp file and rename it into place, which is atomic
582
+ // on the same filesystem.
583
+ const tmpPath = `${connectionPath}.tmp-${process.pid}-${randomUUID()}`;
584
+ deps.fs.writeFileSync(tmpPath, `${JSON.stringify({ ...connection, workspace_source: source }, null, 2)}\n`, 'utf-8');
585
+ deps.fs.renameSync(tmpPath, connectionPath);
586
+ }
587
+ function backgroundStartErrorPath(dataDir) {
588
+ return path.join(dataDir, BACKGROUND_START_ERROR_FILENAME);
589
+ }
590
+ function readBackgroundStartError(dataDir, deps) {
591
+ try {
592
+ return deps.fs.readFileSync(backgroundStartErrorPath(dataDir), 'utf-8').trim() || undefined;
593
+ }
594
+ catch {
595
+ return undefined;
596
+ }
597
+ }
598
+ function recordBackgroundStartError(message, dataDir, isDetachedChild, deps) {
599
+ if (!isDetachedChild)
600
+ return;
601
+ try {
602
+ // Never trust a project-loaded environment variable as a filesystem path.
603
+ // Detached startup owns one fixed diagnostic file inside its resolved
604
+ // broker state directory; foreground failures do not write it at all.
605
+ deps.fs.writeFileSync(backgroundStartErrorPath(dataDir), `${message}\n`, 'utf-8');
606
+ }
607
+ catch {
608
+ // Diagnostics must never replace the original startup error.
609
+ }
610
+ }
537
611
  function readBrokerPid(dataDir, _deps) {
538
612
  const conn = readBrokerConnectionFromFs(_deps.fs, dataDir);
539
613
  return conn?.pid ?? null;
@@ -739,6 +813,7 @@ function cleanupBrokerFiles(paths, deps) {
739
813
  safeUnlink(path.join(paths.dataDir, CONNECTION_FILENAME), deps);
740
814
  safeUnlink(relaySockPath, deps);
741
815
  safeUnlink(runtimePath, deps);
816
+ safeUnlink(backgroundStartErrorPath(paths.dataDir), deps);
742
817
  // Clean up lock files and legacy pid files
743
818
  try {
744
819
  for (const file of deps.fs.readdirSync(paths.dataDir)) {
@@ -777,6 +852,9 @@ function childUpArgsForDetachedStart(options, deps) {
777
852
  if (options.verbose === true && !args.includes('--verbose')) {
778
853
  args.push('--verbose');
779
854
  }
855
+ if (!args.includes('--background-child')) {
856
+ args.push('--background-child');
857
+ }
780
858
  return args;
781
859
  }
782
860
  /** Drop each named option and, for the space-separated form, its value token. */
@@ -859,11 +937,14 @@ async function checkBrokerReadiness(paths, deps, requireApi) {
859
937
  }
860
938
  return { state: 'starting', conn };
861
939
  }
862
- async function waitForBrokerReadiness(paths, deps, waitMs, requireApi, verbose) {
940
+ async function waitForBrokerReadiness(paths, deps, waitMs, requireApi, verbose, stopWhenPidExits) {
863
941
  const deadline = deps.now() + waitMs;
864
942
  let latest = await checkBrokerReadiness(paths, deps, requireApi);
865
943
  vlog(deps, verbose, `Broker readiness: ${latest.state}`);
866
944
  while (latest.state !== 'running' && waitMs > 0 && deps.now() < deadline) {
945
+ if (stopWhenPidExits && !isProcessRunning(stopWhenPidExits, deps)) {
946
+ return latest;
947
+ }
867
948
  await deps.sleep(Math.min(STATUS_POLL_INTERVAL_MS, Math.max(0, deadline - deps.now())));
868
949
  const previousState = latest.state;
869
950
  latest = await checkBrokerReadiness(paths, deps, requireApi);
@@ -988,39 +1069,58 @@ function planCapacitySource(plan) {
988
1069
  }
989
1070
  return plan.mode === 'in-process' ? plan.definition : descriptorCapacitySource(plan.descriptor);
990
1071
  }
991
- /** Read the minimal project session needed during broker startup. */
992
- function readPinnedProjectWorkspaceSession(dataDir, deps) {
993
- try {
994
- const parsed = JSON.parse(deps.fs.readFileSync(projectWorkspaceKeyPath(dataDir), 'utf8'));
995
- const workspaceKey = typeof parsed.workspaceKey === 'string' ? parsed.workspaceKey.trim() || undefined : undefined;
996
- if (!workspaceKey) {
997
- return undefined;
998
- }
999
- const enrolledNodeId = typeof parsed.enrolledNodeId === 'string' ? parsed.enrolledNodeId.trim() || undefined : undefined;
1000
- return {
1001
- workspaceKey,
1002
- ...(enrolledNodeId ? { enrolledNodeId } : {}),
1003
- };
1004
- }
1005
- catch {
1072
+ /**
1073
+ * Apply the resolved workspace to the environment the broker (and any detached
1074
+ * child) inherits, and report which source won. Returns the pinned project
1075
+ * session when the repository pin supplied the selection.
1076
+ */
1077
+ function applyWorkspaceSelection(selection, deps, projectDataDir) {
1078
+ if (!selection) {
1079
+ deps.log('Workspace: none selected (no --workspace-key, no RELAY_WORKSPACE_KEY, no repository pin, ' +
1080
+ 'no active workspace in the machine-global store). A new workspace will be created.');
1006
1081
  return undefined;
1007
1082
  }
1008
- }
1009
- /** Resume the pinned project session unless explicit credentials override it. */
1010
- function resumePinnedProjectWorkspace(options, deps, projectDataDir) {
1011
- const explicitEnvWorkspaceKey = promoteWorkspaceKeyEnvAlias(deps.env);
1012
- if (options.workspaceKey?.trim() || explicitEnvWorkspaceKey || deps.env.RELAY_NODE_TOKEN?.trim()) {
1083
+ deps.log(`Workspace source: ${describeWorkspaceSource(selection.source)} (${selection.origin})`);
1084
+ // Normalize every winning source to the primary env var inherited by the
1085
+ // broker and any detached child. Keep a caller-supplied RELAY_API_KEY intact
1086
+ // when an explicit flag or environment variable won.
1087
+ deps.env.RELAY_WORKSPACE_KEY = selection.key;
1088
+ if (selection.source === 'project' || selection.source === 'store') {
1089
+ deps.env.RELAY_API_KEY = selection.key;
1090
+ }
1091
+ if (selection.source !== 'project') {
1013
1092
  return undefined;
1014
1093
  }
1015
- const session = readPinnedProjectWorkspaceSession(projectDataDir, deps);
1016
- if (session) {
1017
- deps.env.RELAY_WORKSPACE_KEY = session.workspaceKey;
1018
- deps.env.RELAY_API_KEY = session.workspaceKey;
1019
- if (session.enrolledNodeId) {
1020
- deps.env.AGENT_RELAY_ENROLLED_NODE_ID = session.enrolledNodeId;
1021
- }
1094
+ const pinned = readProjectWorkspaceSession(projectDataDir, deps.fs);
1095
+ if (pinned?.enrolledNodeId) {
1096
+ deps.env.AGENT_RELAY_ENROLLED_NODE_ID = pinned.enrolledNodeId;
1097
+ }
1098
+ return pinned;
1099
+ }
1100
+ /** Human-readable name for a precedence-ladder step, for the startup line. */
1101
+ function describeWorkspaceSource(source) {
1102
+ switch (source) {
1103
+ case 'flag':
1104
+ return 'command-line flag';
1105
+ case 'env':
1106
+ return 'environment';
1107
+ case 'project':
1108
+ return 'repository pin';
1109
+ case 'store':
1110
+ return 'machine-global active workspace';
1022
1111
  }
1023
- return session;
1112
+ }
1113
+ /**
1114
+ * Preserve the original source across `--background` re-exec. The detached
1115
+ * child sees the normalized RELAY_WORKSPACE_KEY as an env selection, so this
1116
+ * marker carries only provenance; it never participates in resolution.
1117
+ */
1118
+ function recordWorkspaceBindingSource(selection, deps, overrideSource) {
1119
+ const inheritedSource = workspaceBindingSource(deps.env[WORKSPACE_BINDING_SOURCE_ENV]);
1120
+ const source = overrideSource ??
1121
+ (selection?.source === 'env' && inheritedSource ? inheritedSource : (selection?.source ?? 'created'));
1122
+ deps.env[WORKSPACE_BINDING_SOURCE_ENV] = source;
1123
+ return source;
1024
1124
  }
1025
1125
  export async function runUpCommand(options, deps) {
1026
1126
  ensureBundledAgentRelayMcpCommand(deps);
@@ -1031,7 +1131,25 @@ export async function runUpCommand(options, deps) {
1031
1131
  // --state-dir), so the key must be persisted here even when broker state is
1032
1132
  // redirected elsewhere.
1033
1133
  const projectWorkspaceKeyDataDir = paths.dataDir;
1034
- const resumedProjectSession = resumePinnedProjectWorkspace(options, deps, projectWorkspaceKeyDataDir);
1134
+ // The broker's startup_session_set_with_options() checks RELAY_WORKSPACES_JSON
1135
+ // before any single workspace key, so a flag/env/pin/store resolution here
1136
+ // would report provenance the broker never actually used.
1137
+ const joinsMultiWorkspaceSession = usesMultiWorkspaceEnv(deps.env);
1138
+ const workspaceSelection = joinsMultiWorkspaceSession
1139
+ ? undefined
1140
+ : resolveWorkspaceSelection({
1141
+ workspaceKey: options.workspaceKey,
1142
+ env: deps.env,
1143
+ projectDataDir: projectWorkspaceKeyDataDir,
1144
+ fileSystem: deps.fs,
1145
+ });
1146
+ const workspaceBindingSource = recordWorkspaceBindingSource(workspaceSelection, deps, joinsMultiWorkspaceSession ? 'multi-workspace' : undefined);
1147
+ const resumedProjectSession = joinsMultiWorkspaceSession
1148
+ ? undefined
1149
+ : applyWorkspaceSelection(workspaceSelection, deps, projectWorkspaceKeyDataDir);
1150
+ if (joinsMultiWorkspaceSession) {
1151
+ deps.log(`Workspace source: ${workspaceBindingSourceLabel('multi-workspace')}`);
1152
+ }
1035
1153
  // --state-dir overrides where the broker writes state / connection files
1036
1154
  if (options.stateDir) {
1037
1155
  const resolved = path.resolve(options.stateDir);
@@ -1062,6 +1180,8 @@ export async function runUpCommand(options, deps) {
1062
1180
  deps.exit(1);
1063
1181
  return;
1064
1182
  }
1183
+ const startErrorPath = backgroundStartErrorPath(paths.dataDir);
1184
+ safeUnlink(startErrorPath, deps);
1065
1185
  const args = childUpArgsForDetachedStart(options, deps);
1066
1186
  const invocation = detachedCliInvocation(deps, args);
1067
1187
  let child;
@@ -1079,18 +1199,31 @@ export async function runUpCommand(options, deps) {
1079
1199
  }
1080
1200
  child.unref?.();
1081
1201
  vlog(deps, options.verbose, `Spawned detached broker child (pid: ${child.pid ?? 'unknown'}), waiting for readiness...`);
1082
- const readiness = await waitForBrokerReadiness(paths, deps, DETACHED_START_READY_TIMEOUT_MS, true, options.verbose);
1202
+ const readiness = await waitForBrokerReadiness(paths, deps, DETACHED_START_READY_TIMEOUT_MS, true, options.verbose, child.pid);
1083
1203
  if (readiness.state !== 'running') {
1084
1204
  const pid = readiness.state === 'starting' ? readiness.conn.pid : child.pid;
1085
- deps.error(pid
1086
- ? `Broker background start did not become ready within ${DETACHED_START_READY_TIMEOUT_MS / 1000}s (pid: ${pid}).`
1087
- : `Broker background start did not become ready within ${DETACHED_START_READY_TIMEOUT_MS / 1000}s.`);
1205
+ const childExited = typeof child.pid === 'number' && child.pid > 0 && !isProcessRunning(child.pid, deps);
1206
+ if (childExited) {
1207
+ deps.error(`Broker background child exited before becoming ready (pid: ${child.pid}).`);
1208
+ }
1209
+ else {
1210
+ deps.error(pid
1211
+ ? `Broker background start did not become ready within ${DETACHED_START_READY_TIMEOUT_MS / 1000}s (pid: ${pid}).`
1212
+ : `Broker background start did not become ready within ${DETACHED_START_READY_TIMEOUT_MS / 1000}s.`);
1213
+ }
1088
1214
  if (readiness.state === 'starting') {
1089
1215
  deps.error('Broker process is running, but the API did not become ready.');
1090
1216
  }
1217
+ const detachedError = readBackgroundStartError(paths.dataDir, deps);
1218
+ if (detachedError) {
1219
+ deps.error(`Detached broker error: ${detachedError}`);
1220
+ }
1221
+ else if (childExited) {
1222
+ deps.error('Retry without --background to see the broker startup error.');
1223
+ }
1091
1224
  deps.error('Run `agent-relay status --wait-for=10` for details, or `agent-relay down --force` to clean up.');
1092
1225
  const cleanupPids = new Set();
1093
- if (typeof child.pid === 'number' && child.pid > 0) {
1226
+ if (typeof child.pid === 'number' && child.pid > 0 && isProcessRunning(child.pid, deps)) {
1094
1227
  cleanupPids.add(child.pid);
1095
1228
  }
1096
1229
  if (readiness.state === 'starting') {
@@ -1150,6 +1283,7 @@ export async function runUpCommand(options, deps) {
1150
1283
  deps.log('Broker started.');
1151
1284
  deps.log(`Broker PID: ${readiness.conn.pid}`);
1152
1285
  deps.log('Stop with: agent-relay down');
1286
+ safeUnlink(startErrorPath, deps);
1153
1287
  deps.exit(0);
1154
1288
  return;
1155
1289
  }
@@ -1207,10 +1341,30 @@ export async function runUpCommand(options, deps) {
1207
1341
  await killOrphanedBrokerProcesses(paths.projectRoot, deps);
1208
1342
  const started = await startBrokerWithPortFallback(paths, basePort, deps, options.brokerName, options.verbose);
1209
1343
  relay = started.relay;
1344
+ try {
1345
+ writeBrokerBindingSource(paths.dataDir, workspaceBindingSource, deps);
1346
+ }
1347
+ catch {
1348
+ // Provenance is diagnostic metadata; a broker that came up stays up.
1349
+ }
1350
+ safeUnlink(backgroundStartErrorPath(paths.dataDir), deps);
1210
1351
  deps.log(`Relay API: http://localhost:${started.apiPort}`);
1211
1352
  deps.log(`Project: ${paths.projectRoot}`);
1212
1353
  deps.log('Mode: broker (stdio)');
1213
1354
  deps.log(`Workspace Key: ${relay.workspaceKey ? maskSecret(relay.workspaceKey) : 'unknown'}`);
1355
+ // Minting must be observable: without this line "created a workspace" and
1356
+ // "joined the pinned workspace" print identically.
1357
+ const joinedWorkspaceId = relay.workspaceId ?? 'unknown';
1358
+ // The multi-workspace session always joins a configured membership; it
1359
+ // never mints a new workspace the way an unresolved single key does.
1360
+ if (workspaceSelection || joinsMultiWorkspaceSession) {
1361
+ deps.log(`Workspace: joined ${joinedWorkspaceId}`);
1362
+ }
1363
+ else {
1364
+ deps.log(`Workspace: created new workspace ${joinedWorkspaceId}`);
1365
+ deps.log('Pin a workspace for this repository with `agent-relay up --workspace-key <key>`, ' +
1366
+ 'or select one machine-wide with `agent-relay workspace switch <name>`.');
1367
+ }
1214
1368
  deps.log('Broker started.');
1215
1369
  // Record the workspace this broker joined (explicitly passed or auto-minted)
1216
1370
  // in the DEFAULT project data dir (not any --state-dir override), so later
@@ -1220,6 +1374,10 @@ export async function runUpCommand(options, deps) {
1220
1374
  try {
1221
1375
  writeProjectWorkspaceKey(projectWorkspaceKeyDataDir, relay.workspaceKey ?? undefined, {
1222
1376
  enrolledNodeId: deps.env.AGENT_RELAY_ENROLLED_NODE_ID ?? resumedProjectSession?.enrolledNodeId,
1377
+ // Recording the resolved workspace id lets the NEXT start detect a
1378
+ // conflicting source (a stored enrollment in another workspace) before
1379
+ // the broker comes up, instead of after agents land in the wrong place.
1380
+ workspaceId: relay.workspaceId ?? resumedProjectSession?.workspaceId,
1223
1381
  });
1224
1382
  }
1225
1383
  catch {
@@ -1297,11 +1455,13 @@ export async function runUpCommand(options, deps) {
1297
1455
  stage,
1298
1456
  error_class: classifyBrokerStartError(err),
1299
1457
  });
1458
+ const detailedMessage = describeErrorWithCause(err);
1459
+ recordBackgroundStartError(detailedMessage, paths.dataDir, options.backgroundChild === true, deps);
1300
1460
  if (isBrokerAlreadyRunningError(message)) {
1301
1461
  reportAlreadyRunningError(message, paths.dataDir, deps);
1302
1462
  }
1303
1463
  else {
1304
- deps.error(`Failed to start broker: ${describeErrorWithCause(err)}`);
1464
+ deps.error(`Failed to start broker: ${detailedMessage}`);
1305
1465
  }
1306
1466
  deps.exit(1);
1307
1467
  }
@@ -1447,6 +1607,10 @@ export async function runStatusCommand(deps, options) {
1447
1607
  deps.log('Mode: broker (stdio)');
1448
1608
  deps.log(`PID: ${readiness.conn.pid}`);
1449
1609
  deps.log(`Project: ${paths.projectRoot}`);
1610
+ const source = workspaceBindingSource(readiness.conn.workspace_source);
1611
+ deps.log(source
1612
+ ? `Workspace source: ${workspaceBindingSourceLabel(source)}`
1613
+ : 'Workspace source: unknown (startup provenance was not recorded)');
1450
1614
  // Query the running broker for additional status info
1451
1615
  const statusDetails = readiness.statusDetails ?? (waitMs > 0 ? null : await readBrokerStatusDetails(readiness.conn));
1452
1616
  if (statusDetails) {