@opengeni/react 3.3.2 → 3.4.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 (855) hide show
  1. package/README.md +769 -22
  2. package/dist/accounts.js +2 -0
  3. package/dist/accounts.js.map +1 -1
  4. package/dist/approvals.d.ts +2 -1
  5. package/dist/artifacts-document.js +5 -3
  6. package/dist/artifacts-presentation.js +5 -3
  7. package/dist/artifacts-spreadsheet.d.ts +1 -1
  8. package/dist/artifacts-spreadsheet.js +5 -3
  9. package/dist/artifacts.d.ts +5 -1
  10. package/dist/artifacts.js +536 -42
  11. package/dist/artifacts.js.map +1 -1
  12. package/dist/browser-viewer-ZWR6MWJ7.js +19 -0
  13. package/dist/capability-catalog-row.d.ts +20 -0
  14. package/dist/capability-logo-fallback.d.ts +3 -0
  15. package/dist/chat-interactive-block-4EUKJXUO.js +14 -0
  16. package/dist/chunk-2DDJ6YH6.js +41 -0
  17. package/dist/chunk-2DDJ6YH6.js.map +1 -0
  18. package/dist/chunk-2HVLQ26H.js +183 -0
  19. package/dist/chunk-2HVLQ26H.js.map +1 -0
  20. package/dist/{chunk-XYRDDSXF.js → chunk-2I6R3GCZ.js} +4285 -3980
  21. package/dist/chunk-2I6R3GCZ.js.map +1 -0
  22. package/dist/{chunk-JAQ5K7W6.js → chunk-2SFFYIH3.js} +1178 -166
  23. package/dist/chunk-2SFFYIH3.js.map +1 -0
  24. package/dist/chunk-3PVDYTTB.js +24 -0
  25. package/dist/chunk-3PVDYTTB.js.map +1 -0
  26. package/dist/chunk-3RJKUVCA.js +14068 -0
  27. package/dist/chunk-3RJKUVCA.js.map +1 -0
  28. package/dist/chunk-3UZCWK46.js +298 -0
  29. package/dist/chunk-3UZCWK46.js.map +1 -0
  30. package/dist/chunk-47CKUXTW.js +293 -0
  31. package/dist/chunk-47CKUXTW.js.map +1 -0
  32. package/dist/chunk-4QYP6QUI.js +109 -0
  33. package/dist/chunk-4QYP6QUI.js.map +1 -0
  34. package/dist/chunk-5EY26VZ7.js +1053 -0
  35. package/dist/chunk-5EY26VZ7.js.map +1 -0
  36. package/dist/chunk-5I4VRWBB.js +56 -0
  37. package/dist/chunk-5I4VRWBB.js.map +1 -0
  38. package/dist/chunk-5WOR7UCL.js +191 -0
  39. package/dist/chunk-5WOR7UCL.js.map +1 -0
  40. package/dist/chunk-5YJLGOM5.js +51 -0
  41. package/dist/chunk-5YJLGOM5.js.map +1 -0
  42. package/dist/chunk-6JFN3OWG.js +386 -0
  43. package/dist/chunk-6JFN3OWG.js.map +1 -0
  44. package/dist/chunk-7QJT5O4S.js +35 -0
  45. package/dist/chunk-7QJT5O4S.js.map +1 -0
  46. package/dist/chunk-A55KV72M.js +402 -0
  47. package/dist/chunk-A55KV72M.js.map +1 -0
  48. package/dist/chunk-ADWCLI4G.js +2028 -0
  49. package/dist/chunk-ADWCLI4G.js.map +1 -0
  50. package/dist/chunk-B27E3V37.js +29 -0
  51. package/dist/chunk-B27E3V37.js.map +1 -0
  52. package/dist/chunk-B7TC2A33.js +83 -0
  53. package/dist/chunk-B7TC2A33.js.map +1 -0
  54. package/dist/chunk-BLL56F7R.js +385 -0
  55. package/dist/chunk-BLL56F7R.js.map +1 -0
  56. package/dist/chunk-BOFEQC6K.js +162 -0
  57. package/dist/chunk-BOFEQC6K.js.map +1 -0
  58. package/dist/chunk-CM7LVUKC.js +35 -0
  59. package/dist/chunk-CM7LVUKC.js.map +1 -0
  60. package/dist/chunk-CORUP6JB.js +122 -0
  61. package/dist/chunk-CORUP6JB.js.map +1 -0
  62. package/dist/chunk-CSX5FFKZ.js +35 -0
  63. package/dist/chunk-CSX5FFKZ.js.map +1 -0
  64. package/dist/chunk-EGVHJKGJ.js +3125 -0
  65. package/dist/chunk-EGVHJKGJ.js.map +1 -0
  66. package/dist/{chunk-NEXLLTP4.js → chunk-G76LSA76.js} +6 -5
  67. package/dist/chunk-G76LSA76.js.map +1 -0
  68. package/dist/{chunk-PEI6RANI.js → chunk-GQMRL52F.js} +311 -75
  69. package/dist/chunk-GQMRL52F.js.map +1 -0
  70. package/dist/{chunk-ELIYWBZR.js → chunk-GUUXOISA.js} +1113 -457
  71. package/dist/chunk-GUUXOISA.js.map +1 -0
  72. package/dist/{chunk-HUIKIDM5.js → chunk-I7X2P4FU.js} +225 -116
  73. package/dist/chunk-I7X2P4FU.js.map +1 -0
  74. package/dist/chunk-IGFZFLI2.js +1390 -0
  75. package/dist/chunk-IGFZFLI2.js.map +1 -0
  76. package/dist/{chunk-4MDLEHXL.js → chunk-IMSUVYQB.js} +26 -13
  77. package/dist/chunk-IMSUVYQB.js.map +1 -0
  78. package/dist/chunk-IOYHDEP3.js +38 -0
  79. package/dist/chunk-IOYHDEP3.js.map +1 -0
  80. package/dist/chunk-IZV5FTJC.js +55 -0
  81. package/dist/chunk-IZV5FTJC.js.map +1 -0
  82. package/dist/chunk-KF33GTWN.js +343 -0
  83. package/dist/chunk-KF33GTWN.js.map +1 -0
  84. package/dist/chunk-KSGQ5ODA.js +457 -0
  85. package/dist/chunk-KSGQ5ODA.js.map +1 -0
  86. package/dist/{chunk-ZPBTI3TE.js → chunk-KZC6QRNR.js} +1513 -697
  87. package/dist/chunk-KZC6QRNR.js.map +1 -0
  88. package/dist/chunk-LJUMYUBO.js +218 -0
  89. package/dist/chunk-LJUMYUBO.js.map +1 -0
  90. package/dist/chunk-LSW2OJVV.js +380 -0
  91. package/dist/chunk-LSW2OJVV.js.map +1 -0
  92. package/dist/chunk-M357MHH3.js +2 -0
  93. package/dist/chunk-M357MHH3.js.map +1 -0
  94. package/dist/chunk-MK2TQO4Y.js +24 -0
  95. package/dist/chunk-MK2TQO4Y.js.map +1 -0
  96. package/dist/chunk-N7S72EXK.js +25 -0
  97. package/dist/chunk-N7S72EXK.js.map +1 -0
  98. package/dist/{chunk-YVCHAQZY.js → chunk-NKMYZFHA.js} +616 -621
  99. package/dist/chunk-NKMYZFHA.js.map +1 -0
  100. package/dist/chunk-NX4ADH73.js +14 -0
  101. package/dist/chunk-NX4ADH73.js.map +1 -0
  102. package/dist/chunk-O7F6UJO4.js +71 -0
  103. package/dist/chunk-O7F6UJO4.js.map +1 -0
  104. package/dist/chunk-OYQQJL3J.js +16 -0
  105. package/dist/chunk-OYQQJL3J.js.map +1 -0
  106. package/dist/{chunk-H5YGYNXS.js → chunk-P4BNUO57.js} +60 -17
  107. package/dist/chunk-P4BNUO57.js.map +1 -0
  108. package/dist/{chunk-JVYQRRC4.js → chunk-PEHTX3V7.js} +21 -1
  109. package/dist/chunk-PEHTX3V7.js.map +1 -0
  110. package/dist/chunk-PMV53IMM.js +26 -0
  111. package/dist/chunk-PMV53IMM.js.map +1 -0
  112. package/dist/{chunk-KCGXPP5V.js → chunk-PYEOPA4M.js} +13 -7
  113. package/dist/chunk-PYEOPA4M.js.map +1 -0
  114. package/dist/{chunk-KZ73RL76.js → chunk-QHUE7E4N.js} +8 -3
  115. package/dist/chunk-QHUE7E4N.js.map +1 -0
  116. package/dist/chunk-QV3DJVRZ.js +11 -0
  117. package/dist/chunk-QV3DJVRZ.js.map +1 -0
  118. package/dist/chunk-R5SBNG7S.js +810 -0
  119. package/dist/chunk-R5SBNG7S.js.map +1 -0
  120. package/dist/chunk-RI42H5IU.js +326 -0
  121. package/dist/chunk-RI42H5IU.js.map +1 -0
  122. package/dist/chunk-S2QH6EZ5.js +211 -0
  123. package/dist/chunk-S2QH6EZ5.js.map +1 -0
  124. package/dist/chunk-SHLZMAJN.js +304 -0
  125. package/dist/chunk-SHLZMAJN.js.map +1 -0
  126. package/dist/chunk-TGXK4BAZ.js +293 -0
  127. package/dist/chunk-TGXK4BAZ.js.map +1 -0
  128. package/dist/{chunk-5YK2ZDIG.js → chunk-TNEZNMSN.js} +4 -10
  129. package/dist/chunk-TNEZNMSN.js.map +1 -0
  130. package/dist/{chunk-22KP6LR5.js → chunk-TXS65BB2.js} +3 -1
  131. package/dist/{chunk-22KP6LR5.js.map → chunk-TXS65BB2.js.map} +1 -1
  132. package/dist/chunk-UNJPZGNK.js +63 -0
  133. package/dist/chunk-UNJPZGNK.js.map +1 -0
  134. package/dist/{chunk-IOLVN74D.js → chunk-V5ZUJT2T.js} +175 -42
  135. package/dist/chunk-V5ZUJT2T.js.map +1 -0
  136. package/dist/chunk-WBJY57A3.js +402 -0
  137. package/dist/chunk-WBJY57A3.js.map +1 -0
  138. package/dist/chunk-WIUKB3LD.js +1911 -0
  139. package/dist/chunk-WIUKB3LD.js.map +1 -0
  140. package/dist/{chunk-ICA6R6TW.js → chunk-WNN7NO2N.js} +1451 -355
  141. package/dist/chunk-WNN7NO2N.js.map +1 -0
  142. package/dist/{chunk-3AGBGTRT.js → chunk-X5UVUIMF.js} +1712 -1174
  143. package/dist/chunk-X5UVUIMF.js.map +1 -0
  144. package/dist/chunk-XDA5QJE7.js +402 -0
  145. package/dist/chunk-XDA5QJE7.js.map +1 -0
  146. package/dist/{chunk-VZTQ73XT.js → chunk-XEA5BCMA.js} +3 -1
  147. package/dist/{chunk-VZTQ73XT.js.map → chunk-XEA5BCMA.js.map} +1 -1
  148. package/dist/chunk-XHF7OYAN.js +1150 -0
  149. package/dist/chunk-XHF7OYAN.js.map +1 -0
  150. package/dist/chunk-YSRBFJMQ.js +43 -0
  151. package/dist/chunk-YSRBFJMQ.js.map +1 -0
  152. package/dist/chunk-ZOZAPAUO.js +174 -0
  153. package/dist/chunk-ZOZAPAUO.js.map +1 -0
  154. package/dist/chunk-ZQV5W7YC.js +809 -0
  155. package/dist/chunk-ZQV5W7YC.js.map +1 -0
  156. package/dist/chunk-ZUWF4NF7.js +13 -0
  157. package/dist/chunk-ZUWF4NF7.js.map +1 -0
  158. package/dist/client.d.ts +16 -7
  159. package/dist/clipboard.js +3 -1
  160. package/dist/commands/types.d.ts +2 -0
  161. package/dist/components/anthropic-mark.d.ts +3 -0
  162. package/dist/components/approval-surface.d.ts +19 -6
  163. package/dist/components/artifacts/artifact-chrome.d.ts +120 -0
  164. package/dist/components/artifacts/artifact-sandbox.d.ts +30 -0
  165. package/dist/components/artifacts/artifact-surface.d.ts +4 -2
  166. package/dist/components/artifacts/chat-interactive-block.d.ts +29 -0
  167. package/dist/components/artifacts/deferred-chat-media.d.ts +15 -0
  168. package/dist/components/artifacts/document-editor.d.ts +4 -2
  169. package/dist/components/artifacts/editable-artifact-ui.d.ts +2 -1
  170. package/dist/components/artifacts/editable-artifact-view.d.ts +48 -0
  171. package/dist/components/artifacts/editable-document.d.ts +1 -1
  172. package/dist/components/artifacts/editable-presentation.d.ts +2 -2
  173. package/dist/components/artifacts/editable-spreadsheet.d.ts +8 -3
  174. package/dist/components/artifacts/index.d.ts +9 -2
  175. package/dist/components/artifacts/inline-html-document.d.ts +2 -0
  176. package/dist/components/artifacts/latest-task-runner.d.ts +15 -0
  177. package/dist/components/artifacts/presentation-editor.d.ts +6 -2
  178. package/dist/components/artifacts/published-html-artifact-frame.d.ts +23 -3
  179. package/dist/components/artifacts/session-artifact-viewer.d.ts +39 -0
  180. package/dist/components/artifacts/site-view.d.ts +26 -0
  181. package/dist/components/artifacts/spreadsheet-command-queue.d.ts +20 -0
  182. package/dist/components/artifacts/spreadsheet-download.d.ts +7 -0
  183. package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -0
  184. package/dist/components/artifacts/spreadsheet-grid.d.ts +10 -3
  185. package/dist/components/artifacts/spreadsheet-resize.d.ts +50 -0
  186. package/dist/components/artifacts/spreadsheet-submissions.d.ts +9 -0
  187. package/dist/components/artifacts/visualization-assets.gen.d.ts +2 -0
  188. package/dist/components/browser-select-control.d.ts +16 -0
  189. package/dist/components/browser-viewer.d.ts +11 -2
  190. package/dist/components/chat-composer.d.ts +21 -3
  191. package/dist/components/child-session-link.d.ts +9 -0
  192. package/dist/components/claude-mark.d.ts +3 -0
  193. package/dist/components/code-editor.d.ts +1 -4
  194. package/dist/components/composer-transcription-control.d.ts +7 -0
  195. package/dist/components/composer.d.ts +34 -67
  196. package/dist/components/computer-viewer.d.ts +1 -1
  197. package/dist/components/copy-button.d.ts +3 -1
  198. package/dist/components/desktop-viewer.d.ts +1 -1
  199. package/dist/components/grok-mark.d.ts +26 -0
  200. package/dist/components/human-input-form.d.ts +28 -32
  201. package/dist/components/human-input-surface.d.ts +7 -2
  202. package/dist/components/linkified-text.d.ts +18 -0
  203. package/dist/components/machine-input-display.d.ts +22 -1
  204. package/dist/components/machines/agent-status-icon.d.ts +6 -0
  205. package/dist/components/machines/machine-detail.d.ts +7 -1
  206. package/dist/components/machines/machine-status.d.ts +48 -0
  207. package/dist/components/markdown-table-layout.d.ts +18 -0
  208. package/dist/components/markdown.d.ts +37 -4
  209. package/dist/components/message-timeline.d.ts +86 -7
  210. package/dist/components/model-mark.d.ts +29 -0
  211. package/dist/components/model-policy-picker-menu.d.ts +9 -0
  212. package/dist/components/model-policy-picker.d.ts +35 -28
  213. package/dist/components/new-conversation.d.ts +32 -0
  214. package/dist/components/open-geni-chat.d.ts +73 -0
  215. package/dist/components/open-geni-links.d.ts +68 -0
  216. package/dist/components/openrouter-mark.d.ts +6 -0
  217. package/dist/components/opper-mark.d.ts +7 -0
  218. package/dist/components/preview-loading.d.ts +2 -0
  219. package/dist/components/provider-recovery-notice.d.ts +16 -0
  220. package/dist/components/queue-item-content.d.ts +9 -0
  221. package/dist/components/rehype-secret-refs.d.ts +23 -0
  222. package/dist/components/remark-soft-line-breaks.d.ts +38 -0
  223. package/dist/components/sandbox-files.d.ts +3 -1
  224. package/dist/components/sandbox-workspace.d.ts +18 -0
  225. package/dist/components/secret-question-field.d.ts +31 -0
  226. package/dist/components/secret-ref-chip.d.ts +35 -0
  227. package/dist/components/session-capability-frame.d.ts +37 -0
  228. package/dist/components/session-chrome.d.ts +57 -8
  229. package/dist/components/session-commands-panel.d.ts +6 -0
  230. package/dist/components/session-commands.d.ts +6 -0
  231. package/dist/components/session-connection-request.d.ts +8 -0
  232. package/dist/components/session-conversation.d.ts +157 -0
  233. package/dist/components/session-list.d.ts +39 -0
  234. package/dist/components/session-mcp-capability-card.d.ts +41 -0
  235. package/dist/components/session-proxy-scope.d.ts +35 -0
  236. package/dist/components/session-status.d.ts +4 -1
  237. package/dist/components/session-usage-limit-notice.d.ts +39 -0
  238. package/dist/components/timeline-anchor.d.ts +24 -0
  239. package/dist/components/timeline-annotation-chrome.d.ts +25 -0
  240. package/dist/components/timeline-annotation-layout.d.ts +42 -0
  241. package/dist/components/timeline-annotation-markers.d.ts +8 -0
  242. package/dist/components/timeline-annotation-reveal-context.d.ts +4 -0
  243. package/dist/components/timeline-annotation-shared.d.ts +31 -0
  244. package/dist/components/timeline-annotations-dialog.d.ts +3 -2
  245. package/dist/components/timeline-annotations.d.ts +13 -3
  246. package/dist/components/timeline-reading-progress.d.ts +11 -0
  247. package/dist/components/timeline-search.d.ts +34 -0
  248. package/dist/components/timeline-settlement.d.ts +16 -0
  249. package/dist/components/tool-action-review.d.ts +47 -0
  250. package/dist/components/tool-review-history.d.ts +25 -0
  251. package/dist/components/usage-limit-notice.d.ts +49 -0
  252. package/dist/components/usage-member-list.d.ts +91 -0
  253. package/dist/components/usage-meter.d.ts +73 -0
  254. package/dist/components/use-viewer-menu-dismiss.d.ts +2 -0
  255. package/dist/components/user-message-body.d.ts +10 -1
  256. package/dist/composer-messages.d.ts +74 -0
  257. package/dist/composer-messages.js +9 -0
  258. package/dist/composer-messages.js.map +1 -0
  259. package/dist/composer-voice-events.d.ts +8 -0
  260. package/dist/composer.d.ts +5 -4
  261. package/dist/composer.js +26 -13
  262. package/dist/computer-viewer-GMLDXPON.js +20 -0
  263. package/dist/computer-viewer-GMLDXPON.js.map +1 -0
  264. package/dist/connect-account-identity.d.ts +8 -0
  265. package/dist/connect-accounts.d.ts +15 -0
  266. package/dist/connect-chooser.d.ts +18 -0
  267. package/dist/connect-panel.d.ts +18 -0
  268. package/dist/connect-setup.d.ts +18 -0
  269. package/dist/connect.d.ts +21 -0
  270. package/dist/connect.js +2149 -0
  271. package/dist/connect.js.map +1 -0
  272. package/dist/connection-catalog.d.ts +38 -0
  273. package/dist/connection-discovery.d.ts +12 -0
  274. package/dist/connection-installed.d.ts +17 -0
  275. package/dist/connection-logo.d.ts +9 -0
  276. package/dist/connection-service-logo.d.ts +8 -0
  277. package/dist/connection-service-presentation.d.ts +13 -0
  278. package/dist/connection-skeleton.d.ts +5 -0
  279. package/dist/connection-type-picker.d.ts +6 -0
  280. package/dist/conversation-timeline.d.ts +5 -0
  281. package/dist/desktop.d.ts +7 -0
  282. package/dist/desktop.js +27 -0
  283. package/dist/desktop.js.map +1 -0
  284. package/dist/device-authorization.d.ts +23 -0
  285. package/dist/diffs.d.ts +8 -0
  286. package/dist/diffs.js +15 -0
  287. package/dist/diffs.js.map +1 -0
  288. package/dist/discovery-cache.d.ts +5 -0
  289. package/dist/editor.d.ts +7 -0
  290. package/dist/editor.js +22 -0
  291. package/dist/editor.js.map +1 -0
  292. package/dist/embedded-new-voice-OCRNT6U5.js +18 -0
  293. package/dist/embedded-new-voice-OCRNT6U5.js.map +1 -0
  294. package/dist/embedded-voice-RPHPYVBT.js +18 -0
  295. package/dist/embedded-voice-RPHPYVBT.js.map +1 -0
  296. package/dist/{fleet-decision-row-GGCAT4E4.js → fleet-decision-row-SI63O5V4.js} +4 -1
  297. package/dist/fleet-decision-row-SI63O5V4.js.map +1 -0
  298. package/dist/hooks/latest-question.d.ts +25 -0
  299. package/dist/hooks/shared-machines-list.d.ts +4 -0
  300. package/dist/hooks/shared-query.d.ts +113 -0
  301. package/dist/hooks/use-attached-browsers.d.ts +1 -1
  302. package/dist/hooks/use-auth-runs.d.ts +1 -4
  303. package/dist/hooks/use-available-models.d.ts +1 -1
  304. package/dist/hooks/use-browser-frame-stream.d.ts +1 -1
  305. package/dist/hooks/use-browser-session.d.ts +12 -1
  306. package/dist/hooks/use-client-config-flags.d.ts +49 -0
  307. package/dist/hooks/use-codex-accounts.d.ts +59 -10
  308. package/dist/hooks/use-composer.d.ts +24 -2
  309. package/dist/hooks/use-computer-frame-stream.d.ts +1 -1
  310. package/dist/hooks/use-computer-input-posture.d.ts +13 -0
  311. package/dist/hooks/use-computer-session.d.ts +4 -0
  312. package/dist/hooks/use-connect.d.ts +23 -0
  313. package/dist/hooks/use-desktop-stream.d.ts +1 -1
  314. package/dist/hooks/use-file-attachments.d.ts +12 -0
  315. package/dist/hooks/use-goal.d.ts +2 -1
  316. package/dist/hooks/use-last-started-turn-policy.d.ts +5 -1
  317. package/dist/hooks/use-new-conversation.d.ts +56 -0
  318. package/dist/hooks/use-new-session-draft.d.ts +74 -0
  319. package/dist/hooks/use-realtime-voice-models.d.ts +7 -0
  320. package/dist/hooks/use-sandbox-files.d.ts +4 -1
  321. package/dist/hooks/use-session-conversation.d.ts +49 -0
  322. package/dist/hooks/use-session-events.d.ts +26 -5
  323. package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
  324. package/dist/hooks/use-session-model-account.d.ts +48 -0
  325. package/dist/hooks/use-supergrok-accounts.d.ts +7 -1
  326. package/dist/hooks/use-terminal-stream.d.ts +11 -5
  327. package/dist/hooks/use-usage.d.ts +40 -0
  328. package/dist/hooks/use-vaults.d.ts +53 -0
  329. package/dist/hooks/use-voice-input.d.ts +8 -1
  330. package/dist/hooks/use-workspace-edit.d.ts +4 -0
  331. package/dist/hooks/use-workspace-sessions.d.ts +24 -5
  332. package/dist/human-input-model.d.ts +122 -0
  333. package/dist/identity-link-accounts.d.ts +11 -0
  334. package/dist/identity-link-consent.d.ts +18 -0
  335. package/dist/index.d.ts +82 -20
  336. package/dist/index.js +2340 -6603
  337. package/dist/index.js.map +1 -1
  338. package/dist/interaction.d.ts +1 -1
  339. package/dist/interaction.js +14 -24
  340. package/dist/interaction.js.map +1 -1
  341. package/dist/latest-question-R77KWSUP.js +118 -0
  342. package/dist/latest-question-R77KWSUP.js.map +1 -0
  343. package/dist/lib/clipboard.d.ts +7 -0
  344. package/dist/lib/error-message.d.ts +7 -0
  345. package/dist/lib/format.d.ts +58 -4
  346. package/dist/lib/host-theme.d.ts +77 -0
  347. package/dist/lib/interaction-errors.d.ts +3 -0
  348. package/dist/lib/menu-styles.d.ts +22 -0
  349. package/dist/lib/notify-observer.d.ts +2 -0
  350. package/dist/lib/pierre-diffs-loader.d.ts +20 -0
  351. package/dist/lib/provider-recovery.d.ts +55 -0
  352. package/dist/lib/scrollbars.d.ts +11 -0
  353. package/dist/lib/terminal-input-readiness.d.ts +11 -0
  354. package/dist/lib/transient-retry.d.ts +25 -0
  355. package/dist/lib/workbench-peers.d.ts +36 -0
  356. package/dist/lib/xterm-theme.d.ts +7 -5
  357. package/dist/machines.js +9 -5
  358. package/dist/markdown-table-layout-3PV2UAAH.js +113 -0
  359. package/dist/markdown-table-layout-3PV2UAAH.js.map +1 -0
  360. package/dist/mcp-connection-status.d.ts +14 -0
  361. package/dist/model-mark-paths.d.ts +34 -0
  362. package/dist/model-mark-paths.js +16 -0
  363. package/dist/model-mark-paths.js.map +1 -0
  364. package/dist/model-picker-model.d.ts +111 -0
  365. package/dist/model-picker-model.js +25 -0
  366. package/dist/model-picker-model.js.map +1 -0
  367. package/dist/model-policy-picker-menu-7GJ5URPR.js +17 -0
  368. package/dist/model-policy-picker-menu-7GJ5URPR.js.map +1 -0
  369. package/dist/model-policy.d.ts +30 -3
  370. package/dist/model-policy.js +14 -1
  371. package/dist/native-previews.d.ts +28 -0
  372. package/dist/native-previews.js +82 -0
  373. package/dist/native-previews.js.map +1 -0
  374. package/dist/new-session-draft.d.ts +7 -0
  375. package/dist/new-session-draft.js +639 -0
  376. package/dist/new-session-draft.js.map +1 -0
  377. package/dist/notification-text.d.ts +27 -0
  378. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js → noto-sans-arabic-loader-XSH2KYWJ.js} +3 -1
  379. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js.map → noto-sans-arabic-loader-XSH2KYWJ.js.map} +1 -1
  380. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js → noto-sans-jp-loader.generated-MR5L6V7K.js} +3 -1
  381. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js.map → noto-sans-jp-loader.generated-MR5L6V7K.js.map} +1 -1
  382. package/dist/older-history.d.ts +3 -2
  383. package/dist/organization-model.d.ts +32 -0
  384. package/dist/organization-model.js +65 -0
  385. package/dist/organization-model.js.map +1 -0
  386. package/dist/{platform-activity-row-BJHUHXAT.js → platform-activity-row-5OCKOQWZ.js} +16 -78
  387. package/dist/platform-activity-row-5OCKOQWZ.js.map +1 -0
  388. package/dist/plugin-details.d.ts +14 -0
  389. package/dist/plugin-discovery.d.ts +25 -0
  390. package/dist/preview-loading-paint.d.ts +20 -0
  391. package/dist/provider.d.ts +25 -2
  392. package/dist/queue-presentation-model.d.ts +72 -0
  393. package/dist/queue-presentation-model.js +38 -0
  394. package/dist/queue-presentation-model.js.map +1 -0
  395. package/dist/{queue-surface-implementation-BSKUWHRI.js → queue-surface-implementation-GJBH7NKO.js} +65 -265
  396. package/dist/queue-surface-implementation-GJBH7NKO.js.map +1 -0
  397. package/dist/realtime/embedded-new-voice.d.ts +1 -0
  398. package/dist/realtime/embedded-voice.d.ts +1 -0
  399. package/dist/realtime/realtime-control.d.ts +4 -65
  400. package/dist/realtime/session-realtime.d.ts +78 -0
  401. package/dist/realtime.js +25 -1333
  402. package/dist/realtime.js.map +1 -1
  403. package/dist/sandbox-files-O27IPOZW.js +21 -0
  404. package/dist/sandbox-files-O27IPOZW.js.map +1 -0
  405. package/dist/sandbox-label-model.d.ts +30 -0
  406. package/dist/sandbox-label-model.js +20 -0
  407. package/dist/sandbox-label-model.js.map +1 -0
  408. package/dist/scrollbars.d.ts +1 -0
  409. package/dist/scrollbars.js +10 -0
  410. package/dist/scrollbars.js.map +1 -0
  411. package/dist/secret-reference-label.d.ts +14 -0
  412. package/dist/secrets-ui.d.ts +57 -0
  413. package/dist/session-agents-model.d.ts +10 -0
  414. package/dist/session-agents-model.js +25 -0
  415. package/dist/session-agents-model.js.map +1 -0
  416. package/dist/session-attention-model.d.ts +8 -0
  417. package/dist/session-attention-model.js +47 -0
  418. package/dist/session-attention-model.js.map +1 -0
  419. package/dist/session-auth-recommendation.d.ts +5 -0
  420. package/dist/session-capability-policy.d.ts +31 -0
  421. package/dist/session-feedback-model.d.ts +20 -0
  422. package/dist/session-feedback-model.js +56 -0
  423. package/dist/session-feedback-model.js.map +1 -0
  424. package/dist/session-list-model.d.ts +138 -0
  425. package/dist/session-list-model.js +274 -0
  426. package/dist/session-list-model.js.map +1 -0
  427. package/dist/session-realtime-7DJOIHQW.js +21 -0
  428. package/dist/session-realtime-7DJOIHQW.js.map +1 -0
  429. package/dist/session-realtime.d.ts +7 -0
  430. package/dist/session-realtime.js +19 -0
  431. package/dist/session-realtime.js.map +1 -0
  432. package/dist/session-status-model.d.ts +36 -0
  433. package/dist/session-ui.d.ts +39 -1
  434. package/dist/session-ui.js +126 -12
  435. package/dist/session-ui.js.map +1 -1
  436. package/dist/session.d.ts +8 -2
  437. package/dist/session.js +35 -20
  438. package/dist/site-snapshot.d.ts +14 -0
  439. package/dist/sites-ui.d.ts +24 -0
  440. package/dist/sites.d.ts +3 -0
  441. package/dist/sites.js +14 -0
  442. package/dist/sites.js.map +1 -0
  443. package/dist/skill-discovery.d.ts +47 -0
  444. package/dist/terminal.d.ts +8 -0
  445. package/dist/terminal.js +32 -0
  446. package/dist/terminal.js.map +1 -0
  447. package/dist/testing/session-scenarios.d.ts +10 -0
  448. package/dist/testing/session-scenarios.js +367 -0
  449. package/dist/testing/session-scenarios.js.map +1 -0
  450. package/dist/timeline/activity-rail.d.ts +11 -2
  451. package/dist/timeline/agent-identity.d.ts +30 -0
  452. package/dist/timeline/agent-row.d.ts +31 -0
  453. package/dist/timeline/allowance-exhausted-row.d.ts +24 -0
  454. package/dist/timeline/compaction-copy.d.ts +46 -0
  455. package/dist/timeline/focus-sequence.d.ts +9 -0
  456. package/dist/timeline/genie-copy.d.ts +3 -0
  457. package/dist/timeline/genie-loading.d.ts +47 -0
  458. package/dist/timeline/index.d.ts +10 -5
  459. package/dist/timeline/knowledge-receipt.d.ts +17 -0
  460. package/dist/timeline/message-sender.d.ts +19 -0
  461. package/dist/timeline/notice-presentation.d.ts +13 -0
  462. package/dist/timeline/parsers.d.ts +11 -0
  463. package/dist/timeline/patch-apply-command.d.ts +18 -0
  464. package/dist/timeline/platform-activity-presentation.d.ts +21 -0
  465. package/dist/timeline/platform-activity-row.d.ts +1 -2
  466. package/dist/timeline/presented-image.d.ts +11 -0
  467. package/dist/timeline/projection.d.ts +31 -2
  468. package/dist/timeline/provider-logo.d.ts +15 -0
  469. package/dist/timeline/question-nav-model.d.ts +23 -0
  470. package/dist/timeline/registry.d.ts +12 -1
  471. package/dist/timeline/retained-image.d.ts +18 -0
  472. package/dist/timeline/retained-loaders.d.ts +27 -0
  473. package/dist/timeline/rolling-activity.d.ts +11 -0
  474. package/dist/timeline/screenshot-lightbox.d.ts +23 -1
  475. package/dist/timeline/session-usage-limit-row.d.ts +11 -0
  476. package/dist/timeline/shared.d.ts +4 -1
  477. package/dist/timeline/startup-dispatch-details.d.ts +5 -0
  478. package/dist/timeline/startup-preference.d.ts +3 -0
  479. package/dist/timeline/startup-timings.d.ts +4 -0
  480. package/dist/timeline/tool-display-name.d.ts +5 -2
  481. package/dist/timeline/tool-presentation.d.ts +172 -0
  482. package/dist/timeline/turn-summary-model.d.ts +95 -0
  483. package/dist/timeline/turn-summary.d.ts +20 -54
  484. package/dist/timeline/types.d.ts +179 -7
  485. package/dist/timeline/work-presentation.d.ts +60 -0
  486. package/dist/timeline-annotation-markers-U3G66KUD.js +166 -0
  487. package/dist/timeline-annotation-markers-U3G66KUD.js.map +1 -0
  488. package/dist/timeline-annotation-selection-BI7QTPTZ.js +242 -0
  489. package/dist/timeline-annotation-selection-BI7QTPTZ.js.map +1 -0
  490. package/dist/timeline-annotations-dialog-FOMHSDTE.js +274 -0
  491. package/dist/timeline-annotations-dialog-FOMHSDTE.js.map +1 -0
  492. package/dist/timeline-model.d.ts +29 -0
  493. package/dist/timeline-model.js +254 -0
  494. package/dist/timeline-model.js.map +1 -0
  495. package/dist/usage/allowance-copy.d.ts +25 -0
  496. package/dist/usage/session-usage-limit.d.ts +88 -0
  497. package/dist/usage/summary.d.ts +35 -0
  498. package/dist/usage.d.ts +17 -0
  499. package/dist/usage.js +1187 -0
  500. package/dist/usage.js.map +1 -0
  501. package/dist/voice-recording-owner.d.ts +7 -0
  502. package/package.json +106 -7
  503. package/src/approvals.ts +8 -1
  504. package/src/artifacts-spreadsheet.ts +1 -0
  505. package/src/artifacts.ts +44 -0
  506. package/src/assets/README.md +8 -0
  507. package/src/assets/gmail.ico +0 -0
  508. package/src/assets/outlook.ico +0 -0
  509. package/src/capability-catalog-row.tsx +94 -0
  510. package/src/capability-logo-fallback.ts +22 -0
  511. package/src/client.ts +20 -31
  512. package/src/commands/registry.ts +18 -15
  513. package/src/commands/types.ts +2 -0
  514. package/src/components/anthropic-mark.tsx +10 -0
  515. package/src/components/approval-surface.tsx +401 -138
  516. package/src/components/artifacts/artifact-chrome.tsx +413 -0
  517. package/src/components/artifacts/artifact-sandbox.tsx +193 -0
  518. package/src/components/artifacts/artifact-surface.tsx +40 -20
  519. package/src/components/artifacts/chat-interactive-block.tsx +260 -0
  520. package/src/components/artifacts/deferred-chat-media.tsx +67 -0
  521. package/src/components/artifacts/document-editor.tsx +141 -16
  522. package/src/components/artifacts/editable-artifact-ui.tsx +4 -2
  523. package/src/components/artifacts/editable-artifact-view.tsx +197 -0
  524. package/src/components/artifacts/editable-artifact-workbench.tsx +5 -2
  525. package/src/components/artifacts/editable-document.tsx +37 -36
  526. package/src/components/artifacts/editable-presentation.tsx +36 -18
  527. package/src/components/artifacts/editable-spreadsheet.tsx +543 -103
  528. package/src/components/artifacts/index.ts +45 -0
  529. package/src/components/artifacts/inline-html-document.ts +24 -0
  530. package/src/components/artifacts/latest-task-runner.ts +49 -0
  531. package/src/components/artifacts/presentation-editor.tsx +323 -67
  532. package/src/components/artifacts/published-html-artifact-frame.tsx +122 -5
  533. package/src/components/artifacts/session-artifact-viewer.tsx +378 -0
  534. package/src/components/artifacts/site-view.tsx +121 -0
  535. package/src/components/artifacts/spreadsheet-canvas.ts +5 -10
  536. package/src/components/artifacts/spreadsheet-command-queue.ts +184 -0
  537. package/src/components/artifacts/spreadsheet-download.tsx +109 -0
  538. package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
  539. package/src/components/artifacts/spreadsheet-grid.tsx +344 -144
  540. package/src/components/artifacts/spreadsheet-resize.tsx +421 -0
  541. package/src/components/artifacts/spreadsheet-submissions.tsx +46 -0
  542. package/src/components/artifacts/visualization-assets/base.css +1015 -0
  543. package/src/components/artifacts/visualization-assets/helpers.html +349 -0
  544. package/src/components/artifacts/visualization-assets.gen.ts +3 -0
  545. package/src/components/browser-select-control.tsx +312 -0
  546. package/src/components/browser-viewer.tsx +778 -318
  547. package/src/components/chat-composer.tsx +47 -5
  548. package/src/components/child-session-link.tsx +36 -0
  549. package/src/components/claude-mark.tsx +11 -0
  550. package/src/components/code-editor.tsx +13 -16
  551. package/src/components/composer-transcription-control.tsx +128 -30
  552. package/src/components/composer.tsx +404 -195
  553. package/src/components/computer-viewer.tsx +880 -253
  554. package/src/components/copy-button.tsx +13 -5
  555. package/src/components/desktop-viewer.tsx +1 -1
  556. package/src/components/enrollment-consent.tsx +1 -1
  557. package/src/components/enrollment-device-flow.tsx +1 -1
  558. package/src/components/file-browser.tsx +10 -14
  559. package/src/components/fleet-tile.tsx +5 -3
  560. package/src/components/generated-video-player.tsx +4 -2
  561. package/src/components/grok-mark.tsx +34 -0
  562. package/src/components/human-input-form.tsx +590 -283
  563. package/src/components/human-input-surface.tsx +11 -0
  564. package/src/components/interaction-intervention-banner.tsx +2 -2
  565. package/src/components/linkified-text.tsx +79 -0
  566. package/src/components/machine-card.tsx +35 -78
  567. package/src/components/machine-dock-bar.tsx +1 -1
  568. package/src/components/machine-input-display.ts +197 -3
  569. package/src/components/machines/agent-status-icon.tsx +31 -0
  570. package/src/components/machines/machine-detail.tsx +200 -31
  571. package/src/components/machines/machine-operation-policy.tsx +1 -1
  572. package/src/components/machines/machine-status.ts +241 -0
  573. package/src/components/machines-dashboard.tsx +2 -17
  574. package/src/components/markdown-table-layout.ts +143 -0
  575. package/src/components/markdown.tsx +466 -129
  576. package/src/components/message-timeline.tsx +2370 -667
  577. package/src/components/model-mark.tsx +102 -0
  578. package/src/components/model-picker.tsx +15 -4
  579. package/src/components/model-policy-picker-menu.tsx +371 -0
  580. package/src/components/model-policy-picker.tsx +252 -429
  581. package/src/components/new-conversation.tsx +221 -0
  582. package/src/components/open-geni-chat.tsx +309 -0
  583. package/src/components/open-geni-links.tsx +179 -0
  584. package/src/components/openrouter-mark.tsx +13 -0
  585. package/src/components/opper-mark.tsx +18 -0
  586. package/src/components/pierre-diff.tsx +41 -16
  587. package/src/components/pierre-file.tsx +35 -10
  588. package/src/components/preview-loading.tsx +109 -0
  589. package/src/components/provider-recovery-notice.tsx +49 -0
  590. package/src/components/queue-item-content.ts +14 -0
  591. package/src/components/queue-surface-implementation.tsx +51 -356
  592. package/src/components/queue-surface-state.tsx +3 -1
  593. package/src/components/rehype-secret-refs.ts +98 -0
  594. package/src/components/remark-soft-line-breaks.ts +166 -0
  595. package/src/components/sandbox-files.tsx +91 -15
  596. package/src/components/sandbox-terminal.tsx +84 -13
  597. package/src/components/sandbox-workspace.tsx +147 -51
  598. package/src/components/secret-question-field.tsx +324 -0
  599. package/src/components/secret-ref-chip.tsx +185 -0
  600. package/src/components/session-capability-frame.tsx +197 -0
  601. package/src/components/session-chrome.tsx +842 -251
  602. package/src/components/session-commands-panel.tsx +126 -0
  603. package/src/components/session-commands.tsx +18 -0
  604. package/src/components/session-connection-request.tsx +97 -0
  605. package/src/components/session-conversation.tsx +876 -0
  606. package/src/components/session-list.tsx +257 -0
  607. package/src/components/session-mcp-capability-card.tsx +680 -0
  608. package/src/components/session-proxy-scope.tsx +153 -0
  609. package/src/components/session-status.tsx +26 -17
  610. package/src/components/session-usage-limit-notice.tsx +174 -0
  611. package/src/components/timeline-anchor.tsx +119 -0
  612. package/src/components/timeline-annotation-chrome.tsx +196 -0
  613. package/src/components/timeline-annotation-layout.ts +268 -0
  614. package/src/components/timeline-annotation-markers.tsx +185 -0
  615. package/src/components/timeline-annotation-reveal-context.ts +9 -0
  616. package/src/components/timeline-annotation-selection.tsx +106 -54
  617. package/src/components/timeline-annotation-shared.ts +245 -0
  618. package/src/components/timeline-annotations-dialog.tsx +185 -128
  619. package/src/components/timeline-annotations.tsx +189 -45
  620. package/src/components/timeline-reading-progress.ts +80 -0
  621. package/src/components/timeline-search.tsx +168 -0
  622. package/src/components/timeline-settlement.ts +60 -0
  623. package/src/components/tool-action-review.tsx +650 -0
  624. package/src/components/tool-review-history.tsx +88 -0
  625. package/src/components/usage-limit-notice.tsx +179 -0
  626. package/src/components/usage-member-list.tsx +792 -0
  627. package/src/components/usage-meter.tsx +382 -0
  628. package/src/components/use-viewer-menu-dismiss.ts +33 -0
  629. package/src/components/user-message-body.tsx +27 -2
  630. package/src/components/workspace-dock.tsx +18 -1
  631. package/src/composer-messages.ts +150 -0
  632. package/src/composer-voice-events.ts +12 -0
  633. package/src/composer.ts +6 -1
  634. package/src/connect-account-identity.tsx +34 -0
  635. package/src/connect-accounts.tsx +233 -0
  636. package/src/connect-chooser.tsx +336 -0
  637. package/src/connect-panel.tsx +99 -0
  638. package/src/connect-setup.tsx +355 -0
  639. package/src/connect.ts +60 -0
  640. package/src/connection-assets.d.ts +4 -0
  641. package/src/connection-catalog.tsx +162 -0
  642. package/src/connection-discovery.tsx +238 -0
  643. package/src/connection-installed.tsx +51 -0
  644. package/src/connection-logo.tsx +37 -0
  645. package/src/connection-service-logo.tsx +55 -0
  646. package/src/connection-service-presentation.ts +46 -0
  647. package/src/connection-skeleton.tsx +16 -0
  648. package/src/connection-type-picker.tsx +36 -0
  649. package/src/conversation-timeline.ts +92 -0
  650. package/src/desktop.ts +24 -0
  651. package/src/device-authorization.tsx +98 -0
  652. package/src/diffs.ts +16 -0
  653. package/src/discovery-cache.ts +42 -0
  654. package/src/editor.ts +13 -0
  655. package/src/hooks/internal.ts +3 -2
  656. package/src/hooks/latest-question.ts +164 -0
  657. package/src/hooks/shared-machines-list.ts +34 -1
  658. package/src/hooks/shared-query.ts +721 -0
  659. package/src/hooks/use-attached-browsers.ts +1 -1
  660. package/src/hooks/use-auth-runs.ts +0 -27
  661. package/src/hooks/use-available-models.ts +2 -2
  662. package/src/hooks/use-browser-frame-stream.ts +19 -2
  663. package/src/hooks/use-browser-session.ts +680 -205
  664. package/src/hooks/use-browser-sessions.ts +32 -1
  665. package/src/hooks/use-client-config-flags.ts +118 -0
  666. package/src/hooks/use-codex-accounts.ts +247 -47
  667. package/src/hooks/use-composer.ts +227 -44
  668. package/src/hooks/use-computer-frame-stream.ts +25 -7
  669. package/src/hooks/use-computer-input-posture.ts +94 -0
  670. package/src/hooks/use-computer-session.ts +409 -95
  671. package/src/hooks/use-connect.ts +23 -0
  672. package/src/hooks/use-desktop-stream.ts +19 -28
  673. package/src/hooks/use-file-attachments.ts +80 -4
  674. package/src/hooks/use-goal.ts +79 -9
  675. package/src/hooks/use-last-started-turn-policy.ts +37 -11
  676. package/src/hooks/use-machines.ts +4 -0
  677. package/src/hooks/use-new-conversation.ts +374 -0
  678. package/src/hooks/use-new-session-draft.ts +813 -0
  679. package/src/hooks/use-realtime-voice-models.ts +46 -0
  680. package/src/hooks/use-sandbox-files.ts +68 -20
  681. package/src/hooks/use-session-background-commands.ts +22 -11
  682. package/src/hooks/use-session-capabilities.ts +3 -2
  683. package/src/hooks/use-session-conversation.ts +150 -0
  684. package/src/hooks/use-session-events.ts +531 -335
  685. package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
  686. package/src/hooks/use-session-model-account.ts +174 -0
  687. package/src/hooks/use-session.ts +30 -2
  688. package/src/hooks/use-slash-commands.ts +6 -11
  689. package/src/hooks/use-supergrok-accounts.ts +22 -8
  690. package/src/hooks/use-terminal-stream.ts +153 -27
  691. package/src/hooks/use-transcription.ts +6 -0
  692. package/src/hooks/use-turn-queue.ts +2 -1
  693. package/src/hooks/use-usage.ts +100 -0
  694. package/src/hooks/use-vaults.ts +153 -0
  695. package/src/hooks/use-video-artifact-playback.ts +1 -1
  696. package/src/hooks/use-voice-input.ts +127 -19
  697. package/src/hooks/use-workspace-capture.ts +3 -2
  698. package/src/hooks/use-workspace-edit.ts +23 -5
  699. package/src/hooks/use-workspace-sessions.ts +74 -7
  700. package/src/human-input-model.ts +359 -0
  701. package/src/identity-link-accounts.tsx +148 -0
  702. package/src/identity-link-consent.tsx +192 -0
  703. package/src/index.ts +262 -16
  704. package/src/interaction.ts +1 -0
  705. package/src/lib/clipboard.ts +29 -0
  706. package/src/lib/error-message.ts +37 -0
  707. package/src/lib/format.ts +205 -10
  708. package/src/lib/host-theme.ts +529 -0
  709. package/src/lib/interaction-errors.ts +15 -0
  710. package/src/lib/menu-styles.ts +44 -0
  711. package/src/lib/notify-observer.ts +15 -0
  712. package/src/lib/pierre-diffs-loader.ts +53 -0
  713. package/src/lib/provider-recovery.ts +243 -0
  714. package/src/lib/scrollbars.ts +30 -0
  715. package/src/lib/terminal-input-readiness.ts +51 -0
  716. package/src/lib/transient-retry.ts +99 -0
  717. package/src/lib/workbench-peers.ts +62 -0
  718. package/src/lib/xterm-theme.ts +23 -21
  719. package/src/mcp-connection-status.ts +35 -0
  720. package/src/model-mark-paths.ts +53 -0
  721. package/src/model-picker-model.ts +330 -0
  722. package/src/model-policy.ts +299 -74
  723. package/src/native-previews.ts +100 -0
  724. package/src/new-session-draft.ts +22 -0
  725. package/src/notification-text.ts +81 -0
  726. package/src/older-history.ts +22 -4
  727. package/src/organization-model.ts +118 -0
  728. package/src/plugin-details.tsx +185 -0
  729. package/src/plugin-discovery.tsx +196 -0
  730. package/src/preview-loading-paint.ts +68 -0
  731. package/src/provider.tsx +74 -15
  732. package/src/queue-presentation-model.ts +464 -0
  733. package/src/realtime/dropdown-menu.tsx +19 -12
  734. package/src/realtime/embedded-new-voice.tsx +1 -0
  735. package/src/realtime/embedded-voice.tsx +3 -0
  736. package/src/realtime/realtime-control.tsx +138 -550
  737. package/src/realtime/session-realtime.ts +566 -0
  738. package/src/sandbox-label-model.ts +53 -0
  739. package/src/scrollbars.ts +3 -0
  740. package/src/secret-reference-label.ts +43 -0
  741. package/src/secrets-ui.tsx +59 -0
  742. package/src/session-agents-model.ts +33 -0
  743. package/src/session-attention-model.ts +62 -0
  744. package/src/session-auth-recommendation.ts +48 -0
  745. package/src/session-capability-policy.ts +196 -0
  746. package/src/session-context.ts +3 -4
  747. package/src/session-feedback-model.ts +95 -0
  748. package/src/session-list-model.ts +434 -0
  749. package/src/session-realtime.ts +18 -0
  750. package/src/session-status-model.ts +99 -0
  751. package/src/session-ui.ts +98 -1
  752. package/src/session.ts +17 -1
  753. package/src/site-snapshot.ts +74 -0
  754. package/src/sites-ui.tsx +348 -0
  755. package/src/sites.ts +3 -0
  756. package/src/skill-discovery.tsx +208 -0
  757. package/src/terminal.ts +26 -0
  758. package/src/testing/session-scenarios.ts +404 -0
  759. package/src/timeline/activity-rail.tsx +242 -101
  760. package/src/timeline/agent-identity.tsx +84 -0
  761. package/src/timeline/agent-row.tsx +231 -0
  762. package/src/timeline/allowance-exhausted-row.tsx +86 -0
  763. package/src/timeline/compaction-copy.ts +115 -0
  764. package/src/timeline/fleet-decision-projection.ts +2 -1
  765. package/src/timeline/fleet-decision-row.tsx +1 -0
  766. package/src/timeline/focus-sequence.ts +44 -0
  767. package/src/timeline/genie-copy.ts +19 -0
  768. package/src/timeline/genie-loading.tsx +143 -0
  769. package/src/timeline/index.ts +30 -2
  770. package/src/timeline/knowledge-receipt.tsx +100 -0
  771. package/src/timeline/message-sender.tsx +71 -0
  772. package/src/timeline/notice-presentation.ts +48 -0
  773. package/src/timeline/parsers.ts +42 -2
  774. package/src/timeline/patch-apply-command.tsx +135 -0
  775. package/src/timeline/platform-activity-presentation.ts +166 -0
  776. package/src/timeline/platform-activity-row.tsx +15 -90
  777. package/src/timeline/presented-image.ts +63 -0
  778. package/src/timeline/projection.ts +1742 -76
  779. package/src/timeline/provider-logo.tsx +53 -0
  780. package/src/timeline/question-nav-model.ts +28 -0
  781. package/src/timeline/registry.ts +26 -4
  782. package/src/timeline/retained-image.ts +97 -0
  783. package/src/timeline/retained-loaders.ts +91 -0
  784. package/src/timeline/rolling-activity.tsx +80 -0
  785. package/src/timeline/screenshot-lightbox.tsx +228 -5
  786. package/src/timeline/session-usage-limit-row.tsx +42 -0
  787. package/src/timeline/shared.tsx +42 -15
  788. package/src/timeline/startup-dispatch-details.tsx +36 -0
  789. package/src/timeline/startup-preference.ts +33 -0
  790. package/src/timeline/startup-timings.tsx +145 -0
  791. package/src/timeline/tool-display-name.ts +19 -7
  792. package/src/timeline/tool-presentation.ts +1316 -0
  793. package/src/timeline/tool-renderers.tsx +835 -1138
  794. package/src/timeline/turn-summary-model.ts +343 -0
  795. package/src/timeline/turn-summary.tsx +272 -365
  796. package/src/timeline/types.ts +189 -6
  797. package/src/timeline/work-presentation.ts +189 -0
  798. package/src/timeline-model.ts +143 -0
  799. package/src/usage/allowance-copy.ts +64 -0
  800. package/src/usage/session-usage-limit.ts +266 -0
  801. package/src/usage/summary.ts +93 -0
  802. package/src/usage.ts +70 -0
  803. package/src/voice-recording-owner.ts +22 -0
  804. package/styles/compiled.css +2319 -748
  805. package/styles/connect.css +290 -0
  806. package/styles/connect.d.ts +1 -0
  807. package/styles/effective-tokens.css +1 -0
  808. package/styles/index.css +309 -9
  809. package/styles/responsive.css +99 -7
  810. package/styles/session-capability.css +44 -0
  811. package/styles/tokens.css +134 -66
  812. package/dist/browser-viewer-MKKFRRRV.js +0 -14
  813. package/dist/chunk-2DZVYHVO.js +0 -470
  814. package/dist/chunk-2DZVYHVO.js.map +0 -1
  815. package/dist/chunk-3AGBGTRT.js.map +0 -1
  816. package/dist/chunk-4MDLEHXL.js.map +0 -1
  817. package/dist/chunk-5YK2ZDIG.js.map +0 -1
  818. package/dist/chunk-7EAFGICR.js +0 -302
  819. package/dist/chunk-7EAFGICR.js.map +0 -1
  820. package/dist/chunk-ELIYWBZR.js.map +0 -1
  821. package/dist/chunk-H5YGYNXS.js.map +0 -1
  822. package/dist/chunk-HUIKIDM5.js.map +0 -1
  823. package/dist/chunk-ICA6R6TW.js.map +0 -1
  824. package/dist/chunk-IOLVN74D.js.map +0 -1
  825. package/dist/chunk-JAQ5K7W6.js.map +0 -1
  826. package/dist/chunk-JVYQRRC4.js.map +0 -1
  827. package/dist/chunk-KCGXPP5V.js.map +0 -1
  828. package/dist/chunk-KZ73RL76.js.map +0 -1
  829. package/dist/chunk-NEXLLTP4.js.map +0 -1
  830. package/dist/chunk-PEI6RANI.js.map +0 -1
  831. package/dist/chunk-VTOREEXF.js +0 -142
  832. package/dist/chunk-VTOREEXF.js.map +0 -1
  833. package/dist/chunk-WSK7G5LE.js +0 -170
  834. package/dist/chunk-WSK7G5LE.js.map +0 -1
  835. package/dist/chunk-X4A66EWZ.js +0 -223
  836. package/dist/chunk-X4A66EWZ.js.map +0 -1
  837. package/dist/chunk-XYRDDSXF.js.map +0 -1
  838. package/dist/chunk-YG4QEQF5.js +0 -9557
  839. package/dist/chunk-YG4QEQF5.js.map +0 -1
  840. package/dist/chunk-YVCHAQZY.js.map +0 -1
  841. package/dist/chunk-ZPBTI3TE.js.map +0 -1
  842. package/dist/computer-viewer-FWEZISW4.js +0 -16
  843. package/dist/fleet-decision-row-GGCAT4E4.js.map +0 -1
  844. package/dist/hooks/use-environments.d.ts +0 -41
  845. package/dist/hooks/use-packs.d.ts +0 -32
  846. package/dist/platform-activity-row-BJHUHXAT.js.map +0 -1
  847. package/dist/queue-surface-implementation-BSKUWHRI.js.map +0 -1
  848. package/dist/timeline-annotation-selection-3L7LHGG2.js +0 -187
  849. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +0 -1
  850. package/dist/timeline-annotations-dialog-U7EVLR75.js +0 -196
  851. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +0 -1
  852. package/src/hooks/use-environments.ts +0 -182
  853. package/src/hooks/use-packs.ts +0 -157
  854. /package/dist/{browser-viewer-MKKFRRRV.js.map → browser-viewer-ZWR6MWJ7.js.map} +0 -0
  855. /package/dist/{computer-viewer-FWEZISW4.js.map → chat-interactive-block-4EUKJXUO.js.map} +0 -0
@@ -1,8 +1,11 @@
1
- import {
2
- parseSandboxFileArtifactReceipt,
3
- type GitFileDiff,
4
- type RetainedArtifactReference,
5
- } from "@opengeni/sdk";
1
+ import { useHasToolReview, useRecordedToolReview } from "../components/tool-review-history";
2
+ import { ToolActionReviewCard } from "../components/tool-action-review";
3
+ import { SecretRefText } from "../components/secret-ref-chip";
4
+ import type { ToolReviewStatus } from "@opengeni/sdk";
5
+ import { KnowledgeReceiptRow } from "./knowledge-receipt";
6
+ import { defaultUrlTransform } from "react-markdown";
7
+ import { isRetainedImageContentType, useRetainedImageObjectUrl } from "./retained-image";
8
+ import { parseSandboxFileArtifactReceipt, type RetainedArtifactReference } from "@opengeni/sdk";
6
9
  import {
7
10
  BoxIcon,
8
11
  BrainCircuitIcon,
@@ -33,28 +36,20 @@ import {
33
36
  TerminalIcon,
34
37
  VideoIcon,
35
38
  WrenchIcon,
39
+ type LucideIcon,
36
40
  } from "lucide-react";
37
- import { useEffect, useRef, useState, type ReactNode } from "react";
38
- import { formatBytes, stringifyPayload, tryParseJson } from "../lib/format";
41
+ import { useContext, useState, type ReactNode } from "react";
42
+ import { formatBytes, tryParseJson } from "../lib/format";
39
43
  import { useTimelineComputeLabel } from "./compute-label";
44
+ import { ConnectorLogoIcon, useToolConnectorLogo } from "./provider-logo";
40
45
  import {
41
- applyPatchOpsFromToolItem,
42
- controlCaret,
43
- execTruncated,
44
46
  generatedImageReceipt,
45
- isExecSessionLostBanner,
46
- looksBinary,
47
47
  mediaPreviewFact,
48
- parseExecBannerSessionId,
49
48
  parseToolArgs,
50
49
  retainedScreenshotMetadata,
51
- sandboxCommandExitCode,
52
- stripExecBanner,
53
- tailPeek,
50
+ retainedToolResultImages,
54
51
  unwrapMcpOutput,
55
- v4aToGitFileDiff,
56
52
  screenshotDataUrl,
57
- type ApplyPatchOperation,
58
53
  } from "./parsers";
59
54
  import {
60
55
  createToolRegistry,
@@ -71,18 +66,41 @@ import {
71
66
  TermBlock,
72
67
  Thumbnail,
73
68
  ActivityDisclosure,
69
+ CompactActivityContext,
74
70
  type DisclosureChip,
75
71
  } from "./shared";
76
72
  import { RawPatch, ToolDiff } from "./tool-diff";
73
+ import {
74
+ applyPatchPresentation,
75
+ askPresentation,
76
+ execPresentation,
77
+ genericToolIconKind,
78
+ genericToolPresentation,
79
+ parseDisclosedTools,
80
+ parseSearchHits,
81
+ pathBasename,
82
+ pathDirname,
83
+ runOnPresentation,
84
+ toolSearchPreview,
85
+ toolSearchQuery,
86
+ truncatePreview,
87
+ webSearchPresentation,
88
+ writeStdinPresentation,
89
+ type ToolBody,
90
+ type ToolIconKind,
91
+ type ToolPreview,
92
+ type ToolRowPresentation,
93
+ type WebSearchResult,
94
+ } from "./tool-presentation";
95
+ import { isPatchFilename, PatchApplyCommand } from "./patch-apply-command";
77
96
  import { mcpToolLeaf, toolDisplayName } from "./tool-display-name";
97
+ import { useOpenGeniLinkResolver } from "../components/open-geni-links";
78
98
 
79
99
  /* ----------------------------------------------------------------------------
80
100
  Per-tool renderers
81
-
82
101
  Each renderer takes one projected `ToolCallItem` and returns an `ActivityDisclosure`
83
102
  tuned for that tool's real wire shape. The defaults below populate the
84
103
  registry; the mapping is registered at the bottom of the file.
85
-
86
104
  Restraint is the rule: compact title + one quiet preview, secondary detail
87
105
  only on expand. No loud right-side badges — at most a single settle chip.
88
106
  -------------------------------------------------------------------------- */
@@ -96,226 +114,17 @@ const ICON_SIZE = "size-3.5";
96
114
  * left-to-right.
97
115
  */
98
116
  function RunningPreview({ children }: { children: ReactNode }) {
117
+ const compact = useContext(CompactActivityContext);
99
118
  return (
100
119
  <span className="inline-flex items-center gap-1.5">
101
- <span className="size-1.5 shrink-0 animate-og-pulse rounded-full bg-og-status-running" />
120
+ {!compact && (
121
+ <span className="size-1.5 shrink-0 animate-og-pulse rounded-full bg-og-status-running" />
122
+ )}
102
123
  <span className="min-w-0 truncate">{children}</span>
103
124
  </span>
104
125
  );
105
126
  }
106
127
 
107
- /** Prefix a collapsed preview with the host-supplied active compute label. */
108
- function withComputePreview(label: string | null, preview: string): string {
109
- if (!label) {
110
- return preview;
111
- }
112
- return `on ${label} · ${preview}`;
113
- }
114
-
115
- /* ---- exec_command ---------------------------------------------------------- */
116
-
117
- function ExecRenderer({ item }: ToolRendererProps) {
118
- const args = parseToolArgs(item.arguments);
119
- const cmd = typeof args.cmd === "string" ? args.cmd : "";
120
- const workdir = typeof args.workdir === "string" ? args.workdir : null;
121
- const running = item.status === "running";
122
- const out = item.output;
123
- const title = `$ ${cmd}`;
124
- const computeLabel = useTimelineComputeLabel();
125
-
126
- // No output event ever arrived (item.output stays undefined from creation):
127
- // the turn failed before the output insert — most likely a NUL byte in the
128
- // command output prevented storage. Surface the specific explanation.
129
- // (Cancelled items bypass this: a cancellation is not a NUL-storage failure.)
130
- if (item.status === "failed" && out === undefined) {
131
- return (
132
- <ActivityDisclosure
133
- icon={<TerminalIcon className={ICON_SIZE} />}
134
- iconTone="failed"
135
- title={title}
136
- titleMono
137
- chip={{ tone: "bad", text: "failed" }}
138
- preview={withComputePreview(computeLabel, "output lost — NUL byte could not be stored")}
139
- >
140
- <BodyNote tone="error">
141
- output contained a NUL byte and could not be stored; the turn failed on this tool&apos;s
142
- output insert — no output event ever arrived.
143
- </BodyNote>
144
- </ActivityDisclosure>
145
- );
146
- }
147
-
148
- // An output event arrived but the tool still failed (error:true / MCP isError)
149
- // and the output is empty — show a generic failure rather than claiming NUL.
150
- // (Cancelled items bypass this: a cancellation is not a tool-call failure.)
151
- if (item.status === "failed" && (out == null || out === "")) {
152
- return (
153
- <ActivityDisclosure
154
- icon={<TerminalIcon className={ICON_SIZE} />}
155
- iconTone="failed"
156
- title={title}
157
- titleMono
158
- chip={{ tone: "bad", text: "failed" }}
159
- preview={withComputePreview(computeLabel, "tool call failed")}
160
- >
161
- <BodyNote tone="error">the tool call failed with no output.</BodyNote>
162
- </ActivityDisclosure>
163
- );
164
- }
165
-
166
- if (running) {
167
- const streamed = typeof out === "string" ? stripExecBanner(out) : "";
168
- const runningPreview = streamed ? `${streamed.split("\n").length} lines` : "running…";
169
- return (
170
- <ActivityDisclosure
171
- icon={<TerminalIcon className={ICON_SIZE} />}
172
- iconTone="running"
173
- title={title}
174
- titleMono
175
- running
176
- preview={
177
- <RunningPreview>{withComputePreview(computeLabel, runningPreview)}</RunningPreview>
178
- }
179
- >
180
- {/* The row title is already `$ ${cmd}`; the TermBlock header drops the
181
- command (command={null}) so it never repeats above the output. */}
182
- <TermBlock command={null} workdir={workdir} output={streamed} live />
183
- </ActivityDisclosure>
184
- );
185
- }
186
-
187
- const text = typeof out === "string" ? out : stringifyPayload(out);
188
- const stripped = stripExecBanner(text);
189
- const bgSession = parseExecBannerSessionId(text);
190
- const exitCode = sandboxCommandExitCode(text);
191
- const binary = looksBinary(stripped);
192
-
193
- // Color is spent on the exception only: a clean exit (0) earns NO chip — the
194
- // absence of a red token is the success signal. Background sessions surface a
195
- // muted id; a non-zero exit is the one red token.
196
- let chip: DisclosureChip | undefined;
197
- let iconTone: "accent" | "failed" | "muted" = "muted";
198
- if (bgSession != null) {
199
- chip = { tone: "muted", text: `session ${bgSession}` };
200
- } else if (exitCode != null && exitCode !== 0) {
201
- chip = { tone: "bad", text: `exit ${exitCode}` };
202
- iconTone = "failed";
203
- }
204
-
205
- const peek = binary ? "binary output" : tailPeek(stripped) || "(no output)";
206
- const truncated = execTruncated(text);
207
- const preview = withComputePreview(computeLabel, truncated ? `⋯ truncated · ${peek}` : peek);
208
- // Hand TermBlock the FULL stripped output; it owns the tail/show-more slicing.
209
- const body = binary ? "(binary output suppressed)" : stripped;
210
-
211
- return (
212
- <ActivityDisclosure
213
- icon={<TerminalIcon className={ICON_SIZE} />}
214
- iconTone={iconTone}
215
- title={title}
216
- titleMono
217
- {...(chip ? { chip } : {})}
218
- failed={item.status === "failed"}
219
- cancelled={item.status === "cancelled"}
220
- preview={preview}
221
- >
222
- <TermBlock
223
- command={null}
224
- workdir={workdir}
225
- output={body}
226
- failed={item.status === "failed" || (exitCode != null && exitCode !== 0)}
227
- />
228
- {bgSession != null ? (
229
- <BodyNote>↳ session {bgSession} — a later write_stdin can target this PTY.</BodyNote>
230
- ) : null}
231
- </ActivityDisclosure>
232
- );
233
- }
234
-
235
- /* ---- write_stdin ----------------------------------------------------------- */
236
-
237
- function WriteStdinRenderer({ item }: ToolRendererProps) {
238
- const args = parseToolArgs(item.arguments);
239
- const sessionId =
240
- typeof args.session_id === "string" || typeof args.session_id === "number"
241
- ? args.session_id
242
- : undefined;
243
- const running = item.status === "running";
244
- const text = typeof item.output === "string" ? item.output : stringifyPayload(item.output);
245
- const lost = isExecSessionLostBanner(text);
246
- const keys = controlCaret(typeof args.chars === "string" ? args.chars : "");
247
- const exitCode = sandboxCommandExitCode(text);
248
- const stripped = stripExecBanner(text);
249
-
250
- if (running) {
251
- return (
252
- <ActivityDisclosure
253
- icon={<KeyboardIcon className={ICON_SIZE} />}
254
- iconTone="running"
255
- title={`session ${sessionId} ← ${keys || "∅"}`}
256
- titleMono
257
- running
258
- preview={<RunningPreview>sending…</RunningPreview>}
259
- >
260
- <BodyNote>sending input to session {sessionId}…</BodyNote>
261
- </ActivityDisclosure>
262
- );
263
- }
264
-
265
- // Success (exit 0 or a quiet ack) earns no chip; only a lost PTY / non-zero
266
- // exit gets the one red token.
267
- let chip: DisclosureChip | undefined;
268
- if (lost) {
269
- chip = { tone: "bad", text: "lost" };
270
- } else if (exitCode != null && exitCode !== 0) {
271
- chip = { tone: "bad", text: `exit ${exitCode}` };
272
- }
273
-
274
- return (
275
- <ActivityDisclosure
276
- icon={<KeyboardIcon className={ICON_SIZE} />}
277
- iconTone={lost ? "failed" : "muted"}
278
- title={`session ${sessionId} ← ${keys || "∅"}`}
279
- titleMono
280
- {...(chip ? { chip } : {})}
281
- failed={item.status === "failed"}
282
- cancelled={item.status === "cancelled"}
283
- preview={lost ? `session ${sessionId} PTY vanished` : tailPeek(stripped) || "sent"}
284
- >
285
- {lost ? (
286
- <BodyNote tone="error">{stripped || text}</BodyNote>
287
- ) : (
288
- <TermBlock command={`write_stdin → session ${sessionId}`} output={stripped} />
289
- )}
290
- </ActivityDisclosure>
291
- );
292
- }
293
-
294
- /* ---- apply_patch ----------------------------------------------------------- */
295
-
296
- function verbForOp(op: ApplyPatchOperation | undefined): string {
297
- if (!op) {
298
- return "Edited";
299
- }
300
- return op.type === "create_file"
301
- ? "Created"
302
- : op.type === "delete_file"
303
- ? "Deleted"
304
- : op.moveTo
305
- ? "Renamed"
306
- : "Edited";
307
- }
308
-
309
- function basename(path: string): string {
310
- const parts = path.split("/").filter(Boolean);
311
- return parts.length ? parts[parts.length - 1]! : path;
312
- }
313
-
314
- function dirname(path: string): string {
315
- const idx = path.lastIndexOf("/");
316
- return idx >= 0 ? path.slice(0, idx + 1) : "";
317
- }
318
-
319
128
  /**
320
129
  * The collapsed-row path preview. Diff magnitude is rendered as a SINGLE muted
321
130
  * "+N −M" glyph pair — the saturated add/del green/red is reserved exclusively
@@ -334,8 +143,8 @@ function PathPreview({
334
143
  return (
335
144
  <span className="inline-flex items-center gap-2 truncate font-og-mono">
336
145
  <span className="truncate">
337
- <span className="text-og-fg-subtle">{dirname(path)}</span>
338
- <span className="text-og-fg-muted">{basename(path)}</span>
146
+ <span className="text-og-fg-subtle">{pathDirname(path)}</span>
147
+ <span className="text-og-fg-muted">{pathBasename(path)}</span>
339
148
  </span>
340
149
  {add != null || del != null ? (
341
150
  <span className="shrink-0 text-og-fg-subtle">
@@ -348,162 +157,281 @@ function PathPreview({
348
157
  );
349
158
  }
350
159
 
351
- function ApplyPatchRenderer({ item }: ToolRendererProps) {
352
- const ops = applyPatchOpsFromToolItem(item);
353
- const failed = item.status === "failed";
354
- const cancelled = item.status === "cancelled";
355
- const running = item.status === "running";
356
- const firstOp = ops[0];
357
-
358
- if (running) {
359
- // Show the patch structure from the arguments (available immediately on
360
- // creation), but mark the row clearly as in-progress — not applied yet.
361
- const fileCount = ops.length;
362
- const titleVerb = firstOp ? `Applying ${basename(firstOp.path)}` : "Applying patch";
363
- return (
364
- <ActivityDisclosure
365
- icon={<FileDiffIcon className={ICON_SIZE} />}
366
- iconTone="running"
367
- title={fileCount > 1 ? `Applying ${fileCount} files` : titleVerb}
368
- running
369
- preview={
370
- <RunningPreview>
371
- {fileCount > 1 ? `${fileCount} files` : firstOp ? firstOp.path : "applying…"}
372
- </RunningPreview>
373
- }
374
- >
375
- {ops.map((op, index) => {
376
- const file = safeParseOp(op);
377
- const key = `${op.type}:${op.path}:${index}`;
378
- return file ? (
379
- <ToolDiff key={key} files={[file]} />
380
- ) : (
381
- <div key={key}>
382
- <p className="mb-1 font-og-mono text-og-xs text-og-fg-muted">{op.path}</p>
383
- <RawPatch diff={op.diff ?? ""} />
384
- </div>
385
- );
386
- })}
387
- </ActivityDisclosure>
388
- );
389
- }
160
+ /* ---- shared-model rows ------------------------------------------------------
161
+ exec_command, write_stdin, apply_patch, web search, request_human_input,
162
+ run_on and the generic fallback draw their row from the renderer-neutral
163
+ presentation model in ./tool-presentation, which non-DOM renderers share.
164
+ -------------------------------------------------------------------------- */
390
165
 
391
- if (failed) {
392
- return (
393
- <ActivityDisclosure
394
- icon={<FileDiffIcon className={ICON_SIZE} />}
395
- iconTone="failed"
396
- title={firstOp ? `${verbForOp(firstOp)} ${basename(firstOp.path)}` : "apply_patch"}
397
- chip={{ tone: "bad", text: "failed" }}
398
- preview={typeof item.output === "string" ? item.output : "patch failed"}
399
- >
400
- <PayloadBlock label="Error" value={item.output} failed />
401
- </ActivityDisclosure>
402
- );
166
+ /**
167
+ * The icon for a presentation kind, looked up when a row renders. A module-level
168
+ * table would capture the icons at load time; in a bundle where this module and
169
+ * the icon library's chunk import each other, an icon can still be undefined
170
+ * then, and the row would render an undefined component (React #130).
171
+ */
172
+ function toolIcon(kind: ToolIconKind): LucideIcon {
173
+ switch (kind) {
174
+ case "terminal":
175
+ return TerminalIcon;
176
+ case "keyboard":
177
+ return KeyboardIcon;
178
+ case "file-diff":
179
+ return FileDiffIcon;
180
+ case "search":
181
+ return SearchIcon;
182
+ case "question":
183
+ return MessageCircleQuestionIcon;
184
+ case "target":
185
+ return TargetIcon;
186
+ case "brain":
187
+ return BrainCircuitIcon;
188
+ case "sessions":
189
+ return MessagesSquareIcon;
190
+ case "server":
191
+ return ServerIcon;
192
+ case "server-cog":
193
+ return ServerCogIcon;
194
+ case "calendar":
195
+ return CalendarClockIcon;
196
+ case "panels":
197
+ return PanelsTopLeftIcon;
198
+ case "share":
199
+ return Share2Icon;
200
+ case "message":
201
+ return MessageSquareIcon;
202
+ case "git":
203
+ return FolderGitIcon;
204
+ case "box":
205
+ return BoxIcon;
206
+ case "key":
207
+ return KeyRoundIcon;
208
+ case "file-search":
209
+ return FileSearchIcon;
210
+ case "package-search":
211
+ return PackageSearchIcon;
212
+ case "plug":
213
+ return PlugIcon;
214
+ case "wrench":
215
+ return WrenchIcon;
403
216
  }
217
+ }
404
218
 
405
- // multi-file edit — magnitude stays a single muted glyph; the per-file
406
- // green/red lives only inside the expanded DiffView gutter.
407
- if (ops.length > 1) {
408
- // Parse every op: successfully parsed ones go into ToolDiff; malformed ops
409
- // fall back to a RawPatch display (mirroring the single-op fallback path).
410
- // The count in the title/preview equals ops.length so it is always truthful
411
- // regardless of how many ops parsed successfully.
412
- const parsed = ops.map((op) => safeParseOp(op));
413
- const goodFiles = parsed.filter((f): f is GitFileDiff => f !== null);
414
- const add = goodFiles.reduce((n, f) => n + f.additions, 0);
415
- const del = goodFiles.reduce((n, f) => n + f.deletions, 0);
416
- return (
417
- <ActivityDisclosure
418
- icon={<FileDiffIcon className={ICON_SIZE} />}
419
- iconTone="accent"
420
- title={`Edited ${ops.length} files`}
421
- cancelled={cancelled}
422
- preview={
423
- <span className="inline-flex items-center gap-2 font-og-mono">
424
- <span className="text-og-fg-muted">{ops.length} files</span>
425
- <span className="text-og-fg-subtle">
426
- +{add} −{del}
427
- </span>
219
+ function presentedPreviewNode(preview: ToolPreview): ReactNode {
220
+ switch (preview.kind) {
221
+ case "text":
222
+ return preview.running ? <RunningPreview>{preview.text}</RunningPreview> : preview.text;
223
+ case "path":
224
+ return <PathPreview path={preview.path} add={preview.add} del={preview.del} />;
225
+ case "files":
226
+ return (
227
+ <span className="inline-flex items-center gap-2 font-og-mono">
228
+ <span className="text-og-fg-muted">{preview.count} files</span>
229
+ <span className="text-og-fg-subtle">
230
+ +{preview.add} −{preview.del}
428
231
  </span>
429
- }
430
- >
431
- {ops.map((op, index) => {
432
- const file = parsed[index];
433
- const key = `${op.type}:${op.path}:${index}`;
434
- return file ? (
435
- <ToolDiff key={key} files={[file]} />
436
- ) : (
437
- <div key={key}>
438
- <p className="mb-1 font-og-mono text-og-xs text-og-fg-muted">{op.path}</p>
439
- <RawPatch diff={op.diff ?? ""} />
440
- </div>
441
- );
442
- })}
443
- </ActivityDisclosure>
444
- );
232
+ </span>
233
+ );
234
+ case "malformed":
235
+ return (
236
+ <span className="inline-flex items-center gap-2 font-og-mono">
237
+ <span className="text-og-fg-muted">{preview.name}</span>
238
+ <span className="text-og-fg-subtle">malformed V4A</span>
239
+ </span>
240
+ );
445
241
  }
242
+ }
446
243
 
447
- // single op
448
- if (!firstOp) {
449
- return <GenericRenderer item={item} />;
450
- }
451
- if (firstOp.type === "delete_file") {
452
- return (
453
- <ActivityDisclosure
454
- icon={<FileDiffIcon className={ICON_SIZE} />}
455
- iconTone="failed"
456
- title={`Deleted ${basename(firstOp.path)}`}
457
- cancelled={cancelled}
458
- preview={<PathPreview path={firstOp.path} />}
459
- >
460
- <BodyNote>File deleted — no diff to show.</BodyNote>
461
- </ActivityDisclosure>
462
- );
244
+ function PresentedBody({ body }: { body: ToolBody }) {
245
+ switch (body.kind) {
246
+ case "term":
247
+ return (
248
+ <>
249
+ {/* Exec rows pass command={null}: the row title already carries `$ cmd`. */}
250
+ <TermBlock
251
+ command={body.command}
252
+ {...(body.workdir !== null || body.command === null ? { workdir: body.workdir } : {})}
253
+ output={body.output}
254
+ {...(body.live ? { live: true } : {})}
255
+ {...(body.failed !== undefined ? { failed: body.failed } : {})}
256
+ />
257
+ {body.note ? <BodyNote>{body.note}</BodyNote> : null}
258
+ </>
259
+ );
260
+ case "note":
261
+ return body.tone ? (
262
+ <BodyNote tone={body.tone}>{body.text}</BodyNote>
263
+ ) : (
264
+ <BodyNote>{body.text}</BodyNote>
265
+ );
266
+ case "payloads":
267
+ return (
268
+ <>
269
+ {body.note ? <p className="m-0 py-1 text-og-sm text-og-fg-muted">{body.note}</p> : null}
270
+ {body.blocks.map((block) =>
271
+ block.failed ? (
272
+ <PayloadBlock key={block.label} label={block.label} value={block.value} failed />
273
+ ) : (
274
+ <PayloadBlock key={block.label} label={block.label} value={block.value} />
275
+ ),
276
+ )}
277
+ </>
278
+ );
279
+ case "patch":
280
+ if (body.bare) {
281
+ return <RawPatch diff={body.files[0]?.diff ?? ""} />;
282
+ }
283
+ return (
284
+ <>
285
+ {body.files.map((entry) =>
286
+ entry.file ? (
287
+ <ToolDiff key={entry.key} files={[entry.file]} />
288
+ ) : (
289
+ <div key={entry.key}>
290
+ <p className="mb-1 font-og-mono text-og-xs text-og-fg-muted">{entry.path}</p>
291
+ <RawPatch diff={entry.diff} />
292
+ </div>
293
+ ),
294
+ )}
295
+ </>
296
+ );
297
+ case "web-results":
298
+ return <WebSearchResults results={body.results} />;
463
299
  }
300
+ }
464
301
 
465
- const file = safeParseOp(firstOp);
466
- if (!file) {
467
- return (
468
- <ActivityDisclosure
469
- icon={<FileDiffIcon className={ICON_SIZE} />}
470
- iconTone="accent"
471
- title={`${verbForOp(firstOp)} ${basename(firstOp.path)}`}
472
- cancelled={cancelled}
473
- preview={
474
- <span className="inline-flex items-center gap-2 font-og-mono">
475
- <span className="text-og-fg-muted">{basename(firstOp.path)}</span>
476
- <span className="text-og-fg-subtle">malformed V4A</span>
477
- </span>
478
- }
479
- >
480
- <RawPatch diff={firstOp.diff ?? ""} />
481
- </ActivityDisclosure>
482
- );
302
+ function WebSearchResults({ results }: { results: WebSearchResult[] | null }) {
303
+ const resultOccurrences = new Map<string, number>();
304
+ const keyedResults = results?.map((result) => {
305
+ const contentKey = `${result.domain}\u0000${result.title}\u0000${result.snippet}`;
306
+ const occurrence = (resultOccurrences.get(contentKey) ?? 0) + 1;
307
+ resultOccurrences.set(contentKey, occurrence);
308
+ return { key: `${contentKey}\u0000${occurrence}`, result };
309
+ });
310
+ if (!keyedResults || !keyedResults.length) {
311
+ return <BodyNote>results folded into model context — no list available.</BodyNote>;
483
312
  }
313
+ return (
314
+ <ul className="flex flex-col gap-2">
315
+ {keyedResults.map(({ key, result }) => (
316
+ <li key={key} className="flex gap-2.5">
317
+ <GlobeIcon className="mt-0.5 size-3.5 shrink-0 text-og-fg-subtle" />
318
+ <div className="min-w-0">
319
+ <p className="truncate text-og-base text-og-fg">
320
+ {result.title} <span className="text-og-fg-subtle">{result.domain}</span>
321
+ </p>
322
+ <p className="text-og-sm leading-5 text-og-fg-muted">{result.snippet}</p>
323
+ </div>
324
+ </li>
325
+ ))}
326
+ </ul>
327
+ );
328
+ }
484
329
 
485
- // The collapsed row shows verb + basename (title) and a muted "+N −M"
486
- // (preview); on expand the preview is hidden and the DiffView header carries
487
- // the path + churn — so the filename/stat never appears twice at once.
330
+ /** Draw one shared-model row with the web disclosure primitives. */
331
+ function PresentedToolRow({
332
+ presentation: p,
333
+ logo = null,
334
+ }: {
335
+ presentation: ToolRowPresentation;
336
+ /** A connector's logo, drawn in place of the row's icon. */
337
+ logo?: string | null;
338
+ }) {
339
+ const Icon = toolIcon(p.icon);
488
340
  return (
489
341
  <ActivityDisclosure
490
- icon={<FileDiffIcon className={ICON_SIZE} />}
491
- iconTone="accent"
492
- title={`${verbForOp(firstOp)} ${basename(file.path)}`}
493
- cancelled={cancelled}
494
- preview={<PathPreview path={file.path} add={file.additions} del={file.deletions} />}
342
+ icon={<ConnectorLogoIcon src={logo} fallback={<Icon className={ICON_SIZE} />} />}
343
+ iconTone={p.iconTone}
344
+ // A chip inside the row's own toggle never links.
345
+ title={<SecretRefText text={p.title} linkable={false} />}
346
+ {...(p.titleMono ? { titleMono: true } : {})}
347
+ {...(p.running ? { running: true } : {})}
348
+ {...(p.chip ? { chip: p.chip } : {})}
349
+ {...(p.failed !== undefined ? { failed: p.failed } : {})}
350
+ {...(p.cancelled !== undefined ? { cancelled: p.cancelled } : {})}
351
+ preview={p.preview ? presentedPreviewNode(p.preview) : undefined}
495
352
  >
496
- <ToolDiff files={[file]} />
353
+ <PresentedBody body={p.body} />
497
354
  </ActivityDisclosure>
498
355
  );
499
356
  }
500
357
 
501
- function safeParseOp(op: ApplyPatchOperation): GitFileDiff | null {
502
- try {
503
- return v4aToGitFileDiff(op);
504
- } catch {
505
- return null;
506
- }
358
+ function ExecRenderer({ item }: ToolRendererProps) {
359
+ const computeLabel = useTimelineComputeLabel();
360
+ return <PresentedToolRow presentation={execPresentation(item, { computeLabel })} />;
361
+ }
362
+
363
+ function WriteStdinRenderer({ item }: ToolRendererProps) {
364
+ return <PresentedToolRow presentation={writeStdinPresentation(item)} />;
365
+ }
366
+
367
+ function ApplyPatchRenderer({ item }: ToolRendererProps) {
368
+ const presentation = applyPatchPresentation(item);
369
+ return presentation ? (
370
+ <PresentedToolRow presentation={presentation} />
371
+ ) : (
372
+ <GenericRenderer item={item} />
373
+ );
374
+ }
375
+
376
+ function WebSearchRenderer({ item }: ToolRendererProps) {
377
+ return <PresentedToolRow presentation={webSearchPresentation(item)} />;
378
+ }
379
+
380
+ function AskRenderer({ item }: ToolRendererProps) {
381
+ return <PresentedToolRow presentation={askPresentation(item)} />;
382
+ }
383
+
384
+ function RunOnRenderer({ item }: ToolRendererProps) {
385
+ return <PresentedToolRow presentation={runOnPresentation(item)} />;
386
+ }
387
+
388
+ /**
389
+ * Baseline craft for unmatched tools: family icon + title-cased leaf + honest
390
+ * status preview (Running… / Done / error snippet). No argument-field sniffing —
391
+ * JSON stays in the expandable body only.
392
+ */
393
+ function UnreviewedGenericRenderer({ item, loadRetainedScreenshot }: ToolRendererProps) {
394
+ const logo = useToolConnectorLogo(item.display);
395
+ const { images, output } = retainedToolResultImages(item.output);
396
+ if (images.length === 0 || item.status === "running") {
397
+ return <PresentedToolRow presentation={genericToolPresentation(item)} logo={logo} />;
398
+ }
399
+ // A tool that returned images shows them like a viewed image, with its
400
+ // ordinary result text (and any further images) in the expanded body.
401
+ const presentation = genericToolPresentation({ ...item, output });
402
+ const Icon = toolIcon(presentation.icon);
403
+ const [first, ...rest] = images;
404
+ return (
405
+ <RetainedSessionImageDisclosure
406
+ artifact={first!}
407
+ load={loadRetainedScreenshot}
408
+ title={presentation.title}
409
+ caption={presentation.title}
410
+ noun="image"
411
+ icon={<ConnectorLogoIcon src={logo} fallback={<Icon className={ICON_SIZE} />} />}
412
+ lightboxLabel={presentation.title}
413
+ batched={null}
414
+ failed={presentation.failed ?? false}
415
+ cancelled={presentation.cancelled ?? false}
416
+ >
417
+ <PresentedBody body={presentation.body} />
418
+ {rest.map((image, index) => (
419
+ <RetainedSessionImageDisclosure
420
+ key={image.artifactId}
421
+ artifact={image}
422
+ load={loadRetainedScreenshot}
423
+ title={`Image ${index + 2}`}
424
+ caption={`${presentation.title}, image ${index + 2}`}
425
+ noun="image"
426
+ icon={<ImageIcon className={ICON_SIZE} />}
427
+ lightboxLabel={presentation.title}
428
+ batched={null}
429
+ failed={false}
430
+ cancelled={false}
431
+ />
432
+ ))}
433
+ </RetainedSessionImageDisclosure>
434
+ );
507
435
  }
508
436
 
509
437
  /* ---- computer_call --------------------------------------------------------- */
@@ -579,7 +507,10 @@ function ComputerCallRenderer({ item, loadRetainedScreenshot }: ToolRendererProp
579
507
  // every transport.
580
508
  const functionAction: ComputerAction | undefined =
581
509
  !raw.action && item.name.startsWith("computer_") && item.name !== "computer_call"
582
- ? { type: item.name.slice("computer_".length), ...asComputerArgs(item.arguments) }
510
+ ? {
511
+ type: item.name.slice("computer_".length),
512
+ ...asComputerArgs(item.arguments),
513
+ }
583
514
  : undefined;
584
515
  const action = raw.action ?? functionAction;
585
516
  const actions = raw.actions ?? (action ? [action] : []);
@@ -766,96 +697,107 @@ function ComputerCallRenderer({ item, loadRetainedScreenshot }: ToolRendererProp
766
697
  );
767
698
  }
768
699
 
769
- type RetainedImageState =
770
- | { kind: "loading" }
771
- | { kind: "ready"; url: string }
772
- | { kind: "unavailable"; label: string }
773
- | { kind: "error"; message: string };
774
-
775
- function useRetainedImageObjectUrl(
776
- artifact: RetainedArtifactReference,
777
- load: ToolRendererProps["loadRetainedArtifact"],
778
- ): RetainedImageState {
779
- const [state, setState] = useState<RetainedImageState>({ kind: "loading" });
780
- // Function outputs are commonly serialized JSON. Parsing them creates a new
781
- // object on every render, so depend on the immutable wire value rather than
782
- // object identity; otherwise the loader can refetch after its own setState.
783
- const artifactValue = retainedArtifactValue(artifact);
784
- const stableArtifactRef = useRef({ value: artifactValue, artifact });
785
- if (stableArtifactRef.current.value !== artifactValue) {
786
- stableArtifactRef.current = { value: artifactValue, artifact };
700
+ /** Browser tool images share authenticated screenshot loading, with their own media kind. */
701
+ function BrowserScreenshotRenderer(props: ToolRendererProps) {
702
+ const { item, loadRetainedScreenshot } = props;
703
+ const leaf = mcpToolLeaf(item.name);
704
+ const title =
705
+ leaf === "browser_observe"
706
+ ? "Observed browser"
707
+ : leaf === "browser_act"
708
+ ? "Used browser"
709
+ : "Browser screenshot";
710
+ const retained = retainedScreenshotMetadata(item.output);
711
+ const isFailed = item.status === "failed";
712
+ const isCancelled = item.status === "cancelled";
713
+
714
+ if (item.status === "running" && leaf === "browser_screenshot") {
715
+ return (
716
+ <ActivityDisclosure
717
+ icon={<CameraIcon className={ICON_SIZE} />}
718
+ iconTone="running"
719
+ title={title}
720
+ running
721
+ media={<MediaSkeleton />}
722
+ >
723
+ <BodyNote>capturing frame…</BodyNote>
724
+ </ActivityDisclosure>
725
+ );
726
+ }
727
+
728
+ if (!retained) return <GenericRenderer {...props} />;
729
+ if (!retained.available) {
730
+ const state =
731
+ retained.reason === "expired" || retained.reason === "deleted"
732
+ ? retained.reason
733
+ : "unavailable";
734
+ return (
735
+ <ActivityDisclosure
736
+ icon={<CameraOffIcon className={ICON_SIZE} />}
737
+ iconTone={isFailed ? "failed" : "muted"}
738
+ title={`${title} · ${state}`}
739
+ failed={isFailed}
740
+ cancelled={isCancelled}
741
+ preview={`screenshot ${state}`}
742
+ media={<MediaEmpty />}
743
+ >
744
+ <BodyNote tone={isFailed ? "error" : undefined}>
745
+ Screenshot {state}: {retained.reason.replaceAll("_", " ")}.
746
+ </BodyNote>
747
+ </ActivityDisclosure>
748
+ );
787
749
  }
788
- const stableArtifact = stableArtifactRef.current.artifact;
789
- useEffect(() => {
790
- if (!load) {
791
- setState({ kind: "unavailable", label: "retrieval is not configured" });
792
- return;
793
- }
794
- const controller = new AbortController();
795
- let objectUrl: string | null = null;
796
- setState({ kind: "loading" });
797
- void load(stableArtifact, controller.signal)
798
- .then((source) => {
799
- if (controller.signal.aborted) return;
800
- if (!source) {
801
- setState({ kind: "unavailable", label: "bytes are unavailable" });
802
- return;
803
- }
804
- if (!(source instanceof Uint8Array)) {
805
- if (!source.url.trim()) {
806
- setState({ kind: "unavailable", label: "URL is unavailable" });
807
- return;
808
- }
809
- setState({ kind: "ready", url: source.url });
810
- return;
811
- }
812
- objectUrl = URL.createObjectURL(
813
- new Blob([source as unknown as BlobPart], { type: stableArtifact.contentType }),
814
- );
815
- setState({ kind: "ready", url: objectUrl });
816
- })
817
- .catch((error: unknown) => {
818
- if (controller.signal.aborted) return;
819
- const status =
820
- error && typeof error === "object" && "status" in error
821
- ? Number((error as { status?: unknown }).status)
822
- : null;
823
- setState(
824
- status === 404
825
- ? { kind: "unavailable", label: "deleted" }
826
- : status === 410
827
- ? { kind: "unavailable", label: "expired or unavailable" }
828
- : { kind: "error", message: "retrieval failed" },
829
- );
830
- });
831
- return () => {
832
- controller.abort();
833
- if (objectUrl) URL.revokeObjectURL(objectUrl);
834
- };
835
- }, [stableArtifact, load]);
836
- return state;
750
+ return (
751
+ <RetainedSessionImageDisclosure
752
+ artifact={retained}
753
+ load={loadRetainedScreenshot}
754
+ title={title}
755
+ caption={title}
756
+ noun="screenshot"
757
+ icon={<CameraIcon className={ICON_SIZE} />}
758
+ lightboxLabel="Browser screenshot"
759
+ batched={null}
760
+ failed={isFailed}
761
+ cancelled={isCancelled}
762
+ />
763
+ );
837
764
  }
838
765
 
839
- function retainedArtifactValue(artifact: RetainedArtifactReference): string {
840
- return [
841
- artifact.artifactId,
842
- artifact.kind,
843
- artifact.contentType,
844
- artifact.originalBytes,
845
- artifact.sha256,
846
- artifact.retainedAt,
847
- artifact.dimensions?.width ?? "",
848
- artifact.dimensions?.height ?? "",
849
- artifact.retention.policy,
850
- artifact.retention.expiresAt ?? "",
851
- artifact.retrieval.method,
852
- artifact.retrieval.path,
853
- artifact.retrieval.acceptRanges,
854
- artifact.retrieval.maxRangeBytes,
855
- ].join("\0");
766
+ type RetainedSessionImageDisclosureProps = {
767
+ artifact: RetainedArtifactReference;
768
+ load: ToolRendererProps["loadRetainedArtifact"];
769
+ title: string;
770
+ caption: string;
771
+ noun: "image" | "screenshot";
772
+ icon: ReactNode;
773
+ lightboxLabel: string;
774
+ batched: string | null;
775
+ failed: boolean;
776
+ cancelled: boolean;
777
+ filename?: string;
778
+ defaultOpen?: boolean;
779
+ children?: ReactNode;
780
+ };
781
+
782
+ function RetainedSessionImageDisclosure(props: RetainedSessionImageDisclosureProps) {
783
+ const compact = useContext(CompactActivityContext);
784
+ // The rolling progress label has no screenshot loader by design. Do not run
785
+ // the full image hook there: it would report the missing loader as an error.
786
+ if (compact) {
787
+ return (
788
+ <ActivityDisclosure
789
+ icon={props.icon}
790
+ title={props.title}
791
+ compactPreview={null}
792
+ failed={props.failed}
793
+ cancelled={props.cancelled}
794
+ />
795
+ );
796
+ }
797
+ return <LoadedRetainedSessionImageDisclosure {...props} />;
856
798
  }
857
799
 
858
- function RetainedSessionImageDisclosure({
800
+ function LoadedRetainedSessionImageDisclosure({
859
801
  artifact,
860
802
  load,
861
803
  title,
@@ -866,26 +808,27 @@ function RetainedSessionImageDisclosure({
866
808
  batched,
867
809
  failed,
868
810
  cancelled,
869
- }: {
870
- artifact: RetainedArtifactReference;
871
- load: ToolRendererProps["loadRetainedScreenshot"];
872
- title: string;
873
- caption: string;
874
- noun: "image" | "screenshot";
875
- icon: ReactNode;
876
- lightboxLabel: string;
877
- batched: string | null;
878
- failed: boolean;
879
- cancelled: boolean;
880
- }) {
811
+ filename,
812
+ defaultOpen,
813
+ children,
814
+ }: RetainedSessionImageDisclosureProps) {
881
815
  const state = useRetainedImageObjectUrl(artifact, load);
882
- const downloadFilename = retainedImageFilename(artifact);
816
+ const downloadFilename = filename ?? retainedImageFilename(artifact);
817
+ const figure = {
818
+ src: state.kind === "ready" ? state.url : "",
819
+ caption,
820
+ alt: caption,
821
+ expandLabel: `Expand ${noun}`,
822
+ lightboxLabel,
823
+ downloadFilename,
824
+ };
883
825
 
884
826
  return (
885
827
  <ActivityDisclosure
886
828
  icon={icon}
887
829
  iconTone={failed ? "failed" : state.kind === "ready" ? "accent" : "muted"}
888
830
  title={title}
831
+ defaultOpen={defaultOpen}
889
832
  failed={failed}
890
833
  cancelled={cancelled}
891
834
  preview={
@@ -899,14 +842,7 @@ function RetainedSessionImageDisclosure({
899
842
  }
900
843
  media={
901
844
  state.kind === "ready" ? (
902
- <Thumbnail
903
- src={state.url}
904
- caption={caption}
905
- alt={caption}
906
- expandLabel={`Expand ${noun}`}
907
- lightboxLabel={lightboxLabel}
908
- downloadFilename={downloadFilename}
909
- />
845
+ <Thumbnail {...figure} />
910
846
  ) : state.kind === "loading" ? (
911
847
  <MediaSkeleton />
912
848
  ) : (
@@ -915,14 +851,7 @@ function RetainedSessionImageDisclosure({
915
851
  }
916
852
  >
917
853
  {state.kind === "ready" ? (
918
- <ScreenshotFigure
919
- src={state.url}
920
- caption={caption}
921
- alt={caption}
922
- expandLabel={`Expand ${noun}`}
923
- lightboxLabel={lightboxLabel}
924
- downloadFilename={downloadFilename}
925
- />
854
+ <ScreenshotFigure {...figure} />
926
855
  ) : state.kind === "loading" ? (
927
856
  <BodyNote>Loading the retained {noun}…</BodyNote>
928
857
  ) : state.kind === "unavailable" ? (
@@ -930,15 +859,30 @@ function RetainedSessionImageDisclosure({
930
859
  {noun === "screenshot" ? "Screenshot" : "Image"} {state.label}.
931
860
  </BodyNote>
932
861
  ) : (
933
- <BodyNote tone="error">
934
- {noun === "screenshot" ? "Screenshot" : "Image"} retrieval failed: {state.message}
935
- </BodyNote>
862
+ <ImageRetrievalError noun={noun} retry={state.retry} />
936
863
  )}
937
864
  {batched ? <BodyNote>batched: {batched}</BodyNote> : null}
865
+ {children}
938
866
  </ActivityDisclosure>
939
867
  );
940
868
  }
941
869
 
870
+ function ImageRetrievalError({ noun = "image", retry }: { noun?: string; retry: () => void }) {
871
+ return (
872
+ <BodyNote tone="error">
873
+ {noun === "screenshot" ? "Screenshot" : "Image"} retrieval failed.{" "}
874
+ <button
875
+ type="button"
876
+ aria-label={`Retry ${noun} retrieval`}
877
+ onClick={retry}
878
+ className="underline underline-offset-2"
879
+ >
880
+ Retry
881
+ </button>
882
+ </BodyNote>
883
+ );
884
+ }
885
+
942
886
  function GeneratedImageRenderer({ item, loadRetainedArtifact }: ToolRendererProps) {
943
887
  const args = parseToolArgs(item.arguments);
944
888
  const prompt = typeof args.prompt === "string" ? args.prompt : "";
@@ -1028,6 +972,22 @@ function SandboxFilePublishRenderer({ item, loadRetainedArtifact }: ToolRenderer
1028
972
  return <GenericRenderer item={item} />;
1029
973
  }
1030
974
 
975
+ // The closed SDK receipt has already checked the workspace-qualified route.
976
+ const workspaceId = /^\/v1\/workspaces\/([0-9a-f-]+)\/artifacts\//.exec(
977
+ receipt.artifact.retrieval.path,
978
+ )?.[1];
979
+ const openLink = workspaceId ? (
980
+ <a
981
+ href={`/workspaces/${workspaceId}/artifacts/files/${receipt.artifact.artifactId}`}
982
+ aria-label={`Open ${receipt.filename} in Artifacts`}
983
+ className="inline-flex min-h-7 items-center rounded-og-sm px-2 text-og-sm font-medium text-og-accent-strong hover:bg-og-surface-2 hover:underline focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-10"
984
+ onClick={(event) => event.stopPropagation()}
985
+ onKeyDown={(event) => event.stopPropagation()}
986
+ >
987
+ Open in Artifacts
988
+ </a>
989
+ ) : null;
990
+
1031
991
  const download = async () => {
1032
992
  if (!loadRetainedArtifact) {
1033
993
  setDownloadState("error");
@@ -1060,6 +1020,44 @@ function SandboxFilePublishRenderer({ item, loadRetainedArtifact }: ToolRenderer
1060
1020
  }
1061
1021
  };
1062
1022
 
1023
+ const downloadButton = (
1024
+ <button
1025
+ type="button"
1026
+ onClick={() => void download()}
1027
+ disabled={downloadState === "loading"}
1028
+ className="inline-flex items-center gap-1.5 rounded-og-sm border border-og-border px-2.5 py-1.5 text-og-sm font-medium text-og-fg transition-colors hover:border-og-border-strong hover:bg-og-surface-2 disabled:cursor-wait disabled:opacity-60"
1029
+ >
1030
+ <DownloadIcon className="size-3.5" />
1031
+ {downloadState === "loading"
1032
+ ? "Preparing…"
1033
+ : downloadState === "error"
1034
+ ? "Retry download"
1035
+ : "Download"}
1036
+ </button>
1037
+ );
1038
+
1039
+ if (isRetainedImageContentType(receipt.artifact.contentType)) {
1040
+ return (
1041
+ <RetainedSessionImageDisclosure
1042
+ artifact={receipt.artifact}
1043
+ load={loadRetainedArtifact}
1044
+ title={`Published ${receipt.filename}`}
1045
+ caption={receipt.filename}
1046
+ noun="image"
1047
+ icon={<ImageIcon className={ICON_SIZE} />}
1048
+ lightboxLabel="Image"
1049
+ batched={null}
1050
+ failed={false}
1051
+ cancelled={false}
1052
+ filename={receipt.filename}
1053
+ defaultOpen
1054
+ >
1055
+ {downloadButton}
1056
+ {openLink}
1057
+ </RetainedSessionImageDisclosure>
1058
+ );
1059
+ }
1060
+
1063
1061
  return (
1064
1062
  <ActivityDisclosure
1065
1063
  icon={<DownloadIcon className={ICON_SIZE} />}
@@ -1068,19 +1066,146 @@ function SandboxFilePublishRenderer({ item, loadRetainedArtifact }: ToolRenderer
1068
1066
  defaultOpen
1069
1067
  preview={formatBytes(receipt.artifact.originalBytes)}
1070
1068
  >
1069
+ {downloadButton}
1070
+ {openLink}
1071
+ {isPatchFilename(receipt.filename) ? (
1072
+ <PatchApplyCommand
1073
+ artifact={receipt.artifact}
1074
+ filename={receipt.filename}
1075
+ load={loadRetainedArtifact}
1076
+ />
1077
+ ) : null}
1078
+ </ActivityDisclosure>
1079
+ );
1080
+ }
1081
+
1082
+ type PublishedSiteReceipt = {
1083
+ workspaceId: string;
1084
+ artifactId: string;
1085
+ title: string;
1086
+ revision: number;
1087
+ replayed: boolean;
1088
+ };
1089
+
1090
+ function publishedSiteReceipt(output: unknown): PublishedSiteReceipt | null {
1091
+ const { text, isError } = unwrapMcpOutput(output);
1092
+ if (isError) return null;
1093
+ const parsed = tryParseJson(text);
1094
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
1095
+ const artifact = (parsed as Record<string, unknown>).artifact;
1096
+ const version = (parsed as Record<string, unknown>).version;
1097
+ if (
1098
+ !artifact ||
1099
+ typeof artifact !== "object" ||
1100
+ Array.isArray(artifact) ||
1101
+ !version ||
1102
+ typeof version !== "object" ||
1103
+ Array.isArray(version)
1104
+ ) {
1105
+ return null;
1106
+ }
1107
+ const artifactRecord = artifact as Record<string, unknown>;
1108
+ const versionRecord = version as Record<string, unknown>;
1109
+ if (
1110
+ typeof artifactRecord.workspaceId !== "string" ||
1111
+ typeof artifactRecord.id !== "string" ||
1112
+ typeof artifactRecord.title !== "string" ||
1113
+ typeof versionRecord.revision !== "number" ||
1114
+ !Number.isInteger(versionRecord.revision) ||
1115
+ versionRecord.revision < 1
1116
+ ) {
1117
+ return null;
1118
+ }
1119
+ return {
1120
+ workspaceId: artifactRecord.workspaceId,
1121
+ artifactId: artifactRecord.id,
1122
+ title: artifactRecord.title,
1123
+ revision: versionRecord.revision,
1124
+ replayed: (parsed as Record<string, unknown>).replayed === true,
1125
+ };
1126
+ }
1127
+
1128
+ const SITE_OPEN_CLASS =
1129
+ "inline-flex min-h-7 items-center rounded-og-sm px-2 text-og-sm font-medium text-og-accent-strong hover:bg-og-surface-2 hover:underline focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-10";
1130
+
1131
+ function SiteOpenLink({ receipt }: { receipt: PublishedSiteReceipt }) {
1132
+ const [pending, setPending] = useState(false);
1133
+ const [failed, setFailed] = useState(false);
1134
+ // The console route only exists in the Opengeni console; a host decides.
1135
+ const resolution = useOpenGeniLinkResolver()?.({
1136
+ kind: "site",
1137
+ artifactId: receipt.artifactId,
1138
+ workspaceId: receipt.workspaceId,
1139
+ });
1140
+ const destination = resolution?.href ? defaultUrlTransform(resolution.href) : "";
1141
+ if (destination) {
1142
+ return (
1143
+ <a
1144
+ href={destination}
1145
+ aria-label={`Open ${receipt.title}`}
1146
+ className={SITE_OPEN_CLASS}
1147
+ onClick={(event) => event.stopPropagation()}
1148
+ onKeyDown={(event) => event.stopPropagation()}
1149
+ >
1150
+ Open
1151
+ </a>
1152
+ );
1153
+ }
1154
+ if (resolution?.open) {
1155
+ const open = resolution.open;
1156
+ return (
1071
1157
  <button
1072
1158
  type="button"
1073
- onClick={() => void download()}
1074
- disabled={downloadState === "loading"}
1075
- className="inline-flex items-center gap-1.5 rounded-og-sm border border-og-border px-2.5 py-1.5 text-og-sm font-medium text-og-fg transition-colors hover:border-og-border-strong hover:bg-og-surface-2 disabled:cursor-wait disabled:opacity-60"
1159
+ aria-label={`Open ${receipt.title}`}
1160
+ aria-busy={pending}
1161
+ disabled={pending}
1162
+ className={SITE_OPEN_CLASS}
1163
+ onClick={(event) => {
1164
+ event.stopPropagation();
1165
+ if (pending) return;
1166
+ setPending(true);
1167
+ setFailed(false);
1168
+ void Promise.resolve()
1169
+ .then(open)
1170
+ .catch(() => setFailed(true))
1171
+ .finally(() => setPending(false));
1172
+ }}
1173
+ onKeyDown={(event) => event.stopPropagation()}
1076
1174
  >
1077
- <DownloadIcon className="size-3.5" />
1078
- {downloadState === "loading"
1079
- ? "Preparing…"
1080
- : downloadState === "error"
1081
- ? "Retry download"
1082
- : "Download"}
1175
+ {pending ? "Opening…" : failed ? "Retry open" : "Open"}
1083
1176
  </button>
1177
+ );
1178
+ }
1179
+ return null;
1180
+ }
1181
+
1182
+ function SiteArtifactRenderer({ item }: ToolRendererProps) {
1183
+ const leaf = mcpToolLeaf(item.name);
1184
+ const publishingExisting = leaf === "artifacts_publish";
1185
+ if (item.status === "running") {
1186
+ return (
1187
+ <ActivityDisclosure
1188
+ icon={<PanelsTopLeftIcon className={ICON_SIZE} />}
1189
+ iconTone="running"
1190
+ title={publishingExisting ? "Publishing Site update" : "Publishing Site"}
1191
+ running
1192
+ preview={<RunningPreview>retaining source and compiled HTML…</RunningPreview>}
1193
+ />
1194
+ );
1195
+ }
1196
+ const receipt = publishedSiteReceipt(item.output);
1197
+ if (!receipt || item.status === "failed") return <GenericRenderer item={item} />;
1198
+ return (
1199
+ <ActivityDisclosure
1200
+ icon={<PanelsTopLeftIcon className={ICON_SIZE} />}
1201
+ iconTone="accent"
1202
+ title={publishingExisting ? `Updated ${receipt.title}` : `Published ${receipt.title}`}
1203
+ media={<SiteOpenLink receipt={receipt} />}
1204
+ >
1205
+ <BodyNote>
1206
+ Version {receipt.revision} is live
1207
+ {receipt.replayed ? " (replayed from the original publication)." : "."}
1208
+ </BodyNote>
1084
1209
  </ActivityDisclosure>
1085
1210
  );
1086
1211
  }
@@ -1132,182 +1257,43 @@ function GeneratedImageDisclosure({
1132
1257
  <MediaSkeleton />
1133
1258
  ) : (
1134
1259
  <MediaEmpty />
1135
- )
1136
- }
1137
- >
1138
- {state.kind === "ready" ? (
1139
- <ScreenshotFigure
1140
- src={state.url}
1141
- caption={caption}
1142
- alt={caption}
1143
- expandLabel="Expand generated image"
1144
- lightboxLabel="Generated image"
1145
- />
1146
- ) : state.kind === "loading" ? (
1147
- <BodyNote>Loading the generated image…</BodyNote>
1148
- ) : state.kind === "unavailable" ? (
1149
- <BodyNote>Image {state.label}.</BodyNote>
1150
- ) : (
1151
- <BodyNote tone="error">Image retrieval failed.</BodyNote>
1152
- )}
1153
- <BodyNote>
1154
- {dimensions.width}×{dimensions.height} · {receipt.sandboxPath}
1155
- </BodyNote>
1156
- </ActivityDisclosure>
1157
- );
1158
- }
1159
-
1160
- function retainedImageFilename(artifact: RetainedArtifactReference): string {
1161
- const extension =
1162
- artifact.contentType === "image/jpeg"
1163
- ? "jpg"
1164
- : artifact.contentType === "image/webp"
1165
- ? "webp"
1166
- : "png";
1167
- return `${artifact.kind}-${artifact.artifactId}.${extension}`;
1168
- }
1169
-
1170
- /* ---- web_search ------------------------------------------------------------ */
1171
-
1172
- type WebSearchResult = { title: string; domain: string; snippet: string };
1173
-
1174
- /** Pull a search string from tool-call arguments when providerData.action is sparse. */
1175
- function webSearchQueryFromArguments(args: unknown): string | null {
1176
- if (typeof args === "string") {
1177
- const trimmed = args.trim();
1178
- if (!trimmed) {
1179
- return null;
1180
- }
1181
- try {
1182
- return webSearchQueryFromArguments(JSON.parse(trimmed));
1183
- } catch {
1184
- return trimmed;
1185
- }
1186
- }
1187
- if (!args || typeof args !== "object") {
1188
- return null;
1189
- }
1190
- const record = args as Record<string, unknown>;
1191
- if (typeof record.query === "string" && record.query.trim().length > 0) {
1192
- return record.query;
1193
- }
1194
- if (Array.isArray(record.queries)) {
1195
- const first = record.queries.find(
1196
- (value): value is string => typeof value === "string" && value.trim().length > 0,
1197
- );
1198
- if (first) {
1199
- return first;
1200
- }
1201
- }
1202
- return null;
1203
- }
1204
-
1205
- function WebSearchRenderer({ item }: ToolRendererProps) {
1206
- const raw = (item.raw ?? {}) as {
1207
- providerData?: {
1208
- action?: {
1209
- type?: string;
1210
- query?: string;
1211
- queries?: string[];
1212
- url?: string;
1213
- pattern?: string;
1214
- };
1215
- };
1216
- };
1217
- const action = raw.providerData?.action ?? {};
1218
- const actionType = action.type ?? "search";
1219
- // Responses API deprecated singular `query` in favor of `queries[]`.
1220
- // Codex/current OpenAI often only populate the array.
1221
- const queries = (action.queries ?? []).filter(
1222
- (value): value is string => typeof value === "string" && value.trim().length > 0,
1223
- );
1224
- const searchQuery =
1225
- (typeof action.query === "string" && action.query.trim().length > 0 ? action.query : null) ??
1226
- queries[0] ??
1227
- webSearchQueryFromArguments(item.arguments);
1228
- const running = item.status === "running";
1229
- const query =
1230
- actionType === "open_page"
1231
- ? (action.url ?? "(page unavailable)")
1232
- : actionType === "find_in_page"
1233
- ? action.pattern && action.url
1234
- ? `"${action.pattern}" in ${action.url}`
1235
- : (action.pattern ?? action.url ?? "(page unavailable)")
1236
- : // Codex often emits the live card before action.query/queries land;
1237
- // don't flash the scary unavailable copy while still searching.
1238
- (searchQuery ?? (running ? "…" : "(query unavailable)"));
1239
- const variants = queries.length > 1 ? ` +${queries.length - 1} variants` : "";
1240
- const runningTitle =
1241
- actionType === "open_page"
1242
- ? "Opening web page"
1243
- : actionType === "find_in_page"
1244
- ? "Searching within page"
1245
- : "Searching the web";
1246
- const completedTitle =
1247
- actionType === "open_page"
1248
- ? "Opened web page"
1249
- : actionType === "find_in_page"
1250
- ? "Searched within page"
1251
- : "Searched the web";
1252
- // web_search may surface a results array on the output when the host enriches it.
1253
- // Filter out null/undefined/non-object entries before casting: host-provided
1254
- // data is untrusted and a null element would throw on result.title access.
1255
- const rawResults = (item.output as { results?: unknown } | undefined)?.results;
1256
- const results = Array.isArray(rawResults)
1257
- ? (rawResults as unknown[]).filter((r): r is WebSearchResult => !!r && typeof r === "object")
1258
- : undefined;
1259
- const resultOccurrences = new Map<string, number>();
1260
- const keyedResults = results?.map((result) => {
1261
- const contentKey = `${result.domain}\u0000${result.title}\u0000${result.snippet}`;
1262
- const occurrence = (resultOccurrences.get(contentKey) ?? 0) + 1;
1263
- resultOccurrences.set(contentKey, occurrence);
1264
- return { key: `${contentKey}\u0000${occurrence}`, result };
1265
- });
1266
-
1267
- if (running) {
1268
- return (
1269
- <ActivityDisclosure
1270
- icon={<SearchIcon className={ICON_SIZE} />}
1271
- iconTone="running"
1272
- title={runningTitle}
1273
- running
1274
- preview={<RunningPreview>{`${query}${variants}`}</RunningPreview>}
1275
- >
1276
- <BodyNote>searching… results fold into the model context (no output event).</BodyNote>
1277
- </ActivityDisclosure>
1278
- );
1279
- }
1280
-
1281
- return (
1282
- <ActivityDisclosure
1283
- icon={<SearchIcon className={ICON_SIZE} />}
1284
- iconTone="muted"
1285
- title={completedTitle}
1286
- preview={`${query}${variants}`}
1287
- failed={item.status === "failed"}
1288
- cancelled={item.status === "cancelled"}
1260
+ )
1261
+ }
1289
1262
  >
1290
- {keyedResults && keyedResults.length ? (
1291
- <ul className="flex flex-col gap-2">
1292
- {keyedResults.map(({ key, result }) => (
1293
- <li key={key} className="flex gap-2.5">
1294
- <GlobeIcon className="mt-0.5 size-3.5 shrink-0 text-og-fg-subtle" />
1295
- <div className="min-w-0">
1296
- <p className="truncate text-og-base text-og-fg">
1297
- {result.title} <span className="text-og-fg-subtle">{result.domain}</span>
1298
- </p>
1299
- <p className="text-og-sm leading-5 text-og-fg-muted">{result.snippet}</p>
1300
- </div>
1301
- </li>
1302
- ))}
1303
- </ul>
1263
+ {state.kind === "ready" ? (
1264
+ <ScreenshotFigure
1265
+ src={state.url}
1266
+ caption={caption}
1267
+ alt={caption}
1268
+ expandLabel="Expand generated image"
1269
+ lightboxLabel="Generated image"
1270
+ />
1271
+ ) : state.kind === "loading" ? (
1272
+ <BodyNote>Loading the generated image…</BodyNote>
1273
+ ) : state.kind === "unavailable" ? (
1274
+ <BodyNote>Image {state.label}.</BodyNote>
1304
1275
  ) : (
1305
- <BodyNote>results folded into model context — no list available.</BodyNote>
1276
+ <ImageRetrievalError retry={state.retry} />
1306
1277
  )}
1278
+ <BodyNote>
1279
+ {dimensions.width}×{dimensions.height} · {receipt.sandboxPath}
1280
+ </BodyNote>
1307
1281
  </ActivityDisclosure>
1308
1282
  );
1309
1283
  }
1310
1284
 
1285
+ function retainedImageFilename(artifact: RetainedArtifactReference): string {
1286
+ const extension =
1287
+ artifact.contentType === "image/jpeg"
1288
+ ? "jpg"
1289
+ : artifact.contentType === "image/webp"
1290
+ ? "webp"
1291
+ : "png";
1292
+ return `${artifact.kind}-${artifact.artifactId}.${extension}`;
1293
+ }
1294
+
1295
+ /* ---- web_search ------------------------------------------------------------ */
1296
+
1311
1297
  /* ---- view_image ------------------------------------------------------------ */
1312
1298
 
1313
1299
  const VIEW_IMAGE_ERRORS = [
@@ -1331,7 +1317,7 @@ function ViewImageRenderer({ item, loadRetainedScreenshot }: ToolRendererProps)
1331
1317
  <ActivityDisclosure
1332
1318
  icon={<ImageIcon className={ICON_SIZE} />}
1333
1319
  iconTone="running"
1334
- title={`View ${basename(path)}`}
1320
+ title={`View ${pathBasename(path)}`}
1335
1321
  running
1336
1322
  preview={<RunningPreview>reading…</RunningPreview>}
1337
1323
  media={<MediaSkeleton />}
@@ -1345,7 +1331,7 @@ function ViewImageRenderer({ item, loadRetainedScreenshot }: ToolRendererProps)
1345
1331
  const viewCancelled = item.status === "cancelled";
1346
1332
 
1347
1333
  if (retained) {
1348
- const title = `Viewed ${basename(path)}`;
1334
+ const title = `Viewed ${pathBasename(path)}`;
1349
1335
  if (!retained.available) {
1350
1336
  const state =
1351
1337
  retained.reason === "expired" || retained.reason === "deleted"
@@ -1390,7 +1376,7 @@ function ViewImageRenderer({ item, loadRetainedScreenshot }: ToolRendererProps)
1390
1376
  <ActivityDisclosure
1391
1377
  icon={<ImageIcon className={ICON_SIZE} />}
1392
1378
  iconTone="failed"
1393
- title={`View ${basename(path)}`}
1379
+ title={`View ${pathBasename(path)}`}
1394
1380
  chip={{ tone: "bad", text: tooBig ? "too large" : "error" }}
1395
1381
  preview={text}
1396
1382
  >
@@ -1403,7 +1389,7 @@ function ViewImageRenderer({ item, loadRetainedScreenshot }: ToolRendererProps)
1403
1389
  <ActivityDisclosure
1404
1390
  icon={<ImageIcon className={ICON_SIZE} />}
1405
1391
  iconTone={viewFailed ? "failed" : "muted"}
1406
- title={`Viewed ${basename(path)}`}
1392
+ title={`Viewed ${pathBasename(path)}`}
1407
1393
  failed={viewFailed}
1408
1394
  cancelled={viewCancelled}
1409
1395
  preview={path}
@@ -1417,7 +1403,7 @@ function ViewImageRenderer({ item, loadRetainedScreenshot }: ToolRendererProps)
1417
1403
  <ActivityDisclosure
1418
1404
  icon={<ImageIcon className={ICON_SIZE} />}
1419
1405
  iconTone={viewFailed ? "failed" : "muted"}
1420
- title={`Viewed ${basename(path)} · image omitted · not retained`}
1406
+ title={`Viewed ${pathBasename(path)} · image omitted · not retained`}
1421
1407
  failed={viewFailed}
1422
1408
  cancelled={viewCancelled}
1423
1409
  preview="inline image omitted · not retained"
@@ -1435,7 +1421,7 @@ function ViewImageRenderer({ item, loadRetainedScreenshot }: ToolRendererProps)
1435
1421
  <ActivityDisclosure
1436
1422
  icon={<ImageIcon className={ICON_SIZE} />}
1437
1423
  iconTone={viewFailed ? "failed" : "muted"}
1438
- title={`Viewed ${basename(path)}`}
1424
+ title={`Viewed ${pathBasename(path)}`}
1439
1425
  failed={viewFailed}
1440
1426
  cancelled={viewCancelled}
1441
1427
  preview="(no image)"
@@ -1455,7 +1441,7 @@ function ViewImageRenderer({ item, loadRetainedScreenshot }: ToolRendererProps)
1455
1441
  <ActivityDisclosure
1456
1442
  icon={<ImageIcon className={ICON_SIZE} />}
1457
1443
  iconTone={viewFailed ? "failed" : "accent"}
1458
- title={`Viewed ${basename(path)}`}
1444
+ title={`Viewed ${pathBasename(path)}`}
1459
1445
  failed={viewFailed}
1460
1446
  cancelled={viewCancelled}
1461
1447
  media={<Thumbnail src={text} caption={path} alt={path} />}
@@ -1525,123 +1511,6 @@ function SecretSetRenderer({ item }: ToolRendererProps) {
1525
1511
 
1526
1512
  /* ---- tool_search (progressive MCP disclosure) ------------------------------ */
1527
1513
 
1528
- type DisclosedTool = {
1529
- /** Full wire name (`server__leaf` or bare). */
1530
- name: string;
1531
- /** Server / namespace prefix before `__`, when present. */
1532
- source: string | null;
1533
- /** Leaf tool name after `__`. */
1534
- leaf: string;
1535
- };
1536
-
1537
- function splitToolWireName(name: string): DisclosedTool {
1538
- const boundary = name.indexOf("__");
1539
- if (boundary <= 0) {
1540
- return { name, source: null, leaf: name };
1541
- }
1542
- return {
1543
- name,
1544
- source: name.slice(0, boundary),
1545
- leaf: name.slice(boundary + 2),
1546
- };
1547
- }
1548
-
1549
- /** Capability query from live tool_search args (object or JSON string). */
1550
- function toolSearchQuery(item: ToolRendererProps["item"]): string {
1551
- const fromArgs = parseToolArgs(item.arguments);
1552
- if (typeof fromArgs.query === "string" && fromArgs.query.trim()) {
1553
- return fromArgs.query.trim();
1554
- }
1555
- const raw = item.raw;
1556
- if (raw && typeof raw === "object") {
1557
- const rawArgs = (raw as { arguments?: unknown }).arguments;
1558
- if (typeof rawArgs === "string" && rawArgs.trim()) {
1559
- const parsed = tryParseJson(rawArgs);
1560
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1561
- const query = (parsed as { query?: unknown }).query;
1562
- if (typeof query === "string" && query.trim()) {
1563
- return query.trim();
1564
- }
1565
- }
1566
- } else if (rawArgs && typeof rawArgs === "object" && !Array.isArray(rawArgs)) {
1567
- const query = (rawArgs as { query?: unknown }).query;
1568
- if (typeof query === "string" && query.trim()) {
1569
- return query.trim();
1570
- }
1571
- }
1572
- }
1573
- return "";
1574
- }
1575
-
1576
- /**
1577
- * Parse disclosed tools from the runtime event shape.
1578
- * `normalizeSdkEvent` collapses `tool_search_output.tools[]` into text:
1579
- * "Disclosed tools: a, b" | "No matching tools found."
1580
- * Also accept a structured `tools` array when a host/enricher preserves it.
1581
- */
1582
- function parseDisclosedTools(output: unknown): DisclosedTool[] | null {
1583
- if (output && typeof output === "object" && !Array.isArray(output)) {
1584
- const tools = (output as { tools?: unknown }).tools;
1585
- if (Array.isArray(tools)) {
1586
- return tools
1587
- .map((tool) => {
1588
- if (typeof tool === "string" && tool.trim()) {
1589
- return splitToolWireName(tool.trim());
1590
- }
1591
- if (
1592
- tool &&
1593
- typeof tool === "object" &&
1594
- typeof (tool as { name?: unknown }).name === "string"
1595
- ) {
1596
- const name = (tool as { name: string }).name.trim();
1597
- return name ? splitToolWireName(name) : null;
1598
- }
1599
- return null;
1600
- })
1601
- .filter((tool): tool is DisclosedTool => tool != null);
1602
- }
1603
- }
1604
-
1605
- const { text } = unwrapMcpOutput(output);
1606
- const trimmed = text.trim();
1607
- if (!trimmed) {
1608
- return null;
1609
- }
1610
- if (/^no matching tools found\.?$/i.test(trimmed)) {
1611
- return [];
1612
- }
1613
- const disclosed = trimmed.match(/^disclosed tools:\s*(.+)$/i);
1614
- if (disclosed?.[1]) {
1615
- return disclosed[1]
1616
- .split(",")
1617
- .map((part) => part.trim())
1618
- .filter(Boolean)
1619
- .map(splitToolWireName);
1620
- }
1621
- const parsed = tryParseJson(trimmed);
1622
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1623
- return parseDisclosedTools(parsed);
1624
- }
1625
- return null;
1626
- }
1627
-
1628
- function toolSearchPreview(tools: DisclosedTool[] | null, cancelled: boolean): string | undefined {
1629
- if (cancelled) {
1630
- return undefined;
1631
- }
1632
- if (!tools) {
1633
- return "Done";
1634
- }
1635
- if (tools.length === 0) {
1636
- return "No matches";
1637
- }
1638
- if (tools.length === 1) {
1639
- return tools[0]!.leaf;
1640
- }
1641
- const head = tools[0]!.leaf;
1642
- return `${tools.length} tools · ${truncatePreview(head, 28)}`;
1643
- }
1644
-
1645
1514
  function ToolSearchRenderer({ item }: ToolRendererProps) {
1646
1515
  const query = toolSearchQuery(item);
1647
1516
  const icon = <PackageSearchIcon className={ICON_SIZE} />;
@@ -1726,7 +1595,9 @@ function ToolSearchRenderer({ item }: ToolRendererProps) {
1726
1595
  function DocsSearchRenderer({ item }: ToolRendererProps) {
1727
1596
  const args = parseToolArgs(item.arguments);
1728
1597
  const query = typeof args.query === "string" ? args.query.trim() : "";
1729
- const title = query ? `Search “${truncatePreview(query, 48)}”` : toolDisplayName(item.name);
1598
+ const title = query
1599
+ ? `Search “${truncatePreview(query, 48)}”`
1600
+ : toolDisplayName(item.name, item.display);
1730
1601
  const running = item.status === "running";
1731
1602
 
1732
1603
  if (running) {
@@ -1800,7 +1671,7 @@ function DocsSearchRenderer({ item }: ToolRendererProps) {
1800
1671
  function SetSessionTitleRenderer({ item }: ToolRendererProps) {
1801
1672
  const args = parseToolArgs(item.arguments);
1802
1673
  const titleArg = typeof args.title === "string" ? args.title.trim() : "";
1803
- const display = toolDisplayName(item.name);
1674
+ const display = toolDisplayName(item.name, item.display);
1804
1675
  const previewTitle = titleArg ? truncatePreview(titleArg, 72) : "";
1805
1676
  const icon = <MessagesSquareIcon className={ICON_SIZE} />;
1806
1677
 
@@ -1866,43 +1737,6 @@ function SetSessionTitleRenderer({ item }: ToolRendererProps) {
1866
1737
  );
1867
1738
  }
1868
1739
 
1869
- type SearchHit = { title: string; snippet: string };
1870
-
1871
- function parseSearchHits(outText: string): SearchHit[] | null {
1872
- const parsed = tryParseJson(outText);
1873
- if (parsed == null) {
1874
- return null;
1875
- }
1876
- const list = Array.isArray(parsed)
1877
- ? parsed
1878
- : parsed &&
1879
- typeof parsed === "object" &&
1880
- Array.isArray((parsed as { results?: unknown }).results)
1881
- ? (parsed as { results: unknown[] }).results
1882
- : parsed && typeof parsed === "object" && Array.isArray((parsed as { hits?: unknown }).hits)
1883
- ? (parsed as { hits: unknown[] }).hits
1884
- : null;
1885
- if (!list) {
1886
- return null;
1887
- }
1888
- return list.map((row) => {
1889
- const r = row && typeof row === "object" ? (row as Record<string, unknown>) : {};
1890
- const title =
1891
- (typeof r.title === "string" && r.title) ||
1892
- (typeof r.name === "string" && r.name) ||
1893
- (typeof r.documentTitle === "string" && r.documentTitle) ||
1894
- (typeof r.path === "string" && r.path) ||
1895
- (typeof r.id === "string" && r.id) ||
1896
- "Result";
1897
- const snippet =
1898
- (typeof r.snippet === "string" && r.snippet) ||
1899
- (typeof r.text === "string" && r.text) ||
1900
- (typeof r.content === "string" && r.content) ||
1901
- "";
1902
- return { title, snippet: truncatePreview(snippet, 160) };
1903
- });
1904
- }
1905
-
1906
1740
  /* ---- company memory propose (docs MCP) ------------------------------------- */
1907
1741
 
1908
1742
  function MemoryProposeRenderer({ item }: ToolRendererProps) {
@@ -1957,177 +1791,8 @@ function MemoryProposeRenderer({ item }: ToolRendererProps) {
1957
1791
 
1958
1792
  /* ---- request_human_input --------------------------------------------------- */
1959
1793
 
1960
- function askToolPreview(args: unknown): string | null {
1961
- const record = args && typeof args === "object" ? (args as Record<string, unknown>) : null;
1962
- const questions = Array.isArray(record?.questions) ? record.questions : null;
1963
- if (!questions || questions.length === 0) {
1964
- return null;
1965
- }
1966
- const first = questions[0];
1967
- if (!first || typeof first !== "object") {
1968
- return null;
1969
- }
1970
- const q = first as Record<string, unknown>;
1971
- const text =
1972
- typeof q.label === "string" && q.label.trim()
1973
- ? q.label.trim()
1974
- : typeof q.prompt === "string" && q.prompt.trim()
1975
- ? q.prompt.trim()
1976
- : null;
1977
- if (!text) {
1978
- return null;
1979
- }
1980
- const preview = truncatePreview(text, 90);
1981
- return questions.length > 1 ? `${preview} · ${questions.length} questions` : preview;
1982
- }
1983
-
1984
- function AskRenderer({ item }: ToolRendererProps) {
1985
- const args = parseToolArgs(item.arguments);
1986
- const preview = askToolPreview(args);
1987
- const icon = <MessageCircleQuestionIcon className={ICON_SIZE} />;
1988
- const title = "Ask";
1989
-
1990
- if (item.status === "running") {
1991
- return (
1992
- <ActivityDisclosure
1993
- icon={icon}
1994
- iconTone="running"
1995
- title={title}
1996
- running
1997
- preview={
1998
- preview ? (
1999
- <RunningPreview>{preview}</RunningPreview>
2000
- ) : (
2001
- <RunningPreview>Waiting…</RunningPreview>
2002
- )
2003
- }
2004
- >
2005
- <PayloadBlock label="Arguments" value={args} />
2006
- </ActivityDisclosure>
2007
- );
2008
- }
2009
-
2010
- const { text: outText, isError } = unwrapMcpOutput(item.output);
2011
- if ((isError || item.status === "failed") && item.status !== "cancelled") {
2012
- return (
2013
- <ActivityDisclosure
2014
- icon={icon}
2015
- iconTone="failed"
2016
- title={title}
2017
- chip={{ tone: "bad", text: "error" }}
2018
- preview={truncatePreview(outText, 80) || preview || "Error"}
2019
- >
2020
- <PayloadBlock label="Arguments" value={args} />
2021
- <PayloadBlock label="Error" value={outText} failed />
2022
- </ActivityDisclosure>
2023
- );
2024
- }
2025
-
2026
- return (
2027
- <ActivityDisclosure
2028
- icon={icon}
2029
- iconTone="muted"
2030
- title={title}
2031
- cancelled={item.status === "cancelled"}
2032
- preview={item.status === "cancelled" ? undefined : (preview ?? undefined)}
2033
- >
2034
- <PayloadBlock label="Arguments" value={args} />
2035
- {outText ? <PayloadBlock label="Result" value={outText} /> : null}
2036
- </ActivityDisclosure>
2037
- );
2038
- }
2039
-
2040
1794
  /* ---- run_on ---------------------------------------------------------------- */
2041
1795
 
2042
- function runOnTargetName(output: unknown): string | null {
2043
- const { text } = unwrapMcpOutput(output);
2044
- const parsed = tryParseJson(text);
2045
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
2046
- const name = (parsed as { targetName?: unknown }).targetName;
2047
- if (typeof name === "string" && name.trim()) {
2048
- return name.trim();
2049
- }
2050
- }
2051
- return null;
2052
- }
2053
-
2054
- function runOnOpPreview(args: Record<string, unknown>): string | null {
2055
- const op = args.op;
2056
- if (!op || typeof op !== "object" || Array.isArray(op)) {
2057
- return null;
2058
- }
2059
- const record = op as Record<string, unknown>;
2060
- if (record.kind === "exec" && typeof record.cmd === "string" && record.cmd.trim()) {
2061
- return `$ ${record.cmd.trim()}`;
2062
- }
2063
- if (
2064
- (record.kind === "read" || record.kind === "write") &&
2065
- typeof record.path === "string" &&
2066
- record.path.trim()
2067
- ) {
2068
- return truncatePreview(record.path.trim(), 72);
2069
- }
2070
- return null;
2071
- }
2072
-
2073
- function RunOnRenderer({ item }: ToolRendererProps) {
2074
- const parsedArgs = parseToolArgs(item.arguments);
2075
- const args = parsedArgs;
2076
- const targetName = runOnTargetName(item.output);
2077
- const title = targetName ? `Run on ${targetName}` : "Run on";
2078
- const opPreview = runOnOpPreview(parsedArgs);
2079
- const icon = <ServerIcon className={ICON_SIZE} />;
2080
-
2081
- if (item.status === "running") {
2082
- return (
2083
- <ActivityDisclosure
2084
- icon={icon}
2085
- iconTone="running"
2086
- title={title}
2087
- running
2088
- preview={
2089
- opPreview ? (
2090
- <RunningPreview>{opPreview}</RunningPreview>
2091
- ) : (
2092
- <RunningPreview>Running…</RunningPreview>
2093
- )
2094
- }
2095
- >
2096
- <PayloadBlock label="Arguments" value={args} />
2097
- </ActivityDisclosure>
2098
- );
2099
- }
2100
-
2101
- const { text: outText, isError } = unwrapMcpOutput(item.output);
2102
- if ((isError || item.status === "failed") && item.status !== "cancelled") {
2103
- return (
2104
- <ActivityDisclosure
2105
- icon={icon}
2106
- iconTone="failed"
2107
- title={title}
2108
- chip={{ tone: "bad", text: "error" }}
2109
- preview={truncatePreview(outText, 80) || opPreview || "Error"}
2110
- >
2111
- <PayloadBlock label="Arguments" value={args} />
2112
- <PayloadBlock label="Error" value={outText} failed />
2113
- </ActivityDisclosure>
2114
- );
2115
- }
2116
-
2117
- return (
2118
- <ActivityDisclosure
2119
- icon={icon}
2120
- iconTone="muted"
2121
- title={title}
2122
- cancelled={item.status === "cancelled"}
2123
- preview={item.status === "cancelled" ? undefined : (opPreview ?? undefined)}
2124
- >
2125
- <PayloadBlock label="Arguments" value={args} />
2126
- {outText ? <PayloadBlock label="Result" value={outText} /> : null}
2127
- </ActivityDisclosure>
2128
- );
2129
- }
2130
-
2131
1796
  /* ---- generic fallback (first-party MCP, external MCP, unknown) ------------- */
2132
1797
 
2133
1798
  /**
@@ -2135,150 +1800,127 @@ function RunOnRenderer({ item }: ToolRendererProps) {
2135
1800
  * status preview (Running… / Done / error snippet). No argument-field sniffing —
2136
1801
  * JSON stays in the expandable body only.
2137
1802
  */
2138
- function GenericRenderer({ item }: ToolRendererProps) {
2139
- const running = item.status === "running";
2140
- const args = parseToolArgs(item.arguments);
2141
- const display = toolDisplayName(item.name);
2142
- const icon = <GenericToolIcon name={item.name} />;
2143
- // Goal tools: surface the objective text on the collapsed row so the in-cluster
2144
- // tool replaces the old breakaway GoalRow pill without losing the gist.
2145
- const goalPreview = goalToolPreview(item.name, args);
2146
-
2147
- if (running) {
2148
- return (
2149
- <ActivityDisclosure
2150
- icon={icon}
2151
- iconTone="running"
2152
- title={display}
2153
- running
2154
- preview={goalPreview ?? <RunningPreview>Running…</RunningPreview>}
2155
- >
2156
- <PayloadBlock label="Arguments" value={args} />
2157
- </ActivityDisclosure>
2158
- );
2159
- }
1803
+ function GenericRenderer(props: ToolRendererProps) {
1804
+ const reviewed = useHasToolReview(props.item.callId);
1805
+ if (reviewed && props.item.callId) return <ReviewedGenericRenderer {...props} />;
1806
+ return <UnreviewedGenericRenderer {...props} />;
1807
+ }
2160
1808
 
2161
- const { text: outText, isError } = unwrapMcpOutput(item.output);
2162
- // Cancelled is NOT an error — a user-cancelled tool should not surface the red
2163
- // error chip even if the output payload carries an isError flag (the error may be
2164
- // a consequence of the cancellation, not the tool's own failure).
2165
- if ((isError || item.status === "failed") && item.status !== "cancelled") {
2166
- return (
2167
- <ActivityDisclosure
2168
- icon={icon}
2169
- iconTone="failed"
2170
- title={display}
2171
- chip={{ tone: "bad", text: "error" }}
2172
- preview={truncatePreview(outText, 80) || "Error"}
2173
- >
2174
- <PayloadBlock label="Arguments" value={args} />
2175
- <PayloadBlock label="Error" value={outText} failed />
2176
- </ActivityDisclosure>
2177
- );
2178
- }
1809
+ /** Settled review states earn one quiet gutter word; success and waiting stay chip-free. */
1810
+ const REVIEW_CHIP: Partial<Record<ToolReviewStatus, DisclosureChip>> = {
1811
+ rejected: { tone: "interrupted", text: "declined" },
1812
+ cancelled: { tone: "interrupted", text: "not run" },
1813
+ expired: { tone: "interrupted", text: "expired" },
1814
+ stale: { tone: "interrupted", text: "not run" },
1815
+ revoked: { tone: "interrupted", text: "not run" },
1816
+ blocked: { tone: "interrupted", text: "blocked" },
1817
+ unknown: { tone: "bad", text: "outcome unknown" },
1818
+ partial: { tone: "bad", text: "partly done" },
1819
+ failed: { tone: "bad", text: "failed" },
1820
+ };
2179
1821
 
1822
+ /**
1823
+ * A call that went through approval keeps the ordinary row shape, titled with
1824
+ * what was approved. The expanded body is the saved review plus the result.
1825
+ */
1826
+ function ReviewedGenericRenderer(props: ToolRendererProps) {
1827
+ const { item } = props;
1828
+ const { review, onViewDetails } = useRecordedToolReview(item.callId);
1829
+ const logo = useToolConnectorLogo(item.display);
1830
+ if (!review) return <UnreviewedGenericRenderer {...props} />;
1831
+ const ReviewIcon = toolIcon(genericToolIconKind(item.name));
1832
+ const icon = <ConnectorLogoIcon src={logo} fallback={<ReviewIcon className={ICON_SIZE} />} />;
1833
+ const waiting = review.status === "pending";
1834
+ const running =
1835
+ !waiting &&
1836
+ (review.status === "executing" || review.status === "approved") &&
1837
+ item.status === "running";
1838
+ const { text: outText, isError } = unwrapMcpOutput(retainedToolResultImages(item.output).output);
1839
+ const chip =
1840
+ REVIEW_CHIP[review.status] ??
1841
+ (isError && !running && !waiting ? ({ tone: "bad", text: "error" } as const) : undefined);
2180
1842
  return (
2181
1843
  <ActivityDisclosure
2182
1844
  icon={icon}
2183
- iconTone="muted"
2184
- title={display}
2185
- cancelled={item.status === "cancelled"}
2186
- preview={item.status === "cancelled" ? undefined : (goalPreview ?? "Done")}
1845
+ iconTone={chip?.tone === "bad" ? "failed" : waiting ? "accent" : "muted"}
1846
+ title={review.title}
1847
+ running={running}
1848
+ chip={chip}
1849
+ preview={
1850
+ waiting ? (
1851
+ "Waiting for your approval"
1852
+ ) : running ? (
1853
+ <RunningPreview>Running…</RunningPreview>
1854
+ ) : item.display?.connector ? (
1855
+ // The logo names the connector; only a second account needs words.
1856
+ (item.display.accountLabel ?? undefined)
1857
+ ) : (
1858
+ (review.accountLabel ?? undefined)
1859
+ )
1860
+ }
2187
1861
  >
2188
- <PayloadBlock label="Arguments" value={args} />
2189
- <PayloadBlock label="Result" value={outText} />
1862
+ <div className="py-1" data-approval-id={item.callId} data-review-origin="history">
1863
+ <ToolActionReviewCard
1864
+ bare
1865
+ review={{ ...review, availableActions: [] }}
1866
+ onViewDetails={onViewDetails}
1867
+ />
1868
+ </div>
1869
+ {outText ? <PayloadBlock label="Result" value={outText} failed={isError} /> : null}
2190
1870
  </ActivityDisclosure>
2191
1871
  );
2192
1872
  }
2193
1873
 
2194
- function goalToolPreview(name: string, args: unknown): string | null {
2195
- const leaf = mcpToolLeaf(name);
2196
- if (
2197
- leaf !== "goal_set" &&
2198
- leaf !== "goal_update" &&
2199
- leaf !== "goal_complete" &&
2200
- leaf !== "goal_pause" &&
2201
- leaf !== "goal_wait"
2202
- ) {
2203
- return null;
2204
- }
2205
- const record = args && typeof args === "object" ? (args as Record<string, unknown>) : null;
2206
- if (!record) {
2207
- return null;
2208
- }
2209
- const text =
2210
- typeof record.text === "string"
2211
- ? record.text
2212
- : typeof record.evidence === "string"
2213
- ? record.evidence
2214
- : typeof record.rationale === "string"
2215
- ? record.rationale
2216
- : typeof record.reason === "string"
2217
- ? record.reason
2218
- : typeof record.progressNote === "string"
2219
- ? record.progressNote
2220
- : null;
2221
- return text ? truncatePreview(text, 90) : null;
2222
- }
2223
-
2224
- function truncatePreview(text: string, max: number): string {
2225
- const cleaned = text.replace(/\s+/g, " ").trim();
2226
- if (!cleaned) {
2227
- return "";
2228
- }
2229
- return cleaned.length > max ? `${cleaned.slice(0, max - 1)}…` : cleaned;
2230
- }
1874
+ /* ---- the default registry -------------------------------------------------- */
2231
1875
 
2232
- /** Explicit first-party leaf/prefix → canonical product icons (match nav/pages). */
2233
- function GenericToolIcon({ name }: { name: string }) {
2234
- const leaf = mcpToolLeaf(name);
2235
- const Icon =
2236
- leaf === "request_human_input"
2237
- ? MessageCircleQuestionIcon
2238
- : leaf.startsWith("goal_")
2239
- ? TargetIcon
2240
- : leaf.startsWith("memory_") ||
2241
- leaf === "preference_registry_summary" ||
2242
- leaf === "preference_registry_get"
2243
- ? BrainCircuitIcon
2244
- : leaf.startsWith("session_") ||
2245
- leaf === "sessions_list" ||
2246
- leaf === "set_session_title" ||
2247
- leaf === "set_other_session_title"
2248
- ? MessagesSquareIcon
2249
- : leaf.startsWith("sandbox") || leaf === "sandboxes_list" || leaf === "run_on"
2250
- ? ServerIcon
2251
- : leaf.startsWith("rig_")
2252
- ? ServerCogIcon
2253
- : leaf.startsWith("scheduled_")
2254
- ? CalendarClockIcon
2255
- : leaf.startsWith("artifacts_")
2256
- ? PanelsTopLeftIcon
2257
- : leaf.startsWith("social_")
2258
- ? Share2Icon
2259
- : leaf.startsWith("slack_")
2260
- ? MessageSquareIcon
2261
- : leaf.startsWith("github_")
2262
- ? FolderGitIcon
2263
- : leaf.startsWith("variable_")
2264
- ? BoxIcon
2265
- : leaf.startsWith("environment_")
2266
- ? KeyRoundIcon
2267
- : leaf.includes("document") ||
2268
- leaf.includes("knowledge") ||
2269
- leaf === "list_document_bases"
2270
- ? FileSearchIcon
2271
- : leaf === "tool_search"
2272
- ? PackageSearchIcon
2273
- : leaf === "load_skill"
2274
- ? PlugIcon
2275
- : WrenchIcon;
2276
- return <Icon className={ICON_SIZE} />;
1876
+ function KnowledgeSaveRenderer({ item }: ToolRendererProps) {
1877
+ const output = unwrapMcpOutput(item.output);
1878
+ const parsed = tryParseJson(output.text);
1879
+ if (item.status === "running" || output.isError || !parsed || typeof parsed !== "object")
1880
+ return <GenericRenderer item={item} />;
1881
+ const value = parsed as Record<string, unknown>;
1882
+ const receipt = (value.status === "retained" ? value.receipt : value) as
1883
+ | Record<string, unknown>
1884
+ | undefined;
1885
+ if (
1886
+ !receipt ||
1887
+ typeof receipt.entryId !== "string" ||
1888
+ !["published", "pending", "rejected", "archived"].includes(String(receipt.outcome))
1889
+ )
1890
+ return <GenericRenderer item={item} />;
1891
+ const args = parseToolArgs(item.arguments);
1892
+ const entry = args.entry as Record<string, unknown> | undefined;
1893
+ return (
1894
+ <KnowledgeReceiptRow
1895
+ outcome={receipt.outcome as "published" | "pending" | "rejected" | "archived"}
1896
+ entryId={receipt.entryId}
1897
+ title={
1898
+ typeof entry?.title === "string"
1899
+ ? entry.title
1900
+ : typeof value.filename === "string"
1901
+ ? value.filename
1902
+ : undefined
1903
+ }
1904
+ source={value.status === "retained" || value.retained === true}
1905
+ />
1906
+ );
2277
1907
  }
2278
1908
 
2279
- /* ---- the default registry -------------------------------------------------- */
2280
-
2281
1909
  const BASE_ENTRIES: ToolRegistryEntry[] = [
1910
+ ...[
1911
+ "knowledge_save",
1912
+ "knowledge_archive",
1913
+ "knowledge_retain_file",
1914
+ "knowledge_retain_message",
1915
+ "task_note_promote_knowledge",
1916
+ ].flatMap((name) =>
1917
+ [name, `opengeni__${name}`, `mcp__opengeni__${name}`].map((trustedName) => ({
1918
+ match: "name" as const,
1919
+ name: trustedName,
1920
+ matchPrefixedLeaf: false,
1921
+ render: KnowledgeSaveRenderer,
1922
+ })),
1923
+ ),
2282
1924
  // Provider-native items carry `raw.type` on the wire — this is their source of
2283
1925
  // truth and is consulted first by the registry.
2284
1926
  { match: "rawType", type: "apply_patch_call", render: ApplyPatchRenderer },
@@ -2292,29 +1934,84 @@ const BASE_ENTRIES: ToolRegistryEntry[] = [
2292
1934
  { match: "name", name: "apply_patch_call", render: ApplyPatchRenderer },
2293
1935
  { match: "name", name: "apply_patch", render: ApplyPatchRenderer },
2294
1936
  { match: "name", name: "computer_call", render: ComputerCallRenderer },
1937
+ {
1938
+ match: "name",
1939
+ name: "browser_screenshot",
1940
+ render: BrowserScreenshotRenderer,
1941
+ },
1942
+ { match: "name", name: "browser_observe", render: BrowserScreenshotRenderer },
1943
+ { match: "name", name: "browser_act", render: BrowserScreenshotRenderer },
2295
1944
  // Function-mode computer tools (codex / chat-wire transports).
2296
1945
  { match: "name", name: "computer_screenshot", render: ComputerCallRenderer },
2297
1946
  { match: "name", name: "computer_click", render: ComputerCallRenderer },
2298
- { match: "name", name: "computer_double_click", render: ComputerCallRenderer },
1947
+ {
1948
+ match: "name",
1949
+ name: "computer_double_click",
1950
+ render: ComputerCallRenderer,
1951
+ },
2299
1952
  { match: "name", name: "computer_move", render: ComputerCallRenderer },
2300
1953
  { match: "name", name: "computer_scroll", render: ComputerCallRenderer },
2301
1954
  { match: "name", name: "computer_type", render: ComputerCallRenderer },
2302
1955
  { match: "name", name: "computer_keypress", render: ComputerCallRenderer },
2303
1956
  { match: "name", name: "computer_drag", render: ComputerCallRenderer },
2304
1957
  { match: "name", name: "web_search_call", render: WebSearchRenderer },
2305
- { match: "name", name: "image_generation_call", render: GeneratedImageRenderer },
1958
+ {
1959
+ match: "name",
1960
+ name: "image_generation_call",
1961
+ render: GeneratedImageRenderer,
1962
+ },
2306
1963
  { match: "name", name: "generate_image", render: GeneratedImageRenderer },
2307
1964
  { match: "name", name: "generate_video", render: GeneratedVideoRenderer },
2308
1965
  { match: "name", name: "tool_search", render: ToolSearchRenderer },
2309
1966
  { match: "name", name: "view_image", render: ViewImageRenderer },
2310
- { match: "name", name: "sandbox_file_publish", render: SandboxFilePublishRenderer },
2311
- { match: "name", name: "environment_set_variable", render: SecretSetRenderer },
2312
- { match: "name", name: "variable_set_set_variable", render: SecretSetRenderer },
1967
+ {
1968
+ match: "name",
1969
+ name: "sandbox_file_publish",
1970
+ render: SandboxFilePublishRenderer,
1971
+ },
1972
+ {
1973
+ match: "name",
1974
+ name: "artifacts_create",
1975
+ render: SiteArtifactRenderer,
1976
+ matchPrefixedLeaf: false,
1977
+ },
1978
+ {
1979
+ match: "name",
1980
+ name: "artifacts_publish",
1981
+ render: SiteArtifactRenderer,
1982
+ matchPrefixedLeaf: false,
1983
+ },
1984
+ {
1985
+ match: "name",
1986
+ name: "opengeni__artifacts_create",
1987
+ render: SiteArtifactRenderer,
1988
+ matchPrefixedLeaf: false,
1989
+ },
1990
+ {
1991
+ match: "name",
1992
+ name: "opengeni__artifacts_publish",
1993
+ render: SiteArtifactRenderer,
1994
+ matchPrefixedLeaf: false,
1995
+ },
1996
+ {
1997
+ match: "name",
1998
+ name: "environment_set_variable",
1999
+ render: SecretSetRenderer,
2000
+ },
2001
+ {
2002
+ match: "name",
2003
+ name: "variable_set_set_variable",
2004
+ render: SecretSetRenderer,
2005
+ },
2313
2006
  { match: "name", name: "search_documents", render: DocsSearchRenderer },
2314
2007
  { match: "name", name: "knowledge_search", render: DocsSearchRenderer },
2315
2008
  { match: "name", name: "memory_propose", render: MemoryProposeRenderer },
2316
2009
  { match: "name", name: "set_session_title", render: SetSessionTitleRenderer },
2317
- { match: "name", name: "set_other_session_title", render: SetSessionTitleRenderer },
2010
+ {
2011
+ match: "name",
2012
+ name: "set_other_session_title",
2013
+ render: SetSessionTitleRenderer,
2014
+ },
2318
2015
  ];
2319
2016
 
2320
2017
  /** The built-in tool renderer registry: every first-party tool plus a fallback. */