@agent-native/core 0.126.0 → 0.127.3

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 (181) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +83 -0
  3. package/corpus/core/docs/content/external-agents-catalog.mdx +14 -11
  4. package/corpus/core/docs/content/mcp-protocol.mdx +9 -4
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/a2a/index.ts +2 -0
  7. package/corpus/core/src/agent/production-agent.ts +6 -1
  8. package/corpus/core/src/agent/run-manager.ts +37 -0
  9. package/corpus/core/src/agent/run-store.ts +18 -0
  10. package/corpus/core/src/cli/template-sync.ts +96 -0
  11. package/corpus/core/src/client/MultiTabAssistantChat.tsx +75 -43
  12. package/corpus/core/src/client/agent-chat.ts +19 -1
  13. package/corpus/core/src/client/chat/tool-call-display.tsx +25 -0
  14. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +23 -0
  15. package/corpus/core/src/client/settings/AgentsSection.tsx +565 -71
  16. package/corpus/core/src/client/settings/SecretsSection.tsx +71 -36
  17. package/corpus/core/src/client/use-chat-models.ts +54 -37
  18. package/corpus/core/src/localization/default-messages.ts +11 -0
  19. package/corpus/core/src/mcp/ask-app-task-handle.ts +183 -0
  20. package/corpus/core/src/mcp/builtin-tools.ts +146 -34
  21. package/corpus/core/src/server/agent-capabilities.ts +8 -1
  22. package/corpus/core/src/server/agent-chat-plugin.ts +17 -0
  23. package/corpus/core/src/server/agent-discovery.ts +1 -1
  24. package/corpus/core/src/server/agent-peer-probe.ts +133 -0
  25. package/corpus/core/src/server/builder-design-systems.ts +1 -1
  26. package/corpus/core/src/server/core-routes-plugin.ts +67 -0
  27. package/corpus/core/src/shared/mcp-embed-headers.ts +4 -0
  28. package/corpus/core/src/templates/chat/_gitignore +2 -0
  29. package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +159 -119
  30. package/corpus/templates/chat/_gitignore +2 -0
  31. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +23 -0
  32. package/corpus/templates/design/actions/create-file.ts +15 -0
  33. package/corpus/templates/design/actions/generate-design.ts +40 -0
  34. package/corpus/templates/design/actions/present-design-variants.ts +16 -0
  35. package/corpus/templates/design/app/hooks/use-question-flow.ts +21 -1
  36. package/corpus/templates/design/app/pages/design-editor/save-failure.ts +7 -1
  37. package/corpus/templates/design/changelog/2026-07-28-generated-designs-with-malformed-html-are-now-rejected-at-sa.md +6 -0
  38. package/corpus/templates/design/changelog/2026-07-28-the-model-you-pick-when-starting-a-new-design-is-now-used-fo.md +6 -0
  39. package/corpus/templates/design/server/source-workspace.ts +1 -0
  40. package/corpus/templates/design/shared/html-integrity.ts +608 -18
  41. package/corpus/templates/dispatch/changelog/2026-07-28-pending-apps-are-hidden-by-default-and-app-ownership-details.md +6 -0
  42. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +2 -6
  43. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +5 -1
  44. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +5 -0
  45. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +31 -8
  46. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +15 -0
  47. package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +78 -8
  48. package/corpus/templates/slides/app/components/presentation/PresenterView.tsx +179 -0
  49. package/corpus/templates/slides/app/components/presentation/present-channel.ts +15 -0
  50. package/corpus/templates/slides/app/i18n/en-US.ts +10 -1
  51. package/corpus/templates/slides/app/lib/export-google-slides-client.ts +67 -0
  52. package/corpus/templates/slides/app/lib/export-pptx-client.ts +16 -3
  53. package/corpus/templates/slides/app/pages/DeckEditor.tsx +11 -0
  54. package/corpus/templates/slides/app/pages/Presentation.tsx +11 -3
  55. package/corpus/templates/slides/changelog/2026-07-28-export-to-google-slides-now-creates-the-deck-directly-in-you.md +6 -0
  56. package/corpus/templates/slides/changelog/2026-07-28-opening-a-presentation-link-directly-no-longer-bounces-back-.md +6 -0
  57. package/corpus/templates/slides/changelog/2026-07-28-presenter-view-open-a-second-window-while-presenting-to-see-.md +6 -0
  58. package/corpus/templates/slides/server/routes/api/exports/google-slides.post.ts +90 -0
  59. package/corpus/toolkit/CHANGELOG.md +6 -0
  60. package/corpus/toolkit/package.json +1 -1
  61. package/corpus/toolkit/src/composer/TiptapComposer.tsx +143 -111
  62. package/corpus/toolkit/src/composer/runtime-adapters.tsx +2 -0
  63. package/dist/a2a/index.d.ts +2 -0
  64. package/dist/a2a/index.d.ts.map +1 -1
  65. package/dist/a2a/index.js +1 -0
  66. package/dist/a2a/index.js.map +1 -1
  67. package/dist/agent/production-agent.d.ts +2 -2
  68. package/dist/agent/production-agent.d.ts.map +1 -1
  69. package/dist/agent/production-agent.js +6 -3
  70. package/dist/agent/production-agent.js.map +1 -1
  71. package/dist/agent/run-manager.d.ts +11 -0
  72. package/dist/agent/run-manager.d.ts.map +1 -1
  73. package/dist/agent/run-manager.js +34 -1
  74. package/dist/agent/run-manager.js.map +1 -1
  75. package/dist/agent/run-store.d.ts +8 -0
  76. package/dist/agent/run-store.d.ts.map +1 -1
  77. package/dist/agent/run-store.js +16 -0
  78. package/dist/agent/run-store.js.map +1 -1
  79. package/dist/cli/template-sync.d.ts.map +1 -1
  80. package/dist/cli/template-sync.js +88 -0
  81. package/dist/cli/template-sync.js.map +1 -1
  82. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  83. package/dist/client/MultiTabAssistantChat.js +60 -31
  84. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  85. package/dist/client/agent-chat.d.ts +6 -0
  86. package/dist/client/agent-chat.d.ts.map +1 -1
  87. package/dist/client/agent-chat.js +13 -1
  88. package/dist/client/agent-chat.js.map +1 -1
  89. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  90. package/dist/client/chat/tool-call-display.js +14 -0
  91. package/dist/client/chat/tool-call-display.js.map +1 -1
  92. package/dist/client/resources/mcp-integration-catalog.d.ts +6 -0
  93. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  94. package/dist/client/resources/mcp-integration-catalog.js +16 -0
  95. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  96. package/dist/client/settings/AgentsSection.d.ts.map +1 -1
  97. package/dist/client/settings/AgentsSection.js +250 -30
  98. package/dist/client/settings/AgentsSection.js.map +1 -1
  99. package/dist/client/settings/SecretsSection.d.ts.map +1 -1
  100. package/dist/client/settings/SecretsSection.js +16 -2
  101. package/dist/client/settings/SecretsSection.js.map +1 -1
  102. package/dist/client/use-chat-models.d.ts.map +1 -1
  103. package/dist/client/use-chat-models.js +46 -27
  104. package/dist/client/use-chat-models.js.map +1 -1
  105. package/dist/collab/routes.d.ts +2 -2
  106. package/dist/collab/struct-routes.d.ts +1 -1
  107. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  108. package/dist/localization/default-messages.d.ts +9 -0
  109. package/dist/localization/default-messages.d.ts.map +1 -1
  110. package/dist/localization/default-messages.js +9 -0
  111. package/dist/localization/default-messages.js.map +1 -1
  112. package/dist/mcp/ask-app-task-handle.d.ts +25 -0
  113. package/dist/mcp/ask-app-task-handle.d.ts.map +1 -0
  114. package/dist/mcp/ask-app-task-handle.js +136 -0
  115. package/dist/mcp/ask-app-task-handle.js.map +1 -0
  116. package/dist/mcp/builtin-tools.d.ts +3 -3
  117. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  118. package/dist/mcp/builtin-tools.js +115 -38
  119. package/dist/mcp/builtin-tools.js.map +1 -1
  120. package/dist/observability/routes.d.ts +3 -3
  121. package/dist/progress/routes.d.ts +1 -1
  122. package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
  123. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  124. package/dist/resources/handlers.d.ts +1 -1
  125. package/dist/secrets/routes.d.ts +9 -9
  126. package/dist/server/agent-capabilities.d.ts +7 -1
  127. package/dist/server/agent-capabilities.d.ts.map +1 -1
  128. package/dist/server/agent-capabilities.js +1 -0
  129. package/dist/server/agent-capabilities.js.map +1 -1
  130. package/dist/server/agent-chat-plugin.d.ts +7 -0
  131. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  132. package/dist/server/agent-chat-plugin.js +16 -1
  133. package/dist/server/agent-chat-plugin.js.map +1 -1
  134. package/dist/server/agent-discovery.d.ts +1 -0
  135. package/dist/server/agent-discovery.d.ts.map +1 -1
  136. package/dist/server/agent-discovery.js +1 -1
  137. package/dist/server/agent-discovery.js.map +1 -1
  138. package/dist/server/agent-peer-probe.d.ts +37 -0
  139. package/dist/server/agent-peer-probe.d.ts.map +1 -0
  140. package/dist/server/agent-peer-probe.js +90 -0
  141. package/dist/server/agent-peer-probe.js.map +1 -0
  142. package/dist/server/builder-design-systems.js +1 -1
  143. package/dist/server/builder-design-systems.js.map +1 -1
  144. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  145. package/dist/server/core-routes-plugin.js +53 -0
  146. package/dist/server/core-routes-plugin.js.map +1 -1
  147. package/dist/server/realtime-token.d.ts +1 -1
  148. package/dist/server/transcribe-voice.d.ts +1 -1
  149. package/dist/shared/mcp-embed-headers.d.ts +1 -0
  150. package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
  151. package/dist/shared/mcp-embed-headers.js +4 -0
  152. package/dist/shared/mcp-embed-headers.js.map +1 -1
  153. package/dist/templates/chat/_gitignore +2 -0
  154. package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +159 -119
  155. package/docs/content/external-agents-catalog.mdx +14 -11
  156. package/docs/content/mcp-protocol.mdx +9 -4
  157. package/package.json +2 -2
  158. package/src/a2a/index.ts +2 -0
  159. package/src/agent/production-agent.ts +6 -1
  160. package/src/agent/run-manager.ts +37 -0
  161. package/src/agent/run-store.ts +18 -0
  162. package/src/cli/template-sync.ts +96 -0
  163. package/src/client/MultiTabAssistantChat.tsx +75 -43
  164. package/src/client/agent-chat.ts +19 -1
  165. package/src/client/chat/tool-call-display.tsx +25 -0
  166. package/src/client/resources/mcp-integration-catalog.ts +23 -0
  167. package/src/client/settings/AgentsSection.tsx +565 -71
  168. package/src/client/settings/SecretsSection.tsx +71 -36
  169. package/src/client/use-chat-models.ts +54 -37
  170. package/src/localization/default-messages.ts +11 -0
  171. package/src/mcp/ask-app-task-handle.ts +183 -0
  172. package/src/mcp/builtin-tools.ts +146 -34
  173. package/src/server/agent-capabilities.ts +8 -1
  174. package/src/server/agent-chat-plugin.ts +17 -0
  175. package/src/server/agent-discovery.ts +1 -1
  176. package/src/server/agent-peer-probe.ts +133 -0
  177. package/src/server/builder-design-systems.ts +1 -1
  178. package/src/server/core-routes-plugin.ts +67 -0
  179. package/src/shared/mcp-embed-headers.ts +4 -0
  180. package/src/templates/chat/_gitignore +2 -0
  181. package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +159 -119
@@ -1,9 +1,51 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { IconPlus, IconPencil, IconTrash, IconX } from "@tabler/icons-react";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ButtonBase as ToolkitButtonBase } from "@agent-native/toolkit/ui/button";
3
+ import { IconPlus, IconPencil, IconTrash, IconX, IconCheck, IconLoader2, IconAlertTriangle, IconExternalLink, IconRefresh, } from "@tabler/icons-react";
3
4
  import { useState, useEffect, useRef, useCallback } from "react";
4
- import { getRemoteAgentIdFromPath, isRemoteAgentPath, remoteAgentResourcePath, } from "../../resources/metadata.js";
5
- import { agentNativePath } from "../api-path.js";
5
+ import { buildOpenRoutePath, buildSettingsRoute, STANDARD_SETTINGS_TABS, } from "../../navigation/index.js";
6
+ import { getRemoteAgentIdFromPath, isRemoteAgentPath, REMOTE_AGENT_RESOURCE_PREFIX, remoteAgentResourcePath, } from "../../resources/metadata.js";
7
+ import { agentNativePath, appBasePath } from "../api-path.js";
6
8
  import { Tooltip, TooltipContent, TooltipTrigger, } from "../components/ui/tooltip.js";
9
+ import { useOrg, useSyncA2ASecret } from "../org/hooks.js";
10
+ function describeSkills(publicSkills) {
11
+ if (publicSkills === undefined)
12
+ return null;
13
+ // An empty public skill list only means the card advertises no anonymous-
14
+ // safe actions — the peer still has authenticated reads/writes. Saying so
15
+ // plainly avoids reading as "this agent can't do anything."
16
+ if (publicSkills === 0)
17
+ return "reads require auth";
18
+ return `${publicSkills} public skill${publicSkills === 1 ? "" : "s"}`;
19
+ }
20
+ /** One-line status for the row dot tooltip. */
21
+ function describeProbeTooltip(result) {
22
+ if (!result.reachable) {
23
+ return `Unreachable${result.error ? `: ${result.error}` : ""}`;
24
+ }
25
+ if (result.authorized === false) {
26
+ return "Reachable, but the peer rejected our token — calls will 401 in production";
27
+ }
28
+ if (result.authorized === undefined) {
29
+ return "Reachable; couldn't verify our token";
30
+ }
31
+ return "Reachable and authorized";
32
+ }
33
+ /** Multi-clause status line for the Add popover's Check result. */
34
+ function describeCheckResult(result) {
35
+ if (!result.reachable) {
36
+ return result.error ?? "Not reachable";
37
+ }
38
+ const scheme = result.securitySchemes?.length
39
+ ? result.securitySchemes.join(", ")
40
+ : "no auth scheme advertised";
41
+ const authText = result.authorized === false
42
+ ? "the peer rejected our token — calls will 401 in production"
43
+ : result.authorized === undefined
44
+ ? `couldn't verify our token${result.authError ? ` (${result.authError})` : ""}`
45
+ : "our token works";
46
+ const skills = describeSkills(result.publicSkills);
47
+ return [`Live · ${scheme}`, authText, skills].filter(Boolean).join(" · ");
48
+ }
7
49
  function AgentEditPopover({ agent, onSave, onDelete, onClose, }) {
8
50
  const [name, setName] = useState(agent.name);
9
51
  const [url, setUrl] = useState(agent.url);
@@ -46,14 +88,35 @@ function AgentEditPopover({ agent, onSave, onDelete, onClose, }) {
46
88
  onClose();
47
89
  }, className: "w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent", placeholder: "Description (optional)" }), _jsxs("div", { className: "flex items-center justify-between pt-0.5", children: [_jsxs("button", { onClick: () => onDelete(agent.id), className: "flex items-center gap-1 rounded px-1.5 py-0.5 text-[10px] text-red-400 hover:bg-red-900/20", children: [_jsx(IconTrash, { size: 10 }), "Remove"] }), _jsxs("div", { className: "flex gap-1", children: [_jsx("button", { onClick: onClose, className: "rounded px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground", children: "Cancel" }), _jsx("button", { onClick: handleSave, disabled: !name.trim() || !url.trim(), className: "rounded bg-accent px-2 py-0.5 text-[10px] font-medium text-foreground hover:bg-accent/80 disabled:opacity-40", children: "Save" })] })] })] }) }));
48
90
  }
49
- function AgentAddPopover({ onAdd, onClose, }) {
50
- const [name, setName] = useState("");
51
- const [url, setUrl] = useState("");
52
- const [description, setDescription] = useState("");
91
+ /** Builds an absolute deep link into a peer's own Settings > Agents Add
92
+ * popover, prefilled with THIS app's own name/url/description, via the
93
+ * existing `/_agent-native/open` route's `f_*` filter-forwarding (the only
94
+ * non-reserved params the open route echoes onto the redirect URL instead of
95
+ * only stashing them server-side for `navigate` polling). No new endpoint. */
96
+ function buildPeerRegisterBackLink(peerUrl) {
97
+ const selfUrl = `${window.location.origin}${appBasePath()}`;
98
+ const selfName = document.title.trim() || window.location.hostname;
99
+ const openPath = buildOpenRoutePath({
100
+ view: "settings",
101
+ to: buildSettingsRoute(STANDARD_SETTINGS_TABS.agent),
102
+ params: {
103
+ f_agentName: selfName,
104
+ f_agentUrl: selfUrl,
105
+ },
106
+ });
107
+ return `${peerUrl.replace(/\/+$/, "")}${openPath}`;
108
+ }
109
+ function AgentAddPopover({ initialName = "", initialUrl = "", initialDescription = "", secretSet, syncSecret, onAdd, onClose, }) {
110
+ const [name, setName] = useState(initialName);
111
+ const [url, setUrl] = useState(initialUrl);
112
+ const [description, setDescription] = useState(initialDescription);
113
+ const [check, setCheck] = useState({ status: "idle" });
114
+ const [added, setAdded] = useState(null);
53
115
  const nameRef = useRef(null);
116
+ const urlRef = useRef(null);
54
117
  const popoverRef = useRef(null);
55
118
  useEffect(() => {
56
- const t = setTimeout(() => nameRef.current?.focus(), 50);
119
+ const t = setTimeout(() => (urlRef.current?.value ? nameRef : urlRef).current?.focus(), 50);
57
120
  return () => clearTimeout(t);
58
121
  }, []);
59
122
  useEffect(() => {
@@ -66,40 +129,182 @@ function AgentAddPopover({ onAdd, onClose, }) {
66
129
  document.addEventListener("mousedown", handleClick);
67
130
  return () => document.removeEventListener("mousedown", handleClick);
68
131
  }, [onClose]);
69
- const handleAdd = () => {
70
- if (!name.trim() || !url.trim())
132
+ const handleCheck = useCallback(async () => {
133
+ const trimmedUrl = url.trim();
134
+ if (!trimmedUrl)
71
135
  return;
72
- onAdd(name.trim(), url.trim(), description.trim());
136
+ setCheck({ status: "checking" });
137
+ try {
138
+ const res = await fetch(agentNativePath(`/_agent-native/agents/probe?url=${encodeURIComponent(trimmedUrl)}`));
139
+ const body = await res.json().catch(() => null);
140
+ if (!res.ok) {
141
+ setCheck({
142
+ status: "error",
143
+ message: body?.error ?? `Check failed (${res.status})`,
144
+ });
145
+ return;
146
+ }
147
+ const result = body;
148
+ setCheck({ status: "done", result });
149
+ if (result.reachable) {
150
+ if (!name.trim() && result.name)
151
+ setName(result.name);
152
+ if (!description.trim() && result.description) {
153
+ setDescription(result.description);
154
+ }
155
+ }
156
+ }
157
+ catch (err) {
158
+ setCheck({ status: "error", message: err?.message ?? "Check failed" });
159
+ }
160
+ }, [url, name, description]);
161
+ const handleAdd = async () => {
162
+ const trimmedName = name.trim();
163
+ const trimmedUrl = url.trim();
164
+ if (!trimmedName || !trimmedUrl)
165
+ return;
166
+ const trimmedDescription = description.trim();
167
+ const ok = await onAdd(trimmedName, trimmedUrl, trimmedDescription);
168
+ if (ok) {
169
+ setAdded({
170
+ name: trimmedName,
171
+ url: trimmedUrl,
172
+ description: trimmedDescription,
173
+ });
174
+ }
73
175
  };
74
- return (_jsx("div", { ref: popoverRef, className: "absolute end-0 top-full z-50 mt-1 w-64 rounded-lg border border-border bg-popover p-2.5 shadow-lg", children: _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("input", { ref: nameRef, value: name, onChange: (e) => setName(e.target.value), onKeyDown: (e) => {
176
+ if (added) {
177
+ return (_jsx("div", { ref: popoverRef, className: "absolute end-0 top-full z-50 mt-1 w-72 rounded-lg border border-border bg-popover p-2.5 shadow-lg", children: _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsxs("p", { className: "text-[10px] leading-relaxed text-muted-foreground", children: ["Added ", added.name, " on your side only \u2014 registration is one-way, so it won't know about this app until it's added there too."] }), _jsxs("a", { href: buildPeerRegisterBackLink(added.url), target: "_blank", rel: "noopener noreferrer", className: "inline-flex cursor-pointer items-center justify-center gap-1 rounded bg-accent px-2 py-1 text-[10px] font-medium text-foreground no-underline hover:bg-accent/80", children: [_jsx(IconExternalLink, { size: 10 }), "Open ", added.name, "'s settings"] }), _jsx("button", { onClick: onClose, className: "cursor-pointer rounded px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground", children: "Dismiss" })] }) }));
178
+ }
179
+ const result = check.status === "done" ? check.result : null;
180
+ const unreachable = result ? !result.reachable : false;
181
+ const unauthorized = result ? result.authorized === false : false;
182
+ return (_jsx("div", { ref: popoverRef, className: "absolute end-0 top-full z-50 mt-1 w-72 rounded-lg border border-border bg-popover p-2.5 shadow-lg", children: _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsxs("div", { className: "flex gap-1", children: [_jsx("input", { ref: urlRef, value: url, onChange: (e) => {
183
+ setUrl(e.target.value);
184
+ setCheck({ status: "idle" });
185
+ }, onKeyDown: (e) => {
186
+ if (e.key === "Enter")
187
+ handleCheck();
188
+ if (e.key === "Escape")
189
+ onClose();
190
+ }, className: "w-full flex-1 rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent", placeholder: "URL (e.g. http://localhost:8085)" }), _jsx(ToolkitButtonBase, { type: "button", variant: "outline", onClick: handleCheck, disabled: !url.trim() || check.status === "checking", className: "inline-flex shrink-0 cursor-pointer items-center gap-1 rounded border border-border px-2 py-1 text-[10px] text-muted-foreground hover:bg-accent/40 hover:text-foreground disabled:opacity-40", children: check.status === "checking" ? (_jsx(IconLoader2, { size: 10, className: "animate-spin" })) : ("Check") })] }), check.status === "error" && (_jsxs("p", { className: "flex items-start gap-1 text-[10px] text-red-500", children: [_jsx(IconAlertTriangle, { size: 11, className: "mt-px shrink-0" }), check.message] })), result && (_jsxs("div", { className: `flex items-start gap-1 text-[10px] ${unreachable || unauthorized
191
+ ? "text-amber-600 dark:text-amber-400"
192
+ : "text-green-600 dark:text-green-500"}`, children: [unreachable || unauthorized ? (_jsx(IconAlertTriangle, { size: 11, className: "mt-px shrink-0" })) : (_jsx(IconCheck, { size: 11, className: "mt-px shrink-0" })), _jsxs("span", { className: "leading-relaxed", children: [describeCheckResult(result), unreachable &&
193
+ " — the app may just not be running yet; you can still add it."] })] })), unauthorized &&
194
+ (secretSet === true ? (_jsxs("button", { onClick: () => syncSecret.mutate(undefined), disabled: syncSecret.isPending, className: "inline-flex cursor-pointer items-center gap-1 self-start rounded border border-border px-1.5 py-0.5 text-[10px] text-muted-foreground hover:text-foreground disabled:opacity-40", children: [syncSecret.isPending ? (_jsx(IconLoader2, { size: 10, className: "animate-spin" })) : (_jsx(IconRefresh, { size: 10 })), "Sync secret to apps"] })) : (_jsx("p", { className: "text-[10px] text-muted-foreground", children: secretSet === false ? (_jsxs(_Fragment, { children: ["No shared secret set yet \u2014", " ", _jsx("a", { href: buildSettingsRoute(STANDARD_SETTINGS_TABS.team), className: "underline underline-offset-2 hover:text-foreground", children: "set one on the Team page" }), " ", "first."] })) : ("Ask your workspace owner to sync the shared secret.") }))), _jsx("input", { ref: nameRef, value: name, onChange: (e) => setName(e.target.value), onKeyDown: (e) => {
75
195
  if (e.key === "Enter")
76
196
  handleAdd();
77
197
  if (e.key === "Escape")
78
198
  onClose();
79
- }, className: "w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent", placeholder: "Name" }), _jsx("input", { value: url, onChange: (e) => setUrl(e.target.value), onKeyDown: (e) => {
199
+ }, className: "w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent", placeholder: "Name" }), _jsx("input", { value: description, onChange: (e) => setDescription(e.target.value), onKeyDown: (e) => {
80
200
  if (e.key === "Enter")
81
201
  handleAdd();
82
202
  if (e.key === "Escape")
83
203
  onClose();
84
- }, className: "w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent", placeholder: "URL (e.g. http://localhost:8085)" }), _jsx("input", { value: description, onChange: (e) => setDescription(e.target.value), onKeyDown: (e) => {
85
- if (e.key === "Enter")
86
- handleAdd();
87
- if (e.key === "Escape")
88
- onClose();
89
- }, className: "w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent", placeholder: "Description (optional)" }), _jsxs("div", { className: "flex justify-end gap-1 pt-0.5", children: [_jsx("button", { onClick: onClose, className: "rounded px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground", children: "Cancel" }), _jsx("button", { onClick: handleAdd, disabled: !name.trim() || !url.trim(), className: "rounded bg-accent px-2 py-0.5 text-[10px] font-medium text-foreground hover:bg-accent/80 disabled:opacity-40", children: "Add" })] })] }) }));
204
+ }, className: "w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent", placeholder: "Description (optional)" }), _jsxs("div", { className: "flex justify-end gap-1 pt-0.5", children: [_jsx("button", { onClick: onClose, className: "cursor-pointer rounded px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground", children: "Cancel" }), _jsx("button", { onClick: handleAdd, disabled: !name.trim() || !url.trim(), className: "cursor-pointer rounded bg-accent px-2 py-0.5 text-[10px] font-medium text-foreground hover:bg-accent/80 disabled:opacity-40", children: unreachable ? "Add anyway" : "Add" })] })] }) }));
205
+ }
206
+ function A2ASecretStatusRow({ org, syncSecret, }) {
207
+ if (!org)
208
+ return null;
209
+ const secretSet = org.a2aSecretSet;
210
+ if (secretSet === undefined) {
211
+ // Not an owner/admin — the server omits `a2aSecretSet` entirely for this
212
+ // role rather than reporting `false`, so this must read as "can't see
213
+ // it," never as "not set" (a claim we have no basis for).
214
+ return (_jsx("div", { className: "mb-2 rounded-md border border-border/60 bg-accent/20 px-2 py-1.5 text-[10px] text-muted-foreground", children: "Shared secret is managed by your workspace owner." }));
215
+ }
216
+ if (!secretSet) {
217
+ return (_jsxs("div", { className: "mb-2 rounded-md border border-amber-500/30 bg-amber-500/10 px-2 py-1.5 text-[10px] text-amber-600 dark:text-amber-400", children: ["No shared secret set \u2014 connected apps will reject calls in production.", " ", _jsx("a", { href: buildSettingsRoute(STANDARD_SETTINGS_TABS.team), className: "underline underline-offset-2 hover:text-amber-500", children: "Set one on the Team page" })] }));
218
+ }
219
+ const noDomain = syncSecret.error?.message?.toLowerCase().includes("domain");
220
+ const failures = syncSecret.data?.results.filter((r) => !r.ok) ?? [];
221
+ return (_jsxs("div", { className: "mb-2 flex flex-col gap-1 rounded-md border border-border/60 bg-accent/20 px-2 py-1.5 text-[10px]", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("span", { className: "text-muted-foreground", children: ["Shared secret set", org.allowedDomain ? ` for ${org.allowedDomain}` : ""] }), _jsxs("button", { onClick: () => syncSecret.mutate(undefined), disabled: syncSecret.isPending, className: "inline-flex shrink-0 cursor-pointer items-center gap-1 rounded border border-border px-1.5 py-0.5 text-[10px] text-muted-foreground hover:text-foreground disabled:opacity-40", children: [syncSecret.isPending ? (_jsx(IconLoader2, { size: 10, className: "animate-spin" })) : (_jsx(IconRefresh, { size: 10 })), "Sync to apps"] })] }), syncSecret.data && !syncSecret.isPending && (_jsxs("div", { className: "text-muted-foreground", children: ["Synced to ", syncSecret.data.succeeded, "/", syncSecret.data.total, " app", syncSecret.data.total === 1 ? "" : "s", syncSecret.data.failed > 0
222
+ ? ` (${syncSecret.data.failed} failed)`
223
+ : "", ".", failures.length > 0 && (_jsx("ul", { className: "mt-0.5 list-disc ps-3 text-red-500", children: failures.map((r) => (_jsxs("li", { children: [r.name, ": ", r.error ?? `HTTP ${r.status ?? "?"}`] }, r.id))) }))] })), syncSecret.error && (_jsxs("p", { className: "text-red-500", children: [syncSecret.error.message, noDomain && (_jsxs(_Fragment, { children: [" ", _jsx("a", { href: buildSettingsRoute(STANDARD_SETTINGS_TABS.team), className: "underline underline-offset-2", children: "Set the domain" })] }))] }))] }));
90
224
  }
225
+ /** Query params `/_agent-native/open` forwards onto the redirect target. */
226
+ const PREFILL_PARAMS = [
227
+ "f_agentName",
228
+ "f_agentUrl",
229
+ "f_agentDescription",
230
+ ];
91
231
  export function AgentsSection() {
92
232
  const [agents, setAgents] = useState([]);
93
233
  const [loading, setLoading] = useState(true);
94
234
  const [editingAgent, setEditingAgent] = useState(null);
95
235
  const [showAdd, setShowAdd] = useState(false);
236
+ const [prefill, setPrefill] = useState(null);
237
+ const [probeById, setProbeById] = useState(null);
238
+ const { data: org } = useOrg();
239
+ const syncSecret = useSyncA2ASecret();
240
+ // Landing from a peer's "register back" deep link (see
241
+ // buildPeerRegisterBackLink): the open route only echoes `f_*` params onto
242
+ // the redirect URL, so read those here and open the Add popover prefilled.
243
+ useEffect(() => {
244
+ if (typeof window === "undefined")
245
+ return;
246
+ const params = new URLSearchParams(window.location.search);
247
+ const url = params.get("f_agentUrl");
248
+ if (!url)
249
+ return;
250
+ setPrefill({
251
+ name: params.get("f_agentName") ?? "",
252
+ url,
253
+ description: params.get("f_agentDescription") ?? "",
254
+ });
255
+ setShowAdd(true);
256
+ for (const key of PREFILL_PARAMS)
257
+ params.delete(key);
258
+ const query = params.toString();
259
+ window.history.replaceState(null, "", `${window.location.pathname}${query ? `?${query}` : ""}${window.location.hash}`);
260
+ }, []);
261
+ // One batched probe for the whole list — cheap liveness dots, not one
262
+ // request per row. A row absent from the results (never returned) stays
263
+ // dot-less rather than defaulting to a color that isn't backed by data.
264
+ useEffect(() => {
265
+ let cancelled = false;
266
+ fetch(agentNativePath("/_agent-native/agents/probe"))
267
+ .then((res) => (res.ok ? res.json() : null))
268
+ .then((data) => {
269
+ if (cancelled || !data)
270
+ return;
271
+ const results = Array.isArray(data.results)
272
+ ? data.results
273
+ : [];
274
+ const map = new Map();
275
+ for (const result of results)
276
+ map.set(result.id.toLowerCase(), result);
277
+ setProbeById(map);
278
+ })
279
+ .catch(() => {
280
+ if (!cancelled)
281
+ setProbeById(new Map());
282
+ });
283
+ return () => {
284
+ cancelled = true;
285
+ };
286
+ }, []);
96
287
  const fetchAgents = useCallback(async () => {
97
288
  try {
98
289
  const res = await fetch(agentNativePath("/_agent-native/resources?scope=all"));
99
290
  if (!res.ok)
100
291
  return;
101
292
  const data = await res.json();
102
- const agentResources = (data.resources ?? []).filter((r) => isRemoteAgentPath(r.path));
293
+ // Migrating a remote agent to the canonical `remote-agents/` prefix
294
+ // leaves the legacy `agents/` row in place (resources/store.ts), so
295
+ // every migrated agent has two resources pointing at one URL. Collapse
296
+ // them the way discoverAgents does, canonical winning.
297
+ const byAgentId = new Map();
298
+ for (const resource of (data.resources ?? [])) {
299
+ if (!isRemoteAgentPath(resource.path))
300
+ continue;
301
+ const agentId = getRemoteAgentIdFromPath(resource.path);
302
+ const existing = byAgentId.get(agentId);
303
+ if (existing?.path.startsWith(REMOTE_AGENT_RESOURCE_PREFIX))
304
+ continue;
305
+ byAgentId.set(agentId, resource);
306
+ }
307
+ const agentResources = [...byAgentId.values()];
103
308
  const parsed = await Promise.all(agentResources.map(async (r) => {
104
309
  try {
105
310
  const detail = await fetch(agentNativePath(`/_agent-native/resources/${r.id}`));
@@ -119,7 +324,7 @@ export function AgentsSection() {
119
324
  return null;
120
325
  }
121
326
  }));
122
- setAgents(parsed.filter(Boolean));
327
+ setAgents(parsed.filter((agent) => agent !== null));
123
328
  }
124
329
  finally {
125
330
  setLoading(false);
@@ -147,12 +352,16 @@ export function AgentsSection() {
147
352
  shared: true,
148
353
  }),
149
354
  });
150
- if (res.ok) {
151
- setShowAdd(false);
355
+ // Deliberately don't close the popover here — a successful add shows a
356
+ // follow-up state (registration is one-way; the peer doesn't know
357
+ // about us yet) that the user dismisses explicitly.
358
+ if (res.ok)
152
359
  fetchAgents();
153
- }
360
+ return res.ok;
361
+ }
362
+ catch {
363
+ return false;
154
364
  }
155
- catch { }
156
365
  };
157
366
  const handleSave = async (agent) => {
158
367
  const agentJson = JSON.stringify({
@@ -191,10 +400,21 @@ export function AgentsSection() {
191
400
  return (_jsxs("div", { children: [_jsxs("div", { className: "flex items-center justify-between mb-2", children: [_jsx("div", { className: "text-[10px] text-muted-foreground", children: "@-mention agents in chat to delegate tasks via A2A." }), _jsxs("div", { className: "relative", children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { onClick: () => {
192
401
  setShowAdd(!showAdd);
193
402
  setEditingAgent(null);
194
- }, className: "flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50", children: showAdd ? _jsx(IconX, { size: 12 }) : _jsx(IconPlus, { size: 12 }) }) }), _jsx(TooltipContent, { children: "Add agent" })] }), showAdd && (_jsx(AgentAddPopover, { onAdd: handleAdd, onClose: () => setShowAdd(false) }))] })] }), loading ? (_jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "h-6 w-full rounded bg-muted/50 animate-pulse" }), _jsx("div", { className: "h-6 w-3/4 rounded bg-muted/50 animate-pulse" })] })) : agents.length === 0 ? (_jsxs("button", { onClick: () => setShowAdd(true), className: "flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-[11px] text-muted-foreground hover:text-foreground hover:bg-accent/30", children: [_jsx(IconPlus, { size: 12, className: "shrink-0" }), "Add agent"] })) : (_jsxs("div", { className: "flex flex-col gap-0.5", children: [agents.map((agent) => (_jsxs("div", { className: "group relative", children: [_jsxs("div", { className: "flex w-full items-center gap-2 rounded-md px-2 py-1.5 hover:bg-accent/30", children: [_jsx("span", { className: "text-[11px] font-medium text-foreground truncate shrink-0", children: agent.name }), _jsx("span", { className: "flex-1 text-[10px] text-muted-foreground/60 truncate text-end", children: agent.url }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { onClick: () => {
195
- setEditingAgent(editingAgent === agent.id ? null : agent.id);
196
- setShowAdd(false);
197
- }, className: "shrink-0 rounded p-0.5 text-muted-foreground opacity-0 group-hover:opacity-100 hover:text-foreground hover:bg-accent/50", children: _jsx(IconPencil, { size: 11 }) }) }), _jsx(TooltipContent, { children: "Edit agent" })] })] }), editingAgent === agent.id && (_jsx(AgentEditPopover, { agent: agent, onSave: handleSave, onDelete: handleDelete, onClose: () => setEditingAgent(null) }))] }, agent.id))), _jsxs("button", { onClick: () => {
403
+ }, className: "flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50", children: showAdd ? _jsx(IconX, { size: 12 }) : _jsx(IconPlus, { size: 12 }) }) }), _jsx(TooltipContent, { children: "Add agent" })] }), showAdd && (_jsx(AgentAddPopover, { initialName: prefill?.name, initialUrl: prefill?.url, initialDescription: prefill?.description, secretSet: org?.a2aSecretSet, syncSecret: syncSecret, onAdd: handleAdd, onClose: () => {
404
+ setShowAdd(false);
405
+ setPrefill(null);
406
+ } }))] })] }), _jsx(A2ASecretStatusRow, { org: org, syncSecret: syncSecret }), loading ? (_jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "h-6 w-full rounded bg-muted/50 animate-pulse" }), _jsx("div", { className: "h-6 w-3/4 rounded bg-muted/50 animate-pulse" })] })) : agents.length === 0 ? (_jsxs("button", { onClick: () => setShowAdd(true), className: "flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-[11px] text-muted-foreground hover:text-foreground hover:bg-accent/30", children: [_jsx(IconPlus, { size: 12, className: "shrink-0" }), "Add agent"] })) : (_jsxs("div", { className: "flex flex-col gap-0.5", children: [agents.map((agent) => {
407
+ const probe = probeById?.get(getRemoteAgentIdFromPath(agent.path).toLowerCase());
408
+ const dotState = !probe
409
+ ? null
410
+ : probe.reachable && probe.authorized !== false
411
+ ? "ok"
412
+ : "warn";
413
+ return (_jsxs("div", { className: "group relative", children: [_jsxs("div", { className: "flex w-full items-center gap-2 rounded-md px-2 py-1.5 hover:bg-accent/30", children: [dotState ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: `h-1.5 w-1.5 shrink-0 rounded-full ${dotState === "ok" ? "bg-green-500" : "bg-amber-500"}` }) }), _jsx(TooltipContent, { children: probe && describeProbeTooltip(probe) })] })) : (_jsx("span", { className: "h-1.5 w-1.5 shrink-0 rounded-full bg-transparent" })), _jsx("span", { className: "text-[11px] font-medium text-foreground truncate shrink-0", children: agent.name }), _jsx("span", { className: "flex-1 text-[10px] text-muted-foreground/60 truncate text-end", children: agent.url }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { onClick: () => {
414
+ setEditingAgent(editingAgent === agent.id ? null : agent.id);
415
+ setShowAdd(false);
416
+ }, className: "shrink-0 rounded p-0.5 text-muted-foreground opacity-0 group-hover:opacity-100 hover:text-foreground hover:bg-accent/50", children: _jsx(IconPencil, { size: 11 }) }) }), _jsx(TooltipContent, { children: "Edit agent" })] })] }), editingAgent === agent.id && (_jsx(AgentEditPopover, { agent: agent, onSave: handleSave, onDelete: handleDelete, onClose: () => setEditingAgent(null) }))] }, agent.id));
417
+ }), _jsxs("button", { onClick: () => {
198
418
  setShowAdd(true);
199
419
  setEditingAgent(null);
200
420
  }, className: "flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-[11px] text-muted-foreground hover:text-foreground hover:bg-accent/30", children: [_jsx(IconPlus, { size: 12, className: "shrink-0" }), "Add agent"] })] }))] }));
@@ -1 +1 @@
1
- {"version":3,"file":"AgentsSection.js","sourceRoot":"","sources":["../../../src/client/settings/AgentsSection.tsx"],"names":[],"mappings":"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","sourcesContent":["import { IconPlus, IconPencil, IconTrash, IconX } from \"@tabler/icons-react\";\nimport { useState, useEffect, useRef, useCallback } from \"react\";\n\nimport {\n getRemoteAgentIdFromPath,\n isRemoteAgentPath,\n remoteAgentResourcePath,\n} from \"../../resources/metadata.js\";\nimport { agentNativePath } from \"../api-path.js\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"../components/ui/tooltip.js\";\n\ninterface AgentInfo {\n id: string;\n path: string;\n name: string;\n url: string;\n description?: string;\n}\n\nfunction AgentEditPopover({\n agent,\n onSave,\n onDelete,\n onClose,\n}: {\n agent: AgentInfo;\n onSave: (agent: AgentInfo) => void;\n onDelete: (id: string) => void;\n onClose: () => void;\n}) {\n const [name, setName] = useState(agent.name);\n const [url, setUrl] = useState(agent.url);\n const [description, setDescription] = useState(agent.description ?? \"\");\n const popoverRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n function handleClick(e: MouseEvent) {\n if (\n popoverRef.current &&\n !popoverRef.current.contains(e.target as Node)\n ) {\n onClose();\n }\n }\n document.addEventListener(\"mousedown\", handleClick);\n return () => document.removeEventListener(\"mousedown\", handleClick);\n }, [onClose]);\n\n const handleSave = () => {\n if (!name.trim() || !url.trim()) return;\n onSave({\n ...agent,\n name: name.trim(),\n url: url.trim(),\n description: description.trim() || undefined,\n });\n };\n\n return (\n <div\n ref={popoverRef}\n className=\"absolute end-0 top-full z-50 mt-1 w-64 rounded-lg border border-border bg-popover p-2.5 shadow-lg\"\n >\n <div className=\"flex flex-col gap-1.5\">\n <input\n value={name}\n onChange={(e) => setName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleSave();\n if (e.key === \"Escape\") onClose();\n }}\n className=\"w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent\"\n placeholder=\"Name\"\n />\n <input\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleSave();\n if (e.key === \"Escape\") onClose();\n }}\n className=\"w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent\"\n placeholder=\"URL (e.g. http://localhost:8085)\"\n />\n <input\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleSave();\n if (e.key === \"Escape\") onClose();\n }}\n className=\"w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent\"\n placeholder=\"Description (optional)\"\n />\n <div className=\"flex items-center justify-between pt-0.5\">\n <button\n onClick={() => onDelete(agent.id)}\n className=\"flex items-center gap-1 rounded px-1.5 py-0.5 text-[10px] text-red-400 hover:bg-red-900/20\"\n >\n <IconTrash size={10} />\n Remove\n </button>\n <div className=\"flex gap-1\">\n <button\n onClick={onClose}\n className=\"rounded px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground\"\n >\n Cancel\n </button>\n <button\n onClick={handleSave}\n disabled={!name.trim() || !url.trim()}\n className=\"rounded bg-accent px-2 py-0.5 text-[10px] font-medium text-foreground hover:bg-accent/80 disabled:opacity-40\"\n >\n Save\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nfunction AgentAddPopover({\n onAdd,\n onClose,\n}: {\n onAdd: (name: string, url: string, description: string) => void;\n onClose: () => void;\n}) {\n const [name, setName] = useState(\"\");\n const [url, setUrl] = useState(\"\");\n const [description, setDescription] = useState(\"\");\n const nameRef = useRef<HTMLInputElement>(null);\n const popoverRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const t = setTimeout(() => nameRef.current?.focus(), 50);\n return () => clearTimeout(t);\n }, []);\n\n useEffect(() => {\n function handleClick(e: MouseEvent) {\n if (\n popoverRef.current &&\n !popoverRef.current.contains(e.target as Node)\n ) {\n onClose();\n }\n }\n document.addEventListener(\"mousedown\", handleClick);\n return () => document.removeEventListener(\"mousedown\", handleClick);\n }, [onClose]);\n\n const handleAdd = () => {\n if (!name.trim() || !url.trim()) return;\n onAdd(name.trim(), url.trim(), description.trim());\n };\n\n return (\n <div\n ref={popoverRef}\n className=\"absolute end-0 top-full z-50 mt-1 w-64 rounded-lg border border-border bg-popover p-2.5 shadow-lg\"\n >\n <div className=\"flex flex-col gap-1.5\">\n <input\n ref={nameRef}\n value={name}\n onChange={(e) => setName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleAdd();\n if (e.key === \"Escape\") onClose();\n }}\n className=\"w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent\"\n placeholder=\"Name\"\n />\n <input\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleAdd();\n if (e.key === \"Escape\") onClose();\n }}\n className=\"w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent\"\n placeholder=\"URL (e.g. http://localhost:8085)\"\n />\n <input\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleAdd();\n if (e.key === \"Escape\") onClose();\n }}\n className=\"w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent\"\n placeholder=\"Description (optional)\"\n />\n <div className=\"flex justify-end gap-1 pt-0.5\">\n <button\n onClick={onClose}\n className=\"rounded px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground\"\n >\n Cancel\n </button>\n <button\n onClick={handleAdd}\n disabled={!name.trim() || !url.trim()}\n className=\"rounded bg-accent px-2 py-0.5 text-[10px] font-medium text-foreground hover:bg-accent/80 disabled:opacity-40\"\n >\n Add\n </button>\n </div>\n </div>\n </div>\n );\n}\n\nexport function AgentsSection() {\n const [agents, setAgents] = useState<AgentInfo[]>([]);\n const [loading, setLoading] = useState(true);\n const [editingAgent, setEditingAgent] = useState<string | null>(null);\n const [showAdd, setShowAdd] = useState(false);\n\n const fetchAgents = useCallback(async () => {\n try {\n const res = await fetch(\n agentNativePath(\"/_agent-native/resources?scope=all\"),\n );\n if (!res.ok) return;\n const data = await res.json();\n const agentResources = (data.resources ?? []).filter(\n (r: { path: string }) => isRemoteAgentPath(r.path),\n );\n const parsed = await Promise.all(\n agentResources.map(async (r: { id: string; path: string }) => {\n try {\n const detail = await fetch(\n agentNativePath(`/_agent-native/resources/${r.id}`),\n );\n if (!detail.ok) return null;\n const d = await detail.json();\n const config = JSON.parse(d.content);\n return {\n id: r.id,\n path: r.path,\n name: config.name,\n url: config.url,\n description: config.description,\n };\n } catch {\n return null;\n }\n }),\n );\n setAgents(parsed.filter(Boolean));\n } finally {\n setLoading(false);\n }\n }, []);\n\n useEffect(() => {\n fetchAgents();\n }, [fetchAgents]);\n\n const handleAdd = async (name: string, url: string, description: string) => {\n const id = name.toLowerCase().replace(/[^a-z0-9-]/g, \"-\");\n const agentJson = JSON.stringify(\n {\n id,\n name,\n description: description || undefined,\n url,\n color: \"#6B7280\",\n },\n null,\n 2,\n );\n\n try {\n const res = await fetch(agentNativePath(\"/_agent-native/resources\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n path: remoteAgentResourcePath(id),\n content: agentJson,\n shared: true,\n }),\n });\n if (res.ok) {\n setShowAdd(false);\n fetchAgents();\n }\n } catch {}\n };\n\n const handleSave = async (agent: AgentInfo) => {\n const agentJson = JSON.stringify(\n {\n id: getRemoteAgentIdFromPath(agent.path),\n name: agent.name,\n description: agent.description || undefined,\n url: agent.url,\n color: \"#6B7280\",\n },\n null,\n 2,\n );\n\n try {\n const res = await fetch(\n agentNativePath(`/_agent-native/resources/${agent.id}`),\n {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ content: agentJson }),\n },\n );\n if (res.ok) {\n setEditingAgent(null);\n fetchAgents();\n }\n } catch {}\n };\n\n const handleDelete = async (agentId: string) => {\n try {\n const res = await fetch(\n agentNativePath(`/_agent-native/resources/${agentId}`),\n {\n method: \"DELETE\",\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n if (res.ok) {\n setEditingAgent(null);\n fetchAgents();\n }\n } catch {}\n };\n\n return (\n <div>\n {/* Header with + button */}\n <div className=\"flex items-center justify-between mb-2\">\n <div className=\"text-[10px] text-muted-foreground\">\n @-mention agents in chat to delegate tasks via A2A.\n </div>\n <div className=\"relative\">\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n onClick={() => {\n setShowAdd(!showAdd);\n setEditingAgent(null);\n }}\n className=\"flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50\"\n >\n {showAdd ? <IconX size={12} /> : <IconPlus size={12} />}\n </button>\n </TooltipTrigger>\n <TooltipContent>Add agent</TooltipContent>\n </Tooltip>\n {showAdd && (\n <AgentAddPopover\n onAdd={handleAdd}\n onClose={() => setShowAdd(false)}\n />\n )}\n </div>\n </div>\n\n {/* Agent list */}\n {loading ? (\n <div className=\"space-y-1.5\">\n <div className=\"h-6 w-full rounded bg-muted/50 animate-pulse\" />\n <div className=\"h-6 w-3/4 rounded bg-muted/50 animate-pulse\" />\n </div>\n ) : agents.length === 0 ? (\n <button\n onClick={() => setShowAdd(true)}\n className=\"flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-[11px] text-muted-foreground hover:text-foreground hover:bg-accent/30\"\n >\n <IconPlus size={12} className=\"shrink-0\" />\n Add agent\n </button>\n ) : (\n <div className=\"flex flex-col gap-0.5\">\n {agents.map((agent) => (\n <div key={agent.id} className=\"group relative\">\n <div className=\"flex w-full items-center gap-2 rounded-md px-2 py-1.5 hover:bg-accent/30\">\n <span className=\"text-[11px] font-medium text-foreground truncate shrink-0\">\n {agent.name}\n </span>\n <span className=\"flex-1 text-[10px] text-muted-foreground/60 truncate text-end\">\n {agent.url}\n </span>\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n onClick={() => {\n setEditingAgent(\n editingAgent === agent.id ? null : agent.id,\n );\n setShowAdd(false);\n }}\n className=\"shrink-0 rounded p-0.5 text-muted-foreground opacity-0 group-hover:opacity-100 hover:text-foreground hover:bg-accent/50\"\n >\n <IconPencil size={11} />\n </button>\n </TooltipTrigger>\n <TooltipContent>Edit agent</TooltipContent>\n </Tooltip>\n </div>\n {editingAgent === agent.id && (\n <AgentEditPopover\n agent={agent}\n onSave={handleSave}\n onDelete={handleDelete}\n onClose={() => setEditingAgent(null)}\n />\n )}\n </div>\n ))}\n <button\n onClick={() => {\n setShowAdd(true);\n setEditingAgent(null);\n }}\n className=\"flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-[11px] text-muted-foreground hover:text-foreground hover:bg-accent/30\"\n >\n <IconPlus size={12} className=\"shrink-0\" />\n Add agent\n </button>\n </div>\n )}\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"AgentsSection.js","sourceRoot":"","sources":["../../../src/client/settings/AgentsSection.tsx"],"names":[],"mappings":"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","sourcesContent":["import { ButtonBase as ToolkitButtonBase } from \"@agent-native/toolkit/ui/button\";\nimport {\n IconPlus,\n IconPencil,\n IconTrash,\n IconX,\n IconCheck,\n IconLoader2,\n IconAlertTriangle,\n IconExternalLink,\n IconRefresh,\n} from \"@tabler/icons-react\";\nimport { useState, useEffect, useRef, useCallback } from \"react\";\n\nimport {\n buildOpenRoutePath,\n buildSettingsRoute,\n STANDARD_SETTINGS_TABS,\n} from \"../../navigation/index.js\";\nimport {\n getRemoteAgentIdFromPath,\n isRemoteAgentPath,\n REMOTE_AGENT_RESOURCE_PREFIX,\n remoteAgentResourcePath,\n} from \"../../resources/metadata.js\";\nimport { agentNativePath, appBasePath } from \"../api-path.js\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from \"../components/ui/tooltip.js\";\nimport { useOrg, useSyncA2ASecret } from \"../org/hooks.js\";\n\ninterface AgentInfo {\n id: string;\n path: string;\n name: string;\n url: string;\n description?: string;\n}\n\n/** Wire shape of `GET /_agent-native/agents/probe` (single or batched result). */\ninterface AgentProbeResult {\n url: string;\n reachable: boolean;\n name?: string;\n description?: string;\n securitySchemes?: string[];\n /** Absent (not false) whenever the auth check never ran or never resolved. */\n authorized?: boolean;\n authError?: string;\n publicSkills?: number;\n error?: string;\n}\n\nfunction describeSkills(publicSkills: number | undefined): string | null {\n if (publicSkills === undefined) return null;\n // An empty public skill list only means the card advertises no anonymous-\n // safe actions — the peer still has authenticated reads/writes. Saying so\n // plainly avoids reading as \"this agent can't do anything.\"\n if (publicSkills === 0) return \"reads require auth\";\n return `${publicSkills} public skill${publicSkills === 1 ? \"\" : \"s\"}`;\n}\n\n/** One-line status for the row dot tooltip. */\nfunction describeProbeTooltip(result: AgentProbeResult): string {\n if (!result.reachable) {\n return `Unreachable${result.error ? `: ${result.error}` : \"\"}`;\n }\n if (result.authorized === false) {\n return \"Reachable, but the peer rejected our token — calls will 401 in production\";\n }\n if (result.authorized === undefined) {\n return \"Reachable; couldn't verify our token\";\n }\n return \"Reachable and authorized\";\n}\n\n/** Multi-clause status line for the Add popover's Check result. */\nfunction describeCheckResult(result: AgentProbeResult): string {\n if (!result.reachable) {\n return result.error ?? \"Not reachable\";\n }\n const scheme = result.securitySchemes?.length\n ? result.securitySchemes.join(\", \")\n : \"no auth scheme advertised\";\n const authText =\n result.authorized === false\n ? \"the peer rejected our token — calls will 401 in production\"\n : result.authorized === undefined\n ? `couldn't verify our token${result.authError ? ` (${result.authError})` : \"\"}`\n : \"our token works\";\n const skills = describeSkills(result.publicSkills);\n return [`Live · ${scheme}`, authText, skills].filter(Boolean).join(\" · \");\n}\n\nfunction AgentEditPopover({\n agent,\n onSave,\n onDelete,\n onClose,\n}: {\n agent: AgentInfo;\n onSave: (agent: AgentInfo) => void;\n onDelete: (id: string) => void;\n onClose: () => void;\n}) {\n const [name, setName] = useState(agent.name);\n const [url, setUrl] = useState(agent.url);\n const [description, setDescription] = useState(agent.description ?? \"\");\n const popoverRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n function handleClick(e: MouseEvent) {\n if (\n popoverRef.current &&\n !popoverRef.current.contains(e.target as Node)\n ) {\n onClose();\n }\n }\n document.addEventListener(\"mousedown\", handleClick);\n return () => document.removeEventListener(\"mousedown\", handleClick);\n }, [onClose]);\n\n const handleSave = () => {\n if (!name.trim() || !url.trim()) return;\n onSave({\n ...agent,\n name: name.trim(),\n url: url.trim(),\n description: description.trim() || undefined,\n });\n };\n\n return (\n <div\n ref={popoverRef}\n className=\"absolute end-0 top-full z-50 mt-1 w-64 rounded-lg border border-border bg-popover p-2.5 shadow-lg\"\n >\n <div className=\"flex flex-col gap-1.5\">\n <input\n value={name}\n onChange={(e) => setName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleSave();\n if (e.key === \"Escape\") onClose();\n }}\n className=\"w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent\"\n placeholder=\"Name\"\n />\n <input\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleSave();\n if (e.key === \"Escape\") onClose();\n }}\n className=\"w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent\"\n placeholder=\"URL (e.g. http://localhost:8085)\"\n />\n <input\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleSave();\n if (e.key === \"Escape\") onClose();\n }}\n className=\"w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent\"\n placeholder=\"Description (optional)\"\n />\n <div className=\"flex items-center justify-between pt-0.5\">\n <button\n onClick={() => onDelete(agent.id)}\n className=\"flex items-center gap-1 rounded px-1.5 py-0.5 text-[10px] text-red-400 hover:bg-red-900/20\"\n >\n <IconTrash size={10} />\n Remove\n </button>\n <div className=\"flex gap-1\">\n <button\n onClick={onClose}\n className=\"rounded px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground\"\n >\n Cancel\n </button>\n <button\n onClick={handleSave}\n disabled={!name.trim() || !url.trim()}\n className=\"rounded bg-accent px-2 py-0.5 text-[10px] font-medium text-foreground hover:bg-accent/80 disabled:opacity-40\"\n >\n Save\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n}\n\ntype CheckState =\n | { status: \"idle\" }\n | { status: \"checking\" }\n | { status: \"done\"; result: AgentProbeResult }\n | { status: \"error\"; message: string };\n\ninterface AddedAgentInfo {\n name: string;\n url: string;\n description: string;\n}\n\n/** Builds an absolute deep link into a peer's own Settings > Agents Add\n * popover, prefilled with THIS app's own name/url/description, via the\n * existing `/_agent-native/open` route's `f_*` filter-forwarding (the only\n * non-reserved params the open route echoes onto the redirect URL instead of\n * only stashing them server-side for `navigate` polling). No new endpoint. */\nfunction buildPeerRegisterBackLink(peerUrl: string): string {\n const selfUrl = `${window.location.origin}${appBasePath()}`;\n const selfName = document.title.trim() || window.location.hostname;\n const openPath = buildOpenRoutePath({\n view: \"settings\",\n to: buildSettingsRoute(STANDARD_SETTINGS_TABS.agent),\n params: {\n f_agentName: selfName,\n f_agentUrl: selfUrl,\n },\n });\n return `${peerUrl.replace(/\\/+$/, \"\")}${openPath}`;\n}\n\nfunction AgentAddPopover({\n initialName = \"\",\n initialUrl = \"\",\n initialDescription = \"\",\n secretSet,\n syncSecret,\n onAdd,\n onClose,\n}: {\n initialName?: string;\n initialUrl?: string;\n initialDescription?: string;\n secretSet: boolean | undefined;\n syncSecret: ReturnType<typeof useSyncA2ASecret>;\n onAdd: (name: string, url: string, description: string) => Promise<boolean>;\n onClose: () => void;\n}) {\n const [name, setName] = useState(initialName);\n const [url, setUrl] = useState(initialUrl);\n const [description, setDescription] = useState(initialDescription);\n const [check, setCheck] = useState<CheckState>({ status: \"idle\" });\n const [added, setAdded] = useState<AddedAgentInfo | null>(null);\n const nameRef = useRef<HTMLInputElement>(null);\n const urlRef = useRef<HTMLInputElement>(null);\n const popoverRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const t = setTimeout(\n () => (urlRef.current?.value ? nameRef : urlRef).current?.focus(),\n 50,\n );\n return () => clearTimeout(t);\n }, []);\n\n useEffect(() => {\n function handleClick(e: MouseEvent) {\n if (\n popoverRef.current &&\n !popoverRef.current.contains(e.target as Node)\n ) {\n onClose();\n }\n }\n document.addEventListener(\"mousedown\", handleClick);\n return () => document.removeEventListener(\"mousedown\", handleClick);\n }, [onClose]);\n\n const handleCheck = useCallback(async () => {\n const trimmedUrl = url.trim();\n if (!trimmedUrl) return;\n setCheck({ status: \"checking\" });\n try {\n const res = await fetch(\n agentNativePath(\n `/_agent-native/agents/probe?url=${encodeURIComponent(trimmedUrl)}`,\n ),\n );\n const body = await res.json().catch(() => null);\n if (!res.ok) {\n setCheck({\n status: \"error\",\n message: body?.error ?? `Check failed (${res.status})`,\n });\n return;\n }\n const result = body as AgentProbeResult;\n setCheck({ status: \"done\", result });\n if (result.reachable) {\n if (!name.trim() && result.name) setName(result.name);\n if (!description.trim() && result.description) {\n setDescription(result.description);\n }\n }\n } catch (err: any) {\n setCheck({ status: \"error\", message: err?.message ?? \"Check failed\" });\n }\n }, [url, name, description]);\n\n const handleAdd = async () => {\n const trimmedName = name.trim();\n const trimmedUrl = url.trim();\n if (!trimmedName || !trimmedUrl) return;\n const trimmedDescription = description.trim();\n const ok = await onAdd(trimmedName, trimmedUrl, trimmedDescription);\n if (ok) {\n setAdded({\n name: trimmedName,\n url: trimmedUrl,\n description: trimmedDescription,\n });\n }\n };\n\n if (added) {\n return (\n <div\n ref={popoverRef}\n className=\"absolute end-0 top-full z-50 mt-1 w-72 rounded-lg border border-border bg-popover p-2.5 shadow-lg\"\n >\n <div className=\"flex flex-col gap-1.5\">\n <p className=\"text-[10px] leading-relaxed text-muted-foreground\">\n Added {added.name} on your side only — registration is one-way, so\n it won&apos;t know about this app until it&apos;s added there too.\n </p>\n <a\n href={buildPeerRegisterBackLink(added.url)}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex cursor-pointer items-center justify-center gap-1 rounded bg-accent px-2 py-1 text-[10px] font-medium text-foreground no-underline hover:bg-accent/80\"\n >\n <IconExternalLink size={10} />\n Open {added.name}&apos;s settings\n </a>\n <button\n onClick={onClose}\n className=\"cursor-pointer rounded px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground\"\n >\n Dismiss\n </button>\n </div>\n </div>\n );\n }\n\n const result = check.status === \"done\" ? check.result : null;\n const unreachable = result ? !result.reachable : false;\n const unauthorized = result ? result.authorized === false : false;\n\n return (\n <div\n ref={popoverRef}\n className=\"absolute end-0 top-full z-50 mt-1 w-72 rounded-lg border border-border bg-popover p-2.5 shadow-lg\"\n >\n <div className=\"flex flex-col gap-1.5\">\n <div className=\"flex gap-1\">\n <input\n ref={urlRef}\n value={url}\n onChange={(e) => {\n setUrl(e.target.value);\n setCheck({ status: \"idle\" });\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleCheck();\n if (e.key === \"Escape\") onClose();\n }}\n className=\"w-full flex-1 rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent\"\n placeholder=\"URL (e.g. http://localhost:8085)\"\n />\n <ToolkitButtonBase\n type=\"button\"\n variant=\"outline\"\n onClick={handleCheck}\n disabled={!url.trim() || check.status === \"checking\"}\n className=\"inline-flex shrink-0 cursor-pointer items-center gap-1 rounded border border-border px-2 py-1 text-[10px] text-muted-foreground hover:bg-accent/40 hover:text-foreground disabled:opacity-40\"\n >\n {check.status === \"checking\" ? (\n <IconLoader2 size={10} className=\"animate-spin\" />\n ) : (\n \"Check\"\n )}\n </ToolkitButtonBase>\n </div>\n\n {check.status === \"error\" && (\n <p className=\"flex items-start gap-1 text-[10px] text-red-500\">\n <IconAlertTriangle size={11} className=\"mt-px shrink-0\" />\n {check.message}\n </p>\n )}\n {result && (\n <div\n className={`flex items-start gap-1 text-[10px] ${\n unreachable || unauthorized\n ? \"text-amber-600 dark:text-amber-400\"\n : \"text-green-600 dark:text-green-500\"\n }`}\n >\n {unreachable || unauthorized ? (\n <IconAlertTriangle size={11} className=\"mt-px shrink-0\" />\n ) : (\n <IconCheck size={11} className=\"mt-px shrink-0\" />\n )}\n <span className=\"leading-relaxed\">\n {describeCheckResult(result)}\n {unreachable &&\n \" — the app may just not be running yet; you can still add it.\"}\n </span>\n </div>\n )}\n {unauthorized &&\n (secretSet === true ? (\n <button\n onClick={() => syncSecret.mutate(undefined)}\n disabled={syncSecret.isPending}\n className=\"inline-flex cursor-pointer items-center gap-1 self-start rounded border border-border px-1.5 py-0.5 text-[10px] text-muted-foreground hover:text-foreground disabled:opacity-40\"\n >\n {syncSecret.isPending ? (\n <IconLoader2 size={10} className=\"animate-spin\" />\n ) : (\n <IconRefresh size={10} />\n )}\n Sync secret to apps\n </button>\n ) : (\n <p className=\"text-[10px] text-muted-foreground\">\n {secretSet === false ? (\n <>\n No shared secret set yet —{\" \"}\n <a\n href={buildSettingsRoute(STANDARD_SETTINGS_TABS.team)}\n className=\"underline underline-offset-2 hover:text-foreground\"\n >\n set one on the Team page\n </a>{\" \"}\n first.\n </>\n ) : (\n \"Ask your workspace owner to sync the shared secret.\"\n )}\n </p>\n ))}\n\n <input\n ref={nameRef}\n value={name}\n onChange={(e) => setName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleAdd();\n if (e.key === \"Escape\") onClose();\n }}\n className=\"w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent\"\n placeholder=\"Name\"\n />\n <input\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") handleAdd();\n if (e.key === \"Escape\") onClose();\n }}\n className=\"w-full rounded border border-border bg-background px-2 py-1 text-[11px] text-foreground outline-none placeholder:text-muted-foreground/50 focus:ring-1 focus:ring-accent\"\n placeholder=\"Description (optional)\"\n />\n <div className=\"flex justify-end gap-1 pt-0.5\">\n <button\n onClick={onClose}\n className=\"cursor-pointer rounded px-2 py-0.5 text-[10px] text-muted-foreground hover:text-foreground\"\n >\n Cancel\n </button>\n <button\n onClick={handleAdd}\n disabled={!name.trim() || !url.trim()}\n className=\"cursor-pointer rounded bg-accent px-2 py-0.5 text-[10px] font-medium text-foreground hover:bg-accent/80 disabled:opacity-40\"\n >\n {unreachable ? \"Add anyway\" : \"Add\"}\n </button>\n </div>\n </div>\n </div>\n );\n}\n\nfunction A2ASecretStatusRow({\n org,\n syncSecret,\n}: {\n org: { a2aSecretSet?: boolean; allowedDomain: string | null } | undefined;\n syncSecret: ReturnType<typeof useSyncA2ASecret>;\n}) {\n if (!org) return null;\n const secretSet = org.a2aSecretSet;\n\n if (secretSet === undefined) {\n // Not an owner/admin — the server omits `a2aSecretSet` entirely for this\n // role rather than reporting `false`, so this must read as \"can't see\n // it,\" never as \"not set\" (a claim we have no basis for).\n return (\n <div className=\"mb-2 rounded-md border border-border/60 bg-accent/20 px-2 py-1.5 text-[10px] text-muted-foreground\">\n Shared secret is managed by your workspace owner.\n </div>\n );\n }\n\n if (!secretSet) {\n return (\n <div className=\"mb-2 rounded-md border border-amber-500/30 bg-amber-500/10 px-2 py-1.5 text-[10px] text-amber-600 dark:text-amber-400\">\n No shared secret set — connected apps will reject calls in production.{\" \"}\n <a\n href={buildSettingsRoute(STANDARD_SETTINGS_TABS.team)}\n className=\"underline underline-offset-2 hover:text-amber-500\"\n >\n Set one on the Team page\n </a>\n </div>\n );\n }\n\n const noDomain = syncSecret.error?.message?.toLowerCase().includes(\"domain\");\n const failures = syncSecret.data?.results.filter((r) => !r.ok) ?? [];\n\n return (\n <div className=\"mb-2 flex flex-col gap-1 rounded-md border border-border/60 bg-accent/20 px-2 py-1.5 text-[10px]\">\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-muted-foreground\">\n Shared secret set\n {org.allowedDomain ? ` for ${org.allowedDomain}` : \"\"}\n </span>\n <button\n onClick={() => syncSecret.mutate(undefined)}\n disabled={syncSecret.isPending}\n className=\"inline-flex shrink-0 cursor-pointer items-center gap-1 rounded border border-border px-1.5 py-0.5 text-[10px] text-muted-foreground hover:text-foreground disabled:opacity-40\"\n >\n {syncSecret.isPending ? (\n <IconLoader2 size={10} className=\"animate-spin\" />\n ) : (\n <IconRefresh size={10} />\n )}\n Sync to apps\n </button>\n </div>\n {syncSecret.data && !syncSecret.isPending && (\n <div className=\"text-muted-foreground\">\n Synced to {syncSecret.data.succeeded}/{syncSecret.data.total} app\n {syncSecret.data.total === 1 ? \"\" : \"s\"}\n {syncSecret.data.failed > 0\n ? ` (${syncSecret.data.failed} failed)`\n : \"\"}\n .\n {failures.length > 0 && (\n <ul className=\"mt-0.5 list-disc ps-3 text-red-500\">\n {failures.map((r) => (\n <li key={r.id}>\n {r.name}: {r.error ?? `HTTP ${r.status ?? \"?\"}`}\n </li>\n ))}\n </ul>\n )}\n </div>\n )}\n {syncSecret.error && (\n <p className=\"text-red-500\">\n {syncSecret.error.message}\n {noDomain && (\n <>\n {\" \"}\n <a\n href={buildSettingsRoute(STANDARD_SETTINGS_TABS.team)}\n className=\"underline underline-offset-2\"\n >\n Set the domain\n </a>\n </>\n )}\n </p>\n )}\n </div>\n );\n}\n\n/** Query params `/_agent-native/open` forwards onto the redirect target. */\nconst PREFILL_PARAMS = [\n \"f_agentName\",\n \"f_agentUrl\",\n \"f_agentDescription\",\n] as const;\n\nexport function AgentsSection() {\n const [agents, setAgents] = useState<AgentInfo[]>([]);\n const [loading, setLoading] = useState(true);\n const [editingAgent, setEditingAgent] = useState<string | null>(null);\n const [showAdd, setShowAdd] = useState(false);\n const [prefill, setPrefill] = useState<{\n name: string;\n url: string;\n description: string;\n } | null>(null);\n const [probeById, setProbeById] = useState<Map<\n string,\n AgentProbeResult\n > | null>(null);\n\n const { data: org } = useOrg();\n const syncSecret = useSyncA2ASecret();\n\n // Landing from a peer's \"register back\" deep link (see\n // buildPeerRegisterBackLink): the open route only echoes `f_*` params onto\n // the redirect URL, so read those here and open the Add popover prefilled.\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const params = new URLSearchParams(window.location.search);\n const url = params.get(\"f_agentUrl\");\n if (!url) return;\n setPrefill({\n name: params.get(\"f_agentName\") ?? \"\",\n url,\n description: params.get(\"f_agentDescription\") ?? \"\",\n });\n setShowAdd(true);\n for (const key of PREFILL_PARAMS) params.delete(key);\n const query = params.toString();\n window.history.replaceState(\n null,\n \"\",\n `${window.location.pathname}${query ? `?${query}` : \"\"}${window.location.hash}`,\n );\n }, []);\n\n // One batched probe for the whole list — cheap liveness dots, not one\n // request per row. A row absent from the results (never returned) stays\n // dot-less rather than defaulting to a color that isn't backed by data.\n useEffect(() => {\n let cancelled = false;\n fetch(agentNativePath(\"/_agent-native/agents/probe\"))\n .then((res) => (res.ok ? res.json() : null))\n .then((data) => {\n if (cancelled || !data) return;\n const results = Array.isArray(data.results)\n ? (data.results as Array<AgentProbeResult & { id: string }>)\n : [];\n const map = new Map<string, AgentProbeResult>();\n for (const result of results) map.set(result.id.toLowerCase(), result);\n setProbeById(map);\n })\n .catch(() => {\n if (!cancelled) setProbeById(new Map());\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n const fetchAgents = useCallback(async () => {\n try {\n const res = await fetch(\n agentNativePath(\"/_agent-native/resources?scope=all\"),\n );\n if (!res.ok) return;\n const data = await res.json();\n // Migrating a remote agent to the canonical `remote-agents/` prefix\n // leaves the legacy `agents/` row in place (resources/store.ts), so\n // every migrated agent has two resources pointing at one URL. Collapse\n // them the way discoverAgents does, canonical winning.\n const byAgentId = new Map<string, { id: string; path: string }>();\n for (const resource of (data.resources ?? []) as Array<{\n id: string;\n path: string;\n }>) {\n if (!isRemoteAgentPath(resource.path)) continue;\n const agentId = getRemoteAgentIdFromPath(resource.path);\n const existing = byAgentId.get(agentId);\n if (existing?.path.startsWith(REMOTE_AGENT_RESOURCE_PREFIX)) continue;\n byAgentId.set(agentId, resource);\n }\n const agentResources = [...byAgentId.values()];\n const parsed = await Promise.all(\n agentResources.map(async (r): Promise<AgentInfo | null> => {\n try {\n const detail = await fetch(\n agentNativePath(`/_agent-native/resources/${r.id}`),\n );\n if (!detail.ok) return null;\n const d = await detail.json();\n const config = JSON.parse(d.content);\n return {\n id: r.id,\n path: r.path,\n name: config.name,\n url: config.url,\n description: config.description,\n };\n } catch {\n return null;\n }\n }),\n );\n setAgents(parsed.filter((agent): agent is AgentInfo => agent !== null));\n } finally {\n setLoading(false);\n }\n }, []);\n\n useEffect(() => {\n fetchAgents();\n }, [fetchAgents]);\n\n const handleAdd = async (\n name: string,\n url: string,\n description: string,\n ): Promise<boolean> => {\n const id = name.toLowerCase().replace(/[^a-z0-9-]/g, \"-\");\n const agentJson = JSON.stringify(\n {\n id,\n name,\n description: description || undefined,\n url,\n color: \"#6B7280\",\n },\n null,\n 2,\n );\n\n try {\n const res = await fetch(agentNativePath(\"/_agent-native/resources\"), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n path: remoteAgentResourcePath(id),\n content: agentJson,\n shared: true,\n }),\n });\n // Deliberately don't close the popover here — a successful add shows a\n // follow-up state (registration is one-way; the peer doesn't know\n // about us yet) that the user dismisses explicitly.\n if (res.ok) fetchAgents();\n return res.ok;\n } catch {\n return false;\n }\n };\n\n const handleSave = async (agent: AgentInfo) => {\n const agentJson = JSON.stringify(\n {\n id: getRemoteAgentIdFromPath(agent.path),\n name: agent.name,\n description: agent.description || undefined,\n url: agent.url,\n color: \"#6B7280\",\n },\n null,\n 2,\n );\n\n try {\n const res = await fetch(\n agentNativePath(`/_agent-native/resources/${agent.id}`),\n {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ content: agentJson }),\n },\n );\n if (res.ok) {\n setEditingAgent(null);\n fetchAgents();\n }\n } catch {}\n };\n\n const handleDelete = async (agentId: string) => {\n try {\n const res = await fetch(\n agentNativePath(`/_agent-native/resources/${agentId}`),\n {\n method: \"DELETE\",\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n if (res.ok) {\n setEditingAgent(null);\n fetchAgents();\n }\n } catch {}\n };\n\n return (\n <div>\n {/* Header with + button */}\n <div className=\"flex items-center justify-between mb-2\">\n <div className=\"text-[10px] text-muted-foreground\">\n @-mention agents in chat to delegate tasks via A2A.\n </div>\n <div className=\"relative\">\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n onClick={() => {\n setShowAdd(!showAdd);\n setEditingAgent(null);\n }}\n className=\"flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50\"\n >\n {showAdd ? <IconX size={12} /> : <IconPlus size={12} />}\n </button>\n </TooltipTrigger>\n <TooltipContent>Add agent</TooltipContent>\n </Tooltip>\n {showAdd && (\n <AgentAddPopover\n initialName={prefill?.name}\n initialUrl={prefill?.url}\n initialDescription={prefill?.description}\n secretSet={org?.a2aSecretSet}\n syncSecret={syncSecret}\n onAdd={handleAdd}\n onClose={() => {\n setShowAdd(false);\n setPrefill(null);\n }}\n />\n )}\n </div>\n </div>\n\n <A2ASecretStatusRow org={org} syncSecret={syncSecret} />\n\n {/* Agent list */}\n {loading ? (\n <div className=\"space-y-1.5\">\n <div className=\"h-6 w-full rounded bg-muted/50 animate-pulse\" />\n <div className=\"h-6 w-3/4 rounded bg-muted/50 animate-pulse\" />\n </div>\n ) : agents.length === 0 ? (\n <button\n onClick={() => setShowAdd(true)}\n className=\"flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-[11px] text-muted-foreground hover:text-foreground hover:bg-accent/30\"\n >\n <IconPlus size={12} className=\"shrink-0\" />\n Add agent\n </button>\n ) : (\n <div className=\"flex flex-col gap-0.5\">\n {agents.map((agent) => {\n const probe = probeById?.get(\n getRemoteAgentIdFromPath(agent.path).toLowerCase(),\n );\n const dotState: \"ok\" | \"warn\" | null = !probe\n ? null\n : probe.reachable && probe.authorized !== false\n ? \"ok\"\n : \"warn\";\n return (\n <div key={agent.id} className=\"group relative\">\n <div className=\"flex w-full items-center gap-2 rounded-md px-2 py-1.5 hover:bg-accent/30\">\n {dotState ? (\n <Tooltip>\n <TooltipTrigger asChild>\n <span\n className={`h-1.5 w-1.5 shrink-0 rounded-full ${\n dotState === \"ok\" ? \"bg-green-500\" : \"bg-amber-500\"\n }`}\n />\n </TooltipTrigger>\n <TooltipContent>\n {probe && describeProbeTooltip(probe)}\n </TooltipContent>\n </Tooltip>\n ) : (\n <span className=\"h-1.5 w-1.5 shrink-0 rounded-full bg-transparent\" />\n )}\n <span className=\"text-[11px] font-medium text-foreground truncate shrink-0\">\n {agent.name}\n </span>\n <span className=\"flex-1 text-[10px] text-muted-foreground/60 truncate text-end\">\n {agent.url}\n </span>\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n onClick={() => {\n setEditingAgent(\n editingAgent === agent.id ? null : agent.id,\n );\n setShowAdd(false);\n }}\n className=\"shrink-0 rounded p-0.5 text-muted-foreground opacity-0 group-hover:opacity-100 hover:text-foreground hover:bg-accent/50\"\n >\n <IconPencil size={11} />\n </button>\n </TooltipTrigger>\n <TooltipContent>Edit agent</TooltipContent>\n </Tooltip>\n </div>\n {editingAgent === agent.id && (\n <AgentEditPopover\n agent={agent}\n onSave={handleSave}\n onDelete={handleDelete}\n onClose={() => setEditingAgent(null)}\n />\n )}\n </div>\n );\n })}\n <button\n onClick={() => {\n setShowAdd(true);\n setEditingAgent(null);\n }}\n className=\"flex w-full items-center gap-1.5 rounded-md px-2 py-1.5 text-[11px] text-muted-foreground hover:text-foreground hover:bg-accent/30\"\n >\n <IconPlus size={12} className=\"shrink-0\" />\n Add agent\n </button>\n </div>\n )}\n </div>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"SecretsSection.d.ts","sourceRoot":"","sources":["../../../src/client/settings/SecretsSection.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAiBH,OAAO,KAAoD,MAAM,OAAO,CAAC;AA8DzE,MAAM,WAAW,mBAAmB;IAClC,yFAAyF;IACzF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,EAAE,mBAAmB,qBA8G/D"}
1
+ {"version":3,"file":"SecretsSection.d.ts","sourceRoot":"","sources":["../../../src/client/settings/SecretsSection.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AA0BH,OAAO,KAAoD,MAAM,OAAO,CAAC;AA2DzE,MAAM,WAAW,mBAAmB;IAClC,yFAAyF;IACzF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,EAAE,mBAAmB,qBA8G/D"}