@jini-ai/chat 0.3.10 → 0.5.0
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/CHANGELOG.md +126 -0
- package/README.md +152 -6
- package/dist/core/ag-ui/entry.d.ts +53 -0
- package/dist/core/ag-ui/entry.js +135 -0
- package/dist/core/agentic/chat-capabilities.d.ts +1 -1
- package/dist/core/agentic/index.d.ts +1 -1
- package/dist/core/agentic/index.js +1 -1
- package/dist/core/assistant-content.d.ts +6 -2
- package/dist/core/assistant-content.js +2 -2
- package/dist/core/compact-events.d.ts +3 -1
- package/dist/core/compact-events.js +1 -1
- package/dist/core/events.d.ts +1 -1
- package/dist/core/events.js +1 -1
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +3 -3
- package/dist/core/messages.d.ts +4 -2
- package/dist/core/messages.js +2 -2
- package/dist/core/partial-json.d.ts +6 -2
- package/dist/core/partial-json.js +3 -3
- package/dist/core/persistence/index.d.ts +1 -1
- package/dist/core/persistence/index.js +1 -1
- package/dist/core/persistence/ports.d.ts +2 -123
- package/dist/core/persistence/title.d.ts +3 -1
- package/dist/core/persistence/title.js +1 -1
- package/dist/core/question-form/format.d.ts +16 -7
- package/dist/core/question-form/format.js +3 -3
- package/dist/core/question-form/normalize.d.ts +7 -2
- package/dist/core/question-form/normalize.js +5 -5
- package/dist/core/question-form/scan.d.ts +18 -6
- package/dist/core/question-form/scan.js +14 -14
- package/dist/core/run-activity.d.ts +16 -4
- package/dist/core/run-activity.js +11 -11
- package/dist/core/run-events/entry.d.ts +121 -0
- package/dist/core/run-events/entry.js +290 -0
- package/dist/core/todos.d.ts +20 -8
- package/dist/core/todos.js +11 -11
- package/dist/core/tool-events.d.ts +3 -1
- package/dist/core/tool-events.js +1 -1
- package/dist/core/tool-output.d.ts +3 -1
- package/dist/core/tool-output.js +1 -1
- package/dist/core/tools.d.ts +14 -3
- package/dist/core/tools.js +3 -3
- package/dist/core/transcript.d.ts +13 -5
- package/dist/core/transcript.js +5 -5
- package/dist/core/util/manifest.d.ts +9 -3
- package/dist/core/util/manifest.js +3 -3
- package/dist/core/util/markdown-context.d.ts +11 -3
- package/dist/core/util/markdown-context.js +3 -3
- package/dist/core/util/markup-attributes.d.ts +3 -1
- package/dist/core/util/markup-attributes.js +1 -1
- package/dist/core/util/parser.d.ts +9 -5
- package/dist/core/util/parser.js +13 -13
- package/dist/core/util/recover.d.ts +6 -2
- package/dist/core/util/recover.js +6 -6
- package/dist/core/util/strip.d.ts +19 -5
- package/dist/core/util/strip.js +17 -17
- package/dist/core/util/validate.d.ts +3 -1
- package/dist/core/util/validate.js +1 -1
- package/dist/embed-widget/browser/page-actions.d.ts +121 -0
- package/dist/embed-widget/browser/page-actions.js +113 -0
- package/dist/embed-widget/browser/session-store.d.ts +44 -0
- package/dist/embed-widget/browser/session-store.js +103 -0
- package/dist/embed-widget/react/embed.d.ts +45 -0
- package/dist/embed-widget/react/embed.js +87 -0
- package/dist/embed-widget/react/header.d.ts +21 -0
- package/dist/embed-widget/react/header.js +18 -0
- package/dist/embed-widget/transports/fetch-sse.d.ts +75 -0
- package/dist/embed-widget/transports/fetch-sse.js +175 -0
- package/dist/react/agent-bridge/frontend-session-bridge.d.ts +15 -3
- package/dist/react/agent-bridge/frontend-session-bridge.js +13 -6
- package/dist/react/artifact-types.d.ts +3 -3
- package/dist/react/artifact-types.js +3 -3
- package/dist/react/components/A2uiSurfaceCard.js +34 -13
- package/dist/react/components/AttachmentPreviewModal.hooks.d.ts +7 -2
- package/dist/react/components/AttachmentPreviewModal.hooks.js +5 -5
- package/dist/react/components/AttachmentPreviewModal.js +2 -2
- package/dist/react/components/AttachmentTray.d.ts +3 -1
- package/dist/react/components/AttachmentTray.js +2 -2
- package/dist/react/components/ChatFab.js +1 -1
- package/dist/react/components/Composer.js +9 -9
- package/dist/react/components/ConversationList.js +1 -1
- package/dist/react/components/McpUiSurfaceCard.d.ts +4 -2
- package/dist/react/components/McpUiSurfaceCard.js +5 -5
- package/dist/react/components/MessageList.js +1 -1
- package/dist/react/components/MessageRow.js +9 -9
- package/dist/react/components/QuestionForm.js +5 -5
- package/dist/react/components/ToolCard.js +9 -9
- package/dist/react/components/composer-discovery.d.ts +29 -8
- package/dist/react/components/composer-discovery.js +10 -10
- package/dist/react/ext-event-renderer-registry.d.ts +15 -5
- package/dist/react/ext-event-renderer-registry.js +4 -4
- package/dist/react/features/chat-pane/components/AgentRuntimePicker.d.ts +3 -1
- package/dist/react/features/chat-pane/components/AgentRuntimePicker.js +3 -3
- package/dist/react/features/chat-pane/components/ChatPane.js +43 -43
- package/dist/react/features/chat-pane/create-daemon-attachment-uploader.d.ts +4 -1
- package/dist/react/features/chat-pane/create-daemon-attachment-uploader.js +5 -35
- package/dist/react/features/chat-pane/create-mcp-ui-tool-caller.d.ts +5 -2
- package/dist/react/features/chat-pane/create-mcp-ui-tool-caller.js +1 -1
- package/dist/react/features/chat-pane/hooks/useAgentRuntimePicker.hooks.d.ts +38 -10
- package/dist/react/features/chat-pane/hooks/useAgentRuntimePicker.hooks.js +15 -15
- package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js +31 -31
- package/dist/react/features/chat-pane/hooks/useChatPaneAgentControl.hooks.d.ts +3 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneAgentControl.hooks.js +3 -3
- package/dist/react/features/chat-pane/hooks/useChatPaneComposerPlaceholder.hooks.d.ts +4 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneComposerPlaceholder.hooks.js +1 -1
- package/dist/react/features/chat-pane/hooks/useChatPaneRuntimeInventory.hooks.d.ts +3 -0
- package/dist/react/features/chat-pane/hooks/useChatPaneRuntimeInventory.hooks.js +80 -38
- package/dist/react/features/chat-pane/hooks/useChatPaneWorkingDirectory.hooks.js +57 -57
- package/dist/react/features/chat-pane/rules.d.ts +13 -4
- package/dist/react/features/chat-pane/rules.js +5 -5
- package/dist/react/features/chat-pane/types.d.ts +1 -1
- package/dist/react/hooks/attachment-preview-cache.d.ts +7 -2
- package/dist/react/hooks/attachment-preview-cache.js +2 -2
- package/dist/react/hooks/composer-draft-cache.d.ts +17 -5
- package/dist/react/hooks/composer-draft-cache.js +6 -6
- package/dist/react/hooks/useArtifactStream.d.ts +5 -1
- package/dist/react/hooks/useArtifactStream.js +3 -3
- package/dist/react/hooks/useChatFabDrag.d.ts +9 -4
- package/dist/react/hooks/useChatFabDrag.js +4 -4
- package/dist/react/hooks/useComposer.js +8 -8
- package/dist/react/hooks/useConversation.js +56 -4
- package/dist/react/hooks/useExtEventGroups.d.ts +5 -1
- package/dist/react/hooks/useExtEventGroups.js +1 -1
- package/dist/react/hooks/useLatestOperation.d.ts +8 -3
- package/dist/react/hooks/useLatestOperation.js +4 -4
- package/dist/react/hooks/usePinnedTodos.d.ts +3 -1
- package/dist/react/hooks/usePinnedTodos.js +3 -3
- package/dist/react/hooks/useQuestionForms.d.ts +7 -2
- package/dist/react/hooks/useQuestionForms.js +7 -7
- package/dist/react/hooks/useRunActivity.d.ts +6 -1
- package/dist/react/hooks/useRunActivity.js +3 -3
- package/dist/react/hooks/useRunStream.d.ts +3 -1
- package/dist/react/hooks/useRunStream.js +1 -1
- package/dist/react/hooks/useToolTimeline.d.ts +3 -1
- package/dist/react/hooks/useToolTimeline.js +3 -3
- package/dist/react/index.d.ts +4 -4
- package/dist/react/index.js +6 -6
- package/dist/react/message-blocks.d.ts +10 -4
- package/dist/react/message-blocks.js +2 -2
- package/dist/react/styles/reference.css +3 -3
- package/dist/react/tool-renderer-registry.d.ts +8 -3
- package/dist/react/tool-renderer-registry.js +3 -3
- package/dist/react/util/defined-props.d.ts +3 -1
- package/dist/react/util/defined-props.js +1 -1
- package/dist/server/run-finalizer/entry.d.ts +47 -0
- package/dist/server/run-finalizer/entry.js +156 -0
- package/dist/server/run-finalizer/ports.d.ts +52 -0
- package/dist/store/contracts/index.d.ts +5 -0
- package/dist/store/contracts/index.js +5 -0
- package/dist/store/errors.d.ts +12 -0
- package/dist/store/errors.js +18 -0
- package/dist/store/index.d.ts +4 -0
- package/dist/store/index.js +2 -0
- package/dist/store/legacy/index.d.ts +3 -0
- package/dist/store/legacy/index.js +2 -0
- package/dist/store/legacy/sqlite/conversations.d.ts +129 -0
- package/dist/store/legacy/sqlite/conversations.js +274 -0
- package/dist/store/legacy/sqlite/index.d.ts +6 -0
- package/dist/store/legacy/sqlite/index.js +5 -0
- package/dist/store/legacy/sqlite/messages.d.ts +156 -0
- package/dist/store/legacy/sqlite/messages.js +246 -0
- package/dist/store/legacy/sqlite/schema.d.ts +3 -0
- package/dist/store/legacy/sqlite/schema.js +40 -0
- package/dist/store/pglite/index.d.ts +21 -0
- package/dist/store/pglite/index.js +17 -0
- package/dist/store/ports.d.ts +121 -0
- package/dist/store/ports.js +2 -0
- package/dist/store/postgres/index.d.ts +21 -0
- package/dist/store/postgres/index.js +17 -0
- package/dist/store/sql/boundary.d.ts +28 -0
- package/dist/store/sql/boundary.js +32 -0
- package/dist/store/sql/cursor.d.ts +24 -0
- package/dist/store/sql/cursor.js +49 -0
- package/dist/store/sql/maintenance.d.ts +9 -0
- package/dist/store/sql/maintenance.js +16 -0
- package/dist/store/sql/store.d.ts +22 -0
- package/dist/store/sql/store.js +340 -0
- package/dist/store/sql/tables.d.ts +34 -0
- package/dist/store/sql/tables.js +2 -0
- package/dist/store/sqlite/index.d.ts +36 -0
- package/dist/store/sqlite/index.js +29 -0
- package/dist/store/sqlite/schema.d.ts +49 -0
- package/dist/store/sqlite/schema.js +80 -0
- package/package.json +211 -14
- package/dist/react/features/model-picker/components/CredentialStatusBadge.d.ts +0 -7
- package/dist/react/features/model-picker/components/CredentialStatusBadge.js +0 -20
- package/dist/react/features/model-picker/components/ModelPicker.d.ts +0 -9
- package/dist/react/features/model-picker/components/ModelPicker.js +0 -34
- package/dist/react/features/model-picker/constants.d.ts +0 -6
- package/dist/react/features/model-picker/constants.js +0 -9
- package/dist/react/features/model-picker/dependencies.d.ts +0 -11
- package/dist/react/features/model-picker/dependencies.js +0 -10
- package/dist/react/features/model-picker/hooks/useModelPicker.hooks.d.ts +0 -27
- package/dist/react/features/model-picker/hooks/useModelPicker.hooks.js +0 -62
- package/dist/react/features/model-picker/index.d.ts +0 -32
- package/dist/react/features/model-picker/index.js +0 -29
- package/dist/react/features/model-picker/ports.d.ts +0 -11
- package/dist/react/features/model-picker/rules.d.ts +0 -26
- package/dist/react/features/model-picker/rules.js +0 -84
- package/dist/react/features/model-picker/types.d.ts +0 -38
- package/dist/react/features/model-picker/types.js +0 -2
- package/dist/react/poc-mcp-ui-official/PocMcpUiCard.d.ts +0 -9
- package/dist/react/poc-mcp-ui-official/PocMcpUiCard.js +0 -29
- package/dist/react/poc-mcp-ui-official/create-poc-resource.d.ts +0 -30
- package/dist/react/poc-mcp-ui-official/create-poc-resource.js +0 -44
- /package/dist/{react/features/model-picker → server/run-finalizer}/ports.js +0 -0
|
@@ -87,7 +87,7 @@ function latestResourcesByUri(events) {
|
|
|
87
87
|
* handle that does not, which would take down the whole surface card's render for an otherwise
|
|
88
88
|
* perfectly valid resource), this sanitizes unconditionally: lowercase, collapse every run of
|
|
89
89
|
* disallowed characters to one hyphen, trim the ends. A URI already shaped like
|
|
90
|
-
* `ui://
|
|
90
|
+
* `ui://host/deployment-execute-static-publish/<uuid>` survives this close to verbatim; this only
|
|
91
91
|
* has teeth for a producer whose id scheme this package has never seen.
|
|
92
92
|
*/
|
|
93
93
|
function pendingMirrorHandle(uri) {
|
|
@@ -119,7 +119,7 @@ const UNANSWERED_RESULT = /\b(expired|abandoned|did not respond|run ended)\b/i;
|
|
|
119
119
|
*
|
|
120
120
|
* A held-open question (the tool shows the card and waits) is over once its tool call returns, and
|
|
121
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 (
|
|
122
|
+
* without this an expired, answered or orphaned question still looked answerable (host stuck-chat
|
|
123
123
|
* investigation, 2026-09-27). Only a card still showing that FIRST document closes: a tool that sent
|
|
124
124
|
* a follow-up document for the same `ui://` URI (an outcome after the answer) keeps showing it.
|
|
125
125
|
*
|
|
@@ -172,10 +172,10 @@ export function McpUiSurfaceCard({ events, sandboxProxyUrl, onToolCall, onOpenLi
|
|
|
172
172
|
* The ext-event slot key for MCP-UI: one slot per `ui://` URI. Each resource is its own card, so a
|
|
173
173
|
* run's second card renders where it arrived instead of inside the first card's slot, while a
|
|
174
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 })`
|
|
175
|
+
* `registerExtEventRenderer({ name: MCP_UI_EXT_EVENT_NAME, renderer }, { slotKey: mcpUiSurfaceSlotKey })`
|
|
176
176
|
* when registering a custom MCP-UI renderer; {@link registerMcpUiSurfaceRenderer} already does.
|
|
177
177
|
*/
|
|
178
|
-
export function mcpUiSurfaceSlotKey(data) {
|
|
178
|
+
export function mcpUiSurfaceSlotKey({ data }) {
|
|
179
179
|
return parseUIResource(data)?.resource.uri;
|
|
180
180
|
}
|
|
181
181
|
/**
|
|
@@ -195,6 +195,6 @@ export function mcpUiSurfaceSlotKey(data) {
|
|
|
195
195
|
* @returns An unregister handle, so a test or a hot reload can dispose cleanly.
|
|
196
196
|
*/
|
|
197
197
|
export function registerMcpUiSurfaceRenderer(options) {
|
|
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 });
|
|
198
|
+
return registerExtEventRenderer({ name: options.name ?? MCP_UI_EXT_EVENT_NAME, renderer: (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 });
|
|
199
199
|
}
|
|
200
200
|
//# sourceMappingURL=McpUiSurfaceCard.js.map
|
|
@@ -76,7 +76,7 @@ export function MessageList({ messages, isStreaming = false, scrollIntent = fals
|
|
|
76
76
|
stickToBottomRef.current = isScrolledToBottom(event.currentTarget);
|
|
77
77
|
}, "data-agent-element": "chat-transcript", "data-agent-role": "list", "data-agent-label": "The conversation transcript, oldest message first", children: [messages.map((message) => {
|
|
78
78
|
const isLast = message.id === messages[messages.length - 1]?.id;
|
|
79
|
-
const runStreaming = isLast && isStreaming && !isTerminalRunStatus(message.runStatus);
|
|
79
|
+
const runStreaming = isLast && isStreaming && !isTerminalRunStatus({ status: message.runStatus });
|
|
80
80
|
return (_jsx(MessageRow, { message: message, runStreaming: runStreaming, runSucceeded: message.runStatus === 'succeeded', questionFormInteractive: message.id === activeQuestionFormMessageId, ...(questionFormSubmittedAnswersByMessageId?.[message.id] !== undefined ? { questionFormSubmittedAnswers: questionFormSubmittedAnswersByMessageId[message.id] } : {}), ...(onQuestionFormSubmit !== undefined ? { onQuestionFormSubmit: (text, answers) => onQuestionFormSubmit(message.id, text, answers) } : {}), ...(projectFileNames !== undefined ? { projectFileNames } : {}), ...(onRequestOpenFile !== undefined ? { onRequestOpenFile } : {}), ...(renderAttachment !== undefined ? { renderAttachment } : {}) }, message.id));
|
|
81
81
|
}), pendingPrompt ? (_jsxs("div", { className: "jini-chat-pane__queued", role: "status", "aria-label": t('Message queued — not yet sent, will send once the current run finishes'), "data-testid": "chat-pane-queued", "data-agent-element": "chat-message-pending", "data-agent-role": "region", "data-agent-label": "A message queued to send once the current run finishes", children: [_jsx("div", { className: "jini-chat-pane__queued-text", children: pendingPrompt.text }), _jsx("div", { className: "jini-chat-pane__queued-actions", children: _jsx("button", { type: "button", className: "jini-chat-pane__queued-cancel", onClick: pendingPrompt.onCancel, title: t('Cancel queued message'), children: t('Cancel') }) })] })) : null] }));
|
|
82
82
|
}
|
|
@@ -104,7 +104,7 @@ function isPendingWithNoContent(message, visibleContent, toolRowCount) {
|
|
|
104
104
|
* @complexity O(1) — one comparison plus a set lookup via `isTerminalRunStatus`.
|
|
105
105
|
*/
|
|
106
106
|
function isRunInProgress(runStatus) {
|
|
107
|
-
return runStatus !== undefined && !isTerminalRunStatus(runStatus);
|
|
107
|
+
return runStatus !== undefined && !isTerminalRunStatus({ status: runStatus });
|
|
108
108
|
}
|
|
109
109
|
/** One "Done · 6m 29s · 2612 out · $0.4028" summary line, from the run's own `kind:'usage'` event — never estimated client-side. Renders only the fields the event actually carries, so a transport that supplies partial usage data degrades gracefully instead of showing fabricated zeros. */
|
|
110
110
|
function UsageSummary({ usage }) {
|
|
@@ -138,8 +138,8 @@ function CopyMessageButton({ text, label }) {
|
|
|
138
138
|
}
|
|
139
139
|
export function MessageRow({ message, runStreaming = false, runSucceeded = false, questionFormInteractive = false, questionFormSubmittedAnswers, onQuestionFormSubmit, projectFileNames, onRequestOpenFile, renderAttachment, }) {
|
|
140
140
|
const t = useT();
|
|
141
|
-
const timeline = useToolTimeline(message.events, { runStreaming, runSucceeded });
|
|
142
|
-
const extGroups = useExtEventGroups(message.events, extEventSlot);
|
|
141
|
+
const timeline = useToolTimeline({ events: message.events }, { runStreaming, runSucceeded });
|
|
142
|
+
const extGroups = useExtEventGroups({ events: message.events }, { slotOf: (name, data) => extEventSlot({ name, data }) });
|
|
143
143
|
// Which attachment chip (if any) this row's preview modal is open for. Row-local, not
|
|
144
144
|
// module-level: two attachments in the same message can never be "open" at once, but two
|
|
145
145
|
// different messages opening their own attachments independently is fine and expected.
|
|
@@ -147,8 +147,8 @@ export function MessageRow({ message, runStreaming = false, runSucceeded = false
|
|
|
147
147
|
if (message.role === 'user') {
|
|
148
148
|
return (_jsxs("div", { className: "jini-message jini-message-user", "data-message-id": message.id, "data-agent-element": `chat-message-${message.id}`, "data-agent-role": "region", "data-agent-label": "A message from the user", children: [message.attachments && message.attachments.length > 0 ? (_jsx("div", { className: "jini-message-attachments", children: message.attachments.map((a) => (_jsx("button", { type: "button", className: "jini-message-attachment-chip", onClick: () => setOpenAttachment(a), title: t('Open {name}', { name: a.name }), "aria-label": t('Open {name}', { name: a.name }), children: renderAttachment ? renderAttachment(a) : a.name }, a.path))) })) : null, _jsx("div", { className: "jini-message-content", children: message.content }), _jsx("div", { className: "jini-message-actions jini-message-actions--user", children: _jsx(CopyMessageButton, { text: message.content, label: t('Copy message') }) }), openAttachment ? (_jsx(AttachmentPreviewModal, { attachment: openAttachment, onClose: () => setOpenAttachment(null) })) : null] }));
|
|
149
149
|
}
|
|
150
|
-
const visibleContent = stripArtifact(message.content);
|
|
151
|
-
const segments = splitOnQuestionForms(visibleContent);
|
|
150
|
+
const visibleContent = stripArtifact({ content: message.content });
|
|
151
|
+
const segments = splitOnQuestionForms({ input: visibleContent });
|
|
152
152
|
const usageEvent = message.events?.filter((ev) => ev.kind === 'usage').pop();
|
|
153
153
|
// Interleaving is skipped outright when an artifact was stripped. `stripArtifact` operates on the
|
|
154
154
|
// whole string and trims, so the surviving text no longer lines up with the per-run offsets the
|
|
@@ -156,13 +156,13 @@ export function MessageRow({ message, runStreaming = false, runSucceeded = false
|
|
|
156
156
|
// reason about partial overlaps, an artifact-bearing message keeps the flat layout — those
|
|
157
157
|
// messages are dominated by the artifact panel anyway, so the ordering matters least there.
|
|
158
158
|
const blocks = visibleContent === message.content
|
|
159
|
-
? interleaveMessageBlocks(message.events, message.content, timeline.rows, (ev) => extEventSlot(ev.name, ev.data))
|
|
159
|
+
? interleaveMessageBlocks({ events: message.events, content: message.content, rows: timeline.rows }, { slotOf: (ev) => extEventSlot({ name: ev.name, data: ev.data }) })
|
|
160
160
|
: null;
|
|
161
161
|
const pending = isPendingWithNoContent(message, visibleContent, timeline.rows.length);
|
|
162
162
|
const runInProgress = isRunInProgress(message.runStatus);
|
|
163
163
|
const renderToolCard = (row) => (_jsx(ToolCard, { use: row.use, result: row.result, runStreaming: runStreaming, runSucceeded: runSucceeded, ...(projectFileNames !== undefined ? { projectFileNames } : {}), ...(onRequestOpenFile !== undefined ? { onRequestOpenFile } : {}) }, row.id));
|
|
164
164
|
const renderExtGroup = (group) => {
|
|
165
|
-
const renderer = getExtEventRenderer(group.name);
|
|
165
|
+
const renderer = getExtEventRenderer({ name: group.name });
|
|
166
166
|
if (!renderer)
|
|
167
167
|
return null;
|
|
168
168
|
const node = renderer({
|
|
@@ -181,7 +181,7 @@ export function MessageRow({ message, runStreaming = false, runSucceeded = false
|
|
|
181
181
|
// arrives for it, instead of staying tombstoned for the message's whole lifetime.
|
|
182
182
|
_jsx(ExtEventErrorBoundary, { name: group.name, children: _jsx("div", { children: node }) }, `${group.slot}:${group.events.length}`));
|
|
183
183
|
};
|
|
184
|
-
const renderTextSegments = (text, keyPrefix) => splitOnQuestionForms(text).map((segment, i) => segment.kind === 'text' ? (segment.text.trim() ? (_jsx("div", { className: "jini-message-content", children: _jsx(Markdown, { children: segment.text }) }, `${keyPrefix}-${i}`)) : null) : (_jsx(QuestionForm, { form: segment.form, interactive: questionFormInteractive, ...(questionFormSubmittedAnswers !== undefined ? { submittedAnswers: questionFormSubmittedAnswers } : {}), ...(onQuestionFormSubmit !== undefined ? { onSubmit: onQuestionFormSubmit } : {}) }, `${keyPrefix}-${i}`)));
|
|
184
|
+
const renderTextSegments = (text, keyPrefix) => splitOnQuestionForms({ input: text }).map((segment, i) => segment.kind === 'text' ? (segment.text.trim() ? (_jsx("div", { className: "jini-message-content", children: _jsx(Markdown, { children: segment.text }) }, `${keyPrefix}-${i}`)) : null) : (_jsx(QuestionForm, { form: segment.form, interactive: questionFormInteractive, ...(questionFormSubmittedAnswers !== undefined ? { submittedAnswers: questionFormSubmittedAnswers } : {}), ...(onQuestionFormSubmit !== undefined ? { onSubmit: onQuestionFormSubmit } : {}) }, `${keyPrefix}-${i}`)));
|
|
185
185
|
return (_jsxs("div", { className: "jini-message jini-message-assistant", "data-message-id": message.id, "data-run-status": message.runStatus, "data-agent-element": `chat-message-${message.id}`, "data-agent-role": "region", "data-agent-label": "A reply from the assistant", children: [message.agentName ? _jsx("div", { className: "jini-message-agent", children: message.agentName }) : null, blocks
|
|
186
186
|
? blocks.map((block) => {
|
|
187
187
|
if (block.kind === 'text') {
|
|
@@ -203,7 +203,7 @@ export function MessageRow({ message, runStreaming = false, runSucceeded = false
|
|
|
203
203
|
* Kept a separate component so its one-second tick re-renders only this line, not the whole row.
|
|
204
204
|
*/
|
|
205
205
|
function RunActivityLine({ events, active }) {
|
|
206
|
-
const label = useRunActivity(events, active);
|
|
206
|
+
const label = useRunActivity({ events: events, active: active });
|
|
207
207
|
if (label === null)
|
|
208
208
|
return null;
|
|
209
209
|
return (_jsx("div", { className: "jini-message-pending jini-run-activity", "aria-live": "off", children: label }));
|
|
@@ -81,15 +81,15 @@ export const QuestionForm = forwardRef(function QuestionForm({ form, interactive
|
|
|
81
81
|
return;
|
|
82
82
|
const files = collectFileSubmissions(form, fileAnswers);
|
|
83
83
|
if (files.length > 0)
|
|
84
|
-
onSubmit(formatFormAnswers(form, answers), answers, files);
|
|
84
|
+
onSubmit(formatFormAnswers({ form: form, answers: answers }), answers, files);
|
|
85
85
|
else
|
|
86
|
-
onSubmit(formatFormAnswers(form, answers), answers);
|
|
86
|
+
onSubmit(formatFormAnswers({ form: form, answers: answers }), answers);
|
|
87
87
|
}
|
|
88
88
|
function handleSkipAll() {
|
|
89
89
|
if (locked || !onSubmit)
|
|
90
90
|
return;
|
|
91
91
|
const empty = {};
|
|
92
|
-
onSubmit(formatFormAnswers(form, empty), empty);
|
|
92
|
+
onSubmit(formatFormAnswers({ form: form, answers: empty }), empty);
|
|
93
93
|
}
|
|
94
94
|
const withinSelectionLimits = form.questions.every((q) => {
|
|
95
95
|
if (q.type !== 'checkbox' || q.maxSelections === undefined)
|
|
@@ -192,8 +192,8 @@ function emptyQuestionValue(q) {
|
|
|
192
192
|
}
|
|
193
193
|
function canonicalizeQuestionValue(q, value) {
|
|
194
194
|
if (Array.isArray(value))
|
|
195
|
-
return value.map((entry) => formOptionValueForLabel(q, entry));
|
|
196
|
-
return formOptionValueForLabel(q, value);
|
|
195
|
+
return value.map((entry) => formOptionValueForLabel({ question: q, labelOrValue: entry }));
|
|
196
|
+
return formOptionValueForLabel({ question: q, labelOrValue: value });
|
|
197
197
|
}
|
|
198
198
|
function shouldRenderCustomChoice(q) {
|
|
199
199
|
return q.allowCustom !== false;
|
|
@@ -46,10 +46,10 @@ export function ToolCard({ use, result, runStreaming, runSucceeded, projectFileN
|
|
|
46
46
|
return (_jsx("div", { "data-agent-element": `tool-call-${use.id}`, "data-agent-role": "region", "data-agent-label": `A ${humanizeToolId(name)} tool call the AI made`, children: content }));
|
|
47
47
|
}
|
|
48
48
|
function renderToolCardBody(name, use, result, isStreaming, isSucceeded, fileCtx) {
|
|
49
|
-
const custom = getToolRenderer(name);
|
|
49
|
+
const custom = getToolRenderer({ name: name });
|
|
50
50
|
if (custom) {
|
|
51
51
|
try {
|
|
52
|
-
const node = custom(toRenderProps(use, result, isStreaming, isSucceeded));
|
|
52
|
+
const node = custom(toRenderProps({ use: use, result: result, runStreaming: isStreaming }, { runSucceeded: isSucceeded }));
|
|
53
53
|
if (node !== undefined && node !== null && node !== false)
|
|
54
54
|
return node;
|
|
55
55
|
}
|
|
@@ -67,8 +67,8 @@ function renderToolCardBody(name, use, result, isStreaming, isSucceeded, fileCtx
|
|
|
67
67
|
// visible rather than disappear silently.
|
|
68
68
|
if (EXECUTE_DELEGATED_TOOL_NAMES.has(name) && result && !result.isError)
|
|
69
69
|
return null;
|
|
70
|
-
if (isTodoWriteToolName(name))
|
|
71
|
-
return _jsx(TodoCard, { todos: parseTodoWriteInput(use.input), runStreaming: isStreaming });
|
|
70
|
+
if (isTodoWriteToolName({ name: name }))
|
|
71
|
+
return _jsx(TodoCard, { todos: parseTodoWriteInput({ input: use.input }), runStreaming: isStreaming });
|
|
72
72
|
if (name === 'Write' || name === 'write' || name === 'create_file')
|
|
73
73
|
return _jsx(FileWriteCard, { input: use.input, result: result, runStreaming: isStreaming, runSucceeded: isSucceeded, ctx: fileCtx });
|
|
74
74
|
if (name === 'Edit' || name === 'str_replace_edit')
|
|
@@ -137,21 +137,21 @@ function DelegatedToolCard({ name, input, result, runStreaming, runSucceeded })
|
|
|
137
137
|
const args = EXECUTE_DELEGATED_TOOL_NAMES.has(name) ? wrapper.input : input;
|
|
138
138
|
const target = primaryTarget(args);
|
|
139
139
|
const isRunning = runStreaming && !result;
|
|
140
|
-
return (_jsxs("div", { className: "op-card op-jini-tool", children: [_jsxs("button", { type: "button", className: "op-card-head", onClick: () => setOpen((o) => !o), children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsxs("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: [t('Tool call'), " \u00B7 ", humanizeToolId(toolId)] }), target ? _jsx("span", { className: "op-meta", children: target }) : null, _jsx("span", { className: "op-expand-chev", "aria-hidden": true, children: _jsx(Icon, { name: open ? 'chevron-down' : 'chevron-right', size: 11 }) })] }), _jsx("div", { className: `accordion-collapsible${open ? ' open' : ''}`, children: _jsx("div", { className: "accordion-collapsible-inner", children: _jsxs("div", { className: "op-card-detail", children: [_jsx("pre", { className: "op-command", children: JSON.stringify(args ?? {}) }), result?.content ? _jsx("pre", { className: "op-output", children: truncate(formatToolOutputForDisplay(result.content), 2000) }) : null, _jsx(ToolResultMedia, { result: result })] }) }) }), _jsx(FileErrorDetail, { result: result })] }));
|
|
140
|
+
return (_jsxs("div", { className: "op-card op-jini-tool", children: [_jsxs("button", { type: "button", className: "op-card-head", onClick: () => setOpen((o) => !o), children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsxs("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: [t('Tool call'), " \u00B7 ", humanizeToolId(toolId)] }), target ? _jsx("span", { className: "op-meta", children: target }) : null, _jsx("span", { className: "op-expand-chev", "aria-hidden": true, children: _jsx(Icon, { name: open ? 'chevron-down' : 'chevron-right', size: 11 }) })] }), _jsx("div", { className: `accordion-collapsible${open ? ' open' : ''}`, children: _jsx("div", { className: "accordion-collapsible-inner", children: _jsxs("div", { className: "op-card-detail", children: [_jsx("pre", { className: "op-command", children: JSON.stringify(args ?? {}) }), result?.content ? _jsx("pre", { className: "op-output", children: truncate(formatToolOutputForDisplay({ text: result.content }), 2000) }) : null, _jsx(ToolResultMedia, { result: result })] }) }) }), _jsx(FileErrorDetail, { result: result })] }));
|
|
141
141
|
}
|
|
142
142
|
function SearchToolsCard({ input, result, runStreaming, runSucceeded }) {
|
|
143
143
|
const t = useT();
|
|
144
144
|
const [open, setOpen] = useState(false);
|
|
145
145
|
const obj = (input ?? {});
|
|
146
146
|
const isRunning = runStreaming && !result;
|
|
147
|
-
return (_jsxs("div", { className: "op-card op-jini-tool", children: [_jsxs("button", { type: "button", className: "op-card-head", onClick: () => setOpen((o) => !o), children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Search tools') }), obj.query ? _jsx("span", { className: "op-meta", children: obj.query }) : null, _jsx("span", { className: "op-expand-chev", "aria-hidden": true, children: _jsx(Icon, { name: open ? 'chevron-down' : 'chevron-right', size: 11 }) })] }), _jsx("div", { className: `accordion-collapsible${open ? ' open' : ''}`, children: _jsx("div", { className: "accordion-collapsible-inner", children: _jsx("div", { className: "op-card-detail", children: result?.content ? _jsx("pre", { className: "op-output", children: truncate(formatToolOutputForDisplay(result.content), 2000) }) : null }) }) }), _jsx(FileErrorDetail, { result: result })] }));
|
|
147
|
+
return (_jsxs("div", { className: "op-card op-jini-tool", children: [_jsxs("button", { type: "button", className: "op-card-head", onClick: () => setOpen((o) => !o), children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Search tools') }), obj.query ? _jsx("span", { className: "op-meta", children: obj.query }) : null, _jsx("span", { className: "op-expand-chev", "aria-hidden": true, children: _jsx(Icon, { name: open ? 'chevron-down' : 'chevron-right', size: 11 }) })] }), _jsx("div", { className: `accordion-collapsible${open ? ' open' : ''}`, children: _jsx("div", { className: "accordion-collapsible-inner", children: _jsx("div", { className: "op-card-detail", children: result?.content ? _jsx("pre", { className: "op-output", children: truncate(formatToolOutputForDisplay({ text: result.content }), 2000) }) : null }) }) }), _jsx(FileErrorDetail, { result: result })] }));
|
|
148
148
|
}
|
|
149
149
|
function DescribeToolCard({ input, result, runStreaming, runSucceeded }) {
|
|
150
150
|
const t = useT();
|
|
151
151
|
const [open, setOpen] = useState(false);
|
|
152
152
|
const obj = (input ?? {});
|
|
153
153
|
const isRunning = runStreaming && !result;
|
|
154
|
-
return (_jsxs("div", { className: "op-card op-jini-tool", children: [_jsxs("button", { type: "button", className: "op-card-head", onClick: () => setOpen((o) => !o), children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Describe tool') }), obj.id ? _jsx("span", { className: "op-meta", children: obj.id }) : null, _jsx("span", { className: "op-expand-chev", "aria-hidden": true, children: _jsx(Icon, { name: open ? 'chevron-down' : 'chevron-right', size: 11 }) })] }), _jsx("div", { className: `accordion-collapsible${open ? ' open' : ''}`, children: _jsx("div", { className: "accordion-collapsible-inner", children: _jsx("div", { className: "op-card-detail", children: result?.content ? _jsx("pre", { className: "op-output", children: truncate(formatToolOutputForDisplay(result.content), 2000) }) : null }) }) }), _jsx(FileErrorDetail, { result: result })] }));
|
|
154
|
+
return (_jsxs("div", { className: "op-card op-jini-tool", children: [_jsxs("button", { type: "button", className: "op-card-head", onClick: () => setOpen((o) => !o), children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Describe tool') }), obj.id ? _jsx("span", { className: "op-meta", children: obj.id }) : null, _jsx("span", { className: "op-expand-chev", "aria-hidden": true, children: _jsx(Icon, { name: open ? 'chevron-down' : 'chevron-right', size: 11 }) })] }), _jsx("div", { className: `accordion-collapsible${open ? ' open' : ''}`, children: _jsx("div", { className: "accordion-collapsible-inner", children: _jsx("div", { className: "op-card-detail", children: result?.content ? _jsx("pre", { className: "op-output", children: truncate(formatToolOutputForDisplay({ text: result.content }), 2000) }) : null }) }) }), _jsx(FileErrorDetail, { result: result })] }));
|
|
155
155
|
}
|
|
156
156
|
function OpenInTabButton({ filePath, ctx }) {
|
|
157
157
|
const t = useT();
|
|
@@ -208,7 +208,7 @@ function BashCard({ input, result, runStreaming, runSucceeded }) {
|
|
|
208
208
|
const desc = obj.description;
|
|
209
209
|
const [open, setOpen] = useState(false);
|
|
210
210
|
const isRunning = runStreaming && !result;
|
|
211
|
-
return (_jsxs("div", { className: "op-card op-bash", children: [_jsxs("button", { type: "button", className: "op-card-head", onClick: () => setOpen((o) => !o), children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Bash') }), desc ? _jsx("span", { className: "op-meta op-desc", children: desc }) : null, _jsx("span", { className: "op-expand-chev", "aria-hidden": true, children: _jsx(Icon, { name: open ? 'chevron-down' : 'chevron-right', size: 11 }) })] }), _jsx("div", { className: `accordion-collapsible${open ? ' open' : ''}`, children: _jsx("div", { className: "accordion-collapsible-inner", children: _jsxs("div", { className: "op-card-detail", children: [_jsx("pre", { className: "op-command", children: truncate(command, 400) }), result?.content ? _jsx("pre", { className: "op-output", children: truncate(formatToolOutputForDisplay(result.content), 4000) }) : null] }) }) })] }));
|
|
211
|
+
return (_jsxs("div", { className: "op-card op-bash", children: [_jsxs("button", { type: "button", className: "op-card-head", onClick: () => setOpen((o) => !o), children: [_jsx(ResultBadge, { result: result, runStreaming: runStreaming, runSucceeded: runSucceeded }), _jsx("span", { className: `op-title${isRunning ? ' shimmer-text' : ''}`, children: t('Bash') }), desc ? _jsx("span", { className: "op-meta op-desc", children: desc }) : null, _jsx("span", { className: "op-expand-chev", "aria-hidden": true, children: _jsx(Icon, { name: open ? 'chevron-down' : 'chevron-right', size: 11 }) })] }), _jsx("div", { className: `accordion-collapsible${open ? ' open' : ''}`, children: _jsx("div", { className: "accordion-collapsible-inner", children: _jsxs("div", { className: "op-card-detail", children: [_jsx("pre", { className: "op-command", children: truncate(command, 400) }), result?.content ? _jsx("pre", { className: "op-output", children: truncate(formatToolOutputForDisplay({ text: result.content }), 4000) }) : null] }) }) })] }));
|
|
212
212
|
}
|
|
213
213
|
function GlobCard({ input, result, runStreaming, runSucceeded }) {
|
|
214
214
|
const t = useT();
|
|
@@ -268,7 +268,7 @@ function ToolResultMedia({ result }) {
|
|
|
268
268
|
function FileErrorDetail({ result }) {
|
|
269
269
|
if (!result?.isError || !result.content.trim())
|
|
270
270
|
return null;
|
|
271
|
-
return _jsx("pre", { className: "op-output", children: truncate(formatToolOutputForDisplay(result.content), 1200) });
|
|
271
|
+
return _jsx("pre", { className: "op-output", children: truncate(formatToolOutputForDisplay({ text: result.content }), 1200) });
|
|
272
272
|
}
|
|
273
273
|
function describeInput(input) {
|
|
274
274
|
if (input == null)
|
|
@@ -26,7 +26,9 @@ export interface ComposerSlashQuery {
|
|
|
26
26
|
* still no internal whitespace or slash — so every existing no-argument caller parses identically
|
|
27
27
|
* to before this extension.
|
|
28
28
|
*/
|
|
29
|
-
export declare function parseComposerSlashQuery(draft
|
|
29
|
+
export declare function parseComposerSlashQuery({ draft }: {
|
|
30
|
+
draft: string;
|
|
31
|
+
}): ComposerSlashQuery | null;
|
|
30
32
|
/**
|
|
31
33
|
* Flattens non-empty groups while preserving host order, then filters.
|
|
32
34
|
*
|
|
@@ -34,7 +36,10 @@ export declare function parseComposerSlashQuery(draft: string): ComposerSlashQue
|
|
|
34
36
|
* still being typed (`argument === null`), {@link matchesLockedCommand} once a separator has been
|
|
35
37
|
* typed (`argument !== null`) — see each helper's own doc for why the rule changes at that point.
|
|
36
38
|
*/
|
|
37
|
-
export declare function filterComposerDiscovery(groups
|
|
39
|
+
export declare function filterComposerDiscovery({ groups, query }: {
|
|
40
|
+
groups: readonly ComposerDiscoveryGroup[];
|
|
41
|
+
query: ComposerSlashQuery;
|
|
42
|
+
}): ComposerDiscoveryMatch[];
|
|
38
43
|
export type ComposerSlashResolution = {
|
|
39
44
|
/**
|
|
40
45
|
* The command word is not yet complete, or a required argument hasn't been typed yet.
|
|
@@ -63,11 +68,20 @@ export type ComposerSlashResolution = {
|
|
|
63
68
|
* argument-taking command the moment a separator exists and (if required) the argument is
|
|
64
69
|
* non-blank.
|
|
65
70
|
*/
|
|
66
|
-
export declare function resolveComposerSlashInvocation(draft
|
|
71
|
+
export declare function resolveComposerSlashInvocation({ draft, item }: {
|
|
72
|
+
draft: string;
|
|
73
|
+
item: ComposerDiscoveryItem;
|
|
74
|
+
}): ComposerSlashResolution | null;
|
|
67
75
|
/** Replaces the complete active slash token; callers only invoke this after the parser matches. */
|
|
68
|
-
export declare function replaceComposerSlashTrigger(draft
|
|
76
|
+
export declare function replaceComposerSlashTrigger({ draft, insertText }: {
|
|
77
|
+
draft: string;
|
|
78
|
+
insertText: string;
|
|
79
|
+
}): string;
|
|
69
80
|
/** Adds a menu-selected resource to an existing prompt without concatenating words. */
|
|
70
|
-
export declare function appendComposerDiscovery(draft
|
|
81
|
+
export declare function appendComposerDiscovery({ draft, insertText }: {
|
|
82
|
+
draft: string;
|
|
83
|
+
insertText: string;
|
|
84
|
+
}): string;
|
|
71
85
|
export type ComposerSlashKeyAction = {
|
|
72
86
|
type: 'move';
|
|
73
87
|
offset: -1 | 1;
|
|
@@ -79,7 +93,10 @@ export type ComposerSlashKeyAction = {
|
|
|
79
93
|
type: 'none';
|
|
80
94
|
};
|
|
81
95
|
/** Maps palette keys without owning React state; arrows are intentionally circular at the caller. */
|
|
82
|
-
export declare function resolveComposerSlashKeyAction(key
|
|
96
|
+
export declare function resolveComposerSlashKeyAction({ key, shiftKey }: {
|
|
97
|
+
key: string;
|
|
98
|
+
shiftKey: boolean;
|
|
99
|
+
}): ComposerSlashKeyAction;
|
|
83
100
|
/** The subset of `DOMRect` the popover-positioning math below actually reads, so a caller (or a
|
|
84
101
|
* test) can pass a plain object instead of constructing every read-only `DOMRect` accessor. */
|
|
85
102
|
export interface ComposerMenuAnchorRect {
|
|
@@ -95,7 +112,9 @@ export interface ComposerMenuAnchorRect {
|
|
|
95
112
|
*
|
|
96
113
|
* @complexity Time/space: O(1).
|
|
97
114
|
*/
|
|
98
|
-
export declare function composerDiscoveryMenuPosition(composerRect
|
|
115
|
+
export declare function composerDiscoveryMenuPosition({ composerRect }: {
|
|
116
|
+
composerRect: ComposerMenuAnchorRect;
|
|
117
|
+
}): CSSProperties;
|
|
99
118
|
/**
|
|
100
119
|
* Viewport-fixed position for the slash-command palette, anchored to `.jini-composer`'s live
|
|
101
120
|
* rect. Keeps the palette's existing full-width-minus-insets footprint (`inset-inline: 8px` in
|
|
@@ -103,5 +122,7 @@ export declare function composerDiscoveryMenuPosition(composerRect: ComposerMenu
|
|
|
103
122
|
*
|
|
104
123
|
* @complexity Time/space: O(1).
|
|
105
124
|
*/
|
|
106
|
-
export declare function composerSlashMenuPosition(composerRect
|
|
125
|
+
export declare function composerSlashMenuPosition({ composerRect }: {
|
|
126
|
+
composerRect: ComposerMenuAnchorRect;
|
|
127
|
+
}): CSSProperties;
|
|
107
128
|
//# sourceMappingURL=composer-discovery.d.ts.map
|
|
@@ -5,7 +5,7 @@ const SLASH_QUERY_RE = /^\/([^\s/]*)(?:(\s+)([\s\S]*))?$/;
|
|
|
5
5
|
* still no internal whitespace or slash — so every existing no-argument caller parses identically
|
|
6
6
|
* to before this extension.
|
|
7
7
|
*/
|
|
8
|
-
export function parseComposerSlashQuery(draft) {
|
|
8
|
+
export function parseComposerSlashQuery({ draft }) {
|
|
9
9
|
const match = SLASH_QUERY_RE.exec(draft);
|
|
10
10
|
if (!match)
|
|
11
11
|
return null;
|
|
@@ -42,7 +42,7 @@ function matchesFuzzyCommand(item, normalizedCommand) {
|
|
|
42
42
|
* still being typed (`argument === null`), {@link matchesLockedCommand} once a separator has been
|
|
43
43
|
* typed (`argument !== null`) — see each helper's own doc for why the rule changes at that point.
|
|
44
44
|
*/
|
|
45
|
-
export function filterComposerDiscovery(groups, query) {
|
|
45
|
+
export function filterComposerDiscovery({ groups, query }) {
|
|
46
46
|
const normalizedCommand = query.command.trim().toLowerCase();
|
|
47
47
|
const matchesQuery = query.argument !== null ? matchesLockedCommand : matchesFuzzyCommand;
|
|
48
48
|
const matches = [];
|
|
@@ -69,8 +69,8 @@ export function filterComposerDiscovery(groups, query) {
|
|
|
69
69
|
* argument-taking command the moment a separator exists and (if required) the argument is
|
|
70
70
|
* non-blank.
|
|
71
71
|
*/
|
|
72
|
-
export function resolveComposerSlashInvocation(draft, item) {
|
|
73
|
-
const query = parseComposerSlashQuery(draft);
|
|
72
|
+
export function resolveComposerSlashInvocation({ draft, item }) {
|
|
73
|
+
const query = parseComposerSlashQuery({ draft });
|
|
74
74
|
if (!query)
|
|
75
75
|
return null;
|
|
76
76
|
if (!item.command)
|
|
@@ -90,17 +90,17 @@ export function resolveComposerSlashInvocation(draft, item) {
|
|
|
90
90
|
return { type: 'invoke', argument: query.argument };
|
|
91
91
|
}
|
|
92
92
|
/** Replaces the complete active slash token; callers only invoke this after the parser matches. */
|
|
93
|
-
export function replaceComposerSlashTrigger(draft, insertText) {
|
|
94
|
-
return parseComposerSlashQuery(draft) === null ? draft : insertText;
|
|
93
|
+
export function replaceComposerSlashTrigger({ draft, insertText }) {
|
|
94
|
+
return parseComposerSlashQuery({ draft }) === null ? draft : insertText;
|
|
95
95
|
}
|
|
96
96
|
/** Adds a menu-selected resource to an existing prompt without concatenating words. */
|
|
97
|
-
export function appendComposerDiscovery(draft, insertText) {
|
|
97
|
+
export function appendComposerDiscovery({ draft, insertText }) {
|
|
98
98
|
if (draft.trim() === '')
|
|
99
99
|
return insertText;
|
|
100
100
|
return `${draft.trimEnd()} ${insertText}`;
|
|
101
101
|
}
|
|
102
102
|
/** Maps palette keys without owning React state; arrows are intentionally circular at the caller. */
|
|
103
|
-
export function resolveComposerSlashKeyAction(key, shiftKey) {
|
|
103
|
+
export function resolveComposerSlashKeyAction({ key, shiftKey }) {
|
|
104
104
|
if (key === 'ArrowDown')
|
|
105
105
|
return { type: 'move', offset: 1 };
|
|
106
106
|
if (key === 'ArrowUp')
|
|
@@ -150,7 +150,7 @@ function verticalUpPosition(anchorTop) {
|
|
|
150
150
|
*
|
|
151
151
|
* @complexity Time/space: O(1).
|
|
152
152
|
*/
|
|
153
|
-
export function composerDiscoveryMenuPosition(composerRect) {
|
|
153
|
+
export function composerDiscoveryMenuPosition({ composerRect }) {
|
|
154
154
|
const inset = 8;
|
|
155
155
|
return {
|
|
156
156
|
...verticalUpPosition(composerRect.top),
|
|
@@ -165,7 +165,7 @@ export function composerDiscoveryMenuPosition(composerRect) {
|
|
|
165
165
|
*
|
|
166
166
|
* @complexity Time/space: O(1).
|
|
167
167
|
*/
|
|
168
|
-
export function composerSlashMenuPosition(composerRect) {
|
|
168
|
+
export function composerSlashMenuPosition({ composerRect }) {
|
|
169
169
|
const inset = 8;
|
|
170
170
|
return {
|
|
171
171
|
...verticalUpPosition(composerRect.top),
|
|
@@ -63,11 +63,13 @@ export type ExtEventRenderer = (props: ExtEventRenderProps) => ReactNode;
|
|
|
63
63
|
* occurrence — right for a stream that builds a single surface (A2UI's create → update sequence).
|
|
64
64
|
* It is wrong for a protocol whose events are separate surfaces: MCP-UI emits one resource per
|
|
65
65
|
* card, so a run's second card rendered inside the first card's slot, far above where it arrived,
|
|
66
|
-
* and a run blocked on it waited for an answer nobody could see (
|
|
66
|
+
* and a run blocked on it waited for an answer nobody could see (host stuck-chat investigation,
|
|
67
67
|
* 2026-09-27). A slot key gives each surface its own slot at its own first occurrence; later events
|
|
68
68
|
* with the same key still update that slot in place.
|
|
69
69
|
*/
|
|
70
|
-
export type ExtEventSlotKey = (
|
|
70
|
+
export type ExtEventSlotKey = (required: {
|
|
71
|
+
data: unknown;
|
|
72
|
+
}) => string | undefined;
|
|
71
73
|
export interface ExtEventRendererOptions {
|
|
72
74
|
/** See {@link ExtEventSlotKey}. Omit for one shared slot per `name`. */
|
|
73
75
|
slotKey?: ExtEventSlotKey;
|
|
@@ -76,14 +78,22 @@ export interface ExtEventRendererOptions {
|
|
|
76
78
|
* Register a renderer for an ext-event `name`. Returns an unregister handle so tests / hot-reloads
|
|
77
79
|
* can dispose cleanly. Re-registering the same name overwrites — last writer wins.
|
|
78
80
|
*/
|
|
79
|
-
export declare function registerExtEventRenderer(name
|
|
80
|
-
|
|
81
|
+
export declare function registerExtEventRenderer({ name, renderer }: {
|
|
82
|
+
name: string;
|
|
83
|
+
renderer: ExtEventRenderer;
|
|
84
|
+
}, options?: ExtEventRendererOptions): () => void;
|
|
85
|
+
export declare function getExtEventRenderer({ name }: {
|
|
86
|
+
name: string;
|
|
87
|
+
}): ExtEventRenderer | undefined;
|
|
81
88
|
/**
|
|
82
89
|
* The render slot an ext event belongs to: its `name`, or `name:key` when the name's renderer was
|
|
83
90
|
* registered with a {@link ExtEventSlotKey} that recognizes this event. A throwing key function
|
|
84
91
|
* falls back to the shared slot rather than taking the whole message down.
|
|
85
92
|
*/
|
|
86
|
-
export declare function extEventSlot(name
|
|
93
|
+
export declare function extEventSlot({ name, data }: {
|
|
94
|
+
name: string;
|
|
95
|
+
data: unknown;
|
|
96
|
+
}): string;
|
|
87
97
|
/** Visible mainly for tests. */
|
|
88
98
|
export declare function clearExtEventRenderers(): void;
|
|
89
99
|
//# sourceMappingURL=ext-event-renderer-registry.d.ts.map
|
|
@@ -3,7 +3,7 @@ const renderers = new Map();
|
|
|
3
3
|
* Register a renderer for an ext-event `name`. Returns an unregister handle so tests / hot-reloads
|
|
4
4
|
* can dispose cleanly. Re-registering the same name overwrites — last writer wins.
|
|
5
5
|
*/
|
|
6
|
-
export function registerExtEventRenderer(name, renderer, options = {}) {
|
|
6
|
+
export function registerExtEventRenderer({ name, renderer }, options = {}) {
|
|
7
7
|
const entry = { renderer, slotKey: options.slotKey };
|
|
8
8
|
renderers.set(name, entry);
|
|
9
9
|
return () => {
|
|
@@ -11,7 +11,7 @@ export function registerExtEventRenderer(name, renderer, options = {}) {
|
|
|
11
11
|
renderers.delete(name);
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
|
-
export function getExtEventRenderer(name) {
|
|
14
|
+
export function getExtEventRenderer({ name }) {
|
|
15
15
|
return renderers.get(name)?.renderer;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
@@ -19,13 +19,13 @@ export function getExtEventRenderer(name) {
|
|
|
19
19
|
* registered with a {@link ExtEventSlotKey} that recognizes this event. A throwing key function
|
|
20
20
|
* falls back to the shared slot rather than taking the whole message down.
|
|
21
21
|
*/
|
|
22
|
-
export function extEventSlot(name, data) {
|
|
22
|
+
export function extEventSlot({ name, data }) {
|
|
23
23
|
const slotKey = renderers.get(name)?.slotKey;
|
|
24
24
|
if (!slotKey)
|
|
25
25
|
return name;
|
|
26
26
|
let key;
|
|
27
27
|
try {
|
|
28
|
-
key = slotKey(data);
|
|
28
|
+
key = slotKey({ data });
|
|
29
29
|
}
|
|
30
30
|
catch {
|
|
31
31
|
key = undefined;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { runtimeOptionLabel, runtimePopoverPosition } from '../hooks/useAgentRuntimePicker.hooks.js';
|
|
2
2
|
import type { AgentRuntimePickerProps, ChatPaneAgent } from '../types.js';
|
|
3
3
|
export { runtimeOptionLabel, runtimePopoverPosition };
|
|
4
|
-
export declare function runtimeAgentStatus(agent
|
|
4
|
+
export declare function runtimeAgentStatus({ agent }: {
|
|
5
|
+
agent: ChatPaneAgent;
|
|
6
|
+
}): string;
|
|
5
7
|
/**
|
|
6
8
|
* Renders the package-owned local/API, agent, model, and reasoning picker.
|
|
7
9
|
*
|
|
@@ -7,7 +7,7 @@ import { runtimeOptionLabel, runtimePopoverPosition, useAgentRuntimePicker, } fr
|
|
|
7
7
|
// Re-exported so this module's public surface is unchanged by the behavior extraction; both are
|
|
8
8
|
// pure and now live beside the hook that consumes them.
|
|
9
9
|
export { runtimeOptionLabel, runtimePopoverPosition };
|
|
10
|
-
export function runtimeAgentStatus(agent) {
|
|
10
|
+
export function runtimeAgentStatus({ agent }) {
|
|
11
11
|
if (agent.available === false)
|
|
12
12
|
return agent.diagnostic ?? 'Not found on PATH';
|
|
13
13
|
if (agent.authStatus === 'missing')
|
|
@@ -23,8 +23,8 @@ function RuntimeAgentList({ orderedAgents, value, onChange, agentIconBasePath, t
|
|
|
23
23
|
}
|
|
24
24
|
return (_jsx(_Fragment, { children: orderedAgents.map((agent) => {
|
|
25
25
|
const active = agent.id === value.agentId;
|
|
26
|
-
const status = runtimeAgentStatus(agent);
|
|
27
|
-
return (_jsxs("button", { type: "button", role: "radio", "aria-checked": active, className: `jini-runtime-agent${active ? ' is-active' : ''}`, "aria-current": active ? 'true' : undefined, "aria-label": `${agent.name} · ${status}`, onClick: () => onChange(defaultChatPaneSelection(agent)), children: [_jsx(AgentIcon, { id: agent.id, size: 20, className: "jini-runtime-agent-icon", ...(agentIconBasePath === undefined ? {} : { basePath: agentIconBasePath }) }), _jsx("span", { className: "jini-runtime-agent__copy", children: _jsx("strong", { children: agent.name }) }), agent.supportsTools === false ? (_jsx("span", { className: "jini-runtime-agent__badge", title: t("This CLI has no MCP support, so it cannot use this app's tools."), children: t('No tools') })) : null, _jsx("span", { className: "jini-runtime-agent__status", children: active ? t('selected') : status }), active ? (_jsx(RemixIcon, { name: "check-line", size: 19, className: "jini-runtime-check" })) : null] }, agent.id));
|
|
26
|
+
const status = runtimeAgentStatus({ agent: agent });
|
|
27
|
+
return (_jsxs("button", { type: "button", role: "radio", "aria-checked": active, className: `jini-runtime-agent${active ? ' is-active' : ''}`, "aria-current": active ? 'true' : undefined, "aria-label": `${agent.name} · ${status}`, onClick: () => onChange(defaultChatPaneSelection({ agent: agent })), children: [_jsx(AgentIcon, { id: agent.id, size: 20, className: "jini-runtime-agent-icon", ...(agentIconBasePath === undefined ? {} : { basePath: agentIconBasePath }) }), _jsx("span", { className: "jini-runtime-agent__copy", children: _jsx("strong", { children: agent.name }) }), agent.supportsTools === false ? (_jsx("span", { className: "jini-runtime-agent__badge", title: t("This CLI has no MCP support, so it cannot use this app's tools."), children: t('No tools') })) : null, _jsx("span", { className: "jini-runtime-agent__status", children: active ? t('selected') : status }), active ? (_jsx(RemixIcon, { name: "check-line", size: 19, className: "jini-runtime-check" })) : null] }, agent.id));
|
|
28
28
|
}) }));
|
|
29
29
|
}
|
|
30
30
|
function RuntimeModelSelects({ selectedAgent, value, onChange, t }) {
|