@tanstack/ai 0.46.0 → 0.47.2
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/esm/activities/chat/index.d.ts +36 -11
- package/dist/esm/activities/chat/index.js +440 -79
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.d.ts +1 -0
- package/dist/esm/activities/chat/messages.js +12 -7
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/builder.d.ts +7 -2
- package/dist/esm/activities/chat/middleware/builder.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +10 -3
- package/dist/esm/activities/chat/middleware/compose.js +55 -0
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/chat/middleware/define.d.ts +6 -3
- package/dist/esm/activities/chat/middleware/define.js.map +1 -1
- package/dist/esm/activities/chat/middleware/generic-interrupts.d.ts +13 -0
- package/dist/esm/activities/chat/middleware/generic-interrupts.js +8 -0
- package/dist/esm/activities/chat/middleware/generic-interrupts.js.map +1 -0
- package/dist/esm/activities/chat/middleware/index.d.ts +4 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +54 -3
- package/dist/esm/activities/chat/middleware/types.js +16 -0
- package/dist/esm/activities/chat/middleware/types.js.map +1 -0
- package/dist/esm/activities/chat/stream/processor.js +18 -5
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +6 -0
- package/dist/esm/adapter-internals.js +4 -1
- package/dist/esm/client.d.ts +4 -0
- package/dist/esm/client.js +3 -1
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/generic-interrupt-continuation.d.ts +45 -0
- package/dist/esm/generic-interrupt-continuation.js +80 -0
- package/dist/esm/generic-interrupt-continuation.js.map +1 -0
- package/dist/esm/index.d.ts +6 -1
- package/dist/esm/index.js +4 -1
- package/dist/esm/interrupt-definition.d.ts +113 -0
- package/dist/esm/interrupt-definition.js +169 -0
- package/dist/esm/interrupt-definition.js.map +1 -0
- package/dist/esm/interrupt-resume.d.ts +3 -0
- package/dist/esm/interrupt-resume.js +77 -16
- package/dist/esm/interrupt-resume.js.map +1 -1
- package/dist/esm/interrupts.d.ts +12 -3
- package/dist/esm/interrupts.js.map +1 -1
- package/dist/esm/types.d.ts +11 -3
- package/dist/esm/utilities/chat-params.js +10 -1
- package/dist/esm/utilities/chat-params.js.map +1 -1
- package/package.json +3 -3
- package/skills/ai-core/media-generation/SKILL.md +4 -1
- package/skills/ai-core/middleware/SKILL.md +53 -44
- package/skills/ai-core/structured-outputs/SKILL.md +59 -55
- package/skills/ai-core/tool-calling/SKILL.md +54 -1
- package/src/activities/chat/index.ts +1030 -211
- package/src/activities/chat/messages.ts +11 -3
- package/src/activities/chat/middleware/builder.ts +29 -4
- package/src/activities/chat/middleware/compose.ts +95 -5
- package/src/activities/chat/middleware/define.ts +13 -3
- package/src/activities/chat/middleware/generic-interrupts.ts +26 -0
- package/src/activities/chat/middleware/index.ts +15 -0
- package/src/activities/chat/middleware/types.ts +127 -2
- package/src/activities/chat/stream/processor.ts +21 -0
- package/src/adapter-internals.ts +20 -0
- package/src/client.ts +20 -0
- package/src/generic-interrupt-continuation.ts +162 -0
- package/src/index.ts +34 -0
- package/src/interrupt-definition.ts +581 -0
- package/src/interrupt-resume.ts +156 -25
- package/src/interrupts.ts +13 -3
- package/src/types.ts +11 -3
- package/src/utilities/chat-params.ts +16 -3
|
@@ -11,19 +11,22 @@ import { canonicalInterruptJson, digestInterruptJson } from "../../interrupt-ser
|
|
|
11
11
|
import "../../interrupts.js";
|
|
12
12
|
import { convertSchemaForStructuredOutput, convertSchemaToJsonSchema, isStandardSchema, parseWithStandardSchema } from "./tools/schema-converter.js";
|
|
13
13
|
import { hashSchemaInput, normalizeApprovalSchema } from "./tools/approval-schema.js";
|
|
14
|
-
import { INTERRUPT_BINDING_METADATA_KEY, InterruptResumeValidationError, readUnopenedInterruptBinding, validateInterruptResumeBatch } from "../../interrupt-resume.js";
|
|
14
|
+
import { INTERRUPT_BINDING_METADATA_KEY, InterruptResumeValidationError, readInterruptBinding, readUnopenedInterruptBinding, validateInterruptResumeBatch } from "../../interrupt-resume.js";
|
|
15
|
+
import { INTERRUPT_PAYLOAD_METADATA_KEY, createInterruptBinding, rehydrateInterruptRequest } from "../../interrupt-definition.js";
|
|
16
|
+
import { readGenericInterruptContinuation } from "../../generic-interrupt-continuation.js";
|
|
15
17
|
import { normalizeToolResult } from "../../utilities/tool-result.js";
|
|
16
18
|
import { isProviderExecutedToolCall } from "../../utilities/provider-executed.js";
|
|
17
19
|
import { LazyToolManager } from "./tools/lazy-tool-manager.js";
|
|
18
20
|
import { assertUniqueToolNames } from "./tools/unique-tool-names.js";
|
|
19
21
|
import { MiddlewareAbortError, ToolCallManager, executeToolCalls } from "./tools/tool-calls.js";
|
|
20
22
|
import { maxIterations } from "./agent-loop-strategies.js";
|
|
21
|
-
import { convertMessagesToModelMessages, generateMessageId, modelMessageToUIMessage } from "./messages.js";
|
|
23
|
+
import { convertMessagesToModelMessages, generateMessageId, modelMessageToUIMessage, safeJsonStringify } from "./messages.js";
|
|
22
24
|
import { MiddlewareRunner } from "./middleware/compose.js";
|
|
23
25
|
import { provideSandboxRuntime } from "./middleware/sandbox-runtime.js";
|
|
24
26
|
import { provideRunDisconnect } from "./middleware/run-disconnect.js";
|
|
25
27
|
import { validateCapabilities } from "./middleware/validate.js";
|
|
26
28
|
import { MCPManager } from "./mcp/manager.js";
|
|
29
|
+
import { provideGenericInterruptDefinitionRegistry } from "./middleware/generic-interrupts.js";
|
|
27
30
|
import "./adapter.js";
|
|
28
31
|
import { devtoolsMiddleware } from "@tanstack/ai-event-client";
|
|
29
32
|
import { undoNullWidening } from "@tanstack/ai-utils";
|
|
@@ -51,38 +54,11 @@ function structuralInterruptFailure(error) {
|
|
|
51
54
|
};
|
|
52
55
|
}
|
|
53
56
|
function normalizePublicInterruptBinding(value, expectedInterruptId) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
interruptId: binding.interruptId,
|
|
60
|
-
interruptedRunId: binding.interruptedRunId,
|
|
61
|
-
generation: binding.generation,
|
|
62
|
-
responseSchemaHash: binding.responseSchemaHash,
|
|
63
|
-
...typeof binding.expiresAt === "string" ? { expiresAt: binding.expiresAt } : {}
|
|
64
|
-
};
|
|
65
|
-
if (binding.kind === "generic") return {
|
|
66
|
-
kind: binding.kind,
|
|
67
|
-
...base
|
|
68
|
-
};
|
|
69
|
-
if (typeof binding.toolName !== "string" || typeof binding.toolCallId !== "string") return;
|
|
70
|
-
if (binding.kind === "client-tool-execution" && typeof binding.outputSchemaHash === "string") return {
|
|
71
|
-
kind: binding.kind,
|
|
72
|
-
...base,
|
|
73
|
-
toolName: binding.toolName,
|
|
74
|
-
toolCallId: binding.toolCallId,
|
|
75
|
-
outputSchemaHash: binding.outputSchemaHash
|
|
76
|
-
};
|
|
77
|
-
if (binding.kind === "tool-approval" && Object.prototype.hasOwnProperty.call(binding, "originalArgs") && typeof binding.inputSchemaHash === "string" && typeof binding.approvalSchemaHash === "string") return {
|
|
78
|
-
kind: binding.kind,
|
|
79
|
-
...base,
|
|
80
|
-
toolName: binding.toolName,
|
|
81
|
-
toolCallId: binding.toolCallId,
|
|
82
|
-
originalArgs: binding.originalArgs,
|
|
83
|
-
inputSchemaHash: binding.inputSchemaHash,
|
|
84
|
-
approvalSchemaHash: binding.approvalSchemaHash
|
|
85
|
-
};
|
|
57
|
+
return readInterruptBinding({
|
|
58
|
+
id: expectedInterruptId,
|
|
59
|
+
reason: "",
|
|
60
|
+
metadata: { [INTERRUPT_BINDING_METADATA_KEY]: value }
|
|
61
|
+
});
|
|
86
62
|
}
|
|
87
63
|
/**
|
|
88
64
|
* Create typed options for the chat() function without executing.
|
|
@@ -120,12 +96,15 @@ function combineAbortSignals(a, b) {
|
|
|
120
96
|
}
|
|
121
97
|
var TextEngine = class {
|
|
122
98
|
adapter;
|
|
99
|
+
interruptDefinitions;
|
|
123
100
|
params;
|
|
124
101
|
systemPrompts;
|
|
125
102
|
tools;
|
|
126
103
|
loopStrategy;
|
|
127
104
|
toolCallManager;
|
|
128
105
|
lazyToolManager;
|
|
106
|
+
/** A public interruption terminal must always have this run's start event. */
|
|
107
|
+
hasPublicRunStarted = false;
|
|
129
108
|
initialMessageCount;
|
|
130
109
|
requestId;
|
|
131
110
|
streamId;
|
|
@@ -155,6 +134,8 @@ var TextEngine = class {
|
|
|
155
134
|
finishedEvent = null;
|
|
156
135
|
streamedToolErrorResults = /* @__PURE__ */ new Map();
|
|
157
136
|
deferredToolCallRunFinishedChunks = [];
|
|
137
|
+
/** The model terminal is held until afterModel can choose an interrupt. */
|
|
138
|
+
deferredModelRunFinishedChunks = [];
|
|
158
139
|
earlyTermination = false;
|
|
159
140
|
toolPhase = "continue";
|
|
160
141
|
cyclePhase = "processText";
|
|
@@ -164,6 +145,8 @@ var TextEngine = class {
|
|
|
164
145
|
resumeClientToolResults = /* @__PURE__ */ new Map();
|
|
165
146
|
resumeDeniedToolResults = /* @__PURE__ */ new Map();
|
|
166
147
|
resumeCancelledToolCallIds = /* @__PURE__ */ new Set();
|
|
148
|
+
resumeGenericInterrupts = /* @__PURE__ */ new Map();
|
|
149
|
+
resumeGenericInterruptRequests = /* @__PURE__ */ new Map();
|
|
167
150
|
threadId;
|
|
168
151
|
runIdOverride;
|
|
169
152
|
parentRunIdOverride;
|
|
@@ -185,6 +168,8 @@ var TextEngine = class {
|
|
|
185
168
|
disconnectListeners = [];
|
|
186
169
|
logger;
|
|
187
170
|
structuredOutputResult = null;
|
|
171
|
+
structuredOutputMessageId = null;
|
|
172
|
+
structuredOutputMessageCreatedAt = null;
|
|
188
173
|
combinedStartEmitted = false;
|
|
189
174
|
combinedStructuredMessageId = null;
|
|
190
175
|
validatedStructuredOutput = void 0;
|
|
@@ -195,6 +180,7 @@ var TextEngine = class {
|
|
|
195
180
|
constructor(config, logger) {
|
|
196
181
|
this.logger = logger;
|
|
197
182
|
this.adapter = config.adapter;
|
|
183
|
+
this.interruptDefinitions = new Map((config.params.interrupts ?? []).map((definition) => [definition.id, definition]));
|
|
198
184
|
this.finalStructuredOutput = config.finalStructuredOutput;
|
|
199
185
|
this.params = config.params;
|
|
200
186
|
this.systemPrompts = config.params.systemPrompts || [];
|
|
@@ -266,6 +252,7 @@ var TextEngine = class {
|
|
|
266
252
|
this.disconnectListeners.push(listener);
|
|
267
253
|
if (this.disconnected) this.runDisconnectListener(listener);
|
|
268
254
|
} });
|
|
255
|
+
provideGenericInterruptDefinitionRegistry(this.middlewareCtx, { definitions: this.interruptDefinitions });
|
|
269
256
|
provideSandboxRuntime(this.middlewareCtx, {
|
|
270
257
|
logger: this.logger,
|
|
271
258
|
emit: (event) => {
|
|
@@ -324,10 +311,25 @@ var TextEngine = class {
|
|
|
324
311
|
const transformedConfig = await this.middlewareRunner.runOnConfig(this.middlewareCtx, initialConfig);
|
|
325
312
|
this.applyMiddlewareConfig(transformedConfig);
|
|
326
313
|
await this.applyEphemeralInterruptResume(transformedConfig);
|
|
314
|
+
await this.applyDurableGenericInterruptResolution();
|
|
327
315
|
await this.middlewareRunner.runOnStart(this.middlewareCtx);
|
|
316
|
+
if (this.earlyTermination) {
|
|
317
|
+
yield* this.emitSuccessfulEarlyTermination();
|
|
318
|
+
if (!this.terminalHookCalled) {
|
|
319
|
+
this.terminalHookCalled = true;
|
|
320
|
+
await this.middlewareRunner.runOnFinish(this.middlewareCtx, {
|
|
321
|
+
finishReason: this.lastFinishReason,
|
|
322
|
+
duration: Date.now() - this.streamStartTime,
|
|
323
|
+
content: this.accumulatedContent,
|
|
324
|
+
usage: this.finishedEvent?.usage
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
328
329
|
if ((yield* this.checkForPendingToolCalls()) === "wait") return;
|
|
329
330
|
if (!(!!this.finalStructuredOutput && this.tools.length === 0 && this.finalStructuredOutput.nativeCombined !== true)) do {
|
|
330
|
-
if (this.earlyTermination
|
|
331
|
+
if (this.earlyTermination) break;
|
|
332
|
+
if (this.isCancelled()) return;
|
|
331
333
|
this.logger.agentLoop(`iteration=${this.middlewareCtx.iteration}`, { iteration: this.middlewareCtx.iteration });
|
|
332
334
|
await this.beginCycle();
|
|
333
335
|
if (this.cyclePhase === "processText") {
|
|
@@ -336,7 +338,20 @@ var TextEngine = class {
|
|
|
336
338
|
const iterConfig = this.buildMiddlewareConfig();
|
|
337
339
|
const iterTransformedConfig = await this.middlewareRunner.runOnConfig(this.middlewareCtx, iterConfig);
|
|
338
340
|
this.applyMiddlewareConfig(iterTransformedConfig);
|
|
341
|
+
if (yield* this.emitBoundaryInterrupts("beforeModel", this.createSyntheticFinishedEvent())) {
|
|
342
|
+
this.setToolPhase("wait");
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
339
345
|
yield* this.streamModelResponse();
|
|
346
|
+
if (this.earlyTermination) break;
|
|
347
|
+
if (yield* this.emitBoundaryInterrupts("afterModel", this.finishedEvent ?? this.createSyntheticFinishedEvent())) {
|
|
348
|
+
this.setToolPhase("wait");
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (this.shouldExecuteToolPhase()) {
|
|
352
|
+
this.deferredToolCallRunFinishedChunks.push(...this.deferredModelRunFinishedChunks);
|
|
353
|
+
this.deferredModelRunFinishedChunks = [];
|
|
354
|
+
} else yield* this.flushDeferredModelRunFinishedChunks();
|
|
340
355
|
} else yield* this.processToolCalls();
|
|
341
356
|
this.endCycle();
|
|
342
357
|
} while (await this.shouldContinue());
|
|
@@ -358,7 +373,7 @@ var TextEngine = class {
|
|
|
358
373
|
duration: Date.now() - this.streamStartTime
|
|
359
374
|
});
|
|
360
375
|
} else {
|
|
361
|
-
this.
|
|
376
|
+
this.addTerminalAssistantMessages();
|
|
362
377
|
this.terminalHookCalled = true;
|
|
363
378
|
await this.middlewareRunner.runOnFinish(this.middlewareCtx, {
|
|
364
379
|
finishReason: this.lastFinishReason,
|
|
@@ -513,7 +528,10 @@ var TextEngine = class {
|
|
|
513
528
|
if (chunk.type === EventType.CUSTOM && chunk.name === "structured-output.start") {
|
|
514
529
|
this.combinedStartEmitted = true;
|
|
515
530
|
const startValue = chunk.value;
|
|
516
|
-
if (startValue && typeof startValue === "object" && "messageId" in startValue && typeof startValue.messageId === "string")
|
|
531
|
+
if (startValue && typeof startValue === "object" && "messageId" in startValue && typeof startValue.messageId === "string") {
|
|
532
|
+
this.combinedStructuredMessageId = startValue.messageId;
|
|
533
|
+
this.captureStructuredOutputMessageIdentity(startValue.messageId);
|
|
534
|
+
}
|
|
517
535
|
}
|
|
518
536
|
let outboundChunk = chunk;
|
|
519
537
|
if (this.finalStructuredOutput?.source === "event" && chunk.type === EventType.CUSTOM && chunk.name === "structured-output.complete") {
|
|
@@ -526,6 +544,11 @@ var TextEngine = class {
|
|
|
526
544
|
};
|
|
527
545
|
this.combinedCompleteEmitted = true;
|
|
528
546
|
const value = chunk.value;
|
|
547
|
+
const completeMessageId = readCustomEventMessageId(value);
|
|
548
|
+
if (completeMessageId) {
|
|
549
|
+
this.combinedStructuredMessageId = completeMessageId;
|
|
550
|
+
this.captureStructuredOutputMessageIdentity(completeMessageId);
|
|
551
|
+
}
|
|
529
552
|
if (object !== parsed.object && value && typeof value === "object") outboundChunk = {
|
|
530
553
|
...chunk,
|
|
531
554
|
value: {
|
|
@@ -558,10 +581,15 @@ var TextEngine = class {
|
|
|
558
581
|
const suppressAgentLifecycle = !!this.finalStructuredOutput && this.finalStructuredOutput.yieldChunks && this.finalStructuredOutput.nativeCombined !== true;
|
|
559
582
|
for (const outputChunk of outputChunks) {
|
|
560
583
|
if (suppressAgentLifecycle && (outputChunk.type === EventType.RUN_STARTED || outputChunk.type === EventType.RUN_FINISHED)) continue;
|
|
584
|
+
if (outputChunk.type === EventType.RUN_FINISHED) {
|
|
585
|
+
this.deferredModelRunFinishedChunks.push(outputChunk);
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
561
588
|
if (this.shouldDeferToolCallRunFinished(outputChunk)) {
|
|
562
589
|
this.deferredToolCallRunFinishedChunks.push(outputChunk);
|
|
563
590
|
continue;
|
|
564
591
|
}
|
|
592
|
+
if (outputChunk.type === EventType.RUN_STARTED) this.hasPublicRunStarted = true;
|
|
565
593
|
this.logger.output(`type=${outputChunk.type}`, { chunk: outputChunk });
|
|
566
594
|
yield outputChunk;
|
|
567
595
|
this.middlewareCtx.chunkIndex++;
|
|
@@ -617,6 +645,10 @@ var TextEngine = class {
|
|
|
617
645
|
this.streamIdentityCaptured = true;
|
|
618
646
|
}
|
|
619
647
|
}
|
|
648
|
+
captureStructuredOutputMessageIdentity(messageId) {
|
|
649
|
+
this.structuredOutputMessageId = messageId;
|
|
650
|
+
this.structuredOutputMessageCreatedAt ??= /* @__PURE__ */ new Date();
|
|
651
|
+
}
|
|
620
652
|
handleToolCallStartEvent(chunk) {
|
|
621
653
|
if (typeof chunk.parentMessageId === "string" && chunk.parentMessageId !== "") this.captureStreamMessageIdentity(chunk.parentMessageId);
|
|
622
654
|
this.toolCallManager.addToolCallStartEvent(chunk);
|
|
@@ -643,8 +675,8 @@ var TextEngine = class {
|
|
|
643
675
|
}
|
|
644
676
|
handleRunErrorEvent(chunk) {
|
|
645
677
|
this.earlyTermination = true;
|
|
646
|
-
if (this.
|
|
647
|
-
const message = chunk.message || chunk.error?.message || "Run failed
|
|
678
|
+
if (this.finalizationError === null) {
|
|
679
|
+
const message = chunk.message || chunk.error?.message || "Run failed";
|
|
648
680
|
this.finalizationError = {
|
|
649
681
|
message,
|
|
650
682
|
...chunk.code !== void 0 ? { code: chunk.code } : chunk.error?.code !== void 0 ? { code: chunk.error.code } : {}
|
|
@@ -714,6 +746,11 @@ var TextEngine = class {
|
|
|
714
746
|
if (deferredErrorResults.length > 0) for (const chunk of this.buildToolResultChunks(deferredErrorResults, finishEvent, argsMap)) yield* this.pipeThroughMiddleware(chunk);
|
|
715
747
|
return "continue";
|
|
716
748
|
}
|
|
749
|
+
this.middlewareCtx.phase = "beforeTools";
|
|
750
|
+
if (yield* this.emitBoundaryInterrupts("beforeTools", finishEvent, executablePendingCalls)) {
|
|
751
|
+
this.setToolPhase("wait");
|
|
752
|
+
return "wait";
|
|
753
|
+
}
|
|
717
754
|
const { approvals, clientToolResults } = this.collectClientState();
|
|
718
755
|
const generator = executeToolCalls(executablePendingCalls, this.resolveExecutableTools(executablePendingCalls), approvals, clientToolResults, (eventName, data) => this.createCustomEventChunk(eventName, data), {
|
|
719
756
|
onBeforeToolCall: async (toolCall, tool, args) => {
|
|
@@ -802,6 +839,10 @@ var TextEngine = class {
|
|
|
802
839
|
return;
|
|
803
840
|
}
|
|
804
841
|
this.middlewareCtx.phase = "beforeTools";
|
|
842
|
+
if (yield* this.emitBoundaryInterrupts("beforeTools", finishEvent, executableToolCalls)) {
|
|
843
|
+
this.setToolPhase("wait");
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
805
846
|
const { approvals, clientToolResults } = this.collectClientState();
|
|
806
847
|
const generator = executeToolCalls(executableToolCalls, this.resolveExecutableTools(executableToolCalls), approvals, clientToolResults, (eventName, data) => this.createCustomEventChunk(eventName, data), {
|
|
807
848
|
onBeforeToolCall: async (toolCall, tool, args) => {
|
|
@@ -842,14 +883,22 @@ var TextEngine = class {
|
|
|
842
883
|
needsApproval: executionResult.needsApproval,
|
|
843
884
|
needsClientExecution: executionResult.needsClientExecution
|
|
844
885
|
});
|
|
886
|
+
const afterToolBoundaryChunks = this.buildToolResultChunks(allResults, finishEvent);
|
|
887
|
+
const afterToolRequests = await this.middlewareRunner.runOnInterruptBoundary(this.middlewareCtx);
|
|
888
|
+
if (afterToolRequests.length > 0) {
|
|
889
|
+
for (const chunk of afterToolBoundaryChunks) yield* this.pipeThroughMiddleware(chunk);
|
|
890
|
+
yield* this.emitBoundaryInterrupts("afterTools", finishEvent, toolCalls, afterToolRequests);
|
|
891
|
+
this.setToolPhase("wait");
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
845
894
|
if (executionResult.needsApproval.length > 0 || executionResult.needsClientExecution.length > 0) {
|
|
846
|
-
if (allResults.length > 0) for (const chunk of
|
|
895
|
+
if (allResults.length > 0) for (const chunk of afterToolBoundaryChunks) yield* this.pipeThroughMiddleware(chunk);
|
|
847
896
|
const emitted = yield* this.emitActionableInterruptBoundary(finishEvent, executionResult.needsApproval, executionResult.needsClientExecution);
|
|
848
897
|
this.setToolPhase(emitted ? "wait" : "stop");
|
|
849
898
|
return;
|
|
850
899
|
}
|
|
851
900
|
yield* this.flushDeferredToolCallRunFinishedChunks();
|
|
852
|
-
const toolResultChunks =
|
|
901
|
+
const toolResultChunks = afterToolBoundaryChunks;
|
|
853
902
|
for (const chunk of toolResultChunks) yield* this.pipeThroughMiddleware(chunk);
|
|
854
903
|
if (this.lazyToolManager.hasNewlyDiscoveredTools()) {
|
|
855
904
|
this.tools = this.lazyToolManager.getActiveTools();
|
|
@@ -871,6 +920,37 @@ var TextEngine = class {
|
|
|
871
920
|
}
|
|
872
921
|
this.deferredToolCallRunFinishedChunks = [];
|
|
873
922
|
}
|
|
923
|
+
*flushDeferredModelRunFinishedChunks() {
|
|
924
|
+
for (const chunk of this.deferredModelRunFinishedChunks) {
|
|
925
|
+
this.logger.output(`type=${chunk.type}`, { chunk });
|
|
926
|
+
yield chunk;
|
|
927
|
+
this.middlewareCtx.chunkIndex++;
|
|
928
|
+
}
|
|
929
|
+
this.deferredModelRunFinishedChunks = [];
|
|
930
|
+
}
|
|
931
|
+
async *emitSyntheticRunStarted(finishEvent) {
|
|
932
|
+
if (this.hasPublicRunStarted) return;
|
|
933
|
+
yield* this.pipeThroughMiddleware({
|
|
934
|
+
type: EventType.RUN_STARTED,
|
|
935
|
+
runId: finishEvent.runId,
|
|
936
|
+
threadId: finishEvent.threadId,
|
|
937
|
+
model: finishEvent.model,
|
|
938
|
+
timestamp: Date.now()
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
async *emitSuccessfulEarlyTermination() {
|
|
942
|
+
this.lastFinishReason = "stop";
|
|
943
|
+
const finishEvent = {
|
|
944
|
+
...this.createSyntheticFinishedEvent(),
|
|
945
|
+
finishReason: "stop"
|
|
946
|
+
};
|
|
947
|
+
yield* this.emitSyntheticRunStarted(finishEvent);
|
|
948
|
+
yield* this.pipeThroughMiddleware({
|
|
949
|
+
...finishEvent,
|
|
950
|
+
timestamp: Date.now(),
|
|
951
|
+
outcome: { type: "success" }
|
|
952
|
+
});
|
|
953
|
+
}
|
|
874
954
|
discardDeferredToolCallRunFinishedChunks() {
|
|
875
955
|
this.deferredToolCallRunFinishedChunks = [];
|
|
876
956
|
}
|
|
@@ -889,18 +969,61 @@ var TextEngine = class {
|
|
|
889
969
|
}];
|
|
890
970
|
this.middlewareCtx.messages = this.messages;
|
|
891
971
|
}
|
|
892
|
-
|
|
972
|
+
addTerminalAssistantMessages() {
|
|
893
973
|
this.finalizeCurrentThinkingStep();
|
|
894
|
-
|
|
895
|
-
const
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
}
|
|
974
|
+
const structuredResult = this.structuredOutputResult;
|
|
975
|
+
const raw = structuredResult ? structuredResult.rawText || safeJsonStringify(structuredResult.data) : "";
|
|
976
|
+
const structuredOutput = structuredResult ? {
|
|
977
|
+
type: "structured-output",
|
|
978
|
+
status: "complete",
|
|
979
|
+
data: structuredResult.data,
|
|
980
|
+
partial: structuredResult.data,
|
|
981
|
+
raw,
|
|
982
|
+
...structuredResult.reasoning !== void 0 ? { reasoning: structuredResult.reasoning } : {}
|
|
983
|
+
} : void 0;
|
|
984
|
+
const nativeCombined = this.finalStructuredOutput?.nativeCombined === true;
|
|
985
|
+
const eventSourced = this.finalStructuredOutput?.source === "event";
|
|
986
|
+
const structuredId = this.structuredOutputMessageId ?? this.combinedStructuredMessageId ?? this.currentMessageId ?? this.createId("msg");
|
|
987
|
+
const splitStructuredMessage = Boolean(structuredOutput) && (!nativeCombined || eventSourced) && this.currentMessageId != null && structuredId !== this.currentMessageId;
|
|
988
|
+
const messages = [...this.middlewareCtx.messages];
|
|
989
|
+
const existingStructuredIndex = messages.findIndex((message) => message.role === "assistant" && message.id === structuredId);
|
|
990
|
+
const currentTurnAlreadyRecorded = messages.some((message) => message.role === "assistant" && message.id === this.currentMessageId);
|
|
991
|
+
const thinking = this.accumulatedThinking.length > 0 ? this.accumulatedThinking : void 0;
|
|
992
|
+
const startedLength = messages.length;
|
|
993
|
+
if (structuredOutput && existingStructuredIndex >= 0) {
|
|
994
|
+
const existing = messages[existingStructuredIndex];
|
|
995
|
+
if (existing) messages[existingStructuredIndex] = {
|
|
996
|
+
...existing,
|
|
997
|
+
content: raw || existing.content,
|
|
998
|
+
structuredOutput
|
|
999
|
+
};
|
|
1000
|
+
} else if (structuredOutput && !splitStructuredMessage) {
|
|
1001
|
+
if (!currentTurnAlreadyRecorded) messages.push({
|
|
1002
|
+
role: "assistant",
|
|
1003
|
+
content: this.accumulatedContent || raw || null,
|
|
1004
|
+
id: structuredId,
|
|
1005
|
+
createdAt: this.currentMessageCreatedAt ?? this.structuredOutputMessageCreatedAt ?? /* @__PURE__ */ new Date(),
|
|
1006
|
+
structuredOutput,
|
|
1007
|
+
...thinking ? { thinking } : {}
|
|
1008
|
+
});
|
|
1009
|
+
} else {
|
|
1010
|
+
if (!currentTurnAlreadyRecorded && (this.accumulatedContent !== "" || thinking)) messages.push({
|
|
1011
|
+
role: "assistant",
|
|
1012
|
+
content: this.accumulatedContent || null,
|
|
1013
|
+
id: this.currentMessageId ?? this.createId("msg"),
|
|
1014
|
+
createdAt: this.currentMessageCreatedAt ?? /* @__PURE__ */ new Date(),
|
|
1015
|
+
...thinking ? { thinking } : {}
|
|
1016
|
+
});
|
|
1017
|
+
if (structuredOutput) messages.push({
|
|
1018
|
+
role: "assistant",
|
|
1019
|
+
content: raw || null,
|
|
1020
|
+
id: structuredId,
|
|
1021
|
+
createdAt: this.structuredOutputMessageCreatedAt ?? /* @__PURE__ */ new Date(),
|
|
1022
|
+
structuredOutput
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
if (messages.length === startedLength && existingStructuredIndex < 0) return;
|
|
1026
|
+
this.messages = messages;
|
|
904
1027
|
this.middlewareCtx.messages = this.messages;
|
|
905
1028
|
}
|
|
906
1029
|
/**
|
|
@@ -943,7 +1066,10 @@ var TextEngine = class {
|
|
|
943
1066
|
clientToolResults
|
|
944
1067
|
};
|
|
945
1068
|
}
|
|
946
|
-
|
|
1069
|
+
genericInterruptId() {
|
|
1070
|
+
return this.createId("interrupt");
|
|
1071
|
+
}
|
|
1072
|
+
buildActionableInterrupts(approvals, clientRequests, genericRequests = [], genericInterruptIds = []) {
|
|
947
1073
|
const interrupts = [];
|
|
948
1074
|
for (const approval of approvals) {
|
|
949
1075
|
const tool = this.tools.find((candidate) => candidate.name === approval.toolName);
|
|
@@ -997,15 +1123,47 @@ var TextEngine = class {
|
|
|
997
1123
|
}
|
|
998
1124
|
});
|
|
999
1125
|
}
|
|
1126
|
+
for (const [index, request] of genericRequests.entries()) {
|
|
1127
|
+
const batchIndex = interrupts.length;
|
|
1128
|
+
const id = genericInterruptIds[index];
|
|
1129
|
+
if (!id) throw new Error("Generic interrupt id is unavailable.");
|
|
1130
|
+
const preEmission = createInterruptBinding(request, { batchIndex });
|
|
1131
|
+
interrupts.push({
|
|
1132
|
+
id,
|
|
1133
|
+
reason: request.reason,
|
|
1134
|
+
message: request.message,
|
|
1135
|
+
...preEmission.descriptor.responseSchemaCanonicalJson !== void 0 ? { responseSchema: JSON.parse(preEmission.descriptor.responseSchemaCanonicalJson) } : {},
|
|
1136
|
+
...request.expiresAt !== void 0 ? { expiresAt: request.expiresAt } : {},
|
|
1137
|
+
metadata: {
|
|
1138
|
+
[interruptBindingMetadataKey]: {
|
|
1139
|
+
v: 1,
|
|
1140
|
+
kind: "generic",
|
|
1141
|
+
interruptId: id,
|
|
1142
|
+
definitionId: preEmission.descriptor.definitionId,
|
|
1143
|
+
key: preEmission.descriptor.key,
|
|
1144
|
+
batchIndex,
|
|
1145
|
+
...request.expiresAt !== void 0 ? { expiresAt: request.expiresAt } : {},
|
|
1146
|
+
...preEmission.descriptor.payloadSchemaHash ? { payloadSchemaHash: preEmission.descriptor.payloadSchemaHash } : {},
|
|
1147
|
+
...preEmission.descriptor.responseSchemaHash !== void 0 ? { responseSchemaHash: preEmission.descriptor.responseSchemaHash } : {}
|
|
1148
|
+
},
|
|
1149
|
+
...preEmission.payload !== void 0 ? { [INTERRUPT_PAYLOAD_METADATA_KEY]: preEmission.payload } : {}
|
|
1150
|
+
}
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1154
|
+
for (const interrupt of interrupts) {
|
|
1155
|
+
if (ids.has(interrupt.id)) throw new Error(`Duplicate interrupt id in final batch: ${interrupt.id}`);
|
|
1156
|
+
ids.add(interrupt.id);
|
|
1157
|
+
}
|
|
1000
1158
|
return interrupts;
|
|
1001
1159
|
}
|
|
1002
|
-
buildInterruptFinishedChunk(finishEvent, approvals, clientRequests) {
|
|
1160
|
+
buildInterruptFinishedChunk(finishEvent, approvals, clientRequests, genericRequests = [], genericInterruptIds) {
|
|
1003
1161
|
return {
|
|
1004
1162
|
...finishEvent,
|
|
1005
1163
|
timestamp: Date.now(),
|
|
1006
1164
|
outcome: {
|
|
1007
1165
|
type: "interrupt",
|
|
1008
|
-
interrupts: this.buildActionableInterrupts(approvals, clientRequests)
|
|
1166
|
+
interrupts: this.buildActionableInterrupts(approvals, clientRequests, genericRequests, genericInterruptIds)
|
|
1009
1167
|
}
|
|
1010
1168
|
};
|
|
1011
1169
|
}
|
|
@@ -1013,7 +1171,7 @@ var TextEngine = class {
|
|
|
1013
1171
|
const messages = this.messages.map((message, index) => {
|
|
1014
1172
|
const content = typeof message.content === "string" ? message.content : message.content === null ? void 0 : JSON.stringify(message.content);
|
|
1015
1173
|
const id = message.id || `snapshot_${this.runIdOverride ?? this.requestId}_${index}`;
|
|
1016
|
-
const parts = message.role === "assistant" && message.thinking?.length ? modelMessageToUIMessage(message, id).parts : void 0;
|
|
1174
|
+
const parts = message.role === "assistant" && (message.thinking?.length || message.structuredOutput) ? modelMessageToUIMessage(message, id).parts : void 0;
|
|
1017
1175
|
return {
|
|
1018
1176
|
id,
|
|
1019
1177
|
role: message.role,
|
|
@@ -1097,8 +1255,10 @@ var TextEngine = class {
|
|
|
1097
1255
|
};
|
|
1098
1256
|
yield* this.pipeThroughMiddleware(this.buildInterruptRunErrorChunk(error));
|
|
1099
1257
|
}
|
|
1100
|
-
async *emitActionableInterruptBoundary(finishEvent, approvals, clientRequests) {
|
|
1101
|
-
|
|
1258
|
+
async *emitActionableInterruptBoundary(finishEvent, approvals, clientRequests, genericRequests = []) {
|
|
1259
|
+
yield* this.emitSyntheticRunStarted(finishEvent);
|
|
1260
|
+
const genericInterruptIds = genericRequests.map(() => this.genericInterruptId());
|
|
1261
|
+
const terminal = this.completeEphemeralInterruptBindings(this.buildInterruptFinishedChunk(finishEvent, approvals, clientRequests, genericRequests, genericInterruptIds));
|
|
1102
1262
|
let terminalOutputs;
|
|
1103
1263
|
try {
|
|
1104
1264
|
terminalOutputs = await this.middlewareRunner.runOnChunk(this.middlewareCtx, terminal);
|
|
@@ -1119,6 +1279,59 @@ var TextEngine = class {
|
|
|
1119
1279
|
}
|
|
1120
1280
|
return true;
|
|
1121
1281
|
}
|
|
1282
|
+
async *emitBoundaryInterrupts(phase, finishEvent, toolCalls = [], requests) {
|
|
1283
|
+
this.middlewareCtx.phase = phase;
|
|
1284
|
+
const boundaryRequests = requests ?? await this.middlewareRunner.runOnInterruptBoundary(this.middlewareCtx);
|
|
1285
|
+
if (boundaryRequests.length === 0) return false;
|
|
1286
|
+
for (const request of boundaryRequests) if (this.interruptDefinitions.get(request.definition.id) !== request.definition) throw new Error(`Generic interrupt definition ${request.definition.id} is not registered on this chat.`);
|
|
1287
|
+
if (phase === "afterModel") {
|
|
1288
|
+
if (this.toolCallManager.hasToolCalls()) this.addAssistantToolCallMessage(this.toolCallManager.getToolCalls());
|
|
1289
|
+
else this.addAssistantTextMessageForInterrupt();
|
|
1290
|
+
}
|
|
1291
|
+
const actionable = this.getBoundaryActionableToolRequests(toolCalls);
|
|
1292
|
+
yield* this.emitActionableInterruptBoundary(finishEvent, actionable.approvals, actionable.clientRequests, boundaryRequests);
|
|
1293
|
+
return true;
|
|
1294
|
+
}
|
|
1295
|
+
addAssistantTextMessageForInterrupt() {
|
|
1296
|
+
if (this.accumulatedContent.length === 0) return;
|
|
1297
|
+
this.messages = [...this.messages, {
|
|
1298
|
+
role: "assistant",
|
|
1299
|
+
content: this.accumulatedContent
|
|
1300
|
+
}];
|
|
1301
|
+
this.middlewareCtx.messages = this.messages;
|
|
1302
|
+
}
|
|
1303
|
+
getBoundaryActionableToolRequests(toolCalls) {
|
|
1304
|
+
const { approvals, clientToolResults } = this.collectClientState();
|
|
1305
|
+
const approvalRequests = [];
|
|
1306
|
+
const clientRequests = [];
|
|
1307
|
+
for (const toolCall of toolCalls) {
|
|
1308
|
+
const tool = this.resolveExecutableTools([toolCall]).find((candidate) => candidate.name === toolCall.function.name);
|
|
1309
|
+
if (!tool) continue;
|
|
1310
|
+
let input = {};
|
|
1311
|
+
try {
|
|
1312
|
+
const parsed = JSON.parse(toolCall.function.arguments.trim() || "{}");
|
|
1313
|
+
input = parsed && typeof parsed === "object" ? parsed : {};
|
|
1314
|
+
} catch {
|
|
1315
|
+
input = {};
|
|
1316
|
+
}
|
|
1317
|
+
const approvalId = `approval_${toolCall.id}`;
|
|
1318
|
+
if (tool.needsApproval && !approvals.has(approvalId)) approvalRequests.push({
|
|
1319
|
+
toolCallId: toolCall.id,
|
|
1320
|
+
toolName: toolCall.function.name,
|
|
1321
|
+
input,
|
|
1322
|
+
approvalId
|
|
1323
|
+
});
|
|
1324
|
+
else if (!tool.execute && !clientToolResults.has(toolCall.id) && !this.resumeCancelledToolCallIds.has(toolCall.id)) clientRequests.push({
|
|
1325
|
+
toolCallId: toolCall.id,
|
|
1326
|
+
toolName: toolCall.function.name,
|
|
1327
|
+
input
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
return {
|
|
1331
|
+
approvals: approvalRequests,
|
|
1332
|
+
clientRequests
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1122
1335
|
completeEphemeralInterruptBindings(chunk) {
|
|
1123
1336
|
if (chunk.type !== EventType.RUN_FINISHED || chunk.outcome?.type !== "interrupt") return chunk;
|
|
1124
1337
|
const interruptedRunId = this.runIdOverride ?? this.requestId;
|
|
@@ -1265,7 +1478,7 @@ var TextEngine = class {
|
|
|
1265
1478
|
createSyntheticFinishedEvent() {
|
|
1266
1479
|
return {
|
|
1267
1480
|
type: "RUN_FINISHED",
|
|
1268
|
-
runId: this.
|
|
1481
|
+
runId: this.runIdOverride ?? this.requestId,
|
|
1269
1482
|
threadId: this.threadId,
|
|
1270
1483
|
model: this.params.model,
|
|
1271
1484
|
timestamp: Date.now(),
|
|
@@ -1451,6 +1664,7 @@ var TextEngine = class {
|
|
|
1451
1664
|
const buildSynthesizedStart = (timestamp = Date.now()) => {
|
|
1452
1665
|
const idForStart = structuredMessageId ?? generateMessageId();
|
|
1453
1666
|
structuredMessageId = idForStart;
|
|
1667
|
+
this.captureStructuredOutputMessageIdentity(idForStart);
|
|
1454
1668
|
return {
|
|
1455
1669
|
type: EventType.CUSTOM,
|
|
1456
1670
|
name: "structured-output.start",
|
|
@@ -1468,7 +1682,10 @@ var TextEngine = class {
|
|
|
1468
1682
|
if (!startEmitted && chunk.type === EventType.CUSTOM && chunk.name === "structured-output.start") startEmitted = true;
|
|
1469
1683
|
if (!structuredMessageId) {
|
|
1470
1684
|
const extracted = extractMessageId(chunk);
|
|
1471
|
-
if (extracted)
|
|
1685
|
+
if (extracted) {
|
|
1686
|
+
structuredMessageId = extracted;
|
|
1687
|
+
this.captureStructuredOutputMessageIdentity(extracted);
|
|
1688
|
+
}
|
|
1472
1689
|
}
|
|
1473
1690
|
if (this.finalStructuredOutput.yieldChunks) {
|
|
1474
1691
|
if (!startEmitted && (chunk.type === EventType.TEXT_MESSAGE_START || chunk.type === EventType.TEXT_MESSAGE_CONTENT || chunk.type === EventType.TEXT_MESSAGE_END)) {
|
|
@@ -1495,7 +1712,8 @@ var TextEngine = class {
|
|
|
1495
1712
|
const object = this.finalStructuredOutput.normalize ? this.finalStructuredOutput.normalize(parsed.object) : parsed.object;
|
|
1496
1713
|
this.structuredOutputResult = {
|
|
1497
1714
|
data: object,
|
|
1498
|
-
rawText: parsed.raw
|
|
1715
|
+
rawText: parsed.raw,
|
|
1716
|
+
...parsed.reasoning !== void 0 ? { reasoning: parsed.reasoning } : {}
|
|
1499
1717
|
};
|
|
1500
1718
|
const value = chunk.value;
|
|
1501
1719
|
if (object !== parsed.object && value && typeof value === "object") outboundChunk = {
|
|
@@ -1752,6 +1970,7 @@ var TextEngine = class {
|
|
|
1752
1970
|
input: toolInputs.get(toolCall.id) ?? {}
|
|
1753
1971
|
});
|
|
1754
1972
|
}
|
|
1973
|
+
const genericPending = this.getGenericContinuationPending(interruptedRunId);
|
|
1755
1974
|
const pending = this.buildActionableInterrupts(approvalRequests, clientRequests).flatMap((descriptor) => {
|
|
1756
1975
|
const unopened = readUnopenedInterruptBinding(descriptor);
|
|
1757
1976
|
return unopened ? [{
|
|
@@ -1764,6 +1983,7 @@ var TextEngine = class {
|
|
|
1764
1983
|
}
|
|
1765
1984
|
}] : [];
|
|
1766
1985
|
});
|
|
1986
|
+
pending.push(...genericPending);
|
|
1767
1987
|
const validated = await validateInterruptResumeBatch({
|
|
1768
1988
|
threadId: this.threadId,
|
|
1769
1989
|
interruptedRunId,
|
|
@@ -1779,12 +1999,124 @@ var TextEngine = class {
|
|
|
1779
1999
|
...validated.resumeToolState,
|
|
1780
2000
|
approvals
|
|
1781
2001
|
});
|
|
2002
|
+
const genericResolutions = validated.resumeToolState.genericInterrupts;
|
|
2003
|
+
if (genericPending.length > 0 && genericResolutions) {
|
|
2004
|
+
const resolutions = genericPending.sort((left, right) => {
|
|
2005
|
+
return (left.binding.kind === "generic" ? left.binding.batchIndex ?? 0 : 0) - (right.binding.kind === "generic" ? right.binding.batchIndex ?? 0 : 0);
|
|
2006
|
+
}).flatMap((record) => {
|
|
2007
|
+
const resolution = genericResolutions.get(record.interruptId);
|
|
2008
|
+
if (!resolution || !record.genericRequest) return [];
|
|
2009
|
+
return [resolution.status === "resolved" ? {
|
|
2010
|
+
request: record.genericRequest,
|
|
2011
|
+
status: "resolved",
|
|
2012
|
+
response: resolution.payload
|
|
2013
|
+
} : {
|
|
2014
|
+
request: record.genericRequest,
|
|
2015
|
+
status: "cancelled"
|
|
2016
|
+
}];
|
|
2017
|
+
});
|
|
2018
|
+
const policy = await this.middlewareRunner.runOnInterruptResolution(this.middlewareCtx, {
|
|
2019
|
+
for: (definition) => resolutions.filter((resolution) => resolution.request.definition === definition),
|
|
2020
|
+
all: (...definitions) => definitions.length === 0 ? resolutions : resolutions.filter((resolution) => definitions.includes(resolution.request.definition))
|
|
2021
|
+
});
|
|
2022
|
+
if (policy.toolResume === "stop") this.earlyTermination = true;
|
|
2023
|
+
else if (policy.toolResume === "cancel") for (const request of pendingToolCalls) this.resumeCancelledToolCallIds.add(request.id);
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
getGenericContinuationPending(interruptedRunId) {
|
|
2027
|
+
const fail = (message) => {
|
|
2028
|
+
throw new InterruptResumeValidationError([{
|
|
2029
|
+
scope: "batch",
|
|
2030
|
+
threadId: this.threadId,
|
|
2031
|
+
interruptedRunId,
|
|
2032
|
+
generation: 0,
|
|
2033
|
+
interruptIds: [],
|
|
2034
|
+
code: "stale",
|
|
2035
|
+
message,
|
|
2036
|
+
source: "server",
|
|
2037
|
+
retryable: false
|
|
2038
|
+
}]);
|
|
2039
|
+
};
|
|
2040
|
+
const pending = [];
|
|
2041
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2042
|
+
const batchIndexes = /* @__PURE__ */ new Set();
|
|
2043
|
+
for (const resumeItem of this.params.resume ?? []) {
|
|
2044
|
+
const parsed = readGenericInterruptContinuation(resumeItem.metadata);
|
|
2045
|
+
if (parsed.status === "absent") continue;
|
|
2046
|
+
if (parsed.status === "invalid") return fail(parsed.message);
|
|
2047
|
+
const entry = parsed.value;
|
|
2048
|
+
const id = resumeItem.interruptId;
|
|
2049
|
+
const definition = this.interruptDefinitions.get(entry.definitionId);
|
|
2050
|
+
if (!definition) return fail(`Generic interrupt definition ${entry.definitionId} is unavailable.`);
|
|
2051
|
+
if (ids.has(id) || batchIndexes.has(entry.batchIndex)) return fail("Generic interrupt continuation contains duplicate entries.");
|
|
2052
|
+
ids.add(id);
|
|
2053
|
+
batchIndexes.add(entry.batchIndex);
|
|
2054
|
+
let request;
|
|
2055
|
+
try {
|
|
2056
|
+
request = rehydrateInterruptRequest(definition, {
|
|
2057
|
+
key: entry.key,
|
|
2058
|
+
reason: entry.reason,
|
|
2059
|
+
message: entry.message,
|
|
2060
|
+
...typeof entry.expiresAt === "string" ? { expiresAt: entry.expiresAt } : {},
|
|
2061
|
+
...Object.prototype.hasOwnProperty.call(entry, "payload") ? { payload: entry.payload } : {}
|
|
2062
|
+
});
|
|
2063
|
+
} catch (error) {
|
|
2064
|
+
return fail(`Generic interrupt continuation ${id} is invalid: ${error instanceof Error ? error.message : String(error)}`);
|
|
2065
|
+
}
|
|
2066
|
+
const emitted = createInterruptBinding(request, { batchIndex: entry.batchIndex });
|
|
2067
|
+
if (entry.responseSchemaHash !== emitted.descriptor.responseSchemaHash || entry.payloadSchemaHash !== emitted.descriptor.payloadSchemaHash) return fail(`Generic interrupt continuation ${id} does not match its definition.`);
|
|
2068
|
+
pending.push({
|
|
2069
|
+
interruptId: id,
|
|
2070
|
+
payload: {
|
|
2071
|
+
id,
|
|
2072
|
+
...emitted.descriptor.responseSchemaCanonicalJson !== void 0 ? { responseSchema: JSON.parse(emitted.descriptor.responseSchemaCanonicalJson) } : {}
|
|
2073
|
+
},
|
|
2074
|
+
binding: {
|
|
2075
|
+
v: 1,
|
|
2076
|
+
kind: "generic",
|
|
2077
|
+
interruptId: id,
|
|
2078
|
+
interruptedRunId,
|
|
2079
|
+
generation: 0,
|
|
2080
|
+
definitionId: entry.definitionId,
|
|
2081
|
+
key: entry.key,
|
|
2082
|
+
batchIndex: entry.batchIndex,
|
|
2083
|
+
...typeof entry.expiresAt === "string" ? { expiresAt: entry.expiresAt } : {},
|
|
2084
|
+
...emitted.descriptor.payloadSchemaHash ? { payloadSchemaHash: emitted.descriptor.payloadSchemaHash } : {},
|
|
2085
|
+
...entry.responseSchemaHash !== void 0 ? { responseSchemaHash: entry.responseSchemaHash } : {}
|
|
2086
|
+
},
|
|
2087
|
+
genericRequest: request
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
return pending;
|
|
1782
2091
|
}
|
|
1783
2092
|
applyResumeToolState(state) {
|
|
1784
2093
|
if (state?.approvals) for (const [approvalId, resolution] of state.approvals) this.resumeApprovals.set(approvalId, resolution);
|
|
1785
2094
|
if (state?.clientToolResults) for (const [toolCallId, result] of state.clientToolResults) this.resumeClientToolResults.set(toolCallId, result);
|
|
1786
2095
|
if (state?.deniedToolResults) for (const [toolCallId, result] of state.deniedToolResults) this.resumeDeniedToolResults.set(toolCallId, result);
|
|
1787
2096
|
if (state?.cancelledToolCallIds) for (const toolCallId of state.cancelledToolCallIds) this.resumeCancelledToolCallIds.add(toolCallId);
|
|
2097
|
+
if (state?.genericInterrupts) for (const [interruptId, resolution] of state.genericInterrupts) this.resumeGenericInterrupts.set(interruptId, resolution);
|
|
2098
|
+
if (state?.genericInterruptRequests) for (const [interruptId, request] of state.genericInterruptRequests) this.resumeGenericInterruptRequests.set(interruptId, request);
|
|
2099
|
+
}
|
|
2100
|
+
async applyDurableGenericInterruptResolution() {
|
|
2101
|
+
if (this.resumeGenericInterruptRequests.size === 0) return;
|
|
2102
|
+
const resolutions = [...this.resumeGenericInterruptRequests.entries()].flatMap(([interruptId, request]) => {
|
|
2103
|
+
const resolution = this.resumeGenericInterrupts.get(interruptId);
|
|
2104
|
+
if (!resolution) return [];
|
|
2105
|
+
return [resolution.status === "resolved" ? {
|
|
2106
|
+
request,
|
|
2107
|
+
status: "resolved",
|
|
2108
|
+
response: resolution.payload
|
|
2109
|
+
} : {
|
|
2110
|
+
request,
|
|
2111
|
+
status: "cancelled"
|
|
2112
|
+
}];
|
|
2113
|
+
});
|
|
2114
|
+
const policy = await this.middlewareRunner.runOnInterruptResolution(this.middlewareCtx, {
|
|
2115
|
+
for: (definition) => resolutions.filter((resolution) => resolution.request.definition === definition),
|
|
2116
|
+
all: (...definitions) => definitions.length === 0 ? resolutions : resolutions.filter((resolution) => definitions.includes(resolution.request.definition))
|
|
2117
|
+
});
|
|
2118
|
+
if (policy.toolResume === "stop") this.earlyTermination = true;
|
|
2119
|
+
else if (policy.toolResume === "cancel") for (const toolCall of this.getPendingToolCallsFromMessages()) this.resumeCancelledToolCallIds.add(toolCall.id);
|
|
1788
2120
|
}
|
|
1789
2121
|
applyMiddlewareConfig(config) {
|
|
1790
2122
|
this.applyResumeToolState(config.resumeToolState);
|
|
@@ -1813,6 +2145,7 @@ var TextEngine = class {
|
|
|
1813
2145
|
async *pipeThroughMiddleware(chunk) {
|
|
1814
2146
|
const outputChunks = await this.middlewareRunner.runOnChunk(this.middlewareCtx, chunk);
|
|
1815
2147
|
for (const outputChunk of outputChunks) {
|
|
2148
|
+
if (outputChunk.type === EventType.RUN_STARTED) this.hasPublicRunStarted = true;
|
|
1816
2149
|
yield outputChunk;
|
|
1817
2150
|
this.middlewareCtx.chunkIndex++;
|
|
1818
2151
|
}
|
|
@@ -1914,28 +2247,47 @@ var TextEngine = class {
|
|
|
1914
2247
|
* ```
|
|
1915
2248
|
*/
|
|
1916
2249
|
function chat(options) {
|
|
1917
|
-
|
|
2250
|
+
validateInterruptDefinitions(options.interrupts);
|
|
2251
|
+
validateCapabilities(readRuntimeMiddleware(options.middleware) ?? [], options.adapter);
|
|
1918
2252
|
if (options.tools) assertUniqueToolNames(options.tools);
|
|
1919
2253
|
const { outputSchema, stream } = options;
|
|
1920
|
-
if (outputSchema && stream === true) return runStreamingStructuredOutput({
|
|
1921
|
-
...options,
|
|
2254
|
+
if (outputSchema && stream === true) return runStreamingStructuredOutput(toRuntimeTextActivityOptions(options, {
|
|
1922
2255
|
outputSchema,
|
|
1923
|
-
stream
|
|
1924
|
-
});
|
|
1925
|
-
if (outputSchema) return runAgenticStructuredOutput({
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
});
|
|
1929
|
-
if (stream === false) return runNonStreamingText({
|
|
1930
|
-
...options,
|
|
2256
|
+
stream: true
|
|
2257
|
+
}));
|
|
2258
|
+
if (outputSchema) return runAgenticStructuredOutput(toRuntimeTextActivityOptions(options, {
|
|
2259
|
+
outputSchema,
|
|
2260
|
+
stream: false
|
|
2261
|
+
}));
|
|
2262
|
+
if (stream === false) return runNonStreamingText(toRuntimeTextActivityOptions(options, {
|
|
1931
2263
|
outputSchema: void 0,
|
|
1932
|
-
stream
|
|
1933
|
-
});
|
|
1934
|
-
return runStreamingText({
|
|
1935
|
-
...options,
|
|
2264
|
+
stream: false
|
|
2265
|
+
}));
|
|
2266
|
+
return runStreamingText(toRuntimeTextActivityOptions(options, {
|
|
1936
2267
|
outputSchema: void 0,
|
|
1937
|
-
stream
|
|
1938
|
-
});
|
|
2268
|
+
stream: true
|
|
2269
|
+
}));
|
|
2270
|
+
}
|
|
2271
|
+
function readRuntimeMiddleware(middleware) {
|
|
2272
|
+
if (middleware === void 0) return void 0;
|
|
2273
|
+
if (!Array.isArray(middleware)) throw new TypeError("Chat middleware must be an array.");
|
|
2274
|
+
return middleware;
|
|
2275
|
+
}
|
|
2276
|
+
function toRuntimeTextActivityOptions(options, overrides) {
|
|
2277
|
+
const { middleware, ...rest } = options;
|
|
2278
|
+
return {
|
|
2279
|
+
...rest,
|
|
2280
|
+
...overrides,
|
|
2281
|
+
...middleware === void 0 ? {} : { middleware: readRuntimeMiddleware(middleware) }
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
function validateInterruptDefinitions(definitions) {
|
|
2285
|
+
if (!definitions) return;
|
|
2286
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2287
|
+
for (const definition of definitions) {
|
|
2288
|
+
if (seen.has(definition.id)) throw new Error(`Duplicate interrupt definition id: ${definition.id}`);
|
|
2289
|
+
seen.add(definition.id);
|
|
2290
|
+
}
|
|
1939
2291
|
}
|
|
1940
2292
|
/**
|
|
1941
2293
|
* Publish both delivery-side seams for `stream`.
|
|
@@ -1996,7 +2348,10 @@ async function* streamTextChunks(options, engineRef) {
|
|
|
1996
2348
|
* Runs the full agentic loop (if tools are provided) but returns collected text.
|
|
1997
2349
|
*/
|
|
1998
2350
|
function runNonStreamingText(options) {
|
|
1999
|
-
const stream = runStreamingText(
|
|
2351
|
+
const stream = runStreamingText({
|
|
2352
|
+
...options,
|
|
2353
|
+
stream: true
|
|
2354
|
+
});
|
|
2000
2355
|
return streamToText(stream);
|
|
2001
2356
|
}
|
|
2002
2357
|
/**
|
|
@@ -2064,6 +2419,12 @@ async function runAgenticStructuredOutput(options) {
|
|
|
2064
2419
|
* Uses an `unknown`-input runtime check rather than `as` casts so the engine
|
|
2065
2420
|
* stays cast-free in its hot path.
|
|
2066
2421
|
*/
|
|
2422
|
+
function readCustomEventMessageId(value) {
|
|
2423
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
2424
|
+
if (!("messageId" in value)) return void 0;
|
|
2425
|
+
const messageId = value.messageId;
|
|
2426
|
+
return typeof messageId === "string" && messageId !== "" ? messageId : void 0;
|
|
2427
|
+
}
|
|
2067
2428
|
function readStructuredOutputCompleteValue(value) {
|
|
2068
2429
|
if (typeof value !== "object" || value === null) return null;
|
|
2069
2430
|
if (!("object" in value) || !("raw" in value)) return null;
|