@assistant-ui/core 0.2.18 → 0.2.20
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 +4 -2
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +2 -1
- package/dist/internal.js +2 -1
- package/dist/model-context/interactable-composer-metadata.d.ts +105 -0
- package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -0
- package/dist/model-context/interactable-composer-metadata.js +259 -0
- package/dist/model-context/interactable-composer-metadata.js.map +1 -0
- package/dist/model-context/registry.d.ts.map +1 -1
- package/dist/model-context/registry.js +1 -0
- package/dist/model-context/registry.js.map +1 -1
- package/dist/model-context/types.d.ts +6 -0
- package/dist/model-context/types.d.ts.map +1 -1
- package/dist/model-context/types.js +4 -0
- package/dist/model-context/types.js.map +1 -1
- package/dist/react/AssistantProvider.d.ts +1 -1
- package/dist/react/AssistantProvider.js +1 -1
- package/dist/react/AssistantRuntimeProvider.d.ts +1 -1
- package/dist/react/RuntimeAdapter.js +1 -1
- package/dist/react/client/Interactables.d.ts +8 -2
- package/dist/react/client/Interactables.d.ts.map +1 -1
- package/dist/react/client/Interactables.js +190 -94
- package/dist/react/client/Interactables.js.map +1 -1
- package/dist/react/client/Tools.js +1 -1
- package/dist/react/client/interactable-model-context.d.ts +9 -6
- package/dist/react/client/interactable-model-context.d.ts.map +1 -1
- package/dist/react/client/interactable-model-context.js +154 -32
- package/dist/react/client/interactable-model-context.js.map +1 -1
- package/dist/react/index.d.ts +15 -6
- package/dist/react/index.js +12 -5
- package/dist/react/interactables-legacy/Interactables.d.ts +12 -0
- package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -0
- package/dist/react/interactables-legacy/Interactables.js +264 -0
- package/dist/react/interactables-legacy/Interactables.js.map +1 -0
- package/dist/react/interactables-legacy/interactable-model-context.d.ts +12 -0
- package/dist/react/interactables-legacy/interactable-model-context.d.ts.map +1 -0
- package/dist/react/interactables-legacy/interactable-model-context.js +54 -0
- package/dist/react/interactables-legacy/interactable-model-context.js.map +1 -0
- package/dist/react/interactables-legacy/scopes.d.ts +58 -0
- package/dist/react/interactables-legacy/scopes.d.ts.map +1 -0
- package/dist/react/interactables-legacy/scopes.js +0 -0
- package/dist/react/interactables-legacy/useAssistantInteractable.d.ts +31 -0
- package/dist/react/interactables-legacy/useAssistantInteractable.d.ts.map +1 -0
- package/dist/react/{model-context → interactables-legacy}/useAssistantInteractable.js +6 -2
- package/dist/react/interactables-legacy/useAssistantInteractable.js.map +1 -0
- package/dist/react/interactables-legacy/useInteractableState.d.ts +21 -0
- package/dist/react/interactables-legacy/useInteractableState.d.ts.map +1 -0
- package/dist/react/interactables-legacy/useInteractableState.js +84 -0
- package/dist/react/interactables-legacy/useInteractableState.js.map +1 -0
- package/dist/react/model-context/interactableTool.d.ts +53 -0
- package/dist/react/model-context/interactableTool.d.ts.map +1 -0
- package/dist/react/model-context/interactableTool.js +188 -0
- package/dist/react/model-context/interactableTool.js.map +1 -0
- package/dist/react/model-context/useAssistantContext.js +1 -1
- package/dist/react/model-context/useAssistantDataUI.js +1 -1
- package/dist/react/model-context/useAssistantInstructions.js +1 -1
- package/dist/react/model-context/useAssistantTool.js +1 -1
- package/dist/react/model-context/useAssistantToolUI.js +1 -1
- package/dist/react/model-context/useAuiToolOverrides.js +1 -1
- package/dist/react/model-context/useInteractable.d.ts +73 -0
- package/dist/react/model-context/useInteractable.d.ts.map +1 -0
- package/dist/react/model-context/useInteractable.js +100 -0
- package/dist/react/model-context/useInteractable.js.map +1 -0
- package/dist/react/model-context/useInteractableState.d.ts +8 -5
- package/dist/react/model-context/useInteractableState.d.ts.map +1 -1
- package/dist/react/model-context/useInteractableState.js +48 -48
- package/dist/react/model-context/useInteractableState.js.map +1 -1
- package/dist/react/model-context/useInteractableVersions.d.ts +19 -0
- package/dist/react/model-context/useInteractableVersions.d.ts.map +1 -0
- package/dist/react/model-context/useInteractableVersions.js +51 -0
- package/dist/react/model-context/useInteractableVersions.js.map +1 -0
- package/dist/react/primitive-hooks/useThreadMessageIds.d.ts +15 -0
- package/dist/react/primitive-hooks/useThreadMessageIds.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useThreadMessageIds.js +26 -0
- package/dist/react/primitive-hooks/useThreadMessageIds.js.map +1 -0
- package/dist/react/primitive-hooks/useVoice.js +1 -1
- package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.js +1 -1
- package/dist/react/primitives/composer/ComposerAttachments.js +1 -1
- package/dist/react/primitives/composer/ComposerQueue.js +1 -1
- package/dist/react/primitives/generativeUI/GenerativeUI.d.ts.map +1 -1
- package/dist/react/primitives/generativeUI/GenerativeUI.js +2 -2
- package/dist/react/primitives/generativeUI/GenerativeUI.js.map +1 -1
- package/dist/react/primitives/message/MessageAttachments.js +1 -1
- package/dist/react/primitives/message/MessageGroupedParts.js +1 -1
- package/dist/react/primitives/message/MessageParts.js +1 -1
- package/dist/react/primitives/message/MessageQuote.js +1 -1
- package/dist/react/primitives/part/PartMessages.d.ts +5 -2
- package/dist/react/primitives/part/PartMessages.d.ts.map +1 -1
- package/dist/react/primitives/part/PartMessages.js +8 -8
- package/dist/react/primitives/part/PartMessages.js.map +1 -1
- package/dist/react/primitives/thread/ThreadMessages.d.ts +31 -1
- package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
- package/dist/react/primitives/thread/ThreadMessages.js +64 -2
- package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
- package/dist/react/primitives/thread/ThreadSuggestions.js +1 -1
- package/dist/react/primitives/threadList/ThreadListItems.js +1 -1
- package/dist/react/providers/MessageByIdProvider.d.ts +9 -0
- package/dist/react/providers/MessageByIdProvider.d.ts.map +1 -0
- package/dist/react/providers/MessageByIdProvider.js +57 -0
- package/dist/react/providers/MessageByIdProvider.js.map +1 -0
- package/dist/react/providers/ReadonlyThreadProvider.js +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +3 -0
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +16 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +1 -1
- package/dist/react/runtimes/cloud/auiV0.js +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +1 -1
- package/dist/react/runtimes/createRuntimeExtras.d.ts +20 -0
- package/dist/react/runtimes/createRuntimeExtras.d.ts.map +1 -0
- package/dist/react/runtimes/createRuntimeExtras.js +43 -0
- package/dist/react/runtimes/createRuntimeExtras.js.map +1 -0
- package/dist/react/runtimes/useLocalRuntime.js +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.js +6 -2
- package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
- package/dist/react/runtimes/useStreamingTiming.d.ts +26 -0
- package/dist/react/runtimes/useStreamingTiming.d.ts.map +1 -0
- package/dist/react/runtimes/useStreamingTiming.js +43 -0
- package/dist/react/runtimes/useStreamingTiming.js.map +1 -0
- package/dist/react/types/scopes/interactables.d.ts +40 -24
- package/dist/react/types/scopes/interactables.d.ts.map +1 -1
- package/dist/react/types/store-augmentation.d.ts +3 -1
- package/dist/react/types/store-augmentation.d.ts.map +1 -1
- package/dist/react/utils/useJSONEqual.d.ts +11 -0
- package/dist/react/utils/useJSONEqual.d.ts.map +1 -0
- package/dist/react/utils/useJSONEqual.js +30 -0
- package/dist/react/utils/useJSONEqual.js.map +1 -0
- package/dist/runtime/base/base-composer-runtime-core.d.ts +5 -0
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +13 -0
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/default-edit-composer-runtime-core.js +6 -1
- package/dist/runtime/base/default-edit-composer-runtime-core.js.map +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 +4 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.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 +6 -3
- package/dist/runtime/utils/message-repository.js.map +1 -1
- package/dist/runtime/utils/streaming-timing.d.ts +59 -0
- package/dist/runtime/utils/streaming-timing.d.ts.map +1 -0
- package/dist/runtime/utils/streaming-timing.js +76 -0
- package/dist/runtime/utils/streaming-timing.js.map +1 -0
- package/dist/runtime/utils/thread-message-like.js +2 -1
- package/dist/runtime/utils/thread-message-like.js.map +1 -1
- package/dist/runtimes/external-store/external-store-adapter.d.ts +26 -1
- package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +9 -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 +19 -0
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.js +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/remote-thread-list/types.d.ts +12 -0
- package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
- package/dist/store/clients/suggestions.js +1 -1
- package/dist/store/clients/thread-message-client.js +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.js +1 -1
- package/dist/store/runtime-clients/message-runtime-client.js +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.js +1 -1
- package/dist/tests/remote-thread-list-test-helpers.d.ts +1 -1
- package/dist/tests/remote-thread-list-test-helpers.d.ts.map +1 -1
- package/dist/tests/remote-thread-list-test-helpers.js +3 -2
- package/dist/tests/remote-thread-list-test-helpers.js.map +1 -1
- package/package.json +9 -9
- package/src/index.ts +17 -0
- package/src/internal.ts +7 -0
- package/src/model-context/interactable-composer-metadata.test.ts +550 -0
- package/src/model-context/interactable-composer-metadata.ts +395 -0
- package/src/model-context/registry.ts +5 -0
- package/src/model-context/types.ts +12 -0
- package/src/react/client/Interactables.test.ts +358 -0
- package/src/react/client/Interactables.ts +339 -86
- package/src/react/client/interactable-model-context.test.ts +418 -0
- package/src/react/client/interactable-model-context.ts +252 -58
- package/src/react/index.ts +72 -3
- package/src/react/interactables-legacy/Interactables.ts +332 -0
- package/src/react/interactables-legacy/interactable-model-context.test.ts +54 -0
- package/src/react/interactables-legacy/interactable-model-context.ts +80 -0
- package/src/react/interactables-legacy/scopes.ts +66 -0
- package/src/react/{model-context → interactables-legacy}/useAssistantInteractable.ts +10 -1
- package/src/react/interactables-legacy/useInteractableState.ts +67 -0
- package/src/react/model-context/interactableTool.tsx +175 -0
- package/src/react/model-context/useInteractable.ts +233 -0
- package/src/react/model-context/useInteractableState.ts +31 -23
- package/src/react/model-context/useInteractableVersions.ts +48 -0
- package/src/react/primitive-hooks/useThreadMessageIds.ts +27 -0
- package/src/react/primitives/generativeUI/GenerativeUI.tsx +5 -1
- package/src/react/primitives/part/PartMessages.test.tsx +17 -0
- package/src/react/primitives/part/PartMessages.tsx +13 -8
- package/src/react/primitives/thread/ThreadMessages.tsx +67 -0
- package/src/react/providers/MessageByIdProvider.tsx +23 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +21 -1
- package/src/react/runtimes/createRuntimeExtras.test.ts +58 -0
- package/src/react/runtimes/createRuntimeExtras.ts +68 -0
- package/src/react/runtimes/createRuntimeExtras.use.test.ts +50 -0
- package/src/react/runtimes/useRemoteThreadListRuntime.ts +13 -1
- package/src/react/runtimes/useStreamingTiming.ts +68 -0
- package/src/react/types/scopes/interactables.ts +45 -23
- package/src/react/types/store-augmentation.ts +4 -2
- package/src/react/utils/useJSONEqual.ts +20 -0
- package/src/runtime/base/base-composer-runtime-core.ts +13 -0
- package/src/runtime/base/default-edit-composer-runtime-core.ts +18 -1
- package/src/runtime/base/default-thread-composer-runtime-core.ts +11 -1
- package/src/runtime/utils/message-repository.ts +11 -7
- package/src/runtime/utils/streaming-timing.test.ts +301 -0
- package/src/runtime/utils/streaming-timing.ts +161 -0
- package/src/runtime/utils/thread-message-like.ts +2 -1
- package/src/runtimes/external-store/external-store-adapter.ts +28 -0
- package/src/runtimes/external-store/external-store-thread-runtime-core.ts +33 -1
- package/src/runtimes/local/local-thread-runtime-core.ts +4 -1
- package/src/runtimes/remote-thread-list/types.ts +13 -0
- package/src/tests/MessageRepository.test.ts +2 -0
- package/src/tests/composer-can-send.test.ts +1 -0
- package/src/tests/default-edit-composer-runtime-core.test.ts +78 -1
- package/src/tests/default-thread-composer-runtime-core.test.ts +82 -0
- package/src/tests/external-store-thread-runtime-core.test.ts +337 -0
- package/src/tests/groupParts.test.ts +32 -0
- package/src/tests/remote-thread-list-reactive-threadId.test.ts +90 -0
- package/src/tests/remote-thread-list-test-helpers.ts +2 -1
- package/src/tests/thread-message-like.test.ts +50 -0
- package/dist/react/model-context/useAssistantInteractable.d.ts +0 -22
- package/dist/react/model-context/useAssistantInteractable.d.ts.map +0 -1
- package/dist/react/model-context/useAssistantInteractable.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-edit-composer-runtime-core.js","names":["AppendMessage","ThreadMessage","CompleteAttachment","getThreadMessageText","attachmentsEqual","liftNonTextParts","AttachmentAdapter","DictationAdapter","SendOptions","ThreadRuntimeCore","BaseComposerRuntimeCore","DefaultEditComposerRuntimeCore","canCancel","canSend","isEmpty","getAttachmentAdapter","runtime","adapters","attachments","getDictationAdapter","dictation","_previousText","_previousAttachments","_nonTextPassthrough","_parentId","_sourceId","constructor","endEditCallback","parentId","message","id","setText","setRole","role","content","filter","p","type","setAttachments","setRunConfig","composer","runConfig","sourceId","handleSend","Omit","options","text","attachmentsChanged","startRun","length","append","handleCancel","_notifySubscribers"],"sources":["../../../src/runtime/base/default-edit-composer-runtime-core.ts"],"sourcesContent":["import type { AppendMessage, ThreadMessage } from \"../../types/message\";\nimport type { CompleteAttachment } from \"../../types/attachment\";\nimport { getThreadMessageText } from \"../../utils/text\";\nimport { attachmentsEqual, liftNonTextParts } from \"../../adapters/attachment\";\nimport type { AttachmentAdapter } from \"../../adapters/attachment\";\nimport type { DictationAdapter } from \"../../adapters/speech\";\nimport type { SendOptions } from \"../interfaces/composer-runtime-core\";\nimport type { ThreadRuntimeCore } from \"../interfaces/thread-runtime-core\";\nimport { BaseComposerRuntimeCore } from \"./base-composer-runtime-core\";\n\nexport class DefaultEditComposerRuntimeCore extends BaseComposerRuntimeCore {\n public get canCancel() {\n return true;\n }\n\n public get canSend() {\n return !this.isEmpty;\n }\n\n protected getAttachmentAdapter() {\n return this.runtime.adapters?.attachments;\n }\n\n protected getDictationAdapter() {\n return this.runtime.adapters?.dictation;\n }\n\n private _previousText;\n private _previousAttachments: readonly CompleteAttachment[];\n private _nonTextPassthrough: readonly ThreadMessage[\"content\"][number][];\n private _parentId: string | null;\n private _sourceId: string | null;\n constructor(\n private runtime: ThreadRuntimeCore & {\n adapters?:\n | {\n attachments?: AttachmentAdapter | undefined;\n dictation?: DictationAdapter | undefined;\n }\n | undefined;\n },\n private endEditCallback: () => void,\n { parentId, message }: { parentId: string | null; message: ThreadMessage },\n ) {\n super();\n this._parentId = parentId;\n this._sourceId = message.id;\n this._previousText = getThreadMessageText(message);\n this.setText(this._previousText);\n\n this.setRole(message.role);\n\n if (message.role === \"user\") {\n this._previousAttachments = [\n ...(message.attachments ?? []),\n ...liftNonTextParts(message.content),\n ];\n this._nonTextPassthrough = [];\n } else {\n this._previousAttachments = message.attachments ?? [];\n this._nonTextPassthrough = message.content.filter(\n (p) => p.type !== \"text\",\n );\n }\n this.setAttachments(this._previousAttachments);\n\n this.setRunConfig({ ...runtime.composer.runConfig });\n }\n\n public get parentId() {\n return this._parentId;\n }\n\n public get sourceId() {\n return this._sourceId;\n }\n\n public async handleSend(\n message: Omit<AppendMessage, \"parentId\" | \"sourceId\">,\n options?: SendOptions,\n ) {\n const text = getThreadMessageText(message as AppendMessage);\n const attachmentsChanged = !attachmentsEqual(\n message.attachments ?? [],\n this._previousAttachments,\n );\n\n if (\n text !== this._previousText ||\n attachmentsChanged ||\n options?.startRun\n ) {\n const content =\n this._nonTextPassthrough.length > 0\n ? ([\n ...message.content,\n ...this._nonTextPassthrough,\n ] as AppendMessage[\"content\"])\n : message.content;\n this.runtime.append({\n ...
|
|
1
|
+
{"version":3,"file":"default-edit-composer-runtime-core.js","names":["AppendMessage","ThreadMessage","CompleteAttachment","getThreadMessageText","attachmentsEqual","liftNonTextParts","AttachmentAdapter","DictationAdapter","SendOptions","ThreadRuntimeCore","BaseComposerRuntimeCore","gateInteractableComposerMetadata","DefaultEditComposerRuntimeCore","canCancel","canSend","isEmpty","getAttachmentAdapter","runtime","adapters","attachments","getDictationAdapter","dictation","_previousText","_previousAttachments","_nonTextPassthrough","_parentId","_sourceId","constructor","endEditCallback","parentId","message","id","setText","setRole","role","content","filter","p","type","setAttachments","setRunConfig","composer","runConfig","sourceId","handleSend","Omit","options","text","attachmentsChanged","startRun","length","messages","parentIndex","findIndex","m","composerMetadata","getModelContext","unstable_composerMetadata","slice","enriched","enrichWithComposerMetadata","append","handleCancel","_notifySubscribers"],"sources":["../../../src/runtime/base/default-edit-composer-runtime-core.ts"],"sourcesContent":["import type { AppendMessage, ThreadMessage } from \"../../types/message\";\nimport type { CompleteAttachment } from \"../../types/attachment\";\nimport { getThreadMessageText } from \"../../utils/text\";\nimport { attachmentsEqual, liftNonTextParts } from \"../../adapters/attachment\";\nimport type { AttachmentAdapter } from \"../../adapters/attachment\";\nimport type { DictationAdapter } from \"../../adapters/speech\";\nimport type { SendOptions } from \"../interfaces/composer-runtime-core\";\nimport type { ThreadRuntimeCore } from \"../interfaces/thread-runtime-core\";\nimport { BaseComposerRuntimeCore } from \"./base-composer-runtime-core\";\nimport { gateInteractableComposerMetadata } from \"../../model-context/interactable-composer-metadata\";\n\nexport class DefaultEditComposerRuntimeCore extends BaseComposerRuntimeCore {\n public get canCancel() {\n return true;\n }\n\n public get canSend() {\n return !this.isEmpty;\n }\n\n protected getAttachmentAdapter() {\n return this.runtime.adapters?.attachments;\n }\n\n protected getDictationAdapter() {\n return this.runtime.adapters?.dictation;\n }\n\n private _previousText;\n private _previousAttachments: readonly CompleteAttachment[];\n private _nonTextPassthrough: readonly ThreadMessage[\"content\"][number][];\n private _parentId: string | null;\n private _sourceId: string | null;\n constructor(\n private runtime: ThreadRuntimeCore & {\n adapters?:\n | {\n attachments?: AttachmentAdapter | undefined;\n dictation?: DictationAdapter | undefined;\n }\n | undefined;\n },\n private endEditCallback: () => void,\n { parentId, message }: { parentId: string | null; message: ThreadMessage },\n ) {\n super();\n this._parentId = parentId;\n this._sourceId = message.id;\n this._previousText = getThreadMessageText(message);\n this.setText(this._previousText);\n\n this.setRole(message.role);\n\n if (message.role === \"user\") {\n this._previousAttachments = [\n ...(message.attachments ?? []),\n ...liftNonTextParts(message.content),\n ];\n this._nonTextPassthrough = [];\n } else {\n this._previousAttachments = message.attachments ?? [];\n this._nonTextPassthrough = message.content.filter(\n (p) => p.type !== \"text\",\n );\n }\n this.setAttachments(this._previousAttachments);\n\n this.setRunConfig({ ...runtime.composer.runConfig });\n }\n\n public get parentId() {\n return this._parentId;\n }\n\n public get sourceId() {\n return this._sourceId;\n }\n\n public async handleSend(\n message: Omit<AppendMessage, \"parentId\" | \"sourceId\">,\n options?: SendOptions,\n ) {\n const text = getThreadMessageText(message as AppendMessage);\n const attachmentsChanged = !attachmentsEqual(\n message.attachments ?? [],\n this._previousAttachments,\n );\n\n if (\n text !== this._previousText ||\n attachmentsChanged ||\n options?.startRun\n ) {\n const content =\n this._nonTextPassthrough.length > 0\n ? ([\n ...message.content,\n ...this._nonTextPassthrough,\n ] as AppendMessage[\"content\"])\n : message.content;\n // Gate live state against the new branch's prefix (messages up to the\n // parent): an unchanged interactable re-stamps the prior baseline, an\n // interactable edited since the original message stamps its newest state.\n const messages = this.runtime.messages;\n const parentIndex =\n this._parentId === null\n ? -1\n : messages.findIndex((m) => m.id === this._parentId);\n const composerMetadata = gateInteractableComposerMetadata(\n this.runtime.getModelContext().unstable_composerMetadata,\n messages.slice(0, parentIndex + 1),\n );\n const enriched = this.enrichWithComposerMetadata(\n message,\n composerMetadata,\n );\n this.runtime.append({\n ...enriched,\n content,\n parentId: this._parentId,\n sourceId: this._sourceId,\n startRun: options?.startRun,\n });\n }\n\n this.handleCancel();\n }\n\n public handleCancel() {\n this.endEditCallback();\n this._notifySubscribers();\n }\n}\n"],"mappings":";;;;;AAWA,IAAaY,iCAAb,cAAoDF,wBAAwB;CAuBhEO;CAQAW;CA9BV,IAAWf,YAAY;EACrB,OAAO;CACT;CAEA,IAAWC,UAAU;EACnB,OAAO,CAAC,KAAKC;CACf;CAEA,uBAAiC;EAC/B,OAAO,KAAKE,QAAQC,UAAUC;CAChC;CAEA,sBAAgC;EAC9B,OAAO,KAAKF,QAAQC,UAAUG;CAChC;CAEA;CACA;CACA;CACA;CACA;CACAM,YACE,SAQA,iBACA,EAAEE,UAAUC,WACZ;EACA,MAAM;EAXEb,KAAAA,UAAAA;EAQAW,KAAAA,kBAAAA;EAIR,KAAKH,YAAYI;EACjB,KAAKH,YAAYI,QAAQC;EACzB,KAAKT,gBAAgBnB,qBAAqB2B,OAAO;EACjD,KAAKE,QAAQ,KAAKV,aAAa;EAE/B,KAAKW,QAAQH,QAAQI,IAAI;EAEzB,IAAIJ,QAAQI,SAAS,QAAQ;GAC3B,KAAKX,uBAAuB,CAC1B,GAAIO,QAAQX,eAAe,CAAA,GAC3B,GAAGd,iBAAiByB,QAAQK,OAAO,CAAC;GAEtC,KAAKX,sBAAsB,CAAA;EAC7B,OAAO;GACL,KAAKD,uBAAuBO,QAAQX,eAAe,CAAA;GACnD,KAAKK,sBAAsBM,QAAQK,QAAQC,QACxCC,MAAMA,EAAEC,SAAS,MACpB;EACF;EACA,KAAKC,eAAe,KAAKhB,oBAAoB;EAE7C,KAAKiB,aAAa,EAAE,GAAGvB,QAAQwB,SAASC,UAAU,CAAC;CACrD;CAEA,IAAWb,WAAW;EACpB,OAAO,KAAKJ;CACd;CAEA,IAAWkB,WAAW;EACpB,OAAO,KAAKjB;CACd;CAEA,MAAakB,WACXd,SACAgB,SACA;EACA,MAAMC,OAAO5C,qBAAqB2B,OAAwB;EAC1D,MAAMkB,qBAAqB,CAAC5C,iBAC1B0B,QAAQX,eAAe,CAAA,GACvB,KAAKI,oBACP;EAEA,IACEwB,SAAS,KAAKzB,iBACd0B,sBACAF,SAASG,UACT;GACA,MAAMd,UACJ,KAAKX,oBAAoB0B,SAAS,IAC7B,CACC,GAAGpB,QAAQK,SACX,GAAG,KAAKX,mBAAmB,IAE7BM,QAAQK;GAId,MAAMgB,WAAW,KAAKlC,QAAQkC;GAC9B,MAAMC,cACJ,KAAK3B,cAAc,OACf,KACA0B,SAASE,WAAWC,MAAMA,EAAEvB,OAAO,KAAKN,SAAS;GACvD,MAAM8B,mBAAmB5C,iCACvB,KAAKM,QAAQuC,gBAAgB,CAAC,CAACC,2BAC/BN,SAASO,MAAM,GAAGN,cAAc,CAAC,CACnC;GACA,MAAMO,WAAW,KAAKC,2BACpB9B,SACAyB,gBACF;GACA,KAAKtC,QAAQ4C,OAAO;IAClB,GAAGF;IACHxB;IACAN,UAAU,KAAKJ;IACfkB,UAAU,KAAKjB;IACfuB,UAAUH,SAASG;GACrB,CAAC;EACH;EAEA,KAAKa,aAAa;CACpB;CAEA,eAAsB;EACpB,KAAKlC,gBAAgB;EACrB,KAAKmC,mBAAmB;CAC1B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-thread-composer-runtime-core.d.ts","names":[],"sources":["../../../src/runtime/base/default-thread-composer-runtime-core.ts"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"default-thread-composer-runtime-core.d.ts","names":[],"sources":["../../../src/runtime/base/default-thread-composer-runtime-core.ts"],"mappings":";;;;;;;;;cAea,gCAAA,SACH,uBAAA,YACG,yBAAA;EAAA,QAgCD,OAAA;EAAA,QA9BF,UAAA;EAAA,IACG,SAAA;EAAA,IAIA,OAAA;EAAA,IAIS,KAAA,aAAkB,cAAA;EAItB,cAAA,CAAe,WAAA;EAIf,eAAA,CAAgB,WAAA;EAAA,UAItB,oBAAA,IAAoB,iBAAA;EAAA,UAIpB,mBAAA,IAAmB,gBAAA;cAKnB,OAAA,EAAS,IAAA,CAAK,iBAAA;IACpB,QAAA;MAEM,WAAA,GAAc,iBAAA;MACd,SAAA,GAAY,gBAAA;IAAA;EAAA;EASf,OAAA,IAT+B,WAAA;EA8BzB,UAAA,CACX,OAAA,EAAS,IAAA,CAAK,aAAA,4BACd,OAAA,GAAU,WAAA,GAAW,OAAA;EAoBV,YAAA,IAAY,OAAA;AAAA"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { gateInteractableComposerMetadata } from "../../model-context/interactable-composer-metadata.js";
|
|
1
2
|
import { EMPTY_QUEUE_ITEMS } from "../../store/scopes/queue-item.js";
|
|
2
3
|
import { BaseComposerRuntimeCore } from "./base-composer-runtime-core.js";
|
|
3
4
|
//#region src/runtime/base/default-thread-composer-runtime-core.ts
|
|
@@ -51,8 +52,10 @@ var DefaultThreadComposerRuntimeCore = class extends BaseComposerRuntimeCore {
|
|
|
51
52
|
});
|
|
52
53
|
}
|
|
53
54
|
async handleSend(message, options) {
|
|
55
|
+
const composerMetadata = gateInteractableComposerMetadata(this.runtime.getModelContext().unstable_composerMetadata, this.runtime.messages);
|
|
56
|
+
const enriched = this.enrichWithComposerMetadata(message, composerMetadata);
|
|
54
57
|
this.runtime.append({
|
|
55
|
-
...
|
|
58
|
+
...enriched,
|
|
56
59
|
parentId: this.runtime.messages.at(-1)?.id ?? null,
|
|
57
60
|
sourceId: null,
|
|
58
61
|
startRun: options?.startRun,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-thread-composer-runtime-core.js","names":["AppendMessage","AttachmentAdapter","DictationAdapter","SendOptions","ThreadComposerRuntimeCore","ThreadRuntimeCore","EMPTY_QUEUE_ITEMS","QueueItemState","BaseComposerRuntimeCore","DefaultThreadComposerRuntimeCore","_canCancel","canCancel","canSend","isEmpty","runtime","isSendDisabled","queue","getQueueItems","steerQueueItem","queueItemId","removeQueueItem","getAttachmentAdapter","adapters","attachments","getDictationAdapter","dictation","constructor","Omit","connect","lastIsSendDisabled","lastQueue","subscribe","changed","capabilities","cancel","_notifySubscribers","handleSend","message","options","
|
|
1
|
+
{"version":3,"file":"default-thread-composer-runtime-core.js","names":["AppendMessage","AttachmentAdapter","DictationAdapter","SendOptions","ThreadComposerRuntimeCore","ThreadRuntimeCore","EMPTY_QUEUE_ITEMS","QueueItemState","BaseComposerRuntimeCore","gateInteractableComposerMetadata","DefaultThreadComposerRuntimeCore","_canCancel","canCancel","canSend","isEmpty","runtime","isSendDisabled","queue","getQueueItems","steerQueueItem","queueItemId","removeQueueItem","getAttachmentAdapter","adapters","attachments","getDictationAdapter","dictation","constructor","Omit","connect","lastIsSendDisabled","lastQueue","subscribe","changed","capabilities","cancel","_notifySubscribers","handleSend","message","options","composerMetadata","getModelContext","unstable_composerMetadata","messages","enriched","enrichWithComposerMetadata","append","parentId","at","id","sourceId","startRun","steer","handleCancel","cancelRun"],"sources":["../../../src/runtime/base/default-thread-composer-runtime-core.ts"],"sourcesContent":["import type { AppendMessage } from \"../../types/message\";\nimport type { AttachmentAdapter } from \"../../adapters/attachment\";\nimport type { DictationAdapter } from \"../../adapters/speech\";\nimport type {\n SendOptions,\n ThreadComposerRuntimeCore,\n} from \"../interfaces/composer-runtime-core\";\nimport type { ThreadRuntimeCore } from \"../interfaces/thread-runtime-core\";\nimport {\n EMPTY_QUEUE_ITEMS,\n type QueueItemState,\n} from \"../../store/scopes/queue-item\";\nimport { BaseComposerRuntimeCore } from \"./base-composer-runtime-core\";\nimport { gateInteractableComposerMetadata } from \"../../model-context/interactable-composer-metadata\";\n\nexport class DefaultThreadComposerRuntimeCore\n extends BaseComposerRuntimeCore\n implements ThreadComposerRuntimeCore\n{\n private _canCancel = false;\n public get canCancel() {\n return this._canCancel;\n }\n\n public get canSend() {\n return !this.isEmpty && !this.runtime.isSendDisabled;\n }\n\n public override get queue(): readonly QueueItemState[] {\n return this.runtime.getQueueItems?.() ?? EMPTY_QUEUE_ITEMS;\n }\n\n public override steerQueueItem(queueItemId: string): void {\n this.runtime.steerQueueItem?.(queueItemId);\n }\n\n public override removeQueueItem(queueItemId: string): void {\n this.runtime.removeQueueItem?.(queueItemId);\n }\n\n protected getAttachmentAdapter() {\n return this.runtime.adapters?.attachments;\n }\n\n protected getDictationAdapter() {\n return this.runtime.adapters?.dictation;\n }\n\n constructor(\n private runtime: Omit<ThreadRuntimeCore, \"composer\"> & {\n adapters?:\n | {\n attachments?: AttachmentAdapter | undefined;\n dictation?: DictationAdapter | undefined;\n }\n | undefined;\n },\n ) {\n super();\n this.connect();\n }\n\n public connect() {\n let lastIsSendDisabled = this.runtime.isSendDisabled;\n let lastQueue = this.queue;\n return this.runtime.subscribe(() => {\n let changed = false;\n if (this.canCancel !== this.runtime.capabilities.cancel) {\n this._canCancel = this.runtime.capabilities.cancel;\n changed = true;\n }\n if (lastIsSendDisabled !== this.runtime.isSendDisabled) {\n lastIsSendDisabled = this.runtime.isSendDisabled;\n changed = true;\n }\n if (lastQueue !== this.queue) {\n lastQueue = this.queue;\n changed = true;\n }\n if (changed) this._notifySubscribers();\n });\n }\n\n public async handleSend(\n message: Omit<AppendMessage, \"parentId\" | \"sourceId\">,\n options?: SendOptions,\n ) {\n // Merge provider-contributed metadata onto the outgoing user message\n // (same metadata.custom append path quotes ride). The interactables gate\n // runs here because it needs thread history, unavailable to the provider.\n const composerMetadata = gateInteractableComposerMetadata(\n this.runtime.getModelContext().unstable_composerMetadata,\n this.runtime.messages,\n );\n const enriched = this.enrichWithComposerMetadata(message, composerMetadata);\n\n this.runtime.append({\n ...(enriched as AppendMessage),\n parentId: this.runtime.messages.at(-1)?.id ?? null,\n sourceId: null,\n startRun: options?.startRun,\n steer: options?.steer,\n });\n }\n\n public async handleCancel() {\n this.runtime.cancelRun();\n }\n}\n"],"mappings":";;;;AAeA,IAAaU,mCAAb,cACUF,wBAEV;CA+BYO;CA9BV,aAAqB;CACrB,IAAWH,YAAY;EACrB,OAAO,KAAKD;CACd;CAEA,IAAWE,UAAU;EACnB,OAAO,CAAC,KAAKC,WAAW,CAAC,KAAKC,QAAQC;CACxC;CAEA,IAAoBC,QAAmC;EACrD,OAAO,KAAKF,QAAQG,gBAAgB,KAAKZ;CAC3C;CAEA,eAA+Bc,aAA2B;EACxD,KAAKL,QAAQI,iBAAiBC,WAAW;CAC3C;CAEA,gBAAgCA,aAA2B;EACzD,KAAKL,QAAQM,kBAAkBD,WAAW;CAC5C;CAEA,uBAAiC;EAC/B,OAAO,KAAKL,QAAQQ,UAAUC;CAChC;CAEA,sBAAgC;EAC9B,OAAO,KAAKT,QAAQQ,UAAUG;CAChC;CAEAC,YACE,SAQA;EACA,MAAM;EATEZ,KAAAA,UAAAA;EAUR,KAAKc,QAAQ;CACf;CAEA,UAAiB;EACf,IAAIC,qBAAqB,KAAKf,QAAQC;EACtC,IAAIe,YAAY,KAAKd;EACrB,OAAO,KAAKF,QAAQiB,gBAAgB;GAClC,IAAIC,UAAU;GACd,IAAI,KAAKrB,cAAc,KAAKG,QAAQmB,aAAaC,QAAQ;IACvD,KAAKxB,aAAa,KAAKI,QAAQmB,aAAaC;IAC5CF,UAAU;GACZ;GACA,IAAIH,uBAAuB,KAAKf,QAAQC,gBAAgB;IACtDc,qBAAqB,KAAKf,QAAQC;IAClCiB,UAAU;GACZ;GACA,IAAIF,cAAc,KAAKd,OAAO;IAC5Bc,YAAY,KAAKd;IACjBgB,UAAU;GACZ;GACA,IAAIA,SAAS,KAAKG,mBAAmB;EACvC,CAAC;CACH;CAEA,MAAaC,WACXC,SACAC,SACA;EAIA,MAAMC,mBAAmB/B,iCACvB,KAAKM,QAAQ0B,gBAAgB,CAAC,CAACC,2BAC/B,KAAK3B,QAAQ4B,QACf;EACA,MAAMC,WAAW,KAAKC,2BAA2BP,SAASE,gBAAgB;EAE1E,KAAKzB,QAAQ+B,OAAO;GAClB,GAAIF;GACJG,UAAU,KAAKhC,QAAQ4B,SAASK,GAAG,EAAE,CAAC,EAAEC,MAAM;GAC9CC,UAAU;GACVC,UAAUZ,SAASY;GACnBC,OAAOb,SAASa;EAClB,CAAC;CACH;CAEA,MAAaC,eAAe;EAC1B,KAAKtC,QAAQuC,UAAU;CACzB;AACF"}
|
|
@@ -32,6 +32,7 @@ declare class MessageRepository {
|
|
|
32
32
|
private performOp;
|
|
33
33
|
private _messages;
|
|
34
34
|
get headId(): string | null;
|
|
35
|
+
get canonicalHeadId(): string | null;
|
|
35
36
|
getMessages(headId?: string): readonly ThreadMessage[];
|
|
36
37
|
addOrUpdateMessage(parentId: string | null, message: ThreadMessage): void;
|
|
37
38
|
getMessage(messageId: string): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-repository.d.ts","names":[],"sources":["../../../src/runtime/utils/message-repository.ts"],"mappings":";;;;KAOY,6BAAA;EACV,OAAA,EAAS,aAAA;EACT,QAAA;EACA,SAAA,GAAY,SAAS;AAAA;AAAA,KAGX,yBAAA;EACV,MAAA;EACA,QAAA,EAAU,KAAA;IACR,OAAA,EAAS,aAAA;IACT,QAAA;IACA,SAAA,GAAY,SAAA;EAAA;AAAA;AAAA,cAIH,yBAAA;iCAEU,iBAAA,OAClB,yBAAA;;IAmBC,OAAA,EAAS,iBAAA;IACT,QAAA;EAAA,KACC,OAAA;IACS,MAAA;EAAA,MACX,yBAAA;AAAA;AAAA,cAyDQ,iBAAA;EAAA,QACH,QAAA;EAAA,QACA,IAAA;EAAA,QACA,IAAA;EAAA,QAKA,YAAA;EAAA,QAUA,SAAA;EAAA,QAwDA,SAAA;EAAA,IAQJ,MAAA;
|
|
1
|
+
{"version":3,"file":"message-repository.d.ts","names":[],"sources":["../../../src/runtime/utils/message-repository.ts"],"mappings":";;;;KAOY,6BAAA;EACV,OAAA,EAAS,aAAA;EACT,QAAA;EACA,SAAA,GAAY,SAAS;AAAA;AAAA,KAGX,yBAAA;EACV,MAAA;EACA,QAAA,EAAU,KAAA;IACR,OAAA,EAAS,aAAA;IACT,QAAA;IACA,SAAA,GAAY,SAAA;EAAA;AAAA;AAAA,cAIH,yBAAA;iCAEU,iBAAA,OAClB,yBAAA;;IAmBC,OAAA,EAAS,iBAAA;IACT,QAAA;EAAA,KACC,OAAA;IACS,MAAA;EAAA,MACX,yBAAA;AAAA;AAAA,cAyDQ,iBAAA;EAAA,QACH,QAAA;EAAA,QACA,IAAA;EAAA,QACA,IAAA;EAAA,QAKA,YAAA;EAAA,QAUA,SAAA;EAAA,QAwDA,SAAA;EAAA,IAQJ,MAAA;EAAA,IAIA,eAAA;EAUJ,WAAA,CAAY,MAAA,qBAAe,aAAA;EAuB3B,kBAAA,CAAmB,QAAA,iBAAyB,OAAA,EAAS,aAAA;EAiCrD,UAAA,CAAW,SAAA;;;;;EAcX,aAAA,CAAc,SAAA,UAAmB,aAAA;EAsCjC,WAAA,CAAY,SAAA;EA7RS;;;;;;;;EAAA,QAgTb,gCAAA;EA+BR,cAAA,CAAe,SAAA;EAkBf,SAAA,CAAU,SAAA;EAgDV,KAAA;EAUA,MAAA,IAAU,yBAAA;EAmBV,MAAA;IAAS,MAAA;IAAQ;EAAA,GAAY,yBAAA;AAAA"}
|
|
@@ -87,6 +87,11 @@ var MessageRepository = class {
|
|
|
87
87
|
get headId() {
|
|
88
88
|
return this.head?.current.id ?? null;
|
|
89
89
|
}
|
|
90
|
+
get canonicalHeadId() {
|
|
91
|
+
let head = this.head;
|
|
92
|
+
while (head?.current.metadata?.isOptimistic) head = head.prev;
|
|
93
|
+
return head?.current.id ?? null;
|
|
94
|
+
}
|
|
90
95
|
getMessages(headId) {
|
|
91
96
|
if (headId === void 0 || headId === this.head?.current.id) return this._messages.value;
|
|
92
97
|
const headMessage = this.messages.get(headId);
|
|
@@ -222,10 +227,8 @@ var MessageRepository = class {
|
|
|
222
227
|
parentId: message.prev?.current.id ?? null
|
|
223
228
|
});
|
|
224
229
|
}
|
|
225
|
-
let head = this.head;
|
|
226
|
-
while (head?.current.metadata?.isOptimistic) head = head.prev;
|
|
227
230
|
return {
|
|
228
|
-
headId:
|
|
231
|
+
headId: this.canonicalHeadId,
|
|
229
232
|
messages: exportItems
|
|
230
233
|
};
|
|
231
234
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-repository.js","names":["ThreadMessage","RunConfig","generateId","ThreadMessageLike","getAutoStatus","fromThreadMessageLike","ExportedMessageRepositoryItem","message","parentId","runConfig","ExportedMessageRepository","headId","messages","Array","fromArray","conv","map","m","undefined","idx","id","fromBranchableArray","items","options","fallbackStatus","Error","RepositoryParent","children","next","RepositoryMessage","prev","current","level","findHead","CachedValue","_value","T","constructor","func","value","dirty","MessageRepository","Map","head","root","updateLevels","newLevel","childId","childMessage","get","performOp","newParent","child","operation","parentOrRoot","newParentOrRoot","filter","fallbackId","at","fallback","_messages","getMessages","headMessage","addOrUpdateMessage","existingItem","newItem","set","getMessage","messageId","index","deleteMessage","replacementId","replacement","delete","getBranches","evictOffBranchOptimisticMessages","previousHead","currentHead","onHeadBranch","Set","add","stale","has","metadata","isOptimistic","push","switchToBranch","prevOrRoot","resetHead","clear","length","deleteDescendants","msg","export","exportItems","import"],"sources":["../../../src/runtime/utils/message-repository.ts"],"sourcesContent":["import type { ThreadMessage } from \"../../types/message\";\nimport type { RunConfig } from \"../../types/message\";\nimport { generateId } from \"../../utils/id\";\nimport type { ThreadMessageLike } from \"./thread-message-like\";\nimport { getAutoStatus } from \"./auto-status\";\nimport { fromThreadMessageLike } from \"./thread-message-like\";\n\nexport type ExportedMessageRepositoryItem = {\n message: ThreadMessage;\n parentId: string | null;\n runConfig?: RunConfig;\n};\n\nexport type ExportedMessageRepository = {\n headId?: string | null;\n messages: Array<{\n message: ThreadMessage;\n parentId: string | null;\n runConfig?: RunConfig;\n }>;\n};\n\nexport const ExportedMessageRepository = {\n fromArray: (\n messages: readonly ThreadMessageLike[],\n ): ExportedMessageRepository => {\n const conv = messages.map((m) =>\n fromThreadMessageLike(\n m,\n generateId(),\n getAutoStatus(false, false, false, false, undefined),\n ),\n );\n\n return {\n messages: conv.map((m, idx) => ({\n parentId: idx > 0 ? conv[idx - 1]!.id : null,\n message: m,\n })),\n };\n },\n\n fromBranchableArray: (\n items: readonly {\n message: ThreadMessageLike;\n parentId: string | null;\n }[],\n options?: { headId?: string | null },\n ): ExportedMessageRepository => {\n const fallbackStatus = getAutoStatus(false, false, false, false, undefined);\n return {\n ...(options?.headId !== undefined\n ? { headId: options.headId }\n : undefined),\n messages: items.map(({ message, parentId }) => {\n if (!message.id) {\n throw new Error(\n \"ExportedMessageRepository.fromBranchableArray: Each message must have an 'id' field set.\",\n );\n }\n return {\n parentId,\n message: fromThreadMessageLike(message, message.id, fallbackStatus),\n };\n }),\n };\n },\n};\n\ntype RepositoryParent = {\n children: string[];\n next: RepositoryMessage | null;\n};\n\ntype RepositoryMessage = RepositoryParent & {\n prev: RepositoryMessage | null;\n current: ThreadMessage;\n level: number;\n};\n\nconst findHead = (\n message: RepositoryMessage | RepositoryParent,\n): RepositoryMessage | null => {\n if (message.next) return findHead(message.next);\n if (\"current\" in message) return message;\n return null;\n};\n\nclass CachedValue<T> {\n private _value: T | null = null;\n\n constructor(private func: () => T) {}\n\n get value() {\n if (this._value === null) {\n this._value = this.func();\n }\n return this._value;\n }\n\n dirty() {\n this._value = null;\n }\n}\n\nexport class MessageRepository {\n private messages = new Map<string, RepositoryMessage>();\n private head: RepositoryMessage | null = null;\n private root: RepositoryParent = {\n children: [],\n next: null,\n };\n\n private updateLevels(message: RepositoryMessage, newLevel: number) {\n message.level = newLevel;\n for (const childId of message.children) {\n const childMessage = this.messages.get(childId);\n if (childMessage) {\n this.updateLevels(childMessage, newLevel + 1);\n }\n }\n }\n\n private performOp(\n newParent: RepositoryMessage | null,\n child: RepositoryMessage,\n operation: \"cut\" | \"link\" | \"relink\",\n ) {\n const parentOrRoot = child.prev ?? this.root;\n const newParentOrRoot = newParent ?? this.root;\n\n if (operation === \"relink\" && parentOrRoot === newParentOrRoot) return;\n\n if (operation !== \"link\") {\n parentOrRoot.children = parentOrRoot.children.filter(\n (m) => m !== child.current.id,\n );\n\n if (parentOrRoot.next === child) {\n const fallbackId = parentOrRoot.children.at(-1);\n const fallback = fallbackId ? this.messages.get(fallbackId) : null;\n if (fallback === undefined) {\n throw new Error(\n \"MessageRepository(performOp/cut): Fallback sibling message not found. This is likely an internal bug in assistant-ui.\",\n );\n }\n parentOrRoot.next = fallback;\n }\n }\n\n if (operation !== \"cut\") {\n for (\n let current: RepositoryMessage | null = newParent;\n current;\n current = current.prev\n ) {\n if (current.current.id === child.current.id) {\n throw new Error(\n \"MessageRepository(performOp/link): A message with the same id already exists in the parent tree. This error occurs if the same message id is found multiple times. This is likely an internal bug in assistant-ui.\",\n );\n }\n }\n\n newParentOrRoot.children = [\n ...newParentOrRoot.children,\n child.current.id,\n ];\n\n if (findHead(child) === this.head || newParentOrRoot.next === null) {\n newParentOrRoot.next = child;\n }\n\n child.prev = newParent;\n\n const newLevel = newParent ? newParent.level + 1 : 0;\n this.updateLevels(child, newLevel);\n }\n }\n\n private _messages = new CachedValue<readonly ThreadMessage[]>(() => {\n const messages = new Array<ThreadMessage>((this.head?.level ?? -1) + 1);\n for (let current = this.head; current; current = current.prev) {\n messages[current.level] = current.current;\n }\n return messages;\n });\n\n get headId() {\n return this.head?.current.id ?? null;\n }\n\n getMessages(headId?: string) {\n if (headId === undefined || headId === this.head?.current.id) {\n return this._messages.value;\n }\n\n const headMessage = this.messages.get(headId);\n if (!headMessage) {\n throw new Error(\n \"MessageRepository(getMessages): Head message not found. This is likely an internal bug in assistant-ui.\",\n );\n }\n\n const messages = new Array<ThreadMessage>(headMessage.level + 1);\n for (\n let current: RepositoryMessage | null = headMessage;\n current;\n current = current.prev\n ) {\n messages[current.level] = current.current;\n }\n return messages;\n }\n\n addOrUpdateMessage(parentId: string | null, message: ThreadMessage) {\n const existingItem = this.messages.get(message.id);\n const prev = parentId ? this.messages.get(parentId) : null;\n if (prev === undefined)\n throw new Error(\n \"MessageRepository(addOrUpdateMessage): Parent message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n if (existingItem) {\n existingItem.current = message;\n this.performOp(prev, existingItem, \"relink\");\n this._messages.dirty();\n return;\n }\n\n const newItem: RepositoryMessage = {\n prev,\n current: message,\n next: null,\n children: [],\n level: prev ? prev.level + 1 : 0,\n };\n\n this.messages.set(message.id, newItem);\n this.performOp(prev, newItem, \"link\");\n\n if (this.head === prev) {\n this.head = newItem;\n }\n\n this._messages.dirty();\n }\n\n getMessage(messageId: string) {\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(updateMessage): Message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n return {\n parentId: message.prev?.current.id ?? null,\n message: message.current,\n index: message.level,\n };\n }\n\n deleteMessage(messageId: string, replacementId?: string | null | undefined) {\n const message = this.messages.get(messageId);\n\n if (!message)\n throw new Error(\n \"MessageRepository(deleteMessage): Message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const replacement =\n replacementId === undefined\n ? message.prev\n : replacementId === null\n ? null\n : this.messages.get(replacementId);\n if (replacement === undefined)\n throw new Error(\n \"MessageRepository(deleteMessage): Replacement not found. This is likely an internal bug in assistant-ui.\",\n );\n\n for (const child of message.children) {\n const childMessage = this.messages.get(child);\n if (!childMessage)\n throw new Error(\n \"MessageRepository(deleteMessage): Child message not found. This is likely an internal bug in assistant-ui.\",\n );\n this.performOp(replacement, childMessage, \"relink\");\n }\n\n this.performOp(null, message, \"cut\");\n this.messages.delete(messageId);\n\n if (this.head === message) {\n this.head = findHead(replacement ?? this.root);\n }\n\n this._messages.dirty();\n }\n\n getBranches(messageId: string) {\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(getBranches): Message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const { children } = message.prev ?? this.root;\n return children;\n }\n\n /**\n * Evicts optimistic messages (`metadata.isOptimistic`) the head just moved\n * away from. Since eviction runs on every head move, the only optimistic\n * messages in the repository live on the branch the head previously pointed\n * at — so we walk just that branch rather than the whole repository. Keeps a\n * client→server id swap from leaving a phantom sibling, and drops off-branch\n * placeholders.\n */\n private evictOffBranchOptimisticMessages(\n previousHead: RepositoryMessage | null,\n currentHead: RepositoryMessage | null,\n ) {\n if (!previousHead) return;\n\n const onHeadBranch = new Set<string>();\n for (let current = currentHead; current; current = current.prev) {\n onHeadBranch.add(current.current.id);\n }\n\n const stale: string[] = [];\n for (\n let current: RepositoryMessage | null = previousHead;\n current;\n current = current.prev\n ) {\n // Stop at the first node shared with the current head branch: every\n // ancestor above it is shared too, so nothing further can be off-branch.\n if (onHeadBranch.has(current.current.id)) break;\n if (current.current.metadata?.isOptimistic) {\n stale.push(current.current.id);\n }\n }\n\n for (const id of stale) {\n // A prior deletion may have already removed this node.\n if (this.messages.has(id)) this.deleteMessage(id);\n }\n }\n\n switchToBranch(messageId: string) {\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(switchToBranch): Branch not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const previousHead = this.head;\n const prevOrRoot = message.prev ?? this.root;\n prevOrRoot.next = message;\n\n this.head = findHead(message);\n\n this.evictOffBranchOptimisticMessages(previousHead, this.head);\n\n this._messages.dirty();\n }\n\n resetHead(messageId: string | null) {\n if (messageId === null) {\n this.clear();\n return;\n }\n\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(resetHead): Branch not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const previousHead = this.head;\n\n if (message.children.length > 0) {\n const deleteDescendants = (msg: RepositoryMessage) => {\n for (const childId of msg.children) {\n const childMessage = this.messages.get(childId);\n if (childMessage) {\n deleteDescendants(childMessage);\n this.messages.delete(childId);\n }\n }\n };\n deleteDescendants(message);\n\n message.children = [];\n message.next = null;\n }\n\n this.head = message;\n for (\n let current: RepositoryMessage | null = message;\n current;\n current = current.prev\n ) {\n if (current.prev) {\n current.prev.next = current;\n } else {\n this.root.next = current;\n }\n }\n\n this.evictOffBranchOptimisticMessages(previousHead, this.head);\n\n this._messages.dirty();\n }\n\n clear(): void {\n this.messages.clear();\n this.head = null;\n this.root = {\n children: [],\n next: null,\n };\n this._messages.dirty();\n }\n\n export(): ExportedMessageRepository {\n const exportItems: ExportedMessageRepository[\"messages\"] = [];\n\n // Optimistic messages are ephemeral and never persisted. They're always\n // leaf nodes, so skipping them can't orphan a persisted child.\n for (const [, message] of this.messages) {\n if (message.current.metadata?.isOptimistic) continue;\n exportItems.push({\n message: message.current,\n parentId: message.prev?.current.id ?? null,\n });\n }\n\n // The head may itself be optimistic; walk up to the nearest persisted ancestor.\n let head = this.head;\n while (head?.current.metadata?.isOptimistic) {\n head = head.prev;\n }\n\n return {\n headId: head?.current.id ?? null,\n messages: exportItems,\n };\n }\n\n import({ headId, messages }: ExportedMessageRepository) {\n for (const { message, parentId } of messages) {\n this.addOrUpdateMessage(parentId, message);\n }\n\n this.resetHead(headId ?? messages.at(-1)?.message.id ?? null);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"message-repository.js","names":["ThreadMessage","RunConfig","generateId","ThreadMessageLike","getAutoStatus","fromThreadMessageLike","ExportedMessageRepositoryItem","message","parentId","runConfig","ExportedMessageRepository","headId","messages","Array","fromArray","conv","map","m","undefined","idx","id","fromBranchableArray","items","options","fallbackStatus","Error","RepositoryParent","children","next","RepositoryMessage","prev","current","level","findHead","CachedValue","_value","T","constructor","func","value","dirty","MessageRepository","Map","head","root","updateLevels","newLevel","childId","childMessage","get","performOp","newParent","child","operation","parentOrRoot","newParentOrRoot","filter","fallbackId","at","fallback","_messages","canonicalHeadId","metadata","isOptimistic","getMessages","headMessage","addOrUpdateMessage","existingItem","newItem","set","getMessage","messageId","index","deleteMessage","replacementId","replacement","delete","getBranches","evictOffBranchOptimisticMessages","previousHead","currentHead","onHeadBranch","Set","add","stale","has","push","switchToBranch","prevOrRoot","resetHead","clear","length","deleteDescendants","msg","export","exportItems","import"],"sources":["../../../src/runtime/utils/message-repository.ts"],"sourcesContent":["import type { ThreadMessage } from \"../../types/message\";\nimport type { RunConfig } from \"../../types/message\";\nimport { generateId } from \"../../utils/id\";\nimport type { ThreadMessageLike } from \"./thread-message-like\";\nimport { getAutoStatus } from \"./auto-status\";\nimport { fromThreadMessageLike } from \"./thread-message-like\";\n\nexport type ExportedMessageRepositoryItem = {\n message: ThreadMessage;\n parentId: string | null;\n runConfig?: RunConfig;\n};\n\nexport type ExportedMessageRepository = {\n headId?: string | null;\n messages: Array<{\n message: ThreadMessage;\n parentId: string | null;\n runConfig?: RunConfig;\n }>;\n};\n\nexport const ExportedMessageRepository = {\n fromArray: (\n messages: readonly ThreadMessageLike[],\n ): ExportedMessageRepository => {\n const conv = messages.map((m) =>\n fromThreadMessageLike(\n m,\n generateId(),\n getAutoStatus(false, false, false, false, undefined),\n ),\n );\n\n return {\n messages: conv.map((m, idx) => ({\n parentId: idx > 0 ? conv[idx - 1]!.id : null,\n message: m,\n })),\n };\n },\n\n fromBranchableArray: (\n items: readonly {\n message: ThreadMessageLike;\n parentId: string | null;\n }[],\n options?: { headId?: string | null },\n ): ExportedMessageRepository => {\n const fallbackStatus = getAutoStatus(false, false, false, false, undefined);\n return {\n ...(options?.headId !== undefined\n ? { headId: options.headId }\n : undefined),\n messages: items.map(({ message, parentId }) => {\n if (!message.id) {\n throw new Error(\n \"ExportedMessageRepository.fromBranchableArray: Each message must have an 'id' field set.\",\n );\n }\n return {\n parentId,\n message: fromThreadMessageLike(message, message.id, fallbackStatus),\n };\n }),\n };\n },\n};\n\ntype RepositoryParent = {\n children: string[];\n next: RepositoryMessage | null;\n};\n\ntype RepositoryMessage = RepositoryParent & {\n prev: RepositoryMessage | null;\n current: ThreadMessage;\n level: number;\n};\n\nconst findHead = (\n message: RepositoryMessage | RepositoryParent,\n): RepositoryMessage | null => {\n if (message.next) return findHead(message.next);\n if (\"current\" in message) return message;\n return null;\n};\n\nclass CachedValue<T> {\n private _value: T | null = null;\n\n constructor(private func: () => T) {}\n\n get value() {\n if (this._value === null) {\n this._value = this.func();\n }\n return this._value;\n }\n\n dirty() {\n this._value = null;\n }\n}\n\nexport class MessageRepository {\n private messages = new Map<string, RepositoryMessage>();\n private head: RepositoryMessage | null = null;\n private root: RepositoryParent = {\n children: [],\n next: null,\n };\n\n private updateLevels(message: RepositoryMessage, newLevel: number) {\n message.level = newLevel;\n for (const childId of message.children) {\n const childMessage = this.messages.get(childId);\n if (childMessage) {\n this.updateLevels(childMessage, newLevel + 1);\n }\n }\n }\n\n private performOp(\n newParent: RepositoryMessage | null,\n child: RepositoryMessage,\n operation: \"cut\" | \"link\" | \"relink\",\n ) {\n const parentOrRoot = child.prev ?? this.root;\n const newParentOrRoot = newParent ?? this.root;\n\n if (operation === \"relink\" && parentOrRoot === newParentOrRoot) return;\n\n if (operation !== \"link\") {\n parentOrRoot.children = parentOrRoot.children.filter(\n (m) => m !== child.current.id,\n );\n\n if (parentOrRoot.next === child) {\n const fallbackId = parentOrRoot.children.at(-1);\n const fallback = fallbackId ? this.messages.get(fallbackId) : null;\n if (fallback === undefined) {\n throw new Error(\n \"MessageRepository(performOp/cut): Fallback sibling message not found. This is likely an internal bug in assistant-ui.\",\n );\n }\n parentOrRoot.next = fallback;\n }\n }\n\n if (operation !== \"cut\") {\n for (\n let current: RepositoryMessage | null = newParent;\n current;\n current = current.prev\n ) {\n if (current.current.id === child.current.id) {\n throw new Error(\n \"MessageRepository(performOp/link): A message with the same id already exists in the parent tree. This error occurs if the same message id is found multiple times. This is likely an internal bug in assistant-ui.\",\n );\n }\n }\n\n newParentOrRoot.children = [\n ...newParentOrRoot.children,\n child.current.id,\n ];\n\n if (findHead(child) === this.head || newParentOrRoot.next === null) {\n newParentOrRoot.next = child;\n }\n\n child.prev = newParent;\n\n const newLevel = newParent ? newParent.level + 1 : 0;\n this.updateLevels(child, newLevel);\n }\n }\n\n private _messages = new CachedValue<readonly ThreadMessage[]>(() => {\n const messages = new Array<ThreadMessage>((this.head?.level ?? -1) + 1);\n for (let current = this.head; current; current = current.prev) {\n messages[current.level] = current.current;\n }\n return messages;\n });\n\n get headId() {\n return this.head?.current.id ?? null;\n }\n\n get canonicalHeadId() {\n // Optimistic messages are ephemeral, so persisted callers need the nearest\n // non-optimistic ancestor rather than the raw head.\n let head = this.head;\n while (head?.current.metadata?.isOptimistic) {\n head = head.prev;\n }\n return head?.current.id ?? null;\n }\n\n getMessages(headId?: string) {\n if (headId === undefined || headId === this.head?.current.id) {\n return this._messages.value;\n }\n\n const headMessage = this.messages.get(headId);\n if (!headMessage) {\n throw new Error(\n \"MessageRepository(getMessages): Head message not found. This is likely an internal bug in assistant-ui.\",\n );\n }\n\n const messages = new Array<ThreadMessage>(headMessage.level + 1);\n for (\n let current: RepositoryMessage | null = headMessage;\n current;\n current = current.prev\n ) {\n messages[current.level] = current.current;\n }\n return messages;\n }\n\n addOrUpdateMessage(parentId: string | null, message: ThreadMessage) {\n const existingItem = this.messages.get(message.id);\n const prev = parentId ? this.messages.get(parentId) : null;\n if (prev === undefined)\n throw new Error(\n \"MessageRepository(addOrUpdateMessage): Parent message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n if (existingItem) {\n existingItem.current = message;\n this.performOp(prev, existingItem, \"relink\");\n this._messages.dirty();\n return;\n }\n\n const newItem: RepositoryMessage = {\n prev,\n current: message,\n next: null,\n children: [],\n level: prev ? prev.level + 1 : 0,\n };\n\n this.messages.set(message.id, newItem);\n this.performOp(prev, newItem, \"link\");\n\n if (this.head === prev) {\n this.head = newItem;\n }\n\n this._messages.dirty();\n }\n\n getMessage(messageId: string) {\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(updateMessage): Message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n return {\n parentId: message.prev?.current.id ?? null,\n message: message.current,\n index: message.level,\n };\n }\n\n deleteMessage(messageId: string, replacementId?: string | null | undefined) {\n const message = this.messages.get(messageId);\n\n if (!message)\n throw new Error(\n \"MessageRepository(deleteMessage): Message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const replacement =\n replacementId === undefined\n ? message.prev\n : replacementId === null\n ? null\n : this.messages.get(replacementId);\n if (replacement === undefined)\n throw new Error(\n \"MessageRepository(deleteMessage): Replacement not found. This is likely an internal bug in assistant-ui.\",\n );\n\n for (const child of message.children) {\n const childMessage = this.messages.get(child);\n if (!childMessage)\n throw new Error(\n \"MessageRepository(deleteMessage): Child message not found. This is likely an internal bug in assistant-ui.\",\n );\n this.performOp(replacement, childMessage, \"relink\");\n }\n\n this.performOp(null, message, \"cut\");\n this.messages.delete(messageId);\n\n if (this.head === message) {\n this.head = findHead(replacement ?? this.root);\n }\n\n this._messages.dirty();\n }\n\n getBranches(messageId: string) {\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(getBranches): Message not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const { children } = message.prev ?? this.root;\n return children;\n }\n\n /**\n * Evicts optimistic messages (`metadata.isOptimistic`) the head just moved\n * away from. Since eviction runs on every head move, the only optimistic\n * messages in the repository live on the branch the head previously pointed\n * at — so we walk just that branch rather than the whole repository. Keeps a\n * client→server id swap from leaving a phantom sibling, and drops off-branch\n * placeholders.\n */\n private evictOffBranchOptimisticMessages(\n previousHead: RepositoryMessage | null,\n currentHead: RepositoryMessage | null,\n ) {\n if (!previousHead) return;\n\n const onHeadBranch = new Set<string>();\n for (let current = currentHead; current; current = current.prev) {\n onHeadBranch.add(current.current.id);\n }\n\n const stale: string[] = [];\n for (\n let current: RepositoryMessage | null = previousHead;\n current;\n current = current.prev\n ) {\n // Stop at the first node shared with the current head branch: every\n // ancestor above it is shared too, so nothing further can be off-branch.\n if (onHeadBranch.has(current.current.id)) break;\n if (current.current.metadata?.isOptimistic) {\n stale.push(current.current.id);\n }\n }\n\n for (const id of stale) {\n // A prior deletion may have already removed this node.\n if (this.messages.has(id)) this.deleteMessage(id);\n }\n }\n\n switchToBranch(messageId: string) {\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(switchToBranch): Branch not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const previousHead = this.head;\n const prevOrRoot = message.prev ?? this.root;\n prevOrRoot.next = message;\n\n this.head = findHead(message);\n\n this.evictOffBranchOptimisticMessages(previousHead, this.head);\n\n this._messages.dirty();\n }\n\n resetHead(messageId: string | null) {\n if (messageId === null) {\n this.clear();\n return;\n }\n\n const message = this.messages.get(messageId);\n if (!message)\n throw new Error(\n \"MessageRepository(resetHead): Branch not found. This is likely an internal bug in assistant-ui.\",\n );\n\n const previousHead = this.head;\n\n if (message.children.length > 0) {\n const deleteDescendants = (msg: RepositoryMessage) => {\n for (const childId of msg.children) {\n const childMessage = this.messages.get(childId);\n if (childMessage) {\n deleteDescendants(childMessage);\n this.messages.delete(childId);\n }\n }\n };\n deleteDescendants(message);\n\n message.children = [];\n message.next = null;\n }\n\n this.head = message;\n for (\n let current: RepositoryMessage | null = message;\n current;\n current = current.prev\n ) {\n if (current.prev) {\n current.prev.next = current;\n } else {\n this.root.next = current;\n }\n }\n\n this.evictOffBranchOptimisticMessages(previousHead, this.head);\n\n this._messages.dirty();\n }\n\n clear(): void {\n this.messages.clear();\n this.head = null;\n this.root = {\n children: [],\n next: null,\n };\n this._messages.dirty();\n }\n\n export(): ExportedMessageRepository {\n const exportItems: ExportedMessageRepository[\"messages\"] = [];\n\n // Optimistic messages are ephemeral and never persisted. They're always\n // leaf nodes, so skipping them can't orphan a persisted child.\n for (const [, message] of this.messages) {\n if (message.current.metadata?.isOptimistic) continue;\n exportItems.push({\n message: message.current,\n parentId: message.prev?.current.id ?? null,\n });\n }\n\n return {\n headId: this.canonicalHeadId,\n messages: exportItems,\n };\n }\n\n import({ headId, messages }: ExportedMessageRepository) {\n for (const { message, parentId } of messages) {\n this.addOrUpdateMessage(parentId, message);\n }\n\n this.resetHead(headId ?? messages.at(-1)?.message.id ?? null);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { MessageTiming } from "../../types/message.js";
|
|
2
|
+
|
|
3
|
+
//#region src/runtime/utils/streaming-timing.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Shape-specific accessors that adapt {@link stepStreamingTiming} /
|
|
6
|
+
* {@link useStreamingTiming} to a runtime's message list. Each adapter
|
|
7
|
+
* provides three pure functions over its own message type; the timing state
|
|
8
|
+
* machine itself lives once in core.
|
|
9
|
+
*/
|
|
10
|
+
type StreamingTimingAccessors<TMessage> = {
|
|
11
|
+
/** Resolve the id of the last assistant message, or `undefined` if none. */readonly getAssistantMessageId: (messages: readonly TMessage[]) => string | undefined; /** Total text length (incl. reasoning/thinking) of the assistant message. */
|
|
12
|
+
readonly getTextLength: (messages: readonly TMessage[], messageId: string) => number; /** Number of tool calls recorded on the assistant message. */
|
|
13
|
+
readonly getToolCallCount: (messages: readonly TMessage[], messageId: string) => number;
|
|
14
|
+
};
|
|
15
|
+
type StreamingTimingOptions = {
|
|
16
|
+
/**
|
|
17
|
+
* Estimate token count from a text length. Defaults to `Math.ceil(n / 4)`.
|
|
18
|
+
* Adapters with real usage data can override this.
|
|
19
|
+
*/
|
|
20
|
+
readonly estimateTokens?: (textLength: number) => number;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Mutable tracking state for a single in-flight assistant message. Kept
|
|
24
|
+
* between updates by the caller (a ref in the React hook); `null` means no
|
|
25
|
+
* message is being tracked.
|
|
26
|
+
*
|
|
27
|
+
* `totalChunks` counts content growth deltas observed while streaming. The
|
|
28
|
+
* final delta that lands in the same update as the `isRunning -> false`
|
|
29
|
+
* transition is reconciled at finalize, so `totalChunks` and the token
|
|
30
|
+
* estimate (derived from final length) count the same content.
|
|
31
|
+
*/
|
|
32
|
+
type StreamingTimingState = {
|
|
33
|
+
readonly messageId: string;
|
|
34
|
+
readonly startTime: number;
|
|
35
|
+
readonly firstTokenTime?: number;
|
|
36
|
+
readonly lastContentLength: number;
|
|
37
|
+
readonly totalChunks: number;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Advance the client-side streaming timing tracker by one update.
|
|
41
|
+
*
|
|
42
|
+
* Pure: given the previous state and the current `(messages, isRunning)`
|
|
43
|
+
* snapshot, returns the next state plus any `MessageTiming` finalized this
|
|
44
|
+
* update (empty when streaming is still in flight). The caller owns the
|
|
45
|
+
* state (e.g. a ref) and merges finalized timings into its store.
|
|
46
|
+
*
|
|
47
|
+
* At finalize the text length is recomputed from the final messages, and any
|
|
48
|
+
* final growth delta that landed in the same update as the
|
|
49
|
+
* `isRunning -> false` transition is reconciled, so the token estimate and
|
|
50
|
+
* `totalChunks` count the same content (the per-adapter hooks this replaces
|
|
51
|
+
* reuse a stale growth delta and miss that last chunk).
|
|
52
|
+
*/
|
|
53
|
+
declare const stepStreamingTiming: <TMessage>(state: StreamingTimingState | null, messages: readonly TMessage[], isRunning: boolean, accessors: StreamingTimingAccessors<TMessage>, options: StreamingTimingOptions | undefined, now?: () => number) => {
|
|
54
|
+
readonly state: StreamingTimingState | null;
|
|
55
|
+
readonly timings: Record<string, MessageTiming>;
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
|
58
|
+
export { StreamingTimingAccessors, StreamingTimingOptions, StreamingTimingState, stepStreamingTiming };
|
|
59
|
+
//# sourceMappingURL=streaming-timing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streaming-timing.d.ts","names":[],"sources":["../../../src/runtime/utils/streaming-timing.ts"],"mappings":";;;;;AAQA;;;;KAAY,wBAAA;EAYW,qFAVZ,qBAAA,GACP,QAAA,WAAmB,QAAA,2BASQ;EAAA,SANpB,aAAA,GACP,QAAA,WAAmB,QAAA,IACnB,SAAA,qBANO;EAAA,SASA,gBAAA,GACP,QAAA,WAAmB,QAAA,IACnB,SAAA;AAAA;AAAA,KAIQ,sBAAA;EAVW;;;;EAAA,SAeZ,cAAA,IAAkB,UAAkB;AAAA;;;AAT1B;AAIrB;;;;AAK+C;AAa/C;;KAAY,oBAAA;EAAA,SACD,SAAA;EAAA,SACA,SAAA;EAAA,SACA,cAAA;EAAA,SACA,iBAAA;EAAA,SACA,WAAA;AAAA;;AAAW;AAuCtB;;;;;;;;;;;;cAAa,mBAAA,aACX,KAAA,EAAO,oBAAA,SACP,QAAA,WAAmB,QAAA,IACnB,SAAA,WACA,SAAA,EAAW,wBAAA,CAAyB,QAAA,GACpC,OAAA,EAAS,sBAAA,cACT,GAAA;EAAA,SAES,KAAA,EAAO,oBAAA;EAAA,SACP,OAAA,EAAS,MAAA,SAAe,aAAA;AAAA"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//#region src/runtime/utils/streaming-timing.ts
|
|
2
|
+
const defaultEstimateTokens = (textLength) => Math.ceil(textLength / 4);
|
|
3
|
+
/**
|
|
4
|
+
* Apply one content growth delta to `state`, recording the first-token time
|
|
5
|
+
* on the first growth and bumping the chunk count. Returns `state` unchanged
|
|
6
|
+
* when `len` has not grown.
|
|
7
|
+
*/
|
|
8
|
+
const applyGrowth = (state, len, now) => {
|
|
9
|
+
if (len <= state.lastContentLength) return state;
|
|
10
|
+
return {
|
|
11
|
+
...state,
|
|
12
|
+
firstTokenTime: state.firstTokenTime ?? now() - state.startTime,
|
|
13
|
+
lastContentLength: len,
|
|
14
|
+
totalChunks: state.totalChunks + 1
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Advance the client-side streaming timing tracker by one update.
|
|
19
|
+
*
|
|
20
|
+
* Pure: given the previous state and the current `(messages, isRunning)`
|
|
21
|
+
* snapshot, returns the next state plus any `MessageTiming` finalized this
|
|
22
|
+
* update (empty when streaming is still in flight). The caller owns the
|
|
23
|
+
* state (e.g. a ref) and merges finalized timings into its store.
|
|
24
|
+
*
|
|
25
|
+
* At finalize the text length is recomputed from the final messages, and any
|
|
26
|
+
* final growth delta that landed in the same update as the
|
|
27
|
+
* `isRunning -> false` transition is reconciled, so the token estimate and
|
|
28
|
+
* `totalChunks` count the same content (the per-adapter hooks this replaces
|
|
29
|
+
* reuse a stale growth delta and miss that last chunk).
|
|
30
|
+
*/
|
|
31
|
+
const stepStreamingTiming = (state, messages, isRunning, accessors, options, now = Date.now) => {
|
|
32
|
+
const lastId = accessors.getAssistantMessageId(messages);
|
|
33
|
+
const estimateTokens = options?.estimateTokens ?? defaultEstimateTokens;
|
|
34
|
+
if (isRunning && lastId !== void 0) {
|
|
35
|
+
let next;
|
|
36
|
+
if (state === null || state.messageId !== lastId) next = {
|
|
37
|
+
messageId: lastId,
|
|
38
|
+
startTime: now(),
|
|
39
|
+
lastContentLength: 0,
|
|
40
|
+
totalChunks: 0
|
|
41
|
+
};
|
|
42
|
+
else next = state;
|
|
43
|
+
return {
|
|
44
|
+
state: applyGrowth(next, accessors.getTextLength(messages, next.messageId), now),
|
|
45
|
+
timings: {}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (!isRunning && state !== null) {
|
|
49
|
+
const nowMs = now();
|
|
50
|
+
const reconciled = applyGrowth(state, accessors.getTextLength(messages, state.messageId), () => nowMs);
|
|
51
|
+
const totalStreamTime = nowMs - reconciled.startTime;
|
|
52
|
+
const tokenCount = estimateTokens(reconciled.lastContentLength);
|
|
53
|
+
const toolCallCount = accessors.getToolCallCount(messages, reconciled.messageId);
|
|
54
|
+
const timing = {
|
|
55
|
+
streamStartTime: reconciled.startTime,
|
|
56
|
+
totalStreamTime,
|
|
57
|
+
totalChunks: reconciled.totalChunks,
|
|
58
|
+
toolCallCount,
|
|
59
|
+
...reconciled.firstTokenTime !== void 0 && { firstTokenTime: reconciled.firstTokenTime },
|
|
60
|
+
...tokenCount > 0 && { tokenCount },
|
|
61
|
+
...totalStreamTime > 0 && tokenCount > 0 && { tokensPerSecond: tokenCount / (totalStreamTime / 1e3) }
|
|
62
|
+
};
|
|
63
|
+
return {
|
|
64
|
+
state: null,
|
|
65
|
+
timings: { [reconciled.messageId]: timing }
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
state,
|
|
70
|
+
timings: {}
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
//#endregion
|
|
74
|
+
export { stepStreamingTiming };
|
|
75
|
+
|
|
76
|
+
//# sourceMappingURL=streaming-timing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streaming-timing.js","names":["MessageTiming","StreamingTimingAccessors","getAssistantMessageId","messages","TMessage","getTextLength","messageId","getToolCallCount","StreamingTimingOptions","estimateTokens","textLength","StreamingTimingState","startTime","firstTokenTime","lastContentLength","totalChunks","defaultEstimateTokens","Math","ceil","applyGrowth","state","len","now","stepStreamingTiming","isRunning","accessors","options","Date","timings","Record","lastId","undefined","next","nowMs","reconciled","totalStreamTime","tokenCount","toolCallCount","timing","streamStartTime","tokensPerSecond"],"sources":["../../../src/runtime/utils/streaming-timing.ts"],"sourcesContent":["import type { MessageTiming } from \"../../types/message\";\n\n/**\n * Shape-specific accessors that adapt {@link stepStreamingTiming} /\n * {@link useStreamingTiming} to a runtime's message list. Each adapter\n * provides three pure functions over its own message type; the timing state\n * machine itself lives once in core.\n */\nexport type StreamingTimingAccessors<TMessage> = {\n /** Resolve the id of the last assistant message, or `undefined` if none. */\n readonly getAssistantMessageId: (\n messages: readonly TMessage[],\n ) => string | undefined;\n /** Total text length (incl. reasoning/thinking) of the assistant message. */\n readonly getTextLength: (\n messages: readonly TMessage[],\n messageId: string,\n ) => number;\n /** Number of tool calls recorded on the assistant message. */\n readonly getToolCallCount: (\n messages: readonly TMessage[],\n messageId: string,\n ) => number;\n};\n\nexport type StreamingTimingOptions = {\n /**\n * Estimate token count from a text length. Defaults to `Math.ceil(n / 4)`.\n * Adapters with real usage data can override this.\n */\n readonly estimateTokens?: (textLength: number) => number;\n};\n\n/**\n * Mutable tracking state for a single in-flight assistant message. Kept\n * between updates by the caller (a ref in the React hook); `null` means no\n * message is being tracked.\n *\n * `totalChunks` counts content growth deltas observed while streaming. The\n * final delta that lands in the same update as the `isRunning -> false`\n * transition is reconciled at finalize, so `totalChunks` and the token\n * estimate (derived from final length) count the same content.\n */\nexport type StreamingTimingState = {\n readonly messageId: string;\n readonly startTime: number;\n readonly firstTokenTime?: number;\n readonly lastContentLength: number;\n readonly totalChunks: number;\n};\n\nconst defaultEstimateTokens = (textLength: number): number =>\n Math.ceil(textLength / 4);\n\n/**\n * Apply one content growth delta to `state`, recording the first-token time\n * on the first growth and bumping the chunk count. Returns `state` unchanged\n * when `len` has not grown.\n */\nconst applyGrowth = (\n state: StreamingTimingState,\n len: number,\n now: () => number,\n): StreamingTimingState => {\n if (len <= state.lastContentLength) return state;\n return {\n ...state,\n firstTokenTime: state.firstTokenTime ?? now() - state.startTime,\n lastContentLength: len,\n totalChunks: state.totalChunks + 1,\n };\n};\n\n/**\n * Advance the client-side streaming timing tracker by one update.\n *\n * Pure: given the previous state and the current `(messages, isRunning)`\n * snapshot, returns the next state plus any `MessageTiming` finalized this\n * update (empty when streaming is still in flight). The caller owns the\n * state (e.g. a ref) and merges finalized timings into its store.\n *\n * At finalize the text length is recomputed from the final messages, and any\n * final growth delta that landed in the same update as the\n * `isRunning -> false` transition is reconciled, so the token estimate and\n * `totalChunks` count the same content (the per-adapter hooks this replaces\n * reuse a stale growth delta and miss that last chunk).\n */\nexport const stepStreamingTiming = <TMessage>(\n state: StreamingTimingState | null,\n messages: readonly TMessage[],\n isRunning: boolean,\n accessors: StreamingTimingAccessors<TMessage>,\n options: StreamingTimingOptions | undefined,\n now: () => number = Date.now,\n): {\n readonly state: StreamingTimingState | null;\n readonly timings: Record<string, MessageTiming>;\n} => {\n const lastId = accessors.getAssistantMessageId(messages);\n const estimateTokens = options?.estimateTokens ?? defaultEstimateTokens;\n\n if (isRunning && lastId !== undefined) {\n let next: StreamingTimingState;\n if (state === null || state.messageId !== lastId) {\n next = {\n messageId: lastId,\n startTime: now(),\n lastContentLength: 0,\n totalChunks: 0,\n };\n } else {\n next = state;\n }\n\n return {\n state: applyGrowth(\n next,\n accessors.getTextLength(messages, next.messageId),\n now,\n ),\n timings: {},\n };\n }\n\n if (!isRunning && state !== null) {\n const nowMs = now();\n // Reconcile any final growth that landed with the stop signal so the\n // token estimate (derived from final length) and totalChunks agree.\n const reconciled = applyGrowth(\n state,\n accessors.getTextLength(messages, state.messageId),\n () => nowMs,\n );\n\n const totalStreamTime = nowMs - reconciled.startTime;\n const tokenCount = estimateTokens(reconciled.lastContentLength);\n const toolCallCount = accessors.getToolCallCount(\n messages,\n reconciled.messageId,\n );\n\n const timing: MessageTiming = {\n streamStartTime: reconciled.startTime,\n totalStreamTime,\n totalChunks: reconciled.totalChunks,\n toolCallCount,\n ...(reconciled.firstTokenTime !== undefined && {\n firstTokenTime: reconciled.firstTokenTime,\n }),\n ...(tokenCount > 0 && { tokenCount }),\n ...(totalStreamTime > 0 &&\n tokenCount > 0 && {\n tokensPerSecond: tokenCount / (totalStreamTime / 1000),\n }),\n };\n\n return { state: null, timings: { [reconciled.messageId]: timing } };\n }\n\n return { state, timings: {} };\n};\n"],"mappings":";AAmDA,MAAMgB,yBAAyBN,eAC7BO,KAAKC,KAAKR,aAAa,CAAC;;;;;;AAO1B,MAAMS,eACJC,OACAC,KACAC,QACyB;CACzB,IAAID,OAAOD,MAAMN,mBAAmB,OAAOM;CAC3C,OAAO;EACL,GAAGA;EACHP,gBAAgBO,MAAMP,kBAAkBS,IAAI,IAAIF,MAAMR;EACtDE,mBAAmBO;EACnBN,aAAaK,MAAML,cAAc;CACnC;AACF;;;;;;;;;;;;;;;AAgBA,MAAaQ,uBACXH,OACAjB,UACAqB,WACAC,WACAC,SACAJ,MAAoBK,KAAKL,QAItB;CACH,MAAMQ,SAASL,UAAUvB,sBAAsBC,QAAQ;CACvD,MAAMM,iBAAiBiB,SAASjB,kBAAkBO;CAElD,IAAIQ,aAAaM,WAAWC,KAAAA,GAAW;EACrC,IAAIC;EACJ,IAAIZ,UAAU,QAAQA,MAAMd,cAAcwB,QACxCE,OAAO;GACL1B,WAAWwB;GACXlB,WAAWU,IAAI;GACfR,mBAAmB;GACnBC,aAAa;EACf;OAEAiB,OAAOZ;EAGT,OAAO;GACLA,OAAOD,YACLa,MACAP,UAAUpB,cAAcF,UAAU6B,KAAK1B,SAAS,GAChDgB,GACF;GACAM,SAAS,CAAC;EACZ;CACF;CAEA,IAAI,CAACJ,aAAaJ,UAAU,MAAM;EAChC,MAAMa,QAAQX,IAAI;EAGlB,MAAMY,aAAaf,YACjBC,OACAK,UAAUpB,cAAcF,UAAUiB,MAAMd,SAAS,SAC3C2B,KACR;EAEA,MAAME,kBAAkBF,QAAQC,WAAWtB;EAC3C,MAAMwB,aAAa3B,eAAeyB,WAAWpB,iBAAiB;EAC9D,MAAMuB,gBAAgBZ,UAAUlB,iBAC9BJ,UACA+B,WAAW5B,SACb;EAEA,MAAMgC,SAAwB;GAC5BC,iBAAiBL,WAAWtB;GAC5BuB;GACApB,aAAamB,WAAWnB;GACxBsB;GACA,GAAIH,WAAWrB,mBAAmBkB,KAAAA,KAAa,EAC7ClB,gBAAgBqB,WAAWrB,eAC7B;GACA,GAAIuB,aAAa,KAAK,EAAEA,WAAW;GACnC,GAAID,kBAAkB,KACpBC,aAAa,KAAK,EAChBI,iBAAiBJ,cAAcD,kBAAkB,KACnD;EACJ;EAEA,OAAO;GAAEf,OAAO;GAAMQ,SAAS,GAAGM,WAAW5B,YAAYgC,OAAO;EAAE;CACpE;CAEA,OAAO;EAAElB;EAAOQ,SAAS,CAAC;CAAE;AAC9B"}
|
|
@@ -23,6 +23,7 @@ const fromThreadMessageLike = (like, fallbackId, fallbackStatus) => {
|
|
|
23
23
|
text: like.content
|
|
24
24
|
}] : like.content;
|
|
25
25
|
const sanitizeImageContent = ({ image, ...rest }) => {
|
|
26
|
+
if (typeof image !== "string") return null;
|
|
26
27
|
if (image.match(/^data:image\/(png|jpeg|jpg|gif|webp|svg\+xml);base64,(.*)$/)) return {
|
|
27
28
|
...rest,
|
|
28
29
|
image
|
|
@@ -46,7 +47,7 @@ const fromThreadMessageLike = (like, fallbackId, fallbackStatus) => {
|
|
|
46
47
|
switch (type) {
|
|
47
48
|
case "text":
|
|
48
49
|
case "reasoning":
|
|
49
|
-
if (part.text
|
|
50
|
+
if (!part.text?.trim()) return null;
|
|
50
51
|
return part;
|
|
51
52
|
case "file":
|
|
52
53
|
case "source": return part;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread-message-like.js","names":["parsePartialJsonObject","generateId","ReasoningMessagePart","SourceMessagePart","ThreadStep","MessageStatus","ImageMessagePart","ThreadMessage","ThreadAssistantMessagePart","ThreadAssistantMessage","ThreadUserMessagePart","ThreadUserMessage","ThreadSystemMessage","FileMessagePart","DataMessagePart","GenerativeUIMessagePart","Unstable_AudioMessagePart","CompleteAttachment","MessageTiming","TextMessagePart","ToolApprovalOption","ToolCallTiming","ReadonlyJSONObject","ReadonlyJSONValue","DataPrefixedPart","type","data","ThreadMessageLike","role","content","toolCallId","toolName","args","argsText","artifact","result","isError","parentId","messages","interrupt","payload","timing","approval","id","approved","reason","isAutomatic","options","optionId","resolution","createdAt","Date","status","attachments","Omit","metadata","unstable_state","unstable_annotations","unstable_data","steps","submittedFeedback","isOptimistic","custom","Record","convertDataPrefixedPart","startsWith","undefined","name","substring","fromThreadMessageLike","like","fallbackId","fallbackStatus","common","const","text","sanitizeImageContent","image","rest","dataUri","match","test","console","warn","length","Error","map","part","trim","basePart","commonProps","JSON","stringify","converted","filter","c","att","unsupportedRole"],"sources":["../../../src/runtime/utils/thread-message-like.ts"],"sourcesContent":["import { parsePartialJsonObject } from \"assistant-stream/utils\";\nimport { generateId } from \"../../utils/id\";\nimport type {\n ReasoningMessagePart,\n SourceMessagePart,\n ThreadStep,\n MessageStatus,\n ImageMessagePart,\n ThreadMessage,\n ThreadAssistantMessagePart,\n ThreadAssistantMessage,\n ThreadUserMessagePart,\n ThreadUserMessage,\n ThreadSystemMessage,\n FileMessagePart,\n DataMessagePart,\n GenerativeUIMessagePart,\n Unstable_AudioMessagePart,\n} from \"../../types/message\";\nimport type { CompleteAttachment } from \"../../types/attachment\";\nimport type {\n MessageTiming,\n TextMessagePart,\n ToolApprovalOption,\n ToolCallTiming,\n} from \"../../types/message\";\nimport type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"assistant-stream/utils\";\n\ntype DataPrefixedPart = {\n readonly type: `data-${string}`;\n readonly data: any;\n};\n\nexport type ThreadMessageLike = {\n readonly role: \"assistant\" | \"user\" | \"system\";\n readonly content:\n | string\n | readonly (\n | TextMessagePart\n | ReasoningMessagePart\n | SourceMessagePart\n | ImageMessagePart\n | FileMessagePart\n | DataMessagePart\n | GenerativeUIMessagePart\n | Unstable_AudioMessagePart\n | DataPrefixedPart\n | {\n readonly type: \"tool-call\";\n readonly toolCallId?: string;\n readonly toolName: string;\n readonly args?: ReadonlyJSONObject;\n readonly argsText?: string;\n readonly artifact?: any;\n readonly result?: any | undefined;\n readonly isError?: boolean | undefined;\n readonly parentId?: string | undefined;\n readonly messages?: readonly ThreadMessage[] | undefined;\n readonly interrupt?: { type: \"human\"; payload: unknown };\n readonly timing?: ToolCallTiming;\n readonly approval?: {\n readonly id: string;\n readonly approved?: boolean;\n readonly reason?: string;\n readonly isAutomatic?: boolean;\n readonly options?: readonly ToolApprovalOption[];\n readonly optionId?: string;\n readonly resolution?: \"cancelled\" | \"expired\";\n };\n }\n )[];\n readonly id?: string | undefined;\n readonly createdAt?: Date | undefined;\n readonly status?: MessageStatus | undefined;\n readonly attachments?:\n | readonly (Omit<CompleteAttachment, \"content\"> & {\n readonly content: readonly (ThreadUserMessagePart | DataPrefixedPart)[];\n })[]\n | undefined;\n readonly metadata?:\n | {\n readonly unstable_state?: ReadonlyJSONValue;\n readonly unstable_annotations?:\n | readonly ReadonlyJSONValue[]\n | undefined;\n readonly unstable_data?: readonly ReadonlyJSONValue[] | undefined;\n readonly steps?: readonly ThreadStep[] | undefined;\n readonly timing?: MessageTiming | undefined;\n readonly submittedFeedback?: { readonly type: \"positive\" | \"negative\" };\n readonly isOptimistic?: boolean | undefined;\n readonly custom?: Record<string, unknown> | undefined;\n }\n | undefined;\n};\n\nconst convertDataPrefixedPart = (\n type: string,\n data: unknown,\n): DataMessagePart | undefined => {\n if (!type.startsWith(\"data-\")) return undefined;\n return { type: \"data\", name: type.substring(5), data };\n};\n\n/**\n * @deprecated This API is experimental and may change without notice.\n */\nexport const fromThreadMessageLike = (\n like: ThreadMessageLike,\n fallbackId: string,\n fallbackStatus: MessageStatus,\n): ThreadMessage => {\n const { role, id, createdAt, attachments, status, metadata } = like;\n const common = {\n id: id ?? fallbackId,\n createdAt: createdAt ?? new Date(),\n };\n\n const content =\n typeof like.content === \"string\"\n ? [{ type: \"text\" as const, text: like.content }]\n : like.content;\n\n const sanitizeImageContent = ({\n image,\n ...rest\n }: ImageMessagePart): ImageMessagePart | null => {\n const dataUri = image.match(\n /^data:image\\/(png|jpeg|jpg|gif|webp|svg\\+xml);base64,(.*)$/,\n );\n if (dataUri) {\n return { ...rest, image };\n }\n if (/^(https:\\/\\/|blob:)/.test(image)) {\n return { ...rest, image };\n }\n console.warn(`Invalid image data format detected`);\n return null;\n };\n\n if (role !== \"user\" && attachments?.length)\n throw new Error(\"attachments are only supported for user messages\");\n\n if (role !== \"assistant\" && status)\n throw new Error(\"status is only supported for assistant messages\");\n\n if (role !== \"assistant\" && metadata?.steps)\n throw new Error(\"metadata.steps is only supported for assistant messages\");\n\n switch (role) {\n case \"assistant\":\n return {\n ...common,\n role,\n content: content\n .map((part): ThreadAssistantMessagePart | null => {\n const type = part.type;\n switch (type) {\n case \"text\":\n case \"reasoning\":\n if (part.text.trim().length === 0) return null;\n return part;\n\n case \"file\":\n case \"source\":\n return part;\n\n case \"image\":\n return sanitizeImageContent(part);\n\n case \"data\":\n return part;\n\n case \"generative-ui\":\n return part;\n\n case \"tool-call\": {\n const { parentId, messages, ...basePart } = part;\n const commonProps = {\n ...basePart,\n toolCallId: part.toolCallId ?? `tool-${generateId()}`,\n ...(parentId !== undefined && { parentId }),\n ...(messages !== undefined && { messages }),\n };\n\n if (part.args) {\n return {\n ...commonProps,\n args: part.args,\n argsText: part.argsText ?? JSON.stringify(part.args),\n };\n }\n return {\n ...commonProps,\n args: parsePartialJsonObject(part.argsText ?? \"\") ?? {},\n argsText: part.argsText ?? \"\",\n };\n }\n\n default: {\n const converted = convertDataPrefixedPart(\n type,\n (part as DataPrefixedPart).data,\n );\n if (converted) return converted;\n throw new Error(\n `Unsupported assistant message part type: ${type}`,\n );\n }\n }\n })\n .filter((c) => !!c),\n status: status ?? fallbackStatus,\n metadata: {\n unstable_state: metadata?.unstable_state ?? null,\n unstable_annotations: metadata?.unstable_annotations ?? [],\n unstable_data: metadata?.unstable_data ?? [],\n custom: metadata?.custom ?? {},\n steps: metadata?.steps ?? [],\n ...(metadata?.timing && { timing: metadata.timing }),\n ...(metadata?.submittedFeedback && {\n submittedFeedback: metadata.submittedFeedback,\n }),\n ...(metadata?.isOptimistic && { isOptimistic: true }),\n },\n } satisfies ThreadAssistantMessage;\n\n case \"user\":\n return {\n ...common,\n role,\n content: content.map((part): ThreadUserMessagePart => {\n const type = part.type;\n switch (type) {\n case \"text\":\n case \"image\":\n case \"audio\":\n case \"file\":\n case \"data\":\n return part;\n\n default: {\n const converted = convertDataPrefixedPart(\n type,\n (part as DataPrefixedPart).data,\n );\n if (converted) return converted;\n throw new Error(`Unsupported user message part type: ${type}`);\n }\n }\n }),\n attachments: (attachments ?? []).map((att) => ({\n ...att,\n content: att.content.map((part): ThreadUserMessagePart => {\n const converted = convertDataPrefixedPart(\n part.type,\n (part as DataPrefixedPart).data,\n );\n return converted ?? (part as ThreadUserMessagePart);\n }),\n })),\n metadata: {\n custom: metadata?.custom ?? {},\n },\n } satisfies ThreadUserMessage;\n\n case \"system\":\n if (content.length !== 1 || content[0]!.type !== \"text\")\n throw new Error(\n \"System messages must have exactly one text message part.\",\n );\n\n return {\n ...common,\n role,\n content: content as [TextMessagePart],\n metadata: {\n custom: metadata?.custom ?? {},\n },\n } satisfies ThreadSystemMessage;\n\n default: {\n const unsupportedRole: never = role;\n throw new Error(`Unknown message role: ${unsupportedRole}`);\n }\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"thread-message-like.js","names":["parsePartialJsonObject","generateId","ReasoningMessagePart","SourceMessagePart","ThreadStep","MessageStatus","ImageMessagePart","ThreadMessage","ThreadAssistantMessagePart","ThreadAssistantMessage","ThreadUserMessagePart","ThreadUserMessage","ThreadSystemMessage","FileMessagePart","DataMessagePart","GenerativeUIMessagePart","Unstable_AudioMessagePart","CompleteAttachment","MessageTiming","TextMessagePart","ToolApprovalOption","ToolCallTiming","ReadonlyJSONObject","ReadonlyJSONValue","DataPrefixedPart","type","data","ThreadMessageLike","role","content","toolCallId","toolName","args","argsText","artifact","result","isError","parentId","messages","interrupt","payload","timing","approval","id","approved","reason","isAutomatic","options","optionId","resolution","createdAt","Date","status","attachments","Omit","metadata","unstable_state","unstable_annotations","unstable_data","steps","submittedFeedback","isOptimistic","custom","Record","convertDataPrefixedPart","startsWith","undefined","name","substring","fromThreadMessageLike","like","fallbackId","fallbackStatus","common","const","text","sanitizeImageContent","image","rest","dataUri","match","test","console","warn","length","Error","map","part","trim","basePart","commonProps","JSON","stringify","converted","filter","c","att","unsupportedRole"],"sources":["../../../src/runtime/utils/thread-message-like.ts"],"sourcesContent":["import { parsePartialJsonObject } from \"assistant-stream/utils\";\nimport { generateId } from \"../../utils/id\";\nimport type {\n ReasoningMessagePart,\n SourceMessagePart,\n ThreadStep,\n MessageStatus,\n ImageMessagePart,\n ThreadMessage,\n ThreadAssistantMessagePart,\n ThreadAssistantMessage,\n ThreadUserMessagePart,\n ThreadUserMessage,\n ThreadSystemMessage,\n FileMessagePart,\n DataMessagePart,\n GenerativeUIMessagePart,\n Unstable_AudioMessagePart,\n} from \"../../types/message\";\nimport type { CompleteAttachment } from \"../../types/attachment\";\nimport type {\n MessageTiming,\n TextMessagePart,\n ToolApprovalOption,\n ToolCallTiming,\n} from \"../../types/message\";\nimport type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"assistant-stream/utils\";\n\ntype DataPrefixedPart = {\n readonly type: `data-${string}`;\n readonly data: any;\n};\n\nexport type ThreadMessageLike = {\n readonly role: \"assistant\" | \"user\" | \"system\";\n readonly content:\n | string\n | readonly (\n | TextMessagePart\n | ReasoningMessagePart\n | SourceMessagePart\n | ImageMessagePart\n | FileMessagePart\n | DataMessagePart\n | GenerativeUIMessagePart\n | Unstable_AudioMessagePart\n | DataPrefixedPart\n | {\n readonly type: \"tool-call\";\n readonly toolCallId?: string;\n readonly toolName: string;\n readonly args?: ReadonlyJSONObject;\n readonly argsText?: string;\n readonly artifact?: any;\n readonly result?: any | undefined;\n readonly isError?: boolean | undefined;\n readonly parentId?: string | undefined;\n readonly messages?: readonly ThreadMessage[] | undefined;\n readonly interrupt?: { type: \"human\"; payload: unknown };\n readonly timing?: ToolCallTiming;\n readonly approval?: {\n readonly id: string;\n readonly approved?: boolean;\n readonly reason?: string;\n readonly isAutomatic?: boolean;\n readonly options?: readonly ToolApprovalOption[];\n readonly optionId?: string;\n readonly resolution?: \"cancelled\" | \"expired\";\n };\n }\n )[];\n readonly id?: string | undefined;\n readonly createdAt?: Date | undefined;\n readonly status?: MessageStatus | undefined;\n readonly attachments?:\n | readonly (Omit<CompleteAttachment, \"content\"> & {\n readonly content: readonly (ThreadUserMessagePart | DataPrefixedPart)[];\n })[]\n | undefined;\n readonly metadata?:\n | {\n readonly unstable_state?: ReadonlyJSONValue;\n readonly unstable_annotations?:\n | readonly ReadonlyJSONValue[]\n | undefined;\n readonly unstable_data?: readonly ReadonlyJSONValue[] | undefined;\n readonly steps?: readonly ThreadStep[] | undefined;\n readonly timing?: MessageTiming | undefined;\n readonly submittedFeedback?: { readonly type: \"positive\" | \"negative\" };\n readonly isOptimistic?: boolean | undefined;\n readonly custom?: Record<string, unknown> | undefined;\n }\n | undefined;\n};\n\nconst convertDataPrefixedPart = (\n type: string,\n data: unknown,\n): DataMessagePart | undefined => {\n if (!type.startsWith(\"data-\")) return undefined;\n return { type: \"data\", name: type.substring(5), data };\n};\n\n/**\n * @deprecated This API is experimental and may change without notice.\n */\nexport const fromThreadMessageLike = (\n like: ThreadMessageLike,\n fallbackId: string,\n fallbackStatus: MessageStatus,\n): ThreadMessage => {\n const { role, id, createdAt, attachments, status, metadata } = like;\n const common = {\n id: id ?? fallbackId,\n createdAt: createdAt ?? new Date(),\n };\n\n const content =\n typeof like.content === \"string\"\n ? [{ type: \"text\" as const, text: like.content }]\n : like.content;\n\n const sanitizeImageContent = ({\n image,\n ...rest\n }: ImageMessagePart): ImageMessagePart | null => {\n if (typeof image !== \"string\") return null;\n const dataUri = image.match(\n /^data:image\\/(png|jpeg|jpg|gif|webp|svg\\+xml);base64,(.*)$/,\n );\n if (dataUri) {\n return { ...rest, image };\n }\n if (/^(https:\\/\\/|blob:)/.test(image)) {\n return { ...rest, image };\n }\n console.warn(`Invalid image data format detected`);\n return null;\n };\n\n if (role !== \"user\" && attachments?.length)\n throw new Error(\"attachments are only supported for user messages\");\n\n if (role !== \"assistant\" && status)\n throw new Error(\"status is only supported for assistant messages\");\n\n if (role !== \"assistant\" && metadata?.steps)\n throw new Error(\"metadata.steps is only supported for assistant messages\");\n\n switch (role) {\n case \"assistant\":\n return {\n ...common,\n role,\n content: content\n .map((part): ThreadAssistantMessagePart | null => {\n const type = part.type;\n switch (type) {\n case \"text\":\n case \"reasoning\":\n if (!part.text?.trim()) return null;\n return part;\n\n case \"file\":\n case \"source\":\n return part;\n\n case \"image\":\n return sanitizeImageContent(part);\n\n case \"data\":\n return part;\n\n case \"generative-ui\":\n return part;\n\n case \"tool-call\": {\n const { parentId, messages, ...basePart } = part;\n const commonProps = {\n ...basePart,\n toolCallId: part.toolCallId ?? `tool-${generateId()}`,\n ...(parentId !== undefined && { parentId }),\n ...(messages !== undefined && { messages }),\n };\n\n if (part.args) {\n return {\n ...commonProps,\n args: part.args,\n argsText: part.argsText ?? JSON.stringify(part.args),\n };\n }\n return {\n ...commonProps,\n args: parsePartialJsonObject(part.argsText ?? \"\") ?? {},\n argsText: part.argsText ?? \"\",\n };\n }\n\n default: {\n const converted = convertDataPrefixedPart(\n type,\n (part as DataPrefixedPart).data,\n );\n if (converted) return converted;\n throw new Error(\n `Unsupported assistant message part type: ${type}`,\n );\n }\n }\n })\n .filter((c) => !!c),\n status: status ?? fallbackStatus,\n metadata: {\n unstable_state: metadata?.unstable_state ?? null,\n unstable_annotations: metadata?.unstable_annotations ?? [],\n unstable_data: metadata?.unstable_data ?? [],\n custom: metadata?.custom ?? {},\n steps: metadata?.steps ?? [],\n ...(metadata?.timing && { timing: metadata.timing }),\n ...(metadata?.submittedFeedback && {\n submittedFeedback: metadata.submittedFeedback,\n }),\n ...(metadata?.isOptimistic && { isOptimistic: true }),\n },\n } satisfies ThreadAssistantMessage;\n\n case \"user\":\n return {\n ...common,\n role,\n content: content.map((part): ThreadUserMessagePart => {\n const type = part.type;\n switch (type) {\n case \"text\":\n case \"image\":\n case \"audio\":\n case \"file\":\n case \"data\":\n return part;\n\n default: {\n const converted = convertDataPrefixedPart(\n type,\n (part as DataPrefixedPart).data,\n );\n if (converted) return converted;\n throw new Error(`Unsupported user message part type: ${type}`);\n }\n }\n }),\n attachments: (attachments ?? []).map((att) => ({\n ...att,\n content: att.content.map((part): ThreadUserMessagePart => {\n const converted = convertDataPrefixedPart(\n part.type,\n (part as DataPrefixedPart).data,\n );\n return converted ?? (part as ThreadUserMessagePart);\n }),\n })),\n metadata: {\n custom: metadata?.custom ?? {},\n },\n } satisfies ThreadUserMessage;\n\n case \"system\":\n if (content.length !== 1 || content[0]!.type !== \"text\")\n throw new Error(\n \"System messages must have exactly one text message part.\",\n );\n\n return {\n ...common,\n role,\n content: content as [TextMessagePart],\n metadata: {\n custom: metadata?.custom ?? {},\n },\n } satisfies ThreadSystemMessage;\n\n default: {\n const unsupportedRole: never = role;\n throw new Error(`Unknown message role: ${unsupportedRole}`);\n }\n }\n};\n"],"mappings":"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"}
|
|
@@ -42,6 +42,13 @@ type ExternalStoreThreadListAdapter = {
|
|
|
42
42
|
onDelete?: ((threadId: string) => Promise<void> | void) | undefined;
|
|
43
43
|
};
|
|
44
44
|
type ExternalStoreMessageConverter<T> = (message: T, idx: number) => ThreadMessageLike;
|
|
45
|
+
/**
|
|
46
|
+
* @deprecated This API is still under active development and might change without notice.
|
|
47
|
+
*/
|
|
48
|
+
type ExternalStoreBranchChange = {
|
|
49
|
+
headId: string | null;
|
|
50
|
+
visibleMessageIds: readonly string[];
|
|
51
|
+
};
|
|
45
52
|
type ExternalStoreMessageConverterAdapter<T> = {
|
|
46
53
|
convertMessage: ExternalStoreMessageConverter<T>;
|
|
47
54
|
};
|
|
@@ -78,6 +85,24 @@ type ExternalStoreAdapterBase<T> = {
|
|
|
78
85
|
state?: ReadonlyJSONValue | undefined;
|
|
79
86
|
extras?: unknown;
|
|
80
87
|
setMessages?: ((messages: readonly T[]) => void) | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Fires when the user explicitly switches branches via the runtime's
|
|
90
|
+
* `switchToBranch` action (e.g. a BranchPicker click). It does not fire on
|
|
91
|
+
* adapter resync, `append`, edit/regenerate, content-only updates, or while
|
|
92
|
+
* the thread is running. Consecutive switches that resolve to the same
|
|
93
|
+
* canonical head are de-duped.
|
|
94
|
+
*
|
|
95
|
+
* `headId` is the canonical (persisted) head of the now-visible branch —
|
|
96
|
+
* optimistic/transient ids are never surfaced. `visibleMessageIds` lists the
|
|
97
|
+
* visible path in order.
|
|
98
|
+
*
|
|
99
|
+
* This complements `setMessages` rather than replacing it: switching still
|
|
100
|
+
* requires `setMessages`, and this callback does not on its own enable branch
|
|
101
|
+
* switching.
|
|
102
|
+
*
|
|
103
|
+
* @deprecated This API is still under active development and might change without notice.
|
|
104
|
+
*/
|
|
105
|
+
unstable_onBranchChange?: ((event: ExternalStoreBranchChange) => void) | undefined;
|
|
81
106
|
onImport?: ((messages: readonly ThreadMessage[]) => void) | undefined;
|
|
82
107
|
onExportExternalState?: (() => any) | undefined;
|
|
83
108
|
onLoadExternalState?: ((state: any) => void) | undefined;
|
|
@@ -142,5 +167,5 @@ type ExternalStoreAdapterBase<T> = {
|
|
|
142
167
|
};
|
|
143
168
|
type ExternalStoreAdapter<T = ThreadMessage> = ExternalStoreAdapterBase<T> & (T extends ThreadMessage ? object : ExternalStoreMessageConverterAdapter<T>);
|
|
144
169
|
//#endregion
|
|
145
|
-
export { ExternalStoreAdapter, ExternalStoreMessageConverter, ExternalStoreThreadData, ExternalStoreThreadListAdapter };
|
|
170
|
+
export { ExternalStoreAdapter, ExternalStoreBranchChange, ExternalStoreMessageConverter, ExternalStoreThreadData, ExternalStoreThreadListAdapter };
|
|
146
171
|
//# sourceMappingURL=external-store-adapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-store-adapter.d.ts","names":[],"sources":["../../../src/runtimes/external-store/external-store-adapter.ts"],"mappings":";;;;;;;;;;;;;KAqBY,uBAAA;EACV,MAAA,EAAQ,MAAA;EACR,EAAA;EACA,QAAA;EACA,UAAA;EACA,KAAA;EACA,MAAA,GAAS,MAAM;AAAA;AAAA,KAGL,8BAAA;EAPV;;;EAWA,QAAA;EACA,SAAA;EACA,OAAA,YAAmB,uBAAA;EACnB,eAAA,YAA2B,uBAAA;EAVZ;AAGjB;;EAWE,mBAAA,UAA6B,OAAA;EALV;;;EASnB,gBAAA,KAAqB,QAAA,aAAqB,OAAA;EAC1C,QAAA,IACE,QAAA,UACA,QAAA,cACI,OAAA;EACN,cAAA,KAEM,QAAA,UACA,MAAA,EAAQ,MAAA,kCACL,OAAA;EAET,SAAA,KAAc,QAAA,aAAqB,OAAA;EACnC,WAAA,KAAgB,QAAA,aAAqB,OAAA;EACrC,QAAA,KAAa,QAAA,aAAqB,OAAA;AAAA;AAAA,KAGxB,6BAAA,OACV,OAAA,EAAS,CAAA,EACT,GAAA,aACG,iBAAiB;AAAA,
|
|
1
|
+
{"version":3,"file":"external-store-adapter.d.ts","names":[],"sources":["../../../src/runtimes/external-store/external-store-adapter.ts"],"mappings":";;;;;;;;;;;;;KAqBY,uBAAA;EACV,MAAA,EAAQ,MAAA;EACR,EAAA;EACA,QAAA;EACA,UAAA;EACA,KAAA;EACA,MAAA,GAAS,MAAM;AAAA;AAAA,KAGL,8BAAA;EAPV;;;EAWA,QAAA;EACA,SAAA;EACA,OAAA,YAAmB,uBAAA;EACnB,eAAA,YAA2B,uBAAA;EAVZ;AAGjB;;EAWE,mBAAA,UAA6B,OAAA;EALV;;;EASnB,gBAAA,KAAqB,QAAA,aAAqB,OAAA;EAC1C,QAAA,IACE,QAAA,UACA,QAAA,cACI,OAAA;EACN,cAAA,KAEM,QAAA,UACA,MAAA,EAAQ,MAAA,kCACL,OAAA;EAET,SAAA,KAAc,QAAA,aAAqB,OAAA;EACnC,WAAA,KAAgB,QAAA,aAAqB,OAAA;EACrC,QAAA,KAAa,QAAA,aAAqB,OAAA;AAAA;AAAA,KAGxB,6BAAA,OACV,OAAA,EAAS,CAAA,EACT,GAAA,aACG,iBAAiB;;;;KAKV,yBAAA;EACV,MAAA;EACA,iBAAiB;AAAA;AAAA,KAGd,oCAAA;EACH,cAAA,EAAgB,6BAA6B,CAAC,CAAA;AAAA;AAAA,KAG3C,wBAAA;EAjCuC;;;;;;EAwC1C,UAAA;EAhCc;;;;;;;;EAyCd,cAAA;EApCA;;;;AAAyC;AAG3C;;;EA0CE,SAAA;EACA,SAAA;EACA,QAAA,YAAoB,CAAA;EACpB,iBAAA,GAAoB,yBAAA;EACpB,WAAA,YAAuB,gBAAA;EACvB,KAAA,GAAQ,iBAAA;EACR,MAAA;EAEA,WAAA,KAAgB,QAAA,WAAmB,CAAA;EA1CzB;;;;AAEO;AACjB;;;;;;;;;AAG+C;AAAA;;EAsD/C,uBAAA,KACM,KAAA,EAAO,yBAAA;EAEb,QAAA,KAAa,QAAA,WAAmB,aAAA;EAChC,qBAAA;EACA,mBAAA,KAAwB,KAAA;EACxB,KAAA,GAAQ,OAAA,EAAS,aAAA,KAAkB,OAAA,QAxBA;EA0BnC,KAAA,GAAQ,0BAAA;EACR,MAAA,KAAW,OAAA,EAAS,aAAA,KAAkB,OAAA;EACtC,QAAA,KAAa,SAAA,aAAsB,OAAA;EACnC,QAAA,KACM,QAAA,iBAAyB,MAAA,EAAQ,cAAA,KAAmB,OAAA;EAE1D,QAAA,KAAa,MAAA,EAAQ,eAAA,KAAoB,OAAA;EACzC,QAAA,UAAkB,OAAA;EAClB,eAAA,KACM,OAAA,EAAS,oBAAA,KAAyB,OAAA;EAExC,gBAAA,KACM,OAAA;IAAW,UAAA;IAAoB,OAAA;EAAA;EAErC,uBAAA,KACM,OAAA,EAAS,4BAAA,KAAiC,OAAA;EAEhD,cAAA,GAAiB,6BAAA,CAA8B,CAAA;EAC/C,QAAA;IAEM,WAAA,GAAc,iBAAA;IACd,MAAA,GAAS,sBAAA;IACT,SAAA,GAAY,gBAAA;IACZ,KAAA,GAAQ,oBAAA;IACR,QAAA,GAAW,eAAA;IAJG;;;IAQd,UAAA,GAAa,8BAAA;EAAA;EAGnB,qBAAA;IAEM,IAAA;EAAA;EAiCgB;;;;;;;;;;;;;;;;;;;EAXtB,8BAAA;EA9Da;;;;;;;;;EAwEb,eAAA,KACM,QAAA,EAAU,MAAA,SAAe,mBAAA;AAAA;AAAA,KAIrB,oBAAA,KAAyB,aAAA,IACnC,wBAAA,CAAyB,CAAA,KACtB,CAAA,SAAU,aAAA,YAEP,oCAAA,CAAqC,CAAA"}
|
|
@@ -63,6 +63,15 @@ declare class ExternalStoreThreadRuntimeCore extends BaseThreadRuntimeCore imple
|
|
|
63
63
|
*/
|
|
64
64
|
private _findMessageIdForToolCall;
|
|
65
65
|
switchToBranch(branchId: string): void;
|
|
66
|
+
/**
|
|
67
|
+
* Emit `unstable_onBranchChange` for an explicit branch switch. Reads the
|
|
68
|
+
* canonical head from the repository (which skips optimistic/transient
|
|
69
|
+
* messages) and de-dupes switches that leave the canonical head unchanged.
|
|
70
|
+
* Comparing against the head observed just before the switch — rather than the
|
|
71
|
+
* last emitted head — keeps a switch firing after an adapter resync moved the
|
|
72
|
+
* head elsewhere in the meantime.
|
|
73
|
+
*/
|
|
74
|
+
private _notifyBranchChange;
|
|
66
75
|
append(message: AppendMessage): Promise<void>;
|
|
67
76
|
deleteMessage(messageId: string): Promise<void>;
|
|
68
77
|
getQueueItems(): readonly QueueItemState[];
|
|
@@ -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":";;;;;;;;;;;;;cAkDa,kBAAA,GACX,SAAA,WACA,QAAA,WAAmB,aAAa;AAAA,cAKrB,8BAAA,SACH,qBAAA,YACG,iBAAA;EAAA,QAEH,aAAA;EAAA,IAgBG,YAAA,IAAY,mBAAA;EAAA,QAIf,SAAA;EACD,UAAA;EACA,cAAA;EAAA,IACI,SAAA;EAAA,IAIA,SAAA;EAAA,UAIQ,gBAAA,aAA6B,aAAA;EAAA,IAI5B,KAAA,iEAAK,kBAAA,oCAAA,iBAAA;EAAA,IAId,QAAA;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;EAAA;;;;;;EAAA,QA8K5B,qBAAA;EAuNuB;;;;EAAA,QApJvB,oBAAA;EAAA,QACA,yBAAA;EAoQqB;;;;;;EAAA,QA5PrB,yBAAA;EAmBQ,cAAA,CAAe,QAAA;EApUpB;;;;;;;;EAAA,QAiWH,mBAAA;EAaK,MAAA,CAAO,OAAA,EAAS,aAAA,GAAgB,OAAA;EA8BhC,aAAA,CAAc,SAAA,WAAoB,OAAA;EAoBxC,aAAA,aApB+C,cAAA;EA0B/C,cAAA,CAAe,WAAA;EAIf,eAAA,CAAgB,WAAA;EAIV,QAAA,CAAS,MAAA,EAAQ,cAAA,GAAiB,OAAA;EAclC,SAAA,CAAU,MAAA,EAAQ,eAAA,GAAkB,OAAA;EAO1C,mBAAA;EAOA,mBAAA,CAAoB,KAAA;EAmBpB,SAAA;EA0CA,aAAA,CAAc,OAAA,EAAS,oBAAA;EAMvB,cAAA,CAAe,OAAA,EAAS,qBAAA;EAoBxB,qBAAA,CAAsB,OAAA,EAAS,4BAAA;EAMtB,KAAA,CAAM,eAAA,YAA2B,iBAAA;EAMjC,MAAA,CAAO,IAAA,EAAM,yBAAA;EAAA,QAQrB,cAAA;AAAA"}
|
|
@@ -232,8 +232,27 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
|
|
|
232
232
|
switchToBranch(branchId) {
|
|
233
233
|
if (!this._store.setMessages) throw new Error("Runtime does not support switching branches.");
|
|
234
234
|
if (this._store.isRunning) return;
|
|
235
|
+
const onBranchChange = this._store.unstable_onBranchChange;
|
|
236
|
+
const previousHeadId = onBranchChange ? this.repository.canonicalHeadId : null;
|
|
235
237
|
this.repository.switchToBranch(branchId);
|
|
236
238
|
this.updateMessages(this.repository.getMessages());
|
|
239
|
+
if (onBranchChange) this._notifyBranchChange(previousHeadId, onBranchChange);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Emit `unstable_onBranchChange` for an explicit branch switch. Reads the
|
|
243
|
+
* canonical head from the repository (which skips optimistic/transient
|
|
244
|
+
* messages) and de-dupes switches that leave the canonical head unchanged.
|
|
245
|
+
* Comparing against the head observed just before the switch — rather than the
|
|
246
|
+
* last emitted head — keeps a switch firing after an adapter resync moved the
|
|
247
|
+
* head elsewhere in the meantime.
|
|
248
|
+
*/
|
|
249
|
+
_notifyBranchChange(previousHeadId, onBranchChange) {
|
|
250
|
+
const headId = this.repository.canonicalHeadId;
|
|
251
|
+
if (headId === previousHeadId) return;
|
|
252
|
+
onBranchChange({
|
|
253
|
+
headId,
|
|
254
|
+
visibleMessageIds: this.repository.getMessages().map((m) => m.id)
|
|
255
|
+
});
|
|
237
256
|
}
|
|
238
257
|
async append(message) {
|
|
239
258
|
const isEdit = message.parentId !== (this.messages.at(-1)?.id ?? null);
|