@agent-native/core 0.148.0 → 0.149.0
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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/changelog/2026-08-10-wide-tables-in-analytics-chat-now-scroll-horizontally-within.md +6 -0
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +13 -53
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -70
- package/corpus/templates/calendar/app/lib/event-layout.ts +122 -0
- package/corpus/templates/calendar/app/routes/_app.availability.tsx +1 -1
- package/corpus/templates/calendar/changelog/2026-08-10-opening-availability-settings-from-the-agent-lands-on-the-av.md +6 -0
- package/corpus/templates/calendar/vitest.config.ts +6 -1
- package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +3 -1
- package/corpus/templates/clips/app/components/player/video-player.tsx +44 -4
- package/corpus/templates/clips/changelog/2026-08-10-fixed-transcription-failures-for-recordings-without-captured.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-10-mobile-clips-can-now-enter-fullscreen-on-iphone-safari.md +6 -0
- package/corpus/templates/content/actions/_property-utils.ts +11 -1
- package/corpus/templates/content/actions/view-screen.ts +62 -12
- package/corpus/templates/content/server/plugins/agent-chat.ts +2 -0
- package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +22 -0
- package/corpus/templates/design/AGENTS.md +3 -0
- package/corpus/templates/design/actions/create-design-from-template.ts +19 -0
- package/corpus/templates/design/actions/get-design-snapshot.ts +18 -0
- package/corpus/templates/design/actions/get-design-template.ts +178 -0
- package/corpus/templates/design/actions/list-designs.ts +3 -1
- package/corpus/templates/design/actions/view-screen.ts +31 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +225 -214
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
- package/corpus/templates/design/app/i18n-data.ts +78 -0
- package/corpus/templates/design/app/lib/design-authors.ts +64 -0
- package/corpus/templates/design/app/pages/Index.tsx +114 -4
- package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +1 -0
- package/corpus/templates/design/changelog/2026-08-07-alt-dragging-an-element-to-copy-it-now-keeps-the-copy-visibl.md +6 -0
- package/corpus/templates/design/changelog/2026-08-07-design-cards-now-show-who-created-each-design-with-a-filter-.md +6 -0
- package/corpus/templates/design/changelog/2026-08-07-drawing-a-shape-on-the-overview-canvas-now-keeps-the-shape-s.md +6 -0
- package/corpus/templates/design/changelog/2026-08-07-hold-option-alt-while-dragging-a-frame-on-the-overview-canva.md +6 -0
- package/corpus/templates/design/server/lib/design-template-data.ts +143 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/forms/AGENTS.md +2 -2
- package/corpus/templates/forms/actions/create-form.ts +1 -1
- package/corpus/templates/forms/actions/update-form.ts +5 -5
- package/corpus/templates/forms/changelog/2026-08-10-forms-now-keep-agent-applied-field-changes-visible-after-an-.md +6 -0
- package/corpus/templates/forms/changelog/2026-08-10-published-form-links-always-use-the-working-public-form-url.md +6 -0
- package/corpus/templates/forms/server/plugins/agent-chat.ts +2 -2
- package/corpus/templates/slides/AGENTS.md +4 -3
- package/corpus/templates/slides/actions/import-google-slides-reference.ts +56 -10
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +12 -74
- package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +5 -1
- package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +15 -4
- package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +116 -55
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -0
- package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +143 -18
- package/corpus/templates/slides/app/i18n/en-US.ts +6 -0
- package/corpus/templates/slides/app/lib/generation-state.ts +7 -1
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +32 -14
- package/corpus/templates/slides/app/pages/Index.tsx +110 -128
- package/corpus/templates/slides/changelog/2026-08-10-imported-reference-decks-now-show-clear-success-and-stay-sel.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-pasted-google-slides-links-import-after-reconnecting-google-.md +6 -0
- package/corpus/templates/slides/server/handlers/google-docs-auth.ts +4 -0
- package/corpus/templates/slides/server/lib/google-docs-oauth.ts +13 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +1 -1
- package/dist/agent/engine/registry.d.ts +8 -2
- package/dist/agent/engine/registry.js +63 -29
- package/dist/agent/production-agent.js +7 -0
- package/dist/agent/run-manager.d.ts +5 -4
- package/dist/agent/run-manager.js +6 -5
- package/dist/cli/claude-code-participant.d.ts +2 -1
- package/dist/cli/claude-code-participant.js +19 -0
- package/dist/cli/code-agent-executor.js +223 -0
- package/dist/cli/create-workspace.js +3 -3
- package/dist/cli/create.d.ts +3 -2
- package/dist/cli/create.js +20 -24
- package/dist/client/AgentPanel.js +8 -4
- package/dist/client/AssistantChat.js +34 -1
- package/dist/client/agent-chat-adapter.d.ts +1 -0
- package/dist/client/agent-chat-adapter.js +10 -2
- package/dist/client/blocks/library/diagram.js +1 -1
- package/dist/client/blocks/library/sanitize-html.js +66 -2
- package/dist/client/blocks/library/wireframe-kit.js +69 -0
- package/dist/client/chat/markdown-renderer.d.ts +1 -0
- package/dist/client/chat/markdown-renderer.js +4 -0
- package/dist/client/chat/message-components.d.ts +1 -0
- package/dist/client/chat/message-components.js +9 -1
- package/dist/client/chat/tool-call-display.d.ts +8 -0
- package/dist/client/chat/tool-call-display.js +30 -21
- package/dist/client/chat-first/app-pane.js +1 -1
- package/dist/client/chat-first/apps-rail.d.ts +1 -1
- package/dist/client/chat-first/apps-rail.js +12 -12
- package/dist/client/chat-first/chat-history.d.ts +1 -1
- package/dist/client/chat-first/chat-history.js +2 -0
- package/dist/client/chat-first/copy.js +1 -0
- package/dist/client/chat-first/primary-nav.js +1 -1
- package/dist/client/chat-first/types.d.ts +1 -0
- package/dist/client/resources/McpConnectionSuggestion.js +2 -2
- package/dist/client/settings/SettingsTabsPage.d.ts +3 -1
- package/dist/client/settings/SettingsTabsPage.js +2 -2
- package/dist/client/sse-event-processor.d.ts +8 -0
- package/dist/client/sse-event-processor.js +13 -6
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.d.ts +5 -7
- package/dist/deploy/build.js +19 -12
- package/dist/localization/default-messages.js +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/provider-api/actions/provider-api.d.ts +2 -2
- package/dist/scripts/call-agent.js +3 -3
- package/dist/scripts/describe-workspace-apps.js +1 -1
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-capabilities.js +0 -0
- package/dist/server/agent-chat/context-tools.js +1 -1
- package/dist/server/agent-chat/prompt-resources.js +1 -1
- package/dist/server/agent-chat-plugin.js +1 -1
- package/dist/server/auth.js +190 -42
- package/dist/server/better-auth-migrations.js +53 -0
- package/dist/server/http-response-telemetry.js +7 -1
- package/dist/server/onboarding-html.js +119 -33
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/password-policy.d.ts +2 -1
- package/dist/shared/password-policy.js +2 -1
- package/dist/styles/agent-conversation.css +20 -3
- package/dist/styles/agent-native.css +14 -2
- package/dist/styles/blocks.css +132 -0
- package/dist/templates/default/app/i18n/ar-SA.ts +1 -1
- package/dist/templates/default/app/i18n/de-DE.ts +1 -1
- package/dist/templates/default/app/i18n/en-US.ts +1 -1
- package/dist/templates/default/app/i18n/es-ES.ts +1 -1
- package/dist/templates/default/app/i18n/fr-FR.ts +1 -1
- package/dist/templates/default/app/i18n/hi-IN.ts +1 -1
- package/dist/templates/default/app/i18n/ja-JP.ts +1 -1
- package/dist/templates/default/app/i18n/ko-KR.ts +1 -1
- package/dist/templates/default/app/i18n/pt-BR.ts +1 -1
- package/dist/templates/default/app/i18n/zh-CN.ts +1 -1
- package/dist/templates/default/app/i18n/zh-TW.ts +1 -1
- package/dist/templates/default/pnpm-workspace.yaml +1 -0
- package/dist/templates/workspace-root/pnpm-workspace.yaml +2 -0
- package/docs/content/actions.mdx +4 -1
- package/docs/content/agent-surfaces.mdx +8 -6
- package/docs/content/automations.mdx +1 -1
- package/docs/content/cloneable-saas.mdx +6 -6
- package/docs/content/database.mdx +6 -1
- package/docs/content/deployment.mdx +10 -8
- package/docs/content/docs-components.mdx +22 -14
- package/docs/content/drop-in-agent.mdx +1 -1
- package/docs/content/environment-variables.mdx +4 -3
- package/docs/content/getting-started-actions.mdx +5 -4
- package/docs/content/getting-started-pages.mdx +7 -6
- package/docs/content/getting-started.mdx +4 -3
- package/docs/content/notifications.mdx +6 -6
- package/docs/content/progress.mdx +5 -5
- package/docs/content/routing.mdx +3 -1
- package/docs/content/server-database.mdx +13 -4
- package/docs/content/server-overview.mdx +9 -9
- package/docs/content/server-plugins.mdx +10 -4
- package/docs/content/server-routes.mdx +7 -2
- package/docs/content/tracking.mdx +4 -4
- package/docs/content/what-is-agent-native.mdx +4 -4
- package/package.json +2 -2
- package/src/templates/default/app/i18n/ar-SA.ts +1 -1
- package/src/templates/default/app/i18n/de-DE.ts +1 -1
- package/src/templates/default/app/i18n/en-US.ts +1 -1
- package/src/templates/default/app/i18n/es-ES.ts +1 -1
- package/src/templates/default/app/i18n/fr-FR.ts +1 -1
- package/src/templates/default/app/i18n/hi-IN.ts +1 -1
- package/src/templates/default/app/i18n/ja-JP.ts +1 -1
- package/src/templates/default/app/i18n/ko-KR.ts +1 -1
- package/src/templates/default/app/i18n/pt-BR.ts +1 -1
- package/src/templates/default/app/i18n/zh-CN.ts +1 -1
- package/src/templates/default/app/i18n/zh-TW.ts +1 -1
- package/src/templates/default/pnpm-workspace.yaml +1 -0
- package/src/templates/workspace-root/pnpm-workspace.yaml +2 -0
|
@@ -75,6 +75,7 @@ function prepareToolStackExitClone(clone, snapshot) {
|
|
|
75
75
|
export function ToolCallStackMotion({ children, className, }) {
|
|
76
76
|
const stackRef = useRef(null);
|
|
77
77
|
const previousRef = useRef(new Map());
|
|
78
|
+
const previousStructureRef = useRef([]);
|
|
78
79
|
const activeMotionsRef = useRef(new Map());
|
|
79
80
|
const exitAnimationsRef = useRef(new Map());
|
|
80
81
|
useLayoutEffect(() => {
|
|
@@ -104,6 +105,11 @@ export function ToolCallStackMotion({ children, className, }) {
|
|
|
104
105
|
}
|
|
105
106
|
exitAnimationsRef.current.clear();
|
|
106
107
|
const elements = Array.from(stack.querySelectorAll("[data-agent-tool-call-id], [data-agent-tool-summary]"));
|
|
108
|
+
const structure = elements.map((element, index) => toolStackMotionKey(element, index));
|
|
109
|
+
const previousStructure = previousStructureRef.current;
|
|
110
|
+
const structureChanged = structure.length !== previousStructure.length ||
|
|
111
|
+
structure.some((key, index) => key !== previousStructure[index]);
|
|
112
|
+
previousStructureRef.current = structure;
|
|
107
113
|
const current = new Map();
|
|
108
114
|
for (const [index, element] of elements.entries()) {
|
|
109
115
|
const rect = element.getBoundingClientRect();
|
|
@@ -116,7 +122,11 @@ export function ToolCallStackMotion({ children, className, }) {
|
|
|
116
122
|
});
|
|
117
123
|
}
|
|
118
124
|
previousRef.current = current;
|
|
119
|
-
|
|
125
|
+
// Entry motion changes the measured transform without changing the stack
|
|
126
|
+
// structure. Do not turn that animation's cleanup into a second FLIP pass.
|
|
127
|
+
if (previous.size === 0 ||
|
|
128
|
+
!structureChanged ||
|
|
129
|
+
prefersReducedMotionForToolStack())
|
|
120
130
|
return;
|
|
121
131
|
const summary = elements.find((element) => element.dataset.agentToolSummary !== undefined);
|
|
122
132
|
const summaryRect = summary?.getBoundingClientRect();
|
|
@@ -227,13 +237,6 @@ export function ToolActivityPresentation({ toolName, isRunning, isActiveTail, to
|
|
|
227
237
|
// Presentation follows the active chat tail rather than execution state so
|
|
228
238
|
// that newly revealed completed tools still get their entrance motion.
|
|
229
239
|
const [animateEntry, setAnimateEntry] = useState(isActiveTail);
|
|
230
|
-
const previousActiveTailRef = useRef(isActiveTail);
|
|
231
|
-
useEffect(() => {
|
|
232
|
-
if (isActiveTail && !previousActiveTailRef.current) {
|
|
233
|
-
setAnimateEntry(true);
|
|
234
|
-
}
|
|
235
|
-
previousActiveTailRef.current = isActiveTail;
|
|
236
|
-
}, [isActiveTail]);
|
|
237
240
|
useEffect(() => {
|
|
238
241
|
if (!animateEntry)
|
|
239
242
|
return;
|
|
@@ -438,29 +441,35 @@ export function AnimatedCollapse({ open, children, }) {
|
|
|
438
441
|
* turn. Approve re-issues the turn with the call's `approvalKey`; Deny dismisses
|
|
439
442
|
* the prompt locally (the action stays un-run).
|
|
440
443
|
*/
|
|
441
|
-
function ApprovalAffordance({ toolName, approval, }) {
|
|
444
|
+
function ApprovalAffordance({ toolName, toolCallId, approval, }) {
|
|
442
445
|
const ctx = React.useContext(ApprovalContext);
|
|
443
|
-
const [
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
446
|
+
const [localResolution, setLocalResolution] = useState(null);
|
|
447
|
+
const retainedResolution = ctx?.getApprovalResolution?.(approval.approvalKey, toolCallId) ?? null;
|
|
448
|
+
const resolution = retainedResolution ??
|
|
449
|
+
localResolution ??
|
|
450
|
+
(approval.dismissed === true ? "denied" : null);
|
|
451
|
+
// Once resolved, collapse to a quiet note so a repository refresh cannot
|
|
452
|
+
// restore the action buttons while the continuation is running.
|
|
453
|
+
if (resolution === "approved") {
|
|
448
454
|
return (_jsxs("div", { className: "mt-1.5 text-xs text-muted-foreground", children: ["Approved. Re-running ", toolName, "..."] }));
|
|
449
455
|
}
|
|
450
456
|
// Deny defaults to local-only (the action simply stays un-run). When the
|
|
451
457
|
// host also provided `onDeny` (e.g. a Code session resolving its own
|
|
452
458
|
// pending approval), it fires alongside the local state.
|
|
453
|
-
if (denied) {
|
|
459
|
+
if (resolution === "denied") {
|
|
454
460
|
return (_jsxs("div", { className: "mt-1.5 text-xs text-muted-foreground", children: ["Denied. ", toolName, " did not run."] }));
|
|
455
461
|
}
|
|
456
462
|
return (_jsxs("div", { className: "mt-1.5 flex items-center gap-2 rounded-md border border-border bg-muted/40 px-2.5 py-1.5", children: [_jsx(IconShieldCheck, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsxs("span", { className: "mr-auto text-xs text-muted-foreground", children: ["Approve to run ", toolName, "?"] }), ctx && (_jsxs("button", { type: "button", onClick: () => {
|
|
457
|
-
|
|
463
|
+
setLocalResolution("approved");
|
|
464
|
+
ctx.onApprovalResolved?.(approval.approvalKey, "approved", toolCallId);
|
|
458
465
|
ctx.onApprove(approval.approvalKey);
|
|
459
466
|
}, className: cn("inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90"), children: [_jsx(IconCheck, { className: "h-3.5 w-3.5" }), "Approve"] })), ctx?.onAlwaysAllow && (_jsxs("button", { type: "button", onClick: () => {
|
|
460
|
-
|
|
467
|
+
setLocalResolution("approved");
|
|
468
|
+
ctx.onApprovalResolved?.(approval.approvalKey, "approved", toolCallId);
|
|
461
469
|
ctx.onAlwaysAllow?.(approval.approvalKey);
|
|
462
470
|
}, title: "Approve and always allow this exact command", className: cn("inline-flex items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium transition-colors", "text-foreground hover:bg-muted"), children: [_jsx(IconShieldCheck, { className: "h-3.5 w-3.5" }), "Always allow"] })), _jsxs("button", { type: "button", onClick: () => {
|
|
463
|
-
|
|
471
|
+
setLocalResolution("denied");
|
|
472
|
+
ctx?.onApprovalResolved?.(approval.approvalKey, "denied", toolCallId);
|
|
464
473
|
ctx?.onDeny?.(approval.approvalKey);
|
|
465
474
|
}, className: cn("inline-flex items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium transition-colors", "text-foreground hover:bg-muted"), children: [_jsx(IconX, { className: "h-3.5 w-3.5" }), "Deny"] })] }));
|
|
466
475
|
}
|
|
@@ -481,9 +490,9 @@ export function ToolCallDisplay({ toolName, toolCallId, argsText, args, result,
|
|
|
481
490
|
if (toolKind === "write") {
|
|
482
491
|
return wrapToolDisplay(_jsx(WriteCell, { meta: structuredMeta, isRunning: isRunning }));
|
|
483
492
|
}
|
|
484
|
-
return wrapToolDisplay(_jsx(ToolCallDisplayGeneric, { toolName: toolName, argsText: argsText, args: args, result: result, mcpApp: mcpApp, chatUI: chatUI, isRunning: isRunning, outcome: outcome, isActiveTail: showActiveTail, structuredMeta: structuredMeta, approval: approval, repeatCount: repeatCount }));
|
|
493
|
+
return wrapToolDisplay(_jsx(ToolCallDisplayGeneric, { toolName: toolName, toolCallId: toolCallId, argsText: argsText, args: args, result: result, mcpApp: mcpApp, chatUI: chatUI, isRunning: isRunning, outcome: outcome, isActiveTail: showActiveTail, structuredMeta: structuredMeta, approval: approval, repeatCount: repeatCount }));
|
|
485
494
|
}
|
|
486
|
-
function ToolCallDisplayGeneric({ toolName, argsText, args, result, mcpApp, chatUI, isRunning, outcome, isActiveTail, structuredMeta, approval, repeatCount, }) {
|
|
495
|
+
function ToolCallDisplayGeneric({ toolName, toolCallId, argsText, args, result, mcpApp, chatUI, isRunning, outcome, isActiveTail, structuredMeta, approval, repeatCount, }) {
|
|
487
496
|
const isRawCallAgent = toolName === "call-agent";
|
|
488
497
|
const isAgentCall = toolName.startsWith("agent:") || isRawCallAgent;
|
|
489
498
|
const [expanded, setExpanded] = useState(isAgentCall);
|
|
@@ -585,7 +594,7 @@ function ToolCallDisplayGeneric({ toolName, argsText, args, result, mcpApp, chat
|
|
|
585
594
|
? structuredMeta.agentDurationMs
|
|
586
595
|
: agentActivity?.durationMs }));
|
|
587
596
|
}
|
|
588
|
-
return (_jsxs("div", { className: "group/tool my-0.5 w-full overflow-hidden", children: [mcpApp && _jsx(McpAppRenderer, { app: mcpApp, className: "mb-1.5" }), _jsxs("button", { type: "button", onClick: () => canExpand && setExpanded(!isExpanded), "aria-expanded": canExpand ? isExpanded : undefined, className: cn("flex w-full items-center gap-1.5 rounded-md py-0.5 text-left text-[13px] text-muted-foreground transition-colors", canExpand && "hover:text-foreground", isRunning && "text-muted-foreground"), children: [_jsx("span", { className: "relative flex size-4 shrink-0 items-center justify-center", children: isRunning ? (_jsx(IconLoader2, { className: "size-3.5 animate-spin" })) : isAgentError ? (_jsx(IconCircleX, { className: "size-3.5 text-destructive" })) : isUnknownOutcome ? (_jsx(IconAlertTriangle, { className: "size-3.5 text-muted-foreground" })) : (_jsxs(_Fragment, { children: [_jsx(ToolIcon, { className: cn("size-3.5 transition-opacity", canExpand && "group-hover/tool:opacity-0") }), canExpand && (_jsx(IconChevronRight, { className: cn("absolute size-3.5 opacity-0 transition-[opacity,transform] group-hover/tool:opacity-100", isExpanded && "rotate-90") }))] })) }), _jsx("span", { className: cn("min-w-0 truncate font-normal", isActiveTail && "agent-running-shimmer"), children: displayName }), repeatCount && repeatCount > 1 && (_jsxs("span", { className: "shrink-0 rounded border border-border/60 px-1.5 py-0.5 text-[10px] leading-none text-muted-foreground", title: `Repeated ${repeatCount} times`, children: [repeatCount, "x"] }))] }), _jsx(AnimatedCollapse, { open: isExpanded && !isAgentCall && (hasArgs || result !== undefined), children: _jsxs("div", { className: "mt-1 space-y-2 pl-5", children: [inputPayload && (_jsx(SimpleCodeViewer, { text: inputPayload.text, lang: inputPayload.lang })), resultPayload && (_jsx(ToolOutputPopover, { open: outputOpen, onOpenChange: setOutputOpen, title: outputTitle, payload: resultPayload, children: _jsx("button", { type: "button", "aria-label": `View ${toolName} output`, className: "inline-flex size-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors hover:bg-accent hover:text-foreground", children: _jsx(IconCode, { className: "size-3.5" }) }) }))] }) }), isUnknownOutcome && (_jsx("p", { role: "status", className: "ps-5 text-xs text-muted-foreground", children: "Interrupted before this finished reporting \u2014 it may or may not have completed. Check before retrying." })), approval && (_jsx(ApprovalAffordance, { toolName: toolName, approval: approval }))] }));
|
|
597
|
+
return (_jsxs("div", { className: "group/tool my-0.5 w-full overflow-hidden", children: [mcpApp && _jsx(McpAppRenderer, { app: mcpApp, className: "mb-1.5" }), _jsxs("button", { type: "button", onClick: () => canExpand && setExpanded(!isExpanded), "aria-expanded": canExpand ? isExpanded : undefined, className: cn("flex w-full items-center gap-1.5 rounded-md py-0.5 text-left text-[13px] text-muted-foreground transition-colors", canExpand && "hover:text-foreground", isRunning && "text-muted-foreground"), children: [_jsx("span", { className: "relative flex size-4 shrink-0 items-center justify-center", children: isRunning ? (_jsx(IconLoader2, { className: "size-3.5 animate-spin" })) : isAgentError ? (_jsx(IconCircleX, { className: "size-3.5 text-destructive" })) : isUnknownOutcome ? (_jsx(IconAlertTriangle, { className: "size-3.5 text-muted-foreground" })) : (_jsxs(_Fragment, { children: [_jsx(ToolIcon, { className: cn("size-3.5 transition-opacity", canExpand && "group-hover/tool:opacity-0") }), canExpand && (_jsx(IconChevronRight, { className: cn("absolute size-3.5 opacity-0 transition-[opacity,transform] group-hover/tool:opacity-100", isExpanded && "rotate-90") }))] })) }), _jsx("span", { className: cn("min-w-0 truncate font-normal", isActiveTail && "agent-running-shimmer"), children: displayName }), repeatCount && repeatCount > 1 && (_jsxs("span", { className: "shrink-0 rounded border border-border/60 px-1.5 py-0.5 text-[10px] leading-none text-muted-foreground", title: `Repeated ${repeatCount} times`, children: [repeatCount, "x"] }))] }), _jsx(AnimatedCollapse, { open: isExpanded && !isAgentCall && (hasArgs || result !== undefined), children: _jsxs("div", { className: "mt-1 space-y-2 pl-5", children: [inputPayload && (_jsx(SimpleCodeViewer, { text: inputPayload.text, lang: inputPayload.lang })), resultPayload && (_jsx(ToolOutputPopover, { open: outputOpen, onOpenChange: setOutputOpen, title: outputTitle, payload: resultPayload, children: _jsx("button", { type: "button", "aria-label": `View ${toolName} output`, className: "inline-flex size-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors hover:bg-accent hover:text-foreground", children: _jsx(IconCode, { className: "size-3.5" }) }) }))] }) }), isUnknownOutcome && (_jsx("p", { role: "status", className: "ps-5 text-xs text-muted-foreground", children: "Interrupted before this finished reporting \u2014 it may or may not have completed. Check before retrying." })), approval && (_jsx(ApprovalAffordance, { toolName: toolName, toolCallId: toolCallId, approval: approval }))] }));
|
|
589
598
|
}
|
|
590
599
|
function AgentCallCell({ agentName, activity, progress, responseText, isRunning, isError, durationMs, }) {
|
|
591
600
|
const t = useT();
|
|
@@ -2,5 +2,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Skeleton } from "@agent-native/toolkit/ui";
|
|
3
3
|
import { defaultChatFirstCopy } from "./copy.js";
|
|
4
4
|
export function ChatFirstAppPane({ app, status, embedUrl, errorMessage, onRetry, renderEmbed, copy = defaultChatFirstCopy, }) {
|
|
5
|
-
return (_jsx("section", { "data-chat-first-app-pane": true, className: "flex h-full min-h-0 w-full flex-col overflow-hidden bg-background", "aria-label": app ? `${app.name} app` : copy("appUnavailable"), children: status === "loading" ? (_jsxs("div", { className: "space-y-3 p-4", children: [_jsx(Skeleton, { className: "h-5 w-40" }), _jsx(Skeleton, { className: "h-4 w-full" }), _jsx(Skeleton, { className: "h-4 w-2/3" })] })) : status === "ready" && app && embedUrl ? (_jsx("div", { className: "
|
|
5
|
+
return (_jsx("section", { "data-chat-first-app-pane": true, className: "flex h-full min-h-0 w-full flex-col overflow-hidden bg-background", "aria-label": app ? `${app.name} app` : copy("appUnavailable"), children: status === "loading" ? (_jsxs("div", { className: "space-y-3 p-4", children: [_jsx(Skeleton, { className: "h-5 w-40" }), _jsx(Skeleton, { className: "h-4 w-full" }), _jsx(Skeleton, { className: "h-4 w-2/3" })] })) : status === "ready" && app && embedUrl ? (_jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: renderEmbed({ url: embedUrl, title: app.name }) })) : status === "error" ? (_jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-3 p-6 text-center", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: errorMessage || copy("appUnavailable") }), onRetry ? (_jsx("button", { type: "button", className: "rounded-md border border-border px-3 py-1.5 text-xs text-foreground hover:bg-accent", onClick: onRetry, children: copy("retry") })) : null] })) : (_jsx("div", { className: "flex h-full items-center justify-center p-6 text-center text-sm text-muted-foreground", children: copy("appUnavailable") })) }));
|
|
6
6
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ChatFirstAppRailProps } from "./types.js";
|
|
2
|
-
export declare
|
|
2
|
+
export declare const ChatFirstAppsRail: import("react").MemoExoticComponent<({ apps, activeAppId, loading, error, collapsed, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onOpenAllApps, onCreateApp, createAppTrigger, renderIcon, copy, }: ChatFirstAppRailProps) => import("react").JSX.Element>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger, Skeleton, Tooltip, TooltipContent, TooltipTrigger, } from "@agent-native/toolkit/ui";
|
|
3
|
-
import { IconChevronDown, IconChevronUp, IconPin, IconPlus, } from "@tabler/icons-react";
|
|
4
|
-
import { useMemo, useState } from "react";
|
|
3
|
+
import { IconApps, IconChevronDown, IconChevronUp, IconPin, IconPlus, } from "@tabler/icons-react";
|
|
4
|
+
import { memo, useMemo, useState } from "react";
|
|
5
5
|
import { orderChatFirstAppIds, readChatFirstAppLayout, writeChatFirstAppLayout, } from "../chat-first.js";
|
|
6
6
|
import { cn } from "../utils.js";
|
|
7
7
|
import { defaultChatFirstCopy } from "./copy.js";
|
|
@@ -11,16 +11,16 @@ function AppRows({ apps, activeAppId, layout, onDragStart, onDrop, onDragEnd, on
|
|
|
11
11
|
const orderedApps = orderedIds
|
|
12
12
|
.map((id) => appsById.get(id))
|
|
13
13
|
.filter((app) => Boolean(app));
|
|
14
|
-
return (_jsx("ul", { className: "space-y-
|
|
14
|
+
return (_jsx("ul", { className: "space-y-1", children: orderedApps.map((app) => {
|
|
15
15
|
const active = activeAppId === app.id;
|
|
16
16
|
const pinned = layout.pinnedIds.includes(app.id);
|
|
17
17
|
const index = orderedApps.indexOf(app);
|
|
18
|
-
return (_jsxs(ContextMenu, { children: [_jsx(ContextMenuTrigger, { asChild: true, children: _jsxs("li", { draggable: true, "data-chat-first-app": true, "data-app-id": app.id, className: cn("group flex h-
|
|
18
|
+
return (_jsxs(ContextMenu, { children: [_jsx(ContextMenuTrigger, { asChild: true, children: _jsxs("li", { draggable: true, "data-chat-first-app": true, "data-app-id": app.id, className: cn("group flex h-8 w-full min-w-0 items-center gap-1 rounded-md px-1 text-sm", active
|
|
19
19
|
? "bg-sidebar-accent font-medium text-sidebar-accent-foreground"
|
|
20
20
|
: "text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onDragStart: (event) => {
|
|
21
21
|
event.dataTransfer.effectAllowed = "move";
|
|
22
22
|
onDragStart(app.id);
|
|
23
|
-
}, onDragOver: (event) => event.preventDefault(), onDrop: () => onDrop(app.id), onDragEnd: onDragEnd, children: [_jsxs("button", { type: "button", className: "flex min-w-0 flex-1 items-center gap-2 px-1 text-start", onClick: () => onOpenApp(app), onKeyDown: (event) => {
|
|
23
|
+
}, onDragOver: (event) => event.preventDefault(), onDrop: () => onDrop(app.id), onDragEnd: onDragEnd, children: [_jsxs("button", { type: "button", className: "flex h-full min-w-0 flex-1 items-center gap-2 px-1 text-start", onClick: () => onOpenApp(app), onKeyDown: (event) => {
|
|
24
24
|
if (!event.altKey)
|
|
25
25
|
return;
|
|
26
26
|
if (event.key === "ArrowUp") {
|
|
@@ -31,12 +31,12 @@ function AppRows({ apps, activeAppId, layout, onDragStart, onDrop, onDragEnd, on
|
|
|
31
31
|
event.preventDefault();
|
|
32
32
|
onMove(app.id, 1);
|
|
33
33
|
}
|
|
34
|
-
}, "aria-keyshortcuts": "Alt+ArrowUp Alt+ArrowDown", "aria-label": copy("openApp", { name: app.name }),
|
|
34
|
+
}, "aria-keyshortcuts": "Alt+ArrowUp Alt+ArrowDown", "aria-label": copy("openApp", { name: app.name }), children: [renderIcon(app), _jsx("span", { className: "truncate", children: app.name })] }), _jsx("button", { type: "button", className: cn("flex size-6 shrink-0 items-center justify-center rounded text-sidebar-foreground/45 opacity-0 hover:bg-sidebar-accent hover:text-sidebar-foreground group-hover:opacity-100 focus-visible:opacity-100", pinned && "text-sidebar-foreground/70 opacity-100"), "aria-label": copy(pinned ? "unpinApp" : "pinApp", {
|
|
35
35
|
name: app.name,
|
|
36
|
-
}), "aria-pressed": pinned,
|
|
36
|
+
}), "aria-pressed": pinned, onClick: () => onTogglePinned(app.id), children: _jsx(IconPin, { size: 13, strokeWidth: pinned ? 2.2 : 1.6, "aria-hidden": "true" }) }), _jsx("span", { className: "sr-only", children: `${index + 1} of ${orderedApps.length}` })] }) }), _jsxs(ContextMenuContent, { children: [_jsxs(ContextMenuItem, { onSelect: () => onTogglePinned(app.id), children: [_jsx(IconPin, { size: 14, "aria-hidden": "true" }), pinned ? copy("removePinned") : copy("pinTop")] }), _jsx(ContextMenuSeparator, {}), _jsxs(ContextMenuItem, { disabled: index === 0, onSelect: () => onMove(app.id, -1), children: [_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" }), copy("moveUp")] }), _jsxs(ContextMenuItem, { disabled: index === orderedApps.length - 1, onSelect: () => onMove(app.id, 1), children: [_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" }), copy("moveDown")] })] })] }, app.id));
|
|
37
37
|
}) }));
|
|
38
38
|
}
|
|
39
|
-
export function ChatFirstAppsRail({ apps, activeAppId, loading = false, error, collapsed = false, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onCreateApp, createAppTrigger, renderIcon, copy = defaultChatFirstCopy, }) {
|
|
39
|
+
export const ChatFirstAppsRail = memo(function ChatFirstAppsRail({ apps, activeAppId, loading = false, error, collapsed = false, layout: controlledLayout, onLayoutChange, onLayoutError, onRetry, onOpenApp, onOpenAllApps, onCreateApp, createAppTrigger, renderIcon, copy = defaultChatFirstCopy, }) {
|
|
40
40
|
const [localLayout, setLocalLayout] = useState(() => readChatFirstAppLayout());
|
|
41
41
|
const layout = controlledLayout ?? localLayout;
|
|
42
42
|
const [draggedAppId, setDraggedAppId] = useState(null);
|
|
@@ -95,9 +95,9 @@ export function ChatFirstAppsRail({ apps, activeAppId, loading = false, error, c
|
|
|
95
95
|
if (collapsed) {
|
|
96
96
|
return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "flex flex-col items-center gap-1 px-1.5 pt-2", "aria-label": copy("workspaceApps"), children: [createTrigger, loading && apps.length === 0
|
|
97
97
|
? [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "size-9 rounded-md" }, index)))
|
|
98
|
-
: visibleApps.map((app) => (_jsx("button", { type: "button", "data-chat-first-app": true, "data-app-id": app.id, className: cn("flex size-9 items-center justify-center rounded-md
|
|
98
|
+
: visibleApps.map((app) => (_jsx("button", { type: "button", "data-chat-first-app": true, "data-app-id": app.id, className: cn("flex size-9 items-center justify-center rounded-md", activeAppId === app.id
|
|
99
99
|
? "bg-sidebar-accent text-sidebar-accent-foreground"
|
|
100
|
-
: "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onClick: () => onOpenApp(app), "aria-label": copy("openApp", { name: app.name }), title: app.name, children: renderIcon(app) }, app.id))), error ? (_jsx("span", { className: "size-1.5 rounded-full bg-destructive", title: error, "aria-label": error })) : null] }));
|
|
100
|
+
: "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onClick: () => onOpenApp(app), "aria-label": copy("openApp", { name: app.name }), title: app.name, children: renderIcon(app) }, app.id))), onOpenAllApps ? (_jsx("button", { type: "button", "data-chat-first-all-apps": true, className: "flex size-9 items-center justify-center rounded-md text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", onClick: onOpenAllApps, "aria-label": copy("allApps"), title: copy("allApps"), children: _jsx(IconApps, { size: 16, "aria-hidden": "true" }) })) : null, error ? (_jsx("span", { className: "size-1.5 rounded-full bg-destructive", title: error, "aria-label": error })) : null] }));
|
|
101
101
|
}
|
|
102
|
-
return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "mt-3 px-2 pb-2 pt-2", "aria-label": copy("workspaceApps"), children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 px-1 text-[11px] font-medium text-sidebar-foreground/50", children: [_jsx("span", { children: copy("workspaceApps") }), _jsx("span", { className: "ml-auto", children: createTrigger })] }), loading && apps.length === 0 ? (_jsx("div", { className: "space-y-0.5 px-1", children: [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "h-
|
|
103
|
-
}
|
|
102
|
+
return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "mt-3 px-2 pb-2 pt-2", "aria-label": copy("workspaceApps"), children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 px-1 text-[11px] font-medium text-sidebar-foreground/50", children: [_jsx("span", { children: copy("workspaceApps") }), _jsx("span", { className: "ml-auto", children: createTrigger })] }), loading && apps.length === 0 ? (_jsx("div", { className: "space-y-0.5 px-1", children: [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "h-8 w-full rounded-md" }, index))) })) : apps.length === 0 ? (_jsxs("div", { className: "px-1", children: [_jsx("p", { className: "text-xs text-sidebar-foreground/55", children: copy("noWorkspaceApps") }), createAppTrigger ? (createTrigger) : onCreateApp ? (_jsxs("button", { type: "button", className: "mt-1 inline-flex h-6 items-center gap-1.5 rounded-md border border-sidebar-border px-2 text-xs text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onCreateApp, children: [_jsx(IconPlus, { size: 13, "aria-hidden": "true" }), copy("createApp")] })) : (createTrigger)] })) : (_jsx(AppRows, { apps: visibleApps, activeAppId: activeAppId, layout: layout, onDragStart: setDraggedAppId, onDrop: reorderApps, onDragEnd: () => setDraggedAppId(null), onOpenApp: onOpenApp, onTogglePinned: togglePinned, onMove: moveApp, renderIcon: renderIcon, copy: copy })), hasMoreApps || showAllApps ? (_jsxs("button", { type: "button", className: "mt-0.5 flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/50 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: () => setShowAllApps((value) => !value), children: [showAllApps ? (_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" })) : (_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" })), showAllApps ? copy("showLess") : copy("showMore")] })) : null, onOpenAllApps ? (_jsx("div", { className: "mt-1 border-t border-sidebar-border/60 pt-1", children: _jsxs("button", { type: "button", "data-chat-first-all-apps": true, className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/55 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onOpenAllApps, children: [_jsx(IconApps, { size: 14, "aria-hidden": "true" }), _jsx("span", { children: copy("allApps") })] }) })) : null, error ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "mt-1 flex items-center gap-1 px-1 text-[10px] text-destructive/80", onClick: onRetry, "aria-label": error, children: [_jsx("span", { className: "size-1.5 rounded-full bg-destructive" }), onRetry ? copy("retry") : copy("appsLoadError")] }) }), _jsx(TooltipContent, { side: "right", children: error })] })) : null] }));
|
|
103
|
+
});
|
|
@@ -9,4 +9,4 @@ export interface ChatFirstChatHistoryProps extends Omit<ChatHistoryListProps, "i
|
|
|
9
9
|
* host. Hosts still own thread fetching and actions, but row rhythm and the
|
|
10
10
|
* Chats section treatment stay identical.
|
|
11
11
|
*/
|
|
12
|
-
export declare function ChatFirstChatHistory({ items, label, className, ...props }: ChatFirstChatHistoryProps): import("react").JSX.Element;
|
|
12
|
+
export declare function ChatFirstChatHistory({ items, label, className, ...props }: ChatFirstChatHistoryProps): import("react").JSX.Element | null;
|
|
@@ -6,5 +6,7 @@ import { ChatHistoryList, } from "../chat/ChatHistoryList.js";
|
|
|
6
6
|
* Chats section treatment stay identical.
|
|
7
7
|
*/
|
|
8
8
|
export function ChatFirstChatHistory({ items, label = "Chats", className, ...props }) {
|
|
9
|
+
if (items.length === 0)
|
|
10
|
+
return null;
|
|
9
11
|
return (_jsxs("section", { "data-chat-first-chat-history": true, className: ["min-h-0 min-w-0", className].filter(Boolean).join(" "), children: [_jsx("p", { className: "mb-1 px-2 text-[11px] font-medium text-sidebar-foreground/50", children: label }), _jsx(ChatHistoryList, { ...props, items: items, variant: "rail", className: "min-w-0" })] }));
|
|
10
12
|
}
|
|
@@ -2,5 +2,5 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { IconClock, IconPlus, IconPlugConnected, IconSearch, } from "@tabler/icons-react";
|
|
3
3
|
import { defaultChatFirstCopy } from "./copy.js";
|
|
4
4
|
export function ChatFirstPrimaryNavigation({ onNewChat, onOpenIntegrations, onOpenScheduled, onSearch, copy = defaultChatFirstCopy, }) {
|
|
5
|
-
return (_jsxs(_Fragment, { children: [onNewChat ? (_jsxs("button", { type: "button", className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-[13px] font-medium text-sidebar-foreground/80
|
|
5
|
+
return (_jsxs(_Fragment, { children: [onNewChat ? (_jsxs("button", { type: "button", className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-[13px] font-medium text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: onNewChat, children: [_jsx(IconPlus, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { children: copy("newChat") })] })) : null, _jsxs("button", { type: "button", className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-[13px] font-medium text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: onOpenIntegrations, children: [_jsx(IconPlugConnected, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { children: copy("integrations") })] }), _jsxs("button", { type: "button", className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-[13px] font-medium text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: onOpenScheduled, children: [_jsx(IconClock, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { children: copy("scheduled") })] }), onSearch ? (_jsxs("button", { type: "button", className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-[13px] font-medium text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: onSearch, children: [_jsx(IconSearch, { size: 15, className: "shrink-0", "aria-hidden": "true" }), _jsx("span", { children: copy("search") })] })) : null] }));
|
|
6
6
|
}
|
|
@@ -44,6 +44,7 @@ export interface ChatFirstAppRailProps {
|
|
|
44
44
|
onLayoutError?: (reason: "unavailable" | "write-failed") => void;
|
|
45
45
|
onRetry?: () => void;
|
|
46
46
|
onOpenApp: (app: ChatFirstAppItem) => void;
|
|
47
|
+
onOpenAllApps?: () => void;
|
|
47
48
|
onCreateApp?: () => void;
|
|
48
49
|
createAppTrigger?: ReactNode;
|
|
49
50
|
renderIcon: (app: ChatFirstAppItem) => ReactNode;
|
|
@@ -145,7 +145,7 @@ export function McpConnectionSuggestion({ text, contextText = "", variant = "com
|
|
|
145
145
|
: t("mcpIntegrations.connect");
|
|
146
146
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: variant === "response"
|
|
147
147
|
? "agent-mcp-connection-suggestion agent-mcp-connection-suggestion--response mt-3 flex max-w-[520px] items-center gap-2 rounded-lg border border-primary/20 bg-primary/5 px-3 py-2 text-[12px]"
|
|
148
|
-
: "agent-mcp-connection-suggestion agent-mcp-connection-suggestion--composer mx-auto mb-2 flex w-[min(calc(100%_-_1.5rem),
|
|
148
|
+
: "agent-mcp-connection-suggestion agent-mcp-connection-suggestion--composer mx-auto mb-2 flex w-[min(calc(100%_-_1.5rem),750px)] items-center gap-2 rounded-lg border border-primary/20 bg-primary/5 px-3 py-2 text-[12px]", "data-mcp-connection-suggestion": integration.id, "data-mcp-connection-suggestion-variant": variant, children: [_jsx(McpIntegrationLogo, { name: integration.name, logoUrl: integration.logoUrl, integrationId: integration.id, className: "size-6 rounded-md text-[10px]", imageClassName: "size-5" }), _jsx("span", { className: "min-w-0 flex-1 leading-snug text-foreground", children: t(hasApiFallback(apiFallback)
|
|
149
149
|
? "mcpIntegrations.connectSuggestionWithApiToken"
|
|
150
150
|
: "mcpIntegrations.connectSuggestion", { name: integration.name }) }), _jsxs("button", { type: "button", onClick: () => void connect(), disabled: connecting, "aria-busy": connecting, className: "inline-flex shrink-0 items-center gap-1 rounded-md bg-primary px-2.5 py-1.5 text-[11px] font-medium text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-60", children: [connecting && _jsx(IconLoader2, { className: "h-3 w-3 animate-spin" }), !connecting &&
|
|
151
151
|
canStartOAuth(integration) &&
|
|
@@ -153,7 +153,7 @@ export function McpConnectionSuggestion({ text, contextText = "", variant = "com
|
|
|
153
153
|
!canStartOAuth(integration) &&
|
|
154
154
|
!hasApiFallback(apiFallback) && (_jsx(IconArrowUpRight, { className: "h-3 w-3" }))] }), _jsx("button", { type: "button", onClick: () => setDismissedId(integration.id), className: "inline-flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-background hover:text-foreground", "aria-label": t("mcpIntegrations.dismissSuggestion"), children: _jsx(IconX, { className: "h-3.5 w-3.5" }) })] }), error && (_jsx("div", { className: variant === "response"
|
|
155
155
|
? "agent-mcp-connection-suggestion-error agent-mcp-connection-suggestion-error--response mt-1 max-w-[520px] text-[11px] text-destructive"
|
|
156
|
-
: "agent-mcp-connection-suggestion-error agent-mcp-connection-suggestion-error--composer mx-auto mb-2 w-[min(calc(100%_-_1.5rem),
|
|
156
|
+
: "agent-mcp-connection-suggestion-error agent-mcp-connection-suggestion-error--composer mx-auto mb-2 w-[min(calc(100%_-_1.5rem),750px)] text-[11px] text-destructive", children: error })), _jsx(McpIntegrationDialog, { open: dialogOpen, onOpenChange: (open) => {
|
|
157
157
|
setDialogOpen(open);
|
|
158
158
|
if (!open)
|
|
159
159
|
clearMcpConnectionResume();
|
|
@@ -60,6 +60,8 @@ export interface SettingsTabsPageProps {
|
|
|
60
60
|
defaultTab?: string;
|
|
61
61
|
className?: string;
|
|
62
62
|
navClassName?: string;
|
|
63
|
+
/** Optional content rendered at the top of the settings navigation rail. */
|
|
64
|
+
navHeader?: ReactNode;
|
|
63
65
|
contentClassName?: string;
|
|
64
66
|
/** Whether to render the settings search box. Defaults to true. */
|
|
65
67
|
enableSearch?: boolean;
|
|
@@ -83,5 +85,5 @@ export interface SettingsTabsPageProps {
|
|
|
83
85
|
*/
|
|
84
86
|
onValueChange?: (tabId: string) => void;
|
|
85
87
|
}
|
|
86
|
-
export declare function SettingsTabsPage({ general, account, team, whatsNew, extraTabs, generalLabel, accountLabel, teamLabel, whatsNewLabel, ariaLabel, defaultTab, className, navClassName, contentClassName, enableSearch, searchPlaceholder, searchEntries, generalSearchEntries, value, onValueChange, }: SettingsTabsPageProps): import("react").JSX.Element;
|
|
88
|
+
export declare function SettingsTabsPage({ general, account, team, whatsNew, extraTabs, generalLabel, accountLabel, teamLabel, whatsNewLabel, ariaLabel, defaultTab, className, navClassName, navHeader, contentClassName, enableSearch, searchPlaceholder, searchEntries, generalSearchEntries, value, onValueChange, }: SettingsTabsPageProps): import("react").JSX.Element;
|
|
87
89
|
export {};
|
|
@@ -118,7 +118,7 @@ function isEditableElement(element) {
|
|
|
118
118
|
tagName === "select" ||
|
|
119
119
|
element.isContentEditable);
|
|
120
120
|
}
|
|
121
|
-
export function SettingsTabsPage({ general, account, team, whatsNew, extraTabs = [], generalLabel = "General", accountLabel = "Account", teamLabel = "Team", whatsNewLabel = "What's new", ariaLabel = "Settings sections", defaultTab = "general", className, navClassName, contentClassName, enableSearch = true, searchPlaceholder = "Search settings", searchEntries, generalSearchEntries, value, onValueChange, }) {
|
|
121
|
+
export function SettingsTabsPage({ general, account, team, whatsNew, extraTabs = [], generalLabel = "General", accountLabel = "Account", teamLabel = "Team", whatsNewLabel = "What's new", ariaLabel = "Settings sections", defaultTab = "general", className, navClassName, navHeader, contentClassName, enableSearch = true, searchPlaceholder = "Search settings", searchEntries, generalSearchEntries, value, onValueChange, }) {
|
|
122
122
|
const rootRef = useRef(null);
|
|
123
123
|
const searchInputRef = useRef(null);
|
|
124
124
|
const autoFocusedSearchRef = useRef(false);
|
|
@@ -375,7 +375,7 @@ export function SettingsTabsPage({ general, account, team, whatsNew, extraTabs =
|
|
|
375
375
|
}
|
|
376
376
|
};
|
|
377
377
|
const searching = enableSearch && trimmedQuery.length > 0;
|
|
378
|
-
return (_jsxs("div", { ref: rootRef, className: cn("flex h-full min-h-0 w-full flex-col overflow-hidden bg-background sm:flex-row", className), children: [_jsxs("div", { className: cn("flex shrink-0 flex-col gap-2 bg-background p-2 sm:min-h-0 sm:w-60 sm:overflow-y-auto sm:p-4", navClassName), children: [enableSearch ? (_jsxs("div", { className: "relative sm:mb-2", children: [_jsx(IconSearch, { className: "pointer-events-none absolute start-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx("input", { ref: searchInputRef, type: "search", value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: (event) => {
|
|
378
|
+
return (_jsxs("div", { ref: rootRef, className: cn("flex h-full min-h-0 w-full flex-col overflow-hidden bg-background sm:flex-row", className), children: [_jsxs("div", { className: cn("flex shrink-0 flex-col gap-2 bg-background p-2 sm:min-h-0 sm:w-60 sm:overflow-y-auto sm:p-4", navClassName), children: [navHeader, enableSearch ? (_jsxs("div", { className: "relative sm:mb-2", children: [_jsx(IconSearch, { className: "pointer-events-none absolute start-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx("input", { ref: searchInputRef, type: "search", value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: (event) => {
|
|
379
379
|
if (event.key === "Escape")
|
|
380
380
|
setQuery("");
|
|
381
381
|
if (event.key === "Enter" && results[0]) {
|
|
@@ -188,6 +188,14 @@ export interface SSEStreamOptions {
|
|
|
188
188
|
* the tight foreground 75s/90s windows.
|
|
189
189
|
*/
|
|
190
190
|
durableBackgroundRun?: boolean;
|
|
191
|
+
/**
|
|
192
|
+
* Optional reader-local watchdog override. A background follow reader can
|
|
193
|
+
* use a shorter value because a timeout only detaches that read; the follow
|
|
194
|
+
* loop immediately re-checks the server-owned run state.
|
|
195
|
+
*/
|
|
196
|
+
noProgressTimeoutMs?: number;
|
|
197
|
+
/** Reader-local counterpart to `noProgressTimeoutMs` for action preparation. */
|
|
198
|
+
actionPreparationStallTimeoutMs?: number;
|
|
191
199
|
/**
|
|
192
200
|
* Optional caller-owned preparation watchdog state. Passing the same object
|
|
193
201
|
* across reconnect reads keeps a stuck action preparation from getting a
|
|
@@ -113,15 +113,22 @@ export function sseInFlightWorkDelta(ev) {
|
|
|
113
113
|
*/
|
|
114
114
|
export const SSE_DURABLE_NO_PROGRESS_TIMEOUT_MS = 13 * 60_000;
|
|
115
115
|
export const SSE_DURABLE_ACTION_PREPARATION_STALL_TIMEOUT_MS = 13 * 60_000;
|
|
116
|
+
function sseTimeoutOverrideMs(value) {
|
|
117
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0
|
|
118
|
+
? value
|
|
119
|
+
: undefined;
|
|
120
|
+
}
|
|
116
121
|
export function sseNoProgressTimeoutMs(options) {
|
|
117
|
-
return options?.
|
|
118
|
-
|
|
119
|
-
|
|
122
|
+
return (sseTimeoutOverrideMs(options?.noProgressTimeoutMs) ??
|
|
123
|
+
(options?.durableBackgroundRun === true
|
|
124
|
+
? SSE_DURABLE_NO_PROGRESS_TIMEOUT_MS
|
|
125
|
+
: SSE_NO_PROGRESS_TIMEOUT_MS));
|
|
120
126
|
}
|
|
121
127
|
function sseActionPreparationStallTimeoutMs(options) {
|
|
122
|
-
return options?.
|
|
123
|
-
|
|
124
|
-
|
|
128
|
+
return (sseTimeoutOverrideMs(options?.actionPreparationStallTimeoutMs) ??
|
|
129
|
+
(options?.durableBackgroundRun === true
|
|
130
|
+
? SSE_DURABLE_ACTION_PREPARATION_STALL_TIMEOUT_MS
|
|
131
|
+
: SSE_ACTION_PREPARATION_STALL_TIMEOUT_MS));
|
|
125
132
|
}
|
|
126
133
|
function activityProgressBytes(ev) {
|
|
127
134
|
return typeof ev.progressBytes === "number" &&
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
|
|
14
14
|
*/
|
|
15
15
|
export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
16
|
-
ok?: undefined;
|
|
17
16
|
error: string;
|
|
17
|
+
ok?: undefined;
|
|
18
18
|
} | {
|
|
19
19
|
error?: undefined;
|
|
20
20
|
ok: boolean;
|
package/dist/deploy/build.d.ts
CHANGED
|
@@ -103,12 +103,9 @@ export declare function isIntegrationDurableDispatchDeployEnabled(): boolean;
|
|
|
103
103
|
export declare const NETLIFY_RECURRING_JOBS_FUNCTION_NAME = "agent-native-recurring-jobs";
|
|
104
104
|
export declare function isRecurringJobsDeployEnabled(): boolean;
|
|
105
105
|
/**
|
|
106
|
-
* Keep-warm is
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* quota-limits) as if it were awake 100% of the time with zero users online. On
|
|
110
|
-
* a free-tier Neon that exhausts the compute quota and the database then
|
|
111
|
-
* hard-blocks every read mid-session — the symptom is a dead app, not a slow one.
|
|
106
|
+
* Keep-warm is opt-in because a once-a-minute wake prevents autosuspend on
|
|
107
|
+
* metered scale-to-zero databases. `AGENT_NATIVE_DISABLE_KEEP_WARM` remains a
|
|
108
|
+
* compatibility kill switch and wins when both flags are set.
|
|
112
109
|
*/
|
|
113
110
|
export declare function isKeepWarmDeployEnabled(): boolean;
|
|
114
111
|
/**
|
|
@@ -138,7 +135,8 @@ export declare function resolveKeepWarmSchedule(): string;
|
|
|
138
135
|
* delayed by tens of minutes, which is longer than a scale-to-zero database's
|
|
139
136
|
* autosuspend window and leaves the next visitor to pay the cold-start cost.
|
|
140
137
|
*
|
|
141
|
-
*
|
|
138
|
+
* The feature is opt-in, the background half has its own kill switch, and the
|
|
139
|
+
* cadence is configurable — see
|
|
142
140
|
* `isKeepWarmDeployEnabled`, `isKeepWarmBackgroundDeployEnabled`, and
|
|
143
141
|
* `resolveKeepWarmSchedule`. The tradeoff this function encodes is next-visitor
|
|
144
142
|
* latency against database awake-time, and only the deployment knows which of
|
package/dist/deploy/build.js
CHANGED
|
@@ -2256,24 +2256,26 @@ export function isIntegrationDurableDispatchDeployEnabled() {
|
|
|
2256
2256
|
}
|
|
2257
2257
|
const NETLIFY_KEEP_WARM_FUNCTION_NAME = "agent-native-keep-warm";
|
|
2258
2258
|
export const NETLIFY_RECURRING_JOBS_FUNCTION_NAME = "agent-native-recurring-jobs";
|
|
2259
|
-
/** Shared
|
|
2260
|
-
function
|
|
2259
|
+
/** Shared parser for truthy build environment flags. */
|
|
2260
|
+
function isTruthyEnv(name) {
|
|
2261
2261
|
const value = process.env[name]?.trim();
|
|
2262
2262
|
return !!value && ["1", "true", "yes", "on"].includes(value.toLowerCase());
|
|
2263
2263
|
}
|
|
2264
|
+
/** Shared shape for the `AGENT_NATIVE_DISABLE_*` build kill switches. */
|
|
2265
|
+
function isDisabledByEnv(name) {
|
|
2266
|
+
return isTruthyEnv(name);
|
|
2267
|
+
}
|
|
2264
2268
|
export function isRecurringJobsDeployEnabled() {
|
|
2265
2269
|
return !isDisabledByEnv("AGENT_NATIVE_DISABLE_RECURRING_JOBS");
|
|
2266
2270
|
}
|
|
2267
2271
|
/**
|
|
2268
|
-
* Keep-warm is
|
|
2269
|
-
*
|
|
2270
|
-
*
|
|
2271
|
-
* quota-limits) as if it were awake 100% of the time with zero users online. On
|
|
2272
|
-
* a free-tier Neon that exhausts the compute quota and the database then
|
|
2273
|
-
* hard-blocks every read mid-session — the symptom is a dead app, not a slow one.
|
|
2272
|
+
* Keep-warm is opt-in because a once-a-minute wake prevents autosuspend on
|
|
2273
|
+
* metered scale-to-zero databases. `AGENT_NATIVE_DISABLE_KEEP_WARM` remains a
|
|
2274
|
+
* compatibility kill switch and wins when both flags are set.
|
|
2274
2275
|
*/
|
|
2275
2276
|
export function isKeepWarmDeployEnabled() {
|
|
2276
|
-
return
|
|
2277
|
+
return (isTruthyEnv("AGENT_NATIVE_ENABLE_KEEP_WARM") &&
|
|
2278
|
+
!isDisabledByEnv("AGENT_NATIVE_DISABLE_KEEP_WARM"));
|
|
2277
2279
|
}
|
|
2278
2280
|
/**
|
|
2279
2281
|
* The background warm is a separate, much more expensive knob than the server
|
|
@@ -2286,7 +2288,8 @@ export function isKeepWarmDeployEnabled() {
|
|
|
2286
2288
|
* while dropping the probe storm.
|
|
2287
2289
|
*/
|
|
2288
2290
|
export function isKeepWarmBackgroundDeployEnabled() {
|
|
2289
|
-
return
|
|
2291
|
+
return (isKeepWarmDeployEnabled() &&
|
|
2292
|
+
!isDisabledByEnv("AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND"));
|
|
2290
2293
|
}
|
|
2291
2294
|
const DEFAULT_KEEP_WARM_SCHEDULE = "* * * * *";
|
|
2292
2295
|
/**
|
|
@@ -2319,7 +2322,8 @@ export function resolveKeepWarmSchedule() {
|
|
|
2319
2322
|
* delayed by tens of minutes, which is longer than a scale-to-zero database's
|
|
2320
2323
|
* autosuspend window and leaves the next visitor to pay the cold-start cost.
|
|
2321
2324
|
*
|
|
2322
|
-
*
|
|
2325
|
+
* The feature is opt-in, the background half has its own kill switch, and the
|
|
2326
|
+
* cadence is configurable — see
|
|
2323
2327
|
* `isKeepWarmDeployEnabled`, `isKeepWarmBackgroundDeployEnabled`, and
|
|
2324
2328
|
* `resolveKeepWarmSchedule`. The tradeoff this function encodes is next-visitor
|
|
2325
2329
|
* latency against database awake-time, and only the deployment knows which of
|
|
@@ -2327,7 +2331,10 @@ export function resolveKeepWarmSchedule() {
|
|
|
2327
2331
|
*/
|
|
2328
2332
|
export function emitSingleTemplateNetlifyKeepWarmFunction(projectCwd) {
|
|
2329
2333
|
if (!isKeepWarmDeployEnabled()) {
|
|
2330
|
-
|
|
2334
|
+
const reason = isDisabledByEnv("AGENT_NATIVE_DISABLE_KEEP_WARM")
|
|
2335
|
+
? "AGENT_NATIVE_DISABLE_KEEP_WARM is set."
|
|
2336
|
+
: "AGENT_NATIVE_ENABLE_KEEP_WARM is not enabled.";
|
|
2337
|
+
console.log(`[build] Keep-warm emit skipped: ${reason} ` +
|
|
2331
2338
|
"The database is free to autosuspend; the next visitor after an idle " +
|
|
2332
2339
|
"period pays its cold start.");
|
|
2333
2340
|
return;
|
|
@@ -434,7 +434,7 @@ const messages = {
|
|
|
434
434
|
newTerminal: "New terminal",
|
|
435
435
|
panelOptions: "Agent panel options",
|
|
436
436
|
collapseSidebar: "Collapse sidebar",
|
|
437
|
-
widenChat: "Widen chat
|
|
437
|
+
widenChat: "Widen chat",
|
|
438
438
|
returnChatToLayout: "Return chat to layout",
|
|
439
439
|
expandSidebar: "Expand sidebar",
|
|
440
440
|
hideChats: "Hide chats",
|
|
@@ -41,16 +41,16 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
-
error?: undefined;
|
|
45
44
|
summary: import("./types.js").TraceSummary;
|
|
46
45
|
spans: import("./types.js").TraceSpan[];
|
|
47
46
|
id?: undefined;
|
|
47
|
+
error?: undefined;
|
|
48
48
|
ok?: undefined;
|
|
49
49
|
} | {
|
|
50
|
-
error?: undefined;
|
|
51
50
|
summary?: undefined;
|
|
52
51
|
spans?: undefined;
|
|
53
52
|
id: string;
|
|
53
|
+
error?: undefined;
|
|
54
54
|
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
56
|
summary?: undefined;
|
|
@@ -59,9 +59,9 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
59
59
|
error: any;
|
|
60
60
|
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
-
error?: undefined;
|
|
63
62
|
summary?: undefined;
|
|
64
63
|
spans?: undefined;
|
|
65
64
|
id?: undefined;
|
|
66
65
|
ok: boolean;
|
|
66
|
+
error?: undefined;
|
|
67
67
|
}>>;
|
|
@@ -311,8 +311,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
311
311
|
notes?: string | undefined;
|
|
312
312
|
scope?: "org" | "user" | undefined;
|
|
313
313
|
}, {
|
|
314
|
-
id?: undefined;
|
|
315
314
|
deleted?: undefined;
|
|
315
|
+
id?: undefined;
|
|
316
316
|
providers: {
|
|
317
317
|
id: string;
|
|
318
318
|
label: string;
|
|
@@ -329,9 +329,9 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
329
329
|
label?: undefined;
|
|
330
330
|
message?: undefined;
|
|
331
331
|
} | {
|
|
332
|
-
id?: undefined;
|
|
333
332
|
deleted?: undefined;
|
|
334
333
|
count?: undefined;
|
|
334
|
+
id?: undefined;
|
|
335
335
|
providers?: undefined;
|
|
336
336
|
found: boolean;
|
|
337
337
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
@@ -219,7 +219,7 @@ function formatDownstreamLlmCredentialFailure(agentName, value) {
|
|
|
219
219
|
: null;
|
|
220
220
|
}
|
|
221
221
|
export const tool = {
|
|
222
|
-
description: "Ask a DIFFERENT, separately-deployed app's agent over A2A. Use message by default so the receiving specialist interprets the objective with its own instructions, skills, connected sources, data dictionary, and tools. The receiver owns provider, schema, query, join, and SQL decisions. Use action + input only for an exact, explicitly known, bounded read whose complete input schema is already known; never expose or call a direct action to work around slow or unreliable delegation, and never guess receiver-owned query logic. NEVER use this to call your own app or perform actions you can do with your own tools. Using call-agent on yourself will fail and waste time. " +
|
|
222
|
+
description: "Ask a DIFFERENT, separately-deployed app's agent over A2A. Use message by default so the receiving specialist interprets the objective with its own instructions, skills, connected sources, data dictionary, and tools. The receiver owns provider, schema, query, join, and SQL decisions. Use action + input only for an exact, explicitly known, bounded read whose complete input schema is already known. Never put a create, update, delete, send, save, publish, or any other side effect in action; omit action and send the objective as message instead; never expose or call a direct action to work around slow or unreliable delegation, and never guess receiver-owned query logic. NEVER use this to call your own app or perform actions you can do with your own tools. Using call-agent on yourself will fail and waste time. " +
|
|
223
223
|
'For brand-consistent generated media, the first-party Assets agent is available as agent="assets"; use it when another app needs generated heroes, diagrams, product shots, thumbnails, videos, or design imagery, unless the current app has its own generation action that already delegates there. ' +
|
|
224
224
|
"IMPORTANT — handling the response: " +
|
|
225
225
|
"(a) If it contains a URL or ID, copy it VERBATIM into your reply. Do not 'correct' or pluralize the path (e.g. /deck/ → /decks/), normalize casing, or change the slug — any edit breaks the link. " +
|
|
@@ -243,11 +243,11 @@ export const tool = {
|
|
|
243
243
|
},
|
|
244
244
|
action: {
|
|
245
245
|
type: "string",
|
|
246
|
-
description: "Optional exact read-only action for an explicitly known bounded operation. Prefer message when the target agent must interpret data, choose sources or tools, synthesize, mutate, or do multi-step work. Never guess the action schema or send receiver-owned SQL.",
|
|
246
|
+
description: "Optional exact read-only action for an explicitly known bounded operation. This field is never for creates, updates, deletes, sends, saves, publishes, or any other side effect. Prefer message when the target agent must interpret data, choose sources or tools, synthesize, mutate, or do multi-step work. Never guess the action schema or send receiver-owned SQL.",
|
|
247
247
|
},
|
|
248
248
|
input: {
|
|
249
249
|
type: "object",
|
|
250
|
-
description: "Complete input object for action. The target app validates it and refuses actions that are not explicitly exposed read-only operations.",
|
|
250
|
+
description: "Complete input object for action. The target app validates it and refuses actions that are not explicitly exposed read-only operations. For a mutating request, omit action and use message.",
|
|
251
251
|
additionalProperties: true,
|
|
252
252
|
},
|
|
253
253
|
approvedActions: {
|
|
@@ -22,7 +22,7 @@ export async function run(args, _context, selfAppId) {
|
|
|
22
22
|
.join(", ");
|
|
23
23
|
return `Error: no workspace app "${requested}". Available: ${available || "(none)"}`;
|
|
24
24
|
}
|
|
25
|
-
return formatCapabilityDetail(await loadCapabilities(agent), (appId) => `Ask this specialist with call-agent: agent="${appId}", message="<objective>". Use action + input only for an exact bounded read with a fully known schema.`);
|
|
25
|
+
return formatCapabilityDetail(await loadCapabilities(agent), (appId) => `Ask this specialist with call-agent: agent="${appId}", message="<objective>". Use action + input only for an exact bounded read with a fully known schema; use message for creates, updates, deletes, sends, saves, publishes, or any other side effect.`);
|
|
26
26
|
}
|
|
27
27
|
const agents = await discoverAgents(selfAppId);
|
|
28
28
|
if (agents.length === 0) {
|