@agent-native/core 0.151.2 → 0.152.1
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/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/analytics/actions/bigquery-query.ts +21 -0
- package/corpus/templates/analytics/app/components/ConnectionTestStatus.tsx +65 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +3 -3
- package/corpus/templates/analytics/app/pages/DataSources.tsx +15 -37
- package/corpus/templates/analytics/app/root.tsx +0 -1
- package/corpus/templates/analytics/changelog/2026-08-11-analytics-custom-blocks-keep-loading-with-legacy-bigquery-action.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-11-analytics-routes-company-knowledge-and-slack-context-questio.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/analytics/public/manifest.json +0 -1
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +5 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/assets/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/assets/app/root.tsx +0 -1
- package/corpus/templates/assets/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/assets/public/manifest.json +0 -1
- package/corpus/templates/brain/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/brain/app/root.tsx +0 -1
- package/corpus/templates/brain/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/brain/public/manifest.json +0 -1
- package/corpus/templates/calendar/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +0 -53
- package/corpus/templates/calendar/app/i18n-data.ts +0 -80
- package/corpus/templates/calendar/app/root.tsx +0 -1
- package/corpus/templates/calendar/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-11-google-calendar-connection-buttons-now-open-the-sign-in-flow.md +6 -0
- package/corpus/templates/calendar/public/manifest.json +0 -1
- package/corpus/templates/calendar/server/plugins/auth.ts +0 -10
- package/corpus/templates/chat/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/chat/app/root.tsx +0 -1
- package/corpus/templates/chat/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/chat/public/manifest.json +0 -1
- package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +1 -1
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +6 -0
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +20 -3
- package/corpus/templates/clips/app/i18n/en-US.ts +4 -4
- package/corpus/templates/clips/app/root.tsx +0 -1
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +1 -3
- package/corpus/templates/clips/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-clips-desktop-settings-are-now-easier-to-scan-with-a-wider-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-google-calendar-connections-from-meetings-now-open-directly.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-recording-viewers-can-comment-and-react.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-system-audio-recording-now-lives-inside-the-microphone-menu-.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-timestamped-comments-stay-on-the-video-for-three-seconds-wh.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +773 -1001
- package/corpus/templates/clips/desktop/src/components/MediaDeviceRow.tsx +15 -27
- package/corpus/templates/clips/desktop/src/styles.css +364 -5
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +1 -1
- package/corpus/templates/clips/public/manifest.json +0 -1
- package/corpus/templates/content/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/content/AGENTS.md +3 -0
- package/corpus/templates/content/actions/resolve-content-landing.ts +120 -0
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +196 -30
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +9 -0
- package/corpus/templates/content/app/i18n-data.ts +58 -0
- package/corpus/templates/content/app/lib/content-landing.ts +22 -0
- package/corpus/templates/content/app/root.tsx +0 -1
- package/corpus/templates/content/app/routes/_app._index.tsx +36 -26
- package/corpus/templates/content/changelog/2026-08-03-choose-text-or-a-heading-from-one-compact-formatting-control.md +6 -0
- package/corpus/templates/content/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/content/changelog/2026-08-11-content-opens-your-last-page-on-return-and-gives-new-users-a.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.workspace.session-restore.md +7 -1
- package/corpus/templates/content/docs/solutions/evidence/content-text-selector/README.md +26 -0
- package/corpus/templates/content/parity/matrix.md +1 -0
- package/corpus/templates/content/parity/matrix.ts +22 -0
- package/corpus/templates/content/public/manifest.json +0 -1
- package/corpus/templates/content/shared/content-landing.ts +17 -0
- package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/crm/app/root.tsx +0 -1
- package/corpus/templates/crm/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/crm/public/manifest.json +0 -1
- package/corpus/templates/design/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/design/app/i18n/en-US.ts +5 -0
- package/corpus/templates/design/app/root.tsx +0 -1
- package/corpus/templates/design/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/design/changelog/2026-08-11-design-sharing-now-identifies-viewers-who-can-add-review-com.md +6 -0
- package/corpus/templates/design/public/manifest.json +0 -1
- package/corpus/templates/dispatch/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/dispatch/AGENTS.md +9 -0
- package/corpus/templates/dispatch/actions/list-dispatch-usage-metrics.ts +5 -19
- package/corpus/templates/dispatch/actions/view-screen.ts +10 -3
- package/corpus/templates/dispatch/app/root.tsx +0 -1
- package/corpus/templates/dispatch/app/routes/settings.tsx +13 -1
- package/corpus/templates/dispatch/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-clicking-the-dispatch-logo-returns-to-the-overview-page.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-no-longer-links-to-itself-from-the-app-switcher.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-settings-now-link-directly-to-admin.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-shows-usage-trends-recent-prompts-and-an-agent-revi.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-human-friendly-app-names.md +6 -0
- package/corpus/templates/dispatch/public/manifest.json +0 -1
- package/corpus/templates/dispatch/server/lib/identity-sso.ts +432 -158
- package/corpus/templates/dispatch/server/plugins/identity-sso.ts +204 -123
- package/corpus/templates/dispatch/server/plugins/setup-dispatch.ts +1 -0
- package/corpus/templates/factory/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/factory/app/root.tsx +0 -1
- package/corpus/templates/factory/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/factory/public/manifest.json +0 -1
- package/corpus/templates/forms/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/forms/app/root.tsx +0 -1
- package/corpus/templates/forms/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/forms/public/manifest.json +0 -1
- package/corpus/templates/macros/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/mail/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/mail/app/root.tsx +0 -1
- package/corpus/templates/mail/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/mail/changelog/2026-08-11-google-sign-in-now-opens-the-gmail-connection-flow-directly.md +6 -0
- package/corpus/templates/mail/public/manifest.json +0 -1
- package/corpus/templates/mail/server/plugins/auth.ts +0 -10
- package/corpus/templates/plan/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/plan/app/i18n/en-US.ts +2 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +6 -0
- package/corpus/templates/plan/app/root.tsx +0 -1
- package/corpus/templates/plan/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/plan/changelog/2026-08-11-plan-sharing-now-identifies-viewers-who-can-add-comments.md +6 -0
- package/corpus/templates/plan/public/manifest.json +0 -1
- package/corpus/templates/slides/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +6 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +2 -0
- package/corpus/templates/slides/app/root.tsx +0 -1
- package/corpus/templates/slides/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-share-dialog-uses-commenter-terminology.md +6 -0
- package/corpus/templates/slides/public/manifest.json +0 -1
- package/corpus/templates/tasks/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/tasks/app/root.tsx +0 -1
- package/corpus/templates/tasks/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/tasks/public/manifest.json +0 -1
- package/dist/agent/production-agent.d.ts +8 -0
- package/dist/agent/production-agent.js +67 -22
- package/dist/agent/run-manager.d.ts +7 -0
- package/dist/agent/run-manager.js +46 -3
- package/dist/agent/tool-approval-migrations.d.ts +40 -0
- package/dist/agent/tool-approval-migrations.js +51 -0
- package/dist/agent/tool-approval-store.js +5 -20
- package/dist/cli/templates-meta.js +2 -2
- package/dist/client/AgentPanel.js +10 -1
- package/dist/client/AssistantChat.d.ts +3 -0
- package/dist/client/AssistantChat.js +151 -141
- package/dist/client/agent-page/AgentTabsPage.js +2 -2
- package/dist/client/chat/attachment-adapters.js +8 -2
- package/dist/client/chat/tool-call-display.d.ts +1 -0
- package/dist/client/chat/tool-call-display.js +3 -1
- package/dist/client/chat-first.js +11 -5
- package/dist/client/guided-questions.d.ts +15 -1
- package/dist/client/guided-questions.js +33 -15
- package/dist/client/hooks/index.d.ts +1 -1
- package/dist/client/hooks/index.js +1 -1
- package/dist/client/org/OrgSwitcher.d.ts +3 -1
- package/dist/client/org/OrgSwitcher.js +22 -16
- package/dist/client/org/WorkspaceNotice.js +3 -1
- package/dist/client/resources/index.d.ts +1 -1
- package/dist/client/resources/index.js +1 -1
- package/dist/client/resources/use-mcp-servers.d.ts +1 -0
- package/dist/client/resources/use-mcp-servers.js +14 -0
- package/dist/client/settings/AccountSettingsCard.js +8 -4
- package/dist/client/settings/SettingsPanel.js +46 -13
- package/dist/client/settings/SettingsTabsPage.js +8 -3
- package/dist/client/sharing/ShareButton.d.ts +7 -1
- package/dist/client/sharing/ShareButton.js +14 -7
- package/dist/client/sharing/ShareDialog.js +5 -1
- package/dist/client/sharing/useShareButtonController.d.ts +4 -0
- package/dist/client/sharing/useShareButtonController.js +15 -0
- package/dist/client/sharing/useShareDialogController.d.ts +7 -0
- package/dist/client/sharing/useShareDialogController.js +14 -0
- package/dist/client/sse-event-processor.js +37 -0
- package/dist/client/use-session.d.ts +2 -0
- package/dist/client/use-session.js +69 -5
- package/dist/deploy/build.js +1 -1
- package/dist/file-upload/pre-upload-attachments.d.ts +3 -4
- package/dist/file-upload/pre-upload-attachments.js +65 -17
- package/dist/ingestion/index.d.ts +1 -0
- package/dist/ingestion/index.js +1 -0
- package/dist/ingestion/office.d.ts +3 -3
- package/dist/ingestion/office.js +24 -2
- package/dist/ingestion/spreadsheet.d.ts +41 -0
- package/dist/ingestion/spreadsheet.js +172 -0
- package/dist/integrations/adapters/slack.js +7 -3
- package/dist/integrations/webhook-handler.js +11 -3
- package/dist/localization/default-messages.d.ts +3 -0
- package/dist/localization/default-messages.js +3 -0
- package/dist/mcp/embed-app.js +20 -9
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/oauth-tokens/migrations.d.ts +8 -0
- package/dist/oauth-tokens/migrations.js +36 -0
- package/dist/observability/routes.d.ts +5 -5
- package/dist/org/workspace-url.d.ts +8 -0
- package/dist/org/workspace-url.js +26 -2
- package/dist/progress/routes.d.ts +1 -1
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-chat/context-tools.js +27 -6
- package/dist/server/agent-chat-plugin.js +16 -8
- package/dist/server/auth.d.ts +0 -11
- package/dist/server/auth.js +37 -20
- package/dist/server/better-auth-migrations.js +23 -0
- package/dist/server/email-template.d.ts +5 -0
- package/dist/server/email-template.js +9 -0
- package/dist/server/embed-route.js +17 -5
- package/dist/server/identity-sso-store.d.ts +40 -66
- package/dist/server/identity-sso-store.js +187 -150
- package/dist/server/identity-sso.d.ts +17 -73
- package/dist/server/identity-sso.js +294 -297
- package/dist/server/onboarding-html.d.ts +0 -12
- package/dist/server/onboarding-html.js +46 -269
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/release-migrations.js +8 -0
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/workspace-app-id.d.ts +2 -0
- package/dist/shared/workspace-app-id.js +10 -0
- package/dist/sharing/actions/share-resource.d.ts +1 -0
- package/dist/sharing/actions/share-resource.js +20 -5
- package/dist/templates/chat/.agents/skills/sharing/SKILL.md +1 -1
- package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/dist/templates/chat/app/root.tsx +0 -1
- package/dist/templates/chat/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/dist/templates/chat/public/manifest.json +0 -1
- package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/dist/templates/default/app/root.tsx +0 -1
- package/dist/templates/default/public/manifest.json +0 -1
- package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +22 -11
- package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/docs/content/cross-app-sso.mdx +85 -27
- package/docs/content/template-mail.mdx +0 -6
- package/package.json +4 -3
- package/src/templates/chat/.agents/skills/sharing/SKILL.md +1 -1
- package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/src/templates/chat/app/root.tsx +0 -1
- package/src/templates/chat/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/src/templates/chat/public/manifest.json +0 -1
- package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/src/templates/default/app/root.tsx +0 -1
- package/src/templates/default/public/manifest.json +0 -1
- package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +22 -11
- package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +1 -1
- package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/calendar/changelog/2026-06-23-added-a-heads-up-explaining-google-s-app-not-verified-screen.md +0 -6
- package/corpus/templates/clips/app/components/meetings/google-oauth-identity-notice.tsx +0 -51
- package/corpus/templates/clips/changelog/2026-07-30-google-calendar-warning.md +0 -6
|
@@ -50,6 +50,15 @@ const ROLE_OPTIONS = [
|
|
|
50
50
|
description: "Can edit and manage access",
|
|
51
51
|
},
|
|
52
52
|
];
|
|
53
|
+
function roleMeta(role, copy) {
|
|
54
|
+
const base = ROLE_OPTIONS.find((option) => option.value === role) ?? ROLE_OPTIONS[0];
|
|
55
|
+
const override = copy?.[role];
|
|
56
|
+
return {
|
|
57
|
+
...base,
|
|
58
|
+
label: override?.label ?? base.label,
|
|
59
|
+
description: override?.description ?? base.description,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
53
62
|
function isShareNestedOverlayTarget(target) {
|
|
54
63
|
return (target instanceof Element &&
|
|
55
64
|
target.closest(`[${SHARE_NESTED_OVERLAY_ATTR}]`) !== null);
|
|
@@ -88,7 +97,7 @@ export function ShareButton(props) {
|
|
|
88
97
|
}
|
|
89
98
|
function SharePanel(props) {
|
|
90
99
|
const { resourceTitle, controller } = props;
|
|
91
|
-
const { inviteEmail, setInviteEmail: onInviteEmailChange, activeShareTab, handleShareTabChange, data, policy, visibility, canManage, role, setRole, notifyPeople, setNotifyPeople, shareError, setShareError, suggestionsOpen, setSuggestionsOpen, inFlight, memberSearch, memberSuggestions, knownMembers, shares, handleVisibility, handleHideInSearch, handleAdd, handleChangeRole, handleRemove, } = controller;
|
|
100
|
+
const { inviteEmail, setInviteEmail: onInviteEmailChange, activeShareTab, handleShareTabChange, data, policy, visibility, canManage, role, setRole, notifyPeople, setNotifyPeople, shareMessage, setShareMessage, messageOpen, setMessageOpen, shareError, setShareError, suggestionsOpen, setSuggestionsOpen, inFlight, memberSearch, memberSuggestions, knownMembers, shares, handleVisibility, handleHideInSearch, handleAdd, handleChangeRole, handleRemove, } = controller;
|
|
92
101
|
const hasInviteEmail = inviteEmail.trim().length > 0;
|
|
93
102
|
const isLoading = data === undefined;
|
|
94
103
|
const meta = visibilityMeta(visibility, props.visibilityCopy);
|
|
@@ -117,7 +126,7 @@ function SharePanel(props) {
|
|
|
117
126
|
setShareError(null);
|
|
118
127
|
}, onSubmit: handleAdd, placeholder: policy.requireOrgMemberForUserShares
|
|
119
128
|
? "Add people from your organization"
|
|
120
|
-
: "Add people by email", suggestions: memberSuggestions, search: memberSearch }), _jsx(RoleSelect, { value: role, onChange: setRole }), _jsx("button", { type: "button", onClick: handleAdd, disabled: !hasInviteEmail, className: BUTTON_PRIMARY_SM, children: "Add" })] }), shareError ? (_jsx("div", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: shareError })) : null, hasInviteEmail ? (_jsxs("
|
|
129
|
+
: "Add people by email", suggestions: memberSuggestions, search: memberSearch }), _jsx(RoleSelect, { value: role, onChange: setRole, roleCopy: props.roleCopy }), _jsx("button", { type: "button", onClick: handleAdd, disabled: !hasInviteEmail, className: BUTTON_PRIMARY_SM, children: "Add" })] }), shareError ? (_jsx("div", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: shareError })) : null, hasInviteEmail ? (_jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2 text-xs text-muted-foreground", children: [_jsxs("label", { className: "inline-flex items-center gap-2", children: [_jsx("input", { type: "checkbox", checked: notifyPeople, onChange: (e) => setNotifyPeople(e.target.checked), className: "h-4 w-4 rounded border-input accent-primary" }), "Notify people"] }), notifyPeople ? (_jsx("button", { type: "button", "aria-expanded": messageOpen, onClick: () => setMessageOpen(!messageOpen), className: "rounded-sm px-1 py-0.5 font-medium text-foreground underline decoration-border underline-offset-2 transition-colors hover:bg-accent hover:text-accent-foreground", children: messageOpen ? "Hide message" : "Add a message" })) : null] })) : null, hasInviteEmail && notifyPeople && messageOpen ? (_jsx("div", { className: "rounded-md border border-border/70 bg-muted/20 p-2.5", children: _jsx("textarea", { "aria-label": "Message", placeholder: "Add a short note (optional)", value: shareMessage, onChange: (event) => setShareMessage(event.target.value), maxLength: 500, rows: 3, className: "w-full resize-y rounded-md border border-input bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background" }) })) : null] })) : null, _jsx("div", { className: "mb-2 text-sm font-semibold", children: peopleAccessLabel }), _jsxs("ul", { className: "mb-4 flex flex-col gap-1 list-none p-0 m-0", children: [data?.ownerEmail ? (_jsxs("li", { className: "flex items-center gap-3 px-1 py-1.5 text-sm", children: [_jsx(Avatar, { label: displayName(data.ownerEmail, knownMembers) }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: displayName(data.ownerEmail, knownMembers) }), _jsx("span", { className: "text-xs text-muted-foreground", children: "Owner" })] })) : null, shares.map((s) => (_jsxs("li", { className: cn("flex items-center gap-3 px-1 py-1.5 text-sm", inFlight.has(keyOf(s)) && "opacity-60"), children: [_jsx(Avatar, { label: principalLabel(s, knownMembers), org: s.principalType === "org" }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: principalLabel(s, knownMembers) }), canManage ? (_jsx(RoleSelect, { value: s.role, onChange: (r) => handleChangeRole(s, r), disabled: inFlight.has(keyOf(s)), plain: true, roleCopy: props.roleCopy })) : (_jsx("span", { className: "text-xs text-muted-foreground", children: roleMeta(s.role, props.roleCopy).label })), canManage ? (_jsx("button", { type: "button", "aria-label": "Remove", onClick: () => handleRemove(s), disabled: inFlight.has(keyOf(s)), className: BUTTON_GHOST_ICON, children: _jsx(IconTrash, { size: 14 }) })) : null] }, keyOf(s)))), !shares.length && !data?.ownerEmail ? (_jsx("li", { className: "px-1 py-1.5 text-sm text-muted-foreground", children: "No one has access yet." })) : null] }), _jsx("div", { className: "mb-2 text-sm font-semibold", children: generalAccessLabel }), _jsxs("div", { className: "mb-4 flex items-center gap-3", children: [_jsx("span", { "aria-hidden": true, className: "inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground", children: _jsx(meta.Icon, { size: 16, strokeWidth: 1.75 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(VisibilitySelect, { value: visibility, onChange: handleVisibility, disabled: !canManage, visibilityCopy: props.visibilityCopy, allowPublic: policy.allowPublic }), _jsxs("div", { className: "mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-muted-foreground", children: [_jsx("span", { children: meta.description }), visibility === "org" && props.hideInSearchControl ? (_jsx(AdvancedAccessPopover, { control: props.hideInSearchControl, canManage: canManage, onToggle: handleHideInSearch })) : null] })] })] }), shareError && !canManage ? (_jsx("div", { role: "alert", className: "mb-4 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: shareError })) : null, props.accessNote ? (_jsx("div", { className: "mb-4 rounded-md border border-border bg-muted/35 p-3 text-xs text-muted-foreground", children: props.accessNote })) : null, showShareLinks && shareUrlPlacement === "bottom" ? shareLinks : null, props.shareFooterContent] }));
|
|
121
130
|
if (!hasTabs)
|
|
122
131
|
return sharePanel;
|
|
123
132
|
const tabs = [
|
|
@@ -271,10 +280,11 @@ function SelectItems({ items }) {
|
|
|
271
280
|
return (_jsx(_Fragment, { children: items.map((it) => (_jsxs(Select.Item, { value: it.value, className: selectItemClass, children: [_jsx("span", { className: "absolute start-2 top-2 flex h-4 w-4 items-center justify-center", children: _jsx(Select.ItemIndicator, { children: _jsx(IconCheck, { size: 14 }) }) }), _jsxs("span", { className: "flex flex-col", children: [_jsx(Select.ItemText, { children: it.label }), it.description ? (_jsx("span", { className: "text-xs text-muted-foreground", children: it.description })) : null] })] }, it.value))) }));
|
|
272
281
|
}
|
|
273
282
|
function RoleSelect(props) {
|
|
274
|
-
const current =
|
|
283
|
+
const current = roleMeta(props.value, props.roleCopy);
|
|
284
|
+
const options = ROLE_OPTIONS.map((option) => roleMeta(option.value, props.roleCopy));
|
|
275
285
|
return (_jsxs(Select.Root, { value: props.value, onValueChange: (v) => props.onChange(v), disabled: props.disabled, children: [_jsxs(Select.Trigger, { className: props.plain
|
|
276
286
|
? cn(BUTTON_BASE, "h-7 px-2 bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground")
|
|
277
|
-
: cn(BUTTON_BASE, "h-9 px-3 border border-input bg-card hover:bg-accent hover:text-accent-foreground"), "aria-label": "Role", children: [_jsx(Select.Value, { children: current.label }), _jsx(Select.Icon, { children: _jsx(IconChevronDown, { size: 14 }) })] }), _jsx(Select.Portal, { children: _jsx(Select.Content, { "data-agent-native-share-overlay": "", className: selectContentClass, position: "popper", sideOffset: 4, children: _jsx(Select.Viewport, { children: _jsx(SelectItems, { items:
|
|
287
|
+
: cn(BUTTON_BASE, "h-9 px-3 border border-input bg-card hover:bg-accent hover:text-accent-foreground"), "aria-label": "Role", children: [_jsx(Select.Value, { children: current.label }), _jsx(Select.Icon, { children: _jsx(IconChevronDown, { size: 14 }) })] }), _jsx(Select.Portal, { children: _jsx(Select.Content, { "data-agent-native-share-overlay": "", className: selectContentClass, position: "popper", sideOffset: 4, children: _jsx(Select.Viewport, { children: _jsx(SelectItems, { items: options }) }) }) })] }));
|
|
278
288
|
}
|
|
279
289
|
function VisibilitySelect(props) {
|
|
280
290
|
const allowPrivate = props.allowPrivate !== false;
|
|
@@ -302,9 +312,6 @@ function Avatar({ label, org }) {
|
|
|
302
312
|
function keyOf(s) {
|
|
303
313
|
return `${s.principalType}:${s.principalId}`;
|
|
304
314
|
}
|
|
305
|
-
function cap(s) {
|
|
306
|
-
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
307
|
-
}
|
|
308
315
|
function initials(s) {
|
|
309
316
|
const name = s.split("@")[0] ?? s;
|
|
310
317
|
return (name[0] ?? "?").toUpperCase();
|
|
@@ -56,7 +56,11 @@ function InviteTab({ controller, showVisibility, }) {
|
|
|
56
56
|
return (_jsxs("div", { className: "space-y-4", children: [controller.canManage ? (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-stretch gap-2", children: [_jsx("input", { type: "email", placeholder: controller.labels.addPeopleByEmail, value: controller.invite.email, onChange: (event) => controller.invite.setEmail(event.currentTarget.value), onKeyDown: (event) => {
|
|
57
57
|
if (event.key === "Enter")
|
|
58
58
|
controller.invite.submit();
|
|
59
|
-
}, autoComplete: "off", className: "flex-1 min-w-0 h-9 rounded-md border border-input bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background" }), _jsx(RoleSelect, { controller: controller })] }), controller.invite.showNotifyPeople ? (_jsxs("
|
|
59
|
+
}, autoComplete: "off", className: "flex-1 min-w-0 h-9 rounded-md border border-input bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background" }), _jsx(RoleSelect, { controller: controller })] }), controller.invite.showNotifyPeople ? (_jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2 text-xs text-muted-foreground", children: [_jsxs("label", { className: "inline-flex items-center gap-2", children: [_jsx("input", { type: "checkbox", checked: controller.invite.notifyPeople, onChange: (event) => controller.invite.setNotifyPeople(event.currentTarget.checked), className: "h-4 w-4 rounded border-input accent-primary" }), controller.labels.notifyPeople] }), controller.invite.notifyPeople ? (_jsx(ActionButton, { type: "button", emphasis: "ghost", "aria-expanded": controller.invite.messageOpen, onPress: () => controller.invite.setMessageOpen(!controller.invite.messageOpen), className: "!h-auto !rounded-sm !px-1 !py-0.5 text-xs font-medium !text-foreground underline decoration-border underline-offset-2 hover:!bg-accent hover:!text-accent-foreground active:!scale-100", children: controller.invite.messageOpen
|
|
60
|
+
? controller.labels.hideMessage
|
|
61
|
+
: controller.labels.addMessage })) : null] })) : null, controller.invite.showNotifyPeople &&
|
|
62
|
+
controller.invite.notifyPeople &&
|
|
63
|
+
controller.invite.messageOpen ? (_jsx(TextArea, { "aria-label": controller.labels.addMessage, placeholder: controller.labels.messagePlaceholder, value: controller.invite.message, onChange: (value) => controller.invite.setMessage(value), maxLength: 500, rows: 3, className: "min-h-20 resize-y text-sm" })) : null] })) : null, _jsxs("div", { children: [_jsx("div", { className: "mb-2 text-sm font-semibold", children: controller.labels.peopleWithAccess }), _jsxs("ul", { className: "flex flex-col gap-1 list-none p-0 m-0", children: [controller.people.map((person) => (_jsx(PersonRow, { person: person, canManage: controller.canManage, removeLabel: controller.labels.remove, onRemove: controller.removeShare }, person.key))), !controller.people.length ? (_jsx("li", { className: "px-1 py-1.5 text-sm text-muted-foreground", children: controller.labels.noAccess })) : null] })] }), showVisibility ? (_jsxs("div", { children: [_jsx("div", { className: "mb-2 text-sm font-semibold", children: controller.labels.generalAccess }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { "aria-hidden": true, className: "inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground", children: _jsx(Icon, { size: 16, strokeWidth: 1.75 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(VisibilitySelect, { controller: controller }), _jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: controller.visibility.description })] })] })] })) : null] }));
|
|
60
64
|
}
|
|
61
65
|
function PersonRow({ person, canManage, removeLabel, onRemove, }) {
|
|
62
66
|
return (_jsxs("li", { className: "flex items-center gap-3 px-1 py-1.5 text-sm", children: [_jsx(DesignSystemAvatar, { name: person.label, fallback: person.principalType === "org" ? (_jsx(IconUsersGroup, { size: 14, strokeWidth: 1.75 })) : (person.avatarText), size: "compact", className: "inline-flex h-7 w-7 shrink-0 text-[11px] font-semibold" }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: person.label }), _jsx(Status, { tone: "neutral", size: "compact", className: "border-0 bg-transparent px-0 text-xs text-muted-foreground", children: person.roleLabel }), canManage && person.share ? (_jsx(IconButton, { intent: "danger", emphasis: "ghost", size: "compact", icon: _jsx(IconTrash, { size: 14 }), label: removeLabel, "aria-label": removeLabel, onPress: () => onRemove(person.share), className: cn(BUTTON_GHOST_ICON, "[&_svg]:!size-auto") })) : null] }));
|
|
@@ -71,6 +71,10 @@ export interface ShareButtonController {
|
|
|
71
71
|
setRole: (role: ShareButtonRole) => void;
|
|
72
72
|
notifyPeople: boolean;
|
|
73
73
|
setNotifyPeople: (notify: boolean) => void;
|
|
74
|
+
shareMessage: string;
|
|
75
|
+
setShareMessage: (message: string) => void;
|
|
76
|
+
messageOpen: boolean;
|
|
77
|
+
setMessageOpen: (open: boolean) => void;
|
|
74
78
|
shareError: string | null;
|
|
75
79
|
setShareError: (error: string | null) => void;
|
|
76
80
|
suggestionsOpen: boolean;
|
|
@@ -25,6 +25,8 @@ export function useShareButtonController(options) {
|
|
|
25
25
|
}, [options, shareTabDefaultValue, sharesQuery, visibilityOverride]);
|
|
26
26
|
useEffect(() => {
|
|
27
27
|
setInviteEmail("");
|
|
28
|
+
setShareMessage("");
|
|
29
|
+
setMessageOpen(false);
|
|
28
30
|
}, [options.resourceId, options.resourceType]);
|
|
29
31
|
useEffect(() => {
|
|
30
32
|
if (!options.defaultOpen || appliedDefaultOpenRef.current)
|
|
@@ -102,6 +104,8 @@ export function useShareButtonController(options) {
|
|
|
102
104
|
// the popover cannot drop an in-flight mutation or an unsent invite.
|
|
103
105
|
const [role, setRole] = useState("viewer");
|
|
104
106
|
const [notifyPeople, setNotifyPeople] = useState(true);
|
|
107
|
+
const [shareMessage, setShareMessage] = useState("");
|
|
108
|
+
const [messageOpen, setMessageOpen] = useState(false);
|
|
105
109
|
const [suggestionsOpen, setSuggestionsOpen] = useState(false);
|
|
106
110
|
const [pendingAdds, setPendingAdds] = useState([]);
|
|
107
111
|
const [pendingRemoves, setPendingRemoves] = useState(new Set());
|
|
@@ -177,6 +181,7 @@ export function useShareButtonController(options) {
|
|
|
177
181
|
const trimmed = inviteEmail.trim();
|
|
178
182
|
if (!trimmed || !canManage)
|
|
179
183
|
return;
|
|
184
|
+
const message = notifyPeople ? shareMessage.trim() : "";
|
|
180
185
|
const optimistic = {
|
|
181
186
|
id: `pending-${trimmed}`,
|
|
182
187
|
principalType: "user",
|
|
@@ -189,6 +194,8 @@ export function useShareButtonController(options) {
|
|
|
189
194
|
setShareError(null);
|
|
190
195
|
setPendingAdds((previous) => [...previous, optimistic]);
|
|
191
196
|
setInviteEmail("");
|
|
197
|
+
setShareMessage("");
|
|
198
|
+
setMessageOpen(false);
|
|
192
199
|
setSuggestionsOpen(false);
|
|
193
200
|
addInFlight(key);
|
|
194
201
|
const previous = optimisticallyUpdateShareCache(queryClient, shareQueryKey, (cached) => cached ? { ...cached, shares: [...cached.shares, optimistic] } : cached);
|
|
@@ -200,6 +207,7 @@ export function useShareButtonController(options) {
|
|
|
200
207
|
role,
|
|
201
208
|
notify: notifyPeople,
|
|
202
209
|
resourceUrl: getNotificationUrl(options.shareUrl),
|
|
210
|
+
...(message ? { message } : {}),
|
|
203
211
|
}, {
|
|
204
212
|
onSuccess: () => {
|
|
205
213
|
sharesQuery.refetch().then(() => {
|
|
@@ -212,6 +220,8 @@ export function useShareButtonController(options) {
|
|
|
212
220
|
setPendingAdds((previous) => previous.filter((item) => item.id !== optimistic.id));
|
|
213
221
|
clearInFlight(key);
|
|
214
222
|
setInviteEmail(trimmed);
|
|
223
|
+
setShareMessage((current) => current || message);
|
|
224
|
+
setMessageOpen((current) => current || Boolean(message));
|
|
215
225
|
setShareError(extractShareErrorMessage(error));
|
|
216
226
|
},
|
|
217
227
|
});
|
|
@@ -226,6 +236,7 @@ export function useShareButtonController(options) {
|
|
|
226
236
|
options.resourceType,
|
|
227
237
|
options.shareUrl,
|
|
228
238
|
role,
|
|
239
|
+
shareMessage,
|
|
229
240
|
share,
|
|
230
241
|
queryClient,
|
|
231
242
|
shareQueryKey,
|
|
@@ -361,6 +372,10 @@ export function useShareButtonController(options) {
|
|
|
361
372
|
role,
|
|
362
373
|
setRole,
|
|
363
374
|
notifyPeople,
|
|
375
|
+
shareMessage,
|
|
376
|
+
setShareMessage,
|
|
377
|
+
messageOpen,
|
|
378
|
+
setMessageOpen,
|
|
364
379
|
setNotifyPeople,
|
|
365
380
|
shareError,
|
|
366
381
|
setShareError,
|
|
@@ -62,6 +62,9 @@ export interface ShareDialogController {
|
|
|
62
62
|
peopleWithAccess: string;
|
|
63
63
|
addPeopleByEmail: string;
|
|
64
64
|
notifyPeople: string;
|
|
65
|
+
addMessage: string;
|
|
66
|
+
hideMessage: string;
|
|
67
|
+
messagePlaceholder: string;
|
|
65
68
|
role: string;
|
|
66
69
|
remove: string;
|
|
67
70
|
noAccess: string;
|
|
@@ -87,6 +90,10 @@ export interface ShareDialogController {
|
|
|
87
90
|
notifyPeople: boolean;
|
|
88
91
|
setNotifyPeople: (notify: boolean) => void;
|
|
89
92
|
showNotifyPeople: boolean;
|
|
93
|
+
message: string;
|
|
94
|
+
setMessage: (message: string) => void;
|
|
95
|
+
messageOpen: boolean;
|
|
96
|
+
setMessageOpen: (open: boolean) => void;
|
|
90
97
|
disabled: boolean;
|
|
91
98
|
pending: boolean;
|
|
92
99
|
submit: () => void;
|
|
@@ -18,6 +18,8 @@ export function useShareDialogController({ open, onClose, resourceType, resource
|
|
|
18
18
|
const [email, setEmail] = useState("");
|
|
19
19
|
const [role, setRole] = useState("viewer");
|
|
20
20
|
const [notifyPeople, setNotifyPeople] = useState(true);
|
|
21
|
+
const [message, setMessage] = useState("");
|
|
22
|
+
const [messageOpen, setMessageOpen] = useState(false);
|
|
21
23
|
const [copiedField, setCopiedField] = useState(null);
|
|
22
24
|
const [visibilityOverride, setVisibilityOverride] = useState(null);
|
|
23
25
|
const [mutationError, setMutationError] = useState(null);
|
|
@@ -100,6 +102,7 @@ export function useShareDialogController({ open, onClose, resourceType, resource
|
|
|
100
102
|
const principalId = email.trim();
|
|
101
103
|
if (!canManage || !principalId)
|
|
102
104
|
return;
|
|
105
|
+
const notificationMessage = notifyPeople ? message.trim() : "";
|
|
103
106
|
const optimistic = {
|
|
104
107
|
id: `pending-${principalId}`,
|
|
105
108
|
principalType: "user",
|
|
@@ -116,9 +119,12 @@ export function useShareDialogController({ open, onClose, resourceType, resource
|
|
|
116
119
|
role,
|
|
117
120
|
notify: notifyPeople,
|
|
118
121
|
resourceUrl: getNotificationUrl(shareUrl),
|
|
122
|
+
...(notificationMessage ? { message: notificationMessage } : {}),
|
|
119
123
|
}, {
|
|
120
124
|
onSuccess: () => {
|
|
121
125
|
setEmail("");
|
|
126
|
+
setMessage("");
|
|
127
|
+
setMessageOpen(false);
|
|
122
128
|
void refetch();
|
|
123
129
|
},
|
|
124
130
|
onError: (error) => {
|
|
@@ -129,6 +135,7 @@ export function useShareDialogController({ open, onClose, resourceType, resource
|
|
|
129
135
|
}, [
|
|
130
136
|
canManage,
|
|
131
137
|
email,
|
|
138
|
+
message,
|
|
132
139
|
notifyPeople,
|
|
133
140
|
refetch,
|
|
134
141
|
resourceId,
|
|
@@ -220,6 +227,9 @@ export function useShareDialogController({ open, onClose, resourceType, resource
|
|
|
220
227
|
peopleWithAccess: t("share.peopleWithAccess"),
|
|
221
228
|
addPeopleByEmail: t("share.addPeopleByEmail"),
|
|
222
229
|
notifyPeople: t("share.notifyPeople"),
|
|
230
|
+
addMessage: t("share.addMessage"),
|
|
231
|
+
hideMessage: t("share.hideMessage"),
|
|
232
|
+
messagePlaceholder: t("share.messagePlaceholder"),
|
|
223
233
|
role: t("share.role"),
|
|
224
234
|
remove: t("share.remove"),
|
|
225
235
|
noAccess: t("share.noAccess"),
|
|
@@ -245,6 +255,10 @@ export function useShareDialogController({ open, onClose, resourceType, resource
|
|
|
245
255
|
notifyPeople,
|
|
246
256
|
setNotifyPeople,
|
|
247
257
|
showNotifyPeople: email.trim().length > 0,
|
|
258
|
+
message,
|
|
259
|
+
setMessage,
|
|
260
|
+
messageOpen,
|
|
261
|
+
setMessageOpen,
|
|
248
262
|
disabled: !canManage || email.trim().length === 0,
|
|
249
263
|
pending: shareMutation.isPending,
|
|
250
264
|
submit: submitInvite,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LLM_MISSING_CREDENTIALS_ERROR_CODE, LLM_MISSING_CREDENTIALS_MESSAGE, } from "../agent/engine/credential-errors.js";
|
|
2
|
+
import { emitChatFirstOpenApp } from "./chat-first.js";
|
|
2
3
|
import { formatChatErrorText, normalizeChatError } from "./error-format.js";
|
|
3
4
|
import { humanizeToolLabelText, humanizeToolName, runningToolLabel, } from "./tool-display.js";
|
|
4
5
|
const INTERRUPTED_TOOL_RESULT = "Interrupted before this tool returned a result.";
|
|
@@ -869,6 +870,41 @@ function shouldDispatchStreamProgress(state) {
|
|
|
869
870
|
state.streamProgressDispatched = true;
|
|
870
871
|
return true;
|
|
871
872
|
}
|
|
873
|
+
function emitFirstPartyOpenAppHandoff(ev) {
|
|
874
|
+
if (ev.type !== "tool_done" || (ev.tool ?? "unknown") !== "open_app") {
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
if (ev.isError === true)
|
|
878
|
+
return;
|
|
879
|
+
if (!ev.result?.trim()) {
|
|
880
|
+
console.warn("[chat-first] open_app completed without a readable result; no app pane opened");
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
let result;
|
|
884
|
+
try {
|
|
885
|
+
const parsed = JSON.parse(ev.result);
|
|
886
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
887
|
+
console.warn("[chat-first] open_app completed without a readable result; no app pane opened");
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
result = parsed;
|
|
891
|
+
}
|
|
892
|
+
catch {
|
|
893
|
+
// coercion-ok: unreadable tool output must not be treated as a successful handoff.
|
|
894
|
+
console.warn("[chat-first] open_app completed without a readable result; no app pane opened");
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
const readString = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
898
|
+
const delivery = emitChatFirstOpenApp({
|
|
899
|
+
app: readString(result.app ?? result.appId ?? result.application),
|
|
900
|
+
path: readString(result.path ?? result.targetPath),
|
|
901
|
+
url: readString(result.url ?? result.href),
|
|
902
|
+
view: readString(result.view),
|
|
903
|
+
});
|
|
904
|
+
if (!delivery.delivered) {
|
|
905
|
+
console.warn(`[chat-first] open_app was completed but not delivered (${delivery.reason ?? "unknown"})`);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
872
908
|
/**
|
|
873
909
|
* Process a single SSE event and update the content accumulator.
|
|
874
910
|
* Returns: "continue" to keep going, "done" to stop, or a yield-ready result.
|
|
@@ -1124,6 +1160,7 @@ export function processEvent(ev, content, toolCallCounter, tabId, state) {
|
|
|
1124
1160
|
if (findCompletedToolCallIndex(content, ev.id) >= 0) {
|
|
1125
1161
|
return { action: "continue" };
|
|
1126
1162
|
}
|
|
1163
|
+
emitFirstPartyOpenAppHandoff(ev);
|
|
1127
1164
|
if (typeof window !== "undefined") {
|
|
1128
1165
|
window.dispatchEvent(new CustomEvent("agent-native:tool-done", {
|
|
1129
1166
|
detail: { tool: doneTool, result: ev.result },
|
|
@@ -15,6 +15,8 @@ interface UseSessionResult {
|
|
|
15
15
|
/** Restart the resolve loop, e.g. from a "Try again" control. */
|
|
16
16
|
retry: () => void;
|
|
17
17
|
}
|
|
18
|
+
/** Tell mounted session consumers to re-check auth after a logout. */
|
|
19
|
+
export declare function notifySessionInvalidated(): void;
|
|
18
20
|
/**
|
|
19
21
|
* Client-side hook to get the current auth session.
|
|
20
22
|
*
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
import { setSentryUser, trackSessionStatus } from "./analytics.js";
|
|
3
|
-
import { fetchAuthSessionStatus } from "./client-status-requests.js";
|
|
3
|
+
import { fetchAuthSessionStatus, invalidateClientStatusRequest, } from "./client-status-requests.js";
|
|
4
4
|
const SESSION_CACHE_TTL_MS = 30_000;
|
|
5
5
|
const SESSION_RETRY_DELAY_MS = 1_000;
|
|
6
6
|
const SESSION_MAX_ATTEMPTS = 4;
|
|
7
|
+
const SESSION_INVALIDATION_STORAGE_KEY = "agent-native:session-invalidated";
|
|
8
|
+
const SESSION_STATUS_PATH = "/_agent-native/auth/session";
|
|
7
9
|
let cachedSession;
|
|
8
10
|
let cachedSessionAt = 0;
|
|
9
11
|
let sessionRequest;
|
|
10
12
|
let trackedSessionIdentity;
|
|
13
|
+
let sessionGeneration = 0;
|
|
14
|
+
let sessionInvalidationListenersInstalled = false;
|
|
15
|
+
const sessionInvalidationSubscribers = new Set();
|
|
11
16
|
function hasFreshSessionCache() {
|
|
12
17
|
return (cachedSession !== undefined &&
|
|
13
18
|
Date.now() - cachedSessionAt < SESSION_CACHE_TTL_MS);
|
|
@@ -29,16 +34,61 @@ function publishSessionIdentity(session) {
|
|
|
29
34
|
}
|
|
30
35
|
trackSessionStatus(Boolean(session));
|
|
31
36
|
}
|
|
37
|
+
function invalidateSessionCache() {
|
|
38
|
+
sessionGeneration += 1;
|
|
39
|
+
cachedSession = undefined;
|
|
40
|
+
cachedSessionAt = 0;
|
|
41
|
+
sessionRequest = undefined;
|
|
42
|
+
invalidateClientStatusRequest(SESSION_STATUS_PATH);
|
|
43
|
+
for (const subscriber of sessionInvalidationSubscribers)
|
|
44
|
+
subscriber();
|
|
45
|
+
}
|
|
46
|
+
function installSessionInvalidationListeners() {
|
|
47
|
+
if (sessionInvalidationListenersInstalled ||
|
|
48
|
+
typeof window === "undefined" ||
|
|
49
|
+
typeof document === "undefined") {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
sessionInvalidationListenersInstalled = true;
|
|
53
|
+
window.addEventListener("focus", invalidateSessionCache);
|
|
54
|
+
window.addEventListener("storage", (event) => {
|
|
55
|
+
if (event.key === SESSION_INVALIDATION_STORAGE_KEY) {
|
|
56
|
+
invalidateSessionCache();
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
document.addEventListener("visibilitychange", () => {
|
|
60
|
+
if (document.visibilityState === "visible") {
|
|
61
|
+
invalidateSessionCache();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/** Tell mounted session consumers to re-check auth after a logout. */
|
|
66
|
+
export function notifySessionInvalidated() {
|
|
67
|
+
if (typeof window === "undefined")
|
|
68
|
+
return;
|
|
69
|
+
installSessionInvalidationListeners();
|
|
70
|
+
invalidateSessionCache();
|
|
71
|
+
try {
|
|
72
|
+
window.localStorage.setItem(SESSION_INVALIDATION_STORAGE_KEY, `${Date.now()}:${Math.random()}`);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.warn("Unable to broadcast session invalidation", error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
32
78
|
function fetchSharedSession() {
|
|
33
79
|
if (hasFreshSessionCache())
|
|
34
80
|
return Promise.resolve(cachedSession ?? null);
|
|
35
81
|
if (sessionRequest)
|
|
36
82
|
return sessionRequest;
|
|
37
|
-
|
|
83
|
+
const requestGeneration = sessionGeneration;
|
|
84
|
+
let request;
|
|
85
|
+
const requestResult = (async () => {
|
|
38
86
|
try {
|
|
39
87
|
const result = await fetchAuthSessionStatus();
|
|
40
88
|
if (result.state === "unavailable")
|
|
41
89
|
return undefined;
|
|
90
|
+
if (requestGeneration !== sessionGeneration)
|
|
91
|
+
return undefined;
|
|
42
92
|
const data = result.value;
|
|
43
93
|
const session = data.error ? null : data;
|
|
44
94
|
cachedSession = session;
|
|
@@ -49,9 +99,12 @@ function fetchSharedSession() {
|
|
|
49
99
|
catch {
|
|
50
100
|
return undefined;
|
|
51
101
|
}
|
|
52
|
-
})()
|
|
53
|
-
|
|
102
|
+
})();
|
|
103
|
+
request = requestResult.finally(() => {
|
|
104
|
+
if (sessionRequest === request)
|
|
105
|
+
sessionRequest = undefined;
|
|
54
106
|
});
|
|
107
|
+
sessionRequest = request;
|
|
55
108
|
return sessionRequest;
|
|
56
109
|
}
|
|
57
110
|
/**
|
|
@@ -64,6 +117,7 @@ function fetchSharedSession() {
|
|
|
64
117
|
* Templates should use this instead of building their own auth context.
|
|
65
118
|
*/
|
|
66
119
|
export function useSession() {
|
|
120
|
+
installSessionInvalidationListeners();
|
|
67
121
|
const cached = hasFreshSessionCache() ? (cachedSession ?? null) : null;
|
|
68
122
|
const [session, setSession] = useState(cached);
|
|
69
123
|
const [status, setStatus] = useState(() => {
|
|
@@ -73,11 +127,21 @@ export function useSession() {
|
|
|
73
127
|
});
|
|
74
128
|
const [error, setError] = useState(null);
|
|
75
129
|
const [retryToken, setRetryToken] = useState(0);
|
|
130
|
+
const [invalidationToken, setInvalidationToken] = useState(0);
|
|
76
131
|
const retry = useCallback(() => {
|
|
77
132
|
setError(null);
|
|
78
133
|
setStatus("loading");
|
|
79
134
|
setRetryToken((token) => token + 1);
|
|
80
135
|
}, []);
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
const subscriber = () => {
|
|
138
|
+
setInvalidationToken((token) => token + 1);
|
|
139
|
+
};
|
|
140
|
+
sessionInvalidationSubscribers.add(subscriber);
|
|
141
|
+
return () => {
|
|
142
|
+
sessionInvalidationSubscribers.delete(subscriber);
|
|
143
|
+
};
|
|
144
|
+
}, []);
|
|
81
145
|
useEffect(() => {
|
|
82
146
|
let cancelled = false;
|
|
83
147
|
let retryTimer;
|
|
@@ -108,7 +172,7 @@ export function useSession() {
|
|
|
108
172
|
if (retryTimer)
|
|
109
173
|
clearTimeout(retryTimer);
|
|
110
174
|
};
|
|
111
|
-
}, [retryToken]);
|
|
175
|
+
}, [invalidationToken, retryToken]);
|
|
112
176
|
// Callers that only read `isLoading`/`session` (most of the codebase, not
|
|
113
177
|
// yet migrated to `status`) must not see "unavailable" as "signed out" —
|
|
114
178
|
// that bounces an authenticated user through sign-in-only UI over a
|
package/dist/deploy/build.js
CHANGED
|
@@ -1452,7 +1452,7 @@ export function generateCloudflarePagesStaticShellFromManifest(manifest, basePat
|
|
|
1452
1452
|
? DEFAULT_ROOT_LOADER_REACT_ROUTER_TURBO_STREAM
|
|
1453
1453
|
: EMPTY_REACT_ROUTER_TURBO_STREAM;
|
|
1454
1454
|
// guard:allow-raw-color - static shell loads before app theme tokens exist
|
|
1455
|
-
return `<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/><link rel="
|
|
1455
|
+
return `<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/><link rel="icon" type="image/svg+xml" href="/favicon.svg"/>${modulePreloads}${stylesheets}</head><body><div style="display:flex;align-items:center;justify-content:center;height:100vh;width:100%"><svg role="status" aria-label="Loading" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="animation:an-spin 1s linear infinite;opacity:0.7"><path d="M21 12a9 9 0 1 1-6.219-8.56"></path></svg><style>@keyframes an-spin { to { transform: rotate(360deg) } } @media (prefers-color-scheme: dark) { html { background: #09090b; color: #fafafa } }</style></div><script>window.__reactRouterContext = ${JSON.stringify(context)};window.__reactRouterContext.stream = new ReadableStream({start(controller){window.__reactRouterContext.streamController = controller;}}).pipeThrough(new TextEncoderStream());</script><script type="module" async="">${routeModuleScript}</script><!--$--><script>window.__reactRouterContext.streamController.enqueue(${JSON.stringify(encodedInitialState)});</script><!--$--><script>window.__reactRouterContext.streamController.close();</script><!--/$--><!--/$--></body></html>`;
|
|
1456
1456
|
}
|
|
1457
1457
|
function writeCloudflarePagesStaticShell({ serverDir, distDir, tmpDir, }) {
|
|
1458
1458
|
const serverEntry = path.join(serverDir, "index.js");
|
|
@@ -27,7 +27,7 @@ export interface PreUploadAttachmentsResult {
|
|
|
27
27
|
/** Set when at least one image was uploaded. List of hosted URLs the agent
|
|
28
28
|
* can embed in HTML, slide content, documents, etc. */
|
|
29
29
|
uploaded: PreUploadedImageAttachment[];
|
|
30
|
-
/** Uploaded non-image files (
|
|
30
|
+
/** Uploaded non-image files (documents, spreadsheets, generic binary). Parallel to `uploaded`
|
|
31
31
|
* but for the file/document attachment type. */
|
|
32
32
|
uploadedFiles: PreUploadedFileAttachment[];
|
|
33
33
|
/** True if at least one image or file could not be uploaded because no
|
|
@@ -39,8 +39,7 @@ export interface PreUploadAttachmentsResult {
|
|
|
39
39
|
/** The first provider error, bounded for safe inclusion in the chat hint. */
|
|
40
40
|
uploadError?: string;
|
|
41
41
|
/** A pre-formatted block to inject into the user message text so the agent
|
|
42
|
-
* has
|
|
43
|
-
* is configured. */
|
|
42
|
+
* has hosted URLs and bounded derived source context inline. */
|
|
44
43
|
injectedText: string | null;
|
|
45
44
|
}
|
|
46
45
|
/**
|
|
@@ -63,7 +62,7 @@ export declare function preUploadImageAttachments(opts: {
|
|
|
63
62
|
ownerEmail: string | null | undefined;
|
|
64
63
|
}): Promise<PreUploadAttachmentsResult>;
|
|
65
64
|
/**
|
|
66
|
-
* Pre-upload ALL chat attachments (images AND files
|
|
65
|
+
* Pre-upload ALL chat attachments (images AND files) through the active
|
|
67
66
|
* file-upload provider. When a provider is configured, each attachment gets a
|
|
68
67
|
* `url` property injected so downstream code can store/send URLs instead of
|
|
69
68
|
* base64. The base64 data is kept in-memory for the current turn so vision and
|