@agent-native/core 0.107.0 → 0.107.2
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/core/CHANGELOG.md +16 -0
- package/corpus/core/docs/content/integrations.mdx +27 -27
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/artifact-response.ts +32 -10
- package/corpus/core/src/agent/production-agent.ts +19 -0
- package/corpus/core/src/agent/run-store.ts +98 -2
- package/corpus/core/src/client/AssistantChat.tsx +13 -0
- package/corpus/core/src/client/CommandMenu.tsx +5 -0
- package/corpus/core/src/client/active-run-state.ts +34 -0
- package/corpus/core/src/client/agent-chat-adapter.ts +4 -0
- package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +41 -11
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +27 -9
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +45 -0
- package/corpus/core/src/client/sharing/ShareButton.tsx +30 -9
- package/corpus/core/src/client/use-agent-chat-home-handoff.ts +2 -2
- package/corpus/core/src/integrations/a2a-continuation-processor.ts +107 -3
- package/corpus/core/src/integrations/pending-tasks-store.ts +98 -1
- package/corpus/core/src/integrations/plugin.ts +129 -4
- package/corpus/core/src/integrations/webhook-handler.ts +339 -36
- package/corpus/core/src/localization/default-messages.ts +4 -1
- package/corpus/core/src/secrets/register-framework-secrets.ts +28 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +122 -9
- package/corpus/core/src/vite/client.ts +24 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +29 -21
- package/corpus/templates/analytics/AGENTS.md +8 -0
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1 -1
- package/corpus/templates/analytics/actions/update-dashboard.ts +6 -2
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +35 -7
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +1 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +11 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/extension-slot.ts +11 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +27 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +6 -0
- package/corpus/templates/analytics/changelog/2026-07-17-dashboard-extension-boxes.md +6 -0
- package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +8 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/assets/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/brain/app/hooks/use-navigation-state.ts +6 -1
- package/corpus/templates/brain/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +1 -1
- package/corpus/templates/calendar/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/chat/app/hooks/use-navigation-state.ts +4 -1
- package/corpus/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
- package/corpus/templates/clips/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/content/actions/_content-space-access.ts +45 -2
- package/corpus/templates/content/actions/create-content-database.ts +41 -7
- package/corpus/templates/content/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/content/app/root.tsx +1 -10
- package/corpus/templates/content/changelog/2026-07-17-creating-a-database-now-completes-reliably-in-hosted-workspa.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +4 -1
- package/corpus/templates/dispatch/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/forms/app/components/layout/Layout.tsx +2 -4
- package/corpus/templates/forms/app/components/ui/popover.tsx +25 -1
- package/corpus/templates/forms/app/hooks/use-navigation-state.ts +6 -1
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +13 -10
- package/corpus/templates/forms/app/root.tsx +6 -1
- package/corpus/templates/forms/changelog/2026-07-17-form-builder-popovers-stay-within-the-viewport.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-17-form-editor-loading-matches-current-sidebar-free-layout.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +1 -1
- package/corpus/templates/mail/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +14 -3
- package/corpus/templates/plan/app/hooks/use-navigation-state.ts +8 -1
- package/corpus/templates/plan/app/root.tsx +0 -2
- package/corpus/templates/plan/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/slides/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +23 -23
- package/corpus/templates/slides/changelog/2026-07-17-duplicated-slides-now-stay-in-the-deck.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/tasks/app/components/layout/Layout.tsx +2 -13
- package/corpus/templates/tasks/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it.md +6 -0
- package/dist/a2a/artifact-response.d.ts +5 -1
- package/dist/a2a/artifact-response.d.ts.map +1 -1
- package/dist/a2a/artifact-response.js +18 -12
- package/dist/a2a/artifact-response.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +13 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-store.d.ts +17 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +84 -2
- package/dist/agent/run-store.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +10 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/CommandMenu.d.ts.map +1 -1
- package/dist/client/CommandMenu.js +4 -0
- package/dist/client/CommandMenu.js.map +1 -1
- package/dist/client/active-run-state.d.ts +7 -0
- package/dist/client/active-run-state.d.ts.map +1 -1
- package/dist/client/active-run-state.js +28 -0
- package/dist/client/active-run-state.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +4 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.js +27 -8
- package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +4 -2
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts +6 -0
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +33 -0
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +15 -12
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.js +1 -1
- package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/integrations/a2a-continuation-processor.js +62 -5
- package/dist/integrations/a2a-continuation-processor.js.map +1 -1
- package/dist/integrations/pending-tasks-store.d.ts +3 -0
- package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-store.js +75 -1
- package/dist/integrations/pending-tasks-store.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +81 -5
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts +23 -2
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +257 -35
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/localization/default-messages.d.ts +2 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +3 -1
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +5 -5
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/register-framework-secrets.d.ts.map +1 -1
- package/dist/secrets/register-framework-secrets.js +25 -0
- package/dist/secrets/register-framework-secrets.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat-plugin.d.ts +6 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +94 -7
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/templates/chat/app/hooks/use-navigation-state.ts +4 -1
- package/dist/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +20 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/integrations.mdx +27 -27
- package/package.json +1 -1
- package/src/templates/chat/app/hooks/use-navigation-state.ts +4 -1
- package/src/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
|
@@ -9,6 +9,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
|
|
9
9
|
|
|
10
10
|
import { AgentAskPopover } from "../AgentAskPopover.js";
|
|
11
11
|
import { agentNativePath } from "../api-path.js";
|
|
12
|
+
import { openAgentSettings } from "../CommandMenu.js";
|
|
12
13
|
import {
|
|
13
14
|
Dialog,
|
|
14
15
|
DialogContent,
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
buildMcpOAuthStartUrl,
|
|
28
29
|
createMcpIntegrationFormDefaults,
|
|
29
30
|
filterMcpIntegrations,
|
|
31
|
+
getMcpIntegrationApiFallback,
|
|
30
32
|
getDefaultMcpIntegrations,
|
|
31
33
|
isCustomMcpIntegrationEnabled,
|
|
32
34
|
resolveMcpIntegrationScope,
|
|
@@ -445,6 +447,7 @@ export function McpIntegrationDialog({
|
|
|
445
447
|
)}
|
|
446
448
|
<div className="grid gap-3 sm:grid-cols-2">
|
|
447
449
|
{filteredIntegrations.map((integration) => {
|
|
450
|
+
const apiFallback = getMcpIntegrationApiFallback(integration);
|
|
448
451
|
const connected = connectedUrls.has(
|
|
449
452
|
compareUrl(integration.url),
|
|
450
453
|
);
|
|
@@ -515,15 +518,30 @@ export function McpIntegrationDialog({
|
|
|
515
518
|
{t("mcpIntegrations.connected")}
|
|
516
519
|
</button>
|
|
517
520
|
) : setupOnly ? (
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
521
|
+
<>
|
|
522
|
+
{apiFallback && (
|
|
523
|
+
<button
|
|
524
|
+
type="button"
|
|
525
|
+
onClick={() =>
|
|
526
|
+
openAgentSettings(
|
|
527
|
+
`secrets:${apiFallback.secretKey}`,
|
|
528
|
+
)
|
|
529
|
+
}
|
|
530
|
+
className="inline-flex h-8 flex-1 items-center justify-center gap-1.5 rounded-md bg-primary px-2.5 text-[12px] font-medium text-primary-foreground hover:bg-primary/90"
|
|
531
|
+
>
|
|
532
|
+
{t("mcpIntegrations.useApiToken")}
|
|
533
|
+
</button>
|
|
534
|
+
)}
|
|
535
|
+
<a
|
|
536
|
+
href={integration.docsUrl}
|
|
537
|
+
target="_blank"
|
|
538
|
+
rel="noopener noreferrer"
|
|
539
|
+
className="inline-flex h-8 flex-1 items-center justify-center gap-1.5 rounded-md border border-border bg-background px-2.5 text-[12px] font-medium text-foreground hover:bg-accent"
|
|
540
|
+
>
|
|
541
|
+
{t("mcpIntegrations.viewSetup")}
|
|
542
|
+
<IconExternalLink className="h-3.5 w-3.5" />
|
|
543
|
+
</a>
|
|
544
|
+
</>
|
|
527
545
|
) : (
|
|
528
546
|
<button
|
|
529
547
|
type="button"
|
|
@@ -24,6 +24,7 @@ export type McpIntegrationVerification =
|
|
|
24
24
|
declare const __AGENT_NATIVE_MCP_INTEGRATIONS_CONFIG__:
|
|
25
25
|
| NormalizedMcpIntegrationsConfig
|
|
26
26
|
| undefined;
|
|
27
|
+
declare const __AGENT_NATIVE_TEMPLATE__: string | undefined;
|
|
27
28
|
|
|
28
29
|
export interface DefaultMcpIntegration {
|
|
29
30
|
id: string;
|
|
@@ -41,6 +42,11 @@ export interface DefaultMcpIntegration {
|
|
|
41
42
|
logoUrl: string;
|
|
42
43
|
docsUrl?: string;
|
|
43
44
|
setupNoteKey?: string;
|
|
45
|
+
apiFallback?: {
|
|
46
|
+
secretKey: string;
|
|
47
|
+
docsUrl: string;
|
|
48
|
+
templateUses?: readonly string[];
|
|
49
|
+
};
|
|
44
50
|
headerPlaceholder?: string;
|
|
45
51
|
keywords: string[];
|
|
46
52
|
}
|
|
@@ -272,6 +278,12 @@ export const DEFAULT_MCP_INTEGRATIONS: DefaultMcpIntegration[] = [
|
|
|
272
278
|
logoUrl: mcpIntegrationLogo("figma"),
|
|
273
279
|
docsUrl: "https://developers.figma.com/docs/figma-mcp-server/",
|
|
274
280
|
setupNoteKey: "mcpIntegrations.catalog.figma.setupNote",
|
|
281
|
+
apiFallback: {
|
|
282
|
+
secretKey: "FIGMA_ACCESS_TOKEN",
|
|
283
|
+
docsUrl:
|
|
284
|
+
"https://developers.figma.com/docs/rest-api/personal-access-tokens/",
|
|
285
|
+
templateUses: ["design"],
|
|
286
|
+
},
|
|
275
287
|
keywords: ["design", "figjam", "components", "variables", "canvas"],
|
|
276
288
|
},
|
|
277
289
|
{
|
|
@@ -532,6 +544,39 @@ function readRuntimeMcpIntegrationsConfig(): NormalizedMcpIntegrationsConfig {
|
|
|
532
544
|
return normalizeMcpIntegrationsConfig();
|
|
533
545
|
}
|
|
534
546
|
|
|
547
|
+
function normalizeTemplateName(value: unknown): string | null {
|
|
548
|
+
if (typeof value !== "string") return null;
|
|
549
|
+
const normalized = value.trim().toLowerCase();
|
|
550
|
+
return normalized || null;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function getActiveTemplateName(): string | null {
|
|
554
|
+
try {
|
|
555
|
+
const compiledTemplate = normalizeTemplateName(__AGENT_NATIVE_TEMPLATE__);
|
|
556
|
+
if (compiledTemplate) return compiledTemplate;
|
|
557
|
+
} catch {
|
|
558
|
+
// Test and non-Vite contexts may not define the compile-time constant.
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const runtimeConfig = (
|
|
562
|
+
globalThis as typeof globalThis & {
|
|
563
|
+
__AGENT_NATIVE_CONFIG__?: { template?: unknown };
|
|
564
|
+
}
|
|
565
|
+
).__AGENT_NATIVE_CONFIG__;
|
|
566
|
+
return normalizeTemplateName(runtimeConfig?.template);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
export function getMcpIntegrationApiFallback(
|
|
570
|
+
integration: DefaultMcpIntegration,
|
|
571
|
+
templateName = getActiveTemplateName(),
|
|
572
|
+
): DefaultMcpIntegration["apiFallback"] | null {
|
|
573
|
+
const fallback = integration.apiFallback;
|
|
574
|
+
if (!fallback) return null;
|
|
575
|
+
if (!fallback.templateUses?.length) return fallback;
|
|
576
|
+
if (!templateName) return null;
|
|
577
|
+
return fallback.templateUses.includes(templateName) ? fallback : null;
|
|
578
|
+
}
|
|
579
|
+
|
|
535
580
|
function normalizePresetConfig(
|
|
536
581
|
config?: McpIntegrationsConfigInput | NormalizedMcpIntegrationsConfig,
|
|
537
582
|
): NormalizedMcpIntegrationsConfig {
|
|
@@ -261,6 +261,7 @@ function handleSharePopoverInteractOutside(
|
|
|
261
261
|
*/
|
|
262
262
|
export function ShareButton(props: ShareButtonProps) {
|
|
263
263
|
const [open, setOpen] = useState(false);
|
|
264
|
+
const [inviteEmail, setInviteEmail] = useState("");
|
|
264
265
|
const shareTabDefaultValue = props.shareTabs?.defaultValue ?? "share";
|
|
265
266
|
const [activeShareTab, setActiveShareTab] = useState(shareTabDefaultValue);
|
|
266
267
|
const [pendingVisibility, setPendingVisibility] = useState<Visibility | null>(
|
|
@@ -295,6 +296,10 @@ export function ShareButton(props: ShareButtonProps) {
|
|
|
295
296
|
}
|
|
296
297
|
};
|
|
297
298
|
|
|
299
|
+
useEffect(() => {
|
|
300
|
+
setInviteEmail("");
|
|
301
|
+
}, [props.resourceType, props.resourceId]);
|
|
302
|
+
|
|
298
303
|
const handleShareTabChange = (value: string) => {
|
|
299
304
|
setActiveShareTab(value);
|
|
300
305
|
props.shareTabs?.onValueChange?.(value);
|
|
@@ -399,6 +404,8 @@ export function ShareButton(props: ShareButtonProps) {
|
|
|
399
404
|
>
|
|
400
405
|
<SharePanel
|
|
401
406
|
{...props}
|
|
407
|
+
inviteEmail={inviteEmail}
|
|
408
|
+
onInviteEmailChange={setInviteEmail}
|
|
402
409
|
sharesQuery={sharesQuery}
|
|
403
410
|
visibilityOverride={pendingVisibility}
|
|
404
411
|
onVisibilityChange={handleVisibilityChange}
|
|
@@ -575,6 +582,8 @@ function mergeMembers(existing: OrgMember[], next: OrgMember[]): OrgMember[] {
|
|
|
575
582
|
|
|
576
583
|
function SharePanel(
|
|
577
584
|
props: ShareButtonProps & {
|
|
585
|
+
inviteEmail: string;
|
|
586
|
+
onInviteEmailChange: (email: string) => void;
|
|
578
587
|
sharesQuery: ReturnType<typeof useActionQuery<SharesResponse>>;
|
|
579
588
|
visibilityOverride: Visibility | null;
|
|
580
589
|
onVisibilityChange: (visibility: Visibility) => Promise<void>;
|
|
@@ -591,17 +600,18 @@ function SharePanel(
|
|
|
591
600
|
visibilityOverride,
|
|
592
601
|
onVisibilityChange,
|
|
593
602
|
onClose,
|
|
603
|
+
inviteEmail,
|
|
604
|
+
onInviteEmailChange,
|
|
594
605
|
} = props;
|
|
595
606
|
|
|
596
607
|
const share = useActionMutation("share-resource");
|
|
597
608
|
const unshare = useActionMutation("unshare-resource");
|
|
598
609
|
|
|
599
|
-
const [email, setEmail] = useState("");
|
|
600
610
|
const [role, setRole] = useState<Role>("viewer");
|
|
601
611
|
const [notifyPeople, setNotifyPeople] = useState(true);
|
|
602
612
|
const [shareError, setShareError] = useState<string | null>(null);
|
|
603
613
|
const [suggestionsOpen, setSuggestionsOpen] = useState(false);
|
|
604
|
-
const hasInviteEmail =
|
|
614
|
+
const hasInviteEmail = inviteEmail.trim().length > 0;
|
|
605
615
|
|
|
606
616
|
// Optimistic overlays so clicks feel instant.
|
|
607
617
|
const [pendingAdds, setPendingAdds] = useState<Share[]>([]);
|
|
@@ -685,7 +695,10 @@ function SharePanel(
|
|
|
685
695
|
.map((s) => ({ ...s, role: roleOverrides[keyOf(s)] ?? s.role })),
|
|
686
696
|
...pendingAdds,
|
|
687
697
|
];
|
|
688
|
-
const memberSearch = useOrgMemberSearch(
|
|
698
|
+
const memberSearch = useOrgMemberSearch(
|
|
699
|
+
inviteEmail,
|
|
700
|
+
canManage && suggestionsOpen,
|
|
701
|
+
);
|
|
689
702
|
const excludedMemberEmails = new Set<string>();
|
|
690
703
|
if (data?.ownerEmail) excludedMemberEmails.add(data.ownerEmail.toLowerCase());
|
|
691
704
|
for (const s of shares) {
|
|
@@ -726,7 +739,7 @@ function SharePanel(
|
|
|
726
739
|
};
|
|
727
740
|
|
|
728
741
|
const handleAdd = () => {
|
|
729
|
-
const trimmed =
|
|
742
|
+
const trimmed = inviteEmail.trim();
|
|
730
743
|
if (!trimmed) return;
|
|
731
744
|
const optimistic: Share = {
|
|
732
745
|
id: `pending-${trimmed}`,
|
|
@@ -739,7 +752,7 @@ function SharePanel(
|
|
|
739
752
|
if (inFlight.has(k)) return;
|
|
740
753
|
setShareError(null);
|
|
741
754
|
setPendingAdds((p) => [...p, optimistic]);
|
|
742
|
-
|
|
755
|
+
onInviteEmailChange("");
|
|
743
756
|
setSuggestionsOpen(false);
|
|
744
757
|
addInFlight(k);
|
|
745
758
|
share.mutate(
|
|
@@ -762,7 +775,7 @@ function SharePanel(
|
|
|
762
775
|
onError: (err: any) => {
|
|
763
776
|
setPendingAdds((p) => p.filter((s) => s.id !== optimistic.id));
|
|
764
777
|
clearInFlight(k);
|
|
765
|
-
|
|
778
|
+
onInviteEmailChange(trimmed);
|
|
766
779
|
setShareError(extractShareErrorMessage(err));
|
|
767
780
|
},
|
|
768
781
|
},
|
|
@@ -898,15 +911,15 @@ function SharePanel(
|
|
|
898
911
|
<div className="mb-4 space-y-2">
|
|
899
912
|
<div className="flex items-stretch gap-2">
|
|
900
913
|
<MemberAutocomplete
|
|
901
|
-
value={
|
|
914
|
+
value={inviteEmail}
|
|
902
915
|
open={suggestionsOpen}
|
|
903
916
|
onOpenChange={setSuggestionsOpen}
|
|
904
917
|
onValueChange={(next) => {
|
|
905
|
-
|
|
918
|
+
onInviteEmailChange(next);
|
|
906
919
|
if (shareError) setShareError(null);
|
|
907
920
|
}}
|
|
908
921
|
onSelectMember={(member) => {
|
|
909
|
-
|
|
922
|
+
onInviteEmailChange(member.email);
|
|
910
923
|
setSuggestionsOpen(false);
|
|
911
924
|
if (shareError) setShareError(null);
|
|
912
925
|
}}
|
|
@@ -920,6 +933,14 @@ function SharePanel(
|
|
|
920
933
|
search={memberSearch}
|
|
921
934
|
/>
|
|
922
935
|
<RoleSelect value={role} onChange={setRole} />
|
|
936
|
+
<button
|
|
937
|
+
type="button"
|
|
938
|
+
onClick={handleAdd}
|
|
939
|
+
disabled={!hasInviteEmail}
|
|
940
|
+
className={BUTTON_PRIMARY_SM}
|
|
941
|
+
>
|
|
942
|
+
Add
|
|
943
|
+
</button>
|
|
923
944
|
</div>
|
|
924
945
|
{shareError ? (
|
|
925
946
|
<div
|
|
@@ -35,7 +35,7 @@ export interface UseAgentChatHomeHandoffLinksOptions {
|
|
|
35
35
|
enabled?: boolean;
|
|
36
36
|
/** How long the handoff marker remains valid. Defaults to 6 hours. */
|
|
37
37
|
ttlMs?: number;
|
|
38
|
-
/** Only intercept links if a recent handoff marker already exists. */
|
|
38
|
+
/** Only intercept links if a recent handoff marker already exists. Defaults to true. */
|
|
39
39
|
requireActiveHandoff?: boolean;
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -148,7 +148,7 @@ export function useAgentChatHomeHandoffLinks({
|
|
|
148
148
|
isChatPath,
|
|
149
149
|
enabled = true,
|
|
150
150
|
ttlMs,
|
|
151
|
-
requireActiveHandoff =
|
|
151
|
+
requireActiveHandoff = true,
|
|
152
152
|
}: UseAgentChatHomeHandoffLinksOptions): void {
|
|
153
153
|
const location = useLocation();
|
|
154
154
|
const navigate = useNavigate();
|
|
@@ -171,11 +171,22 @@ async function processClaimedContinuation(
|
|
|
171
171
|
...(auth.apiKeyFallbacks ? { fallbackApiKeys: auth.apiKeyFallbacks } : {}),
|
|
172
172
|
});
|
|
173
173
|
const deadline = Date.now() + PROCESSOR_WAIT_MS;
|
|
174
|
+
const recoverableArtifactSecrets =
|
|
175
|
+
await resolveContinuationArtifactSecrets(continuation);
|
|
174
176
|
let task: Task | null = null;
|
|
177
|
+
let latestRecoverableArtifactText: string | null = null;
|
|
175
178
|
|
|
176
179
|
try {
|
|
177
180
|
while (Date.now() < deadline) {
|
|
178
181
|
task = await client.getTask(continuation.a2aTaskId);
|
|
182
|
+
const recoverableArtifactText = extractVerifiedRecoverableArtifactText(
|
|
183
|
+
task,
|
|
184
|
+
continuation.agentUrl,
|
|
185
|
+
recoverableArtifactSecrets,
|
|
186
|
+
);
|
|
187
|
+
if (recoverableArtifactText) {
|
|
188
|
+
latestRecoverableArtifactText = recoverableArtifactText;
|
|
189
|
+
}
|
|
179
190
|
if (TERMINAL_STATES.has(task.status.state)) break;
|
|
180
191
|
await reportA2AContinuationProgress(continuation, progress, task);
|
|
181
192
|
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
@@ -183,6 +194,17 @@ async function processClaimedContinuation(
|
|
|
183
194
|
} catch (err) {
|
|
184
195
|
if (isTransientA2APollError(err)) {
|
|
185
196
|
if (shouldStopPollingRemoteTask(continuation)) {
|
|
197
|
+
if (latestRecoverableArtifactText) {
|
|
198
|
+
await deliverAndCompleteA2AContinuation(
|
|
199
|
+
continuation,
|
|
200
|
+
adapter,
|
|
201
|
+
formatRecoverableArtifactFallbackText(
|
|
202
|
+
latestRecoverableArtifactText,
|
|
203
|
+
),
|
|
204
|
+
progress,
|
|
205
|
+
);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
186
208
|
await notifyAndFailA2AContinuation(
|
|
187
209
|
continuation,
|
|
188
210
|
adapter,
|
|
@@ -209,6 +231,15 @@ async function processClaimedContinuation(
|
|
|
209
231
|
|
|
210
232
|
if (!task || !TERMINAL_STATES.has(task.status.state)) {
|
|
211
233
|
if (shouldStopPollingRemoteTask(continuation)) {
|
|
234
|
+
if (latestRecoverableArtifactText) {
|
|
235
|
+
await deliverAndCompleteA2AContinuation(
|
|
236
|
+
continuation,
|
|
237
|
+
adapter,
|
|
238
|
+
formatRecoverableArtifactFallbackText(latestRecoverableArtifactText),
|
|
239
|
+
progress,
|
|
240
|
+
);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
212
243
|
await notifyAndFailA2AContinuation(
|
|
213
244
|
continuation,
|
|
214
245
|
adapter,
|
|
@@ -222,6 +253,15 @@ async function processClaimedContinuation(
|
|
|
222
253
|
}
|
|
223
254
|
|
|
224
255
|
if (task.status.state !== "completed") {
|
|
256
|
+
if (latestRecoverableArtifactText) {
|
|
257
|
+
await deliverAndCompleteA2AContinuation(
|
|
258
|
+
continuation,
|
|
259
|
+
adapter,
|
|
260
|
+
formatRecoverableArtifactFallbackText(latestRecoverableArtifactText),
|
|
261
|
+
progress,
|
|
262
|
+
);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
225
265
|
const reason =
|
|
226
266
|
extractTaskText(task) ||
|
|
227
267
|
`Remote A2A task ${continuation.a2aTaskId} ended with state ${task.status.state}`;
|
|
@@ -234,6 +274,15 @@ async function processClaimedContinuation(
|
|
|
234
274
|
continuation.agentUrl,
|
|
235
275
|
);
|
|
236
276
|
if (!text.trim()) {
|
|
277
|
+
if (latestRecoverableArtifactText) {
|
|
278
|
+
await deliverAndCompleteA2AContinuation(
|
|
279
|
+
continuation,
|
|
280
|
+
adapter,
|
|
281
|
+
formatRecoverableArtifactFallbackText(latestRecoverableArtifactText),
|
|
282
|
+
progress,
|
|
283
|
+
);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
237
286
|
await notifyAndFailA2AContinuation(
|
|
238
287
|
continuation,
|
|
239
288
|
adapter,
|
|
@@ -382,6 +431,8 @@ async function deliverAndCompleteA2AContinuation(
|
|
|
382
431
|
`${deliveryContinuation.platform} response delivery timed out`,
|
|
383
432
|
);
|
|
384
433
|
let persistenceError: unknown;
|
|
434
|
+
const artifactSecrets =
|
|
435
|
+
await resolveContinuationArtifactSecrets(deliveryContinuation);
|
|
385
436
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
386
437
|
try {
|
|
387
438
|
await persistA2AContinuationDelivery(
|
|
@@ -389,6 +440,7 @@ async function deliverAndCompleteA2AContinuation(
|
|
|
389
440
|
outgoing,
|
|
390
441
|
deliveryReceipt,
|
|
391
442
|
text,
|
|
443
|
+
artifactSecrets,
|
|
392
444
|
);
|
|
393
445
|
persistenceError = undefined;
|
|
394
446
|
break;
|
|
@@ -463,6 +515,7 @@ async function persistA2AContinuationDelivery(
|
|
|
463
515
|
outgoing: OutgoingMessage,
|
|
464
516
|
receipt: PlatformDeliveryReceipt,
|
|
465
517
|
artifactText: string,
|
|
518
|
+
artifactSecrets: readonly string[],
|
|
466
519
|
): Promise<void> {
|
|
467
520
|
const mapping = await getThreadMapping(
|
|
468
521
|
continuation.platform,
|
|
@@ -480,9 +533,12 @@ async function persistA2AContinuationDelivery(
|
|
|
480
533
|
}
|
|
481
534
|
if (!Array.isArray(repo.messages)) repo.messages = [];
|
|
482
535
|
|
|
483
|
-
const artifacts = extractA2AArtifactIdentities(
|
|
484
|
-
{ tool: "call-agent", result: artifactText },
|
|
485
|
-
|
|
536
|
+
const artifacts = extractA2AArtifactIdentities(
|
|
537
|
+
[{ tool: "call-agent", result: artifactText }],
|
|
538
|
+
{
|
|
539
|
+
persistedArtifactSecrets: artifactSecrets,
|
|
540
|
+
},
|
|
541
|
+
);
|
|
486
542
|
const metadata: Record<string, unknown> = {
|
|
487
543
|
integrationDeliveryAttempted: true,
|
|
488
544
|
integrationDelivery: {
|
|
@@ -726,6 +782,24 @@ async function signFreshContinuationTokens(
|
|
|
726
782
|
return tokens;
|
|
727
783
|
}
|
|
728
784
|
|
|
785
|
+
async function resolveContinuationArtifactSecrets(
|
|
786
|
+
continuation: A2AContinuation,
|
|
787
|
+
): Promise<string[]> {
|
|
788
|
+
const secrets: string[] = [];
|
|
789
|
+
const add = (secret: string | null | undefined) => {
|
|
790
|
+
const value = secret?.trim();
|
|
791
|
+
if (value && !secrets.includes(value)) secrets.push(value);
|
|
792
|
+
};
|
|
793
|
+
add(process.env.A2A_SECRET);
|
|
794
|
+
if (continuation.orgId) {
|
|
795
|
+
try {
|
|
796
|
+
const { getOrgA2ASecret } = await import("../org/context.js");
|
|
797
|
+
add(await getOrgA2ASecret(continuation.orgId));
|
|
798
|
+
} catch {}
|
|
799
|
+
}
|
|
800
|
+
return secrets;
|
|
801
|
+
}
|
|
802
|
+
|
|
729
803
|
function isLikelyJwt(token: string): boolean {
|
|
730
804
|
return token.split(".").length === 3;
|
|
731
805
|
}
|
|
@@ -740,6 +814,36 @@ function extractTaskText(task: Task): string {
|
|
|
740
814
|
.join("\n");
|
|
741
815
|
}
|
|
742
816
|
|
|
817
|
+
function extractVerifiedRecoverableArtifactText(
|
|
818
|
+
task: Task,
|
|
819
|
+
agentUrl: string,
|
|
820
|
+
artifactSecrets: readonly string[],
|
|
821
|
+
): string | null {
|
|
822
|
+
if (task.status.message?.metadata?.agentNativeRecoverableArtifacts !== true) {
|
|
823
|
+
return null;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
const text = formatContinuationArtifactText(extractTaskText(task), agentUrl);
|
|
827
|
+
if (!text.trim()) return null;
|
|
828
|
+
|
|
829
|
+
// Require the signed identity ledger so arbitrary peer progress prose cannot
|
|
830
|
+
// prematurely complete the continuation.
|
|
831
|
+
const artifacts = extractA2AArtifactIdentities(
|
|
832
|
+
[{ tool: "call-agent", result: text }],
|
|
833
|
+
{
|
|
834
|
+
persistedArtifactSecrets: artifactSecrets,
|
|
835
|
+
},
|
|
836
|
+
);
|
|
837
|
+
return artifacts.length > 0 ? text : null;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
function formatRecoverableArtifactFallbackText(text: string): string {
|
|
841
|
+
return text.replace(
|
|
842
|
+
"The agent is still working on the full response, but these verified artifacts already exist:",
|
|
843
|
+
"The downstream agent did not finish its full response, but these verified artifacts already exist:",
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
|
|
743
847
|
function formatContinuationArtifactText(
|
|
744
848
|
text: string,
|
|
745
849
|
agentUrl: string,
|
|
@@ -272,6 +272,19 @@ export async function claimPendingTask(
|
|
|
272
272
|
AND active.status = 'processing'
|
|
273
273
|
AND active.id <> integration_pending_tasks.id
|
|
274
274
|
)
|
|
275
|
+
AND NOT EXISTS (
|
|
276
|
+
SELECT 1 FROM integration_pending_tasks earlier
|
|
277
|
+
WHERE earlier.platform = integration_pending_tasks.platform
|
|
278
|
+
AND earlier.external_thread_id = integration_pending_tasks.external_thread_id
|
|
279
|
+
AND earlier.status = 'pending'
|
|
280
|
+
AND (
|
|
281
|
+
earlier.created_at < integration_pending_tasks.created_at
|
|
282
|
+
OR (
|
|
283
|
+
earlier.created_at = integration_pending_tasks.created_at
|
|
284
|
+
AND earlier.id < integration_pending_tasks.id
|
|
285
|
+
)
|
|
286
|
+
)
|
|
287
|
+
)
|
|
275
288
|
RETURNING id, platform, external_thread_id, payload, owner_email, org_id, status, attempts, error_message, created_at, updated_at, completed_at`
|
|
276
289
|
: `UPDATE integration_pending_tasks
|
|
277
290
|
SET status = ?, attempts = attempts + 1, updated_at = ?
|
|
@@ -282,6 +295,19 @@ export async function claimPendingTask(
|
|
|
282
295
|
AND active.external_thread_id = integration_pending_tasks.external_thread_id
|
|
283
296
|
AND active.status = 'processing'
|
|
284
297
|
AND active.id <> integration_pending_tasks.id
|
|
298
|
+
)
|
|
299
|
+
AND NOT EXISTS (
|
|
300
|
+
SELECT 1 FROM integration_pending_tasks earlier
|
|
301
|
+
WHERE earlier.platform = integration_pending_tasks.platform
|
|
302
|
+
AND earlier.external_thread_id = integration_pending_tasks.external_thread_id
|
|
303
|
+
AND earlier.status = 'pending'
|
|
304
|
+
AND (
|
|
305
|
+
earlier.created_at < integration_pending_tasks.created_at
|
|
306
|
+
OR (
|
|
307
|
+
earlier.created_at = integration_pending_tasks.created_at
|
|
308
|
+
AND earlier.id < integration_pending_tasks.id
|
|
309
|
+
)
|
|
310
|
+
)
|
|
285
311
|
)`,
|
|
286
312
|
args: ["processing", now, id],
|
|
287
313
|
});
|
|
@@ -312,7 +338,7 @@ export async function getNextPendingTaskIdForThread(
|
|
|
312
338
|
const { rows } = await getDbExec().execute({
|
|
313
339
|
sql: `SELECT id FROM integration_pending_tasks
|
|
314
340
|
WHERE platform = ? AND external_thread_id = ? AND status = 'pending'
|
|
315
|
-
ORDER BY created_at ASC LIMIT 1`,
|
|
341
|
+
ORDER BY created_at ASC, id ASC LIMIT 1`,
|
|
316
342
|
args: [platform, externalThreadId],
|
|
317
343
|
});
|
|
318
344
|
return rows[0]?.id ? String(rows[0].id) : null;
|
|
@@ -354,6 +380,77 @@ export async function markTaskRetryable(
|
|
|
354
380
|
});
|
|
355
381
|
}
|
|
356
382
|
|
|
383
|
+
export async function stageTaskDeliveryPayload(
|
|
384
|
+
id: string,
|
|
385
|
+
payload: string,
|
|
386
|
+
): Promise<void> {
|
|
387
|
+
await ensureTable();
|
|
388
|
+
const client = getDbExec();
|
|
389
|
+
const now = Date.now();
|
|
390
|
+
const result = await client.execute({
|
|
391
|
+
sql: `UPDATE integration_pending_tasks
|
|
392
|
+
SET payload = ?, updated_at = ?
|
|
393
|
+
WHERE id = ? AND status = 'processing'`,
|
|
394
|
+
args: [payload, now, id],
|
|
395
|
+
});
|
|
396
|
+
const affected = Number(
|
|
397
|
+
(result as { rowsAffected?: number }).rowsAffected ??
|
|
398
|
+
(result as { rowCount?: number }).rowCount ??
|
|
399
|
+
0,
|
|
400
|
+
);
|
|
401
|
+
if (affected === 0) {
|
|
402
|
+
throw new Error("Integration task is no longer claimable for delivery");
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export async function markTaskDeliveryRetryable(
|
|
407
|
+
id: string,
|
|
408
|
+
payload: string,
|
|
409
|
+
errorMessage: string,
|
|
410
|
+
): Promise<void> {
|
|
411
|
+
await ensureTable();
|
|
412
|
+
const client = getDbExec();
|
|
413
|
+
const result = await client.execute({
|
|
414
|
+
sql: `UPDATE integration_pending_tasks
|
|
415
|
+
SET status = ?, payload = ?, updated_at = ?, error_message = ?
|
|
416
|
+
WHERE id = ? AND status = 'processing'`,
|
|
417
|
+
args: ["pending", payload, Date.now(), errorMessage.slice(0, 2000), id],
|
|
418
|
+
});
|
|
419
|
+
const affected = Number(
|
|
420
|
+
(result as { rowsAffected?: number }).rowsAffected ??
|
|
421
|
+
(result as { rowCount?: number }).rowCount ??
|
|
422
|
+
0,
|
|
423
|
+
);
|
|
424
|
+
if (affected === 0) {
|
|
425
|
+
throw new Error(
|
|
426
|
+
"Integration task is no longer claimable for delivery retry",
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export async function failTaskDeliveryTransition(
|
|
432
|
+
id: string,
|
|
433
|
+
errorMessage: string,
|
|
434
|
+
): Promise<void> {
|
|
435
|
+
await ensureTable();
|
|
436
|
+
const result = await getDbExec().execute({
|
|
437
|
+
sql: `UPDATE integration_pending_tasks
|
|
438
|
+
SET status = ?, updated_at = ?, error_message = ?, payload = ?
|
|
439
|
+
WHERE id = ? AND status = 'processing'`,
|
|
440
|
+
args: ["failed", Date.now(), errorMessage.slice(0, 2000), "{}", id],
|
|
441
|
+
});
|
|
442
|
+
const affected = Number(
|
|
443
|
+
(result as { rowsAffected?: number }).rowsAffected ??
|
|
444
|
+
(result as { rowCount?: number }).rowCount ??
|
|
445
|
+
0,
|
|
446
|
+
);
|
|
447
|
+
if (affected === 0) {
|
|
448
|
+
throw new Error(
|
|
449
|
+
"Integration task delivery failure transition lost its race",
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
357
454
|
/** Mark a task as failed and stash an error message. */
|
|
358
455
|
export async function markTaskFailed(
|
|
359
456
|
id: string,
|