@assistant-ui/core 0.3.15 → 0.3.16
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/internal.d.ts +7 -2
- package/dist/internal.js +8 -3
- package/dist/model-context/frame/host.d.ts.map +1 -1
- package/dist/model-context/frame/host.js +3 -2
- package/dist/model-context/frame/host.js.map +1 -1
- package/dist/model-context/frame/provider.d.ts +2 -0
- package/dist/model-context/frame/provider.d.ts.map +1 -1
- package/dist/model-context/frame/provider.js +88 -25
- package/dist/model-context/frame/provider.js.map +1 -1
- package/dist/model-context/interactable-composer-metadata.d.ts +1 -1
- package/dist/model-context/interactable-composer-metadata.js +1 -1
- package/dist/model-context/interactable-composer-metadata.js.map +1 -1
- package/dist/model-context/registry.d.ts +2 -2
- package/dist/model-context/registry.d.ts.map +1 -1
- package/dist/model-context/registry.js +9 -28
- package/dist/model-context/registry.js.map +1 -1
- package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
- package/dist/react/client/InMemoryThreadList.js +24 -20
- package/dist/react/client/InMemoryThreadList.js.map +1 -1
- package/dist/react/client/Interactables.d.ts.map +1 -1
- package/dist/react/client/Interactables.js +59 -177
- package/dist/react/client/Interactables.js.map +1 -1
- package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
- package/dist/react/client/RemoteThreadList.js +23 -56
- package/dist/react/client/RemoteThreadList.js.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
- package/dist/react/interactables-legacy/Interactables.js +56 -174
- package/dist/react/interactables-legacy/Interactables.js.map +1 -1
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts +24 -0
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -0
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +138 -0
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -0
- package/dist/react/primitives/composer/ComposerIf.js.map +1 -1
- package/dist/react/primitives/composer/ComposerQueue.d.ts +2 -1
- package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageParts.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +23 -55
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -1
- package/dist/react/runtimes/assistant-transport/runManager.js +2 -12
- package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +3 -10
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +20 -67
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts +12 -0
- package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts.map +1 -0
- package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js +10 -0
- package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js.map +1 -0
- package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -1
- package/dist/react/runtimes/createMessageConverter.d.ts +2 -1
- package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
- package/dist/react/runtimes/external-message-converter.d.ts +6 -25
- package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
- package/dist/react/runtimes/external-message-converter.js +33 -233
- package/dist/react/runtimes/external-message-converter.js.map +1 -1
- package/dist/runtime/api/composer-runtime.d.ts +1 -1
- package/dist/runtime/api/composer-runtime.js.map +1 -1
- package/dist/runtime/api/thread-runtime.d.ts +1 -2
- package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +14 -29
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -4
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.js +3 -9
- package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.js +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
- package/dist/runtime/interfaces/composer-runtime-core.d.ts +1 -1
- package/dist/runtime/interfaces/thread-runtime-core.d.ts +1 -1
- package/dist/runtime/internal.d.ts +2 -1
- package/dist/runtime/internal.js +2 -1
- package/dist/runtime/queue/external-thread-queue-adapter.d.ts +1 -1
- package/dist/runtime/queue/message-queue.d.ts.map +1 -1
- package/dist/runtime/queue/message-queue.js +1 -1
- package/dist/runtime/queue/message-queue.js.map +1 -1
- package/dist/runtime/queue/queue-item.d.ts +12 -0
- package/dist/runtime/queue/queue-item.d.ts.map +1 -0
- package/dist/runtime/queue/queue-item.js +6 -0
- package/dist/runtime/queue/queue-item.js.map +1 -0
- package/dist/runtime/utils/abortable-thread-load.d.ts +15 -0
- package/dist/runtime/utils/abortable-thread-load.d.ts.map +1 -0
- package/dist/runtime/utils/abortable-thread-load.js +35 -0
- package/dist/runtime/utils/abortable-thread-load.js.map +1 -0
- package/dist/runtime/utils/attachment-add-operations.d.ts +18 -0
- package/dist/runtime/utils/attachment-add-operations.d.ts.map +1 -0
- package/dist/runtime/utils/attachment-add-operations.js +43 -0
- package/dist/runtime/utils/attachment-add-operations.js.map +1 -0
- package/dist/runtime/utils/auto-status.d.ts +6 -1
- package/dist/runtime/utils/auto-status.d.ts.map +1 -1
- package/dist/runtime/utils/auto-status.js +7 -1
- package/dist/runtime/utils/auto-status.js.map +1 -1
- package/dist/runtime/utils/external-message-conversion.d.ts +47 -0
- package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -0
- package/dist/runtime/utils/external-message-conversion.js +221 -0
- package/dist/runtime/utils/external-message-conversion.js.map +1 -0
- package/dist/runtime/utils/external-store-message.d.ts +2 -1
- package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
- package/dist/runtime/utils/external-store-message.js +2 -1
- package/dist/runtime/utils/external-store-message.js.map +1 -1
- package/dist/runtime/utils/message-repository-session.d.ts +30 -0
- package/dist/runtime/utils/message-repository-session.d.ts.map +1 -0
- package/dist/runtime/utils/message-repository-session.js +130 -0
- package/dist/runtime/utils/message-repository-session.js.map +1 -0
- package/dist/runtime/utils/message-repository.d.ts.map +1 -1
- package/dist/runtime/utils/message-repository.js +3 -4
- package/dist/runtime/utils/message-repository.js.map +1 -1
- package/dist/runtime/utils/pending-tool-calls.d.ts +20 -0
- package/dist/runtime/utils/pending-tool-calls.d.ts.map +1 -0
- package/dist/runtime/utils/pending-tool-calls.js +22 -0
- package/dist/runtime/utils/pending-tool-calls.js.map +1 -0
- package/dist/runtime/utils/thread-message-like.d.ts +2 -1
- package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
- package/dist/runtime/utils/thread-message-like.js.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +2 -5
- 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 +3 -10
- 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 +19 -1
- 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 +122 -20
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts +3 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.js +20 -2
- package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +12 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.js +77 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +2 -0
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
- package/dist/store/clients/external-thread.d.ts.map +1 -1
- package/dist/store/clients/external-thread.js +74 -99
- package/dist/store/clients/external-thread.js.map +1 -1
- package/dist/store/clients/single-thread-list.d.ts.map +1 -1
- package/dist/store/clients/single-thread-list.js +1 -1
- package/dist/store/clients/single-thread-list.js.map +1 -1
- package/dist/store/clients/suggestions.d.ts.map +1 -1
- package/dist/store/clients/suggestions.js +16 -7
- package/dist/store/clients/suggestions.js.map +1 -1
- package/dist/store/index.d.ts +4 -2
- package/dist/store/scope-registration.d.ts +1 -1
- package/dist/store/scopes/composer.d.ts +2 -1
- package/dist/store/scopes/composer.d.ts.map +1 -1
- package/dist/store/scopes/message.d.ts +1 -1
- package/dist/store/scopes/queue-item.d.ts +2 -9
- package/dist/store/scopes/queue-item.d.ts.map +1 -1
- package/dist/store/scopes/queue-item.js +0 -6
- package/dist/types/message.d.ts +7 -3
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js.map +1 -1
- package/dist/utils/data-url.d.ts +14 -1
- package/dist/utils/data-url.d.ts.map +1 -1
- package/dist/utils/data-url.js +13 -1
- package/dist/utils/data-url.js.map +1 -1
- package/dist/utils/invoke-user-callback.d.ts +5 -0
- package/dist/utils/invoke-user-callback.d.ts.map +1 -0
- package/dist/utils/invoke-user-callback.js +19 -0
- package/dist/utils/invoke-user-callback.js.map +1 -0
- package/dist/utils/normalizePartStatus.js.map +1 -1
- package/package.json +10 -10
- package/src/internal.ts +12 -0
- package/src/model-context/frame/host.test.ts +91 -7
- package/src/model-context/frame/host.ts +7 -2
- package/src/model-context/frame/provider.test.ts +267 -12
- package/src/model-context/frame/provider.ts +113 -31
- package/src/model-context/interactable-composer-metadata.ts +1 -1
- package/src/model-context/registry.test.ts +44 -0
- package/src/model-context/registry.ts +13 -35
- package/src/react/client/InMemoryThreadList.test.tsx +49 -0
- package/src/react/client/InMemoryThreadList.ts +7 -1
- package/src/react/client/Interactables.ts +19 -178
- package/src/react/client/RemoteThreadList.test.ts +96 -0
- package/src/react/client/RemoteThreadList.ts +27 -59
- package/src/react/interactables-legacy/Interactables.ts +20 -177
- package/src/react/interactables-shared/useInteractablePersistenceQueue.test.tsx +161 -0
- package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +206 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +26 -56
- package/src/react/runtimes/assistant-transport/runManager.ts +4 -17
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +92 -0
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +40 -141
- package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.test.ts +37 -0
- package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.ts +18 -0
- package/src/react/runtimes/external-message-converter.test.ts +0 -14
- package/src/react/runtimes/external-message-converter.test.tsx +77 -0
- package/src/react/runtimes/external-message-converter.ts +58 -481
- package/src/runtime/api/composer-runtime.ts +1 -1
- package/src/runtime/api/message-runtime.test.ts +28 -1
- package/src/runtime/base/base-composer-runtime-core.ts +20 -40
- package/src/runtime/base/base-thread-runtime-core.test.ts +16 -0
- package/src/runtime/base/base-thread-runtime-core.ts +6 -11
- package/src/runtime/base/default-thread-composer-runtime-core.ts +1 -4
- package/src/runtime/interfaces/composer-runtime-core.ts +1 -1
- package/src/runtime/interfaces/thread-runtime-core.ts +1 -1
- package/src/runtime/internal.ts +5 -0
- package/src/runtime/queue/external-thread-queue-adapter.ts +1 -1
- package/src/runtime/queue/message-queue.ts +1 -4
- package/src/runtime/queue/queue-item.ts +10 -0
- package/src/runtime/utils/abortable-thread-load.test.ts +87 -0
- package/src/runtime/utils/abortable-thread-load.ts +53 -0
- package/src/runtime/utils/attachment-add-operations.ts +59 -0
- package/src/runtime/utils/auto-status.ts +33 -0
- package/src/runtime/utils/external-message-conversion.test.ts +113 -0
- package/src/runtime/utils/external-message-conversion.ts +494 -0
- package/src/runtime/utils/external-store-message.ts +2 -0
- package/src/runtime/utils/message-repository-session.test.ts +195 -0
- package/src/runtime/utils/message-repository-session.ts +176 -0
- package/src/runtime/utils/message-repository.ts +7 -4
- package/src/runtime/utils/pending-tool-calls.test.ts +57 -0
- package/src/runtime/utils/pending-tool-calls.ts +35 -0
- package/src/runtime/utils/thread-message-like.ts +2 -0
- package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +6 -14
- package/src/runtimes/external-store/external-store-thread-runtime-core.ts +175 -28
- package/src/runtimes/local/local-thread-runtime-core.test.ts +172 -1
- package/src/runtimes/local/local-thread-runtime-core.ts +48 -3
- package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +37 -0
- package/src/runtimes/remote-thread-list/remote-thread-state.ts +123 -0
- package/src/runtimes/tool-invocations/EDGE_CASES.md +17 -0
- package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +140 -0
- package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +3 -0
- package/src/store/clients/external-thread.ts +16 -61
- package/src/store/clients/single-thread-list.ts +4 -1
- package/src/store/clients/suggestions.test.ts +31 -0
- package/src/store/clients/suggestions.ts +18 -19
- package/src/store/scopes/message.ts +1 -1
- package/src/store/scopes/queue-item.ts +2 -9
- package/src/tests/MessageRepository.test.ts +77 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +194 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +158 -0
- package/src/tests/external-store-thread-runtime-core-adapter.test.ts +164 -0
- package/src/tests/external-store-thread-runtime-core.test.ts +642 -0
- package/src/tests/external-thread-attachments.test.tsx +173 -0
- package/src/tests/single-thread-list-item.test.tsx +47 -0
- package/src/tests/thread-message-like.test.ts +39 -1
- package/src/types/message.ts +8 -3
- package/src/utils/data-url.test.ts +52 -1
- package/src/utils/data-url.ts +21 -0
- package/src/utils/invoke-user-callback.test.ts +82 -0
- package/src/utils/invoke-user-callback.ts +31 -0
- package/src/utils/normalizePartStatus.ts +1 -1
- package/dist/store/scopes/queue-item.js.map +0 -1
|
@@ -16,9 +16,13 @@ import type {
|
|
|
16
16
|
import {
|
|
17
17
|
getExternalStoreMessages,
|
|
18
18
|
bindExternalStoreMessage,
|
|
19
|
+
FALLBACK_ID_PREFIX,
|
|
19
20
|
} from "../../runtime/utils/external-store-message";
|
|
20
21
|
import { ThreadMessageConverter } from "./thread-message-converter";
|
|
21
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
getContentAutoStatus,
|
|
24
|
+
isAutoStatus,
|
|
25
|
+
} from "../../runtime/utils/auto-status";
|
|
22
26
|
import {
|
|
23
27
|
fromThreadMessageLike,
|
|
24
28
|
type ThreadMessageLike,
|
|
@@ -39,8 +43,11 @@ import {
|
|
|
39
43
|
MessageRepository,
|
|
40
44
|
} from "../../runtime/utils/message-repository";
|
|
41
45
|
import { generateId } from "../../utils/id";
|
|
42
|
-
import {
|
|
43
|
-
|
|
46
|
+
import {
|
|
47
|
+
ToolInvocationTracker,
|
|
48
|
+
type ToolExecutionStatus,
|
|
49
|
+
} from "../tool-invocations/ToolInvocationTracker";
|
|
50
|
+
import { EMPTY_QUEUE_ITEMS } from "../../runtime/queue/queue-item";
|
|
44
51
|
import type { QuoteInfo } from "../../types/quote";
|
|
45
52
|
import {
|
|
46
53
|
captureThreadRuntimeGeneration,
|
|
@@ -110,6 +117,7 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
110
117
|
}
|
|
111
118
|
// Unlike `isLoading`: pass `undefined` through to preserve the `getThreadState` fallback.
|
|
112
119
|
public get isRunning(): boolean | undefined {
|
|
120
|
+
if (this._hasExecutingTools(this._store)) return true;
|
|
113
121
|
return this._store.isRunning;
|
|
114
122
|
}
|
|
115
123
|
|
|
@@ -136,6 +144,16 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
136
144
|
|
|
137
145
|
private _converter = new ThreadMessageConverter();
|
|
138
146
|
|
|
147
|
+
// Ids the host was asked to delete via onDelete. The snapshot pass evicts
|
|
148
|
+
// them from the repository once the host's array no longer carries them;
|
|
149
|
+
// an id the host kept is dropped from the set without eviction.
|
|
150
|
+
// Branch-changing mutations (edit, branch switch, reload) invalidate the
|
|
151
|
+
// set, because after them the incoming array omits off-branch ids for
|
|
152
|
+
// reasons unrelated to deletion. Plain tail sends do not clear: a tail
|
|
153
|
+
// append cannot make a visible id absent, so id-absence stays unambiguous
|
|
154
|
+
// and a delete whose confirmation races a send keeps its eviction.
|
|
155
|
+
private _pendingDeleteEvictions = new Set<string>();
|
|
156
|
+
|
|
139
157
|
private _store!: ExternalStoreAdapter<any>;
|
|
140
158
|
|
|
141
159
|
private _getInitializePromise?: () => Promise<unknown> | undefined;
|
|
@@ -153,6 +171,51 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
153
171
|
* snapshot — only when `adapter.unstable_enableToolInvocations === true`.
|
|
154
172
|
*/
|
|
155
173
|
private _toolInvocations: ToolInvocationTracker | null = null;
|
|
174
|
+
private _toolStatuses: ReadonlyMap<string, ToolExecutionStatus> = new Map();
|
|
175
|
+
private _effectiveIsRunning = false;
|
|
176
|
+
private _inTrackerUpdate = false;
|
|
177
|
+
private _pendingRunningRefresh = false;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Tracker mutations initiated by this class (setState, reset) can publish
|
|
181
|
+
* status changes synchronously. Re-entering the snapshot pipeline from
|
|
182
|
+
* inside them would feed the tracker a stale snapshot and consume the
|
|
183
|
+
* restore arming a reset just installed, so the running refresh is
|
|
184
|
+
* deferred until the mutation returns and stays off the tracker.
|
|
185
|
+
*/
|
|
186
|
+
private _runTrackerUpdate(fn: () => void): void {
|
|
187
|
+
this._inTrackerUpdate = true;
|
|
188
|
+
try {
|
|
189
|
+
fn();
|
|
190
|
+
} finally {
|
|
191
|
+
this._inTrackerUpdate = false;
|
|
192
|
+
}
|
|
193
|
+
if (this._pendingRunningRefresh) {
|
|
194
|
+
this._pendingRunningRefresh = false;
|
|
195
|
+
this._refreshEffectiveIsRunning();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private _refreshEffectiveIsRunning(): void {
|
|
200
|
+
const isRunning = this._getEffectiveIsRunning(this._store);
|
|
201
|
+
if (this._effectiveIsRunning === isRunning) return;
|
|
202
|
+
this._effectiveIsRunning = isRunning;
|
|
203
|
+
this._notifyEventSubscribers(isRunning ? "runStart" : "runEnd", {});
|
|
204
|
+
this._notifySubscribers();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private _hasExecutingTools(store: ExternalStoreAdapter<any>): boolean {
|
|
208
|
+
if (store.unstable_enableToolInvocations !== true) return false;
|
|
209
|
+
if (this._toolInvocations === null) return false;
|
|
210
|
+
for (const status of this._toolStatuses.values()) {
|
|
211
|
+
if (status.type === "executing") return true;
|
|
212
|
+
}
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private _getEffectiveIsRunning(store: ExternalStoreAdapter<any>): boolean {
|
|
217
|
+
return (store.isRunning ?? false) || this._hasExecutingTools(store);
|
|
218
|
+
}
|
|
156
219
|
|
|
157
220
|
public override beginEdit(messageId: string) {
|
|
158
221
|
if (!this._store.onEdit)
|
|
@@ -172,12 +235,17 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
172
235
|
public __internal_setAdapter(store: ExternalStoreAdapter<any>) {
|
|
173
236
|
if (this._store === store) return;
|
|
174
237
|
|
|
175
|
-
|
|
238
|
+
this._updateStoreSnapshot(store);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private _updateStoreSnapshot(store: ExternalStoreAdapter<any>) {
|
|
242
|
+
const previousIsRunning = this._effectiveIsRunning;
|
|
176
243
|
this.isDisabled = store.isDisabled ?? false;
|
|
177
244
|
this.isSendDisabled = store.isSendDisabled ?? false;
|
|
178
245
|
|
|
179
246
|
const oldStore = this._store as ExternalStoreAdapter<any> | undefined;
|
|
180
247
|
this._store = store;
|
|
248
|
+
const isRunning = this._getEffectiveIsRunning(store);
|
|
181
249
|
if (oldStore?.queue !== store.queue) {
|
|
182
250
|
this._transformedQueue = undefined;
|
|
183
251
|
store.queue?.__internal_setDispatchTransform?.((message) => {
|
|
@@ -229,7 +297,8 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
229
297
|
if (
|
|
230
298
|
oldStore &&
|
|
231
299
|
oldStore.isRunning === store.isRunning &&
|
|
232
|
-
oldStore.messageRepository === store.messageRepository
|
|
300
|
+
oldStore.messageRepository === store.messageRepository &&
|
|
301
|
+
previousIsRunning === isRunning
|
|
233
302
|
) {
|
|
234
303
|
this._notifySubscribers();
|
|
235
304
|
return;
|
|
@@ -252,6 +321,7 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
252
321
|
this.repository.deleteMessage(message.id);
|
|
253
322
|
}
|
|
254
323
|
}
|
|
324
|
+
this._pendingDeleteEvictions.clear();
|
|
255
325
|
this.repository.resetHead(headId);
|
|
256
326
|
messages = this.repository.getMessages();
|
|
257
327
|
}
|
|
@@ -264,7 +334,8 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
264
334
|
this._converter = new ThreadMessageConverter();
|
|
265
335
|
} else if (
|
|
266
336
|
oldStore.isRunning === store.isRunning &&
|
|
267
|
-
oldStore.messages === store.messages
|
|
337
|
+
oldStore.messages === store.messages &&
|
|
338
|
+
previousIsRunning === isRunning
|
|
268
339
|
) {
|
|
269
340
|
this._notifySubscribers();
|
|
270
341
|
// no conversion update
|
|
@@ -278,27 +349,31 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
278
349
|
if (!store.convertMessage) return m;
|
|
279
350
|
|
|
280
351
|
const isLast = idx === (store.messages?.length ?? 0) - 1;
|
|
281
|
-
const
|
|
282
|
-
isLast,
|
|
283
|
-
isRunning,
|
|
284
|
-
false,
|
|
285
|
-
false,
|
|
286
|
-
undefined,
|
|
287
|
-
);
|
|
352
|
+
const fallbackId = `${FALLBACK_ID_PREFIX}${idx}`;
|
|
288
353
|
|
|
289
354
|
if (
|
|
290
355
|
cache &&
|
|
291
356
|
(cache.role !== "assistant" ||
|
|
292
357
|
!isAutoStatus(cache.status) ||
|
|
293
|
-
cache.status ===
|
|
294
|
-
|
|
358
|
+
cache.status ===
|
|
359
|
+
getContentAutoStatus(cache.content, isLast, isRunning))
|
|
360
|
+
) {
|
|
361
|
+
if (
|
|
362
|
+
cache.id.startsWith(FALLBACK_ID_PREFIX) &&
|
|
363
|
+
cache.id !== fallbackId
|
|
364
|
+
) {
|
|
365
|
+
const updated = { ...cache, id: fallbackId };
|
|
366
|
+
bindExternalStoreMessage(updated, m);
|
|
367
|
+
return updated;
|
|
368
|
+
}
|
|
295
369
|
return cache;
|
|
370
|
+
}
|
|
296
371
|
|
|
297
372
|
const messageLike = store.convertMessage(m, idx);
|
|
298
373
|
const newMessage = fromThreadMessageLike(
|
|
299
374
|
messageLike,
|
|
300
|
-
|
|
301
|
-
|
|
375
|
+
fallbackId,
|
|
376
|
+
getContentAutoStatus(messageLike.content, isLast, isRunning),
|
|
302
377
|
);
|
|
303
378
|
bindExternalStoreMessage(newMessage, m);
|
|
304
379
|
return newMessage;
|
|
@@ -324,6 +399,20 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
324
399
|
const parent = messages[i - 1];
|
|
325
400
|
this.repository.addOrUpdateMessage(parent?.id ?? null, message);
|
|
326
401
|
}
|
|
402
|
+
|
|
403
|
+
if (this._pendingDeleteEvictions.size > 0) {
|
|
404
|
+
const incomingIds = new Set(messages.map((m) => m.id));
|
|
405
|
+
for (const id of this._pendingDeleteEvictions) {
|
|
406
|
+
this._pendingDeleteEvictions.delete(id);
|
|
407
|
+
if (incomingIds.has(id)) continue;
|
|
408
|
+
try {
|
|
409
|
+
this.repository.getMessage(id);
|
|
410
|
+
} catch {
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
this.repository.deleteMessage(id);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
327
416
|
} else {
|
|
328
417
|
throw new Error(
|
|
329
418
|
"ExternalStoreAdapter must provide either 'messages' or 'messageRepository'",
|
|
@@ -333,8 +422,9 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
333
422
|
// Common logic for both paths
|
|
334
423
|
if (messages.length > 0) this.ensureInitialized();
|
|
335
424
|
|
|
336
|
-
|
|
337
|
-
|
|
425
|
+
this._effectiveIsRunning = isRunning;
|
|
426
|
+
if (previousIsRunning !== isRunning) {
|
|
427
|
+
if (isRunning) {
|
|
338
428
|
this._notifyEventSubscribers("runStart", {});
|
|
339
429
|
} else {
|
|
340
430
|
this._notifyEventSubscribers("runEnd", {});
|
|
@@ -361,7 +451,7 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
361
451
|
|
|
362
452
|
this._messages = this.repository.getMessages();
|
|
363
453
|
|
|
364
|
-
this._driveToolInvocations();
|
|
454
|
+
this._runTrackerUpdate(() => this._driveToolInvocations());
|
|
365
455
|
|
|
366
456
|
this._notifySubscribers();
|
|
367
457
|
}
|
|
@@ -380,6 +470,7 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
380
470
|
if (this._toolInvocations) {
|
|
381
471
|
this._toolInvocations.reset();
|
|
382
472
|
this._toolInvocations = null;
|
|
473
|
+
this._toolStatuses = new Map();
|
|
383
474
|
this._store.setToolStatuses?.({});
|
|
384
475
|
}
|
|
385
476
|
return;
|
|
@@ -423,7 +514,19 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
423
514
|
}
|
|
424
515
|
},
|
|
425
516
|
onStatusesChange: (statuses) => {
|
|
426
|
-
this.
|
|
517
|
+
const hadExecutingTools = this._hasExecutingTools(this._store);
|
|
518
|
+
this._toolStatuses = statuses;
|
|
519
|
+
try {
|
|
520
|
+
this._store.setToolStatuses?.(Object.fromEntries(statuses));
|
|
521
|
+
} finally {
|
|
522
|
+
if (hadExecutingTools !== this._hasExecutingTools(this._store)) {
|
|
523
|
+
if (this._inTrackerUpdate) {
|
|
524
|
+
this._pendingRunningRefresh = true;
|
|
525
|
+
} else {
|
|
526
|
+
this._updateStoreSnapshot(this._store);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
427
530
|
},
|
|
428
531
|
},
|
|
429
532
|
);
|
|
@@ -431,7 +534,7 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
431
534
|
|
|
432
535
|
this._toolInvocations.setState({
|
|
433
536
|
messages: this._messages,
|
|
434
|
-
isRunning: this._store
|
|
537
|
+
isRunning: this._getEffectiveIsRunning(this._store),
|
|
435
538
|
...(this._store.isLoading !== undefined && {
|
|
436
539
|
isLoading: this._store.isLoading,
|
|
437
540
|
}),
|
|
@@ -475,7 +578,7 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
475
578
|
throw new Error("Runtime does not support switching branches.");
|
|
476
579
|
|
|
477
580
|
// Silently ignore branch switches while running
|
|
478
|
-
if (this._store
|
|
581
|
+
if (this._getEffectiveIsRunning(this._store)) {
|
|
479
582
|
return;
|
|
480
583
|
}
|
|
481
584
|
|
|
@@ -485,6 +588,7 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
485
588
|
: null;
|
|
486
589
|
|
|
487
590
|
this.repository.switchToBranch(branchId);
|
|
591
|
+
this._pendingDeleteEvictions.clear();
|
|
488
592
|
this.updateMessages(this.repository.getMessages());
|
|
489
593
|
if (onBranchChange) {
|
|
490
594
|
this._notifyBranchChange(previousHeadId, onBranchChange);
|
|
@@ -547,7 +651,7 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
547
651
|
// Buffering does not start a run, so the tool-abort below must wait
|
|
548
652
|
// until the queue flushes. By then the prior run (and its tools) has
|
|
549
653
|
// settled.
|
|
550
|
-
if (message.steer ?? this._store
|
|
654
|
+
if (message.steer ?? this._getEffectiveIsRunning(this._store))
|
|
551
655
|
this._store.queue.steer(message);
|
|
552
656
|
else this._store.queue.enqueue(message);
|
|
553
657
|
return;
|
|
@@ -574,6 +678,7 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
574
678
|
if (isEdit) {
|
|
575
679
|
if (!this._store.onEdit)
|
|
576
680
|
throw new Error("Runtime does not support editing messages.");
|
|
681
|
+
this._pendingDeleteEvictions.clear();
|
|
577
682
|
await this._store.onEdit(message);
|
|
578
683
|
} else {
|
|
579
684
|
await this._store.onNew(message);
|
|
@@ -582,14 +687,29 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
582
687
|
|
|
583
688
|
public async deleteMessage(messageId: string): Promise<void> {
|
|
584
689
|
if (this._store.onDelete) {
|
|
585
|
-
|
|
690
|
+
// The host owns deletion here, and it may decline (fail a server call,
|
|
691
|
+
// cancel a confirm dialog, ignore an off-branch id). The eviction is
|
|
692
|
+
// therefore deferred to the snapshot pass, which evicts only once the
|
|
693
|
+
// host's own array no longer carries the id. Registered before the
|
|
694
|
+
// callback because an optimistic host publishes that snapshot while
|
|
695
|
+
// the callback is still awaited.
|
|
696
|
+
const wasVisible = this.repository
|
|
697
|
+
.getMessages()
|
|
698
|
+
.some((m) => m.id === messageId);
|
|
699
|
+
if (wasVisible) this._pendingDeleteEvictions.add(messageId);
|
|
700
|
+
try {
|
|
701
|
+
await this._store.onDelete(messageId);
|
|
702
|
+
} catch (error) {
|
|
703
|
+
this._pendingDeleteEvictions.delete(messageId);
|
|
704
|
+
throw error;
|
|
705
|
+
}
|
|
586
706
|
return;
|
|
587
707
|
}
|
|
588
708
|
|
|
589
709
|
if (!this._store.setMessages)
|
|
590
710
|
throw new Error("Runtime does not support deleting messages.");
|
|
591
711
|
|
|
592
|
-
if (this._store
|
|
712
|
+
if (this._getEffectiveIsRunning(this._store)) {
|
|
593
713
|
await this._toolInvocations?.abort();
|
|
594
714
|
}
|
|
595
715
|
|
|
@@ -597,7 +717,32 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
597
717
|
const messageIndex = messages.findIndex((m) => m.id === messageId);
|
|
598
718
|
if (messageIndex === -1) throw new Error("Message not found.");
|
|
599
719
|
|
|
720
|
+
this._pendingDeleteEvictions.clear();
|
|
600
721
|
this.updateMessages(messages.filter((message) => message.id !== messageId));
|
|
722
|
+
this._evictDeletedMessage(messageId);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// The snapshot pass only relinks incoming messages; it never evicts, so
|
|
726
|
+
// without this the deleted message survives as a sibling branch that the
|
|
727
|
+
// branch picker can resurrect into the host store. `_messages` is refreshed
|
|
728
|
+
// before notifying so `messages` and the branch graph agree at notify time,
|
|
729
|
+
// mirroring the end of the snapshot pass.
|
|
730
|
+
private _evictDeletedMessage(messageId: string) {
|
|
731
|
+
// Positional fallback ids are remapped in the snapshot pass; evicting
|
|
732
|
+
// them first leaves the pre-renumber node as a sibling of the new head.
|
|
733
|
+
if (messageId.startsWith(FALLBACK_ID_PREFIX)) return;
|
|
734
|
+
// A synchronous host update (e.g. a setMessages that re-entered the
|
|
735
|
+
// snapshot pass) may have evicted the message already; only that case is
|
|
736
|
+
// skipped, so genuine repository errors still propagate.
|
|
737
|
+
try {
|
|
738
|
+
this.repository.getMessage(messageId);
|
|
739
|
+
} catch {
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
this.repository.deleteMessage(messageId);
|
|
744
|
+
this._messages = this.repository.getMessages();
|
|
745
|
+
this._notifySubscribers();
|
|
601
746
|
}
|
|
602
747
|
|
|
603
748
|
public getQueueItems() {
|
|
@@ -622,6 +767,8 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
622
767
|
if (!this._store.onReload)
|
|
623
768
|
throw new Error("Runtime does not support reloading messages.");
|
|
624
769
|
|
|
770
|
+
this._pendingDeleteEvictions.clear();
|
|
771
|
+
|
|
625
772
|
// Auto-abort in-flight client-side tool executions when a run reloads;
|
|
626
773
|
// any results that land afterward would target a turn that no longer
|
|
627
774
|
// exists. See `append` above for full rationale.
|
|
@@ -655,7 +802,7 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
655
802
|
// back here via __internal_setAdapter. The tracker publishes the
|
|
656
803
|
// cleared status map itself, so adapter-side statuses reset only when
|
|
657
804
|
// the tracker is the source of truth.
|
|
658
|
-
this._toolInvocations?.reset();
|
|
805
|
+
this._runTrackerUpdate(() => this._toolInvocations?.reset());
|
|
659
806
|
|
|
660
807
|
this._store.onLoadExternalState(state);
|
|
661
808
|
}
|
|
@@ -666,7 +813,7 @@ export class ExternalStoreThreadRuntimeCore
|
|
|
666
813
|
* without run-cancel semantics (`onCancel`, composer draft restoration).
|
|
667
814
|
*/
|
|
668
815
|
public unstable_notifySessionReset(): void {
|
|
669
|
-
this._toolInvocations?.reset();
|
|
816
|
+
this._runTrackerUpdate(() => this._toolInvocations?.reset());
|
|
670
817
|
this._store.queue?.__internal_notifyCancelled?.();
|
|
671
818
|
}
|
|
672
819
|
|
|
@@ -7,7 +7,11 @@ import type {
|
|
|
7
7
|
} from "../../runtime/utils/chat-model-adapter";
|
|
8
8
|
import type { AppendMessage } from "../../types/message";
|
|
9
9
|
import type { LocalRuntimeOptionsBase } from "./local-runtime-options";
|
|
10
|
-
import
|
|
10
|
+
import {
|
|
11
|
+
ExportedMessageRepository,
|
|
12
|
+
type ExportedMessageRepositoryItem,
|
|
13
|
+
} from "../../runtime/utils/message-repository";
|
|
14
|
+
import type { ThreadMessageLike } from "../../runtime/utils/thread-message-like";
|
|
11
15
|
import type { ThreadSuggestion } from "../../runtime/interfaces/thread-runtime-core";
|
|
12
16
|
import { isMessageNotSentError } from "../../types/error";
|
|
13
17
|
|
|
@@ -2002,3 +2006,170 @@ describe("LocalRuntimeCore composer.canCancel", () => {
|
|
|
2002
2006
|
thread.cancelRun();
|
|
2003
2007
|
});
|
|
2004
2008
|
});
|
|
2009
|
+
|
|
2010
|
+
describe("LocalThreadRuntimeCore imported approvals", () => {
|
|
2011
|
+
const pausedOnApproval = (
|
|
2012
|
+
approval: { id: string } | undefined,
|
|
2013
|
+
extra: Record<string, unknown> = {},
|
|
2014
|
+
): ThreadMessageLike[] => [
|
|
2015
|
+
{ role: "user", content: [{ type: "text", text: "send an email" }] },
|
|
2016
|
+
{
|
|
2017
|
+
role: "assistant",
|
|
2018
|
+
content: [
|
|
2019
|
+
{
|
|
2020
|
+
type: "tool-call",
|
|
2021
|
+
toolCallId: "call-send_email",
|
|
2022
|
+
toolName: "send_email",
|
|
2023
|
+
args: {},
|
|
2024
|
+
argsText: "{}",
|
|
2025
|
+
...(approval !== undefined ? { approval } : {}),
|
|
2026
|
+
...extra,
|
|
2027
|
+
},
|
|
2028
|
+
],
|
|
2029
|
+
},
|
|
2030
|
+
];
|
|
2031
|
+
|
|
2032
|
+
const createImportedThread = (
|
|
2033
|
+
initialMessages: ThreadMessageLike[],
|
|
2034
|
+
history?: LocalRuntimeOptionsBase["adapters"]["history"],
|
|
2035
|
+
) => {
|
|
2036
|
+
const runs: ChatModelRunOptions[] = [];
|
|
2037
|
+
const core = new LocalRuntimeCore(
|
|
2038
|
+
{
|
|
2039
|
+
adapters: {
|
|
2040
|
+
chatModel: {
|
|
2041
|
+
async run(options) {
|
|
2042
|
+
runs.push(options);
|
|
2043
|
+
return { content: [{ type: "text", text: "done" }] };
|
|
2044
|
+
},
|
|
2045
|
+
},
|
|
2046
|
+
...(history !== undefined && { history }),
|
|
2047
|
+
},
|
|
2048
|
+
unstable_humanToolNames: ["send_email"],
|
|
2049
|
+
},
|
|
2050
|
+
initialMessages,
|
|
2051
|
+
);
|
|
2052
|
+
return { thread: core.threads.getMainThreadRuntimeCore(), runs };
|
|
2053
|
+
};
|
|
2054
|
+
|
|
2055
|
+
it("resumes the run after approving an imported pending approval", async () => {
|
|
2056
|
+
const { thread, runs } = createImportedThread(
|
|
2057
|
+
pausedOnApproval({ id: "a1" }),
|
|
2058
|
+
);
|
|
2059
|
+
|
|
2060
|
+
expect(thread.messages.at(-1)?.status).toMatchObject({
|
|
2061
|
+
type: "requires-action",
|
|
2062
|
+
reason: "tool-calls",
|
|
2063
|
+
});
|
|
2064
|
+
|
|
2065
|
+
thread.respondToToolApproval({ approvalId: "a1", approved: true });
|
|
2066
|
+
await flush();
|
|
2067
|
+
|
|
2068
|
+
expect(runs).toHaveLength(1);
|
|
2069
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("complete");
|
|
2070
|
+
});
|
|
2071
|
+
|
|
2072
|
+
it("keeps the interrupt reason for an imported interrupted tool call", () => {
|
|
2073
|
+
const { thread } = createImportedThread(
|
|
2074
|
+
pausedOnApproval(undefined, {
|
|
2075
|
+
interrupt: { type: "human", payload: { question: "which one?" } },
|
|
2076
|
+
}),
|
|
2077
|
+
);
|
|
2078
|
+
|
|
2079
|
+
expect(thread.messages.at(-1)?.status).toMatchObject({
|
|
2080
|
+
type: "requires-action",
|
|
2081
|
+
reason: "interrupt",
|
|
2082
|
+
});
|
|
2083
|
+
});
|
|
2084
|
+
|
|
2085
|
+
it("resumes the run after approving an approval restored by the history adapter", async () => {
|
|
2086
|
+
const { thread, runs } = createImportedThread([], {
|
|
2087
|
+
async load() {
|
|
2088
|
+
return ExportedMessageRepository.fromArray(
|
|
2089
|
+
pausedOnApproval({ id: "a1" }),
|
|
2090
|
+
);
|
|
2091
|
+
},
|
|
2092
|
+
async append() {},
|
|
2093
|
+
async update() {},
|
|
2094
|
+
});
|
|
2095
|
+
|
|
2096
|
+
thread.__internal_load();
|
|
2097
|
+
await flush();
|
|
2098
|
+
|
|
2099
|
+
expect(thread.messages.at(-1)?.status).toMatchObject({
|
|
2100
|
+
type: "requires-action",
|
|
2101
|
+
reason: "tool-calls",
|
|
2102
|
+
});
|
|
2103
|
+
|
|
2104
|
+
thread.respondToToolApproval({ approvalId: "a1", approved: true });
|
|
2105
|
+
await flush();
|
|
2106
|
+
|
|
2107
|
+
expect(runs).toHaveLength(1);
|
|
2108
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("complete");
|
|
2109
|
+
});
|
|
2110
|
+
|
|
2111
|
+
it("normalizes a serialized approval pause that lost its auto-status marker", async () => {
|
|
2112
|
+
const { thread, runs } = createImportedThread([]);
|
|
2113
|
+
thread.import(
|
|
2114
|
+
JSON.parse(
|
|
2115
|
+
JSON.stringify(
|
|
2116
|
+
ExportedMessageRepository.fromArray(pausedOnApproval({ id: "a1" })),
|
|
2117
|
+
),
|
|
2118
|
+
),
|
|
2119
|
+
);
|
|
2120
|
+
|
|
2121
|
+
expect(thread.messages.at(-1)?.status).toMatchObject({
|
|
2122
|
+
type: "requires-action",
|
|
2123
|
+
reason: "tool-calls",
|
|
2124
|
+
});
|
|
2125
|
+
|
|
2126
|
+
thread.respondToToolApproval({ approvalId: "a1", approved: true });
|
|
2127
|
+
await flush();
|
|
2128
|
+
|
|
2129
|
+
expect(runs).toHaveLength(1);
|
|
2130
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("complete");
|
|
2131
|
+
});
|
|
2132
|
+
|
|
2133
|
+
it("completes an imported interrupt that has nothing left to act on", () => {
|
|
2134
|
+
const { thread } = createImportedThread([
|
|
2135
|
+
{ role: "user", content: [{ type: "text", text: "send an email" }] },
|
|
2136
|
+
{
|
|
2137
|
+
role: "assistant",
|
|
2138
|
+
status: { type: "requires-action", reason: "interrupt" },
|
|
2139
|
+
content: [
|
|
2140
|
+
{
|
|
2141
|
+
type: "tool-call",
|
|
2142
|
+
toolCallId: "call-send_email",
|
|
2143
|
+
toolName: "send_email",
|
|
2144
|
+
args: {},
|
|
2145
|
+
argsText: "{}",
|
|
2146
|
+
result: { sent: true },
|
|
2147
|
+
},
|
|
2148
|
+
],
|
|
2149
|
+
} as ThreadMessageLike,
|
|
2150
|
+
]);
|
|
2151
|
+
|
|
2152
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("complete");
|
|
2153
|
+
});
|
|
2154
|
+
|
|
2155
|
+
it("resumes the run after adding a result to an imported resultless tool call", async () => {
|
|
2156
|
+
const { thread, runs } = createImportedThread(pausedOnApproval(undefined));
|
|
2157
|
+
|
|
2158
|
+
expect(thread.messages.at(-1)?.status).toMatchObject({
|
|
2159
|
+
type: "requires-action",
|
|
2160
|
+
reason: "tool-calls",
|
|
2161
|
+
});
|
|
2162
|
+
|
|
2163
|
+
thread.addToolResult({
|
|
2164
|
+
messageId: thread.messages.at(-1)!.id,
|
|
2165
|
+
toolCallId: "call-send_email",
|
|
2166
|
+
toolName: "send_email",
|
|
2167
|
+
result: { sent: true },
|
|
2168
|
+
isError: false,
|
|
2169
|
+
});
|
|
2170
|
+
await flush();
|
|
2171
|
+
|
|
2172
|
+
expect(runs).toHaveLength(1);
|
|
2173
|
+
expect(thread.messages.at(-1)?.status?.type).toBe("complete");
|
|
2174
|
+
});
|
|
2175
|
+
});
|
|
@@ -5,6 +5,8 @@ import type {
|
|
|
5
5
|
ChatModelRunResult,
|
|
6
6
|
} from "../../runtime/utils/chat-model-adapter";
|
|
7
7
|
import { shouldContinue } from "./should-continue";
|
|
8
|
+
import { getAutoStatus } from "../../runtime/utils/auto-status";
|
|
9
|
+
import type { ExportedMessageRepository } from "../../runtime/utils/message-repository";
|
|
8
10
|
import type { LocalRuntimeOptionsBase } from "./local-runtime-options";
|
|
9
11
|
import { consumeSuggestionResult } from "../../adapters/suggestion";
|
|
10
12
|
import type {
|
|
@@ -21,7 +23,7 @@ import type {
|
|
|
21
23
|
AppendMessage,
|
|
22
24
|
ThreadAssistantMessage,
|
|
23
25
|
} from "../../types/message";
|
|
24
|
-
import type { RunConfig } from "../../types/message";
|
|
26
|
+
import type { RunConfig, ThreadMessage } from "../../types/message";
|
|
25
27
|
import { MessageNotSentError, toAssistantError } from "../../types/error";
|
|
26
28
|
import type { ModelContextProvider } from "../../model-context/types";
|
|
27
29
|
import {
|
|
@@ -32,7 +34,7 @@ import type { QueuePlacement } from "../../runtime/queue/external-thread-queue-a
|
|
|
32
34
|
import {
|
|
33
35
|
EMPTY_QUEUE_ITEMS,
|
|
34
36
|
type QueueItemState,
|
|
35
|
-
} from "../../
|
|
37
|
+
} from "../../runtime/queue/queue-item";
|
|
36
38
|
import {
|
|
37
39
|
captureThreadRuntimeGeneration,
|
|
38
40
|
invalidateThreadRuntime,
|
|
@@ -49,6 +51,45 @@ class AbortError extends Error {
|
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
// `shouldContinue` resumes only on `tool-calls` and `resumeToolCall` throws, so
|
|
55
|
+
// an imported `interrupt` with no interrupt payload would be stranded here.
|
|
56
|
+
// Provenance cannot gate it: a repository that went through JSON has no marker.
|
|
57
|
+
// The replacement stays content-derived, so a message with nothing resultless
|
|
58
|
+
// left to act on lands on `complete` rather than an unactionable pause.
|
|
59
|
+
const withLocalPauseReason = (message: ThreadMessage): ThreadMessage => {
|
|
60
|
+
if (
|
|
61
|
+
message.role !== "assistant" ||
|
|
62
|
+
message.status.type !== "requires-action" ||
|
|
63
|
+
message.status.reason !== "interrupt" ||
|
|
64
|
+
message.content.some(
|
|
65
|
+
(c) =>
|
|
66
|
+
c.type === "tool-call" && c.result === undefined && c.interrupt != null,
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
return message;
|
|
70
|
+
return {
|
|
71
|
+
...message,
|
|
72
|
+
status: getAutoStatus(
|
|
73
|
+
false,
|
|
74
|
+
false,
|
|
75
|
+
false,
|
|
76
|
+
message.content.some(
|
|
77
|
+
(c) => c.type === "tool-call" && c.result === undefined,
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const withLocalPauseReasons = (
|
|
84
|
+
data: ExportedMessageRepository,
|
|
85
|
+
): ExportedMessageRepository => ({
|
|
86
|
+
...data,
|
|
87
|
+
messages: data.messages.map((item) => ({
|
|
88
|
+
...item,
|
|
89
|
+
message: withLocalPauseReason(item.message),
|
|
90
|
+
})),
|
|
91
|
+
});
|
|
92
|
+
|
|
52
93
|
export class LocalThreadRuntimeCore
|
|
53
94
|
extends BaseThreadRuntimeCore
|
|
54
95
|
implements ThreadRuntimeCore
|
|
@@ -272,7 +313,7 @@ export class LocalThreadRuntimeCore
|
|
|
272
313
|
this._loadPromise = promise
|
|
273
314
|
.then((repo) => {
|
|
274
315
|
if (!repo) return;
|
|
275
|
-
this.repository.import(repo);
|
|
316
|
+
this.repository.import(withLocalPauseReasons(repo));
|
|
276
317
|
if (repo.messages.length > 0) {
|
|
277
318
|
this.ensureInitialized();
|
|
278
319
|
}
|
|
@@ -434,6 +475,10 @@ export class LocalThreadRuntimeCore
|
|
|
434
475
|
throw new Error("Runtime does not support exporting external states.");
|
|
435
476
|
}
|
|
436
477
|
|
|
478
|
+
public override import(data: ExportedMessageRepository) {
|
|
479
|
+
super.import(withLocalPauseReasons(data));
|
|
480
|
+
}
|
|
481
|
+
|
|
437
482
|
public importExternalState(): void {
|
|
438
483
|
throw new Error("Runtime does not support importing external states.");
|
|
439
484
|
}
|