@relayflows/sdk 2.0.54 → 2.0.56

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 (180) hide show
  1. package/dist/authored-broker.d.ts +13 -0
  2. package/dist/authored-broker.d.ts.map +1 -0
  3. package/dist/authored-broker.js +75 -0
  4. package/dist/authored-broker.js.map +1 -0
  5. package/dist/authored-budget.d.ts +1 -1
  6. package/dist/authored-budget.d.ts.map +1 -1
  7. package/dist/authored-budget.js +8 -2
  8. package/dist/authored-budget.js.map +1 -1
  9. package/dist/authored-flow-error.d.ts +6 -0
  10. package/dist/authored-flow-error.d.ts.map +1 -1
  11. package/dist/authored-flow-error.js.map +1 -1
  12. package/dist/authored-flow-executor.d.ts +11 -0
  13. package/dist/authored-flow-executor.d.ts.map +1 -1
  14. package/dist/authored-flow-executor.js +104 -20
  15. package/dist/authored-flow-executor.js.map +1 -1
  16. package/dist/authored-flow-loader.d.ts.map +1 -1
  17. package/dist/authored-flow-loader.js +42 -3
  18. package/dist/authored-flow-loader.js.map +1 -1
  19. package/dist/authored-helper-effect.d.ts.map +1 -1
  20. package/dist/authored-helper-effect.js +1 -1
  21. package/dist/authored-helper-effect.js.map +1 -1
  22. package/dist/authored-hooks.d.ts +2 -1
  23. package/dist/authored-hooks.d.ts.map +1 -1
  24. package/dist/authored-hooks.js +2 -2
  25. package/dist/authored-hooks.js.map +1 -1
  26. package/dist/authored-human.d.ts +18 -0
  27. package/dist/authored-human.d.ts.map +1 -1
  28. package/dist/authored-human.js +32 -4
  29. package/dist/authored-human.js.map +1 -1
  30. package/dist/authored-mcp.d.ts.map +1 -1
  31. package/dist/authored-mcp.js +3 -2
  32. package/dist/authored-mcp.js.map +1 -1
  33. package/dist/authored-node-entry.js +4 -0
  34. package/dist/authored-node-entry.js.map +1 -1
  35. package/dist/authored-node-runner.d.ts +8 -0
  36. package/dist/authored-node-runner.d.ts.map +1 -1
  37. package/dist/authored-node-runner.js +60 -3
  38. package/dist/authored-node-runner.js.map +1 -1
  39. package/dist/authored-plugin-effect.d.ts.map +1 -1
  40. package/dist/authored-plugin-effect.js +3 -2
  41. package/dist/authored-plugin-effect.js.map +1 -1
  42. package/dist/authored-preflight.d.ts.map +1 -1
  43. package/dist/authored-preflight.js +2 -1
  44. package/dist/authored-preflight.js.map +1 -1
  45. package/dist/authored-root.d.ts.map +1 -1
  46. package/dist/authored-root.js +13 -3
  47. package/dist/authored-root.js.map +1 -1
  48. package/dist/authored-serial-ordinals.d.ts +41 -0
  49. package/dist/authored-serial-ordinals.d.ts.map +1 -0
  50. package/dist/authored-serial-ordinals.js +45 -0
  51. package/dist/authored-serial-ordinals.js.map +1 -0
  52. package/dist/authored-worker-step.d.ts +5 -1
  53. package/dist/authored-worker-step.d.ts.map +1 -1
  54. package/dist/authored-worker-step.js +19 -2
  55. package/dist/authored-worker-step.js.map +1 -1
  56. package/dist/budget-preflight.d.ts.map +1 -1
  57. package/dist/budget-preflight.js +11 -2
  58. package/dist/budget-preflight.js.map +1 -1
  59. package/dist/bundle-transport.d.ts +2 -0
  60. package/dist/bundle-transport.d.ts.map +1 -1
  61. package/dist/bundle-transport.js +4 -0
  62. package/dist/bundle-transport.js.map +1 -1
  63. package/dist/cached-flow-runtime.d.ts +3 -0
  64. package/dist/cached-flow-runtime.d.ts.map +1 -0
  65. package/dist/cached-flow-runtime.js +37 -0
  66. package/dist/cached-flow-runtime.js.map +1 -0
  67. package/dist/cli/check.d.ts.map +1 -1
  68. package/dist/cli/check.js +13 -1
  69. package/dist/cli/check.js.map +1 -1
  70. package/dist/cli/cloud-deploy.js +6 -5
  71. package/dist/cli/cloud-deploy.js.map +1 -1
  72. package/dist/cli/cloud-mirror-session.d.ts +8 -2
  73. package/dist/cli/cloud-mirror-session.d.ts.map +1 -1
  74. package/dist/cli/cloud-mirror-session.js +20 -27
  75. package/dist/cli/cloud-mirror-session.js.map +1 -1
  76. package/dist/cli/direct-run.d.ts.map +1 -1
  77. package/dist/cli/direct-run.js +2 -1
  78. package/dist/cli/direct-run.js.map +1 -1
  79. package/dist/cli/run.d.ts.map +1 -1
  80. package/dist/cli/run.js +19 -4
  81. package/dist/cli/run.js.map +1 -1
  82. package/dist/cli.d.ts.map +1 -1
  83. package/dist/cli.js +39 -18
  84. package/dist/cli.js.map +1 -1
  85. package/dist/cloud-deploy.d.ts +3 -3
  86. package/dist/cloud-deploy.d.ts.map +1 -1
  87. package/dist/cloud-deploy.js +15 -9
  88. package/dist/cloud-deploy.js.map +1 -1
  89. package/dist/cloud-flow-source.d.ts +13 -0
  90. package/dist/cloud-flow-source.d.ts.map +1 -0
  91. package/dist/cloud-flow-source.js +55 -0
  92. package/dist/cloud-flow-source.js.map +1 -0
  93. package/dist/cloud-mirror-transport.d.ts +2 -0
  94. package/dist/cloud-mirror-transport.d.ts.map +1 -1
  95. package/dist/cloud-mirror-transport.js +2 -0
  96. package/dist/cloud-mirror-transport.js.map +1 -1
  97. package/dist/cloud-mirror.d.ts.map +1 -1
  98. package/dist/cloud-mirror.js +10 -1
  99. package/dist/cloud-mirror.js.map +1 -1
  100. package/dist/communication/broker-preflight.d.ts +3 -0
  101. package/dist/communication/broker-preflight.d.ts.map +1 -0
  102. package/dist/communication/broker-preflight.js +22 -0
  103. package/dist/communication/broker-preflight.js.map +1 -0
  104. package/dist/communication/broker-spec.d.ts +20 -0
  105. package/dist/communication/broker-spec.d.ts.map +1 -0
  106. package/dist/communication/broker-spec.js +46 -0
  107. package/dist/communication/broker-spec.js.map +1 -0
  108. package/dist/communication/broker-worker.d.ts +17 -0
  109. package/dist/communication/broker-worker.d.ts.map +1 -0
  110. package/dist/communication/broker-worker.js +214 -0
  111. package/dist/communication/broker-worker.js.map +1 -0
  112. package/dist/communication/pty.d.ts +29 -0
  113. package/dist/communication/pty.d.ts.map +1 -0
  114. package/dist/communication/pty.js +40 -0
  115. package/dist/communication/pty.js.map +1 -0
  116. package/dist/communication/relay.d.ts +18 -0
  117. package/dist/communication/relay.d.ts.map +1 -1
  118. package/dist/communication/relay.js +4 -0
  119. package/dist/communication/relay.js.map +1 -1
  120. package/dist/communication/skills.d.ts +3 -0
  121. package/dist/communication/skills.d.ts.map +1 -0
  122. package/dist/communication/skills.js +17 -0
  123. package/dist/communication/skills.js.map +1 -0
  124. package/dist/communication/worker.d.ts.map +1 -1
  125. package/dist/communication/worker.js +9 -26
  126. package/dist/communication/worker.js.map +1 -1
  127. package/dist/failure-kinds.d.ts +1 -1
  128. package/dist/failure-kinds.d.ts.map +1 -1
  129. package/dist/failure-kinds.js +2 -0
  130. package/dist/failure-kinds.js.map +1 -1
  131. package/dist/plugin-manifest.js +1 -1
  132. package/dist/plugin-manifest.js.map +1 -1
  133. package/dist/preflight.d.ts.map +1 -1
  134. package/dist/preflight.js +2 -1
  135. package/dist/preflight.js.map +1 -1
  136. package/dist/worker.d.ts +1 -0
  137. package/dist/worker.d.ts.map +1 -1
  138. package/dist/worker.js +12 -0
  139. package/dist/worker.js.map +1 -1
  140. package/package.json +2 -2
  141. package/src/authored-broker.ts +73 -0
  142. package/src/authored-budget.ts +6 -2
  143. package/src/authored-flow-error.ts +6 -0
  144. package/src/authored-flow-executor.ts +115 -20
  145. package/src/authored-flow-loader.ts +43 -3
  146. package/src/authored-helper-effect.ts +2 -1
  147. package/src/authored-hooks.ts +4 -3
  148. package/src/authored-human.ts +35 -4
  149. package/src/authored-mcp.ts +7 -2
  150. package/src/authored-node-entry.ts +4 -1
  151. package/src/authored-node-runner.ts +57 -3
  152. package/src/authored-plugin-effect.ts +7 -2
  153. package/src/authored-preflight.ts +2 -1
  154. package/src/authored-root.ts +13 -3
  155. package/src/authored-serial-ordinals.ts +76 -0
  156. package/src/authored-worker-step.ts +17 -2
  157. package/src/budget-preflight.ts +9 -1
  158. package/src/bundle-transport.ts +4 -0
  159. package/src/cached-flow-runtime.ts +32 -0
  160. package/src/cli/check.ts +11 -1
  161. package/src/cli/cloud-deploy.ts +5 -5
  162. package/src/cli/cloud-mirror-session.ts +23 -29
  163. package/src/cli/direct-run.ts +2 -1
  164. package/src/cli/run.ts +18 -3
  165. package/src/cli.ts +31 -13
  166. package/src/cloud-deploy.ts +19 -14
  167. package/src/cloud-flow-source.ts +58 -0
  168. package/src/cloud-mirror-transport.ts +4 -0
  169. package/src/cloud-mirror.ts +9 -1
  170. package/src/communication/broker-preflight.ts +22 -0
  171. package/src/communication/broker-spec.ts +48 -0
  172. package/src/communication/broker-worker.ts +198 -0
  173. package/src/communication/pty.ts +47 -0
  174. package/src/communication/relay.ts +12 -1
  175. package/src/communication/skills.ts +16 -0
  176. package/src/communication/worker.ts +9 -26
  177. package/src/failure-kinds.ts +2 -0
  178. package/src/plugin-manifest.ts +1 -1
  179. package/src/preflight.ts +3 -1
  180. package/src/worker.ts +11 -0
@@ -0,0 +1,47 @@
1
+ import { mkdir, mkdtemp, rm, symlink } from 'node:fs/promises';
2
+ import { basename, join } from 'node:path';
3
+ import { agentEnvironment } from './environment.js';
4
+ import { resolveCliModel } from '../cli-adapter.js';
5
+ import type { RelayBroker } from './relay.js';
6
+
7
+ export const shellQuote = (value: string) => "'" + value.replaceAll("'", "'\"'\"'") + "'";
8
+
9
+ /** Both communication steps and retained sessions use Relay's PTY launch contract. */
10
+ export async function spawnFlowPty(broker: Pick<RelayBroker, 'spawnPty'>, options: {
11
+ name: string; cli: string; identity?: string; model?: string; task: string;
12
+ skipRelayPrompt?: boolean;
13
+ cwd: string; dataDir: string; channels: string[]; team?: string;
14
+ environment?: NodeJS.ProcessEnv; socket: string; token: string;
15
+ secrets?: string[]; scopeSecrets?: boolean;
16
+ metadata?: { runId: string; flow: string; step: string };
17
+ }) {
18
+ const identity = options.identity ?? options.cli;
19
+ const argv0 = basename(identity);
20
+ let command = options.cli;
21
+ let link: string | undefined;
22
+ try {
23
+ if (basename(command) !== argv0) {
24
+ const root = join(options.dataDir, 'communication');
25
+ await mkdir(root, { recursive: true, mode: 0o700 });
26
+ link = await mkdtemp(join(root, 'cli-'));
27
+ command = join(link, argv0);
28
+ await symlink(options.cli, command, 'file');
29
+ }
30
+ const handle = await broker.spawnPty({ name: options.name, cli: argv0.replace(/\.exe$/i, ''),
31
+ task: options.task, channels: options.channels, skipRelayPrompt: options.skipRelayPrompt ?? true,
32
+ ...(options.team === undefined ? {} : { team: options.team }),
33
+ model: resolveCliModel(identity, options.model), cwd: options.cwd,
34
+ harnessConfig: { runtime: 'pty', command: shellQuote(command), args: [], cwd: options.cwd,
35
+ env: { ...agentEnvironment(identity, options.environment ?? process.env,
36
+ options.secrets, options.scopeSecrets ?? true),
37
+ RELAYFLOW_COMMUNICATION_SOCKET: options.socket, RELAYFLOW_COMMUNICATION_TOKEN: options.token,
38
+ ...(options.metadata === undefined ? {} : {
39
+ RELAYFLOW_RUN_ID: options.metadata.runId, RELAYFLOW_FLOW_NAME: options.metadata.flow,
40
+ RELAYFLOW_STEP_NAME: options.metadata.step,
41
+ }) }, delivery: { mode: 'pty-injection', format: 'relay-block' } } });
42
+ return { handle, close: async () => { if (link) await rm(link, { recursive: true, force: true }); } };
43
+ } catch (error) {
44
+ if (link) await rm(link, { recursive: true, force: true });
45
+ throw error;
46
+ }
47
+ }
@@ -25,7 +25,8 @@ export interface RelayMessaging {
25
25
  };
26
26
  }
27
27
  interface RelayModules {
28
- HarnessDriverClient: { spawn(options: Record<string, unknown>): Promise<RelayBroker> };
28
+ HarnessDriverClient: { spawn(options: Record<string, unknown>): Promise<RelayBroker>;
29
+ connect(options: { cwd: string }): LocalBroker };
29
30
  RelaycastMessagingClient: new (options: Record<string, unknown>) => RelayMessaging;
30
31
  }
31
32
  export async function loadRelayModules(): Promise<RelayModules> {
@@ -100,3 +101,13 @@ async function createRuntime(dataDir: string, runId: string): Promise<RelayRunti
100
101
  throw error;
101
102
  }
102
103
  }
104
+
105
+ /** Connected clients disconnect on shutdown; they never terminate the user's broker. */
106
+ export interface LocalBroker extends RelayBroker {
107
+ connectEvents(): void;
108
+ sendMessage(input: { to: string; text: string; from: string; mode: 'wait'; data: Record<string, unknown> }): Promise<{ event_id: string; targets: string[] }>;
109
+ }
110
+ export async function connectLocalBroker(cwd: string): Promise<LocalBroker> {
111
+ const { HarnessDriverClient } = await loadRelayModules();
112
+ return HarnessDriverClient.connect({ cwd });
113
+ }
@@ -0,0 +1,16 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { basename } from 'node:path';
4
+
5
+ const execute = promisify(execFile);
6
+ /** Install full skill directories (including references) using the canonical package. */
7
+ export async function installBrokerSkills(skills: string[], cli: string, cwd: string, signal: AbortSignal): Promise<void> {
8
+ if (!skills.length) return;
9
+ const identity = basename(cli).replace(/\.exe$/i, '');
10
+ const harness = ({ claude: 'claude-code', codex: 'codex', gemini: 'gemini-cli',
11
+ 'cursor-agent': 'cursor', opencode: 'opencode' } as Record<string, string>)[identity];
12
+ if (!harness) throw new Error(`Skill installation is unsupported for broker CLI ${identity}`);
13
+ await execute('npx', ['--yes', 'skills', 'add', 'https://github.com/AgentWorkforce/skills',
14
+ '--skill', ...skills, '--agent', harness, '--yes', '--copy'],
15
+ { cwd, signal, timeout: 120_000, maxBuffer: 1024 * 1024 });
16
+ }
@@ -1,6 +1,4 @@
1
1
  import { communicationHistory } from './history.js';
2
- import { mkdir, mkdtemp, rm, symlink } from 'node:fs/promises';
3
- import { basename, join } from 'node:path';
4
2
  import { setTimeout as delay } from 'node:timers/promises';
5
3
  import type { JournalClient } from '../journal-client.js';
6
4
  import type { StepDispatchEvent } from '../protocol.js';
@@ -8,13 +6,12 @@ import type { ResolvedKernelAgentStep } from '../resolved-cli-identity.js';
8
6
  import { isLeaseLost, withWorkerLease } from '../worker-lease.js';
9
7
  import { isReadInterruptionError } from '../journal-read-policy.js';
10
8
  import { workerInstruction } from '../worker-input.js';
11
- import { resolveCliModel } from '../cli-adapter.js';
12
9
  import { resolveAgentCwd } from '../agent-cwd.js';
13
10
  import { channelName, type CommunicationInstruction } from './spec.js';
14
11
  import { acquireRelayRuntime, type RelayHandle } from './relay.js';
15
12
  import { CommunicationSession } from './session.js';
16
13
  import { openCommunicationTools } from './tools.js';
17
- import { agentEnvironment } from './environment.js';
14
+ import { spawnFlowPty, shellQuote } from './pty.js';
18
15
 
19
16
  export function requireCommunicationCli(cli: string | undefined): void {
20
17
  if (!cli?.trim()) throw new Error('Agent communication requires a declared CLI executable');
@@ -62,7 +59,7 @@ async function run(client: JournalClient, dispatch: StepDispatchEvent, instructi
62
59
  let pumping: Promise<void> | undefined;
63
60
  let receipts = Promise.resolve();
64
61
  let unsubscribe: (() => void) | undefined;
65
- let cliLinkDirectory: string | undefined;
62
+ let pty: Awaited<ReturnType<typeof spawnFlowPty>> | undefined;
66
63
  try {
67
64
  let resolve!: (value: unknown) => void;
68
65
  let reject!: (reason: unknown) => void;
@@ -74,8 +71,7 @@ async function run(client: JournalClient, dispatch: StepDispatchEvent, instructi
74
71
  if (signal.aborted) abort();
75
72
  const session = new CommunicationSession(client, dispatch, instruction, relay, summary => resolve({ summary }), reject);
76
73
  tools = await openCommunicationTools(request => session.invoke(request));
77
- const quote = (value: string) => "'" + value.replaceAll("'", "'\"'\"'") + "'";
78
- const helper = `node ${quote(tools.helperPath)}`;
74
+ const helper = `node ${shellQuote(tools.helperPath)}`;
79
75
  const history = await communicationHistory(client, dispatch, instruction);
80
76
  const prompt = `${workerInstruction(instruction.instruction, dispatch)}${history}\n\nYou are flow agent ${dispatch.step_id}. Concurrent peers send messages automatically into your session through Relay. Do not poll for messages. Use your shell/terminal execution tool with ${helper} for flow communication:\n- send PEER STABLE_ID 'message text' (outgoing peers: ${instruction.outgoing.join(', ') || 'none'})\n- ack PEER DELIVERY_SEQ only after processing that message (incoming peers: ${instruction.incoming.join(', ') || 'none'})\n- complete 'summary' when your task and conversation are finished.\nUse these helpers for all peer communication; do not use Relay MCP messaging tools. Stay available for injected messages until the conversation is complete. Use stable semantic message IDs so retries deduplicate sends.`;
81
77
  const name = `${relay.prefix}-${dispatch.step_id}`;
@@ -89,24 +85,11 @@ async function run(client: JournalClient, dispatch: StepDispatchEvent, instructi
89
85
  });
90
86
  void receipts.catch(reject);
91
87
  });
92
- // Relay supplies each CLI's launch flags and injection behavior.
93
- const cliIdentity = spec.cli_identity ?? spec.cli!;
94
- const argv0 = basename(cliIdentity);
95
- let command = spec.cli!;
96
- if (basename(command) !== argv0) {
97
- const linkRoot = join(dataDir, 'communication');
98
- await mkdir(linkRoot, { recursive: true, mode: 0o700 });
99
- cliLinkDirectory = await mkdtemp(join(linkRoot, 'cli-'));
100
- command = join(cliLinkDirectory, argv0);
101
- await symlink(spec.cli!, command, 'file');
102
- }
103
- handle = await relay.broker.spawnPty({ name, cli: basename(cliIdentity).replace(/\.exe$/i, ''), task: prompt, channels: [], skipRelayPrompt: true,
104
- model: resolveCliModel(cliIdentity, spec.model), cwd: directory,
105
- harnessConfig: { runtime: 'pty', command: quote(command), args: [],
106
- cwd: directory, env: { ...agentEnvironment(cliIdentity, environment ?? process.env,
107
- spec.secrets, client.supportsFeature?.('step_secret_scoping') ?? true),
108
- RELAYFLOW_COMMUNICATION_SOCKET: tools.path, RELAYFLOW_COMMUNICATION_TOKEN: tools.token },
109
- delivery: { mode: 'pty-injection', format: 'relay-block' } } });
88
+ pty = await spawnFlowPty(relay.broker, { name, cli: spec.cli!, identity: spec.cli_identity,
89
+ model: spec.model, task: prompt, channels: [], cwd: directory, dataDir,
90
+ environment, socket: tools.path, token: tools.token,
91
+ secrets: spec.secrets, scopeSecrets: client.supportsFeature?.('step_secret_scoping') ?? true });
92
+ handle = pty.handle;
110
93
  const ready = await handle.waitForReady(Math.min(instruction.timeoutMs, 90_000));
111
94
  if (ready.reason !== 'ready') throw new Error(`Communication agent did not become ready: ${ready.reason}`);
112
95
  pumping = (async () => {
@@ -123,7 +106,7 @@ async function run(client: JournalClient, dispatch: StepDispatchEvent, instructi
123
106
  finally { try { await handle?.release('Flow communication attempt ended', { deleteIdentity: true }); }
124
107
  finally { try { await tools?.close(); } finally {
125
108
  try { await relay.close(); }
126
- finally { if (cliLinkDirectory) await rm(cliLinkDirectory, { recursive: true, force: true }); }
109
+ finally { await pty?.close(); }
127
110
  } } }
128
111
  }
129
112
  }
@@ -138,6 +138,8 @@ export const RUN_FAILURE_KINDS = [
138
138
  'unsafe_parallel_runtime',
139
139
  /** A predicate `.gate(fn)` judged false; the verdict is journaled as `<step>.gate`. */
140
140
  'gate_failed',
141
+ /** A resumed serial body reached a journaled step id with a different call; refused, not executed. */
142
+ 'serial_replay_order_mismatch',
141
143
  /** `flows answer` named a wait the run is not asking: unknown, or already answered. */
142
144
  'human_wait_unknown',
143
145
  /** `f.memory.learn` was given a finding without non-empty string fields. */
@@ -39,7 +39,7 @@ export interface PluginManifest {
39
39
  preflight: { credentials: readonly string[]; servers: readonly string[] };
40
40
  }
41
41
  const identifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
42
- const reserved = new Set(['run', 'llm', 'agent', 'human', 'dispatch', 'done', 'cloud', 'memory', 'mcp', 'slack', '__proto__', 'constructor', 'prototype', 'then']);
42
+ const reserved = new Set(['run', 'llm', 'agent', 'message', 'release', 'human', 'dispatch', 'done', 'cloud', 'memory', 'mcp', 'slack', '__proto__', 'constructor', 'prototype', 'then']);
43
43
  const object = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);
44
44
  const strings = (v: unknown): v is string[] => Array.isArray(v) && v.every(s => typeof s === 'string' && s.trim().length > 0);
45
45
  export function pluginPackageName(name: string): string {
package/src/preflight.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { brokerInstruction } from './communication/broker-spec.js';
1
2
  import { communicationInstruction } from './communication/spec.js';
2
3
  import { loadPlugins, type LoadedPlugin } from './plugin-loader.js';
3
4
  import { PluginError } from './plugin-manifest.js';
@@ -340,7 +341,8 @@ function preflightSync(flow: unknown, options: PreflightOptions): PreflightResul
340
341
  }
341
342
  const resolution = resolutionByStep.get(step.id)!;
342
343
  probeResolvedCli(resolution, options.probes, cliProbeResults, diagnostics,
343
- step.type === 'agent' && communicationInstruction(step.instruction) !== undefined, step.secrets);
344
+ step.type === 'agent' && (communicationInstruction(step.instruction) !== undefined || brokerInstruction(step.instruction) !== undefined),
345
+ step.secrets);
344
346
  }
345
347
 
346
348
  for (const trigger of compiled.triggers ?? []) {
package/src/worker.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { brokerInstruction } from './communication/broker-spec.js';
2
+ import { BrokerSessions } from './communication/broker-worker.js';
1
3
  import { communicationInstruction } from './communication/spec.js';
2
4
  import { workerSpend } from './worker-spend.js';
3
5
  import { reportedCost } from './reported-cost.js';
@@ -60,6 +62,7 @@ export interface AgentWorkerOptions {
60
62
  * shutdown.
61
63
  */
62
64
  export class AgentWorker extends EventEmitter {
65
+ private readonly brokerSessions: BrokerSessions;
63
66
  private attached = false;
64
67
  private closing = false;
65
68
  private readonly inFlight: Set<Promise<void>> = new Set();
@@ -69,6 +72,7 @@ export class AgentWorker extends EventEmitter {
69
72
  private readonly options: AgentWorkerOptions,
70
73
  ) {
71
74
  super();
75
+ this.brokerSessions = new BrokerSessions(options.runRoot, options.environment);
72
76
  }
73
77
 
74
78
  async attach(): Promise<void> {
@@ -107,6 +111,7 @@ export class AgentWorker extends EventEmitter {
107
111
  if (pending.length > 0) {
108
112
  await Promise.allSettled(pending);
109
113
  }
114
+ await this.brokerSessions.close();
110
115
  this.attached = false;
111
116
  }
112
117
 
@@ -128,6 +133,12 @@ export class AgentWorker extends EventEmitter {
128
133
  await completeHelperDispatch(this.client, dispatch, helper, this.options.dataDir);
129
134
  return;
130
135
  }
136
+ const broker = brokerInstruction(spec.instruction);
137
+ if (broker) {
138
+ if (!this.options.dataDir) throw new Error('Broker agents require a worker data directory');
139
+ await this.brokerSessions.complete(this.client, dispatch, broker, this.options.dataDir);
140
+ return;
141
+ }
131
142
  const communication = communicationInstruction(spec.instruction);
132
143
  if (communication) {
133
144
  if (!this.options.dataDir) throw new Error('Agent communication requires a worker data directory');