@kortix/ui 0.14.12 → 0.14.14

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 (438) hide show
  1. package/README.md +1 -1
  2. package/dist/_vendor/kortix-shared.js +521 -7045
  3. package/dist/_vendor/shared/app-config.d.ts +8 -0
  4. package/dist/_vendor/shared/artifact-annotations.d.ts +708 -0
  5. package/dist/_vendor/shared/artifact-comments.d.ts +327 -0
  6. package/dist/_vendor/shared/index.d.ts +3 -1
  7. package/dist/_vendor/shared/my-kortix-agent.d.ts +40 -0
  8. package/dist/_vendor/shared/peer-message-prompt.d.ts +35 -0
  9. package/dist/_vendor/shared/pixel-mark.d.ts +11 -0
  10. package/dist/_vendor/shared/prompt-attachments.d.ts +1 -0
  11. package/dist/_vendor/shared/reminder-prompt.d.ts +8 -2
  12. package/dist/_vendor/shared/runtime-versions.d.ts +8 -0
  13. package/dist/artifacts/artifact-frame.d.ts +1 -0
  14. package/dist/artifacts/artifact-frame.js +18 -5
  15. package/dist/artifacts/frame-policy.d.ts +1 -1
  16. package/dist/artifacts/frame-policy.js +10 -5
  17. package/dist/customize/agents/agent-access-text.d.ts +16 -0
  18. package/dist/customize/agents/agent-access-text.js +54 -0
  19. package/dist/customize/agents/agent-approval-rules.d.ts +8 -0
  20. package/dist/customize/agents/agent-approval-rules.js +26 -0
  21. package/dist/customize/agents/agent-authority-card.js +4 -1
  22. package/dist/customize/agents/agent-cover-card.d.ts +4 -1
  23. package/dist/customize/agents/agent-cover-card.js +2 -2
  24. package/dist/customize/agents/agent-detail-aside.d.ts +1 -2
  25. package/dist/customize/agents/agent-detail-aside.js +8 -11
  26. package/dist/customize/agents/agent-drives-section.d.ts +23 -7
  27. package/dist/customize/agents/agent-drives-section.js +107 -41
  28. package/dist/customize/agents/agent-instructions.d.ts +11 -0
  29. package/dist/customize/agents/agent-instructions.js +114 -0
  30. package/dist/customize/agents/agent-model-view.d.ts +17 -0
  31. package/dist/customize/agents/agent-model-view.js +67 -0
  32. package/dist/customize/agents/agent-overview.d.ts +53 -0
  33. package/dist/customize/agents/agent-overview.js +242 -0
  34. package/dist/customize/agents/agent-page-chrome.d.ts +128 -0
  35. package/dist/customize/agents/agent-page-chrome.js +145 -0
  36. package/dist/customize/agents/agent-page.d.ts +13 -1
  37. package/dist/customize/agents/agent-page.js +478 -367
  38. package/dist/customize/agents/agent-pin-button.d.ts +10 -0
  39. package/dist/customize/agents/agent-pin-button.js +20 -0
  40. package/dist/customize/agents/agent-policy-sets-section.d.ts +16 -0
  41. package/dist/customize/agents/agent-policy-sets-section.js +80 -0
  42. package/dist/customize/agents/agent-policy-sets.js +1 -1
  43. package/dist/customize/agents/agent-section-rail.d.ts +21 -0
  44. package/dist/customize/agents/agent-section-rail.js +23 -0
  45. package/dist/customize/agents/agent-share-control.d.ts +4 -3
  46. package/dist/customize/agents/agent-share-control.js +15 -16
  47. package/dist/customize/agents/{agent-people-section.d.ts → agent-share-dialog.d.ts} +3 -1
  48. package/dist/customize/agents/{agent-people-section.js → agent-share-dialog.js} +21 -21
  49. package/dist/customize/agents/agent-tabs.d.ts +95 -0
  50. package/dist/customize/agents/agent-tabs.js +199 -0
  51. package/dist/customize/agents/agents-page.d.ts +0 -21
  52. package/dist/customize/agents/agents-page.js +104 -118
  53. package/dist/customize/agents/connectors-required.d.ts +16 -0
  54. package/dist/customize/agents/connectors-required.js +19 -0
  55. package/dist/customize/agents/create-agent-modal.d.ts +19 -0
  56. package/dist/customize/agents/create-agent-modal.js +94 -0
  57. package/dist/customize/connectors/agent-access.d.ts +4 -4
  58. package/dist/customize/connectors/agent-access.js +7 -10
  59. package/dist/customize/connectors/connector-identity.d.ts +12 -0
  60. package/dist/customize/connectors/connector-identity.js +15 -2
  61. package/dist/customize/connectors/connector-routes.js +2 -2
  62. package/dist/customize/connectors/connectors-page.d.ts +14 -1
  63. package/dist/customize/connectors/connectors-page.js +101 -32
  64. package/dist/customize/connectors/detail/app-page.js +1 -1
  65. package/dist/customize/connectors/detail/connector-agents.js +0 -3
  66. package/dist/customize/connectors/detail/connector-page.d.ts +1 -1
  67. package/dist/customize/connectors/detail/connector-page.js +5 -2
  68. package/dist/customize/connectors/detail/connector-policy-sets.js +1 -1
  69. package/dist/customize/connectors/detail/connector-triggers.js +2 -2
  70. package/dist/customize/connectors/use-agent-access.d.ts +7 -5
  71. package/dist/customize/connectors/use-agent-access.js +2 -6
  72. package/dist/customize/models/models-page.d.ts +7 -1
  73. package/dist/customize/models/models-page.js +2 -2
  74. package/dist/customize/project-settings/project-admin-sections.d.ts +63 -0
  75. package/dist/customize/project-settings/project-admin-sections.js +80 -0
  76. package/dist/customize/project-settings/project-settings-page.d.ts +1 -1
  77. package/dist/customize/project-settings/project-settings-page.js +4 -13
  78. package/dist/customize/project-settings/project-settings-sections.d.ts +5 -5
  79. package/dist/customize/project-settings/project-settings-sections.js +6 -20
  80. package/dist/customize/providers/provider-accordion.js +29 -17
  81. package/dist/customize/providers/provider-connect.js +1 -1
  82. package/dist/customize/providers/provider-sheet.d.ts +4 -1
  83. package/dist/customize/providers/provider-sheet.js +15 -5
  84. package/dist/customize/secrets/secrets-page.d.ts +5 -1
  85. package/dist/customize/secrets/secrets-page.js +2 -2
  86. package/dist/customize/sections/gateway-view.d.ts +10 -2
  87. package/dist/customize/sections/gateway-view.js +16 -4
  88. package/dist/customize/sections/llm-provider/chatgpt-subscription-connect.d.ts +12 -5
  89. package/dist/customize/sections/llm-provider/chatgpt-subscription-connect.js +131 -8
  90. package/dist/customize/sections/llm-provider/claude-code-step.d.ts +10 -0
  91. package/dist/customize/sections/llm-provider/claude-code-step.js +32 -0
  92. package/dist/customize/sections/llm-provider/models-tab.js +5 -1
  93. package/dist/customize/sections/view/account-secret-resources-panel.d.ts +5 -3
  94. package/dist/customize/sections/view/account-secret-resources-panel.js +122 -19
  95. package/dist/customize/sections/view/agent-editor-access-fields.d.ts +1 -1
  96. package/dist/customize/sections/view/agent-editor-access-fields.js +8 -4
  97. package/dist/customize/sections/view/agent-editor-basics-fields.d.ts +4 -1
  98. package/dist/customize/sections/view/agent-editor-basics-fields.js +33 -4
  99. package/dist/customize/sections/view/agent-editor-catalog.d.ts +2 -2
  100. package/dist/customize/sections/view/agent-editor-catalog.js +10 -12
  101. package/dist/customize/sections/view/agent-editor-primitives.d.ts +1 -1
  102. package/dist/customize/sections/view/agent-editor-primitives.js +4 -4
  103. package/dist/customize/sections/view/agent-editor.d.ts +37 -26
  104. package/dist/customize/sections/view/agent-editor.js +19 -19
  105. package/dist/customize/sections/view/grant-mode-field.d.ts +8 -20
  106. package/dist/customize/sections/view/grant-mode-field.js +31 -57
  107. package/dist/customize/sections/view/permission-editor.d.ts +3 -1
  108. package/dist/customize/sections/view/permission-editor.js +19 -10
  109. package/dist/customize/sections/view/review-view.d.ts +8 -0
  110. package/dist/customize/sections/view/review-view.js +12 -1
  111. package/dist/customize/sections/view/secret-delivery.js +1 -4
  112. package/dist/customize/sections/view/secrets-view.d.ts +7 -1
  113. package/dist/customize/sections/view/secrets-view.js +42 -6
  114. package/dist/customize/settings/rail.js +2 -8
  115. package/dist/customize/settings/settings-tabs.d.ts +2 -2
  116. package/dist/customize/settings/settings-tabs.js +17 -19
  117. package/dist/customize/settings/tabs/experimental-tab.d.ts +3 -0
  118. package/dist/customize/settings/tabs/experimental-tab.js +6 -1
  119. package/dist/customize/settings/tabs/sandbox-tab.d.ts +4 -6
  120. package/dist/customize/settings/tabs/sandbox-tab.js +9 -9
  121. package/dist/customize/shared/access-list.d.ts +39 -0
  122. package/dist/customize/shared/access-list.js +62 -0
  123. package/dist/customize/shared/agent-resource-scope.d.ts +87 -0
  124. package/dist/customize/shared/agent-resource-scope.js +123 -0
  125. package/dist/customize/shared/capability-library.d.ts +41 -0
  126. package/dist/customize/shared/capability-library.js +271 -0
  127. package/dist/customize/shared/capability-page-shell.d.ts +18 -1
  128. package/dist/customize/shared/capability-page-shell.js +21 -1
  129. package/dist/customize/shared/catalog/catalog-space-tabs.d.ts +14 -0
  130. package/dist/customize/shared/catalog/catalog-space-tabs.js +20 -0
  131. package/dist/customize/shared/customize-tab-bar.d.ts +13 -0
  132. package/dist/customize/shared/customize-tab-bar.js +95 -0
  133. package/dist/customize/shared/entity/entity-modal.d.ts +17 -1
  134. package/dist/customize/shared/entity/entity-modal.js +10 -9
  135. package/dist/customize/shared/leave-guard.d.ts +23 -0
  136. package/dist/customize/shared/leave-guard.js +87 -0
  137. package/dist/customize/shared/standalone-settings-nav.js +4 -1
  138. package/dist/customize/shared/tool-rules.d.ts +11 -0
  139. package/dist/customize/shared/tool-rules.js +29 -0
  140. package/dist/customize/skills/builtin-skill-modal.d.ts +6 -0
  141. package/dist/customize/skills/builtin-skill-modal.js +41 -0
  142. package/dist/customize/skills/skills-page.d.ts +21 -18
  143. package/dist/customize/skills/skills-page.js +16 -110
  144. package/dist/customize/triggers/runtime-triggers.d.ts +17 -0
  145. package/dist/customize/triggers/runtime-triggers.js +57 -0
  146. package/dist/customize/triggers/schedule/event-trigger-copy.js +2 -2
  147. package/dist/customize/triggers/schedule/post-to-field.js +2 -2
  148. package/dist/customize/triggers/schedule/schedule-copy.d.ts +11 -6
  149. package/dist/customize/triggers/schedule/schedule-copy.js +29 -8
  150. package/dist/customize/triggers/schedule/schedule-detail-sheet.js +7 -3
  151. package/dist/customize/triggers/schedule/trigger-composer-logic.d.ts +26 -1
  152. package/dist/customize/triggers/schedule/trigger-composer-logic.js +64 -3
  153. package/dist/customize/triggers/schedule/trigger-composer.js +57 -20
  154. package/dist/customize/triggers/schedule/trigger-edit-logic.js +18 -5
  155. package/dist/customize/triggers/schedule/trigger-filter.d.ts +10 -0
  156. package/dist/customize/triggers/schedule/trigger-filter.js +16 -0
  157. package/dist/customize/triggers/schedule/trigger-runs-panel.js +1 -0
  158. package/dist/customize/triggers/schedule/trigger-toast.js +2 -1
  159. package/dist/customize/triggers/schedule/use-trigger-mutations.d.ts +1 -1
  160. package/dist/customize/triggers/schedule/use-trigger-mutations.js +1 -1
  161. package/dist/customize/triggers/schedule/when-app.d.ts +15 -0
  162. package/dist/customize/triggers/schedule/when-app.js +21 -0
  163. package/dist/customize/triggers/schedule/when-change-request.d.ts +10 -0
  164. package/dist/customize/triggers/schedule/when-change-request.js +15 -0
  165. package/dist/customize/triggers/schedule-view.d.ts +17 -3
  166. package/dist/customize/triggers/schedule-view.js +46 -16
  167. package/dist/customize/triggers/triggers-page.d.ts +3 -3
  168. package/dist/customize/triggers/triggers-page.js +2 -2
  169. package/dist/customize/use-configure-thread.d.ts +8 -2
  170. package/dist/customize/use-configure-thread.js +30 -18
  171. package/dist/files/project/components/change-request-detail-dialog.js +2 -3
  172. package/dist/files/project/components/drive-explorer.js +6 -5
  173. package/dist/files/project/explorer-source.d.ts +3 -0
  174. package/dist/files/project/upload-batch.d.ts +3 -3
  175. package/dist/files/project/upload-batch.js +7 -6
  176. package/dist/files/runtime/sandbox-explorer-source.js +3 -3
  177. package/dist/i18n/en.d.ts +1330 -53
  178. package/dist/i18n/en.js +1422 -145
  179. package/dist/i18n/ui-catalog/remaining-ui-translation-keys.generated.js +16 -7
  180. package/dist/internal/auth/auth-primitives.d.ts +1 -3
  181. package/dist/internal/auth/auth-primitives.js +2 -2
  182. package/dist/internal/billing/session-cost-format.d.ts +1 -0
  183. package/dist/internal/billing/session-cost-format.js +11 -0
  184. package/dist/internal/components/agent-mark.d.ts +12 -1
  185. package/dist/internal/components/agent-mark.js +34 -11
  186. package/dist/internal/components/nav-rail.d.ts +22 -0
  187. package/dist/internal/components/nav-rail.js +16 -0
  188. package/dist/internal/components/repository-disconnected.js +2 -2
  189. package/dist/internal/goals/goal-meta.d.ts +15 -1
  190. package/dist/internal/goals/goal-meta.js +42 -2
  191. package/dist/internal/hooks/projects/use-agent-config.d.ts +11 -7
  192. package/dist/internal/hooks/projects/use-agent-config.js +28 -11
  193. package/dist/internal/hooks/use-slash-focus.d.ts +7 -1
  194. package/dist/internal/hooks/use-slash-focus.js +9 -3
  195. package/dist/internal/layout/project-capability-nav.d.ts +24 -0
  196. package/dist/internal/layout/project-capability-nav.js +86 -0
  197. package/dist/internal/layout/project-page-header.d.ts +1 -1
  198. package/dist/internal/layout/project-page-header.js +1 -1
  199. package/dist/internal/layout/project-settings-nav.d.ts +3 -2
  200. package/dist/internal/layout/project-settings-nav.js +5 -3
  201. package/dist/internal/lib/browser-noise/rules/old-browsers.js +1 -1
  202. package/dist/internal/lib/capability-tab-routes.d.ts +34 -3
  203. package/dist/internal/lib/capability-tab-routes.js +76 -8
  204. package/dist/internal/lib/manifest-health.d.ts +10 -0
  205. package/dist/internal/lib/manifest-health.js +20 -0
  206. package/dist/internal/lib/pixel-mark.d.ts +4 -1
  207. package/dist/internal/lib/pixel-mark.js +33 -6
  208. package/dist/internal/lib/project-actions.d.ts +1 -1
  209. package/dist/internal/lib/project-actions.js +0 -4
  210. package/dist/internal/lib/provider-branding.js +2 -0
  211. package/dist/internal/lib/space-scope.d.ts +29 -0
  212. package/dist/internal/lib/space-scope.js +28 -0
  213. package/dist/internal/lib/use-project-feature-flags.js +3 -3
  214. package/dist/internal/lib/web-notifications.d.ts +74 -8
  215. package/dist/internal/lib/web-notifications.js +243 -117
  216. package/dist/internal/notifications/notification-rows.d.ts +14 -7
  217. package/dist/internal/notifications/notification-rows.js +21 -14
  218. package/dist/internal/notifications/session-toast-cards.d.ts +2 -2
  219. package/dist/internal/reminders/reminder-format.d.ts +6 -2
  220. package/dist/internal/reminders/reminder-format.js +7 -1
  221. package/dist/internal/stores/active-space-store.d.ts +35 -0
  222. package/dist/internal/stores/active-space-store.js +29 -0
  223. package/dist/internal/stores/kortix-computer-store.js +13 -4
  224. package/dist/internal/stores/pinned-agents-store.d.ts +26 -0
  225. package/dist/internal/stores/pinned-agents-store.js +20 -0
  226. package/dist/internal/stores/session-panel-memory-store.js +3 -0
  227. package/dist/internal/stores/turn-attention-store.d.ts +5 -4
  228. package/dist/lib/routes.d.ts +16 -1
  229. package/dist/lib/routes.js +15 -3
  230. package/dist/markdown/doc-editor/doc-embed.d.ts +31 -0
  231. package/dist/markdown/doc-editor/doc-embed.js +168 -0
  232. package/dist/markdown/doc-editor/doc-extensions.d.ts +26 -0
  233. package/dist/markdown/doc-editor/doc-extensions.js +149 -0
  234. package/dist/markdown/doc-editor/doc-menus.d.ts +9 -0
  235. package/dist/markdown/doc-editor/doc-menus.js +56 -0
  236. package/dist/markdown/doc-editor/markdown.d.ts +12 -0
  237. package/dist/markdown/doc-editor/markdown.js +140 -0
  238. package/dist/markdown/genui/components/inline.d.ts +1 -1
  239. package/dist/primitives/badge.d.ts +1 -1
  240. package/dist/primitives/button.d.ts +1 -1
  241. package/dist/primitives/pixel-kortix-mark.d.ts +5 -4
  242. package/dist/primitives/pixel-kortix-mark.js +5 -15
  243. package/dist/primitives/tag.d.ts +1 -1
  244. package/dist/primitives/textarea.js +14 -0
  245. package/dist/primitives/toast.d.ts +2 -0
  246. package/dist/primitives/toast.js +4 -2
  247. package/dist/providers/ui-provider.d.ts +16 -0
  248. package/dist/session/actions/session-label.d.ts +1 -1
  249. package/dist/session/actions/session-label.js +3 -3
  250. package/dist/session/agents/agent-model.d.ts +92 -0
  251. package/dist/session/agents/agent-model.js +123 -0
  252. package/dist/session/agents/agent-state-mark.d.ts +10 -0
  253. package/dist/session/agents/agent-state-mark.js +22 -0
  254. package/dist/session/agents/agent-strip.d.ts +58 -0
  255. package/dist/session/agents/agent-strip.js +259 -0
  256. package/dist/session/agents/held-messages.d.ts +33 -0
  257. package/dist/session/agents/held-messages.js +50 -0
  258. package/dist/session/agents/legacy-subagent-chat.d.ts +11 -0
  259. package/dist/session/agents/legacy-subagent-chat.js +19 -0
  260. package/dist/session/agents/session-peek.d.ts +20 -0
  261. package/dist/session/agents/session-peek.js +67 -0
  262. package/dist/session/agents/session-prompt.d.ts +13 -0
  263. package/dist/session/agents/session-prompt.js +18 -0
  264. package/dist/session/agents/steer-session.d.ts +13 -0
  265. package/dist/session/agents/steer-session.js +29 -0
  266. package/dist/session/agents/subagent-dialog-content.d.ts +11 -0
  267. package/dist/session/agents/subagent-dialog-content.js +115 -0
  268. package/dist/session/agents/subagent-view.d.ts +8 -0
  269. package/dist/session/agents/subagent-view.js +18 -0
  270. package/dist/session/agents/tasks-panel.d.ts +13 -0
  271. package/dist/session/agents/tasks-panel.js +41 -0
  272. package/dist/session/agents/workflow-run-controls.d.ts +43 -0
  273. package/dist/session/agents/workflow-run-controls.js +141 -0
  274. package/dist/session/composer/ComposerCard.d.ts +1 -1
  275. package/dist/session/composer/ComposerCard.js +3 -3
  276. package/dist/session/composer/agent-selector.js +13 -19
  277. package/dist/session/composer/composer-underbar.d.ts +3 -1
  278. package/dist/session/composer/composer-underbar.js +2 -2
  279. package/dist/session/composer/composer.d.ts +5 -0
  280. package/dist/session/composer/composer.js +16 -10
  281. package/dist/session/composer/editor/composer-editor.js +19 -8
  282. package/dist/session/composer/editor/ultracode.d.ts +34 -0
  283. package/dist/session/composer/editor/ultracode.js +127 -0
  284. package/dist/session/composer/send-blockers.d.ts +1 -11
  285. package/dist/session/composer/send-blockers.js +1 -11
  286. package/dist/session/composer/session-composer-readiness.d.ts +1 -28
  287. package/dist/session/composer/session-composer-readiness.js +5 -16
  288. package/dist/session/header/reminder-parts.d.ts +1 -1
  289. package/dist/session/header/session-site-header.js +6 -3
  290. package/dist/session/header/subagent-hover-card.d.ts +0 -2
  291. package/dist/session/header/subagent-hover-card.js +1 -4
  292. package/dist/session/models/claude-connection-slot.d.ts +11 -0
  293. package/dist/session/models/claude-connection-slot.js +30 -0
  294. package/dist/session/models/model-availability.d.ts +3 -1
  295. package/dist/session/models/model-availability.js +3 -0
  296. package/dist/session/models/model-selector.js +11 -12
  297. package/dist/session/models/model-tags.d.ts +9 -0
  298. package/dist/session/models/model-tags.js +8 -0
  299. package/dist/session/panel/easy/easy-panel.js +1 -1
  300. package/dist/session/panel/easy/step-icon.js +5 -1
  301. package/dist/session/panel/mobile-tool-drawer.js +1 -1
  302. package/dist/session/panel/session-action-panel-column.js +1 -1
  303. package/dist/session/panel/session-desktop.js +41 -14
  304. package/dist/session/panel/session-panel-provider.d.ts +1 -1
  305. package/dist/session/panel/session-panel-provider.js +7 -11
  306. package/dist/session/prompts/question-prompt.js +3 -1
  307. package/dist/session/status/session-error-banner.js +4 -1
  308. package/dist/session/thread/session-chat.js +286 -296
  309. package/dist/session/thread/session-context-modal.js +29 -10
  310. package/dist/session/thread/session-context-spawned-sessions.d.ts +13 -0
  311. package/dist/session/thread/session-context-spawned-sessions.js +44 -0
  312. package/dist/session/thread/session-context-sub-sessions.js +3 -8
  313. package/dist/session/thread/transcript.d.ts +3 -0
  314. package/dist/session/thread/transcript.js +32 -8
  315. package/dist/session/tool/shared/full-view-action.d.ts +29 -0
  316. package/dist/session/tool/shared/full-view-action.js +43 -0
  317. package/dist/session/tool/tool-part-renderer.js +10 -2
  318. package/dist/session/tool/tools/agent-message-tool.js +2 -2
  319. package/dist/session/tool/tools/agent-spawn-tool.js +2 -2
  320. package/dist/session/tool/tools/agent-status-tool.js +2 -2
  321. package/dist/session/tool/tools/kortix-action-tool.d.ts +2 -0
  322. package/dist/session/tool/tools/kortix-action-tool.js +148 -0
  323. package/dist/session/tool/tools/kortix-tool-card.d.ts +83 -0
  324. package/dist/session/tool/tools/kortix-tool-card.js +186 -0
  325. package/dist/session/tool/tools/multi-agent-tool-bodies.d.ts +11 -0
  326. package/dist/session/tool/tools/multi-agent-tool-bodies.js +141 -0
  327. package/dist/session/tool/tools/multi-agent-tool-data.d.ts +63 -0
  328. package/dist/session/tool/tools/multi-agent-tool-data.js +118 -0
  329. package/dist/session/tool/tools/multi-agent-tools.d.ts +9 -0
  330. package/dist/session/tool/tools/multi-agent-tools.js +38 -0
  331. package/dist/session/tool/tools/question-tool.js +1 -1
  332. package/dist/session/tool/tools/register.d.ts +3 -0
  333. package/dist/session/tool/tools/register.js +3 -0
  334. package/dist/session/tool/tools/session-spawn-tool.js +2 -2
  335. package/dist/session/tool/tools/task-tool.d.ts +1 -1
  336. package/dist/session/tool/tools/task-tool.js +29 -41
  337. package/dist/session/tool/tools/workflow-progress.d.ts +34 -0
  338. package/dist/session/tool/tools/workflow-progress.js +186 -0
  339. package/dist/session/tool/tools/workflow-run.d.ts +128 -0
  340. package/dist/session/tool/tools/workflow-run.js +251 -0
  341. package/dist/session/tool/tools/workflow-tool.d.ts +9 -0
  342. package/dist/session/tool/tools/workflow-tool.js +253 -0
  343. package/dist/session/turn/notice-batch-group.d.ts +12 -0
  344. package/dist/session/turn/notice-batch-group.js +17 -0
  345. package/dist/session/turn/peer-message-row.d.ts +14 -0
  346. package/dist/session/turn/peer-message-row.js +57 -0
  347. package/dist/session/turn/subagent-notice-row.d.ts +4 -0
  348. package/dist/session/turn/subagent-notice-row.js +50 -0
  349. package/dist/session/turn/subagent-notice.d.ts +58 -0
  350. package/dist/session/turn/subagent-notice.js +111 -0
  351. package/dist/session/turn/user-message.js +24 -5
  352. package/dist/styles/tokens.css +2 -0
  353. package/dist/styles.css +1 -1
  354. package/dist/terminal/session-terminal-connect-bar.js +1 -1
  355. package/dist/workspace/admin/project-admin-dashboard.d.ts +53 -0
  356. package/dist/workspace/admin/project-admin-dashboard.js +252 -0
  357. package/dist/workspace/admin/project-admin-nav.d.ts +20 -0
  358. package/dist/workspace/admin/project-admin-nav.js +41 -0
  359. package/dist/workspace/home/welcome-body.js +25 -3
  360. package/dist/workspace/index.d.ts +6 -2
  361. package/dist/workspace/index.js +12 -2
  362. package/dist/workspace/layout/new-session-create.js +3 -3
  363. package/dist/workspace/layout/project-home.js +3 -3
  364. package/dist/workspace/memory/memory-labels.js +5 -0
  365. package/dist/workspace/repository-disconnected.js +2 -2
  366. package/dist/workspace/sessions/board-dispatch.d.ts +52 -0
  367. package/dist/workspace/sessions/board-dispatch.js +110 -0
  368. package/dist/workspace/sessions/board-grouping.d.ts +63 -0
  369. package/dist/workspace/sessions/board-grouping.js +141 -0
  370. package/dist/workspace/sessions/board-peek.d.ts +15 -0
  371. package/dist/workspace/sessions/board-peek.js +54 -0
  372. package/dist/workspace/sessions/board-state-mark.d.ts +15 -0
  373. package/dist/workspace/sessions/board-state-mark.js +24 -0
  374. package/dist/workspace/sessions/project-sessions-view.js +42 -31
  375. package/dist/workspace/sessions/sessions-board.d.ts +12 -0
  376. package/dist/workspace/sessions/sessions-board.js +214 -0
  377. package/dist/workspace/sidebar/footer/project-manifest-issues-alert.d.ts +46 -0
  378. package/dist/workspace/sidebar/footer/project-manifest-issues-alert.js +85 -0
  379. package/dist/workspace/sidebar/footer/sidebar-alert.js +1 -1
  380. package/dist/workspace/sidebar/new-agent-modal-provider.d.ts +12 -0
  381. package/dist/workspace/sidebar/new-agent-modal-provider.js +19 -0
  382. package/dist/workspace/sidebar/project-nav-items.d.ts +3 -1
  383. package/dist/workspace/sidebar/project-nav-items.js +53 -5
  384. package/dist/workspace/sidebar/project-session-list.js +35 -12
  385. package/dist/workspace/sidebar/project-sidebar.js +6 -3
  386. package/dist/workspace/sidebar/session-filter-menu.js +2 -1
  387. package/dist/workspace/space/space-blocks.d.ts +25 -0
  388. package/dist/workspace/space/space-blocks.js +124 -0
  389. package/dist/workspace/space/space-filter-tabs.d.ts +17 -0
  390. package/dist/workspace/space/space-filter-tabs.js +31 -0
  391. package/dist/workspace/space/space-page.d.ts +24 -0
  392. package/dist/workspace/space/space-page.js +157 -0
  393. package/dist/workspace/space/space-room.d.ts +20 -0
  394. package/dist/workspace/space/space-room.js +27 -0
  395. package/dist/workspace/space/space-tab-menu.d.ts +14 -0
  396. package/dist/workspace/space/space-tab-menu.js +127 -0
  397. package/dist/workspace/space/space-tab-panels.d.ts +9 -0
  398. package/dist/workspace/space/space-tab-panels.js +203 -0
  399. package/dist/workspace/space/space-templates.d.ts +34 -0
  400. package/dist/workspace/space/space-templates.js +71 -0
  401. package/dist/workspace/space/use-space-filter.d.ts +10 -0
  402. package/dist/workspace/space/use-space-filter.js +40 -0
  403. package/dist/workspace/space/use-space-manifest.d.ts +8 -0
  404. package/dist/workspace/space/use-space-manifest.js +27 -0
  405. package/dist/workspace/spaces/space-timeline.js +2 -2
  406. package/package.json +56 -53
  407. package/skills/kortix-ui/SKILL.md +1 -1
  408. package/skills/kortix-ui/references/custom-dashboard.md +10 -4
  409. package/skills/kortix-ui/references/modules.md +10 -8
  410. package/dist/_vendor/shared/meta-agent.d.ts +0 -7
  411. package/dist/customize/agents/agent-grant-pages.d.ts +0 -37
  412. package/dist/customize/agents/agent-grant-pages.js +0 -226
  413. package/dist/customize/agents/agent-triggers-section.d.ts +0 -17
  414. package/dist/customize/agents/agent-triggers-section.js +0 -109
  415. package/dist/customize/migrate-to-v2/index.d.ts +0 -5
  416. package/dist/customize/migrate-to-v2/index.js +0 -5
  417. package/dist/customize/migrate-to-v2/migration-prompt.d.ts +0 -16
  418. package/dist/customize/migrate-to-v2/migration-prompt.js +0 -59
  419. package/dist/customize/migrate-to-v2/upgrade-defs.d.ts +0 -30
  420. package/dist/customize/migrate-to-v2/upgrade-defs.js +0 -43
  421. package/dist/customize/migrate-to-v2/upgrade-view.d.ts +0 -32
  422. package/dist/customize/migrate-to-v2/upgrade-view.js +0 -152
  423. package/dist/customize/migrate-to-v2/use-run-upgrade.d.ts +0 -18
  424. package/dist/customize/migrate-to-v2/use-run-upgrade.js +0 -16
  425. package/dist/customize/shared/capability-tabs.d.ts +0 -3
  426. package/dist/customize/shared/capability-tabs.js +0 -137
  427. package/dist/customize/skills/skill-scope.d.ts +0 -14
  428. package/dist/customize/skills/skill-scope.js +0 -18
  429. package/dist/internal/lib/manifest-version.d.ts +0 -21
  430. package/dist/internal/lib/manifest-version.js +0 -55
  431. package/dist/session/composer/hooks/use-composer-escape-stop.d.ts +0 -6
  432. package/dist/session/composer/hooks/use-composer-escape-stop.js +0 -28
  433. package/dist/session/thread/sub-session-modal-content.d.ts +0 -8
  434. package/dist/session/thread/sub-session-modal-content.js +0 -23
  435. package/dist/session/thread/sub-session-modal.d.ts +0 -2
  436. package/dist/session/thread/sub-session-modal.js +0 -14
  437. package/dist/workspace/sidebar/footer/project-manifest-upgrade-alert.d.ts +0 -13
  438. package/dist/workspace/sidebar/footer/project-manifest-upgrade-alert.js +0 -48
@@ -59,6 +59,11 @@ export interface WebNotificationPayload {
59
59
  notificationCenter?: boolean;
60
60
  }
61
61
  export declare function setServerPushPreferences(kinds: NotificationPreferences['kinds'] | null | undefined): void;
62
+ /**
63
+ * The payload names a project and the URL shows a different one. Both sides
64
+ * must be known: on a page outside any project this answers false.
65
+ */
66
+ export declare function isOtherProject(projectId: string | null | undefined): boolean;
62
67
  /**
63
68
  * Check if the user is NOT actively looking at the app — either switched
64
69
  * to another Chrome tab (`document.hidden`) or switched to another app
@@ -79,6 +84,12 @@ export declare function spacesThreadHref(location: {
79
84
  sessionId: string;
80
85
  projectId: string | null | undefined;
81
86
  }, cache: QueryClient): string | null;
87
+ /**
88
+ * Report a session as on screen while a surface shows it outside its route,
89
+ * such as a side sheet. Takes the Kortix `session_id`. Returns the cleanup. A
90
+ * session registered twice stays on screen until both cleanups run.
91
+ */
92
+ export declare function markSessionOnScreen(sessionId: string): () => void;
82
93
  /**
83
94
  * Check if the user is currently viewing a specific session.
84
95
  * Checks the tab store (dashboard session tabs) and the current URL
@@ -92,6 +103,35 @@ export declare function isViewingSession(sessionId: string): boolean;
92
103
  * Check if the browser supports the Notification API.
93
104
  */
94
105
  export declare function isNotificationSupported(): boolean;
106
+ /**
107
+ * The one policy for the in-app channels and the OS gate. `os` answers only
108
+ * for what is on screen and which project it is: `sendWebNotification` keeps
109
+ * the permission, preference, kind and `onlyWhenHidden` gates.
110
+ *
111
+ * - Another project wins: no toast and no sound. A finished or failed turn
112
+ * marks the favicon dot. The OS fires only while the window is in the
113
+ * background.
114
+ * - The session on screen gets no toast of any kind, with or without focus:
115
+ * the chat shows it. A question or a permission keeps its sound: the agent
116
+ * is blocked.
117
+ * - The dot needs a session id, because the attention store is per session.
118
+ */
119
+ export declare function notificationChannels(input: {
120
+ type: WebNotificationType;
121
+ /** The payload names a session. */
122
+ hasSession: boolean;
123
+ /** That session is on screen, whichever window has focus. */
124
+ sessionOnScreen: boolean;
125
+ /** The window is visible and has focus (`!isTabHidden()`). */
126
+ windowFocused: boolean;
127
+ /** `isOtherProject(payload.projectId)`. */
128
+ otherProject: boolean;
129
+ }): {
130
+ toast: boolean;
131
+ sound: boolean;
132
+ attention: boolean;
133
+ os: boolean;
134
+ };
95
135
  /**
96
136
  * Send a browser web notification, respecting user preferences.
97
137
  *
@@ -100,6 +140,26 @@ export declare function isNotificationSupported(): boolean;
100
140
  export declare function sendWebNotification(payload: WebNotificationPayload,
101
141
  /** Skip all preference/permission gates (used for test notifications) */
102
142
  force?: boolean): Notification | null;
143
+ /** What a toast on screen is about. */
144
+ type ToastSubject = {
145
+ sessionId?: string | null;
146
+ projectId?: string | null;
147
+ };
148
+ /**
149
+ * Whether a toast on screen no longer qualifies: its session is on screen, or
150
+ * its project is another project. A toast about another session of the project
151
+ * on screen stays. Pure: `view` answers for the screen.
152
+ */
153
+ export declare function isStaleToast(toast: ToastSubject, view: {
154
+ onScreen: (sessionId: string) => boolean;
155
+ otherProject: (projectId: string | null | undefined) => boolean;
156
+ }): boolean;
157
+ /**
158
+ * Dismiss every notification toast that no longer qualifies, after the screen
159
+ * moved to another route or tab. A stack drops those items: the rest stay as a
160
+ * stack, or as a single toast when one remains.
161
+ */
162
+ export declare function dismissStaleNotificationToasts(): void;
103
163
  /** The in-app toast alone — no sound, badge or OS notification (an inbox arrival). */
104
164
  export declare const showInAppNotification: (payload: WebNotificationPayload) => void;
105
165
  /**
@@ -118,10 +178,11 @@ export declare function sessionDisplayName(sessionTitle: string | undefined, ses
118
178
  * never receives its own broadcast, so nothing doubles locally.
119
179
  */
120
180
  export declare function notifyTaskComplete(sessionId: string, sessionTitle: string | undefined, tI18nComplete: UiTranslator,
121
- /** Set when the SDK's turn-end watch found this end: the session page was
122
- * not mounted here. `watchedSinceMs` is when the watch started; `projectId`
123
- * is the session's project, which the page on screen may not be. */
124
- handoff?: {
181
+ /** `projectId`: the session's project, from the SDK's live stream or its
182
+ * turn-end watch. The page on screen may belong to another project.
183
+ * `watchedSinceMs`: set only when the watch found this end, because the
184
+ * session page was not mounted here: when the watch started. */
185
+ extra?: {
125
186
  watchedSinceMs?: number;
126
187
  projectId?: string;
127
188
  }): void;
@@ -135,13 +196,18 @@ export declare function notifyTaskCompleteFor({ sessionId, sessionTitle, project
135
196
  * Notify that a session encountered an error.
136
197
  */
137
198
  export declare function notifySessionError(sessionId: string, errorTitle: string, sessionTitle: string | undefined, tI18nComplete: UiTranslator,
138
- /** The session's project, from the SDK's turn-end watch (see `notifyTaskComplete`). */
139
- projectIdOverride?: string): void;
199
+ /** The session's project, from the SDK (see `notifyTaskComplete`). */
200
+ projectId?: string): void;
140
201
  /**
141
202
  * Notify that Kortix is asking the user a question.
142
203
  */
143
- export declare function notifyQuestion(sessionId: string, questionText: string, sessionTitle: string | undefined, tI18nComplete: UiTranslator): void;
204
+ export declare function notifyQuestion(sessionId: string, questionText: string, sessionTitle: string | undefined, tI18nComplete: UiTranslator,
205
+ /** The session's project, from the SDK (see `notifyTaskComplete`). */
206
+ projectId?: string): void;
144
207
  /**
145
208
  * Notify that Kortix needs a permission grant.
146
209
  */
147
- export declare function notifyPermissionRequest(sessionId: string, toolName: string, sessionTitle: string | undefined, tI18nComplete: UiTranslator): void;
210
+ export declare function notifyPermissionRequest(sessionId: string, toolName: string, sessionTitle: string | undefined, tI18nComplete: UiTranslator,
211
+ /** The session's project, from the SDK (see `notifyTaskComplete`). */
212
+ projectId?: string): void;
213
+ export {};
@@ -5,12 +5,12 @@ import { createElement } from 'react';
5
5
  * Provides a thin wrapper around the browser Notification API that respects
6
6
  * the user's preferences stored in the web-notification-store.
7
7
  *
8
- * All notification dispatching flows through `sendWebNotification()`. The
9
- * in-app channels — toast, sound, favicon badge — fire whenever the user is
10
- * NOT watching the session (another browser tab, another app, or another
11
- * in-app session), with no permission or preference prerequisite: a default
12
- * profile must still see that a turn finished. The native OS notification
13
- * additionally checks:
8
+ * All notification dispatching flows through `sendWebNotification()`.
9
+ * `notificationChannels()` decides the toast, the sound, the favicon dot and
10
+ * the OS gate from one table: a toast belongs to the project on screen, and
11
+ * never to the session on screen. The in-app channels have no permission or
12
+ * preference prerequisite: a default profile must still see that a turn
13
+ * finished. The native OS notification additionally checks:
14
14
  * 1. Browser support for the Notification API
15
15
  * 2. Permission is granted
16
16
  * 3. Master enable toggle is on
@@ -41,6 +41,7 @@ import { broadcastTurnComplete, noteTurnCompleteShown, turnCompleteShownSince, }
41
41
  import { getSharedQueryClient } from './query-client-singleton.js';
42
42
  import { playSound } from './sounds.js';
43
43
  import { openTabAndNavigate, useTabStore } from '../stores/tab-store.js';
44
+ import { projectIdFromPathname } from '../stores/project-switch-store.js';
44
45
  import { useTurnAttentionStore } from '../stores/turn-attention-store.js';
45
46
  import { useWebNotificationStore } from '../stores/web-notification-store.js';
46
47
  import { listChangeRequests, normalizeAppPathname, } from '@kortix/sdk';
@@ -113,19 +114,32 @@ const TYPE_TO_SOUND = {
113
114
  // Core
114
115
  // ============================================================================
115
116
  /**
116
- * The project a session belongs to, read from the URL at the moment the
117
- * notification is RAISED — not when it is clicked.
117
+ * The project in the URL. The fallback for an event that names no project: the
118
+ * SDK names the project whenever its stream knows it, because the URL may
119
+ * already show the next project when the event lands.
118
120
  *
119
- * The event that raises a notification comes off that session's own live
120
- * stream, so the user is on `/projects/<id>/sessions/<sid>` right then. By the
121
- * time they click, minutes later, they may be anywhere, so reading the path at
122
- * click time would resolve the wrong project or none at all.
121
+ * It is read when the notification is RAISED, not when it is clicked. By the
122
+ * time they click, minutes later, the person may be anywhere, so reading the
123
+ * path at click time would resolve the wrong project or none at all.
124
+ *
125
+ * An optional two-letter locale segment leads (`/de/projects/…`).
126
+ * `/projects/new` and `/projects/start` are pages, not projects.
123
127
  */
124
128
  function currentProjectId() {
125
129
  if (typeof window === 'undefined')
126
130
  return null;
127
- const match = window.location.pathname.match(/^\/projects\/([^/]+)/);
128
- return match ? match[1] : null;
131
+ const path = normalizeAppPathname(window.location.pathname);
132
+ return projectIdFromPathname(path.replace(/^\/[a-z]{2}(?=\/projects(?:\/|$))/, ''));
133
+ }
134
+ /**
135
+ * The payload names a project and the URL shows a different one. Both sides
136
+ * must be known: on a page outside any project this answers false.
137
+ */
138
+ export function isOtherProject(projectId) {
139
+ if (!projectId)
140
+ return false;
141
+ const current = currentProjectId();
142
+ return current !== null && current !== projectId;
129
143
  }
130
144
  /**
131
145
  * Navigate to a session by opening/activating its tab and navigating to it.
@@ -197,6 +211,24 @@ export function spacesThreadHref(location, session, cache) {
197
211
  params.set('thread', session.sessionId);
198
212
  return `${location.pathname}?${params.toString()}`;
199
213
  }
214
+ /** Mounts per session of a surface that shows it outside its route. */
215
+ const sessionsOnScreen = new Map();
216
+ /**
217
+ * Report a session as on screen while a surface shows it outside its route,
218
+ * such as a side sheet. Takes the Kortix `session_id`. Returns the cleanup. A
219
+ * session registered twice stays on screen until both cleanups run.
220
+ */
221
+ export function markSessionOnScreen(sessionId) {
222
+ sessionsOnScreen.set(sessionId, (sessionsOnScreen.get(sessionId) ?? 0) + 1);
223
+ dismissStaleNotificationToasts();
224
+ return () => {
225
+ const count = (sessionsOnScreen.get(sessionId) ?? 1) - 1;
226
+ if (count > 0)
227
+ sessionsOnScreen.set(sessionId, count);
228
+ else
229
+ sessionsOnScreen.delete(sessionId);
230
+ };
231
+ }
200
232
  /**
201
233
  * Check if the user is currently viewing a specific session.
202
234
  * Checks the tab store (dashboard session tabs) and the current URL
@@ -206,6 +238,9 @@ export function spacesThreadHref(location, session, cache) {
206
238
  * whatever session the user is looking at.
207
239
  */
208
240
  export function isViewingSession(sessionId) {
241
+ // A mounted surface whose session id is in neither the URL nor the tab store.
242
+ if (sessionsOnScreen.has(sessionId))
243
+ return true;
209
244
  // Dashboard: the active tab ID is the session ID for session tabs. The
210
245
  // persisted tab ID can outlive navigation (the store never clears it when
211
246
  // the user clicks elsewhere), so only trust it while the URL still agrees
@@ -241,6 +276,41 @@ export function isViewingSession(sessionId) {
241
276
  export function isNotificationSupported() {
242
277
  return typeof window !== 'undefined' && 'Notification' in window;
243
278
  }
279
+ /**
280
+ * The one policy for the in-app channels and the OS gate. `os` answers only
281
+ * for what is on screen and which project it is: `sendWebNotification` keeps
282
+ * the permission, preference, kind and `onlyWhenHidden` gates.
283
+ *
284
+ * - Another project wins: no toast and no sound. A finished or failed turn
285
+ * marks the favicon dot. The OS fires only while the window is in the
286
+ * background.
287
+ * - The session on screen gets no toast of any kind, with or without focus:
288
+ * the chat shows it. A question or a permission keeps its sound: the agent
289
+ * is blocked.
290
+ * - The dot needs a session id, because the attention store is per session.
291
+ */
292
+ export function notificationChannels(input) {
293
+ const { type, hasSession, windowFocused } = input;
294
+ const completion = type === 'completion';
295
+ const error = type === 'error';
296
+ if (input.otherProject) {
297
+ return {
298
+ toast: false,
299
+ sound: false,
300
+ attention: hasSession && (completion || error),
301
+ os: !windowFocused,
302
+ };
303
+ }
304
+ const blocking = type === 'question' || type === 'permission';
305
+ const onScreen = hasSession && input.sessionOnScreen;
306
+ const watching = windowFocused && (onScreen || !hasSession);
307
+ return {
308
+ toast: !onScreen && (error || !watching),
309
+ sound: blocking || !watching,
310
+ attention: hasSession && completion && !watching,
311
+ os: !(windowFocused && onScreen),
312
+ };
313
+ }
244
314
  /**
245
315
  * Send a browser web notification, respecting user preferences.
246
316
  *
@@ -251,34 +321,30 @@ export function sendWebNotification(payload,
251
321
  force = false) {
252
322
  const { preferences } = useWebNotificationStore.getState();
253
323
  const notificationCenter = fromNotificationCenter(payload);
254
- // The user watching the session live already sees the turn finish in the
255
- // chat — repeating it as a toast, a sound, a badge or an OS notification
256
- // would only be noise. `force` (the settings test button) never counts as
257
- // watching: it exists to prove the channels work.
258
- const watching = !force && !isTabHidden() && (!payload.sessionId || isViewingSession(payload.sessionId));
324
+ // `force` (the settings test button) never counts as on screen, focused or
325
+ // another project: it exists to prove the channels work.
326
+ const { sessionId } = payload;
327
+ const channels = notificationChannels({
328
+ type: payload.type,
329
+ hasSession: !!sessionId,
330
+ sessionOnScreen: !force && !!sessionId && isViewingSession(sessionId),
331
+ windowFocused: !force && !isTabHidden(),
332
+ otherProject: !force && isOtherProject(payload.projectId),
333
+ });
259
334
  // The in-app channels come first and answer to nobody: the toast is the one
260
335
  // channel that works on a default profile — permission untouched, browser
261
336
  // notifications disabled — which is exactly where the completion signal
262
337
  // used to die (the old permission gates returned before the toast ran).
263
- // A failed turn toasts even to the person watching it: the toast is how an
264
- // error is noticed. A finished turn does not: the chat already shows it.
265
- if (!watching || payload.type === 'error')
338
+ if (channels.toast)
266
339
  showInAppToast(payload);
267
- if (!watching) {
268
- if (payload.type === 'completion' && payload.sessionId) {
269
- // The toast fades after 8 s; the favicon badge is what a returning
270
- // user still sees, so a finished turn registers for it here.
271
- useTurnAttentionStore.getState().markTurnComplete(payload.sessionId);
272
- }
340
+ // The toast fades after 8 s; the favicon badge is what a returning user
341
+ // still sees.
342
+ if (channels.attention && sessionId) {
343
+ useTurnAttentionStore.getState().markTurnComplete(sessionId);
273
344
  }
274
345
  // Sound plays independently of browser notification preferences — the sound
275
- // store has its own pack/event/volume settings to control it. A question or
276
- // permission request is worth interrupting even while the user watches the
277
- // session (the agent is blocked); everything else only plays when the
278
- // session is not being watched.
279
- const isBlockingType = payload.type === 'question' || payload.type === 'permission';
280
- const skipSound = !isBlockingType && watching;
281
- if (!skipSound && (force || preferences.playSound !== false)) {
346
+ // store has its own pack/event/volume settings to control it.
347
+ if (channels.sound && (force || preferences.playSound !== false)) {
282
348
  playSound(TYPE_TO_SOUND[payload.type]);
283
349
  }
284
350
  // The OS notification keeps every gate it had: a browser without the
@@ -303,12 +369,9 @@ force = false) {
303
369
  if (prefKey && !preferences[prefKey])
304
370
  return null;
305
371
  }
306
- // Active session check — skip notifications for the session the user
307
- // is currently looking at (they can already see the question/permission
308
- // inline in the chat).
309
- if (payload.sessionId && !isTabHidden() && isViewingSession(payload.sessionId)) {
372
+ // On-screen and other-project check: see `notificationChannels`.
373
+ if (!channels.os)
310
374
  return null;
311
- }
312
375
  // Visibility check — questions and permissions always show since the
313
376
  // agent is blocked waiting for user input
314
377
  const isBlocking = payload.type === 'question' || payload.type === 'permission';
@@ -436,6 +499,48 @@ function openPayload(payload, forceNavigation) {
436
499
  softNavigate(payload.href);
437
500
  }
438
501
  }
502
+ /**
503
+ * Whether a toast on screen no longer qualifies: its session is on screen, or
504
+ * its project is another project. A toast about another session of the project
505
+ * on screen stays. Pure: `view` answers for the screen.
506
+ */
507
+ export function isStaleToast(toast, view) {
508
+ return (!!toast.sessionId && view.onScreen(toast.sessionId)) || view.otherProject(toast.projectId);
509
+ }
510
+ const toastView = () => ({ onScreen: isViewingSession, otherProject: isOtherProject });
511
+ /**
512
+ * The bound of `liveToasts`. A toast that never mounts never closes, so its
513
+ * entry would stay: the oldest entry leaves first.
514
+ */
515
+ const MAX_LIVE_TOASTS = 32;
516
+ /** The generic notification toasts on screen. Session cards: `shownSessionToasts`. */
517
+ const liveToasts = new Map();
518
+ function trackLiveToast(toast) {
519
+ // Nothing can make it stale.
520
+ if (!toast.sessionId && !toast.projectId)
521
+ return;
522
+ liveToasts.delete(toast.id);
523
+ liveToasts.set(toast.id, toast);
524
+ if (liveToasts.size > MAX_LIVE_TOASTS)
525
+ liveToasts.delete(liveToasts.keys().next().value);
526
+ }
527
+ /**
528
+ * Dismiss every notification toast that no longer qualifies, after the screen
529
+ * moved to another route or tab. A stack drops those items: the rest stay as a
530
+ * stack, or as a single toast when one remains.
531
+ */
532
+ export function dismissStaleNotificationToasts() {
533
+ const view = toastView();
534
+ for (const toast of [...liveToasts.values()]) {
535
+ if (!isStaleToast(toast, view))
536
+ continue;
537
+ liveToasts.delete(toast.id);
538
+ dismissToast(toast.id);
539
+ }
540
+ const kept = shownSessionToasts.filter((item) => !isStaleToast(item, view));
541
+ if (kept.length !== shownSessionToasts.length)
542
+ renderSessionToasts(kept);
543
+ }
439
544
  /**
440
545
  * Show an in-app toast notification.
441
546
  * This always works regardless of OS notification settings.
@@ -449,8 +554,10 @@ function showInAppToast(payload) {
449
554
  const id = `web-notification-${payload.tag ?? Date.now()}`;
450
555
  const { sessionId, href, actionLabel } = payload;
451
556
  const automation = payload.type === 'automation_failed' || payload.type === 'automation_recovered';
557
+ trackLiveToast({ id, sessionId, projectId: payload.projectId });
452
558
  TOAST_BY_VARIANT[NOTIFICATION_TOAST_VARIANT[payload.type]](payload.inAppTitle ?? payload.title, {
453
559
  id,
560
+ onClose: () => liveToasts.delete(id),
454
561
  // With an entity row the subject is named there; the body is only the detail.
455
562
  description: payload.entityLabel ? payload.preview : payload.body,
456
563
  entity: payload.entityLabel
@@ -487,50 +594,72 @@ const SESSION_STACK_ID = 'web-notification-session-stack';
487
594
  const singleSessionToastId = (sessionId) => `web-notification-session-${sessionId}`;
488
595
  /**
489
596
  * Show a finished (entity toast) or failed (agent card) turn. While one is on
490
- * screen, the next turn replaces it with a stack listing both.
597
+ * screen, the next turn replaces it with a stack listing both. An item that no
598
+ * longer qualifies stays out of the stack.
491
599
  */
492
600
  function showSessionToast(payload, sessionId) {
493
- const previous = shownSessionToasts.filter((item) => item.sessionId !== sessionId);
494
- const item = {
495
- sessionId,
496
- label: payload.entityLabel ?? payload.title,
497
- failed: payload.type === 'error',
498
- at: Date.now(),
499
- payload,
500
- };
601
+ const view = toastView();
602
+ const previous = shownSessionToasts.filter((item) => item.sessionId !== sessionId && !isStaleToast(item, view));
603
+ renderSessionToasts([
604
+ ...previous,
605
+ {
606
+ sessionId,
607
+ projectId: payload.projectId,
608
+ label: payload.entityLabel ?? payload.title,
609
+ failed: payload.type === 'error',
610
+ at: Date.now(),
611
+ payload,
612
+ },
613
+ ]);
614
+ }
615
+ function openSessionToast(target) {
616
+ openPayload(target.payload, false);
617
+ target.payload.onClick?.();
618
+ }
619
+ /**
620
+ * Put `items` on screen: nothing, one single toast, or one stack. Any other
621
+ * session toast on screen leaves.
622
+ */
623
+ function renderSessionToasts(items) {
624
+ const before = shownSessionToasts;
501
625
  const generation = ++sessionToastGeneration;
502
626
  const onClose = () => {
503
627
  if (generation === sessionToastGeneration)
504
628
  shownSessionToasts = [];
505
629
  };
506
- shownSessionToasts = [...previous, item];
507
- const open = (target) => {
508
- openPayload(target.payload, false);
509
- target.payload.onClick?.();
510
- };
511
- const openLabel = payload.actionLabel ?? 'Open';
512
- if (previous.length > 0) {
513
- for (const shown of previous)
630
+ shownSessionToasts = items;
631
+ const only = items.length === 1 ? items[0] : null;
632
+ // A dismissal lands a frame later, so the toast about to show is not dismissed.
633
+ for (const shown of before) {
634
+ if (shown.sessionId !== only?.sessionId)
514
635
  dismissToast(singleSessionToastId(shown.sessionId));
515
- const items = shownSessionToasts;
516
- customToast((dismiss) => (createElement(SessionStackCard, {
517
- title: payload.stackTitle?.(items.length) ?? `${items.length} tasks finished`,
518
- items,
519
- openLabel,
520
- onOpen: (id) => {
521
- const target = items.find((shown) => shown.sessionId === id);
522
- if (target)
523
- open(target);
524
- },
525
- onDismiss: dismiss,
526
- })), { id: SESSION_STACK_ID, duration: NOTIFICATION_VISIBLE_MS, onClose });
636
+ }
637
+ if (before.length > 1 && items.length < 2)
638
+ dismissToast(SESSION_STACK_ID);
639
+ if (only) {
640
+ showSingleSessionToast(only, onClose);
527
641
  return;
528
642
  }
643
+ if (items.length === 0)
644
+ return;
645
+ const { payload } = items[items.length - 1];
646
+ const openLabel = payload.actionLabel ?? 'Open';
647
+ customToast((dismiss) => (createElement(SessionStackCard, {
648
+ title: payload.stackTitle?.(items.length) ?? `${items.length} tasks finished`,
649
+ items,
650
+ openLabel,
651
+ onOpen: (id) => {
652
+ const target = items.find((shown) => shown.sessionId === id);
653
+ if (target)
654
+ openSessionToast(target);
655
+ },
656
+ onDismiss: dismiss,
657
+ })), { id: SESSION_STACK_ID, duration: NOTIFICATION_VISIBLE_MS, onClose });
658
+ }
659
+ function showSingleSessionToast(item, onClose) {
660
+ const { payload, sessionId } = item;
661
+ const openLabel = payload.actionLabel ?? 'Open';
529
662
  const id = singleSessionToastId(sessionId);
530
- // A toast for the session on screen (it fired while the window had no
531
- // focus) names nothing and opens nothing: the person is already there.
532
- // Read at render, so it follows where the person is now.
533
- const elsewhere = () => !isViewingSession(sessionId);
534
663
  const cr = payload.changeRequest;
535
664
  // `?cr=` opens the change request's review dialog on the session page.
536
665
  const review = () => {
@@ -543,10 +672,10 @@ function showSessionToast(payload, sessionId) {
543
672
  if (item.failed) {
544
673
  customToast((dismiss) => createElement(SessionFailedCard, {
545
674
  title: payload.inAppTitle ?? payload.title,
546
- label: elsewhere() ? item.label : undefined,
675
+ label: item.label,
547
676
  detail: payload.preview,
548
677
  openLabel,
549
- onOpen: elsewhere() ? () => open(item) : undefined,
678
+ onOpen: () => openSessionToast(item),
550
679
  onDismiss: dismiss,
551
680
  }), { id, duration: NOTIFICATION_VISIBLE_MS, onClose });
552
681
  return;
@@ -555,23 +684,17 @@ function showSessionToast(payload, sessionId) {
555
684
  message: payload.inAppTitle ?? payload.title,
556
685
  icon: toastIcon('success'),
557
686
  variant: 'success',
558
- // On the session itself the change request card is in the transcript,
559
- // so the toast only says it is there.
560
- options: elsewhere()
561
- ? {
562
- entity: {
563
- icon: cr ? createElement(GitPullRequestIcon, { className: 'size-3.5' }) : undefined,
564
- label: item.label,
565
- },
566
- description: cr?.detail,
567
- actions: [
568
- ...(cr ? [{ label: cr.reviewLabel, onClick: review }] : []),
569
- { label: payload.openSessionLabel ?? openLabel, onClick: () => open(item) },
570
- ],
571
- }
572
- : cr
573
- ? { description: cr.detail }
574
- : undefined,
687
+ options: {
688
+ entity: {
689
+ icon: cr ? createElement(GitPullRequestIcon, { className: 'size-3.5' }) : undefined,
690
+ label: item.label,
691
+ },
692
+ description: cr?.detail,
693
+ actions: [
694
+ ...(cr ? [{ label: cr.reviewLabel, onClick: review }] : []),
695
+ { label: payload.openSessionLabel ?? openLabel, onClick: () => openSessionToast(item) },
696
+ ],
697
+ },
575
698
  onDismiss: dismiss,
576
699
  }), { id, duration: NOTIFICATION_VISIBLE_MS, onClose });
577
700
  }
@@ -599,19 +722,20 @@ export function sessionDisplayName(sessionTitle, sessionId) {
599
722
  * never receives its own broadcast, so nothing doubles locally.
600
723
  */
601
724
  export function notifyTaskComplete(sessionId, sessionTitle, tI18nComplete,
602
- /** Set when the SDK's turn-end watch found this end: the session page was
603
- * not mounted here. `watchedSinceMs` is when the watch started; `projectId`
604
- * is the session's project, which the page on screen may not be. */
605
- handoff) {
725
+ /** `projectId`: the session's project, from the SDK's live stream or its
726
+ * turn-end watch. The page on screen may belong to another project.
727
+ * `watchedSinceMs`: set only when the watch found this end, because the
728
+ * session page was not mounted here: when the watch started. */
729
+ extra) {
606
730
  // Another tab's stream already announced this end and its relay reached
607
731
  // this tab: the watch is the second announcer of the same end.
608
- const since = handoff?.watchedSinceMs;
732
+ const since = extra?.watchedSinceMs;
609
733
  if (since !== undefined && turnCompleteShownSince(sessionId, since))
610
734
  return;
611
- // Captured now, while the raising event proves which project is open — a
612
- // receiving tab may be on a page whose path holds no project id, and may
613
- // never have loaded the project's `notification_center` flag.
614
- const projectId = handoff?.projectId ?? currentProjectId();
735
+ // Resolved here and relayed whole: a receiving tab may be on a page whose
736
+ // path holds no project id, and may never have loaded the project's
737
+ // `notification_center` flag.
738
+ const projectId = extra?.projectId ?? currentProjectId();
615
739
  const msg = {
616
740
  sessionId,
617
741
  sessionTitle,
@@ -691,8 +815,8 @@ function openChangeRequestOf(projectId, sessionId) {
691
815
  * Notify that a session encountered an error.
692
816
  */
693
817
  export function notifySessionError(sessionId, errorTitle, sessionTitle, tI18nComplete,
694
- /** The session's project, from the SDK's turn-end watch (see `notifyTaskComplete`). */
695
- projectIdOverride) {
818
+ /** The session's project, from the SDK (see `notifyTaskComplete`). */
819
+ projectId) {
696
820
  const name = sessionDisplayName(sessionTitle, sessionId);
697
821
  const label = sessionTitle ? `"${name.slice(0, 50)}"` : name;
698
822
  sendWebNotification({
@@ -705,17 +829,21 @@ projectIdOverride) {
705
829
  tag: `error:${sessionId}`,
706
830
  sessionId,
707
831
  actionLabel: tI18nComplete.raw('texted077f3d8125'),
708
- // Captured now, while the raising event proves which project is open.
709
- projectId: projectIdOverride ?? currentProjectId(),
832
+ projectId: projectId ?? currentProjectId(),
710
833
  });
711
834
  }
835
+ /** The session as a question or a permission names it in its body. */
836
+ function blockedSessionLabel(sessionTitle, sessionId) {
837
+ const name = sessionDisplayName(sessionTitle, sessionId);
838
+ return sessionTitle ? `"${name.slice(0, 40)}"` : name;
839
+ }
712
840
  /**
713
841
  * Notify that Kortix is asking the user a question.
714
842
  */
715
- export function notifyQuestion(sessionId, questionText, sessionTitle, tI18nComplete) {
716
- const label = sessionTitle
717
- ? `"${sessionTitle.slice(0, 40)}"`
718
- : `Session ${sessionId.slice(0, 8)}`;
843
+ export function notifyQuestion(sessionId, questionText, sessionTitle, tI18nComplete,
844
+ /** The session's project, from the SDK (see `notifyTaskComplete`). */
845
+ projectId) {
846
+ const label = blockedSessionLabel(sessionTitle, sessionId);
719
847
  sendWebNotification({
720
848
  type: 'question',
721
849
  title: tI18nComplete.raw('text6e8c98a8560e'),
@@ -723,17 +851,16 @@ export function notifyQuestion(sessionId, questionText, sessionTitle, tI18nCompl
723
851
  tag: `question:${sessionId}`,
724
852
  sessionId,
725
853
  actionLabel: tI18nComplete.raw('texted077f3d8125'),
726
- // Captured now, while the raising event proves which project is open.
727
- projectId: currentProjectId(),
854
+ projectId: projectId ?? currentProjectId(),
728
855
  });
729
856
  }
730
857
  /**
731
858
  * Notify that Kortix needs a permission grant.
732
859
  */
733
- export function notifyPermissionRequest(sessionId, toolName, sessionTitle, tI18nComplete) {
734
- const label = sessionTitle
735
- ? `"${sessionTitle.slice(0, 40)}"`
736
- : `Session ${sessionId.slice(0, 8)}`;
860
+ export function notifyPermissionRequest(sessionId, toolName, sessionTitle, tI18nComplete,
861
+ /** The session's project, from the SDK (see `notifyTaskComplete`). */
862
+ projectId) {
863
+ const label = blockedSessionLabel(sessionTitle, sessionId);
737
864
  sendWebNotification({
738
865
  type: 'permission',
739
866
  title: tI18nComplete.raw('text7e497182c7ed'),
@@ -741,7 +868,6 @@ export function notifyPermissionRequest(sessionId, toolName, sessionTitle, tI18n
741
868
  tag: `permission:${sessionId}`,
742
869
  sessionId,
743
870
  actionLabel: tI18nComplete.raw('texted077f3d8125'),
744
- // Captured now, while the raising event proves which project is open.
745
- projectId: currentProjectId(),
871
+ projectId: projectId ?? currentProjectId(),
746
872
  });
747
873
  }