@masons/agent-network 0.5.30 → 0.5.32

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 (43) hide show
  1. package/dist/broker/broker-daemon.d.ts +20 -0
  2. package/dist/broker/broker-daemon.d.ts.map +1 -1
  3. package/dist/broker/broker-daemon.js +243 -2
  4. package/dist/broker/claude-code-spawn-driver.d.ts +14 -0
  5. package/dist/broker/claude-code-spawn-driver.d.ts.map +1 -0
  6. package/dist/broker/claude-code-spawn-driver.js +39 -0
  7. package/dist/broker/codex-spawn-driver-stub.d.ts +7 -0
  8. package/dist/broker/codex-spawn-driver-stub.d.ts.map +1 -0
  9. package/dist/broker/codex-spawn-driver-stub.js +13 -0
  10. package/dist/broker/control-event-dispatcher.d.ts +1 -0
  11. package/dist/broker/control-event-dispatcher.d.ts.map +1 -1
  12. package/dist/broker/control-event-types.d.ts +11 -13
  13. package/dist/broker/control-event-types.d.ts.map +1 -1
  14. package/dist/broker/entry.d.ts +8 -0
  15. package/dist/broker/entry.d.ts.map +1 -1
  16. package/dist/broker/entry.js +108 -2
  17. package/dist/broker/ipc-server.d.ts +7 -0
  18. package/dist/broker/ipc-server.d.ts.map +1 -1
  19. package/dist/broker/ipc-server.js +20 -0
  20. package/dist/broker/network-presence.d.ts +31 -0
  21. package/dist/broker/network-presence.d.ts.map +1 -0
  22. package/dist/broker/network-presence.js +109 -0
  23. package/dist/broker/services-event-client.d.ts +21 -0
  24. package/dist/broker/services-event-client.d.ts.map +1 -0
  25. package/dist/broker/services-event-client.js +221 -0
  26. package/dist/broker/spawn-correlation.d.ts +28 -0
  27. package/dist/broker/spawn-correlation.d.ts.map +1 -0
  28. package/dist/broker/spawn-correlation.js +77 -0
  29. package/dist/broker/spawn-driver.d.ts +27 -0
  30. package/dist/broker/spawn-driver.d.ts.map +1 -0
  31. package/dist/broker/spawn-driver.js +15 -0
  32. package/dist/broker/task-hint-handler.d.ts +21 -0
  33. package/dist/broker/task-hint-handler.d.ts.map +1 -0
  34. package/dist/broker/task-hint-handler.js +33 -0
  35. package/dist/broker/transition-state-retry-queue.d.ts +20 -0
  36. package/dist/broker/transition-state-retry-queue.d.ts.map +1 -0
  37. package/dist/broker/transition-state-retry-queue.js +48 -0
  38. package/dist/broker-client/broker-client.d.ts +1 -0
  39. package/dist/broker-client/broker-client.d.ts.map +1 -1
  40. package/dist/broker-client/broker-client.js +1 -0
  41. package/dist/version.d.ts +1 -1
  42. package/dist/version.js +1 -1
  43. package/package.json +3 -2
@@ -1,12 +1,17 @@
1
+ import { spawn as spawnChild } from "node:child_process";
1
2
  import type { IncomingMessage } from "node:http";
2
3
  import type { ClosedEndpointLookup } from "./closed-endpoint-lookup.js";
3
4
  import type { ConnectorWS } from "./connector-ws.js";
4
5
  import { type ControlEventDispatcher } from "./control-event-dispatcher.js";
6
+ import type { ControlAck } from "./control-event-types.js";
5
7
  import { type EndpointState } from "./endpoint-state-machine.js";
6
8
  import type { BrokerLogger } from "./logger.js";
9
+ import { type NetworkPresence } from "./network-presence.js";
7
10
  import type { BrokerPaths } from "./paths.js";
8
11
  import { type BufferedMessage } from "./reconnecting-buffer.js";
9
12
  import type { RuntimeEndpointPort } from "./runtime-endpoint-port.js";
13
+ import type { ServicesEventClient } from "./services-event-client.js";
14
+ import type { SpawnDriverRegistry } from "./spawn-driver.js";
10
15
  import { type UndispatchedInbox } from "./undispatched-inbox.js";
11
16
  export interface BrokerDaemonOptions {
12
17
  paths: BrokerPaths;
@@ -18,6 +23,18 @@ export interface BrokerDaemonOptions {
18
23
  inboxCapacity?: number;
19
24
  graceMs?: number;
20
25
  livenessProbe?: (plugin_pid: number) => boolean;
26
+ spawnDriverRegistry?: SpawnDriverRegistry;
27
+ spawnFn?: typeof spawnChild;
28
+ spawnRateLimitMaxInWindow?: number;
29
+ spawnRateLimitWindowMs?: number;
30
+ spawnCorrelationTimeoutMs?: number;
31
+ presenceGraceMs?: number;
32
+ retryQueueCapacity?: number;
33
+ servicesEventClientFactory?: (deps: {
34
+ dispatcher: ControlEventDispatcher;
35
+ logger: BrokerLogger;
36
+ }) => ServicesEventClient;
37
+ postControlAck?: (ack: ControlAck) => Promise<void>;
21
38
  }
22
39
  export interface RunningBroker {
23
40
  bearerToken: string;
@@ -27,6 +44,9 @@ export interface RunningBroker {
27
44
  state(endpoint_id: string): EndpointState | undefined;
28
45
  undispatchedInbox: UndispatchedInbox;
29
46
  controlEventDispatcher: ControlEventDispatcher;
47
+ networkPresence(): NetworkPresence;
48
+ pendingSpawnCount(): number;
49
+ retryQueueSize(): number;
30
50
  }
31
51
  export declare function startBrokerDaemon(opts: BrokerDaemonOptions): Promise<RunningBroker>;
32
52
  export declare function readPluginPidHeader(req: IncomingMessage): number | null;
@@ -1 +1 @@
1
- {"version":3,"file":"broker-daemon.d.ts","sourceRoot":"","sources":["../../src/broker/broker-daemon.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EACL,KAAK,sBAAsB,EAE5B,MAAM,+BAA+B,CAAC;AAQvC,OAAO,EAEL,KAAK,aAAa,EAEnB,MAAM,6BAA6B,CAAC;AAarC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EACL,KAAK,eAAe,EAErB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAEL,KAAK,iBAAiB,EAEvB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,WAAW,CAAC;IACvB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,MAAM,EAAE,YAAY,CAAC;IAGrB,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAE5C,aAAa,CAAC,EAAE,MAAM,CAAC;IAGvB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;CACjD;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAExC,YAAY,IAAI,MAAM,CAAC;IAGvB,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IAEtD,iBAAiB,EAAE,iBAAiB,CAAC;IAErC,sBAAsB,EAAE,sBAAsB,CAAC;CAChD;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,aAAa,CAAC,CAqnBxB;AAGD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,CAMvE;AAED,YAAY,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"broker-daemon.d.ts","sourceRoot":"","sources":["../../src/broker/broker-daemon.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAqB,KAAK,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE5E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EACL,KAAK,sBAAsB,EAE5B,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EACV,UAAU,EAIX,MAAM,0BAA0B,CAAC;AAOlC,OAAO,EAEL,KAAK,aAAa,EAEnB,MAAM,6BAA6B,CAAC;AAcrC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,KAAK,eAAe,EAErB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EACL,KAAK,eAAe,EAErB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAKtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAG7D,OAAO,EAEL,KAAK,iBAAiB,EAEvB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,WAAW,CAAC;IACvB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,MAAM,EAAE,YAAY,CAAC;IAGrB,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAE5C,aAAa,CAAC,EAAE,MAAM,CAAC;IAGvB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;IAKhD,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAG1C,OAAO,CAAC,EAAE,OAAO,UAAU,CAAC;IAG5B,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAGhC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAGnC,eAAe,CAAC,EAAE,MAAM,CAAC;IAGzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAK5B,0BAA0B,CAAC,EAAE,CAAC,IAAI,EAAE;QAClC,UAAU,EAAE,sBAAsB,CAAC;QACnC,MAAM,EAAE,YAAY,CAAC;KACtB,KAAK,mBAAmB,CAAC;IAK1B,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAExC,YAAY,IAAI,MAAM,CAAC;IAGvB,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IAEtD,iBAAiB,EAAE,iBAAiB,CAAC;IAErC,sBAAsB,EAAE,sBAAsB,CAAC;IAE/C,eAAe,IAAI,eAAe,CAAC;IAEnC,iBAAiB,IAAI,MAAM,CAAC;IAE5B,cAAc,IAAI,MAAM,CAAC;CAC1B;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,aAAa,CAAC,CA67BxB;AAGD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,CAMvE;AAED,YAAY,EAAE,eAAe,EAAE,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { spawn as spawnChild } from "node:child_process";
1
2
  import { randomUUID } from "node:crypto";
2
3
  import { createControlEventDispatcher, } from "./control-event-dispatcher.js";
3
4
  import { deleteDiscoveryFile, mintBearerToken, writeDiscoveryFile, } from "./discovery-file.js";
@@ -5,13 +6,21 @@ import { EndpointRegistry } from "./endpoint-registry.js";
5
6
  import { DEFAULT_GRACE_MS, transition, } from "./endpoint-state-machine.js";
6
7
  import { createGraceTimerManager } from "./grace-timer.js";
7
8
  import { BrokerHttpError, pushToPlugin, startIPCServer, } from "./ipc-server.js";
9
+ import { transitionPresence, } from "./network-presence.js";
8
10
  import { isPluginAlive as defaultIsPluginAlive } from "./plugin-liveness.js";
9
11
  import { createReconnectingBufferManager, } from "./reconnecting-buffer.js";
10
12
  import { RoutingTable } from "./routing-table.js";
13
+ import { createSpawnCorrelationManager, createSpawnRateLimiter, } from "./spawn-correlation.js";
14
+ import { TASK_HINT_MAX_LENGTH, updateTaskHint } from "./task-hint-handler.js";
15
+ import { createTransitionStateRetryQueue } from "./transition-state-retry-queue.js";
11
16
  import { createUndispatchedInbox, } from "./undispatched-inbox.js";
12
17
  export async function startBrokerDaemon(opts) {
13
- const { paths, asNodeId: _asNodeIdReservedForA3, connector, apiPort, logger, closedEndpointLookup, livenessProbe = defaultIsPluginAlive, } = opts;
18
+ const { paths, asNodeId: _asNodeIdReservedForA3, connector, apiPort, logger, closedEndpointLookup, livenessProbe = defaultIsPluginAlive, spawnDriverRegistry, spawnFn = spawnChild, servicesEventClientFactory, } = opts;
14
19
  const graceMs = opts.graceMs ?? DEFAULT_GRACE_MS;
20
+ const presenceGraceMs = opts.presenceGraceMs;
21
+ const postControlAck = opts.postControlAck ??
22
+ (async () => {
23
+ });
15
24
  void _asNodeIdReservedForA3;
16
25
  logger.info("connecting_to_connector");
17
26
  await connector.connect();
@@ -28,14 +37,91 @@ export async function startBrokerDaemon(opts) {
28
37
  const reconnectingBuffers = createReconnectingBufferManager(paths.buffersDir);
29
38
  const graceTimers = createGraceTimerManager();
30
39
  const controlEventDispatcher = createControlEventDispatcher();
40
+ const spawnCorrelation = createSpawnCorrelationManager({
41
+ timeoutMs: opts.spawnCorrelationTimeoutMs,
42
+ });
43
+ const spawnRateLimiter = createSpawnRateLimiter({
44
+ maxInWindow: opts.spawnRateLimitMaxInWindow,
45
+ windowMs: opts.spawnRateLimitWindowMs,
46
+ });
47
+ const retryQueue = createTransitionStateRetryQueue({
48
+ capacity: opts.retryQueueCapacity,
49
+ onDrop: (dropped) => {
50
+ logger.warn("transition_state_retry_queue_overflow", {
51
+ endpoint_id: dropped.endpoint_id,
52
+ state: dropped.params.state,
53
+ reason: dropped.params.reason,
54
+ enqueued_at: dropped.enqueued_at,
55
+ });
56
+ },
57
+ });
58
+ let networkPresence = "offline";
59
+ let presenceGraceTimer;
60
+ const applyPresenceTransition = (event) => {
61
+ const result = transitionPresence(networkPresence, event, {
62
+ graceMs: presenceGraceMs,
63
+ });
64
+ if (result.ignored)
65
+ return;
66
+ networkPresence = result.next;
67
+ for (const effect of result.effects) {
68
+ switch (effect.type) {
69
+ case "start_grace_timer":
70
+ if (presenceGraceTimer)
71
+ clearTimeout(presenceGraceTimer);
72
+ presenceGraceTimer = setTimeout(() => {
73
+ applyPresenceTransition({ type: "presence_grace_expired" });
74
+ }, effect.deadline_ms);
75
+ presenceGraceTimer.unref?.();
76
+ break;
77
+ case "cancel_grace_timer":
78
+ if (presenceGraceTimer) {
79
+ clearTimeout(presenceGraceTimer);
80
+ presenceGraceTimer = undefined;
81
+ }
82
+ break;
83
+ case "emit_presence":
84
+ logger.info("network_presence_changed", {
85
+ presence: effect.presence,
86
+ reason: effect.reason,
87
+ });
88
+ if (effect.presence === "online") {
89
+ void retryQueue.flush(async (entry) => {
90
+ await apiPort.transitionState(entry.endpoint_id, entry.params);
91
+ });
92
+ }
93
+ break;
94
+ }
95
+ }
96
+ };
97
+ applyPresenceTransition({ type: "connector_connected" });
98
+ connector.on("connected", () => {
99
+ applyPresenceTransition({ type: "connector_connected" });
100
+ });
101
+ connector.on("disconnected", () => {
102
+ applyPresenceTransition({ type: "connector_disconnected" });
103
+ });
31
104
  const channels = new Map();
32
105
  const wsEndpoints = new WeakMap();
33
106
  const displacedWs = new WeakSet();
34
107
  const emitTransition = (endpoint_id, state, reason) => {
35
- void apiPort.transitionState(endpoint_id, {
108
+ const params = {
36
109
  state,
37
110
  reason,
38
111
  ts: new Date().toISOString(),
112
+ };
113
+ if (networkPresence === "online") {
114
+ void apiPort.transitionState(endpoint_id, params);
115
+ return;
116
+ }
117
+ const accepted = retryQueue.enqueue(endpoint_id, params);
118
+ logger.info("transition_state_deferred", {
119
+ endpoint_id,
120
+ state,
121
+ reason,
122
+ presence: networkPresence,
123
+ queued: accepted,
124
+ retry_queue_size: retryQueue.size(),
39
125
  });
40
126
  };
41
127
  const handleTransition = (endpoint_id, event) => {
@@ -153,6 +239,84 @@ export async function startBrokerDaemon(opts) {
153
239
  });
154
240
  return { ok: true };
155
241
  });
242
+ controlEventDispatcher.on("spawn_request", async (event) => {
243
+ if (!spawnRateLimiter.tryConsume()) {
244
+ return {
245
+ ok: false,
246
+ detail: "spawn_rate_limited",
247
+ };
248
+ }
249
+ if (!spawnDriverRegistry) {
250
+ return {
251
+ ok: false,
252
+ detail: "spawn_driver_registry_not_configured",
253
+ };
254
+ }
255
+ const driver = spawnDriverRegistry.lookup(event.runtime_kind);
256
+ if (!driver) {
257
+ return {
258
+ ok: false,
259
+ detail: `runtime_kind_unsupported: ${event.runtime_kind}`,
260
+ };
261
+ }
262
+ const avail = await driver.isAvailable();
263
+ if (!avail.available) {
264
+ return {
265
+ ok: false,
266
+ detail: avail.reason ?? "driver_unavailable",
267
+ };
268
+ }
269
+ const cmd = driver.buildSpawnCommand({
270
+ prompt: event.prompt,
271
+ spawn_token: event.idempotency_key,
272
+ });
273
+ let child;
274
+ try {
275
+ child = spawnFn(cmd.binary, cmd.args, {
276
+ detached: true,
277
+ stdio: "ignore",
278
+ windowsHide: true,
279
+ env: { ...process.env, ...(cmd.env ?? {}) },
280
+ cwd: cmd.cwd,
281
+ });
282
+ child.unref();
283
+ }
284
+ catch (err) {
285
+ return {
286
+ ok: false,
287
+ detail: `spawn_failed: ${err instanceof Error ? err.message : String(err)}`,
288
+ };
289
+ }
290
+ spawnCorrelation.track(event.idempotency_key, () => {
291
+ logger.warn("spawn_correlation_timeout", {
292
+ spawn_token: event.idempotency_key,
293
+ runtime_kind: event.runtime_kind,
294
+ });
295
+ void postControlAck({
296
+ idempotency_key: event.idempotency_key,
297
+ status: "failed",
298
+ detail: "spawn_correlation_timeout",
299
+ });
300
+ });
301
+ logger.info("spawn_request_initiated", {
302
+ spawn_token: event.idempotency_key,
303
+ runtime_kind: event.runtime_kind,
304
+ pid: child.pid,
305
+ });
306
+ return { ok: true, ack_hint: "received" };
307
+ });
308
+ controlEventDispatcher.on("force_unregister", async (event) => {
309
+ const entry = registry.get(event.endpoint_id);
310
+ if (!entry) {
311
+ return { ok: true };
312
+ }
313
+ logger.info("force_unregister_received", {
314
+ endpoint_id: event.endpoint_id,
315
+ reason: event.reason,
316
+ });
317
+ handleTransition(event.endpoint_id, { type: "explicit_unregister" });
318
+ return { ok: true };
319
+ });
156
320
  const handlers = {
157
321
  async registerEndpoint(body, _ipcWs) {
158
322
  const boundWs = channels.get(body.plugin_pid);
@@ -191,6 +355,25 @@ export async function startBrokerDaemon(opts) {
191
355
  }
192
356
  bound.add(apiResp.endpoint_id);
193
357
  emitTransition(apiResp.endpoint_id, "active", "register");
358
+ if (typeof body.spawn_token === "string") {
359
+ const pending = spawnCorrelation.consume(body.spawn_token);
360
+ if (pending) {
361
+ logger.info("endpoint_spawned_correlation_resolved", {
362
+ endpoint_id: apiResp.endpoint_id,
363
+ spawn_token: body.spawn_token,
364
+ });
365
+ void postControlAck({
366
+ idempotency_key: pending.spawn_token,
367
+ status: "applied",
368
+ });
369
+ }
370
+ else {
371
+ logger.warn("endpoint_register_unknown_spawn_token", {
372
+ endpoint_id: apiResp.endpoint_id,
373
+ spawn_token: body.spawn_token,
374
+ });
375
+ }
376
+ }
194
377
  logger.info("endpoint_registered", {
195
378
  endpoint_id: apiResp.endpoint_id,
196
379
  plugin_pid: body.plugin_pid,
@@ -291,6 +474,7 @@ export async function startBrokerDaemon(opts) {
291
474
  type: "dispatch_undispatched",
292
475
  version: 1,
293
476
  idempotency_key: `local-${randomUUID()}`,
477
+ emitted_at: Date.now(),
294
478
  undispatched_id,
295
479
  target_endpoint_id,
296
480
  });
@@ -298,6 +482,27 @@ export async function startBrokerDaemon(opts) {
298
482
  throw new BrokerHttpError(400, "dispatch_failed", result.detail ?? "unknown");
299
483
  }
300
484
  },
485
+ async setTaskHint(body) {
486
+ const result = updateTaskHint({ registry }, {
487
+ endpoint_id: body.endpoint_id,
488
+ plugin_pid: body.plugin_pid,
489
+ task_hint: body.task_hint,
490
+ });
491
+ if (!result.ok) {
492
+ const status = result.code === "endpoint_unknown"
493
+ ? 404
494
+ : result.code === "ownership_mismatch"
495
+ ? 403
496
+ : 400;
497
+ throw new BrokerHttpError(status, result.code, result.message);
498
+ }
499
+ logger.info("task_hint_updated", {
500
+ endpoint_id: body.endpoint_id,
501
+ truncated: result.truncated,
502
+ length: result.effective.length,
503
+ max: TASK_HINT_MAX_LENGTH,
504
+ });
505
+ },
301
506
  };
302
507
  const bearerToken = mintBearerToken();
303
508
  const ipc = await startIPCServer({
@@ -421,13 +626,46 @@ export async function startBrokerDaemon(opts) {
421
626
  bearerToken,
422
627
  });
423
628
  logger.info("discovery_written", { path: paths.discoveryFile });
629
+ let servicesEventClient;
630
+ if (servicesEventClientFactory) {
631
+ servicesEventClient = servicesEventClientFactory({
632
+ dispatcher: controlEventDispatcher,
633
+ logger,
634
+ });
635
+ try {
636
+ await servicesEventClient.start();
637
+ }
638
+ catch (err) {
639
+ logger.warn("services_event_client_start_failed", {
640
+ err: err instanceof Error ? err.message : String(err),
641
+ });
642
+ }
643
+ }
424
644
  let shutdownStarted = false;
425
645
  const shutdown = async (reason) => {
426
646
  if (shutdownStarted)
427
647
  return;
428
648
  shutdownStarted = true;
429
649
  logger.info("shutdown_begin", { reason });
650
+ if (servicesEventClient) {
651
+ try {
652
+ await servicesEventClient.stop();
653
+ }
654
+ catch {
655
+ }
656
+ }
430
657
  graceTimers.cancelAll();
658
+ spawnCorrelation.cancelAll();
659
+ if (presenceGraceTimer) {
660
+ clearTimeout(presenceGraceTimer);
661
+ presenceGraceTimer = undefined;
662
+ }
663
+ if (retryQueue.size() > 0) {
664
+ logger.warn("retry_queue_dropped_on_shutdown", {
665
+ size: retryQueue.size(),
666
+ reason,
667
+ });
668
+ }
431
669
  const endpoints = registry.list();
432
670
  await Promise.all(endpoints.map(async (entry) => {
433
671
  try {
@@ -459,6 +697,9 @@ export async function startBrokerDaemon(opts) {
459
697
  state: (endpoint_id) => registry.get(endpoint_id)?.state,
460
698
  undispatchedInbox,
461
699
  controlEventDispatcher,
700
+ networkPresence: () => networkPresence,
701
+ pendingSpawnCount: () => spawnCorrelation.size(),
702
+ retryQueueSize: () => retryQueue.size(),
462
703
  };
463
704
  }
464
705
  export function readPluginPidHeader(req) {
@@ -0,0 +1,14 @@
1
+ import type { DriverAvailability, SpawnCommand, SpawnContext, SpawnDriver } from "./spawn-driver.js";
2
+ export interface ClaudeCodeSpawnDriverOptions {
3
+ binary?: string;
4
+ isOnPath?: (binary: string) => boolean;
5
+ }
6
+ export declare class ClaudeCodeSpawnDriver implements SpawnDriver {
7
+ readonly runtime_kind = "claude-code";
8
+ private readonly binary;
9
+ private readonly isOnPath;
10
+ constructor(opts?: ClaudeCodeSpawnDriverOptions);
11
+ buildSpawnCommand(ctx: SpawnContext): SpawnCommand;
12
+ isAvailable(): Promise<DriverAvailability>;
13
+ }
14
+ //# sourceMappingURL=claude-code-spawn-driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code-spawn-driver.d.ts","sourceRoot":"","sources":["../../src/broker/claude-code-spawn-driver.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACZ,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAG3B,MAAM,WAAW,4BAA4B;IAE3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAMhB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;CACxC;AAED,qBAAa,qBAAsB,YAAW,WAAW;IACvD,QAAQ,CAAC,YAAY,iBAAiB;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8B;gBAE3C,IAAI,GAAE,4BAAiC;IAKnD,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,YAAY;IAS5C,WAAW,IAAI,OAAO,CAAC,kBAAkB,CAAC;CASjD"}
@@ -0,0 +1,39 @@
1
+ import { execFileSync } from "node:child_process";
2
+ export class ClaudeCodeSpawnDriver {
3
+ runtime_kind = "claude-code";
4
+ binary;
5
+ isOnPath;
6
+ constructor(opts = {}) {
7
+ this.binary = opts.binary ?? "claude";
8
+ this.isOnPath = opts.isOnPath ?? defaultIsOnPath;
9
+ }
10
+ buildSpawnCommand(ctx) {
11
+ return {
12
+ binary: this.binary,
13
+ args: ["--bg", ctx.prompt],
14
+ env: { MASONS_SPAWN_TOKEN: ctx.spawn_token },
15
+ cwd: ctx.cwd,
16
+ };
17
+ }
18
+ async isAvailable() {
19
+ if (this.isOnPath(this.binary)) {
20
+ return { available: true };
21
+ }
22
+ return {
23
+ available: false,
24
+ reason: `\`${this.binary}\` CLI not found in PATH. Install Claude Code or check $PATH.`,
25
+ };
26
+ }
27
+ }
28
+ function defaultIsOnPath(binary) {
29
+ if (!/^[a-zA-Z0-9._-]+$/.test(binary))
30
+ return false;
31
+ const probe = process.platform === "win32" ? "where" : "which";
32
+ try {
33
+ execFileSync(probe, [binary], { stdio: "ignore" });
34
+ return true;
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
@@ -0,0 +1,7 @@
1
+ import type { DriverAvailability, SpawnCommand, SpawnContext, SpawnDriver } from "./spawn-driver.js";
2
+ export declare class CodexSpawnDriverStub implements SpawnDriver {
3
+ readonly runtime_kind = "codex";
4
+ buildSpawnCommand(_ctx: SpawnContext): SpawnCommand;
5
+ isAvailable(): Promise<DriverAvailability>;
6
+ }
7
+ //# sourceMappingURL=codex-spawn-driver-stub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-spawn-driver-stub.d.ts","sourceRoot":"","sources":["../../src/broker/codex-spawn-driver-stub.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACZ,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAE3B,qBAAa,oBAAqB,YAAW,WAAW;IACtD,QAAQ,CAAC,YAAY,WAAW;IAEhC,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY;IAW7C,WAAW,IAAI,OAAO,CAAC,kBAAkB,CAAC;CAYjD"}
@@ -0,0 +1,13 @@
1
+ export class CodexSpawnDriverStub {
2
+ runtime_kind = "codex";
3
+ buildSpawnCommand(_ctx) {
4
+ throw new Error("CodexSpawnDriverStub.buildSpawnCommand called — broker logic bug; " +
5
+ "isAvailable() should have short-circuited the spawn before this point");
6
+ }
7
+ async isAvailable() {
8
+ return {
9
+ available: false,
10
+ reason: "codex driver not registered on this broker — Codex distribution will ship a SpawnDriver in a future release (broker restart required after install)",
11
+ };
12
+ }
13
+ }
@@ -2,6 +2,7 @@ import type { ControlEvent } from "./control-event-types.js";
2
2
  export declare const DEFAULT_IDEMPOTENCY_RING_SIZE = 1024;
3
3
  export type ControlEventOutcome = {
4
4
  ok: true;
5
+ ack_hint?: "received";
5
6
  } | {
6
7
  ok: false;
7
8
  detail: string;
@@ -1 +1 @@
1
- {"version":3,"file":"control-event-dispatcher.d.ts","sourceRoot":"","sources":["../../src/broker/control-event-dispatcher.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7D,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAElD,MAAM,MAAM,mBAAmB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/E,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,IAAI,CACvE,KAAK,EAAE,CAAC,KACL,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAElC,MAAM,WAAW,sBAAsB;IAErC,EAAE,CAAC,CAAC,SAAS,YAAY,EACvB,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EACf,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAC9B,IAAI,CAAC;IAKR,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAE5D,QAAQ,IAAI,MAAM,CAAC;IAEnB,OAAO,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3C;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,GAAE,6BAAkC,GACvC,sBAAsB,CAqDxB"}
1
+ {"version":3,"file":"control-event-dispatcher.d.ts","sourceRoot":"","sources":["../../src/broker/control-event-dispatcher.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7D,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAYlD,MAAM,MAAM,mBAAmB,GAC3B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,UAAU,CAAA;CAAE,GACnC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,IAAI,CACvE,KAAK,EAAE,CAAC,KACL,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAElC,MAAM,WAAW,sBAAsB;IAErC,EAAE,CAAC,CAAC,SAAS,YAAY,EACvB,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,EACf,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAC9B,IAAI,CAAC;IAKR,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAE5D,QAAQ,IAAI,MAAM,CAAC;IAEnB,OAAO,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3C;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,4BAA4B,CAC1C,IAAI,GAAE,6BAAkC,GACvC,sBAAsB,CAqDxB"}
@@ -1,30 +1,28 @@
1
- export type ControlEvent = DispatchUndispatchedEvent | SpawnRequestEvent | ForceUnregisterEvent;
2
- export interface DispatchUndispatchedEvent {
3
- type: "dispatch_undispatched";
1
+ export interface ControlEventBase {
4
2
  version: 1;
5
3
  idempotency_key: string;
4
+ emitted_at: number;
5
+ }
6
+ export type ControlEvent = DispatchUndispatchedEvent | SpawnRequestEvent | ForceUnregisterEvent;
7
+ export interface DispatchUndispatchedEvent extends ControlEventBase {
8
+ type: "dispatch_undispatched";
6
9
  undispatched_id: string;
7
10
  target_endpoint_id: string;
8
11
  }
9
- export interface SpawnRequestEvent {
12
+ export interface SpawnRequestEvent extends ControlEventBase {
10
13
  type: "spawn_request";
11
- version: 1;
12
- idempotency_key: string;
13
- request_id: string;
14
14
  prompt: string;
15
- runtime_kind: string;
15
+ runtime_kind: "claude-code" | "openclaw" | "codex" | "custom";
16
16
  origin: "passport-spawn";
17
17
  }
18
- export interface ForceUnregisterEvent {
18
+ export interface ForceUnregisterEvent extends ControlEventBase {
19
19
  type: "force_unregister";
20
- version: 1;
21
- idempotency_key: string;
22
20
  endpoint_id: string;
23
21
  reason: string;
24
22
  }
25
23
  export interface ControlAck {
26
- request_id: string;
27
- status: "ok" | "error";
24
+ idempotency_key: string;
25
+ status: "received" | "applied" | "failed";
28
26
  detail?: string;
29
27
  }
30
28
  //# sourceMappingURL=control-event-types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"control-event-types.d.ts","sourceRoot":"","sources":["../../src/broker/control-event-types.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,YAAY,GACpB,yBAAyB,GACzB,iBAAiB,GACjB,oBAAoB,CAAC;AAGzB,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,uBAAuB,CAAC;IAC9B,OAAO,EAAE,CAAC,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAGD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,CAAC,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAGD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,CAAC,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAGD,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
1
+ {"version":3,"file":"control-event-types.d.ts","sourceRoot":"","sources":["../../src/broker/control-event-types.ts"],"names":[],"mappings":"AAuCA,MAAM,WAAW,gBAAgB;IAI/B,OAAO,EAAE,CAAC,CAAC;IAWX,eAAe,EAAE,MAAM,CAAC;IAKxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAGD,MAAM,MAAM,YAAY,GACpB,yBAAyB,GACzB,iBAAiB,GACjB,oBAAoB,CAAC;AAGzB,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IACjE,IAAI,EAAE,uBAAuB,CAAC;IAE9B,eAAe,EAAE,MAAM,CAAC;IAExB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAGD,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IACzD,IAAI,EAAE,eAAe,CAAC;IAEtB,MAAM,EAAE,MAAM,CAAC;IAMf,YAAY,EAAE,aAAa,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;IAG9D,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAGD,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAC5D,IAAI,EAAE,kBAAkB,CAAC;IAEzB,WAAW,EAAE,MAAM,CAAC;IAEpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAwBD,MAAM,WAAW,UAAU;IAEzB,eAAe,EAAE,MAAM,CAAC;IAExB,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;IAE1C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
@@ -1,5 +1,13 @@
1
1
  import { type BrokerLogger } from "./logger.js";
2
2
  import type { RuntimeEndpointPort } from "./runtime-endpoint-port.js";
3
+ interface ResolvedCredentials {
4
+ accountId: string;
5
+ connectorUrl: string;
6
+ token: string;
7
+ apiHost: string;
8
+ }
9
+ export declare function resolveCredentialsFromFile(filePath: string, accountId: string, envApiHost: string | undefined): Promise<ResolvedCredentials>;
3
10
  export declare function buildApiPort(apiHost: string, runtimeKey: string, logger: BrokerLogger): RuntimeEndpointPort;
4
11
  export declare function main(): Promise<void>;
12
+ export {};
5
13
  //# sourceMappingURL=entry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/broker/entry.ts"],"names":[],"mappings":"AAiCA,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,aAAa,CAAC;AAEpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAsDtE,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,YAAY,GACnB,mBAAmB,CAkErB;AAYD,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAgD1C"}
1
+ {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/broker/entry.ts"],"names":[],"mappings":"AA6EA,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,aAAa,CAAC;AAEpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAMtE,UAAU,mBAAmB;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAUD,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CA8F9B;AAyDD,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,YAAY,GACnB,mBAAmB,CAkErB;AAYD,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CA8F1C"}