@kortix/ui 0.14.8 → 0.14.9

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 (528) hide show
  1. package/README.md +2 -2
  2. package/dist/_vendor/kortix-shared.js +63 -3
  3. package/dist/_vendor/shared/app-config.d.ts +175 -0
  4. package/dist/_vendor/shared/channel-message.d.ts +34 -5
  5. package/dist/_vendor/shared/index.d.ts +1 -0
  6. package/dist/_vendor/shared/space-slug.d.ts +37 -0
  7. package/dist/access/access/access-detail-shell.js +1 -1
  8. package/dist/access/access/principal-picker.d.ts +11 -0
  9. package/dist/access/access/principal-picker.js +3 -2
  10. package/dist/apps/app-preview-pool.d.ts +7 -2
  11. package/dist/apps/app-preview-pool.js +11 -5
  12. package/dist/apps/app-shared.d.ts +22 -15
  13. package/dist/apps/app-shared.js +37 -19
  14. package/dist/apps/index.d.ts +2 -2
  15. package/dist/apps/index.js +2 -2
  16. package/dist/artifacts/artifact-frame.d.ts +16 -0
  17. package/dist/artifacts/artifact-frame.js +51 -0
  18. package/dist/artifacts/artifact-session.d.ts +18 -0
  19. package/dist/artifacts/artifact-session.js +101 -0
  20. package/dist/artifacts/frame-policy.d.ts +30 -0
  21. package/dist/artifacts/frame-policy.js +61 -0
  22. package/dist/artifacts/index.d.ts +3 -0
  23. package/dist/artifacts/index.js +5 -0
  24. package/dist/browser/sandbox-browser-chrome.d.ts +4 -1
  25. package/dist/browser/sandbox-browser-chrome.js +2 -2
  26. package/dist/customize/agents/agent-grant-pages.d.ts +5 -1
  27. package/dist/customize/agents/agent-grant-pages.js +30 -4
  28. package/dist/customize/agents/agent-page.js +3 -1
  29. package/dist/customize/agents/agent-policy-sets.d.ts +43 -0
  30. package/dist/customize/agents/agent-policy-sets.js +72 -0
  31. package/dist/customize/agents/agent-triggers-section.js +2 -1
  32. package/dist/customize/connectors/add-mcp/add-mcp-server-dialog.d.ts +6 -1
  33. package/dist/customize/connectors/add-mcp/add-mcp-server-dialog.js +10 -4
  34. package/dist/customize/connectors/agent-access.d.ts +35 -0
  35. package/dist/customize/connectors/agent-access.js +77 -0
  36. package/dist/customize/connectors/catalog/browse-sections.d.ts +2 -29
  37. package/dist/customize/connectors/catalog/browse-sections.js +7 -74
  38. package/dist/customize/connectors/catalog/catalog-entry.d.ts +18 -37
  39. package/dist/customize/connectors/catalog/catalog-entry.js +28 -40
  40. package/dist/customize/connectors/catalog/connector-browse.d.ts +3 -32
  41. package/dist/customize/connectors/catalog/connector-browse.js +19 -50
  42. package/dist/customize/connectors/catalog/use-catalog.d.ts +20 -59
  43. package/dist/customize/connectors/catalog/use-catalog.js +48 -175
  44. package/dist/customize/connectors/connector-identity.d.ts +9 -11
  45. package/dist/customize/connectors/connector-identity.js +13 -42
  46. package/dist/customize/connectors/connector-moves-banner.d.ts +2 -1
  47. package/dist/customize/connectors/connector-moves-banner.js +4 -6
  48. package/dist/customize/connectors/connector-routes.d.ts +21 -9
  49. package/dist/customize/connectors/connector-routes.js +29 -20
  50. package/dist/customize/connectors/connectors-page.js +53 -82
  51. package/dist/customize/connectors/detail/app-page.js +226 -98
  52. package/dist/customize/connectors/detail/computer-app-page.d.ts +16 -0
  53. package/dist/customize/connectors/detail/computer-app-page.js +39 -0
  54. package/dist/customize/connectors/detail/connector-accounts.js +3 -1
  55. package/dist/customize/connectors/detail/connector-advanced-tools.d.ts +4 -1
  56. package/dist/customize/connectors/detail/connector-advanced-tools.js +2 -2
  57. package/dist/customize/connectors/detail/connector-agents.d.ts +14 -0
  58. package/dist/customize/connectors/detail/connector-agents.js +69 -0
  59. package/dist/customize/connectors/detail/connector-detail-tabs.js +2 -1
  60. package/dist/customize/connectors/detail/connector-move-row.d.ts +8 -5
  61. package/dist/customize/connectors/detail/connector-move-row.js +23 -14
  62. package/dist/customize/connectors/detail/connector-page.d.ts +4 -1
  63. package/dist/customize/connectors/detail/connector-page.js +14 -10
  64. package/dist/customize/connectors/detail/connector-policy-sets.d.ts +28 -0
  65. package/dist/customize/connectors/detail/connector-policy-sets.js +203 -0
  66. package/dist/customize/connectors/detail/connector-settings.d.ts +4 -1
  67. package/dist/customize/connectors/detail/connector-settings.js +7 -3
  68. package/dist/customize/connectors/detail/connector-status.d.ts +19 -6
  69. package/dist/customize/connectors/detail/connector-status.js +34 -10
  70. package/dist/customize/connectors/detail/connector-tabs.d.ts +4 -1
  71. package/dist/customize/connectors/detail/connector-tabs.js +8 -2
  72. package/dist/customize/connectors/detail/connector-tools.d.ts +3 -3
  73. package/dist/customize/connectors/detail/connector-tools.js +105 -48
  74. package/dist/customize/connectors/detail/email-inboxes.d.ts +43 -0
  75. package/dist/customize/connectors/detail/email-inboxes.js +184 -0
  76. package/dist/customize/connectors/detail/provider-info.d.ts +1 -1
  77. package/dist/customize/connectors/detail/provider-info.js +2 -2
  78. package/dist/customize/connectors/detail/status-badge.d.ts +1 -1
  79. package/dist/customize/connectors/detail/status-badge.js +4 -5
  80. package/dist/customize/connectors/detail/use-connector-detail.d.ts +6 -0
  81. package/dist/customize/connectors/detail/use-connector-detail.js +14 -4
  82. package/dist/customize/connectors/install/install-dialog.d.ts +23 -0
  83. package/dist/customize/connectors/install/install-dialog.js +40 -0
  84. package/dist/customize/connectors/install/install.d.ts +13 -2
  85. package/dist/customize/connectors/install/install.js +9 -4
  86. package/dist/customize/connectors/install/use-install.d.ts +19 -2
  87. package/dist/customize/connectors/install/use-install.js +65 -37
  88. package/dist/customize/connectors/install/way-kind.d.ts +5 -0
  89. package/dist/customize/connectors/install/way-kind.js +16 -0
  90. package/dist/customize/connectors/provider-label.d.ts +2 -1
  91. package/dist/customize/connectors/provider-label.js +13 -3
  92. package/dist/customize/connectors/tools/pattern-rule-draft.d.ts +4 -0
  93. package/dist/customize/connectors/tools/pattern-rule-draft.js +12 -3
  94. package/dist/customize/connectors/tools/policy-sets.d.ts +27 -0
  95. package/dist/customize/connectors/tools/policy-sets.js +41 -0
  96. package/dist/customize/connectors/tools/tool-groups.d.ts +32 -9
  97. package/dist/customize/connectors/tools/tool-groups.js +78 -16
  98. package/dist/customize/connectors/tools/tool-policy-control.d.ts +11 -10
  99. package/dist/customize/connectors/tools/tool-policy-control.js +15 -16
  100. package/dist/customize/connectors/tools/tool-policy-labels.d.ts +6 -5
  101. package/dist/customize/connectors/tools/tool-policy-labels.js +17 -16
  102. package/dist/customize/connectors/tools/tool-policy.d.ts +50 -4
  103. package/dist/customize/connectors/tools/tool-policy.js +74 -8
  104. package/dist/customize/connectors/use-agent-access.d.ts +85 -0
  105. package/dist/customize/connectors/use-agent-access.js +72 -0
  106. package/dist/customize/migrate-to-v2/index.d.ts +1 -2
  107. package/dist/customize/migrate-to-v2/index.js +1 -2
  108. package/dist/customize/migrate-to-v2/migration-prompt.d.ts +12 -25
  109. package/dist/customize/migrate-to-v2/migration-prompt.js +34 -236
  110. package/dist/customize/migrate-to-v2/upgrade-defs.d.ts +1 -1
  111. package/dist/customize/migrate-to-v2/upgrade-defs.js +7 -6
  112. package/dist/customize/migrate-to-v2/upgrade-view.js +7 -7
  113. package/dist/customize/migrate-to-v2/use-run-upgrade.d.ts +3 -3
  114. package/dist/customize/providers/provider-accordion.js +17 -2
  115. package/dist/customize/sections/component/channel-access-panel.d.ts +16 -0
  116. package/dist/customize/sections/component/channel-access-panel.js +146 -0
  117. package/dist/customize/sections/component/channel-connect-hero.d.ts +3 -2
  118. package/dist/customize/sections/component/channel-connect-hero.js +3 -2
  119. package/dist/customize/sections/component/channel-hero-frame.d.ts +31 -0
  120. package/dist/customize/sections/component/channel-hero-frame.js +40 -0
  121. package/dist/customize/sections/component/channel-mocks.d.ts +6 -0
  122. package/dist/customize/sections/component/channel-mocks.js +46 -0
  123. package/dist/customize/sections/component/channel-row.d.ts +13 -3
  124. package/dist/customize/sections/component/channel-row.js +10 -4
  125. package/dist/customize/sections/component/email-connect-form.d.ts +27 -0
  126. package/dist/customize/sections/component/email-connect-form.js +183 -0
  127. package/dist/customize/sections/component/slack-connect-card.d.ts +7 -0
  128. package/dist/customize/sections/component/slack-connect-card.js +2 -3
  129. package/dist/customize/sections/component/teams-connect-card.d.ts +7 -0
  130. package/dist/customize/sections/component/teams-connect-card.js +2 -3
  131. package/dist/customize/sections/component/telegram-channel-row.js +16 -25
  132. package/dist/customize/sections/connectors-view.d.ts +1 -15
  133. package/dist/customize/sections/connectors-view.js +81 -314
  134. package/dist/customize/sections/view/agent-editor-catalog.js +5 -0
  135. package/dist/customize/sections/view/channels-view.d.ts +5 -1
  136. package/dist/customize/sections/view/channels-view.js +61 -75
  137. package/dist/customize/sections/view/gateway/gateway-overview.js +10 -2
  138. package/dist/customize/settings/tabs/experimental-tab.js +35 -16
  139. package/dist/customize/shared/catalog/catalog-empty-state.d.ts +1 -12
  140. package/dist/customize/shared/catalog/catalog-empty-state.js +3 -3
  141. package/dist/customize/shared/channels-switch-dialog.d.ts +18 -0
  142. package/dist/customize/shared/channels-switch-dialog.js +30 -0
  143. package/dist/customize/shared/feature-gate-screen.js +27 -9
  144. package/dist/customize/shared/use-customize-prefetch.d.ts +2 -3
  145. package/dist/customize/shared/use-customize-prefetch.js +9 -27
  146. package/dist/customize/triggers/schedule/post-to-field.d.ts +18 -0
  147. package/dist/customize/triggers/schedule/post-to-field.js +49 -0
  148. package/dist/customize/triggers/schedule/schedule-copy.d.ts +10 -6
  149. package/dist/customize/triggers/schedule/schedule-copy.js +42 -11
  150. package/dist/customize/triggers/schedule/schedule-detail-sheet.js +2 -1
  151. package/dist/customize/triggers/schedule/trigger-composer-logic.d.ts +22 -0
  152. package/dist/customize/triggers/schedule/trigger-composer-logic.js +25 -0
  153. package/dist/customize/triggers/schedule/trigger-composer.d.ts +5 -0
  154. package/dist/customize/triggers/schedule/trigger-composer.js +23 -14
  155. package/dist/customize/triggers/schedule/trigger-edit-logic.js +3 -0
  156. package/dist/customize/triggers/schedule/trigger-filter.js +1 -1
  157. package/dist/customize/triggers/schedule/trigger-runs-logic.d.ts +2 -2
  158. package/dist/customize/triggers/schedule/trigger-runs-panel.js +1 -0
  159. package/dist/customize/triggers/schedule/trigger-toast.js +2 -1
  160. package/dist/customize/triggers/schedule-view.js +5 -2
  161. package/dist/files/index.d.ts +1 -0
  162. package/dist/files/index.js +1 -0
  163. package/dist/files/project/change-request-recovery.d.ts +1 -26
  164. package/dist/files/project/change-request-recovery.js +1 -59
  165. package/dist/files/project/components/change-request-detail-dialog.js +13 -3
  166. package/dist/files/project/components/drive-explorer.js +15 -15
  167. package/dist/files/project/components/drive-toolbar.d.ts +16 -2
  168. package/dist/files/project/components/drive-toolbar.js +14 -5
  169. package/dist/files/project/hooks/use-change-request-recovery.js +3 -19
  170. package/dist/files/project/hooks/use-change-requests.d.ts +1 -0
  171. package/dist/files/project/hooks/use-change-requests.js +11 -2
  172. package/dist/files/renderers/image-frame.d.ts +46 -0
  173. package/dist/files/renderers/image-frame.js +59 -0
  174. package/dist/files/renderers/image-renderer.d.ts +11 -1
  175. package/dist/files/renderers/image-renderer.js +13 -3
  176. package/dist/files/renderers/pdf/pdf-clipboard.d.ts +2 -0
  177. package/dist/files/renderers/pdf/pdf-clipboard.js +21 -0
  178. package/dist/files/renderers/pdf/pdf-viewer.js +4 -2
  179. package/dist/files/renderers/show-content-renderer.d.ts +9 -1
  180. package/dist/files/renderers/show-content-renderer.js +56 -14
  181. package/dist/files/runtime/file-source.js +2 -0
  182. package/dist/files/runtime/hooks/use-binary-blob.d.ts +10 -1
  183. package/dist/files/runtime/hooks/use-binary-blob.js +10 -4
  184. package/dist/files/runtime/hooks/use-shared-image.d.ts +41 -0
  185. package/dist/files/runtime/hooks/use-shared-image.js +177 -0
  186. package/dist/files/viewer/file-content-renderer.d.ts +22 -1
  187. package/dist/files/viewer/file-content-renderer.js +50 -7
  188. package/dist/files/viewer/file-header.d.ts +7 -3
  189. package/dist/files/viewer/file-header.js +20 -11
  190. package/dist/files/viewer/file-preview-modal.d.ts +1 -1
  191. package/dist/files/viewer/file-preview-modal.js +10 -22
  192. package/dist/files/viewer/file-source.d.ts +13 -0
  193. package/dist/files/viewer/use-file-content-state.d.ts +2 -0
  194. package/dist/files/viewer/use-file-content-state.js +28 -10
  195. package/dist/files/viewer/viewer-actions.d.ts +3 -1
  196. package/dist/files/viewer/viewer-actions.js +2 -2
  197. package/dist/hooks/use-media-query.d.ts +1 -0
  198. package/dist/hooks/use-mobile.d.ts +5 -0
  199. package/dist/hooks/use-mobile.js +8 -0
  200. package/dist/i18n/en.d.ts +873 -85
  201. package/dist/i18n/en.js +888 -100
  202. package/dist/i18n/ui-catalog/localize-ui-catalog.js +4 -0
  203. package/dist/i18n/ui-catalog/remaining-ui-translation-keys.generated.js +15 -3
  204. package/dist/index.d.ts +2 -2
  205. package/dist/index.js +2 -2
  206. package/dist/internal/account-hub/account-settings-sidebar.js +1 -1
  207. package/dist/internal/account-hub/hub-sheet.d.ts +9 -1
  208. package/dist/internal/account-hub/hub-sheet.js +21 -5
  209. package/dist/internal/account-hub/sections.d.ts +1 -1
  210. package/dist/internal/account-hub/sections.js +2 -1
  211. package/dist/internal/auth/auth-card-shell.js +3 -3
  212. package/dist/internal/components/agent-mark.js +5 -2
  213. package/dist/internal/connector-logo/connector-logo-source.d.ts +63 -0
  214. package/dist/internal/connector-logo/connector-logo-source.js +48 -0
  215. package/dist/internal/connector-logo/connector-logo.d.ts +39 -0
  216. package/dist/internal/connector-logo/connector-logo.js +56 -0
  217. package/dist/internal/connector-logo/connector-mark-files.d.ts +11 -0
  218. package/dist/internal/connector-logo/connector-mark-files.js +35 -0
  219. package/dist/internal/files/file-read-retry.d.ts +13 -0
  220. package/dist/internal/files/file-read-retry.js +23 -3
  221. package/dist/internal/files/session-drive-files.d.ts +5 -1
  222. package/dist/internal/files/session-drive-files.js +7 -2
  223. package/dist/internal/hooks/channels/use-channels-installations.d.ts +32 -5
  224. package/dist/internal/hooks/channels/use-channels-installations.js +55 -7
  225. package/dist/internal/hooks/channels/use-email-inboxes.d.ts +19 -0
  226. package/dist/internal/hooks/channels/use-email-inboxes.js +48 -0
  227. package/dist/internal/hooks/connectors/use-add-account.js +1 -1
  228. package/dist/internal/hooks/index.d.ts +1 -1
  229. package/dist/internal/hooks/index.js +1 -1
  230. package/dist/internal/hooks/projects/use-new-project-session.d.ts +9 -0
  231. package/dist/internal/hooks/projects/use-new-project-session.js +28 -12
  232. package/dist/internal/hooks/use-auto-scroll.d.ts +8 -1
  233. package/dist/internal/hooks/use-auto-scroll.js +4 -4
  234. package/dist/internal/icons/telegram.d.ts +5 -0
  235. package/dist/internal/icons/telegram.js +6 -1
  236. package/dist/internal/layout/nav-hover-card.d.ts +35 -0
  237. package/dist/internal/layout/nav-hover-card.js +84 -0
  238. package/dist/internal/layout/project-nav-row.d.ts +3 -1
  239. package/dist/internal/layout/project-nav-row.js +3 -2
  240. package/dist/internal/layout/project-page-header.d.ts +4 -2
  241. package/dist/internal/layout/project-page-header.js +4 -3
  242. package/dist/internal/layout/project-settings-nav.js +59 -2
  243. package/dist/internal/lib/browser-error-noise.d.ts +1 -1
  244. package/dist/internal/lib/browser-error-noise.js +1 -1
  245. package/dist/internal/lib/browser-noise/rules/injected-scripts.d.ts +7 -0
  246. package/dist/internal/lib/browser-noise/rules/injected-scripts.js +15 -0
  247. package/dist/internal/lib/manifest-version.d.ts +1 -1
  248. package/dist/internal/lib/manifest-version.js +2 -2
  249. package/dist/internal/lib/memory-href.d.ts +16 -0
  250. package/dist/internal/lib/memory-href.js +46 -0
  251. package/dist/internal/lib/use-project-feature-flags.js +9 -9
  252. package/dist/internal/lib/utils/heic-convert.d.ts +1 -1
  253. package/dist/internal/lib/utils/heic-convert.js +3 -4
  254. package/dist/internal/lib/web-notifications.d.ts +20 -0
  255. package/dist/internal/lib/web-notifications.js +48 -4
  256. package/dist/internal/notifications/notification-bell.d.ts +4 -1
  257. package/dist/internal/notifications/notification-bell.js +2 -2
  258. package/dist/internal/selection/selection-bar.d.ts +2 -1
  259. package/dist/internal/selection/selection-bar.js +1 -1
  260. package/dist/internal/stores/file-preview-store.d.ts +29 -2
  261. package/dist/internal/stores/file-preview-store.js +19 -1
  262. package/dist/internal/stores/kortix-computer-store.js +3 -0
  263. package/dist/internal/stores/sidebar-nav-store.d.ts +1 -1
  264. package/dist/internal/stores/sidebar-nav-store.js +3 -1
  265. package/dist/lib/routes.d.ts +13 -1
  266. package/dist/lib/routes.js +13 -0
  267. package/dist/markdown/index.d.ts +2 -1
  268. package/dist/markdown/index.js +2 -1
  269. package/dist/markdown/setup-links/approval-decision.d.ts +1 -0
  270. package/dist/markdown/setup-links/approval-decision.js +6 -2
  271. package/dist/markdown/setup-links/connector-handshake.d.ts +3 -1
  272. package/dist/markdown/setup-links/connector-handshake.js +12 -10
  273. package/dist/markdown/setup-links/secret-intake-form.d.ts +0 -10
  274. package/dist/markdown/setup-links/secret-intake-form.js +1 -1
  275. package/dist/markdown/setup-links/show-connect-hero.d.ts +28 -0
  276. package/dist/markdown/setup-links/show-connect-hero.js +91 -0
  277. package/dist/markdown/unified-markdown.js +5 -5
  278. package/dist/primitives/badge.d.ts +1 -1
  279. package/dist/primitives/badge.js +2 -0
  280. package/dist/primitives/disclosure.js +14 -2
  281. package/dist/primitives/empty-art.d.ts +12 -0
  282. package/dist/primitives/empty-art.js +11 -0
  283. package/dist/primitives/mermaid-renderer.d.ts +2 -0
  284. package/dist/primitives/mermaid-renderer.js +21 -15
  285. package/dist/primitives/resizable.js +6 -1
  286. package/dist/primitives/resize-seam.d.ts +11 -0
  287. package/dist/primitives/resize-seam.js +12 -0
  288. package/dist/primitives/sidebar-panel.js +4 -7
  289. package/dist/primitives/tabs.d.ts +14 -1
  290. package/dist/primitives/tabs.js +1 -1
  291. package/dist/providers/ui-provider.d.ts +9 -0
  292. package/dist/session/composer/ComposerAboveCard.d.ts +1 -1
  293. package/dist/session/composer/ComposerAboveCard.js +2 -2
  294. package/dist/session/composer/ComposerCard.d.ts +1 -1
  295. package/dist/session/composer/ComposerCard.js +3 -3
  296. package/dist/session/composer/agent-selector.d.ts +13 -1
  297. package/dist/session/composer/agent-selector.js +25 -5
  298. package/dist/session/composer/composer-agent-access.d.ts +15 -0
  299. package/dist/session/composer/composer-agent-access.js +27 -0
  300. package/dist/session/composer/composer-chat-input.d.ts +8 -2
  301. package/dist/session/composer/composer-chat-input.js +8 -6
  302. package/dist/session/composer/composer-logic.d.ts +16 -0
  303. package/dist/session/composer/composer-logic.js +12 -0
  304. package/dist/session/composer/composer-underbar.d.ts +3 -1
  305. package/dist/session/composer/composer-underbar.js +4 -3
  306. package/dist/session/composer/composer.d.ts +15 -1
  307. package/dist/session/composer/composer.js +19 -1
  308. package/dist/session/composer/draft/composer-draft.d.ts +6 -2
  309. package/dist/session/composer/draft/composer-draft.js +5 -1
  310. package/dist/session/composer/reasoning-effort-selector.js +1 -1
  311. package/dist/session/models/chatgpt-connection-button.d.ts +11 -0
  312. package/dist/session/models/chatgpt-connection-button.js +54 -0
  313. package/dist/session/models/model-selector.js +5 -2
  314. package/dist/session/models/use-model-connection-gate.d.ts +5 -5
  315. package/dist/session/models/use-model-connection-gate.js +5 -5
  316. package/dist/session/outcomes/jump-to-outcome.d.ts +10 -0
  317. package/dist/session/outcomes/jump-to-outcome.js +11 -0
  318. package/dist/session/outcomes/session-outcomes-bridge.d.ts +14 -0
  319. package/dist/session/outcomes/session-outcomes-bridge.js +12 -0
  320. package/dist/session/outcomes/session-outcomes-provider.d.ts +3 -1
  321. package/dist/session/outcomes/session-outcomes-provider.js +33 -3
  322. package/dist/session/panel/easy/app-preview.d.ts +10 -0
  323. package/dist/session/panel/easy/app-preview.js +31 -12
  324. package/dist/session/panel/easy/change-requests-card.d.ts +7 -0
  325. package/dist/session/panel/easy/change-requests-card.js +17 -0
  326. package/dist/session/panel/easy/easy-panel-logic.js +7 -1
  327. package/dist/session/panel/easy/easy-panel.js +5 -2
  328. package/dist/session/panel/easy/file-preview.js +1 -1
  329. package/dist/session/panel/easy/outputs-card.d.ts +2 -1
  330. package/dist/session/panel/easy/outputs-card.js +3 -2
  331. package/dist/session/panel/mobile-tool-drawer.js +7 -1
  332. package/dist/session/panel/session-audit-panel.js +9 -2
  333. package/dist/session/panel/session-detail-panel.js +9 -3
  334. package/dist/session/panel/session-files-explorer.d.ts +9 -0
  335. package/dist/session/panel/session-files-explorer.js +12 -3
  336. package/dist/session/panel/session-panel-provider.d.ts +15 -1
  337. package/dist/session/panel/session-panel-provider.js +32 -7
  338. package/dist/session/panel/shared/derive-panels.d.ts +8 -1
  339. package/dist/session/panel/shared/derive-panels.js +12 -2
  340. package/dist/session/panel/shared/output-priority.js +2 -0
  341. package/dist/session/participants/session-participants.d.ts +5 -1
  342. package/dist/session/participants/session-participants.js +9 -2
  343. package/dist/session/prompts/approval-request.d.ts +5 -0
  344. package/dist/session/prompts/approval-request.js +7 -1
  345. package/dist/session/prompts/session-approval-review.d.ts +9 -0
  346. package/dist/session/prompts/session-approval-review.js +18 -0
  347. package/dist/session/status/session-error-banner.js +2 -49
  348. package/dist/session/status/session-starting-loader.d.ts +14 -2
  349. package/dist/session/status/session-starting-loader.js +16 -4
  350. package/dist/session/thread/after-first-prompt.d.ts +7 -0
  351. package/dist/session/thread/after-first-prompt.js +8 -0
  352. package/dist/session/thread/detect-command.d.ts +12 -0
  353. package/dist/session/thread/detect-command.js +20 -0
  354. package/dist/session/thread/session-chat.d.ts +3 -1
  355. package/dist/session/thread/session-chat.js +377 -252
  356. package/dist/session/thread/session-thread.d.ts +3 -1
  357. package/dist/session/thread/session-thread.js +9 -2
  358. package/dist/session/thread/thread-boot-rows.d.ts +36 -0
  359. package/dist/session/thread/thread-boot-rows.js +89 -0
  360. package/dist/session/thread/transcript-rows.d.ts +31 -0
  361. package/dist/session/thread/transcript-rows.js +3 -0
  362. package/dist/session/thread/transcript.d.ts +0 -9
  363. package/dist/session/thread/transcript.js +104 -19
  364. package/dist/session/tool/shared/infrastructure-contexts.d.ts +28 -0
  365. package/dist/session/tool/shared/infrastructure-contexts.js +31 -3
  366. package/dist/session/tool/tool-part-renderer.d.ts +5 -4
  367. package/dist/session/tool/tool-part-renderer.js +8 -3
  368. package/dist/session/tool/tools/bash-tool.js +7 -6
  369. package/dist/session/tool/tools/channel-send-card.d.ts +1 -2
  370. package/dist/session/tool/tools/channel-send.d.ts +2 -1
  371. package/dist/session/tool/tools/codemode-tool.js +1 -1
  372. package/dist/session/tool/tools/connector-tools.js +1 -1
  373. package/dist/session/tool/tools/image-gen-tool.d.ts +1 -1
  374. package/dist/session/tool/tools/image-gen-tool.js +240 -53
  375. package/dist/session/tool/tools/show-tool.js +19 -8
  376. package/dist/session/turn/channel-brand.d.ts +8 -3
  377. package/dist/session/turn/channel-brand.js +20 -4
  378. package/dist/session/turn/message-author.d.ts +8 -0
  379. package/dist/session/turn/message-author.js +24 -0
  380. package/dist/session/turn/message-row.d.ts +52 -0
  381. package/dist/session/turn/message-row.js +44 -0
  382. package/dist/session/turn/plan-anchor.d.ts +1 -1
  383. package/dist/session/turn/plan-anchor.js +1 -1
  384. package/dist/session/turn/user-message.d.ts +7 -1
  385. package/dist/session/turn/user-message.js +21 -7
  386. package/dist/styles.css +1 -1
  387. package/dist/workspace/home/access-requests-bell.d.ts +9 -2
  388. package/dist/workspace/home/access-requests-bell.js +20 -6
  389. package/dist/workspace/home/first-chat.d.ts +8 -21
  390. package/dist/workspace/home/first-chat.js +22 -72
  391. package/dist/workspace/index.d.ts +6 -0
  392. package/dist/workspace/index.js +6 -0
  393. package/dist/workspace/layout/project-home.d.ts +14 -2
  394. package/dist/workspace/layout/project-home.js +60 -21
  395. package/dist/workspace/layout/use-project-home-send.d.ts +9 -1
  396. package/dist/workspace/layout/use-project-home-send.js +82 -3
  397. package/dist/workspace/memory/body-doc.d.ts +17 -0
  398. package/dist/workspace/memory/body-doc.js +208 -0
  399. package/dist/workspace/memory/body-extensions.d.ts +38 -0
  400. package/dist/workspace/memory/body-extensions.js +103 -0
  401. package/dist/workspace/memory/company-memory-page.d.ts +8 -0
  402. package/dist/workspace/memory/company-memory-page.js +16 -0
  403. package/dist/workspace/memory/memory-all.d.ts +20 -0
  404. package/dist/workspace/memory/memory-all.js +27 -0
  405. package/dist/workspace/memory/memory-body.d.ts +19 -0
  406. package/dist/workspace/memory/memory-body.js +139 -0
  407. package/dist/workspace/memory/memory-empty.d.ts +13 -0
  408. package/dist/workspace/memory/memory-empty.js +28 -0
  409. package/dist/workspace/memory/memory-history.d.ts +27 -0
  410. package/dist/workspace/memory/memory-history.js +108 -0
  411. package/dist/workspace/memory/memory-labels.d.ts +16 -0
  412. package/dist/workspace/memory/memory-labels.js +47 -0
  413. package/dist/workspace/memory/memory-page-list.d.ts +34 -0
  414. package/dist/workspace/memory/memory-page-list.js +156 -0
  415. package/dist/workspace/memory/memory-reader.d.ts +58 -0
  416. package/dist/workspace/memory/memory-reader.js +123 -0
  417. package/dist/workspace/memory/memory-view.d.ts +21 -0
  418. package/dist/workspace/memory/memory-view.js +124 -0
  419. package/dist/workspace/memory/remember-popover.d.ts +19 -0
  420. package/dist/workspace/memory/remember-popover.js +61 -0
  421. package/dist/workspace/memory/remember-this.d.ts +12 -0
  422. package/dist/workspace/memory/remember-this.js +89 -0
  423. package/dist/workspace/memory/source-mark.d.ts +18 -0
  424. package/dist/workspace/memory/source-mark.js +79 -0
  425. package/dist/workspace/memory/used-memory-chip.d.ts +14 -0
  426. package/dist/workspace/memory/used-memory-chip.js +61 -0
  427. package/dist/workspace/new/advanced-fields.d.ts +2 -1
  428. package/dist/workspace/new/advanced-fields.js +2 -2
  429. package/dist/workspace/new/source-param.d.ts +1 -1
  430. package/dist/workspace/new/source-param.js +3 -1
  431. package/dist/workspace/new/use-create-workspace.d.ts +8 -10
  432. package/dist/workspace/new/use-create-workspace.js +3 -3
  433. package/dist/workspace/projects/onboarding/app-logo.d.ts +7 -2
  434. package/dist/workspace/projects/onboarding/app-logo.js +6 -13
  435. package/dist/workspace/projects/policies-panel.js +5 -3
  436. package/dist/workspace/review/review-actions.d.ts +4 -2
  437. package/dist/workspace/review/review-actions.js +8 -2
  438. package/dist/workspace/review/review-center.js +11 -7
  439. package/dist/workspace/review/review-detail.d.ts +10 -0
  440. package/dist/workspace/review/review-detail.js +101 -13
  441. package/dist/workspace/saved-session-skeleton.d.ts +2 -2
  442. package/dist/workspace/saved-session-skeleton.js +3 -3
  443. package/dist/workspace/session-workspace.js +15 -13
  444. package/dist/workspace/sessions/project-sessions-view.js +3 -2
  445. package/dist/workspace/sessions/sessions-toolbar.d.ts +3 -1
  446. package/dist/workspace/sessions/sessions-toolbar.js +2 -2
  447. package/dist/workspace/sidebar/footer/project-apps-nav.d.ts +5 -0
  448. package/dist/workspace/sidebar/footer/project-apps-nav.js +41 -2
  449. package/dist/workspace/sidebar/footer/project-artifacts-nav.d.ts +6 -0
  450. package/dist/workspace/sidebar/footer/project-artifacts-nav.js +22 -0
  451. package/dist/workspace/sidebar/footer/project-manifest-upgrade-alert.js +13 -13
  452. package/dist/workspace/sidebar/footer/project-memory-nav.d.ts +7 -0
  453. package/dist/workspace/sidebar/footer/project-memory-nav.js +19 -0
  454. package/dist/workspace/sidebar/nav-row-meta.js +14 -2
  455. package/dist/workspace/sidebar/project-nav-items.js +3 -0
  456. package/dist/workspace/sidebar/project-session-list.js +68 -56
  457. package/dist/workspace/sidebar/project-sidebar.js +9 -2
  458. package/dist/workspace/sidebar/project-spaces-nav.d.ts +8 -0
  459. package/dist/workspace/sidebar/project-spaces-nav.js +69 -0
  460. package/dist/workspace/sidebar/session-group-order.d.ts +9 -0
  461. package/dist/workspace/sidebar/session-group-order.js +25 -0
  462. package/dist/workspace/sidebar/session-group-sections.d.ts +62 -0
  463. package/dist/workspace/sidebar/session-group-sections.js +267 -0
  464. package/dist/workspace/sidebar/session-list-chrome.d.ts +28 -0
  465. package/dist/workspace/sidebar/session-list-chrome.js +50 -0
  466. package/dist/workspace/spaces/channel-glyph.d.ts +5 -0
  467. package/dist/workspace/spaces/channel-glyph.js +6 -0
  468. package/dist/workspace/spaces/channel-setup.d.ts +16 -0
  469. package/dist/workspace/spaces/channel-setup.js +105 -0
  470. package/dist/workspace/spaces/channels-onboarding.d.ts +13 -0
  471. package/dist/workspace/spaces/channels-onboarding.js +37 -0
  472. package/dist/workspace/spaces/create-channel-dialog.d.ts +16 -0
  473. package/dist/workspace/spaces/create-channel-dialog.js +158 -0
  474. package/dist/workspace/spaces/private-channels-not-in.d.ts +9 -0
  475. package/dist/workspace/spaces/private-channels-not-in.js +69 -0
  476. package/dist/workspace/spaces/space-agents.d.ts +10 -0
  477. package/dist/workspace/spaces/space-agents.js +10 -0
  478. package/dist/workspace/spaces/space-details-dialog.d.ts +20 -0
  479. package/dist/workspace/spaces/space-details-dialog.js +176 -0
  480. package/dist/workspace/spaces/space-link-label.d.ts +49 -0
  481. package/dist/workspace/spaces/space-link-label.js +73 -0
  482. package/dist/workspace/spaces/space-links.d.ts +19 -0
  483. package/dist/workspace/spaces/space-links.js +230 -0
  484. package/dist/workspace/spaces/space-members.d.ts +46 -0
  485. package/dist/workspace/spaces/space-members.js +182 -0
  486. package/dist/workspace/spaces/space-roster.d.ts +15 -0
  487. package/dist/workspace/spaces/space-roster.js +44 -0
  488. package/dist/workspace/spaces/space-threads-tab.d.ts +56 -0
  489. package/dist/workspace/spaces/space-threads-tab.js +156 -0
  490. package/dist/workspace/spaces/space-timeline-view.d.ts +254 -0
  491. package/dist/workspace/spaces/space-timeline-view.js +235 -0
  492. package/dist/workspace/spaces/space-timeline.d.ts +106 -0
  493. package/dist/workspace/spaces/space-timeline.js +183 -0
  494. package/dist/workspace/spaces/space-triggers.d.ts +13 -0
  495. package/dist/workspace/spaces/space-triggers.js +78 -0
  496. package/dist/workspace/spaces/space-view.d.ts +37 -0
  497. package/dist/workspace/spaces/space-view.js +436 -0
  498. package/package.json +27 -36
  499. package/skills/kortix-ui/SKILL.md +3 -3
  500. package/skills/kortix-ui/references/custom-dashboard.md +3 -3
  501. package/skills/kortix-ui/references/modules.md +28 -4
  502. package/skills/kortix-ui/references/theming.md +1 -1
  503. package/dist/customize/connectors/catalog/merge-search.d.ts +0 -10
  504. package/dist/customize/connectors/catalog/merge-search.js +0 -26
  505. package/dist/customize/connectors/detail/find-easy-connect-app.d.ts +0 -29
  506. package/dist/customize/connectors/detail/find-easy-connect-app.js +0 -22
  507. package/dist/customize/migrate-to-v2/use-migrate-to-v2.d.ts +0 -9
  508. package/dist/customize/migrate-to-v2/use-migrate-to-v2.js +0 -24
  509. package/dist/customize/sections/connector-connection-header.d.ts +0 -36
  510. package/dist/customize/sections/connector-connection-header.js +0 -33
  511. package/dist/customize/sections/connector-connection-modal.d.ts +0 -34
  512. package/dist/customize/sections/connector-connection-modal.js +0 -72
  513. package/dist/customize/sections/discover-catalogue-card.d.ts +0 -19
  514. package/dist/customize/sections/discover-catalogue-card.js +0 -26
  515. package/dist/customize/sections/discover-catalogue-detail.d.ts +0 -15
  516. package/dist/customize/sections/discover-catalogue-detail.js +0 -24
  517. package/dist/customize/sections/discover-catalogue.d.ts +0 -17
  518. package/dist/customize/sections/discover-catalogue.js +0 -132
  519. package/dist/customize/sections/llm-provider/provider-access-menu.d.ts +0 -10
  520. package/dist/customize/sections/llm-provider/provider-access-menu.js +0 -23
  521. package/dist/files/project/components/drive-empty-art.d.ts +0 -2
  522. package/dist/files/project/components/drive-empty-art.js +0 -5
  523. package/dist/internal/hooks/use-media-query.d.ts +0 -2
  524. package/dist/internal/share/public-share-link-button.d.ts +0 -14
  525. package/dist/internal/share/public-share-link-button.js +0 -17
  526. package/dist/markdown/setup-links/app-credentials-form.d.ts +0 -12
  527. package/dist/markdown/setup-links/app-credentials-form.js +0 -115
  528. /package/dist/{internal/hooks → hooks}/use-media-query.js +0 -0
@@ -12,10 +12,14 @@ import { useQueuedDraftStore, useQueuedDrafts } from '../composer/stores/queued-
12
12
  import { SESSION_LOG_MINOR, SESSION_LOG_SCHEMA, listSessionPrompts, projectSessionConnection, sessionLogOfV1Rows, sessionLogPromptMessage, sessionLogRetrying, sessionLogTurns, fetchSessionAttachment, sessionLogFullTextPlan, sessionLogHarnessSwitches, sessionLogRestoreNotice, sessionLogWithFullTexts, } from '@kortix/sdk';
13
13
  import { useProjectSession, useSessionLogOlder, useSessionLogRecord, useSessionLogThread, useSessionMessageAuthors, useSessionModelUsage, useSessionParticipants, } from '@kortix/sdk/react';
14
14
  import { ArrowBendUpLeftIcon, CaretDownIcon, StackIcon as Layers, } from '@phosphor-icons/react';
15
+ import { jumpToOutcome } from '../outcomes/jump-to-outcome.js';
16
+ import { useIsMobile, useIsPhone } from '../../hooks/use-mobile.js';
15
17
  import { useUiLink, useUiPathname, useUiRouter, useUiShallowNavigate, useUiSearchParams, useUiViewerId, } from '../../providers/ui-provider.js';
16
- import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
18
+ import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
19
+ import { TranscriptRowsContext } from './transcript-rows.js';
20
+ import { AgentBusyRow, PendingPromptRows } from './thread-boot-rows.js';
17
21
  import { createPortal } from 'react-dom';
18
- import { AUTHOR_RETRY_DELAYS_MS, authorForTurn, awaitsAgent, missingAuthorKey, resolveTranscriptAuthors, showAuthorName, stoppedByName, } from '../turn/message-author.js';
22
+ import { AUTHOR_RETRY_DELAYS_MS, authorForTurn, awaitsAgent, missingAuthorKey, resolveTranscriptAuthors, rewindableMessageIds, showAuthorName, stoppedByName, } from '../turn/message-author.js';
19
23
  import { stopRefusalMessage } from '../status/stop-refusal.js';
20
24
  import { QueuedPromptList } from '../composer/queued-prompt-list.js';
21
25
  import { runtimePermissionLocksComposer } from '../composer/send-blockers.js';
@@ -35,7 +39,7 @@ import { ProjectFilesProvider } from '../../files/project/context.js';
35
39
  import { useOptionalSessionPanel } from '../panel/session-panel-provider.js';
36
40
  import { COMPOSER_SHELL_CLASS, Composer as SessionChatInput, } from '../composer/composer.js';
37
41
  import { ComposerDropZone } from '../composer/composer-drop-zone.js';
38
- import { resolveComposerAgent } from '../composer/composer-agent-access.js';
42
+ import { channelRunnableAgents, resolveComposerAgent, withChannelAgents } from '../composer/composer-agent-access.js';
39
43
  import { acknowledgeQuoteRequests, } from '../composer/composer-logic.js';
40
44
  import { sessionSlashFiles } from '../composer/menus/slash-files.js';
41
45
  import { resolveFirstPromptHandover, transcriptCarriesFirstPrompt, } from './first-prompt-handover.js';
@@ -66,6 +70,7 @@ import { ChatMinimap } from './chat-minimap.js';
66
70
  import { usePlanInChat } from './plan-surface.js';
67
71
  import { SessionStartingLoader } from '../status/session-starting-loader.js';
68
72
  import { ToolActivateContext } from '../tool/tool-renderers.js';
73
+ import { ReadOnlySessionContext } from '../tool/shared/infrastructure-contexts.js';
69
74
  import { firstPromptAttachments, retainSentAttachmentPreviews, sentAttachmentsForTurn, } from '../composer/sent-attachment-previews.js';
70
75
  import { buildOptimisticPromptTextWithUploads, promptFileParts, sentAttachmentsOf, } from '../composer/uploaded-file-refs.js';
71
76
  import { useAutoScroll } from '../../internal/hooks/use-auto-scroll.js';
@@ -84,9 +89,9 @@ import { openTabAndNavigate, useTabStore } from '../../internal/stores/tab-store
84
89
  import { projectSessionHref } from '../../internal/lib/navigation/session-href.js';
85
90
  import { getRetryInfo } from '../../internal/session-types/index.js';
86
91
  import { useModelPricingLookup } from '../../internal/lib/model-pricing.js';
87
- import { isAbortError, latestPromptModel, turnEndNotice } from '@kortix/sdk';
92
+ import { isAbortError, latestPromptModel, SESSION_NOTICE, turnEndNotice } from '@kortix/sdk';
88
93
  import { failureShownByTurn, failureSupersededByTurn, persistedFailureText, } from './persisted-turn-failure.js';
89
- import { abandonOptimisticSend, applyOptimisticAbort, ascendingId, awaitAbortSettlement, beginOptimisticSend, classifySendError, clearStartStash, formatModelString, formatPromptModel, markOptimisticSendDispatched, markOptimisticSendInboxBacked, mintSessionWireMessageId, readStartStash, recoverFromSendFailure, rejectQuestion, replyToPermission, replyToQuestion, requestRuntimeReconnect, startSessionWithPrompt, useAbortRuntimeSession, useExecuteRuntimeCommand, useProjectConfig, useRuntimeAgents, useRuntimeBootStalled, useRuntimeCommands, useRuntimeConfig, useRuntimeConnectionStore, useRuntimePendingStore, useRuntimePhase, useRuntimeProviders, useRuntimeReady, useRuntimeSession, useRuntimeSessions, useRuntimeSupports, useSessionModelSelection, useSessionPrompts, useSessionStateStore, useSessionSync, useSessionTurnOutcome, useSessionWorking, useSessionWorkingStore, } from '@kortix/sdk/react';
94
+ import { abandonOptimisticSend, applyOptimisticAbort, ascendingId, awaitAbortSettlement, beginOptimisticSend, classifySendError, clearStartStash, formatModelString, formatPromptModel, markOptimisticSendDispatched, markOptimisticSendInboxBacked, mintSessionWireMessageId, readStartStash, recoverFromSendFailure, rejectQuestion, replyToPermission, replyToQuestion, requestRuntimeReconnect, startSessionWithPrompt, useAbortRuntimeSession, useExecuteRuntimeCommand, useProjectConfig, useRuntimeAgents, useProjectSpaces, useRuntimeBootStalled, useRuntimeCommands, useRuntimeConfig, useRuntimeConnectionStore, useRuntimePendingStore, useRuntimePhase, useRuntimeProviders, useRuntimeReady, useRuntimeSession, useRuntimeSessions, useRuntimeSupports, useSessionModelSelection, useSessionPrompts, useSessionStateStore, useSessionSync, useSessionTurnOutcome, useSessionWorking, useSessionWorkingStore, } from '@kortix/sdk/react';
90
95
  import { useStableCallback } from '../../internal/hooks/use-stable-callback.js';
91
96
  import { useReloadForensics } from '../status/reload-forensics.js';
92
97
  import { resolveLastTurnWorking, serverHoldsOpenTurn, sessionComposerReadiness, } from '../composer/session-composer-readiness.js';
@@ -170,8 +175,20 @@ const TRANSCRIPT_THROTTLE_MS = 50;
170
175
  /** `useSessionMessages` input when no `useSession` owns this chat: reads nothing. */
171
176
  const DETACHED_SESSION_MESSAGES = { projectId: '', sessionId: '', runtimeSessionId: null };
172
177
  const NO_FULL_TEXTS = new Map();
173
- export function SessionChat({ sessionId, projectSessionId, sessionState, projectId, boundAgentName, headerLeadingAction, hideHeader, readOnly, inputReplacement, initialScrollTop, savedHistoryScope, onContentReady, deferComposerFocus, hostQuote, }) {
178
+ /** The composer notice in the host's words (`TranscriptRows.notices`), else the SDK's. */
179
+ function hostNotice(notice, notices) {
180
+ if (!notice || !notices)
181
+ return notice;
182
+ const key = Object.keys(SESSION_NOTICE).find((candidate) => SESSION_NOTICE[candidate] === notice);
183
+ return (key && notices[key]) || notice;
184
+ }
185
+ export function SessionChat({ sessionId, projectSessionId, sessionState, projectId, boundAgentName, headerLeadingAction, hideHeader, readOnly, composerPlaceholder, inputReplacement, initialScrollTop, savedHistoryScope, onContentReady, deferComposerFocus, hostQuote, }) {
174
186
  const tHardcodedUi = useTranslations('hardcodedUi');
187
+ // A channel's thread panel: turns are message rows, full width, end-anchored.
188
+ const rowsContext = useContext(TranscriptRowsContext);
189
+ const transcriptRows = rowsContext !== null;
190
+ const isMobile = useIsMobile();
191
+ const isPhone = useIsPhone();
175
192
  const tQueue = useTranslations('threads');
176
193
  const tComposerAttachments = useTranslations('hardcodedUi.composerAttachments');
177
194
  const onboardingActive = useOnboardingModeStore((s) => s.active);
@@ -412,7 +429,7 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
412
429
  // chat has no projectId, but runs inside the parent scope's project: its
413
430
  // roster is that project's. Only a chat with neither falls back to the
414
431
  // runtime's own agent list, which a pi box does not serve (KRTX-2887).
415
- const { data: agents } = useRuntimeAgents({
432
+ const { data: rosterAgents } = useRuntimeAgents({
416
433
  directory: session?.directory,
417
434
  projectId: projectId ?? (scopeProjectId || undefined),
418
435
  });
@@ -507,6 +524,15 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
507
524
  const projectSessionRow = useProjectSession(projectId, projectSessionId ?? undefined, {
508
525
  enabled: !!projectId && !!projectSessionId,
509
526
  }).data;
527
+ // A channel thread: whoever posts in the channel runs its agents, granted or
528
+ // not (the server applies the same rule).
529
+ const { data: projectSpaces } = useProjectSpaces(projectId, {
530
+ enabled: !!projectId && !!projectSessionRow?.space_id,
531
+ });
532
+ const threadChannelAgents = useMemo(() => channelRunnableAgents(projectSpaces?.find((space) => space.space_id === projectSessionRow?.space_id), projectConfig?.default_agent ?? projectConfig?.open_code_default_agent), [projectSpaces, projectSessionRow?.space_id, projectConfig?.default_agent, projectConfig?.open_code_default_agent]);
533
+ const agents = useMemo(() => withChannelAgents(rosterAgents, threadChannelAgents), [rosterAgents, threadChannelAgents]);
534
+ // Edit-and-rewind only on the viewer's own message with nobody else's after it.
535
+ const rewindable = useMemo(() => rewindableMessageIds(userMessageIds, transcriptAuthors.byMessage, viewerId, projectSessionRow?.created_by), [userMessageIds, transcriptAuthors.byMessage, viewerId, projectSessionRow?.created_by]);
510
536
  // ---- Unified model/agent/variant state (1:1 port of SolidJS local.tsx) ----
511
537
  // The composer opens on the model the session already runs (its pin, or its
512
538
  // last prompt's model), so a session set from the CLI, the SDK or Slack does
@@ -1095,7 +1121,7 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
1095
1121
  const undoToastId = `queue-undo-${sessionId}-${removed.prompt_id}`;
1096
1122
  undoToast(tHardcodedUi.raw('i18nComplete.text2c6041fda32c'), {
1097
1123
  id: undoToastId,
1098
- duration: 5000,
1124
+ duration: 5000, // audit:allow a toast lifetime in ms, not motion/react seconds
1099
1125
  undoLabel: tHardcodedUi.raw('i18nComplete.texta737e54996f8'),
1100
1126
  // The SAME `clientMessageId`, so an undo re-creates ONE row and a
1101
1127
  // double-click cannot create two. A FRESH wire id, because
@@ -1166,6 +1192,7 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
1166
1192
  const messageCount = messages?.length ?? 0;
1167
1193
  const { scrollRef, contentRef, spacerElRef, showScrollButton, scrollToBottom, smoothScrollToAbsoluteBottom, anchorTurn, startAtTop, } = useAutoScroll({
1168
1194
  hasContent: messageCount > 0,
1195
+ room: !transcriptRows,
1169
1196
  });
1170
1197
  // Cmd/Ctrl+P prints the WHOLE conversation. The shortcut is intercepted
1171
1198
  // because neither half of what printing needs can be done in CSS: the
@@ -1350,6 +1377,18 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
1350
1377
  const awaitingUserInput = pendingQuestions.length > 0 || pendingPermissions.length > 0;
1351
1378
  const QUESTION_PROMPT_ANIMATION_MS = 320;
1352
1379
  const activePendingQuestion = pendingQuestions[0] ?? null;
1380
+ // A new question docks over the bottom of the transcript, and answering it
1381
+ // needs what the agent just wrote (a long tool run can leave the reader
1382
+ // short of the end). Bring the end into view once per question; a reader
1383
+ // who scrolls up afterwards stays where they are.
1384
+ const shownQuestionIdRef = useRef(null);
1385
+ useEffect(() => {
1386
+ const id = activePendingQuestion?.id ?? null;
1387
+ if (!id || id === shownQuestionIdRef.current)
1388
+ return;
1389
+ shownQuestionIdRef.current = id;
1390
+ smoothScrollToAbsoluteBottom();
1391
+ }, [activePendingQuestion?.id, smoothScrollToAbsoluteBottom]);
1353
1392
  const [renderedQuestion, setRenderedQuestion] = useState(null);
1354
1393
  const [questionPromptVisible, setQuestionPromptVisible] = useState(false);
1355
1394
  // A connector call waiting on a decision docks onto the composer the way a
@@ -1996,6 +2035,68 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
1996
2035
  scrollEl.scrollTo({ top: Math.max(0, offset), behavior: 'smooth' });
1997
2036
  clearJumpTarget();
1998
2037
  }, [targetMessageId, clearJumpTarget, contentRef, scrollRef]);
2038
+ const tChangeRequests = useTranslations('sessionChangeRequests');
2039
+ const mobileChangeRequests = useIsMobile();
2040
+ const [jumpingToOutcome, setJumpingToOutcome] = useState(false);
2041
+ const outcomeJumpRef = useRef({ sessionId, hasOlder, isLoadingOlder, loadOlder });
2042
+ useEffect(() => {
2043
+ outcomeJumpRef.current = { sessionId, hasOlder, isLoadingOlder, loadOlder };
2044
+ }, [sessionId, hasOlder, isLoadingOlder, loadOlder]);
2045
+ const handleJumpToOutcome = useCallback(async (resolveTurn, createdAt) => {
2046
+ if (jumpingToOutcome)
2047
+ return;
2048
+ setJumpingToOutcome(true);
2049
+ const currentSession = sessionId;
2050
+ const settle = () => new Promise((resolve) => requestAnimationFrame(() => resolve()));
2051
+ try {
2052
+ const earliest = Math.min(...turnSpans.flatMap((span) => span.startedAt === null ? [] : [span.startedAt]));
2053
+ await jumpToOutcome({
2054
+ needsHistory: hasOlder && (!Number.isFinite(createdAt) || createdAt < earliest),
2055
+ history: {
2056
+ readState: () => outcomeJumpRef.current.sessionId === currentSession
2057
+ ? outcomeJumpRef.current : { hasOlder: false, isLoadingOlder: false },
2058
+ loadOlder: () => outcomeJumpRef.current.loadOlder(),
2059
+ settle,
2060
+ },
2061
+ resolve: () => outcomeJumpRef.current.sessionId === currentSession ? resolveTurn() : undefined,
2062
+ loadError: () => errorToast(tChangeRequests('loadError')),
2063
+ unavailable: () => {
2064
+ if (outcomeJumpRef.current.sessionId === currentSession)
2065
+ errorToast(tChangeRequests('unavailable'));
2066
+ },
2067
+ scroll: async (key) => {
2068
+ if (mobileChangeRequests) {
2069
+ const store = useKortixComputerStore.getState();
2070
+ if (store.isExpanded)
2071
+ store.toggleExpanded();
2072
+ store.setIsSidePanelOpen(false);
2073
+ store.setIsActionPanelOpen(false);
2074
+ }
2075
+ await settle();
2076
+ await settle();
2077
+ releaseOlderAnchor();
2078
+ const target = key ? Array.from(contentRef.current?.querySelectorAll('[data-turn-id]') ?? [])
2079
+ .find((node) => node.dataset.turnId === key) : undefined;
2080
+ const scroll = scrollRef.current;
2081
+ if (!target || !scroll) {
2082
+ return false;
2083
+ }
2084
+ const top = target.getBoundingClientRect().top - scroll.getBoundingClientRect().top + scroll.scrollTop;
2085
+ scroll.scrollTo({ top: Math.max(0, top), behavior: 'instant' });
2086
+ // Static feedback, not motion: keyboard and reduced-motion navigation stay instant.
2087
+ target.classList.add('bg-accent');
2088
+ window.setTimeout(() => target.classList.remove('bg-accent'), 1_000);
2089
+ return true;
2090
+ },
2091
+ });
2092
+ }
2093
+ catch {
2094
+ errorToast(tChangeRequests('loadError'));
2095
+ }
2096
+ finally {
2097
+ setJumpingToOutcome(false);
2098
+ }
2099
+ }, [jumpingToOutcome, sessionId, hasOlder, turnSpans, mobileChangeRequests, releaseOlderAnchor, contentRef, scrollRef, tChangeRequests]);
1999
2100
  // Reset on session change
2000
2101
  useEffect(() => {
2001
2102
  clearSendReceipt();
@@ -3421,252 +3522,276 @@ export function SessionChat({ sessionId, projectSessionId, sessionState, project
3421
3522
  return (_jsx("div", { className: "bg-background relative flex h-full flex-col", "data-testid": "session-chat", children: _jsx(SessionStartingLoader, { projectId: projectId, sessionId: projectSessionId }) }));
3422
3523
  }
3423
3524
  return (
3424
- // Outcomes' cache coherence and the shareable `?cr=` link both hinge on
3425
- // ONE `ProjectFilesProvider`: `useChangeRequests` inside
3426
- // `SessionOutcomesProvider` and `ChangeRequestDetailDialog` below both read
3427
- // their project id from this context, which is what keeps them on the same
3428
- // React Query cache entry (see `outcomes/session-outcomes-provider.tsx`).
3429
- _jsx(ProjectFilesProvider, { value: { projectId: projectId ?? '', ref: outcomesBaseRef, defaultBranch: outcomesBaseRef }, children: _jsxs(ComposerDropZone, { className: cn('relative flex h-full flex-col',
3430
- // Transparent in the welcome state so the root-level full-bleed wallpaper
3431
- // (portaled into SessionLayout) reads through; solid once real content
3432
- // takes over. Same base color either way, so non-welcome is unchanged.
3433
- shouldShowWelcomeOverlay ? 'bg-transparent' : 'bg-background'), "data-testid": "session-chat", children: [isPreparingPrint && (_jsxs("div", { "data-print-hide": true, role: "status", className: "bg-popover text-muted-foreground fixed bottom-6 left-1/2 z-50 flex -translate-x-1/2 items-center gap-2 rounded-lg border px-4 py-2 text-xs shadow-lg", children: [_jsx(Loading, { className: "size-3.5 shrink-0" }), tHardcodedUi.raw('i18nComplete.text7dac2ca010fe')] })), wallpaperLayer
3434
- ? welcomeWallpaper && createPortal(welcomeWallpaper, wallpaperLayer)
3435
- : welcomeWallpaper, !hideHeader && (_jsx(SessionSiteHeader, { sessionId: sessionId, sessionTitle: session?.title || 'Untitled', leadingAction: headerLeadingAction, parent: parentCrumb })), _jsx(SessionContextModal, { open: contextModalOpen, onOpenChange: setContextModalOpen,
3436
- // A chat with no `useSession` source: the raw explorer lists the
3437
- // rows `useSessionSync` read for it.
3438
- detachedRows: sessionState ? undefined : hookMessages, source: sessionState ?? DETACHED_SESSION_MESSAGES, session: session, sessionRow: projectSessionRow, providers: providers, allSessions: allSessions, servedModel: sessionServedModel, billedCost: sessionBilledCost(modelUsage) }), _jsx(CompactModal, { sessionId: sessionId, open: compactModalOpen, onOpenChange: setCompactModalOpen }), _jsx(ChangeRequestDetailDialog, { crId: openCrId, onClose: () => setOpenCrId(null), onOpenChangeRequest: setOpenCrId }), _jsxs(SessionBodyRow, { actionPanel: !hideHeader && !readOnly, children: [isNotFound ? (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col items-center justify-center gap-3 px-6 text-center", children: [_jsx("div", { className: "text-muted-foreground text-sm", children: tHardcodedUi.raw('componentsSessionSessionChat.line5821JsxTextThisSessionIsNotAccessibleRightNow') }), _jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { href: "/", prefetch: true, onClick: () => {
3439
- try {
3440
- if (sessionId)
3441
- useTabStore.getState().closeTab?.(sessionId);
3442
- }
3443
- catch { }
3444
- }, children: tHardcodedUi.raw('componentsSessionSessionChat.line5833JsxTextGoToHome') }) })] })) : (_jsxs("div", { ref: chatAreaRef, className: "relative z-10 min-h-0 flex-1", children: [_jsx("div", { ref: scrollContainerCallbackRef, className: cn(
3445
- // overflow-anchor:none — this scroll area does ALL of its own
3446
- // anchoring (useAutoScroll's spacer + RAF follow, the send-path
3447
- // turn anchor, and the history-prepend content-space restore in
3448
- // session-history-scroll.ts). The browser's native scroll
3449
- // anchoring (default `overflow-anchor: auto`) tries to
3450
- // compensate for the SAME prepends independently, and the two
3451
- // corrections stacking is the other half of the scroll-up
3452
- // teleport: ours restores the reader's position, then the
3453
- // native one nudges it again.
3454
- 'scrollbar-hide relative z-10 h-full flex-1 overflow-y-auto [scroll-behavior:auto] [overflow-anchor:none]', shouldShowWelcomeOverlay ? 'bg-transparent' : 'bg-background'), onMouseUp: handleChatMouseUp, onMouseDown: handleChatMouseDown, onScroll: handleChatScroll, children: _jsxs("div", { ref: contentRef, role: "log",
3455
- // Width and gutters live in `SESSION_TRANSCRIPT_CLASS`: the
3456
- // instant shell draws this same column and the two crossfade
3457
- // into each other, so a difference here is a sideways jump on
3458
- // screen, not a style opinion. See session-body.tsx.
3459
- //
3460
- // No bottom padding, there or here: the space under the last
3461
- // message is the auto-scroll spacer's job alone. The `pb-32`
3462
- // that used to sit here stacked 128px of dead space on top of
3463
- // it — the "extra inset at the bottom of the session".
3464
- className: SESSION_TRANSCRIPT_CLASS, children: [_jsxs("div", { className: "flex min-w-0 flex-col", children: [_jsx(SessionPrintHeader, { title: session?.title || 'Untitled', agentName: composerAgentName }), (hasOlder || showOlderLoading) && (_jsxs("div", { className: "mb-6 flex flex-col items-center gap-2", children: [hasOlder && (_jsx("div", { ref: olderSentinelRef, "aria-hidden": true, className: "h-px w-full" })), showOlderLoading && (_jsxs("div", { role: "status", className: "text-muted-foreground flex items-center gap-2 py-1 text-xs", children: [_jsx(Loading, { className: "size-3.5 shrink-0" }), tHardcodedUi.raw('i18nComplete.text85bf890776a7')] })), !showOlderLoading &&
3465
- !olderPullFailed &&
3466
- olderAutoloadExhausted({ hasOlder, autoLoadedPages }) && (_jsx(Button, { type: "button", variant: "outline-ghost", size: "sm", onClick: () => void handleLoadOlder(), children: tHardcodedUi.raw('i18nComplete.textf17671d83db0') })), olderPullFailed && !showOlderLoading && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-muted-foreground text-xs", children: tHardcodedUi.raw('i18nComplete.textb03a0041ce33') }), _jsx(Button, { type: "button", variant: "outline-ghost", size: "sm", onClick: () => void handleLoadOlder(), children: tHardcodedUi.raw('i18nComplete.text942087cc2d41') })] }))] })), restoreNotice && !restoreDismissed && (_jsx(RestoreNotice, { notice: restoreNotice, onDismiss: () => dismissRestore(historyKey, restoreNotice.generation) })), _jsx(SessionOutcomesProvider, { projectSessionId: projectSessionId, turnSpans: turnSpans, busy: effectiveBusy, onOpen: handleOpenOutcome, children: _jsxs(ToolActivateContext.Provider, { value: toolActivate, children: [showFirstPromptPreview &&
3467
- firstPromptSource &&
3468
- queuedSyntheticMessages.length === 0 && (_jsx(OptimisticTurn, { text: buildOptimisticPromptTextWithUploads(firstPromptSource.text, firstPromptSource.files),
3469
- // Preserve a real failed-send status through the
3470
- // boot-shell handover without inventing upload progress.
3471
- // A first prompt held on its uploads carries its own.
3472
- uploadStatus: firstPromptSource.uploadStatus ?? firstPromptUploadStatus, agentNames: agentNames, onFileClick: openFileInComputer, sessionId: sessionId,
3473
- // The first item of the conversation, like turn 0, which has no
3474
- // top margin. `first:mt-0` misses here: the list opens with a
3475
- // zero-height <header>, so the stand-in sat 44px lower than turn 0
3476
- // and every swap between the two jumped the bubble (KRTX-2931).
3477
- className: "mt-0",
3478
- // An undelivered first prompt is a turn about to start, as the
3479
- // shell above this chat shows it: busy from the first frame,
3480
- // before the working projection has read the inbox (KRTX-2931).
3481
- busy: turns.length === 0 && (lastTurnWorking || promptInbox.prompts.some(isFirstPromptRow)) })), turns.map((turn, turnIndex) => {
3482
- // Check if this turn is a compaction summary — and
3483
- // whether it actually PRODUCED one. A failed/aborted
3484
- // attempt (compaction-flagged, no content, not the
3485
- // working turn) renders as one slim row, draws no
3486
- // divider, and stacks tight against a neighbouring
3487
- // failed attempt.
3488
- const compaction = turn.compaction;
3489
- const hasCompaction = compaction !== null;
3490
- const isTurnWorking = turn.live;
3491
- // `inFlight` (message state) alongside the projection:
3492
- // classifying by projection alone flipped a streaming
3493
- // compaction to "failed" for the frames where the two
3494
- // disagree, popping the divider in and out — a layout
3495
- // bounce right where the reader is looking.
3496
- const isFailedCompaction = compaction !== null &&
3497
- !compaction.hasContent &&
3498
- !compaction.inFlight &&
3499
- !isTurnWorking;
3500
- // Retries collapse to ONE visible row, GLOBALLY: a
3501
- // failed attempt with ANY later compaction turn (a
3502
- // retry, or the one that finally landed) is history —
3503
- // it keeps its TurnViewport (stable keys, scroll
3504
- // anchors) but renders no content, and an empty
3505
- // TurnViewport costs 0px (turn-viewport.tsx RULE 2).
3506
- // Adjacency was not enough: attempts whose error landed
3507
- // on a plain assistant message used to interleave as
3508
- // unclassified turns, breaking every consecutive run.
3509
- const suppressedFailedCompaction = isFailedCompaction && turnIndex < lastCompactionTurnIndex;
3510
- // Notification-only early-return removed: it rendered the
3511
- // user's pty_* card but skipped the turn's replies,
3512
- // hiding every subsequent assistant response in that turn.
3513
- // Fall through to the normal turn renderer instead.
3514
- const confirmedActive = turnIsConfirmedActive({
3515
- isTurnWorking,
3516
- turnId: turn.id,
3517
- activeTurnId,
3518
- pendingDelivery: !!working.pendingDelivery,
3519
- });
3520
- const pendingPrompt = !confirmedActive && turn.assistant.length === 0
3521
- ? pendingPromptsByMessageId.get(turn.id)
3522
- : undefined;
3523
- // A queued prompt is not in the runtime transcript yet;
3524
- // its author still shows (`authorForTurn`).
3525
- const turnAuthor = authorForTurn(transcriptAuthors, messageAuthors, turn.id, pendingPrompt?.wire_message_id);
3526
- return (_jsx(TranscriptTurnRow
3527
- // ONE element per prompt: keyed by the id the
3528
- // bubble was FIRST painted under, so the swap to a
3529
- // re-minted echo id re-renders this node instead
3530
- // of mounting a new one (opacity keeps animating,
3531
- // hover state survives, nothing jumps).
3532
- , { turnId: turn.id, modelOptions: modelOptions, suppressed: suppressedFailedCompaction, continuedWith: harnessSwitches.get(turn.id)?.harness, fullTextState: loaded.state[turn.id] ?? 'idle', onLoadFullText: loadFullTexts, showBusyRow: showFallbackBusyRow &&
3533
- fallbackBusyRowTurnId === turn.id,
3534
- // Queued bubbles STACK: a pending turn right after
3535
- // another pending turn sits close to it, like a
3536
- // list of what is waiting — not a turn's width
3537
- // apart as if each had been answered in between.
3538
- // (Failed compaction rows need no stacking rule any
3539
- // more — at most one is visible at a time.)
3540
- viewportClassName: turnIndex === 0
3541
- ? ''
3542
- : // A compaction and the work after it continue the turn above.
3543
- turn.compaction || turn.continues
3544
- ? 'mt-6'
3545
- : lastTurnWorking &&
3546
- pendingTurnIds.has(turn.id) &&
3547
- pendingTurnIds.has(turns[turnIndex - 1].id)
3548
- ? 'mt-3'
3549
- : 'mt-12', turn: turn, author: turnAuthor, showAuthor: showAuthorName(turnAuthor, groupChat, viewerId), stoppedBy: stoppedByName(messageAuthors, turn.id, viewerId), servedModel: servedModelOfTurn(modelUsage, turn.id, turns[turnIndex - 1]?.id), turnOutcome: turnOutcome, isLast: turn.id === lastUserMessageId, ownsPlan: turn.id === planAnchorId, sessionId: sessionId, projectId: projectId, projectSessionId: projectSessionId, sessionStatus: sessionStatus, permissions: pendingPermissions, questions: pendingQuestions, agentNames: agentNames, isFirstTurn: turnIndex === 0,
3550
- // Handed over only once the stand-in has stepped
3551
- // aside — while it is up it draws these itself.
3552
- pendingText: turnIndex === 0 ? firstTurnHandover?.text : undefined, pendingAttachments: sentAttachmentsForTurn({
3553
- sentByMessage: sentAttachmentsByMessage,
3554
- messageId: turn.id,
3555
- originId: optimisticOriginOf(sessionId, turn.id),
3556
- isFirstTurn: turnIndex === 0,
3557
- firstTurnHandover: firstTurnHandover?.attachments,
3558
- firstTurnSent: firstPromptAttachments(projectSessionId),
3559
- queuedRowAttachments: inboxRowsByMessageId.get(turn.id)?.attachments,
3560
- }), uploadStatus: heldSendFailures?.[turn.id]
3561
- ? {
3562
- state: 'failed',
3563
- message: heldSendFailures[turn.id].message,
3564
- onRetry: () => retryHeldSend(sessionId, turn.id, resendHeldSend, (error) => classifySessionError(error).message),
3565
- }
3566
- : turnIndex === 0 && firstTurnHandover?.attachments.length
3567
- ? firstPromptUploadStatus
3568
- : undefined, sessionWorking: lastTurnWorking, isWorkingTurn: turn.live, suppressBusyIndicator: suppressWorkingTurnBusy, awaitingUser: awaitingUserInput,
3569
- // An idle send stays in the inbox until delivery,
3570
- // but nothing runs ahead of it: queued only while
3571
- // it waits behind a turn (`turnRendersQueued`).
3572
- pending: turnRendersQueued({
3525
+ // A read-only view (the sub-session modal) offers no fix inside a tool card.
3526
+ _jsx(ReadOnlySessionContext.Provider, { value: !!readOnly, children: _jsx(ProjectFilesProvider, { value: { projectId: projectId ?? '', ref: outcomesBaseRef, defaultBranch: outcomesBaseRef }, children: _jsxs(ComposerDropZone, { className: cn('relative flex h-full flex-col',
3527
+ // Transparent in the welcome state so the root-level full-bleed wallpaper
3528
+ // (portaled into SessionLayout) reads through; solid once real content
3529
+ // takes over. Same base color either way, so non-welcome is unchanged.
3530
+ shouldShowWelcomeOverlay ? 'bg-transparent' : 'bg-background'), "data-testid": "session-chat", children: [isPreparingPrint && (_jsxs("div", { "data-print-hide": true, role: "status", className: "bg-popover text-muted-foreground fixed bottom-6 left-1/2 z-50 flex -translate-x-1/2 items-center gap-2 rounded-lg border px-4 py-2 text-xs shadow-lg", children: [_jsx(Loading, { className: "size-3.5 shrink-0" }), tHardcodedUi.raw('i18nComplete.text7dac2ca010fe')] })), wallpaperLayer
3531
+ ? welcomeWallpaper && createPortal(welcomeWallpaper, wallpaperLayer)
3532
+ : welcomeWallpaper, !hideHeader && (_jsx(SessionSiteHeader, { sessionId: sessionId, sessionTitle: session?.title || 'Untitled', leadingAction: headerLeadingAction, parent: parentCrumb })), _jsx(SessionContextModal, { open: contextModalOpen, onOpenChange: setContextModalOpen,
3533
+ // A chat with no `useSession` source: the raw explorer lists the
3534
+ // rows `useSessionSync` read for it.
3535
+ detachedRows: sessionState ? undefined : hookMessages, source: sessionState ?? DETACHED_SESSION_MESSAGES, session: session, sessionRow: projectSessionRow, providers: providers, allSessions: allSessions, servedModel: sessionServedModel, billedCost: sessionBilledCost(modelUsage) }), _jsx(CompactModal, { sessionId: sessionId, open: compactModalOpen, onOpenChange: setCompactModalOpen }), _jsx(ChangeRequestDetailDialog, { crId: openCrId, onClose: () => setOpenCrId(null), onOpenChangeRequest: setOpenCrId }), _jsxs(SessionBodyRow, { actionPanel: !hideHeader && !readOnly, children: [isNotFound ? (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col items-center justify-center gap-3 px-6 text-center", children: [_jsx("div", { className: "text-muted-foreground text-sm", children: tHardcodedUi.raw('componentsSessionSessionChat.line5821JsxTextThisSessionIsNotAccessibleRightNow') }), _jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { href: "/", prefetch: true, onClick: () => {
3536
+ try {
3537
+ if (sessionId)
3538
+ useTabStore.getState().closeTab?.(sessionId);
3539
+ }
3540
+ catch { }
3541
+ }, children: tHardcodedUi.raw('componentsSessionSessionChat.line5833JsxTextGoToHome') }) })] })) : (_jsxs("div", { ref: chatAreaRef, className: "relative z-10 min-h-0 flex-1", children: [_jsx("div", { ref: scrollContainerCallbackRef, className: cn(
3542
+ // overflow-anchor:none — this scroll area does ALL of its own
3543
+ // anchoring (useAutoScroll's spacer + RAF follow, the send-path
3544
+ // turn anchor, and the history-prepend content-space restore in
3545
+ // session-history-scroll.ts). The browser's native scroll
3546
+ // anchoring (default `overflow-anchor: auto`) tries to
3547
+ // compensate for the SAME prepends independently, and the two
3548
+ // corrections stacking is the other half of the scroll-up
3549
+ // teleport: ours restores the reader's position, then the
3550
+ // native one nudges it again.
3551
+ 'scrollbar-hide relative z-10 h-full flex-1 overflow-y-auto [scroll-behavior:auto] [overflow-anchor:none]', shouldShowWelcomeOverlay ? 'bg-transparent' : 'bg-background'), onMouseUp: handleChatMouseUp, onMouseDown: handleChatMouseDown, onScroll: handleChatScroll, children: _jsxs("div", { ref: contentRef, role: "log",
3552
+ // Width and gutters live in `SESSION_TRANSCRIPT_CLASS`: the
3553
+ // instant shell draws this same column and the two crossfade
3554
+ // into each other, so a difference here is a sideways jump on
3555
+ // screen, not a style opinion. See session-body.tsx.
3556
+ //
3557
+ // No bottom padding, there or here: the space under the last
3558
+ // message is the auto-scroll spacer's job alone. The `pb-32`
3559
+ // that used to sit here stacked 128px of dead space on top of
3560
+ // it — the "extra inset at the bottom of the session".
3561
+ // Rows run edge to edge: each row owns its gutters and its hover fill.
3562
+ className: transcriptRows ? 'w-full min-w-0 pt-4' : SESSION_TRANSCRIPT_CLASS, children: [_jsxs("div", { className: "flex min-w-0 flex-col", children: [_jsx(SessionPrintHeader, { title: session?.title || 'Untitled', agentName: composerAgentName }), (hasOlder || showOlderLoading) && (_jsxs("div", { className: "mb-6 flex flex-col items-center gap-2", children: [hasOlder && (_jsx("div", { ref: olderSentinelRef, "aria-hidden": true, className: "h-px w-full" })), showOlderLoading && (_jsxs("div", { role: "status", className: "text-muted-foreground flex items-center gap-2 py-1 text-xs", children: [_jsx(Loading, { className: "size-3.5 shrink-0" }), tHardcodedUi.raw('i18nComplete.text85bf890776a7')] })), !showOlderLoading &&
3563
+ !olderPullFailed &&
3564
+ olderAutoloadExhausted({ hasOlder, autoLoadedPages }) && (_jsx(Button, { type: "button", variant: "outline-ghost", size: "sm", onClick: () => void handleLoadOlder(), children: tHardcodedUi.raw('i18nComplete.textf17671d83db0') })), olderPullFailed && !showOlderLoading && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-muted-foreground text-xs", children: tHardcodedUi.raw('i18nComplete.textb03a0041ce33') }), _jsx(Button, { type: "button", variant: "outline-ghost", size: "sm", onClick: () => void handleLoadOlder(), children: tHardcodedUi.raw('i18nComplete.text942087cc2d41') })] }))] })), restoreNotice && !restoreDismissed && (_jsx(RestoreNotice, { notice: restoreNotice, onDismiss: () => dismissRestore(historyKey, restoreNotice.generation) })), _jsx(SessionOutcomesProvider, { projectSessionId: projectSessionId, turnSpans: turnSpans, busy: effectiveBusy, onOpen: handleOpenOutcome, onJump: handleJumpToOutcome, jumping: jumpingToOutcome, children: _jsxs(ToolActivateContext.Provider, { value: toolActivate, children: [showFirstPromptPreview &&
3565
+ firstPromptSource &&
3566
+ queuedSyntheticMessages.length === 0 &&
3567
+ rowsContext && (
3568
+ // A channel thread draws the stand-in as rows, the same
3569
+ // rows its boot body drew (`ThreadBootRows`).
3570
+ _jsx(PendingPromptRows, { text: buildOptimisticPromptTextWithUploads(firstPromptSource.text, firstPromptSource.files), author: rowsContext.owner, sessionId: sessionId, busy: turns.length === 0 && (lastTurnWorking || promptInbox.prompts.some(isFirstPromptRow)) })), showFirstPromptPreview &&
3571
+ firstPromptSource &&
3572
+ queuedSyntheticMessages.length === 0 &&
3573
+ !rowsContext && (_jsx(OptimisticTurn, { text: buildOptimisticPromptTextWithUploads(firstPromptSource.text, firstPromptSource.files),
3574
+ // Preserve a real failed-send status through the
3575
+ // boot-shell handover without inventing upload progress.
3576
+ // A first prompt held on its uploads carries its own.
3577
+ uploadStatus: firstPromptSource.uploadStatus ?? firstPromptUploadStatus, agentNames: agentNames, onFileClick: openFileInComputer, sessionId: sessionId,
3578
+ // The first item of the conversation, like turn 0, which has no
3579
+ // top margin. `first:mt-0` misses here: the list opens with a
3580
+ // zero-height <header>, so the stand-in sat 44px lower than turn 0
3581
+ // and every swap between the two jumped the bubble (KRTX-2931).
3582
+ className: "mt-0",
3583
+ // An undelivered first prompt is a turn about to start, as the
3584
+ // shell above this chat shows it: busy from the first frame,
3585
+ // before the working projection has read the inbox (KRTX-2931).
3586
+ busy: turns.length === 0 && (lastTurnWorking || promptInbox.prompts.some(isFirstPromptRow)) })), turns.map((turn, turnIndex) => {
3587
+ // Check if this turn is a compaction summary — and
3588
+ // whether it actually PRODUCED one. A failed/aborted
3589
+ // attempt (compaction-flagged, no content, not the
3590
+ // working turn) renders as one slim row, draws no
3591
+ // divider, and stacks tight against a neighbouring
3592
+ // failed attempt.
3593
+ const compaction = turn.compaction;
3594
+ const hasCompaction = compaction !== null;
3595
+ const isTurnWorking = turn.live;
3596
+ // `inFlight` (message state) alongside the projection:
3597
+ // classifying by projection alone flipped a streaming
3598
+ // compaction to "failed" for the frames where the two
3599
+ // disagree, popping the divider in and out — a layout
3600
+ // bounce right where the reader is looking.
3601
+ const isFailedCompaction = compaction !== null &&
3602
+ !compaction.hasContent &&
3603
+ !compaction.inFlight &&
3604
+ !isTurnWorking;
3605
+ // Retries collapse to ONE visible row, GLOBALLY: a
3606
+ // failed attempt with ANY later compaction turn (a
3607
+ // retry, or the one that finally landed) is history —
3608
+ // it keeps its TurnViewport (stable keys, scroll
3609
+ // anchors) but renders no content, and an empty
3610
+ // TurnViewport costs 0px (turn-viewport.tsx RULE 2).
3611
+ // Adjacency was not enough: attempts whose error landed
3612
+ // on a plain assistant message used to interleave as
3613
+ // unclassified turns, breaking every consecutive run.
3614
+ const suppressedFailedCompaction = isFailedCompaction && turnIndex < lastCompactionTurnIndex;
3615
+ // Notification-only early-return removed: it rendered the
3616
+ // user's pty_* card but skipped the turn's replies,
3617
+ // hiding every subsequent assistant response in that turn.
3618
+ // Fall through to the normal turn renderer instead.
3619
+ const confirmedActive = turnIsConfirmedActive({
3620
+ isTurnWorking,
3573
3621
  turnId: turn.id,
3574
- resolution: workingTurn,
3575
- sessionWorking: lastTurnWorking,
3576
- confirmedActive,
3577
- inboxPrompt: pendingPrompt,
3578
- }), pendingPrompt: pendingPrompt, onRetryQueued: stableRetryQueued, onRemoveQueued: stableRemoveQueued, queuedPromptViewer: queuedPromptViewer, interruptedBeforeRun: interruptedTurnIds.has(turn.id), isCompaction: hasCompaction, onOpenCompactionSummary: panel ? stableOpenCompactionSummary : undefined, onOpenPastedContent: panel ? stableOpenPastedContent : undefined, providers: providers, commandMessages: commandMessagesRef.current, commands: commands, disableToolNavigation: disableToolNavigation, onPermissionReply: stablePermissionReply, onRewind: stableRewind, editingText: rewindTarget?.messageId === turn.id
3579
- ? rewindTarget.text
3580
- : null, editPending: editSendPending || !!sessionState?.rewindPending, onEditCancel: stableEditCancel, onEditSend: stableEditSend, rewindDisabled: !!readOnly ||
3581
- !runtimeCanRewind ||
3582
- !sessionState ||
3583
- isBusy ||
3584
- sessionState.rewindPending ||
3585
- // The runtime is not idle while queued prompts
3586
- // are still on their way to it — a rewind mid-
3587
- // delivery fails downstream with "Session is
3588
- // busy" (measured); refuse it up front instead.
3589
- promptInbox.prompts.length > 0 }, turnRenderKeys.get(turn.id)));
3590
- })] }) }), isOptimisticCompacting && !hasCompactionTurn && (_jsx("div", { className: turns.length > 0 ? 'mt-12' : 'mt-2', children: _jsx(CompactionMarker, { running: true }) })), [
3591
- ...(turnOutcome.recent_failures ?? []),
3592
- ...(turnOutcome.last_ended?.end_reason === 'failed' &&
3593
- !turnOutcome.recent_failures?.some((failure) => failure.message_id === turnOutcome.last_ended?.message_id)
3594
- ? [turnOutcome.last_ended]
3595
- : []),
3596
- ].filter((failure) => !isAbortError(failure.error) &&
3597
- !failureShownByTurn(failure, turns) &&
3598
- !failureSupersededByTurn(failure, turns) &&
3599
- (!failure.error?.message || failure.error.message !== commandError?.message)).map((failure) => {
3600
- const messageId = failure.message_id ?? 'persisted-turn-failure';
3601
- // Use the SDK's settle window for a cause that may arrive
3602
- // one frame later, including an unnamed failed last turn.
3603
- const notice = turnEndNotice({
3604
- ...turnOutcome,
3605
- recent_failures: [{ ...failure, message_id: messageId, error: failure.error ?? null }],
3606
- }, messageId, { hasError: false, isAbort: false });
3607
- return notice ? (_jsx(TurnErrorDisplay, { errorText: notice.kind === 'unexplained'
3608
- ? tHardcodedUi.raw('i18nComplete.text73112526c03a')
3609
- : persistedFailureText(failure.error), className: "mt-2" }, messageId)) : null;
3610
- }), commandError && (_jsx(TurnErrorDisplay, { error: commandError, isAbort: isAbortError(commandError.cause), modelOptions: modelOptions, className: "mt-2" })), showFallbackBusyRow && fallbackBusyRowTurnId === null && (_jsx(SessionBusyIndicator, { sessionId: sessionId,
3611
- // Matches the stand-in's row spacing under a bubble
3612
- // (`OptimisticTurn`), so the crossfade into the real
3613
- // transcript does not move it. Nothing above it when
3614
- // the transcript is empty, so no margin there.
3615
- className: turns.length === 0 ? undefined : 'mt-6' }))] }), _jsx("div", { ref: spacerElRef, "data-scroll-spacer": true })] }) }), selectionPopup && (_jsx("div", { "data-reply-popup": true, className: "absolute z-50", style: {
3616
- left: `${selectionPopup.x}px`,
3617
- top: `${selectionPopup.y}px`,
3618
- transform: 'translate(-50%, -100%)',
3619
- }, children: _jsxs(Button, { onClick: handleSelectionReply, size: "sm", className: "animate-in fade-in-0 zoom-in-95 duration-normal origin-bottom px-3 text-xs ease-out has-[>svg]:px-3", children: [tHardcodedUi.raw('i18nComplete.textc253f451bdd5'), _jsx(ArrowBendUpLeftIcon, { className: "size-4 shrink-0" })] }) })), _jsx(ChatMinimap, { turns: turns, scrollRef: scrollRef, contentRef: contentRef }), _jsx("div", { className: cn('absolute inset-x-0 bottom-4 z-20 flex justify-center', !showScrollButton && 'pointer-events-none'), children: _jsx(Button, { variant: "outline", size: "icon-md", "aria-hidden": !showScrollButton, tabIndex: showScrollButton ? undefined : -1, className: cn('hit-area-2 bg-background hover:bg-muted rounded-full', 'transition-[opacity,scale] ease-[cubic-bezier(0.23,1,0.32,1)] active:scale-[0.96] motion-reduce:scale-100 motion-reduce:transition-opacity', showScrollButton
3620
- ? 'duration-normal scale-100 opacity-100'
3621
- : 'duration-fast scale-[0.97] opacity-0'), onClick: smoothScrollToAbsoluteBottom, children: _jsx(CaretDownIcon, { className: "size-4" }) }) })] })), readOnly && inputReplacement ? (_jsx("div", { className: cn(COMPOSER_SHELL_CLASS, 'pb-4'), children: inputReplacement })) : null, !readOnly && (_jsx(_Fragment, { children: _jsx(SessionChatInput
3622
- // `undefined`, not `true`, once released: the composer's own
3623
- // viewport rule (>= 640px) still decides, so this never forces
3624
- // focus onto a phone keyboard.
3625
- , {
3622
+ activeTurnId,
3623
+ pendingDelivery: !!working.pendingDelivery,
3624
+ });
3625
+ const pendingPrompt = !confirmedActive && turn.assistant.length === 0
3626
+ ? pendingPromptsByMessageId.get(turn.id)
3627
+ : undefined;
3628
+ // A queued prompt is not in the runtime transcript yet;
3629
+ // its author still shows (`authorForTurn`).
3630
+ const turnAuthor = authorForTurn(transcriptAuthors, messageAuthors, turn.id, pendingPrompt?.wire_message_id);
3631
+ return (_jsx(TranscriptTurnRow
3632
+ // ONE element per prompt: keyed by the id the
3633
+ // bubble was FIRST painted under, so the swap to a
3634
+ // re-minted echo id re-renders this node instead
3635
+ // of mounting a new one (opacity keeps animating,
3636
+ // hover state survives, nothing jumps).
3637
+ , { turnId: turn.id, modelOptions: modelOptions, suppressed: suppressedFailedCompaction, continuedWith: harnessSwitches.get(turn.id)?.harness, fullTextState: loaded.state[turn.id] ?? 'idle', onLoadFullText: loadFullTexts, showBusyRow: showFallbackBusyRow &&
3638
+ fallbackBusyRowTurnId === turn.id,
3639
+ // Queued bubbles STACK: a pending turn right after
3640
+ // another pending turn sits close to it, like a
3641
+ // list of what is waiting — not a turn's width
3642
+ // apart as if each had been answered in between.
3643
+ // (Failed compaction rows need no stacking rule any
3644
+ // more — at most one is visible at a time.)
3645
+ viewportClassName: turnIndex === 0 || transcriptRows
3646
+ ? ''
3647
+ : // A compaction and the work after it continue the turn above.
3648
+ turn.compaction || turn.continues
3649
+ ? 'mt-6'
3650
+ : lastTurnWorking &&
3651
+ pendingTurnIds.has(turn.id) &&
3652
+ pendingTurnIds.has(turns[turnIndex - 1].id)
3653
+ ? 'mt-3'
3654
+ : 'mt-12', turn: turn, author: turnAuthor, showAuthor: showAuthorName(turnAuthor, groupChat, viewerId), stoppedBy: stoppedByName(messageAuthors, turn.id, viewerId), servedModel: servedModelOfTurn(modelUsage, turn.id, turns[turnIndex - 1]?.id), turnOutcome: turnOutcome, isLast: turn.id === lastUserMessageId, ownsPlan: turn.id === planAnchorId, sessionId: sessionId, projectId: projectId, projectSessionId: projectSessionId, sessionStatus: sessionStatus, permissions: pendingPermissions, questions: pendingQuestions, agentNames: agentNames, isFirstTurn: turnIndex === 0,
3655
+ // Handed over only once the stand-in has stepped
3656
+ // aside — while it is up it draws these itself.
3657
+ pendingText: turnIndex === 0 ? firstTurnHandover?.text : undefined, pendingAttachments: sentAttachmentsForTurn({
3658
+ sentByMessage: sentAttachmentsByMessage,
3659
+ messageId: turn.id,
3660
+ originId: optimisticOriginOf(sessionId, turn.id),
3661
+ isFirstTurn: turnIndex === 0,
3662
+ firstTurnHandover: firstTurnHandover?.attachments,
3663
+ firstTurnSent: firstPromptAttachments(projectSessionId),
3664
+ queuedRowAttachments: inboxRowsByMessageId.get(turn.id)?.attachments,
3665
+ }), uploadStatus: heldSendFailures?.[turn.id]
3666
+ ? {
3667
+ state: 'failed',
3668
+ message: heldSendFailures[turn.id].message,
3669
+ onRetry: () => retryHeldSend(sessionId, turn.id, resendHeldSend, (error) => classifySessionError(error).message),
3670
+ }
3671
+ : turnIndex === 0 && firstTurnHandover?.attachments.length
3672
+ ? firstPromptUploadStatus
3673
+ : undefined, sessionWorking: lastTurnWorking, isWorkingTurn: turn.live, suppressBusyIndicator: suppressWorkingTurnBusy, awaitingUser: awaitingUserInput,
3674
+ // An idle send stays in the inbox until delivery,
3675
+ // but nothing runs ahead of it: queued only while
3676
+ // it waits behind a turn (`turnRendersQueued`).
3677
+ // The first turn, with no turn working, has
3678
+ // nothing ahead of it: a session's first prompt is
3679
+ // starting, not queued, while the computer comes up
3680
+ // and before the turn layer marks it queued or live.
3681
+ pending: turnIndex === 0 && workingTurn.workingTurnId === null && !confirmedActive
3682
+ ? pendingPrompt?.state === 'waiting' || pendingPrompt?.state === 'failed'
3683
+ : turnRendersQueued({
3684
+ turnId: turn.id,
3685
+ resolution: workingTurn,
3686
+ sessionWorking: lastTurnWorking,
3687
+ confirmedActive,
3688
+ inboxPrompt: pendingPrompt,
3689
+ }), pendingPrompt: pendingPrompt, onRetryQueued: stableRetryQueued, onRemoveQueued: stableRemoveQueued, queuedPromptViewer: queuedPromptViewer, interruptedBeforeRun: interruptedTurnIds.has(turn.id), isCompaction: hasCompaction, onOpenCompactionSummary: panel ? stableOpenCompactionSummary : undefined, onOpenPastedContent: panel ? stableOpenPastedContent : undefined, providers: providers, commandMessages: commandMessagesRef.current, commands: commands, disableToolNavigation: disableToolNavigation, onPermissionReply: stablePermissionReply, onRewind: stableRewind, editingText: rewindTarget?.messageId === turn.id
3690
+ ? rewindTarget.text
3691
+ : null, editPending: editSendPending || !!sessionState?.rewindPending, onEditCancel: stableEditCancel, onEditSend: stableEditSend, rewindDisabled: !!readOnly ||
3692
+ (rewindable !== null && !rewindable.has(turn.id)) ||
3693
+ !runtimeCanRewind ||
3694
+ !sessionState ||
3695
+ isBusy ||
3696
+ sessionState.rewindPending ||
3697
+ // The runtime is not idle while queued prompts
3698
+ // are still on their way to it — a rewind mid-
3699
+ // delivery fails downstream with "Session is
3700
+ // busy" (measured); refuse it up front instead.
3701
+ promptInbox.prompts.length > 0 }, turnRenderKeys.get(turn.id)));
3702
+ })] }) }), isOptimisticCompacting && !hasCompactionTurn && (_jsx("div", { className: turns.length > 0 ? 'mt-12' : 'mt-2', children: _jsx(CompactionMarker, { running: true }) })), [
3703
+ ...(turnOutcome.recent_failures ?? []),
3704
+ ...(turnOutcome.last_ended?.end_reason === 'failed' &&
3705
+ !turnOutcome.recent_failures?.some((failure) => failure.message_id === turnOutcome.last_ended?.message_id)
3706
+ ? [turnOutcome.last_ended]
3707
+ : []),
3708
+ ].filter((failure) => !isAbortError(failure.error) &&
3709
+ !failureShownByTurn(failure, turns) &&
3710
+ !failureSupersededByTurn(failure, turns) &&
3711
+ (!failure.error?.message || failure.error.message !== commandError?.message)).map((failure) => {
3712
+ const messageId = failure.message_id ?? 'persisted-turn-failure';
3713
+ // Use the SDK's settle window for a cause that may arrive
3714
+ // one frame later, including an unnamed failed last turn.
3715
+ const notice = turnEndNotice({
3716
+ ...turnOutcome,
3717
+ recent_failures: [{ ...failure, message_id: messageId, error: failure.error ?? null }],
3718
+ }, messageId, { hasError: false, isAbort: false });
3719
+ return notice ? (_jsx(TurnErrorDisplay, { errorText: notice.kind === 'unexplained'
3720
+ ? tHardcodedUi.raw('i18nComplete.text73112526c03a')
3721
+ : persistedFailureText(failure.error), className: "mt-2" }, messageId)) : null;
3722
+ }), commandError && (_jsx(TurnErrorDisplay, { error: commandError, isAbort: isAbortError(commandError.cause), modelOptions: modelOptions, className: "mt-2" })), showFallbackBusyRow && fallbackBusyRowTurnId === null && (rowsContext ? (
3723
+ // A channel thread waits in the agent's answer row.
3724
+ _jsx(AgentBusyRow, { sessionId: sessionId })) : (_jsx(SessionBusyIndicator, { sessionId: sessionId,
3725
+ // Matches the stand-in's row spacing under a bubble
3726
+ // (`OptimisticTurn`), so the crossfade into the real
3727
+ // transcript does not move it. Nothing above it when
3728
+ // the transcript is empty, so no margin there.
3729
+ className: turns.length === 0 ? undefined : 'mt-6' })))] }), _jsx("div", { ref: spacerElRef, "data-scroll-spacer": true })] }) }), selectionPopup && (_jsx("div", { "data-reply-popup": true, className: "absolute z-50", style: {
3730
+ left: `${selectionPopup.x}px`,
3731
+ top: `${selectionPopup.y}px`,
3732
+ transform: 'translate(-50%, -100%)',
3733
+ }, children: _jsxs(Button, { onClick: handleSelectionReply, size: "sm", className: "animate-in fade-in-0 zoom-in-95 duration-normal origin-bottom px-3 text-xs ease-out has-[>svg]:px-3", children: [tHardcodedUi.raw('i18nComplete.textc253f451bdd5'), _jsx(ArrowBendUpLeftIcon, { className: "size-4 shrink-0" })] }) })), transcriptRows ? null : (_jsx(ChatMinimap, { turns: turns, scrollRef: scrollRef, contentRef: contentRef })), _jsx("div", { className: cn('absolute inset-x-0 bottom-4 z-20 flex justify-center', !showScrollButton && 'pointer-events-none'), children: _jsx(Button, { variant: "outline", size: "icon-md", "aria-hidden": !showScrollButton, tabIndex: showScrollButton ? undefined : -1, className: cn('hit-area-2 bg-background hover:bg-muted rounded-full', 'transition-[opacity,scale] ease-[cubic-bezier(0.23,1,0.32,1)] active:scale-[0.96] motion-reduce:scale-100 motion-reduce:transition-opacity', showScrollButton
3734
+ ? 'duration-normal scale-100 opacity-100'
3735
+ : 'duration-fast scale-[0.97] opacity-0'), onClick: smoothScrollToAbsoluteBottom, children: _jsx(CaretDownIcon, { className: "size-4" }) }) })] })), readOnly && inputReplacement ? (_jsx("div", { className: cn(COMPOSER_SHELL_CLASS, 'pb-4'), children: inputReplacement })) : null, !readOnly && (_jsx(_Fragment, { children: _jsx(SessionChatInput
3626
3736
  // `undefined`, not `true`, once released: the composer's own
3627
3737
  // viewport rule (>= 640px) still decides, so this never forces
3628
3738
  // focus onto a phone keyboard.
3629
- autoFocus: deferComposerFocus ? false : undefined, onSend: async (text, files, mentions, attachments, placement) => {
3630
- if (await queueEdit.save(text))
3631
- return;
3632
- // Enter while a turn runs steers it (D9.1); Cmd/Ctrl+Enter is Queue List.
3633
- await handleSend(text, files, mentions, attachments, composerSendDelivery(placement ?? 'transcript', isBusyRef.current));
3634
- }, prefill: composerPrefill, onPrefillApplied: (id) => {
3635
- useSessionComposerPrefillStore.getState().clearPrefill(sessionId, id);
3636
- },
3637
- // Up from the first row takes the queue back; the placeholder
3638
- // says so while there is something to take.
3639
- onArrowUpAtStart: () => queueEdit.takeBack(), hint: canTakeBackQueue ? tHardcodedUi.raw('i18nComplete.text03a01dd53ffa') : undefined,
3640
- // Editing a queued message: the send saves it back into the
3641
- // queue, so the control says Submit, never Stop.
3642
- submitLabel: queueEdit.editing ? tQueue('submitEdit') : null, draftScope: composerDraftScope, draftActive: !deferComposerFocus, attachRequestId: attachRequestId, isBusy: isBusy,
3643
- // The ONE projection, not the 300 ms busy fade: it is what
3644
- // decides whether a `/` command may be dispatched, and a fade
3645
- // timer that has already lapsed would let one abort a live turn.
3646
- // `effectiveBusy` folds in optimistic compaction (not a turn, so
3647
- // not in `working`) and the retrying-turn predicate, so this
3648
- // site reads one value instead of re-OR-ing a term onto it.
3649
- sessionWorking: effectiveBusy,
3650
- // Gates `/` COMMANDS only. A prompt typed at a sleeping box is
3651
- // an inbox row and goes out when the box answers; a command has
3652
- // no row, and `runCommand` swallows it silently until the
3653
- // runtime is switched.
3654
- runtimeReady: runtimeReady, rewind: composerRewind, onStop: handleStop, canStop: canStop, escCount: escCount, agents: local.agent.list, selectedAgent: composerAgentName, onAgentChange: handleAgentChange, noAccessibleAgents: noAccessibleAgents, commands: chatCommands, commandsResolved: agentCommands !== undefined, slashFiles: chatSlashFiles, onOpenPastedContent: panel ? stableOpenPastedContent : undefined, onCommand: handleCommand, onHoldCommand: handleHoldCommand, models: local.model.list, selectedModel: local.model.currentKey ?? null, onModelChange: handleModelChange, modelDefaultControls: chatModelDefaultControls, variants: local.model.variant.list, selectedVariant: local.model.variant.current ?? null, onVariantChange: handleVariantChange, messages: messages, sessionId: sessionId, projectId: projectId, providers: providers, modelRequired: true, modelsLoading: providersLoading, onContextClick: handleContextClick, onCompactClick: runtimeCanCompact ? handleCompactClick : undefined, quoteRequests: quoteRequests, onQuoteRequestsApplied: handleQuoteRequestsApplied,
3655
- // Only lock the input into question-answer mode while the session is
3656
- // actually busy (a live question keeps the run busy). If a question
3657
- // chip is ever showing while the session is idle — e.g. a dead /
3658
- // abandoned question the agent left behind — the input stays unlocked
3659
- // so a typed message is sent to the agent instead of being swallowed
3660
- // as a custom answer.
3661
- lockForQuestion: !!renderedQuestion && isBusy,
3662
- // Same dead-prompt guard as questions: only lock while the agent is
3663
- // actually paused on the decision (isBusy), so a stale card can't
3664
- // swallow the composer on an idle session.
3665
- lockForApproval: runtimePermissionLocksComposer(pendingPermissions.length, isBusy), onCustomAnswer: handleCustomAnswer, questionButtonLabel: renderedQuestion ? questionAction.label : null, questionActive: !!renderedQuestion || approvalDocked, questionCanAct: questionAction.canAct, onQuestionAction: handleQuestionAction, aboveSlot: chatAboveSlot, inputSlot: chatInputSlot, toolbarSlot: chatToolbarSlot,
3666
- // The shell can now render on a cached transcript alone, i.e. before
3667
- // the sandbox answers — so sending has to be gated separately from
3668
- // reading. See sessionComposerReadiness.
3669
- notice: composerReadiness.notice, onNoticeRetry: composerReadiness.notice && composerReadiness.retryable
3670
- ? requestRuntimeReconnect
3671
- : undefined }) }))] })] }) }));
3739
+ , {
3740
+ // `undefined`, not `true`, once released: the composer's own
3741
+ // viewport rule (>= 640px) still decides, so this never forces
3742
+ // focus onto a phone keyboard.
3743
+ autoFocus: deferComposerFocus ? false : undefined, onSend: async (text, files, mentions, attachments, placement) => {
3744
+ if (await queueEdit.save(text))
3745
+ return;
3746
+ // Enter while a turn runs steers it (D9.1); Cmd/Ctrl+Enter is Queue List.
3747
+ await handleSend(text, files, mentions, attachments, composerSendDelivery(placement ?? 'transcript', isBusyRef.current));
3748
+ }, prefill: composerPrefill, onPrefillApplied: (id) => {
3749
+ useSessionComposerPrefillStore.getState().clearPrefill(sessionId, id);
3750
+ },
3751
+ // Up from the first row takes the queue back; the placeholder
3752
+ // says so while there is something to take.
3753
+ onArrowUpAtStart: () => queueEdit.takeBack(),
3754
+ // A host placeholder ("Reply…") is static: the rotating tips would replace it.
3755
+ hint: canTakeBackQueue ? tHardcodedUi.raw('i18nComplete.text03a01dd53ffa') : composerPlaceholder,
3756
+ // Editing a queued message: the send saves it back into the
3757
+ // queue, so the control says Submit, never Stop.
3758
+ submitLabel: queueEdit.editing ? tQueue('submitEdit') : null, draftScope: composerDraftScope, draftActive: !deferComposerFocus, attachRequestId: attachRequestId, isBusy: isBusy,
3759
+ // The ONE projection, not the 300 ms busy fade: it is what
3760
+ // decides whether a `/` command may be dispatched, and a fade
3761
+ // timer that has already lapsed would let one abort a live turn.
3762
+ // `effectiveBusy` folds in optimistic compaction (not a turn, so
3763
+ // not in `working`) and the retrying-turn predicate, so this
3764
+ // site reads one value instead of re-OR-ing a term onto it.
3765
+ sessionWorking: effectiveBusy,
3766
+ // Gates `/` COMMANDS only. A prompt typed at a sleeping box is
3767
+ // an inbox row and goes out when the box answers; a command has
3768
+ // no row, and `runCommand` swallows it silently until the
3769
+ // runtime is switched.
3770
+ runtimeReady: runtimeReady, rewind: composerRewind, onStop: handleStop, canStop: canStop, escCount: escCount, agents: local.agent.list, selectedAgent: composerAgentName, onAgentChange: handleAgentChange, noAccessibleAgents: noAccessibleAgents, commands: chatCommands, commandsResolved: agentCommands !== undefined, slashFiles: chatSlashFiles, onOpenPastedContent: panel ? stableOpenPastedContent : undefined, onCommand: handleCommand, onHoldCommand: handleHoldCommand, models: local.model.list, selectedModel: local.model.currentKey ?? null, onModelChange: handleModelChange, modelDefaultControls: chatModelDefaultControls, variants: local.model.variant.list, selectedVariant: local.model.variant.current ?? null, onVariantChange: handleVariantChange, messages: messages, sessionId: sessionId, projectId: projectId, providers: providers, modelRequired: true, modelsLoading: providersLoading, onContextClick: handleContextClick, onCompactClick: runtimeCanCompact ? handleCompactClick : undefined, quoteRequests: quoteRequests, onQuoteRequestsApplied: handleQuoteRequestsApplied,
3771
+ // Only lock the input into question-answer mode while the session is
3772
+ // actually busy (a live question keeps the run busy). If a question
3773
+ // chip is ever showing while the session is idle — e.g. a dead /
3774
+ // abandoned question the agent left behind — the input stays unlocked
3775
+ // so a typed message is sent to the agent instead of being swallowed
3776
+ // as a custom answer.
3777
+ lockForQuestion: !!renderedQuestion && isBusy,
3778
+ // Same dead-prompt guard as questions: only lock while the agent is
3779
+ // actually paused on the decision (isBusy), so a stale card can't
3780
+ // swallow the composer on an idle session.
3781
+ lockForApproval: runtimePermissionLocksComposer(pendingPermissions.length, isBusy), onCustomAnswer: handleCustomAnswer, questionButtonLabel: renderedQuestion ? questionAction.label : null, questionActive: !!renderedQuestion || approvalDocked, questionCanAct: questionAction.canAct, onQuestionAction: handleQuestionAction, aboveSlot: chatAboveSlot, inputSlot: chatInputSlot, toolbarSlot: chatToolbarSlot,
3782
+ // The shell can now render on a cached transcript alone, i.e. before
3783
+ // the sandbox answers — so sending has to be gated separately from
3784
+ // reading. See sessionComposerReadiness.
3785
+ notice: hostNotice(composerReadiness.notice, rowsContext?.notices), noticeAsleep: composerReadiness.notice === SESSION_NOTICE.idle,
3786
+ // Inside the thread panel's bordered box, as the channel composer.
3787
+ // A phone keeps the row under the card; a 720px desktop window does not.
3788
+ underbarPlacement: transcriptRows && !isPhone ? 'inline' : undefined,
3789
+ // The panel's toolbar is narrow: the agent is its tile, the model keeps its name.
3790
+ agentPickerCompact: transcriptRows && !isPhone,
3791
+ // A thread panel's rows run `px-5`, and the channel composer
3792
+ // beside it sits `pb-4` off the window's bottom edge: the same
3793
+ // margins, so the two boxes line up.
3794
+ parentClassName: transcriptRows ? 'max-w-none px-5 pb-4' : undefined, onNoticeRetry: composerReadiness.notice && composerReadiness.retryable
3795
+ ? requestRuntimeReconnect
3796
+ : undefined }) }))] })] }) }) }));
3672
3797
  }