@assistant-ui/core 0.2.6 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/duplicate-detection.d.ts +5 -0
- package/dist/internal/duplicate-detection.d.ts.map +1 -0
- package/dist/internal/duplicate-detection.js +11 -0
- package/dist/internal/duplicate-detection.js.map +1 -0
- package/dist/react/AssistantProvider.d.ts.map +1 -1
- package/dist/react/AssistantProvider.js.map +1 -1
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +1 -2
- package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.js.map +1 -1
- package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
- package/dist/react/runtimes/external-message-converter.d.ts +1 -1
- package/dist/react/runtimes/external-message-converter.js.map +1 -1
- package/dist/runtime/api/attachment-runtime.d.ts.map +1 -1
- package/dist/runtime/api/attachment-runtime.js.map +1 -1
- package/dist/runtime/interfaces/thread-runtime-core.d.ts +8 -0
- package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-adapter.d.ts +31 -0
- package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +25 -0
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js +94 -3
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +168 -0
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -0
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +449 -0
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -0
- package/dist/subscribable/subscribable.d.ts.map +1 -1
- package/dist/subscribable/subscribable.js.map +1 -1
- package/package.json +3 -3
- package/src/adapters/index.ts +1 -4
- package/src/index.ts +10 -0
- package/src/internal/duplicate-detection.ts +26 -0
- package/src/react/AssistantProvider.tsx +2 -3
- package/src/react/index.ts +1 -6
- package/src/react/primitives/chainOfThought/ChainOfThoughtParts.tsx +1 -2
- package/src/react/primitives/message/MessageAttachments.test.tsx +1 -1
- package/src/react/primitives/message/MessageGroupedParts.tsx +1 -1
- package/src/react/runtimes/external-message-converter.ts +1 -1
- package/src/runtime/api/attachment-runtime.ts +1 -2
- package/src/runtime/interfaces/thread-runtime-core.ts +8 -0
- package/src/runtime/internal.ts +1 -4
- package/src/runtimes/external-store/external-store-adapter.ts +33 -0
- package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +1 -3
- package/src/runtimes/external-store/external-store-thread-runtime-core.ts +161 -4
- package/src/runtimes/tool-invocations/EDGE_CASES.md +194 -0
- package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +1054 -0
- package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +783 -0
- package/src/subscribable/subscribable.ts +3 -3
- package/src/tests/OptimisticState-delete-crash.test.ts +2 -0
- package/src/tests/OptimisticState-list-race.test.ts +2 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +5 -5
- package/src/tests/auiV0Encode.test.ts +1 -1
- package/src/tests/composer-can-send.test.ts +8 -4
- package/src/tests/duplicate-detection.test.ts +34 -0
- package/src/tests/external-store-thread-list-runtime-core.test.ts +1 -1
- package/src/tests/external-store-thread-runtime-core.test.ts +7 -6
- package/src/tests/no-unsafe-process-env.test.ts +1 -0
- package/src/tests/remote-thread-list-isLoading.test.ts +2 -0
- package/src/tests/thread-message-like.test.ts +4 -1
- package/src/types/index.ts +1 -4
- package/dist/react/runtimes/useToolInvocations.d.ts +0 -53
- package/dist/react/runtimes/useToolInvocations.d.ts.map +0 -1
- package/dist/react/runtimes/useToolInvocations.js +0 -380
- package/dist/react/runtimes/useToolInvocations.js.map +0 -1
- package/src/react/runtimes/useToolInvocations.ts +0 -694
|
@@ -35,9 +35,34 @@ declare class ExternalStoreThreadRuntimeCore extends BaseThreadRuntimeCore imple
|
|
|
35
35
|
extras: unknown;
|
|
36
36
|
private _converter;
|
|
37
37
|
private _store;
|
|
38
|
+
/**
|
|
39
|
+
* Client-side tool-invocations pipeline. Constructed lazily on first
|
|
40
|
+
* snapshot — only when `adapter.unstable_enableToolInvocations === true`.
|
|
41
|
+
*/
|
|
42
|
+
private _toolInvocations;
|
|
38
43
|
beginEdit(messageId: string): void;
|
|
39
44
|
constructor(contextProvider: ModelContextProvider, store: ExternalStoreAdapter<any>);
|
|
40
45
|
__internal_setAdapter(store: ExternalStoreAdapter<any>): void;
|
|
46
|
+
/**
|
|
47
|
+
* Feed the current message snapshot into the tool-invocations tracker.
|
|
48
|
+
* Opt-in via `adapter.unstable_enableToolInvocations: true`. The tracker
|
|
49
|
+
* itself is fail-silent — see ToolInvocationTracker for the
|
|
50
|
+
* state-transition contract.
|
|
51
|
+
*/
|
|
52
|
+
private _driveToolInvocations;
|
|
53
|
+
/**
|
|
54
|
+
* Lookup table from `toolCallId` to the owning assistant message's `id`,
|
|
55
|
+
* rebuilt lazily when `_messages` changes (see `_messagesForToolCallIndex`).
|
|
56
|
+
*/
|
|
57
|
+
private _toolCallToMessageId;
|
|
58
|
+
private _messagesForToolCallIndex;
|
|
59
|
+
/**
|
|
60
|
+
* Look up the assistant message that owns a tool-call part. Lazily builds
|
|
61
|
+
* (and caches) a `toolCallId → messageId` map keyed off the current
|
|
62
|
+
* `_messages` reference, so onResult dispatches stay O(1) instead of
|
|
63
|
+
* walking the full thread on every result.
|
|
64
|
+
*/
|
|
65
|
+
private _findMessageIdForToolCall;
|
|
41
66
|
switchToBranch(branchId: string): void;
|
|
42
67
|
append(message: AppendMessage): Promise<void>;
|
|
43
68
|
startRun(config: StartRunConfig): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-store-thread-runtime-core.d.ts","names":[],"sources":["../../../src/runtimes/external-store/external-store-thread-runtime-core.ts"],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"external-store-thread-runtime-core.d.ts","names":[],"sources":["../../../src/runtimes/external-store/external-store-thread-runtime-core.ts"],"mappings":";;;;;;;;;;;;cA6Ca,kBAAA,GACX,SAAA,WACA,QAAA,WAAmB,aAAa;AAAA,cAKrB,8BAAA,SACH,qBAAA,YACG,iBAAA;EAAA,QAEH,sBAAA;EAAA,QACA,qBAAA;EAAA,QAEA,aAAA;EAAA,IAeG,YAAA,CAAA,GAAY,mBAAA;EAAA,QAIf,SAAA;EACD,UAAA;EACA,cAAA;EAAA,IACI,SAAA,CAAA;EAAA,IAIA,SAAA,CAAA;EAAA,UAIQ,gBAAA,CAAA,YAA6B,aAAA;EAAA,IAI5B,KAAA,CAAA,gEAAK,kBAAA,oCAAA,iBAAA;EAAA,IAId,QAAA,CAAA;kBARkD,iBAAA;;;;;;;EAYtD,WAAA,WAAsB,gBAAA;EACtB,MAAA;EAAA,QAEC,UAAA;EAAA,QAEA,MAAA;;;;;UAMA,gBAAA;EAEQ,SAAA,CAAU,SAAA;cAQxB,eAAA,EAAiB,oBAAA,EACjB,KAAA,EAAO,oBAAA;EAMF,qBAAA,CAAsB,KAAA,EAAO,oBAAA;EAqSW;;;;;;EAAA,QAhIvC,qBAAA;EA2PqB;;;;EAAA,QAxLrB,oBAAA;EAAA,QACA,yBAAA;EApTG;;;;;;EAAA,QA4TH,yBAAA;EAmBQ,cAAA,CAAe,QAAA;EAalB,MAAA,CAAO,OAAA,EAAS,aAAA,GAAgB,OAAA;EAoBhC,QAAA,CAAS,MAAA,EAAQ,cAAA,GAAiB,OAAA;EAYlC,SAAA,CAAU,MAAA,EAAQ,eAAA,GAAkB,OAAA;EAO1C,mBAAA,CAAA;EAOA,mBAAA,CAAoB,KAAA;EAmBpB,SAAA,CAAA;EAuCA,aAAA,CAAc,OAAA,EAAS,oBAAA;EAMvB,cAAA,CAAe,OAAA,EAAS,qBAAA;EAoBxB,qBAAA,CAAsB,OAAA,EAAS,4BAAA;EAMtB,KAAA,CAAM,eAAA,YAA2B,iBAAA;EAOjC,MAAA,CAAO,IAAA,EAAM,yBAAA;EAAA,QAWrB,cAAA;AAAA"}
|
|
@@ -5,6 +5,7 @@ import { ExportedMessageRepository, MessageRepository } from "../../runtime/util
|
|
|
5
5
|
import { getThreadMessageText } from "../../utils/text.js";
|
|
6
6
|
import { BaseThreadRuntimeCore } from "../../runtime/base/base-thread-runtime-core.js";
|
|
7
7
|
import { ThreadMessageConverter } from "./thread-message-converter.js";
|
|
8
|
+
import { ToolInvocationTracker } from "../tool-invocations/ToolInvocationTracker.js";
|
|
8
9
|
//#region src/runtimes/external-store/external-store-thread-runtime-core.ts
|
|
9
10
|
const EMPTY_ARRAY = Object.freeze([]);
|
|
10
11
|
const shallowEqual = (a, b) => {
|
|
@@ -58,6 +59,11 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
|
|
|
58
59
|
extras = void 0;
|
|
59
60
|
_converter = new ThreadMessageConverter();
|
|
60
61
|
_store;
|
|
62
|
+
/**
|
|
63
|
+
* Client-side tool-invocations pipeline. Constructed lazily on first
|
|
64
|
+
* snapshot — only when `adapter.unstable_enableToolInvocations === true`.
|
|
65
|
+
*/
|
|
66
|
+
_toolInvocations = null;
|
|
61
67
|
beginEdit(messageId) {
|
|
62
68
|
if (!this._store.onEdit) throw new Error("Runtime does not support editing.");
|
|
63
69
|
super.beginEdit(messageId);
|
|
@@ -140,8 +146,82 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
|
|
|
140
146
|
});
|
|
141
147
|
this.repository.resetHead(this._assistantOptimisticId ?? messages.at(-1)?.id ?? null);
|
|
142
148
|
this._messages = this.repository.getMessages();
|
|
149
|
+
this._driveToolInvocations();
|
|
143
150
|
this._notifySubscribers();
|
|
144
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Feed the current message snapshot into the tool-invocations tracker.
|
|
154
|
+
* Opt-in via `adapter.unstable_enableToolInvocations: true`. The tracker
|
|
155
|
+
* itself is fail-silent — see ToolInvocationTracker for the
|
|
156
|
+
* state-transition contract.
|
|
157
|
+
*/
|
|
158
|
+
_driveToolInvocations() {
|
|
159
|
+
if (!this._store.unstable_enableToolInvocations) {
|
|
160
|
+
if (this._toolInvocations) {
|
|
161
|
+
this._toolInvocations.reset();
|
|
162
|
+
this._toolInvocations = null;
|
|
163
|
+
this._store.setToolStatuses?.({});
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (!this._toolInvocations) this._toolInvocations = new ToolInvocationTracker(() => this.getModelContext().tools, {
|
|
168
|
+
onResult: (command) => {
|
|
169
|
+
try {
|
|
170
|
+
const messageId = this._findMessageIdForToolCall(command.toolCallId);
|
|
171
|
+
if (messageId === void 0) return;
|
|
172
|
+
this._store.onAddToolResult?.({
|
|
173
|
+
messageId,
|
|
174
|
+
toolCallId: command.toolCallId,
|
|
175
|
+
toolName: command.toolName,
|
|
176
|
+
result: command.result,
|
|
177
|
+
isError: command.isError,
|
|
178
|
+
...command.artifact !== void 0 && { artifact: command.artifact },
|
|
179
|
+
...command.modelContent !== void 0 && { modelContent: command.modelContent }
|
|
180
|
+
});
|
|
181
|
+
} catch (err) {
|
|
182
|
+
console.error("[ExternalStoreThreadRuntimeCore] onAddToolResult dispatch failed", err);
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
onStatusesChange: (statuses) => {
|
|
186
|
+
this._store.setToolStatuses?.(Object.fromEntries(statuses));
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
this._toolInvocations.setState({
|
|
190
|
+
messages: this._messages,
|
|
191
|
+
isRunning: this._store.isRunning ?? false,
|
|
192
|
+
...this._store.isLoading !== void 0 && { isLoading: this._store.isLoading }
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Lookup table from `toolCallId` to the owning assistant message's `id`,
|
|
197
|
+
* rebuilt lazily when `_messages` changes (see `_messagesForToolCallIndex`).
|
|
198
|
+
*/
|
|
199
|
+
_toolCallToMessageId = /* @__PURE__ */ new Map();
|
|
200
|
+
_messagesForToolCallIndex = null;
|
|
201
|
+
/**
|
|
202
|
+
* Look up the assistant message that owns a tool-call part. Lazily builds
|
|
203
|
+
* (and caches) a `toolCallId → messageId` map keyed off the current
|
|
204
|
+
* `_messages` reference, so onResult dispatches stay O(1) instead of
|
|
205
|
+
* walking the full thread on every result.
|
|
206
|
+
*/
|
|
207
|
+
_findMessageIdForToolCall(toolCallId) {
|
|
208
|
+
if (this._messagesForToolCallIndex !== this._messages) {
|
|
209
|
+
this._toolCallToMessageId.clear();
|
|
210
|
+
const visit = (messages) => {
|
|
211
|
+
for (const message of messages) {
|
|
212
|
+
if (!Array.isArray(message.content)) continue;
|
|
213
|
+
for (const part of message.content) {
|
|
214
|
+
if (!part || part.type !== "tool-call") continue;
|
|
215
|
+
this._toolCallToMessageId.set(part.toolCallId, message.id);
|
|
216
|
+
if (part.messages) visit(part.messages);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
visit(this._messages);
|
|
221
|
+
this._messagesForToolCallIndex = this._messages;
|
|
222
|
+
}
|
|
223
|
+
return this._toolCallToMessageId.get(toolCallId);
|
|
224
|
+
}
|
|
145
225
|
switchToBranch(branchId) {
|
|
146
226
|
if (!this._store.setMessages) throw new Error("Runtime does not support switching branches.");
|
|
147
227
|
if (this._store.isRunning) return;
|
|
@@ -149,6 +229,7 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
|
|
|
149
229
|
this.updateMessages(this.repository.getMessages());
|
|
150
230
|
}
|
|
151
231
|
async append(message) {
|
|
232
|
+
if (message.startRun ?? message.role === "user") await this._toolInvocations?.abort();
|
|
152
233
|
if (message.parentId !== (this.messages.at(-1)?.id ?? null)) {
|
|
153
234
|
if (!this._store.onEdit) throw new Error("Runtime does not support editing messages.");
|
|
154
235
|
await this._store.onEdit(message);
|
|
@@ -156,6 +237,7 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
|
|
|
156
237
|
}
|
|
157
238
|
async startRun(config) {
|
|
158
239
|
if (!this._store.onReload) throw new Error("Runtime does not support reloading messages.");
|
|
240
|
+
await this._toolInvocations?.abort();
|
|
159
241
|
await this._store.onReload(config.parentId, config);
|
|
160
242
|
}
|
|
161
243
|
async resumeRun(config) {
|
|
@@ -168,10 +250,15 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
|
|
|
168
250
|
}
|
|
169
251
|
importExternalState(state) {
|
|
170
252
|
if (!this._store.onLoadExternalState) throw new Error("Runtime does not support importing external states.");
|
|
253
|
+
if (this._toolInvocations) {
|
|
254
|
+
this._toolInvocations.reset();
|
|
255
|
+
this._store.setToolStatuses?.({});
|
|
256
|
+
}
|
|
171
257
|
this._store.onLoadExternalState(state);
|
|
172
258
|
}
|
|
173
259
|
cancelRun() {
|
|
174
260
|
if (!this._store.onCancel) throw new Error("Runtime does not support cancelling runs.");
|
|
261
|
+
this._toolInvocations?.abort();
|
|
175
262
|
this._store.onCancel();
|
|
176
263
|
if (this._assistantOptimisticId) {
|
|
177
264
|
this.repository.deleteMessage(this._assistantOptimisticId);
|
|
@@ -190,12 +277,16 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
|
|
|
190
277
|
}, 0);
|
|
191
278
|
}
|
|
192
279
|
addToolResult(options) {
|
|
193
|
-
if (!this._store.onAddToolResult
|
|
280
|
+
if (!this._store.onAddToolResult) throw new Error("Runtime does not support tool results.");
|
|
194
281
|
this._store.onAddToolResult?.(options);
|
|
195
282
|
}
|
|
196
283
|
resumeToolCall(options) {
|
|
197
|
-
if (
|
|
198
|
-
this._store.onResumeToolCall
|
|
284
|
+
if (this._toolInvocations?.resume(options.toolCallId, options.payload) ?? false) return;
|
|
285
|
+
if (this._store.onResumeToolCall) {
|
|
286
|
+
this._store.onResumeToolCall(options);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
throw new Error(`Tool call ${options.toolCallId} is not waiting for resume.`);
|
|
199
290
|
}
|
|
200
291
|
respondToToolApproval(options) {
|
|
201
292
|
if (!this._store.onRespondToToolApproval) throw new Error("Runtime does not support tool approvals.");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-store-thread-runtime-core.js","names":[],"sources":["../../../src/runtimes/external-store/external-store-thread-runtime-core.ts"],"sourcesContent":["import type { AppendMessage, ThreadMessage } from \"../../types/message\";\nimport type {\n AddToolResultOptions,\n ResumeRunConfig,\n ResumeToolCallOptions,\n RespondToToolApprovalOptions,\n StartRunConfig,\n ThreadSuggestion,\n} from \"../../runtime/interfaces/thread-runtime-core\";\n\nimport type { ExternalStoreAdapter } from \"./external-store-adapter\";\nimport {\n getExternalStoreMessages,\n bindExternalStoreMessage,\n} from \"../../runtime/utils/external-store-message\";\nimport { ThreadMessageConverter } from \"./thread-message-converter\";\nimport { getAutoStatus, isAutoStatus } from \"../../runtime/utils/auto-status\";\nimport {\n fromThreadMessageLike,\n type ThreadMessageLike,\n} from \"../../runtime/utils/thread-message-like\";\nimport { getThreadMessageText } from \"../../utils/text\";\nimport type {\n RuntimeCapabilities,\n ThreadRuntimeCore,\n} from \"../../runtime/interfaces/thread-runtime-core\";\nimport { BaseThreadRuntimeCore } from \"../../runtime/base/base-thread-runtime-core\";\nimport type { ModelContextProvider } from \"../../model-context/types\";\nimport {\n ExportedMessageRepository,\n MessageRepository,\n} from \"../../runtime/utils/message-repository\";\n\nconst EMPTY_ARRAY: readonly ThreadSuggestion[] = Object.freeze([]);\n\nconst shallowEqual = (a: object, b: object): boolean => {\n const aKeys = Object.keys(a);\n if (aKeys.length !== Object.keys(b).length) return false;\n for (const key of aKeys) {\n if ((a as any)[key] !== (b as any)[key]) return false;\n }\n return true;\n};\n\nexport const hasUpcomingMessage = (\n isRunning: boolean,\n messages: readonly ThreadMessage[],\n) => {\n return isRunning && messages[messages.length - 1]?.role !== \"assistant\";\n};\n\nexport class ExternalStoreThreadRuntimeCore\n extends BaseThreadRuntimeCore\n implements ThreadRuntimeCore\n{\n private _assistantOptimisticId: string | null = null;\n private _lastSyncedMessageIds = new Set<string>();\n\n private _capabilities: RuntimeCapabilities = {\n switchToBranch: false,\n switchBranchDuringRun: false,\n edit: false,\n reload: false,\n cancel: false,\n unstable_copy: false,\n speech: false,\n dictation: false,\n voice: false,\n attachments: false,\n feedback: false,\n queue: false,\n };\n\n public get capabilities() {\n return this._capabilities;\n }\n\n private _messages!: readonly ThreadMessage[];\n public isDisabled!: boolean;\n public isSendDisabled!: boolean;\n public get isLoading() {\n return this._store.isLoading ?? false;\n }\n // Unlike `isLoading`: pass `undefined` through to preserve the `getThreadState` fallback.\n public get isRunning(): boolean | undefined {\n return this._store.isRunning;\n }\n\n protected override _getBaseMessages(): readonly ThreadMessage[] {\n return this._messages;\n }\n\n public override get state() {\n return this._store.state ?? super.state;\n }\n\n public get adapters() {\n return this._store.adapters;\n }\n\n public suggestions: readonly ThreadSuggestion[] = [];\n public extras: unknown = undefined;\n\n private _converter = new ThreadMessageConverter();\n\n private _store!: ExternalStoreAdapter<any>;\n\n public override beginEdit(messageId: string) {\n if (!this._store.onEdit)\n throw new Error(\"Runtime does not support editing.\");\n\n super.beginEdit(messageId);\n }\n\n constructor(\n contextProvider: ModelContextProvider,\n store: ExternalStoreAdapter<any>,\n ) {\n super(contextProvider);\n this.__internal_setAdapter(store);\n }\n\n public __internal_setAdapter(store: ExternalStoreAdapter<any>) {\n if (this._store === store) return;\n\n const isRunning = store.isRunning ?? false;\n this.isDisabled = store.isDisabled ?? false;\n this.isSendDisabled = store.isSendDisabled ?? false;\n\n const oldStore = this._store as ExternalStoreAdapter<any> | undefined;\n this._store = store;\n if (this.extras !== store.extras) {\n this.extras = store.extras;\n }\n\n const newSuggestions = store.suggestions ?? EMPTY_ARRAY;\n if (!shallowEqual(this.suggestions, newSuggestions)) {\n this.suggestions = newSuggestions;\n }\n\n const newCapabilities: RuntimeCapabilities = {\n switchToBranch: this._store.setMessages !== undefined,\n switchBranchDuringRun: false,\n edit: this._store.onEdit !== undefined,\n reload: this._store.onReload !== undefined,\n cancel: this._store.onCancel !== undefined,\n speech: this._store.adapters?.speech !== undefined,\n dictation: this._store.adapters?.dictation !== undefined,\n voice: this._store.adapters?.voice !== undefined,\n unstable_copy: this._store.unstable_capabilities?.copy !== false,\n attachments: !!this._store.adapters?.attachments,\n feedback: !!this._store.adapters?.feedback,\n queue: false,\n };\n if (!shallowEqual(this._capabilities, newCapabilities)) {\n this._capabilities = newCapabilities;\n }\n\n let messages: readonly ThreadMessage[];\n\n if (store.messageRepository) {\n // Handle messageRepository\n if (\n oldStore &&\n oldStore.isRunning === store.isRunning &&\n oldStore.messageRepository === store.messageRepository\n ) {\n this._notifySubscribers();\n return;\n }\n\n // Clear and import the message repository\n this.repository.clear();\n this._assistantOptimisticId = null;\n this._lastSyncedMessageIds = new Set();\n this.repository.import(store.messageRepository);\n\n messages = this.repository.getMessages();\n } else if (store.messages) {\n // Handle messages array\n\n if (oldStore) {\n // flush the converter cache when the convertMessage prop changes\n if (oldStore.convertMessage !== store.convertMessage) {\n this._converter = new ThreadMessageConverter();\n } else if (\n oldStore.isRunning === store.isRunning &&\n oldStore.messages === store.messages\n ) {\n this._notifySubscribers();\n // no conversion update\n return;\n }\n }\n\n messages = !store.convertMessage\n ? store.messages\n : this._converter.convertMessages(store.messages, (cache, m, idx) => {\n if (!store.convertMessage) return m;\n\n const isLast = idx === (store.messages?.length ?? 0) - 1;\n const autoStatus = getAutoStatus(\n isLast,\n isRunning,\n false,\n false,\n undefined,\n );\n\n if (\n cache &&\n (cache.role !== \"assistant\" ||\n !isAutoStatus(cache.status) ||\n cache.status === autoStatus)\n )\n return cache;\n\n const messageLike = store.convertMessage(m, idx);\n const newMessage = fromThreadMessageLike(\n messageLike,\n idx.toString(),\n autoStatus,\n );\n bindExternalStoreMessage(newMessage, m);\n return newMessage;\n });\n\n const nextIds = new Set(messages.map((m) => m.id));\n for (const prevId of this._lastSyncedMessageIds) {\n if (!nextIds.has(prevId)) this.repository.deleteMessage(prevId);\n }\n this._lastSyncedMessageIds = nextIds;\n\n for (let i = 0; i < messages.length; i++) {\n const message = messages[i]!;\n const parent = messages[i - 1];\n this.repository.addOrUpdateMessage(parent?.id ?? null, message);\n }\n } else {\n throw new Error(\n \"ExternalStoreAdapter must provide either 'messages' or 'messageRepository'\",\n );\n }\n\n // Common logic for both paths\n if (messages.length > 0) this.ensureInitialized();\n\n if ((oldStore?.isRunning ?? false) !== (store.isRunning ?? false)) {\n if (store.isRunning) {\n this._notifyEventSubscribers(\"runStart\", {});\n } else {\n this._notifyEventSubscribers(\"runEnd\", {});\n }\n }\n\n if (this._assistantOptimisticId) {\n this.repository.deleteMessage(this._assistantOptimisticId);\n this._assistantOptimisticId = null;\n }\n\n if (hasUpcomingMessage(isRunning, messages)) {\n this._assistantOptimisticId = this.repository.appendOptimisticMessage(\n messages.at(-1)?.id ?? null,\n {\n role: \"assistant\",\n content: [],\n },\n );\n }\n\n this.repository.resetHead(\n this._assistantOptimisticId ?? messages.at(-1)?.id ?? null,\n );\n\n this._messages = this.repository.getMessages();\n this._notifySubscribers();\n }\n\n public override switchToBranch(branchId: string): void {\n if (!this._store.setMessages)\n throw new Error(\"Runtime does not support switching branches.\");\n\n // Silently ignore branch switches while running\n if (this._store.isRunning) {\n return;\n }\n\n this.repository.switchToBranch(branchId);\n this.updateMessages(this.repository.getMessages());\n }\n\n public async append(message: AppendMessage): Promise<void> {\n if (message.parentId !== (this.messages.at(-1)?.id ?? null)) {\n if (!this._store.onEdit)\n throw new Error(\"Runtime does not support editing messages.\");\n await this._store.onEdit(message);\n } else {\n await this._store.onNew(message);\n }\n }\n\n public async startRun(config: StartRunConfig): Promise<void> {\n if (!this._store.onReload)\n throw new Error(\"Runtime does not support reloading messages.\");\n\n await this._store.onReload(config.parentId, config);\n }\n\n public async resumeRun(config: ResumeRunConfig): Promise<void> {\n if (!this._store.onResume)\n throw new Error(\"Runtime does not support resuming runs.\");\n\n await this._store.onResume(config);\n }\n\n public exportExternalState(): any {\n if (!this._store.onExportExternalState)\n throw new Error(\"Runtime does not support exporting external states.\");\n\n return this._store.onExportExternalState();\n }\n\n public importExternalState(state: any): void {\n if (!this._store.onLoadExternalState)\n throw new Error(\"Runtime does not support importing external states.\");\n\n this._store.onLoadExternalState(state);\n }\n\n public cancelRun(): void {\n if (!this._store.onCancel)\n throw new Error(\"Runtime does not support cancelling runs.\");\n\n this._store.onCancel();\n\n if (this._assistantOptimisticId) {\n this.repository.deleteMessage(this._assistantOptimisticId);\n this._assistantOptimisticId = null;\n }\n\n let messages = this.repository.getMessages();\n const previousMessage = messages[messages.length - 1];\n if (\n previousMessage?.role === \"user\" &&\n previousMessage.id === messages.at(-1)?.id // ensure the previous message is a leaf node\n ) {\n this.repository.deleteMessage(previousMessage.id);\n this._lastSyncedMessageIds.delete(previousMessage.id);\n if (!this.composer.text.trim()) {\n this.composer.setText(getThreadMessageText(previousMessage));\n }\n\n messages = this.repository.getMessages();\n } else {\n this._notifySubscribers();\n }\n\n // resync messages (for reloading, to restore the previous branch)\n setTimeout(() => {\n this.updateMessages(messages);\n }, 0);\n }\n\n public addToolResult(options: AddToolResultOptions) {\n if (!this._store.onAddToolResult && !this._store.onAddToolResult)\n throw new Error(\"Runtime does not support tool results.\");\n this._store.onAddToolResult?.(options);\n }\n\n public resumeToolCall(options: ResumeToolCallOptions) {\n if (!this._store.onResumeToolCall)\n throw new Error(\"Runtime does not support resuming tool calls.\");\n this._store.onResumeToolCall(options);\n }\n\n public respondToToolApproval(options: RespondToToolApprovalOptions) {\n if (!this._store.onRespondToToolApproval)\n throw new Error(\"Runtime does not support tool approvals.\");\n this._store.onRespondToToolApproval(options);\n }\n\n public override reset(initialMessages?: readonly ThreadMessageLike[]) {\n this._lastSyncedMessageIds = new Set();\n const repo = new MessageRepository();\n repo.import(ExportedMessageRepository.fromArray(initialMessages ?? []));\n this.updateMessages(repo.getMessages());\n }\n\n public override import(data: ExportedMessageRepository) {\n this._assistantOptimisticId = null;\n this._lastSyncedMessageIds = new Set();\n\n super.import(data);\n\n if (this._store.onImport) {\n this._store.onImport(this.repository.getMessages());\n }\n }\n\n private updateMessages = (messages: readonly ThreadMessage[]) => {\n const hasConverter = this._store.convertMessage !== undefined;\n if (hasConverter) {\n this._store.setMessages?.(messages.flatMap(getExternalStoreMessages));\n } else {\n // TODO mark this as readonly in v0.12.0\n this._store.setMessages?.(messages as ThreadMessage[]);\n }\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"external-store-thread-runtime-core.js","names":[],"sources":["../../../src/runtimes/external-store/external-store-thread-runtime-core.ts"],"sourcesContent":["import type { AppendMessage, ThreadMessage } from \"../../types/message\";\nimport type {\n AddToolResultOptions,\n ResumeRunConfig,\n ResumeToolCallOptions,\n RespondToToolApprovalOptions,\n StartRunConfig,\n ThreadSuggestion,\n} from \"../../runtime/interfaces/thread-runtime-core\";\n\nimport type { ExternalStoreAdapter } from \"./external-store-adapter\";\nimport {\n getExternalStoreMessages,\n bindExternalStoreMessage,\n} from \"../../runtime/utils/external-store-message\";\nimport { ThreadMessageConverter } from \"./thread-message-converter\";\nimport { getAutoStatus, isAutoStatus } from \"../../runtime/utils/auto-status\";\nimport {\n fromThreadMessageLike,\n type ThreadMessageLike,\n} from \"../../runtime/utils/thread-message-like\";\nimport { getThreadMessageText } from \"../../utils/text\";\nimport type {\n RuntimeCapabilities,\n ThreadRuntimeCore,\n} from \"../../runtime/interfaces/thread-runtime-core\";\nimport { BaseThreadRuntimeCore } from \"../../runtime/base/base-thread-runtime-core\";\nimport type { ModelContextProvider } from \"../../model-context/types\";\nimport {\n ExportedMessageRepository,\n MessageRepository,\n} from \"../../runtime/utils/message-repository\";\nimport { ToolInvocationTracker } from \"../tool-invocations/ToolInvocationTracker\";\n\nconst EMPTY_ARRAY: readonly ThreadSuggestion[] = Object.freeze([]);\n\nconst shallowEqual = (a: object, b: object): boolean => {\n const aKeys = Object.keys(a);\n if (aKeys.length !== Object.keys(b).length) return false;\n for (const key of aKeys) {\n if ((a as any)[key] !== (b as any)[key]) return false;\n }\n return true;\n};\n\nexport const hasUpcomingMessage = (\n isRunning: boolean,\n messages: readonly ThreadMessage[],\n) => {\n return isRunning && messages[messages.length - 1]?.role !== \"assistant\";\n};\n\nexport class ExternalStoreThreadRuntimeCore\n extends BaseThreadRuntimeCore\n implements ThreadRuntimeCore\n{\n private _assistantOptimisticId: string | null = null;\n private _lastSyncedMessageIds = new Set<string>();\n\n private _capabilities: RuntimeCapabilities = {\n switchToBranch: false,\n switchBranchDuringRun: false,\n edit: false,\n reload: false,\n cancel: false,\n unstable_copy: false,\n speech: false,\n dictation: false,\n voice: false,\n attachments: false,\n feedback: false,\n queue: false,\n };\n\n public get capabilities() {\n return this._capabilities;\n }\n\n private _messages!: readonly ThreadMessage[];\n public isDisabled!: boolean;\n public isSendDisabled!: boolean;\n public get isLoading() {\n return this._store.isLoading ?? false;\n }\n // Unlike `isLoading`: pass `undefined` through to preserve the `getThreadState` fallback.\n public get isRunning(): boolean | undefined {\n return this._store.isRunning;\n }\n\n protected override _getBaseMessages(): readonly ThreadMessage[] {\n return this._messages;\n }\n\n public override get state() {\n return this._store.state ?? super.state;\n }\n\n public get adapters() {\n return this._store.adapters;\n }\n\n public suggestions: readonly ThreadSuggestion[] = [];\n public extras: unknown = undefined;\n\n private _converter = new ThreadMessageConverter();\n\n private _store!: ExternalStoreAdapter<any>;\n\n /**\n * Client-side tool-invocations pipeline. Constructed lazily on first\n * snapshot — only when `adapter.unstable_enableToolInvocations === true`.\n */\n private _toolInvocations: ToolInvocationTracker | null = null;\n\n public override beginEdit(messageId: string) {\n if (!this._store.onEdit)\n throw new Error(\"Runtime does not support editing.\");\n\n super.beginEdit(messageId);\n }\n\n constructor(\n contextProvider: ModelContextProvider,\n store: ExternalStoreAdapter<any>,\n ) {\n super(contextProvider);\n this.__internal_setAdapter(store);\n }\n\n public __internal_setAdapter(store: ExternalStoreAdapter<any>) {\n if (this._store === store) return;\n\n const isRunning = store.isRunning ?? false;\n this.isDisabled = store.isDisabled ?? false;\n this.isSendDisabled = store.isSendDisabled ?? false;\n\n const oldStore = this._store as ExternalStoreAdapter<any> | undefined;\n this._store = store;\n if (this.extras !== store.extras) {\n this.extras = store.extras;\n }\n\n const newSuggestions = store.suggestions ?? EMPTY_ARRAY;\n if (!shallowEqual(this.suggestions, newSuggestions)) {\n this.suggestions = newSuggestions;\n }\n\n const newCapabilities: RuntimeCapabilities = {\n switchToBranch: this._store.setMessages !== undefined,\n switchBranchDuringRun: false,\n edit: this._store.onEdit !== undefined,\n reload: this._store.onReload !== undefined,\n cancel: this._store.onCancel !== undefined,\n speech: this._store.adapters?.speech !== undefined,\n dictation: this._store.adapters?.dictation !== undefined,\n voice: this._store.adapters?.voice !== undefined,\n unstable_copy: this._store.unstable_capabilities?.copy !== false,\n attachments: !!this._store.adapters?.attachments,\n feedback: !!this._store.adapters?.feedback,\n queue: false,\n };\n if (!shallowEqual(this._capabilities, newCapabilities)) {\n this._capabilities = newCapabilities;\n }\n\n let messages: readonly ThreadMessage[];\n\n if (store.messageRepository) {\n // Handle messageRepository\n if (\n oldStore &&\n oldStore.isRunning === store.isRunning &&\n oldStore.messageRepository === store.messageRepository\n ) {\n this._notifySubscribers();\n return;\n }\n\n // Clear and import the message repository\n this.repository.clear();\n this._assistantOptimisticId = null;\n this._lastSyncedMessageIds = new Set();\n this.repository.import(store.messageRepository);\n\n messages = this.repository.getMessages();\n } else if (store.messages) {\n // Handle messages array\n\n if (oldStore) {\n // flush the converter cache when the convertMessage prop changes\n if (oldStore.convertMessage !== store.convertMessage) {\n this._converter = new ThreadMessageConverter();\n } else if (\n oldStore.isRunning === store.isRunning &&\n oldStore.messages === store.messages\n ) {\n this._notifySubscribers();\n // no conversion update\n return;\n }\n }\n\n messages = !store.convertMessage\n ? store.messages\n : this._converter.convertMessages(store.messages, (cache, m, idx) => {\n if (!store.convertMessage) return m;\n\n const isLast = idx === (store.messages?.length ?? 0) - 1;\n const autoStatus = getAutoStatus(\n isLast,\n isRunning,\n false,\n false,\n undefined,\n );\n\n if (\n cache &&\n (cache.role !== \"assistant\" ||\n !isAutoStatus(cache.status) ||\n cache.status === autoStatus)\n )\n return cache;\n\n const messageLike = store.convertMessage(m, idx);\n const newMessage = fromThreadMessageLike(\n messageLike,\n idx.toString(),\n autoStatus,\n );\n bindExternalStoreMessage(newMessage, m);\n return newMessage;\n });\n\n const nextIds = new Set(messages.map((m) => m.id));\n for (const prevId of this._lastSyncedMessageIds) {\n if (!nextIds.has(prevId)) this.repository.deleteMessage(prevId);\n }\n this._lastSyncedMessageIds = nextIds;\n\n for (let i = 0; i < messages.length; i++) {\n const message = messages[i]!;\n const parent = messages[i - 1];\n this.repository.addOrUpdateMessage(parent?.id ?? null, message);\n }\n } else {\n throw new Error(\n \"ExternalStoreAdapter must provide either 'messages' or 'messageRepository'\",\n );\n }\n\n // Common logic for both paths\n if (messages.length > 0) this.ensureInitialized();\n\n if ((oldStore?.isRunning ?? false) !== (store.isRunning ?? false)) {\n if (store.isRunning) {\n this._notifyEventSubscribers(\"runStart\", {});\n } else {\n this._notifyEventSubscribers(\"runEnd\", {});\n }\n }\n\n if (this._assistantOptimisticId) {\n this.repository.deleteMessage(this._assistantOptimisticId);\n this._assistantOptimisticId = null;\n }\n\n if (hasUpcomingMessage(isRunning, messages)) {\n this._assistantOptimisticId = this.repository.appendOptimisticMessage(\n messages.at(-1)?.id ?? null,\n {\n role: \"assistant\",\n content: [],\n },\n );\n }\n\n this.repository.resetHead(\n this._assistantOptimisticId ?? messages.at(-1)?.id ?? null,\n );\n\n this._messages = this.repository.getMessages();\n\n this._driveToolInvocations();\n\n this._notifySubscribers();\n }\n\n /**\n * Feed the current message snapshot into the tool-invocations tracker.\n * Opt-in via `adapter.unstable_enableToolInvocations: true`. The tracker\n * itself is fail-silent — see ToolInvocationTracker for the\n * state-transition contract.\n */\n private _driveToolInvocations(): void {\n if (!this._store.unstable_enableToolInvocations) {\n // Adapter did not opt in (default). If a tracker was previously\n // constructed (e.g. the adapter just toggled the flag off via a\n // dynamic swap), drop it so subsequent snapshots are no-ops.\n if (this._toolInvocations) {\n this._toolInvocations.reset();\n this._toolInvocations = null;\n this._store.setToolStatuses?.({});\n }\n return;\n }\n\n if (!this._toolInvocations) {\n this._toolInvocations = new ToolInvocationTracker(\n () => this.getModelContext().tools,\n {\n onResult: (command) => {\n try {\n const messageId = this._findMessageIdForToolCall(\n command.toolCallId,\n );\n if (messageId === undefined) {\n // The tool call no longer exists in the snapshot (e.g.\n // rolled back). Drop the result.\n return;\n }\n this._store.onAddToolResult?.({\n messageId,\n toolCallId: command.toolCallId,\n toolName: command.toolName,\n result: command.result,\n isError: command.isError,\n ...(command.artifact !== undefined && {\n artifact: command.artifact,\n }),\n ...(command.modelContent !== undefined && {\n modelContent: command.modelContent,\n }),\n });\n } catch (err) {\n console.error(\n \"[ExternalStoreThreadRuntimeCore] onAddToolResult dispatch failed\",\n err,\n );\n }\n },\n onStatusesChange: (statuses) => {\n this._store.setToolStatuses?.(Object.fromEntries(statuses));\n },\n },\n );\n }\n\n this._toolInvocations.setState({\n messages: this._messages,\n isRunning: this._store.isRunning ?? false,\n ...(this._store.isLoading !== undefined && {\n isLoading: this._store.isLoading,\n }),\n });\n }\n\n /**\n * Lookup table from `toolCallId` to the owning assistant message's `id`,\n * rebuilt lazily when `_messages` changes (see `_messagesForToolCallIndex`).\n */\n private _toolCallToMessageId = new Map<string, string>();\n private _messagesForToolCallIndex: readonly ThreadMessage[] | null = null;\n\n /**\n * Look up the assistant message that owns a tool-call part. Lazily builds\n * (and caches) a `toolCallId → messageId` map keyed off the current\n * `_messages` reference, so onResult dispatches stay O(1) instead of\n * walking the full thread on every result.\n */\n private _findMessageIdForToolCall(toolCallId: string): string | undefined {\n if (this._messagesForToolCallIndex !== this._messages) {\n this._toolCallToMessageId.clear();\n const visit = (messages: readonly ThreadMessage[]): void => {\n for (const message of messages) {\n if (!Array.isArray(message.content)) continue;\n for (const part of message.content) {\n if (!part || part.type !== \"tool-call\") continue;\n this._toolCallToMessageId.set(part.toolCallId, message.id);\n if (part.messages) visit(part.messages);\n }\n }\n };\n visit(this._messages);\n this._messagesForToolCallIndex = this._messages;\n }\n return this._toolCallToMessageId.get(toolCallId);\n }\n\n public override switchToBranch(branchId: string): void {\n if (!this._store.setMessages)\n throw new Error(\"Runtime does not support switching branches.\");\n\n // Silently ignore branch switches while running\n if (this._store.isRunning) {\n return;\n }\n\n this.repository.switchToBranch(branchId);\n this.updateMessages(this.repository.getMessages());\n }\n\n public async append(message: AppendMessage): Promise<void> {\n // Auto-abort in-flight client-side tool executions when a new run is\n // about to start. Without this, a tool that finishes after the new turn\n // begins would feed a stale result into `onAddToolResult`, racing with\n // the new turn the user just initiated. `startRun` defaults to true for\n // user messages — matches the satellites' historical opt-in cancel\n // behavior, which is now built in.\n if (message.startRun ?? message.role === \"user\") {\n await this._toolInvocations?.abort();\n }\n\n if (message.parentId !== (this.messages.at(-1)?.id ?? null)) {\n if (!this._store.onEdit)\n throw new Error(\"Runtime does not support editing messages.\");\n await this._store.onEdit(message);\n } else {\n await this._store.onNew(message);\n }\n }\n\n public async startRun(config: StartRunConfig): Promise<void> {\n if (!this._store.onReload)\n throw new Error(\"Runtime does not support reloading messages.\");\n\n // Auto-abort in-flight client-side tool executions when a run reloads;\n // any results that land afterward would target a turn that no longer\n // exists. See `append` above for full rationale.\n await this._toolInvocations?.abort();\n\n await this._store.onReload(config.parentId, config);\n }\n\n public async resumeRun(config: ResumeRunConfig): Promise<void> {\n if (!this._store.onResume)\n throw new Error(\"Runtime does not support resuming runs.\");\n\n await this._store.onResume(config);\n }\n\n public exportExternalState(): any {\n if (!this._store.onExportExternalState)\n throw new Error(\"Runtime does not support exporting external states.\");\n\n return this._store.onExportExternalState();\n }\n\n public importExternalState(state: any): void {\n if (!this._store.onLoadExternalState)\n throw new Error(\"Runtime does not support importing external states.\");\n\n // Re-arm the tracker so the next adapter snapshot (containing the\n // imported state) is treated as historical — no streamCall/execute\n // fires for the loaded tool calls. The adapter is expected to update\n // its messages in response to onLoadExternalState; that update flows\n // back here via __internal_setAdapter. We only clear adapter-side\n // tool statuses when the tracker is the source of truth — otherwise\n // we'd wipe statuses the adapter is managing on its own.\n if (this._toolInvocations) {\n this._toolInvocations.reset();\n this._store.setToolStatuses?.({});\n }\n\n this._store.onLoadExternalState(state);\n }\n\n public cancelRun(): void {\n if (!this._store.onCancel)\n throw new Error(\"Runtime does not support cancelling runs.\");\n\n // Abort any in-flight client-side tool executions. Fire-and-forget —\n // the abort resolves once executions settle, but we don't gate the\n // cancel on it.\n void this._toolInvocations?.abort();\n\n this._store.onCancel();\n\n if (this._assistantOptimisticId) {\n this.repository.deleteMessage(this._assistantOptimisticId);\n this._assistantOptimisticId = null;\n }\n\n let messages = this.repository.getMessages();\n const previousMessage = messages[messages.length - 1];\n if (\n previousMessage?.role === \"user\" &&\n previousMessage.id === messages.at(-1)?.id // ensure the previous message is a leaf node\n ) {\n this.repository.deleteMessage(previousMessage.id);\n this._lastSyncedMessageIds.delete(previousMessage.id);\n if (!this.composer.text.trim()) {\n this.composer.setText(getThreadMessageText(previousMessage));\n }\n\n messages = this.repository.getMessages();\n } else {\n this._notifySubscribers();\n }\n\n // resync messages (for reloading, to restore the previous branch)\n setTimeout(() => {\n this.updateMessages(messages);\n }, 0);\n }\n\n public addToolResult(options: AddToolResultOptions) {\n if (!this._store.onAddToolResult)\n throw new Error(\"Runtime does not support tool results.\");\n this._store.onAddToolResult?.(options);\n }\n\n public resumeToolCall(options: ResumeToolCallOptions) {\n // Tracker owns its own human-input handlers — let it resume in-process\n // tool calls without round-tripping through the adapter. Falls back to\n // the adapter's onResumeToolCall (if any) for tool calls the tracker\n // doesn't know about.\n const handled =\n this._toolInvocations?.resume(options.toolCallId, options.payload) ??\n false;\n if (handled) return;\n\n if (this._store.onResumeToolCall) {\n this._store.onResumeToolCall(options);\n return;\n }\n\n throw new Error(\n `Tool call ${options.toolCallId} is not waiting for resume.`,\n );\n }\n\n public respondToToolApproval(options: RespondToToolApprovalOptions) {\n if (!this._store.onRespondToToolApproval)\n throw new Error(\"Runtime does not support tool approvals.\");\n this._store.onRespondToToolApproval(options);\n }\n\n public override reset(initialMessages?: readonly ThreadMessageLike[]) {\n this._lastSyncedMessageIds = new Set();\n const repo = new MessageRepository();\n repo.import(ExportedMessageRepository.fromArray(initialMessages ?? []));\n this.updateMessages(repo.getMessages());\n }\n\n public override import(data: ExportedMessageRepository) {\n this._assistantOptimisticId = null;\n this._lastSyncedMessageIds = new Set();\n\n super.import(data);\n\n if (this._store.onImport) {\n this._store.onImport(this.repository.getMessages());\n }\n }\n\n private updateMessages = (messages: readonly ThreadMessage[]) => {\n const hasConverter = this._store.convertMessage !== undefined;\n if (hasConverter) {\n this._store.setMessages?.(messages.flatMap(getExternalStoreMessages));\n } else {\n // TODO mark this as readonly in v0.12.0\n this._store.setMessages?.(messages as ThreadMessage[]);\n }\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { ThreadMessage } from "../../types/message.js";
|
|
2
|
+
import { Tool, ToolModelContentPart } from "assistant-stream";
|
|
3
|
+
import { ReadonlyJSONValue } from "assistant-stream/utils";
|
|
4
|
+
|
|
5
|
+
//#region src/runtimes/tool-invocations/ToolInvocationTracker.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Streaming execution state for a frontend tool.
|
|
8
|
+
*/
|
|
9
|
+
type ToolExecutionStatus = {
|
|
10
|
+
type: "executing";
|
|
11
|
+
} | {
|
|
12
|
+
type: "interrupt";
|
|
13
|
+
payload: {
|
|
14
|
+
type: "human";
|
|
15
|
+
payload: unknown;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
type AddToolResultCommand = {
|
|
19
|
+
readonly type: "add-tool-result";
|
|
20
|
+
readonly toolCallId: string;
|
|
21
|
+
readonly toolName: string;
|
|
22
|
+
readonly result: ReadonlyJSONValue;
|
|
23
|
+
readonly isError: boolean;
|
|
24
|
+
readonly artifact?: ReadonlyJSONValue;
|
|
25
|
+
readonly modelContent?: readonly ToolModelContentPart[];
|
|
26
|
+
};
|
|
27
|
+
type ToolInvocationTrackerSnapshot = {
|
|
28
|
+
readonly messages: readonly ThreadMessage[]; /** Whether the producing runtime is currently streaming new output. */
|
|
29
|
+
readonly isRunning: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the producing runtime is still loading historical state.
|
|
32
|
+
* When `true`, every snapshot is treated as historical (no `streamCall` /
|
|
33
|
+
* `execute` fires). When `false`, processing resumes as live.
|
|
34
|
+
*/
|
|
35
|
+
readonly isLoading?: boolean;
|
|
36
|
+
};
|
|
37
|
+
type ToolInvocationTrackerCallbacks = {
|
|
38
|
+
/**
|
|
39
|
+
* Invoked when a client-side `execute()` returns a result and the runtime
|
|
40
|
+
* needs to feed it back into the conversation.
|
|
41
|
+
*/
|
|
42
|
+
onResult: (command: AddToolResultCommand) => void;
|
|
43
|
+
/**
|
|
44
|
+
* Invoked whenever the per-tool-call status map changes (executing /
|
|
45
|
+
* interrupt / cleared). The callback receives a fresh map; mutating the
|
|
46
|
+
* argument is not supported.
|
|
47
|
+
*/
|
|
48
|
+
onStatusesChange: (statuses: ReadonlyMap<string, ToolExecutionStatus>) => void;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Plain-class port of the former `useToolInvocations` React hook. Owns the
|
|
52
|
+
* assistant-stream pipeline that drives client-side `streamCall` / `execute`
|
|
53
|
+
* for tool-call parts surfaced by a thread runtime, plus the per-tool-call
|
|
54
|
+
* status map that consumers render against.
|
|
55
|
+
*
|
|
56
|
+
* **Contract**: `streamCall` (and `execute`) fires exactly once per logical
|
|
57
|
+
* `toolCallId`. Args mutations after first completion, result replacement,
|
|
58
|
+
* and result clearing are *not* surfaced through additional `streamCall`
|
|
59
|
+
* invocations — by design — so hosts cannot observe spurious re-fires of
|
|
60
|
+
* side effects. The follow-up `reader.events()` API will expose those
|
|
61
|
+
* post-completion transitions to consumers that opt in.
|
|
62
|
+
*
|
|
63
|
+
* State-transition safety: every public method that observes runtime state
|
|
64
|
+
* (`setState`, `reset`, `abort`, `resume`) wraps its work in try/catch and
|
|
65
|
+
* logs to `console.error` rather than throwing. The tracker is built into
|
|
66
|
+
* the hot message-processing path, so a malformed snapshot must never crash
|
|
67
|
+
* the host runtime. See ./EDGE_CASES.md for the known non-trivial state
|
|
68
|
+
* transitions and what each does today.
|
|
69
|
+
*/
|
|
70
|
+
declare class ToolInvocationTracker {
|
|
71
|
+
private readonly _getTools;
|
|
72
|
+
private readonly _callbacks;
|
|
73
|
+
private readonly _entries;
|
|
74
|
+
/**
|
|
75
|
+
* Tool call ids whose `execute` should be short-circuited in the wrapper.
|
|
76
|
+
* Populated when an entry is created with a result already attached
|
|
77
|
+
* (history reload, mid-run resume, etc.) — `execute` is suppressed so
|
|
78
|
+
* client-side side effects don't double-run. Membership outlives the
|
|
79
|
+
* entry: `reset()` deliberately does *not* clear this so post-abort
|
|
80
|
+
* cancellation `result` chunks for pre-resolved entries can still be
|
|
81
|
+
* recognized and dropped. Growth is bounded by the number of pre-resolved
|
|
82
|
+
* tool calls observed in the session.
|
|
83
|
+
*/
|
|
84
|
+
private readonly _skipExecuteStreamIds;
|
|
85
|
+
private readonly _humanInput;
|
|
86
|
+
/** In-flight `execute` invocations keyed by tool call id. */
|
|
87
|
+
private readonly _executing;
|
|
88
|
+
private readonly _settledResolvers;
|
|
89
|
+
private _statuses;
|
|
90
|
+
private _ac;
|
|
91
|
+
private _pendingRestore;
|
|
92
|
+
/** Cached last snapshot, used to skip processing on identical re-renders. */
|
|
93
|
+
private _lastSnapshot;
|
|
94
|
+
private _isRunning;
|
|
95
|
+
private _controller;
|
|
96
|
+
/**
|
|
97
|
+
* Set when the assistant-stream pipeline has died (errored out via
|
|
98
|
+
* `.pipeTo(...).catch(...)`). The next `setState` re-initializes the
|
|
99
|
+
* pipeline and demotes all active entries to restored so they survive
|
|
100
|
+
* across the restart without re-firing `streamCall` (preserves the
|
|
101
|
+
* "exactly once" contract). Capped at a single auto-restart per session
|
|
102
|
+
* — repeated failures keep the tracker dead with a more visible error.
|
|
103
|
+
*/
|
|
104
|
+
private _pipelineDead;
|
|
105
|
+
private _pipelineRestartUsed;
|
|
106
|
+
constructor(getTools: () => Record<string, Tool> | undefined, callbacks: ToolInvocationTrackerCallbacks);
|
|
107
|
+
/**
|
|
108
|
+
* Build the assistant-stream pipeline. Called once from the constructor
|
|
109
|
+
* and at most once again if `_pipelineDead` is set (see F.4 in
|
|
110
|
+
* EDGE_CASES.md).
|
|
111
|
+
*/
|
|
112
|
+
private _initPipeline;
|
|
113
|
+
/**
|
|
114
|
+
* Feed the next observed snapshot into the tracker. Called from the host
|
|
115
|
+
* runtime whenever its message list / running state changes.
|
|
116
|
+
*/
|
|
117
|
+
setState(snapshot: ToolInvocationTrackerSnapshot): void;
|
|
118
|
+
/**
|
|
119
|
+
* Reset the tracker so the next observed snapshot is treated as historical.
|
|
120
|
+
* Clears entries and aborts any in-flight executions. Used by callers like
|
|
121
|
+
* `importExternalState` to mark a freshly loaded state as restored.
|
|
122
|
+
*/
|
|
123
|
+
reset(): void;
|
|
124
|
+
/**
|
|
125
|
+
* Abort any in-flight `execute()` invocations. Resolves once all of them
|
|
126
|
+
* have settled (or immediately if none are running).
|
|
127
|
+
*/
|
|
128
|
+
abort(): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Resolve a pending human-input request for the given tool call. Returns
|
|
131
|
+
* `true` if a pending request was resumed, `false` if the tracker has no
|
|
132
|
+
* outstanding request for that id (the caller should fall back to its own
|
|
133
|
+
* dispatch path).
|
|
134
|
+
*/
|
|
135
|
+
resume(toolCallId: string, payload: unknown): boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Returns the current tool execution status map. The returned `Map` is
|
|
138
|
+
* the tracker's internal store — do not mutate it. Treat the reference
|
|
139
|
+
* as a snapshot that may be replaced wholesale on the next status
|
|
140
|
+
* transition.
|
|
141
|
+
*/
|
|
142
|
+
getStatuses(): ReadonlyMap<string, ToolExecutionStatus>;
|
|
143
|
+
private _getWrappedTools;
|
|
144
|
+
private _onHumanInput;
|
|
145
|
+
private _onExecutionStart;
|
|
146
|
+
private _onExecutionEnd;
|
|
147
|
+
private _handleResultChunk;
|
|
148
|
+
private _invokeOnResult;
|
|
149
|
+
private _invokeOnStatusesChange;
|
|
150
|
+
private _setStatus;
|
|
151
|
+
private _deleteStatus;
|
|
152
|
+
private _hasExecutableTool;
|
|
153
|
+
private _shouldCloseArgsStream;
|
|
154
|
+
private _startActiveEntry;
|
|
155
|
+
/**
|
|
156
|
+
* Demote every active entry back to the restored phase. Used by the
|
|
157
|
+
* pipeline-restart path so that, after a fresh pipeline is built, the
|
|
158
|
+
* next observed snapshot does not re-fire `streamCall` for tool calls
|
|
159
|
+
* that already fired pre-death. Args / hasResult tracking is preserved
|
|
160
|
+
* so signature comparisons still work.
|
|
161
|
+
*/
|
|
162
|
+
private _demoteEntriesToRestored;
|
|
163
|
+
private _processArgsText;
|
|
164
|
+
private _processMessages;
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
export { AddToolResultCommand, ToolExecutionStatus, ToolInvocationTracker, ToolInvocationTrackerCallbacks, ToolInvocationTrackerSnapshot };
|
|
168
|
+
//# sourceMappingURL=ToolInvocationTracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToolInvocationTracker.d.ts","names":[],"sources":["../../../src/runtimes/tool-invocations/ToolInvocationTracker.ts"],"mappings":";;;;;;;AAoBA;KAAY,mBAAA;EACN,IAAA;AAAA;EAEA,IAAA;EACA,OAAA;IAAW,IAAA;IAAe,OAAA;EAAA;AAAA;AAAA,KAGpB,oBAAA;EAAA,SACD,IAAA;EAAA,SACA,UAAA;EAAA,SACA,QAAA;EAAA,SACA,MAAA,EAAQ,iBAAA;EAAA,SACR,OAAA;EAAA,SACA,QAAA,GAAW,iBAAA;EAAA,SACX,YAAA,YAAwB,oBAAA;AAAA;AAAA,KAGvB,6BAAA;EAAA,SACD,QAAA,WAAmB,aAAa,IARhC;EAAA,SAUA,SAAA;EATQ;;;;;EAAA,SAeR,SAAA;AAAA;AAAA,KAGC,8BAAA;EAZA;;;;EAiBV,QAAA,GAAW,OAAA,EAAS,oBAAA;EAhBQ;;;;AAQV;EAclB,gBAAA,GACE,QAAA,EAAU,WAAA,SAAoB,mBAAA;AAAA;;;;;;;;;;;;;;;AAAoB;AAiEtD;;;;;cAAa,qBAAA;EAAA,iBACM,SAAA;EAAA,iBACA,UAAA;EAAA,iBAEA,QAAA;EAqPK;;;;;;;;;;EAAA,iBA1OL,qBAAA;EAAA,iBACA,WAAA;EAcT;EAAA,iBANS,UAAA;EAAA,iBACA,iBAAA;EAAA,QAET,SAAA;EAAA,QAEA,GAAA;EAAA,QACA,eAAA;;UAGA,aAAA;EAAA,QACA,UAAA;EAAA,QAEA,WAAA;EAeK;;;;;;;;EAAA,QALL,aAAA;EAAA,QACA,oBAAA;cAGN,QAAA,QAAgB,MAAA,SAAe,IAAA,eAC/B,SAAA,EAAW,8BAAA;EAkLqB;;;;;EAAA,QArK1B,aAAA;EAoPA;;;;EArMD,QAAA,CAAS,QAAA,EAAU,6BAAA;EA2RlB;;;;;EAxND,KAAA,CAAA;EAuSC;;;AAwGgB;EA7XjB,KAAA,CAAA,GAAS,OAAA;;;;;;;EAiCT,MAAA,CAAO,UAAA,UAAoB,OAAA;;;;;;;EAoB3B,WAAA,CAAA,GAAe,WAAA,SAAoB,mBAAA;EAAA,QAMlC,gBAAA;EAAA,QA8BA,aAAA;EAAA,QAuBA,iBAAA;EAAA,QAOA,eAAA;EAAA,QAkBA,kBAAA;EAAA,QAqCA,eAAA;EAAA,QAWA,uBAAA;EAAA,QAaA,UAAA;EAAA,QAOA,aAAA;EAAA,QAUA,kBAAA;EAAA,QAKA,sBAAA;EAAA,QAgBA,iBAAA;;;;;;;;UA8BA,wBAAA;EAAA,QAWA,gBAAA;EAAA,QAwGA,gBAAA;AAAA"}
|