veryfront 0.1.419 → 0.1.421
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/esm/deno.js +1 -1
- package/esm/src/agent/hosted-chat-execution-runtime.d.ts +81 -0
- package/esm/src/agent/hosted-chat-execution-runtime.d.ts.map +1 -0
- package/esm/src/agent/hosted-chat-execution-runtime.js +284 -0
- package/esm/src/agent/index.d.ts +1 -0
- package/esm/src/agent/index.d.ts.map +1 -1
- package/esm/src/agent/index.js +1 -0
- package/esm/src/agent/runtime/index.d.ts +1 -0
- package/esm/src/agent/runtime/index.d.ts.map +1 -1
- package/esm/src/agent/runtime/index.js +28 -4
- package/esm/src/agent/types.d.ts +17 -1
- package/esm/src/agent/types.d.ts.map +1 -1
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
- package/src/deno.js +1 -1
- package/src/deps/esm.sh/@types/react-dom@19.2.3/client.d.ts +1 -1
- package/src/deps/esm.sh/@types/{react@19.2.14 → react@19.2.3}/global.d.ts +0 -1
- package/src/deps/esm.sh/@types/{react@19.2.14 → react@19.2.3}/index.d.ts +24 -93
- package/src/deps/esm.sh/react-dom@19.2.4/client.d.ts +1 -1
- package/src/src/agent/hosted-chat-execution-runtime.ts +501 -0
- package/src/src/agent/index.ts +15 -0
- package/src/src/agent/runtime/index.ts +37 -9
- package/src/src/agent/types.ts +21 -1
- package/src/src/utils/version-constant.ts +1 -1
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildChatStreamChunkMessageMetadata,
|
|
3
|
+
extractChatMessageMetadata,
|
|
4
|
+
} from "../chat/chat-ui-message-helpers.js";
|
|
5
|
+
import { getLastStreamStep } from "../chat/final-step-fallback.js";
|
|
6
|
+
import type { ChatUiMessage, ChatUiMessageChunk, MessageMetadata } from "../chat/types.js";
|
|
7
|
+
import {
|
|
8
|
+
buildDetachedFallbackChunks,
|
|
9
|
+
buildDetachedFallbackMessageState,
|
|
10
|
+
buildFinalizedMessageFallbackChunks,
|
|
11
|
+
buildFinalizedMessageState,
|
|
12
|
+
} from "./hosted-finalized-message.js";
|
|
13
|
+
import type {
|
|
14
|
+
HostedChatRuntimeStreamResult,
|
|
15
|
+
HostedChatRuntimeToUiMessageStreamOptions,
|
|
16
|
+
} from "./hosted-chat-runtime-contract.js";
|
|
17
|
+
import type { HostedLifecycleTerminalState } from "./hosted-lifecycle.js";
|
|
18
|
+
import {
|
|
19
|
+
type ConversationHostedTerminalRuntimeAdapter,
|
|
20
|
+
type ConversationHostedTerminalStateInput,
|
|
21
|
+
dispatchConversationHostedStreamErrorState,
|
|
22
|
+
dispatchConversationHostedTerminalState,
|
|
23
|
+
resolveConversationHostedTerminalState,
|
|
24
|
+
toConversationHostedTerminalState,
|
|
25
|
+
} from "./conversation-hosted-terminal.js";
|
|
26
|
+
import {
|
|
27
|
+
createHostedMirroredUiStream,
|
|
28
|
+
type MirroredToolChunkState,
|
|
29
|
+
} from "./mirrored-tool-chunk-state.js";
|
|
30
|
+
import {
|
|
31
|
+
finalizeHostedDetached,
|
|
32
|
+
finalizeHostedResponse,
|
|
33
|
+
type FinalizeHostedResponseOptions,
|
|
34
|
+
type HostedDetachedFinalizationState,
|
|
35
|
+
type HostedResponseFinalizationState,
|
|
36
|
+
} from "./hosted-stream-finalization.js";
|
|
37
|
+
import {
|
|
38
|
+
getEmptyHostedFinalizedMessageTerminalError,
|
|
39
|
+
getHostedStreamErrorText,
|
|
40
|
+
shouldFailEmptyHostedFinalizedMessage,
|
|
41
|
+
} from "./hosted-stream-terminal-error.js";
|
|
42
|
+
import type { ConversationRunChunkMirror } from "./conversation-run-chunk-mirror.js";
|
|
43
|
+
import type { BuildChatStreamChunkMessageMetadataInput } from "../chat/chat-ui-message-helpers.js";
|
|
44
|
+
import type { createChatStreamWatchdog } from "../chat/stream-watchdog.js";
|
|
45
|
+
|
|
46
|
+
const INCOMPLETE_TOOL_CALLS_PART_ERROR_TEXT = "Assistant ended before tool execution completed";
|
|
47
|
+
|
|
48
|
+
const FINALIZATION_TERMINAL_STATE_FALLBACK_MODEL_ID = "";
|
|
49
|
+
|
|
50
|
+
export interface HostedChatExecutionRuntime {
|
|
51
|
+
agentUIStream: AsyncIterable<ChatUiMessageChunk<MessageMetadata>>;
|
|
52
|
+
fail: (error: unknown) => Promise<void>;
|
|
53
|
+
waitForFinish: () => Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface HostedChatExecutionRuntimeLogger {
|
|
57
|
+
error: (message: string, metadata?: Record<string, unknown>) => void;
|
|
58
|
+
warn: (message: string, metadata?: Record<string, unknown>) => void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface HostedChatExecutionRunContext {
|
|
62
|
+
withContext: <T>(fn: () => T) => T;
|
|
63
|
+
setMessageId?: (messageId: string) => void;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface HostedChatExecutionLifecycleAdapter
|
|
67
|
+
extends ConversationHostedTerminalRuntimeAdapter {
|
|
68
|
+
durableRootRun: {
|
|
69
|
+
runId: string;
|
|
70
|
+
messageId?: string | null;
|
|
71
|
+
} | null;
|
|
72
|
+
durableRunMirror: ConversationRunChunkMirror | null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type HostedChatExecutionRootStreamWatchdog = ReturnType<typeof createChatStreamWatchdog>;
|
|
76
|
+
|
|
77
|
+
export interface HostedChatExecutionRuntimeBootstrap {
|
|
78
|
+
cleanup: () => Promise<void>;
|
|
79
|
+
lifecycleAdapter: HostedChatExecutionLifecycleAdapter;
|
|
80
|
+
rootStreamWatchdog: HostedChatExecutionRootStreamWatchdog;
|
|
81
|
+
streamResult: HostedChatRuntimeStreamResult;
|
|
82
|
+
streamingMessageId: string | null;
|
|
83
|
+
capturedMessageId: string | null;
|
|
84
|
+
capturedConversationId?: string;
|
|
85
|
+
mirroredToolChunkState: MirroredToolChunkState;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface CreateHostedChatExecutionRuntimeInput {
|
|
89
|
+
agentId: string;
|
|
90
|
+
modelId: string;
|
|
91
|
+
originalMessages: ChatUiMessage[];
|
|
92
|
+
responseMessageId?: string;
|
|
93
|
+
runContext: HostedChatExecutionRunContext;
|
|
94
|
+
abortSignal: AbortSignal;
|
|
95
|
+
bootstrap: HostedChatExecutionRuntimeBootstrap;
|
|
96
|
+
logger?: HostedChatExecutionRuntimeLogger;
|
|
97
|
+
incompleteToolCallsPartErrorText?: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
type SharedFinalizationHooks = Pick<
|
|
101
|
+
FinalizeHostedResponseOptions<ChatUiMessage, ChatUiMessageChunk<MessageMetadata>>,
|
|
102
|
+
| "resolveEmptyTerminalError"
|
|
103
|
+
| "appendFallbackChunk"
|
|
104
|
+
| "flushMirror"
|
|
105
|
+
| "dispatchTerminalState"
|
|
106
|
+
| "resolveTerminalState"
|
|
107
|
+
| "cleanup"
|
|
108
|
+
| "streamError"
|
|
109
|
+
>;
|
|
110
|
+
|
|
111
|
+
export function toHostedChatExecutionFinalState(
|
|
112
|
+
input: ConversationHostedTerminalStateInput,
|
|
113
|
+
): HostedLifecycleTerminalState {
|
|
114
|
+
return toConversationHostedTerminalState({
|
|
115
|
+
state: input,
|
|
116
|
+
fallbackModelId: FINALIZATION_TERMINAL_STATE_FALLBACK_MODEL_ID,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function cleanupAfterHostedChatExecutionFinalization(input: {
|
|
121
|
+
cleanup: () => Promise<void>;
|
|
122
|
+
logger?: HostedChatExecutionRuntimeLogger;
|
|
123
|
+
}): Promise<void> {
|
|
124
|
+
await input.cleanup().catch((cleanupError: unknown) => {
|
|
125
|
+
input.logger?.error("Runtime cleanup failed during finalization", {
|
|
126
|
+
error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError),
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function createHostedChatStreamFinalizationHooks(input: {
|
|
132
|
+
lifecycleAdapter: HostedChatExecutionLifecycleAdapter;
|
|
133
|
+
cleanup: () => Promise<void>;
|
|
134
|
+
streamError: unknown;
|
|
135
|
+
logger?: HostedChatExecutionRuntimeLogger;
|
|
136
|
+
}): SharedFinalizationHooks {
|
|
137
|
+
return {
|
|
138
|
+
resolveEmptyTerminalError: (
|
|
139
|
+
{ finalStep, streamError }: { finalStep: unknown; streamError?: unknown | null },
|
|
140
|
+
) => getEmptyHostedFinalizedMessageTerminalError({ finalStep, streamError }),
|
|
141
|
+
appendFallbackChunk: (chunk: ChatUiMessageChunk<MessageMetadata>) =>
|
|
142
|
+
input.lifecycleAdapter.durableRunMirror?.handleChunk(chunk),
|
|
143
|
+
flushMirror: () => input.lifecycleAdapter.durableRunMirror?.flush(),
|
|
144
|
+
dispatchTerminalState: async (terminalState) => {
|
|
145
|
+
await dispatchConversationHostedTerminalState(input.lifecycleAdapter, terminalState);
|
|
146
|
+
},
|
|
147
|
+
resolveTerminalState: ({ isAborted, hasIncompleteToolParts }: {
|
|
148
|
+
isAborted: boolean;
|
|
149
|
+
hasIncompleteToolParts: boolean;
|
|
150
|
+
}) =>
|
|
151
|
+
toHostedChatExecutionFinalState(
|
|
152
|
+
resolveConversationHostedTerminalState({ isAborted, hasIncompleteToolParts }),
|
|
153
|
+
),
|
|
154
|
+
cleanup: () =>
|
|
155
|
+
cleanupAfterHostedChatExecutionFinalization({
|
|
156
|
+
cleanup: input.cleanup,
|
|
157
|
+
logger: input.logger,
|
|
158
|
+
}),
|
|
159
|
+
streamError: input.streamError,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function createHostedChatFinalizeResponseBuildState(input: {
|
|
164
|
+
responseMessage: ChatUiMessage;
|
|
165
|
+
isAborted: boolean;
|
|
166
|
+
lifecycleAdapter: HostedChatExecutionLifecycleAdapter;
|
|
167
|
+
mirroredToolChunkState: MirroredToolChunkState;
|
|
168
|
+
capturedMessageId: string | null;
|
|
169
|
+
incompleteToolCallsPartErrorText: string;
|
|
170
|
+
}): (
|
|
171
|
+
finalStep: unknown,
|
|
172
|
+
) => Promise<HostedResponseFinalizationState<ChatUiMessage, ChatUiMessageChunk<MessageMetadata>>> {
|
|
173
|
+
return async (finalStep) => {
|
|
174
|
+
const { persistedMessage, sanitizedFinalizedMessage, hasIncompleteFinalizedToolParts } =
|
|
175
|
+
buildFinalizedMessageState({
|
|
176
|
+
responseMessage: input.responseMessage,
|
|
177
|
+
isAborted: input.isAborted,
|
|
178
|
+
finalStep,
|
|
179
|
+
incompleteToolCallsPartErrorText: input.incompleteToolCallsPartErrorText,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
persistedMessage,
|
|
184
|
+
finalizedMessage: sanitizedFinalizedMessage,
|
|
185
|
+
fallbackChunks:
|
|
186
|
+
sanitizedFinalizedMessage.parts.length > 0 && input.lifecycleAdapter.durableRunMirror
|
|
187
|
+
? buildFinalizedMessageFallbackChunks({
|
|
188
|
+
persistedMessage,
|
|
189
|
+
sanitizedFinalizedMessage,
|
|
190
|
+
finalStep,
|
|
191
|
+
mirroredToolChunkState: input.mirroredToolChunkState,
|
|
192
|
+
capturedMessageId: input.capturedMessageId,
|
|
193
|
+
hasIncompleteFinalizedToolParts,
|
|
194
|
+
})
|
|
195
|
+
: [],
|
|
196
|
+
hasIncompleteToolParts: hasIncompleteFinalizedToolParts,
|
|
197
|
+
metadata: extractChatMessageMetadata(sanitizedFinalizedMessage.metadata),
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function createHostedChatFinalizeDetachedBuildState(input: {
|
|
203
|
+
capturedMessageId: string | null;
|
|
204
|
+
isAborted: boolean;
|
|
205
|
+
lifecycleAdapter: HostedChatExecutionLifecycleAdapter;
|
|
206
|
+
mirroredToolChunkState: MirroredToolChunkState;
|
|
207
|
+
mirroredDurableOutput: boolean;
|
|
208
|
+
incompleteToolCallsPartErrorText: string;
|
|
209
|
+
}): (
|
|
210
|
+
finalStep: unknown,
|
|
211
|
+
) => Promise<HostedDetachedFinalizationState<ChatUiMessageChunk<MessageMetadata>>> {
|
|
212
|
+
return async (finalStep) => {
|
|
213
|
+
const { finalizedFallbackMessage, hasIncompleteFallbackToolParts } =
|
|
214
|
+
buildDetachedFallbackMessageState({
|
|
215
|
+
capturedMessageId: input.capturedMessageId,
|
|
216
|
+
finalStep,
|
|
217
|
+
isAborted: input.isAborted,
|
|
218
|
+
incompleteToolCallsPartErrorText: input.incompleteToolCallsPartErrorText,
|
|
219
|
+
});
|
|
220
|
+
const fallbackParts = finalizedFallbackMessage.parts;
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
hasContent: fallbackParts.length > 0,
|
|
224
|
+
fallbackChunks: fallbackParts.length > 0 && input.lifecycleAdapter.durableRunMirror &&
|
|
225
|
+
input.capturedMessageId
|
|
226
|
+
? buildDetachedFallbackChunks({
|
|
227
|
+
fallbackParts,
|
|
228
|
+
finalStep,
|
|
229
|
+
mirroredToolChunkState: input.mirroredToolChunkState,
|
|
230
|
+
mirroredDurableOutput: input.mirroredDurableOutput,
|
|
231
|
+
capturedMessageId: input.capturedMessageId,
|
|
232
|
+
hasIncompleteFallbackToolParts,
|
|
233
|
+
})
|
|
234
|
+
: [],
|
|
235
|
+
hasIncompleteToolParts: hasIncompleteFallbackToolParts,
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function finalizeExecutionFailure(input: {
|
|
241
|
+
lifecycleAdapter: HostedChatExecutionLifecycleAdapter;
|
|
242
|
+
error: unknown;
|
|
243
|
+
conversationId?: string;
|
|
244
|
+
runId?: string;
|
|
245
|
+
logMessage: string;
|
|
246
|
+
logger?: HostedChatExecutionRuntimeLogger;
|
|
247
|
+
}): Promise<void> {
|
|
248
|
+
await dispatchConversationHostedStreamErrorState(input.lifecycleAdapter, input.error).catch(
|
|
249
|
+
(finalizeError) => {
|
|
250
|
+
input.logger?.error(input.logMessage, {
|
|
251
|
+
conversationId: input.conversationId,
|
|
252
|
+
runId: input.runId,
|
|
253
|
+
error: finalizeError instanceof Error ? finalizeError.message : String(finalizeError),
|
|
254
|
+
});
|
|
255
|
+
},
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function createStreamMessageMetadataBuilder(input: {
|
|
260
|
+
agentId: string;
|
|
261
|
+
modelId: string;
|
|
262
|
+
lifecycleAdapter: HostedChatExecutionLifecycleAdapter;
|
|
263
|
+
streamingMessageId: string | null;
|
|
264
|
+
}): HostedChatRuntimeToUiMessageStreamOptions["messageMetadata"] {
|
|
265
|
+
return ({ part }) =>
|
|
266
|
+
buildChatStreamChunkMessageMetadata(
|
|
267
|
+
{
|
|
268
|
+
agentId: input.agentId,
|
|
269
|
+
modelId: input.modelId,
|
|
270
|
+
...(input.lifecycleAdapter.durableRootRun
|
|
271
|
+
? { runId: input.lifecycleAdapter.durableRootRun.runId }
|
|
272
|
+
: {}),
|
|
273
|
+
...(input.streamingMessageId ? { streamingMessageId: input.streamingMessageId } : {}),
|
|
274
|
+
part: {
|
|
275
|
+
type: part.type,
|
|
276
|
+
...("totalUsage" in part ? { totalUsage: part.totalUsage } : {}),
|
|
277
|
+
},
|
|
278
|
+
} satisfies BuildChatStreamChunkMessageMetadataInput,
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function logCleanupError(input: {
|
|
283
|
+
error: unknown;
|
|
284
|
+
logger?: HostedChatExecutionRuntimeLogger;
|
|
285
|
+
}): void {
|
|
286
|
+
input.logger?.error("Runtime cleanup failed", {
|
|
287
|
+
error: input.error instanceof Error ? input.error.message : String(input.error),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function finalizeResponseFinish(input: {
|
|
292
|
+
responseMessage: ChatUiMessage;
|
|
293
|
+
isAborted: boolean;
|
|
294
|
+
streamResult: { steps: PromiseLike<readonly unknown[]> };
|
|
295
|
+
lastStreamError: unknown;
|
|
296
|
+
lifecycleAdapter: HostedChatExecutionLifecycleAdapter;
|
|
297
|
+
mirroredToolChunkState: MirroredToolChunkState;
|
|
298
|
+
capturedMessageId: string | null;
|
|
299
|
+
incompleteToolCallsPartErrorText: string;
|
|
300
|
+
cleanup: () => Promise<void>;
|
|
301
|
+
logger?: HostedChatExecutionRuntimeLogger;
|
|
302
|
+
}): Promise<void> {
|
|
303
|
+
const hooks = createHostedChatStreamFinalizationHooks({
|
|
304
|
+
lifecycleAdapter: input.lifecycleAdapter,
|
|
305
|
+
cleanup: input.cleanup,
|
|
306
|
+
streamError: input.lastStreamError,
|
|
307
|
+
logger: input.logger,
|
|
308
|
+
});
|
|
309
|
+
const buildState = createHostedChatFinalizeResponseBuildState({
|
|
310
|
+
responseMessage: input.responseMessage,
|
|
311
|
+
isAborted: input.isAborted,
|
|
312
|
+
lifecycleAdapter: input.lifecycleAdapter,
|
|
313
|
+
mirroredToolChunkState: input.mirroredToolChunkState,
|
|
314
|
+
capturedMessageId: input.capturedMessageId,
|
|
315
|
+
incompleteToolCallsPartErrorText: input.incompleteToolCallsPartErrorText,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
await finalizeHostedResponse({
|
|
319
|
+
isAborted: input.isAborted,
|
|
320
|
+
getFinalStep: () => getLastStreamStep(input.streamResult),
|
|
321
|
+
buildState,
|
|
322
|
+
shouldFailEmptyMessage: ({ isAborted, message }) =>
|
|
323
|
+
shouldFailEmptyHostedFinalizedMessage({ isAborted, message }),
|
|
324
|
+
...hooks,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async function finalizeDetachedStreamEnd(input: {
|
|
329
|
+
capturedMessageId: string | null;
|
|
330
|
+
streamResult: { steps: PromiseLike<readonly unknown[]> };
|
|
331
|
+
isAborted: boolean;
|
|
332
|
+
lastStreamError: unknown;
|
|
333
|
+
lifecycleAdapter: HostedChatExecutionLifecycleAdapter;
|
|
334
|
+
mirroredToolChunkState: MirroredToolChunkState;
|
|
335
|
+
mirroredDurableOutput: boolean;
|
|
336
|
+
incompleteToolCallsPartErrorText: string;
|
|
337
|
+
cleanup: () => Promise<void>;
|
|
338
|
+
logger?: HostedChatExecutionRuntimeLogger;
|
|
339
|
+
}): Promise<void> {
|
|
340
|
+
const hooks = createHostedChatStreamFinalizationHooks({
|
|
341
|
+
lifecycleAdapter: input.lifecycleAdapter,
|
|
342
|
+
cleanup: input.cleanup,
|
|
343
|
+
streamError: input.lastStreamError,
|
|
344
|
+
logger: input.logger,
|
|
345
|
+
});
|
|
346
|
+
const buildState = createHostedChatFinalizeDetachedBuildState({
|
|
347
|
+
capturedMessageId: input.capturedMessageId,
|
|
348
|
+
isAborted: input.isAborted,
|
|
349
|
+
lifecycleAdapter: input.lifecycleAdapter,
|
|
350
|
+
mirroredToolChunkState: input.mirroredToolChunkState,
|
|
351
|
+
mirroredDurableOutput: input.mirroredDurableOutput,
|
|
352
|
+
incompleteToolCallsPartErrorText: input.incompleteToolCallsPartErrorText,
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
await finalizeHostedDetached({
|
|
356
|
+
isAborted: input.isAborted,
|
|
357
|
+
mirroredDurableOutput: input.mirroredDurableOutput,
|
|
358
|
+
getFinalStep: () => getLastStreamStep(input.streamResult),
|
|
359
|
+
buildState,
|
|
360
|
+
...hooks,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function resolveStreamingMessageId(input: {
|
|
365
|
+
conversationId?: string;
|
|
366
|
+
lifecycleAdapter: HostedChatExecutionLifecycleAdapter;
|
|
367
|
+
runContext: HostedChatExecutionRunContext;
|
|
368
|
+
}): string | null {
|
|
369
|
+
const streamingMessageId = input.lifecycleAdapter.durableRootRun?.messageId ?? null;
|
|
370
|
+
if (input.conversationId && !streamingMessageId) {
|
|
371
|
+
throw new Error("DURABLE_CHAT_ROOT_REQUIRES_CONVERSATION");
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (streamingMessageId) {
|
|
375
|
+
input.runContext.setMessageId?.(streamingMessageId);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return streamingMessageId;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export function createHostedChatExecutionRuntime(
|
|
382
|
+
input: CreateHostedChatExecutionRuntimeInput,
|
|
383
|
+
): HostedChatExecutionRuntime {
|
|
384
|
+
let finishPromise: Promise<void> = Promise.resolve();
|
|
385
|
+
let lastStreamError: unknown = null;
|
|
386
|
+
let finishHandlerStarted = false;
|
|
387
|
+
let mirroredDurableOutput = false;
|
|
388
|
+
const incompleteToolCallsPartErrorText = input.incompleteToolCallsPartErrorText ??
|
|
389
|
+
INCOMPLETE_TOOL_CALLS_PART_ERROR_TEXT;
|
|
390
|
+
const streamingMessageId = resolveStreamingMessageId({
|
|
391
|
+
conversationId: input.bootstrap.capturedConversationId,
|
|
392
|
+
lifecycleAdapter: input.bootstrap.lifecycleAdapter,
|
|
393
|
+
runContext: input.runContext,
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
const finalizeDetachedStreamEndIfNeeded = async () => {
|
|
397
|
+
if (finishHandlerStarted) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
finishHandlerStarted = true;
|
|
402
|
+
await finalizeDetachedStreamEnd({
|
|
403
|
+
capturedMessageId: input.bootstrap.capturedMessageId,
|
|
404
|
+
streamResult: input.bootstrap.streamResult,
|
|
405
|
+
isAborted: input.abortSignal.aborted,
|
|
406
|
+
lastStreamError,
|
|
407
|
+
lifecycleAdapter: input.bootstrap.lifecycleAdapter,
|
|
408
|
+
mirroredToolChunkState: input.bootstrap.mirroredToolChunkState,
|
|
409
|
+
mirroredDurableOutput,
|
|
410
|
+
incompleteToolCallsPartErrorText,
|
|
411
|
+
cleanup: input.bootstrap.cleanup,
|
|
412
|
+
logger: input.logger,
|
|
413
|
+
});
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
const fail = async (error: unknown) => {
|
|
417
|
+
await input.runContext.withContext(async () => {
|
|
418
|
+
input.bootstrap.rootStreamWatchdog.dispose();
|
|
419
|
+
await input.bootstrap.cleanup().catch((cleanupError: unknown) => {
|
|
420
|
+
logCleanupError({ error: cleanupError, logger: input.logger });
|
|
421
|
+
});
|
|
422
|
+
await finalizeExecutionFailure({
|
|
423
|
+
lifecycleAdapter: input.bootstrap.lifecycleAdapter,
|
|
424
|
+
error,
|
|
425
|
+
conversationId: input.bootstrap.capturedConversationId,
|
|
426
|
+
runId: input.bootstrap.lifecycleAdapter.durableRootRun?.runId,
|
|
427
|
+
logMessage: "Failed to mark durable chat root run as failed",
|
|
428
|
+
logger: input.logger,
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const streamOptions: HostedChatRuntimeToUiMessageStreamOptions = {
|
|
434
|
+
sendReasoning: true,
|
|
435
|
+
originalMessages: input.originalMessages,
|
|
436
|
+
onError: (error) => {
|
|
437
|
+
lastStreamError = error;
|
|
438
|
+
return input.runContext.withContext(() => getHostedStreamErrorText(error));
|
|
439
|
+
},
|
|
440
|
+
onFinish: ({ responseMessage, isAborted }) => {
|
|
441
|
+
finishHandlerStarted = true;
|
|
442
|
+
finishPromise = input.runContext.withContext(() =>
|
|
443
|
+
finalizeResponseFinish({
|
|
444
|
+
responseMessage,
|
|
445
|
+
isAborted,
|
|
446
|
+
streamResult: input.bootstrap.streamResult,
|
|
447
|
+
lastStreamError,
|
|
448
|
+
lifecycleAdapter: input.bootstrap.lifecycleAdapter,
|
|
449
|
+
mirroredToolChunkState: input.bootstrap.mirroredToolChunkState,
|
|
450
|
+
capturedMessageId: input.bootstrap.capturedMessageId,
|
|
451
|
+
incompleteToolCallsPartErrorText,
|
|
452
|
+
cleanup: input.bootstrap.cleanup,
|
|
453
|
+
logger: input.logger,
|
|
454
|
+
}).catch((error) =>
|
|
455
|
+
finalizeExecutionFailure({
|
|
456
|
+
lifecycleAdapter: input.bootstrap.lifecycleAdapter,
|
|
457
|
+
error,
|
|
458
|
+
conversationId: input.bootstrap.capturedConversationId,
|
|
459
|
+
runId: input.bootstrap.lifecycleAdapter.durableRootRun?.runId,
|
|
460
|
+
logMessage: "Failed to finalize durable chat root run",
|
|
461
|
+
logger: input.logger,
|
|
462
|
+
})
|
|
463
|
+
)
|
|
464
|
+
);
|
|
465
|
+
},
|
|
466
|
+
messageMetadata: createStreamMessageMetadataBuilder({
|
|
467
|
+
agentId: input.agentId,
|
|
468
|
+
modelId: input.modelId,
|
|
469
|
+
lifecycleAdapter: input.bootstrap.lifecycleAdapter,
|
|
470
|
+
streamingMessageId,
|
|
471
|
+
}),
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
if (input.responseMessageId) {
|
|
475
|
+
const responseMessageId = input.responseMessageId;
|
|
476
|
+
streamOptions.generateMessageId = () => responseMessageId;
|
|
477
|
+
}
|
|
478
|
+
const agentUIStream = input.bootstrap.streamResult.toUIMessageStream(streamOptions);
|
|
479
|
+
|
|
480
|
+
return {
|
|
481
|
+
agentUIStream: createHostedMirroredUiStream({
|
|
482
|
+
sourceStream: agentUIStream,
|
|
483
|
+
rootStreamWatchdog: input.bootstrap.rootStreamWatchdog,
|
|
484
|
+
mirroredToolChunkState: input.bootstrap.mirroredToolChunkState,
|
|
485
|
+
appendChunk: (chunk) => input.bootstrap.lifecycleAdapter.durableRunMirror?.handleChunk(chunk),
|
|
486
|
+
setMirroredOutput: (value) => {
|
|
487
|
+
mirroredDurableOutput = value;
|
|
488
|
+
},
|
|
489
|
+
logger: input.logger,
|
|
490
|
+
}),
|
|
491
|
+
fail,
|
|
492
|
+
waitForFinish: async () => {
|
|
493
|
+
try {
|
|
494
|
+
await finalizeDetachedStreamEndIfNeeded();
|
|
495
|
+
await finishPromise;
|
|
496
|
+
} finally {
|
|
497
|
+
input.bootstrap.rootStreamWatchdog.dispose();
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
};
|
|
501
|
+
}
|
package/src/src/agent/index.ts
CHANGED
|
@@ -799,6 +799,21 @@ export {
|
|
|
799
799
|
type DetachedFallbackMessageState,
|
|
800
800
|
type FinalizedMessageState,
|
|
801
801
|
} from "./hosted-finalized-message.js";
|
|
802
|
+
export {
|
|
803
|
+
cleanupAfterHostedChatExecutionFinalization,
|
|
804
|
+
createHostedChatExecutionRuntime,
|
|
805
|
+
type CreateHostedChatExecutionRuntimeInput,
|
|
806
|
+
createHostedChatFinalizeDetachedBuildState,
|
|
807
|
+
createHostedChatFinalizeResponseBuildState,
|
|
808
|
+
createHostedChatStreamFinalizationHooks,
|
|
809
|
+
type HostedChatExecutionLifecycleAdapter,
|
|
810
|
+
type HostedChatExecutionRootStreamWatchdog,
|
|
811
|
+
type HostedChatExecutionRunContext,
|
|
812
|
+
type HostedChatExecutionRuntime,
|
|
813
|
+
type HostedChatExecutionRuntimeBootstrap,
|
|
814
|
+
type HostedChatExecutionRuntimeLogger,
|
|
815
|
+
toHostedChatExecutionFinalState,
|
|
816
|
+
} from "./hosted-chat-execution-runtime.js";
|
|
802
817
|
export {
|
|
803
818
|
finalizeHostedDetached,
|
|
804
819
|
type FinalizeHostedDetachedOptions,
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
type MessagePart,
|
|
22
22
|
type ResolvedRuntimeState,
|
|
23
23
|
type ToolCall,
|
|
24
|
+
type ToolExecutionResultRequest,
|
|
24
25
|
type ToolResultPart,
|
|
25
26
|
} from "../types.js";
|
|
26
27
|
import { ensureModelReady, type ModelRuntime, resolveModel } from "../../provider/index.js";
|
|
@@ -530,6 +531,15 @@ export class AgentRuntime {
|
|
|
530
531
|
};
|
|
531
532
|
}
|
|
532
533
|
|
|
534
|
+
private async notifyToolResult(
|
|
535
|
+
request: Omit<ToolExecutionResultRequest, "agentId">,
|
|
536
|
+
): Promise<void> {
|
|
537
|
+
await this.config.onToolResult?.({
|
|
538
|
+
agentId: this.id,
|
|
539
|
+
...request,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
|
|
533
543
|
/**
|
|
534
544
|
* Generate a response (non-streaming)
|
|
535
545
|
*/
|
|
@@ -932,18 +942,27 @@ export class AgentRuntime {
|
|
|
932
942
|
const startTime = Date.now();
|
|
933
943
|
|
|
934
944
|
const cacheCtx = tryGetCacheKeyContext();
|
|
945
|
+
const executionContext = {
|
|
946
|
+
toolCallId: tc.toolCallId,
|
|
947
|
+
...toolContext,
|
|
948
|
+
projectId: cacheCtx?.projectId ?? toolContext?.projectId,
|
|
949
|
+
};
|
|
935
950
|
const result = await executeConfiguredTool(
|
|
936
951
|
tc.toolName,
|
|
937
952
|
toolCall.args,
|
|
938
953
|
this.config.tools,
|
|
939
|
-
|
|
940
|
-
toolCallId: tc.toolCallId,
|
|
941
|
-
...toolContext,
|
|
942
|
-
projectId: cacheCtx?.projectId ?? toolContext?.projectId,
|
|
943
|
-
},
|
|
954
|
+
executionContext,
|
|
944
955
|
allowedRemoteToolNames,
|
|
945
956
|
this.config.remoteTools,
|
|
946
957
|
);
|
|
958
|
+
await this.notifyToolResult({
|
|
959
|
+
mode: "generate",
|
|
960
|
+
toolName: tc.toolName,
|
|
961
|
+
toolCallId: tc.toolCallId,
|
|
962
|
+
input: toolCall.args,
|
|
963
|
+
result,
|
|
964
|
+
context: executionContext,
|
|
965
|
+
});
|
|
947
966
|
|
|
948
967
|
toolCall.status = "completed";
|
|
949
968
|
toolCall.result = result;
|
|
@@ -1280,18 +1299,27 @@ export class AgentRuntime {
|
|
|
1280
1299
|
|
|
1281
1300
|
callbacks?.onToolCall?.(toolCall);
|
|
1282
1301
|
|
|
1302
|
+
const executionContext = {
|
|
1303
|
+
toolCallId: tc.id,
|
|
1304
|
+
...toolContext,
|
|
1305
|
+
};
|
|
1283
1306
|
const result = await executeConfiguredTool(
|
|
1284
1307
|
tc.name,
|
|
1285
1308
|
toolCall.args,
|
|
1286
1309
|
this.config.tools,
|
|
1287
|
-
|
|
1288
|
-
toolCallId: tc.id,
|
|
1289
|
-
...toolContext,
|
|
1290
|
-
},
|
|
1310
|
+
executionContext,
|
|
1291
1311
|
allowedRemoteToolNames,
|
|
1292
1312
|
this.config.remoteTools,
|
|
1293
1313
|
);
|
|
1294
1314
|
throwIfAborted(abortSignal);
|
|
1315
|
+
await this.notifyToolResult({
|
|
1316
|
+
mode: "stream",
|
|
1317
|
+
toolName: tc.name,
|
|
1318
|
+
toolCallId: tc.id,
|
|
1319
|
+
input: toolCall.args,
|
|
1320
|
+
result,
|
|
1321
|
+
context: executionContext,
|
|
1322
|
+
});
|
|
1295
1323
|
|
|
1296
1324
|
toolCall.status = "completed";
|
|
1297
1325
|
toolCall.result = result;
|
package/src/src/agent/types.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
**************************/
|
|
4
4
|
|
|
5
5
|
import type { ModelRuntime } from "../provider/types.js";
|
|
6
|
-
import type { RemoteToolSource, Tool } from "../tool/index.js";
|
|
6
|
+
import type { RemoteToolSource, Tool, ToolExecutionContext } from "../tool/index.js";
|
|
7
7
|
import { INVALID_ARGUMENT } from "../errors/error-registry.js";
|
|
8
8
|
import type { Memory } from "./memory/memory-interface.js";
|
|
9
9
|
|
|
@@ -105,6 +105,12 @@ export interface AgentConfig {
|
|
|
105
105
|
* host-owned context during a long-lived run.
|
|
106
106
|
*/
|
|
107
107
|
resolveRuntimeState?: RuntimeStateResolver;
|
|
108
|
+
/**
|
|
109
|
+
* Optional hook invoked after the runtime executes a configured local,
|
|
110
|
+
* registry, integration, or remote tool and before the tool result is
|
|
111
|
+
* persisted or streamed back to callers.
|
|
112
|
+
*/
|
|
113
|
+
onToolResult?: ToolExecutionResultHandler;
|
|
108
114
|
/**
|
|
109
115
|
* Enable skills for this agent.
|
|
110
116
|
* - true: include all discovered skills from skills/ directory
|
|
@@ -158,6 +164,20 @@ export type RuntimeStateResolver = (
|
|
|
158
164
|
request: RuntimeStateRequest,
|
|
159
165
|
) => ResolvedRuntimeState | undefined | Promise<ResolvedRuntimeState | undefined>;
|
|
160
166
|
|
|
167
|
+
export interface ToolExecutionResultRequest {
|
|
168
|
+
agentId: string;
|
|
169
|
+
mode: "generate" | "stream";
|
|
170
|
+
toolName: string;
|
|
171
|
+
toolCallId: string;
|
|
172
|
+
input: Record<string, unknown>;
|
|
173
|
+
result: unknown;
|
|
174
|
+
context?: ToolExecutionContext;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export type ToolExecutionResultHandler = (
|
|
178
|
+
request: ToolExecutionResultRequest,
|
|
179
|
+
) => void | Promise<void>;
|
|
180
|
+
|
|
161
181
|
// Import for use in AgentMiddleware
|
|
162
182
|
import type { AgentContext, AgentResponse } from "./schemas/index.js";
|
|
163
183
|
|