@assistant-ui/core 0.3.17 → 0.3.18
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/adapters/attachment.d.ts.map +1 -1
- package/dist/adapters/attachment.js +5 -4
- package/dist/adapters/attachment.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +3 -2
- package/dist/internal.js +3 -2
- package/dist/model-context/frame/host.d.ts.map +1 -1
- package/dist/model-context/frame/host.js +10 -4
- 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 +30 -7
- package/dist/model-context/frame/provider.js.map +1 -1
- package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
- package/dist/model-context/interactable-composer-metadata.js +16 -9
- package/dist/model-context/interactable-composer-metadata.js.map +1 -1
- package/dist/model-context/registry.d.ts.map +1 -1
- package/dist/model-context/registry.js +3 -5
- package/dist/model-context/registry.js.map +1 -1
- package/dist/model-context/types.d.ts.map +1 -1
- package/dist/model-context/types.js +5 -4
- package/dist/model-context/types.js.map +1 -1
- package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
- package/dist/react/client/InMemoryThreadList.js +62 -56
- 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 +24 -27
- package/dist/react/client/Interactables.js.map +1 -1
- package/dist/react/client/RemoteThreadList.d.ts +1 -0
- package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
- package/dist/react/client/RemoteThreadList.js +151 -141
- package/dist/react/client/RemoteThreadList.js.map +1 -1
- package/dist/react/client/Tools.d.ts.map +1 -1
- package/dist/react/client/Tools.js +18 -16
- package/dist/react/client/Tools.js.map +1 -1
- package/dist/react/interactables-legacy/Interactables.js +242 -121
- package/dist/react/interactables-legacy/Interactables.js.map +1 -1
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -1
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +11 -9
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -1
- package/dist/react/model-context/makeAssistantTool.d.ts +2 -2
- package/dist/react/model-context/makeAssistantTool.js +1 -1
- package/dist/react/model-context/makeAssistantTool.js.map +1 -1
- package/dist/react/model-context/useAssistantTool.d.ts +2 -2
- package/dist/react/model-context/useAssistantTool.js +1 -1
- package/dist/react/model-context/useAssistantTool.js.map +1 -1
- package/dist/react/model-context/useToolArgsStatus.d.ts.map +1 -1
- package/dist/react/model-context/useToolArgsStatus.js +2 -1
- package/dist/react/model-context/useToolArgsStatus.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +16 -8
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +5 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +103 -103
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -0
- package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.js +13 -11
- package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +4 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +401 -34
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +2 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -1
- package/dist/react/runtimes/useExternalStoreRuntime.js +75 -46
- package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.js +63 -23
- package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.d.ts +7 -3
- package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.js +2 -2
- package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
- package/dist/runtime/api/thread-list-runtime.d.ts +3 -0
- package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-runtime.js +18 -14
- package/dist/runtime/api/thread-list-runtime.js.map +1 -1
- package/dist/runtime/api/thread-runtime.d.ts +4 -0
- package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-runtime.js +1 -1
- package/dist/runtime/api/thread-runtime.js.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +16 -12
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.js +6 -5
- package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
- package/dist/runtime/interfaces/composer-runtime-core.d.ts +12 -5
- package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +4 -1
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtime/utils/external-message-conversion.js +1 -1
- package/dist/runtime/utils/external-message-conversion.js.map +1 -1
- package/dist/runtime/utils/message-repository.d.ts +1 -0
- package/dist/runtime/utils/message-repository.d.ts.map +1 -1
- package/dist/runtime/utils/message-repository.js +15 -6
- package/dist/runtime/utils/message-repository.js.map +1 -1
- package/dist/runtime/utils/thread-message-like.js +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.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +21 -27
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
- package/dist/runtimes/internal.d.ts +2 -2
- package/dist/runtimes/internal.js +2 -2
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +18 -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 +130 -42
- package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
- package/dist/runtimes/remote-thread-list/title-generation.d.ts +49 -0
- package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -0
- package/dist/runtimes/remote-thread-list/title-generation.js +197 -0
- package/dist/runtimes/remote-thread-list/title-generation.js.map +1 -0
- package/dist/runtimes/remote-thread-list/title.d.ts +3 -1
- package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/title.js +9 -1
- package/dist/runtimes/remote-thread-list/title.js.map +1 -1
- package/dist/runtimes/remote-thread-list/types.d.ts +10 -0
- package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
- package/dist/store/clients/external-thread.d.ts.map +1 -1
- package/dist/store/clients/external-thread.js +19 -3
- package/dist/store/clients/external-thread.js.map +1 -1
- package/dist/store/clients/model-context-client.d.ts.map +1 -1
- package/dist/store/clients/model-context-client.js +2 -1
- package/dist/store/clients/model-context-client.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 -0
- 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 +5 -4
- package/dist/store/clients/suggestions.js.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.d.ts +1 -0
- package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.js +97 -61
- package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/message-runtime-client.d.ts +1 -0
- package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/message-runtime-client.js +221 -144
- package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-list-runtime-client.js +54 -52
- package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.js +193 -121
- package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
- package/dist/store/scopes/composer.d.ts +10 -2
- package/dist/store/scopes/composer.d.ts.map +1 -1
- package/dist/store/scopes/message.d.ts +30 -1
- package/dist/store/scopes/message.d.ts.map +1 -1
- package/dist/store/scopes/thread-list-item.d.ts +2 -2
- package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
- package/dist/store/scopes/thread.d.ts +8 -0
- package/dist/store/scopes/thread.d.ts.map +1 -1
- package/dist/store/scopes/threads.d.ts +1 -0
- package/dist/store/scopes/threads.d.ts.map +1 -1
- package/dist/types/attachment.d.ts +2 -1
- package/dist/types/attachment.d.ts.map +1 -1
- package/dist/types/attachment.js +2 -1
- package/dist/types/attachment.js.map +1 -1
- package/dist/utils/abortable-iterable.d.ts +19 -0
- package/dist/utils/abortable-iterable.d.ts.map +1 -0
- package/dist/utils/abortable-iterable.js +72 -0
- package/dist/utils/abortable-iterable.js.map +1 -0
- package/dist/utils/composite-context-provider.d.ts.map +1 -1
- package/dist/utils/composite-context-provider.js +20 -2
- package/dist/utils/composite-context-provider.js.map +1 -1
- package/dist/utils/record.d.ts +11 -0
- package/dist/utils/record.d.ts.map +1 -0
- package/dist/utils/record.js +12 -0
- package/dist/utils/record.js.map +1 -0
- package/package.json +10 -10
- package/src/adapters/attachment.test.ts +50 -1
- package/src/adapters/attachment.ts +7 -4
- package/src/index.ts +1 -0
- package/src/internal.ts +4 -0
- package/src/model-context/frame/host.test.ts +24 -0
- package/src/model-context/frame/host.ts +10 -4
- package/src/model-context/frame/provider.test.ts +71 -0
- package/src/model-context/frame/provider.ts +37 -7
- package/src/model-context/interactable-composer-metadata.test.ts +28 -0
- package/src/model-context/interactable-composer-metadata.ts +9 -3
- package/src/model-context/registry.test.ts +12 -0
- package/src/model-context/registry.ts +3 -2
- package/src/model-context/types.test.ts +25 -0
- package/src/model-context/types.ts +9 -4
- package/src/react/RuntimeAdapter.test.ts +34 -0
- package/src/react/client/InMemoryThreadList.ts +1 -0
- package/src/react/client/Interactables.test.ts +28 -0
- package/src/react/client/Interactables.ts +15 -13
- package/src/react/client/RemoteThreadList.background.test.ts +148 -6
- package/src/react/client/RemoteThreadList.concurrent.test.tsx +42 -1
- package/src/react/client/RemoteThreadList.test.ts +345 -3
- package/src/react/client/RemoteThreadList.ts +166 -110
- package/src/react/client/Tools.ts +22 -11
- package/src/react/interactables-legacy/Interactables.ts +16 -16
- package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +9 -10
- package/src/react/model-context/makeAssistantTool.ts +2 -2
- package/src/react/model-context/useAssistantTool.ts +2 -2
- package/src/react/model-context/useToolArgsStatus.test.tsx +30 -0
- package/src/react/model-context/useToolArgsStatus.ts +2 -1
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.destroy.test.tsx +172 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +33 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +7 -1
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +28 -5
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.load-error.test.ts +132 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +105 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +136 -111
- package/src/react/runtimes/RemoteThreadResource.test.ts +9 -0
- package/src/react/runtimes/RemoteThreadResource.ts +19 -12
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +483 -6
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +504 -68
- package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +15 -0
- package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +1 -0
- package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +41 -0
- package/src/react/runtimes/useExternalStoreRuntime.ts +10 -4
- package/src/react/runtimes/useLocalRuntime.test.tsx +20 -0
- package/src/react/runtimes/useRemoteThreadListRuntime.load-error.test.tsx +108 -0
- package/src/react/runtimes/useRemoteThreadListRuntime.ts +33 -0
- package/src/runtime/api/message-runtime.test.ts +75 -0
- package/src/runtime/api/thread-list-item-runtime.ts +10 -3
- package/src/runtime/api/thread-list-runtime.ts +21 -11
- package/src/runtime/api/thread-runtime.test.ts +78 -0
- package/src/runtime/api/thread-runtime.ts +8 -1
- package/src/runtime/base/base-composer-runtime-core.ts +36 -8
- package/src/runtime/base/base-thread-runtime-core.test.ts +55 -1
- package/src/runtime/base/base-thread-runtime-core.ts +6 -4
- package/src/runtime/interfaces/composer-runtime-core.ts +10 -5
- package/src/runtime/interfaces/thread-list-runtime-core.ts +5 -1
- package/src/runtime/utils/external-message-conversion.test.ts +68 -0
- package/src/runtime/utils/external-message-conversion.ts +1 -1
- package/src/runtime/utils/message-repository.ts +29 -17
- package/src/runtime/utils/thread-message-like.ts +1 -1
- package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +18 -13
- package/src/runtimes/internal.ts +1 -0
- package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +128 -2
- package/src/runtimes/remote-thread-list/remote-thread-state.ts +182 -42
- package/src/runtimes/remote-thread-list/title-generation.test.ts +602 -0
- package/src/runtimes/remote-thread-list/title-generation.ts +329 -0
- package/src/runtimes/remote-thread-list/title.test.ts +62 -0
- package/src/runtimes/remote-thread-list/title.ts +13 -0
- package/src/runtimes/remote-thread-list/types.ts +10 -0
- package/src/store/clients/external-thread.ts +48 -3
- package/src/store/clients/model-context-client.ts +5 -1
- package/src/store/clients/single-thread-list.ts +1 -0
- package/src/store/clients/suggestions.test.ts +40 -0
- package/src/store/clients/suggestions.ts +13 -10
- package/src/store/runtime-clients/composer-runtime-client.ts +42 -9
- package/src/store/runtime-clients/message-runtime-client.ts +54 -6
- package/src/store/runtime-clients/thread-list-item-runtime-client.ts +4 -2
- package/src/store/runtime-clients/thread-list-runtime-client.ts +1 -0
- package/src/store/runtime-clients/thread-runtime-client.ts +58 -10
- package/src/store/scopes/composer.ts +16 -4
- package/src/store/scopes/message.ts +14 -0
- package/src/store/scopes/thread-list-item.ts +5 -2
- package/src/store/scopes/thread.ts +4 -0
- package/src/store/scopes/threads.ts +1 -0
- package/src/tests/MessageRepository.test.ts +116 -0
- package/src/tests/OptimisticState-list-race.test.ts +1 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-initialize-adapter-change.test.ts +38 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +47 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +89 -0
- package/src/tests/attachment-adapters.test.ts +14 -0
- package/src/tests/base-composer-runtime-core.test.ts +39 -0
- package/src/tests/external-store-thread-list-runtime-core.test.ts +83 -0
- package/src/tests/external-store-thread-runtime-core-adapter.test.ts +27 -18
- package/src/tests/external-thread-attachments.test.tsx +139 -0
- package/src/tests/external-thread-parity.test.tsx +23 -0
- package/src/tests/remote-thread-list-isLoading.test.ts +1 -0
- package/src/tests/thread-list-runtime-getLoadThreadsPromise.test.ts +1 -0
- package/src/tests/thread-switch-events.test.tsx +78 -0
- package/src/tests/tools-scope-migration.test.tsx +28 -0
- package/src/types/attachment.ts +4 -0
- package/src/utils/abortable-iterable.test.ts +213 -0
- package/src/utils/abortable-iterable.ts +93 -0
- package/src/utils/composite-context-provider.test.ts +25 -0
- package/src/utils/composite-context-provider.ts +23 -2
- package/src/utils/record.test.ts +48 -0
- package/src/utils/record.ts +11 -0
package/src/internal.ts
CHANGED
|
@@ -67,6 +67,10 @@ export {
|
|
|
67
67
|
resolveFilePartSource,
|
|
68
68
|
} from "./utils/data-url";
|
|
69
69
|
export { invokeUserCallback } from "./utils/invoke-user-callback";
|
|
70
|
+
export {
|
|
71
|
+
abortableIterable,
|
|
72
|
+
openAbortableIterable,
|
|
73
|
+
} from "./utils/abortable-iterable";
|
|
70
74
|
export { detectImageMediaType } from "./utils/image-media-type";
|
|
71
75
|
export {
|
|
72
76
|
resolveFileMediaType,
|
|
@@ -133,6 +133,30 @@ describe("AssistantFrameHost", () => {
|
|
|
133
133
|
host.dispose();
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
+
it("cleans up tool calls when posting the request fails", async () => {
|
|
137
|
+
const { execute, host, postMessage } = createHost();
|
|
138
|
+
const error = new Error("postMessage failed");
|
|
139
|
+
const abortController = new AbortController();
|
|
140
|
+
const removeEventListener = vi.spyOn(
|
|
141
|
+
abortController.signal,
|
|
142
|
+
"removeEventListener",
|
|
143
|
+
);
|
|
144
|
+
postMessage.mockImplementation((data) => {
|
|
145
|
+
if (data.message.type === "tool-call") throw error;
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
await expect(
|
|
149
|
+
execute({}, { ...executionContext, abortSignal: abortController.signal }),
|
|
150
|
+
).rejects.toBe(error);
|
|
151
|
+
|
|
152
|
+
expect(vi.getTimerCount()).toBe(0);
|
|
153
|
+
expect(removeEventListener).toHaveBeenCalledWith(
|
|
154
|
+
"abort",
|
|
155
|
+
expect.any(Function),
|
|
156
|
+
);
|
|
157
|
+
host.dispose();
|
|
158
|
+
});
|
|
159
|
+
|
|
136
160
|
it("rejects tool results that carry an empty error message", async () => {
|
|
137
161
|
const { dispatchMessage, execute, getToolCallId, host } = createHost();
|
|
138
162
|
const result = Promise.resolve(
|
|
@@ -195,10 +195,16 @@ export class AssistantFrameHost implements ModelContextProvider {
|
|
|
195
195
|
}, timeout);
|
|
196
196
|
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
197
197
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
198
|
+
try {
|
|
199
|
+
this._iframeWindow.postMessage(
|
|
200
|
+
{ channel: FRAME_MESSAGE_CHANNEL, message },
|
|
201
|
+
this._targetOrigin,
|
|
202
|
+
);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
const pending = this._pendingRequests.get(message.id);
|
|
205
|
+
this._pendingRequests.delete(message.id);
|
|
206
|
+
pending?.reject(error);
|
|
207
|
+
}
|
|
202
208
|
});
|
|
203
209
|
}
|
|
204
210
|
|
|
@@ -120,6 +120,42 @@ describe("AssistantFrameProvider", () => {
|
|
|
120
120
|
await vi.waitFor(() => expect(execute).toHaveBeenCalledOnce());
|
|
121
121
|
});
|
|
122
122
|
|
|
123
|
+
it("does not broadcast from a disposed provider", async () => {
|
|
124
|
+
vi.useFakeTimers();
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
AssistantFrameProvider.addModelContextProvider({
|
|
128
|
+
getModelContext: () => ({ system: "disposed context" }),
|
|
129
|
+
});
|
|
130
|
+
AssistantFrameProvider.dispose();
|
|
131
|
+
|
|
132
|
+
AssistantFrameProvider.addModelContextProvider({
|
|
133
|
+
getModelContext: () => ({ system: "current context" }),
|
|
134
|
+
});
|
|
135
|
+
vi.mocked(parentWindow.postMessage).mockClear();
|
|
136
|
+
|
|
137
|
+
await vi.runAllTimersAsync();
|
|
138
|
+
|
|
139
|
+
expect(parentWindow.postMessage).toHaveBeenCalledOnce();
|
|
140
|
+
expect(parentWindow.postMessage).toHaveBeenCalledWith(
|
|
141
|
+
{
|
|
142
|
+
channel: FRAME_MESSAGE_CHANNEL,
|
|
143
|
+
message: {
|
|
144
|
+
type: "model-context-update",
|
|
145
|
+
context: {
|
|
146
|
+
system: "current context",
|
|
147
|
+
tools: {},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
window.location.origin,
|
|
152
|
+
);
|
|
153
|
+
} finally {
|
|
154
|
+
AssistantFrameProvider.dispose();
|
|
155
|
+
vi.useRealTimers();
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
123
159
|
it("reports a failure even when the thrown error has an empty message", async () => {
|
|
124
160
|
const execute = vi.fn(async () => {
|
|
125
161
|
throw new Error();
|
|
@@ -146,6 +182,41 @@ describe("AssistantFrameProvider", () => {
|
|
|
146
182
|
});
|
|
147
183
|
});
|
|
148
184
|
|
|
185
|
+
it("reports tool results that cannot cross the frame boundary", async () => {
|
|
186
|
+
const consoleError = vi
|
|
187
|
+
.spyOn(console, "error")
|
|
188
|
+
.mockImplementation(() => undefined);
|
|
189
|
+
const execute = vi.fn(async () => () => undefined);
|
|
190
|
+
vi.mocked(parentWindow.postMessage).mockImplementation((data) => {
|
|
191
|
+
structuredClone(data);
|
|
192
|
+
});
|
|
193
|
+
AssistantFrameProvider.addModelContextProvider({
|
|
194
|
+
getModelContext: () => ({
|
|
195
|
+
tools: { sensitiveTool: { execute } },
|
|
196
|
+
}),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
dispatchToolCall(window.location.origin);
|
|
200
|
+
|
|
201
|
+
await vi.waitFor(() => {
|
|
202
|
+
expect(consoleError).toHaveBeenCalledWith(
|
|
203
|
+
"[assistant-ui] AssistantFrame tool result could not be sent.",
|
|
204
|
+
expect.objectContaining({ name: "DataCloneError" }),
|
|
205
|
+
);
|
|
206
|
+
expect(parentWindow.postMessage).toHaveBeenCalledWith(
|
|
207
|
+
{
|
|
208
|
+
channel: FRAME_MESSAGE_CHANNEL,
|
|
209
|
+
message: {
|
|
210
|
+
type: "tool-result",
|
|
211
|
+
id: "tool-call-1",
|
|
212
|
+
error: "Tool result could not be sent across the frame boundary",
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
{ targetOrigin: window.location.origin },
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
149
220
|
it("aborts in-flight tool calls when the parent cancels them", async () => {
|
|
150
221
|
let toolSignal: AbortSignal | undefined;
|
|
151
222
|
const execute = vi.fn(
|
|
@@ -47,13 +47,18 @@ export class AssistantFrameProvider {
|
|
|
47
47
|
private _targetOrigin: string;
|
|
48
48
|
private _strictRegistrations = 0;
|
|
49
49
|
private _wildcardRegistrations = 0;
|
|
50
|
+
private _startupTimer: ReturnType<typeof setTimeout> | undefined;
|
|
51
|
+
private _disposed = false;
|
|
50
52
|
|
|
51
53
|
private constructor(targetOrigin: string = getDefaultTargetOrigin()) {
|
|
52
54
|
this._targetOrigin = targetOrigin;
|
|
53
55
|
this.handleMessage = this.handleMessage.bind(this);
|
|
54
56
|
window.addEventListener("message", this.handleMessage);
|
|
55
57
|
|
|
56
|
-
setTimeout(() =>
|
|
58
|
+
this._startupTimer = setTimeout(() => {
|
|
59
|
+
this._startupTimer = undefined;
|
|
60
|
+
this.broadcastUpdate();
|
|
61
|
+
}, 0);
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
private static getInstance(targetOrigin?: string): AssistantFrameProvider {
|
|
@@ -106,7 +111,12 @@ export class AssistantFrameProvider {
|
|
|
106
111
|
break;
|
|
107
112
|
|
|
108
113
|
case "tool-call":
|
|
109
|
-
this.handleToolCall(message, event)
|
|
114
|
+
void this.handleToolCall(message, event).catch((error: unknown) => {
|
|
115
|
+
console.error(
|
|
116
|
+
"[assistant-ui] AssistantFrame tool call failed.",
|
|
117
|
+
error,
|
|
118
|
+
);
|
|
119
|
+
});
|
|
110
120
|
break;
|
|
111
121
|
|
|
112
122
|
case "tool-cancel":
|
|
@@ -156,11 +166,25 @@ export class AssistantFrameProvider {
|
|
|
156
166
|
if (this._activeToolCalls.get(message.id) !== activeCall) return;
|
|
157
167
|
this._activeToolCalls.delete(message.id);
|
|
158
168
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
169
|
+
try {
|
|
170
|
+
this.sendMessage(event, {
|
|
171
|
+
type: "tool-result",
|
|
172
|
+
id: message.id,
|
|
173
|
+
...(error !== undefined ? { error } : { result }),
|
|
174
|
+
});
|
|
175
|
+
} catch (sendError) {
|
|
176
|
+
if (error !== undefined) throw sendError;
|
|
177
|
+
|
|
178
|
+
console.error(
|
|
179
|
+
"[assistant-ui] AssistantFrame tool result could not be sent.",
|
|
180
|
+
sendError,
|
|
181
|
+
);
|
|
182
|
+
this.sendMessage(event, {
|
|
183
|
+
type: "tool-result",
|
|
184
|
+
id: message.id,
|
|
185
|
+
error: "Tool result could not be sent across the frame boundary",
|
|
186
|
+
});
|
|
187
|
+
}
|
|
164
188
|
}
|
|
165
189
|
|
|
166
190
|
private cancelToolCall(id: string) {
|
|
@@ -244,6 +268,7 @@ export class AssistantFrameProvider {
|
|
|
244
268
|
}
|
|
245
269
|
|
|
246
270
|
private broadcastUpdate() {
|
|
271
|
+
if (this._disposed) return;
|
|
247
272
|
if (window.parent && window.parent !== window) {
|
|
248
273
|
const updateMessage: FrameMessage = {
|
|
249
274
|
type: "model-context-update",
|
|
@@ -375,6 +400,11 @@ export class AssistantFrameProvider {
|
|
|
375
400
|
static dispose() {
|
|
376
401
|
if (AssistantFrameProvider._instance) {
|
|
377
402
|
const instance = AssistantFrameProvider._instance;
|
|
403
|
+
instance._disposed = true;
|
|
404
|
+
if (instance._startupTimer !== undefined) {
|
|
405
|
+
clearTimeout(instance._startupTimer);
|
|
406
|
+
instance._startupTimer = undefined;
|
|
407
|
+
}
|
|
378
408
|
window.removeEventListener("message", instance.handleMessage);
|
|
379
409
|
|
|
380
410
|
let cleanupFailed = false;
|
|
@@ -131,6 +131,34 @@ describe("shallowMergeInteractableState", () => {
|
|
|
131
131
|
});
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
+
it("uses the first valid patch for each array item id", () => {
|
|
135
|
+
expect(
|
|
136
|
+
shallowMergeInteractableState(
|
|
137
|
+
{ tasks: [{ id: 1, title: "Original", done: false }] },
|
|
138
|
+
{
|
|
139
|
+
tasks: {
|
|
140
|
+
update: [
|
|
141
|
+
null,
|
|
142
|
+
{ title: "Missing id" },
|
|
143
|
+
{ id: "1", title: "String id" },
|
|
144
|
+
{ id: 1, title: "First", done: true },
|
|
145
|
+
{ id: 1, title: "Second" },
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
),
|
|
150
|
+
).toEqual({ tasks: [{ id: 1, title: "First", done: true }] });
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("does not match NaN array item ids", () => {
|
|
154
|
+
expect(
|
|
155
|
+
shallowMergeInteractableState(
|
|
156
|
+
{ tasks: [{ id: Number.NaN, title: "Original" }] },
|
|
157
|
+
{ tasks: { update: [{ id: Number.NaN, title: "Patched" }] } },
|
|
158
|
+
),
|
|
159
|
+
).toEqual({ tasks: [{ id: Number.NaN, title: "Original" }] });
|
|
160
|
+
});
|
|
161
|
+
|
|
134
162
|
it("keeps raw array replacement semantics", () => {
|
|
135
163
|
expect(
|
|
136
164
|
shallowMergeInteractableState(
|
|
@@ -94,12 +94,18 @@ function applyArrayUpdate(
|
|
|
94
94
|
|
|
95
95
|
const patches = update.update;
|
|
96
96
|
if (Array.isArray(patches) && patches.length > 0) {
|
|
97
|
+
const patchesById = new Map<string | number, Record<string, unknown>>();
|
|
98
|
+
for (const candidate of patches) {
|
|
99
|
+
const id = getArrayItemId(candidate);
|
|
100
|
+
if (id !== undefined && !Number.isNaN(id) && !patchesById.has(id)) {
|
|
101
|
+
patchesById.set(id, candidate as Record<string, unknown>);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
97
105
|
next = next.map((item) => {
|
|
98
106
|
const id = getArrayItemId(item);
|
|
99
107
|
if (id === undefined || !isRecord(item)) return item;
|
|
100
|
-
const patch =
|
|
101
|
-
(candidate) => isRecord(candidate) && candidate.id === id,
|
|
102
|
-
);
|
|
108
|
+
const patch = patchesById.get(id);
|
|
103
109
|
return patch ? { ...item, ...patch } : item;
|
|
104
110
|
});
|
|
105
111
|
}
|
|
@@ -2,6 +2,18 @@ import { describe, expect, it, vi } from "vitest";
|
|
|
2
2
|
import { ModelContextRegistry } from "./registry";
|
|
3
3
|
|
|
4
4
|
describe("ModelContextRegistry", () => {
|
|
5
|
+
it.each(["__proto__", "constructor", "toString"])(
|
|
6
|
+
"retains a tool named %s",
|
|
7
|
+
(toolName) => {
|
|
8
|
+
const registry = new ModelContextRegistry();
|
|
9
|
+
registry.addTool({ toolName });
|
|
10
|
+
|
|
11
|
+
const tools = registry.getModelContext().tools!;
|
|
12
|
+
expect(Object.hasOwn(tools, toolName)).toBe(true);
|
|
13
|
+
expect(Object.keys(tools)).toEqual([toolName]);
|
|
14
|
+
},
|
|
15
|
+
);
|
|
16
|
+
|
|
5
17
|
it("notifies every subscriber and rethrows when a registration subscriber throws", () => {
|
|
6
18
|
const registry = new ModelContextRegistry();
|
|
7
19
|
const error = new Error("subscriber failed");
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
ModelContextRegistryInstructionHandle,
|
|
14
14
|
ModelContextRegistryProviderHandle,
|
|
15
15
|
} from "./registry-handles";
|
|
16
|
+
import { nullProtoRecord } from "../utils/record";
|
|
16
17
|
|
|
17
18
|
export class ModelContextRegistry implements ModelContextProvider {
|
|
18
19
|
private _tools = new Map<symbol, AssistantToolProps<any, any>>();
|
|
@@ -34,7 +35,7 @@ export class ModelContextRegistry implements ModelContextProvider {
|
|
|
34
35
|
const system =
|
|
35
36
|
instructions.length > 0 ? instructions.join("\n\n") : undefined;
|
|
36
37
|
|
|
37
|
-
const tools
|
|
38
|
+
const tools = nullProtoRecord<Tool<any, any>>();
|
|
38
39
|
for (const toolProps of this._tools.values()) {
|
|
39
40
|
const { toolName, render, ...tool } = toolProps;
|
|
40
41
|
tools[toolName] = tool;
|
|
@@ -54,7 +55,7 @@ export class ModelContextRegistry implements ModelContextProvider {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
if (providerContexts.tools) {
|
|
57
|
-
context.tools =
|
|
58
|
+
context.tools = nullProtoRecord(context.tools, providerContexts.tools);
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
if (providerContexts.callSettings) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { mergeModelContexts } from "./types";
|
|
3
|
+
|
|
4
|
+
const provider = (tools: Record<string, any>, priority?: number) =>
|
|
5
|
+
({ getModelContext: () => ({ tools, priority }) }) as any;
|
|
6
|
+
|
|
7
|
+
describe("mergeModelContexts", () => {
|
|
8
|
+
it.each(["__proto__", "constructor", "toString"])(
|
|
9
|
+
"retains and prioritizes a tool named %s",
|
|
10
|
+
(name) => {
|
|
11
|
+
const highPriorityTool = { description: "high", parameters: {} };
|
|
12
|
+
const lowPriorityTool = { description: "low", parameters: {} };
|
|
13
|
+
const otherTool = { description: "other", parameters: {} };
|
|
14
|
+
const result = mergeModelContexts(
|
|
15
|
+
new Set([
|
|
16
|
+
provider({ [name]: highPriorityTool, ok: otherTool }, 1),
|
|
17
|
+
provider({ [name]: lowPriorityTool }, 0),
|
|
18
|
+
]),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
expect(Object.keys(result.tools ?? {})).toEqual([name, "ok"]);
|
|
22
|
+
expect(result.tools?.[name]?.description).toBe("high");
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Unsubscribe } from "../types/unsubscribe";
|
|
2
2
|
import type { Tool } from "assistant-stream";
|
|
3
|
+
import { nullProtoRecord } from "../utils/record";
|
|
3
4
|
|
|
4
5
|
export type LanguageModelV1CallSettings = {
|
|
5
6
|
maxTokens?: number;
|
|
@@ -68,7 +69,7 @@ export const mergeModelContexts = (
|
|
|
68
69
|
.map((c) => c.getModelContext())
|
|
69
70
|
.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
70
71
|
|
|
71
|
-
const toolPriorities
|
|
72
|
+
const toolPriorities = nullProtoRecord<number>();
|
|
72
73
|
|
|
73
74
|
return configs.reduce((acc, config) => {
|
|
74
75
|
const priority = config.priority ?? 0;
|
|
@@ -81,7 +82,9 @@ export const mergeModelContexts = (
|
|
|
81
82
|
}
|
|
82
83
|
if (config.tools) {
|
|
83
84
|
for (const [name, tool] of Object.entries(config.tools)) {
|
|
84
|
-
const
|
|
85
|
+
const hasExisting =
|
|
86
|
+
acc.tools !== undefined && Object.hasOwn(acc.tools, name);
|
|
87
|
+
const existing = hasExisting ? acc.tools![name] : undefined;
|
|
85
88
|
if (existing && existing !== tool) {
|
|
86
89
|
const existingPriority = toolPriorities[name]!;
|
|
87
90
|
if (existingPriority === priority) {
|
|
@@ -106,9 +109,11 @@ export const mergeModelContexts = (
|
|
|
106
109
|
continue;
|
|
107
110
|
}
|
|
108
111
|
|
|
109
|
-
if (!acc.tools) acc.tools =
|
|
112
|
+
if (!acc.tools) acc.tools = nullProtoRecord();
|
|
110
113
|
acc.tools[name] = stripOverwrite(tool);
|
|
111
|
-
toolPriorities
|
|
114
|
+
if (!Object.hasOwn(toolPriorities, name)) {
|
|
115
|
+
toolPriorities[name] = priority;
|
|
116
|
+
}
|
|
112
117
|
}
|
|
113
118
|
}
|
|
114
119
|
if (config.config) {
|
|
@@ -69,6 +69,40 @@ describe("RuntimeAdapter via the neutral store entry", () => {
|
|
|
69
69
|
handle.destroy();
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
it("emits composer.send for a thread.append, flagging a suggestion", async () => {
|
|
73
|
+
const { runtime, onNew } = createRuntime({
|
|
74
|
+
suggestions: [{ prompt: "hi there" }],
|
|
75
|
+
});
|
|
76
|
+
const handle = createAssistantClient(
|
|
77
|
+
AuiConfig({ threads: RuntimeAdapter(runtime) }),
|
|
78
|
+
);
|
|
79
|
+
handle.subscribe(() => {});
|
|
80
|
+
const aui = handle.getClient();
|
|
81
|
+
const sent = vi.fn();
|
|
82
|
+
aui.on({ scope: "thread", event: "composer.send" }, sent);
|
|
83
|
+
|
|
84
|
+
flushTapSync(() =>
|
|
85
|
+
aui.thread.append({ content: [{ type: "text", text: "hi there" }] }),
|
|
86
|
+
);
|
|
87
|
+
flushTapSync(() => aui.thread.append("plain text"));
|
|
88
|
+
await new Promise((resolve) => setTimeout(resolve));
|
|
89
|
+
|
|
90
|
+
expect(onNew).toHaveBeenCalledTimes(2);
|
|
91
|
+
expect(sent).toHaveBeenCalledTimes(2);
|
|
92
|
+
expect(sent.mock.calls[0]![0]).toMatchObject({
|
|
93
|
+
chars: 8,
|
|
94
|
+
attachments: 0,
|
|
95
|
+
suggestion: true,
|
|
96
|
+
});
|
|
97
|
+
expect(sent.mock.calls[1]![0]).toMatchObject({
|
|
98
|
+
chars: 10,
|
|
99
|
+
attachments: 0,
|
|
100
|
+
});
|
|
101
|
+
expect(sent.mock.calls[1]![0]).not.toHaveProperty("suggestion");
|
|
102
|
+
|
|
103
|
+
handle.destroy();
|
|
104
|
+
});
|
|
105
|
+
|
|
72
106
|
it("registers the client's model context on the runtime", () => {
|
|
73
107
|
const { runtime } = createRuntime();
|
|
74
108
|
const register = vi.spyOn(runtime, "registerModelContextProvider");
|
|
@@ -210,6 +210,34 @@ describe("Interactables registration", () => {
|
|
|
210
210
|
expect(stateOf(root, "n1")).toEqual({ v: 7 });
|
|
211
211
|
});
|
|
212
212
|
|
|
213
|
+
// `definitions` is read by bare key with ids that reach the runtime from
|
|
214
|
+
// model tool calls, so it must stay prototype-free through every transition.
|
|
215
|
+
it.each(["__proto__", "constructor", "toString"])(
|
|
216
|
+
"registers, updates and unregisters an interactable named %s",
|
|
217
|
+
(id) => {
|
|
218
|
+
root = mount();
|
|
219
|
+
const unregister = root.getValue().register(reg(id));
|
|
220
|
+
expect(stateOf(root, id)).toEqual({ v: 0 });
|
|
221
|
+
|
|
222
|
+
root.getValue().setState(id, () => ({ v: 1 }));
|
|
223
|
+
expect(stateOf(root, id)).toEqual({ v: 1 });
|
|
224
|
+
expect(Object.keys(root.getValue().getState().definitions)).toEqual([id]);
|
|
225
|
+
|
|
226
|
+
unregister();
|
|
227
|
+
expect(Object.keys(root.getValue().getState().definitions)).toEqual([]);
|
|
228
|
+
},
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
it("keeps the state records prototype-free", () => {
|
|
232
|
+
root = mount();
|
|
233
|
+
root.getValue().register(reg("n1"));
|
|
234
|
+
root.getValue().setState("n1", () => ({ v: 1 }));
|
|
235
|
+
|
|
236
|
+
const state = root.getValue().getState();
|
|
237
|
+
expect(Object.getPrototypeOf(state.definitions)).toBeNull();
|
|
238
|
+
expect(Object.getPrototypeOf(state.persistence)).toBeNull();
|
|
239
|
+
});
|
|
240
|
+
|
|
213
241
|
it("restores detached state when an instance re-registers in-session", async () => {
|
|
214
242
|
root = mount();
|
|
215
243
|
const unregister = root.getValue().register(reg("n1"));
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
} from "../../model-context/interactable-composer-metadata";
|
|
26
26
|
import { notifySubscribers as notifyStateSubscribers } from "../../subscribable/subscribable";
|
|
27
27
|
import { useInteractablePersistenceQueue } from "../interactables-shared/useInteractablePersistenceQueue";
|
|
28
|
+
import { nullProtoRecord } from "../../utils/record";
|
|
28
29
|
|
|
29
30
|
type RestorePersistedStateOptions = {
|
|
30
31
|
stash: Map<string, unknown>;
|
|
@@ -77,8 +78,8 @@ const useInteractablesResource = ({
|
|
|
77
78
|
persistence,
|
|
78
79
|
}: Unstable_InteractablesConfig = {}): ClientOutput<"unstable_interactables"> => {
|
|
79
80
|
const [state, setState] = useState<Unstable_InteractablesState>(() => ({
|
|
80
|
-
definitions:
|
|
81
|
-
persistence:
|
|
81
|
+
definitions: nullProtoRecord(),
|
|
82
|
+
persistence: nullProtoRecord(),
|
|
82
83
|
}));
|
|
83
84
|
|
|
84
85
|
const clientRef = useAssistantClientRef();
|
|
@@ -123,7 +124,8 @@ const useInteractablesResource = ({
|
|
|
123
124
|
);
|
|
124
125
|
|
|
125
126
|
const exportState = useCallback((): Unstable_InteractablePersistedState => {
|
|
126
|
-
const result
|
|
127
|
+
const result =
|
|
128
|
+
nullProtoRecord<Unstable_InteractablePersistedState[string]>();
|
|
127
129
|
for (const [id, def] of Object.entries(stateRef.current.definitions)) {
|
|
128
130
|
if (def.scope === "thread") continue; // thread items persist via snapshot, not the adapter
|
|
129
131
|
result[id] = { name: def.name, state: def.state };
|
|
@@ -167,7 +169,7 @@ const useInteractablesResource = ({
|
|
|
167
169
|
}
|
|
168
170
|
setStateAndRef((prev) => {
|
|
169
171
|
let changed = false;
|
|
170
|
-
const definitions =
|
|
172
|
+
const definitions = nullProtoRecord(prev.definitions);
|
|
171
173
|
for (const [id, entry] of Object.entries(saved)) {
|
|
172
174
|
const def = definitions[id];
|
|
173
175
|
if (!def || !shouldApply(id, def)) continue;
|
|
@@ -260,10 +262,9 @@ const useInteractablesResource = ({
|
|
|
260
262
|
if (!existing) return prev;
|
|
261
263
|
return {
|
|
262
264
|
...prev,
|
|
263
|
-
definitions: {
|
|
264
|
-
...prev.definitions,
|
|
265
|
+
definitions: nullProtoRecord(prev.definitions, {
|
|
265
266
|
[id]: { ...existing, state: updater(existing.state) },
|
|
266
|
-
},
|
|
267
|
+
}),
|
|
267
268
|
};
|
|
268
269
|
});
|
|
269
270
|
if (stateRef.current.definitions[id]?.scope !== "thread") {
|
|
@@ -450,8 +451,7 @@ const useInteractablesResource = ({
|
|
|
450
451
|
|
|
451
452
|
setStateAndRef((prev) => ({
|
|
452
453
|
...prev,
|
|
453
|
-
definitions: {
|
|
454
|
-
...prev.definitions,
|
|
454
|
+
definitions: nullProtoRecord(prev.definitions, {
|
|
455
455
|
[def.id]: {
|
|
456
456
|
id: def.id,
|
|
457
457
|
name: def.name,
|
|
@@ -466,7 +466,7 @@ const useInteractablesResource = ({
|
|
|
466
466
|
loaded ??
|
|
467
467
|
def.initialState,
|
|
468
468
|
},
|
|
469
|
-
},
|
|
469
|
+
}),
|
|
470
470
|
}));
|
|
471
471
|
|
|
472
472
|
return () => {
|
|
@@ -498,9 +498,11 @@ const useInteractablesResource = ({
|
|
|
498
498
|
}
|
|
499
499
|
}
|
|
500
500
|
partialSchemaCacheRef.current.delete(def.id);
|
|
501
|
-
const
|
|
502
|
-
const
|
|
503
|
-
|
|
501
|
+
const definitions = nullProtoRecord(prev.definitions);
|
|
502
|
+
const persistence = nullProtoRecord(prev.persistence);
|
|
503
|
+
delete definitions[def.id];
|
|
504
|
+
delete persistence[def.id];
|
|
505
|
+
return { ...prev, definitions, persistence };
|
|
504
506
|
});
|
|
505
507
|
};
|
|
506
508
|
},
|