@jini-ai/chat 0.3.8 → 0.3.10
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/core/assistant-content.d.ts +29 -0
- package/dist/core/assistant-content.js +41 -0
- package/dist/core/compact-events.d.ts +23 -0
- package/dist/core/compact-events.js +23 -0
- package/dist/core/events.d.ts +2 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +4 -0
- package/dist/core/run-activity.d.ts +88 -0
- package/dist/core/run-activity.js +153 -0
- package/dist/core/tool-output.d.ts +13 -0
- package/dist/core/tool-output.js +24 -0
- package/dist/react/components/Markdown.js +21 -7
- package/dist/react/components/McpUiSurfaceCard.d.ts +9 -1
- package/dist/react/components/McpUiSurfaceCard.js +43 -2
- package/dist/react/components/MessageRow.js +28 -7
- package/dist/react/components/ToolCard.js +6 -6
- package/dist/react/ext-event-renderer-registry.d.ts +39 -1
- package/dist/react/ext-event-renderer-registry.js +23 -4
- package/dist/react/features/chat-pane/components/ChatPane.js +6 -4
- package/dist/react/features/chat-pane/create-mcp-ui-tool-caller.js +15 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneRuntimeInventory.hooks.d.ts +15 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneRuntimeInventory.hooks.js +113 -14
- package/dist/react/features/chat-pane/styles.d.ts +1 -1
- package/dist/react/features/chat-pane/styles.js +7 -2
- package/dist/react/features/chat-pane/types.d.ts +7 -0
- package/dist/react/hooks/useConversation.js +13 -66
- package/dist/react/hooks/useExtEventGroups.d.ts +13 -1
- package/dist/react/hooks/useExtEventGroups.js +38 -9
- package/dist/react/hooks/useRunActivity.d.ts +12 -0
- package/dist/react/hooks/useRunActivity.js +44 -0
- package/dist/react/index.d.ts +3 -3
- package/dist/react/index.js +2 -2
- package/dist/react/message-blocks.d.ts +6 -1
- package/dist/react/message-blocks.js +14 -13
- package/package.json +7 -6
- package/dist/core/agentic/chat-capabilities.d.ts.map +0 -1
- package/dist/core/agentic/chat-capabilities.js.map +0 -1
- package/dist/core/agentic/index.d.ts.map +0 -1
- package/dist/core/agentic/index.js.map +0 -1
- package/dist/core/events.d.ts.map +0 -1
- package/dist/core/events.js.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js.map +0 -1
- package/dist/core/messages.d.ts.map +0 -1
- package/dist/core/messages.js.map +0 -1
- package/dist/core/partial-json.d.ts.map +0 -1
- package/dist/core/partial-json.js.map +0 -1
- package/dist/core/persistence/index.d.ts.map +0 -1
- package/dist/core/persistence/index.js.map +0 -1
- package/dist/core/persistence/ports.d.ts.map +0 -1
- package/dist/core/persistence/ports.js.map +0 -1
- package/dist/core/persistence/title.d.ts.map +0 -1
- package/dist/core/persistence/title.js.map +0 -1
- package/dist/core/question-form/format.d.ts.map +0 -1
- package/dist/core/question-form/format.js.map +0 -1
- package/dist/core/question-form/normalize.d.ts.map +0 -1
- package/dist/core/question-form/normalize.js.map +0 -1
- package/dist/core/question-form/scan.d.ts.map +0 -1
- package/dist/core/question-form/scan.js.map +0 -1
- package/dist/core/question-form/types.d.ts.map +0 -1
- package/dist/core/question-form/types.js.map +0 -1
- package/dist/core/question-form.d.ts.map +0 -1
- package/dist/core/question-form.js.map +0 -1
- package/dist/core/todos.d.ts.map +0 -1
- package/dist/core/todos.js.map +0 -1
- package/dist/core/tool-events.d.ts.map +0 -1
- package/dist/core/tool-events.js.map +0 -1
- package/dist/core/tools.d.ts.map +0 -1
- package/dist/core/tools.js.map +0 -1
- package/dist/core/transcript.d.ts.map +0 -1
- package/dist/core/transcript.js.map +0 -1
- package/dist/core/transport.d.ts.map +0 -1
- package/dist/core/transport.js.map +0 -1
- package/dist/core/util/index.d.ts.map +0 -1
- package/dist/core/util/index.js.map +0 -1
- package/dist/core/util/manifest.d.ts.map +0 -1
- package/dist/core/util/manifest.js.map +0 -1
- package/dist/core/util/markdown-context.d.ts.map +0 -1
- package/dist/core/util/markdown-context.js.map +0 -1
- package/dist/core/util/markup-attributes.d.ts.map +0 -1
- package/dist/core/util/markup-attributes.js.map +0 -1
- package/dist/core/util/parser.d.ts.map +0 -1
- package/dist/core/util/parser.js.map +0 -1
- package/dist/core/util/pointer.d.ts.map +0 -1
- package/dist/core/util/pointer.js.map +0 -1
- package/dist/core/util/recover.d.ts.map +0 -1
- package/dist/core/util/recover.js.map +0 -1
- package/dist/core/util/strip.d.ts.map +0 -1
- package/dist/core/util/strip.js.map +0 -1
- package/dist/core/util/types.d.ts.map +0 -1
- package/dist/core/util/types.js.map +0 -1
- package/dist/core/util/validate.d.ts.map +0 -1
- package/dist/core/util/validate.js.map +0 -1
- package/dist/react/agent-bridge/frontend-session-bridge.d.ts.map +0 -1
- package/dist/react/agent-bridge/frontend-session-bridge.js.map +0 -1
- package/dist/react/artifact-types.d.ts.map +0 -1
- package/dist/react/artifact-types.js.map +0 -1
- package/dist/react/chat-pane.d.ts.map +0 -1
- package/dist/react/chat-pane.js.map +0 -1
- package/dist/react/components/A2uiSurfaceCard.d.ts.map +0 -1
- package/dist/react/components/A2uiSurfaceCard.js.map +0 -1
- package/dist/react/components/AttachmentPreviewModal.d.ts.map +0 -1
- package/dist/react/components/AttachmentPreviewModal.hooks.d.ts.map +0 -1
- package/dist/react/components/AttachmentPreviewModal.hooks.js.map +0 -1
- package/dist/react/components/AttachmentPreviewModal.js.map +0 -1
- package/dist/react/components/AttachmentTray.d.ts.map +0 -1
- package/dist/react/components/AttachmentTray.js.map +0 -1
- package/dist/react/components/ChatFab.d.ts.map +0 -1
- package/dist/react/components/ChatFab.js.map +0 -1
- package/dist/react/components/Composer.d.ts.map +0 -1
- package/dist/react/components/Composer.js.map +0 -1
- package/dist/react/components/ComposerDiscovery.d.ts.map +0 -1
- package/dist/react/components/ComposerDiscovery.js.map +0 -1
- package/dist/react/components/ConversationList.d.ts.map +0 -1
- package/dist/react/components/ConversationList.js.map +0 -1
- package/dist/react/components/ExtEventErrorBoundary.d.ts.map +0 -1
- package/dist/react/components/ExtEventErrorBoundary.js.map +0 -1
- package/dist/react/components/Icon.d.ts.map +0 -1
- package/dist/react/components/Icon.js.map +0 -1
- package/dist/react/components/JiniChatProvider.d.ts.map +0 -1
- package/dist/react/components/JiniChatProvider.js.map +0 -1
- package/dist/react/components/Markdown.d.ts.map +0 -1
- package/dist/react/components/Markdown.js.map +0 -1
- package/dist/react/components/McpUiSurfaceCard.d.ts.map +0 -1
- package/dist/react/components/McpUiSurfaceCard.js.map +0 -1
- package/dist/react/components/MessageList.d.ts.map +0 -1
- package/dist/react/components/MessageList.js.map +0 -1
- package/dist/react/components/MessageRow.d.ts.map +0 -1
- package/dist/react/components/MessageRow.js.map +0 -1
- package/dist/react/components/NextStepActions.d.ts.map +0 -1
- package/dist/react/components/NextStepActions.js.map +0 -1
- package/dist/react/components/QuestionForm.d.ts.map +0 -1
- package/dist/react/components/QuestionForm.js.map +0 -1
- package/dist/react/components/QuestionsPanel.d.ts.map +0 -1
- package/dist/react/components/QuestionsPanel.js.map +0 -1
- package/dist/react/components/TodoCard.d.ts.map +0 -1
- package/dist/react/components/TodoCard.js.map +0 -1
- package/dist/react/components/ToolCard.d.ts.map +0 -1
- package/dist/react/components/ToolCard.js.map +0 -1
- package/dist/react/components/composer-discovery.d.ts.map +0 -1
- package/dist/react/components/composer-discovery.js.map +0 -1
- package/dist/react/ext-event-renderer-registry.d.ts.map +0 -1
- package/dist/react/ext-event-renderer-registry.js.map +0 -1
- package/dist/react/features/chat-pane/agent-tools.d.ts.map +0 -1
- package/dist/react/features/chat-pane/agent-tools.js.map +0 -1
- package/dist/react/features/chat-pane/components/AgentRuntimePicker.d.ts.map +0 -1
- package/dist/react/features/chat-pane/components/AgentRuntimePicker.js.map +0 -1
- package/dist/react/features/chat-pane/components/ChatPane.d.ts.map +0 -1
- package/dist/react/features/chat-pane/components/ChatPane.js.map +0 -1
- package/dist/react/features/chat-pane/create-daemon-attachment-uploader.d.ts.map +0 -1
- package/dist/react/features/chat-pane/create-daemon-attachment-uploader.js.map +0 -1
- package/dist/react/features/chat-pane/create-mcp-ui-tool-caller.d.ts.map +0 -1
- package/dist/react/features/chat-pane/create-mcp-ui-tool-caller.js.map +0 -1
- package/dist/react/features/chat-pane/hooks/useAgentRuntimePicker.hooks.d.ts.map +0 -1
- package/dist/react/features/chat-pane/hooks/useAgentRuntimePicker.hooks.js.map +0 -1
- package/dist/react/features/chat-pane/hooks/useChatPane.hooks.d.ts.map +0 -1
- package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js.map +0 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneAgentControl.hooks.d.ts.map +0 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneAgentControl.hooks.js.map +0 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneComposerPlaceholder.hooks.d.ts.map +0 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneComposerPlaceholder.hooks.js.map +0 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneControlsHeight.hooks.d.ts.map +0 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneControlsHeight.hooks.js.map +0 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneFileDrop.hooks.d.ts.map +0 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneFileDrop.hooks.js.map +0 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneRuntimeInventory.hooks.d.ts.map +0 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneRuntimeInventory.hooks.js.map +0 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneWorkingDirectory.hooks.d.ts.map +0 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneWorkingDirectory.hooks.js.map +0 -1
- package/dist/react/features/chat-pane/index.d.ts.map +0 -1
- package/dist/react/features/chat-pane/index.js.map +0 -1
- package/dist/react/features/chat-pane/react/components/AgentRuntimePicker.d.ts +0 -12
- package/dist/react/features/chat-pane/react/components/AgentRuntimePicker.d.ts.map +0 -1
- package/dist/react/features/chat-pane/react/components/AgentRuntimePicker.js +0 -102
- package/dist/react/features/chat-pane/react/components/AgentRuntimePicker.js.map +0 -1
- package/dist/react/features/chat-pane/react/components/ChatPane.d.ts +0 -10
- package/dist/react/features/chat-pane/react/components/ChatPane.d.ts.map +0 -1
- package/dist/react/features/chat-pane/react/components/ChatPane.js +0 -137
- package/dist/react/features/chat-pane/react/components/ChatPane.js.map +0 -1
- package/dist/react/features/chat-pane/react/hooks/useAgentRuntimePicker.hooks.d.ts +0 -133
- package/dist/react/features/chat-pane/react/hooks/useAgentRuntimePicker.hooks.d.ts.map +0 -1
- package/dist/react/features/chat-pane/react/hooks/useAgentRuntimePicker.hooks.js +0 -258
- package/dist/react/features/chat-pane/react/hooks/useAgentRuntimePicker.hooks.js.map +0 -1
- package/dist/react/features/chat-pane/react/hooks/useChatPane.hooks.d.ts +0 -66
- package/dist/react/features/chat-pane/react/hooks/useChatPane.hooks.d.ts.map +0 -1
- package/dist/react/features/chat-pane/react/hooks/useChatPane.hooks.js +0 -223
- package/dist/react/features/chat-pane/react/hooks/useChatPane.hooks.js.map +0 -1
- package/dist/react/features/chat-pane/react/hooks/useChatPaneAgentControl.hooks.d.ts +0 -38
- package/dist/react/features/chat-pane/react/hooks/useChatPaneAgentControl.hooks.d.ts.map +0 -1
- package/dist/react/features/chat-pane/react/hooks/useChatPaneAgentControl.hooks.js +0 -252
- package/dist/react/features/chat-pane/react/hooks/useChatPaneAgentControl.hooks.js.map +0 -1
- package/dist/react/features/chat-pane/react/hooks/useChatPaneFileDrop.hooks.d.ts +0 -26
- package/dist/react/features/chat-pane/react/hooks/useChatPaneFileDrop.hooks.d.ts.map +0 -1
- package/dist/react/features/chat-pane/react/hooks/useChatPaneFileDrop.hooks.js +0 -60
- package/dist/react/features/chat-pane/react/hooks/useChatPaneFileDrop.hooks.js.map +0 -1
- package/dist/react/features/chat-pane/react/hooks/useChatPaneRuntimeInventory.hooks.d.ts +0 -25
- package/dist/react/features/chat-pane/react/hooks/useChatPaneRuntimeInventory.hooks.d.ts.map +0 -1
- package/dist/react/features/chat-pane/react/hooks/useChatPaneRuntimeInventory.hooks.js +0 -83
- package/dist/react/features/chat-pane/react/hooks/useChatPaneRuntimeInventory.hooks.js.map +0 -1
- package/dist/react/features/chat-pane/react/hooks/useChatPaneWorkingDirectory.hooks.d.ts +0 -38
- package/dist/react/features/chat-pane/react/hooks/useChatPaneWorkingDirectory.hooks.d.ts.map +0 -1
- package/dist/react/features/chat-pane/react/hooks/useChatPaneWorkingDirectory.hooks.js +0 -184
- package/dist/react/features/chat-pane/react/hooks/useChatPaneWorkingDirectory.hooks.js.map +0 -1
- package/dist/react/features/chat-pane/react/styles.d.ts +0 -2
- package/dist/react/features/chat-pane/react/styles.d.ts.map +0 -1
- package/dist/react/features/chat-pane/react/styles.js +0 -863
- package/dist/react/features/chat-pane/react/styles.js.map +0 -1
- package/dist/react/features/chat-pane/rules.d.ts.map +0 -1
- package/dist/react/features/chat-pane/rules.js.map +0 -1
- package/dist/react/features/chat-pane/styles.d.ts.map +0 -1
- package/dist/react/features/chat-pane/styles.js.map +0 -1
- package/dist/react/features/chat-pane/types.d.ts.map +0 -1
- package/dist/react/features/chat-pane/types.js.map +0 -1
- package/dist/react/features/model-picker/components/CredentialStatusBadge.d.ts.map +0 -1
- package/dist/react/features/model-picker/components/CredentialStatusBadge.js.map +0 -1
- package/dist/react/features/model-picker/components/ModelPicker.d.ts.map +0 -1
- package/dist/react/features/model-picker/components/ModelPicker.js.map +0 -1
- package/dist/react/features/model-picker/constants.d.ts.map +0 -1
- package/dist/react/features/model-picker/constants.js.map +0 -1
- package/dist/react/features/model-picker/dependencies.d.ts.map +0 -1
- package/dist/react/features/model-picker/dependencies.js.map +0 -1
- package/dist/react/features/model-picker/hooks/useModelPicker.hooks.d.ts.map +0 -1
- package/dist/react/features/model-picker/hooks/useModelPicker.hooks.js.map +0 -1
- package/dist/react/features/model-picker/index.d.ts.map +0 -1
- package/dist/react/features/model-picker/index.js.map +0 -1
- package/dist/react/features/model-picker/ports.d.ts.map +0 -1
- package/dist/react/features/model-picker/ports.js.map +0 -1
- package/dist/react/features/model-picker/react/components/CredentialStatusBadge.d.ts +0 -7
- package/dist/react/features/model-picker/react/components/CredentialStatusBadge.d.ts.map +0 -1
- package/dist/react/features/model-picker/react/components/CredentialStatusBadge.js +0 -20
- package/dist/react/features/model-picker/react/components/CredentialStatusBadge.js.map +0 -1
- package/dist/react/features/model-picker/react/components/ModelPicker.d.ts +0 -9
- package/dist/react/features/model-picker/react/components/ModelPicker.d.ts.map +0 -1
- package/dist/react/features/model-picker/react/components/ModelPicker.js +0 -34
- package/dist/react/features/model-picker/react/components/ModelPicker.js.map +0 -1
- package/dist/react/features/model-picker/react/hooks/useModelPicker.hooks.d.ts +0 -27
- package/dist/react/features/model-picker/react/hooks/useModelPicker.hooks.d.ts.map +0 -1
- package/dist/react/features/model-picker/react/hooks/useModelPicker.hooks.js +0 -62
- package/dist/react/features/model-picker/react/hooks/useModelPicker.hooks.js.map +0 -1
- package/dist/react/features/model-picker/rules.d.ts.map +0 -1
- package/dist/react/features/model-picker/rules.js.map +0 -1
- package/dist/react/features/model-picker/types.d.ts.map +0 -1
- package/dist/react/features/model-picker/types.js.map +0 -1
- package/dist/react/hooks/attachment-preview-cache.d.ts.map +0 -1
- package/dist/react/hooks/attachment-preview-cache.js.map +0 -1
- package/dist/react/hooks/composer-draft-cache.d.ts.map +0 -1
- package/dist/react/hooks/composer-draft-cache.js.map +0 -1
- package/dist/react/hooks/context.d.ts.map +0 -1
- package/dist/react/hooks/context.js.map +0 -1
- package/dist/react/hooks/testing/fake-transport.d.ts.map +0 -1
- package/dist/react/hooks/testing/fake-transport.js.map +0 -1
- package/dist/react/hooks/useArtifactStream.d.ts.map +0 -1
- package/dist/react/hooks/useArtifactStream.js.map +0 -1
- package/dist/react/hooks/useChatFabDrag.d.ts.map +0 -1
- package/dist/react/hooks/useChatFabDrag.js.map +0 -1
- package/dist/react/hooks/useComposer.d.ts.map +0 -1
- package/dist/react/hooks/useComposer.js.map +0 -1
- package/dist/react/hooks/useConversation.d.ts.map +0 -1
- package/dist/react/hooks/useConversation.js.map +0 -1
- package/dist/react/hooks/useCopyToClipboard.d.ts.map +0 -1
- package/dist/react/hooks/useCopyToClipboard.js.map +0 -1
- package/dist/react/hooks/useExtEventGroups.d.ts.map +0 -1
- package/dist/react/hooks/useExtEventGroups.js.map +0 -1
- package/dist/react/hooks/useLatestOperation.d.ts.map +0 -1
- package/dist/react/hooks/useLatestOperation.js.map +0 -1
- package/dist/react/hooks/usePinnedTodos.d.ts.map +0 -1
- package/dist/react/hooks/usePinnedTodos.js.map +0 -1
- package/dist/react/hooks/useQuestionForms.d.ts.map +0 -1
- package/dist/react/hooks/useQuestionForms.js.map +0 -1
- package/dist/react/hooks/useRunStream.d.ts.map +0 -1
- package/dist/react/hooks/useRunStream.js.map +0 -1
- package/dist/react/hooks/useToolTimeline.d.ts.map +0 -1
- package/dist/react/hooks/useToolTimeline.js.map +0 -1
- package/dist/react/index.d.ts.map +0 -1
- package/dist/react/index.js.map +0 -1
- package/dist/react/message-blocks.d.ts.map +0 -1
- package/dist/react/message-blocks.js.map +0 -1
- package/dist/react/poc-mcp-ui-official/PocMcpUiCard.d.ts.map +0 -1
- package/dist/react/poc-mcp-ui-official/PocMcpUiCard.js.map +0 -1
- package/dist/react/poc-mcp-ui-official/create-poc-resource.d.ts.map +0 -1
- package/dist/react/poc-mcp-ui-official/create-poc-resource.js.map +0 -1
- package/dist/react/slots.d.ts.map +0 -1
- package/dist/react/slots.js.map +0 -1
- package/dist/react/tool-renderer-registry.d.ts.map +0 -1
- package/dist/react/tool-renderer-registry.js.map +0 -1
- package/dist/react/util/defined-props.d.ts.map +0 -1
- package/dist/react/util/defined-props.js.map +0 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module assistant-content
|
|
3
|
+
*
|
|
4
|
+
* The ONE rule for turning a turn's events into its saved text `content`.
|
|
5
|
+
*
|
|
6
|
+
* Why it exists: `content` used to be every `text` event glued together (`out += ev.text`). A model
|
|
7
|
+
* that writes a short note, calls a tool, then answers produced `"Need project id. Find
|
|
8
|
+
* list_projects.`test_table` now exists…"` — the working note fused onto the answer mid-sentence,
|
|
9
|
+
* in the saved row, the copy button, and any transcript rebuilt from it. The live pane already
|
|
10
|
+
* shows those as separate steps (text, tool card, text); the saved string now keeps them apart too,
|
|
11
|
+
* with a blank line (a Markdown paragraph break) wherever a tool call or card sat between them.
|
|
12
|
+
*
|
|
13
|
+
* Every producer of `content` must use this: the browser (`useConversation`) and any host that
|
|
14
|
+
* saves a finished turn server-side. `interleaveMessageBlocks` accepts both this shape and the old
|
|
15
|
+
* glued one, so rows saved before this change still render in order.
|
|
16
|
+
*/
|
|
17
|
+
import type { AgentEvent } from './events.js';
|
|
18
|
+
/** What separates two text runs that a tool call or card sat between. */
|
|
19
|
+
export declare const ASSISTANT_STEP_SEPARATOR = "\n\n";
|
|
20
|
+
/**
|
|
21
|
+
* A turn's text, with a paragraph break between runs separated by a tool call or card.
|
|
22
|
+
*
|
|
23
|
+
* @param events - The turn's events, in arrival order.
|
|
24
|
+
* @complexity O(total text length).
|
|
25
|
+
*/
|
|
26
|
+
export declare function assistantContentFromEvents(events: readonly AgentEvent[] | undefined): string;
|
|
27
|
+
/** The pre-2026-09-27 rule (plain concatenation), kept only so older saved rows still match. */
|
|
28
|
+
export declare function legacyAssistantContentFromEvents(events: readonly AgentEvent[] | undefined): string;
|
|
29
|
+
//# sourceMappingURL=assistant-content.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** What separates two text runs that a tool call or card sat between. */
|
|
2
|
+
export const ASSISTANT_STEP_SEPARATOR = '\n\n';
|
|
3
|
+
function separatorBefore(out, text) {
|
|
4
|
+
if (out.length === 0 || out.endsWith(ASSISTANT_STEP_SEPARATOR) || text.startsWith('\n'))
|
|
5
|
+
return '';
|
|
6
|
+
return out.endsWith('\n') ? '\n' : ASSISTANT_STEP_SEPARATOR;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A turn's text, with a paragraph break between runs separated by a tool call or card.
|
|
10
|
+
*
|
|
11
|
+
* @param events - The turn's events, in arrival order.
|
|
12
|
+
* @complexity O(total text length).
|
|
13
|
+
*/
|
|
14
|
+
export function assistantContentFromEvents(events) {
|
|
15
|
+
let out = '';
|
|
16
|
+
let stepBoundary = false;
|
|
17
|
+
for (const ev of events ?? []) {
|
|
18
|
+
if (ev.kind === 'text') {
|
|
19
|
+
if (ev.text.length === 0)
|
|
20
|
+
continue;
|
|
21
|
+
if (stepBoundary)
|
|
22
|
+
out += separatorBefore(out, ev.text);
|
|
23
|
+
out += ev.text;
|
|
24
|
+
stepBoundary = false;
|
|
25
|
+
}
|
|
26
|
+
else if (ev.kind === 'tool_use' || ev.kind === 'ext') {
|
|
27
|
+
stepBoundary = true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
/** The pre-2026-09-27 rule (plain concatenation), kept only so older saved rows still match. */
|
|
33
|
+
export function legacyAssistantContentFromEvents(events) {
|
|
34
|
+
let out = '';
|
|
35
|
+
for (const ev of events ?? []) {
|
|
36
|
+
if (ev.kind === 'text')
|
|
37
|
+
out += ev.text;
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=assistant-content.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module compact-events
|
|
3
|
+
*
|
|
4
|
+
* Shrinks a finished turn's events before they are saved.
|
|
5
|
+
*
|
|
6
|
+
* With partial-message streaming on, the CLI reports text a few tokens at a time, so one answer
|
|
7
|
+
* arrives as ~100 `text` events (`"Here"`, `" are your"`, …). Live, that is what makes words appear
|
|
8
|
+
* as they are written. Saved, it only repeats `{"kind":"text","text":…}` per token. Joining each
|
|
9
|
+
* run of back-to-back `text` events (and back-to-back `thinking` events) into one keeps the saved
|
|
10
|
+
* row's meaning intact: nothing is reordered, and any other event between two deltas — a tool call,
|
|
11
|
+
* a card, a status or raw line — still separates them, so `assistantContentFromEvents` and the
|
|
12
|
+
* pane's step layout read the same before and after.
|
|
13
|
+
*/
|
|
14
|
+
import type { AgentEvent } from './events.js';
|
|
15
|
+
/**
|
|
16
|
+
* The events with every run of adjacent `text` (or adjacent `thinking`) events joined into one.
|
|
17
|
+
* Returns a new array; the input is not changed.
|
|
18
|
+
*
|
|
19
|
+
* @param events - A turn's events, in arrival order.
|
|
20
|
+
* @complexity O(number of events + total text length).
|
|
21
|
+
*/
|
|
22
|
+
export declare function mergeAdjacentTextEvents(events: readonly AgentEvent[] | undefined): AgentEvent[];
|
|
23
|
+
//# sourceMappingURL=compact-events.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
function isDelta(event) {
|
|
2
|
+
return event.kind === 'text' || event.kind === 'thinking';
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* The events with every run of adjacent `text` (or adjacent `thinking`) events joined into one.
|
|
6
|
+
* Returns a new array; the input is not changed.
|
|
7
|
+
*
|
|
8
|
+
* @param events - A turn's events, in arrival order.
|
|
9
|
+
* @complexity O(number of events + total text length).
|
|
10
|
+
*/
|
|
11
|
+
export function mergeAdjacentTextEvents(events) {
|
|
12
|
+
const out = [];
|
|
13
|
+
for (const event of events ?? []) {
|
|
14
|
+
const last = out[out.length - 1];
|
|
15
|
+
if (last && isDelta(event) && isDelta(last) && last.kind === event.kind) {
|
|
16
|
+
out[out.length - 1] = { kind: last.kind, text: last.text + event.text };
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
out.push(event);
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=compact-events.js.map
|
package/dist/core/events.d.ts
CHANGED
|
@@ -39,6 +39,8 @@ export type AgentEvent = {
|
|
|
39
39
|
label: string;
|
|
40
40
|
detail?: string;
|
|
41
41
|
code?: string;
|
|
42
|
+
/** Structured fields for a coded status (e.g. `api_retry`'s attempt numbers). See `run-activity.ts`. */
|
|
43
|
+
data?: Readonly<Record<string, string | number>>;
|
|
42
44
|
} | {
|
|
43
45
|
kind: 'text';
|
|
44
46
|
text: string;
|
package/dist/core/index.d.ts
CHANGED
|
@@ -16,12 +16,16 @@ export * from './events.js';
|
|
|
16
16
|
export * from './messages.js';
|
|
17
17
|
export * from './partial-json.js';
|
|
18
18
|
export * from './tool-events.js';
|
|
19
|
+
export * from './tool-output.js';
|
|
19
20
|
export * from './transport.js';
|
|
20
21
|
export * from './tools.js';
|
|
21
22
|
export * from './todos.js';
|
|
22
23
|
export * from './question-form.js';
|
|
23
24
|
export * from './util/index.js';
|
|
24
25
|
export * from './transcript.js';
|
|
26
|
+
export * from './run-activity.js';
|
|
27
|
+
export * from './assistant-content.js';
|
|
28
|
+
export * from './compact-events.js';
|
|
25
29
|
/**
|
|
26
30
|
* The chat pane's own capability manifest (`CHAT_CAPABILITIES`). The framework-free agent-control
|
|
27
31
|
* vocabulary this used to sit alongside — `CapabilityDef`, `PAGE_CAPABILITIES`, the
|
package/dist/core/index.js
CHANGED
|
@@ -16,12 +16,16 @@ export * from './events.js';
|
|
|
16
16
|
export * from './messages.js';
|
|
17
17
|
export * from './partial-json.js';
|
|
18
18
|
export * from './tool-events.js';
|
|
19
|
+
export * from './tool-output.js';
|
|
19
20
|
export * from './transport.js';
|
|
20
21
|
export * from './tools.js';
|
|
21
22
|
export * from './todos.js';
|
|
22
23
|
export * from './question-form.js';
|
|
23
24
|
export * from './util/index.js';
|
|
24
25
|
export * from './transcript.js';
|
|
26
|
+
export * from './run-activity.js';
|
|
27
|
+
export * from './assistant-content.js';
|
|
28
|
+
export * from './compact-events.js';
|
|
25
29
|
/**
|
|
26
30
|
* The chat pane's own capability manifest (`CHAT_CAPABILITIES`). The framework-free agent-control
|
|
27
31
|
* vocabulary this used to sit alongside — `CapabilityDef`, `PAGE_CAPABILITIES`, the
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module run-activity
|
|
3
|
+
*
|
|
4
|
+
* What a running turn is doing right now, as one short line ("Running Page Fill… 1 m 20 s").
|
|
5
|
+
*
|
|
6
|
+
* Why: once a turn's first tool row appeared, nothing on screen showed the run was still alive —
|
|
7
|
+
* the old "Thinking…" placeholder only showed while the message was completely empty. Model think
|
|
8
|
+
* time, long tool calls, held cards and API overload retries all looked identical: dead.
|
|
9
|
+
*
|
|
10
|
+
* Shape: {@link deriveRunActivity} reads the events the pane already has and returns the latest
|
|
11
|
+
* signal; {@link RUN_ACTIVITY_LABELS} is the ONE table that turns a signal into words. The clock
|
|
12
|
+
* (seconds since the signal began) is the caller's: the React hook ticks it once a second. Nothing
|
|
13
|
+
* here special-cases a particular tool.
|
|
14
|
+
*
|
|
15
|
+
* Status events this reads (a host translates its runtime's heartbeats into them):
|
|
16
|
+
* - `code: 'api_retry'`, `data: { attempt, maxAttempts, service? }` — the model API is overloaded.
|
|
17
|
+
* - `code: 'tool_progress'`, `data: { elapsedSeconds }` — a tool call is still running.
|
|
18
|
+
* - `code: 'thinking'` — the model is reasoning (keeps the idle timer honest).
|
|
19
|
+
*/
|
|
20
|
+
import type { AgentEvent } from './events.js';
|
|
21
|
+
/** The latest thing a running turn is doing. */
|
|
22
|
+
export type RunActivity = {
|
|
23
|
+
readonly kind: 'thinking';
|
|
24
|
+
} | {
|
|
25
|
+
readonly kind: 'writing';
|
|
26
|
+
} | {
|
|
27
|
+
readonly kind: 'finding-tool';
|
|
28
|
+
} | {
|
|
29
|
+
readonly kind: 'running-tool';
|
|
30
|
+
readonly tool: string;
|
|
31
|
+
readonly reportedSeconds?: number;
|
|
32
|
+
} | {
|
|
33
|
+
readonly kind: 'awaiting-answer';
|
|
34
|
+
} | {
|
|
35
|
+
readonly kind: 'retrying';
|
|
36
|
+
readonly attempt?: number;
|
|
37
|
+
readonly maxAttempts?: number;
|
|
38
|
+
readonly service?: string;
|
|
39
|
+
};
|
|
40
|
+
/** {@link deriveRunActivity}'s result: the signal, plus a key that changes whenever the signal restarts. */
|
|
41
|
+
export interface RunActivityState {
|
|
42
|
+
readonly activity: RunActivity;
|
|
43
|
+
/** Changes when a new signal begins, so the caller restarts its clock (e.g. `tool:<id>`). */
|
|
44
|
+
readonly key: string;
|
|
45
|
+
/** Count of events the person can see (text, tool rows, cards). A change means "something new". */
|
|
46
|
+
readonly visibleCount: number;
|
|
47
|
+
}
|
|
48
|
+
/** After this long with nothing new on screen, a thinking/writing line says "Still working…". */
|
|
49
|
+
export declare const RUN_ACTIVITY_IDLE_MS = 20000;
|
|
50
|
+
/** `"page.fill"` → `"Page Fill"`, `"mcp__supabase__list_projects"` → `"List Projects"`. Display only. */
|
|
51
|
+
export declare function humanizeToolName(name: string, input?: unknown): string;
|
|
52
|
+
/**
|
|
53
|
+
* The latest signal in a running turn's events.
|
|
54
|
+
*
|
|
55
|
+
* Precedence, newest wins: an API retry with nothing visible after it; a card that arrived while a
|
|
56
|
+
* tool call is still open (the tool is waiting on the person); the newest open tool call; else
|
|
57
|
+
* writing (last visible event is text) or thinking.
|
|
58
|
+
*
|
|
59
|
+
* @complexity O(n) in events, O(open tool calls) extra space.
|
|
60
|
+
*/
|
|
61
|
+
export declare function deriveRunActivity(events: readonly AgentEvent[] | undefined): RunActivityState;
|
|
62
|
+
/** `4` → `"4 s"`, `80` → `"1 m 20 s"`, `120` → `"2 m 0 s"`. */
|
|
63
|
+
export declare function formatActivityClock(totalSeconds: number): string;
|
|
64
|
+
/** A translator: the chat package's `t(key, vars)`. */
|
|
65
|
+
export type RunActivityTranslate = (key: string, vars?: Record<string, string | number>) => string;
|
|
66
|
+
/** The clock values a label may use. */
|
|
67
|
+
export interface RunActivityClock {
|
|
68
|
+
/** Seconds since this signal began (already corrected by any `tool_progress` report). */
|
|
69
|
+
readonly seconds: number;
|
|
70
|
+
/** Milliseconds since anything new appeared on screen. */
|
|
71
|
+
readonly idleMs: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* THE event → words table. One entry per {@link RunActivity} kind; nothing else in the package
|
|
75
|
+
* decides what the activity line says. Keys are English source strings for the host's `t()`.
|
|
76
|
+
*/
|
|
77
|
+
export declare const RUN_ACTIVITY_LABELS: {
|
|
78
|
+
readonly [K in RunActivity['kind']]: (activity: Extract<RunActivity, {
|
|
79
|
+
kind: K;
|
|
80
|
+
}>, clock: RunActivityClock, t: RunActivityTranslate) => string;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* The activity line's text for a signal and clock.
|
|
84
|
+
*
|
|
85
|
+
* @complexity O(1).
|
|
86
|
+
*/
|
|
87
|
+
export declare function describeRunActivity(activity: RunActivity, clock: RunActivityClock, t: RunActivityTranslate): string;
|
|
88
|
+
//# sourceMappingURL=run-activity.d.ts.map
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/** Tool names whose only job is finding the right tool (MCP tool search / schema lookup). */
|
|
2
|
+
const DISCOVERY_TOOL_NAMES = new Set([
|
|
3
|
+
'ToolSearch',
|
|
4
|
+
'search_tools',
|
|
5
|
+
'describe_tool',
|
|
6
|
+
'mcp__jini__search_tools',
|
|
7
|
+
'mcp__jini__describe_tool',
|
|
8
|
+
]);
|
|
9
|
+
/** Transport wrappers whose real tool id is in `input.toolId`. */
|
|
10
|
+
const WRAPPER_TOOL_NAMES = new Set(['execute_delegated_tool', 'mcp__jini__execute_delegated_tool']);
|
|
11
|
+
/** Ext events that are a card for the person (a form, a confirm, a sign-in). */
|
|
12
|
+
const CARD_EXT_NAMES = new Set(['mcp-ui', 'a2ui']);
|
|
13
|
+
/** After this long with nothing new on screen, a thinking/writing line says "Still working…". */
|
|
14
|
+
export const RUN_ACTIVITY_IDLE_MS = 20_000;
|
|
15
|
+
function isRecord(value) {
|
|
16
|
+
return typeof value === 'object' && value !== null;
|
|
17
|
+
}
|
|
18
|
+
function numberField(data, key) {
|
|
19
|
+
const value = isRecord(data) ? data[key] : undefined;
|
|
20
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
21
|
+
}
|
|
22
|
+
function stringField(data, key) {
|
|
23
|
+
const value = isRecord(data) ? data[key] : undefined;
|
|
24
|
+
return typeof value === 'string' && value ? value : undefined;
|
|
25
|
+
}
|
|
26
|
+
/** `"page.fill"` → `"Page Fill"`, `"mcp__supabase__list_projects"` → `"List Projects"`. Display only. */
|
|
27
|
+
export function humanizeToolName(name, input) {
|
|
28
|
+
const real = WRAPPER_TOOL_NAMES.has(name) ? (stringField(input, 'toolId') ?? name) : name;
|
|
29
|
+
const bare = real.startsWith('mcp__') ? real.split('__').slice(2).join('__') || real : real;
|
|
30
|
+
return bare
|
|
31
|
+
.split(/[._\s]+/)
|
|
32
|
+
.filter(Boolean)
|
|
33
|
+
.map((word) => word[0].toUpperCase() + word.slice(1))
|
|
34
|
+
.join(' ');
|
|
35
|
+
}
|
|
36
|
+
function isVisible(event) {
|
|
37
|
+
return event.kind === 'text' ? event.text.length > 0 : event.kind === 'tool_use' || event.kind === 'tool_result' || event.kind === 'ext';
|
|
38
|
+
}
|
|
39
|
+
function scanEvent(scan, event, index) {
|
|
40
|
+
if (isVisible(event)) {
|
|
41
|
+
scan.lastVisible = { index, kind: event.kind };
|
|
42
|
+
scan.visibleCount += 1;
|
|
43
|
+
}
|
|
44
|
+
if (event.kind === 'tool_use') {
|
|
45
|
+
scan.open = scan.open.filter((tool) => tool.id !== event.id);
|
|
46
|
+
scan.open.push({ id: event.id, name: event.name, input: event.input, index });
|
|
47
|
+
}
|
|
48
|
+
else if (event.kind === 'tool_result') {
|
|
49
|
+
scan.open = scan.open.filter((tool) => tool.id !== event.toolUseId);
|
|
50
|
+
}
|
|
51
|
+
else if (event.kind === 'ext' && CARD_EXT_NAMES.has(event.name)) {
|
|
52
|
+
scan.lastCardIndex = index;
|
|
53
|
+
}
|
|
54
|
+
else if (event.kind === 'status' && event.code === 'api_retry') {
|
|
55
|
+
scan.retry = { index, data: event.data };
|
|
56
|
+
}
|
|
57
|
+
else if (event.kind === 'status' && event.code === 'tool_progress') {
|
|
58
|
+
const seconds = numberField(event.data, 'elapsedSeconds');
|
|
59
|
+
if (seconds !== undefined)
|
|
60
|
+
scan.progress = { index, seconds };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The latest signal in a running turn's events.
|
|
65
|
+
*
|
|
66
|
+
* Precedence, newest wins: an API retry with nothing visible after it; a card that arrived while a
|
|
67
|
+
* tool call is still open (the tool is waiting on the person); the newest open tool call; else
|
|
68
|
+
* writing (last visible event is text) or thinking.
|
|
69
|
+
*
|
|
70
|
+
* @complexity O(n) in events, O(open tool calls) extra space.
|
|
71
|
+
*/
|
|
72
|
+
export function deriveRunActivity(events) {
|
|
73
|
+
const scan = { open: [], lastVisible: null, lastCardIndex: -1, retry: null, progress: null, visibleCount: 0 };
|
|
74
|
+
(events ?? []).forEach((event, index) => scanEvent(scan, event, index));
|
|
75
|
+
const visibleCount = scan.visibleCount;
|
|
76
|
+
const lastVisibleIndex = scan.lastVisible?.index ?? -1;
|
|
77
|
+
if (scan.retry && scan.retry.index > lastVisibleIndex) {
|
|
78
|
+
const data = scan.retry.data;
|
|
79
|
+
const attempt = numberField(data, 'attempt');
|
|
80
|
+
const maxAttempts = numberField(data, 'maxAttempts');
|
|
81
|
+
const service = stringField(data, 'service');
|
|
82
|
+
return {
|
|
83
|
+
activity: {
|
|
84
|
+
kind: 'retrying',
|
|
85
|
+
...(attempt !== undefined ? { attempt } : {}),
|
|
86
|
+
...(maxAttempts !== undefined ? { maxAttempts } : {}),
|
|
87
|
+
...(service !== undefined ? { service } : {}),
|
|
88
|
+
},
|
|
89
|
+
key: `retry:${lastVisibleIndex}`,
|
|
90
|
+
visibleCount,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const newestOpen = scan.open[scan.open.length - 1];
|
|
94
|
+
if (newestOpen) {
|
|
95
|
+
if (scan.lastCardIndex > newestOpen.index) {
|
|
96
|
+
return { activity: { kind: 'awaiting-answer' }, key: `card:${scan.lastCardIndex}`, visibleCount };
|
|
97
|
+
}
|
|
98
|
+
if (DISCOVERY_TOOL_NAMES.has(newestOpen.name)) {
|
|
99
|
+
return { activity: { kind: 'finding-tool' }, key: `tool:${newestOpen.id}`, visibleCount };
|
|
100
|
+
}
|
|
101
|
+
const reported = scan.progress && scan.progress.index > newestOpen.index ? scan.progress.seconds : undefined;
|
|
102
|
+
return {
|
|
103
|
+
activity: {
|
|
104
|
+
kind: 'running-tool',
|
|
105
|
+
tool: humanizeToolName(newestOpen.name, newestOpen.input),
|
|
106
|
+
...(reported !== undefined ? { reportedSeconds: reported } : {}),
|
|
107
|
+
},
|
|
108
|
+
key: `tool:${newestOpen.id}`,
|
|
109
|
+
visibleCount,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (scan.lastVisible?.kind === 'text') {
|
|
113
|
+
return { activity: { kind: 'writing' }, key: `writing:${lastVisibleIndex}`, visibleCount };
|
|
114
|
+
}
|
|
115
|
+
return { activity: { kind: 'thinking' }, key: `thinking:${lastVisibleIndex}`, visibleCount };
|
|
116
|
+
}
|
|
117
|
+
/** `4` → `"4 s"`, `80` → `"1 m 20 s"`, `120` → `"2 m 0 s"`. */
|
|
118
|
+
export function formatActivityClock(totalSeconds) {
|
|
119
|
+
const seconds = Math.max(0, Math.floor(totalSeconds));
|
|
120
|
+
const minutes = Math.floor(seconds / 60);
|
|
121
|
+
return minutes > 0 ? `${minutes} m ${seconds % 60} s` : `${seconds} s`;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* THE event → words table. One entry per {@link RunActivity} kind; nothing else in the package
|
|
125
|
+
* decides what the activity line says. Keys are English source strings for the host's `t()`.
|
|
126
|
+
*/
|
|
127
|
+
export const RUN_ACTIVITY_LABELS = {
|
|
128
|
+
thinking: (_a, clock, t) => clock.idleMs >= RUN_ACTIVITY_IDLE_MS
|
|
129
|
+
? t('Still working… {time}', { time: formatActivityClock(clock.seconds) })
|
|
130
|
+
: t('Thinking… {time}', { time: formatActivityClock(clock.seconds) }),
|
|
131
|
+
writing: (_a, clock, t) => clock.idleMs >= RUN_ACTIVITY_IDLE_MS
|
|
132
|
+
? t('Still working… {time}', { time: formatActivityClock(clock.seconds) })
|
|
133
|
+
: t('Writing…'),
|
|
134
|
+
'finding-tool': (_a, clock, t) => t('Finding the right tool… {time}', { time: formatActivityClock(clock.seconds) }),
|
|
135
|
+
'running-tool': (a, clock, t) => t('Running {tool}… {time}', { tool: a.tool, time: formatActivityClock(clock.seconds) }),
|
|
136
|
+
'awaiting-answer': (_a, clock, t) => t('Waiting for your answer above · {time}', { time: formatActivityClock(clock.seconds) }),
|
|
137
|
+
retrying: (a, _clock, t) => {
|
|
138
|
+
const who = a.service ? t("{service}'s servers are busy", { service: a.service }) : t('The AI service is busy');
|
|
139
|
+
return a.attempt !== undefined && a.maxAttempts !== undefined
|
|
140
|
+
? t('{who} — retrying ({attempt} of {max})…', { who, attempt: a.attempt, max: a.maxAttempts })
|
|
141
|
+
: t('{who} — retrying…', { who });
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* The activity line's text for a signal and clock.
|
|
146
|
+
*
|
|
147
|
+
* @complexity O(1).
|
|
148
|
+
*/
|
|
149
|
+
export function describeRunActivity(activity, clock, t) {
|
|
150
|
+
const label = RUN_ACTIVITY_LABELS[activity.kind];
|
|
151
|
+
return label(activity, clock, t);
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=run-activity.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a tool result's text for a person to read. Tool results travel to the model as compact
|
|
3
|
+
* JSON (`@jini-ai/mcp`'s `okResult` — indentation made nested results ~2.5× larger), so a view that
|
|
4
|
+
* shows one re-indents it here, at render time. Only a JSON object or array is re-indented; any other
|
|
5
|
+
* text (shell output, an error message, a bare JSON scalar, something that merely starts with `{`)
|
|
6
|
+
* comes back unchanged.
|
|
7
|
+
*
|
|
8
|
+
* @param text - A `tool_result` event's flattened `content`.
|
|
9
|
+
* @returns The value re-serialized with two-space indentation, or `text` as given.
|
|
10
|
+
* @complexity O(n) in `text.length`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatToolOutputForDisplay(text: string): string;
|
|
13
|
+
//# sourceMappingURL=tool-output.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a tool result's text for a person to read. Tool results travel to the model as compact
|
|
3
|
+
* JSON (`@jini-ai/mcp`'s `okResult` — indentation made nested results ~2.5× larger), so a view that
|
|
4
|
+
* shows one re-indents it here, at render time. Only a JSON object or array is re-indented; any other
|
|
5
|
+
* text (shell output, an error message, a bare JSON scalar, something that merely starts with `{`)
|
|
6
|
+
* comes back unchanged.
|
|
7
|
+
*
|
|
8
|
+
* @param text - A `tool_result` event's flattened `content`.
|
|
9
|
+
* @returns The value re-serialized with two-space indentation, or `text` as given.
|
|
10
|
+
* @complexity O(n) in `text.length`.
|
|
11
|
+
*/
|
|
12
|
+
export function formatToolOutputForDisplay(text) {
|
|
13
|
+
const trimmed = text.trim();
|
|
14
|
+
const first = trimmed[0];
|
|
15
|
+
if (first !== '{' && first !== '[')
|
|
16
|
+
return text;
|
|
17
|
+
try {
|
|
18
|
+
return JSON.stringify(JSON.parse(trimmed), null, 2);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return text;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=tool-output.js.map
|
|
@@ -131,6 +131,7 @@ function collectTableRows(lines, start) {
|
|
|
131
131
|
}
|
|
132
132
|
return { rows, next: i };
|
|
133
133
|
}
|
|
134
|
+
const ORDERED_ITEM = /^\s*\d+[.)]\s+/;
|
|
134
135
|
function parseBlocks(input) {
|
|
135
136
|
const lines = input.split(/\r?\n/);
|
|
136
137
|
const blocks = [];
|
|
@@ -204,13 +205,14 @@ function parseBlocks(input) {
|
|
|
204
205
|
i = next;
|
|
205
206
|
continue;
|
|
206
207
|
}
|
|
207
|
-
if (
|
|
208
|
+
if (ORDERED_ITEM.test(line)) {
|
|
209
|
+
const start = Number(/\d+/.exec(line)[0]);
|
|
208
210
|
const items = [];
|
|
209
|
-
while (i < lines.length &&
|
|
210
|
-
items.push(lines[i].replace(
|
|
211
|
-
i
|
|
211
|
+
while (i < lines.length && ORDERED_ITEM.test(lines[i])) {
|
|
212
|
+
items.push(lines[i].replace(ORDERED_ITEM, ''));
|
|
213
|
+
i = nextLooseListLine(lines, i + 1);
|
|
212
214
|
}
|
|
213
|
-
blocks.push({ kind: 'ol', items });
|
|
215
|
+
blocks.push({ kind: 'ol', start, items });
|
|
214
216
|
continue;
|
|
215
217
|
}
|
|
216
218
|
const paragraph = [];
|
|
@@ -222,6 +224,18 @@ function parseBlocks(input) {
|
|
|
222
224
|
}
|
|
223
225
|
return blocks;
|
|
224
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* A "loose" numbered list puts a blank line between items ("1. a\n\n2. b").
|
|
229
|
+
* Skip those blank lines when the next non-blank line is another numbered
|
|
230
|
+
* item, so the items stay in ONE `<ol>` (numbered 1..n) instead of n lists
|
|
231
|
+
* that each show "1.". Otherwise return `i` unchanged and the list ends.
|
|
232
|
+
*/
|
|
233
|
+
function nextLooseListLine(lines, i) {
|
|
234
|
+
let j = i;
|
|
235
|
+
while (j < lines.length && lines[j].trim() === '')
|
|
236
|
+
j += 1;
|
|
237
|
+
return j > i && j < lines.length && ORDERED_ITEM.test(lines[j]) ? j : i;
|
|
238
|
+
}
|
|
225
239
|
function isBlockStart(lines, i) {
|
|
226
240
|
const line = lines[i];
|
|
227
241
|
return (/^```/.test(line)
|
|
@@ -229,7 +243,7 @@ function isBlockStart(lines, i) {
|
|
|
229
243
|
|| /^(-{3,}|\*{3,}|_{3,})\s*$/.test(line)
|
|
230
244
|
|| /^>\s?/.test(line)
|
|
231
245
|
|| /^\s*[-*]\s+/.test(line)
|
|
232
|
-
||
|
|
246
|
+
|| ORDERED_ITEM.test(line)
|
|
233
247
|
|| isTableStartAt(lines, i));
|
|
234
248
|
}
|
|
235
249
|
function renderBlock(block) {
|
|
@@ -243,7 +257,7 @@ function renderBlock(block) {
|
|
|
243
257
|
case 'ul':
|
|
244
258
|
return (_jsx("ul", { children: block.items.map((item, i) => (_jsx("li", { children: renderInline(item) }, i))) }));
|
|
245
259
|
case 'ol':
|
|
246
|
-
return (_jsx("ol", { children: block.items.map((item, i) => (_jsx("li", { children: renderInline(item) }, i))) }));
|
|
260
|
+
return (_jsx("ol", { start: block.start === 1 ? undefined : block.start, children: block.items.map((item, i) => (_jsx("li", { children: renderInline(item) }, i))) }));
|
|
247
261
|
case 'bq':
|
|
248
262
|
return _jsx("blockquote", { children: renderInline(block.text) });
|
|
249
263
|
case 'code':
|
|
@@ -24,7 +24,15 @@ export interface McpUiSurfaceCardProps extends ExtEventRenderProps {
|
|
|
24
24
|
maxHeight?: number;
|
|
25
25
|
}
|
|
26
26
|
/** Registered against `ext-event-renderer-registry.ts`'s `'mcp-ui'` name — see module doc. */
|
|
27
|
-
export declare function McpUiSurfaceCard({ events, sandboxProxyUrl, onToolCall, onOpenLink, maxHeight }: McpUiSurfaceCardProps): import("react").JSX.Element;
|
|
27
|
+
export declare function McpUiSurfaceCard({ events, sandboxProxyUrl, onToolCall, onOpenLink, maxHeight, call, runStreaming }: McpUiSurfaceCardProps): import("react").JSX.Element;
|
|
28
|
+
/**
|
|
29
|
+
* The ext-event slot key for MCP-UI: one slot per `ui://` URI. Each resource is its own card, so a
|
|
30
|
+
* run's second card renders where it arrived instead of inside the first card's slot, while a
|
|
31
|
+
* re-emitted resource with the same URI still updates its card in place. Pass it as
|
|
32
|
+
* `registerExtEventRenderer(MCP_UI_EXT_EVENT_NAME, renderer, { slotKey: mcpUiSurfaceSlotKey })`
|
|
33
|
+
* when registering a custom MCP-UI renderer; {@link registerMcpUiSurfaceRenderer} already does.
|
|
34
|
+
*/
|
|
35
|
+
export declare function mcpUiSurfaceSlotKey(data: unknown): string | undefined;
|
|
28
36
|
/**
|
|
29
37
|
* Registers {@link McpUiSurfaceCard} against the ext-event registry.
|
|
30
38
|
*
|
|
@@ -112,10 +112,41 @@ function pendingMirrorLabel(t, plan) {
|
|
|
112
112
|
function PendingSurfaceMirror({ uri, plan, t }) {
|
|
113
113
|
return (_jsx("div", { "aria-hidden": "true", style: { display: 'none' }, "data-agent-element": pendingMirrorHandle(uri), "data-agent-role": "status", "data-agent-label": pendingMirrorLabel(t, plan) }));
|
|
114
114
|
}
|
|
115
|
+
/** Words in a finished call's result that mean nobody answered in time. */
|
|
116
|
+
const UNANSWERED_RESULT = /\b(expired|abandoned|did not respond|run ended)\b/i;
|
|
117
|
+
/**
|
|
118
|
+
* Why a card can no longer be answered, or `undefined` while it still can.
|
|
119
|
+
*
|
|
120
|
+
* A held-open question (the tool shows the card and waits) is over once its tool call returns, and
|
|
121
|
+
* dead once its run ends without that return. Its first document stays on screen either way, so
|
|
122
|
+
* without this an expired, answered or orphaned question still looked answerable (Tovu stuck-chat
|
|
123
|
+
* investigation, 2026-09-27). Only a card still showing that FIRST document closes: a tool that sent
|
|
124
|
+
* a follow-up document for the same `ui://` URI (an outcome after the answer) keeps showing it.
|
|
125
|
+
*
|
|
126
|
+
* The labels are read from the call's result text, so they are a best guess for a producer this
|
|
127
|
+
* package has not seen; the card is closed either way, which is the property that matters.
|
|
128
|
+
*/
|
|
129
|
+
function closedLabel(t, call, runStreaming, documentsForUri) {
|
|
130
|
+
if (call === undefined || documentsForUri > 1)
|
|
131
|
+
return undefined;
|
|
132
|
+
if (call.result === undefined)
|
|
133
|
+
return runStreaming ? undefined : t('This question expired');
|
|
134
|
+
return UNANSWERED_RESULT.test(call.result.content) ? t('This question expired') : t('Answered');
|
|
135
|
+
}
|
|
136
|
+
/** A closed card: the question's title, when the call gave one, and why it is closed. */
|
|
137
|
+
function ClosedSurface({ call, label }) {
|
|
138
|
+
const title = call?.input?.title;
|
|
139
|
+
return (_jsx("div", { className: "mcpui-surface-card mcpui-surface-card-closed", role: "status", children: typeof title === 'string' && title.trim() !== '' ? `${title}: ${label}` : label }));
|
|
140
|
+
}
|
|
115
141
|
/** Registered against `ext-event-renderer-registry.ts`'s `'mcp-ui'` name — see module doc. */
|
|
116
|
-
export function McpUiSurfaceCard({ events, sandboxProxyUrl, onToolCall, onOpenLink, maxHeight }) {
|
|
142
|
+
export function McpUiSurfaceCard({ events, sandboxProxyUrl, onToolCall, onOpenLink, maxHeight, call, runStreaming }) {
|
|
117
143
|
const t = useT();
|
|
118
144
|
const resources = useMemo(() => latestResourcesByUri(events), [events]);
|
|
145
|
+
// One card per slot is the registered shape (`mcpUiSurfaceSlotKey`), so `call` belongs to exactly
|
|
146
|
+
// that card. A group holding several URIs has no single call to judge them by, and stays live.
|
|
147
|
+
const closed = resources.length === 1 ? closedLabel(t, call, runStreaming, events.length) : undefined;
|
|
148
|
+
if (closed !== undefined)
|
|
149
|
+
return _jsx(ClosedSurface, { call: call, label: closed });
|
|
119
150
|
if (resources.length === 0) {
|
|
120
151
|
// Visible rather than silent: an `ext` event named `mcp-ui` that carries no parseable resource
|
|
121
152
|
// is a producer bug, and a transcript that renders nothing for it hides the bug behind what
|
|
@@ -137,6 +168,16 @@ export function McpUiSurfaceCard({ events, sandboxProxyUrl, onToolCall, onOpenLi
|
|
|
137
168
|
sessionKey: `${resource.resource.uri}:${resource.resource.text}`, ...(onToolCall === undefined ? {} : { onToolCall }), ...(onOpenLink === undefined ? {} : { onOpenLink }), ...(height === undefined || Number.isNaN(height) ? {} : { initialHeight: height }), ...(maxHeight === undefined ? {} : { maxHeight }) }), plan !== undefined && _jsx(PendingSurfaceMirror, { uri: resource.resource.uri, plan: plan, t: t })] }, resource.resource.uri));
|
|
138
169
|
}) }));
|
|
139
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* The ext-event slot key for MCP-UI: one slot per `ui://` URI. Each resource is its own card, so a
|
|
173
|
+
* run's second card renders where it arrived instead of inside the first card's slot, while a
|
|
174
|
+
* re-emitted resource with the same URI still updates its card in place. Pass it as
|
|
175
|
+
* `registerExtEventRenderer(MCP_UI_EXT_EVENT_NAME, renderer, { slotKey: mcpUiSurfaceSlotKey })`
|
|
176
|
+
* when registering a custom MCP-UI renderer; {@link registerMcpUiSurfaceRenderer} already does.
|
|
177
|
+
*/
|
|
178
|
+
export function mcpUiSurfaceSlotKey(data) {
|
|
179
|
+
return parseUIResource(data)?.resource.uri;
|
|
180
|
+
}
|
|
140
181
|
/**
|
|
141
182
|
* Registers {@link McpUiSurfaceCard} against the ext-event registry.
|
|
142
183
|
*
|
|
@@ -154,6 +195,6 @@ export function McpUiSurfaceCard({ events, sandboxProxyUrl, onToolCall, onOpenLi
|
|
|
154
195
|
* @returns An unregister handle, so a test or a hot reload can dispose cleanly.
|
|
155
196
|
*/
|
|
156
197
|
export function registerMcpUiSurfaceRenderer(options) {
|
|
157
|
-
return registerExtEventRenderer(options.name ?? MCP_UI_EXT_EVENT_NAME, (props) => (_jsx(McpUiSurfaceCard, { ...props, sandboxProxyUrl: options.sandboxProxyUrl, ...(options.onToolCall === undefined ? {} : { onToolCall: options.onToolCall }), ...(options.onOpenLink === undefined ? {} : { onOpenLink: options.onOpenLink }), ...(options.maxHeight === undefined ? {} : { maxHeight: options.maxHeight }) })));
|
|
198
|
+
return registerExtEventRenderer(options.name ?? MCP_UI_EXT_EVENT_NAME, (props) => (_jsx(McpUiSurfaceCard, { ...props, sandboxProxyUrl: options.sandboxProxyUrl, ...(options.onToolCall === undefined ? {} : { onToolCall: options.onToolCall }), ...(options.onOpenLink === undefined ? {} : { onOpenLink: options.onOpenLink }), ...(options.maxHeight === undefined ? {} : { maxHeight: options.maxHeight }) })), { slotKey: mcpUiSurfaceSlotKey });
|
|
158
199
|
}
|
|
159
200
|
//# sourceMappingURL=McpUiSurfaceCard.js.map
|