@agent-native/core 0.154.5 → 0.157.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/.agents/skills/account-health/SKILL.md +76 -0
- package/corpus/templates/analytics/.agents/skills/bigquery/SKILL.md +3 -0
- package/corpus/templates/analytics/.agents/skills/cross-source-analysis/SKILL.md +2 -1
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +6 -5
- package/corpus/templates/analytics/AGENTS.md +3 -0
- package/corpus/templates/analytics/actions/account-deep-dive.ts +3 -2
- package/corpus/templates/analytics/actions/bigquery.ts +1 -1
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +4 -1
- package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyCombobox.tsx +12 -4
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyValueCombobox.tsx +5 -2
- package/corpus/templates/analytics/app/pages/adhoc/explorer/property-label.ts +32 -0
- package/corpus/templates/analytics/changelog/2026-08-13-account-health-readouts-verify-customer-scope-and-contract-metrics.md +7 -0
- package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +43 -17
- package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +17 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +4 -0
- package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +3 -1
- package/corpus/templates/assets/app/i18n-data.ts +21 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +17 -13
- package/corpus/templates/calendar/AGENTS.md +2 -0
- package/corpus/templates/calendar/DEVELOPING.md +4 -4
- package/corpus/templates/calendar/actions/get-settings.ts +4 -11
- package/corpus/templates/calendar/actions/update-availability.ts +15 -4
- package/corpus/templates/calendar/actions/update-settings.ts +15 -4
- package/corpus/templates/calendar/app/components/booking/DatePicker.tsx +9 -3
- package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +21 -19
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +9 -6
- package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +25 -3
- package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +10 -3
- package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +9 -9
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -3
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +10 -4
- package/corpus/templates/calendar/app/i18n-data.ts +30 -0
- package/corpus/templates/calendar/app/lib/availability-schedule.ts +53 -0
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +5 -0
- package/corpus/templates/calendar/app/pages/AvailabilitySettings.tsx +90 -28
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +135 -61
- package/corpus/templates/calendar/app/pages/BookingPage.tsx +2 -0
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +30 -15
- package/corpus/templates/calendar/app/pages/Settings.tsx +60 -1
- package/corpus/templates/calendar/changelog/2026-08-13-find-a-time-now-closes-from-a-clear-header-action.md +6 -0
- package/corpus/templates/calendar/server/handlers/bookings.ts +2 -1
- package/corpus/templates/calendar/server/handlers/settings.ts +12 -16
- package/corpus/templates/calendar/shared/api.ts +1 -0
- package/corpus/templates/calendar/shared/availability-schedule.ts +64 -0
- package/corpus/templates/calendar/shared/calendar-week.ts +21 -0
- package/corpus/templates/calendar/shared/settings.ts +44 -0
- package/corpus/templates/clips/AGENTS.md +2 -0
- package/corpus/templates/clips/actions/add-comment.ts +5 -2
- package/corpus/templates/clips/actions/finalize-recording.ts +31 -1
- package/corpus/templates/clips/actions/get-recording-player-data.ts +9 -0
- package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +21 -1
- package/corpus/templates/clips/actions/reply-to-comment.ts +5 -2
- package/corpus/templates/clips/actions/update-comment.ts +9 -2
- package/corpus/templates/clips/app/components/library/search-bar.tsx +26 -13
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +135 -3
- package/corpus/templates/clips/app/components/player/comments-panel.tsx +5 -99
- package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +7 -3
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +102 -59
- package/corpus/templates/clips/app/components/player/video-player.tsx +84 -24
- package/corpus/templates/clips/app/components/workspace/notifications-list.tsx +11 -3
- package/corpus/templates/clips/app/i18n/en-US.ts +12 -0
- package/corpus/templates/clips/app/lib/public-meeting.ts +11 -3
- package/corpus/templates/clips/app/lib/search-focus.ts +15 -0
- package/corpus/templates/clips/app/root.tsx +2 -1
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +94 -26
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +24 -7
- package/corpus/templates/clips/app/routes/embed.$shareId.tsx +8 -4
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +29 -8
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +24 -6
- package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +72 -26
- package/corpus/templates/clips/changelog/2026-08-13-inline-markdown-comments.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-13-private-meeting-agent-links.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +116 -19
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +13 -29
- package/corpus/templates/clips/desktop/src/lib/recording-request.ts +39 -0
- package/corpus/templates/clips/desktop/src/lib/settings-navigation.ts +11 -0
- package/corpus/templates/clips/desktop/src/lib/whisper-model-picker.ts +7 -0
- package/corpus/templates/clips/desktop/src/styles.css +117 -0
- package/corpus/templates/clips/server/db/index.ts +10 -0
- package/corpus/templates/clips/server/db/schema.ts +1 -0
- package/corpus/templates/clips/server/lib/recordings.ts +3 -1
- package/corpus/templates/clips/server/lib/seekable-media-state.ts +90 -0
- package/corpus/templates/clips/server/plugins/db.ts +5 -0
- package/corpus/templates/clips/server/routes/api/public-meeting.get.ts +37 -4
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +16 -5
- package/corpus/templates/clips/shared/clips-ai-prefs.ts +3 -0
- package/corpus/templates/clips/shared/meeting-agent-access.ts +5 -0
- package/corpus/templates/content/actions/add-comment.ts +2 -1
- package/corpus/templates/content/actions/update-comment.ts +1 -1
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +24 -15
- package/corpus/templates/design/actions/generate-design.ts +10 -7
- package/corpus/templates/design/actions/present-design-variants.ts +35 -6
- package/corpus/templates/design/actions/run-design-audit.ts +33 -0
- package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-catches-a-screen-that-renders-behind-an.md +6 -0
- package/corpus/templates/design/shared/design-review.ts +1 -0
- package/corpus/templates/design/shared/html-integrity.ts +66 -0
- package/corpus/templates/dispatch/.env.example +5 -0
- package/corpus/templates/dispatch/AGENTS.md +24 -19
- package/corpus/templates/dispatch/app/i18n-data.ts +20 -0
- package/corpus/templates/dispatch/server/lib/identity-sso.ts +21 -90
- package/corpus/templates/dispatch/server/plugins/identity-sso.ts +3 -12
- package/corpus/templates/dispatch/shared/feature-flags.ts +4 -0
- package/corpus/templates/factory/AGENTS.md +5 -1
- package/corpus/templates/factory/README.md +23 -0
- package/corpus/templates/factory/actions/view-screen.ts +17 -0
- package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +239 -0
- package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +2 -0
- package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +130 -7
- package/corpus/templates/factory/app/i18n/en-US.ts +20 -0
- package/corpus/templates/factory/app/routes/factory.tsx +41 -0
- package/corpus/templates/factory/package.json +1 -0
- package/corpus/templates/factory/server/factory-graph/contracts.ts +2 -0
- package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -0
- package/corpus/templates/factory/server/plugins/dispatch-db.ts +1 -0
- package/corpus/templates/factory/server/plugins/setup-dispatch.ts +3 -0
- package/corpus/templates/mail/app/lib/native-mcp-exclusions.ts +3 -0
- package/corpus/templates/mail/vite.config.ts +7 -0
- package/corpus/templates/plan/actions/reply-to-plan-comment.ts +7 -2
- package/corpus/templates/plan/actions/resolve-plan-comment.ts +2 -2
- package/corpus/templates/plan/actions/update-visual-plan.ts +1 -1
- package/corpus/templates/plan/app/pages/PlansPage.tsx +29 -20
- package/corpus/templates/slides/actions/add-slide-comment.ts +1 -1
- package/corpus/templates/slides/actions/update-slide-comment.ts +1 -1
- package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +9 -6
- package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +98 -29
- package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +45 -91
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +94 -2
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +17 -29
- package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +42 -2
- package/corpus/templates/slides/app/global.css +7 -40
- package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +130 -45
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/production-agent.d.ts +30 -0
- package/dist/agent/production-agent.js +105 -9
- package/dist/agent/thread-data-builder.js +17 -1
- package/dist/agent/types.d.ts +9 -0
- package/dist/cli/skills-content/help.js +6 -4
- package/dist/cli/skills.js +17 -2
- package/dist/cli/workspacify.d.ts +5 -0
- package/dist/cli/workspacify.js +65 -8
- package/dist/client/AgentPanel.d.ts +5 -0
- package/dist/client/AgentPanel.js +32 -13
- package/dist/client/AssistantChat.d.ts +36 -1
- package/dist/client/AssistantChat.js +175 -110
- package/dist/client/MultiTabAssistantChat.js +44 -27
- package/dist/client/active-run-state.d.ts +14 -1
- package/dist/client/active-run-state.js +0 -0
- package/dist/client/agent-chat-adapter.js +107 -5
- package/dist/client/analytics.js +9 -2
- package/dist/client/chat/markdown-renderer.js +4 -2
- package/dist/client/chat/message-components.d.ts +2 -2
- package/dist/client/chat/message-components.js +69 -35
- package/dist/client/chat/run-recovery.d.ts +4 -1
- package/dist/client/chat/run-recovery.js +62 -30
- package/dist/client/chat/tool-call-display.d.ts +12 -3
- package/dist/client/chat/tool-call-display.js +79 -40
- package/dist/client/chat/widgets/DataChartRenderer.js +3 -1
- package/dist/client/chat/widgets/DataChartWidget.js +8 -2
- package/dist/client/chat/widgets/DataInsightsWidget.js +3 -1
- package/dist/client/chat/widgets/DataTableWidget.js +23 -9
- package/dist/client/chat/widgets/builtin-tool-renderers.js +3 -1
- package/dist/client/chat-first/apps-rail.js +9 -4
- package/dist/client/chat-first/primary-nav.d.ts +2 -1
- package/dist/client/chat-first/primary-nav.js +3 -3
- package/dist/client/chat-first/types.d.ts +5 -1
- package/dist/client/components/ui/message-scroller.d.ts +1 -1
- package/dist/client/components/ui/message-scroller.js +7 -3
- package/dist/client/composer/runtime-adapters.js +3 -2
- package/dist/client/context-xray/ContextMeter.js +5 -1
- package/dist/client/context-xray/ContextXRayPanel.js +5 -5
- package/dist/client/error-format.d.ts +4 -0
- package/dist/client/error-format.js +93 -5
- package/dist/client/i18n.js +83 -17
- package/dist/client/markdown/InlineMarkdown.d.ts +23 -0
- package/dist/client/markdown/InlineMarkdown.js +65 -0
- package/dist/client/markdown/index.d.ts +1 -0
- package/dist/client/markdown/index.js +1 -0
- package/dist/client/observability/ThumbsFeedback.js +10 -8
- package/dist/client/review/ReviewThreadPanel.js +2 -1
- package/dist/client/run-stream-ownership.d.ts +39 -0
- package/dist/client/run-stream-ownership.js +65 -0
- package/dist/client/sharing/ShareButton.js +160 -54
- package/dist/coding-tools/run-code.js +2 -1
- package/dist/coding-tools/sandbox/background.d.ts +1 -0
- package/dist/coding-tools/sandbox/background.js +8 -1
- package/dist/coding-tools/sandbox/executions-store.d.ts +2 -0
- package/dist/coding-tools/sandbox/executions-store.js +41 -3
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/feature-flags/index.d.ts +1 -1
- package/dist/feature-flags/index.js +1 -1
- package/dist/feature-flags/store.d.ts +10 -0
- package/dist/feature-flags/store.js +112 -1
- package/dist/guards/no-env-credentials.js +4 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/integrations/adapters/slack.js +5 -1
- package/dist/localization/core-messages/ar-SA.d.ts +3 -0
- package/dist/localization/core-messages/ar-SA.js +541 -0
- package/dist/localization/core-messages/de-DE.d.ts +3 -0
- package/dist/localization/core-messages/de-DE.js +513 -0
- package/dist/localization/core-messages/en-US.d.ts +513 -0
- package/dist/localization/core-messages/en-US.js +513 -0
- package/dist/localization/core-messages/es-ES.d.ts +3 -0
- package/dist/localization/core-messages/es-ES.js +520 -0
- package/dist/localization/core-messages/fr-FR.d.ts +3 -0
- package/dist/localization/core-messages/fr-FR.js +520 -0
- package/dist/localization/core-messages/hi-IN.d.ts +3 -0
- package/dist/localization/core-messages/hi-IN.js +513 -0
- package/dist/localization/core-messages/ja-JP.d.ts +3 -0
- package/dist/localization/core-messages/ja-JP.js +506 -0
- package/dist/localization/core-messages/ko-KR.d.ts +3 -0
- package/dist/localization/core-messages/ko-KR.js +506 -0
- package/dist/localization/core-messages/pt-BR.d.ts +3 -0
- package/dist/localization/core-messages/pt-BR.js +520 -0
- package/dist/localization/core-messages/zh-CN.d.ts +3 -0
- package/dist/localization/core-messages/zh-CN.js +506 -0
- package/dist/localization/core-messages/zh-TW.d.ts +3 -0
- package/dist/localization/core-messages/zh-TW.js +506 -0
- package/dist/localization/core-messages.d.ts +524 -0
- package/dist/localization/core-messages.js +124 -0
- package/dist/localization/default-messages.d.ts +3 -0
- package/dist/localization/default-messages.js +3 -0
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +2 -2
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +15 -15
- package/dist/resources/agents.js +40 -5
- package/dist/resources/metadata.d.ts +10 -0
- package/dist/resources/metadata.js +7 -3
- package/dist/review/actions/create-review-comment.js +1 -1
- package/dist/review/actions/reply-review-comment.js +1 -1
- package/dist/review/actions/resolve-review-thread.js +1 -1
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/action-routes.js +32 -6
- package/dist/server/agent-chat/plugin-options.d.ts +7 -2
- package/dist/server/agent-chat-plugin.d.ts +10 -0
- package/dist/server/agent-chat-plugin.js +144 -41
- package/dist/server/agent-discovery.js +14 -3
- package/dist/server/agent-run-context.d.ts +7 -1
- package/dist/server/agent-run-context.js +9 -2
- package/dist/server/agent-teams-run-queue.d.ts +3 -0
- package/dist/server/agent-teams.js +38 -9
- package/dist/server/core-routes-plugin.js +1 -1
- package/dist/server/identity-sso-migrations.d.ts +7 -0
- package/dist/server/identity-sso-migrations.js +36 -0
- package/dist/server/identity-sso-store.d.ts +3 -2
- package/dist/server/identity-sso-store.js +8 -3
- package/dist/server/index.d.ts +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/release-migrations.js +4 -0
- package/dist/server/request-context.d.ts +7 -0
- package/dist/server/request-context.js +15 -0
- package/dist/tracking/providers.js +13 -3
- package/dist/workspace-files/tool.js +1 -1
- package/docs/content/agent-surfaces.mdx +30 -0
- package/package.json +3 -2
|
@@ -8,6 +8,7 @@ import { Spinner } from "@agent-native/toolkit/ui/spinner";
|
|
|
8
8
|
import { IconCircleCheck, IconDots, IconMessageCircle, IconSend, IconTrash, IconX, } from "@tabler/icons-react";
|
|
9
9
|
import { useMemo, useState } from "react";
|
|
10
10
|
import { useFormatters } from "../i18n.js";
|
|
11
|
+
import { InlineMarkdown } from "../markdown/index.js";
|
|
11
12
|
import { cn } from "../utils.js";
|
|
12
13
|
import { ReviewCommentComposer } from "./ReviewCommentComposer.js";
|
|
13
14
|
import { ReviewStatusBadge } from "./ReviewStatusBadge.js";
|
|
@@ -126,7 +127,7 @@ function CommentBubble({ comment, compact = false, resolvedLabel, reviewerLabel,
|
|
|
126
127
|
const bodyIsResolutionNote = resolutionNote !== null &&
|
|
127
128
|
resolutionNote === comment.body.trim() &&
|
|
128
129
|
isResolutionNoteMetadata(comment.metadata);
|
|
129
|
-
return (_jsxs("div", { className: "flex min-w-0 items-start gap-2.5", children: [_jsx(Avatar, { className: compact ? "size-5" : "size-7", children: _jsx(AvatarFallback, { className: "text-[10px] font-semibold text-muted-foreground", children: authorInitials(author) }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: "truncate text-sm font-medium", children: author }), _jsx("time", { className: "shrink-0 text-[11px] text-muted-foreground", dateTime: comment.createdAt, children: formatCommentDate(comment.createdAt, formatDate) }), comment.status === "resolved" ? (_jsx("span", { className: "hidden shrink-0 rounded-full bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground @xs/review:inline-flex", children: resolvedLabel })) : null] }), !bodyIsResolutionNote ? (_jsx(
|
|
130
|
+
return (_jsxs("div", { className: "flex min-w-0 items-start gap-2.5", children: [_jsx(Avatar, { className: compact ? "size-5" : "size-7", children: _jsx(AvatarFallback, { className: "text-[10px] font-semibold text-muted-foreground", children: authorInitials(author) }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: "truncate text-sm font-medium", children: author }), _jsx("time", { className: "shrink-0 text-[11px] text-muted-foreground", dateTime: comment.createdAt, children: formatCommentDate(comment.createdAt, formatDate) }), comment.status === "resolved" ? (_jsx("span", { className: "hidden shrink-0 rounded-full bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground @xs/review:inline-flex", children: resolvedLabel })) : null] }), !bodyIsResolutionNote ? (_jsx(InlineMarkdown, { content: comment.body, className: cn("mt-1 text-foreground", compact ? "text-xs leading-5" : "text-sm leading-5") })) : null, resolutionNote ? (_jsxs("div", { className: "mt-1.5 flex min-w-0 items-start gap-1.5 rounded-md bg-muted/60 px-2 py-1.5 text-muted-foreground", "aria-label": resolvedLabel, children: [_jsx(IconCircleCheck, { className: "mt-0.5 size-3.5 shrink-0" }), _jsx(InlineMarkdown, { content: resolutionNote, className: "min-w-0 text-xs leading-4" })] })) : null] })] }));
|
|
130
131
|
}
|
|
131
132
|
function capabilityAllowsThread(capability, thread) {
|
|
132
133
|
return typeof capability === "function" ? capability(thread) : capability;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exactly one SSE reader may fold a given run into UI state.
|
|
3
|
+
*
|
|
4
|
+
* Two readers attach to the same run routinely: the chat adapter's own stream,
|
|
5
|
+
* and AssistantChat's reconnect reader for runs with no live adapter stream
|
|
6
|
+
* (page reload, tab restore). When both are attached they build two independent
|
|
7
|
+
* accumulators from the same events, and the UI renders both — duplicate tool
|
|
8
|
+
* cards (one spinning, one static) and the same assistant text streaming twice.
|
|
9
|
+
*
|
|
10
|
+
* The previous guard read a React ref (`isRuntimeRunningRef`) at attach time and
|
|
11
|
+
* re-checked it from a 1s poll. That is a race, not a lock: the ref lags a
|
|
12
|
+
* render behind, the poll is skipped while the tab is hidden, and the refs were
|
|
13
|
+
* per-component-instance while MultiTabAssistantChat mounts several instances
|
|
14
|
+
* against one run. Ownership therefore lives here — module scope, one registry
|
|
15
|
+
* per browser tab, claimed and checked synchronously.
|
|
16
|
+
*
|
|
17
|
+
* Claims are advisory in one direction only: a reader that does not hold the
|
|
18
|
+
* claim must not mutate UI state. It may still drain its socket to completion.
|
|
19
|
+
*/
|
|
20
|
+
/** Mint a token identifying one reader. Not shared between readers. */
|
|
21
|
+
export declare function createRunStreamToken(label?: string): symbol;
|
|
22
|
+
/**
|
|
23
|
+
* Take ownership of a run's UI fold. Returns false when another live reader
|
|
24
|
+
* already holds it — the caller must not attach, or must attach read-only.
|
|
25
|
+
* Re-claiming with the same token is a no-op success so retry loops are safe.
|
|
26
|
+
*/
|
|
27
|
+
export declare function claimRunStream(threadId: string, runId: string, token: symbol): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Take ownership even if another reader holds it, and return whether the claim
|
|
30
|
+
* changed hands. The adapter's own stream outranks the reconnect fallback: when
|
|
31
|
+
* the user sends a message the adapter legitimately becomes the owner, and the
|
|
32
|
+
* displaced reader learns it lost via `ownsRunStream`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function preemptRunStream(threadId: string, runId: string, token: symbol): boolean;
|
|
35
|
+
export declare function ownsRunStream(threadId: string, runId: string, token: symbol): boolean;
|
|
36
|
+
/** Release only if still held by this token, so a late unmount cannot free a successor's claim. */
|
|
37
|
+
export declare function releaseRunStream(threadId: string, runId: string, token: symbol): void;
|
|
38
|
+
/** Test seam. */
|
|
39
|
+
export declare function __resetRunStreamOwnership(): void;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exactly one SSE reader may fold a given run into UI state.
|
|
3
|
+
*
|
|
4
|
+
* Two readers attach to the same run routinely: the chat adapter's own stream,
|
|
5
|
+
* and AssistantChat's reconnect reader for runs with no live adapter stream
|
|
6
|
+
* (page reload, tab restore). When both are attached they build two independent
|
|
7
|
+
* accumulators from the same events, and the UI renders both — duplicate tool
|
|
8
|
+
* cards (one spinning, one static) and the same assistant text streaming twice.
|
|
9
|
+
*
|
|
10
|
+
* The previous guard read a React ref (`isRuntimeRunningRef`) at attach time and
|
|
11
|
+
* re-checked it from a 1s poll. That is a race, not a lock: the ref lags a
|
|
12
|
+
* render behind, the poll is skipped while the tab is hidden, and the refs were
|
|
13
|
+
* per-component-instance while MultiTabAssistantChat mounts several instances
|
|
14
|
+
* against one run. Ownership therefore lives here — module scope, one registry
|
|
15
|
+
* per browser tab, claimed and checked synchronously.
|
|
16
|
+
*
|
|
17
|
+
* Claims are advisory in one direction only: a reader that does not hold the
|
|
18
|
+
* claim must not mutate UI state. It may still drain its socket to completion.
|
|
19
|
+
*/
|
|
20
|
+
const owners = new Map();
|
|
21
|
+
function runKey(threadId, runId) {
|
|
22
|
+
return `${threadId} ${runId}`;
|
|
23
|
+
}
|
|
24
|
+
/** Mint a token identifying one reader. Not shared between readers. */
|
|
25
|
+
export function createRunStreamToken(label) {
|
|
26
|
+
return Symbol(label ?? "run-stream-reader");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Take ownership of a run's UI fold. Returns false when another live reader
|
|
30
|
+
* already holds it — the caller must not attach, or must attach read-only.
|
|
31
|
+
* Re-claiming with the same token is a no-op success so retry loops are safe.
|
|
32
|
+
*/
|
|
33
|
+
export function claimRunStream(threadId, runId, token) {
|
|
34
|
+
const key = runKey(threadId, runId);
|
|
35
|
+
const current = owners.get(key);
|
|
36
|
+
if (current && current !== token)
|
|
37
|
+
return false;
|
|
38
|
+
owners.set(key, token);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Take ownership even if another reader holds it, and return whether the claim
|
|
43
|
+
* changed hands. The adapter's own stream outranks the reconnect fallback: when
|
|
44
|
+
* the user sends a message the adapter legitimately becomes the owner, and the
|
|
45
|
+
* displaced reader learns it lost via `ownsRunStream`.
|
|
46
|
+
*/
|
|
47
|
+
export function preemptRunStream(threadId, runId, token) {
|
|
48
|
+
const key = runKey(threadId, runId);
|
|
49
|
+
const current = owners.get(key);
|
|
50
|
+
owners.set(key, token);
|
|
51
|
+
return current !== token;
|
|
52
|
+
}
|
|
53
|
+
export function ownsRunStream(threadId, runId, token) {
|
|
54
|
+
return owners.get(runKey(threadId, runId)) === token;
|
|
55
|
+
}
|
|
56
|
+
/** Release only if still held by this token, so a late unmount cannot free a successor's claim. */
|
|
57
|
+
export function releaseRunStream(threadId, runId, token) {
|
|
58
|
+
const key = runKey(threadId, runId);
|
|
59
|
+
if (owners.get(key) === token)
|
|
60
|
+
owners.delete(key);
|
|
61
|
+
}
|
|
62
|
+
/** Test seam. */
|
|
63
|
+
export function __resetRunStreamOwnership() {
|
|
64
|
+
owners.clear();
|
|
65
|
+
}
|
|
@@ -4,6 +4,7 @@ import { IconLock, IconWorld, IconTrash, IconCheck, IconChevronDown, IconLoader2
|
|
|
4
4
|
import { useEffect, useId, useRef, useState, } from "react";
|
|
5
5
|
import { writeClipboardText } from "../clipboard.js";
|
|
6
6
|
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
|
|
7
|
+
import { useT } from "../i18n.js";
|
|
7
8
|
import { cn } from "../utils.js";
|
|
8
9
|
import { useShareButtonController, } from "./useShareButtonController.js";
|
|
9
10
|
// Match shadcn's <Button size="sm" variant="outline"> sizing so the trigger
|
|
@@ -17,46 +18,77 @@ const SHARE_NESTED_OVERLAY_ATTR = "data-agent-native-share-overlay";
|
|
|
17
18
|
const SHARE_NESTED_OVERLAY_Z = "z-[100020]";
|
|
18
19
|
const VIS_META = {
|
|
19
20
|
private: {
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
labelKey: "agentChat.share.private",
|
|
22
|
+
descriptionKey: "agentChat.share.privateDescription",
|
|
23
|
+
defaultLabel: "Private",
|
|
24
|
+
defaultDescription: "Only people with access can view",
|
|
22
25
|
Icon: IconLock,
|
|
23
26
|
},
|
|
24
27
|
org: {
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
labelKey: "agentChat.share.organization",
|
|
29
|
+
descriptionKey: "agentChat.share.organizationDescription",
|
|
30
|
+
defaultLabel: "Organization",
|
|
31
|
+
defaultDescription: "Anyone in your organization can view",
|
|
27
32
|
Icon: IconUsersGroup,
|
|
28
33
|
},
|
|
29
34
|
public: {
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
labelKey: "agentChat.share.public",
|
|
36
|
+
descriptionKey: "agentChat.share.publicDescription",
|
|
37
|
+
defaultLabel: "Public",
|
|
38
|
+
defaultDescription: "Anyone with the link can view",
|
|
32
39
|
Icon: IconWorld,
|
|
33
40
|
},
|
|
34
41
|
};
|
|
35
|
-
function visibilityMeta(visibility, copy) {
|
|
42
|
+
function visibilityMeta(visibility, t, copy) {
|
|
36
43
|
const base = VIS_META[visibility];
|
|
37
44
|
const override = copy?.[visibility];
|
|
38
45
|
return {
|
|
39
|
-
|
|
40
|
-
label: override?.label ??
|
|
41
|
-
|
|
46
|
+
Icon: base.Icon,
|
|
47
|
+
label: override?.label ??
|
|
48
|
+
t(base.labelKey, {
|
|
49
|
+
defaultValue: base.defaultLabel,
|
|
50
|
+
}),
|
|
51
|
+
description: override?.description ??
|
|
52
|
+
t(base.descriptionKey, {
|
|
53
|
+
defaultValue: base.defaultDescription,
|
|
54
|
+
}),
|
|
42
55
|
};
|
|
43
56
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
function roleOptions(t) {
|
|
58
|
+
return [
|
|
59
|
+
{
|
|
60
|
+
value: "viewer",
|
|
61
|
+
label: t("agentChat.share.viewer", { defaultValue: "Viewer" }),
|
|
62
|
+
description: t("agentChat.share.viewerDescription", {
|
|
63
|
+
defaultValue: "Can view",
|
|
64
|
+
}),
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
value: "commenter",
|
|
68
|
+
label: t("agentChat.share.commenter", { defaultValue: "Commenter" }),
|
|
69
|
+
description: t("agentChat.share.commenterDescription", {
|
|
70
|
+
defaultValue: "Can view and add comments",
|
|
71
|
+
}),
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
value: "editor",
|
|
75
|
+
label: t("agentChat.share.editor", { defaultValue: "Editor" }),
|
|
76
|
+
description: t("agentChat.share.editorDescription", {
|
|
77
|
+
defaultValue: "Can edit",
|
|
78
|
+
}),
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
value: "admin",
|
|
82
|
+
label: t("agentChat.share.admin", { defaultValue: "Admin" }),
|
|
83
|
+
description: t("agentChat.share.adminDescription", {
|
|
84
|
+
defaultValue: "Can edit and manage access",
|
|
85
|
+
}),
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
function roleMeta(role, t, copy) {
|
|
90
|
+
const options = roleOptions(t);
|
|
91
|
+
const base = options.find((option) => option.value === role) ?? options[0];
|
|
60
92
|
const override = copy?.[role];
|
|
61
93
|
return {
|
|
62
94
|
...base,
|
|
@@ -81,6 +113,7 @@ function handleSharePopoverInteractOutside(event) {
|
|
|
81
113
|
* dark mode in any shadcn template.
|
|
82
114
|
*/
|
|
83
115
|
export function ShareButton(props) {
|
|
116
|
+
const t = useT();
|
|
84
117
|
const controller = useShareButtonController({
|
|
85
118
|
resourceType: props.resourceType,
|
|
86
119
|
resourceId: props.resourceId,
|
|
@@ -93,22 +126,32 @@ export function ShareButton(props) {
|
|
|
93
126
|
});
|
|
94
127
|
const triggerVisibility = controller.triggerVisibility;
|
|
95
128
|
const triggerMeta = triggerVisibility
|
|
96
|
-
? visibilityMeta(triggerVisibility, props.visibilityCopy)
|
|
129
|
+
? visibilityMeta(triggerVisibility, t, props.visibilityCopy)
|
|
97
130
|
: null;
|
|
98
131
|
const TriggerIcon = triggerMeta?.Icon ?? IconShare3;
|
|
99
132
|
const iconOnly = props.trigger === "icon";
|
|
100
133
|
const showTriggerIcon = iconOnly || props.trigger === "label-icon";
|
|
101
|
-
const triggerLabel = iconOnly && triggerMeta
|
|
102
|
-
|
|
134
|
+
const triggerLabel = iconOnly && triggerMeta
|
|
135
|
+
? t("agentChat.share.triggerWithVisibility", {
|
|
136
|
+
defaultValue: "Share ({{visibility}})",
|
|
137
|
+
visibility: triggerMeta.label,
|
|
138
|
+
})
|
|
139
|
+
: t("agentChat.share.share", { defaultValue: "Share" });
|
|
140
|
+
return (_jsxs(Popover, { open: controller.open, onOpenChange: controller.handleOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: cn(iconOnly ? BUTTON_GHOST_ICON : BUTTON_OUTLINE_SM, props.triggerClassName), "aria-label": triggerLabel, title: triggerLabel, children: [showTriggerIcon && _jsx(TriggerIcon, { size: 16, strokeWidth: 1.75 }), !iconOnly && (_jsx("span", { children: t("agentChat.share.share", { defaultValue: "Share" }) }))] }) }), _jsx(PopoverContent, { align: "end", sideOffset: 6, "data-agent-native-share-overlay": "", className: cn("z-[2000] w-[min(460px,92vw)] rounded-lg p-4 shadow-lg", SHARE_POPOVER_SURFACE, props.popoverClassName), onOpenAutoFocus: (e) => e.preventDefault(), onInteractOutside: handleSharePopoverInteractOutside, children: _jsx(SharePanel, { ...props, controller: controller }) })] }));
|
|
103
141
|
}
|
|
104
142
|
function SharePanel(props) {
|
|
143
|
+
const t = useT();
|
|
105
144
|
const { controller } = props;
|
|
106
145
|
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;
|
|
107
146
|
const hasInviteEmail = inviteEmail.trim().length > 0;
|
|
108
147
|
const isLoading = data === undefined;
|
|
109
|
-
const meta = visibilityMeta(visibility, props.visibilityCopy);
|
|
110
|
-
const peopleAccessLabel = props.peopleAccessLabel ??
|
|
111
|
-
|
|
148
|
+
const meta = visibilityMeta(visibility, t, props.visibilityCopy);
|
|
149
|
+
const peopleAccessLabel = props.peopleAccessLabel ??
|
|
150
|
+
t("agentChat.share.peopleWithAccess", {
|
|
151
|
+
defaultValue: "People with access",
|
|
152
|
+
});
|
|
153
|
+
const generalAccessLabel = props.generalAccessLabel ??
|
|
154
|
+
t("agentChat.share.generalAccess", { defaultValue: "General access" });
|
|
112
155
|
const shareLinks = (_jsxs(_Fragment, { children: [props.shareUrl ? (_jsx(CopyLinkField, { value: props.shareUrl, label: props.shareUrlLabel, description: props.shareUrlDescription })) : props.shareUrlPlaceholder ? (_jsxs("div", { className: "mb-4 rounded-md border border-dashed border-border bg-muted/20 px-3 py-2.5 text-xs text-muted-foreground", children: [props.shareUrlLabel ? (_jsx("div", { className: "mb-0.5 font-medium text-foreground", children: props.shareUrlLabel })) : null, props.shareUrlPlaceholder] })) : null, props.secondaryShareUrl ? (_jsx(CopyLinkField, { value: props.secondaryShareUrl, label: props.secondaryShareUrlLabel, description: props.secondaryShareUrlDescription })) : null] }));
|
|
113
156
|
const showShareLinks = (props.showShareLinks ?? true) &&
|
|
114
157
|
(Boolean(props.shareUrl) ||
|
|
@@ -117,7 +160,8 @@ function SharePanel(props) {
|
|
|
117
160
|
const shareUrlPlacement = props.shareUrlPlacement ?? "top";
|
|
118
161
|
const extraTabs = props.shareTabs?.tabs.filter((tab) => tab.value !== "context") ?? [];
|
|
119
162
|
const hasTabs = extraTabs.length > 0;
|
|
120
|
-
const shareTabLabel = props.shareTabs?.shareLabel ??
|
|
163
|
+
const shareTabLabel = props.shareTabs?.shareLabel ??
|
|
164
|
+
t("agentChat.share.shareLink", { defaultValue: "Share link" });
|
|
121
165
|
const sharePanel = isLoading ? (_jsxs("div", { children: [showShareLinks ? (_jsx("div", { className: "mb-4 h-9 rounded-md bg-muted animate-pulse" })) : null, _jsx("div", { className: "mb-2 text-sm font-semibold", children: generalAccessLabel }), _jsx("div", { className: "mb-4 h-9 rounded-md bg-muted animate-pulse" }), _jsx("div", { className: "mb-2 text-sm font-semibold", children: peopleAccessLabel }), _jsx("div", { className: "mb-4 h-7 rounded-md bg-muted animate-pulse" })] })) : (_jsxs("div", { children: [showShareLinks && shareUrlPlacement === "top" ? shareLinks : null, _jsxs("div", { className: "mb-4", children: [_jsx("div", { className: "mb-2 text-sm font-semibold", children: generalAccessLabel }), _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(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", { className: "sr-only", children: meta.description }), visibility === "org" && props.hideInSearchControl ? (_jsx(AdvancedAccessPopover, { control: props.hideInSearchControl, canManage: canManage, onToggle: handleHideInSearch })) : null] })] })] })] }), _jsxs("div", { className: "mb-4 space-y-4", children: [_jsx("div", { className: "text-sm font-semibold", children: peopleAccessLabel }), canManage ? (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-stretch gap-2", children: [_jsx(MemberAutocomplete, { value: inviteEmail, open: suggestionsOpen, onOpenChange: setSuggestionsOpen, onValueChange: (next) => {
|
|
122
166
|
onInviteEmailChange(next);
|
|
123
167
|
if (shareError)
|
|
@@ -128,8 +172,28 @@ function SharePanel(props) {
|
|
|
128
172
|
if (shareError)
|
|
129
173
|
setShareError(null);
|
|
130
174
|
}, onSubmit: handleAdd, placeholder: policy.requireOrgMemberForUserShares
|
|
131
|
-
? "
|
|
132
|
-
|
|
175
|
+
? t("agentChat.share.addPeopleOrganization", {
|
|
176
|
+
defaultValue: "Add people from your organization",
|
|
177
|
+
})
|
|
178
|
+
: t("agentChat.share.addPeopleEmail", {
|
|
179
|
+
defaultValue: "Add people by email",
|
|
180
|
+
}), suggestions: memberSuggestions, search: memberSearch }), _jsx(RoleSelect, { value: role, onChange: setRole, roleCopy: props.roleCopy, allowedRoles: props.allowedRoles }), _jsx("button", { type: "button", onClick: handleAdd, disabled: !hasInviteEmail, className: BUTTON_PRIMARY_SM, children: t("agentChat.share.add", { defaultValue: "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" }), t("agentChat.share.notifyPeople", {
|
|
181
|
+
defaultValue: "Notify people",
|
|
182
|
+
})] }), 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
|
|
183
|
+
? t("agentChat.share.hideMessage", {
|
|
184
|
+
defaultValue: "Hide message",
|
|
185
|
+
})
|
|
186
|
+
: t("agentChat.share.addMessage", {
|
|
187
|
+
defaultValue: "Add a message",
|
|
188
|
+
}) })) : 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": t("agentChat.share.message", {
|
|
189
|
+
defaultValue: "Message",
|
|
190
|
+
}), placeholder: t("agentChat.share.messagePlaceholder", {
|
|
191
|
+
defaultValue: "Add a short note (optional)",
|
|
192
|
+
}), 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, _jsxs("ul", { className: "flex list-none flex-col gap-1 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, t) }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: displayName(data.ownerEmail, knownMembers, t) }), _jsx("span", { className: "text-xs text-muted-foreground", children: t("agentChat.share.owner", { defaultValue: "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, t), org: s.principalType === "org" }), _jsx("span", { className: "flex-1 min-w-0 truncate", children: principalLabel(s, knownMembers, t) }), canManage ? (_jsx(RoleSelect, { value: s.role, onChange: (r) => handleChangeRole(s, r), disabled: inFlight.has(keyOf(s)), plain: true, roleCopy: props.roleCopy, allowedRoles: props.allowedRoles })) : (_jsx("span", { className: "text-xs text-muted-foreground", children: roleMeta(s.role, t, props.roleCopy).label })), canManage ? (_jsx("button", { type: "button", "aria-label": t("agentChat.share.remove", {
|
|
193
|
+
defaultValue: "Remove",
|
|
194
|
+
}), 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: t("agentChat.share.noAccess", {
|
|
195
|
+
defaultValue: "No one has access yet.",
|
|
196
|
+
}) })) : 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] }));
|
|
133
197
|
if (!hasTabs)
|
|
134
198
|
return sharePanel;
|
|
135
199
|
const tabs = [
|
|
@@ -144,17 +208,30 @@ function SharePanel(props) {
|
|
|
144
208
|
const activeTab = tabs.some((tab) => tab.value === activeShareTab)
|
|
145
209
|
? activeShareTab
|
|
146
210
|
: "share";
|
|
147
|
-
return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx("div", { role: "tablist", "aria-label": "
|
|
211
|
+
return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx("div", { role: "tablist", "aria-label": t("agentChat.share.shareOptions", {
|
|
212
|
+
defaultValue: "Share options",
|
|
213
|
+
}), className: "flex gap-1 rounded-xl bg-muted/70 p-1", children: tabs.map((tab) => {
|
|
148
214
|
const active = tab.value === activeTab;
|
|
149
215
|
return (_jsx("button", { type: "button", role: "tab", "aria-selected": active, disabled: tab.disabled, onClick: () => handleShareTabChange(tab.value), className: cn("h-11 min-w-0 flex-1 rounded-lg px-3 text-sm font-medium text-muted-foreground transition-colors hover:bg-background/70 hover:text-foreground disabled:pointer-events-none disabled:opacity-50", active &&
|
|
150
216
|
"bg-background text-foreground shadow-sm ring-2 ring-primary"), children: _jsx("span", { className: "block truncate", children: tab.label }) }, tab.value));
|
|
151
217
|
}) }), _jsx("div", { role: "tabpanel", children: tabs.find((tab) => tab.value === activeTab)?.content })] }));
|
|
152
218
|
}
|
|
153
219
|
function AdvancedAccessPopover({ control, canManage, onToggle, }) {
|
|
154
|
-
|
|
155
|
-
|
|
220
|
+
const t = useT();
|
|
221
|
+
return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { type: "button", disabled: !canManage, className: "inline-flex items-center gap-1 rounded-sm px-1 py-0.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50", children: [t("agentChat.share.advanced", { defaultValue: "Advanced" }), _jsx(IconChevronDown, { size: 12, strokeWidth: 1.8 })] }) }), _jsx(PopoverContent, { align: "start", sideOffset: 6, "data-agent-native-share-overlay": "", onOpenAutoFocus: (event) => event.preventDefault(), className: cn(SHARE_NESTED_OVERLAY_Z, "w-72 p-3 shadow-lg", SHARE_POPOVER_SURFACE), children: _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium text-foreground", children: t("agentChat.share.advancedAccess", {
|
|
222
|
+
defaultValue: "Advanced access",
|
|
223
|
+
}) }), _jsx("div", { className: "mt-1 text-xs leading-5 text-muted-foreground", children: t("agentChat.share.advancedDescription", {
|
|
224
|
+
defaultValue: "Control how organization access appears in search.",
|
|
225
|
+
}) })] }), _jsxs("button", { type: "button", role: "switch", "aria-checked": control.checked, disabled: !canManage || control.pending, onClick: onToggle, className: cn("flex w-full items-start gap-3 rounded-md border border-border/70 bg-card px-3 py-2.5 text-start transition-colors hover:bg-accent/45 disabled:cursor-not-allowed disabled:opacity-60", control.checked && "border-border bg-accent/35 text-foreground"), children: [_jsx("span", { "aria-hidden": true, className: cn("relative mt-0.5 inline-flex h-5 w-9 shrink-0 items-center rounded-full border border-border bg-muted-foreground/25 transition-colors", control.checked && "border-primary/70 bg-primary"), children: _jsx("span", { className: cn("ml-0.5 size-4 rounded-full bg-popover shadow-sm transition-transform", control.checked && "translate-x-4") }) }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsxs("span", { className: "flex items-center gap-1.5 text-sm font-medium text-foreground", children: [_jsx(IconSearchOff, { size: 14, strokeWidth: 1.8 }), control.label ??
|
|
226
|
+
t("agentChat.share.hideInSearch", {
|
|
227
|
+
defaultValue: "Hide in search",
|
|
228
|
+
})] }), _jsx("span", { className: "mt-0.5 block text-xs leading-5 text-muted-foreground", children: control.description ??
|
|
229
|
+
t("agentChat.share.linkCanStillOpen", {
|
|
230
|
+
defaultValue: "People with the link can still open this.",
|
|
231
|
+
}) })] })] })] }) })] }));
|
|
156
232
|
}
|
|
157
233
|
function MemberAutocomplete({ value, open, onOpenChange, onValueChange, onSelectMember, onSubmit, placeholder, suggestions, search, }) {
|
|
234
|
+
const t = useT();
|
|
158
235
|
const rawListboxId = useId();
|
|
159
236
|
const listboxId = rawListboxId.replace(/:/g, "");
|
|
160
237
|
const inputRef = useRef(null);
|
|
@@ -247,12 +324,23 @@ function MemberAutocomplete({ value, open, onOpenChange, onValueChange, onSelect
|
|
|
247
324
|
return (_jsxs("div", { id: optionId(listboxId, index), role: "option", "aria-selected": active, onMouseDown: (event) => event.preventDefault(), onMouseEnter: () => setActiveIndex(index), onClick: () => chooseMember(member), className: cn("flex cursor-pointer select-none flex-col rounded-sm px-3 py-2 text-sm outline-none", active
|
|
248
325
|
? "bg-accent text-accent-foreground"
|
|
249
326
|
: "text-foreground hover:bg-accent hover:text-accent-foreground"), children: [_jsx("span", { className: "truncate font-medium", children: member.name?.trim() || member.email }), member.name?.trim() ? (_jsx("span", { className: "truncate text-xs text-muted-foreground", children: member.email })) : null] }, member.email));
|
|
250
|
-
}), search.isLoading && suggestions.length === 0 ? (_jsx("div", { className: "px-3 py-3 text-sm text-muted-foreground", children:
|
|
327
|
+
}), search.isLoading && suggestions.length === 0 ? (_jsx("div", { className: "px-3 py-3 text-sm text-muted-foreground", children: t("agentChat.share.searching", {
|
|
328
|
+
defaultValue: "Searching...",
|
|
329
|
+
}) })) : null, search.error ? (_jsx("div", { className: "px-3 py-3 text-sm text-muted-foreground", children: t("agentChat.share.loadPeopleFailed", {
|
|
330
|
+
defaultValue: "Could not load people.",
|
|
331
|
+
}) })) : null, !search.isLoading && !search.error && suggestions.length === 0 ? (_jsx("div", { className: "px-3 py-3 text-sm text-muted-foreground", children: value.trim()
|
|
332
|
+
? t("agentChat.share.noMatches", {
|
|
333
|
+
defaultValue: "No matches.",
|
|
334
|
+
})
|
|
335
|
+
: t("agentChat.share.noPeopleFound", {
|
|
336
|
+
defaultValue: "No people found.",
|
|
337
|
+
}) })) : null, search.isLoadingMore ? (_jsxs("div", { className: "flex items-center gap-2 px-3 py-2 text-sm text-muted-foreground", children: [_jsx(IconLoader2, { "aria-hidden": true, size: 14, strokeWidth: 1.8, className: "animate-spin" }), t("agentChat.share.loading", { defaultValue: "Loading..." })] })) : null, search.hasMore && !search.isLoadingMore ? (_jsx("button", { type: "button", onMouseDown: (event) => event.preventDefault(), onClick: search.loadMore, className: "mt-1 flex w-full items-center justify-center rounded-sm px-3 py-2 text-sm font-medium text-muted-foreground hover:bg-accent hover:text-accent-foreground", children: t("agentChat.share.loadMore", { defaultValue: "Load more" }) })) : null] }) })] }));
|
|
251
338
|
}
|
|
252
339
|
function optionId(baseId, index) {
|
|
253
340
|
return `${baseId}-option-${index}`;
|
|
254
341
|
}
|
|
255
|
-
function CopyLinkField({ value, label
|
|
342
|
+
function CopyLinkField({ value, label, description, }) {
|
|
343
|
+
const t = useT();
|
|
256
344
|
const [copied, setCopied] = useState(false);
|
|
257
345
|
const resetRef = useRef(undefined);
|
|
258
346
|
useEffect(() => {
|
|
@@ -272,7 +360,12 @@ function CopyLinkField({ value, label = "Share link", description, }) {
|
|
|
272
360
|
setCopied(false);
|
|
273
361
|
}
|
|
274
362
|
};
|
|
275
|
-
return (_jsxs("div", { className: "mb-4 flex items-center justify-between gap-4", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-sm font-semibold", children: label
|
|
363
|
+
return (_jsxs("div", { className: "mb-4 flex items-center justify-between gap-4", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-sm font-semibold", children: label ??
|
|
364
|
+
t("agentChat.share.shareLink", { defaultValue: "Share link" }) }), description ? (_jsx("div", { className: "mt-1 text-xs text-muted-foreground", children: description })) : null] }), _jsxs("button", { type: "button", onClick: handleCopy, "aria-label": copied
|
|
365
|
+
? t("agentChat.share.copied", { defaultValue: "Copied" })
|
|
366
|
+
: t("agentChat.share.copy", { defaultValue: "Copy" }), className: "inline-flex h-9 shrink-0 items-center gap-2 whitespace-nowrap rounded-md border border-input bg-card px-3 text-sm font-medium text-foreground hover:bg-accent", children: [copied ? _jsx(IconCheck, { size: 15 }) : _jsx(IconLink, { size: 15 }), copied
|
|
367
|
+
? t("agentChat.share.copied", { defaultValue: "Copied" })
|
|
368
|
+
: t("agentChat.share.copy", { defaultValue: "Copy" })] })] }));
|
|
276
369
|
}
|
|
277
370
|
// ---------------------------------------------------------------------------
|
|
278
371
|
// Radix Select wrappers styled like shadcn Select (no native <select> anywhere)
|
|
@@ -283,17 +376,22 @@ function SelectItems({ items }) {
|
|
|
283
376
|
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))) }));
|
|
284
377
|
}
|
|
285
378
|
function RoleSelect(props) {
|
|
286
|
-
const
|
|
287
|
-
const
|
|
288
|
-
const
|
|
379
|
+
const t = useT();
|
|
380
|
+
const allOptions = roleOptions(t);
|
|
381
|
+
const current = roleMeta(props.value, t, props.roleCopy);
|
|
382
|
+
const allowedRoles = props.allowedRoles ?? allOptions.map((item) => item.value);
|
|
383
|
+
const options = allOptions
|
|
384
|
+
.filter((option) => option.value === props.value || allowedRoles.includes(option.value))
|
|
385
|
+
.map((option) => roleMeta(option.value, t, props.roleCopy));
|
|
289
386
|
return (_jsxs(Select.Root, { value: props.value, onValueChange: (v) => props.onChange(v), disabled: props.disabled, children: [_jsxs(Select.Trigger, { className: props.plain
|
|
290
387
|
? cn(BUTTON_BASE, "h-7 px-2 bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground")
|
|
291
|
-
: 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 }) }) }) })] }));
|
|
388
|
+
: cn(BUTTON_BASE, "h-9 px-3 border border-input bg-card hover:bg-accent hover:text-accent-foreground"), "aria-label": t("agentChat.share.role", { defaultValue: "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 }) }) }) })] }));
|
|
292
389
|
}
|
|
293
390
|
function VisibilitySelect(props) {
|
|
391
|
+
const t = useT();
|
|
294
392
|
const allowPrivate = props.allowPrivate !== false;
|
|
295
393
|
const allowPublic = props.allowPublic !== false;
|
|
296
|
-
const current = visibilityMeta(props.value, props.visibilityCopy);
|
|
394
|
+
const current = visibilityMeta(props.value, t, props.visibilityCopy);
|
|
297
395
|
const options = Object.keys(VIS_META).filter((k) => {
|
|
298
396
|
if (k === props.value)
|
|
299
397
|
return true;
|
|
@@ -303,10 +401,12 @@ function VisibilitySelect(props) {
|
|
|
303
401
|
return false;
|
|
304
402
|
return true;
|
|
305
403
|
});
|
|
306
|
-
return (_jsxs(Select.Root, { value: props.value, onValueChange: (v) => props.onChange(v), disabled: props.disabled, children: [_jsxs(Select.Trigger, { className: cn(BUTTON_BASE, "h-7 px-1 -ms-1 bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground"), "aria-label": "
|
|
404
|
+
return (_jsxs(Select.Root, { value: props.value, onValueChange: (v) => props.onChange(v), disabled: props.disabled, children: [_jsxs(Select.Trigger, { className: cn(BUTTON_BASE, "h-7 px-1 -ms-1 bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground"), "aria-label": t("agentChat.share.generalAccess", {
|
|
405
|
+
defaultValue: "General access",
|
|
406
|
+
}), 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.map((k) => ({
|
|
307
407
|
value: k,
|
|
308
|
-
label: visibilityMeta(k, props.visibilityCopy).label,
|
|
309
|
-
description: visibilityMeta(k, props.visibilityCopy)
|
|
408
|
+
label: visibilityMeta(k, t, props.visibilityCopy).label,
|
|
409
|
+
description: visibilityMeta(k, t, props.visibilityCopy)
|
|
310
410
|
.description,
|
|
311
411
|
})) }) }) }) })] }));
|
|
312
412
|
}
|
|
@@ -320,18 +420,24 @@ function initials(s) {
|
|
|
320
420
|
const name = s.split("@")[0] ?? s;
|
|
321
421
|
return (name[0] ?? "?").toUpperCase();
|
|
322
422
|
}
|
|
323
|
-
function principalLabel(share, members) {
|
|
423
|
+
function principalLabel(share, members, t) {
|
|
324
424
|
const serverLabel = share.displayName?.trim();
|
|
325
425
|
if (serverLabel)
|
|
326
426
|
return serverLabel;
|
|
327
427
|
if (share.principalType === "org")
|
|
328
|
-
return "
|
|
329
|
-
|
|
428
|
+
return t("agentChat.share.organization", {
|
|
429
|
+
defaultValue: "Organization",
|
|
430
|
+
});
|
|
431
|
+
return displayName(share.principalId, members, t);
|
|
330
432
|
}
|
|
331
|
-
function displayName(emailOrId, members) {
|
|
433
|
+
function displayName(emailOrId, members, t) {
|
|
332
434
|
const normalized = emailOrId.trim().toLowerCase();
|
|
333
435
|
const match = members.find((m) => m.email.toLowerCase() === normalized);
|
|
334
436
|
if (match?.name && match.name.trim())
|
|
335
437
|
return match.name;
|
|
336
|
-
return normalized.includes("@")
|
|
438
|
+
return normalized.includes("@")
|
|
439
|
+
? emailOrId
|
|
440
|
+
: t("agentChat.share.unknownPerson", {
|
|
441
|
+
defaultValue: "Unknown person",
|
|
442
|
+
});
|
|
337
443
|
}
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
*/
|
|
31
31
|
import crypto from "node:crypto";
|
|
32
32
|
import http from "node:http";
|
|
33
|
-
import { getRequestUserEmail } from "../server/request-context.js";
|
|
33
|
+
import { getRequestRunContext, getRequestUserEmail, } from "../server/request-context.js";
|
|
34
34
|
import { failExpiredSandboxExecution, getSandboxExecutionForOwner, } from "./sandbox/executions-store.js";
|
|
35
35
|
import { BACKGROUND_DEFAULT_TIMEOUT_MS, BACKGROUND_MAX_TIMEOUT_MS, SANDBOX_EXECUTION_REDRIVE_AFTER_MS, driveSandboxExecution, enqueueSandboxExecution, getSandboxAdapter, isQueuedSandboxAdapter, registerSandboxExecutionRunner, resolveExecutionSandboxAdapter, } from "./sandbox/index.js";
|
|
36
36
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
@@ -289,6 +289,7 @@ async function enqueueBackgroundRunCode(input) {
|
|
|
289
289
|
owner,
|
|
290
290
|
orgId: input.context?.orgId ?? null,
|
|
291
291
|
threadId: input.context?.threadId ?? null,
|
|
292
|
+
allowedActionNames: getRequestRunContext()?.allowedActionNames,
|
|
292
293
|
});
|
|
293
294
|
const hasGetTool = Boolean(input.getActions()["get-code-execution"]);
|
|
294
295
|
return JSON.stringify({
|
|
@@ -106,6 +106,7 @@ export async function enqueueSandboxExecution(input) {
|
|
|
106
106
|
code: input.code,
|
|
107
107
|
timeoutMs: input.timeoutMs,
|
|
108
108
|
maxOutputChars: input.maxOutputChars,
|
|
109
|
+
allowedActionNames: input.allowedActionNames,
|
|
109
110
|
});
|
|
110
111
|
let driveNote;
|
|
111
112
|
try {
|
|
@@ -201,7 +202,13 @@ export async function processQueuedSandboxExecution(executionId) {
|
|
|
201
202
|
threadId: claimed.threadId ?? undefined,
|
|
202
203
|
actionName: "run-code",
|
|
203
204
|
};
|
|
204
|
-
const output = await runWithRequestContext({
|
|
205
|
+
const output = await runWithRequestContext({
|
|
206
|
+
userEmail: claimed.owner,
|
|
207
|
+
orgId: claimed.orgId ?? undefined,
|
|
208
|
+
run: claimed.allowedActionNames === undefined
|
|
209
|
+
? undefined
|
|
210
|
+
: { allowedActionNames: claimed.allowedActionNames },
|
|
211
|
+
}, () => runner.execute({
|
|
205
212
|
code: claimed.code,
|
|
206
213
|
timeoutMs: claimed.timeoutMs,
|
|
207
214
|
context,
|
|
@@ -51,6 +51,7 @@ export interface SandboxExecutionRow {
|
|
|
51
51
|
timedOut: boolean;
|
|
52
52
|
error: string | null;
|
|
53
53
|
bridgeToolsUsed: string[];
|
|
54
|
+
allowedActionNames: string[] | undefined;
|
|
54
55
|
createdAt: number;
|
|
55
56
|
startedAt: number | null;
|
|
56
57
|
finishedAt: number | null;
|
|
@@ -65,6 +66,7 @@ export interface CreateSandboxExecutionInput {
|
|
|
65
66
|
maxOutputChars: number;
|
|
66
67
|
maxAttempts?: number;
|
|
67
68
|
runtime?: string;
|
|
69
|
+
allowedActionNames?: readonly string[];
|
|
68
70
|
}
|
|
69
71
|
export interface FinalizeSandboxExecutionInput {
|
|
70
72
|
status: Extract<SandboxExecutionStatus, "succeeded" | "failed" | "timed_out">;
|