@kortix/ui 0.14.11 → 0.14.13

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 (447) 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/browser/browser-panel.d.ts +2 -1
  18. package/dist/browser/browser-panel.js +12 -2
  19. package/dist/customize/agents/agent-access-text.d.ts +16 -0
  20. package/dist/customize/agents/agent-access-text.js +54 -0
  21. package/dist/customize/agents/agent-approval-rules.d.ts +8 -0
  22. package/dist/customize/agents/agent-approval-rules.js +26 -0
  23. package/dist/customize/agents/agent-authority-card.js +4 -1
  24. package/dist/customize/agents/agent-cover-card.d.ts +4 -1
  25. package/dist/customize/agents/agent-cover-card.js +2 -2
  26. package/dist/customize/agents/agent-detail-aside.d.ts +1 -2
  27. package/dist/customize/agents/agent-detail-aside.js +8 -11
  28. package/dist/customize/agents/agent-drives-section.d.ts +23 -7
  29. package/dist/customize/agents/agent-drives-section.js +107 -41
  30. package/dist/customize/agents/agent-instructions.d.ts +11 -0
  31. package/dist/customize/agents/agent-instructions.js +114 -0
  32. package/dist/customize/agents/agent-model-view.d.ts +17 -0
  33. package/dist/customize/agents/agent-model-view.js +67 -0
  34. package/dist/customize/agents/agent-overview.d.ts +53 -0
  35. package/dist/customize/agents/agent-overview.js +242 -0
  36. package/dist/customize/agents/agent-page-chrome.d.ts +128 -0
  37. package/dist/customize/agents/agent-page-chrome.js +145 -0
  38. package/dist/customize/agents/agent-page.d.ts +13 -1
  39. package/dist/customize/agents/agent-page.js +478 -367
  40. package/dist/customize/agents/agent-pin-button.d.ts +10 -0
  41. package/dist/customize/agents/agent-pin-button.js +20 -0
  42. package/dist/customize/agents/agent-policy-sets-section.d.ts +16 -0
  43. package/dist/customize/agents/agent-policy-sets-section.js +80 -0
  44. package/dist/customize/agents/agent-policy-sets.js +1 -1
  45. package/dist/customize/agents/agent-section-rail.d.ts +21 -0
  46. package/dist/customize/agents/agent-section-rail.js +23 -0
  47. package/dist/customize/agents/agent-share-control.d.ts +4 -3
  48. package/dist/customize/agents/agent-share-control.js +15 -16
  49. package/dist/customize/agents/{agent-people-section.d.ts → agent-share-dialog.d.ts} +3 -1
  50. package/dist/customize/agents/{agent-people-section.js → agent-share-dialog.js} +21 -21
  51. package/dist/customize/agents/agent-tabs.d.ts +95 -0
  52. package/dist/customize/agents/agent-tabs.js +199 -0
  53. package/dist/customize/agents/agents-page.d.ts +0 -21
  54. package/dist/customize/agents/agents-page.js +104 -118
  55. package/dist/customize/agents/connectors-required.d.ts +16 -0
  56. package/dist/customize/agents/connectors-required.js +19 -0
  57. package/dist/customize/agents/create-agent-modal.d.ts +19 -0
  58. package/dist/customize/agents/create-agent-modal.js +94 -0
  59. package/dist/customize/connectors/agent-access.d.ts +4 -4
  60. package/dist/customize/connectors/agent-access.js +7 -10
  61. package/dist/customize/connectors/connector-identity.d.ts +12 -0
  62. package/dist/customize/connectors/connector-identity.js +15 -2
  63. package/dist/customize/connectors/connector-routes.js +2 -2
  64. package/dist/customize/connectors/connectors-page.d.ts +14 -1
  65. package/dist/customize/connectors/connectors-page.js +101 -32
  66. package/dist/customize/connectors/detail/app-page.js +1 -1
  67. package/dist/customize/connectors/detail/connector-agents.js +0 -3
  68. package/dist/customize/connectors/detail/connector-page.d.ts +1 -1
  69. package/dist/customize/connectors/detail/connector-page.js +5 -2
  70. package/dist/customize/connectors/detail/connector-policy-sets.js +1 -1
  71. package/dist/customize/connectors/detail/connector-triggers.js +2 -2
  72. package/dist/customize/connectors/use-agent-access.d.ts +7 -5
  73. package/dist/customize/connectors/use-agent-access.js +2 -6
  74. package/dist/customize/models/models-page.d.ts +7 -1
  75. package/dist/customize/models/models-page.js +2 -2
  76. package/dist/customize/project-settings/project-admin-sections.d.ts +63 -0
  77. package/dist/customize/project-settings/project-admin-sections.js +80 -0
  78. package/dist/customize/project-settings/project-settings-page.d.ts +1 -1
  79. package/dist/customize/project-settings/project-settings-page.js +4 -13
  80. package/dist/customize/project-settings/project-settings-sections.d.ts +5 -5
  81. package/dist/customize/project-settings/project-settings-sections.js +6 -20
  82. package/dist/customize/providers/provider-accordion.js +29 -17
  83. package/dist/customize/providers/provider-connect.js +1 -1
  84. package/dist/customize/providers/provider-sheet.d.ts +4 -1
  85. package/dist/customize/providers/provider-sheet.js +15 -5
  86. package/dist/customize/secrets/secrets-page.d.ts +5 -1
  87. package/dist/customize/secrets/secrets-page.js +2 -2
  88. package/dist/customize/sections/gateway-view.d.ts +10 -2
  89. package/dist/customize/sections/gateway-view.js +16 -4
  90. package/dist/customize/sections/llm-provider/chatgpt-subscription-connect.d.ts +12 -5
  91. package/dist/customize/sections/llm-provider/chatgpt-subscription-connect.js +131 -8
  92. package/dist/customize/sections/llm-provider/claude-code-step.d.ts +10 -0
  93. package/dist/customize/sections/llm-provider/claude-code-step.js +32 -0
  94. package/dist/customize/sections/llm-provider/models-tab.js +5 -1
  95. package/dist/customize/sections/view/account-secret-resources-panel.d.ts +5 -3
  96. package/dist/customize/sections/view/account-secret-resources-panel.js +122 -19
  97. package/dist/customize/sections/view/agent-editor-access-fields.d.ts +1 -1
  98. package/dist/customize/sections/view/agent-editor-access-fields.js +8 -4
  99. package/dist/customize/sections/view/agent-editor-basics-fields.d.ts +4 -1
  100. package/dist/customize/sections/view/agent-editor-basics-fields.js +33 -4
  101. package/dist/customize/sections/view/agent-editor-catalog.d.ts +2 -2
  102. package/dist/customize/sections/view/agent-editor-catalog.js +10 -12
  103. package/dist/customize/sections/view/agent-editor-primitives.d.ts +1 -1
  104. package/dist/customize/sections/view/agent-editor-primitives.js +4 -4
  105. package/dist/customize/sections/view/agent-editor.d.ts +37 -26
  106. package/dist/customize/sections/view/agent-editor.js +19 -19
  107. package/dist/customize/sections/view/grant-mode-field.d.ts +8 -20
  108. package/dist/customize/sections/view/grant-mode-field.js +31 -57
  109. package/dist/customize/sections/view/permission-editor.d.ts +3 -1
  110. package/dist/customize/sections/view/permission-editor.js +19 -10
  111. package/dist/customize/sections/view/review-view.d.ts +8 -0
  112. package/dist/customize/sections/view/review-view.js +12 -1
  113. package/dist/customize/sections/view/secret-delivery.js +1 -4
  114. package/dist/customize/sections/view/secrets-view.d.ts +7 -1
  115. package/dist/customize/sections/view/secrets-view.js +42 -6
  116. package/dist/customize/settings/rail.js +2 -8
  117. package/dist/customize/settings/settings-tabs.d.ts +2 -2
  118. package/dist/customize/settings/settings-tabs.js +17 -19
  119. package/dist/customize/settings/tabs/experimental-tab.d.ts +3 -0
  120. package/dist/customize/settings/tabs/experimental-tab.js +6 -1
  121. package/dist/customize/settings/tabs/sandbox-tab.d.ts +4 -6
  122. package/dist/customize/settings/tabs/sandbox-tab.js +9 -9
  123. package/dist/customize/shared/access-list.d.ts +39 -0
  124. package/dist/customize/shared/access-list.js +62 -0
  125. package/dist/customize/shared/agent-resource-scope.d.ts +87 -0
  126. package/dist/customize/shared/agent-resource-scope.js +123 -0
  127. package/dist/customize/shared/capability-library.d.ts +41 -0
  128. package/dist/customize/shared/capability-library.js +271 -0
  129. package/dist/customize/shared/capability-page-shell.d.ts +18 -1
  130. package/dist/customize/shared/capability-page-shell.js +21 -1
  131. package/dist/customize/shared/catalog/catalog-space-tabs.d.ts +14 -0
  132. package/dist/customize/shared/catalog/catalog-space-tabs.js +20 -0
  133. package/dist/customize/shared/customize-tab-bar.d.ts +13 -0
  134. package/dist/customize/shared/customize-tab-bar.js +95 -0
  135. package/dist/customize/shared/entity/entity-modal.d.ts +17 -1
  136. package/dist/customize/shared/entity/entity-modal.js +10 -9
  137. package/dist/customize/shared/leave-guard.d.ts +23 -0
  138. package/dist/customize/shared/leave-guard.js +87 -0
  139. package/dist/customize/shared/standalone-settings-nav.js +4 -1
  140. package/dist/customize/shared/tool-rules.d.ts +11 -0
  141. package/dist/customize/shared/tool-rules.js +29 -0
  142. package/dist/customize/skills/builtin-skill-modal.d.ts +6 -0
  143. package/dist/customize/skills/builtin-skill-modal.js +41 -0
  144. package/dist/customize/skills/skills-page.d.ts +21 -18
  145. package/dist/customize/skills/skills-page.js +16 -110
  146. package/dist/customize/triggers/runtime-triggers.d.ts +17 -0
  147. package/dist/customize/triggers/runtime-triggers.js +57 -0
  148. package/dist/customize/triggers/schedule/event-trigger-copy.js +2 -2
  149. package/dist/customize/triggers/schedule/post-to-field.js +2 -2
  150. package/dist/customize/triggers/schedule/schedule-copy.d.ts +11 -6
  151. package/dist/customize/triggers/schedule/schedule-copy.js +29 -8
  152. package/dist/customize/triggers/schedule/schedule-detail-sheet.js +7 -3
  153. package/dist/customize/triggers/schedule/trigger-composer-logic.d.ts +26 -1
  154. package/dist/customize/triggers/schedule/trigger-composer-logic.js +64 -3
  155. package/dist/customize/triggers/schedule/trigger-composer.js +57 -20
  156. package/dist/customize/triggers/schedule/trigger-edit-logic.js +18 -5
  157. package/dist/customize/triggers/schedule/trigger-filter.d.ts +10 -0
  158. package/dist/customize/triggers/schedule/trigger-filter.js +16 -0
  159. package/dist/customize/triggers/schedule/trigger-runs-panel.js +1 -0
  160. package/dist/customize/triggers/schedule/trigger-toast.js +2 -1
  161. package/dist/customize/triggers/schedule/use-trigger-mutations.d.ts +1 -1
  162. package/dist/customize/triggers/schedule/use-trigger-mutations.js +1 -1
  163. package/dist/customize/triggers/schedule/when-app.d.ts +15 -0
  164. package/dist/customize/triggers/schedule/when-app.js +21 -0
  165. package/dist/customize/triggers/schedule/when-change-request.d.ts +10 -0
  166. package/dist/customize/triggers/schedule/when-change-request.js +15 -0
  167. package/dist/customize/triggers/schedule-view.d.ts +17 -3
  168. package/dist/customize/triggers/schedule-view.js +46 -16
  169. package/dist/customize/triggers/triggers-page.d.ts +3 -3
  170. package/dist/customize/triggers/triggers-page.js +2 -2
  171. package/dist/customize/use-configure-thread.d.ts +8 -2
  172. package/dist/customize/use-configure-thread.js +30 -18
  173. package/dist/files/project/components/change-request-detail-dialog.js +2 -3
  174. package/dist/files/project/components/drive-explorer.js +6 -5
  175. package/dist/files/project/explorer-source.d.ts +3 -0
  176. package/dist/files/project/upload-batch.d.ts +3 -3
  177. package/dist/files/project/upload-batch.js +7 -6
  178. package/dist/files/runtime/sandbox-explorer-source.js +3 -3
  179. package/dist/i18n/en.d.ts +1331 -53
  180. package/dist/i18n/en.js +1423 -145
  181. package/dist/i18n/ui-catalog/remaining-ui-translation-keys.generated.js +16 -7
  182. package/dist/internal/auth/auth-primitives.d.ts +1 -3
  183. package/dist/internal/auth/auth-primitives.js +2 -2
  184. package/dist/internal/billing/session-cost-format.d.ts +1 -0
  185. package/dist/internal/billing/session-cost-format.js +11 -0
  186. package/dist/internal/components/agent-mark.d.ts +12 -1
  187. package/dist/internal/components/agent-mark.js +34 -11
  188. package/dist/internal/components/nav-rail.d.ts +22 -0
  189. package/dist/internal/components/nav-rail.js +16 -0
  190. package/dist/internal/components/repository-disconnected.js +2 -2
  191. package/dist/internal/goals/goal-meta.d.ts +15 -1
  192. package/dist/internal/goals/goal-meta.js +42 -2
  193. package/dist/internal/hooks/projects/use-agent-config.d.ts +11 -7
  194. package/dist/internal/hooks/projects/use-agent-config.js +28 -11
  195. package/dist/internal/hooks/use-slash-focus.d.ts +7 -1
  196. package/dist/internal/hooks/use-slash-focus.js +9 -3
  197. package/dist/internal/layout/project-capability-nav.d.ts +24 -0
  198. package/dist/internal/layout/project-capability-nav.js +86 -0
  199. package/dist/internal/layout/project-page-header.d.ts +1 -1
  200. package/dist/internal/layout/project-page-header.js +1 -1
  201. package/dist/internal/layout/project-settings-nav.d.ts +3 -2
  202. package/dist/internal/layout/project-settings-nav.js +5 -3
  203. package/dist/internal/lib/browser-noise/rules/extensions.js +3 -0
  204. package/dist/internal/lib/browser-noise/rules/old-browsers.js +1 -1
  205. package/dist/internal/lib/capability-tab-routes.d.ts +34 -3
  206. package/dist/internal/lib/capability-tab-routes.js +76 -8
  207. package/dist/internal/lib/manifest-health.d.ts +10 -0
  208. package/dist/internal/lib/manifest-health.js +20 -0
  209. package/dist/internal/lib/pixel-mark.d.ts +4 -1
  210. package/dist/internal/lib/pixel-mark.js +33 -6
  211. package/dist/internal/lib/project-actions.d.ts +1 -1
  212. package/dist/internal/lib/project-actions.js +0 -4
  213. package/dist/internal/lib/provider-branding.js +2 -0
  214. package/dist/internal/lib/space-scope.d.ts +29 -0
  215. package/dist/internal/lib/space-scope.js +28 -0
  216. package/dist/internal/lib/use-project-feature-flags.js +3 -3
  217. package/dist/internal/lib/web-notifications.d.ts +74 -8
  218. package/dist/internal/lib/web-notifications.js +243 -117
  219. package/dist/internal/notifications/notification-rows.d.ts +14 -7
  220. package/dist/internal/notifications/notification-rows.js +21 -14
  221. package/dist/internal/notifications/session-toast-cards.d.ts +2 -2
  222. package/dist/internal/reminders/reminder-format.d.ts +6 -2
  223. package/dist/internal/reminders/reminder-format.js +7 -1
  224. package/dist/internal/stores/active-space-store.d.ts +35 -0
  225. package/dist/internal/stores/active-space-store.js +29 -0
  226. package/dist/internal/stores/kortix-computer-store.d.ts +2 -1
  227. package/dist/internal/stores/kortix-computer-store.js +18 -7
  228. package/dist/internal/stores/pinned-agents-store.d.ts +26 -0
  229. package/dist/internal/stores/pinned-agents-store.js +20 -0
  230. package/dist/internal/stores/session-panel-memory-store.js +3 -0
  231. package/dist/internal/stores/turn-attention-store.d.ts +5 -4
  232. package/dist/lib/routes.d.ts +16 -1
  233. package/dist/lib/routes.js +15 -3
  234. package/dist/markdown/doc-editor/doc-embed.d.ts +31 -0
  235. package/dist/markdown/doc-editor/doc-embed.js +168 -0
  236. package/dist/markdown/doc-editor/doc-extensions.d.ts +26 -0
  237. package/dist/markdown/doc-editor/doc-extensions.js +149 -0
  238. package/dist/markdown/doc-editor/doc-menus.d.ts +9 -0
  239. package/dist/markdown/doc-editor/doc-menus.js +56 -0
  240. package/dist/markdown/doc-editor/markdown.d.ts +12 -0
  241. package/dist/markdown/doc-editor/markdown.js +140 -0
  242. package/dist/markdown/genui/components/inline.d.ts +1 -1
  243. package/dist/primitives/badge.d.ts +1 -1
  244. package/dist/primitives/button.d.ts +1 -1
  245. package/dist/primitives/pixel-kortix-mark.d.ts +5 -4
  246. package/dist/primitives/pixel-kortix-mark.js +5 -15
  247. package/dist/primitives/tag.d.ts +1 -1
  248. package/dist/primitives/textarea.js +14 -0
  249. package/dist/primitives/toast.d.ts +2 -0
  250. package/dist/primitives/toast.js +4 -2
  251. package/dist/providers/ui-provider.d.ts +16 -0
  252. package/dist/session/actions/session-label.d.ts +1 -1
  253. package/dist/session/actions/session-label.js +3 -3
  254. package/dist/session/agents/agent-model.d.ts +92 -0
  255. package/dist/session/agents/agent-model.js +123 -0
  256. package/dist/session/agents/agent-state-mark.d.ts +10 -0
  257. package/dist/session/agents/agent-state-mark.js +22 -0
  258. package/dist/session/agents/agent-strip.d.ts +58 -0
  259. package/dist/session/agents/agent-strip.js +259 -0
  260. package/dist/session/agents/held-messages.d.ts +33 -0
  261. package/dist/session/agents/held-messages.js +50 -0
  262. package/dist/session/agents/legacy-subagent-chat.d.ts +11 -0
  263. package/dist/session/agents/legacy-subagent-chat.js +19 -0
  264. package/dist/session/agents/session-peek.d.ts +20 -0
  265. package/dist/session/agents/session-peek.js +67 -0
  266. package/dist/session/agents/session-prompt.d.ts +13 -0
  267. package/dist/session/agents/session-prompt.js +18 -0
  268. package/dist/session/agents/steer-session.d.ts +13 -0
  269. package/dist/session/agents/steer-session.js +29 -0
  270. package/dist/session/agents/subagent-dialog-content.d.ts +11 -0
  271. package/dist/session/agents/subagent-dialog-content.js +115 -0
  272. package/dist/session/agents/subagent-view.d.ts +8 -0
  273. package/dist/session/agents/subagent-view.js +18 -0
  274. package/dist/session/agents/tasks-panel.d.ts +13 -0
  275. package/dist/session/agents/tasks-panel.js +41 -0
  276. package/dist/session/agents/workflow-run-controls.d.ts +43 -0
  277. package/dist/session/agents/workflow-run-controls.js +141 -0
  278. package/dist/session/composer/ComposerCard.d.ts +1 -1
  279. package/dist/session/composer/ComposerCard.js +3 -3
  280. package/dist/session/composer/agent-selector.js +13 -19
  281. package/dist/session/composer/composer-underbar.d.ts +3 -1
  282. package/dist/session/composer/composer-underbar.js +2 -2
  283. package/dist/session/composer/composer.d.ts +5 -0
  284. package/dist/session/composer/composer.js +16 -10
  285. package/dist/session/composer/editor/composer-editor.js +19 -8
  286. package/dist/session/composer/editor/ultracode.d.ts +34 -0
  287. package/dist/session/composer/editor/ultracode.js +127 -0
  288. package/dist/session/composer/index.d.ts +1 -0
  289. package/dist/session/composer/index.js +1 -0
  290. package/dist/session/composer/send-blockers.d.ts +1 -11
  291. package/dist/session/composer/send-blockers.js +1 -11
  292. package/dist/session/composer/session-composer-readiness.d.ts +1 -28
  293. package/dist/session/composer/session-composer-readiness.js +5 -16
  294. package/dist/session/header/reminder-parts.d.ts +1 -1
  295. package/dist/session/header/session-site-header.js +6 -3
  296. package/dist/session/header/subagent-hover-card.d.ts +0 -2
  297. package/dist/session/header/subagent-hover-card.js +1 -4
  298. package/dist/session/models/claude-connection-slot.d.ts +11 -0
  299. package/dist/session/models/claude-connection-slot.js +30 -0
  300. package/dist/session/models/model-availability.d.ts +3 -1
  301. package/dist/session/models/model-availability.js +3 -0
  302. package/dist/session/models/model-selector.js +11 -12
  303. package/dist/session/models/model-tags.d.ts +9 -0
  304. package/dist/session/models/model-tags.js +8 -0
  305. package/dist/session/panel/easy/detail-view.js +7 -0
  306. package/dist/session/panel/easy/easy-panel.js +1 -1
  307. package/dist/session/panel/easy/step-icon.js +5 -1
  308. package/dist/session/panel/mobile-tool-drawer.js +7 -3
  309. package/dist/session/panel/open-session-quick-view.js +1 -1
  310. package/dist/session/panel/session-action-panel-column.js +1 -1
  311. package/dist/session/panel/session-desktop.js +41 -14
  312. package/dist/session/panel/session-panel-provider.d.ts +1 -1
  313. package/dist/session/panel/session-panel-provider.js +7 -11
  314. package/dist/session/prompts/question-prompt.js +3 -1
  315. package/dist/session/status/session-error-banner.js +4 -1
  316. package/dist/session/thread/session-chat.js +286 -296
  317. package/dist/session/thread/session-context-modal.js +29 -10
  318. package/dist/session/thread/session-context-spawned-sessions.d.ts +13 -0
  319. package/dist/session/thread/session-context-spawned-sessions.js +44 -0
  320. package/dist/session/thread/session-context-sub-sessions.js +3 -8
  321. package/dist/session/thread/transcript.d.ts +3 -0
  322. package/dist/session/thread/transcript.js +32 -8
  323. package/dist/session/tool/shared/full-view-action.d.ts +29 -0
  324. package/dist/session/tool/shared/full-view-action.js +43 -0
  325. package/dist/session/tool/tool-part-renderer.js +10 -2
  326. package/dist/session/tool/tools/agent-message-tool.js +2 -2
  327. package/dist/session/tool/tools/agent-spawn-tool.js +2 -2
  328. package/dist/session/tool/tools/agent-status-tool.js +2 -2
  329. package/dist/session/tool/tools/kortix-action-tool.d.ts +2 -0
  330. package/dist/session/tool/tools/kortix-action-tool.js +148 -0
  331. package/dist/session/tool/tools/kortix-tool-card.d.ts +83 -0
  332. package/dist/session/tool/tools/kortix-tool-card.js +186 -0
  333. package/dist/session/tool/tools/multi-agent-tool-bodies.d.ts +11 -0
  334. package/dist/session/tool/tools/multi-agent-tool-bodies.js +141 -0
  335. package/dist/session/tool/tools/multi-agent-tool-data.d.ts +63 -0
  336. package/dist/session/tool/tools/multi-agent-tool-data.js +118 -0
  337. package/dist/session/tool/tools/multi-agent-tools.d.ts +9 -0
  338. package/dist/session/tool/tools/multi-agent-tools.js +38 -0
  339. package/dist/session/tool/tools/question-tool.js +1 -1
  340. package/dist/session/tool/tools/register.d.ts +3 -0
  341. package/dist/session/tool/tools/register.js +3 -0
  342. package/dist/session/tool/tools/session-spawn-tool.js +2 -2
  343. package/dist/session/tool/tools/task-tool.d.ts +1 -1
  344. package/dist/session/tool/tools/task-tool.js +29 -41
  345. package/dist/session/tool/tools/workflow-progress.d.ts +34 -0
  346. package/dist/session/tool/tools/workflow-progress.js +186 -0
  347. package/dist/session/tool/tools/workflow-run.d.ts +128 -0
  348. package/dist/session/tool/tools/workflow-run.js +251 -0
  349. package/dist/session/tool/tools/workflow-tool.d.ts +9 -0
  350. package/dist/session/tool/tools/workflow-tool.js +253 -0
  351. package/dist/session/turn/notice-batch-group.d.ts +12 -0
  352. package/dist/session/turn/notice-batch-group.js +17 -0
  353. package/dist/session/turn/peer-message-row.d.ts +14 -0
  354. package/dist/session/turn/peer-message-row.js +57 -0
  355. package/dist/session/turn/subagent-notice-row.d.ts +4 -0
  356. package/dist/session/turn/subagent-notice-row.js +50 -0
  357. package/dist/session/turn/subagent-notice.d.ts +58 -0
  358. package/dist/session/turn/subagent-notice.js +111 -0
  359. package/dist/session/turn/user-message.d.ts +5 -4
  360. package/dist/session/turn/user-message.js +31 -10
  361. package/dist/styles/tokens.css +2 -0
  362. package/dist/styles.css +1 -1
  363. package/dist/terminal/session-terminal-connect-bar.js +1 -1
  364. package/dist/workspace/admin/project-admin-dashboard.d.ts +53 -0
  365. package/dist/workspace/admin/project-admin-dashboard.js +252 -0
  366. package/dist/workspace/admin/project-admin-nav.d.ts +20 -0
  367. package/dist/workspace/admin/project-admin-nav.js +41 -0
  368. package/dist/workspace/home/welcome-body.js +25 -3
  369. package/dist/workspace/index.d.ts +6 -2
  370. package/dist/workspace/index.js +12 -2
  371. package/dist/workspace/layout/new-session-create.js +3 -3
  372. package/dist/workspace/layout/project-home.js +3 -3
  373. package/dist/workspace/memory/memory-labels.js +5 -0
  374. package/dist/workspace/repository-disconnected.js +2 -2
  375. package/dist/workspace/sessions/board-dispatch.d.ts +52 -0
  376. package/dist/workspace/sessions/board-dispatch.js +110 -0
  377. package/dist/workspace/sessions/board-grouping.d.ts +63 -0
  378. package/dist/workspace/sessions/board-grouping.js +141 -0
  379. package/dist/workspace/sessions/board-peek.d.ts +15 -0
  380. package/dist/workspace/sessions/board-peek.js +54 -0
  381. package/dist/workspace/sessions/board-state-mark.d.ts +15 -0
  382. package/dist/workspace/sessions/board-state-mark.js +24 -0
  383. package/dist/workspace/sessions/project-sessions-view.js +42 -31
  384. package/dist/workspace/sessions/sessions-board.d.ts +12 -0
  385. package/dist/workspace/sessions/sessions-board.js +214 -0
  386. package/dist/workspace/sidebar/footer/project-manifest-issues-alert.d.ts +46 -0
  387. package/dist/workspace/sidebar/footer/project-manifest-issues-alert.js +85 -0
  388. package/dist/workspace/sidebar/footer/sidebar-alert.js +1 -1
  389. package/dist/workspace/sidebar/new-agent-modal-provider.d.ts +12 -0
  390. package/dist/workspace/sidebar/new-agent-modal-provider.js +19 -0
  391. package/dist/workspace/sidebar/project-nav-items.d.ts +3 -1
  392. package/dist/workspace/sidebar/project-nav-items.js +53 -5
  393. package/dist/workspace/sidebar/project-session-list.js +35 -12
  394. package/dist/workspace/sidebar/project-sidebar.js +6 -3
  395. package/dist/workspace/sidebar/session-filter-menu.js +2 -1
  396. package/dist/workspace/space/space-blocks.d.ts +25 -0
  397. package/dist/workspace/space/space-blocks.js +124 -0
  398. package/dist/workspace/space/space-filter-tabs.d.ts +17 -0
  399. package/dist/workspace/space/space-filter-tabs.js +31 -0
  400. package/dist/workspace/space/space-page.d.ts +24 -0
  401. package/dist/workspace/space/space-page.js +157 -0
  402. package/dist/workspace/space/space-room.d.ts +20 -0
  403. package/dist/workspace/space/space-room.js +27 -0
  404. package/dist/workspace/space/space-tab-menu.d.ts +14 -0
  405. package/dist/workspace/space/space-tab-menu.js +127 -0
  406. package/dist/workspace/space/space-tab-panels.d.ts +9 -0
  407. package/dist/workspace/space/space-tab-panels.js +203 -0
  408. package/dist/workspace/space/space-templates.d.ts +34 -0
  409. package/dist/workspace/space/space-templates.js +71 -0
  410. package/dist/workspace/space/use-space-filter.d.ts +10 -0
  411. package/dist/workspace/space/use-space-filter.js +40 -0
  412. package/dist/workspace/space/use-space-manifest.d.ts +8 -0
  413. package/dist/workspace/space/use-space-manifest.js +27 -0
  414. package/dist/workspace/spaces/space-timeline.js +2 -2
  415. package/package.json +56 -53
  416. package/skills/kortix-ui/SKILL.md +1 -1
  417. package/skills/kortix-ui/references/custom-dashboard.md +10 -4
  418. package/skills/kortix-ui/references/modules.md +11 -9
  419. package/dist/_vendor/shared/meta-agent.d.ts +0 -7
  420. package/dist/customize/agents/agent-grant-pages.d.ts +0 -37
  421. package/dist/customize/agents/agent-grant-pages.js +0 -226
  422. package/dist/customize/agents/agent-triggers-section.d.ts +0 -17
  423. package/dist/customize/agents/agent-triggers-section.js +0 -109
  424. package/dist/customize/migrate-to-v2/index.d.ts +0 -5
  425. package/dist/customize/migrate-to-v2/index.js +0 -5
  426. package/dist/customize/migrate-to-v2/migration-prompt.d.ts +0 -16
  427. package/dist/customize/migrate-to-v2/migration-prompt.js +0 -59
  428. package/dist/customize/migrate-to-v2/upgrade-defs.d.ts +0 -30
  429. package/dist/customize/migrate-to-v2/upgrade-defs.js +0 -43
  430. package/dist/customize/migrate-to-v2/upgrade-view.d.ts +0 -32
  431. package/dist/customize/migrate-to-v2/upgrade-view.js +0 -152
  432. package/dist/customize/migrate-to-v2/use-run-upgrade.d.ts +0 -18
  433. package/dist/customize/migrate-to-v2/use-run-upgrade.js +0 -16
  434. package/dist/customize/shared/capability-tabs.d.ts +0 -3
  435. package/dist/customize/shared/capability-tabs.js +0 -137
  436. package/dist/customize/skills/skill-scope.d.ts +0 -14
  437. package/dist/customize/skills/skill-scope.js +0 -18
  438. package/dist/internal/lib/manifest-version.d.ts +0 -21
  439. package/dist/internal/lib/manifest-version.js +0 -55
  440. package/dist/session/composer/hooks/use-composer-escape-stop.d.ts +0 -6
  441. package/dist/session/composer/hooks/use-composer-escape-stop.js +0 -28
  442. package/dist/session/thread/sub-session-modal-content.d.ts +0 -8
  443. package/dist/session/thread/sub-session-modal-content.js +0 -23
  444. package/dist/session/thread/sub-session-modal.d.ts +0 -2
  445. package/dist/session/thread/sub-session-modal.js +0 -14
  446. package/dist/workspace/sidebar/footer/project-manifest-upgrade-alert.d.ts +0 -13
  447. package/dist/workspace/sidebar/footer/project-manifest-upgrade-alert.js +0 -48
@@ -1,126 +1,99 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { AgentMark } from '../../internal/components/agent-mark.js';
4
- import { EntityFilePane } from '../shared/entity/entity-modal.js';
5
- import { agentStatus } from './agent-status.js';
6
- import { useDefaultAgent } from './use-default-agent.js';
7
- import { useTranslations as useI18nTranslations } from '../../i18n/index.js';
3
+ import { MY_KORTIX_AGENT_NAME } from '../../_vendor/kortix-shared.js';
4
+ import { adminPath } from '../../lib/routes.js';
8
5
  /**
9
- * /projects/[id]/agent/[name] — one agent, as a full-page editor. The core
10
- * screen of Customize.
6
+ * /projects/[id]/customize/agents/[name]: one agent, as a page with a rail.
11
7
  *
12
- * Customize is agent-centric (Marko, 2026-09-01): the agent is the only
13
- * object a project manager grants a person or a group, so every other
14
- * decision — who may use it, which model, which skills, which connectors and
15
- * secrets, when it runs — hangs off it. This page is built like the Settings
16
- * page (Marko, 2026-09-03: "make it a regular full page Agent Editor"): a
17
- * header, a left rail of topics, and one full-width pane per topic.
8
+ * Customize is agent-first. My Kortix is the one agent a person talks to; every
9
+ * other agent is a work product with only the access its work needs. So the
10
+ * page is the agent: one identity header, one rail, and room to edit.
18
11
  *
19
- * ┌ Agents › name ● Name chips [Start session] [⋯] ┐
20
- * ├──────────────┬──────────────────────────────────────────────────────┤
21
- * │ Overview │ │
22
- * │ People │ the selected topic's pane, one column, │
23
- * │ Access │ `max-w-2xl` like the Settings page │
24
- * │ Triggers │ │
25
- * │ Model │ │
26
- * │ Workspace │ │
27
- * │ Tools │ │
28
- * │ Basics │ │
29
- * ├──────────────┴──────────────────────────────────────────────────────┤
30
- * │ Unsaved changes · commits to the repo [Discard] [Save ⌘S] │
31
- * └─────────────────────────────────────────────────────────────────────┘
12
+ * ┌ ▣ researcher [Share] [Start session] [⋯] ┐
13
+ * │ ● Enabled · Space: workspace · People start it │
14
+ * ├ Overview ┬ the row, full width ┤
15
+ * │ Triggers │ │
16
+ * │ Access: Connectors None · Secrets All · Skills All · Tools … │
17
+ * │ Runtime: Model · Workspace │
18
+ * └────────────────── [ ● Unsaved changes Skills Discard Save ⌘S ]
32
19
  *
33
- * The rail is the Settings rail's dialect (`project-settings-page.tsx`,
34
- * `settings-panel.tsx`): same width, same row classes, a real `<Link>` per
35
- * topic so a topic has a URL (`?section=<key>`) a person can be sent to. The
36
- * draft is shared across topics, so switching never drops an edit, and the
37
- * save bar spans the page foot. Below `lg` the rail is a horizontal row.
20
+ * The URL names the tab and its view (`agent-tabs.ts`); every old
21
+ * `?section=` link still resolves. The rail lists every view as its own row
22
+ * (2026-10-28: Access and Runtime are group labels, not a second level), each
23
+ * with its grant state. A view renders the full resource screen in agent
24
+ * scope (one component, two variants), embedded without its own title
25
+ * (`CapabilityShellEmbedProvider`).
38
26
  *
39
- * Two bodies behind one header:
27
+ * The draft is shared across rows, so a row switch never drops an edit; the
28
+ * row with an edit carries a dot, and the save bar names the changed rows. A
29
+ * link off the page while dirty asks Stay / Leave / Save and leave.
40
30
  *
41
- * - **Editable** — a v2 (kortix.yaml) project and a caller with
42
- * `project.agent.write`. Every field is live; Save round-trips the whole
43
- * `agents.<name>` block through the agent-config route, which commits it to
44
- * the project repo. State lives in `useAgentDraft`; the panes are
45
- * `AgentConfigSections` with the page's own Overview, People and Triggers
46
- * slotted in.
47
- * - **Read-only** — a v1 project, or a caller without write. Overview shows
48
- * the agent's source file; People, Access (the legacy scope mirror),
49
- * Triggers and Model (the gateway pin) remain. We degrade, never blank.
31
+ * Two bodies under one frame:
32
+ * - **Editable**: a v2+ project and a caller with `project.agent.write`.
33
+ * Save round-trips the whole `agents.<name>` block through the agent-config
34
+ * route, which commits it to the project repo.
35
+ * - **Read-only**: a v1 project or a caller without write. The same tabs and
36
+ * the same Overview, with values in place of controls and a lock banner.
37
+ * My Kortix (a platform agent) has no page here: the project home starts it,
38
+ * and its route reads as not found.
50
39
  *
51
40
  * The page is keyed on the agent name by its route, so switching agents
52
- * remounts every draft rather than carrying one agent's edits onto another.
41
+ * remounts every draft.
53
42
  */
54
- import { MarkdownWithFrontmatter } from '../../markdown/markdown-frontmatter.js';
55
- import { Badge } from '../../primitives/badge.js';
43
+ import { getProjectDetail, listProjectResourceGrants, readProjectFile, } from '@kortix/sdk';
44
+ import { contract, qk, useFeatureFlag, useProjectAccountId, useUnifiedTriggers } from '@kortix/sdk/react';
45
+ import { DotsThreeIcon, LockSimpleIcon, PlayIcon, StarIcon, TrashIcon } from '@phosphor-icons/react';
46
+ import { useQueries, useQuery } from '@tanstack/react-query';
47
+ import { useCallback, useEffect, useMemo, useState } from 'react';
48
+ import { useLocale, useTranslations as useI18nTranslations } from '../../i18n/index.js';
49
+ import { AgentMark, agentDisplayName } from '../../internal/components/agent-mark.js';
50
+ import { useAgentConfig, useUpdateAgentConfig, useProjectCapabilities, } from '../../internal/hooks/projects/use-agent-config.js';
51
+ import { useNewProjectSession } from '../../internal/hooks/projects/use-new-project-session.js';
52
+ import { agentHref, capabilityTabHref } from '../../internal/lib/capability-tab-routes.js';
53
+ import { PROJECT_ACTIONS } from '../../internal/lib/project-actions.js';
54
+ import { useProjectCan } from '../../internal/lib/use-project-can.js';
56
55
  import { Button } from '../../primitives/button.js';
57
56
  import { ConfirmDialog } from '../../primitives/confirm-dialog.js';
58
57
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '../../primitives/dropdown-menu.js';
58
+ import { EmptyState } from '../../primitives/empty-state.js';
59
+ import { ErrorState } from '../../primitives/error-state.js';
59
60
  import Hint from '../../primitives/hint.js';
60
61
  import { InfoBanner } from '../../primitives/info-banner.js';
61
- import { Kbd } from '../../primitives/kbd.js';
62
- import Loading from '../../primitives/loading.js';
63
- import { Skeleton } from '../../primitives/skeleton.js';
64
- import { Tabs, TabsList, TabsTrigger } from '../../primitives/tabs.js';
65
- import { Textarea } from '../../primitives/textarea.js';
66
62
  import { errorToast, successToast } from '../../primitives/toast.js';
67
- import { EmptyState } from '../../primitives/empty-state.js';
68
- import { ErrorState } from '../../primitives/error-state.js';
69
- import { configEntitySourcePath } from '../sections/component/config-entity-source-path.js';
70
- import { AgentConfigSections, agentConfigSectionKeys, agentEditorOptionQueries, DEFAULT_AGENT_CONFIG_SECTION, isAgentConfigSectionKey, registryGrantOptions, useAgentDraft, useAgentEditorOptions, useLocalizedAgentConfigCatalog, } from '../sections/view/agent-editor.js';
71
- import { formatMode, toArray } from '../shared/utils.js';
72
- import { editConfigPrompt, useConfigureThread, } from '../use-configure-thread.js';
73
- import { agentConfigNeedsUpgrade, useAgentConfig, useUpdateAgentConfig, } from '../../internal/hooks/projects/use-agent-config.js';
74
- import { useNewProjectSession } from '../../internal/hooks/projects/use-new-project-session.js';
75
- import { PROJECT_ACTIONS } from '../../internal/lib/project-actions.js';
76
- import { useProjectCan } from '../../internal/lib/use-project-can.js';
77
- import { cn } from '../../internal/lib/utils.js';
78
- import { getProjectDetail, readProjectFile, } from '@kortix/sdk';
79
- import { contract, qk, useFeatureFlag, useProjectAccountId } from '@kortix/sdk/react';
80
- import { capitalizeWords, isMetaAgentName, META_AGENT_DISPLAY_NAME } from '../../_vendor/kortix-shared.js';
81
- import { BookOpenTextIcon, CaretRightIcon, CommandIcon, CpuIcon, CubeIcon, DotsThreeIcon, FileTextIcon, GlobeIcon, HardDriveIcon, KeyIcon, PlayIcon, PlugsConnectedIcon, PuzzlePieceIcon, RobotIcon, SlidersHorizontalIcon, StarIcon, TerminalWindowIcon, TimerIcon, TreeStructureIcon, UsersIcon, WrenchIcon, } from '@phosphor-icons/react';
82
- import { useQueries, useQuery, useQueryClient } from '@tanstack/react-query';
83
- import { AnimatePresence, m } from 'motion/react';
84
63
  import { UiLink as Link, useUiPathname, useUiRouter, useUiSearchParams } from '../../providers/ui-provider.js';
85
- import { useCallback, useEffect, useMemo, useState } from 'react';
86
- import { FadedScrollArea } from '../../primitives/faded-scroll-area.js';
87
- import { SETTINGS_SIDEBAR_WIDTH_PX } from '../../internal/account-hub/account-settings-shell.js';
88
- import { capabilityTabHref } from '../../internal/lib/capability-tab-routes.js';
89
- import { useIsMobile } from '../../internal/hooks/index.js';
90
- import { EditorSection, EditorSectionStyleProvider } from '../sections/view/agent-editor-primitives.js';
91
- import { SettingsSectionHeader } from '../../primitives/settings-section-header.js';
92
- import { AgentDrivesSection } from './agent-drives-section.js';
64
+ import { configEntitySourcePath } from '../sections/component/config-entity-source-path.js';
65
+ import { AGENT_CONFIG_SECTIONS, AgentConfigSections, agentEditorOptionQueries, registryGrantOptions, useAgentDraft, useAgentEditorOptions, useLocalizedAgentConfigCatalog, } from '../sections/view/agent-editor.js';
66
+ import { EditorSectionStyleProvider } from '../sections/view/agent-editor-primitives.js';
67
+ import { ToolsSection } from '../sections/view/permission-editor.js';
68
+ import { useAgentGrant } from '../shared/agent-resource-scope.js';
69
+ import { CapabilityShellEmbedProvider } from '../shared/capability-page-shell.js';
70
+ import { LeaveGuardProvider, useUnsavedChangesGuard } from '../shared/leave-guard.js';
71
+ import { toArray } from '../shared/utils.js';
72
+ import { ExtensionsPage, SkillsPage, ToolsPage } from '../skills/skills-page.js';
73
+ import { ConnectorsPage } from '../connectors/connectors-page.js';
74
+ import { SecretsPage } from '../secrets/secrets-page.js';
75
+ import { TriggersPage } from '../triggers/triggers-page.js';
76
+ import { editConfigPrompt, useConfigureThread } from '../use-configure-thread.js';
93
77
  import { AgentAuthorityCard } from './agent-authority-card.js';
94
78
  import { AgentModel, AgentScope } from './agent-detail-aside.js';
95
- import { AppsGrantPage, ConnectorsGrantPage, SecretsGrantPage, SkillsGrantPage, } from './agent-grant-pages.js';
96
- import { connectorPolicySetOptions } from './agent-policy-sets.js';
97
- import { AgentPeopleSection } from './agent-people-section.js';
79
+ import { AgentDrivesSection } from './agent-drives-section.js';
80
+ import { pruneRequiredConnectors } from './connectors-required.js';
81
+ import { useGrantLabel } from './agent-access-text.js';
82
+ import { AgentOverview, grantExcluded, grantOf } from './agent-overview.js';
83
+ import { AgentIdentityHeader, AgentPageSkeleton, AgentStatePage, AgentTabBar, AgentTabBody, LeaveDialog, SaveBar, } from './agent-page-chrome.js';
84
+ import { AgentModelView } from './agent-model-view.js';
85
+ import { AgentApprovalRules } from './agent-approval-rules.js';
86
+ import { AgentSectionRail, railGroupOf } from './agent-section-rail.js';
87
+ import { AgentPinButton } from './agent-pin-button.js';
98
88
  import { AgentShareControl } from './agent-share-control.js';
99
- import { AgentTriggersSection } from './agent-triggers-section.js';
100
- /** The rail's icons — the one place that draws them (`AGENT_CONFIG_SECTIONS`
101
- * is pure data, imported by the editor module). */
102
- const SECTION_ICON = {
103
- overview: FileTextIcon,
104
- people: UsersIcon,
105
- basics: SlidersHorizontalIcon,
106
- triggers: TimerIcon,
107
- skills: BookOpenTextIcon,
108
- subagents: TreeStructureIcon,
109
- commands: CommandIcon,
110
- extensions: PuzzlePieceIcon,
111
- connectors: PlugsConnectedIcon,
112
- secrets: KeyIcon,
113
- apps: GlobeIcon,
114
- drives: HardDriveIcon,
115
- actions: TerminalWindowIcon,
116
- model: CpuIcon,
117
- tools: WrenchIcon,
118
- workspace: CubeIcon,
119
- };
120
- export function AgentPage({ projectId, agentName }) {
89
+ import { ACCESS_VIEWS, AGENT_TABS, agentLocationHref, changedItems, closestAgentName, resolveAgentLocation, railKeyOf, sectionLocationHref, } from './agent-tabs.js';
90
+ import { useSpacesEnabled } from '../../internal/lib/space-scope.js';
91
+ import { connectorPolicySetOptions } from './agent-policy-sets.js';
92
+ import { AgentPolicySetsPanel, useAgentPolicySetControls } from './agent-policy-sets-section.js';
93
+ import { useDefaultAgent } from './use-default-agent.js';
94
+ export function AgentPage({ projectId, agentName, renderApps, }) {
95
+ const t = useI18nTranslations('agentEditor.tabs');
121
96
  const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
122
- // `accountId` skips useProjectCan's own getProject and lets the IAM probe
123
- // run on the first render instead of waiting a round-trip for it.
124
97
  const accountId = useProjectAccountId(projectId);
125
98
  const canWrite = useProjectCan(projectId, PROJECT_ACTIONS.PROJECT_AGENT_WRITE, { accountId }).allowed === true;
126
99
  const detailQuery = useQuery({
@@ -129,336 +102,474 @@ export function AgentPage({ projectId, agentName }) {
129
102
  ...contract('config'),
130
103
  });
131
104
  const config = detailQuery.data?.config ?? null;
132
- const agent = toArray(config?.agents).find((a) => a.name === agentName) ?? null;
133
- // Meta (and any platform-owned agent) is injected by the API, not declared in
134
- // kortix.yaml. Its configuration is fixed: never editable, no agent-config read.
135
- const isPlatform = agent?.platform === true;
136
- // All reads start on the first render, in parallel. The agent-config read
137
- // used to wait for detail to list the agent, and the editor's option reads
138
- // for the editor to mount: three round trips in a row, each a Git read on
139
- // the API. Now the skeleton lasts as long as the slowest single read.
105
+ // A platform agent (My Kortix) has nothing to edit here: not found.
106
+ const agent = toArray(config?.agents).find((a) => a.name === agentName && !a.platform) ?? null;
107
+ // Every read starts on the first render, in parallel: the skeleton lasts as
108
+ // long as the slowest single read, not three in a row.
140
109
  const configQuery = useAgentConfig(projectId, agentName);
141
110
  const editorOptionQueries = agentEditorOptionQueries(projectId);
142
111
  useQueries({
143
- queries: [
144
- editorOptionQueries.secrets,
145
- editorOptionQueries.connectors,
146
- editorOptionQueries.sandboxes,
147
- ].map((query) => ({ ...query, enabled: canWrite })),
112
+ queries: [editorOptionQueries.secrets, editorOptionQueries.connectors, editorOptionQueries.sandboxes].map((query) => ({ ...query, enabled: canWrite })),
148
113
  });
149
- if (detailQuery.isLoading || (agent && !isPlatform && configQuery.isLoading)) {
114
+ if (detailQuery.isLoading || (agent && configQuery.isLoading)) {
150
115
  return _jsx(AgentPageSkeleton, {});
151
116
  }
117
+ const retry = (_jsx(Button, { variant: "outline", size: "sm", onClick: () => void detailQuery.refetch(), children: tI18nComplete.raw('text942087cc2d41') }));
152
118
  if (detailQuery.isError || !config) {
153
- return (_jsx(CenteredState, { children: _jsx(ErrorState, { size: "sm", title: tI18nComplete.raw('textd70d09b7d701'), action: _jsx(Button, { variant: "outline", size: "sm", onClick: () => detailQuery.refetch(), children: tI18nComplete.raw('text942087cc2d41') }) }) }));
119
+ return (_jsx(AgentStatePage, { title: agentDisplayName(agentName), children: _jsx(ErrorState, { size: "sm", title: t('loadError'), description: _jsx(ErrorDetails, { error: detailQuery.error }), action: retry }) }));
154
120
  }
155
121
  if (!agent) {
156
- return (_jsx(CenteredState, { children: _jsx(EmptyState, { icon: RobotIcon, size: "sm", title: tI18nComplete('text8136e0c475c7', { value0: agentName }), description: tI18nComplete.raw('text2ad30464c3ed'), action: _jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { href: capabilityTabHref(projectId, 'agent'), children: tI18nComplete.raw('text54c32d3e2cfa') }) }) }) }));
122
+ // Only agents this page opens: a platform agent (My Kortix) is not found here either.
123
+ const suggestion = closestAgentName(agentName, toArray(config.agents)
124
+ .filter((a) => !a.platform)
125
+ .map((a) => a.name));
126
+ return (_jsx(AgentStatePage, { title: t('notFoundHeader'), children: _jsx(EmptyState, { size: "sm", title: t('notFoundTitle'), description: _jsxs("span", { className: "space-y-1", children: [_jsx("span", { className: "block", children: t.rich('notFoundBody', { name: () => _jsx("span", { className: "font-mono", children: agentName }) }) }), suggestion ? (_jsx("span", { className: "block", children: t.rich('didYouMean', {
127
+ link: () => (_jsx(Link, { href: agentHref(projectId, suggestion), className: "text-foreground underline underline-offset-4", children: agentDisplayName(suggestion) })),
128
+ }) })) : null] }), action: _jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsx(Link, { href: capabilityTabHref(projectId, 'agent'), children: t('allAgents') }) }) }) }));
129
+ }
130
+ // A writer whose agent-config read failed would otherwise land on the
131
+ // read-only page with no word on why: say what failed, offer the retry.
132
+ if (canWrite && configQuery.isError) {
133
+ return (_jsx(AgentStatePage, { title: agentDisplayName(agent.name), children: _jsx(ErrorState, { size: "sm", title: t('configLoadError', { name: agentDisplayName(agent.name) }), description: _jsx(ErrorDetails, { error: configQuery.error }), action: _jsx(Button, { variant: "outline", size: "sm", onClick: () => void configQuery.refetch(), children: tI18nComplete.raw('text942087cc2d41') }) }) }));
157
134
  }
158
- const editable = !isPlatform && canWrite && configQuery.data?.editable === true;
159
- const isV1 = !isPlatform && agentConfigNeedsUpgrade(configQuery.data);
160
- return editable ? (_jsx(EditableAgentPage, { projectId: projectId, agent: agent, config: config, initial: configQuery.data?.block ?? {}, schemaVersion: configQuery.data?.schema_version ?? 2 })) : (_jsx(ReadOnlyAgentPage, { projectId: projectId, agent: agent, config: config, canWrite: canWrite, showUpgradeHint: isV1, isPlatform: isPlatform }));
135
+ const editable = canWrite && configQuery.data?.editable === true;
136
+ return editable ? (_jsx(EditableAgentPage, { projectId: projectId, agent: agent, config: config, initial: configQuery.data?.block ?? {}, renderApps: renderApps })) : (_jsx(ReadOnlyAgentPage, { projectId: projectId, agent: agent, config: config, block: configQuery.data?.block ?? blockFromScope(agent), canWrite: canWrite }));
137
+ }
138
+ /** The raw failure, folded, in mono. No fold when there is nothing to show. */
139
+ function ErrorDetails({ error }) {
140
+ const t = useI18nTranslations('agentEditor.tabs');
141
+ const message = error instanceof Error ? error.message : typeof error === 'string' ? error : '';
142
+ if (!message)
143
+ return null;
144
+ return (_jsxs("details", { className: "mt-2 text-left", children: [_jsx("summary", { className: "text-muted-foreground hover:text-foreground cursor-pointer text-xs transition-colors", children: t('details') }), _jsx("p", { className: "text-muted-foreground mt-2 font-mono text-xs break-words", children: message })] }));
145
+ }
146
+ /** The block a reader sees when the agent-config read returned nothing. */
147
+ function blockFromScope(agent) {
148
+ const scope = agent.scope;
149
+ return {
150
+ enabled: agent.enabled,
151
+ space: agent.space ?? undefined,
152
+ sandbox: agent.sandbox ?? undefined,
153
+ connectors: scope?.connectors,
154
+ secrets: scope?.env,
155
+ apps: scope?.apps,
156
+ kortix_permissions: scope?.kortix_permissions ?? scope?.kortix_cli,
157
+ behavior: {
158
+ description: agent.description ?? undefined,
159
+ model: agent.model ?? undefined,
160
+ mode: agent.mode ?? undefined,
161
+ hidden: agent.hidden,
162
+ },
163
+ };
164
+ }
165
+ // ─── Shared hooks ────────────────────────────────────────────────────────────
166
+ function useAgentLocation(tabs, views) {
167
+ const params = useUiSearchParams();
168
+ return resolveAgentLocation({ tab: params.get('tab'), view: params.get('view'), section: params.get('section') }, tabs, views);
169
+ }
170
+ /** How many triggers start this agent (kortix.yaml and runtime), and how
171
+ * many grants name it. `GET /triggers/all` leaves out what the caller may not read. */
172
+ function useAgentUseCounts(projectId, agentName, config) {
173
+ const accountId = useProjectAccountId(projectId);
174
+ const canManageMembers = useProjectCan(projectId, PROJECT_ACTIONS.PROJECT_MEMBERS_MANAGE, { accountId }).allowed === true;
175
+ const triggers = useUnifiedTriggers(projectId);
176
+ const grants = useQuery({
177
+ queryKey: qk.project.resourceGrants(projectId),
178
+ queryFn: () => listProjectResourceGrants(projectId),
179
+ enabled: canManageMembers,
180
+ retry: false,
181
+ ...contract('inventory'),
182
+ });
183
+ // A trigger stored as `default` starts the project's default agent.
184
+ const fallback = config.default_agent ?? config.open_code_default_agent ?? null;
185
+ return {
186
+ triggerCount: triggers.data
187
+ ? triggers.data.triggers.filter((a) => a.target.kind === 'agent' && (a.target.agent === 'default' && fallback ? fallback : a.target.agent) === agentName).length
188
+ : undefined,
189
+ peopleCount: grants.data
190
+ ? grants.data.grants.filter((g) => g.resource_type === 'agent' && g.resource_id === agentName).length
191
+ : undefined,
192
+ };
193
+ }
194
+ /** The Overview's catalog totals and the connectors that need a sign-in. */
195
+ function useOverviewFacts(projectId, agentName, config, block) {
196
+ const queries = agentEditorOptionQueries(projectId);
197
+ const connectors = useQuery(queries.connectors);
198
+ const secrets = useQuery(queries.secrets);
199
+ const counts = useAgentUseCounts(projectId, agentName, config);
200
+ const all = connectors.data?.connectors ?? [];
201
+ const granted = block.connectors;
202
+ const needsSignIn = all
203
+ .filter((c) => c.status === 'needs_auth' && (granted === 'all' || (Array.isArray(granted) && granted.includes(c.slug))))
204
+ .map((c) => c.slug);
205
+ const secretIds = new Set((secrets.data?.items ?? []).map((s) => s.identifier));
206
+ return {
207
+ totals: {
208
+ skills: toArray(config.skills).length,
209
+ ...(connectors.data ? { connectors: all.length } : {}),
210
+ ...(secrets.data ? { secrets: secretIds.size } : {}),
211
+ },
212
+ needsSignIn,
213
+ ...counts,
214
+ };
215
+ }
216
+ /** The header's meta line: status, space (only with spaces on), who starts it, default. */
217
+ function useHeaderFacts(projectId, agent, config, block, editable) {
218
+ const t = useI18nTranslations('agentEditor.tabs');
219
+ const tPage = useI18nTranslations('agentEditor.page');
220
+ const enabled = block.enabled !== false;
221
+ // Editable pages pass the draft: read it alone, so Project-wide reads as
222
+ // Project-wide before the save. A reader's block already carries the space.
223
+ const space = (editable ? block.space : (block.space ?? agent.space)) || null;
224
+ const mode = (block.behavior?.mode ?? agent.mode ?? 'primary').toLowerCase();
225
+ const isDefault = (config.default_agent ?? config.open_code_default_agent) === agent.name;
226
+ // Spaces are Work in progress (`project_channels`): off, no Space fact.
227
+ const spacesOn = useSpacesEnabled(projectId);
228
+ return [
229
+ { label: enabled ? t('enabled') : t('off'), tone: enabled ? 'success' : 'neutral' },
230
+ ...(spacesOn ? [{ label: space ? t('spaceValue', { space }) : t('projectWide') }] : []),
231
+ { label: mode === 'subagent' ? t('startSubagent') : mode === 'all' ? t('startBoth') : t('startPrimary') },
232
+ ...(isDefault ? [{ label: tPage('default') }] : []),
233
+ ];
161
234
  }
162
- // ─── Frame ─────────────────────────────────────────────────────────────────
163
235
  /**
164
- * Header on top, then the rail beside the pane, then the footer. The pane is
165
- * the one scroller; the rail is the Settings rail
166
- * (`project-settings-page.tsx`), row for row.
236
+ * The header's Share dialog. The old `?tab=people` link opens it; closing it
237
+ * drops that query, so a reload or a later tab click does not reopen it.
167
238
  */
168
- function AgentPageFrame({ header, section, sections, sectionHref, pane, footer, }) {
169
- const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
170
- const isMobile = useIsMobile();
171
- const localizedConfig = useLocalizedAgentConfigCatalog();
172
- const sectionKeys = new Set(sections);
173
- const items = localizedConfig.sections.filter((s) => sectionKeys.has(s.key));
174
- // The Preferences rail's rule: a heading over a lone group labels nothing.
175
- const groups = localizedConfig.groups
176
- .map((group) => ({
177
- group,
178
- items: items.filter((s) => s.group === group),
179
- }))
180
- .filter((g) => g.items.length > 0);
181
- const showGroupLabels = groups.length > 1;
182
- const trigger = (item, horizontal) => {
183
- const SectionIcon = SECTION_ICON[item.key];
184
- return (_jsx(TabsTrigger, { value: item.key, asChild: true, size: horizontal ? undefined : 'md', className: horizontal
185
- ? 'w-auto shrink-0 gap-2.5 px-3 py-0.75 whitespace-nowrap'
186
- : cn(
187
- // The Settings rail's row dialect (`project-settings-page.tsx`).
188
- 'gap-2 px-2.5 py-1 font-normal transition-none has-[>svg]:px-2.5', 'text-foreground data-[state=inactive]:text-foreground hover:bg-hover hover:text-foreground', 'data-[state=active]:bg-active data-[state=active]:font-medium', '[&_svg]:text-muted-foreground data-[state=active]:[&_svg]:text-foreground'), children: _jsxs(Link, { href: sectionHref(item.key), prefetch: true, children: [_jsx(SectionIcon, { className: "size-4 shrink-0" }), _jsx("span", { className: cn(!horizontal && 'truncate'), children: item.label })] }) }, item.key));
189
- };
190
- return (_jsxs("div", { className: "relative flex h-full min-h-0 flex-1 flex-col", children: [_jsx("div", { className: "border-border/60 shrink-0 border-b px-5 py-3", children: header }), _jsxs("div", { className: cn('min-h-0 flex-1', isMobile ? 'flex flex-col overflow-hidden' : 'grid overflow-hidden'), style: isMobile ? undefined : { gridTemplateColumns: `${SETTINGS_SIDEBAR_WIDTH_PX}px 1fr` }, children: [isMobile ? (_jsx("nav", { "aria-label": tI18nComplete.raw('text7be318295927'), className: "border-border/60 flex h-auto shrink-0 items-center border-b", children: _jsx(FadedScrollArea, { orientation: "horizontal", fadeColor: "from-background", className: "min-w-0 flex-1 py-2", children: _jsx(Tabs, { value: section, className: "w-fit", children: _jsx(TabsList, { orientation: "horizontal", className: "w-fit", children: items.map((item) => trigger(item, true)) }) }) }) })) : (_jsx("aside", { className: "flex min-h-0 flex-col border-r bg-inherit", children: _jsx("nav", { "aria-label": tI18nComplete.raw('text7be318295927'), className: "flex min-h-0 flex-1 [scrollbar-width:none] flex-col gap-4 overflow-y-auto px-2 pt-3 pb-2 [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden", children: _jsx(Tabs, { value: section, orientation: "vertical", className: "space-y-3", children: groups.map((g) => (_jsxs("div", { children: [showGroupLabels ? (_jsx("div", { className: "text-muted-foreground flex h-7 items-center px-2.5 text-xs font-medium", children: g.group })) : null, _jsx(TabsList, { orientation: "vertical", className: "w-full", children: g.items.map((item) => trigger(item, false)) })] }, g.group))) }) }) })), _jsx("main", { className: "flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto", children: _jsx("div", { className: "mx-auto w-full max-w-2xl px-4 py-10 pb-32 lg:py-14 lg:pb-32", children: pane }) })] }), footer] }));
239
+ function useShareDialog(openedByLink) {
240
+ const pathname = useUiPathname();
241
+ const router = useUiRouter();
242
+ const [open, setOpenState] = useState(openedByLink);
243
+ const setOpen = useCallback((next) => {
244
+ setOpenState(next);
245
+ if (!next && openedByLink)
246
+ router.replace(pathname);
247
+ }, [openedByLink, router, pathname]);
248
+ return { open, setOpen };
191
249
  }
192
- function CenteredState({ children }) {
193
- return (_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", children: _jsx("div", { className: "mx-auto w-full max-w-3xl px-6 py-16", children: children }) }));
250
+ /**
251
+ * Mod+S saves the agent, only while the draft holds an edit. With nothing to
252
+ * save the key keeps the browser's own behavior. A key pressed inside an open
253
+ * dialog (an embedded screen's own editor, the secret form) belongs to that
254
+ * dialog, never to the agent's draft.
255
+ */
256
+ function useSaveShortcut(dirty, onSave) {
257
+ useEffect(() => {
258
+ if (!dirty)
259
+ return;
260
+ const handler = (event) => {
261
+ if (!(event.metaKey || event.ctrlKey) || event.shiftKey || event.altKey || event.key !== 's')
262
+ return;
263
+ const target = event.target instanceof Element ? event.target : null;
264
+ if (target?.closest('[role="dialog"], [role="alertdialog"]'))
265
+ return;
266
+ event.preventDefault();
267
+ onSave();
268
+ };
269
+ window.addEventListener('keydown', handler);
270
+ return () => window.removeEventListener('keydown', handler);
271
+ }, [dirty, onSave]);
194
272
  }
195
- function AgentPageSkeleton() {
196
- return (_jsxs("div", { className: "flex h-full min-h-0 flex-1 flex-col", children: [_jsxs("div", { className: "border-border/60 shrink-0 space-y-4 border-b px-5 pt-5 pb-4", children: [_jsx(Skeleton, { className: "h-4 w-32 rounded-sm" }), _jsx(Skeleton, { className: "h-8 w-56 rounded-sm" })] }), _jsxs("div", { className: "grid min-h-0 flex-1", style: { gridTemplateColumns: `${SETTINGS_SIDEBAR_WIDTH_PX}px 1fr` }, children: [_jsx("div", { className: "space-y-1.5 border-r px-2 pt-3", children: Array.from({ length: 6 }).map((_, i) => (
197
- // biome-ignore lint/suspicious/noArrayIndexKey: fixed-length placeholder
198
- _jsx(Skeleton, { className: "h-8 w-full rounded-sm" }, i))) }), _jsxs("div", { className: "mx-auto w-full max-w-2xl space-y-6 px-6 py-8", children: [_jsx(Skeleton, { className: "h-11 w-full rounded-md" }), _jsx(Skeleton, { className: "h-72 w-full rounded-md" })] })] })] }));
273
+ /**
274
+ * The rail's rows, one flat list in the order of `agents.<name>` in
275
+ * kortix.yaml: Overview, Model, every grant (`countOf` gives its state),
276
+ * Workspace, then Triggers and Drives, which live outside the block. Model and
277
+ * Workspace show their value, or "Project default" when the block sets none.
278
+ */
279
+ function useRailItems(pathname, views, counts, countOf, values = {}) {
280
+ const t = useI18nTranslations('agentEditor.tabs');
281
+ const tPage = useI18nTranslations('agentEditor.page');
282
+ const catalog = useLocalizedAgentConfigCatalog();
283
+ const labelOf = (key) => catalog.sections.find((s) => s.key === key)?.label ?? key;
284
+ const viewRow = (view, count) => views.includes(view) ? [{ key: view, label: labelOf(view), href: sectionLocationHref(pathname, view), count }] : [];
285
+ const grants = ACCESS_VIEWS.filter((view) => view !== 'drives').flatMap((view) => viewRow(view, countOf?.(view) ?? undefined));
286
+ return [
287
+ { key: 'overview', label: t('tabOverview'), href: agentLocationHref(pathname, 'overview') },
288
+ ...viewRow('model', values.model ?? tPage('projectDefault')),
289
+ ...grants,
290
+ ...viewRow('workspace', values.workspace ?? tPage('projectDefault')),
291
+ {
292
+ key: 'triggers',
293
+ label: t('tabTriggers'),
294
+ href: agentLocationHref(pathname, 'triggers'),
295
+ count: counts.triggerCount || undefined,
296
+ },
297
+ ...viewRow('drives'),
298
+ ];
199
299
  }
200
- // ─── Section routing ───────────────────────────────────────────────────────
201
300
  /**
202
- * Which topic the page shows. Seeded from `?section=` so the Access hub and
203
- * the Triggers tab can deep-link one; the rail's links write the same param,
204
- * so the URL always names the topic on screen.
301
+ * The rail rows as the page's top tabs: Overview, Model, Capabilities (the
302
+ * grants of what the agent can do), Access (what it can reach), Workspace,
303
+ * Triggers. A tab with several views shows them as pills.
205
304
  */
206
- function useAgentSection(available) {
207
- const searchParams = useUiSearchParams();
208
- const fromUrl = searchParams.get('section');
209
- if (isAgentConfigSectionKey(fromUrl) && available.includes(fromUrl))
210
- return fromUrl;
211
- return available.find((k) => k === DEFAULT_AGENT_CONFIG_SECTION) ?? available[0];
305
+ function useTabGroups(items) {
306
+ const t = useI18nTranslations('agentEditor.tabs');
307
+ const pick = (keys) => keys.flatMap((key) => items.filter((item) => item.key === key));
308
+ const one = (key) => ({ key, label: items.find((item) => item.key === key)?.label ?? key, items: pick([key]) });
309
+ return [
310
+ one('overview'),
311
+ one('model'),
312
+ { key: 'capabilities', label: t('tabCapabilities'), items: pick(['tools', 'skills', 'subagents', 'commands', 'extensions']) },
313
+ { key: 'access', label: t('tabAccess'), items: pick(['connectors', 'secrets', 'apps', 'actions', 'drives']) },
314
+ one('workspace'),
315
+ one('triggers'),
316
+ ];
212
317
  }
213
- function sectionHrefFor(pathname) {
214
- return (key) => `${pathname}?section=${key}`;
318
+ /** The tabs over the one scroller. */
319
+ function TabLayout({ tabs, children }) {
320
+ return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col", children: [tabs, _jsx("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: children })] }));
215
321
  }
216
- // ─── Header ────────────────────────────────────────────────────────────────
322
+ // ─── Header actions ──────────────────────────────────────────────────────────
217
323
  /**
218
- * The two actions every agent has, shared by the full header and the
219
- * compact bar so the two cannot offer different things.
220
- *
221
- * "Start session" creates a session booted on THIS agent through the same
222
- * hook the sidebar's New session button uses — the same guard against double
223
- * clicks, the same billing gate, the same navigation. It is the page's
224
- * primary action because an agent exists to be run. A subagent is called by
225
- * other agents, never picked for a session — the composer hides it — so the
226
- * button is disabled with the reason, not offered.
324
+ * Share, Start session, and the ⋯ menu. Share renders for everyone and gates
325
+ * itself on `project.members.manage`. The ⋯ menu needs `project.agent.write`.
326
+ * Start session boots a session
327
+ * on THIS agent through the sidebar's New session hook. A subagent is called
328
+ * by other agents, never picked for a session, so its button is disabled with
329
+ * the reason. Below `sm` Start session is an icon button.
227
330
  */
228
- function AgentActions({ projectId, agent, config, canWrite, }) {
331
+ function AgentActions({ projectId, agent, config, canWrite, share, }) {
332
+ const t = useI18nTranslations('agentEditor.tabs');
229
333
  const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
230
- const queryClient = useQueryClient();
231
334
  const startSession = useNewProjectSession(projectId);
232
335
  const configure = useConfigureThread(projectId);
233
- const [confirmEditSource, setConfirmEditSource] = useState(false);
336
+ const makeDefault = useDefaultAgent(projectId);
337
+ const [confirm, setConfirm] = useState(null);
234
338
  const isDefault = (config.default_agent ?? config.open_code_default_agent) === agent.name;
235
339
  const mode = agent.mode?.toLowerCase();
236
340
  const startBlocked = agent.enabled === false
237
- ? 'This agent is disabled.'
341
+ ? t('startDisabled')
238
342
  : mode === 'subagent'
239
- ? 'Subagents are called by other agents, not started directly.'
343
+ ? t('startSubagentBlocked')
240
344
  : null;
241
- const makeDefault = useDefaultAgent(projectId);
242
- const pathname = useUiPathname();
243
- return (_jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [_jsx(AgentShareControl, { projectId: projectId, agentName: agent.name, peopleHref: `${pathname}?section=people` }), startBlocked ? (_jsx(Hint, { label: startBlocked, children: _jsx("span", { className: "inline-flex", children: _jsxs(Button, { size: "sm", disabled: true, children: [_jsx(PlayIcon, { weight: "fill", className: "size-3.5 shrink-0" }), tI18nComplete.raw('textb1c52ee3677d')] }) }) })) : (_jsxs(Button, { size: "sm", onClick: () => startSession({ create: { agent_name: agent.name } }), children: [_jsx(PlayIcon, { weight: "fill", className: "size-3.5 shrink-0" }), tI18nComplete.raw('textb1c52ee3677d')] })), canWrite ? (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "secondary", size: "icon-sm", "aria-label": tI18nComplete.raw('textf8d46c2570e7'), children: _jsx(DotsThreeIcon, { className: "size-4" }) }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsxs(DropdownMenuItem, { disabled: isDefault || agent.enabled === false || makeDefault.isPending, onSelect: () => makeDefault.mutate(agent.name), children: [_jsx(StarIcon, { className: "size-4" }), isDefault
244
- ? tI18nComplete.raw('texte8cb80e5c5cb')
245
- : tI18nComplete.raw('texte8f43f910a4e')] }), _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuItem, { disabled: configure.pending, onSelect: () => setConfirmEditSource(true), children: tI18nComplete.raw('text4930821dd07b') })] })] })) : null, _jsx(ConfirmDialog, { open: confirmEditSource, onOpenChange: setConfirmEditSource, title: tI18nComplete.raw('text24ba36a8ec86'), description: tI18nComplete('text13324ae62b23', { value0: agent.name }), confirmLabel: tI18nComplete.raw('text0bce1a892dec'), cancelLabel: tI18nComplete.raw('text19766ed6ccb2'), onConfirm: () => {
246
- setConfirmEditSource(false);
247
- configure.start(editConfigPrompt('agent', agent.name, agent.path));
345
+ const startLabel = tI18nComplete.raw('textb1c52ee3677d');
346
+ const start = (_jsxs(_Fragment, { children: [_jsxs(Button, { size: "sm", disabled: startBlocked !== null, onClick: () => startSession({ create: { agent_name: agent.name } }), className: "hidden sm:inline-flex", children: [_jsx(PlayIcon, { weight: "fill", className: "size-3.5 shrink-0" }), startLabel] }), _jsx(Button, { size: "icon-base", "aria-label": startLabel, disabled: startBlocked !== null, onClick: () => startSession({ create: { agent_name: agent.name } }), className: "sm:hidden", children: _jsx(PlayIcon, { weight: "fill", className: "size-3.5" }) })] }));
347
+ return (_jsxs(_Fragment, { children: [_jsx(AgentPinButton, { projectId: projectId, agent: agent.name }), _jsx(AgentShareControl, { projectId: projectId, agentName: agent.name, open: share.open, onOpenChange: share.setOpen }), startBlocked ? (_jsx(Hint, { label: startBlocked, children: _jsx("span", { className: "inline-flex", children: start }) })) : (start), canWrite ? (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-base", "aria-label": tI18nComplete.raw('textf8d46c2570e7'), children: _jsx(DotsThreeIcon, { className: "size-4" }) }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsxs(DropdownMenuItem, { disabled: isDefault || agent.enabled === false || makeDefault.isPending, onSelect: () => makeDefault.mutate(agent.name), children: [_jsx(StarIcon, { className: "size-4" }), isDefault ? tI18nComplete.raw('texte8cb80e5c5cb') : tI18nComplete.raw('texte8f43f910a4e')] }), _jsxs(DropdownMenuItem, { disabled: configure.pending, onSelect: () => setConfirm('edit'), children: [_jsx(AgentMark, { slug: MY_KORTIX_AGENT_NAME }), t('askToEditAgent')] }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { variant: "destructive", disabled: configure.pending, onSelect: () => setConfirm('delete'), children: [_jsx(TrashIcon, { className: "size-4" }), t('deleteAgent', { name: agentDisplayName(agent.name) })] })] })] })) : null, _jsx(ConfirmDialog, { open: confirm !== null, onOpenChange: (open) => (open ? undefined : setConfirm(null)), title: confirm === 'delete' ? t('deleteTitle', { name: agentDisplayName(agent.name) }) : tI18nComplete.raw('text24ba36a8ec86'), description: confirm === 'delete'
348
+ ? t('deleteBody')
349
+ : tI18nComplete('text13324ae62b23', { value0: agent.name }), confirmLabel: tI18nComplete.raw('text0bce1a892dec'), cancelLabel: tI18nComplete.raw('text19766ed6ccb2'), confirmVariant: confirm === 'delete' ? 'destructive' : 'default', onConfirm: () => {
350
+ const kind = confirm;
351
+ setConfirm(null);
352
+ configure.start(kind === 'delete'
353
+ ? t('deletePrompt', { name: agent.name, path: agent.path })
354
+ : editConfigPrompt('agent', agent.name, agent.path));
248
355
  } })] }));
249
356
  }
250
- /** The status chips beside the name. Only states worth flagging. */
251
- function AgentChips({ agent, config, size, }) {
252
- const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
253
- const { showMode, isDefault, disabled } = agentStatus(agent, config);
254
- return (_jsxs("span", { className: "flex items-center gap-1.5", children: [showMode ? (_jsx(Badge, { variant: "secondary", size: size, children: formatMode(agent.mode ?? '') })) : null, isDefault ? (_jsx(Badge, { variant: "secondary", size: size, children: tI18nComplete.raw('text21b111cbfe6e') })) : null, disabled ? (_jsx(Badge, { variant: "muted", size: size, children: tI18nComplete.raw('text75081b593d15') })) : null] }));
255
- }
256
357
  /**
257
- * Breadcrumb, avatar, name, chips, actions — the full header at the top of
258
- * the document. `children` is rendered under the title; the read-only body
259
- * passes the description there.
358
+ * The one column every Access and Runtime view sits in, embedded resource
359
+ * screen or settings form alike: the same width, the same top offset.
360
+ * Triggers is not a view: it keeps the full width its list
361
+ * and detail split needs.
260
362
  */
261
- function AgentHeader({ projectId, agent, config, canWrite, children, }) {
262
- const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
263
- return (_jsxs("header", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("nav", { "aria-label": tI18nComplete.raw('text2bd873d6c734'), className: "flex min-w-0 items-center gap-1.5 text-sm", children: [_jsx(Link, { href: capabilityTabHref(projectId, 'agent'), prefetch: true, className: "text-muted-foreground hover:text-foreground shrink-0 transition-colors", children: tI18nComplete.raw('text279b44d2ab4b') }), _jsx(CaretRightIcon, { "aria-hidden": true, className: "text-muted-foreground size-3.5 shrink-0" }), _jsx(AgentMark, { slug: agent.name }), _jsx("h1", { className: "text-foreground truncate text-sm font-semibold", children: isMetaAgentName(agent.name) ? META_AGENT_DISPLAY_NAME : capitalizeWords(agent.name) }), _jsx(AgentChips, { agent: agent, config: config, size: "xs" })] }), _jsx(AgentActions, { projectId: projectId, agent: agent, config: config, canWrite: canWrite })] }), children] }));
363
+ /** A view beside the Capabilities or Access rail, centered in the space right of the rail. */
364
+ const RAIL_COLUMN = 'mx-auto max-w-3xl';
365
+ /** A one-view tab (Workspace, Model): centered, the Overview's column. */
366
+ const CENTER_COLUMN = 'mx-auto max-w-3xl';
367
+ /** Frames a full resource screen in agent scope, with `lead` atop its scroller. */
368
+ function Embedded({ agentName, lead, column, children, }) {
369
+ return (
370
+ // A view in a column sits beside the rail, which names no view: the screen shows its own title.
371
+ _jsx(CapabilityShellEmbedProvider, { value: { agentName, lead, className: column, titled: column !== undefined }, children: _jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: children }) }));
264
372
  }
265
- // ─── Editable body ─────────────────────────────────────────────────────────
266
- /** What a v1 project, or a reader without write, can still see. */
267
- const READ_ONLY_SECTIONS = [
268
- 'overview',
269
- 'people',
270
- 'triggers',
271
- 'actions',
272
- 'model',
273
- ];
274
- /** A platform agent (Meta) shows one page: its identity and the platform notice.
275
- * Its people, triggers, model and permissions are platform-managed, not project
276
- * settings to inspect here. */
277
- const PLATFORM_SECTIONS = ['overview'];
278
- function EditableAgentPage({ projectId, agent, config, initial, schemaVersion, }) {
373
+ /** Runtime > Model's one way out to the project-wide model screens. */
374
+ function ProjectModelsLink({ projectId }) {
375
+ const t = useI18nTranslations('agentEditor.tabs');
376
+ return (_jsx("p", { className: "text-muted-foreground text-sm text-pretty", children: t.rich('projectModelsLink', {
377
+ link: (chunks) => (_jsx(Link, { href: adminPath(projectId, 'models'), className: "text-foreground decoration-border hover:decoration-foreground underline underline-offset-4 transition-colors", children: chunks })),
378
+ }) }));
379
+ }
380
+ // ─── Editable body ───────────────────────────────────────────────────────────
381
+ function EditableAgentPage({ projectId, agent, config, initial, renderApps, }) {
382
+ const tCapabilities = useI18nTranslations('agentCapabilities');
279
383
  const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
384
+ const locale = useLocale();
280
385
  const editor = useAgentDraft(initial);
281
386
  const options = useAgentEditorOptions(projectId);
387
+ const capabilities = useProjectCapabilities(projectId);
282
388
  const update = useUpdateAgentConfig(projectId, agent.name);
389
+ const configure = useConfigureThread(projectId);
283
390
  const [confirmDiscard, setConfirmDiscard] = useState(false);
391
+ const pathname = useUiPathname();
392
+ const drivesOn = useFeatureFlag(projectId, 'drives').enabled;
393
+ const views = useMemo(() => AGENT_CONFIG_SECTIONS.map((s) => s.key).filter((k) => drivesOn || k !== 'drives'), [drivesOn]);
394
+ const location = useAgentLocation(AGENT_TABS, views);
395
+ const share = useShareDialog(location.open === 'share');
396
+ const registryOptions = useMemo(() => registryGrantOptions({ manifest_raw: config.manifest_raw, agents: toArray(config.agents) }, agent.name), [config.manifest_raw, config.agents, agent.name]);
284
397
  const skillsOptions = toArray(config.skills).map((skill) => ({
285
398
  id: skill.name,
286
399
  label: skill.name,
287
400
  description: skill.description ?? undefined,
288
401
  }));
289
- const pathname = useUiPathname();
290
- // Drives exists only with Volumes on (the project's derived `drives` flag).
291
- const drivesOn = useFeatureFlag(projectId, 'drives').enabled;
292
- const sections = useMemo(() => agentConfigSectionKeys(schemaVersion).filter((k) => drivesOn || k !== 'drives'), [schemaVersion, drivesOn]);
293
- const registryOptions = useMemo(() => registryGrantOptions({ manifest_raw: config.manifest_raw, agents: toArray(config.agents) }, agent.name), [config.manifest_raw, config.agents, agent.name]);
294
402
  const policySetOptions = useMemo(() => connectorPolicySetOptions(config.manifest_raw), [config.manifest_raw]);
295
- const section = useAgentSection(sections);
403
+ const policySetControl = useAgentPolicySetControls(projectId, editor, policySetOptions);
404
+ const surface = { kind: 'agent', agentName: agent.name };
405
+ const defaultAgent = config.default_agent ?? config.open_code_default_agent;
406
+ const name = agentDisplayName(agent.name);
407
+ const sourcePath = editor.draft.file ?? configEntitySourcePath(agent.path);
408
+ const { draft, set } = editor;
409
+ const connectorsGrant = useAgentGrant(draft.connectors, (v) => {
410
+ set('connectors', v);
411
+ // `connectors_required` is no longer enforced: keep it inside the grant.
412
+ const kept = pruneRequiredConnectors(draft.connectors_required, v);
413
+ if (kept !== draft.connectors_required)
414
+ set('connectors_required', kept);
415
+ });
416
+ const secretsGrant = useAgentGrant(draft.secrets, (v) => set('secrets', v));
417
+ const appsGrant = useAgentGrant(draft.apps, (v) => set('apps', v));
418
+ const facts = useOverviewFacts(projectId, agent.name, config, draft);
419
+ const headerFacts = useHeaderFacts(projectId, agent, config, draft, true);
420
+ const spacesOn = useSpacesEnabled(projectId);
296
421
  const onSave = useCallback(async () => {
297
422
  if (!editor.isDirty || update.isPending)
298
- return;
423
+ return !editor.isDirty;
299
424
  try {
300
425
  const response = await update.mutateAsync(editor.draft);
301
426
  editor.commit(response.block ?? editor.draft);
302
- successToast(tI18nComplete('text6b0bbffdbd6e', { value0: capitalizeWords(agent.name) }));
427
+ successToast(tI18nComplete('text6b0bbffdbd6e', { value0: name }));
428
+ return true;
303
429
  }
304
430
  catch (e) {
305
431
  errorToast(e?.message ?? tI18nComplete.raw('text166442c65e8e'));
432
+ return false;
306
433
  }
307
- }, [editor, update, agent.name, tI18nComplete]);
308
- // Rail links change only `?section=` on this same path; the guard lets those
309
- // through so switching topics never asks about unsaved edits — the draft is
310
- // shared across topics and nothing is lost.
434
+ }, [editor, update, name, tI18nComplete]);
435
+ const saveNow = useCallback(() => void onSave(), [onSave]);
436
+ // Tab links change only the query on this path: the draft survives them, so
437
+ // the guard lets them through.
311
438
  const leaveGuard = useUnsavedChangesGuard(editor.isDirty, pathname);
312
- useSaveShortcut(onSave);
313
- return (_jsx(AgentPageFrame, { header: _jsx(AgentHeader, { projectId: projectId, agent: agent, config: config, canWrite: true }), section: section, sections: sections, sectionHref: sectionHrefFor(pathname), pane: _jsx(AgentConfigSections, { section: section, editor: editor, options: options, skillsOptions: skillsOptions, registryOptions: registryOptions, skills: _jsx(SkillsGrantPage, { projectId: projectId, config: config, editor: editor }), connectors: _jsx(ConnectorsGrantPage, { projectId: projectId, editor: editor, policySetOptions: policySetOptions }), secrets: _jsx(SecretsGrantPage, { projectId: projectId, editor: editor }), apps: _jsx(AppsGrantPage, { projectId: projectId, editor: editor }), drives: _jsx(AgentDrivesSection, { projectId: projectId, agentName: agent.name }), overview: _jsx(OverviewPane, { description: editor.oc.description ?? '', onDescriptionChange: (next) => editor.setOc('description', next), descriptionHelp: editor.oc.mode === 'subagent'
314
- ? 'Required. This is how other agents decide to call it.'
315
- : 'One line on what this agent is for. Other agents read it when picking a subagent.', prompt: editor.oc.prompt ?? '', onPromptChange: (next) => editor.setOc('prompt', next) }), triggers: _jsx(AgentTriggersSection, { projectId: projectId, agentName: agent.name, defaultAgent: config.default_agent ?? config.open_code_default_agent }), people: _jsx(AgentPeopleSection, { projectId: projectId, agentName: agent.name }), authority: _jsx(AgentAuthorityCard, { projectId: projectId, agentName: agent.name, grant: editor.draft.kortix_permissions ?? editor.draft.kortix_cli }) }), footer: _jsxs(_Fragment, { children: [_jsx(SaveBar, { dirty: editor.isDirty, pending: update.isPending, onDiscard: () => setConfirmDiscard(true), onSave: onSave }), _jsx(ConfirmDialog, { open: confirmDiscard, onOpenChange: setConfirmDiscard, title: tI18nComplete.raw('text3b13192b9d88'), description: tI18nComplete('text8a36cce34f68', { value0: capitalizeWords(agent.name) }), confirmLabel: tI18nComplete.raw('texteb1a70e39274'), cancelLabel: tI18nComplete.raw('texte76fd2add010'), confirmVariant: "destructive", onConfirm: () => {
439
+ useSaveShortcut(editor.isDirty, saveNow);
440
+ const grantLabel = useGrantLabel();
441
+ // A tool's aliases go off with it (`agent` takes `task`): the tab counts tools, not names.
442
+ const toolAliases = new Set((capabilities.data?.tools ?? []).flatMap((tool) => tool.aliases));
443
+ const countOf = (view) => {
444
+ if (view === 'drives')
445
+ return null;
446
+ const value = grantOf(draft, view);
447
+ const excluded = grantExcluded(value);
448
+ return view === 'tools' && excluded.length > 0
449
+ ? grantLabel({ exclude: excluded.filter((id) => !toolAliases.has(id)) })
450
+ : grantLabel(value);
451
+ };
452
+ const railItems = useRailItems(pathname, views, facts, countOf, {
453
+ model: modelName(editor.oc.model),
454
+ workspace: draft.sandbox,
455
+ });
456
+ const tabGroups = useTabGroups(railItems);
457
+ const dirtyItems = useMemo(() => changedItems(editor.draft, editor.baseline), [editor.draft, editor.baseline]);
458
+ const changed = new Intl.ListFormat(locale, { type: 'conjunction' }).format(dirtyItems.map((key) => railItems.find((item) => item.key === key)?.label ?? key));
459
+ const editorSection = (view) => (_jsx(AgentConfigSections, { section: view, editor: editor, options: options, skillsOptions: skillsOptions, registryOptions: registryOptions, apps: null, drives: _jsx(AgentDrivesSection, { projectId: projectId, agentName: agent.name, repositoryAccess: draft.repository_access !== false }), sectionStyle: "tab" }));
460
+ const body = (() => {
461
+ const view = location.view;
462
+ switch (location.tab) {
463
+ case 'overview':
464
+ return (_jsx(AgentTabBody, { children: _jsx(AgentOverview, { agent: agent, config: config, block: draft, prompt: editor.oc.prompt ?? '', editor: editor, sourcePath: sourcePath, showSpace: spacesOn, onAskMyKortix: () => configure.start(editConfigPrompt('agent', agent.name, agent.path)), focusInstructions: location.open === 'instructions' }) }));
465
+ case 'access':
466
+ case 'runtime': {
467
+ if (!view)
468
+ return null;
469
+ // Every Access view is the ONE component Project admin shows for the
470
+ // same library, in agent mode: the agent's grant row and a switch per
471
+ // item over the project's catalog (Marko, 2026-10-27).
472
+ if (view === 'skills' || view === 'tools' || view === 'extensions') {
473
+ const Page = view === 'skills' ? SkillsPage : view === 'tools' ? ToolsPage : ExtensionsPage;
474
+ return (_jsx(Embedded, { agentName: agent.name, column: RAIL_COLUMN, children: _jsx(Page, { projectId: projectId, agent: { name: agent.name, editor }, footer: view === 'tools' ? (_jsx(AgentApprovalRules, { projectId: projectId, editor: editor, rules: _jsx(EditorSectionStyleProvider, { value: "panel", children: _jsx(ToolsSection, { permission: editor.oc.permission, onChange: (next) => editor.setOc('permission', next), title: tCapabilities('rulesTitle') }) }) })) : undefined }) }));
475
+ }
476
+ if (view === 'connectors')
477
+ return (_jsx(Embedded, { agentName: agent.name, column: RAIL_COLUMN, children: _jsx(ConnectorsPage, { projectId: projectId, surface: surface, grant: connectorsGrant, rowControl: policySetControl, agentPanel: _jsxs(_Fragment, { children: [_jsx(AgentPolicySetsPanel, { projectId: projectId, editor: editor, catalog: policySetOptions, unlistedOnly: true }), _jsx(RequiredConnectorsNote, { names: draft.connectors_required })] }) }) }));
478
+ if (view === 'secrets')
479
+ return (_jsx(Embedded, { agentName: agent.name, column: RAIL_COLUMN, children: _jsx(SecretsPage, { projectId: projectId, surface: surface, grant: secretsGrant }) }));
480
+ if (view === 'apps' && renderApps)
481
+ return (_jsx(Embedded, { agentName: agent.name, column: RAIL_COLUMN, children: renderApps({ agentName: agent.name, grant: appsGrant, needsAppRead: !grantsAppRead(draft.kortix_permissions ?? draft.kortix_cli) }) }));
482
+ if (view === 'drives')
483
+ return (_jsx(Embedded, { agentName: agent.name, column: RAIL_COLUMN, children: _jsx(AgentDrivesSection, { projectId: projectId, agentName: agent.name, repositoryAccess: draft.repository_access !== false }) }));
484
+ // Model is this agent's model only. The project-wide gateway screens
485
+ // (providers, routing, costs, logs) live once, in Project admin.
486
+ if (view === 'model')
487
+ return (_jsx(AgentTabBody, { children: _jsx(AgentModelView, { oc: editor.oc, setOc: editor.setOc, footer: _jsx(ProjectModelsLink, { projectId: projectId }) }) }));
488
+ return (_jsx(AgentTabBody, { className: `${view === 'workspace' ? CENTER_COLUMN : RAIL_COLUMN} space-y-5`, children: _jsx(CapabilityShellEmbedProvider, { value: { agentName: agent.name }, children: editorSection(view) }) }));
489
+ }
490
+ case 'triggers':
491
+ return (_jsx(Embedded, { agentName: agent.name, children: _jsx(TriggersPage, { projectId: projectId, surface: surface, defaultAgent: defaultAgent }) }));
492
+ }
493
+ })();
494
+ return (
495
+ // Embedded screens leave through the router too (a configure session):
496
+ // they ask the same question as a link click.
497
+ _jsx(LeaveGuardProvider, { value: leaveGuard.confirmLeave, children: _jsxs("div", { className: "relative flex h-full min-h-0 flex-1 flex-col", children: [_jsx(AgentIdentityHeader, { slug: agent.name, title: name, facts: headerFacts, actions: _jsx(AgentActions, { projectId: projectId, agent: agent, config: config, canWrite: true, share: share }) }), _jsx(TabLayout, { tabs: _jsx(AgentTabBar, { groups: tabGroups, active: railKeyOf(location), dirty: dirtyItems }), children: _jsx(AgentSectionRail, { group: railGroupOf(tabGroups, railKeyOf(location)), active: railKeyOf(location), dirty: dirtyItems, children: body }) }), _jsx(SaveBar, { dirty: editor.isDirty, pending: update.isPending, changed: changed, onDiscard: () => setConfirmDiscard(true), onSave: saveNow }), _jsx(ConfirmDialog, { open: confirmDiscard, onOpenChange: setConfirmDiscard, title: tI18nComplete.raw('text3b13192b9d88'), description: tI18nComplete('text8a36cce34f68', { value0: name }), confirmLabel: tI18nComplete.raw('texteb1a70e39274'), cancelLabel: tI18nComplete.raw('texte76fd2add010'), confirmVariant: "destructive", onConfirm: () => {
316
498
  setConfirmDiscard(false);
317
499
  editor.discard();
318
- } }), _jsx(ConfirmDialog, { open: leaveGuard.pendingHref !== null, onOpenChange: (open) => {
319
- if (!open)
320
- leaveGuard.stay();
321
- }, title: tI18nComplete.raw('text2190d03af90a'), description: tI18nComplete('textbbc5a3f2ca48', { value0: capitalizeWords(agent.name) }), confirmLabel: tI18nComplete.raw('textfc6e4a408d56'), cancelLabel: tI18nComplete.raw('texte76fd2add010'), confirmVariant: "destructive", onConfirm: leaveGuard.leave })] }) }));
500
+ } }), _jsx(LeaveDialog, { open: leaveGuard.asking, changed: changed, pending: update.isPending, onStay: leaveGuard.stay, onLeave: leaveGuard.leave, onSaveAndLeave: async () => {
501
+ if (await onSave())
502
+ leaveGuard.leave();
503
+ } })] }) }));
322
504
  }
323
- /**
324
- * The Overview topic — what the agent IS: one line of description and the
325
- * instructions. Laid out like a Settings pane: the pane title, then each
326
- * field under its own section heading. A field is its own surface; no box
327
- * wraps a heading and a field together.
328
- */
329
- function OverviewPane({ description, onDescriptionChange, descriptionHelp, prompt, onPromptChange, }) {
505
+ // ─── Read-only body ──────────────────────────────────────────────────────────
506
+ /** A reader's tabs and views: what can be shown without the editor. */
507
+ const READ_ONLY_VIEWS = ['actions', 'model'];
508
+ function ReadOnlyAgentPage({ projectId, agent, config, block, canWrite, }) {
509
+ const t = useI18nTranslations('agentEditor.tabs');
330
510
  const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
331
- const tEditor = useI18nTranslations('agentEditor');
332
- return (_jsxs(_Fragment, { children: [_jsx(SettingsSectionHeader, { title: tEditor('overviewTitle'), className: "pb-1" }), _jsx(EditorSection, { title: tI18nComplete.raw('text526e0087cc3f'), description: descriptionHelp, bare: true, children: _jsx(Textarea, { "aria-label": tI18nComplete.raw('text526e0087cc3f'), value: description, placeholder: tI18nComplete.raw('text446f4eabf99f'), minHeight: 44, className: "text-sm", onChange: (e) => onDescriptionChange(e.target.value) }) }), _jsx(InstructionsSection, { value: prompt, onChange: onPromptChange })] }));
333
- }
334
- /**
335
- * The instructions — the agent's system prompt, and the thing that most IS
336
- * the agent. Two views of one field: Edit is the textarea, Preview renders
337
- * the markdown the way the agent's source file reads. It opens on Preview
338
- * when there is something to read and on Edit when there is not; the toggle
339
- * is the segmented `TabsList` at the section heading's right edge.
340
- */
341
- function InstructionsSection({ value, onChange, }) {
342
- const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
343
- const [view, setView] = useState(value.trim() ? 'preview' : 'edit');
344
- return (_jsx(EditorSection, { title: tI18nComplete.raw('text934652dce41d'), description: tI18nComplete.raw('text5fe8921b712c'), trailing: _jsx(Tabs, { value: view, onValueChange: (next) => setView(next), children: _jsxs(TabsList, { "aria-label": tI18nComplete.raw('texte75bb2d0d554'), children: [_jsx(TabsTrigger, { value: "edit", children: tI18nComplete.raw('text464c4ffd019e') }), _jsx(TabsTrigger, { value: "preview", children: tI18nComplete.raw('text324b134f57c7') })] }) }), bare: true, children: view === 'edit' ? (_jsx(Textarea, { "aria-label": tI18nComplete.raw('text934652dce41d'), value: value, placeholder: tI18nComplete.raw('text1436469ad07f'), minHeight: 420, autoFocus: value.trim().length > 0, className: "font-mono text-xs leading-relaxed", onChange: (e) => onChange(e.target.value) })) : (_jsx("div", { className: "bg-popover min-h-[420px] rounded-md border px-5 py-4", children: _jsx(MarkdownWithFrontmatter, { content: value }) })) }));
345
- }
346
- /**
347
- * The save bar. Exists only while there is something to save — an always-on
348
- * disabled pair of buttons is chrome that never earns its row. It floats at
349
- * the bottom centre of the page in the Drive selection bar's shape
350
- * (`project-files/components/drive-selection-bar.tsx`): a `bg-popover` card
351
- * as wide as its content — the state as a title over one muted line, then
352
- * Discard and Save as a spaced pair (a confirm is never glued to its cancel).
353
- * It sits outside both scrollers, so it is reachable
354
- * from any point in a long instruction set.
355
- */
356
- function SaveBar({ dirty, pending, onDiscard, onSave, }) {
357
- const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
358
- return (_jsx(AnimatePresence, { initial: false, children: dirty ? (_jsx(m.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: 8 }, transition: { type: 'spring', duration: 0.3, bounce: 0 }, className: "pointer-events-none absolute inset-x-0 bottom-4 z-30 flex justify-center px-4", children: _jsxs("div", { className: "bg-popover text-popover-foreground border-border pointer-events-auto flex max-w-full items-center gap-3 rounded-lg border p-2 pl-3.5 shadow-md", children: [_jsxs("div", { className: "min-w-0", "aria-live": "polite", children: [_jsx("p", { className: "text-foreground truncate text-sm font-medium", children: tI18nComplete.raw('textefa5855ff509') }), _jsx("p", { className: "text-muted-foreground truncate text-xs", children: tI18nComplete.raw('textc8134fda3694') })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [_jsx(Button, { variant: "secondary", size: "sm", onClick: onDiscard, disabled: pending, children: tI18nComplete.raw('texteb1a70e39274') }), _jsxs(Button, { variant: "secondary", size: "sm", className: "gap-1.5", onClick: onSave, disabled: pending, children: [pending ? _jsx(Loading, { className: "size-3.5 shrink-0" }) : null, tI18nComplete.raw('text1509f561f241'), _jsx(Kbd, { children: tI18nComplete.raw('text8cd003e6301b') })] })] })] }) }, "save-bar")) : null }));
359
- }
360
- /**
361
- * Mod+S saves. The page is a document you edit, and every editor a person
362
- * has used saves on that chord; without it the browser offers to save the
363
- * HTML, which is never what anyone meant.
364
- */
365
- function useSaveShortcut(onSave) {
366
- useEffect(() => {
367
- const handler = (event) => {
368
- if ((event.metaKey || event.ctrlKey) &&
369
- !event.shiftKey &&
370
- !event.altKey &&
371
- event.key === 's') {
372
- event.preventDefault();
373
- onSave();
374
- }
375
- };
376
- window.addEventListener('keydown', handler);
377
- return () => window.removeEventListener('keydown', handler);
378
- }, [onSave]);
379
- }
380
- /**
381
- * Holds a dirty page on the page until the person decides.
382
- *
383
- * Two exits, two guards. A tab close or a reload reaches only `beforeunload`,
384
- * so that is armed while dirty. An in-app link — the sidebar, a tab, the
385
- * breadcrumb — never fires it, and the App Router has no navigation event to
386
- * cancel, so anchor clicks are intercepted in the capture phase instead: the
387
- * click is swallowed, the target href is parked, and the page asks. `leave`
388
- * pushes the parked href; `stay` drops it. Modified clicks (new tab) and
389
- * external links pass through — nothing is lost by opening a second tab.
390
- */
391
- function useUnsavedChangesGuard(isDirty, currentPath) {
392
- const router = useUiRouter();
393
- const [pendingHref, setPendingHref] = useState(null);
394
- useEffect(() => {
395
- if (!isDirty)
396
- return;
397
- const onBeforeUnload = (event) => {
398
- event.preventDefault();
399
- };
400
- const onClick = (event) => {
401
- if (event.defaultPrevented || event.button !== 0)
402
- return;
403
- if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
404
- return;
405
- const anchor = event.target?.closest?.('a[href]');
406
- if (!(anchor instanceof HTMLAnchorElement))
407
- return;
408
- if (anchor.target && anchor.target !== '_self')
409
- return;
410
- if (anchor.origin !== window.location.origin)
411
- return;
412
- const href = anchor.pathname + anchor.search + anchor.hash;
413
- if (href === window.location.pathname + window.location.search + window.location.hash)
414
- return;
415
- // A link that only changes `?section=` on this page switches topic; the
416
- // draft survives that, so it never needs the question.
417
- if (currentPath && anchor.pathname === currentPath)
418
- return;
419
- event.preventDefault();
420
- event.stopPropagation();
421
- setPendingHref(href);
422
- };
423
- window.addEventListener('beforeunload', onBeforeUnload);
424
- document.addEventListener('click', onClick, true);
425
- return () => {
426
- window.removeEventListener('beforeunload', onBeforeUnload);
427
- document.removeEventListener('click', onClick, true);
428
- };
429
- }, [isDirty, currentPath]);
430
- return {
431
- pendingHref,
432
- stay: () => setPendingHref(null),
433
- leave: () => {
434
- const href = pendingHref;
435
- setPendingHref(null);
436
- if (href)
437
- router.push(href);
438
- },
439
- };
440
- }
441
- // ─── Read-only body ────────────────────────────────────────────────────────
442
- function ReadOnlyAgentPage({ projectId, agent, config, canWrite, showUpgradeHint, isPlatform, }) {
443
- const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
444
- const tUpgrades = useI18nTranslations('settings.upgrades');
445
- // The real repo path, with any manifest anchor stripped. Agents declared in
446
- // the manifest rather than as their own file carry one
447
- // (`kortix.yaml#agents.<name>`), and reading that verbatim is a 404.
511
+ const pathname = useUiPathname();
448
512
  const sourcePath = configEntitySourcePath(agent.path);
513
+ // The served block carries the prompt when the read returned it; otherwise
514
+ // read the agent's source file.
449
515
  const fileQuery = useQuery({
450
516
  queryKey: ['entity-file-content', projectId, sourcePath],
451
517
  queryFn: () => readProjectFile(projectId, sourcePath),
452
518
  staleTime: 30_000,
453
- // Meta's "source" is /workspace/AGENTS.md, not a repo file — reading it 404s.
454
- enabled: !isPlatform,
519
+ enabled: block.behavior?.prompt === undefined,
455
520
  });
456
- const pathname = useUiPathname();
457
- const section = useAgentSection(isPlatform ? PLATFORM_SECTIONS : READ_ONLY_SECTIONS);
458
- const source = (_jsx(EditorSection, { title: tI18nComplete.raw('text934652dce41d'), description: _jsx("span", { className: "font-mono", children: sourcePath }), lead: true, bare: true, children: _jsx("div", { className: "bg-popover overflow-hidden rounded-md border", children: _jsx(EntityFilePane, { path: sourcePath, content: fileQuery.data?.content, isLoading: fileQuery.isLoading, isError: fileQuery.isError, error: fileQuery.error, onRetry: () => {
459
- void fileQuery.refetch();
460
- }, skeletonRows: 4, errorTitle: tI18nComplete.raw('text2f2f540650bf') }) }) }));
461
- return (_jsx(AgentPageFrame, { header: _jsx(AgentHeader, { projectId: projectId, agent: agent, config: config, canWrite: canWrite && !isPlatform, children: agent.description ? (_jsx("p", { className: "text-muted-foreground max-w-2xl text-sm text-pretty", children: agent.description })) : null }), section: section, sections: isPlatform ? PLATFORM_SECTIONS : READ_ONLY_SECTIONS, sectionHref: sectionHrefFor(pathname), pane: _jsx(EditorSectionStyleProvider, { value: "panel", children: _jsxs("div", { className: "space-y-8", children: [isPlatform ? (_jsx(InfoBanner, { tone: "info", title: tI18nComplete.raw('text85b7a59cdbd5'), children: tI18nComplete.raw('textd9c0c74b514b') })) : showUpgradeHint ? (_jsx(InfoBanner, { tone: "info", title: tI18nComplete.raw('textdc7ab144ca89'), children: tUpgrades.rich('agentEditorHint', {
462
- code: (chunks) => _jsx("span", { className: "font-mono", children: chunks }),
463
- }) })) : null, isPlatform ? null : section === 'overview' ? (_jsxs(_Fragment, { children: [source, _jsx(AgentScope, { projectId: projectId, agentName: agent.name, scope: agent.scope })] })) : section === 'people' ? (_jsx(AgentPeopleSection, { projectId: projectId, agentName: agent.name })) : section === 'triggers' ? (_jsx(AgentTriggersSection, { projectId: projectId, agentName: agent.name, defaultAgent: config.default_agent ?? config.open_code_default_agent })) : section === 'actions' ? (_jsx(AgentAuthorityCard, { projectId: projectId, agentName: agent.name, grant: agent.scope?.kortix_permissions ?? agent.scope?.kortix_cli })) : (_jsxs("div", { className: "space-y-3", children: [_jsx(AgentModel, { projectId: projectId, agentName: agent.name }), _jsx("p", { className: "text-muted-foreground text-xs text-pretty", children: tI18nComplete.raw('text9dee2a1acfd9') })] }))] }) }) }));
521
+ const prompt = block.behavior?.prompt ?? (fileQuery.data ? stripFrontmatter(fileQuery.data.content) : null);
522
+ const views = useMemo(() => {
523
+ const own = ACCESS_VIEWS.filter((v) => v === 'skills' || v === 'connectors' || v === 'secrets' || v === 'apps' || v === 'actions');
524
+ return [...own, 'model'];
525
+ }, []);
526
+ const location = useAgentLocation(AGENT_TABS, READ_ONLY_VIEWS);
527
+ const share = useShareDialog(location.open === 'share');
528
+ const facts = useOverviewFacts(projectId, agent.name, config, block);
529
+ const headerFacts = useHeaderFacts(projectId, agent, config, block, false);
530
+ const spacesOn = useSpacesEnabled(projectId);
531
+ const railItems = useRailItems(pathname, READ_ONLY_VIEWS, facts, undefined, {
532
+ model: modelName(block.behavior?.model),
533
+ workspace: block.sandbox,
534
+ });
535
+ const tabGroups = useTabGroups(railItems);
536
+ const name = agentDisplayName(agent.name);
537
+ // A reader has a page for Kortix permissions and Model only. Every other
538
+ // Access row shows its grant by name on the Overview and links nowhere.
539
+ const banner = (_jsx(InfoBanner, { tone: "neutral", icon: LockSimpleIcon, title: t('readOnlyTitle'), children: t.rich('readOnlyBody', { code: (chunks) => _jsx("span", { className: "font-mono", children: chunks }) }) }));
540
+ const body = (() => {
541
+ switch (location.tab) {
542
+ case 'overview':
543
+ return (_jsxs(AgentTabBody, { className: "space-y-6", children: [banner, _jsx(AgentOverview, { agent: agent, config: config, block: block, prompt: prompt, sourcePath: sourcePath, showSpace: spacesOn })] }));
544
+ case 'access':
545
+ case 'runtime':
546
+ return (_jsxs(AgentTabBody, { className: `${location.view === 'model' || location.view === 'workspace' ? CENTER_COLUMN : RAIL_COLUMN} space-y-6`, children: [banner, _jsx(EditorSectionStyleProvider, { value: "panel", children: location.view === 'model' ? (_jsxs("div", { className: "space-y-3", children: [_jsx(AgentModel, { projectId: projectId, agentName: agent.name }), _jsx("p", { className: "text-muted-foreground text-xs text-pretty", children: tI18nComplete.raw('text9dee2a1acfd9') })] })) : (_jsxs("div", { className: "space-y-8", children: [_jsx(AgentScope, { projectId: projectId, agentName: agent.name, scope: agent.scope }), _jsx(AgentAuthorityCard, { projectId: projectId, agentName: agent.name, grant: block.kortix_permissions ?? block.kortix_cli })] })) })] }));
547
+ case 'triggers':
548
+ // The same Triggers screen; its own controls decide what a reader may change.
549
+ return (_jsx(Embedded, { agentName: agent.name, children: _jsx(TriggersPage, { projectId: projectId, surface: { kind: 'agent', agentName: agent.name }, defaultAgent: config.default_agent ?? config.open_code_default_agent }) }));
550
+ }
551
+ })();
552
+ return (_jsxs("div", { className: "relative flex h-full min-h-0 flex-1 flex-col", children: [_jsx(AgentIdentityHeader, { slug: agent.name, title: name, facts: headerFacts, actions: _jsx(AgentActions, { projectId: projectId, agent: agent, config: config, canWrite: canWrite, share: share }) }), _jsx(TabLayout, { tabs: _jsx(AgentTabBar, { groups: tabGroups, active: railKeyOf(location) }), children: _jsx(AgentSectionRail, { group: railGroupOf(tabGroups, railKeyOf(location)), active: railKeyOf(location), children: body }) })] }));
553
+ }
554
+ /** A markdown file's body without its frontmatter. */
555
+ function stripFrontmatter(content) {
556
+ return content.replace(/^---\n[\s\S]*?\n---\n?/, '');
557
+ }
558
+ /** Whether a Kortix permissions grant lets the agent open project Apps. */
559
+ function grantsAppRead(grant) {
560
+ return grant === 'all' || (Array.isArray(grant) && grant.includes('project.app.read'));
561
+ }
562
+ /** The Model row's value: the model id after its provider (`kortix/kimi-k2` → `kimi-k2`). */
563
+ function modelName(model) {
564
+ return model ? model.slice(model.lastIndexOf('/') + 1) : undefined;
565
+ }
566
+ /**
567
+ * `connectors_required` (kortix.yaml), when the block sets it: read-only,
568
+ * because sessions no longer wait for these connectors.
569
+ */
570
+ function RequiredConnectorsNote({ names }) {
571
+ const t = useI18nTranslations('agentEditor.tabs');
572
+ if (!names || names.length === 0)
573
+ return null;
574
+ return (_jsxs("section", { className: "space-y-1", children: [_jsx("h2", { className: "text-sm font-medium", children: t('requiredConnectors') }), _jsx("p", { className: "text-foreground font-mono text-sm", children: names.join(', ') }), _jsx("p", { className: "text-muted-foreground text-xs text-pretty", children: t('requiredConnectorsHelp') })] }));
464
575
  }