@opengeni/react 1.0.1 → 1.0.2-canary.37191796234001

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 (629) hide show
  1. package/README.md +651 -21
  2. package/dist/accounts.d.ts +64 -0
  3. package/dist/accounts.js +822 -0
  4. package/dist/accounts.js.map +1 -0
  5. package/dist/approvals.d.ts +2 -1
  6. package/dist/artifacts-document.js +3 -2
  7. package/dist/artifacts-presentation.js +3 -2
  8. package/dist/artifacts-spreadsheet.js +2 -2
  9. package/dist/artifacts.d.ts +4 -1
  10. package/dist/artifacts.js +518 -42
  11. package/dist/artifacts.js.map +1 -1
  12. package/dist/browser-viewer-GTR5XQ7X.js +16 -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-UGBTNB3L.js +11 -0
  16. package/dist/chat-interactive-block-UGBTNB3L.js.map +1 -0
  17. package/dist/{chunk-5UQPNLIO.js → chunk-23J2ZNXT.js} +4117 -3896
  18. package/dist/chunk-23J2ZNXT.js.map +1 -0
  19. package/dist/chunk-447JLCBU.js +209 -0
  20. package/dist/chunk-447JLCBU.js.map +1 -0
  21. package/dist/chunk-4RVYOY4O.js +379 -0
  22. package/dist/chunk-4RVYOY4O.js.map +1 -0
  23. package/dist/chunk-5CVXZA3Y.js +292 -0
  24. package/dist/chunk-5CVXZA3Y.js.map +1 -0
  25. package/dist/chunk-5J4FOW6Q.js +108 -0
  26. package/dist/chunk-5J4FOW6Q.js.map +1 -0
  27. package/dist/chunk-A2PI6UMY.js +41 -0
  28. package/dist/chunk-A2PI6UMY.js.map +1 -0
  29. package/dist/{chunk-7WCLAEFV.js → chunk-A4UJB2MM.js} +37 -27
  30. package/dist/chunk-A4UJB2MM.js.map +1 -0
  31. package/dist/chunk-B3V6TG77.js +41 -0
  32. package/dist/chunk-B3V6TG77.js.map +1 -0
  33. package/dist/chunk-BRM6FVZA.js +302 -0
  34. package/dist/chunk-BRM6FVZA.js.map +1 -0
  35. package/dist/chunk-C5X5AJXK.js +182 -0
  36. package/dist/chunk-C5X5AJXK.js.map +1 -0
  37. package/dist/chunk-CR7U7HAR.js +453 -0
  38. package/dist/chunk-CR7U7HAR.js.map +1 -0
  39. package/dist/{chunk-R3AZOAB5.js → chunk-DYWBUX5Q.js} +29 -123
  40. package/dist/chunk-DYWBUX5Q.js.map +1 -0
  41. package/dist/chunk-E3G7SAYZ.js +3124 -0
  42. package/dist/chunk-E3G7SAYZ.js.map +1 -0
  43. package/dist/chunk-ELCE5SXV.js +359 -0
  44. package/dist/chunk-ELCE5SXV.js.map +1 -0
  45. package/dist/chunk-ERGYJIBB.js +21 -0
  46. package/dist/chunk-ERGYJIBB.js.map +1 -0
  47. package/dist/chunk-FFA2OVOV.js +319 -0
  48. package/dist/chunk-FFA2OVOV.js.map +1 -0
  49. package/dist/chunk-FZCP527Y.js +116 -0
  50. package/dist/chunk-FZCP527Y.js.map +1 -0
  51. package/dist/chunk-GIMRELBW.js +14 -0
  52. package/dist/chunk-GIMRELBW.js.map +1 -0
  53. package/dist/{chunk-6KLX2RH5.js → chunk-H2NJQORO.js} +1069 -79
  54. package/dist/chunk-H2NJQORO.js.map +1 -0
  55. package/dist/chunk-I7IWRQLN.js +1608 -0
  56. package/dist/chunk-I7IWRQLN.js.map +1 -0
  57. package/dist/{chunk-HA46CYQK.js → chunk-IQUCRAT7.js} +792 -537
  58. package/dist/chunk-IQUCRAT7.js.map +1 -0
  59. package/dist/chunk-IWIDV7SB.js +1 -0
  60. package/dist/chunk-IWIDV7SB.js.map +1 -0
  61. package/dist/chunk-J2QWVSQY.js +312 -0
  62. package/dist/chunk-J2QWVSQY.js.map +1 -0
  63. package/dist/chunk-JFNEV4LU.js +2733 -0
  64. package/dist/chunk-JFNEV4LU.js.map +1 -0
  65. package/dist/chunk-JT4JW7BC.js +54 -0
  66. package/dist/chunk-JT4JW7BC.js.map +1 -0
  67. package/dist/chunk-K3HQAKQX.js +24 -0
  68. package/dist/chunk-K3HQAKQX.js.map +1 -0
  69. package/dist/{chunk-C5ZPSSS5.js → chunk-NEXLLTP4.js} +2 -1
  70. package/dist/{chunk-C5ZPSSS5.js.map → chunk-NEXLLTP4.js.map} +1 -1
  71. package/dist/chunk-NZUI76EM.js +371 -0
  72. package/dist/chunk-NZUI76EM.js.map +1 -0
  73. package/dist/{chunk-PEI6RANI.js → chunk-PNY3JKBJ.js} +309 -74
  74. package/dist/chunk-PNY3JKBJ.js.map +1 -0
  75. package/dist/{chunk-IGC6A2ZI.js → chunk-Q5QBEZIQ.js} +777 -246
  76. package/dist/chunk-Q5QBEZIQ.js.map +1 -0
  77. package/dist/chunk-RHREZ7BO.js +11 -0
  78. package/dist/chunk-RHREZ7BO.js.map +1 -0
  79. package/dist/chunk-SCOTPE2I.js +589 -0
  80. package/dist/chunk-SCOTPE2I.js.map +1 -0
  81. package/dist/{chunk-B6WPZSTT.js → chunk-SJZDKVW2.js} +1331 -1163
  82. package/dist/chunk-SJZDKVW2.js.map +1 -0
  83. package/dist/chunk-TMJRG4YC.js +12803 -0
  84. package/dist/chunk-TMJRG4YC.js.map +1 -0
  85. package/dist/chunk-TMYGL6X5.js +742 -0
  86. package/dist/chunk-TMYGL6X5.js.map +1 -0
  87. package/dist/chunk-UGLEYEMP.js +33 -0
  88. package/dist/chunk-UGLEYEMP.js.map +1 -0
  89. package/dist/chunk-UK4TINKT.js +27 -0
  90. package/dist/chunk-UK4TINKT.js.map +1 -0
  91. package/dist/chunk-UTRTG6LH.js +220 -0
  92. package/dist/chunk-UTRTG6LH.js.map +1 -0
  93. package/dist/chunk-WEHMPOXK.js +39 -0
  94. package/dist/chunk-WEHMPOXK.js.map +1 -0
  95. package/dist/chunk-WRHIZYZF.js +988 -0
  96. package/dist/chunk-WRHIZYZF.js.map +1 -0
  97. package/dist/{chunk-HUIKIDM5.js → chunk-WUISZ3WU.js} +223 -115
  98. package/dist/chunk-WUISZ3WU.js.map +1 -0
  99. package/dist/chunk-XCHWDJ22.js +107 -0
  100. package/dist/chunk-XCHWDJ22.js.map +1 -0
  101. package/dist/chunk-XOAMRDDK.js +481 -0
  102. package/dist/chunk-XOAMRDDK.js.map +1 -0
  103. package/dist/chunk-XUW2UWQM.js +299 -0
  104. package/dist/chunk-XUW2UWQM.js.map +1 -0
  105. package/dist/{chunk-KCGXPP5V.js → chunk-Y2PRMYLC.js} +11 -6
  106. package/dist/chunk-Y2PRMYLC.js.map +1 -0
  107. package/dist/chunk-YBXQDQFX.js +1772 -0
  108. package/dist/chunk-YBXQDQFX.js.map +1 -0
  109. package/dist/{chunk-KZ73RL76.js → chunk-YQEAA6LT.js} +7 -3
  110. package/dist/chunk-YQEAA6LT.js.map +1 -0
  111. package/dist/chunk-YV4L3ILF.js +9 -0
  112. package/dist/chunk-YV4L3ILF.js.map +1 -0
  113. package/dist/{chunk-LLOLUHVG.js → chunk-YWFG7ZX7.js} +1358 -2746
  114. package/dist/chunk-YWFG7ZX7.js.map +1 -0
  115. package/dist/chunk-ZYRGEKWS.js +216 -0
  116. package/dist/chunk-ZYRGEKWS.js.map +1 -0
  117. package/dist/client.d.ts +7 -4
  118. package/dist/clipboard.js +1 -0
  119. package/dist/commands/types.d.ts +2 -0
  120. package/dist/components/anthropic-mark.d.ts +3 -0
  121. package/dist/components/approval-surface.d.ts +15 -0
  122. package/dist/components/artifacts/artifact-chrome.d.ts +120 -0
  123. package/dist/components/artifacts/artifact-sandbox.d.ts +27 -0
  124. package/dist/components/artifacts/artifact-surface.d.ts +3 -1
  125. package/dist/components/artifacts/chat-interactive-block.d.ts +29 -0
  126. package/dist/components/artifacts/deferred-chat-media.d.ts +15 -0
  127. package/dist/components/artifacts/document-editor.d.ts +4 -2
  128. package/dist/components/artifacts/editable-artifact-ui.d.ts +2 -1
  129. package/dist/components/artifacts/editable-artifact-view.d.ts +46 -0
  130. package/dist/components/artifacts/editable-document.d.ts +1 -1
  131. package/dist/components/artifacts/editable-presentation.d.ts +2 -2
  132. package/dist/components/artifacts/editable-spreadsheet.d.ts +2 -1
  133. package/dist/components/artifacts/index.d.ts +8 -1
  134. package/dist/components/artifacts/inline-html-document.d.ts +2 -0
  135. package/dist/components/artifacts/latest-task-runner.d.ts +15 -0
  136. package/dist/components/artifacts/presentation-editor.d.ts +6 -2
  137. package/dist/components/artifacts/published-html-artifact-frame.d.ts +48 -3
  138. package/dist/components/artifacts/session-artifact-viewer.d.ts +39 -0
  139. package/dist/components/artifacts/site-view.d.ts +23 -0
  140. package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -0
  141. package/dist/components/artifacts/visualization-assets.gen.d.ts +2 -0
  142. package/dist/components/browser-select-control.d.ts +16 -0
  143. package/dist/components/browser-viewer.d.ts +10 -1
  144. package/dist/components/chat-composer.d.ts +21 -3
  145. package/dist/components/child-session-link.d.ts +5 -0
  146. package/dist/components/claude-mark.d.ts +3 -0
  147. package/dist/components/code-editor.d.ts +1 -4
  148. package/dist/components/composer.d.ts +45 -4
  149. package/dist/components/computer-viewer.d.ts +4 -1
  150. package/dist/components/copy-button.d.ts +3 -1
  151. package/dist/components/desktop-viewer.d.ts +1 -1
  152. package/dist/components/enrollment-consent.d.ts +6 -3
  153. package/dist/components/file-browser.d.ts +10 -1
  154. package/dist/components/fleet-tile.d.ts +2 -2
  155. package/dist/components/grok-mark.d.ts +26 -0
  156. package/dist/components/human-input-form.d.ts +21 -2
  157. package/dist/components/human-input-surface.d.ts +6 -3
  158. package/dist/components/machine-card.d.ts +4 -1
  159. package/dist/components/machines/machine-detail.d.ts +4 -1
  160. package/dist/components/machines/machine-operation-policy.d.ts +11 -0
  161. package/dist/components/machines-dashboard.d.ts +3 -1
  162. package/dist/components/markdown-table-layout.d.ts +12 -0
  163. package/dist/components/markdown.d.ts +47 -3
  164. package/dist/components/message-timeline.d.ts +72 -9
  165. package/dist/components/model-mark.d.ts +29 -0
  166. package/dist/components/model-policy-picker-menu.d.ts +9 -0
  167. package/dist/components/model-policy-picker.d.ts +62 -13
  168. package/dist/components/open-geni-chat.d.ts +55 -0
  169. package/dist/components/open-geni-links.d.ts +68 -0
  170. package/dist/components/openrouter-mark.d.ts +6 -0
  171. package/dist/components/pierre-file.d.ts +1 -1
  172. package/dist/components/preview-loading.d.ts +2 -0
  173. package/dist/components/queue-item-content.d.ts +9 -0
  174. package/dist/components/sandbox-files.d.ts +13 -2
  175. package/dist/components/sandbox-workspace.d.ts +43 -3
  176. package/dist/components/session-capability-frame.d.ts +35 -0
  177. package/dist/components/session-chrome.d.ts +66 -8
  178. package/dist/components/session-commands-panel.d.ts +6 -0
  179. package/dist/components/session-commands.d.ts +6 -0
  180. package/dist/components/session-connection-request.d.ts +8 -0
  181. package/dist/components/session-conversation.d.ts +86 -0
  182. package/dist/components/session-list.d.ts +36 -0
  183. package/dist/components/session-mcp-capability-card.d.ts +41 -0
  184. package/dist/components/timeline-anchor.d.ts +24 -0
  185. package/dist/components/timeline-annotation-chrome.d.ts +25 -0
  186. package/dist/components/timeline-annotation-layout.d.ts +42 -0
  187. package/dist/components/timeline-annotation-markers.d.ts +8 -0
  188. package/dist/components/timeline-annotation-reveal-context.d.ts +4 -0
  189. package/dist/components/timeline-annotation-shared.d.ts +31 -0
  190. package/dist/components/timeline-annotations-dialog.d.ts +3 -2
  191. package/dist/components/timeline-annotations.d.ts +13 -3
  192. package/dist/components/timeline-reading-progress.d.ts +11 -0
  193. package/dist/components/timeline-search.d.ts +34 -0
  194. package/dist/components/timeline-settlement.d.ts +16 -0
  195. package/dist/components/tip-follow.d.ts +16 -0
  196. package/dist/components/usage-limit-notice.d.ts +49 -0
  197. package/dist/components/usage-member-list.d.ts +91 -0
  198. package/dist/components/usage-meter.d.ts +73 -0
  199. package/dist/components/use-viewer-menu-dismiss.d.ts +2 -0
  200. package/dist/components/user-message-body.d.ts +10 -1
  201. package/dist/components/workspace-dock.d.ts +6 -2
  202. package/dist/composer.d.ts +5 -4
  203. package/dist/composer.js +15 -9
  204. package/dist/computer-viewer-ENHVAZ2Q.js +19 -0
  205. package/dist/computer-viewer-ENHVAZ2Q.js.map +1 -0
  206. package/dist/connect-account-identity.d.ts +8 -0
  207. package/dist/connect-accounts.d.ts +15 -0
  208. package/dist/connect-chooser.d.ts +18 -0
  209. package/dist/connect-panel.d.ts +18 -0
  210. package/dist/connect-setup.d.ts +16 -0
  211. package/dist/connect.d.ts +21 -0
  212. package/dist/connect.js +2129 -0
  213. package/dist/connect.js.map +1 -0
  214. package/dist/connection-catalog.d.ts +38 -0
  215. package/dist/connection-discovery.d.ts +12 -0
  216. package/dist/connection-installed.d.ts +17 -0
  217. package/dist/connection-logo.d.ts +9 -0
  218. package/dist/connection-service-logo.d.ts +8 -0
  219. package/dist/connection-service-presentation.d.ts +13 -0
  220. package/dist/connection-skeleton.d.ts +5 -0
  221. package/dist/connection-type-picker.d.ts +6 -0
  222. package/dist/conversation-timeline.d.ts +5 -0
  223. package/dist/desktop.d.ts +7 -0
  224. package/dist/desktop.js +26 -0
  225. package/dist/desktop.js.map +1 -0
  226. package/dist/device-authorization.d.ts +23 -0
  227. package/dist/diffs.d.ts +8 -0
  228. package/dist/diffs.js +14 -0
  229. package/dist/diffs.js.map +1 -0
  230. package/dist/discovery-cache.d.ts +5 -0
  231. package/dist/editor.d.ts +7 -0
  232. package/dist/editor.js +21 -0
  233. package/dist/editor.js.map +1 -0
  234. package/dist/embedded-session-client.d.ts +23 -0
  235. package/dist/file-node-visibility.d.ts +15 -0
  236. package/dist/{fleet-decision-row-GGCAT4E4.js → fleet-decision-row-YZ3ZKWMM.js} +2 -1
  237. package/dist/fleet-decision-row-YZ3ZKWMM.js.map +1 -0
  238. package/dist/hooks/channel-cache.d.ts +23 -0
  239. package/dist/hooks/latest-question.d.ts +25 -0
  240. package/dist/hooks/shared-machines-list.d.ts +14 -0
  241. package/dist/hooks/use-attached-browsers.d.ts +2 -1
  242. package/dist/hooks/use-browser-frame-stream.d.ts +1 -1
  243. package/dist/hooks/use-browser-session.d.ts +12 -1
  244. package/dist/hooks/use-channels.d.ts +1 -0
  245. package/dist/hooks/use-codex-accounts.d.ts +25 -7
  246. package/dist/hooks/use-composer.d.ts +45 -9
  247. package/dist/hooks/use-computer-frame-stream.d.ts +1 -1
  248. package/dist/hooks/use-computer-input-posture.d.ts +13 -0
  249. package/dist/hooks/use-computer-session.d.ts +2 -0
  250. package/dist/hooks/use-connect.d.ts +22 -0
  251. package/dist/hooks/use-desktop-stream.d.ts +1 -1
  252. package/dist/hooks/use-file-attachments.d.ts +21 -2
  253. package/dist/hooks/use-machine-chip.d.ts +3 -3
  254. package/dist/hooks/use-machines.d.ts +19 -1
  255. package/dist/hooks/use-sandbox-files.d.ts +5 -1
  256. package/dist/hooks/use-session-background-commands.d.ts +14 -0
  257. package/dist/hooks/use-session-events.d.ts +30 -8
  258. package/dist/hooks/use-session-lineage.d.ts +4 -0
  259. package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
  260. package/dist/hooks/use-session.d.ts +6 -0
  261. package/dist/hooks/use-turn-queue.d.ts +12 -0
  262. package/dist/hooks/use-usage.d.ts +40 -0
  263. package/dist/hooks/use-workspace-edit.d.ts +4 -0
  264. package/dist/hooks/use-workspace-sessions.d.ts +27 -5
  265. package/dist/human-input.d.ts +3 -1
  266. package/dist/identity-link-accounts.d.ts +11 -0
  267. package/dist/identity-link-consent.d.ts +18 -0
  268. package/dist/index.d.ts +60 -17
  269. package/dist/index.js +2237 -6200
  270. package/dist/index.js.map +1 -1
  271. package/dist/interaction.d.ts +1 -1
  272. package/dist/interaction.js +35 -184
  273. package/dist/interaction.js.map +1 -1
  274. package/dist/latest-question-755OW5DS.js +117 -0
  275. package/dist/latest-question-755OW5DS.js.map +1 -0
  276. package/dist/lib/error-message.d.ts +7 -0
  277. package/dist/lib/format.d.ts +36 -2
  278. package/dist/lib/host-theme.d.ts +70 -0
  279. package/dist/lib/interaction-errors.d.ts +6 -0
  280. package/dist/lib/menu-styles.d.ts +22 -0
  281. package/dist/lib/pierre-diffs-loader.d.ts +20 -0
  282. package/dist/lib/workbench-peers.d.ts +36 -0
  283. package/dist/lib/xterm-theme.d.ts +7 -5
  284. package/dist/machines.d.ts +4 -2
  285. package/dist/machines.js +14 -4
  286. package/dist/markdown-table-layout-QXSLK4BY.js +106 -0
  287. package/dist/markdown-table-layout-QXSLK4BY.js.map +1 -0
  288. package/dist/mcp-connection-status.d.ts +14 -0
  289. package/dist/model-policy-picker-menu-PWRI7R2X.js +14 -0
  290. package/dist/model-policy-picker-menu-PWRI7R2X.js.map +1 -0
  291. package/dist/model-policy.d.ts +19 -3
  292. package/dist/model-policy.js +9 -1
  293. package/dist/older-history.d.ts +30 -0
  294. package/dist/platform-activity-row-627BFZ46.js +182 -0
  295. package/dist/platform-activity-row-627BFZ46.js.map +1 -0
  296. package/dist/plugin-details.d.ts +14 -0
  297. package/dist/plugin-discovery.d.ts +25 -0
  298. package/dist/provider.d.ts +13 -1
  299. package/dist/{queue-surface-implementation-2KVJTYXH.js → queue-surface-implementation-ZHFI5JBP.js} +131 -44
  300. package/dist/queue-surface-implementation-ZHFI5JBP.js.map +1 -0
  301. package/dist/realtime/realtime-control.d.ts +6 -3
  302. package/dist/realtime.js +133 -74
  303. package/dist/realtime.js.map +1 -1
  304. package/dist/sandbox-files-BBC47SLD.js +20 -0
  305. package/dist/sandbox-files-BBC47SLD.js.map +1 -0
  306. package/dist/session-auth-recommendation.d.ts +5 -0
  307. package/dist/session-capability-policy.d.ts +12 -0
  308. package/dist/session-ui.d.ts +30 -2
  309. package/dist/session-ui.js +85 -7
  310. package/dist/session-ui.js.map +1 -1
  311. package/dist/session.d.ts +8 -2
  312. package/dist/session.js +30 -19
  313. package/dist/sites-ui.d.ts +34 -0
  314. package/dist/sites.d.ts +3 -0
  315. package/dist/sites.js +11 -0
  316. package/dist/sites.js.map +1 -0
  317. package/dist/skill-discovery.d.ts +47 -0
  318. package/dist/terminal.d.ts +8 -0
  319. package/dist/terminal.js +31 -0
  320. package/dist/terminal.js.map +1 -0
  321. package/dist/timeline/activity-rail.d.ts +11 -2
  322. package/dist/timeline/allowance-exhausted-row.d.ts +24 -0
  323. package/dist/timeline/compaction-copy.d.ts +10 -0
  324. package/dist/timeline/entrance.d.ts +13 -1
  325. package/dist/timeline/genie-loading.d.ts +34 -0
  326. package/dist/timeline/index.d.ts +6 -2
  327. package/dist/timeline/knowledge-receipt.d.ts +17 -0
  328. package/dist/timeline/platform-activity-row.d.ts +14 -0
  329. package/dist/timeline/presented-image.d.ts +6 -0
  330. package/dist/timeline/projection.d.ts +31 -2
  331. package/dist/timeline/registry.d.ts +2 -0
  332. package/dist/timeline/retained-image.d.ts +18 -0
  333. package/dist/timeline/rolling-activity.d.ts +11 -0
  334. package/dist/timeline/screenshot-lightbox.d.ts +16 -1
  335. package/dist/timeline/shared.d.ts +8 -3
  336. package/dist/timeline/startup-preference.d.ts +3 -0
  337. package/dist/timeline/startup-timings.d.ts +4 -0
  338. package/dist/timeline/tool-display-name.d.ts +2 -1
  339. package/dist/timeline/turn-summary.d.ts +32 -3
  340. package/dist/timeline/types.d.ts +141 -7
  341. package/dist/timeline-annotation-markers-GNDHTGBD.js +165 -0
  342. package/dist/timeline-annotation-markers-GNDHTGBD.js.map +1 -0
  343. package/dist/timeline-annotation-selection-DMLMM2TX.js +241 -0
  344. package/dist/timeline-annotation-selection-DMLMM2TX.js.map +1 -0
  345. package/dist/timeline-annotations-dialog-ELRDBHTS.js +273 -0
  346. package/dist/timeline-annotations-dialog-ELRDBHTS.js.map +1 -0
  347. package/dist/types/machines.d.ts +2 -2
  348. package/dist/usage/allowance-copy.d.ts +25 -0
  349. package/dist/usage/summary.d.ts +35 -0
  350. package/dist/usage.d.ts +13 -0
  351. package/dist/usage.js +1074 -0
  352. package/dist/usage.js.map +1 -0
  353. package/package.json +44 -4
  354. package/src/accounts.tsx +1072 -0
  355. package/src/approvals.ts +8 -1
  356. package/src/artifacts.ts +42 -0
  357. package/src/assets/README.md +8 -0
  358. package/src/assets/gmail.ico +0 -0
  359. package/src/assets/outlook.ico +0 -0
  360. package/src/capability-catalog-row.tsx +94 -0
  361. package/src/capability-logo-fallback.ts +22 -0
  362. package/src/client.ts +22 -13
  363. package/src/commands/registry.ts +18 -15
  364. package/src/commands/types.ts +2 -0
  365. package/src/components/anthropic-mark.tsx +10 -0
  366. package/src/components/approval-surface.tsx +171 -37
  367. package/src/components/artifacts/artifact-chrome.tsx +413 -0
  368. package/src/components/artifacts/artifact-sandbox.tsx +185 -0
  369. package/src/components/artifacts/artifact-surface.tsx +39 -19
  370. package/src/components/artifacts/chat-interactive-block.tsx +260 -0
  371. package/src/components/artifacts/deferred-chat-media.tsx +67 -0
  372. package/src/components/artifacts/document-editor.tsx +141 -16
  373. package/src/components/artifacts/editable-artifact-ui.tsx +4 -2
  374. package/src/components/artifacts/editable-artifact-view.tsx +187 -0
  375. package/src/components/artifacts/editable-artifact-workbench.tsx +5 -2
  376. package/src/components/artifacts/editable-document.tsx +37 -36
  377. package/src/components/artifacts/editable-presentation.tsx +36 -18
  378. package/src/components/artifacts/editable-spreadsheet.tsx +5 -2
  379. package/src/components/artifacts/index.ts +44 -0
  380. package/src/components/artifacts/inline-html-document.ts +24 -0
  381. package/src/components/artifacts/latest-task-runner.ts +49 -0
  382. package/src/components/artifacts/presentation-editor.tsx +323 -67
  383. package/src/components/artifacts/published-html-artifact-frame.tsx +338 -5
  384. package/src/components/artifacts/session-artifact-viewer.tsx +376 -0
  385. package/src/components/artifacts/site-view.tsx +116 -0
  386. package/src/components/artifacts/spreadsheet-canvas.ts +5 -10
  387. package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
  388. package/src/components/artifacts/spreadsheet-grid.tsx +12 -13
  389. package/src/components/artifacts/visualization-assets/base.css +1015 -0
  390. package/src/components/artifacts/visualization-assets/helpers.html +349 -0
  391. package/src/components/artifacts/visualization-assets.gen.ts +3 -0
  392. package/src/components/browser-select-control.tsx +312 -0
  393. package/src/components/browser-viewer.tsx +1054 -314
  394. package/src/components/chat-composer.tsx +131 -85
  395. package/src/components/child-session-link.tsx +27 -0
  396. package/src/components/claude-mark.tsx +10 -0
  397. package/src/components/code-editor.tsx +13 -16
  398. package/src/components/composer-transcription-control.tsx +7 -5
  399. package/src/components/composer.tsx +376 -49
  400. package/src/components/computer-viewer.tsx +1041 -329
  401. package/src/components/copy-button.tsx +13 -5
  402. package/src/components/desktop-viewer.tsx +1 -1
  403. package/src/components/enrollment-consent.tsx +65 -7
  404. package/src/components/enrollment-device-flow.tsx +1 -1
  405. package/src/components/file-browser.tsx +158 -34
  406. package/src/components/fleet-tile.tsx +8 -16
  407. package/src/components/generated-video-player.tsx +4 -2
  408. package/src/components/grok-mark.tsx +33 -0
  409. package/src/components/human-input-form.tsx +455 -114
  410. package/src/components/human-input-surface.tsx +18 -7
  411. package/src/components/interaction-intervention-banner.tsx +2 -2
  412. package/src/components/machine-card.tsx +119 -4
  413. package/src/components/machine-dock-bar.tsx +1 -1
  414. package/src/components/machine-input-display.ts +23 -2
  415. package/src/components/machines/machine-detail.tsx +52 -2
  416. package/src/components/machines/machine-operation-policy.tsx +179 -0
  417. package/src/components/machines-dashboard.tsx +8 -17
  418. package/src/components/markdown-table-layout.ts +132 -0
  419. package/src/components/markdown.tsx +473 -44
  420. package/src/components/message-timeline.tsx +2498 -663
  421. package/src/components/model-mark.tsx +90 -0
  422. package/src/components/model-picker.tsx +15 -4
  423. package/src/components/model-policy-picker-menu.tsx +424 -0
  424. package/src/components/model-policy-picker.tsx +337 -381
  425. package/src/components/open-geni-chat.tsx +319 -0
  426. package/src/components/open-geni-links.tsx +179 -0
  427. package/src/components/openrouter-mark.tsx +13 -0
  428. package/src/components/pierre-diff.tsx +41 -16
  429. package/src/components/pierre-file.tsx +36 -11
  430. package/src/components/preview-loading.tsx +154 -0
  431. package/src/components/queue-item-content.ts +14 -0
  432. package/src/components/queue-surface-implementation.tsx +132 -42
  433. package/src/components/queue-surface-state.tsx +3 -1
  434. package/src/components/sandbox-files.tsx +267 -35
  435. package/src/components/sandbox-terminal.tsx +48 -11
  436. package/src/components/sandbox-workspace.tsx +337 -135
  437. package/src/components/session-capability-frame.tsx +192 -0
  438. package/src/components/session-chrome.tsx +1027 -328
  439. package/src/components/session-commands-panel.tsx +126 -0
  440. package/src/components/session-commands.tsx +18 -0
  441. package/src/components/session-connection-request.tsx +97 -0
  442. package/src/components/session-conversation.tsx +512 -0
  443. package/src/components/session-list.tsx +250 -0
  444. package/src/components/session-mcp-capability-card.tsx +680 -0
  445. package/src/components/session-status.tsx +1 -1
  446. package/src/components/timeline-anchor.tsx +119 -0
  447. package/src/components/timeline-annotation-chrome.tsx +196 -0
  448. package/src/components/timeline-annotation-layout.ts +268 -0
  449. package/src/components/timeline-annotation-markers.tsx +185 -0
  450. package/src/components/timeline-annotation-reveal-context.ts +9 -0
  451. package/src/components/timeline-annotation-selection.tsx +106 -54
  452. package/src/components/timeline-annotation-shared.ts +245 -0
  453. package/src/components/timeline-annotations-dialog.tsx +185 -128
  454. package/src/components/timeline-annotations.tsx +189 -45
  455. package/src/components/timeline-reading-progress.ts +80 -0
  456. package/src/components/timeline-search.tsx +168 -0
  457. package/src/components/timeline-settlement.ts +60 -0
  458. package/src/components/tip-follow.ts +70 -7
  459. package/src/components/tooltip.tsx +38 -5
  460. package/src/components/usage-limit-notice.tsx +179 -0
  461. package/src/components/usage-member-list.tsx +792 -0
  462. package/src/components/usage-meter.tsx +382 -0
  463. package/src/components/use-viewer-menu-dismiss.ts +33 -0
  464. package/src/components/user-message-body.tsx +194 -49
  465. package/src/components/workspace-dock.tsx +141 -175
  466. package/src/composer.ts +6 -1
  467. package/src/connect-account-identity.tsx +34 -0
  468. package/src/connect-accounts.tsx +233 -0
  469. package/src/connect-chooser.tsx +336 -0
  470. package/src/connect-panel.tsx +99 -0
  471. package/src/connect-setup.tsx +307 -0
  472. package/src/connect.ts +60 -0
  473. package/src/connection-assets.d.ts +4 -0
  474. package/src/connection-catalog.tsx +162 -0
  475. package/src/connection-discovery.tsx +235 -0
  476. package/src/connection-installed.tsx +51 -0
  477. package/src/connection-logo.tsx +37 -0
  478. package/src/connection-service-logo.tsx +55 -0
  479. package/src/connection-service-presentation.ts +46 -0
  480. package/src/connection-skeleton.tsx +16 -0
  481. package/src/connection-type-picker.tsx +36 -0
  482. package/src/conversation-timeline.ts +92 -0
  483. package/src/desktop.ts +24 -0
  484. package/src/device-authorization.tsx +98 -0
  485. package/src/diffs.ts +16 -0
  486. package/src/discovery-cache.ts +42 -0
  487. package/src/editor.ts +13 -0
  488. package/src/embedded-session-client.ts +104 -0
  489. package/src/file-node-visibility.ts +66 -0
  490. package/src/hooks/channel-cache.ts +104 -0
  491. package/src/hooks/internal.ts +3 -2
  492. package/src/hooks/latest-question.ts +164 -0
  493. package/src/hooks/shared-machines-list.ts +239 -0
  494. package/src/hooks/use-attached-browsers.ts +5 -1
  495. package/src/hooks/use-available-models.ts +20 -14
  496. package/src/hooks/use-browser-frame-stream.ts +25 -3
  497. package/src/hooks/use-browser-session.ts +271 -71
  498. package/src/hooks/use-browser-sessions.ts +7 -1
  499. package/src/hooks/use-channels.ts +32 -10
  500. package/src/hooks/use-codex-accounts.ts +78 -21
  501. package/src/hooks/use-composer.ts +1034 -320
  502. package/src/hooks/use-computer-frame-stream.ts +39 -16
  503. package/src/hooks/use-computer-input-posture.ts +94 -0
  504. package/src/hooks/use-computer-session.ts +110 -32
  505. package/src/hooks/use-computer-sessions.ts +8 -1
  506. package/src/hooks/use-connect.ts +23 -0
  507. package/src/hooks/use-desktop-stream.ts +19 -28
  508. package/src/hooks/use-file-attachments.ts +118 -10
  509. package/src/hooks/use-goal.ts +36 -9
  510. package/src/hooks/use-human-input.ts +31 -4
  511. package/src/hooks/use-last-started-turn-policy.ts +15 -13
  512. package/src/hooks/use-machine-chip.ts +23 -6
  513. package/src/hooks/use-machines.ts +104 -6
  514. package/src/hooks/use-sandbox-files.ts +253 -150
  515. package/src/hooks/use-sandbox-git.ts +58 -29
  516. package/src/hooks/use-session-background-commands.ts +67 -0
  517. package/src/hooks/use-session-capabilities.ts +21 -2
  518. package/src/hooks/use-session-events.ts +759 -433
  519. package/src/hooks/use-session-lineage.ts +28 -6
  520. package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
  521. package/src/hooks/use-session.ts +75 -16
  522. package/src/hooks/use-slash-commands.ts +6 -11
  523. package/src/hooks/use-turn-queue.ts +113 -13
  524. package/src/hooks/use-usage.ts +100 -0
  525. package/src/hooks/use-voice-input.ts +12 -1
  526. package/src/hooks/use-workspace-capture.ts +3 -2
  527. package/src/hooks/use-workspace-edit.ts +23 -5
  528. package/src/hooks/use-workspace-sessions.ts +99 -11
  529. package/src/human-input.ts +12 -1
  530. package/src/identity-link-accounts.tsx +148 -0
  531. package/src/identity-link-consent.tsx +192 -0
  532. package/src/index.ts +157 -12
  533. package/src/interaction.ts +1 -0
  534. package/src/lib/error-message.ts +37 -0
  535. package/src/lib/format.ts +150 -14
  536. package/src/lib/host-theme.ts +511 -0
  537. package/src/lib/interaction-errors.ts +33 -0
  538. package/src/lib/menu-styles.ts +44 -0
  539. package/src/lib/pierre-diffs-loader.ts +53 -0
  540. package/src/lib/workbench-peers.ts +62 -0
  541. package/src/lib/xterm-theme.ts +23 -21
  542. package/src/machines.ts +9 -1
  543. package/src/mcp-connection-status.ts +35 -0
  544. package/src/model-policy.ts +223 -69
  545. package/src/older-history.ts +87 -0
  546. package/src/plugin-details.tsx +185 -0
  547. package/src/plugin-discovery.tsx +196 -0
  548. package/src/provider.tsx +55 -14
  549. package/src/realtime/dropdown-menu.tsx +19 -12
  550. package/src/realtime/realtime-control.tsx +133 -46
  551. package/src/session-auth-recommendation.ts +48 -0
  552. package/src/session-capability-policy.ts +83 -0
  553. package/src/session-context.ts +1 -0
  554. package/src/session-ui.ts +66 -2
  555. package/src/session.ts +18 -1
  556. package/src/sites-ui.tsx +402 -0
  557. package/src/sites.ts +3 -0
  558. package/src/skill-discovery.tsx +208 -0
  559. package/src/terminal.ts +26 -0
  560. package/src/timeline/activity-rail.tsx +171 -80
  561. package/src/timeline/allowance-exhausted-row.tsx +86 -0
  562. package/src/timeline/compaction-copy.ts +42 -0
  563. package/src/timeline/entrance.tsx +28 -3
  564. package/src/timeline/fleet-decision-projection.ts +2 -1
  565. package/src/timeline/fleet-decision-row.tsx +1 -0
  566. package/src/timeline/genie-loading.tsx +136 -0
  567. package/src/timeline/index.ts +11 -0
  568. package/src/timeline/knowledge-receipt.tsx +100 -0
  569. package/src/timeline/parsers.ts +2 -2
  570. package/src/timeline/platform-activity-row.tsx +216 -0
  571. package/src/timeline/presented-image.ts +49 -0
  572. package/src/timeline/projection.ts +1600 -84
  573. package/src/timeline/registry.ts +15 -3
  574. package/src/timeline/retained-image.ts +97 -0
  575. package/src/timeline/rolling-activity.tsx +92 -0
  576. package/src/timeline/screenshot-lightbox.tsx +115 -17
  577. package/src/timeline/shared.tsx +47 -15
  578. package/src/timeline/startup-preference.ts +33 -0
  579. package/src/timeline/startup-timings.tsx +145 -0
  580. package/src/timeline/tool-display-name.ts +8 -6
  581. package/src/timeline/tool-renderers.tsx +497 -128
  582. package/src/timeline/turn-summary.tsx +208 -44
  583. package/src/timeline/types.ts +186 -7
  584. package/src/types/machines.ts +10 -0
  585. package/src/usage/allowance-copy.ts +64 -0
  586. package/src/usage/summary.ts +93 -0
  587. package/src/usage.ts +50 -0
  588. package/styles/compiled.css +2098 -757
  589. package/styles/connect.css +284 -0
  590. package/styles/connect.d.ts +1 -0
  591. package/styles/effective-tokens.css +1 -0
  592. package/styles/index.css +201 -9
  593. package/styles/responsive.css +27 -1
  594. package/styles/session-capability.css +44 -0
  595. package/styles/tokens.css +135 -65
  596. package/dist/chunk-5UQPNLIO.js.map +0 -1
  597. package/dist/chunk-6KLX2RH5.js.map +0 -1
  598. package/dist/chunk-7WCLAEFV.js.map +0 -1
  599. package/dist/chunk-B6WPZSTT.js.map +0 -1
  600. package/dist/chunk-FK6VG4LL.js +0 -90
  601. package/dist/chunk-FK6VG4LL.js.map +0 -1
  602. package/dist/chunk-HA46CYQK.js.map +0 -1
  603. package/dist/chunk-HUIKIDM5.js.map +0 -1
  604. package/dist/chunk-IGC6A2ZI.js.map +0 -1
  605. package/dist/chunk-KCGXPP5V.js.map +0 -1
  606. package/dist/chunk-KZ73RL76.js.map +0 -1
  607. package/dist/chunk-LAKLF4PA.js +0 -141
  608. package/dist/chunk-LAKLF4PA.js.map +0 -1
  609. package/dist/chunk-LLOLUHVG.js.map +0 -1
  610. package/dist/chunk-MKPIAGVL.js +0 -465
  611. package/dist/chunk-MKPIAGVL.js.map +0 -1
  612. package/dist/chunk-PEI6RANI.js.map +0 -1
  613. package/dist/chunk-R3AZOAB5.js.map +0 -1
  614. package/dist/chunk-WSK7G5LE.js +0 -170
  615. package/dist/chunk-WSK7G5LE.js.map +0 -1
  616. package/dist/chunk-XBZWOM2K.js +0 -218
  617. package/dist/chunk-XBZWOM2K.js.map +0 -1
  618. package/dist/chunk-Y7OEDUJ5.js +0 -8810
  619. package/dist/chunk-Y7OEDUJ5.js.map +0 -1
  620. package/dist/fleet-decision-row-GGCAT4E4.js.map +0 -1
  621. package/dist/hooks/use-packs.d.ts +0 -32
  622. package/dist/interactive-workbench-viewers-TOHBXJEE.js +0 -15
  623. package/dist/queue-surface-implementation-2KVJTYXH.js.map +0 -1
  624. package/dist/timeline-annotation-selection-3L7LHGG2.js +0 -187
  625. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +0 -1
  626. package/dist/timeline-annotations-dialog-U7EVLR75.js +0 -196
  627. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +0 -1
  628. package/src/hooks/use-packs.ts +0 -157
  629. /package/dist/{interactive-workbench-viewers-TOHBXJEE.js.map → browser-viewer-GTR5XQ7X.js.map} +0 -0
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ FileSystemRouteIdentity,
2
3
  FsChangedPayload,
3
4
  FsReadResponse,
4
5
  FsTreeNode,
@@ -65,7 +66,8 @@ export type SandboxWriteFileOptions = {
65
66
  /** A node in the Pierre file tree. `children === undefined` ⇒ an unexpanded dir
66
67
  * (lazy treeMode); `children: []` ⇒ an expanded-but-empty dir. */
67
68
  export type FileTreeNode = {
68
- path: string; // workspace-relative POSIX
69
+ /** Opaque path returned by the selected target's FileSystem boundary. */
70
+ path: string;
69
71
  name: string;
70
72
  kind: "file" | "dir";
71
73
  children?: FileTreeNode[] | undefined;
@@ -83,6 +85,9 @@ export type UseSandboxFilesOptions = ClientOverride & {
83
85
  events?: SessionEvent[] | undefined;
84
86
  /** Initial path to list (workspace root by default). */
85
87
  rootPath?: string | undefined;
88
+ /** Exact capability identity for the selected filesystem route. A route/root
89
+ * change resets this hook and makes stale provider requests fail retryably. */
90
+ route?: FileSystemRouteIdentity | undefined;
86
91
  /** Hold off the initial list (e.g. panel collapsed). Default true. */
87
92
  enabled?: boolean | undefined;
88
93
  /** Pause live reads/invalidation while preserving the last rendered result. */
@@ -162,16 +167,25 @@ export type SandboxFilesGitSummary = {
162
167
  };
163
168
 
164
169
  function normalizeRoot(root: string): string {
165
- const normalized = root
166
- .replaceAll("\\", "/")
167
- .replace(/^\.\/+/, "")
168
- .replace(/\/+$/, "");
170
+ const slashNormalized = root.replaceAll("\\", "/").replace(/^\.\/+/, "");
171
+ if (slashNormalized === "/") return "/";
172
+ const normalized = slashNormalized.replace(/\/+$/, "");
169
173
  return normalized === "." ? "" : normalized;
170
174
  }
171
175
 
172
- function qualifyStatusPath(root: string, path: string): string {
176
+ function isAbsoluteTreePath(path: string): boolean {
177
+ return path.startsWith("/") || /^[A-Za-z]:\//u.test(path);
178
+ }
179
+
180
+ function joinTreeRoot(root: string, path: string): string {
181
+ if (!root || isAbsoluteTreePath(path)) return path;
182
+ return root === "/" ? `/${path}` : `${root}/${path}`;
183
+ }
184
+
185
+ function qualifyStatusPath(root: string, path: string, treeRoot: string): string {
173
186
  const normalized = normalizeRoot(root);
174
- return normalized ? `${normalized}/${path}` : path;
187
+ const workspaceRelative = joinTreeRoot(normalized, path);
188
+ return joinTreeRoot(normalizeRoot(treeRoot), workspaceRelative);
175
189
  }
176
190
 
177
191
  function summarizeStatuses(
@@ -194,6 +208,11 @@ function parentOf(path: string): string {
194
208
  return i <= 0 ? "" : path.slice(0, i);
195
209
  }
196
210
 
211
+ function parentWithinTree(path: string, rootPath: string): string {
212
+ const parent = parentOf(path);
213
+ return parent === "" && normalizeRoot(rootPath) === "/" && path.startsWith("/") ? "/" : parent;
214
+ }
215
+
197
216
  /** The leaf name of a POSIX path. */
198
217
  function leafOf(path: string): string {
199
218
  const i = path.lastIndexOf("/");
@@ -282,33 +301,23 @@ function findNodeByPath(nodes: FileTreeNode[], path: string): FileTreeNode | und
282
301
  return undefined;
283
302
  }
284
303
 
285
- /** Root plus every directory whose children are currently loaded. Re-listing
286
- * this small visible frontier refreshes agent shell edits without expanding or
287
- * traversing hidden directories. */
288
- function loadedDirectoryPaths(nodes: FileTreeNode[]): string[] {
289
- const paths = [""];
290
- const walk = (list: FileTreeNode[]) => {
291
- for (const node of list) {
292
- if (node.kind !== "dir" || node.children === undefined) continue;
293
- paths.push(node.path);
294
- walk(node.children);
295
- }
296
- };
297
- walk(nodes);
298
- return paths;
299
- }
304
+ const FS_LIST_BATCH_REQUEST_LIMIT = 16;
300
305
 
301
306
  /** Repository paths are capability-advertised and therefore safe to use as a
302
307
  * bounded initial lazy-tree frontier. Listing their ancestors concurrently with
303
308
  * the root avoids a root → repositories → repo remote waterfall. */
304
309
  function initialDirectoryFrontier(rootPath: string, repoPaths: readonly string[]): string[] {
305
- const rootParts = normalizeRoot(rootPath).split("/").filter(Boolean);
310
+ const root = normalizeRoot(rootPath);
311
+ const rootParts = root.split("/").filter(Boolean);
312
+ const rootPrefix = root.startsWith("//") ? "//" : root.startsWith("/") ? "/" : "";
306
313
  const paths = new Set<string>();
307
314
  for (const repoPath of repoPaths) {
308
- const parts = normalizeRoot(repoPath).split("/").filter(Boolean);
315
+ const canonicalRepoPath = joinTreeRoot(root, normalizeRoot(repoPath));
316
+ const parts = canonicalRepoPath.split("/").filter(Boolean);
309
317
  if (rootParts.some((part, index) => parts[index] !== part)) continue;
310
318
  for (let depth = rootParts.length + 1; depth <= parts.length; depth += 1) {
311
- paths.add(parts.slice(0, depth).join("/"));
319
+ const path = parts.slice(0, depth).join("/");
320
+ paths.add(`${rootPrefix}${path}`);
312
321
  }
313
322
  }
314
323
  // A workspace with hundreds of repositories should remain lazy. This still
@@ -333,8 +342,8 @@ function initialDirectoryFrontier(rootPath: string, repoPaths: readonly string[]
333
342
 
334
343
  /** True when `parent` is the root ("") or a dir node that is actually present in
335
344
  * the loaded tree (so an insert/remove there would be visible). */
336
- function parentIsLoaded(nodes: FileTreeNode[], parent: string): boolean {
337
- if (parent === "") return true;
345
+ function parentIsLoaded(nodes: FileTreeNode[], parent: string, rootPath = ""): boolean {
346
+ if (parent === normalizeRoot(rootPath)) return true;
338
347
  const node = findNodeByPath(nodes, parent);
339
348
  return Boolean(node && node.kind === "dir" && node.children !== undefined);
340
349
  }
@@ -342,8 +351,13 @@ function parentIsLoaded(nodes: FileTreeNode[], parent: string): boolean {
342
351
  /** Insert `child` under `parent` (immutably), keeping siblings sorted. A no-op
343
352
  * (returns the same array) when the parent isn't a loaded dir or the child
344
353
  * already exists. */
345
- function insertNode(nodes: FileTreeNode[], parent: string, child: FileTreeNode): FileTreeNode[] {
346
- if (parent === "") {
354
+ function insertNode(
355
+ nodes: FileTreeNode[],
356
+ parent: string,
357
+ child: FileTreeNode,
358
+ rootPath = "",
359
+ ): FileTreeNode[] {
360
+ if (parent === normalizeRoot(rootPath)) {
347
361
  if (nodes.some((n) => n.path === child.path)) return nodes;
348
362
  return sortNodes([...nodes, child]);
349
363
  }
@@ -354,16 +368,16 @@ function insertNode(nodes: FileTreeNode[], parent: string, child: FileTreeNode):
354
368
  return { ...node, children: sortNodes([...node.children, child]) };
355
369
  }
356
370
  if (node.kind === "dir" && node.children && parent.startsWith(`${node.path}/`)) {
357
- return { ...node, children: insertNode(node.children, parent, child) };
371
+ return { ...node, children: insertNode(node.children, parent, child, rootPath) };
358
372
  }
359
373
  return node;
360
374
  });
361
375
  }
362
376
 
363
377
  /** Remove the node at `path` (immutably). No-op when it isn't in the tree. */
364
- function removeNode(nodes: FileTreeNode[], path: string): FileTreeNode[] {
365
- const parent = parentOf(path);
366
- if (parent === "") {
378
+ function removeNode(nodes: FileTreeNode[], path: string, rootPath = ""): FileTreeNode[] {
379
+ const parent = parentWithinTree(path, rootPath);
380
+ if (parent === normalizeRoot(rootPath)) {
367
381
  if (!nodes.some((n) => n.path === path)) return nodes;
368
382
  return nodes.filter((n) => n.path !== path);
369
383
  }
@@ -376,7 +390,7 @@ function removeNode(nodes: FileTreeNode[], path: string): FileTreeNode[] {
376
390
  };
377
391
  }
378
392
  if (node.kind === "dir" && node.children && parent.startsWith(`${node.path}/`)) {
379
- return { ...node, children: removeNode(node.children, path) };
393
+ return { ...node, children: removeNode(node.children, path, rootPath) };
380
394
  }
381
395
  return node;
382
396
  });
@@ -486,6 +500,15 @@ export function useSandboxFiles(
486
500
  const enabled = (options.enabled ?? true) && Boolean(sessionId);
487
501
  const active = options.active ?? true;
488
502
  const rootPath = options.rootPath ?? "";
503
+ const routeEpoch = options.route?.epoch ?? null;
504
+ const routeRoot = options.route?.root ?? null;
505
+ const route = useMemo<FileSystemRouteIdentity | undefined>(
506
+ () =>
507
+ routeEpoch !== null && routeRoot !== null
508
+ ? { epoch: routeEpoch, root: routeRoot }
509
+ : undefined,
510
+ [routeEpoch, routeRoot],
511
+ );
489
512
  const repoPathsKey = [
490
513
  ...new Set((options.repoPaths?.length ? options.repoPaths : [rootPath]).map(normalizeRoot)),
491
514
  ].join("\u0000");
@@ -498,7 +521,7 @@ export function useSandboxFiles(
498
521
  const acceptsEventReadsRef = useRef(acceptsEventReads);
499
522
  acceptsEventReadsRef.current = acceptsEventReads;
500
523
  const hasLiveIoFence = options.liveness !== undefined;
501
- const identityKey = `${workspaceId}\u0000${sessionId ?? ""}\u0000${rootPath}\u0000${repoPathsKey}`;
524
+ const identityKey = `${workspaceId}\u0000${sessionId ?? ""}\u0000${rootPath}\u0000${repoPathsKey}\u0000${routeEpoch ?? ""}\u0000${routeRoot ?? ""}`;
502
525
 
503
526
  const [tree, setTree] = useState<FileTreeNode[]>([]);
504
527
  // A ref mirror of the current tree — lets the optimistic path snapshot the
@@ -527,9 +550,9 @@ export function useSandboxFiles(
527
550
  const lastSeqRef = useRef(0);
528
551
  const pendingParentsRef = useRef<Set<string>>(new Set());
529
552
  const pendingGitRef = useRef(false);
530
- const pendingAgentToolRef = useRef(false);
553
+ const eventReconcileInFlightRef = useRef<Promise<void> | null>(null);
554
+ const eventReconcilePendingRef = useRef(false);
531
555
  const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
532
- const lastCommandRefreshAtRef = useRef(0);
533
556
 
534
557
  // ── Self-emitted fs.changed de-dupe ───────────────────────────────────────
535
558
  // Every one of OUR mutations emits an `fs.changed` event back on the live log
@@ -584,34 +607,16 @@ export function useSandboxFiles(
584
607
  setGitLoading(true);
585
608
  setError(null);
586
609
  try {
587
- // Start the optional Git tint and the actual directory listing together.
588
- // A remote status read must never block the first useful file-tree paint.
610
+ // Paint the useful file tree before the optional Git tint. Separate remote
611
+ // requests for one Modal box are deliberately serialized by the API; firing
612
+ // both here only made the Git request contend with the file request during
613
+ // cold starts.
589
614
  const frontierPaths = initialDirectoryFrontier(rootPath, repoPaths);
590
615
  const listRequests = [rootPath, ...frontierPaths].map((path) => ({
591
616
  path,
592
617
  depth: 1,
618
+ ...(route ? { route } : {}),
593
619
  }));
594
- const statusPromise =
595
- repoPaths.length > 0
596
- ? client
597
- .gitReadBatch(
598
- workspaceId,
599
- sessionId,
600
- {
601
- requests: repoPaths.map((root) => ({
602
- status: { path: root },
603
- })),
604
- },
605
- { signal: refreshAbort.signal },
606
- )
607
- .then((batch) =>
608
- batch.results.map((result, index) => ({
609
- root: repoPaths[index] ?? "",
610
- status: result.status,
611
- })),
612
- )
613
- .catch(() => null)
614
- : Promise.resolve([]);
615
620
  // Root plus the bounded repository frontier share one API request, one
616
621
  // direct holder, and one provider attach. The old per-directory fan-out
617
622
  // paid that fixed control-plane cost up to nine times on every Files open.
@@ -644,7 +649,27 @@ export function useSandboxFiles(
644
649
  setSource("live");
645
650
  setLoading(false);
646
651
 
647
- const statuses = await statusPromise;
652
+ const statuses =
653
+ repoPaths.length > 0
654
+ ? await client
655
+ .gitReadBatch(
656
+ workspaceId,
657
+ sessionId,
658
+ {
659
+ requests: repoPaths.map((root) => ({
660
+ status: { path: root },
661
+ })),
662
+ },
663
+ { signal: refreshAbort.signal },
664
+ )
665
+ .then((batch) =>
666
+ batch.results.map((result, index) => ({
667
+ root: repoPaths[index] ?? "",
668
+ status: result.status,
669
+ })),
670
+ )
671
+ .catch(() => null)
672
+ : [];
648
673
  if (refreshGenerationRef.current !== generation) return;
649
674
  if (statuses) {
650
675
  const overlay = new Map<string, FileTreeStatus>();
@@ -652,7 +677,7 @@ export function useSandboxFiles(
652
677
  for (const file of status.files) {
653
678
  const code = file.worktree ?? file.index;
654
679
  const mapped = code ? GIT_STATUS_TO_TREE[code] : undefined;
655
- if (mapped) overlay.set(qualifyStatusPath(root, file.path), mapped);
680
+ if (mapped) overlay.set(qualifyStatusPath(root, file.path, rootPath), mapped);
656
681
  }
657
682
  }
658
683
  statusRef.current = overlay;
@@ -671,7 +696,7 @@ export function useSandboxFiles(
671
696
  if (refreshGenerationRef.current === generation) setGitLoading(false);
672
697
  if (refreshAbortRef.current === refreshAbort) refreshAbortRef.current = null;
673
698
  }
674
- }, [client, workspaceId, sessionId, rootPath, repoPaths, applyStatus]);
699
+ }, [client, workspaceId, sessionId, rootPath, repoPaths, route, applyStatus]);
675
700
 
676
701
  // Seed (or re-seed) the tree from a turn-end capture — the COLD/offline paint,
677
702
  // zero Channel-A calls. The tree index is workspace-relative (`treeIndex`); the
@@ -731,7 +756,7 @@ export function useSandboxFiles(
731
756
  const listed = await client.fsList(
732
757
  workspaceId,
733
758
  sessionId,
734
- { path, depth: 1 },
759
+ { path, depth: 1, ...(route ? { route } : {}) },
735
760
  { signal: identitySignal },
736
761
  );
737
762
  if (identityGenerationRef.current !== identityGeneration) return;
@@ -751,7 +776,7 @@ export function useSandboxFiles(
751
776
  }
752
777
  }
753
778
  },
754
- [client, workspaceId, sessionId, capture, source, applyStatus],
779
+ [client, workspaceId, sessionId, capture, source, route, applyStatus],
755
780
  );
756
781
 
757
782
  const readFile = useCallback(
@@ -844,9 +869,14 @@ export function useSandboxFiles(
844
869
  }
845
870
  throw new Error("Captured file download failed after refreshing its URL.");
846
871
  }
847
- return await client.fsRead(workspaceId, sessionId, { path }, { signal });
872
+ return await client.fsRead(
873
+ workspaceId,
874
+ sessionId,
875
+ { path, ...(route ? { route } : {}) },
876
+ { signal },
877
+ );
848
878
  },
849
- [client, workspaceId, sessionId, capture, source],
879
+ [client, workspaceId, sessionId, capture, source, route],
850
880
  );
851
881
 
852
882
  // TARGETED reconcile of a single directory — re-list ONE parent at depth 1 and
@@ -866,13 +896,19 @@ export function useSandboxFiles(
866
896
  : identitySignal;
867
897
  // Skip parents that aren't currently loaded/expanded in the tree (nothing
868
898
  // visible to update; they re-list fresh on the next expand).
869
- if (!parentIsLoaded(treeRef.current, path)) return;
899
+ if (!parentIsLoaded(treeRef.current, path, rootPath)) return;
870
900
  try {
871
- const listed = await client.fsList(workspaceId, sessionId, { path, depth: 1 }, { signal });
901
+ const listed = await client.fsList(
902
+ workspaceId,
903
+ sessionId,
904
+ { path, depth: 1, ...(route ? { route } : {}) },
905
+ { signal },
906
+ );
872
907
  if (identityGenerationRef.current !== identityGeneration) return;
873
908
  const children = (listed.root.children ?? []).map((node) => fsNodeToTree(node));
874
- if (path === "") setTree((prev) => applyStatus(mergeRootChildren(prev, children)));
875
- else
909
+ if (path === normalizeRoot(rootPath)) {
910
+ setTree((prev) => applyStatus(mergeRootChildren(prev, children)));
911
+ } else
876
912
  setTree((prev) => {
877
913
  const existing = findNodeByPath(prev, path);
878
914
  const merged = existing?.children
@@ -885,7 +921,64 @@ export function useSandboxFiles(
885
921
  // root-refresh here (that would collapse the tree the user is working in).
886
922
  }
887
923
  },
888
- [client, workspaceId, sessionId, applyStatus],
924
+ [client, workspaceId, sessionId, rootPath, route, applyStatus],
925
+ );
926
+
927
+ // Reconcile the visible directory frontier through bounded batch requests.
928
+ // One tool completion can affect several expanded directories; issuing one
929
+ // provider request per directory created a self-inflicted lock convoy.
930
+ const reconcilePaths = useCallback(
931
+ async (requestedPaths: readonly string[], requestSignal?: AbortSignal) => {
932
+ if (!sessionId) return;
933
+ const identityGeneration = identityGenerationRef.current;
934
+ const identitySignal = identityAbortRef.current.signal;
935
+ const signal = requestSignal
936
+ ? AbortSignal.any([identitySignal, requestSignal])
937
+ : identitySignal;
938
+ const paths = [...new Set(requestedPaths)].filter((path) =>
939
+ parentIsLoaded(treeRef.current, path, rootPath),
940
+ );
941
+ for (let offset = 0; offset < paths.length; offset += FS_LIST_BATCH_REQUEST_LIMIT) {
942
+ const chunk = paths.slice(offset, offset + FS_LIST_BATCH_REQUEST_LIMIT);
943
+ try {
944
+ const listed = await client.fsListBatch(
945
+ workspaceId,
946
+ sessionId,
947
+ {
948
+ requests: chunk.map((path) => ({
949
+ path,
950
+ depth: 1,
951
+ ...(route ? { route } : {}),
952
+ })),
953
+ },
954
+ { signal },
955
+ );
956
+ if (identityGenerationRef.current !== identityGeneration) return;
957
+ setTree((previous) => {
958
+ let next = previous;
959
+ for (const [index, path] of chunk.entries()) {
960
+ const result = listed.results[index];
961
+ if (!result) continue;
962
+ const children = (result.root.children ?? []).map((node) => fsNodeToTree(node));
963
+ if (path === normalizeRoot(rootPath)) {
964
+ next = mergeRootChildren(next, children);
965
+ } else {
966
+ const existing = findNodeByPath(next, path);
967
+ const merged = existing?.children
968
+ ? mergeChildren(existing.children, children)
969
+ : children;
970
+ next = replaceChildren(next, path, merged);
971
+ }
972
+ }
973
+ return applyStatus(next);
974
+ });
975
+ } catch {
976
+ // Reconciliation is best-effort. Preserve the current visible tree and
977
+ // let the next real event or explicit refresh reconcile it.
978
+ }
979
+ }
980
+ },
981
+ [client, workspaceId, sessionId, rootPath, route, applyStatus],
889
982
  );
890
983
 
891
984
  // Run a Channel-A op behind an OPTIMISTIC tree edit. `apply` splices the change
@@ -950,7 +1043,7 @@ export function useSandboxFiles(
950
1043
  if (!sessionId) throw new Error("no session");
951
1044
  const identityGeneration = identityGenerationRef.current;
952
1045
  const identitySignal = identityAbortRef.current.signal;
953
- const parent = parentOf(path);
1046
+ const parent = parentWithinTree(path, rootPath);
954
1047
  // Splice a new file node in immediately ONLY when the path doesn't already
955
1048
  // exist in a loaded parent (an editor SAVE to an existing file mutates no
956
1049
  // tree shape — just content — so it needs no optimistic node, no reconcile).
@@ -963,13 +1056,13 @@ export function useSandboxFiles(
963
1056
  };
964
1057
  return await runOptimistic(
965
1058
  "write",
966
- (nodes) => (exists ? nodes : insertNode(nodes, parent, node)),
1059
+ (nodes) => (exists ? nodes : insertNode(nodes, parent, node, rootPath)),
967
1060
  async () => {
968
1061
  if (!writeOptions.force && writeOptions.expectedContent !== undefined) {
969
1062
  const live = await client.fsRead(
970
1063
  workspaceId,
971
1064
  sessionId,
972
- { path },
1065
+ { path, ...(route ? { route } : {}) },
973
1066
  { signal: identitySignal },
974
1067
  );
975
1068
  if (identityGenerationRef.current !== identityGeneration) {
@@ -988,18 +1081,19 @@ export function useSandboxFiles(
988
1081
  path,
989
1082
  content,
990
1083
  overwrite: true,
1084
+ ...(route ? { route } : {}),
991
1085
  });
992
1086
  },
993
1087
  exists ? [] : [parent],
994
1088
  );
995
1089
  },
996
- [client, workspaceId, sessionId, tree, runOptimistic],
1090
+ [client, workspaceId, sessionId, tree, rootPath, route, runOptimistic],
997
1091
  );
998
1092
 
999
1093
  const createFile = useCallback(
1000
1094
  async (path: string): Promise<void> => {
1001
1095
  if (!sessionId) throw new Error("no session");
1002
- const parent = parentOf(path);
1096
+ const parent = parentWithinTree(path, rootPath);
1003
1097
  const node: FileTreeNode = {
1004
1098
  path,
1005
1099
  name: leafOf(path),
@@ -1008,23 +1102,24 @@ export function useSandboxFiles(
1008
1102
  };
1009
1103
  await runOptimistic(
1010
1104
  "create file",
1011
- (nodes) => insertNode(nodes, parent, node),
1105
+ (nodes) => insertNode(nodes, parent, node, rootPath),
1012
1106
  () =>
1013
1107
  client.fsWrite(workspaceId, sessionId, {
1014
1108
  path,
1015
1109
  content: "",
1016
1110
  overwrite: false,
1111
+ ...(route ? { route } : {}),
1017
1112
  }),
1018
1113
  [parent],
1019
1114
  );
1020
1115
  },
1021
- [client, workspaceId, sessionId, runOptimistic],
1116
+ [client, workspaceId, sessionId, rootPath, route, runOptimistic],
1022
1117
  );
1023
1118
 
1024
1119
  const createDir = useCallback(
1025
1120
  async (path: string): Promise<void> => {
1026
1121
  if (!sessionId) throw new Error("no session");
1027
- const parent = parentOf(path);
1122
+ const parent = parentWithinTree(path, rootPath);
1028
1123
  // A freshly-created dir is empty + expanded: children:[] (not undefined, so
1029
1124
  // it doesn't show the lazy-expand marker over a dir we KNOW is empty).
1030
1125
  const node: FileTreeNode = {
@@ -1035,12 +1130,17 @@ export function useSandboxFiles(
1035
1130
  };
1036
1131
  await runOptimistic(
1037
1132
  "create folder",
1038
- (nodes) => insertNode(nodes, parent, node),
1039
- () => client.fsMkdir(workspaceId, sessionId, { path, recursive: true }),
1133
+ (nodes) => insertNode(nodes, parent, node, rootPath),
1134
+ () =>
1135
+ client.fsMkdir(workspaceId, sessionId, {
1136
+ path,
1137
+ recursive: true,
1138
+ ...(route ? { route } : {}),
1139
+ }),
1040
1140
  [parent],
1041
1141
  );
1042
1142
  },
1043
- [client, workspaceId, sessionId, runOptimistic],
1143
+ [client, workspaceId, sessionId, rootPath, route, runOptimistic],
1044
1144
  );
1045
1145
 
1046
1146
  const deleteEntry = useCallback(
@@ -1048,19 +1148,24 @@ export function useSandboxFiles(
1048
1148
  if (!sessionId) throw new Error("no session");
1049
1149
  await runOptimistic(
1050
1150
  "delete",
1051
- (nodes) => removeNode(nodes, path),
1052
- () => client.fsDelete(workspaceId, sessionId, { path, recursive }),
1053
- [parentOf(path)],
1151
+ (nodes) => removeNode(nodes, path, rootPath),
1152
+ () =>
1153
+ client.fsDelete(workspaceId, sessionId, {
1154
+ path,
1155
+ recursive,
1156
+ ...(route ? { route } : {}),
1157
+ }),
1158
+ [parentWithinTree(path, rootPath)],
1054
1159
  );
1055
1160
  },
1056
- [client, workspaceId, sessionId, runOptimistic],
1161
+ [client, workspaceId, sessionId, rootPath, route, runOptimistic],
1057
1162
  );
1058
1163
 
1059
1164
  const moveEntry = useCallback(
1060
1165
  async (path: string, newPath: string, opts?: { overwrite?: boolean }): Promise<void> => {
1061
1166
  if (!sessionId) throw new Error("no session");
1062
- const from = parentOf(path);
1063
- const to = parentOf(newPath);
1167
+ const from = parentWithinTree(path, rootPath);
1168
+ const to = parentWithinTree(newPath, rootPath);
1064
1169
  await runOptimistic(
1065
1170
  "move",
1066
1171
  (nodes) => {
@@ -1068,18 +1173,19 @@ export function useSandboxFiles(
1068
1173
  if (!moving) return nodes; // not loaded — let the reconcile pick it up
1069
1174
  // Re-path the moved subtree, drop it from its old parent, splice into new.
1070
1175
  const moved = repathNode(moving, path, newPath);
1071
- return insertNode(removeNode(nodes, path), to, moved);
1176
+ return insertNode(removeNode(nodes, path, rootPath), to, moved, rootPath);
1072
1177
  },
1073
1178
  () =>
1074
1179
  client.fsMove(workspaceId, sessionId, {
1075
1180
  path,
1076
1181
  newPath,
1077
1182
  overwrite: opts?.overwrite ?? false,
1183
+ ...(route ? { route } : {}),
1078
1184
  }),
1079
1185
  to === from ? [from] : [from, to],
1080
1186
  );
1081
1187
  },
1082
- [client, workspaceId, sessionId, runOptimistic],
1188
+ [client, workspaceId, sessionId, rootPath, route, runOptimistic],
1083
1189
  );
1084
1190
 
1085
1191
  // Initial paint + reset on identity change. Source selection:
@@ -1107,14 +1213,14 @@ export function useSandboxFiles(
1107
1213
  const identityChanged = previousIdentityRef.current !== identityKey;
1108
1214
  previousIdentityRef.current = identityKey;
1109
1215
  if (identityChanged || !enabled) {
1216
+ if (identityChanged) eventReconcileInFlightRef.current = null;
1110
1217
  identityAbortRef.current.abort();
1111
1218
  identityAbortRef.current = new AbortController();
1112
1219
  identityGenerationRef.current += 1;
1113
1220
  lastSeqRef.current = 0;
1114
1221
  pendingParentsRef.current = new Set();
1115
1222
  pendingGitRef.current = false;
1116
- pendingAgentToolRef.current = false;
1117
- lastCommandRefreshAtRef.current = 0;
1223
+ eventReconcilePendingRef.current = false;
1118
1224
  if (debounceRef.current) {
1119
1225
  clearTimeout(debounceRef.current);
1120
1226
  debounceRef.current = null;
@@ -1155,6 +1261,7 @@ export function useSandboxFiles(
1155
1261
  refreshAbortRef.current = null;
1156
1262
  refreshGenerationRef.current += 1;
1157
1263
  eventReadAbortRef.current.abort();
1264
+ eventReconcilePendingRef.current = false;
1158
1265
  };
1159
1266
  }, [
1160
1267
  enabled,
@@ -1196,7 +1303,7 @@ export function useSandboxFiles(
1196
1303
  for (const file of status.files) {
1197
1304
  const code = file.worktree ?? file.index;
1198
1305
  const mapped = code ? GIT_STATUS_TO_TREE[code] : undefined;
1199
- if (mapped) overlay.set(qualifyStatusPath(root, file.path), mapped);
1306
+ if (mapped) overlay.set(qualifyStatusPath(root, file.path, rootPath), mapped);
1200
1307
  }
1201
1308
  }
1202
1309
  statusRef.current = overlay;
@@ -1208,9 +1315,47 @@ export function useSandboxFiles(
1208
1315
  if (identityGenerationRef.current === identityGeneration) setGitLoading(false);
1209
1316
  }
1210
1317
  },
1211
- [client, workspaceId, sessionId, repoPaths, applyStatus],
1318
+ [client, workspaceId, sessionId, repoPaths, rootPath, applyStatus],
1212
1319
  );
1213
1320
 
1321
+ const performEventReconcile = useCallback(async () => {
1322
+ const parents = pendingParentsRef.current;
1323
+ pendingParentsRef.current = new Set();
1324
+ const wantGit = pendingGitRef.current;
1325
+ pendingGitRef.current = false;
1326
+ const signal = eventReadAbortRef.current.signal;
1327
+ const paths = [...parents];
1328
+
1329
+ // File content is the primary Files-tab result. Reconcile it first, then add
1330
+ // the optional Git tint through a second serial request. This matches the
1331
+ // API's exact-provider serialization instead of making the two reads contend.
1332
+ await reconcilePaths(paths, signal);
1333
+ if (wantGit) await refreshGitOverlay(signal);
1334
+ }, [reconcilePaths, refreshGitOverlay]);
1335
+ const performEventReconcileRef = useRef(performEventReconcile);
1336
+ performEventReconcileRef.current = performEventReconcile;
1337
+ const reconcilePendingEvents = useCallback(async () => {
1338
+ eventReconcilePendingRef.current = true;
1339
+ const existing = eventReconcileInFlightRef.current;
1340
+ if (existing) return await existing;
1341
+ const identityGeneration = identityGenerationRef.current;
1342
+ const run = (async () => {
1343
+ while (
1344
+ eventReconcilePendingRef.current &&
1345
+ identityGenerationRef.current === identityGeneration
1346
+ ) {
1347
+ eventReconcilePendingRef.current = false;
1348
+ await performEventReconcileRef.current();
1349
+ }
1350
+ })();
1351
+ eventReconcileInFlightRef.current = run;
1352
+ try {
1353
+ await run;
1354
+ } finally {
1355
+ if (eventReconcileInFlightRef.current === run) eventReconcileInFlightRef.current = null;
1356
+ }
1357
+ }, []);
1358
+
1214
1359
  // Auto-reconcile on fs/git change notifications — TARGETED, never a root
1215
1360
  // collapse-reload, and de-duped against our OWN mutations.
1216
1361
  //
@@ -1221,6 +1366,8 @@ export function useSandboxFiles(
1221
1366
  // reconciles ONLY the affected parent directories (in place, expansion
1222
1367
  // preserved). Bursts are debounced into a single reconcile pass.
1223
1368
  // • A git.changed just re-tints (refreshes the status overlay) — no fs re-list.
1369
+ // • Generic command/tool output performs no provider reads. The canonical
1370
+ // turn-end capture refreshes the root once after the command burst settles.
1224
1371
  const events = options.events;
1225
1372
 
1226
1373
  useEffect(() => {
@@ -1238,12 +1385,11 @@ export function useSandboxFiles(
1238
1385
  }
1239
1386
  pendingParentsRef.current = new Set();
1240
1387
  pendingGitRef.current = false;
1241
- pendingAgentToolRef.current = false;
1388
+ eventReconcilePendingRef.current = false;
1242
1389
  return;
1243
1390
  }
1244
1391
  let sawNew = false;
1245
1392
  let contentChanged = false;
1246
- let sawCommandDelta = false;
1247
1393
  for (const event of events) {
1248
1394
  if (event.sequence <= lastSeqRef.current) continue;
1249
1395
  if (event.type === "fs.changed") {
@@ -1256,8 +1402,10 @@ export function useSandboxFiles(
1256
1402
  continue;
1257
1403
  contentChanged = true;
1258
1404
  for (const change of payload.changes ?? []) {
1259
- pendingParentsRef.current.add(parentOf(change.path));
1260
- if (change.oldPath) pendingParentsRef.current.add(parentOf(change.oldPath));
1405
+ pendingParentsRef.current.add(parentWithinTree(change.path, rootPath));
1406
+ if (change.oldPath) {
1407
+ pendingParentsRef.current.add(parentWithinTree(change.oldPath, rootPath));
1408
+ }
1261
1409
  }
1262
1410
  } else if (event.type === "git.changed") {
1263
1411
  sawNew = true;
@@ -1273,73 +1421,27 @@ export function useSandboxFiles(
1273
1421
  )
1274
1422
  continue;
1275
1423
  pendingGitRef.current = true;
1276
- } else if (event.type === "agent.toolCall.output") {
1277
- // Agent shell/apply-patch writes do not necessarily pass through the
1278
- // structured FS service, so they may have no fs.changed notification.
1279
- // A completed tool is a bounded invalidation point: refresh only the
1280
- // currently-loaded directory frontier, never poll and never traverse a
1281
- // collapsed subtree.
1282
- sawNew = true;
1283
- contentChanged = true;
1284
- pendingAgentToolRef.current = true;
1285
- // Shell/apply-patch writes bypass the structured Git service too, so no
1286
- // git.changed event is guaranteed. Refresh the summary/tints alongside
1287
- // the visible tree frontier; both remote reads can run concurrently.
1288
- pendingGitRef.current = true;
1289
- } else if (event.type === "sandbox.command.output.delta") {
1290
- // Ordinary shell writes can bypass Channel-A and therefore emit no
1291
- // fs.changed/git.changed event until the command completes. Reconcile
1292
- // the visible frontier at most once per second while output streams so
1293
- // Files and Changes stay useful without turning the dock into polling.
1294
- sawNew = true;
1295
- contentChanged = true;
1296
- sawCommandDelta = true;
1297
- pendingAgentToolRef.current = true;
1298
- pendingGitRef.current = true;
1299
1424
  }
1300
1425
  }
1301
1426
  // Advance the high-water mark past everything we've folded.
1302
1427
  for (const event of events)
1303
1428
  if (event.sequence > lastSeqRef.current) lastSeqRef.current = event.sequence;
1304
1429
  if (!sawNew) return;
1305
- if (contentChanged && !sawCommandDelta) setContentRevision((revision) => revision + 1);
1430
+ if (contentChanged) setContentRevision((revision) => revision + 1);
1306
1431
 
1307
1432
  if (debounceRef.current) clearTimeout(debounceRef.current);
1308
1433
  const identityGeneration = identityGenerationRef.current;
1309
- const delay = sawCommandDelta
1310
- ? Math.max(0, 1_000 - (Date.now() - lastCommandRefreshAtRef.current))
1311
- : 150;
1312
1434
  debounceRef.current = setTimeout(() => {
1313
1435
  debounceRef.current = null;
1314
1436
  if (identityGenerationRef.current !== identityGeneration) return;
1315
1437
  if (!acceptsEventReadsRef.current) {
1316
1438
  pendingParentsRef.current = new Set();
1317
1439
  pendingGitRef.current = false;
1318
- pendingAgentToolRef.current = false;
1319
1440
  return;
1320
1441
  }
1321
- if (sawCommandDelta) {
1322
- lastCommandRefreshAtRef.current = Date.now();
1323
- setContentRevision((revision) => revision + 1);
1324
- }
1325
- const parents = pendingParentsRef.current;
1326
- pendingParentsRef.current = new Set();
1327
- const wantGit = pendingGitRef.current;
1328
- pendingGitRef.current = false;
1329
- const wantAgentTool = pendingAgentToolRef.current;
1330
- pendingAgentToolRef.current = false;
1331
- // Reconcile the changed directories and Git overlay concurrently. Both are
1332
- // independent remote reads; applyStatus makes their completion order safe.
1333
- const eventReadSignal = eventReadAbortRef.current.signal;
1334
- void (async () => {
1335
- const paths = wantAgentTool ? loadedDirectoryPaths(treeRef.current) : [...parents];
1336
- await Promise.all([
1337
- wantGit ? refreshGitOverlay(eventReadSignal) : Promise.resolve(),
1338
- Promise.all(paths.map((path) => reconcilePath(path, eventReadSignal))),
1339
- ]);
1340
- })();
1341
- }, delay);
1342
- }, [enabled, active, acceptsEventReads, events, reconcilePath, refreshGitOverlay]);
1442
+ void reconcilePendingEvents();
1443
+ }, 150);
1444
+ }, [enabled, active, acceptsEventReads, events, rootPath, reconcilePendingEvents]);
1343
1445
 
1344
1446
  useEffect(() => {
1345
1447
  if (active && acceptsEventReads) return;
@@ -1349,7 +1451,7 @@ export function useSandboxFiles(
1349
1451
  }
1350
1452
  pendingParentsRef.current = new Set();
1351
1453
  pendingGitRef.current = false;
1352
- pendingAgentToolRef.current = false;
1454
+ eventReconcilePendingRef.current = false;
1353
1455
  }, [active, acceptsEventReads]);
1354
1456
 
1355
1457
  useEffect(
@@ -1357,6 +1459,7 @@ export function useSandboxFiles(
1357
1459
  refreshAbortRef.current?.abort();
1358
1460
  identityAbortRef.current.abort();
1359
1461
  eventReadAbortRef.current.abort();
1462
+ eventReconcilePendingRef.current = false;
1360
1463
  identityGenerationRef.current += 1;
1361
1464
  if (debounceRef.current) clearTimeout(debounceRef.current);
1362
1465
  },