@dougbots/avenor-core 0.11.0 → 0.12.1

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.
package/dist/index.d.ts CHANGED
@@ -140,6 +140,15 @@ interface WorkflowHeartbeatParams {
140
140
  lease_id: string;
141
141
  owner_token: string;
142
142
  }
143
+ interface WorkflowControllerCreateParams {
144
+ controller_id: string;
145
+ max_inflight: number;
146
+ }
147
+ declare class RpcError extends Error {
148
+ readonly code: number;
149
+ readonly data?: unknown;
150
+ constructor(code: number, message: string, data?: unknown);
151
+ }
143
152
  declare class Client {
144
153
  private socket;
145
154
  private rl;
@@ -159,7 +168,9 @@ declare class Client {
159
168
  });
160
169
  private startReadLoop;
161
170
  private pushEvent;
162
- call(method: string, params?: unknown): Promise<unknown>;
171
+ call(method: string, params?: unknown, opts?: {
172
+ timeoutMs?: number;
173
+ }): Promise<unknown>;
163
174
  subscribe(options?: SubscribeOptions): Promise<void>;
164
175
  events(options?: SubscribeOptions): AsyncIterableIterator<Event>;
165
176
  history(options: HistoryOptions): Promise<HistoryResult>;
@@ -183,8 +194,14 @@ declare class Client {
183
194
  workflowComplete(params: WorkflowCompleteParams): Promise<Record<string, unknown>>;
184
195
  workflowGate(params: WorkflowGateParams): Promise<Record<string, unknown>>;
185
196
  workflowHeartbeat(params: WorkflowHeartbeatParams): Promise<Record<string, unknown>>;
197
+ workflowControllerStatus(controllerId: string): Promise<Record<string, unknown>>;
198
+ workflowReady(controllerId: string, limit?: number): Promise<Record<string, unknown>>;
199
+ workflowControllerList(): Promise<Record<string, unknown>>;
200
+ workflowControllerCreate(params: WorkflowControllerCreateParams): Promise<Record<string, unknown>>;
201
+ workflowControllerEnable(controllerId: string): Promise<Record<string, unknown>>;
202
+ workflowControllerDisable(controllerId: string, reason: string): Promise<Record<string, unknown>>;
186
203
  brokerSend(fromRunId: string, toRunId: string, message: string, role?: string): Promise<void>;
187
- brokerAsk(toRunId: string, message: string, role?: string): Promise<Record<string, unknown>>;
204
+ brokerAsk(toRunId: string, message: string, role?: string, timeoutMs?: number): Promise<Record<string, unknown>>;
188
205
  brokerPeers(): Promise<Array<Record<string, unknown>>>;
189
206
  brokerCancel(messageId: string): Promise<void>;
190
207
  brokerReceive(): Promise<Array<{
@@ -414,8 +431,16 @@ declare class Supervisor {
414
431
  getRunByReference(reference: string): RunInfo | undefined;
415
432
  /** Match a stored run by its broker runtime ID. */
416
433
  getRunByRuntimeId(runtimeId: string): RunInfo | undefined;
434
+ /**
435
+ * The live singleton when it owns supervisorId, null otherwise. Single
436
+ * source of truth for the ownership check, so callers cannot observe
437
+ * isCurrentInstance true while currentInstance is null.
438
+ */
439
+ static getInstance(supervisorId: string): Supervisor | null;
417
440
  static isCurrentInstance(supervisorId: string): boolean;
418
441
  static currentInstance(): Supervisor | null;
442
+ /** Iterate stored runs without exposing the internal map. */
443
+ forEachRun(callback: (info: RunInfo) => void): void;
419
444
  getClient(): Client;
420
445
  spawn(params: SpawnParams, runId?: `${string}-${string}-${string}-${string}-${string}`, metadata?: SpawnMetadata): Promise<RunInfo>;
421
446
  close(options?: {
@@ -481,6 +506,12 @@ interface StatusToolArgs {
481
506
  supervisorId?: string;
482
507
  view?: StatusView;
483
508
  }
509
+ /** List form of the status tool result (no runId). structuredContent must be
510
+ * an object, so the runs array is wrapped with a count. */
511
+ interface StatusListResult {
512
+ runs: StatusResult[];
513
+ count: number;
514
+ }
484
515
  interface StatusResult {
485
516
  run_id: string;
486
517
  label: string;
@@ -519,7 +550,7 @@ interface StatusResult {
519
550
  final_output?: string;
520
551
  final_output_truncated?: boolean;
521
552
  }
522
- declare const statusTool: (args: StatusToolArgs) => Promise<StatusResult | StatusResult[]>;
553
+ declare const statusTool: (args: StatusToolArgs) => Promise<StatusResult | StatusListResult>;
523
554
  //#endregion
524
555
  //#region src/tools/events.d.ts
525
556
  interface EventsToolArgs {
@@ -623,6 +654,7 @@ interface AskToolArgs {
623
654
  toRunId: string;
624
655
  message: string;
625
656
  supervisorId?: string;
657
+ timeoutMs?: number;
626
658
  }
627
659
  interface AskResult {
628
660
  reply: string;
@@ -742,4 +774,23 @@ type WorkflowGateResult = Record<string, unknown>;
742
774
  declare function createWorkflowGateTool(getSupervisorClient: GetSupervisorClient): (args: WorkflowGateToolArgs) => Promise<WorkflowGateResult>;
743
775
  declare const workflowGateTool: (args: WorkflowGateToolArgs) => Promise<WorkflowGateResult>;
744
776
  //#endregion
745
- export { type AskResult, type AskToolArgs, type BrokerReceiveArgs, type BrokerReceiveResult, type BrokerReplyArgs, type BrokerReplyResult, type CancelResult, type CancelToolArgs, type Client, DEFAULT_RUN_REDUCER_LIMITS, type Event, type ExecutionIdentity, type FollowUpToolArgs, type FollowUpToolResult, type HistoryOptions, type HistoryResult, type InboundAsk, type InspectResult, type NormalizedRunEvent, type ObserveRunOptions, type PeerEntry, type PeersResult, type ResultResult, type ResultToolArgs, type RunChunkRole, type RunIdentity, type RunInfo, type RunObserver, type RunObserverClient, type RunPermissionOption, type RunPermissionSummary, RunReducer, type RunReducerLimits, type RunSnapshot, type RunSnapshotSeed, type RunToolState, type RunToolSummary, type RunTranscriptEntry, type SpawnMetadata, type SpawnParams, type SpawnResult, type SpawnSelectionInput, type SpawnToolArgs, type SpawnToolResult, type StatusResult, type StatusToolArgs, type StatusView, type SubscribeOptions, Supervisor, type SupervisorOptions, THINKING_LEVELS, type ThinkingLevel, type ThinkingOutcome, type WorkflowCompleteParams, type WorkflowCompleteResult, type WorkflowCompleteToolArgs, type WorkflowEventsResult, type WorkflowEventsToolArgs, type WorkflowGateOperation, type WorkflowGateParams, type WorkflowGateResult, type WorkflowGateToolArgs, type WorkflowHeartbeatParams, type WorkflowInspectResult, type WorkflowInspectToolArgs, type WorkflowStatusResult, type WorkflowStatusToolArgs, type WorkflowWaitResult, type WorkflowWaitToolArgs, answerPermissionTool, askTool, brokerReceiveTool, brokerReplyTool, cancelTool, createRunSnapshot, createWorkflowCompleteTool, createWorkflowEventsTool, createWorkflowGateTool, createWorkflowInspectTool, createWorkflowStatusTool, createWorkflowWaitTool, dial, evaluateThinkingPolicy, eventsTool, extractEventText, findAvenorBinary, followUpTool, getInstallDir, getVersion, inspectTool, installerBinaryPath, isThinkingLevel, normalizeRunEvent, observeRun, peersTool, reduceRunSnapshot, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool, validateSpawnSelection, validateThinking, validateThinkingForBackend, validateThinkingForBackendResume, workflowCompleteTool, workflowEventsTool, workflowGateTool, workflowInspectTool, workflowStatusTool, workflowWaitTool };
777
+ //#region src/tools/workflow-controller-status.d.ts
778
+ interface WorkflowControllerStatusToolArgs {
779
+ controllerId?: string;
780
+ supervisorId?: string;
781
+ }
782
+ type WorkflowControllerStatusResult = Record<string, unknown>;
783
+ declare function createWorkflowControllerStatusTool(getSupervisorClient: GetSupervisorClient): (args: WorkflowControllerStatusToolArgs) => Promise<WorkflowControllerStatusResult>;
784
+ declare const workflowControllerStatusTool: (args: WorkflowControllerStatusToolArgs) => Promise<WorkflowControllerStatusResult>;
785
+ //#endregion
786
+ //#region src/tools/workflow-ready.d.ts
787
+ interface WorkflowReadyToolArgs {
788
+ controllerId: string;
789
+ limit?: number;
790
+ supervisorId?: string;
791
+ }
792
+ type WorkflowReadyResult = Record<string, unknown>;
793
+ declare function createWorkflowReadyTool(getSupervisorClient: GetSupervisorClient): (args: WorkflowReadyToolArgs) => Promise<WorkflowReadyResult>;
794
+ declare const workflowReadyTool: (args: WorkflowReadyToolArgs) => Promise<WorkflowReadyResult>;
795
+ //#endregion
796
+ export { type AskResult, type AskToolArgs, type BrokerReceiveArgs, type BrokerReceiveResult, type BrokerReplyArgs, type BrokerReplyResult, type CancelResult, type CancelToolArgs, type Client, DEFAULT_RUN_REDUCER_LIMITS, type Event, type ExecutionIdentity, type FollowUpToolArgs, type FollowUpToolResult, type HistoryOptions, type HistoryResult, type InboundAsk, type InspectResult, type NormalizedRunEvent, type ObserveRunOptions, type PeerEntry, type PeersResult, type ResultResult, type ResultToolArgs, RpcError, type RunChunkRole, type RunIdentity, type RunInfo, type RunObserver, type RunObserverClient, type RunPermissionOption, type RunPermissionSummary, RunReducer, type RunReducerLimits, type RunSnapshot, type RunSnapshotSeed, type RunToolState, type RunToolSummary, type RunTranscriptEntry, type SpawnMetadata, type SpawnParams, type SpawnResult, type SpawnSelectionInput, type SpawnToolArgs, type SpawnToolResult, type StatusListResult, type StatusResult, type StatusToolArgs, type StatusView, type SubscribeOptions, Supervisor, type SupervisorOptions, THINKING_LEVELS, type ThinkingLevel, type ThinkingOutcome, type WorkflowCompleteParams, type WorkflowCompleteResult, type WorkflowCompleteToolArgs, type WorkflowControllerCreateParams, type WorkflowControllerStatusResult, type WorkflowControllerStatusToolArgs, type WorkflowEventsResult, type WorkflowEventsToolArgs, type WorkflowGateOperation, type WorkflowGateParams, type WorkflowGateResult, type WorkflowGateToolArgs, type WorkflowHeartbeatParams, type WorkflowInspectResult, type WorkflowInspectToolArgs, type WorkflowReadyResult, type WorkflowReadyToolArgs, type WorkflowStatusResult, type WorkflowStatusToolArgs, type WorkflowWaitResult, type WorkflowWaitToolArgs, answerPermissionTool, askTool, brokerReceiveTool, brokerReplyTool, cancelTool, createRunSnapshot, createWorkflowCompleteTool, createWorkflowControllerStatusTool, createWorkflowEventsTool, createWorkflowGateTool, createWorkflowInspectTool, createWorkflowReadyTool, createWorkflowStatusTool, createWorkflowWaitTool, dial, evaluateThinkingPolicy, eventsTool, extractEventText, findAvenorBinary, followUpTool, getInstallDir, getVersion, inspectTool, installerBinaryPath, isThinkingLevel, normalizeRunEvent, observeRun, peersTool, reduceRunSnapshot, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool, validateSpawnSelection, validateThinking, validateThinkingForBackend, validateThinkingForBackendResume, workflowCompleteTool, workflowControllerStatusTool, workflowEventsTool, workflowGateTool, workflowInspectTool, workflowReadyTool, workflowStatusTool, workflowWaitTool };
package/dist/index.js CHANGED
@@ -25,6 +25,16 @@ function removeResolver(resolvers, target) {
25
25
  const index = resolvers.indexOf(target);
26
26
  if (index >= 0) resolvers.splice(index, 1);
27
27
  }
28
+ var RpcError = class extends Error {
29
+ code;
30
+ data;
31
+ constructor(code, message, data) {
32
+ super(`rpc error [${code}]: ${message}`);
33
+ this.name = "RpcError";
34
+ this.code = code;
35
+ this.data = data;
36
+ }
37
+ };
28
38
  var Client = class {
29
39
  socket;
30
40
  rl;
@@ -73,7 +83,7 @@ var Client = class {
73
83
  if (pc) {
74
84
  clearTimeout(pc.timer);
75
85
  this.pending.delete(id);
76
- if (parsed.error) pc.reject(/* @__PURE__ */ new Error(`rpc error [${parsed.error.code}]: ${parsed.error.message}`));
86
+ if (parsed.error) pc.reject(new RpcError(parsed.error.code, parsed.error.message, parsed.error.data));
77
87
  else pc.resolve(parsed.result);
78
88
  }
79
89
  return;
@@ -123,7 +133,7 @@ var Client = class {
123
133
  if (this.eventQueue.length < 256) this.eventQueue.push(ev);
124
134
  else this.dropped++;
125
135
  }
126
- async call(method, params) {
136
+ async call(method, params, opts) {
127
137
  if (this.isClosed()) throw new Error("control socket is closed");
128
138
  this.startReadLoop();
129
139
  const id = ++this.nextID;
@@ -134,11 +144,14 @@ var Client = class {
134
144
  };
135
145
  if (params !== void 0) req.params = params;
136
146
  const data = JSON.stringify(req) + "\n";
147
+ const MAX_TIMER_MS = 2147483647;
148
+ const requested = opts?.timeoutMs !== void 0 ? !Number.isFinite(opts.timeoutMs) || opts.timeoutMs > MAX_TIMER_MS - 1e4 ? MAX_TIMER_MS : Math.max(this.callTimeout, opts.timeoutMs + 1e4) : this.callTimeout;
149
+ const timeout = Number.isFinite(requested) ? Math.min(requested, MAX_TIMER_MS) : MAX_TIMER_MS;
137
150
  return new Promise((resolve, reject) => {
138
151
  const timer = setTimeout(() => {
139
152
  this.pending.delete(id);
140
153
  reject(/* @__PURE__ */ new Error("read response: timeout"));
141
- }, this.callTimeout);
154
+ }, timeout);
142
155
  this.pending.set(id, {
143
156
  resolve,
144
157
  reject,
@@ -352,6 +365,32 @@ var Client = class {
352
365
  if (params.activation_id !== void 0) command.activation_id = params.activation_id;
353
366
  return this.workflowCommand(params.workflow_id, command);
354
367
  }
368
+ async workflowControllerStatus(controllerId) {
369
+ return this.call("workflow.controller.status", { controller_id: controllerId });
370
+ }
371
+ async workflowReady(controllerId, limit) {
372
+ const params = { controller_id: controllerId };
373
+ if (limit !== void 0) params.limit = limit;
374
+ return this.call("workflow.ready", params);
375
+ }
376
+ async workflowControllerList() {
377
+ return this.call("workflow.controller.list");
378
+ }
379
+ async workflowControllerCreate(params) {
380
+ return this.call("workflow.controller.create", {
381
+ controller_id: params.controller_id,
382
+ max_inflight: params.max_inflight
383
+ });
384
+ }
385
+ async workflowControllerEnable(controllerId) {
386
+ return this.call("workflow.controller.enable", { controller_id: controllerId });
387
+ }
388
+ async workflowControllerDisable(controllerId, reason) {
389
+ return this.call("workflow.controller.disable", {
390
+ controller_id: controllerId,
391
+ reason
392
+ });
393
+ }
355
394
  async brokerSend(fromRunId, toRunId, message, role) {
356
395
  await this.call("broker_send", {
357
396
  from_run_id: fromRunId,
@@ -360,12 +399,14 @@ var Client = class {
360
399
  role: role ?? "agent"
361
400
  });
362
401
  }
363
- async brokerAsk(toRunId, message, role) {
364
- return await this.call("broker_ask", {
402
+ async brokerAsk(toRunId, message, role, timeoutMs) {
403
+ const params = {
365
404
  to_run_id: toRunId,
366
405
  message,
367
406
  role: role ?? "agent"
368
- });
407
+ };
408
+ if (timeoutMs !== void 0) params.timeout_ms = timeoutMs;
409
+ return await this.call("broker_ask", params, timeoutMs !== void 0 ? { timeoutMs } : void 0);
369
410
  }
370
411
  async brokerPeers() {
371
412
  return (await this.call("broker_peers")).sessions ?? [];
@@ -1508,12 +1549,24 @@ var Supervisor = class Supervisor {
1508
1549
  if (!runtimeId) return void 0;
1509
1550
  for (const runInfo of this.runs.values()) if (runInfo.runtimeId === runtimeId) return runInfo;
1510
1551
  }
1552
+ /**
1553
+ * The live singleton when it owns supervisorId, null otherwise. Single
1554
+ * source of truth for the ownership check, so callers cannot observe
1555
+ * isCurrentInstance true while currentInstance is null.
1556
+ */
1557
+ static getInstance(supervisorId) {
1558
+ return Supervisor.instance !== null && Supervisor.instance.supervisorId === supervisorId ? Supervisor.instance : null;
1559
+ }
1511
1560
  static isCurrentInstance(supervisorId) {
1512
- return Supervisor.instance !== null && Supervisor.instance.supervisorId === supervisorId;
1561
+ return Supervisor.getInstance(supervisorId) !== null;
1513
1562
  }
1514
1563
  static currentInstance() {
1515
1564
  return Supervisor.instance;
1516
1565
  }
1566
+ /** Iterate stored runs without exposing the internal map. */
1567
+ forEachRun(callback) {
1568
+ for (const runInfo of this.runs.values()) callback(runInfo);
1569
+ }
1517
1570
  getClient() {
1518
1571
  if (this.crashed) {
1519
1572
  const state = this.crashCode === 0 ? "exited" : "crashed";
@@ -1663,8 +1716,8 @@ async function getSupervisorClient(supervisorId) {
1663
1716
  };
1664
1717
  }
1665
1718
  const id = validateSupervisorSocketPath(supervisorId);
1666
- const current = Supervisor.currentInstance();
1667
- if (current !== null && validateSupervisorSocketPath(current.supervisorId) === id) return {
1719
+ const current = Supervisor.getInstance(id);
1720
+ if (current !== null) return {
1668
1721
  client: current.getClient(),
1669
1722
  isSingleton: true,
1670
1723
  sup: current,
@@ -2345,7 +2398,10 @@ async function executeStatusTool(args, getSupervisorClient) {
2345
2398
  session_id: entry.session_id
2346
2399
  }, translateStatus(rawStatusPhase(entry), null)), args.view));
2347
2400
  }
2348
- return results;
2401
+ return {
2402
+ runs: results,
2403
+ count: results.length
2404
+ };
2349
2405
  } finally {
2350
2406
  if (!isSingleton) client.close();
2351
2407
  }
@@ -2383,7 +2439,10 @@ async function executeStatusTool(args, getSupervisorClient) {
2383
2439
  session_id: entry.session_id
2384
2440
  }, translateStatus(rawStatusPhase(entry), null)), args.view));
2385
2441
  }
2386
- return results;
2442
+ return {
2443
+ runs: results,
2444
+ count: results.length
2445
+ };
2387
2446
  }
2388
2447
  function createStatusTool(getSupervisorClient) {
2389
2448
  return (args) => executeStatusTool(args, getSupervisorClient);
@@ -2811,6 +2870,15 @@ function rejectSessionConflict(sentinel, liveStatus) {
2811
2870
  const liveStopReason = value(liveStatus?.stop_reason);
2812
2871
  if (sentinelStopReason === "session_id_conflict" || liveStopReason === "session_id_conflict") throw new Error("run is not resumable: session_id_conflict");
2813
2872
  }
2873
+ const RESUMABLE_SENTINEL_STATUSES = new Set([
2874
+ "DONE",
2875
+ "FAILED",
2876
+ "BLOCKED"
2877
+ ]);
2878
+ function rejectNotResumableStatus(sentinel) {
2879
+ const status = (sentinel?._status ?? "").trim().toUpperCase();
2880
+ if (status !== "" && !RESUMABLE_SENTINEL_STATUSES.has(status)) throw new Error(`run is not resumable (status: ${status.toLowerCase()})`);
2881
+ }
2814
2882
  async function executeFollowUpTool(args, getSupervisorClient) {
2815
2883
  if (args.supervisorId) {
2816
2884
  const { client, isSingleton, sup, supervisorId } = await getSupervisorClient(args.supervisorId);
@@ -2826,6 +2894,7 @@ async function executeFollowUpTool(args, getSupervisorClient) {
2826
2894
  } catch {}
2827
2895
  const sentinel = await parseSentinel(liveStatus?.sentinel_file ?? runInfo?.sentinelPath ?? path.join(runsRoot(), args.runId, "sentinel.done"));
2828
2896
  rejectSessionConflict(sentinel, liveStatus);
2897
+ rejectNotResumableStatus(sentinel);
2829
2898
  const sessionId = sentinel?.SESSION ?? liveStatus?.session_id ?? fallbackSessionId;
2830
2899
  if (!sessionId) throw new Error("run has no session to resume");
2831
2900
  const agent = resolvedIdentity(liveStatus, "effective_agent", "agent", runInfo?.effectiveAgent, runInfo?.agent);
@@ -2904,6 +2973,7 @@ async function executeFollowUpTool(args, getSupervisorClient) {
2904
2973
  if (!runInfo) throw new Error(`run not found: ${args.runId}`);
2905
2974
  const sentinel = await parseSentinel(runInfo.sentinelPath);
2906
2975
  rejectSessionConflict(sentinel, null);
2976
+ rejectNotResumableStatus(sentinel);
2907
2977
  const sessionId = sentinel?.SESSION ?? runInfo.sessionId;
2908
2978
  if (!sessionId) throw new Error("run has no session to resume");
2909
2979
  const client = sup.getClient();
@@ -2956,21 +3026,23 @@ async function shutdownTool(args) {
2956
3026
  const cleanedUp = [];
2957
3027
  if (args.supervisorId) {
2958
3028
  const supervisorId = validateSupervisorSocketPath(args.supervisorId);
2959
- if (Supervisor.isCurrentInstance(supervisorId)) {
2960
- const sup = Supervisor.currentInstance();
2961
- if (!sup) throw new Error("supervisor not started");
3029
+ const sup = Supervisor.getInstance(supervisorId);
3030
+ if (sup) {
2962
3031
  await sup.getClient().shutdown(args.force ? "force" : "graceful");
2963
- const runs = sup.runs;
2964
- for (const info of runs.values()) {
2965
- try {
2966
- await fs.promises.unlink(info.sentinelPath);
2967
- cleanedUp.push(info.sentinelPath);
2968
- } catch {}
2969
- try {
2970
- await fs.promises.unlink(info.eventLogPath);
2971
- cleanedUp.push(info.eventLogPath);
2972
- } catch {}
2973
- }
3032
+ const cleanups = [];
3033
+ sup.forEachRun((info) => {
3034
+ cleanups.push((async () => {
3035
+ try {
3036
+ await fs.promises.unlink(info.sentinelPath);
3037
+ cleanedUp.push(info.sentinelPath);
3038
+ } catch {}
3039
+ try {
3040
+ await fs.promises.unlink(info.eventLogPath);
3041
+ cleanedUp.push(info.eventLogPath);
3042
+ } catch {}
3043
+ })());
3044
+ });
3045
+ await Promise.all(cleanups);
2974
3046
  await sup.close({ skipShutdown: true });
2975
3047
  } else {
2976
3048
  const client = await dial(supervisorId);
@@ -3004,8 +3076,7 @@ async function shutdownTool(args) {
3004
3076
  try {
3005
3077
  await sup.getClient().shutdown(args.force ? "force" : "graceful");
3006
3078
  } catch {}
3007
- const runs = sup.runs;
3008
- for (const info of runs.values()) {
3079
+ sup.forEachRun((info) => {
3009
3080
  try {
3010
3081
  fs.unlinkSync(info.sentinelPath);
3011
3082
  cleanedUp.push(info.sentinelPath);
@@ -3014,7 +3085,7 @@ async function shutdownTool(args) {
3014
3085
  fs.unlinkSync(info.eventLogPath);
3015
3086
  cleanedUp.push(info.eventLogPath);
3016
3087
  } catch {}
3017
- }
3088
+ });
3018
3089
  await sup.close({ skipShutdown: true });
3019
3090
  return {
3020
3091
  ok: true,
@@ -3037,11 +3108,24 @@ async function executeAskTool(args, getSupervisorClient) {
3037
3108
  const { client, isSingleton, sup, supervisorId } = await getSupervisorClient(args.supervisorId);
3038
3109
  try {
3039
3110
  const target = (isSingleton && sup ? findLocalRunByReference(sup, args.toRunId) : findExternalRun(supervisorId, args.toRunId))?.runtimeId ?? args.toRunId;
3040
- const result = await client.brokerAsk(target, args.message);
3041
- return {
3042
- reply: extractReplyMessage(result),
3043
- from_run_id: result.from_run_id ?? ""
3044
- };
3111
+ const timeoutMs = args.timeoutMs !== void 0 && Number.isFinite(args.timeoutMs) && args.timeoutMs > 0 ? args.timeoutMs : void 0;
3112
+ try {
3113
+ const result = await client.brokerAsk(target, args.message, void 0, timeoutMs);
3114
+ return {
3115
+ reply: extractReplyMessage(result),
3116
+ from_run_id: result.from_run_id ?? ""
3117
+ };
3118
+ } catch (error) {
3119
+ if (error instanceof RpcError && error.data && typeof error.data === "object") {
3120
+ const data = error.data;
3121
+ const messageId = data.message_id;
3122
+ if (typeof messageId === "string" && messageId) {
3123
+ if (data.pending === false) throw new Error(`ask failed (message_id: ${messageId} — the pending ask was already withdrawn): ${error.message}`);
3124
+ throw new Error(`ask failed (message_id: ${messageId} — pass to avenor_cancel to withdraw): ${error.message}`);
3125
+ }
3126
+ }
3127
+ throw error;
3128
+ }
3045
3129
  } finally {
3046
3130
  if (!isSingleton) client.close();
3047
3131
  }
@@ -3203,4 +3287,22 @@ function createWorkflowGateTool(getSupervisorClient) {
3203
3287
  }
3204
3288
  const workflowGateTool = createWorkflowGateTool(getSupervisorClient);
3205
3289
  //#endregion
3206
- export { DEFAULT_RUN_REDUCER_LIMITS, RunReducer, Supervisor, THINKING_LEVELS, answerPermissionTool, askTool, brokerReceiveTool, brokerReplyTool, cancelTool, createRunSnapshot, createWorkflowCompleteTool, createWorkflowEventsTool, createWorkflowGateTool, createWorkflowInspectTool, createWorkflowStatusTool, createWorkflowWaitTool, dial, evaluateThinkingPolicy, eventsTool, extractEventText, findAvenorBinary, followUpTool, getInstallDir, getVersion, inspectTool, installerBinaryPath, isThinkingLevel, normalizeRunEvent, observeRun, peersTool, reduceRunSnapshot, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool, validateSpawnSelection, validateThinking, validateThinkingForBackend, validateThinkingForBackendResume, workflowCompleteTool, workflowEventsTool, workflowGateTool, workflowInspectTool, workflowStatusTool, workflowWaitTool };
3290
+ //#region src/tools/workflow-controller-status.ts
3291
+ function createWorkflowControllerStatusTool(getSupervisorClient) {
3292
+ return async (args) => {
3293
+ if (!args.controllerId) return withWorkflowClient(args.supervisorId, (client) => client.workflowControllerList(), getSupervisorClient);
3294
+ return withWorkflowClient(args.supervisorId, (client) => client.workflowControllerStatus(args.controllerId), getSupervisorClient);
3295
+ };
3296
+ }
3297
+ const workflowControllerStatusTool = createWorkflowControllerStatusTool(getSupervisorClient);
3298
+ //#endregion
3299
+ //#region src/tools/workflow-ready.ts
3300
+ function createWorkflowReadyTool(getSupervisorClient) {
3301
+ return async (args) => {
3302
+ if (!args.controllerId) throw new Error("controllerId is required");
3303
+ return withWorkflowClient(args.supervisorId, (client) => client.workflowReady(args.controllerId, args.limit), getSupervisorClient);
3304
+ };
3305
+ }
3306
+ const workflowReadyTool = createWorkflowReadyTool(getSupervisorClient);
3307
+ //#endregion
3308
+ export { DEFAULT_RUN_REDUCER_LIMITS, RpcError, RunReducer, Supervisor, THINKING_LEVELS, answerPermissionTool, askTool, brokerReceiveTool, brokerReplyTool, cancelTool, createRunSnapshot, createWorkflowCompleteTool, createWorkflowControllerStatusTool, createWorkflowEventsTool, createWorkflowGateTool, createWorkflowInspectTool, createWorkflowReadyTool, createWorkflowStatusTool, createWorkflowWaitTool, dial, evaluateThinkingPolicy, eventsTool, extractEventText, findAvenorBinary, followUpTool, getInstallDir, getVersion, inspectTool, installerBinaryPath, isThinkingLevel, normalizeRunEvent, observeRun, peersTool, reduceRunSnapshot, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool, validateSpawnSelection, validateThinking, validateThinkingForBackend, validateThinkingForBackendResume, workflowCompleteTool, workflowControllerStatusTool, workflowEventsTool, workflowGateTool, workflowInspectTool, workflowReadyTool, workflowStatusTool, workflowWaitTool };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dougbots/avenor-core",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",