@jini-ai/chat 0.3.11 → 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.
Files changed (205) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +152 -6
  3. package/dist/core/ag-ui/entry.d.ts +53 -0
  4. package/dist/core/ag-ui/entry.js +135 -0
  5. package/dist/core/agentic/chat-capabilities.d.ts +1 -1
  6. package/dist/core/agentic/index.d.ts +1 -1
  7. package/dist/core/agentic/index.js +1 -1
  8. package/dist/core/assistant-content.d.ts +6 -2
  9. package/dist/core/assistant-content.js +2 -2
  10. package/dist/core/compact-events.d.ts +3 -1
  11. package/dist/core/compact-events.js +1 -1
  12. package/dist/core/events.d.ts +1 -1
  13. package/dist/core/events.js +1 -1
  14. package/dist/core/index.d.ts +3 -3
  15. package/dist/core/index.js +3 -3
  16. package/dist/core/messages.d.ts +4 -2
  17. package/dist/core/messages.js +2 -2
  18. package/dist/core/partial-json.d.ts +6 -2
  19. package/dist/core/partial-json.js +3 -3
  20. package/dist/core/persistence/index.d.ts +1 -1
  21. package/dist/core/persistence/index.js +1 -1
  22. package/dist/core/persistence/ports.d.ts +2 -123
  23. package/dist/core/persistence/title.d.ts +3 -1
  24. package/dist/core/persistence/title.js +1 -1
  25. package/dist/core/question-form/format.d.ts +16 -7
  26. package/dist/core/question-form/format.js +3 -3
  27. package/dist/core/question-form/normalize.d.ts +7 -2
  28. package/dist/core/question-form/normalize.js +5 -5
  29. package/dist/core/question-form/scan.d.ts +18 -6
  30. package/dist/core/question-form/scan.js +14 -14
  31. package/dist/core/run-activity.d.ts +16 -4
  32. package/dist/core/run-activity.js +11 -11
  33. package/dist/core/run-events/entry.d.ts +121 -0
  34. package/dist/core/run-events/entry.js +290 -0
  35. package/dist/core/todos.d.ts +20 -8
  36. package/dist/core/todos.js +11 -11
  37. package/dist/core/tool-events.d.ts +3 -1
  38. package/dist/core/tool-events.js +1 -1
  39. package/dist/core/tool-output.d.ts +3 -1
  40. package/dist/core/tool-output.js +1 -1
  41. package/dist/core/tools.d.ts +14 -3
  42. package/dist/core/tools.js +3 -3
  43. package/dist/core/transcript.d.ts +13 -5
  44. package/dist/core/transcript.js +5 -5
  45. package/dist/core/util/manifest.d.ts +9 -3
  46. package/dist/core/util/manifest.js +3 -3
  47. package/dist/core/util/markdown-context.d.ts +11 -3
  48. package/dist/core/util/markdown-context.js +3 -3
  49. package/dist/core/util/markup-attributes.d.ts +3 -1
  50. package/dist/core/util/markup-attributes.js +1 -1
  51. package/dist/core/util/parser.d.ts +9 -5
  52. package/dist/core/util/parser.js +13 -13
  53. package/dist/core/util/recover.d.ts +6 -2
  54. package/dist/core/util/recover.js +6 -6
  55. package/dist/core/util/strip.d.ts +19 -5
  56. package/dist/core/util/strip.js +17 -17
  57. package/dist/core/util/validate.d.ts +3 -1
  58. package/dist/core/util/validate.js +1 -1
  59. package/dist/embed-widget/browser/page-actions.d.ts +121 -0
  60. package/dist/embed-widget/browser/page-actions.js +113 -0
  61. package/dist/embed-widget/browser/session-store.d.ts +44 -0
  62. package/dist/embed-widget/browser/session-store.js +103 -0
  63. package/dist/embed-widget/react/embed.d.ts +45 -0
  64. package/dist/embed-widget/react/embed.js +87 -0
  65. package/dist/embed-widget/react/header.d.ts +21 -0
  66. package/dist/embed-widget/react/header.js +18 -0
  67. package/dist/embed-widget/transports/fetch-sse.d.ts +75 -0
  68. package/dist/embed-widget/transports/fetch-sse.js +175 -0
  69. package/dist/react/agent-bridge/frontend-session-bridge.d.ts +15 -3
  70. package/dist/react/agent-bridge/frontend-session-bridge.js +13 -6
  71. package/dist/react/artifact-types.d.ts +3 -3
  72. package/dist/react/artifact-types.js +3 -3
  73. package/dist/react/components/A2uiSurfaceCard.js +34 -13
  74. package/dist/react/components/AttachmentPreviewModal.hooks.d.ts +7 -2
  75. package/dist/react/components/AttachmentPreviewModal.hooks.js +5 -5
  76. package/dist/react/components/AttachmentPreviewModal.js +2 -2
  77. package/dist/react/components/AttachmentTray.d.ts +3 -1
  78. package/dist/react/components/AttachmentTray.js +2 -2
  79. package/dist/react/components/ChatFab.js +1 -1
  80. package/dist/react/components/Composer.js +9 -9
  81. package/dist/react/components/ConversationList.js +1 -1
  82. package/dist/react/components/McpUiSurfaceCard.d.ts +4 -2
  83. package/dist/react/components/McpUiSurfaceCard.js +5 -5
  84. package/dist/react/components/MessageList.js +1 -1
  85. package/dist/react/components/MessageRow.js +9 -9
  86. package/dist/react/components/QuestionForm.js +5 -5
  87. package/dist/react/components/ToolCard.js +9 -9
  88. package/dist/react/components/composer-discovery.d.ts +29 -8
  89. package/dist/react/components/composer-discovery.js +10 -10
  90. package/dist/react/ext-event-renderer-registry.d.ts +15 -5
  91. package/dist/react/ext-event-renderer-registry.js +4 -4
  92. package/dist/react/features/chat-pane/components/AgentRuntimePicker.d.ts +3 -1
  93. package/dist/react/features/chat-pane/components/AgentRuntimePicker.js +3 -3
  94. package/dist/react/features/chat-pane/components/ChatPane.js +43 -43
  95. package/dist/react/features/chat-pane/create-daemon-attachment-uploader.d.ts +4 -1
  96. package/dist/react/features/chat-pane/create-daemon-attachment-uploader.js +5 -35
  97. package/dist/react/features/chat-pane/create-mcp-ui-tool-caller.d.ts +5 -2
  98. package/dist/react/features/chat-pane/create-mcp-ui-tool-caller.js +1 -1
  99. package/dist/react/features/chat-pane/hooks/useAgentRuntimePicker.hooks.d.ts +38 -10
  100. package/dist/react/features/chat-pane/hooks/useAgentRuntimePicker.hooks.js +15 -15
  101. package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js +31 -31
  102. package/dist/react/features/chat-pane/hooks/useChatPaneAgentControl.hooks.d.ts +3 -1
  103. package/dist/react/features/chat-pane/hooks/useChatPaneAgentControl.hooks.js +3 -3
  104. package/dist/react/features/chat-pane/hooks/useChatPaneComposerPlaceholder.hooks.d.ts +4 -1
  105. package/dist/react/features/chat-pane/hooks/useChatPaneComposerPlaceholder.hooks.js +1 -1
  106. package/dist/react/features/chat-pane/hooks/useChatPaneRuntimeInventory.hooks.js +30 -30
  107. package/dist/react/features/chat-pane/hooks/useChatPaneWorkingDirectory.hooks.js +57 -57
  108. package/dist/react/features/chat-pane/rules.d.ts +13 -4
  109. package/dist/react/features/chat-pane/rules.js +5 -5
  110. package/dist/react/features/chat-pane/types.d.ts +1 -1
  111. package/dist/react/hooks/attachment-preview-cache.d.ts +7 -2
  112. package/dist/react/hooks/attachment-preview-cache.js +2 -2
  113. package/dist/react/hooks/composer-draft-cache.d.ts +17 -5
  114. package/dist/react/hooks/composer-draft-cache.js +6 -6
  115. package/dist/react/hooks/useArtifactStream.d.ts +5 -1
  116. package/dist/react/hooks/useArtifactStream.js +3 -3
  117. package/dist/react/hooks/useChatFabDrag.d.ts +9 -4
  118. package/dist/react/hooks/useChatFabDrag.js +4 -4
  119. package/dist/react/hooks/useComposer.js +8 -8
  120. package/dist/react/hooks/useConversation.js +56 -4
  121. package/dist/react/hooks/useExtEventGroups.d.ts +5 -1
  122. package/dist/react/hooks/useExtEventGroups.js +1 -1
  123. package/dist/react/hooks/useLatestOperation.d.ts +8 -3
  124. package/dist/react/hooks/useLatestOperation.js +4 -4
  125. package/dist/react/hooks/usePinnedTodos.d.ts +3 -1
  126. package/dist/react/hooks/usePinnedTodos.js +3 -3
  127. package/dist/react/hooks/useQuestionForms.d.ts +7 -2
  128. package/dist/react/hooks/useQuestionForms.js +7 -7
  129. package/dist/react/hooks/useRunActivity.d.ts +6 -1
  130. package/dist/react/hooks/useRunActivity.js +3 -3
  131. package/dist/react/hooks/useRunStream.d.ts +3 -1
  132. package/dist/react/hooks/useRunStream.js +1 -1
  133. package/dist/react/hooks/useToolTimeline.d.ts +3 -1
  134. package/dist/react/hooks/useToolTimeline.js +3 -3
  135. package/dist/react/index.d.ts +4 -4
  136. package/dist/react/index.js +6 -6
  137. package/dist/react/message-blocks.d.ts +10 -4
  138. package/dist/react/message-blocks.js +2 -2
  139. package/dist/react/styles/reference.css +3 -3
  140. package/dist/react/tool-renderer-registry.d.ts +8 -3
  141. package/dist/react/tool-renderer-registry.js +3 -3
  142. package/dist/react/util/defined-props.d.ts +3 -1
  143. package/dist/react/util/defined-props.js +1 -1
  144. package/dist/server/run-finalizer/entry.d.ts +47 -0
  145. package/dist/server/run-finalizer/entry.js +156 -0
  146. package/dist/server/run-finalizer/ports.d.ts +52 -0
  147. package/dist/store/contracts/index.d.ts +5 -0
  148. package/dist/store/contracts/index.js +5 -0
  149. package/dist/store/errors.d.ts +12 -0
  150. package/dist/store/errors.js +18 -0
  151. package/dist/store/index.d.ts +4 -0
  152. package/dist/store/index.js +2 -0
  153. package/dist/store/legacy/index.d.ts +3 -0
  154. package/dist/store/legacy/index.js +2 -0
  155. package/dist/store/legacy/sqlite/conversations.d.ts +129 -0
  156. package/dist/store/legacy/sqlite/conversations.js +274 -0
  157. package/dist/store/legacy/sqlite/index.d.ts +6 -0
  158. package/dist/store/legacy/sqlite/index.js +5 -0
  159. package/dist/store/legacy/sqlite/messages.d.ts +156 -0
  160. package/dist/store/legacy/sqlite/messages.js +246 -0
  161. package/dist/store/legacy/sqlite/schema.d.ts +3 -0
  162. package/dist/store/legacy/sqlite/schema.js +40 -0
  163. package/dist/store/pglite/index.d.ts +21 -0
  164. package/dist/store/pglite/index.js +17 -0
  165. package/dist/store/ports.d.ts +121 -0
  166. package/dist/store/ports.js +2 -0
  167. package/dist/store/postgres/index.d.ts +21 -0
  168. package/dist/store/postgres/index.js +17 -0
  169. package/dist/store/sql/boundary.d.ts +28 -0
  170. package/dist/store/sql/boundary.js +32 -0
  171. package/dist/store/sql/cursor.d.ts +24 -0
  172. package/dist/store/sql/cursor.js +49 -0
  173. package/dist/store/sql/maintenance.d.ts +9 -0
  174. package/dist/store/sql/maintenance.js +16 -0
  175. package/dist/store/sql/store.d.ts +22 -0
  176. package/dist/store/sql/store.js +340 -0
  177. package/dist/store/sql/tables.d.ts +34 -0
  178. package/dist/store/sql/tables.js +2 -0
  179. package/dist/store/sqlite/index.d.ts +36 -0
  180. package/dist/store/sqlite/index.js +29 -0
  181. package/dist/store/sqlite/schema.d.ts +49 -0
  182. package/dist/store/sqlite/schema.js +80 -0
  183. package/package.json +211 -14
  184. package/dist/react/features/model-picker/components/CredentialStatusBadge.d.ts +0 -7
  185. package/dist/react/features/model-picker/components/CredentialStatusBadge.js +0 -20
  186. package/dist/react/features/model-picker/components/ModelPicker.d.ts +0 -9
  187. package/dist/react/features/model-picker/components/ModelPicker.js +0 -34
  188. package/dist/react/features/model-picker/constants.d.ts +0 -6
  189. package/dist/react/features/model-picker/constants.js +0 -9
  190. package/dist/react/features/model-picker/dependencies.d.ts +0 -11
  191. package/dist/react/features/model-picker/dependencies.js +0 -10
  192. package/dist/react/features/model-picker/hooks/useModelPicker.hooks.d.ts +0 -27
  193. package/dist/react/features/model-picker/hooks/useModelPicker.hooks.js +0 -62
  194. package/dist/react/features/model-picker/index.d.ts +0 -32
  195. package/dist/react/features/model-picker/index.js +0 -29
  196. package/dist/react/features/model-picker/ports.d.ts +0 -11
  197. package/dist/react/features/model-picker/rules.d.ts +0 -26
  198. package/dist/react/features/model-picker/rules.js +0 -84
  199. package/dist/react/features/model-picker/types.d.ts +0 -38
  200. package/dist/react/features/model-picker/types.js +0 -2
  201. package/dist/react/poc-mcp-ui-official/PocMcpUiCard.d.ts +0 -9
  202. package/dist/react/poc-mcp-ui-official/PocMcpUiCard.js +0 -29
  203. package/dist/react/poc-mcp-ui-official/create-poc-resource.d.ts +0 -30
  204. package/dist/react/poc-mcp-ui-official/create-poc-resource.js +0 -44
  205. /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://tovu/deployment-execute-static-publish/<uuid>` survives this close to verbatim; this only
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 (Tovu stuck-chat
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: string): ComposerSlashQuery | null;
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: readonly ComposerDiscoveryGroup[], query: ComposerSlashQuery): ComposerDiscoveryMatch[];
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: string, item: ComposerDiscoveryItem): ComposerSlashResolution | null;
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: string, insertText: string): string;
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: string, insertText: string): string;
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: string, shiftKey: boolean): ComposerSlashKeyAction;
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: ComposerMenuAnchorRect): CSSProperties;
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: ComposerMenuAnchorRect): CSSProperties;
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 (Tovu stuck-chat investigation,
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 = (data: unknown) => string | undefined;
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: string, renderer: ExtEventRenderer, options?: ExtEventRendererOptions): () => void;
80
- export declare function getExtEventRenderer(name: string): ExtEventRenderer | undefined;
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: string, data: unknown): string;
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: ChatPaneAgent): string;
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 }) {