@kortix/ui 0.14.6 → 0.14.8

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 (186) hide show
  1. package/dist/_vendor/kortix-shared.js +490 -382
  2. package/dist/_vendor/shared/runtime-versions.d.ts +4 -0
  3. package/dist/access/access/access-dialog-share.js +4 -7
  4. package/dist/access/access/access-dialog.js +2 -1
  5. package/dist/access/access/principal-picker.js +6 -5
  6. package/dist/customize/agents/agent-authority-card.js +1 -2
  7. package/dist/customize/agents/agent-cover-card.d.ts +17 -0
  8. package/dist/customize/agents/agent-cover-card.js +13 -0
  9. package/dist/customize/agents/agent-detail-aside.js +15 -14
  10. package/dist/customize/agents/agent-drives-section.js +5 -6
  11. package/dist/customize/agents/agent-grant-pages.js +68 -70
  12. package/dist/customize/agents/agent-page.js +27 -19
  13. package/dist/customize/agents/agent-people-section.js +18 -17
  14. package/dist/customize/agents/agent-triggers-section.js +6 -5
  15. package/dist/customize/agents/agents-page.js +43 -49
  16. package/dist/customize/connectors/catalog/catalog-entry.d.ts +14 -0
  17. package/dist/customize/connectors/catalog/catalog-entry.js +18 -1
  18. package/dist/customize/connectors/catalog/connector-browse.d.ts +28 -0
  19. package/dist/customize/connectors/catalog/connector-browse.js +18 -5
  20. package/dist/customize/connectors/detail/app-page.js +19 -5
  21. package/dist/customize/connectors/detail/connector-modal.d.ts +10 -13
  22. package/dist/customize/connectors/detail/connector-modal.js +18 -101
  23. package/dist/customize/connectors/detail/connector-notice.js +6 -2
  24. package/dist/customize/connectors/detail/connector-page.d.ts +30 -0
  25. package/dist/customize/connectors/detail/connector-page.js +26 -13
  26. package/dist/customize/migrate-to-v2/migration-prompt.d.ts +1 -0
  27. package/dist/customize/migrate-to-v2/migration-prompt.js +1 -0
  28. package/dist/customize/sections/llm-provider/custom-provider-form.js +24 -7
  29. package/dist/customize/sections/llm-provider/custom-provider-panel.js +44 -8
  30. package/dist/customize/sections/llm-provider/types.d.ts +3 -0
  31. package/dist/customize/sections/llm-provider/utils.js +2 -2
  32. package/dist/customize/sections/view/agent-editor-access-fields.d.ts +9 -16
  33. package/dist/customize/sections/view/agent-editor-access-fields.js +30 -30
  34. package/dist/customize/sections/view/agent-editor-basics-fields.js +3 -10
  35. package/dist/customize/sections/view/agent-editor-primitives.d.ts +28 -10
  36. package/dist/customize/sections/view/agent-editor-primitives.js +32 -19
  37. package/dist/customize/sections/view/agent-editor.js +3 -3
  38. package/dist/customize/sections/view/channels-view.js +2 -6
  39. package/dist/customize/sections/view/gateway/gateway-routing.js +3 -3
  40. package/dist/customize/sections/view/grant-mode-field.d.ts +2 -5
  41. package/dist/customize/sections/view/grant-mode-field.js +10 -18
  42. package/dist/customize/sections/view/permission-editor.js +11 -8
  43. package/dist/customize/sections/view/secrets-view.js +2 -1
  44. package/dist/customize/settings/tabs/general-tab.d.ts +9 -2
  45. package/dist/customize/settings/tabs/general-tab.js +33 -11
  46. package/dist/customize/settings/tabs/transfer-workspace-dialog.d.ts +4 -0
  47. package/dist/customize/settings/tabs/transfer-workspace-dialog.js +82 -0
  48. package/dist/customize/shared/new-entity-menu.js +4 -1
  49. package/dist/customize/skills/skills-page.js +4 -5
  50. package/dist/customize/triggers/schedule/then-fields.js +1 -5
  51. package/dist/customize/triggers/schedule-view.js +4 -2
  52. package/dist/files/project/api/runtime-files.js +3 -2
  53. package/dist/files/project/file-source.js +18 -1
  54. package/dist/files/project/hooks/use-file-content.js +2 -0
  55. package/dist/files/viewer/file-header.js +1 -1
  56. package/dist/files/viewer/file-preview-modal.d.ts +1 -1
  57. package/dist/files/viewer/file-preview-modal.js +12 -2
  58. package/dist/files/viewer/file-source.d.ts +3 -1
  59. package/dist/files/viewer/use-file-content-state.js +26 -7
  60. package/dist/files/viewer/viewer-actions.js +2 -2
  61. package/dist/i18n/en.d.ts +393 -12
  62. package/dist/i18n/en.js +400 -19
  63. package/dist/internal/components/agent-mark.d.ts +38 -0
  64. package/dist/internal/components/agent-mark.js +38 -0
  65. package/dist/internal/files/types.d.ts +2 -0
  66. package/dist/internal/goals/goal-meta.d.ts +30 -0
  67. package/dist/internal/goals/goal-meta.js +55 -0
  68. package/dist/internal/lib/pixel-mark.d.ts +33 -0
  69. package/dist/internal/lib/pixel-mark.js +152 -0
  70. package/dist/internal/lib/query-client-singleton.d.ts +9 -3
  71. package/dist/internal/lib/query-client-singleton.js +18 -12
  72. package/dist/internal/lib/use-project-feature-flags.js +3 -3
  73. package/dist/internal/share/session-shares-modal.js +3 -7
  74. package/dist/internal/stores/kortix-computer-store.js +14 -2
  75. package/dist/internal/stores/session-hover-store.d.ts +32 -0
  76. package/dist/internal/stores/session-hover-store.js +38 -0
  77. package/dist/internal/stores/sidebar-nav-store.d.ts +1 -1
  78. package/dist/internal/stores/sidebar-nav-store.js +3 -1
  79. package/dist/lib/routes.d.ts +2 -0
  80. package/dist/lib/routes.js +1 -0
  81. package/dist/markdown/genui/components/inline.d.ts +1 -1
  82. package/dist/markdown/setup-links/app-credentials-form.d.ts +12 -0
  83. package/dist/markdown/setup-links/app-credentials-form.js +115 -0
  84. package/dist/markdown/setup-links/secret-intake-form.d.ts +10 -0
  85. package/dist/markdown/setup-links/secret-intake-form.js +1 -1
  86. package/dist/primitives/entity-avatar.d.ts +4 -1
  87. package/dist/primitives/entity-avatar.js +4 -2
  88. package/dist/primitives/settings-section-header.d.ts +1 -1
  89. package/dist/primitives/user-avatar.d.ts +3 -1
  90. package/dist/primitives/user-avatar.js +4 -2
  91. package/dist/providers/ui-provider.d.ts +1 -1
  92. package/dist/session/composer/agent-selector.d.ts +7 -1
  93. package/dist/session/composer/agent-selector.js +20 -10
  94. package/dist/session/composer/attachment-tile.d.ts +0 -2
  95. package/dist/session/composer/attachment-tile.js +0 -8
  96. package/dist/session/composer/clipboard-files.d.ts +2 -1
  97. package/dist/session/composer/composer-draft-recovery.d.ts +1 -2
  98. package/dist/session/composer/composer-draft-recovery.js +0 -13
  99. package/dist/session/composer/context-ring.d.ts +1 -1
  100. package/dist/session/composer/context-ring.js +2 -2
  101. package/dist/session/composer/context-usage.d.ts +8 -6
  102. package/dist/session/composer/context-usage.js +9 -7
  103. package/dist/session/composer/inbox-row-claims.d.ts +2 -2
  104. package/dist/session/composer/inbox-row-claims.js +1 -1
  105. package/dist/session/composer/queue-projection.d.ts +3 -2
  106. package/dist/session/composer/queued-prompt-edit.d.ts +1 -1
  107. package/dist/session/composer/reasoning-effort-selector.d.ts +2 -1
  108. package/dist/session/composer/session-composer-readiness.d.ts +7 -22
  109. package/dist/session/composer/session-composer-readiness.js +12 -40
  110. package/dist/session/composer/token-progress.d.ts +1 -1
  111. package/dist/session/composer/token-progress.js +6 -6
  112. package/dist/session/header/export-transcript-modal.d.ts +3 -2
  113. package/dist/session/header/export-transcript-modal.js +6 -3
  114. package/dist/session/header/session-goal-indicator.d.ts +1 -0
  115. package/dist/session/header/session-goal-indicator.js +41 -0
  116. package/dist/session/header/session-site-header.js +2 -1
  117. package/dist/session/models/model-selector.d.ts +3 -1
  118. package/dist/session/models/model-selector.js +3 -2
  119. package/dist/session/panel/easy/plan-card.d.ts +2 -0
  120. package/dist/session/panel/easy/plan-card.js +2 -0
  121. package/dist/session/panel/session-action-panel-column.js +11 -1
  122. package/dist/session/panel/session-audit-shared.d.ts +0 -4
  123. package/dist/session/panel/session-audit-shared.js +1 -1
  124. package/dist/session/panel/session-changes-shared.d.ts +2 -1
  125. package/dist/session/prompts/session-permission-prompt.js +6 -3
  126. package/dist/session/status/provisioning-failure.d.ts +2 -1
  127. package/dist/session/status/session-error-banner.js +8 -6
  128. package/dist/session/status/session-starting-loader.d.ts +24 -31
  129. package/dist/session/status/session-starting-loader.js +32 -81
  130. package/dist/session/thread/chat-minimap-items.d.ts +4 -4
  131. package/dist/session/thread/first-prompt-handover.d.ts +3 -2
  132. package/dist/session/thread/message-parsing.d.ts +3 -3
  133. package/dist/session/thread/plan-surface.d.ts +1 -0
  134. package/dist/session/thread/plan-surface.js +1 -0
  135. package/dist/session/thread/session-chat.js +5 -12
  136. package/dist/session/thread/session-context-metrics.d.ts +15 -0
  137. package/dist/session/thread/session-context-metrics.js +10 -1
  138. package/dist/session/thread/session-context-modal.d.ts +4 -2
  139. package/dist/session/thread/session-context-modal.js +16 -9
  140. package/dist/session/thread/session-history-scroll.d.ts +2 -1
  141. package/dist/session/thread/session-thread.js +1 -1
  142. package/dist/session/thread/transcript.d.ts +4 -0
  143. package/dist/session/thread/transcript.js +18 -5
  144. package/dist/session/thread/turn-share.d.ts +16 -0
  145. package/dist/session/thread/turn-share.js +162 -0
  146. package/dist/session/tool/tools/apply-patch-tool.d.ts +1 -0
  147. package/dist/session/tool/tools/apply-patch-tool.js +1 -0
  148. package/dist/session/tool/tools/pty-kill-tool.d.ts +1 -0
  149. package/dist/session/tool/tools/pty-kill-tool.js +1 -0
  150. package/dist/session/tool/tools/pty-read-tool.d.ts +2 -0
  151. package/dist/session/tool/tools/pty-read-tool.js +2 -0
  152. package/dist/session/tool/tools/pty-spawn-tool.d.ts +1 -0
  153. package/dist/session/tool/tools/pty-spawn-tool.js +1 -0
  154. package/dist/session/tool/tools/pty-write-tool.d.ts +1 -0
  155. package/dist/session/tool/tools/pty-write-tool.js +1 -0
  156. package/dist/session/tool/tools/todo-write-tool.d.ts +1 -0
  157. package/dist/session/tool/tools/todo-write-tool.js +1 -0
  158. package/dist/session/turn/message-author-label.js +6 -2
  159. package/dist/session/turn/plan-anchor.d.ts +3 -0
  160. package/dist/session/turn/plan-anchor.js +3 -0
  161. package/dist/session/turn/plan-card.d.ts +7 -0
  162. package/dist/session/turn/plan-card.js +7 -0
  163. package/dist/styles.css +1 -1
  164. package/dist/terminal/pty-connection.d.ts +5 -4
  165. package/dist/terminal/session-terminal-connect-bar.js +2 -1
  166. package/dist/workspace/index.d.ts +1 -0
  167. package/dist/workspace/index.js +1 -0
  168. package/dist/workspace/saved-session-skeleton-shape.d.ts +4 -3
  169. package/dist/workspace/saved-session-skeleton.d.ts +5 -1
  170. package/dist/workspace/saved-session-skeleton.js +5 -4
  171. package/dist/workspace/session-workspace.js +4 -5
  172. package/dist/workspace/sidebar/footer/project-goals-nav.d.ts +6 -0
  173. package/dist/workspace/sidebar/footer/project-goals-nav.js +26 -0
  174. package/dist/workspace/sidebar/modal/session-embed-section.d.ts +25 -0
  175. package/dist/workspace/sidebar/modal/session-embed-section.js +71 -0
  176. package/dist/workspace/sidebar/modal/share-session-modal.js +2 -2
  177. package/dist/workspace/sidebar/nav-row-meta.js +14 -2
  178. package/dist/workspace/sidebar/project-nav-items.js +3 -0
  179. package/dist/workspace/sidebar/project-session-list.js +12 -3
  180. package/dist/workspace/sidebar/session-brief-hover-card.js +33 -9
  181. package/package.json +12 -4
  182. package/skills/kortix-ui/references/modules.md +2 -0
  183. package/dist/session/status/use-readiness-settling.d.ts +0 -18
  184. package/dist/session/status/use-readiness-settling.js +0 -43
  185. package/dist/workspace/sidebar/modal/session-public-link-section.d.ts +0 -13
  186. package/dist/workspace/sidebar/modal/session-public-link-section.js +0 -66
@@ -12,7 +12,9 @@ export type RuntimeVersions = {
12
12
  bun: string;
13
13
  bunSha256Amd64: string;
14
14
  bunSha256Arm64: string;
15
+ /** @deprecated OpenCode only. OpenCode is sunset (2026-10-23): no new code. See apps/kortix-sandbox-agent-server/src/harness/README.md "OpenCode sunset". */
15
16
  opencode: string;
17
+ /** @deprecated OpenCode only. OpenCode is sunset (2026-10-23): no new code. See apps/kortix-sandbox-agent-server/src/harness/README.md "OpenCode sunset". */
16
18
  opencodeSdk: string;
17
19
  agentBrowser: string;
18
20
  playwright: string;
@@ -35,7 +37,9 @@ export declare const PYTHON_PACKAGE_FLOOR_IMPORTS: Record<string, string>;
35
37
  export declare const BUN_VERSION: string;
36
38
  export declare const BUN_SHA256_AMD64: string;
37
39
  export declare const BUN_SHA256_ARM64: string;
40
+ /** @deprecated OpenCode only. OpenCode is sunset (2026-10-23): no new code. See apps/kortix-sandbox-agent-server/src/harness/README.md "OpenCode sunset". */
38
41
  export declare const OPENCODE_VERSION: string;
42
+ /** @deprecated OpenCode only. OpenCode is sunset (2026-10-23): no new code. See apps/kortix-sandbox-agent-server/src/harness/README.md "OpenCode sunset". */
39
43
  export declare const OPENCODE_SDK_VERSION: string;
40
44
  export declare const OPENCODE_USER_AGENT: string;
41
45
  export declare const AGENT_BROWSER_VERSION: string;
@@ -13,6 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  //
14
14
  // There is no expiry field here on purpose: when the last narrowing grant
15
15
  // expires, the account silently widens to everyone.
16
+ import { AgentMark } from '../../internal/components/agent-mark.js';
16
17
  import { Button } from '../../primitives/button.js';
17
18
  import { EntityAvatar } from '../../primitives/entity-avatar.js';
18
19
  import { Field, FieldLabel } from '../../primitives/field.js';
@@ -25,7 +26,7 @@ import { useTranslations as useI18nTranslations } from '../../i18n/index.js';
25
26
  import { cn } from '../../internal/lib/utils.js';
26
27
  import { createAssignment, revokeAssignment, shareConnection, } from '@kortix/sdk';
27
28
  import { invalidatePermissionProbes, qk } from '@kortix/sdk/react';
28
- import { LockIcon, RobotIcon, UsersIcon, UsersThreeIcon, XIcon } from '@phosphor-icons/react';
29
+ import { LockIcon, UsersIcon, UsersThreeIcon, XIcon } from '@phosphor-icons/react';
29
30
  import { useMutation, useQueryClient } from '@tanstack/react-query';
30
31
  import { useState } from 'react';
31
32
  import { EMPTY_PRINCIPAL_SELECTION, PrincipalPicker, } from './principal-picker.js';
@@ -176,14 +177,10 @@ export function ShareAccessBody({ open, onOpenChange, accountId, projectId, proj
176
177
  if (save.isPending)
177
178
  return;
178
179
  onOpenChange(next);
179
- }, children: _jsxs(ModalContent, { className: "sm:max-w-md", children: [_jsxs(ModalHeader, { children: [_jsx(ModalTitle, { children: t('shareTitle', { label: object.label }) }), _jsx(ModalDescription, { children: privateOwner ? t('sharePrivateDescription') : t('shareDescription') })] }), _jsxs(ModalBody, { className: "max-h-[60vh] space-y-4 overflow-y-auto", children: [_jsxs(Field, { className: "gap-1.5", children: [_jsx(FieldLabel, { children: t('whoCanUse') }), _jsx("ul", { className: "space-y-2", "data-testid": "share-audience", children: privateOwner ? (_jsx(AudienceRow, { avatar: _jsx(UserAvatar, { email: privateOwner.label, size: "sm" }), label: privateOwner.label, meta: keepOwner ? t('ownerMeta') : t('removedMeta'), removed: !keepOwner, action: keepOwner ? (_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "size-8 shrink-0", "aria-label": t('remove', { name: privateOwner.label }), disabled: save.isPending, onClick: () => setKeepOwner(false), children: _jsx(XIcon, { className: "size-3.5" }) })) : (_jsx(Button, { type: "button", variant: "ghost", size: "sm", disabled: save.isPending, onClick: () => setKeepOwner(true), children: t('keep') })) })) : current.length === 0 ? (_jsx(AudienceRow, { avatar: _jsx(EntityAvatar, { icon: UsersThreeIcon, label: everyoneLabel, size: "sm" }), label: everyoneLabel, meta: t('everyoneDefaultMeta') })) : (current.map((grant) => {
180
+ }, children: _jsxs(ModalContent, { className: "sm:max-w-md", children: [_jsxs(ModalHeader, { children: [_jsx(ModalTitle, { children: t('shareTitle', { label: object.label }) }), _jsx(ModalDescription, { children: privateOwner ? t('sharePrivateDescription') : t('shareDescription') })] }), _jsxs(ModalBody, { className: "max-h-[60vh] space-y-4 overflow-y-auto", children: [_jsxs(Field, { className: "gap-1.5", children: [_jsx(FieldLabel, { children: t('whoCanUse') }), _jsx("ul", { className: "space-y-2", "data-testid": "share-audience", children: privateOwner ? (_jsx(AudienceRow, { avatar: _jsx(UserAvatar, { email: privateOwner.label, hint: privateOwner.label, size: "sm" }), label: privateOwner.label, meta: keepOwner ? t('ownerMeta') : t('removedMeta'), removed: !keepOwner, action: keepOwner ? (_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "size-8 shrink-0", "aria-label": t('remove', { name: privateOwner.label }), disabled: save.isPending, onClick: () => setKeepOwner(false), children: _jsx(XIcon, { className: "size-3.5" }) })) : (_jsx(Button, { type: "button", variant: "ghost", size: "sm", disabled: save.isPending, onClick: () => setKeepOwner(true), children: t('keep') })) })) : current.length === 0 ? (_jsx(AudienceRow, { avatar: _jsx(EntityAvatar, { icon: UsersThreeIcon, label: everyoneLabel, hint: everyoneLabel, size: "sm" }), label: everyoneLabel, meta: t('everyoneDefaultMeta') })) : (current.map((grant) => {
180
181
  const isRemoved = removed.has(grant.grant_id);
181
182
  const label = grant.principal_type === 'project' ? everyoneLabel : grant.label;
182
- return (_jsx(AudienceRow, { avatar: grant.principal_type === 'member' ? (_jsx(UserAvatar, { email: grant.label, size: "sm" })) : (_jsx(EntityAvatar, { icon: grant.principal_type === 'group'
183
- ? UsersIcon
184
- : grant.principal_type === 'agent'
185
- ? RobotIcon
186
- : UsersThreeIcon, label: label, size: "sm" })), label: label, meta: isRemoved
183
+ return (_jsx(AudienceRow, { avatar: grant.principal_type === 'member' ? (_jsx(UserAvatar, { email: grant.label, hint: grant.label, size: "sm" })) : grant.principal_type === 'agent' ? (_jsx(AgentMark, { slug: grant.label })) : (_jsx(EntityAvatar, { icon: grant.principal_type === 'group' ? UsersIcon : UsersThreeIcon, label: label, hint: label, size: "sm" })), label: label, meta: isRemoved
187
184
  ? t('removedMeta')
188
185
  : grant.principal_type === 'group'
189
186
  ? t('groupMeta')
@@ -1,5 +1,6 @@
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';
3
4
  import { useTranslations as useI18nTranslations } from '../../i18n/index.js';
4
5
  // AccessDialog — THE modal for every "give / edit access" interaction.
5
6
  //
@@ -909,7 +910,7 @@ function FixedPrincipals({ principals }) {
909
910
  const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
910
911
  const tAgents = useI18nTranslations('agentPrincipals');
911
912
  return (_jsx("ul", { className: "space-y-2", children: principals.map((principal) => (_jsxs("li", { className: "bg-popover flex items-center gap-2.5 rounded-md border px-3 py-2", children: [principal.avatar ??
912
- (principal.type === 'group' ? (_jsx(EntityAvatar, { label: principal.label, size: "sm" })) : principal.type === 'agent' ? (_jsx(EntityAvatar, { icon: RobotIcon, label: principal.label, size: "sm" })) : (_jsx(UserAvatar, { email: principal.label, size: "sm" }))), _jsx("span", { className: "text-foreground min-w-0 flex-1 truncate text-sm font-medium", children: principal.label }), principal.type === 'group' ? (_jsx(Badge, { variant: "outline", size: "sm", children: tI18nComplete.raw('text34ca0e766088') })) : principal.type === 'agent' ? (_jsx(Badge, { variant: "outline", size: "sm", children: tAgents('agentBadge') })) : null] }, principal.id))) }));
913
+ (principal.type === 'group' ? (_jsx(EntityAvatar, { label: principal.label, hint: principal.label, size: "sm" })) : principal.type === 'agent' ? (_jsx(AgentMark, { slug: principal.label })) : (_jsx(UserAvatar, { email: principal.label, hint: principal.label, size: "sm" }))), _jsx("span", { className: "text-foreground min-w-0 flex-1 truncate text-sm font-medium", children: principal.label }), principal.type === 'group' ? (_jsx(Badge, { variant: "outline", size: "sm", children: tI18nComplete.raw('text34ca0e766088') })) : principal.type === 'agent' ? (_jsx(Badge, { variant: "outline", size: "sm", children: tAgents('agentBadge') })) : null] }, principal.id))) }));
913
914
  }
914
915
  function ScopeLine({ icon: Icon, label, items, }) {
915
916
  return (_jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsx(Icon, { className: "text-muted-foreground/70 size-3.5 shrink-0" }), _jsx("span", { className: "text-muted-foreground text-xs font-medium", children: label }), items.map((name) => (_jsx(Badge, { variant: "outline", size: "xs", className: "font-mono", children: name }, name)))] }));
@@ -1,5 +1,6 @@
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';
3
4
  import { useTranslations } from '../../i18n/index.js';
4
5
  // PrincipalPicker — THE "who is this for?" control.
5
6
  //
@@ -25,7 +26,7 @@ import { useUiHostConfig, useUiViewer } from '../../providers/ui-provider.js';
25
26
  import { cn } from '../../internal/lib/utils.js';
26
27
  import { listAccountMembers, listGroups, listProjectAccess } from '@kortix/sdk';
27
28
  import { contract, qk } from '@kortix/sdk/react';
28
- import { CheckCircleIcon, MagnifyingGlassIcon, RobotIcon, UserPlusIcon, UsersIcon, UsersThreeIcon, } from '@phosphor-icons/react';
29
+ import { CheckCircleIcon, MagnifyingGlassIcon, UserPlusIcon, UsersIcon, UsersThreeIcon, } from '@phosphor-icons/react';
29
30
  import { useQuery } from '@tanstack/react-query';
30
31
  import { useMemo, useState } from 'react';
31
32
  import { principalLabel } from './access-shared.js';
@@ -219,7 +220,7 @@ export function PrincipalPicker({ scope, selection = 'multi', kinds = ['member',
219
220
  : selected.kind === 'member'
220
221
  ? principalLabel(rosterMembers.find((m) => m.user_id === selected.id)) || selected.id
221
222
  : selected.id;
222
- return (_jsxs("div", { className: cn('bg-popover flex items-center gap-2.5 rounded-md border px-3 py-2', className), children: [selected.kind === 'group' ? (_jsx(EntityAvatar, { icon: UsersIcon, label: label, size: "sm" })) : selected.kind === 'agent' ? (_jsx(EntityAvatar, { icon: RobotIcon, label: label, size: "sm" })) : (_jsx(UserAvatar, { email: label, size: "sm" })), _jsx("span", { className: "text-foreground min-w-0 flex-1 truncate text-sm font-medium", children: label }), _jsx(Badge, { variant: "outline", size: "sm", children: selected.kind === 'group'
223
+ return (_jsxs("div", { className: cn('bg-popover flex items-center gap-2.5 rounded-md border px-3 py-2', className), children: [selected.kind === 'group' ? (_jsx(EntityAvatar, { icon: UsersIcon, label: label, hint: label, size: "sm" })) : selected.kind === 'agent' ? (_jsx(AgentMark, { slug: label })) : (_jsx(UserAvatar, { email: label, hint: label, size: "sm" })), _jsx("span", { className: "text-foreground min-w-0 flex-1 truncate text-sm font-medium", children: label }), _jsx(Badge, { variant: "outline", size: "sm", children: selected.kind === 'group'
223
224
  ? 'Group'
224
225
  : selected.kind === 'invite'
225
226
  ? 'Invite'
@@ -258,10 +259,10 @@ export function PrincipalPicker({ scope, selection = 'multi', kinds = ['member',
258
259
  filteredMembers.length === 0 &&
259
260
  filteredAgents.length === 0 &&
260
261
  !inviteCandidate &&
261
- !showEveryone ? (_jsx("p", { className: "text-muted-foreground px-3 py-6 text-center text-xs", children: tI18nComplete.raw('texte8dd87902b91') })) : (_jsxs(_Fragment, { children: [showEveryone && everyone ? (_jsx(PickerRow, { selected: value.everyone === true, selection: selection, disabled: disabled, onSelect: () => pick({ kind: 'everyone', id: scope.kind === 'project' ? scope.projectId : '' }), leading: _jsx(EntityAvatar, { icon: UsersThreeIcon, label: everyone.label, size: "sm" }), label: everyone.label })) : null, inviteCandidate ? (_jsx(PickerRow, { selected: inviteSet.has(inviteCandidate), selection: selection, disabled: disabled, onSelect: () => pick({ kind: 'invite', id: inviteCandidate }), leading: _jsx("span", { className: "border-muted-foreground/40 text-muted-foreground flex size-6 shrink-0 items-center justify-center rounded-full border border-dashed", children: _jsx(UserPlusIcon, { className: "size-3.5" }) }), label: `Invite ${inviteCandidate}`, suffix: tI18nComplete.raw('text10d9e23d0185') })) : null, filteredGroups.length > 0 ? (_jsxs(_Fragment, { children: [_jsx(PickerSectionLabel, { children: tI18nComplete.raw('text39bbb719fa2b') }), filteredGroups.map((group) => (_jsx(PickerRow, { selected: groupSet.has(group.group_id), selection: selection, disabled: disabled, onSelect: () => pick({ kind: 'group', id: group.group_id }), leading: _jsx(EntityAvatar, { icon: UsersIcon, label: group.name, size: "sm" }), label: group.name, suffix: tI18nComplete.raw('text24f15c9196e8') }, group.group_id)))] })) : null, filteredMembers.length > 0 ? (_jsxs(_Fragment, { children: [filteredGroups.length > 0 ? (_jsx(PickerSectionLabel, { children: tI18nComplete.raw('text1044a4c056d0') })) : null, filteredMembers.map((member) => {
262
+ !showEveryone ? (_jsx("p", { className: "text-muted-foreground px-3 py-6 text-center text-xs", children: tI18nComplete.raw('texte8dd87902b91') })) : (_jsxs(_Fragment, { children: [showEveryone && everyone ? (_jsx(PickerRow, { selected: value.everyone === true, selection: selection, disabled: disabled, onSelect: () => pick({ kind: 'everyone', id: scope.kind === 'project' ? scope.projectId : '' }), leading: _jsx(EntityAvatar, { icon: UsersThreeIcon, label: everyone.label, hint: everyone.label, size: "sm" }), label: everyone.label })) : null, inviteCandidate ? (_jsx(PickerRow, { selected: inviteSet.has(inviteCandidate), selection: selection, disabled: disabled, onSelect: () => pick({ kind: 'invite', id: inviteCandidate }), leading: _jsx("span", { className: "border-muted-foreground/40 text-muted-foreground flex size-6 shrink-0 items-center justify-center rounded-full border border-dashed", children: _jsx(UserPlusIcon, { className: "size-3.5" }) }), label: `Invite ${inviteCandidate}`, suffix: tI18nComplete.raw('text10d9e23d0185') })) : null, filteredGroups.length > 0 ? (_jsxs(_Fragment, { children: [_jsx(PickerSectionLabel, { children: tI18nComplete.raw('text39bbb719fa2b') }), filteredGroups.map((group) => (_jsx(PickerRow, { selected: groupSet.has(group.group_id), selection: selection, disabled: disabled, onSelect: () => pick({ kind: 'group', id: group.group_id }), leading: _jsx(EntityAvatar, { icon: UsersIcon, label: group.name, hint: group.name, size: "sm" }), label: group.name, suffix: tI18nComplete.raw('text24f15c9196e8') }, group.group_id)))] })) : null, filteredMembers.length > 0 ? (_jsxs(_Fragment, { children: [filteredGroups.length > 0 ? (_jsx(PickerSectionLabel, { children: tI18nComplete.raw('text1044a4c056d0') })) : null, filteredMembers.map((member) => {
262
263
  const label = principalLabel(member) || member.user_id;
263
- return (_jsx(PickerRow, { selected: memberSet.has(member.user_id), selection: selection, disabled: disabled, onSelect: () => pick({ kind: 'member', id: member.user_id }), leading: _jsx(UserAvatar, { email: label, size: "sm", variant: "primary" }), label: label, suffix: member.user_id === viewerId ? '(you)' : undefined }, member.user_id));
264
- })] })) : null, filteredAgents.length > 0 ? (_jsxs(_Fragment, { children: [_jsx(PickerSectionLabel, { children: tAgents('agentsSection') }), filteredAgents.map((agent) => (_jsx(PickerRow, { selected: agentSet.has(agent.service_account_id), selection: selection, disabled: disabled, onSelect: () => pick({ kind: 'agent', id: agent.service_account_id }), leading: _jsx(EntityAvatar, { icon: RobotIcon, label: agent.agent_name ?? '', size: "sm" }), label: agent.agent_name ?? agent.name, suffix: tAgents('agentBadge') }, agent.service_account_id)))] })) : null] })) })] }));
264
+ return (_jsx(PickerRow, { selected: memberSet.has(member.user_id), selection: selection, disabled: disabled, onSelect: () => pick({ kind: 'member', id: member.user_id }), leading: _jsx(UserAvatar, { email: label, hint: label, size: "sm", variant: "primary" }), label: label, suffix: member.user_id === viewerId ? '(you)' : undefined }, member.user_id));
265
+ })] })) : null, filteredAgents.length > 0 ? (_jsxs(_Fragment, { children: [_jsx(PickerSectionLabel, { children: tAgents('agentsSection') }), filteredAgents.map((agent) => (_jsx(PickerRow, { selected: agentSet.has(agent.service_account_id), selection: selection, disabled: disabled, onSelect: () => pick({ kind: 'agent', id: agent.service_account_id }), leading: _jsx(AgentMark, { slug: agent.agent_name ?? agent.name }), label: agent.agent_name ?? agent.name, suffix: tAgents('agentBadge') }, agent.service_account_id)))] })) : null] })) })] }));
265
266
  }
266
267
  function PickerSectionLabel({ children }) {
267
268
  return (_jsx("p", { className: "text-muted-foreground px-2 pt-2 pb-1 text-xs font-medium first:pt-1.5", children: children }));
@@ -22,7 +22,6 @@ import { useAgentAuthority, } from '../../access/access/agent-principals.js';
22
22
  import { useTranslations as useI18nTranslations } from '../../i18n/index.js';
23
23
  import { getProjectDetail } from '@kortix/sdk';
24
24
  import { contract, qk } from '@kortix/sdk/react';
25
- import { ArrowUpRightIcon } from '@phosphor-icons/react';
26
25
  import { useQuery } from '@tanstack/react-query';
27
26
  export function AgentAuthorityCard({ projectId, agentName, grant, }) {
28
27
  const t = useI18nTranslations('agentPrincipals');
@@ -39,7 +38,7 @@ export function AgentAuthorityCard({ projectId, agentName, grant, }) {
39
38
  : authority.declared.length === 0
40
39
  ? t('declaredNone')
41
40
  : t('declaredCount', { count: authority.declared.length });
42
- return (_jsx("div", { "data-testid": "agent-authority", children: _jsxs(EditorSection, { title: t('title'), description: t('description'), trailing: _jsx(Badge, { variant: "outline", size: "sm", className: "tabular-nums", children: t('effectiveCount', { count: authority.effective.length }) }), children: [_jsx(SettingRow, { label: t('kortixPermissions'), help: t('kortixPermissionsHelp', { agent: agentName }), children: _jsx("span", { className: "text-foreground block text-sm sm:text-right", children: declaredLabel }) }), _jsx(SettingRow, { label: t('ceiling'), help: t('ceilingHelp'), children: _jsxs("div", { className: "flex flex-wrap items-center gap-1.5 sm:justify-end", "data-testid": "agent-ceiling", children: [ceiling.kind === 'loading' ? (_jsx(Skeleton, { className: "h-5 w-32" })) : ceiling.kind === 'hidden' ? (_jsx("span", { className: "text-muted-foreground text-sm", children: t('ceilingHidden') })) : ceiling.kind === 'default' ? (_jsx("span", { className: "text-foreground text-sm", children: t('ceilingDefault') })) : (ceiling.roleKeys.map((key) => (_jsx(Badge, { variant: "outline", size: "sm", className: "capitalize", children: key }, key)))), accountId && ceiling.kind !== 'hidden' && ceiling.kind !== 'loading' ? (_jsx(Button, { asChild: true, variant: "ghost", size: "sm", className: "gap-1 px-2", children: _jsxs(HubLink, { to: hubTarget(accountId, { tab: 'access-projects', project: projectId }), children: [t('changeCeiling'), _jsx(ArrowUpRightIcon, { className: "size-3.5 shrink-0" })] }) })) : null] }) }), _jsx(SettingRow, { label: t('humanOnly'), help: t('humanOnlyHelp'), children: _jsx("span", { className: "text-foreground block text-sm sm:text-right", children: t('humanOnlyValue') }) }), _jsxs(SettingBlock, { label: t('effective'), help: t('effectiveHelp'), children: [_jsx("ul", { className: "flex flex-wrap gap-1", "data-testid": "agent-effective-permissions", children: authority.effective.map((permission) => (_jsx("li", { children: _jsx(Badge, { variant: "outline", size: "xs", className: "font-mono", children: permission }) }, permission))) }), authority.outsideCeiling.length > 0 ? (_jsx(DroppedList, { label: t('outsideCeiling'), permissions: authority.outsideCeiling, testId: "agent-outside-ceiling" })) : null, authority.humanOnly.length > 0 ? (_jsx(DroppedList, { label: t('droppedHumanOnly'), permissions: authority.humanOnly, testId: "agent-dropped-human-only" })) : null] })] }) }));
41
+ return (_jsx("div", { "data-testid": "agent-authority", children: _jsxs(EditorSection, { title: t('title'), description: t('description'), children: [_jsx(SettingRow, { label: t('kortixPermissions'), help: t('kortixPermissionsHelp', { agent: agentName }), children: _jsx("span", { className: "text-foreground block text-sm sm:text-right", children: declaredLabel }) }), _jsx(SettingRow, { label: t('ceiling'), help: t('ceilingHelp'), children: _jsxs("div", { className: "flex flex-wrap items-center gap-1.5 sm:justify-end", "data-testid": "agent-ceiling", children: [ceiling.kind === 'loading' ? (_jsx(Skeleton, { className: "h-5 w-32" })) : ceiling.kind === 'hidden' ? (_jsx("span", { className: "text-muted-foreground text-sm", children: t('ceilingHidden') })) : ceiling.kind === 'default' ? (_jsx("span", { className: "text-foreground text-sm", children: t('ceilingDefault') })) : (ceiling.roleKeys.map((key) => (_jsx(Badge, { variant: "outline", size: "sm", className: "capitalize", children: key }, key)))), accountId && ceiling.kind !== 'hidden' && ceiling.kind !== 'loading' ? (_jsx(Button, { asChild: true, variant: "secondary", size: "sm", children: _jsx(HubLink, { to: hubTarget(accountId, { tab: 'access-projects', project: projectId }), children: t('changeCeiling') }) })) : null] }) }), _jsx(SettingRow, { label: t('humanOnly'), help: t('humanOnlyHelp'), children: _jsx("span", { className: "text-foreground block text-sm sm:text-right", children: t('humanOnlyValue') }) }), _jsxs(SettingBlock, { label: t('effective'), help: t('effectiveHelp'), children: [_jsx("ul", { className: "flex flex-wrap gap-1", "data-testid": "agent-effective-permissions", children: authority.effective.map((permission) => (_jsx("li", { children: _jsx(Badge, { variant: "outline", size: "xs", className: "font-mono", children: permission }) }, permission))) }), authority.outsideCeiling.length > 0 ? (_jsx(DroppedList, { label: t('outsideCeiling'), permissions: authority.outsideCeiling, testId: "agent-outside-ceiling" })) : null, authority.humanOnly.length > 0 ? (_jsx(DroppedList, { label: t('droppedHumanOnly'), permissions: authority.humanOnly, testId: "agent-dropped-human-only" })) : null] })] }) }));
43
42
  }
44
43
  function DroppedList({ label, permissions, testId, }) {
45
44
  return (_jsxs("div", { className: "space-y-1.5", "data-testid": testId, children: [_jsx("p", { className: "text-muted-foreground text-xs", children: label }), _jsx("ul", { className: "flex flex-wrap gap-1", children: permissions.map((permission) => (_jsx("li", { children: _jsx(Badge, { variant: "muted", size: "xs", className: "font-mono line-through", children: permission }) }, permission))) })] }));
@@ -0,0 +1,17 @@
1
+ import type { ReactNode } from 'react';
2
+ /**
3
+ * An Agents-page card led by the agent's pixel-mark cover. The cover gives
4
+ * every agent a face that is the same wherever the agent appears, so a long
5
+ * list is scanned by shape and tone before names are read.
6
+ */
7
+ export declare function AgentCoverCard({ slug, href, onIntent, title, description, badges, meta, coverLabel, }: {
8
+ slug: string;
9
+ href: string;
10
+ onIntent?: () => void;
11
+ title: ReactNode;
12
+ description?: string | null;
13
+ badges?: ReactNode;
14
+ meta?: ReactNode;
15
+ /** A short pill on the cover's top-left corner — "Default". */
16
+ coverLabel?: string;
17
+ }): import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { UiLink as Link } from '../../providers/ui-provider.js';
4
+ import { AgentMarkGlyph, agentMarkVars } from '../../internal/components/agent-mark.js';
5
+ import { cn } from '../../internal/lib/utils.js';
6
+ /**
7
+ * An Agents-page card led by the agent's pixel-mark cover. The cover gives
8
+ * every agent a face that is the same wherever the agent appears, so a long
9
+ * list is scanned by shape and tone before names are read.
10
+ */
11
+ export function AgentCoverCard({ slug, href, onIntent, title, description, badges, meta, coverLabel, }) {
12
+ return (_jsxs(Link, { href: href, prefetch: true, onPointerEnter: onIntent, onFocus: onIntent, style: agentMarkVars(slug), className: cn('group bg-card border-border/60 flex flex-col overflow-hidden rounded-md border text-left', 'hover:border-border transition-[border-color] duration-(--duration-normal) ease-out', 'focus-visible:ring-ring/50 focus-visible:ring-2 focus-visible:outline-none'), children: [_jsxs("span", { className: "relative flex h-31 items-center justify-center bg-(--mark-tile)", children: [_jsx(AgentMarkGlyph, { slug: slug, size: 56 }), coverLabel ? (_jsx("span", { className: "bg-background/20 absolute top-2.5 left-2.5 rounded-sm px-2 py-0.5 text-xs font-medium text-(--mark-ink) backdrop-blur-sm", children: coverLabel })) : null] }), _jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-1 px-3.5 pt-3 pb-3.5", children: [_jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "text-foreground truncate text-sm font-medium", children: title }), badges] }), description ? (_jsx("span", { className: "text-muted-foreground line-clamp-2 text-xs text-pretty", children: description })) : null, meta ? (_jsx("span", { className: "text-muted-foreground mt-auto flex flex-wrap items-center gap-x-2 gap-y-0.5 pt-1.5 text-xs", children: meta })) : null] })] }));
13
+ }
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useTranslations as useI18nTranslations } from '../../i18n/index.js';
4
4
  /**
5
5
  * The v1 fallback cards of the agent page, plus the scope-checklist helpers
@@ -19,10 +19,10 @@ import { useTranslations as useI18nTranslations } from '../../i18n/index.js';
19
19
  */
20
20
  import { Badge } from '../../primitives/badge.js';
21
21
  import { Button } from '../../primitives/button.js';
22
- import { Label } from '../../primitives/label.js';
23
22
  import Loading from '../../primitives/loading.js';
24
23
  import { errorToast, successToast } from '../../primitives/toast.js';
25
24
  import { ModelSelector } from '../../session/models/model-selector.js';
25
+ import { EditorSection, SettingRow } from '../sections/view/agent-editor-primitives.js';
26
26
  import { PROJECT_ACTIONS } from '../../internal/lib/project-actions.js';
27
27
  import { useProjectCan } from '../../internal/lib/use-project-can.js';
28
28
  import { cn } from '../../internal/lib/utils.js';
@@ -51,7 +51,11 @@ export function AgentModel({ projectId, agentName }) {
51
51
  ? (models.find((x) => x.providerID === m.providerID && x.modelID === m.modelID)?.modelName ??
52
52
  `${m.providerID}/${m.modelID}`)
53
53
  : null;
54
- return (_jsxs("div", { className: "bg-popover space-y-3 rounded-md border px-4 py-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Label, { children: tI18nComplete.raw('text5e2c614c23f0') }), explicit ? (_jsx(Badge, { variant: "muted", size: "xs", children: tI18nComplete.raw('textf20c87946555') })) : null] }), canManage ? (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(ModelSelector, { models: models, providers: providers, selectedModel: explicit, onSelect: (m) => {
54
+ const help = explicit ? (_jsxs(_Fragment, { children: [tI18nComplete.raw('text40124896532d'), ' ', _jsx("span", { className: "text-foreground font-medium", children: nameOf(explicit) }), "."] })) : (_jsxs(_Fragment, { children: [tI18nComplete.raw('text46e3bd02feee'), resolved ? (_jsxs(_Fragment, { children: [' ', "(", _jsx("span", { className: "text-foreground font-medium", children: nameOf(resolved) }), ")"] })) : null, tI18nComplete.raw('textd5bf3036405d')] }));
55
+ return (_jsx(EditorSection, { title: tI18nComplete.raw('text5e2c614c23f0'), description: help, lead: true, children: _jsx(SettingRow, { label: tI18nComplete.raw('text34455bf1d857'), children: canManage ? (_jsxs("div", { className: "flex items-center gap-2 sm:justify-end", children: [explicit ? (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => {
56
+ void defaults.clearAgentDefault(agentName);
57
+ successToast(tI18nComplete('text86781ce4c5b4', { value0: agentName }));
58
+ }, children: tI18nComplete.raw('textbc5b45ae7b60') })) : null, _jsx(ModelSelector, { models: models, providers: providers, selectedModel: explicit, onSelect: (m) => {
55
59
  if (m) {
56
60
  void defaults.setAgentDefault(agentName, m);
57
61
  successToast(`${agentName} → ${nameOf(m)}`);
@@ -59,10 +63,7 @@ export function AgentModel({ projectId, agentName }) {
59
63
  else {
60
64
  void defaults.clearAgentDefault(agentName);
61
65
  }
62
- } }), explicit ? (_jsx(Button, { variant: "ghost", size: "sm", className: "h-7 px-2 text-xs", onClick: () => {
63
- void defaults.clearAgentDefault(agentName);
64
- successToast(tI18nComplete('text86781ce4c5b4', { value0: agentName }));
65
- }, children: tI18nComplete.raw('textbc5b45ae7b60') })) : null] })) : (_jsx(Badge, { variant: "outline", size: "sm", className: "font-mono", children: nameOf(resolved) ?? tI18nComplete.raw('text1f0a5246c481') })), _jsx("p", { className: "text-muted-foreground text-xs leading-relaxed text-pretty", children: explicit ? (_jsxs(_Fragment, { children: [tI18nComplete.raw('text40124896532d'), ' ', _jsx("span", { className: "text-foreground font-medium", children: nameOf(explicit) }), "."] })) : (_jsxs(_Fragment, { children: [tI18nComplete.raw('text46e3bd02feee'), resolved ? (_jsxs(_Fragment, { children: [' ', "(", _jsx("span", { className: "text-foreground font-medium", children: nameOf(resolved) }), ")"] })) : null, tI18nComplete.raw('textd5bf3036405d')] })) })] }));
66
+ } })] })) : (_jsx("span", { className: "text-foreground block truncate text-sm sm:text-right", children: nameOf(resolved) ?? tI18nComplete.raw('text1f0a5246c481') })) }) }));
66
67
  }
67
68
  /**
68
69
  * An agent's manifest allowlist (`agents:` in kortix.yaml, or the legacy
@@ -125,13 +126,13 @@ function AgentScopeCard({ projectId, agentName, scope, }) {
125
126
  });
126
127
  // Non-managers get the read-only mirror (the old presentation).
127
128
  if (!canManage) {
128
- return (_jsxs("div", { className: "bg-popover space-y-3 rounded-md border px-4 py-4", children: [_jsx(Label, { children: tI18nComplete.raw('textec5ba0abb717') }), _jsxs("dl", { className: "space-y-2", children: [_jsx(ScopeRow, { label: tI18nComplete.raw('textd8707d411d99'), value: scope.env }), _jsx(ScopeRow, { label: tI18nComplete.raw('textc3d2e79ebdd0'), value: scope.connectors }), _jsx(ScopeRow, { label: tI18nComplete.raw('text8f12a6e05e6d'), value: scope.kortix_permissions ?? scope.kortix_cli })] }), _jsx("p", { className: "text-muted-foreground text-xs leading-relaxed text-pretty", children: tI18nComplete.raw('textac4c418e412f') })] }));
129
+ return (_jsx(EditorSection, { title: tI18nComplete.raw('textec5ba0abb717'), bare: true, children: _jsxs("div", { className: "bg-popover space-y-3 rounded-md border px-4 py-4", children: [_jsxs("dl", { className: "space-y-2", children: [_jsx(ScopeRow, { label: tI18nComplete.raw('textd8707d411d99'), value: scope.env }), _jsx(ScopeRow, { label: tI18nComplete.raw('textc3d2e79ebdd0'), value: scope.connectors }), _jsx(ScopeRow, { label: tI18nComplete.raw('text8f12a6e05e6d'), value: scope.kortix_permissions ?? scope.kortix_cli })] }), _jsx("p", { className: "text-muted-foreground text-xs leading-relaxed text-pretty", children: tI18nComplete.raw('textac4c418e412f') })] }) }));
129
130
  }
130
- return (_jsxs("div", { className: "bg-popover space-y-4 rounded-md border px-4 py-4", children: [_jsx(Label, { children: tI18nComplete.raw('textec5ba0abb717') }), _jsx(ScopeEditor, { label: tI18nComplete.raw('textd8707d411d99'), allLabel: tI18nComplete.raw('text922315177668'), emptyLabel: tI18nComplete.raw('text82a37b8c2266'), value: env, options: secretOptions, match: "case-insensitive", onChange: setEnv }, `env-${editorNonce}`), _jsx(ScopeEditor, { label: tI18nComplete.raw('textc3d2e79ebdd0'), allLabel: tI18nComplete.raw('texta2e8754e4899'), emptyLabel: tI18nComplete.raw('text389fb5217392'), value: connectors, options: connectorOptions, onChange: setConnectors }, `connectors-${editorNonce}`), _jsx("dl", { children: _jsx(ScopeRow, { label: tI18nComplete.raw('text8f12a6e05e6d'), value: scope.kortix_permissions ?? scope.kortix_cli }) }), dirty ? (_jsxs("div", { className: "border-border/50 flex items-center justify-end gap-2 border-t pt-3", children: [_jsx(Button, { variant: "ghost", size: "sm", disabled: save.isPending, onClick: () => {
131
- setEnv(scope.env);
132
- setConnectors(scope.connectors);
133
- setEditorNonce((n) => n + 1);
134
- }, children: tI18nComplete.raw('textdaee7606b339') }), _jsxs(Button, { size: "sm", disabled: save.isPending, onClick: () => save.mutate(), children: [save.isPending && _jsx(Loading, { className: "size-3.5 shrink-0" }), tI18nComplete.raw('text1509f561f241')] })] })) : null] }));
131
+ return (_jsx(EditorSection, { title: tI18nComplete.raw('textec5ba0abb717'), bare: true, children: _jsxs("div", { className: "bg-popover space-y-4 rounded-md border px-4 py-4", children: [_jsx(ScopeEditor, { label: tI18nComplete.raw('textd8707d411d99'), allLabel: tI18nComplete.raw('text922315177668'), emptyLabel: tI18nComplete.raw('text82a37b8c2266'), value: env, options: secretOptions, match: "case-insensitive", onChange: setEnv }, `env-${editorNonce}`), _jsx(ScopeEditor, { label: tI18nComplete.raw('textc3d2e79ebdd0'), allLabel: tI18nComplete.raw('texta2e8754e4899'), emptyLabel: tI18nComplete.raw('text389fb5217392'), value: connectors, options: connectorOptions, onChange: setConnectors }, `connectors-${editorNonce}`), _jsx("dl", { children: _jsx(ScopeRow, { label: tI18nComplete.raw('text8f12a6e05e6d'), value: scope.kortix_permissions ?? scope.kortix_cli }) }), dirty ? (_jsxs("div", { className: "border-border/50 flex items-center justify-end gap-2 border-t pt-3", children: [_jsx(Button, { variant: "ghost", size: "sm", disabled: save.isPending, onClick: () => {
132
+ setEnv(scope.env);
133
+ setConnectors(scope.connectors);
134
+ setEditorNonce((n) => n + 1);
135
+ }, children: tI18nComplete.raw('textdaee7606b339') }), _jsxs(Button, { size: "sm", disabled: save.isPending, onClick: () => save.mutate(), children: [save.isPending && _jsx(Loading, { className: "size-3.5 shrink-0" }), tI18nComplete.raw('text1509f561f241')] })] })) : null] }) }));
135
136
  }
136
137
  /**
137
138
  * The Secrets checklist, built from the project's secrets.
@@ -233,7 +234,7 @@ function ScopeEditor({ label, allLabel, emptyLabel, value, options, match = 'exa
233
234
  return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("span", { className: "text-muted-foreground text-xs", children: label }), _jsx("div", { className: "border-border/70 inline-flex shrink-0 overflow-hidden rounded-md border", children: ['all', 'specific', 'none'].map((m) => (_jsx("button", { type: "button", onClick: () => pick(m), className: cn('px-2.5 py-1 text-xs capitalize', 'transition-[color,background-color] active:scale-[0.96]', mode === m
234
235
  ? 'bg-secondary text-foreground font-medium'
235
236
  : 'text-muted-foreground hover:bg-muted/50'), children: m }, m))) })] }), mode === 'all' ? (_jsx("p", { className: "text-muted-foreground text-xs text-pretty", children: allLabel })) : null, mode === 'specific' &&
236
- (rows.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-xs", children: emptyLabel })) : (_jsx("div", { className: "border-border/60 max-h-44 overflow-y-auto rounded-md border p-1", children: rows.map((o) => (_jsxs("button", { type: "button", "aria-pressed": o.selected, onClick: () => toggle(o.id), className: cn('flex w-full items-center gap-2 rounded px-2 py-1 text-left text-xs transition-colors', o.selected ? 'bg-secondary' : 'hover:bg-muted/50'), children: [_jsx("span", { className: cn('flex size-4 shrink-0 items-center justify-center rounded border', o.selected
237
+ (rows.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-xs", children: emptyLabel })) : (_jsx("div", { className: "border-border/60 max-h-44 overflow-y-auto rounded-md border p-1", children: rows.map((o) => (_jsxs("button", { type: "button", "aria-pressed": o.selected, onClick: () => toggle(o.id), className: cn('flex w-full items-center gap-2 rounded-sm px-2 py-1 text-left text-xs transition-colors', o.selected ? 'bg-secondary' : 'hover:bg-muted/50'), children: [_jsx("span", { className: cn('flex size-4 shrink-0 items-center justify-center rounded-sm border', o.selected
237
238
  ? 'border-foreground bg-foreground text-background'
238
239
  : 'border-border/70'), children: o.selected && _jsx(Check, { className: "size-3" }) }), _jsx("span", { className: "min-w-0 flex-1 truncate font-mono", children: o.label }), o.hint ? (_jsx("span", { className: "text-muted-foreground max-w-[40%] shrink-0 truncate font-mono", children: o.hint })) : null, o.orphan && (_jsx("span", { className: "text-kortix-orange", children: tI18nComplete.raw('textffa63583dfa6') }))] }, o.id))) })))] }));
239
240
  }
@@ -3,14 +3,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { uiRoutes } from '../../lib/routes.js';
4
4
  import { EmptyState } from '../../primitives/empty-state.js';
5
5
  import { ErrorState } from '../../primitives/error-state.js';
6
- import { EditorSection } from '../sections/view/agent-editor-primitives.js';
6
+ import { EditorList, EditorSection } from '../sections/view/agent-editor-primitives.js';
7
7
  import { Button } from '../../primitives/button.js';
8
8
  import { Skeleton } from '../../primitives/skeleton.js';
9
9
  import { Switch } from '../../primitives/switch.js';
10
10
  import { errorToast } from '../../primitives/toast.js';
11
11
  import { useDriveFolder, useFolderAccess, useProjectDrive, useShareFolder, useUnshareFolder, } from '../../files/drives/use-drives.js';
12
12
  import { useTranslations } from '../../i18n/index.js';
13
- import { HardDriveIcon } from '@phosphor-icons/react';
14
13
  import { UiLink as Link } from '../../providers/ui-provider.js';
15
14
  /**
16
15
  * Which shared drives an agent gets, on the agent's page, beside Apps and
@@ -27,7 +26,7 @@ export function AgentDrivesSection({ projectId, agentName }) {
27
26
  const drives = (root.data?.entries ?? []).filter((e) => e.type === 'dir' && !/^\/?Users(\/|$)/.test(e.path));
28
27
  let body;
29
28
  if (drive.isLoading || root.isLoading) {
30
- body = (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-13 w-full rounded-md" }), _jsx(Skeleton, { className: "h-13 w-full rounded-md" })] }));
29
+ body = (_jsx(EditorList, { children: [0, 1].map((i) => (_jsx("li", { className: "flex items-center gap-3 px-4 py-3", children: _jsx(Skeleton, { className: "h-4 w-40 rounded-sm" }) }, i))) }));
31
30
  }
32
31
  else if (drive.isError || root.isError || !drive.data) {
33
32
  body = (_jsx(ErrorState, { size: "sm", title: t('loadError'), action: _jsx(Button, { variant: "outline", size: "sm", onClick: () => void root.refetch(), children: t('retry') }) }));
@@ -36,9 +35,9 @@ export function AgentDrivesSection({ projectId, agentName }) {
36
35
  body = _jsx(EmptyState, { size: "sm", title: t('noCompanyDrives') });
37
36
  }
38
37
  else {
39
- body = (_jsx("ul", { className: "space-y-2", children: drives.map((entry) => (_jsx(AgentDriveRow, { driveId: drive.data.driveId, entry: entry, agentName: agentName }, entry.path))) }));
38
+ body = (_jsx(EditorList, { children: drives.map((entry) => (_jsx(AgentDriveRow, { driveId: drive.data.driveId, entry: entry, agentName: agentName }, entry.path))) }));
40
39
  }
41
- return (_jsx(EditorSection, { title: t('customizeTitle'), description: t('accessAgentsCompanyHint'), trailing: driveHref ? (_jsx(Button, { asChild: true, variant: "ghost", size: "sm", children: _jsx(Link, { href: driveHref, children: t('driveLabel') }) })) : undefined, children: _jsx("div", { className: "py-4", children: body }) }));
40
+ return (_jsx(EditorSection, { title: t('customizeTitle'), description: t('accessAgentsCompanyHint'), trailing: driveHref ? (_jsx(Button, { asChild: true, variant: "secondary", size: "sm", children: _jsx(Link, { href: driveHref, children: t('driveLabel') }) })) : undefined, lead: true, bare: true, children: body }));
42
41
  }
43
42
  function AgentDriveRow({ driveId, entry, agentName }) {
44
43
  const t = useTranslations('drives');
@@ -63,5 +62,5 @@ function AgentDriveRow({ driveId, entry, agentName }) {
63
62
  errorToast(t('shareFailed'));
64
63
  }
65
64
  };
66
- return (_jsxs("li", { className: "bg-popover flex items-center gap-3 rounded-md border px-4 py-2", children: [_jsx("span", { className: "bg-muted text-muted-foreground flex size-9 shrink-0 items-center justify-center rounded-md", children: _jsx(HardDriveIcon, { className: "size-4" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-foreground truncate text-sm font-medium", children: entry.name }), _jsx("p", { className: "text-muted-foreground truncate font-mono text-xs", children: inherited ? t('everyoneInProject') : `/drives${path}` })] }), access.isLoading ? (_jsx(Skeleton, { className: "h-5 w-9 rounded-full" })) : (_jsx(Switch, { checked: !!own || inherited, disabled: !canManage || inherited || pending, onCheckedChange: (v) => void toggle(v), "aria-label": t('levelFor', { name: entry.name }) }))] }));
65
+ return (_jsxs("li", { className: "flex items-center gap-3 px-4 py-2.5", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-foreground truncate text-sm font-medium", children: entry.name }), _jsx("p", { className: "text-muted-foreground truncate font-mono text-xs", children: inherited ? t('everyoneInProject') : `/drives${path}` })] }), access.isLoading ? (_jsx(Skeleton, { className: "h-5 w-9 rounded-full" })) : (_jsx(Switch, { checked: !!own || inherited, disabled: !canManage || inherited || pending, onCheckedChange: (v) => void toggle(v), "aria-label": t('levelFor', { name: entry.name }) }))] }));
67
66
  }
@@ -3,75 +3,79 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
3
3
  import { uiRoutes } from '../../lib/routes.js';
4
4
  import { useTranslations as useI18nTranslations } from '../../i18n/index.js';
5
5
  /**
6
- * The agent editor's Access pages — Skills, Connectors, Secrets — built from
7
- * the SAME cards and the SAME modals as the project-wide tabs of the same
8
- * name (Marko, 2026-09-03: "hardcore reuse same components as much as
9
- * possible & same modals/dialogues"). A card here is a `CatalogCard` in
10
- * select mode: its checkbox says whether the agent is granted the thing, its
11
- * body opens the thing itself — `EntityDetailModal` for a skill,
12
- * `ConnectorModal` for a connector, `ProjectSecretDialog` for a secret.
6
+ * The agent editor's Access pages — Skills, Connectors, Secrets, Apps. Each
7
+ * opens the SAME modal as the project-wide tab of the same name:
8
+ * `EntityDetailModal` for a skill, `ConnectorModal` for a connector,
9
+ * `ProjectSecretDialog` for a secret.
13
10
  *
14
- * The grant itself is still the All · Pick · None machine from
15
- * `grant-mode-field.tsx`; the catalog stays on screen in every mode so All
16
- * reads as "every card checked" and None as "every card unchecked".
11
+ * Layout is the Settings page's: the pane title, one All · Pick · None row
12
+ * (`grant-mode-field.tsx`), then the catalog as one bordered list. The list
13
+ * stays on screen in every mode, so a person can still open any entry.
17
14
  */
18
15
  import { Badge } from '../../primitives/badge.js';
19
16
  import { Button } from '../../primitives/button.js';
17
+ import { Checkbox } from '../../primitives/checkbox.js';
18
+ import { Skeleton } from '../../primitives/skeleton.js';
20
19
  import { EmptyState } from '../../primitives/empty-state.js';
21
20
  import { ErrorState } from '../../primitives/error-state.js';
22
21
  import { connectorDisplayName } from '../connectors/connector-filter.js';
23
- import { ConnectorAppIcon, ConnectorConnectedMark, ConnectorStatusBadge, } from '../connectors/connector-identity.js';
22
+ import { ConnectorAppIcon, ConnectorStatusBadge, } from '../connectors/connector-identity.js';
24
23
  import { ConnectorModal } from '../connectors/detail/connector-modal.js';
25
24
  import { providerLabel } from '../connectors/provider-label.js';
26
- import { CatalogCard } from '../shared/catalog/catalog-card.js';
27
- import { CatalogGridSkeleton } from '../shared/catalog/catalog-grid.js';
28
25
  import { detailSelection } from '../shared/detail-selection.js';
29
26
  import { EntityDetailModal } from '../shared/entity/entity-modal.js';
30
- import { connectorSetupStatus } from '../sections/connector-connection-form.js';
31
- import { GrantHeaderTrailing, RequiredConnectorToggle, } from '../sections/view/agent-editor-access-fields.js';
32
- import { EditorSection } from '../sections/view/agent-editor-primitives.js';
27
+ import { ManageButton, RequiredConnectorToggle, } from '../sections/view/agent-editor-access-fields.js';
28
+ import { EditorList, EditorSection } from '../sections/view/agent-editor-primitives.js';
33
29
  import { GrantModeField } from '../sections/view/grant-mode-field.js';
34
30
  import { buildRows, ProjectSecretDialog, } from '../sections/view/secrets-view.js';
35
31
  import { toArray } from '../shared/utils.js';
36
32
  import { PROJECT_ACTIONS } from '../../internal/lib/project-actions.js';
33
+ import { cn } from '../../internal/lib/utils.js';
37
34
  import { useProjectCan } from '../../internal/lib/use-project-can.js';
38
35
  import { listConnectors, listProjectSecrets, } from '@kortix/sdk';
39
36
  import { contract, qk, useProjectAccountId, useProjectApps, } from '@kortix/sdk/react';
40
- import { GlobeIcon, KeyIcon, PlusIcon, SparkleIcon } from '@phosphor-icons/react';
37
+ import { PlusIcon } from '@phosphor-icons/react';
41
38
  import { useQuery, useQueryClient } from '@tanstack/react-query';
42
39
  import { useState } from 'react';
43
40
  import { appGrantRows } from './agent-apps-grant.js';
44
41
  /**
45
- * All · Pick · None over a grid of selectable cards. `items` is the project's
46
- * catalog; a granted id the catalog no longer has still renders, flagged, so
47
- * a stale grant is visible rather than silently dropped.
42
+ * All · Pick · None over the project's catalog, as one bordered list. The
43
+ * checkbox exists only in Pick mode, where it is the control; in All and
44
+ * None the mode row above already answers for every row. A granted id the
45
+ * catalog no longer has still renders, flagged, so a stale grant is visible
46
+ * rather than silently dropped.
48
47
  */
49
- function GrantCatalog({ value, onChange, allLabel, items, isLoading, isError, onRetry, empty, trailingFor, }) {
48
+ function GrantCatalog({ value, onChange, items, isLoading, isError, onRetry, empty, trailingFor, }) {
50
49
  const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
51
- return (_jsx(GrantModeField, { value: value, onChange: onChange, allLabel: allLabel, noneLabel: tI18nComplete.raw('text765b240c2900'), alwaysRender: true, children: ({ selected, toggle, mode }) => {
52
- if (isLoading)
53
- return _jsx(CatalogGridSkeleton, {});
50
+ return (_jsx(GrantModeField, { value: value, onChange: onChange, alwaysRender: true, children: ({ selected, toggle, mode }) => {
51
+ if (isLoading) {
52
+ return (_jsx(EditorList, { children: [0, 1, 2].map((i) => (_jsx("li", { className: "flex items-center gap-3 px-4 py-3", children: _jsx(Skeleton, { className: "h-4 w-40 rounded-sm" }) }, i))) }));
53
+ }
54
54
  if (isError) {
55
55
  return (_jsx(ErrorState, { size: "sm", title: tI18nComplete.raw('text3f2d97c61a7e'), action: onRetry ? (_jsx(Button, { variant: "outline", size: "sm", onClick: onRetry, children: tI18nComplete.raw('text942087cc2d41') })) : undefined }));
56
56
  }
57
57
  const known = new Set(items.map((i) => i.id));
58
- const orphans = [...selected].filter((id) => !known.has(id));
58
+ const orphans = [...selected]
59
+ .filter((id) => !known.has(id))
60
+ .map((id) => ({
61
+ id,
62
+ title: _jsx("span", { className: "font-mono", children: id }),
63
+ description: tI18nComplete.raw('textfecc36d012be'),
64
+ status: (_jsx(Badge, { variant: "destructive", size: "xs", children: tI18nComplete.raw('text6be36ca49ee8') })),
65
+ }));
59
66
  if (items.length === 0 && orphans.length === 0)
60
67
  return _jsx(_Fragment, { children: empty });
61
- const checked = (id) => mode === 'all' || (mode === 'pick' && selected.has(id));
62
- return (_jsxs("div", { className: "grid gap-2", children: [items.map((item) => (_jsx(CatalogCard, { ...item.card, trailing: _jsxs("span", { className: "flex items-center gap-1.5", children: [item.card.trailing, mode === 'pick' && selected.has(item.id) ? trailingFor?.(item.id) : null] }), onClick: item.onOpen, select: {
63
- checked: checked(item.id),
64
- onCheckedChange: () => toggle(item.id),
65
- disabled: mode !== 'pick',
66
- label: tI18nComplete('textd4170e74c3b0', { value0: item.id }),
67
- } }, item.id))), orphans.map((id) => (_jsx(CatalogCard, { title: _jsx("span", { className: "font-mono", children: id }), description: tI18nComplete.raw('textfecc36d012be'), badges: _jsx(Badge, { variant: "destructive", size: "xs", children: tI18nComplete.raw('text6be36ca49ee8') }), select: {
68
- checked: true,
69
- onCheckedChange: () => toggle(id),
70
- disabled: mode !== 'pick',
71
- label: tI18nComplete('textd4170e74c3b0', { value0: id }),
72
- } }, id)))] }));
68
+ return (_jsx(EditorList, { children: [...items, ...orphans].map((item) => (_jsx(GrantRow, { item: item, mode: mode, checked: selected.has(item.id), onToggle: () => toggle(item.id), checkLabel: tI18nComplete('textd4170e74c3b0', { value0: item.id }), trailing: mode === 'pick' && selected.has(item.id) ? trailingFor?.(item.id) : null }, item.id))) }));
73
69
  } }));
74
70
  }
71
+ /**
72
+ * One catalog row. The checkbox and the body are two sibling controls, never
73
+ * one nested in the other: the checkbox grants, the body opens the entity.
74
+ */
75
+ function GrantRow({ item, mode, checked, onToggle, checkLabel, trailing, }) {
76
+ const body = (_jsxs(_Fragment, { children: [item.leading, _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "text-foreground block truncate text-sm font-medium", children: item.title }), item.description ? (_jsx("span", { className: "text-muted-foreground block truncate text-xs", children: item.description })) : null] })] }));
77
+ return (_jsxs("li", { className: cn('flex min-h-13 items-center gap-3 px-4 py-2 transition-colors duration-fast', item.onOpen && 'hover:bg-hover', mode === 'none' && 'opacity-60'), children: [mode === 'pick' ? (_jsx(Checkbox, { variant: "black", checked: checked, onCheckedChange: onToggle, "aria-label": checkLabel })) : null, item.onOpen ? (_jsx("button", { type: "button", onClick: item.onOpen, className: "focus-visible:ring-ring/50 flex min-w-0 flex-1 items-center gap-3 rounded-sm text-left focus-visible:ring-2 focus-visible:outline-none", children: body })) : (_jsx("span", { className: "flex min-w-0 flex-1 items-center gap-3", children: body })), item.status || trailing ? (_jsxs("span", { className: "flex shrink-0 items-center gap-2", children: [item.status, trailing] })) : null] }));
78
+ }
75
79
  // ─── Skills ──────────────────────────────────────────────────────────────
76
80
  export function SkillsGrantPage({ projectId, config, editor, }) {
77
81
  const tI18nComplete = useI18nTranslations('hardcodedUi.i18nComplete');
@@ -82,11 +86,12 @@ export function SkillsGrantPage({ projectId, config, editor, }) {
82
86
  record: skills.find((skill) => skill.path === selectedPath),
83
87
  isSuccess: true,
84
88
  });
85
- return (_jsxs(_Fragment, { children: [_jsx(EditorSection, { title: tI18nComplete.raw('text66d0f523a379'), description: tI18nComplete.raw('text836cd8e8a791'), trailing: _jsx(GrantHeaderTrailing, { value: editor.draft.skills, tab: "skills", label: tI18nComplete.raw('text78abcbbfb830') }), children: _jsx("div", { className: "py-4", children: _jsx(GrantCatalog, { value: editor.draft.skills, onChange: (v) => editor.set('skills', v), allLabel: tI18nComplete.raw('textf059c2dbe260'), items: skills.map((skill) => ({
86
- id: skill.name,
87
- card: { title: skill.name, description: skill.description },
88
- onOpen: () => setSelectedPath(skill.path),
89
- })), empty: _jsx(EmptyState, { icon: SparkleIcon, size: "sm", title: tI18nComplete.raw('text32ae9b80f832'), description: tI18nComplete.raw('text62d220bbfcc8') }) }) }) }), _jsx(EntityDetailModal, { projectId: projectId, entity: detail.record, kind: "skill", open: detail.open, isResolving: detail.isResolving, onOpenChange: (next) => {
89
+ return (_jsxs(_Fragment, { children: [_jsx(EditorSection, { title: tI18nComplete.raw('text66d0f523a379'), description: tI18nComplete.raw('text836cd8e8a791'), trailing: _jsx(ManageButton, { tab: "skills" }), lead: true, bare: true, children: _jsx(GrantCatalog, { value: editor.draft.skills, onChange: (v) => editor.set('skills', v), items: skills.map((skill) => ({
90
+ id: skill.name,
91
+ title: skill.name,
92
+ description: skill.description,
93
+ onOpen: () => setSelectedPath(skill.path),
94
+ })), empty: _jsx(EmptyState, { size: "sm", title: tI18nComplete.raw('text32ae9b80f832'), description: tI18nComplete.raw('text62d220bbfcc8') }) }) }), _jsx(EntityDetailModal, { projectId: projectId, entity: detail.record, kind: "skill", open: detail.open, isResolving: detail.isResolving, onOpenChange: (next) => {
90
95
  if (!next)
91
96
  setSelectedPath(null);
92
97
  } })] }));
@@ -114,7 +119,7 @@ export function ConnectorsGrantPage({ projectId, editor, }) {
114
119
  setDetailSlug(null);
115
120
  const invalidate = () => queryClient.invalidateQueries({ queryKey: qk.project.connectors(projectId) });
116
121
  const { draft, set } = editor;
117
- return (_jsxs(_Fragment, { children: [_jsx(EditorSection, { title: tI18nComplete.raw('textc3d2e79ebdd0'), description: tI18nComplete.raw('textc174fc077197'), trailing: _jsx(GrantHeaderTrailing, { value: draft.connectors, tab: "connectors", label: tI18nComplete.raw('textd83250185d41') }), children: _jsxs("div", { className: "space-y-2 py-4", children: [_jsx(GrantCatalog, { value: draft.connectors, onChange: (v) => {
122
+ return (_jsxs(_Fragment, { children: [_jsx(EditorSection, { title: tI18nComplete.raw('textc3d2e79ebdd0'), description: tI18nComplete.raw('textc174fc077197'), trailing: _jsx(ManageButton, { tab: "connectors" }), lead: true, bare: true, children: _jsxs("div", { className: "space-y-3", children: [_jsx(GrantCatalog, { value: draft.connectors, onChange: (v) => {
118
123
  set('connectors', v);
119
124
  const required = draft.connectors_required ?? [];
120
125
  const picked = new Set(Array.isArray(v) ? v : []);
@@ -124,15 +129,12 @@ export function ConnectorsGrantPage({ projectId, editor, }) {
124
129
  if (kept.length !== required.length) {
125
130
  set('connectors_required', kept.length ? kept : undefined);
126
131
  }
127
- }, allLabel: tI18nComplete.raw('text35e54cea7f41'), isLoading: connectorsQuery.isLoading, isError: connectorsQuery.isError, onRetry: () => void connectorsQuery.refetch(), items: connectors.map((connector) => ({
132
+ }, isLoading: connectorsQuery.isLoading, isError: connectorsQuery.isError, onRetry: () => void connectorsQuery.refetch(), items: connectors.map((connector) => ({
128
133
  id: connector.slug,
129
- card: {
130
- leading: _jsx(ConnectorAppIcon, { connector: connector, size: "lg" }),
131
- title: connectorDisplayName(connector),
132
- description: `${providerLabel(connector.provider)} · ${connector.actions.length} ${connector.actions.length === 1 ? 'action' : 'actions'}`,
133
- badges: _jsx(ConnectorStatusBadge, { connector: connector }),
134
- trailing: connectorSetupStatus(connector) === 'connected' ? (_jsx(ConnectorConnectedMark, {})) : undefined,
135
- },
134
+ leading: _jsx(ConnectorAppIcon, { connector: connector, size: "sm" }),
135
+ title: connectorDisplayName(connector),
136
+ description: `${providerLabel(connector.provider)} · ${connector.actions.length} ${connector.actions.length === 1 ? 'action' : 'actions'}`,
137
+ status: _jsx(ConnectorStatusBadge, { connector: connector }),
136
138
  onOpen: () => setDetailSlug(connector.slug),
137
139
  })), trailingFor: (slug) => (_jsx(RequiredConnectorToggle, { active: draft.connectors_required?.includes(slug) === true, onToggle: () => {
138
140
  const current = draft.connectors_required ?? [];
@@ -160,16 +162,13 @@ export function SecretsGrantPage({ projectId, editor }) {
160
162
  open: false,
161
163
  row: null,
162
164
  });
163
- return (_jsxs(_Fragment, { children: [_jsx(EditorSection, { title: tI18nComplete.raw('textd8707d411d99'), description: tI18nComplete.raw('textbb74cc7dce98'), trailing: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(GrantHeaderTrailing, { value: editor.draft.secrets, tab: "secrets", label: tI18nComplete.raw('text0b38e3daeb93') }), canWrite ? (_jsxs(Button, { variant: "secondary", size: "sm", className: "gap-1.5", onClick: () => setDialog({ open: true, row: null }), children: [_jsx(PlusIcon, { className: "size-4" }), tI18nComplete.raw('text18fdd549b2ed')] })) : null] }), children: _jsx("div", { className: "py-4", children: _jsx(GrantCatalog, { value: editor.draft.secrets, onChange: (v) => editor.set('secrets', v), allLabel: tI18nComplete.raw('text86fdc0c950d5'), isLoading: secretsQuery.isLoading, isError: secretsQuery.isError, onRetry: () => void secretsQuery.refetch(), items: rows.map((row) => ({
164
- id: row.identifier,
165
- card: {
166
- leading: (_jsx("span", { className: "bg-muted text-muted-foreground flex size-9 items-center justify-center rounded-md", children: _jsx(KeyIcon, { className: "size-4" }) })),
167
- title: _jsx("span", { className: "font-mono", children: row.identifier }),
168
- description: row.purpose ?? (row.key !== row.identifier ? `Env var ${row.key}` : null),
169
- badges: (_jsxs(_Fragment, { children: [!row.configured ? (_jsx(Badge, { variant: "destructive", size: "xs", children: tI18nComplete.raw('text4fd0272a5628') })) : null, row.system ? (_jsx(Badge, { variant: "muted", size: "xs", children: tI18nComplete.raw('text6725e7bbcd28') })) : null] })),
170
- },
171
- onOpen: () => setDialog({ open: true, row }),
172
- })), empty: _jsx(EmptyState, { icon: KeyIcon, size: "sm", title: tI18nComplete.raw('text5aa40906d187'), description: tI18nComplete.raw('textfc7605717e58'), action: canWrite ? (_jsxs(Button, { variant: "outline", size: "sm", className: "gap-1.5", onClick: () => setDialog({ open: true, row: null }), children: [_jsx(PlusIcon, { className: "size-4" }), tI18nComplete.raw('text98aa52b34c8f')] })) : undefined }) }) }) }), _jsx(ProjectSecretDialog, { projectId: projectId, row: dialog.row, open: dialog.open, onOpenChange: (open) => setDialog((d) => ({ ...d, open })) })] }));
165
+ return (_jsxs(_Fragment, { children: [_jsx(EditorSection, { title: tI18nComplete.raw('textd8707d411d99'), description: tI18nComplete.raw('textbb74cc7dce98'), trailing: canWrite ? (_jsxs(Button, { variant: "secondary", size: "sm", className: "gap-1.5", onClick: () => setDialog({ open: true, row: null }), children: [_jsx(PlusIcon, { className: "size-4" }), tI18nComplete.raw('text18fdd549b2ed')] })) : undefined, lead: true, bare: true, children: _jsx(GrantCatalog, { value: editor.draft.secrets, onChange: (v) => editor.set('secrets', v), isLoading: secretsQuery.isLoading, isError: secretsQuery.isError, onRetry: () => void secretsQuery.refetch(), items: rows.map((row) => ({
166
+ id: row.identifier,
167
+ title: _jsx("span", { className: "font-mono", children: row.identifier }),
168
+ description: row.purpose ?? (row.key !== row.identifier ? `Env var ${row.key}` : null),
169
+ status: !row.configured || row.system ? (_jsxs(_Fragment, { children: [!row.configured ? (_jsx(Badge, { variant: "destructive", size: "xs", children: tI18nComplete.raw('text4fd0272a5628') })) : null, row.system ? (_jsx(Badge, { variant: "muted", size: "xs", children: tI18nComplete.raw('text6725e7bbcd28') })) : null] })) : undefined,
170
+ onOpen: () => setDialog({ open: true, row }),
171
+ })), empty: _jsx(EmptyState, { size: "sm", title: tI18nComplete.raw('text5aa40906d187'), description: tI18nComplete.raw('textfc7605717e58'), action: canWrite ? (_jsxs(Button, { variant: "outline", size: "sm", className: "gap-1.5", onClick: () => setDialog({ open: true, row: null }), children: [_jsx(PlusIcon, { className: "size-4" }), tI18nComplete.raw('text98aa52b34c8f')] })) : undefined }) }) }), _jsx(ProjectSecretDialog, { projectId: projectId, row: dialog.row, open: dialog.open, onOpenChange: (open) => setDialog((d) => ({ ...d, open })) })] }));
173
172
  }
174
173
  // ─── Apps ────────────────────────────────────────────────────────────────
175
174
  /**
@@ -190,13 +189,12 @@ export function AppsGrantPage({ projectId, editor }) {
190
189
  const t = useI18nTranslations('agentApps');
191
190
  const appsQuery = useProjectApps(projectId);
192
191
  const rows = appGrantRows(appsQuery.data);
193
- return (_jsx(EditorSection, { title: t('title'), description: t('description'), trailing: _jsx(GrantHeaderTrailing, { value: editor.draft.apps, href: (id) => uiRoutes().apps(id), label: t('manageApps') }), children: _jsxs("div", { className: "space-y-3 py-4", "data-testid": "agent-apps-grant", children: [_jsx(GrantCatalog, { value: editor.draft.apps, onChange: (v) => editor.set('apps', v), allLabel: t('allLabel'), isLoading: appsQuery.isLoading, isError: appsQuery.isError, onRetry: () => void appsQuery.refetch(), items: rows.map((row) => ({
194
- id: row.id,
195
- card: {
196
- leading: (_jsx("span", { className: "bg-muted text-muted-foreground flex size-9 items-center justify-center rounded-md", children: _jsx(GlobeIcon, { className: "size-4" }) })),
197
- title: _jsx("span", { className: "font-mono", children: row.id }),
198
- description: row.name === row.id ? null : row.name,
199
- badges: row.needsGrant ? (_jsx(Badge, { variant: "outline", size: "xs", children: t('needsGrant') })) : (_jsx(Badge, { variant: "muted", size: "xs", children: t('openWithoutGrant') })),
200
- },
201
- })), empty: _jsx(EmptyState, { icon: GlobeIcon, size: "sm", title: t('emptyTitle'), description: t('emptyDescription') }) }), _jsx("p", { className: "text-muted-foreground text-xs text-pretty", children: t('grantNote') })] }) }));
192
+ return (_jsx(EditorSection, { title: t('title'), description: t('description'), trailing: _jsx(ManageButton, { href: (id) => uiRoutes().apps(id) }), lead: true, bare: true, children: _jsx("div", { className: "space-y-3", "data-testid": "agent-apps-grant", children: _jsx(GrantCatalog, { value: editor.draft.apps, onChange: (v) => editor.set('apps', v), isLoading: appsQuery.isLoading, isError: appsQuery.isError, onRetry: () => void appsQuery.refetch(), items: rows.map((row) => ({
193
+ id: row.id,
194
+ title: _jsx("span", { className: "font-mono", children: row.id }),
195
+ description: row.name === row.id ? null : row.name,
196
+ // The grant decides admission for a restricted or private App, the
197
+ // normal case. Only an App that admits agents without it is flagged.
198
+ status: row.needsGrant ? undefined : (_jsx(Badge, { variant: "muted", size: "xs", children: t('openWithoutGrant') })),
199
+ })), empty: _jsx(EmptyState, { size: "sm", title: t('emptyTitle'), description: t('emptyDescription') }) }) }) }));
202
200
  }