@dougbots/avenor-core 0.16.1 → 0.18.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.
- package/dist/index.d.ts +73 -17
- package/dist/index.js +388 -116
- package/dist/postinstall.d.ts +1 -1
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -145,6 +145,7 @@ interface WorkflowControllerCreateParams {
|
|
|
145
145
|
max_inflight: number;
|
|
146
146
|
}
|
|
147
147
|
interface MessageSendParams {
|
|
148
|
+
expected_sentinel_file?: string;
|
|
148
149
|
to_run_id: string;
|
|
149
150
|
message_id: string;
|
|
150
151
|
generation: string;
|
|
@@ -207,6 +208,12 @@ interface MessagePeer {
|
|
|
207
208
|
interface MessagePeersResult {
|
|
208
209
|
peers: MessagePeer[];
|
|
209
210
|
generation?: string;
|
|
211
|
+
/**
|
|
212
|
+
* Set when the listing read only part of the peer set. The peers beside it
|
|
213
|
+
* are the ones that were read, so a partial list is never read as a
|
|
214
|
+
* complete one.
|
|
215
|
+
*/
|
|
216
|
+
peers_error?: string;
|
|
210
217
|
}
|
|
211
218
|
interface MessagePreflightResult {
|
|
212
219
|
protocol: number;
|
|
@@ -311,7 +318,6 @@ declare class Client {
|
|
|
311
318
|
events(options?: SubscribeOptions): AsyncIterableIterator<Event>;
|
|
312
319
|
history(options: HistoryOptions): Promise<HistoryResult>;
|
|
313
320
|
status(runtimeId?: string): Promise<Record<string, unknown>>;
|
|
314
|
-
result(runtimeId?: string): Promise<Record<string, unknown>>;
|
|
315
321
|
list(): Promise<Array<Record<string, unknown>>>;
|
|
316
322
|
spawn(params: SpawnParams): Promise<SpawnResult>;
|
|
317
323
|
shutdown(mode: string): Promise<void>;
|
|
@@ -350,7 +356,9 @@ declare class Client {
|
|
|
350
356
|
messagePreflight(): Promise<MessagePreflightResult>;
|
|
351
357
|
resume(params: ResumeParams): Promise<ResumeResult>;
|
|
352
358
|
sessionRename(sessionId: string, name: string): Promise<SessionRenameResult>;
|
|
353
|
-
sessionLast(runtimeId: string): Promise<SessionLastResult>;
|
|
359
|
+
sessionLast(runtimeId: string, expectedSentinelFile?: string): Promise<SessionLastResult>;
|
|
360
|
+
/** The complete terminal reply for a runtime, with the sentinel that turn currently owns. */
|
|
361
|
+
result(runtimeId: string): Promise<Record<string, unknown>>;
|
|
354
362
|
sessionLastSet(runtimeId: string, finalOutput: string, stopReason: string, truncated: boolean): Promise<void>;
|
|
355
363
|
workflowCancel(workflowId: string, actor: string, reason: string): Promise<Record<string, unknown>>;
|
|
356
364
|
isClosed(): boolean;
|
|
@@ -517,7 +525,12 @@ interface RunSnapshot {
|
|
|
517
525
|
}
|
|
518
526
|
declare function createRunSnapshot(limits?: Partial<RunReducerLimits>): RunSnapshot;
|
|
519
527
|
declare function reduceRunSnapshot(current: RunSnapshot, input: unknown, limits?: Partial<RunReducerLimits>): RunSnapshot;
|
|
520
|
-
|
|
528
|
+
/**
|
|
529
|
+
* A snapshot as the inspect surface and an observer publish it: the reducer's
|
|
530
|
+
* dedupe bookkeeping stays internal to `RunReducer`.
|
|
531
|
+
*/
|
|
532
|
+
type RunSnapshotView = Omit<RunSnapshot, '_seen_seq_by_scope' | '_recent_legacy_keys' | '_last_semantic_fingerprint' | '_last_source_event'>;
|
|
533
|
+
type RunSnapshotSeed = Partial<RunSnapshotView>;
|
|
521
534
|
declare class RunReducer {
|
|
522
535
|
private state;
|
|
523
536
|
constructor(limits?: Partial<RunReducerLimits>, initial?: RunSnapshotSeed);
|
|
@@ -619,20 +632,46 @@ declare class Supervisor {
|
|
|
619
632
|
registerResumed(run: RunInfo): void;
|
|
620
633
|
/** The one place a run's ID and label are registered, for spawn and for resume. */
|
|
621
634
|
private register;
|
|
622
|
-
/**
|
|
635
|
+
/** The stored runs that carry a broker runtime ID. */
|
|
636
|
+
getRunsByRuntimeId(runtimeId: string): RunInfo[];
|
|
637
|
+
/**
|
|
638
|
+
* Match a stored run by its broker runtime ID, returning the entry only
|
|
639
|
+
* when exactly one run carries the id. Several registered turns can share
|
|
640
|
+
* a runtime (a warm resume, or a restart that re-minted one while the
|
|
641
|
+
* pre-restart entry stays registered); the current turn is identified by
|
|
642
|
+
* the sentinel the runtime holds, so a shared id is disambiguated with
|
|
643
|
+
* getRunByRuntimeTurn rather than by whichever entry was inserted last.
|
|
644
|
+
*/
|
|
623
645
|
getRunByRuntimeId(runtimeId: string): RunInfo | undefined;
|
|
646
|
+
/**
|
|
647
|
+
* Return the entry for (runtimeId, sentinelPath) only when exactly one
|
|
648
|
+
* stored run carries both. A sentinel no entry carries, or one carried by
|
|
649
|
+
* several entries, is claimed by no turn.
|
|
650
|
+
*/
|
|
651
|
+
getRunByRuntimeTurn(runtimeId: string, sentinelPath: string): RunInfo | undefined;
|
|
624
652
|
/**
|
|
625
653
|
* The live singleton when it owns supervisorId, null otherwise. Single
|
|
626
654
|
* source of truth for the ownership check, so callers cannot observe
|
|
627
655
|
* isCurrentInstance true while currentInstance is null.
|
|
628
656
|
*/
|
|
657
|
+
/**
|
|
658
|
+
* Build an instance that owns socketPath without spawning a supervisor
|
|
659
|
+
* process, and adopt it as the live singleton. `start` is the production
|
|
660
|
+
* writer of both; this exists so a test can drive the in-process plane
|
|
661
|
+
* without forging private state, and hands back the previous singleton so
|
|
662
|
+
* the caller can restore it.
|
|
663
|
+
*/
|
|
664
|
+
static adoptTestInstance(binaryPath: string, socketPath: string): {
|
|
665
|
+
supervisor: Supervisor;
|
|
666
|
+
restore: () => void;
|
|
667
|
+
};
|
|
629
668
|
static getInstance(supervisorId: string): Supervisor | null;
|
|
630
669
|
static isCurrentInstance(supervisorId: string): boolean;
|
|
631
670
|
static currentInstance(): Supervisor | null;
|
|
632
671
|
/** Iterate stored runs without exposing the internal map. */
|
|
633
672
|
forEachRun(callback: (info: RunInfo) => void): void;
|
|
634
673
|
getClient(): Client;
|
|
635
|
-
spawn(params: SpawnParams, runId?:
|
|
674
|
+
spawn(params: SpawnParams, runId?: string, metadata?: SpawnMetadata): Promise<RunInfo>;
|
|
636
675
|
close(options?: {
|
|
637
676
|
skipShutdown?: boolean;
|
|
638
677
|
}): Promise<void>;
|
|
@@ -743,6 +782,7 @@ interface StatusResult {
|
|
|
743
782
|
latest_seq?: number;
|
|
744
783
|
final_output?: string;
|
|
745
784
|
final_output_truncated?: boolean;
|
|
785
|
+
runtime_output_available?: boolean;
|
|
746
786
|
}
|
|
747
787
|
declare const statusTool: (args: StatusToolArgs) => Promise<StatusResult | StatusListResult>;
|
|
748
788
|
//#endregion
|
|
@@ -762,6 +802,11 @@ interface EventsToolResult {
|
|
|
762
802
|
[key: string]: unknown;
|
|
763
803
|
}>;
|
|
764
804
|
}
|
|
805
|
+
/**
|
|
806
|
+
* Stream the parsed records of one NDJSON event log. Malformed lines are
|
|
807
|
+
* dropped; an unreadable log rejects, so a caller that cannot read the log
|
|
808
|
+
* never sees a silent empty tail.
|
|
809
|
+
*/
|
|
765
810
|
declare const eventsTool: (args: EventsToolArgs) => Promise<EventsToolResult>;
|
|
766
811
|
//#endregion
|
|
767
812
|
//#region src/tools/inspect.d.ts
|
|
@@ -775,7 +820,7 @@ interface InspectResult {
|
|
|
775
820
|
run_id: string;
|
|
776
821
|
label: string;
|
|
777
822
|
status: StatusResult;
|
|
778
|
-
snapshot:
|
|
823
|
+
snapshot: RunSnapshotView;
|
|
779
824
|
transcript: RunSnapshot['transcript'];
|
|
780
825
|
tools: RunSnapshot['tools'];
|
|
781
826
|
live_tools: RunSnapshot['live_tools'];
|
|
@@ -961,9 +1006,9 @@ interface MessageProvider {
|
|
|
961
1006
|
surface(): MessageSurface;
|
|
962
1007
|
role(): string;
|
|
963
1008
|
resolve(ref: string): Promise<Resolution>;
|
|
964
|
-
state(runId: string): Promise<RunFacts>;
|
|
965
|
-
tell(targetRunId: string, message: string): Promise<ProviderResult>;
|
|
966
|
-
ask(targetRunId: string, message: string, timeoutMs?: number, signal?: AbortSignal): Promise<ProviderResult>;
|
|
1009
|
+
state(runId: string, publicRunId?: string): Promise<RunFacts>;
|
|
1010
|
+
tell(targetRunId: string, message: string, publicRunId?: string): Promise<ProviderResult>;
|
|
1011
|
+
ask(targetRunId: string, message: string, timeoutMs?: number, signal?: AbortSignal, publicRunId?: string): Promise<ProviderResult>;
|
|
967
1012
|
reply(inboundAskId: string, message: string, askerRunId: string): Promise<ProviderResult>;
|
|
968
1013
|
withdraw(messageId: string): Promise<ProviderResult>;
|
|
969
1014
|
controlReply?(target: string, payload: unknown): Promise<ProviderResult>;
|
|
@@ -978,7 +1023,7 @@ interface MessageProvider {
|
|
|
978
1023
|
sessionId?: string;
|
|
979
1024
|
name: string;
|
|
980
1025
|
}): Promise<ProviderResult>;
|
|
981
|
-
last(runId: string): Promise<ProviderResult>;
|
|
1026
|
+
last(runId: string, publicRunId?: string): Promise<ProviderResult>;
|
|
982
1027
|
peers(): Promise<PeersProviderResult>;
|
|
983
1028
|
/** Host surfaces that own a turn loop publish their own finished turn. */
|
|
984
1029
|
recordLastTurn?(text: string, stopReason: string, truncated: boolean): Promise<void>;
|
|
@@ -1092,6 +1137,7 @@ interface MessageClient {
|
|
|
1092
1137
|
generation: string;
|
|
1093
1138
|
body: string;
|
|
1094
1139
|
no_reply?: boolean;
|
|
1140
|
+
expected_sentinel_file?: string;
|
|
1095
1141
|
}): Promise<MessageSendResult>;
|
|
1096
1142
|
messageWait(params: {
|
|
1097
1143
|
message_id: string;
|
|
@@ -1121,7 +1167,7 @@ interface MessageClient {
|
|
|
1121
1167
|
on_event?: string;
|
|
1122
1168
|
}): Promise<ResumeResult>;
|
|
1123
1169
|
sessionRename(sessionId: string, name: string): Promise<SessionRenameResult>;
|
|
1124
|
-
sessionLast(runtimeId: string): Promise<SessionLastResult>;
|
|
1170
|
+
sessionLast(runtimeId: string, expectedSentinelFile?: string): Promise<SessionLastResult>;
|
|
1125
1171
|
sessionLastSet(runtimeId: string, finalOutput: string, stopReason: string, truncated: boolean): Promise<void>;
|
|
1126
1172
|
status(runtimeId?: string): Promise<Record<string, unknown>>;
|
|
1127
1173
|
}
|
|
@@ -1166,7 +1212,10 @@ declare function installAskClaims(provider: MessageProvider, claims: AskClaims,
|
|
|
1166
1212
|
/**
|
|
1167
1213
|
* Build the bound host provider: one supervisor, one generation, one control
|
|
1168
1214
|
* client. It never reads an ambient singleton or retargets, and it never
|
|
1169
|
-
* preflights per call.
|
|
1215
|
+
* preflights per call. Listing-backed lookups fail fast: an incomplete
|
|
1216
|
+
* listing refuses every resolution and state read, even for a peer the
|
|
1217
|
+
* partial set contains, so a wire `peers_error` resolves the same way the Go
|
|
1218
|
+
* providers do. Self and public-ID resolution bypass the listing.
|
|
1170
1219
|
*/
|
|
1171
1220
|
declare function createHostMessageProvider(options: HostProviderOptions): MessageProvider;
|
|
1172
1221
|
interface ChildProviderOptions {
|
|
@@ -1181,17 +1230,24 @@ interface ChildProviderOptions {
|
|
|
1181
1230
|
/**
|
|
1182
1231
|
* Build a child provider over the surface's inherited broker. A child never
|
|
1183
1232
|
* learns a public run id, and it cannot read a sentinel, so it is never
|
|
1184
|
-
* resumable from here: the supervisor owns a resume.
|
|
1233
|
+
* resumable from here: the supervisor owns a resume. Listing-backed lookups
|
|
1234
|
+
* fail fast: an incomplete listing refuses every resolution and state read,
|
|
1235
|
+
* even for a peer the partial set contains, so a wire `peers_error` resolves
|
|
1236
|
+
* the same way the Go providers do. Self resolution bypasses the listing.
|
|
1185
1237
|
*/
|
|
1186
1238
|
declare function createChildMessageProvider(options: ChildProviderOptions): MessageProvider;
|
|
1187
1239
|
//#endregion
|
|
1188
1240
|
//#region src/tools/peers.d.ts
|
|
1189
1241
|
interface PeersToolArgs extends PeersInput {}
|
|
1190
1242
|
type PeersResult = PeersRouterResult;
|
|
1191
|
-
/** The one avenor_peers handler; it resolves and reads through the bound provider.
|
|
1192
|
-
|
|
1243
|
+
/** The one avenor_peers handler; it resolves and reads through the bound provider.
|
|
1244
|
+
*
|
|
1245
|
+
* Admission belongs to the shared router for every surface, so the argument is
|
|
1246
|
+
* the untyped JSON a tool call carries and `executePeers` validates it first —
|
|
1247
|
+
* the same shape `messageTool` takes. */
|
|
1248
|
+
declare function peersTool(args: unknown, provider: MessageProvider): Promise<PeersResult>;
|
|
1193
1249
|
/** Bind the peers handler to one provider. */
|
|
1194
|
-
declare function createPeersTool(provider: MessageProvider): (args:
|
|
1250
|
+
declare function createPeersTool(provider: MessageProvider): (args: unknown) => Promise<PeersResult>;
|
|
1195
1251
|
//#endregion
|
|
1196
1252
|
//#region src/tools/kill.d.ts
|
|
1197
1253
|
interface KillToolArgs {
|
|
@@ -1318,4 +1374,4 @@ type WorkflowCancelResult = Record<string, unknown>;
|
|
|
1318
1374
|
declare function createWorkflowCancelTool(getSupervisorClient: GetSupervisorClient): (args: WorkflowCancelArgs) => Promise<WorkflowCancelResult>;
|
|
1319
1375
|
declare const workflowCancelTool: (args: WorkflowCancelArgs) => Promise<WorkflowCancelResult>;
|
|
1320
1376
|
//#endregion
|
|
1321
|
-
export { type AskClaims, type BrokerReceiveArgs, type BrokerReceiveResult, type ChildBroker, type Client, ClosedSocketError, DEFAULT_RUN_REDUCER_LIMITS, type Event, type ExecutionIdentity, ForeignPeerError, type HistoryOptions, type HistoryResult, type InboundAsk, type InspectResult, type KillResult, type KillToolArgs, MAX_FINAL_OUTPUT_RUNES, MAX_WAIT_MS, MESSAGE_OUTCOMES, type MessageCallerKind, type MessageClient, type MessageInput, type MessageOutcome, type MessageProvider, type MessageSurface, type NormalizedRunEvent, type ObserveRunOptions, type OwnerAttachOptions, OwnerAuthError, OwnerRecordMissingError, type PeerRecord, type PeersInput, type PeersProviderResult, type PeersResult, type PeersRouterResult, type PeersToolArgs, type PendingAsk, type ProviderResult, type Resolution, type ResultResult, type ResultToolArgs, RpcError, type RunChunkRole, type RunFacts, 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, SUPERVISOR_RUN_ID, type SpawnMetadata, type SpawnParams, type SpawnResult, type SpawnSelectionInput, type SpawnToolArgs, type SpawnToolResult, StaleOwnerRecordError, type StatusListResult, type StatusResult, type StatusToolArgs, type StatusView, type SubscribeOptions, Supervisor, type SupervisorClientOptions, type SupervisorOptions, THINKING_LEVELS, type ThinkingLevel, type ThinkingOutcome, ToolError, type WorkflowCancelArgs, type WorkflowCancelResult, 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, assertMessageInput, assertPeersInput, attachOwner, boundFinalOutput, brokerReceiveTool, createChildMessageProvider, createHostMessageProvider, createMessageTool, createPeersTool, createRunSnapshot, createWorkflowCancelTool, createWorkflowCompleteTool, createWorkflowControllerStatusTool, createWorkflowEventsTool, createWorkflowGateTool, createWorkflowInspectTool, createWorkflowReadyTool, createWorkflowStatusTool, createWorkflowWaitTool, dial, evaluateThinkingPolicy, eventsTool, executeMessage, executePeers, extractEventText, extractReplyMessage, extractTurnText, findAvenorBinary, getInstallDir, getSupervisorClient, getVersion, inspectTool, installAskClaims, installerBinaryPath, isClosedSocketError, isGenerationMismatchError, isThinkingLevel, killTool, messageTool, newMsgID, normalizeAskTimeoutMs, normalizeRunEvent, normalizeTurnStopReason, observeRun, peersDetails, peersTool, quote, quoteList, reduceRunSnapshot, renderAskFailure, renderLast, renderOutcome, renderPeerList, repair, resultOutput, resultTool, sanitize, shutdownTool, socketsRoot, spawnTool, statusTool, validateSpawnSelection, validateThinking, validateThinkingForBackend, validateThinkingForBackendResume, workflowCancelTool, workflowCompleteTool, workflowControllerStatusTool, workflowEventsTool, workflowGateTool, workflowInspectTool, workflowReadyTool, workflowStatusTool, workflowWaitTool };
|
|
1377
|
+
export { type AskClaims, type BrokerReceiveArgs, type BrokerReceiveResult, type ChildBroker, type Client, ClosedSocketError, DEFAULT_RUN_REDUCER_LIMITS, type Event, type ExecutionIdentity, ForeignPeerError, type HistoryOptions, type HistoryResult, type InboundAsk, type InspectResult, type KillResult, type KillToolArgs, MAX_FINAL_OUTPUT_RUNES, MAX_WAIT_MS, MESSAGE_OUTCOMES, type MessageCallerKind, type MessageClient, type MessageInput, type MessageOutcome, type MessageProvider, type MessageSurface, type NormalizedRunEvent, type ObserveRunOptions, type OwnerAttachOptions, OwnerAuthError, OwnerRecordMissingError, type PeerRecord, type PeersInput, type PeersProviderResult, type PeersResult, type PeersRouterResult, type PeersToolArgs, type PendingAsk, type ProviderResult, type Resolution, type ResultResult, type ResultToolArgs, RpcError, type RunChunkRole, type RunFacts, type RunIdentity, type RunInfo, type RunObserver, type RunObserverClient, type RunPermissionOption, type RunPermissionSummary, RunReducer, type RunReducerLimits, type RunSnapshot, type RunSnapshotSeed, type RunSnapshotView, type RunToolState, type RunToolSummary, type RunTranscriptEntry, SUPERVISOR_RUN_ID, type SpawnMetadata, type SpawnParams, type SpawnResult, type SpawnSelectionInput, type SpawnToolArgs, type SpawnToolResult, StaleOwnerRecordError, type StatusListResult, type StatusResult, type StatusToolArgs, type StatusView, type SubscribeOptions, Supervisor, type SupervisorClientOptions, type SupervisorOptions, THINKING_LEVELS, type ThinkingLevel, type ThinkingOutcome, ToolError, type WorkflowCancelArgs, type WorkflowCancelResult, 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, assertMessageInput, assertPeersInput, attachOwner, boundFinalOutput, brokerReceiveTool, createChildMessageProvider, createHostMessageProvider, createMessageTool, createPeersTool, createRunSnapshot, createWorkflowCancelTool, createWorkflowCompleteTool, createWorkflowControllerStatusTool, createWorkflowEventsTool, createWorkflowGateTool, createWorkflowInspectTool, createWorkflowReadyTool, createWorkflowStatusTool, createWorkflowWaitTool, dial, evaluateThinkingPolicy, eventsTool, executeMessage, executePeers, extractEventText, extractReplyMessage, extractTurnText, findAvenorBinary, getInstallDir, getSupervisorClient, getVersion, inspectTool, installAskClaims, installerBinaryPath, isClosedSocketError, isGenerationMismatchError, isThinkingLevel, killTool, messageTool, newMsgID, normalizeAskTimeoutMs, normalizeRunEvent, normalizeTurnStopReason, observeRun, peersDetails, peersTool, quote, quoteList, reduceRunSnapshot, renderAskFailure, renderLast, renderOutcome, renderPeerList, repair, resultOutput, resultTool, sanitize, shutdownTool, socketsRoot, spawnTool, statusTool, validateSpawnSelection, validateThinking, validateThinkingForBackend, validateThinkingForBackendResume, workflowCancelTool, workflowCompleteTool, workflowControllerStatusTool, workflowEventsTool, workflowGateTool, workflowInspectTool, workflowReadyTool, workflowStatusTool, workflowWaitTool };
|
package/dist/index.js
CHANGED
|
@@ -368,10 +368,6 @@ var Client = class {
|
|
|
368
368
|
const params = runtimeId ? { runtime_id: runtimeId } : void 0;
|
|
369
369
|
return this.call("status", params);
|
|
370
370
|
}
|
|
371
|
-
async result(runtimeId) {
|
|
372
|
-
const params = runtimeId ? { runtime_id: runtimeId } : void 0;
|
|
373
|
-
return this.call("result", params);
|
|
374
|
-
}
|
|
375
371
|
async list() {
|
|
376
372
|
return this.call("list");
|
|
377
373
|
}
|
|
@@ -504,7 +500,7 @@ var Client = class {
|
|
|
504
500
|
});
|
|
505
501
|
}
|
|
506
502
|
async messageSend(params) {
|
|
507
|
-
return this.call("message_send", params);
|
|
503
|
+
return this.call(params.expected_sentinel_file ? "message_send_turn" : "message_send", params);
|
|
508
504
|
}
|
|
509
505
|
async messageWait(params) {
|
|
510
506
|
const requested = params.timeout_ms !== void 0 && params.timeout_ms > 0 ? params.timeout_ms : MESSAGE_MAX_WAIT_MS;
|
|
@@ -535,8 +531,15 @@ var Client = class {
|
|
|
535
531
|
name
|
|
536
532
|
});
|
|
537
533
|
}
|
|
538
|
-
async sessionLast(runtimeId) {
|
|
539
|
-
return this.call("session_last", {
|
|
534
|
+
async sessionLast(runtimeId, expectedSentinelFile) {
|
|
535
|
+
return this.call(expectedSentinelFile ? "session_last_turn" : "session_last", {
|
|
536
|
+
runtime_id: runtimeId,
|
|
537
|
+
...expectedSentinelFile ? { expected_sentinel_file: expectedSentinelFile } : {}
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
/** The complete terminal reply for a runtime, with the sentinel that turn currently owns. */
|
|
541
|
+
async result(runtimeId) {
|
|
542
|
+
return this.call("result", { runtime_id: runtimeId });
|
|
540
543
|
}
|
|
541
544
|
async sessionLastSet(runtimeId, finalOutput, stopReason, truncated) {
|
|
542
545
|
await this.call("session_last_set", {
|
|
@@ -2149,16 +2152,56 @@ var Supervisor = class Supervisor {
|
|
|
2149
2152
|
this.runs.set(run.runId, run);
|
|
2150
2153
|
this.aliases.set(run.label, run);
|
|
2151
2154
|
}
|
|
2152
|
-
/**
|
|
2155
|
+
/** The stored runs that carry a broker runtime ID. */
|
|
2156
|
+
getRunsByRuntimeId(runtimeId) {
|
|
2157
|
+
if (!runtimeId) return [];
|
|
2158
|
+
return [...this.runs.values()].filter((info) => info.runtimeId === runtimeId);
|
|
2159
|
+
}
|
|
2160
|
+
/**
|
|
2161
|
+
* Match a stored run by its broker runtime ID, returning the entry only
|
|
2162
|
+
* when exactly one run carries the id. Several registered turns can share
|
|
2163
|
+
* a runtime (a warm resume, or a restart that re-minted one while the
|
|
2164
|
+
* pre-restart entry stays registered); the current turn is identified by
|
|
2165
|
+
* the sentinel the runtime holds, so a shared id is disambiguated with
|
|
2166
|
+
* getRunByRuntimeTurn rather than by whichever entry was inserted last.
|
|
2167
|
+
*/
|
|
2153
2168
|
getRunByRuntimeId(runtimeId) {
|
|
2154
|
-
|
|
2155
|
-
|
|
2169
|
+
const matches = this.getRunsByRuntimeId(runtimeId);
|
|
2170
|
+
return matches.length === 1 ? matches[0] : void 0;
|
|
2171
|
+
}
|
|
2172
|
+
/**
|
|
2173
|
+
* Return the entry for (runtimeId, sentinelPath) only when exactly one
|
|
2174
|
+
* stored run carries both. A sentinel no entry carries, or one carried by
|
|
2175
|
+
* several entries, is claimed by no turn.
|
|
2176
|
+
*/
|
|
2177
|
+
getRunByRuntimeTurn(runtimeId, sentinelPath) {
|
|
2178
|
+
if (!runtimeId || !sentinelPath) return void 0;
|
|
2179
|
+
const matches = this.getRunsByRuntimeId(runtimeId).filter((info) => info.sentinelPath === sentinelPath);
|
|
2180
|
+
return matches.length === 1 ? matches[0] : void 0;
|
|
2156
2181
|
}
|
|
2157
2182
|
/**
|
|
2158
2183
|
* The live singleton when it owns supervisorId, null otherwise. Single
|
|
2159
2184
|
* source of truth for the ownership check, so callers cannot observe
|
|
2160
2185
|
* isCurrentInstance true while currentInstance is null.
|
|
2161
2186
|
*/
|
|
2187
|
+
/**
|
|
2188
|
+
* Build an instance that owns socketPath without spawning a supervisor
|
|
2189
|
+
* process, and adopt it as the live singleton. `start` is the production
|
|
2190
|
+
* writer of both; this exists so a test can drive the in-process plane
|
|
2191
|
+
* without forging private state, and hands back the previous singleton so
|
|
2192
|
+
* the caller can restore it.
|
|
2193
|
+
*/
|
|
2194
|
+
static adoptTestInstance(binaryPath, socketPath) {
|
|
2195
|
+
const previous = Supervisor.instance;
|
|
2196
|
+
const supervisor = new Supervisor(binaryPath, socketPath);
|
|
2197
|
+
Supervisor.instance = supervisor;
|
|
2198
|
+
return {
|
|
2199
|
+
supervisor,
|
|
2200
|
+
restore: () => {
|
|
2201
|
+
Supervisor.instance = previous;
|
|
2202
|
+
}
|
|
2203
|
+
};
|
|
2204
|
+
}
|
|
2162
2205
|
static getInstance(supervisorId) {
|
|
2163
2206
|
if (Supervisor.instance === null) return null;
|
|
2164
2207
|
if (Supervisor.instance.supervisorId === supervisorId) return Supervisor.instance;
|
|
@@ -2637,7 +2680,7 @@ var thinking_policy_umpire_default = {
|
|
|
2637
2680
|
{
|
|
2638
2681
|
"op": "condIn",
|
|
2639
2682
|
"condition": "backend",
|
|
2640
|
-
"values": ["claude"
|
|
2683
|
+
"values": ["claude"]
|
|
2641
2684
|
},
|
|
2642
2685
|
{
|
|
2643
2686
|
"op": "condEq",
|
|
@@ -2657,7 +2700,7 @@ var thinking_policy_umpire_default = {
|
|
|
2657
2700
|
}
|
|
2658
2701
|
]
|
|
2659
2702
|
},
|
|
2660
|
-
"reason": "claude
|
|
2703
|
+
"reason": "claude supports low through max only on a new session"
|
|
2661
2704
|
}]
|
|
2662
2705
|
}
|
|
2663
2706
|
}]
|
|
@@ -2781,6 +2824,55 @@ function validateThinkingForBackendResume(backend, value) {
|
|
|
2781
2824
|
throw new Error(`backend ${JSON.stringify(backend)} does not support parameter "thinking"`);
|
|
2782
2825
|
}
|
|
2783
2826
|
//#endregion
|
|
2827
|
+
//#region src/tools/sentinel-status.ts
|
|
2828
|
+
/**
|
|
2829
|
+
* Sentinel statuses that settle the provider session itself, so the recorded
|
|
2830
|
+
* session id is a loadable transcript. TIMEOUT and KILLED are supervisor-imposed
|
|
2831
|
+
* terminations (time budget, cancellation) where an automatic continuation would
|
|
2832
|
+
* surprise. Matches the Go `readSentinelSession`.
|
|
2833
|
+
*/
|
|
2834
|
+
const RESUMABLE_SENTINEL_STATUSES = new Set([
|
|
2835
|
+
"DONE",
|
|
2836
|
+
"FAILED",
|
|
2837
|
+
"BLOCKED"
|
|
2838
|
+
]);
|
|
2839
|
+
/**
|
|
2840
|
+
* The terminal sentinel statuses the provider's state fallback answers from,
|
|
2841
|
+
* matching the Go `terminalStatusFromRunInfo` set: a run that settled before
|
|
2842
|
+
* a supervisor restart keeps its outcome discoverable by run id.
|
|
2843
|
+
*/
|
|
2844
|
+
const TERMINAL_SENTINEL_STATUSES = new Set([
|
|
2845
|
+
"DONE",
|
|
2846
|
+
"FAILED",
|
|
2847
|
+
"BLOCKED",
|
|
2848
|
+
"TIMEOUT",
|
|
2849
|
+
"KILLED"
|
|
2850
|
+
]);
|
|
2851
|
+
/**
|
|
2852
|
+
* The result word a sentinel status settles on. Both the status surface and the
|
|
2853
|
+
* message provider's state fallback answer from this one mapping, so the same
|
|
2854
|
+
* sentinel file cannot classify differently between them. BLOCKED reads as
|
|
2855
|
+
* failed: it names why the run stopped, not a result of its own.
|
|
2856
|
+
*/
|
|
2857
|
+
function sentinelStatusToResult(sentinelStatus) {
|
|
2858
|
+
switch (sentinelStatus) {
|
|
2859
|
+
case "DONE": return "done";
|
|
2860
|
+
case "FAILED": return "failed";
|
|
2861
|
+
case "TIMEOUT": return "timeout";
|
|
2862
|
+
case "KILLED": return "killed";
|
|
2863
|
+
case "BLOCKED": return "failed";
|
|
2864
|
+
default: return "running";
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
/**
|
|
2868
|
+
* Whether a terminal sentinel leaves a session worth resuming. Mirrors the only
|
|
2869
|
+
* hit Go's `runFactsFromStatus` reaches for a registry entry: a non-empty
|
|
2870
|
+
* session id. The sentinel word never decides this.
|
|
2871
|
+
*/
|
|
2872
|
+
function resumableFromSession(sessionId) {
|
|
2873
|
+
return typeof sessionId === "string" && sessionId !== "";
|
|
2874
|
+
}
|
|
2875
|
+
//#endregion
|
|
2784
2876
|
//#region src/tools/run-resolution.ts
|
|
2785
2877
|
/** Resolve a local public run ID before considering a label alias. */
|
|
2786
2878
|
function findLocalRunByReference(sup, reference) {
|
|
@@ -2821,16 +2913,6 @@ async function sentinelExists(filePath) {
|
|
|
2821
2913
|
function rawStatusPhase(source) {
|
|
2822
2914
|
return stringField(source ?? {}, "phase") || stringField(source ?? {}, "status") || "running";
|
|
2823
2915
|
}
|
|
2824
|
-
function sentinelStatusToResult(sentinelStatus) {
|
|
2825
|
-
switch (sentinelStatus) {
|
|
2826
|
-
case "DONE": return "done";
|
|
2827
|
-
case "FAILED": return "failed";
|
|
2828
|
-
case "TIMEOUT": return "timeout";
|
|
2829
|
-
case "KILLED": return "killed";
|
|
2830
|
-
case "BLOCKED": return "failed";
|
|
2831
|
-
default: return "running";
|
|
2832
|
-
}
|
|
2833
|
-
}
|
|
2834
2916
|
function translateStatus(rawPhase, sentinelData) {
|
|
2835
2917
|
const sentinelStatus = sentinelData?._status;
|
|
2836
2918
|
const phase = rawPhase.toLowerCase();
|
|
@@ -2933,16 +3015,19 @@ function shapeStatusResult(result, view = "full") {
|
|
|
2933
3015
|
};
|
|
2934
3016
|
}
|
|
2935
3017
|
async function buildRunStatus(runInfo, liveStatus) {
|
|
2936
|
-
|
|
3018
|
+
const liveSentinel = stringField(liveStatus ?? {}, "sentinel_file");
|
|
3019
|
+
const belongsToRun = !liveSentinel || liveSentinel === runInfo.sentinelPath;
|
|
3020
|
+
if (liveStatus && belongsToRun) retainLiveIdentity(runInfo, liveStatus);
|
|
2937
3021
|
let sentinel = null;
|
|
2938
3022
|
if (await sentinelExists(runInfo.sentinelPath)) sentinel = await parseSentinel(runInfo.sentinelPath);
|
|
2939
|
-
const
|
|
2940
|
-
|
|
3023
|
+
const source = belongsToRun ? liveStatus : null;
|
|
3024
|
+
const translated = belongsToRun && liveStatus ? translateStatus(rawStatusPhase(source), sentinel) : sentinel ? sentinelStatusToResult(sentinel._status) : translateStatus(rawStatusPhase(source), null);
|
|
3025
|
+
const result = buildBaseStatus(source ?? {}, {
|
|
2941
3026
|
run_id: runInfo.runId,
|
|
2942
3027
|
label: runInfo.label,
|
|
2943
3028
|
prefer_fallback_run_id: true,
|
|
2944
3029
|
runtime_id: runInfo.runtimeId,
|
|
2945
|
-
session_id: sentinel?.SESSION ??
|
|
3030
|
+
session_id: sentinel?.SESSION ?? source?.session_id ?? runInfo.sessionId,
|
|
2946
3031
|
stop_reason: sentinel?.STOP_REASON,
|
|
2947
3032
|
identity: {
|
|
2948
3033
|
roster_file: runInfo.rosterFile,
|
|
@@ -2956,6 +3041,10 @@ async function buildRunStatus(runInfo, liveStatus) {
|
|
|
2956
3041
|
effective_backend: runInfo.effectiveBackend
|
|
2957
3042
|
}
|
|
2958
3043
|
}, translated);
|
|
3044
|
+
result.label = runInfo.label;
|
|
3045
|
+
if (!belongsToRun || !result.event_path) result.event_path = runInfo.eventLogPath;
|
|
3046
|
+
if (!belongsToRun || !liveStatus) result.runtime_output_available = false;
|
|
3047
|
+
return result;
|
|
2959
3048
|
}
|
|
2960
3049
|
async function queryLiveStatus(client, runtimeId) {
|
|
2961
3050
|
if (!runtimeId) return null;
|
|
@@ -3112,27 +3201,41 @@ function matchesFilters(record, args) {
|
|
|
3112
3201
|
if (args.after_seq !== void 0 && seq !== void 0 && seq <= args.after_seq) return false;
|
|
3113
3202
|
return true;
|
|
3114
3203
|
}
|
|
3115
|
-
|
|
3204
|
+
/**
|
|
3205
|
+
* Stream the parsed records of one NDJSON event log. Malformed lines are
|
|
3206
|
+
* dropped; an unreadable log rejects, so a caller that cannot read the log
|
|
3207
|
+
* never sees a silent empty tail.
|
|
3208
|
+
*/
|
|
3209
|
+
async function* readNdjsonRecords(filePath) {
|
|
3116
3210
|
const stream = fs.createReadStream(filePath, { encoding: "utf-8" });
|
|
3117
3211
|
const rl = readline.createInterface({
|
|
3118
3212
|
input: stream,
|
|
3119
3213
|
crlfDelay: Infinity
|
|
3120
3214
|
});
|
|
3121
|
-
const events = [];
|
|
3122
3215
|
try {
|
|
3123
3216
|
for await (const line of rl) {
|
|
3124
3217
|
if (!line.trim()) continue;
|
|
3218
|
+
let parsed;
|
|
3125
3219
|
try {
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3220
|
+
parsed = JSON.parse(line);
|
|
3221
|
+
} catch {
|
|
3222
|
+
continue;
|
|
3223
|
+
}
|
|
3224
|
+
const record = asRecord(parsed);
|
|
3225
|
+
if (record) yield record;
|
|
3131
3226
|
}
|
|
3132
3227
|
} finally {
|
|
3133
3228
|
rl.close();
|
|
3134
3229
|
stream.close();
|
|
3135
3230
|
}
|
|
3231
|
+
}
|
|
3232
|
+
async function readNdjsonEvents(filePath, args) {
|
|
3233
|
+
const events = [];
|
|
3234
|
+
for await (const record of readNdjsonRecords(filePath)) {
|
|
3235
|
+
if (!matchesFilters(record, args)) continue;
|
|
3236
|
+
events.push(clipEvent(record));
|
|
3237
|
+
if (events.length > args.limit) events.splice(0, events.length - args.limit);
|
|
3238
|
+
}
|
|
3136
3239
|
return events;
|
|
3137
3240
|
}
|
|
3138
3241
|
async function resolveRun(args, getSupervisorClient) {
|
|
@@ -3221,7 +3324,7 @@ async function executeEventsTool(args, getSupervisorClient) {
|
|
|
3221
3324
|
} catch (error) {
|
|
3222
3325
|
failures.push(contextualError(`read event log ${resolved.eventLogPath}`, error));
|
|
3223
3326
|
}
|
|
3224
|
-
if (resolved.runtimeId) try {
|
|
3327
|
+
if (resolved.runtimeId && !resolved.runInfo) try {
|
|
3225
3328
|
const history = await resolved.client.history({
|
|
3226
3329
|
runtime_id: resolved.runtimeId,
|
|
3227
3330
|
limit,
|
|
@@ -3333,23 +3436,31 @@ function sleep(ms, signal) {
|
|
|
3333
3436
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
3334
3437
|
});
|
|
3335
3438
|
}
|
|
3336
|
-
async function
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3439
|
+
async function outputFromEvents(filePath) {
|
|
3440
|
+
let output;
|
|
3441
|
+
for await (const record of readNdjsonRecords(filePath)) try {
|
|
3442
|
+
const event = normalizeRunEvent(record);
|
|
3443
|
+
if (event?.event === "session.end" && event.final_output !== void 0) output = event.final_output;
|
|
3444
|
+
} catch {}
|
|
3445
|
+
return output;
|
|
3446
|
+
}
|
|
3447
|
+
async function realResult(supervisorId, runtimeId) {
|
|
3448
|
+
const { client, isSingleton } = await getSupervisorClient(supervisorId);
|
|
3449
|
+
try {
|
|
3450
|
+
return await client.result(runtimeId);
|
|
3451
|
+
} finally {
|
|
3452
|
+
if (!isSingleton) client.close();
|
|
3345
3453
|
}
|
|
3346
|
-
|
|
3347
|
-
|
|
3454
|
+
}
|
|
3455
|
+
async function realSentinelPath(supervisorId, runId) {
|
|
3456
|
+
const { sup, supervisorId: resolvedId } = await getSupervisorClient(supervisorId);
|
|
3457
|
+
return (sup ? sup.getRunByReference(runId) : void 0)?.sentinelPath ?? findExternalRun(resolvedId, runId)?.sentinelPath;
|
|
3348
3458
|
}
|
|
3349
3459
|
const defaultDeps = {
|
|
3350
3460
|
status: statusTool,
|
|
3351
3461
|
inspect: inspectTool,
|
|
3352
|
-
|
|
3462
|
+
result: realResult,
|
|
3463
|
+
sentinelPath: realSentinelPath,
|
|
3353
3464
|
sleep,
|
|
3354
3465
|
now: Date.now,
|
|
3355
3466
|
pollIntervalMs: DEFAULT_POLL_INTERVAL_MS
|
|
@@ -3388,14 +3499,25 @@ async function executeResultTool(args, deps) {
|
|
|
3388
3499
|
if (TERMINAL_STATUSES$1.has(status.status)) {
|
|
3389
3500
|
let exactOutputRetrieved = false;
|
|
3390
3501
|
try {
|
|
3391
|
-
|
|
3502
|
+
let output = status.event_path ? await outputFromEvents(status.event_path).catch(() => void 0) : void 0;
|
|
3392
3503
|
if (output !== void 0) {
|
|
3393
3504
|
status.final_output = output;
|
|
3394
3505
|
status.final_output_truncated = false;
|
|
3395
3506
|
exactOutputRetrieved = true;
|
|
3396
3507
|
}
|
|
3397
3508
|
} catch {}
|
|
3398
|
-
if (status.
|
|
3509
|
+
if (!exactOutputRetrieved && status.runtime_output_available !== false && status.runtime_id) try {
|
|
3510
|
+
const result = await deps.result(args.supervisorId, status.runtime_id);
|
|
3511
|
+
const output = typeof result.final_output === "string" ? result.final_output : void 0;
|
|
3512
|
+
const sentinel = await deps.sentinelPath(args.supervisorId, status.run_id);
|
|
3513
|
+
const matchesTurn = sentinel === void 0 ? true : sentinel !== "" && typeof result.sentinel_file === "string" && result.sentinel_file === sentinel;
|
|
3514
|
+
if (output !== void 0 && matchesTurn) {
|
|
3515
|
+
status.final_output = output;
|
|
3516
|
+
status.final_output_truncated = false;
|
|
3517
|
+
exactOutputRetrieved = true;
|
|
3518
|
+
}
|
|
3519
|
+
} catch {}
|
|
3520
|
+
if (!exactOutputRetrieved && status.final_output === void 0 && status.runtime_output_available !== false) try {
|
|
3399
3521
|
const inspected = await deps.inspect({
|
|
3400
3522
|
runId: args.runId,
|
|
3401
3523
|
supervisorId: args.supervisorId
|
|
@@ -5223,7 +5345,7 @@ var message_route_conformance_default = {
|
|
|
5223
5345
|
"expect": {
|
|
5224
5346
|
"code": "unsupported_action",
|
|
5225
5347
|
"facts": {},
|
|
5226
|
-
"message": "action \"control_reply\" is
|
|
5348
|
+
"message": "action \"control_reply\" is retained in the contract but no surface can deliver one"
|
|
5227
5349
|
}
|
|
5228
5350
|
},
|
|
5229
5351
|
{
|
|
@@ -5242,7 +5364,7 @@ var message_route_conformance_default = {
|
|
|
5242
5364
|
"expect": {
|
|
5243
5365
|
"code": "unsupported_action",
|
|
5244
5366
|
"facts": {},
|
|
5245
|
-
"message": "action \"control_reply\" is
|
|
5367
|
+
"message": "action \"control_reply\" is retained in the contract but no surface can deliver one"
|
|
5246
5368
|
}
|
|
5247
5369
|
},
|
|
5248
5370
|
{
|
|
@@ -5261,7 +5383,7 @@ var message_route_conformance_default = {
|
|
|
5261
5383
|
"expect": {
|
|
5262
5384
|
"code": "unsupported_action",
|
|
5263
5385
|
"facts": {},
|
|
5264
|
-
"message": "action \"control_reply\" is
|
|
5386
|
+
"message": "action \"control_reply\" is retained in the contract but no surface can deliver one"
|
|
5265
5387
|
}
|
|
5266
5388
|
},
|
|
5267
5389
|
{
|
|
@@ -5280,7 +5402,7 @@ var message_route_conformance_default = {
|
|
|
5280
5402
|
"expect": {
|
|
5281
5403
|
"code": "unsupported_action",
|
|
5282
5404
|
"facts": {},
|
|
5283
|
-
"message": "action \"control_reply\" is
|
|
5405
|
+
"message": "action \"control_reply\" is retained in the contract but no surface can deliver one"
|
|
5284
5406
|
}
|
|
5285
5407
|
},
|
|
5286
5408
|
{
|
|
@@ -5299,7 +5421,7 @@ var message_route_conformance_default = {
|
|
|
5299
5421
|
"expect": {
|
|
5300
5422
|
"code": "unsupported_action",
|
|
5301
5423
|
"facts": {},
|
|
5302
|
-
"message": "action \"control_reply\" is
|
|
5424
|
+
"message": "action \"control_reply\" is retained in the contract but no surface can deliver one"
|
|
5303
5425
|
}
|
|
5304
5426
|
},
|
|
5305
5427
|
{
|
|
@@ -5318,7 +5440,7 @@ var message_route_conformance_default = {
|
|
|
5318
5440
|
"expect": {
|
|
5319
5441
|
"code": "unsupported_action",
|
|
5320
5442
|
"facts": {},
|
|
5321
|
-
"message": "action \"control_reply\" is
|
|
5443
|
+
"message": "action \"control_reply\" is retained in the contract but no surface can deliver one"
|
|
5322
5444
|
}
|
|
5323
5445
|
},
|
|
5324
5446
|
{
|
|
@@ -8274,6 +8396,41 @@ function renderAskFailure(messageId, target, outcome = "possibly-pending") {
|
|
|
8274
8396
|
function normalizeAskTimeoutMs(timeoutMs) {
|
|
8275
8397
|
return timeoutMs !== void 0 && Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : void 0;
|
|
8276
8398
|
}
|
|
8399
|
+
/** Render a value the way Go's `%q` verb does (`strconv.Quote`). */
|
|
8400
|
+
function goQuote(value) {
|
|
8401
|
+
let out = "\"";
|
|
8402
|
+
for (const character of value) out += goEscapedRune(character);
|
|
8403
|
+
return `${out}"`;
|
|
8404
|
+
}
|
|
8405
|
+
const GO_PRINTABLE = /[\p{L}\p{M}\p{N}\p{P}\p{S}]/u;
|
|
8406
|
+
/**
|
|
8407
|
+
* One rune in `strconv.Quote` form: short escapes for a quote, a backslash, a
|
|
8408
|
+
* bell and the whitespace controls; `\xNN` for the remaining ASCII escapes;
|
|
8409
|
+
* `\uNNNN` and `\UNNNNNNNN` for a non-printable rune; the rune itself when it
|
|
8410
|
+
* is printable. Printable is Go's `strconv.IsPrint` — ASCII space through `~`,
|
|
8411
|
+
* and elsewhere the categories L, M, N, P and S. V8's Unicode tables can be
|
|
8412
|
+
* newer than Go's, so a rune assigned since Go's tables were generated renders
|
|
8413
|
+
* escaped here where Go writes it verbatim; that direction only fails to match
|
|
8414
|
+
* a sentence, never invents one.
|
|
8415
|
+
*/
|
|
8416
|
+
function goEscapedRune(character) {
|
|
8417
|
+
const code = character.codePointAt(0) ?? 0;
|
|
8418
|
+
switch (character) {
|
|
8419
|
+
case "\"": return "\\\"";
|
|
8420
|
+
case "\\": return "\\\\";
|
|
8421
|
+
case "\x07": return "\\a";
|
|
8422
|
+
case "\b": return "\\b";
|
|
8423
|
+
case "\f": return "\\f";
|
|
8424
|
+
case "\n": return "\\n";
|
|
8425
|
+
case "\r": return "\\r";
|
|
8426
|
+
case " ": return "\\t";
|
|
8427
|
+
case "\v": return "\\v";
|
|
8428
|
+
}
|
|
8429
|
+
if (code < 128 ? code >= 32 && code !== 127 : GO_PRINTABLE.test(character)) return character;
|
|
8430
|
+
if (code < 128) return `\\x${code.toString(16).padStart(2, "0")}`;
|
|
8431
|
+
if (code < 65536) return `\\u${code.toString(16).padStart(4, "0")}`;
|
|
8432
|
+
return `\\U${code.toString(16).padStart(8, "0")}`;
|
|
8433
|
+
}
|
|
8277
8434
|
//#endregion
|
|
8278
8435
|
//#region ../../schemas/message.input.json
|
|
8279
8436
|
var message_input_default = {
|
|
@@ -8409,11 +8566,11 @@ var message_input_default = {
|
|
|
8409
8566
|
"properties": {
|
|
8410
8567
|
"action": {
|
|
8411
8568
|
"const": "control_reply",
|
|
8412
|
-
"description": "
|
|
8569
|
+
"description": "Retained in the contract for the retired claude-channel backend's reply path; no surface can deliver one."
|
|
8413
8570
|
},
|
|
8414
8571
|
"target": {
|
|
8415
8572
|
"type": "string",
|
|
8416
|
-
"description": "Opaque
|
|
8573
|
+
"description": "Opaque control message id, passed through unchanged."
|
|
8417
8574
|
},
|
|
8418
8575
|
"payload": {
|
|
8419
8576
|
"type": "object",
|
|
@@ -8452,7 +8609,7 @@ var message_route_umpire_default = {
|
|
|
8452
8609
|
},
|
|
8453
8610
|
"controlReplySupported": {
|
|
8454
8611
|
"type": "boolean",
|
|
8455
|
-
"description": "
|
|
8612
|
+
"description": "Retained in the contract; no surface can deliver a control reply now that the claude-channel sidecar is retired."
|
|
8456
8613
|
}
|
|
8457
8614
|
},
|
|
8458
8615
|
fields: {
|
|
@@ -8509,7 +8666,7 @@ var message_route_umpire_default = {
|
|
|
8509
8666
|
"condition": "controlReplySupported"
|
|
8510
8667
|
}]
|
|
8511
8668
|
},
|
|
8512
|
-
"reason": "action \"control_reply\" is
|
|
8669
|
+
"reason": "action \"control_reply\" is retained in the contract but no surface can deliver one"
|
|
8513
8670
|
},
|
|
8514
8671
|
{
|
|
8515
8672
|
"type": "fairWhen",
|
|
@@ -8953,7 +9110,7 @@ async function tell(provider, input, resolution) {
|
|
|
8953
9110
|
const label = resolutionLabel(resolution, input.target ?? "");
|
|
8954
9111
|
let result;
|
|
8955
9112
|
try {
|
|
8956
|
-
result = await provider.tell(resolution.runId, input.message ?? "");
|
|
9113
|
+
result = await provider.tell(resolution.runId, input.message ?? "", resolution.publicRunId);
|
|
8957
9114
|
} catch (err) {
|
|
8958
9115
|
throw asToolError(err);
|
|
8959
9116
|
}
|
|
@@ -8971,7 +9128,7 @@ async function ask(provider, input, resolution, signal) {
|
|
|
8971
9128
|
const label = resolutionLabel(resolution, input.target ?? "");
|
|
8972
9129
|
let result;
|
|
8973
9130
|
try {
|
|
8974
|
-
result = await provider.ask(resolution.runId, input.message ?? "", input.timeout_ms, signal);
|
|
9131
|
+
result = await provider.ask(resolution.runId, input.message ?? "", input.timeout_ms, signal, resolution.publicRunId);
|
|
8975
9132
|
} catch (err) {
|
|
8976
9133
|
throw asToolError(err);
|
|
8977
9134
|
}
|
|
@@ -9088,9 +9245,10 @@ async function executeMessage(provider, input, options) {
|
|
|
9088
9245
|
conditions.targetMatch = resolution.match;
|
|
9089
9246
|
const denial = permissionDenial(action, conditions);
|
|
9090
9247
|
if (denial) throw denial;
|
|
9248
|
+
if (action === "resume" && typed.idempotency_key) return resume(provider, typed, resolution);
|
|
9091
9249
|
let facts;
|
|
9092
9250
|
try {
|
|
9093
|
-
facts = await provider.state(resolution.runId);
|
|
9251
|
+
facts = await provider.state(resolution.runId, resolution.publicRunId);
|
|
9094
9252
|
} catch (err) {
|
|
9095
9253
|
throw refused(err);
|
|
9096
9254
|
}
|
|
@@ -9168,7 +9326,7 @@ async function executePeers(provider, input) {
|
|
|
9168
9326
|
const resolution = await resolveTarget(provider, typed.target ?? "");
|
|
9169
9327
|
let last;
|
|
9170
9328
|
try {
|
|
9171
|
-
last = await provider.last(resolution.runId);
|
|
9329
|
+
last = await provider.last(resolution.runId, resolution.publicRunId);
|
|
9172
9330
|
} catch (err) {
|
|
9173
9331
|
throw asToolError(err);
|
|
9174
9332
|
}
|
|
@@ -9302,19 +9460,6 @@ function createMessageTool(provider) {
|
|
|
9302
9460
|
return (args, options) => messageTool(args, provider, options);
|
|
9303
9461
|
}
|
|
9304
9462
|
//#endregion
|
|
9305
|
-
//#region src/tools/sentinel-status.ts
|
|
9306
|
-
/**
|
|
9307
|
-
* Sentinel statuses that settle the provider session itself, so the recorded
|
|
9308
|
-
* session id is a loadable transcript. TIMEOUT and KILLED are supervisor-imposed
|
|
9309
|
-
* terminations (time budget, cancellation) where an automatic continuation would
|
|
9310
|
-
* surprise. Matches the Go `readSentinelSession`.
|
|
9311
|
-
*/
|
|
9312
|
-
const RESUMABLE_SENTINEL_STATUSES = new Set([
|
|
9313
|
-
"DONE",
|
|
9314
|
-
"FAILED",
|
|
9315
|
-
"BLOCKED"
|
|
9316
|
-
]);
|
|
9317
|
-
//#endregion
|
|
9318
9463
|
//#region src/tools/message-provider.ts
|
|
9319
9464
|
const SUPERVISOR_RUN_ID = "supervisor";
|
|
9320
9465
|
const TERMINAL_STATUSES = new Set([
|
|
@@ -9354,6 +9499,16 @@ function isGenerationMismatchError(error) {
|
|
|
9354
9499
|
}
|
|
9355
9500
|
return error instanceof Error && /generation mismatch/i.test(error.message);
|
|
9356
9501
|
}
|
|
9502
|
+
/**
|
|
9503
|
+
* Matches Go's `statusReportsNoRuntime`: true only for the supervisor's absence
|
|
9504
|
+
* answer for this exact runtime, so a refusal or a transport failure stays the
|
|
9505
|
+
* caller's error. The sentence is rendered with `goQuote` because the
|
|
9506
|
+
* supervisor formats the id with Go's `%q`.
|
|
9507
|
+
*/
|
|
9508
|
+
function statusReportsNoRuntime(error, runId) {
|
|
9509
|
+
if (!(error instanceof RpcError) || error.code !== -32e3) return false;
|
|
9510
|
+
return rpcText(error) === `runtime ${goQuote(runId)} not found`;
|
|
9511
|
+
}
|
|
9357
9512
|
/** Reject an abort exactly the way fetch does, so the ask cleanup path runs. */
|
|
9358
9513
|
function raceAbort(promise, signal) {
|
|
9359
9514
|
if (!signal) return promise;
|
|
@@ -9425,15 +9580,42 @@ function translateRunFacts(rawStatus, rawPhase) {
|
|
|
9425
9580
|
live: !TERMINAL_STATUSES.has(status)
|
|
9426
9581
|
};
|
|
9427
9582
|
}
|
|
9428
|
-
|
|
9429
|
-
|
|
9583
|
+
/**
|
|
9584
|
+
* Classify one broker peer row into the facts the router gates on. The Go
|
|
9585
|
+
* providers share this translation through `messaging.PeerRunFacts`: a peer row
|
|
9586
|
+
* carries no session and no sentinel, so the run is never resumable from here —
|
|
9587
|
+
* the supervisor that spawned it owns a resume.
|
|
9588
|
+
*/
|
|
9589
|
+
function peerFacts(peer) {
|
|
9590
|
+
const translation = translateRunFacts(peer?.status ?? "", "");
|
|
9591
|
+
return {
|
|
9592
|
+
live: translation.live,
|
|
9593
|
+
resumable: false,
|
|
9594
|
+
status: translation.status
|
|
9595
|
+
};
|
|
9596
|
+
}
|
|
9597
|
+
/** Read one sentinel file's status word, or undefined when the file is unreadable. */
|
|
9598
|
+
async function readSentinelStatus(filePath) {
|
|
9430
9599
|
try {
|
|
9431
|
-
|
|
9432
|
-
return RESUMABLE_SENTINEL_STATUSES.has(status);
|
|
9600
|
+
return ((await fs.promises.readFile(filePath, "utf-8")).trim().split("\n")[0] ?? "").trim().toUpperCase();
|
|
9433
9601
|
} catch {
|
|
9434
|
-
return
|
|
9602
|
+
return;
|
|
9435
9603
|
}
|
|
9436
9604
|
}
|
|
9605
|
+
/**
|
|
9606
|
+
* Read one sentinel file's terminal result word, or undefined when the file is
|
|
9607
|
+
* unreadable or holds a status that is not terminal. The mapping is the status
|
|
9608
|
+
* surface's, so both classify a sentinel the same way.
|
|
9609
|
+
*/
|
|
9610
|
+
async function terminalSentinelStatus(filePath) {
|
|
9611
|
+
const status = await readSentinelStatus(filePath);
|
|
9612
|
+
if (!status || !TERMINAL_SENTINEL_STATUSES.has(status)) return void 0;
|
|
9613
|
+
return sentinelStatusToResult(status);
|
|
9614
|
+
}
|
|
9615
|
+
async function sentinelResumable(filePath) {
|
|
9616
|
+
const status = await readSentinelStatus(filePath);
|
|
9617
|
+
return status !== void 0 && RESUMABLE_SENTINEL_STATUSES.has(status);
|
|
9618
|
+
}
|
|
9437
9619
|
function peerRecords(peers) {
|
|
9438
9620
|
return (peers.peers ?? []).map((peer) => {
|
|
9439
9621
|
const session = peer.session;
|
|
@@ -9480,13 +9662,20 @@ function installAskClaims(provider, claims, pendingAsks, pollGeneration) {
|
|
|
9480
9662
|
/**
|
|
9481
9663
|
* Build the bound host provider: one supervisor, one generation, one control
|
|
9482
9664
|
* client. It never reads an ambient singleton or retargets, and it never
|
|
9483
|
-
* preflights per call.
|
|
9665
|
+
* preflights per call. Listing-backed lookups fail fast: an incomplete
|
|
9666
|
+
* listing refuses every resolution and state read, even for a peer the
|
|
9667
|
+
* partial set contains, so a wire `peers_error` resolves the same way the Go
|
|
9668
|
+
* providers do. Self and public-ID resolution bypass the listing.
|
|
9484
9669
|
*/
|
|
9485
9670
|
function createHostMessageProvider(options) {
|
|
9486
9671
|
const { surface, supervisorId, generation, client, pendingAsks, pollGeneration } = options;
|
|
9487
9672
|
const claims = /* @__PURE__ */ new Map();
|
|
9488
9673
|
async function listedPeers() {
|
|
9489
|
-
|
|
9674
|
+
const result = await client.messagePeers({ generation });
|
|
9675
|
+
return {
|
|
9676
|
+
peers: peerRecords(result),
|
|
9677
|
+
detail: result.peers_error || void 0
|
|
9678
|
+
};
|
|
9490
9679
|
}
|
|
9491
9680
|
async function retractAsk(messageId) {
|
|
9492
9681
|
try {
|
|
@@ -9496,10 +9685,33 @@ function createHostMessageProvider(options) {
|
|
|
9496
9685
|
});
|
|
9497
9686
|
} catch {}
|
|
9498
9687
|
}
|
|
9499
|
-
function publicRunIdFor(runId) {
|
|
9500
|
-
const
|
|
9501
|
-
if (
|
|
9502
|
-
|
|
9688
|
+
async function publicRunIdFor(runId) {
|
|
9689
|
+
const instance = Supervisor.getInstance(supervisorId);
|
|
9690
|
+
if (instance && instance.getRunsByRuntimeId(runId).length > 0) {
|
|
9691
|
+
const unique = instance.getRunByRuntimeId(runId);
|
|
9692
|
+
if (unique) return unique.runId;
|
|
9693
|
+
const status = await client.status(runId);
|
|
9694
|
+
const match = typeof status.sentinel_file === "string" ? instance.getRunByRuntimeTurn(runId, status.sentinel_file) : void 0;
|
|
9695
|
+
if (!match) throw new Error(`turn identity unavailable for ${runId}`);
|
|
9696
|
+
return match.runId;
|
|
9697
|
+
}
|
|
9698
|
+
const matches = listExternalRuns(supervisorId).filter((info) => info.runtimeId === runId);
|
|
9699
|
+
if (matches.length <= 1) return matches[0]?.runId;
|
|
9700
|
+
const status = await client.status(runId);
|
|
9701
|
+
const match = matches.find((info) => info.sentinelPath === status.sentinel_file);
|
|
9702
|
+
if (!match) throw new Error(`turn identity unavailable for ${runId}`);
|
|
9703
|
+
return match.runId;
|
|
9704
|
+
}
|
|
9705
|
+
function publicRunInfo(publicRunId) {
|
|
9706
|
+
if (!publicRunId) return void 0;
|
|
9707
|
+
const instance = Supervisor.getInstance(supervisorId);
|
|
9708
|
+
return instance ? instance.getRunById(publicRunId) : findExternalRunById(supervisorId, publicRunId);
|
|
9709
|
+
}
|
|
9710
|
+
function expectedSentinel(publicRunId) {
|
|
9711
|
+
if (!publicRunId) return void 0;
|
|
9712
|
+
const info = publicRunInfo(publicRunId);
|
|
9713
|
+
if (!info?.sentinelPath) throw new Error(`turn identity unavailable for ${publicRunId}`);
|
|
9714
|
+
return info.sentinelPath;
|
|
9503
9715
|
}
|
|
9504
9716
|
function unique(info) {
|
|
9505
9717
|
return {
|
|
@@ -9509,6 +9721,18 @@ function createHostMessageProvider(options) {
|
|
|
9509
9721
|
match: "unique"
|
|
9510
9722
|
};
|
|
9511
9723
|
}
|
|
9724
|
+
async function peerRunState(runId) {
|
|
9725
|
+
let listed;
|
|
9726
|
+
try {
|
|
9727
|
+
listed = await listedPeers();
|
|
9728
|
+
} catch (error) {
|
|
9729
|
+
if (isGenerationMismatchError(error)) throw error;
|
|
9730
|
+
return;
|
|
9731
|
+
}
|
|
9732
|
+
if (listed.detail) return void 0;
|
|
9733
|
+
const peer = listed.peers.find((entry) => entry.run_id === runId);
|
|
9734
|
+
return peer ? peerFacts(peer) : void 0;
|
|
9735
|
+
}
|
|
9512
9736
|
const provider = {
|
|
9513
9737
|
caller: () => ({
|
|
9514
9738
|
kind: "owner",
|
|
@@ -9533,18 +9757,19 @@ function createHostMessageProvider(options) {
|
|
|
9533
9757
|
const external = findExternalRunById(supervisorId, ref);
|
|
9534
9758
|
if (external?.runtimeId) return unique(external);
|
|
9535
9759
|
}
|
|
9536
|
-
const peers = await listedPeers();
|
|
9760
|
+
const { peers, detail } = await listedPeers();
|
|
9761
|
+
if (detail) throw new Error(`the peer listing is incomplete: ${detail}`);
|
|
9537
9762
|
const exact = peers.find((peer) => peer.run_id === ref);
|
|
9538
9763
|
if (exact) return {
|
|
9539
9764
|
runId: exact.run_id,
|
|
9540
|
-
publicRunId: publicRunIdFor(exact.run_id),
|
|
9765
|
+
publicRunId: await publicRunIdFor(exact.run_id),
|
|
9541
9766
|
label: firstNonEmpty(exact.label, exact.run_id),
|
|
9542
9767
|
match: "unique"
|
|
9543
9768
|
};
|
|
9544
9769
|
const candidates = peers.filter((peer) => peer.label === ref).map((peer) => peer.run_id);
|
|
9545
9770
|
if (candidates.length === 1) return {
|
|
9546
9771
|
runId: candidates[0],
|
|
9547
|
-
publicRunId: publicRunIdFor(candidates[0]),
|
|
9772
|
+
publicRunId: await publicRunIdFor(candidates[0]),
|
|
9548
9773
|
label: ref,
|
|
9549
9774
|
match: "unique"
|
|
9550
9775
|
};
|
|
@@ -9558,18 +9783,38 @@ function createHostMessageProvider(options) {
|
|
|
9558
9783
|
match: "none"
|
|
9559
9784
|
};
|
|
9560
9785
|
},
|
|
9561
|
-
async state(runId) {
|
|
9786
|
+
async state(runId, publicRunId) {
|
|
9562
9787
|
if (runId === "supervisor") {
|
|
9563
|
-
const
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
resumable: false,
|
|
9567
|
-
status: translation.status
|
|
9568
|
-
};
|
|
9788
|
+
const { peers, detail } = await listedPeers();
|
|
9789
|
+
if (detail) throw new Error(`the peer listing is incomplete: ${detail}`);
|
|
9790
|
+
return peerFacts(peers.find((entry) => entry.run_id === SUPERVISOR_RUN_ID));
|
|
9569
9791
|
}
|
|
9570
|
-
|
|
9792
|
+
let status;
|
|
9793
|
+
try {
|
|
9794
|
+
status = await client.status(runId);
|
|
9795
|
+
} catch (error) {
|
|
9796
|
+
if (!statusReportsNoRuntime(error, runId)) throw error;
|
|
9797
|
+
const registered = Supervisor.getInstance(supervisorId)?.getRunById(runId) ?? findExternalRunById(supervisorId, runId);
|
|
9798
|
+
if (registered) {
|
|
9799
|
+
const terminal = await terminalSentinelStatus(registered.sentinelPath);
|
|
9800
|
+
if (terminal) return {
|
|
9801
|
+
live: false,
|
|
9802
|
+
resumable: resumableFromSession(registered.sessionId),
|
|
9803
|
+
status: terminal
|
|
9804
|
+
};
|
|
9805
|
+
}
|
|
9806
|
+
const listed = await peerRunState(runId);
|
|
9807
|
+
if (listed) return listed;
|
|
9808
|
+
throw error;
|
|
9809
|
+
}
|
|
9810
|
+
const info = publicRunInfo(publicRunId);
|
|
9811
|
+
if (info && typeof status.sentinel_file === "string" && status.sentinel_file !== info.sentinelPath) return {
|
|
9812
|
+
live: false,
|
|
9813
|
+
resumable: false,
|
|
9814
|
+
status: await terminalSentinelStatus(info.sentinelPath) ?? "unknown"
|
|
9815
|
+
};
|
|
9571
9816
|
const translation = translateRunFacts(typeof status.status === "string" ? status.status : "", typeof status.phase === "string" ? status.phase : "");
|
|
9572
|
-
let resumable =
|
|
9817
|
+
let resumable = resumableFromSession(status.session_id);
|
|
9573
9818
|
if (!resumable && typeof status.sentinel_file === "string") resumable = await sentinelResumable(status.sentinel_file);
|
|
9574
9819
|
return {
|
|
9575
9820
|
live: translation.live,
|
|
@@ -9577,7 +9822,8 @@ function createHostMessageProvider(options) {
|
|
|
9577
9822
|
status: translation.status
|
|
9578
9823
|
};
|
|
9579
9824
|
},
|
|
9580
|
-
async tell(targetRunId, message) {
|
|
9825
|
+
async tell(targetRunId, message, publicRunId) {
|
|
9826
|
+
const expected = expectedSentinel(publicRunId);
|
|
9581
9827
|
const messageId = newMsgID();
|
|
9582
9828
|
const body = JSON.stringify({
|
|
9583
9829
|
from: SUPERVISOR_RUN_ID,
|
|
@@ -9593,7 +9839,8 @@ function createHostMessageProvider(options) {
|
|
|
9593
9839
|
message_id: messageId,
|
|
9594
9840
|
generation,
|
|
9595
9841
|
body,
|
|
9596
|
-
no_reply: true
|
|
9842
|
+
no_reply: true,
|
|
9843
|
+
...expected ? { expected_sentinel_file: expected } : {}
|
|
9597
9844
|
});
|
|
9598
9845
|
} catch (error) {
|
|
9599
9846
|
const outcome = controlOutcome(error);
|
|
@@ -9608,7 +9855,8 @@ function createHostMessageProvider(options) {
|
|
|
9608
9855
|
messageId: firstNonEmpty(result.message_id, messageId)
|
|
9609
9856
|
};
|
|
9610
9857
|
},
|
|
9611
|
-
async ask(targetRunId, message, timeoutMs, signal) {
|
|
9858
|
+
async ask(targetRunId, message, timeoutMs, signal, publicRunId) {
|
|
9859
|
+
const expected = expectedSentinel(publicRunId);
|
|
9612
9860
|
const messageId = newMsgID();
|
|
9613
9861
|
const body = JSON.stringify({
|
|
9614
9862
|
id: messageId,
|
|
@@ -9625,7 +9873,8 @@ function createHostMessageProvider(options) {
|
|
|
9625
9873
|
to_run_id: targetRunId,
|
|
9626
9874
|
message_id: messageId,
|
|
9627
9875
|
generation,
|
|
9628
|
-
body
|
|
9876
|
+
body,
|
|
9877
|
+
...expected ? { expected_sentinel_file: expected } : {}
|
|
9629
9878
|
});
|
|
9630
9879
|
} catch (error) {
|
|
9631
9880
|
const outcome = controlOutcome(error);
|
|
@@ -9754,10 +10003,20 @@ function createHostMessageProvider(options) {
|
|
|
9754
10003
|
previousLabel: renamed.previous_label
|
|
9755
10004
|
};
|
|
9756
10005
|
},
|
|
9757
|
-
async last(runId) {
|
|
10006
|
+
async last(runId, publicRunId) {
|
|
10007
|
+
const expected = expectedSentinel(publicRunId);
|
|
10008
|
+
if (expected) {
|
|
10009
|
+
let status;
|
|
10010
|
+
try {
|
|
10011
|
+
status = await client.status(runId);
|
|
10012
|
+
} catch (error) {
|
|
10013
|
+
throw authorityError(error);
|
|
10014
|
+
}
|
|
10015
|
+
if (typeof status.sentinel_file === "string" && status.sentinel_file !== expected) throw authorityError(new RpcError(-32e3, `turn no longer current for runtime ${runId}`, { code: "invalid-request" }));
|
|
10016
|
+
}
|
|
9758
10017
|
let last;
|
|
9759
10018
|
try {
|
|
9760
|
-
last = await client.sessionLast(runId);
|
|
10019
|
+
last = await client.sessionLast(runId, expected);
|
|
9761
10020
|
} catch (error) {
|
|
9762
10021
|
throw authorityError(error);
|
|
9763
10022
|
}
|
|
@@ -9771,7 +10030,11 @@ function createHostMessageProvider(options) {
|
|
|
9771
10030
|
};
|
|
9772
10031
|
},
|
|
9773
10032
|
async peers() {
|
|
9774
|
-
|
|
10033
|
+
const { peers, detail } = await listedPeers();
|
|
10034
|
+
return {
|
|
10035
|
+
peers,
|
|
10036
|
+
detail
|
|
10037
|
+
};
|
|
9775
10038
|
},
|
|
9776
10039
|
async recordLastTurn(text, stopReason, truncated) {
|
|
9777
10040
|
await client.sessionLastSet(SUPERVISOR_RUN_ID, text, stopReason, truncated);
|
|
@@ -9842,13 +10105,20 @@ function renderReply(reply) {
|
|
|
9842
10105
|
/**
|
|
9843
10106
|
* Build a child provider over the surface's inherited broker. A child never
|
|
9844
10107
|
* learns a public run id, and it cannot read a sentinel, so it is never
|
|
9845
|
-
* resumable from here: the supervisor owns a resume.
|
|
10108
|
+
* resumable from here: the supervisor owns a resume. Listing-backed lookups
|
|
10109
|
+
* fail fast: an incomplete listing refuses every resolution and state read,
|
|
10110
|
+
* even for a peer the partial set contains, so a wire `peers_error` resolves
|
|
10111
|
+
* the same way the Go providers do. Self resolution bypasses the listing.
|
|
9846
10112
|
*/
|
|
9847
10113
|
function createChildMessageProvider(options) {
|
|
9848
10114
|
const { surface, runId, broker, pendingAsks, pollGeneration } = options;
|
|
9849
10115
|
const claims = /* @__PURE__ */ new Map();
|
|
9850
10116
|
async function listedPeers() {
|
|
9851
|
-
|
|
10117
|
+
const result = await broker.peers();
|
|
10118
|
+
return {
|
|
10119
|
+
peers: result.peers,
|
|
10120
|
+
detail: result.detail
|
|
10121
|
+
};
|
|
9852
10122
|
}
|
|
9853
10123
|
const provider = {
|
|
9854
10124
|
caller: () => ({
|
|
@@ -9867,7 +10137,8 @@ function createChildMessageProvider(options) {
|
|
|
9867
10137
|
label: ref,
|
|
9868
10138
|
match: "unique"
|
|
9869
10139
|
};
|
|
9870
|
-
const peers = await listedPeers();
|
|
10140
|
+
const { peers, detail } = await listedPeers();
|
|
10141
|
+
if (detail) throw new Error(`the peer listing is incomplete: ${detail}`);
|
|
9871
10142
|
const exact = peers.find((peer) => peer.run_id === ref);
|
|
9872
10143
|
if (exact) return {
|
|
9873
10144
|
runId: exact.run_id,
|
|
@@ -9891,14 +10162,11 @@ function createChildMessageProvider(options) {
|
|
|
9891
10162
|
};
|
|
9892
10163
|
},
|
|
9893
10164
|
async state(targetRunId) {
|
|
9894
|
-
const
|
|
10165
|
+
const { peers, detail } = await listedPeers();
|
|
10166
|
+
if (detail) throw new Error(`the peer listing is incomplete: ${detail}`);
|
|
10167
|
+
const peer = peers.find((entry) => entry.run_id === targetRunId);
|
|
9895
10168
|
if (!peer) throw new Error(`run ${targetRunId} is not visible`);
|
|
9896
|
-
|
|
9897
|
-
return {
|
|
9898
|
-
live: translation.live,
|
|
9899
|
-
resumable: false,
|
|
9900
|
-
status: translation.status
|
|
9901
|
-
};
|
|
10169
|
+
return peerFacts(peer);
|
|
9902
10170
|
},
|
|
9903
10171
|
tell: (targetRunId, message) => broker.tell(targetRunId, message, newMsgID()),
|
|
9904
10172
|
ask: (targetRunId, message, timeoutMs, signal) => broker.ask(targetRunId, message, newMsgID(), timeoutMs, signal),
|
|
@@ -9920,7 +10188,11 @@ function createChildMessageProvider(options) {
|
|
|
9920
10188
|
}
|
|
9921
10189
|
//#endregion
|
|
9922
10190
|
//#region src/tools/peers.ts
|
|
9923
|
-
/** The one avenor_peers handler; it resolves and reads through the bound provider.
|
|
10191
|
+
/** The one avenor_peers handler; it resolves and reads through the bound provider.
|
|
10192
|
+
*
|
|
10193
|
+
* Admission belongs to the shared router for every surface, so the argument is
|
|
10194
|
+
* the untyped JSON a tool call carries and `executePeers` validates it first —
|
|
10195
|
+
* the same shape `messageTool` takes. */
|
|
9924
10196
|
async function peersTool(args, provider) {
|
|
9925
10197
|
return executePeers(provider, args);
|
|
9926
10198
|
}
|
package/dist/postinstall.d.ts
CHANGED
|
@@ -4,6 +4,6 @@ import { n as getVersion, r as installerBinaryPath, t as getInstallDir } from ".
|
|
|
4
4
|
declare function platformMapping(platform: string, arch: string): string | null;
|
|
5
5
|
declare function verifyChecksum(buffer: Buffer, expectedHash: string): boolean;
|
|
6
6
|
declare function parseChecksums(content: string, assetName: string): string | null;
|
|
7
|
-
declare function postinstall(fetchFn?:
|
|
7
|
+
declare function postinstall(fetchFn?: (url: string) => Promise<Response>, platform?: string, arch?: string): Promise<void>;
|
|
8
8
|
//#endregion
|
|
9
9
|
export { getInstallDir, getVersion, installerBinaryPath, parseChecksums, platformMapping, postinstall, verifyChecksum };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dougbots/avenor-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsdown",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
26
27
|
"test": "bun test",
|
|
27
28
|
"test:e2e": "AVENOR_REAL_INTEGRATION=1 bun test",
|
|
28
29
|
"dev": "tsdown --watch",
|