@kortix/ui 0.14.4 → 0.14.6

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 (228) hide show
  1. package/README.md +8 -4
  2. package/dist/access/access/access-dialog.js +8 -5
  3. package/dist/access/access/copy-row.js +2 -2
  4. package/dist/access/sharing-picker.js +2 -1
  5. package/dist/browser/app-in-browser.js +5 -3
  6. package/dist/browser/browser-panel.js +6 -6
  7. package/dist/browser/sandbox-browser-chrome.d.ts +4 -1
  8. package/dist/browser/sandbox-browser-chrome.js +4 -3
  9. package/dist/customize/agents/agent-access.d.ts +20 -0
  10. package/dist/customize/agents/agent-access.js +17 -0
  11. package/dist/customize/agents/agent-grant-pages.js +5 -6
  12. package/dist/customize/agents/agent-page.js +2 -1
  13. package/dist/customize/agents/agents-page.js +18 -1
  14. package/dist/customize/connectors/add-mcp/add-mcp-server.js +4 -2
  15. package/dist/customize/connectors/connector-routes.d.ts +3 -1
  16. package/dist/customize/connectors/connector-routes.js +18 -0
  17. package/dist/customize/connectors/connectors-page.js +23 -4
  18. package/dist/customize/connectors/detail/connector-advanced-tools.js +4 -6
  19. package/dist/customize/providers/provider-accordion.js +2 -1
  20. package/dist/customize/providers/provider-connect.js +2 -1
  21. package/dist/customize/sections/component/telegram-channel-row.js +1 -1
  22. package/dist/customize/sections/connector-connection-modal.js +11 -9
  23. package/dist/customize/sections/connectors-view.js +11 -13
  24. package/dist/customize/sections/llm-provider/constants.d.ts +0 -1
  25. package/dist/customize/sections/llm-provider/constants.js +0 -2
  26. package/dist/customize/sections/llm-provider/models-tab-group.d.ts +1 -1
  27. package/dist/customize/sections/llm-provider/models-tab-group.js +7 -4
  28. package/dist/customize/sections/llm-provider/models-tab-row.d.ts +2 -18
  29. package/dist/customize/sections/llm-provider/models-tab-row.js +6 -30
  30. package/dist/customize/sections/llm-provider/models-tab.d.ts +0 -1
  31. package/dist/customize/sections/llm-provider/models-tab.js +1 -10
  32. package/dist/customize/sections/llm-provider/use-connected-providers.js +2 -2
  33. package/dist/customize/sections/llm-provider/utils.d.ts +1 -1
  34. package/dist/customize/sections/llm-provider/utils.js +7 -14
  35. package/dist/customize/sections/view/gateway/_shared.js +1 -2
  36. package/dist/customize/sections/view/gateway/gateway-routing.js +18 -20
  37. package/dist/customize/sections/view/sandbox-provider-coverage.d.ts +3 -18
  38. package/dist/customize/sections/view/sandbox-provider-coverage.js +7 -27
  39. package/dist/customize/settings/rail-copy-context.js +5 -3
  40. package/dist/customize/settings/tabs/sandbox-tab.d.ts +2 -27
  41. package/dist/customize/settings/tabs/sandbox-tab.js +13 -112
  42. package/dist/customize/settings/tabs/sandbox-template-form.js +9 -12
  43. package/dist/customize/shared/capability-tabs.js +2 -1
  44. package/dist/customize/shared/entity/entity-modal.d.ts +5 -1
  45. package/dist/customize/shared/entity/entity-modal.js +7 -4
  46. package/dist/customize/skills/remove-skill-button.d.ts +16 -0
  47. package/dist/customize/skills/remove-skill-button.js +42 -0
  48. package/dist/customize/skills/skills-page.js +4 -6
  49. package/dist/customize/triggers/schedule/event-trigger-copy.js +3 -2
  50. package/dist/customize/triggers/schedule/schedule-builder.js +7 -4
  51. package/dist/customize/triggers/schedule/trigger-composer.js +5 -2
  52. package/dist/customize/triggers/schedule/trigger-list.d.ts +0 -2
  53. package/dist/customize/triggers/schedule/trigger-list.js +1 -18
  54. package/dist/customize/triggers/schedule/trigger-tile.d.ts +1 -32
  55. package/dist/customize/triggers/schedule/trigger-tile.js +1 -16
  56. package/dist/customize/triggers/schedule/when-event.js +1 -1
  57. package/dist/diff/diff-view.js +9 -10
  58. package/dist/files/editor/code-editor.js +2 -3
  59. package/dist/files/project/components/change-request-detail-dialog.d.ts +3 -1
  60. package/dist/files/project/components/change-request-detail-dialog.js +3 -5
  61. package/dist/files/project/components/change-request-status-badge.d.ts +12 -0
  62. package/dist/files/project/components/change-request-status-badge.js +27 -0
  63. package/dist/files/project/components/change-requests-panel.js +1 -1
  64. package/dist/files/project/components/drive-explorer.js +2 -1
  65. package/dist/files/project/components/drive-grid-view.js +11 -7
  66. package/dist/files/project/components/drive-list-view.js +4 -4
  67. package/dist/files/project/components/file-breadcrumbs.js +1 -1
  68. package/dist/files/project/components/file-explorer-page.js +1 -1
  69. package/dist/files/project/components/file-history-popover.js +1 -6
  70. package/dist/files/project/components/file-search.js +6 -1
  71. package/dist/files/project/components/open-change-request-dialog.js +2 -1
  72. package/dist/files/project/components/version-selector-logic.d.ts +11 -0
  73. package/dist/files/project/components/version-selector-logic.js +26 -0
  74. package/dist/files/project/components/version-selector.js +11 -20
  75. package/dist/files/project/hooks/use-change-requests.d.ts +14 -1
  76. package/dist/files/project/hooks/use-change-requests.js +44 -25
  77. package/dist/files/project/hooks/use-server-health.js +17 -15
  78. package/dist/files/renderers/docx/docx-viewer.js +4 -6
  79. package/dist/files/renderers/image-renderer.js +6 -24
  80. package/dist/files/renderers/pdf/pdf-renderer.d.ts +3 -1
  81. package/dist/files/renderers/pdf/pdf-renderer.js +2 -2
  82. package/dist/files/renderers/pdf/pdf-viewer.d.ts +3 -0
  83. package/dist/files/renderers/pdf/pdf-viewer.js +16 -7
  84. package/dist/files/renderers/presentation/FullScreenPresentationViewer.js +2 -11
  85. package/dist/files/renderers/show-content-renderer.js +1 -1
  86. package/dist/files/renderers/sqlite-renderer.js +8 -3
  87. package/dist/files/renderers/xlsx/xlsx-viewer.js +15 -16
  88. package/dist/files/tree/components/file-tree-item.js +1 -1
  89. package/dist/files/viewer/file-content-renderer.d.ts +4 -1
  90. package/dist/files/viewer/file-content-renderer.js +2 -2
  91. package/dist/files/viewer/file-preview-modal.js +1 -1
  92. package/dist/files/viewer/use-file-content-state.js +1 -1
  93. package/dist/i18n/en.d.ts +36 -35
  94. package/dist/i18n/en.js +45 -44
  95. package/dist/index.d.ts +1 -1
  96. package/dist/internal/desktop/desktop-back-button.js +5 -3
  97. package/dist/internal/hooks/use-auto-scroll.js +5 -3
  98. package/dist/internal/hooks/use-notification-center.js +21 -15
  99. package/dist/internal/hooks/use-review-session-summary.d.ts +6 -0
  100. package/dist/internal/hooks/use-review-session-summary.js +11 -0
  101. package/dist/internal/lib/browser-error-noise.d.ts +1 -1
  102. package/dist/internal/lib/browser-error-noise.js +1 -1
  103. package/dist/internal/lib/browser-noise/rules/rejections.d.ts +10 -0
  104. package/dist/internal/lib/browser-noise/rules/rejections.js +27 -1
  105. package/dist/internal/lib/provider-branding.js +14 -3
  106. package/dist/internal/lib/trigger-clock.d.ts +42 -0
  107. package/dist/internal/lib/trigger-clock.js +34 -0
  108. package/dist/internal/lib/web-notifications.d.ts +6 -0
  109. package/dist/internal/lib/web-notifications.js +57 -7
  110. package/dist/internal/notifications/notification-bell.d.ts +13 -0
  111. package/dist/internal/notifications/notification-bell.js +1 -1
  112. package/dist/internal/notifications/notification-rows.js +2 -1
  113. package/dist/internal/outcomes/outcome-types.d.ts +5 -0
  114. package/dist/internal/reminders/reminder-filter.d.ts +12 -0
  115. package/dist/internal/reminders/reminder-filter.js +34 -0
  116. package/dist/internal/reminders/reminder-format.d.ts +6 -0
  117. package/dist/internal/reminders/reminder-format.js +20 -2
  118. package/dist/internal/stores/kortix-computer-store.d.ts +1 -1
  119. package/dist/internal/stores/session-hover-store.d.ts +20 -5
  120. package/dist/internal/stores/session-hover-store.js +31 -13
  121. package/dist/internal/stores/session-panel-memory-store.js +2 -0
  122. package/dist/markdown/code/inline-code.js +8 -0
  123. package/dist/markdown/code/output-paths-context.d.ts +18 -0
  124. package/dist/markdown/code/output-paths-context.js +35 -0
  125. package/dist/markdown/genui/components/charts.js +6 -5
  126. package/dist/primitives/count-badge.d.ts +3 -1
  127. package/dist/primitives/count-badge.js +3 -3
  128. package/dist/primitives/glyph-paths-source.js +1 -1
  129. package/dist/primitives/glyph-paths.generated.js +1 -1
  130. package/dist/primitives/map.js +2 -1
  131. package/dist/primitives/preview-image.js +4 -2
  132. package/dist/primitives/textarea.js +1 -1
  133. package/dist/primitives/typed-title.js +12 -14
  134. package/dist/primitives/typing-animation.js +4 -2
  135. package/dist/providers/ui-provider.d.ts +57 -0
  136. package/dist/providers/ui-provider.js +4 -0
  137. package/dist/session/composer/ComposerCard.d.ts +2 -0
  138. package/dist/session/composer/ComposerCard.js +5 -1
  139. package/dist/session/composer/agent-selector.js +17 -5
  140. package/dist/session/composer/composer-chat-input.js +5 -3
  141. package/dist/session/composer/composer-logic.js +8 -7
  142. package/dist/session/composer/composer-toolbar.js +4 -2
  143. package/dist/session/composer/composer-underbar.js +3 -1
  144. package/dist/session/composer/composer.js +34 -5
  145. package/dist/session/composer/draft/composer-draft.js +4 -4
  146. package/dist/session/composer/menus/slash-actions.d.ts +22 -2
  147. package/dist/session/composer/menus/slash-actions.js +20 -0
  148. package/dist/session/composer/menus/slash-items.js +19 -3
  149. package/dist/session/composer/pasted-text.js +3 -2
  150. package/dist/session/composer/stores/queued-draft-store.js +3 -2
  151. package/dist/session/header/reminder-parts.d.ts +294 -0
  152. package/dist/session/header/reminder-parts.js +120 -0
  153. package/dist/session/header/session-reminders-indicator.d.ts +13 -0
  154. package/dist/session/header/session-reminders-indicator.js +142 -35
  155. package/dist/session/media/sandbox-url-detector.js +0 -7
  156. package/dist/session/models/model-availability.d.ts +23 -0
  157. package/dist/session/models/model-availability.js +20 -0
  158. package/dist/session/models/model-grouping.js +1 -4
  159. package/dist/session/models/model-selector.js +23 -14
  160. package/dist/session/models/use-model-connection-gate.js +4 -1
  161. package/dist/session/outcomes/change-request-outcomes.js +24 -3
  162. package/dist/session/outcomes/session-outcomes-provider.d.ts +3 -1
  163. package/dist/session/outcomes/session-outcomes-provider.js +31 -3
  164. package/dist/session/overrides/provider-secret-pool-editor.js +2 -1
  165. package/dist/session/overrides/session-overrides-composer.d.ts +1 -1
  166. package/dist/session/overrides/session-overrides-composer.js +5 -1
  167. package/dist/session/panel/advanced/advanced-panel.js +3 -7
  168. package/dist/session/panel/easy/app-preview.js +12 -2
  169. package/dist/session/panel/easy/easy-panel-logic.d.ts +1 -1
  170. package/dist/session/panel/easy/easy-panel-logic.js +1 -1
  171. package/dist/session/panel/easy/outputs-card.js +2 -1
  172. package/dist/session/panel/easy/panel-card.js +5 -3
  173. package/dist/session/panel/easy/preview-load.d.ts +14 -0
  174. package/dist/session/panel/easy/preview-load.js +15 -0
  175. package/dist/session/panel/easy/step-detail-body.js +2 -7
  176. package/dist/session/panel/mobile-tool-drawer.js +4 -2
  177. package/dist/session/panel/open-session-quick-view.js +2 -1
  178. package/dist/session/panel/panel-fallback.js +8 -4
  179. package/dist/session/panel/session-panel-provider.d.ts +1 -0
  180. package/dist/session/panel/session-panel-provider.js +19 -3
  181. package/dist/session/panel/session-reminders-panel.d.ts +4 -0
  182. package/dist/session/panel/session-reminders-panel.js +109 -0
  183. package/dist/session/panel/shared/use-panel-derivations.js +6 -4
  184. package/dist/session/panel/shared/use-panel-handoffs.d.ts +2 -2
  185. package/dist/session/panel/shared/use-panel-handoffs.js +5 -1
  186. package/dist/session/panel/use-session-panel-layout.js +7 -5
  187. package/dist/session/print/session-print-header.d.ts +0 -10
  188. package/dist/session/print/session-print-header.js +25 -9
  189. package/dist/session/status/session-surface.js +1 -1
  190. package/dist/session/thread/optimistic-turn.d.ts +5 -1
  191. package/dist/session/thread/optimistic-turn.js +8 -6
  192. package/dist/session/thread/session-chat.d.ts +6 -1
  193. package/dist/session/thread/session-chat.js +34 -12
  194. package/dist/session/thread/session-history.js +3 -1
  195. package/dist/session/thread/session-thread.d.ts +10 -1
  196. package/dist/session/thread/session-thread.js +2 -2
  197. package/dist/session/thread/transcript.js +13 -7
  198. package/dist/session/tool/shared/file-list.js +19 -2
  199. package/dist/session/tool/shared/infrastructure-contexts.d.ts +7 -0
  200. package/dist/session/tool/shared/infrastructure-contexts.js +7 -0
  201. package/dist/session/tool/shared/infrastructure-output.js +9 -5
  202. package/dist/session/tool/shared/infrastructure-row-header.js +9 -1
  203. package/dist/session/tool/shared/infrastructure-rows.js +7 -5
  204. package/dist/session/tool/tools/bash-tool.js +10 -6
  205. package/dist/session/tool/tools/web-search-tool.js +25 -3
  206. package/dist/session/turn/burst-step-body.js +4 -0
  207. package/dist/session/turn/recovered-failures.d.ts +8 -0
  208. package/dist/session/turn/recovered-failures.js +32 -0
  209. package/dist/session/turn/streaming-cadence.js +5 -8
  210. package/dist/styles.css +1 -1
  211. package/dist/terminal/session-terminal-panel.js +3 -5
  212. package/dist/theme/theme.js +2 -1
  213. package/dist/workspace/projects/modal/github-import-pickers.js +7 -6
  214. package/dist/workspace/review/review-center.js +13 -10
  215. package/dist/workspace/session-workspace.js +5 -9
  216. package/dist/workspace/sessions/deleted-session-row.js +5 -2
  217. package/dist/workspace/sidebar/footer/project-computer-nav.js +6 -4
  218. package/dist/workspace/sidebar/footer/project-mcp-nav.js +6 -2
  219. package/dist/workspace/sidebar/modal/rename-session-modal.js +8 -3
  220. package/dist/workspace/sidebar/project-session-list.js +52 -55
  221. package/dist/workspace/sidebar/session-brief-hover-card.d.ts +35 -18
  222. package/dist/workspace/sidebar/session-brief-hover-card.js +114 -37
  223. package/dist/workspace/sidebar/session-status-mark.d.ts +15 -1
  224. package/dist/workspace/sidebar/session-status-mark.js +63 -17
  225. package/package.json +7 -3
  226. package/skills/kortix-ui/SKILL.md +1 -1
  227. package/dist/customize/sections/view/sandbox-provider-result.d.ts +0 -47
  228. package/dist/customize/sections/view/sandbox-provider-result.js +0 -79
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { HighlightedCode } from '../../../markdown/code/index.js';
4
4
  import { TextShimmer } from '../../../primitives/text-shimmer.js';
5
- import { BasicTool, partInput, partMetadata, partOutput, partStatus, partStreamingInput, StructuredOutput, ToolRunningContext, useToolCardFrame, useToolCardPad, useToolIndent, useToolOpen, partRawOutput, } from '../shared/infrastructure.js';
5
+ import { BasicTool, partInput, partMetadata, partOutput, partStatus, partStreamingInput, StructuredOutput, RecoveredCallsContext, ToolRunningContext, useToolCardFrame, useToolCardPad, useToolIndent, useToolOpen, partRawOutput, } from '../shared/infrastructure.js';
6
6
  import { GenericTool } from '../generic-tool.js';
7
7
  import { ToolRegistry } from '../shared/registry.js';
8
8
  import { cn } from '../../../lib/utils.js';
@@ -140,7 +140,7 @@ export function dedentCommand(raw) {
140
140
  * The exit-code strip keeps its shorter 8px vertical inset: it is metadata
141
141
  * about the card, not a third pane of content.
142
142
  */
143
- function CommandBlock({ command, output, richOutput, exitCode, settled, }) {
143
+ function CommandBlock({ command, output, richOutput, exitCode, recovered = false, settled, }) {
144
144
  const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
145
145
  const hasOutput = Boolean(richOutput || output);
146
146
  const failed = typeof exitCode === 'number' && exitCode !== 0;
@@ -161,7 +161,7 @@ function CommandBlock({ command, output, richOutput, exitCode, settled, }) {
161
161
  _jsxs("div", { className: cn('relative overflow-hidden', frame), children: [_jsx("div", { "data-scrollable": true, className: cn('max-h-64 overflow-auto', frame && 'bg-muted/40'), children: _jsx("pre", { className: cn('text-foreground font-mono text-xs leading-relaxed wrap-break-word whitespace-pre-wrap [&_code]:border-none [&_code]:bg-transparent [&_code]:p-0 [&_code]:text-xs [&_code]:leading-relaxed [&_code]:whitespace-pre-wrap [&_pre]:whitespace-pre-wrap [&_span]:border-none [&_span]:outline-none', paneInset, 'pr-11'), children: _jsx(HighlightedCode, { code: command, language: "bash", children: command }) }) }), _jsx("div", { className: "absolute top-2 right-2", children: _jsx(CopyButton, { code: command, className: "text-muted-foreground hover:text-foreground" }) }), (hasOutput || settled) && (_jsx("div", { className: "border-border/60 border-t", children: richOutput ? (richOutput) : output ? (_jsxs("div", { className: "relative", children: [_jsx("div", { "data-scrollable": true, className: "max-h-80 overflow-auto", children: _jsx("div", { className: cn('text-muted-foreground font-mono text-xs leading-relaxed wrap-break-word whitespace-pre-wrap', paneInset, 'pr-11'), children: output }) }), _jsx("div", { className: "absolute top-2 right-2", children: _jsx(CopyButton, { code: output, className: "text-muted-foreground hover:text-foreground" }) })] })) : (_jsx("p", { className: cn('text-muted-foreground', paneInset, 'text-xs leading-relaxed'), children: tI18nComplete.raw('textf7e31759b202') })) })), failed && (_jsxs("div", { className: cn(
162
162
  // `kortix-red`, matching the trigger's "Command failed": the strip
163
163
  // is the card's own copy of the verdict, so the two agree in tone.
164
- 'border-border/60 text-kortix-red border-t py-2 font-mono text-xs tabular-nums',
164
+ 'border-border/60 border-t py-2 font-mono text-xs tabular-nums', recovered ? 'text-muted-foreground' : 'text-kortix-red',
165
165
  // Metadata about the card, not a third pane: it keeps the shorter
166
166
  // 8px vertical inset, and only the horizontal one is the surface's
167
167
  // to supply.
@@ -244,9 +244,13 @@ export function BashTool({ call, defaultOpen, forceOpen, locked }) {
244
244
  const exitCode = useMemo(() => {
245
245
  if (call.status !== 'complete')
246
246
  return undefined;
247
- return shellExitCode(partRawOutput(call));
247
+ // OpenCode tags the code in the output; pi records it beside the output (KRTX-3021).
248
+ const recorded = partMetadata(call).exit;
249
+ return shellExitCode(partRawOutput(call)) ?? (typeof recorded === 'number' ? recorded : undefined);
248
250
  }, [call]);
249
251
  const failed = typeof exitCode === 'number' && exitCode !== 0;
252
+ // A failure the agent moved past keeps its words, not the red (KRTX-3021).
253
+ const recovered = useContext(RecoveredCallsContext).has(call.id);
250
254
  // `teams send "…"` / `slack send …` / `telegram send …` IS the reply the
251
255
  // person in the channel received. It renders as that message — badge, text,
252
256
  // attachment — with the shell call kept underneath for the record. A failed
@@ -268,12 +272,12 @@ export function BashTool({ call, defaultOpen, forceOpen, locked }) {
268
272
  if (!command && toolKind(call.name) !== 'bash') {
269
273
  return _jsx(GenericTool, { call: call, defaultOpen: defaultOpen, forceOpen: forceOpen, locked: locked });
270
274
  }
271
- return (_jsxs(BasicTool, { icon: send ? (_jsx(ChannelBrandMark, { platform: send.platform, className: "size-4 shrink-0" })) : (_jsx(TerminalIcon, { className: "size-4 shrink-0" })), trigger: isStalePending ? (_jsx("div", { className: "flex min-w-0 flex-1 items-center gap-1.5", children: _jsx(TextShimmer, { duration: 1, spread: 2, children: tI18nComplete.raw('textb93900bded31') }) })) : commandPreview ? (_jsx(BashTrigger, { title: title, failed: failed, command: command, commandPreview: send ? (sendText ?? send.file ?? '') : commandPreview, extraLines: send ? 0 : extraLines, live: running && status !== 'completed' && status !== 'error' })) : null,
275
+ return (_jsxs(BasicTool, { icon: send ? (_jsx(ChannelBrandMark, { platform: send.platform, className: "size-4 shrink-0" })) : (_jsx(TerminalIcon, { className: "size-4 shrink-0" })), trigger: isStalePending ? (_jsx("div", { className: "flex min-w-0 flex-1 items-center gap-1.5", children: _jsx(TextShimmer, { duration: 1, spread: 2, children: tI18nComplete.raw('textb93900bded31') }) })) : commandPreview ? (_jsx(BashTrigger, { title: title, failed: failed && !recovered, command: command, commandPreview: send ? (sendText ?? send.file ?? '') : commandPreview, extraLines: send ? 0 : extraLines, live: running && status !== 'completed' && status !== 'error' })) : null,
272
276
  // The reply is the point of the row, so it opens showing it.
273
277
  defaultOpen: defaultOpen || Boolean(send), forceOpen: forceOpen, locked: locked, children: [send && (_jsx("div", { className: cn('mb-1.5', indent && 'mt-1.5', indent), children: _jsx(ChannelSendCard, { send: send }) })), command && (
274
278
  // `CommandBlock` is a bordered card like the shared three, so it takes
275
279
  // the same gate: the seam belongs to the inline row it hangs under, not
276
280
  // to the panel body that already supplies `px-3 py-3`.
277
- _jsx("div", { className: cn(indent && 'mt-1.5', indent), children: _jsx(CommandBlock, { command: command, output: plainOutput, richOutput: richOutput, exitCode: exitCode, settled: status === 'completed' || status === 'error' }) }))] }));
281
+ _jsx("div", { className: cn(indent && 'mt-1.5', indent), children: _jsx(CommandBlock, { command: command, output: plainOutput, richOutput: richOutput, exitCode: exitCode, recovered: recovered, settled: status === 'completed' || status === 'error' }) }))] }));
278
282
  }
279
283
  ToolRegistry.register('bash', BashTool);
@@ -1,13 +1,19 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { BasicTool, isErrorOutput, partInput, partOutput, partStatus, ToolOutputFallback, partRawOutput, } from '../shared/infrastructure.js';
4
+ import { JsonFailureOutputCard } from '../shared/infrastructure-output.js';
4
5
  import { ToolRegistry } from '../shared/registry.js';
5
6
  import { ToolResultCard } from '../shared/result-card.js';
6
7
  import { WebSourceRow } from '../shared/web-source-row.js';
7
8
  import { MagnifyingGlassIcon as Search } from '@phosphor-icons/react';
8
9
  import { useMemo } from 'react';
9
10
  import { humanizeSearchQuery } from '@kortix/sdk';
10
- import { parseWebSearchOutput, } from '@kortix/sdk';
11
+ import { parseJsonFailure, parseWebSearchOutput, } from '@kortix/sdk';
12
+ /** A failed query's error as the shared failure card reads it: a nested JSON
13
+ * message (a provider's "over plan quota") becomes its own line. */
14
+ function queryFailure(error) {
15
+ return parseJsonFailure(JSON.stringify({ success: false, error })) ?? { errorSummary: error };
16
+ }
11
17
  /**
12
18
  * Every source, flat, inside one bordered card.
13
19
  *
@@ -26,8 +32,24 @@ import { parseWebSearchOutput, } from '@kortix/sdk';
26
32
  * get a muted one-line query caption between segments — a caption, not a
27
33
  * control. No accordions, no "show more" button.
28
34
  */
35
+ /** The one error every query of a batch failed with, or null. A provider
36
+ * limit fails them all alike, and five copies of it read as five problems. */
37
+ function sharedFailure(queryResults) {
38
+ const first = queryResults[0]?.error;
39
+ if (queryResults.length < 2 || !first)
40
+ return null;
41
+ return queryResults.every((qr) => qr.error === first && qr.sources.length === 0) ? first : null;
42
+ }
43
+ function QueryCaption({ query }) {
44
+ return (_jsxs("div", { className: "text-muted-foreground flex items-center gap-2 px-2 pt-2 pb-1 text-xs", children: [_jsx(Search, { className: "size-3 shrink-0" }), _jsx("span", { className: "truncate", children: humanizeSearchQuery(query) })] }));
45
+ }
29
46
  function FlatSourceList({ queryResults }) {
30
- return (_jsx(ToolResultCard, { children: queryResults.map((qr, qi) => (_jsxs("div", { children: [queryResults.length > 1 && qr.sources.length > 0 && (_jsxs("div", { className: "text-muted-foreground/70 flex items-center gap-2 px-2 pt-2 pb-1 text-xs", children: [_jsx(Search, { className: "size-3 shrink-0" }), _jsx("span", { className: "truncate", children: humanizeSearchQuery(qr.query) })] })), qr.sources.map((src) => (_jsx(WebSourceRow, { url: src.url, title: src.title || src.url }, src.url)))] }, qi))) }));
47
+ const shared = sharedFailure(queryResults);
48
+ if (shared) {
49
+ // The failure once, then the searches it stopped (KRTX-3022).
50
+ return (_jsxs(ToolResultCard, { children: [_jsx(JsonFailureOutputCard, { failure: queryFailure(shared) }), queryResults.map((qr, qi) => (_jsx(QueryCaption, { query: qr.query }, qi)))] }));
51
+ }
52
+ return (_jsx(ToolResultCard, { children: queryResults.map((qr, qi) => (_jsxs("div", { children: [queryResults.length > 1 && (qr.sources.length > 0 || qr.error) && _jsx(QueryCaption, { query: qr.query }), qr.error && _jsx(JsonFailureOutputCard, { failure: queryFailure(qr.error) }), qr.sources.map((src) => (_jsx(WebSourceRow, { url: src.url, title: src.title || src.url }, src.url)))] }, qi))) }));
31
53
  }
32
54
  export function WebSearchTool({ call, defaultOpen, forceOpen, locked }) {
33
55
  const input = partInput(call);
@@ -58,7 +80,7 @@ export function WebSearchTool({ call, defaultOpen, forceOpen, locked }) {
58
80
  const triggerBadge = status === 'completed' && !isError && totalSources > 0
59
81
  ? `${totalSources} ${totalSources === 1 ? 'result' : 'results'}`
60
82
  : undefined;
61
- return (_jsx(BasicTool, { icon: _jsx(Search, { className: "size-3.5 shrink-0" }), trigger: _jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1.5", children: [_jsx("span", { className: "text-foreground min-w-0 truncate text-sm", children: triggerLabel }), triggerBadge && (_jsx("span", { className: "text-muted-foreground/70 ml-auto shrink-0 text-sm whitespace-nowrap", children: triggerBadge }))] }), defaultOpen: defaultOpen, forceOpen: forceOpen, locked: locked, children: _jsx(_Fragment, { children: isError ? (_jsx(ToolOutputFallback, { output: output, toolName: "web_search" })) : queryResults.length > 0 ? (_jsx(FlatSourceList, { queryResults: queryResults })) : output ? (_jsx(ToolOutputFallback, { output: output, isStreaming: status === 'running', toolName: "web_search" })) : null }) }));
83
+ return (_jsx(BasicTool, { icon: _jsx(Search, { className: "size-3.5 shrink-0" }), trigger: _jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1.5", children: [_jsx("span", { className: "text-foreground min-w-0 truncate text-sm", children: triggerLabel }), triggerBadge && (_jsx("span", { className: "text-muted-foreground ml-auto shrink-0 text-sm whitespace-nowrap", children: triggerBadge }))] }), defaultOpen: defaultOpen, forceOpen: forceOpen, locked: locked, children: _jsx(_Fragment, { children: isError ? (_jsx(ToolOutputFallback, { output: output, toolName: "web_search" })) : queryResults.length > 0 ? (_jsx(FlatSourceList, { queryResults: queryResults })) : output ? (_jsx(ToolOutputFallback, { output: output, isStreaming: status === 'running', toolName: "web_search" })) : null }) }));
62
84
  }
63
85
  ToolRegistry.register('websearch', WebSearchTool);
64
86
  ToolRegistry.register('web-search', WebSearchTool);
@@ -5,6 +5,7 @@ import { ActivityGroupStep } from './activity-group-step.js';
5
5
  import { ActivityStep, StepLabelRow } from './activity-step.js';
6
6
  import { AnsweredQuestionStep, isAnsweredQuestionCall } from './answered-question-step.js';
7
7
  import { stepLabel } from '@kortix/sdk';
8
+ import { CompactionMarker } from './compaction-card.js';
8
9
  import { ToolRegistry } from '../tool/shared/registry.js';
9
10
  /** A host's `ToolRegistry.register` row for this tool wins over the chip and question rows. */
10
11
  const hostRow = (call) => ToolRegistry.replaced(call.name, call.kind);
@@ -44,6 +45,9 @@ export function StepBody({ step, bareRow = false, running, sessionId, disableNav
44
45
  const calls = step.blocks;
45
46
  return isFileChipRun(calls) ? (_jsx(ActivityFileChipStep, { calls: calls, sessionId: sessionId, disableNavigation: disableNavigation })) : (_jsx(ActivityGroupStep, { step: { id: calls[0].call_id, family: step.family, label: step.label, parts: calls, status: step.status, durationMs: step.durationMs }, sessionId: sessionId, disableNavigation: disableNavigation }));
46
47
  }
48
+ if (step.block.type === 'compaction') {
49
+ return _jsx(CompactionMarker, { running: running && step.block.summary === null, summary: step.block.summary ?? undefined });
50
+ }
47
51
  // Neither tool nor reasoning: the label row only. The turn modules never
48
52
  // silently drop a block (`stepLabel` falls back to a generic 'Used' label).
49
53
  if (step.block.type !== 'tool_call') {
@@ -0,0 +1,8 @@
1
+ import { type SessionLogBlock } from '@kortix/sdk';
2
+ /**
3
+ * The failed commands of a turn that a later step worked past: a call after them
4
+ * completed and did not fail. A probe such as `ls missing-dir` exits non-zero by
5
+ * design, and the agent carries on; only a failure the turn ended on is still
6
+ * the news (KRTX-3021).
7
+ */
8
+ export declare function recoveredShellFailures(blocks: readonly SessionLogBlock[]): Set<string>;
@@ -0,0 +1,32 @@
1
+ import { shellExitCode, toolCallMetadata, toolCallRawOutput, } from '@kortix/sdk';
2
+ /** A shell command that ran and exited non-zero, by either harness's record of its code. */
3
+ function failedCommand(block) {
4
+ if (block.name !== 'bash' || block.status !== 'complete')
5
+ return false;
6
+ const recorded = toolCallMetadata(block).exit;
7
+ const exit = shellExitCode(toolCallRawOutput(block)) ?? (typeof recorded === 'number' ? recorded : undefined);
8
+ return typeof exit === 'number' && exit !== 0;
9
+ }
10
+ /**
11
+ * The failed commands of a turn that a later step worked past: a call after them
12
+ * completed and did not fail. A probe such as `ls missing-dir` exits non-zero by
13
+ * design, and the agent carries on; only a failure the turn ended on is still
14
+ * the news (KRTX-3021).
15
+ */
16
+ export function recoveredShellFailures(blocks) {
17
+ const recovered = new Set();
18
+ const pending = [];
19
+ for (const block of blocks) {
20
+ if (block.type !== 'tool_call')
21
+ continue;
22
+ if (failedCommand(block)) {
23
+ pending.push(block.id);
24
+ }
25
+ else if (block.status === 'complete') {
26
+ for (const id of pending)
27
+ recovered.add(id);
28
+ pending.length = 0;
29
+ }
30
+ }
31
+ return recovered;
32
+ }
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { useEffect, useRef, useState } from 'react';
2
+ import { useEffect, useState } from 'react';
3
3
  /**
4
4
  * Streamed text reaches the client in network chunks of any size and at any
5
5
  * spacing: one token every 16 ms, or a paragraph after a 300 ms stall.
@@ -287,14 +287,11 @@ export function useStreamingCadence(rawValue, active, revealKey, clock = default
287
287
  const resume = kept && clock.now() - kept.at < RESUME_WINDOW_MS && kept.len < value.length;
288
288
  return { text: resume ? value.slice(0, kept.len) : value, streaming: active };
289
289
  });
290
- const pacerRef = useRef(null);
291
- if (pacerRef.current === null) {
292
- pacerRef.current = createStreamPacer((text, streaming) => setState({ text, streaming }), state.text, clock, state.streaming);
293
- }
290
+ const [pacer] = useState(() => createStreamPacer((text, streaming) => setState({ text, streaming }), state.text, clock, state.streaming));
294
291
  useEffect(() => {
295
- pacerRef.current?.push(value, active);
296
- }, [value, active]);
297
- useEffect(() => () => pacerRef.current?.dispose(), []);
292
+ pacer.push(value, active);
293
+ }, [pacer, value, active]);
294
+ useEffect(() => () => pacer.dispose(), [pacer]);
298
295
  useEffect(() => {
299
296
  if (revealKey && state.streaming)
300
297
  rememberReveal(revealKey, state.text.length, clock.now());