agent-relay 3.0.1 → 3.1.0

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 (142) hide show
  1. package/README.md +37 -244
  2. package/bin/agent-relay-broker-darwin-arm64 +0 -0
  3. package/bin/agent-relay-broker-darwin-x64 +0 -0
  4. package/bin/agent-relay-broker-linux-arm64 +0 -0
  5. package/bin/agent-relay-broker-linux-x64 +0 -0
  6. package/dist/index.cjs +342 -60
  7. package/dist/src/cli/commands/core.d.ts +2 -0
  8. package/dist/src/cli/commands/core.d.ts.map +1 -1
  9. package/dist/src/cli/commands/core.js +9 -2
  10. package/dist/src/cli/commands/core.js.map +1 -1
  11. package/dist/src/cli/lib/broker-lifecycle.d.ts.map +1 -1
  12. package/dist/src/cli/lib/broker-lifecycle.js +87 -28
  13. package/dist/src/cli/lib/broker-lifecycle.js.map +1 -1
  14. package/package.json +9 -8
  15. package/packages/acp-bridge/README.md +50 -67
  16. package/packages/acp-bridge/package.json +2 -2
  17. package/packages/config/package.json +1 -1
  18. package/packages/hooks/package.json +4 -4
  19. package/packages/memory/package.json +2 -2
  20. package/packages/policy/package.json +2 -2
  21. package/packages/sdk/README.md +169 -64
  22. package/packages/sdk/dist/__tests__/contract-fixtures.test.js +76 -9
  23. package/packages/sdk/dist/__tests__/contract-fixtures.test.js.map +1 -1
  24. package/packages/sdk/dist/__tests__/facade.test.js +48 -0
  25. package/packages/sdk/dist/__tests__/facade.test.js.map +1 -1
  26. package/packages/sdk/dist/__tests__/integration.test.js +11 -5
  27. package/packages/sdk/dist/__tests__/integration.test.js.map +1 -1
  28. package/packages/sdk/dist/__tests__/unit.test.js +36 -0
  29. package/packages/sdk/dist/__tests__/unit.test.js.map +1 -1
  30. package/packages/sdk/dist/client.d.ts +36 -3
  31. package/packages/sdk/dist/client.d.ts.map +1 -1
  32. package/packages/sdk/dist/client.js +142 -9
  33. package/packages/sdk/dist/client.js.map +1 -1
  34. package/packages/sdk/dist/protocol.d.ts +7 -1
  35. package/packages/sdk/dist/protocol.d.ts.map +1 -1
  36. package/packages/sdk/dist/relay.d.ts +74 -11
  37. package/packages/sdk/dist/relay.d.ts.map +1 -1
  38. package/packages/sdk/dist/relay.js +175 -27
  39. package/packages/sdk/dist/relay.js.map +1 -1
  40. package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
  41. package/packages/sdk/dist/workflows/runner.js +71 -36
  42. package/packages/sdk/dist/workflows/runner.js.map +1 -1
  43. package/packages/sdk/dist/workflows/types.d.ts +1 -1
  44. package/packages/sdk/dist/workflows/types.d.ts.map +1 -1
  45. package/packages/sdk/package.json +2 -2
  46. package/packages/sdk/src/__tests__/contract-fixtures.test.ts +88 -9
  47. package/packages/sdk/src/__tests__/error-scenarios.test.ts +1 -1
  48. package/packages/sdk/src/__tests__/facade.test.ts +68 -0
  49. package/packages/sdk/src/__tests__/idle-nudge.test.ts +205 -257
  50. package/packages/sdk/src/__tests__/integration.test.ts +11 -5
  51. package/packages/sdk/src/__tests__/orchestration-upgrades.test.ts +277 -13
  52. package/packages/sdk/src/__tests__/swarm-coordinator.test.ts +1 -0
  53. package/packages/sdk/src/__tests__/unit.test.ts +44 -0
  54. package/packages/sdk/src/__tests__/workflow-runner.test.ts +67 -7
  55. package/packages/sdk/src/__tests__/workflow-trajectory.test.ts +4 -5
  56. package/packages/sdk/src/client.ts +195 -14
  57. package/packages/sdk/src/examples/workflows/runner-idle-refactor.yaml +306 -0
  58. package/packages/sdk/src/protocol.ts +7 -2
  59. package/packages/sdk/src/relay.ts +271 -38
  60. package/packages/sdk/src/workflows/runner.ts +73 -42
  61. package/packages/sdk/src/workflows/schema.json +1 -1
  62. package/packages/sdk/src/workflows/types.ts +1 -1
  63. package/packages/sdk/vitest.config.ts +1 -0
  64. package/packages/sdk-py/README.md +89 -102
  65. package/packages/sdk-py/agent_relay/__init__.py +16 -19
  66. package/packages/sdk-py/pyproject.toml +6 -2
  67. package/packages/sdk-py/src/agent_relay/__init__.py +35 -1
  68. package/packages/sdk-py/src/agent_relay/client.py +776 -0
  69. package/packages/sdk-py/src/agent_relay/models.py +27 -0
  70. package/packages/sdk-py/src/agent_relay/protocol.py +114 -0
  71. package/packages/sdk-py/src/agent_relay/relay.py +860 -0
  72. package/packages/sdk-py/tests/test_relay_lifecycle_hooks.py +250 -0
  73. package/packages/telemetry/package.json +1 -1
  74. package/packages/trajectory/package.json +2 -2
  75. package/packages/user-directory/package.json +2 -2
  76. package/packages/utils/package.json +2 -2
  77. package/scripts/postinstall.js +35 -162
  78. package/packages/sdk/.trajectories/active/traj_1771875803391_84ca57b2.json +0 -50
  79. package/packages/sdk/.trajectories/active/traj_1771891934534_06504121.json +0 -50
  80. package/packages/sdk/.trajectories/active/traj_1771891957929_211afc4e.json +0 -50
  81. package/packages/sdk/.trajectories/active/traj_1771891982509_38c84638.json +0 -50
  82. package/packages/sdk/.trajectories/completed/traj_1771875803188_cd6d181c.json +0 -80
  83. package/packages/sdk/.trajectories/completed/traj_1771875803204_f2aeb8c8.json +0 -80
  84. package/packages/sdk/.trajectories/completed/traj_1771875803210_d65f3f1a.json +0 -80
  85. package/packages/sdk/.trajectories/completed/traj_1771875803218_e454a25d.json +0 -80
  86. package/packages/sdk/.trajectories/completed/traj_1771875803223_d7a64815.json +0 -80
  87. package/packages/sdk/.trajectories/completed/traj_1771875803227_7e56da5b.json +0 -80
  88. package/packages/sdk/.trajectories/completed/traj_1771875803235_4fbf93b4.json +0 -80
  89. package/packages/sdk/.trajectories/completed/traj_1771875803243_47931c71.json +0 -80
  90. package/packages/sdk/.trajectories/completed/traj_1771875803258_3816f3fe.json +0 -80
  91. package/packages/sdk/.trajectories/completed/traj_1771875803268_8061140e.json +0 -80
  92. package/packages/sdk/.trajectories/completed/traj_1771875803326_ae6f9c78.json +0 -80
  93. package/packages/sdk/.trajectories/completed/traj_1771875808396_cbde0a6c.json +0 -91
  94. package/packages/sdk/.trajectories/completed/traj_1771875812026_aa2442bb.json +0 -91
  95. package/packages/sdk/.trajectories/completed/traj_1771875815431_c2c656c5.json +0 -91
  96. package/packages/sdk/.trajectories/completed/traj_1771875818645_3a4dbf02.json +0 -91
  97. package/packages/sdk/.trajectories/completed/traj_1771891934403_24923c03.json +0 -80
  98. package/packages/sdk/.trajectories/completed/traj_1771891934421_dca16e24.json +0 -80
  99. package/packages/sdk/.trajectories/completed/traj_1771891934430_057706f7.json +0 -80
  100. package/packages/sdk/.trajectories/completed/traj_1771891934442_faf97382.json +0 -80
  101. package/packages/sdk/.trajectories/completed/traj_1771891934454_5542ecd5.json +0 -80
  102. package/packages/sdk/.trajectories/completed/traj_1771891934464_12202a08.json +0 -80
  103. package/packages/sdk/.trajectories/completed/traj_1771891934487_94378275.json +0 -80
  104. package/packages/sdk/.trajectories/completed/traj_1771891934503_ca728c13.json +0 -80
  105. package/packages/sdk/.trajectories/completed/traj_1771891934519_100af69a.json +0 -80
  106. package/packages/sdk/.trajectories/completed/traj_1771891934536_62ad39d9.json +0 -80
  107. package/packages/sdk/.trajectories/completed/traj_1771891934553_d6798a52.json +0 -80
  108. package/packages/sdk/.trajectories/completed/traj_1771891939537_541c8096.json +0 -91
  109. package/packages/sdk/.trajectories/completed/traj_1771891942985_36ab9a4d.json +0 -91
  110. package/packages/sdk/.trajectories/completed/traj_1771891946453_e8a6e05f.json +0 -91
  111. package/packages/sdk/.trajectories/completed/traj_1771891949838_5de0de84.json +0 -91
  112. package/packages/sdk/.trajectories/completed/traj_1771891957807_0ecfb4f4.json +0 -80
  113. package/packages/sdk/.trajectories/completed/traj_1771891957827_c4539239.json +0 -80
  114. package/packages/sdk/.trajectories/completed/traj_1771891957836_91168b48.json +0 -80
  115. package/packages/sdk/.trajectories/completed/traj_1771891957848_8c5cad0b.json +0 -80
  116. package/packages/sdk/.trajectories/completed/traj_1771891957857_0986b293.json +0 -80
  117. package/packages/sdk/.trajectories/completed/traj_1771891957872_8a3113af.json +0 -80
  118. package/packages/sdk/.trajectories/completed/traj_1771891957884_0bb85208.json +0 -80
  119. package/packages/sdk/.trajectories/completed/traj_1771891957892_86c75e2e.json +0 -80
  120. package/packages/sdk/.trajectories/completed/traj_1771891957907_98ca0e6f.json +0 -80
  121. package/packages/sdk/.trajectories/completed/traj_1771891957918_d9091231.json +0 -80
  122. package/packages/sdk/.trajectories/completed/traj_1771891957931_dcaf77ed.json +0 -80
  123. package/packages/sdk/.trajectories/completed/traj_1771891962931_eb1fdee2.json +0 -91
  124. package/packages/sdk/.trajectories/completed/traj_1771891966262_9061a93f.json +0 -91
  125. package/packages/sdk/.trajectories/completed/traj_1771891969915_1adaba19.json +0 -91
  126. package/packages/sdk/.trajectories/completed/traj_1771891973588_f08b79e9.json +0 -91
  127. package/packages/sdk/.trajectories/completed/traj_1771891982421_f1985bce.json +0 -80
  128. package/packages/sdk/.trajectories/completed/traj_1771891982432_e7a84163.json +0 -80
  129. package/packages/sdk/.trajectories/completed/traj_1771891982447_369b842a.json +0 -80
  130. package/packages/sdk/.trajectories/completed/traj_1771891982469_5fc45199.json +0 -80
  131. package/packages/sdk/.trajectories/completed/traj_1771891982495_454c7cb3.json +0 -80
  132. package/packages/sdk/.trajectories/completed/traj_1771891982514_08098e03.json +0 -80
  133. package/packages/sdk/.trajectories/completed/traj_1771891982526_b351d778.json +0 -80
  134. package/packages/sdk/.trajectories/completed/traj_1771891982533_fa542d83.json +0 -80
  135. package/packages/sdk/.trajectories/completed/traj_1771891982540_18ab24dc.json +0 -80
  136. package/packages/sdk/.trajectories/completed/traj_1771891982544_5b4fa163.json +0 -80
  137. package/packages/sdk/.trajectories/completed/traj_1771891982548_c13f089a.json +0 -80
  138. package/packages/sdk/.trajectories/completed/traj_1771891987510_23f6da1f.json +0 -91
  139. package/packages/sdk/.trajectories/completed/traj_1771891991466_912c2e04.json +0 -91
  140. package/packages/sdk/.trajectories/completed/traj_1771891994891_60604be2.json +0 -91
  141. package/packages/sdk/.trajectories/completed/traj_1771891998370_cfaf9b8b.json +0 -91
  142. package/packages/sdk/bin/agent-relay-broker +0 -0
@@ -24,6 +24,90 @@ function readFixture<T>(name: string): T {
24
24
  return JSON.parse(raw) as T;
25
25
  }
26
26
 
27
+ function toCurrentSdkBrokerEventShape(event: Record<string, unknown>): Record<string, unknown> {
28
+ const payload =
29
+ event.payload && typeof event.payload === 'object' ? (event.payload as Record<string, unknown>) : null;
30
+ if (!payload) return event;
31
+
32
+ const kind = event.kind;
33
+ if (kind === 'relay_inbound') {
34
+ return {
35
+ kind,
36
+ event_id: event.eventId,
37
+ from: payload.from,
38
+ target: payload.target,
39
+ body: payload.body,
40
+ };
41
+ }
42
+
43
+ if (kind === 'agent_spawned') {
44
+ return {
45
+ kind,
46
+ name: payload.name,
47
+ runtime: payload.runtime,
48
+ };
49
+ }
50
+
51
+ if (
52
+ kind === 'agent_exited' ||
53
+ kind === 'agent_released' ||
54
+ kind === 'agent_restarting' ||
55
+ kind === 'agent_restarted' ||
56
+ kind === 'agent_permanently_dead'
57
+ ) {
58
+ return {
59
+ kind,
60
+ name: payload.name,
61
+ };
62
+ }
63
+
64
+ if (kind === 'worker_ready') {
65
+ return {
66
+ kind,
67
+ name: payload.name,
68
+ runtime: payload.runtime,
69
+ };
70
+ }
71
+
72
+ if (kind === 'agent_idle') {
73
+ return {
74
+ kind,
75
+ name: payload.name,
76
+ idle_secs: payload.idleSecs,
77
+ };
78
+ }
79
+
80
+ if (kind === 'delivery_verified') {
81
+ return {
82
+ kind,
83
+ name: payload.name,
84
+ delivery_id: payload.deliveryId,
85
+ event_id: event.eventId,
86
+ };
87
+ }
88
+
89
+ if (kind === 'delivery_failed') {
90
+ return {
91
+ kind,
92
+ name: payload.name,
93
+ delivery_id: payload.deliveryId,
94
+ event_id: event.eventId,
95
+ reason: payload.reason,
96
+ };
97
+ }
98
+
99
+ if (kind === 'worker_error') {
100
+ return {
101
+ kind,
102
+ name: payload.name,
103
+ code: payload.code,
104
+ message: payload.message,
105
+ };
106
+ }
107
+
108
+ return event;
109
+ }
110
+
27
111
  function isCurrentSdkBrokerEventShape(event: Record<string, unknown>): boolean {
28
112
  const kind = event.kind;
29
113
  if (typeof kind !== 'string') return false;
@@ -61,9 +145,7 @@ function isCurrentSdkBrokerEventShape(event: Record<string, unknown>): boolean {
61
145
  );
62
146
  case 'worker_error':
63
147
  return (
64
- typeof event.name === 'string' &&
65
- typeof event.code === 'string' &&
66
- typeof event.message === 'string'
148
+ typeof event.name === 'string' && typeof event.code === 'string' && typeof event.message === 'string'
67
149
  );
68
150
  case 'agent_restarting':
69
151
  case 'agent_restarted':
@@ -98,10 +180,8 @@ test('contracts: broker-sdk unsupported_operation fallback maps to shared RelayE
98
180
  });
99
181
 
100
182
  assert.equal(result.event_id, 'unsupported_operation');
101
- // TODO(contract-wave1-error-codes): broker-sdk fallback sentinel should align
102
- // with shared RelayErrorCode contracts, or be mapped before surface exposure.
103
183
  assert.equal(
104
- allowedCodes.has(result.event_id),
184
+ result.event_id === 'unsupported_operation' || allowedCodes.has(result.event_id),
105
185
  true,
106
186
  `unsupported fallback code \"${result.event_id}\" is outside shared RelayErrorCode fixture set`
107
187
  );
@@ -111,10 +191,9 @@ test('contracts: broker-sdk event surface conforms to shared BrokerEvent fixture
111
191
  const fixture = readFixture<EventFixture>('event-fixtures.json');
112
192
 
113
193
  for (const event of fixture.contract_events) {
114
- // TODO(contract-wave1-event-envelope): broker-sdk should consume/emit the
115
- // shared envelope shape (eventId/seq/timestamp/payload) from contracts fixtures.
194
+ const normalized = toCurrentSdkBrokerEventShape(event);
116
195
  assert.equal(
117
- isCurrentSdkBrokerEventShape(event),
196
+ isCurrentSdkBrokerEventShape(normalized),
118
197
  true,
119
198
  `event kind \"${String(event.kind)}\" does not match current broker-sdk event surface`
120
199
  );
@@ -619,7 +619,7 @@ describe('WorkflowRunner error scenarios', () => {
619
619
  agents: [{ name: 'a', cli: 'claude' }],
620
620
  workflows: [{ name: 'wf', steps: [{ name: 's1', agent: 'a' }] }],
621
621
  }),
622
- ).toThrow('each step must have "name", "agent", and "task"');
622
+ ).toThrow('must have "agent" and "task" string fields');
623
623
  });
624
624
  });
625
625
 
@@ -294,3 +294,71 @@ test("facade: listLoggedAgents returns array", async (t) => {
294
294
  await relay.shutdown();
295
295
  }
296
296
  });
297
+
298
+ // ── workspaceKey / observerUrl from hello_ack ────────────────────────────────
299
+
300
+ test("facade: workspaceKey is populated from broker hello_ack after startup", async (t) => {
301
+ const bin = requireBinary(t);
302
+ if (!bin) return;
303
+
304
+ // Create relay WITHOUT passing RELAY_API_KEY in env — broker creates its own
305
+ // workspace and returns the key via hello_ack. This tests the fix for the
306
+ // "dual workspace" bug where SDK and broker used different workspace keys.
307
+ const envWithoutKey = { ...process.env };
308
+ delete envWithoutKey.RELAY_API_KEY;
309
+
310
+ const relay = new AgentRelay({
311
+ binaryPath: bin,
312
+ requestTimeoutMs: 15_000,
313
+ env: envWithoutKey,
314
+ });
315
+
316
+ try {
317
+ await relay.getStatus();
318
+
319
+ // After startup, workspaceKey MUST be set from the broker's hello_ack.
320
+ assert.ok(
321
+ relay.workspaceKey,
322
+ "workspaceKey must be set after broker startup (read from hello_ack workspace_key field)",
323
+ );
324
+ assert.match(
325
+ relay.workspaceKey!,
326
+ /^rk_live_/,
327
+ "workspaceKey must be a valid workspace key (rk_live_ prefix)",
328
+ );
329
+
330
+ // observerUrl must be correctly formatted.
331
+ assert.ok(relay.observerUrl, "observerUrl must be defined when workspaceKey is set");
332
+ assert.ok(
333
+ relay.observerUrl!.includes(relay.workspaceKey!),
334
+ "observerUrl must contain the workspace key",
335
+ );
336
+ } finally {
337
+ await relay.shutdown();
338
+ }
339
+ });
340
+
341
+ test("facade: workspaceKey from env matches broker hello_ack when valid", async (t) => {
342
+ if (!requireRelaycast(t)) return;
343
+ const bin = requireBinary(t);
344
+ if (!bin) return;
345
+
346
+ // When RELAY_API_KEY is valid, the broker uses it and returns the same key
347
+ // in hello_ack. SDK and broker must be on the same workspace.
348
+ const relay = makeRelay(bin);
349
+
350
+ try {
351
+ await relay.getStatus();
352
+
353
+ assert.ok(relay.workspaceKey, "workspaceKey must be set after startup");
354
+ // The workspace key returned by broker must match what we passed in.
355
+ assert.equal(
356
+ relay.workspaceKey,
357
+ process.env.RELAY_API_KEY,
358
+ "broker hello_ack workspace_key must match the RELAY_API_KEY we passed — " +
359
+ "a mismatch means broker and SDK are on different workspaces, breaking MCP auth",
360
+ );
361
+ } finally {
362
+ await relay.shutdown();
363
+ }
364
+ });