@tanstack/ai 0.46.0 → 0.47.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/activities/chat/index.d.ts +36 -11
- package/dist/esm/activities/chat/index.js +435 -76
- 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 +1 -1
- 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 +1024 -203
- 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,7 +311,21 @@ 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
331
|
if (this.earlyTermination || this.isCancelled()) return;
|
|
@@ -336,7 +337,19 @@ var TextEngine = class {
|
|
|
336
337
|
const iterConfig = this.buildMiddlewareConfig();
|
|
337
338
|
const iterTransformedConfig = await this.middlewareRunner.runOnConfig(this.middlewareCtx, iterConfig);
|
|
338
339
|
this.applyMiddlewareConfig(iterTransformedConfig);
|
|
340
|
+
if (yield* this.emitBoundaryInterrupts("beforeModel", this.createSyntheticFinishedEvent())) {
|
|
341
|
+
this.setToolPhase("wait");
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
339
344
|
yield* this.streamModelResponse();
|
|
345
|
+
if (yield* this.emitBoundaryInterrupts("afterModel", this.finishedEvent ?? this.createSyntheticFinishedEvent())) {
|
|
346
|
+
this.setToolPhase("wait");
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (this.shouldExecuteToolPhase()) {
|
|
350
|
+
this.deferredToolCallRunFinishedChunks.push(...this.deferredModelRunFinishedChunks);
|
|
351
|
+
this.deferredModelRunFinishedChunks = [];
|
|
352
|
+
} else yield* this.flushDeferredModelRunFinishedChunks();
|
|
340
353
|
} else yield* this.processToolCalls();
|
|
341
354
|
this.endCycle();
|
|
342
355
|
} while (await this.shouldContinue());
|
|
@@ -358,7 +371,7 @@ var TextEngine = class {
|
|
|
358
371
|
duration: Date.now() - this.streamStartTime
|
|
359
372
|
});
|
|
360
373
|
} else {
|
|
361
|
-
this.
|
|
374
|
+
this.addTerminalAssistantMessages();
|
|
362
375
|
this.terminalHookCalled = true;
|
|
363
376
|
await this.middlewareRunner.runOnFinish(this.middlewareCtx, {
|
|
364
377
|
finishReason: this.lastFinishReason,
|
|
@@ -513,7 +526,10 @@ var TextEngine = class {
|
|
|
513
526
|
if (chunk.type === EventType.CUSTOM && chunk.name === "structured-output.start") {
|
|
514
527
|
this.combinedStartEmitted = true;
|
|
515
528
|
const startValue = chunk.value;
|
|
516
|
-
if (startValue && typeof startValue === "object" && "messageId" in startValue && typeof startValue.messageId === "string")
|
|
529
|
+
if (startValue && typeof startValue === "object" && "messageId" in startValue && typeof startValue.messageId === "string") {
|
|
530
|
+
this.combinedStructuredMessageId = startValue.messageId;
|
|
531
|
+
this.captureStructuredOutputMessageIdentity(startValue.messageId);
|
|
532
|
+
}
|
|
517
533
|
}
|
|
518
534
|
let outboundChunk = chunk;
|
|
519
535
|
if (this.finalStructuredOutput?.source === "event" && chunk.type === EventType.CUSTOM && chunk.name === "structured-output.complete") {
|
|
@@ -526,6 +542,11 @@ var TextEngine = class {
|
|
|
526
542
|
};
|
|
527
543
|
this.combinedCompleteEmitted = true;
|
|
528
544
|
const value = chunk.value;
|
|
545
|
+
const completeMessageId = readCustomEventMessageId(value);
|
|
546
|
+
if (completeMessageId) {
|
|
547
|
+
this.combinedStructuredMessageId = completeMessageId;
|
|
548
|
+
this.captureStructuredOutputMessageIdentity(completeMessageId);
|
|
549
|
+
}
|
|
529
550
|
if (object !== parsed.object && value && typeof value === "object") outboundChunk = {
|
|
530
551
|
...chunk,
|
|
531
552
|
value: {
|
|
@@ -558,10 +579,15 @@ var TextEngine = class {
|
|
|
558
579
|
const suppressAgentLifecycle = !!this.finalStructuredOutput && this.finalStructuredOutput.yieldChunks && this.finalStructuredOutput.nativeCombined !== true;
|
|
559
580
|
for (const outputChunk of outputChunks) {
|
|
560
581
|
if (suppressAgentLifecycle && (outputChunk.type === EventType.RUN_STARTED || outputChunk.type === EventType.RUN_FINISHED)) continue;
|
|
582
|
+
if (outputChunk.type === EventType.RUN_FINISHED) {
|
|
583
|
+
this.deferredModelRunFinishedChunks.push(outputChunk);
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
561
586
|
if (this.shouldDeferToolCallRunFinished(outputChunk)) {
|
|
562
587
|
this.deferredToolCallRunFinishedChunks.push(outputChunk);
|
|
563
588
|
continue;
|
|
564
589
|
}
|
|
590
|
+
if (outputChunk.type === EventType.RUN_STARTED) this.hasPublicRunStarted = true;
|
|
565
591
|
this.logger.output(`type=${outputChunk.type}`, { chunk: outputChunk });
|
|
566
592
|
yield outputChunk;
|
|
567
593
|
this.middlewareCtx.chunkIndex++;
|
|
@@ -617,6 +643,10 @@ var TextEngine = class {
|
|
|
617
643
|
this.streamIdentityCaptured = true;
|
|
618
644
|
}
|
|
619
645
|
}
|
|
646
|
+
captureStructuredOutputMessageIdentity(messageId) {
|
|
647
|
+
this.structuredOutputMessageId = messageId;
|
|
648
|
+
this.structuredOutputMessageCreatedAt ??= /* @__PURE__ */ new Date();
|
|
649
|
+
}
|
|
620
650
|
handleToolCallStartEvent(chunk) {
|
|
621
651
|
if (typeof chunk.parentMessageId === "string" && chunk.parentMessageId !== "") this.captureStreamMessageIdentity(chunk.parentMessageId);
|
|
622
652
|
this.toolCallManager.addToolCallStartEvent(chunk);
|
|
@@ -714,6 +744,11 @@ var TextEngine = class {
|
|
|
714
744
|
if (deferredErrorResults.length > 0) for (const chunk of this.buildToolResultChunks(deferredErrorResults, finishEvent, argsMap)) yield* this.pipeThroughMiddleware(chunk);
|
|
715
745
|
return "continue";
|
|
716
746
|
}
|
|
747
|
+
this.middlewareCtx.phase = "beforeTools";
|
|
748
|
+
if (yield* this.emitBoundaryInterrupts("beforeTools", finishEvent, executablePendingCalls)) {
|
|
749
|
+
this.setToolPhase("wait");
|
|
750
|
+
return "wait";
|
|
751
|
+
}
|
|
717
752
|
const { approvals, clientToolResults } = this.collectClientState();
|
|
718
753
|
const generator = executeToolCalls(executablePendingCalls, this.resolveExecutableTools(executablePendingCalls), approvals, clientToolResults, (eventName, data) => this.createCustomEventChunk(eventName, data), {
|
|
719
754
|
onBeforeToolCall: async (toolCall, tool, args) => {
|
|
@@ -802,6 +837,10 @@ var TextEngine = class {
|
|
|
802
837
|
return;
|
|
803
838
|
}
|
|
804
839
|
this.middlewareCtx.phase = "beforeTools";
|
|
840
|
+
if (yield* this.emitBoundaryInterrupts("beforeTools", finishEvent, executableToolCalls)) {
|
|
841
|
+
this.setToolPhase("wait");
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
805
844
|
const { approvals, clientToolResults } = this.collectClientState();
|
|
806
845
|
const generator = executeToolCalls(executableToolCalls, this.resolveExecutableTools(executableToolCalls), approvals, clientToolResults, (eventName, data) => this.createCustomEventChunk(eventName, data), {
|
|
807
846
|
onBeforeToolCall: async (toolCall, tool, args) => {
|
|
@@ -842,14 +881,22 @@ var TextEngine = class {
|
|
|
842
881
|
needsApproval: executionResult.needsApproval,
|
|
843
882
|
needsClientExecution: executionResult.needsClientExecution
|
|
844
883
|
});
|
|
884
|
+
const afterToolBoundaryChunks = this.buildToolResultChunks(allResults, finishEvent);
|
|
885
|
+
const afterToolRequests = await this.middlewareRunner.runOnInterruptBoundary(this.middlewareCtx);
|
|
886
|
+
if (afterToolRequests.length > 0) {
|
|
887
|
+
for (const chunk of afterToolBoundaryChunks) yield* this.pipeThroughMiddleware(chunk);
|
|
888
|
+
yield* this.emitBoundaryInterrupts("afterTools", finishEvent, toolCalls, afterToolRequests);
|
|
889
|
+
this.setToolPhase("wait");
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
845
892
|
if (executionResult.needsApproval.length > 0 || executionResult.needsClientExecution.length > 0) {
|
|
846
|
-
if (allResults.length > 0) for (const chunk of
|
|
893
|
+
if (allResults.length > 0) for (const chunk of afterToolBoundaryChunks) yield* this.pipeThroughMiddleware(chunk);
|
|
847
894
|
const emitted = yield* this.emitActionableInterruptBoundary(finishEvent, executionResult.needsApproval, executionResult.needsClientExecution);
|
|
848
895
|
this.setToolPhase(emitted ? "wait" : "stop");
|
|
849
896
|
return;
|
|
850
897
|
}
|
|
851
898
|
yield* this.flushDeferredToolCallRunFinishedChunks();
|
|
852
|
-
const toolResultChunks =
|
|
899
|
+
const toolResultChunks = afterToolBoundaryChunks;
|
|
853
900
|
for (const chunk of toolResultChunks) yield* this.pipeThroughMiddleware(chunk);
|
|
854
901
|
if (this.lazyToolManager.hasNewlyDiscoveredTools()) {
|
|
855
902
|
this.tools = this.lazyToolManager.getActiveTools();
|
|
@@ -871,6 +918,37 @@ var TextEngine = class {
|
|
|
871
918
|
}
|
|
872
919
|
this.deferredToolCallRunFinishedChunks = [];
|
|
873
920
|
}
|
|
921
|
+
*flushDeferredModelRunFinishedChunks() {
|
|
922
|
+
for (const chunk of this.deferredModelRunFinishedChunks) {
|
|
923
|
+
this.logger.output(`type=${chunk.type}`, { chunk });
|
|
924
|
+
yield chunk;
|
|
925
|
+
this.middlewareCtx.chunkIndex++;
|
|
926
|
+
}
|
|
927
|
+
this.deferredModelRunFinishedChunks = [];
|
|
928
|
+
}
|
|
929
|
+
async *emitSyntheticRunStarted(finishEvent) {
|
|
930
|
+
if (this.hasPublicRunStarted) return;
|
|
931
|
+
yield* this.pipeThroughMiddleware({
|
|
932
|
+
type: EventType.RUN_STARTED,
|
|
933
|
+
runId: finishEvent.runId,
|
|
934
|
+
threadId: finishEvent.threadId,
|
|
935
|
+
model: finishEvent.model,
|
|
936
|
+
timestamp: Date.now()
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
async *emitSuccessfulEarlyTermination() {
|
|
940
|
+
this.lastFinishReason = "stop";
|
|
941
|
+
const finishEvent = {
|
|
942
|
+
...this.createSyntheticFinishedEvent(),
|
|
943
|
+
finishReason: "stop"
|
|
944
|
+
};
|
|
945
|
+
yield* this.emitSyntheticRunStarted(finishEvent);
|
|
946
|
+
yield* this.pipeThroughMiddleware({
|
|
947
|
+
...finishEvent,
|
|
948
|
+
timestamp: Date.now(),
|
|
949
|
+
outcome: { type: "success" }
|
|
950
|
+
});
|
|
951
|
+
}
|
|
874
952
|
discardDeferredToolCallRunFinishedChunks() {
|
|
875
953
|
this.deferredToolCallRunFinishedChunks = [];
|
|
876
954
|
}
|
|
@@ -889,18 +967,61 @@ var TextEngine = class {
|
|
|
889
967
|
}];
|
|
890
968
|
this.middlewareCtx.messages = this.messages;
|
|
891
969
|
}
|
|
892
|
-
|
|
970
|
+
addTerminalAssistantMessages() {
|
|
893
971
|
this.finalizeCurrentThinkingStep();
|
|
894
|
-
|
|
895
|
-
const
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
}
|
|
972
|
+
const structuredResult = this.structuredOutputResult;
|
|
973
|
+
const raw = structuredResult ? structuredResult.rawText || safeJsonStringify(structuredResult.data) : "";
|
|
974
|
+
const structuredOutput = structuredResult ? {
|
|
975
|
+
type: "structured-output",
|
|
976
|
+
status: "complete",
|
|
977
|
+
data: structuredResult.data,
|
|
978
|
+
partial: structuredResult.data,
|
|
979
|
+
raw,
|
|
980
|
+
...structuredResult.reasoning !== void 0 ? { reasoning: structuredResult.reasoning } : {}
|
|
981
|
+
} : void 0;
|
|
982
|
+
const nativeCombined = this.finalStructuredOutput?.nativeCombined === true;
|
|
983
|
+
const eventSourced = this.finalStructuredOutput?.source === "event";
|
|
984
|
+
const structuredId = this.structuredOutputMessageId ?? this.combinedStructuredMessageId ?? this.currentMessageId ?? this.createId("msg");
|
|
985
|
+
const splitStructuredMessage = Boolean(structuredOutput) && (!nativeCombined || eventSourced) && this.currentMessageId != null && structuredId !== this.currentMessageId;
|
|
986
|
+
const messages = [...this.middlewareCtx.messages];
|
|
987
|
+
const existingStructuredIndex = messages.findIndex((message) => message.role === "assistant" && message.id === structuredId);
|
|
988
|
+
const currentTurnAlreadyRecorded = messages.some((message) => message.role === "assistant" && message.id === this.currentMessageId);
|
|
989
|
+
const thinking = this.accumulatedThinking.length > 0 ? this.accumulatedThinking : void 0;
|
|
990
|
+
const startedLength = messages.length;
|
|
991
|
+
if (structuredOutput && existingStructuredIndex >= 0) {
|
|
992
|
+
const existing = messages[existingStructuredIndex];
|
|
993
|
+
if (existing) messages[existingStructuredIndex] = {
|
|
994
|
+
...existing,
|
|
995
|
+
content: raw || existing.content,
|
|
996
|
+
structuredOutput
|
|
997
|
+
};
|
|
998
|
+
} else if (structuredOutput && !splitStructuredMessage) {
|
|
999
|
+
if (!currentTurnAlreadyRecorded) messages.push({
|
|
1000
|
+
role: "assistant",
|
|
1001
|
+
content: this.accumulatedContent || raw || null,
|
|
1002
|
+
id: structuredId,
|
|
1003
|
+
createdAt: this.currentMessageCreatedAt ?? this.structuredOutputMessageCreatedAt ?? /* @__PURE__ */ new Date(),
|
|
1004
|
+
structuredOutput,
|
|
1005
|
+
...thinking ? { thinking } : {}
|
|
1006
|
+
});
|
|
1007
|
+
} else {
|
|
1008
|
+
if (!currentTurnAlreadyRecorded && (this.accumulatedContent !== "" || thinking)) messages.push({
|
|
1009
|
+
role: "assistant",
|
|
1010
|
+
content: this.accumulatedContent || null,
|
|
1011
|
+
id: this.currentMessageId ?? this.createId("msg"),
|
|
1012
|
+
createdAt: this.currentMessageCreatedAt ?? /* @__PURE__ */ new Date(),
|
|
1013
|
+
...thinking ? { thinking } : {}
|
|
1014
|
+
});
|
|
1015
|
+
if (structuredOutput) messages.push({
|
|
1016
|
+
role: "assistant",
|
|
1017
|
+
content: raw || null,
|
|
1018
|
+
id: structuredId,
|
|
1019
|
+
createdAt: this.structuredOutputMessageCreatedAt ?? /* @__PURE__ */ new Date(),
|
|
1020
|
+
structuredOutput
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
if (messages.length === startedLength && existingStructuredIndex < 0) return;
|
|
1024
|
+
this.messages = messages;
|
|
904
1025
|
this.middlewareCtx.messages = this.messages;
|
|
905
1026
|
}
|
|
906
1027
|
/**
|
|
@@ -943,7 +1064,10 @@ var TextEngine = class {
|
|
|
943
1064
|
clientToolResults
|
|
944
1065
|
};
|
|
945
1066
|
}
|
|
946
|
-
|
|
1067
|
+
genericInterruptId() {
|
|
1068
|
+
return this.createId("interrupt");
|
|
1069
|
+
}
|
|
1070
|
+
buildActionableInterrupts(approvals, clientRequests, genericRequests = [], genericInterruptIds = []) {
|
|
947
1071
|
const interrupts = [];
|
|
948
1072
|
for (const approval of approvals) {
|
|
949
1073
|
const tool = this.tools.find((candidate) => candidate.name === approval.toolName);
|
|
@@ -997,15 +1121,47 @@ var TextEngine = class {
|
|
|
997
1121
|
}
|
|
998
1122
|
});
|
|
999
1123
|
}
|
|
1124
|
+
for (const [index, request] of genericRequests.entries()) {
|
|
1125
|
+
const batchIndex = interrupts.length;
|
|
1126
|
+
const id = genericInterruptIds[index];
|
|
1127
|
+
if (!id) throw new Error("Generic interrupt id is unavailable.");
|
|
1128
|
+
const preEmission = createInterruptBinding(request, { batchIndex });
|
|
1129
|
+
interrupts.push({
|
|
1130
|
+
id,
|
|
1131
|
+
reason: request.reason,
|
|
1132
|
+
message: request.message,
|
|
1133
|
+
...preEmission.descriptor.responseSchemaCanonicalJson !== void 0 ? { responseSchema: JSON.parse(preEmission.descriptor.responseSchemaCanonicalJson) } : {},
|
|
1134
|
+
...request.expiresAt !== void 0 ? { expiresAt: request.expiresAt } : {},
|
|
1135
|
+
metadata: {
|
|
1136
|
+
[interruptBindingMetadataKey]: {
|
|
1137
|
+
v: 1,
|
|
1138
|
+
kind: "generic",
|
|
1139
|
+
interruptId: id,
|
|
1140
|
+
definitionId: preEmission.descriptor.definitionId,
|
|
1141
|
+
key: preEmission.descriptor.key,
|
|
1142
|
+
batchIndex,
|
|
1143
|
+
...request.expiresAt !== void 0 ? { expiresAt: request.expiresAt } : {},
|
|
1144
|
+
...preEmission.descriptor.payloadSchemaHash ? { payloadSchemaHash: preEmission.descriptor.payloadSchemaHash } : {},
|
|
1145
|
+
...preEmission.descriptor.responseSchemaHash !== void 0 ? { responseSchemaHash: preEmission.descriptor.responseSchemaHash } : {}
|
|
1146
|
+
},
|
|
1147
|
+
...preEmission.payload !== void 0 ? { [INTERRUPT_PAYLOAD_METADATA_KEY]: preEmission.payload } : {}
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1152
|
+
for (const interrupt of interrupts) {
|
|
1153
|
+
if (ids.has(interrupt.id)) throw new Error(`Duplicate interrupt id in final batch: ${interrupt.id}`);
|
|
1154
|
+
ids.add(interrupt.id);
|
|
1155
|
+
}
|
|
1000
1156
|
return interrupts;
|
|
1001
1157
|
}
|
|
1002
|
-
buildInterruptFinishedChunk(finishEvent, approvals, clientRequests) {
|
|
1158
|
+
buildInterruptFinishedChunk(finishEvent, approvals, clientRequests, genericRequests = [], genericInterruptIds) {
|
|
1003
1159
|
return {
|
|
1004
1160
|
...finishEvent,
|
|
1005
1161
|
timestamp: Date.now(),
|
|
1006
1162
|
outcome: {
|
|
1007
1163
|
type: "interrupt",
|
|
1008
|
-
interrupts: this.buildActionableInterrupts(approvals, clientRequests)
|
|
1164
|
+
interrupts: this.buildActionableInterrupts(approvals, clientRequests, genericRequests, genericInterruptIds)
|
|
1009
1165
|
}
|
|
1010
1166
|
};
|
|
1011
1167
|
}
|
|
@@ -1013,7 +1169,7 @@ var TextEngine = class {
|
|
|
1013
1169
|
const messages = this.messages.map((message, index) => {
|
|
1014
1170
|
const content = typeof message.content === "string" ? message.content : message.content === null ? void 0 : JSON.stringify(message.content);
|
|
1015
1171
|
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;
|
|
1172
|
+
const parts = message.role === "assistant" && (message.thinking?.length || message.structuredOutput) ? modelMessageToUIMessage(message, id).parts : void 0;
|
|
1017
1173
|
return {
|
|
1018
1174
|
id,
|
|
1019
1175
|
role: message.role,
|
|
@@ -1097,8 +1253,10 @@ var TextEngine = class {
|
|
|
1097
1253
|
};
|
|
1098
1254
|
yield* this.pipeThroughMiddleware(this.buildInterruptRunErrorChunk(error));
|
|
1099
1255
|
}
|
|
1100
|
-
async *emitActionableInterruptBoundary(finishEvent, approvals, clientRequests) {
|
|
1101
|
-
|
|
1256
|
+
async *emitActionableInterruptBoundary(finishEvent, approvals, clientRequests, genericRequests = []) {
|
|
1257
|
+
yield* this.emitSyntheticRunStarted(finishEvent);
|
|
1258
|
+
const genericInterruptIds = genericRequests.map(() => this.genericInterruptId());
|
|
1259
|
+
const terminal = this.completeEphemeralInterruptBindings(this.buildInterruptFinishedChunk(finishEvent, approvals, clientRequests, genericRequests, genericInterruptIds));
|
|
1102
1260
|
let terminalOutputs;
|
|
1103
1261
|
try {
|
|
1104
1262
|
terminalOutputs = await this.middlewareRunner.runOnChunk(this.middlewareCtx, terminal);
|
|
@@ -1119,6 +1277,59 @@ var TextEngine = class {
|
|
|
1119
1277
|
}
|
|
1120
1278
|
return true;
|
|
1121
1279
|
}
|
|
1280
|
+
async *emitBoundaryInterrupts(phase, finishEvent, toolCalls = [], requests) {
|
|
1281
|
+
this.middlewareCtx.phase = phase;
|
|
1282
|
+
const boundaryRequests = requests ?? await this.middlewareRunner.runOnInterruptBoundary(this.middlewareCtx);
|
|
1283
|
+
if (boundaryRequests.length === 0) return false;
|
|
1284
|
+
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.`);
|
|
1285
|
+
if (phase === "afterModel") {
|
|
1286
|
+
if (this.toolCallManager.hasToolCalls()) this.addAssistantToolCallMessage(this.toolCallManager.getToolCalls());
|
|
1287
|
+
else this.addAssistantTextMessageForInterrupt();
|
|
1288
|
+
}
|
|
1289
|
+
const actionable = this.getBoundaryActionableToolRequests(toolCalls);
|
|
1290
|
+
yield* this.emitActionableInterruptBoundary(finishEvent, actionable.approvals, actionable.clientRequests, boundaryRequests);
|
|
1291
|
+
return true;
|
|
1292
|
+
}
|
|
1293
|
+
addAssistantTextMessageForInterrupt() {
|
|
1294
|
+
if (this.accumulatedContent.length === 0) return;
|
|
1295
|
+
this.messages = [...this.messages, {
|
|
1296
|
+
role: "assistant",
|
|
1297
|
+
content: this.accumulatedContent
|
|
1298
|
+
}];
|
|
1299
|
+
this.middlewareCtx.messages = this.messages;
|
|
1300
|
+
}
|
|
1301
|
+
getBoundaryActionableToolRequests(toolCalls) {
|
|
1302
|
+
const { approvals, clientToolResults } = this.collectClientState();
|
|
1303
|
+
const approvalRequests = [];
|
|
1304
|
+
const clientRequests = [];
|
|
1305
|
+
for (const toolCall of toolCalls) {
|
|
1306
|
+
const tool = this.resolveExecutableTools([toolCall]).find((candidate) => candidate.name === toolCall.function.name);
|
|
1307
|
+
if (!tool) continue;
|
|
1308
|
+
let input = {};
|
|
1309
|
+
try {
|
|
1310
|
+
const parsed = JSON.parse(toolCall.function.arguments.trim() || "{}");
|
|
1311
|
+
input = parsed && typeof parsed === "object" ? parsed : {};
|
|
1312
|
+
} catch {
|
|
1313
|
+
input = {};
|
|
1314
|
+
}
|
|
1315
|
+
const approvalId = `approval_${toolCall.id}`;
|
|
1316
|
+
if (tool.needsApproval && !approvals.has(approvalId)) approvalRequests.push({
|
|
1317
|
+
toolCallId: toolCall.id,
|
|
1318
|
+
toolName: toolCall.function.name,
|
|
1319
|
+
input,
|
|
1320
|
+
approvalId
|
|
1321
|
+
});
|
|
1322
|
+
else if (!tool.execute && !clientToolResults.has(toolCall.id) && !this.resumeCancelledToolCallIds.has(toolCall.id)) clientRequests.push({
|
|
1323
|
+
toolCallId: toolCall.id,
|
|
1324
|
+
toolName: toolCall.function.name,
|
|
1325
|
+
input
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
return {
|
|
1329
|
+
approvals: approvalRequests,
|
|
1330
|
+
clientRequests
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1122
1333
|
completeEphemeralInterruptBindings(chunk) {
|
|
1123
1334
|
if (chunk.type !== EventType.RUN_FINISHED || chunk.outcome?.type !== "interrupt") return chunk;
|
|
1124
1335
|
const interruptedRunId = this.runIdOverride ?? this.requestId;
|
|
@@ -1265,7 +1476,7 @@ var TextEngine = class {
|
|
|
1265
1476
|
createSyntheticFinishedEvent() {
|
|
1266
1477
|
return {
|
|
1267
1478
|
type: "RUN_FINISHED",
|
|
1268
|
-
runId: this.
|
|
1479
|
+
runId: this.runIdOverride ?? this.requestId,
|
|
1269
1480
|
threadId: this.threadId,
|
|
1270
1481
|
model: this.params.model,
|
|
1271
1482
|
timestamp: Date.now(),
|
|
@@ -1451,6 +1662,7 @@ var TextEngine = class {
|
|
|
1451
1662
|
const buildSynthesizedStart = (timestamp = Date.now()) => {
|
|
1452
1663
|
const idForStart = structuredMessageId ?? generateMessageId();
|
|
1453
1664
|
structuredMessageId = idForStart;
|
|
1665
|
+
this.captureStructuredOutputMessageIdentity(idForStart);
|
|
1454
1666
|
return {
|
|
1455
1667
|
type: EventType.CUSTOM,
|
|
1456
1668
|
name: "structured-output.start",
|
|
@@ -1468,7 +1680,10 @@ var TextEngine = class {
|
|
|
1468
1680
|
if (!startEmitted && chunk.type === EventType.CUSTOM && chunk.name === "structured-output.start") startEmitted = true;
|
|
1469
1681
|
if (!structuredMessageId) {
|
|
1470
1682
|
const extracted = extractMessageId(chunk);
|
|
1471
|
-
if (extracted)
|
|
1683
|
+
if (extracted) {
|
|
1684
|
+
structuredMessageId = extracted;
|
|
1685
|
+
this.captureStructuredOutputMessageIdentity(extracted);
|
|
1686
|
+
}
|
|
1472
1687
|
}
|
|
1473
1688
|
if (this.finalStructuredOutput.yieldChunks) {
|
|
1474
1689
|
if (!startEmitted && (chunk.type === EventType.TEXT_MESSAGE_START || chunk.type === EventType.TEXT_MESSAGE_CONTENT || chunk.type === EventType.TEXT_MESSAGE_END)) {
|
|
@@ -1495,7 +1710,8 @@ var TextEngine = class {
|
|
|
1495
1710
|
const object = this.finalStructuredOutput.normalize ? this.finalStructuredOutput.normalize(parsed.object) : parsed.object;
|
|
1496
1711
|
this.structuredOutputResult = {
|
|
1497
1712
|
data: object,
|
|
1498
|
-
rawText: parsed.raw
|
|
1713
|
+
rawText: parsed.raw,
|
|
1714
|
+
...parsed.reasoning !== void 0 ? { reasoning: parsed.reasoning } : {}
|
|
1499
1715
|
};
|
|
1500
1716
|
const value = chunk.value;
|
|
1501
1717
|
if (object !== parsed.object && value && typeof value === "object") outboundChunk = {
|
|
@@ -1752,6 +1968,7 @@ var TextEngine = class {
|
|
|
1752
1968
|
input: toolInputs.get(toolCall.id) ?? {}
|
|
1753
1969
|
});
|
|
1754
1970
|
}
|
|
1971
|
+
const genericPending = this.getGenericContinuationPending(interruptedRunId);
|
|
1755
1972
|
const pending = this.buildActionableInterrupts(approvalRequests, clientRequests).flatMap((descriptor) => {
|
|
1756
1973
|
const unopened = readUnopenedInterruptBinding(descriptor);
|
|
1757
1974
|
return unopened ? [{
|
|
@@ -1764,6 +1981,7 @@ var TextEngine = class {
|
|
|
1764
1981
|
}
|
|
1765
1982
|
}] : [];
|
|
1766
1983
|
});
|
|
1984
|
+
pending.push(...genericPending);
|
|
1767
1985
|
const validated = await validateInterruptResumeBatch({
|
|
1768
1986
|
threadId: this.threadId,
|
|
1769
1987
|
interruptedRunId,
|
|
@@ -1779,12 +1997,124 @@ var TextEngine = class {
|
|
|
1779
1997
|
...validated.resumeToolState,
|
|
1780
1998
|
approvals
|
|
1781
1999
|
});
|
|
2000
|
+
const genericResolutions = validated.resumeToolState.genericInterrupts;
|
|
2001
|
+
if (genericPending.length > 0 && genericResolutions) {
|
|
2002
|
+
const resolutions = genericPending.sort((left, right) => {
|
|
2003
|
+
return (left.binding.kind === "generic" ? left.binding.batchIndex ?? 0 : 0) - (right.binding.kind === "generic" ? right.binding.batchIndex ?? 0 : 0);
|
|
2004
|
+
}).flatMap((record) => {
|
|
2005
|
+
const resolution = genericResolutions.get(record.interruptId);
|
|
2006
|
+
if (!resolution || !record.genericRequest) return [];
|
|
2007
|
+
return [resolution.status === "resolved" ? {
|
|
2008
|
+
request: record.genericRequest,
|
|
2009
|
+
status: "resolved",
|
|
2010
|
+
response: resolution.payload
|
|
2011
|
+
} : {
|
|
2012
|
+
request: record.genericRequest,
|
|
2013
|
+
status: "cancelled"
|
|
2014
|
+
}];
|
|
2015
|
+
});
|
|
2016
|
+
const policy = await this.middlewareRunner.runOnInterruptResolution(this.middlewareCtx, {
|
|
2017
|
+
for: (definition) => resolutions.filter((resolution) => resolution.request.definition === definition),
|
|
2018
|
+
all: (...definitions) => definitions.length === 0 ? resolutions : resolutions.filter((resolution) => definitions.includes(resolution.request.definition))
|
|
2019
|
+
});
|
|
2020
|
+
if (policy.toolResume === "stop") this.earlyTermination = true;
|
|
2021
|
+
else if (policy.toolResume === "cancel") for (const request of pendingToolCalls) this.resumeCancelledToolCallIds.add(request.id);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
getGenericContinuationPending(interruptedRunId) {
|
|
2025
|
+
const fail = (message) => {
|
|
2026
|
+
throw new InterruptResumeValidationError([{
|
|
2027
|
+
scope: "batch",
|
|
2028
|
+
threadId: this.threadId,
|
|
2029
|
+
interruptedRunId,
|
|
2030
|
+
generation: 0,
|
|
2031
|
+
interruptIds: [],
|
|
2032
|
+
code: "stale",
|
|
2033
|
+
message,
|
|
2034
|
+
source: "server",
|
|
2035
|
+
retryable: false
|
|
2036
|
+
}]);
|
|
2037
|
+
};
|
|
2038
|
+
const pending = [];
|
|
2039
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2040
|
+
const batchIndexes = /* @__PURE__ */ new Set();
|
|
2041
|
+
for (const resumeItem of this.params.resume ?? []) {
|
|
2042
|
+
const parsed = readGenericInterruptContinuation(resumeItem.metadata);
|
|
2043
|
+
if (parsed.status === "absent") continue;
|
|
2044
|
+
if (parsed.status === "invalid") return fail(parsed.message);
|
|
2045
|
+
const entry = parsed.value;
|
|
2046
|
+
const id = resumeItem.interruptId;
|
|
2047
|
+
const definition = this.interruptDefinitions.get(entry.definitionId);
|
|
2048
|
+
if (!definition) return fail(`Generic interrupt definition ${entry.definitionId} is unavailable.`);
|
|
2049
|
+
if (ids.has(id) || batchIndexes.has(entry.batchIndex)) return fail("Generic interrupt continuation contains duplicate entries.");
|
|
2050
|
+
ids.add(id);
|
|
2051
|
+
batchIndexes.add(entry.batchIndex);
|
|
2052
|
+
let request;
|
|
2053
|
+
try {
|
|
2054
|
+
request = rehydrateInterruptRequest(definition, {
|
|
2055
|
+
key: entry.key,
|
|
2056
|
+
reason: entry.reason,
|
|
2057
|
+
message: entry.message,
|
|
2058
|
+
...typeof entry.expiresAt === "string" ? { expiresAt: entry.expiresAt } : {},
|
|
2059
|
+
...Object.prototype.hasOwnProperty.call(entry, "payload") ? { payload: entry.payload } : {}
|
|
2060
|
+
});
|
|
2061
|
+
} catch (error) {
|
|
2062
|
+
return fail(`Generic interrupt continuation ${id} is invalid: ${error instanceof Error ? error.message : String(error)}`);
|
|
2063
|
+
}
|
|
2064
|
+
const emitted = createInterruptBinding(request, { batchIndex: entry.batchIndex });
|
|
2065
|
+
if (entry.responseSchemaHash !== emitted.descriptor.responseSchemaHash || entry.payloadSchemaHash !== emitted.descriptor.payloadSchemaHash) return fail(`Generic interrupt continuation ${id} does not match its definition.`);
|
|
2066
|
+
pending.push({
|
|
2067
|
+
interruptId: id,
|
|
2068
|
+
payload: {
|
|
2069
|
+
id,
|
|
2070
|
+
...emitted.descriptor.responseSchemaCanonicalJson !== void 0 ? { responseSchema: JSON.parse(emitted.descriptor.responseSchemaCanonicalJson) } : {}
|
|
2071
|
+
},
|
|
2072
|
+
binding: {
|
|
2073
|
+
v: 1,
|
|
2074
|
+
kind: "generic",
|
|
2075
|
+
interruptId: id,
|
|
2076
|
+
interruptedRunId,
|
|
2077
|
+
generation: 0,
|
|
2078
|
+
definitionId: entry.definitionId,
|
|
2079
|
+
key: entry.key,
|
|
2080
|
+
batchIndex: entry.batchIndex,
|
|
2081
|
+
...typeof entry.expiresAt === "string" ? { expiresAt: entry.expiresAt } : {},
|
|
2082
|
+
...emitted.descriptor.payloadSchemaHash ? { payloadSchemaHash: emitted.descriptor.payloadSchemaHash } : {},
|
|
2083
|
+
...entry.responseSchemaHash !== void 0 ? { responseSchemaHash: entry.responseSchemaHash } : {}
|
|
2084
|
+
},
|
|
2085
|
+
genericRequest: request
|
|
2086
|
+
});
|
|
2087
|
+
}
|
|
2088
|
+
return pending;
|
|
1782
2089
|
}
|
|
1783
2090
|
applyResumeToolState(state) {
|
|
1784
2091
|
if (state?.approvals) for (const [approvalId, resolution] of state.approvals) this.resumeApprovals.set(approvalId, resolution);
|
|
1785
2092
|
if (state?.clientToolResults) for (const [toolCallId, result] of state.clientToolResults) this.resumeClientToolResults.set(toolCallId, result);
|
|
1786
2093
|
if (state?.deniedToolResults) for (const [toolCallId, result] of state.deniedToolResults) this.resumeDeniedToolResults.set(toolCallId, result);
|
|
1787
2094
|
if (state?.cancelledToolCallIds) for (const toolCallId of state.cancelledToolCallIds) this.resumeCancelledToolCallIds.add(toolCallId);
|
|
2095
|
+
if (state?.genericInterrupts) for (const [interruptId, resolution] of state.genericInterrupts) this.resumeGenericInterrupts.set(interruptId, resolution);
|
|
2096
|
+
if (state?.genericInterruptRequests) for (const [interruptId, request] of state.genericInterruptRequests) this.resumeGenericInterruptRequests.set(interruptId, request);
|
|
2097
|
+
}
|
|
2098
|
+
async applyDurableGenericInterruptResolution() {
|
|
2099
|
+
if (this.resumeGenericInterruptRequests.size === 0) return;
|
|
2100
|
+
const resolutions = [...this.resumeGenericInterruptRequests.entries()].flatMap(([interruptId, request]) => {
|
|
2101
|
+
const resolution = this.resumeGenericInterrupts.get(interruptId);
|
|
2102
|
+
if (!resolution) return [];
|
|
2103
|
+
return [resolution.status === "resolved" ? {
|
|
2104
|
+
request,
|
|
2105
|
+
status: "resolved",
|
|
2106
|
+
response: resolution.payload
|
|
2107
|
+
} : {
|
|
2108
|
+
request,
|
|
2109
|
+
status: "cancelled"
|
|
2110
|
+
}];
|
|
2111
|
+
});
|
|
2112
|
+
const policy = await this.middlewareRunner.runOnInterruptResolution(this.middlewareCtx, {
|
|
2113
|
+
for: (definition) => resolutions.filter((resolution) => resolution.request.definition === definition),
|
|
2114
|
+
all: (...definitions) => definitions.length === 0 ? resolutions : resolutions.filter((resolution) => definitions.includes(resolution.request.definition))
|
|
2115
|
+
});
|
|
2116
|
+
if (policy.toolResume === "stop") this.earlyTermination = true;
|
|
2117
|
+
else if (policy.toolResume === "cancel") for (const toolCall of this.getPendingToolCallsFromMessages()) this.resumeCancelledToolCallIds.add(toolCall.id);
|
|
1788
2118
|
}
|
|
1789
2119
|
applyMiddlewareConfig(config) {
|
|
1790
2120
|
this.applyResumeToolState(config.resumeToolState);
|
|
@@ -1813,6 +2143,7 @@ var TextEngine = class {
|
|
|
1813
2143
|
async *pipeThroughMiddleware(chunk) {
|
|
1814
2144
|
const outputChunks = await this.middlewareRunner.runOnChunk(this.middlewareCtx, chunk);
|
|
1815
2145
|
for (const outputChunk of outputChunks) {
|
|
2146
|
+
if (outputChunk.type === EventType.RUN_STARTED) this.hasPublicRunStarted = true;
|
|
1816
2147
|
yield outputChunk;
|
|
1817
2148
|
this.middlewareCtx.chunkIndex++;
|
|
1818
2149
|
}
|
|
@@ -1914,28 +2245,47 @@ var TextEngine = class {
|
|
|
1914
2245
|
* ```
|
|
1915
2246
|
*/
|
|
1916
2247
|
function chat(options) {
|
|
1917
|
-
|
|
2248
|
+
validateInterruptDefinitions(options.interrupts);
|
|
2249
|
+
validateCapabilities(readRuntimeMiddleware(options.middleware) ?? [], options.adapter);
|
|
1918
2250
|
if (options.tools) assertUniqueToolNames(options.tools);
|
|
1919
2251
|
const { outputSchema, stream } = options;
|
|
1920
|
-
if (outputSchema && stream === true) return runStreamingStructuredOutput({
|
|
1921
|
-
...options,
|
|
2252
|
+
if (outputSchema && stream === true) return runStreamingStructuredOutput(toRuntimeTextActivityOptions(options, {
|
|
1922
2253
|
outputSchema,
|
|
1923
|
-
stream
|
|
1924
|
-
});
|
|
1925
|
-
if (outputSchema) return runAgenticStructuredOutput({
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
});
|
|
1929
|
-
if (stream === false) return runNonStreamingText({
|
|
1930
|
-
...options,
|
|
2254
|
+
stream: true
|
|
2255
|
+
}));
|
|
2256
|
+
if (outputSchema) return runAgenticStructuredOutput(toRuntimeTextActivityOptions(options, {
|
|
2257
|
+
outputSchema,
|
|
2258
|
+
stream: false
|
|
2259
|
+
}));
|
|
2260
|
+
if (stream === false) return runNonStreamingText(toRuntimeTextActivityOptions(options, {
|
|
1931
2261
|
outputSchema: void 0,
|
|
1932
|
-
stream
|
|
1933
|
-
});
|
|
1934
|
-
return runStreamingText({
|
|
1935
|
-
...options,
|
|
2262
|
+
stream: false
|
|
2263
|
+
}));
|
|
2264
|
+
return runStreamingText(toRuntimeTextActivityOptions(options, {
|
|
1936
2265
|
outputSchema: void 0,
|
|
1937
|
-
stream
|
|
1938
|
-
});
|
|
2266
|
+
stream: true
|
|
2267
|
+
}));
|
|
2268
|
+
}
|
|
2269
|
+
function readRuntimeMiddleware(middleware) {
|
|
2270
|
+
if (middleware === void 0) return void 0;
|
|
2271
|
+
if (!Array.isArray(middleware)) throw new TypeError("Chat middleware must be an array.");
|
|
2272
|
+
return middleware;
|
|
2273
|
+
}
|
|
2274
|
+
function toRuntimeTextActivityOptions(options, overrides) {
|
|
2275
|
+
const { middleware, ...rest } = options;
|
|
2276
|
+
return {
|
|
2277
|
+
...rest,
|
|
2278
|
+
...overrides,
|
|
2279
|
+
...middleware === void 0 ? {} : { middleware: readRuntimeMiddleware(middleware) }
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
function validateInterruptDefinitions(definitions) {
|
|
2283
|
+
if (!definitions) return;
|
|
2284
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2285
|
+
for (const definition of definitions) {
|
|
2286
|
+
if (seen.has(definition.id)) throw new Error(`Duplicate interrupt definition id: ${definition.id}`);
|
|
2287
|
+
seen.add(definition.id);
|
|
2288
|
+
}
|
|
1939
2289
|
}
|
|
1940
2290
|
/**
|
|
1941
2291
|
* Publish both delivery-side seams for `stream`.
|
|
@@ -1996,7 +2346,10 @@ async function* streamTextChunks(options, engineRef) {
|
|
|
1996
2346
|
* Runs the full agentic loop (if tools are provided) but returns collected text.
|
|
1997
2347
|
*/
|
|
1998
2348
|
function runNonStreamingText(options) {
|
|
1999
|
-
const stream = runStreamingText(
|
|
2349
|
+
const stream = runStreamingText({
|
|
2350
|
+
...options,
|
|
2351
|
+
stream: true
|
|
2352
|
+
});
|
|
2000
2353
|
return streamToText(stream);
|
|
2001
2354
|
}
|
|
2002
2355
|
/**
|
|
@@ -2064,6 +2417,12 @@ async function runAgenticStructuredOutput(options) {
|
|
|
2064
2417
|
* Uses an `unknown`-input runtime check rather than `as` casts so the engine
|
|
2065
2418
|
* stays cast-free in its hot path.
|
|
2066
2419
|
*/
|
|
2420
|
+
function readCustomEventMessageId(value) {
|
|
2421
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
2422
|
+
if (!("messageId" in value)) return void 0;
|
|
2423
|
+
const messageId = value.messageId;
|
|
2424
|
+
return typeof messageId === "string" && messageId !== "" ? messageId : void 0;
|
|
2425
|
+
}
|
|
2067
2426
|
function readStructuredOutputCompleteValue(value) {
|
|
2068
2427
|
if (typeof value !== "object" || value === null) return null;
|
|
2069
2428
|
if (!("object" in value) || !("raw" in value)) return null;
|