@agent-native/core 0.153.0 → 0.153.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +21 -19
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +3 -2
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +3 -2
- package/corpus/templates/calendar/app/lib/event-layout.ts +16 -35
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +27 -55
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +104 -66
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +45 -43
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +0 -1
- package/corpus/templates/clips/desktop/src/app.tsx +818 -442
- package/corpus/templates/clips/desktop/src/styles.css +244 -2
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +2 -2
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +1 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +584 -1
- package/corpus/templates/clips/desktop/src-tauri/src/rewind_clip.rs +25 -0
- package/corpus/templates/clips/desktop/src-tauri/src/util.rs +1 -1
- package/corpus/templates/factory/app/routes/factory.tsx +0 -3
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/ShareDialog.tsx +28 -25
- package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +50 -0
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +12 -60
- package/dist/agent/production-agent.d.ts +8 -0
- package/dist/agent/production-agent.js +25 -2
- package/dist/agent/run-manager.js +13 -3
- package/dist/client/AgentPanel.js +6 -4
- package/dist/client/org/WorkspaceNotice.js +7 -4
- package/dist/client/sharing/ShareButton.d.ts +2 -2
- package/dist/client/sharing/ShareButton.js +18 -13
- package/dist/client/sharing/ShareDialog.d.ts +1 -1
- package/dist/client/sharing/ShareDialog.js +16 -11
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/auth.js +18 -4
- package/dist/server/better-auth-instance.js +8 -1
- package/dist/server/magic-link-attribution.d.ts +21 -0
- package/dist/server/magic-link-attribution.js +115 -0
- package/dist/server/realtime-token.d.ts +1 -1
- package/package.json +1 -1
|
@@ -136,6 +136,8 @@ pub(crate) fn rewind_clip_prepare(
|
|
|
136
136
|
let sources = screen_memory::rewind_clip_sources(&app);
|
|
137
137
|
let output = artifact_path(&app, &artifact_label)?;
|
|
138
138
|
#[cfg(target_os = "macos")]
|
|
139
|
+
let recovery_intent = (server_url.clone(), recording_id.clone());
|
|
140
|
+
#[cfg(target_os = "macos")]
|
|
139
141
|
let shared_sink = match screen_memory::prepare_shared_clip_sink(
|
|
140
142
|
&app,
|
|
141
143
|
output,
|
|
@@ -168,6 +170,29 @@ pub(crate) fn rewind_clip_prepare(
|
|
|
168
170
|
release_temporary_audio(&app, temporary_audio);
|
|
169
171
|
return Err(not_compatible("shared Rewind Clip sinks require macOS"));
|
|
170
172
|
}
|
|
173
|
+
#[cfg(target_os = "macos")]
|
|
174
|
+
if let (Some(server_url), Some(recording_id)) = recovery_intent {
|
|
175
|
+
if !server_url.trim().is_empty() && !recording_id.trim().is_empty() {
|
|
176
|
+
let (width, height) = shared_sink.dimensions();
|
|
177
|
+
if let Err(error) = native_screen::persist_recording_intent(
|
|
178
|
+
shared_sink.path(),
|
|
179
|
+
&recording_id,
|
|
180
|
+
&server_url,
|
|
181
|
+
native_screen::MP4_RECORDING_MIME_TYPE,
|
|
182
|
+
Some(width),
|
|
183
|
+
Some(height),
|
|
184
|
+
include_mic || include_system_audio,
|
|
185
|
+
include_mic,
|
|
186
|
+
include_system_audio,
|
|
187
|
+
has_camera,
|
|
188
|
+
true,
|
|
189
|
+
) {
|
|
190
|
+
shared_sink.cancel();
|
|
191
|
+
release_temporary_audio(&app, temporary_audio);
|
|
192
|
+
return Err(error);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
171
196
|
let response_sources = sources.clone();
|
|
172
197
|
let mut active = state.0.lock().map_err(|error| error.to_string())?;
|
|
173
198
|
if active.is_some() {
|
|
@@ -7,7 +7,7 @@ use crate::state::{
|
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
const POPOVER_SHADOW_GUTTER_LOGICAL: f64 = 24.0;
|
|
10
|
-
const POPOVER_DEFAULT_WIDTH_LOGICAL: f64 =
|
|
10
|
+
const POPOVER_DEFAULT_WIDTH_LOGICAL: f64 = 320.0;
|
|
11
11
|
const POPOVER_DEFAULT_HEIGHT_LOGICAL: f64 = 520.0;
|
|
12
12
|
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
@@ -1516,9 +1516,6 @@ function RulesView({ t }: { t: ReturnType<typeof useT> }) {
|
|
|
1516
1516
|
<CardTitle className="text-base">
|
|
1517
1517
|
{t("factoryRoute.rulesTitle")}
|
|
1518
1518
|
</CardTitle>
|
|
1519
|
-
<p className="text-sm text-muted-foreground">
|
|
1520
|
-
{t("factoryRoute.rulesDescription")}
|
|
1521
|
-
</p>
|
|
1522
1519
|
</CardHeader>
|
|
1523
1520
|
<CardContent className="space-y-2">
|
|
1524
1521
|
<Button
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
IconCheck,
|
|
7
7
|
IconLoader2,
|
|
8
8
|
IconExternalLink,
|
|
9
|
+
IconChevronDown,
|
|
9
10
|
} from "@tabler/icons-react";
|
|
10
11
|
import { useState, type ReactNode } from "react";
|
|
11
12
|
|
|
@@ -86,7 +87,7 @@ export default function ShareDialog({ deck, children }: ShareDialogProps) {
|
|
|
86
87
|
<PopoverTrigger asChild>{children}</PopoverTrigger>
|
|
87
88
|
<PopoverContent
|
|
88
89
|
align="end"
|
|
89
|
-
className="w-[420px] bg-card border-border p-4"
|
|
90
|
+
className="w-[min(420px,calc(100vw-2rem))] bg-card border-border p-4"
|
|
90
91
|
>
|
|
91
92
|
{showCloudUpgrade || isLocal ? (
|
|
92
93
|
<CloudUpgrade
|
|
@@ -104,35 +105,37 @@ export default function ShareDialog({ deck, children }: ShareDialogProps) {
|
|
|
104
105
|
<IconShare2 className="w-4 h-4 text-[#609FF8]" />
|
|
105
106
|
{t("share.title")}
|
|
106
107
|
</div>
|
|
107
|
-
<div className="text-muted-foreground text-xs mt-0.5">
|
|
108
|
-
{t("share.description", { title: deck.title })}
|
|
109
|
-
</div>
|
|
110
108
|
</div>
|
|
111
109
|
|
|
112
110
|
<div className="space-y-4">
|
|
113
111
|
{!shareUrl ? (
|
|
114
112
|
<>
|
|
115
|
-
<
|
|
116
|
-
<
|
|
113
|
+
<details className="group rounded-md border border-border bg-muted/30">
|
|
114
|
+
<summary className="flex cursor-pointer list-none items-center justify-between gap-3 px-3 py-2.5 text-sm font-medium [&::-webkit-details-marker]:hidden">
|
|
117
115
|
{t("share.whatGetsShared")}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
116
|
+
<IconChevronDown className="size-4 shrink-0 text-muted-foreground transition-transform group-open:rotate-180" />
|
|
117
|
+
</summary>
|
|
118
|
+
<div className="space-y-3 border-t border-border px-3 py-3 text-xs text-muted-foreground">
|
|
119
|
+
<ul className="space-y-1">
|
|
120
|
+
<li>
|
|
121
|
+
{t("share.slideContent", {
|
|
122
|
+
count: deck.slides.length,
|
|
123
|
+
})}
|
|
124
|
+
</li>
|
|
125
|
+
<li>{t("share.presentationView")}</li>
|
|
126
|
+
</ul>
|
|
127
|
+
<div>
|
|
128
|
+
<h4 className="mb-1 font-medium text-foreground/90">
|
|
129
|
+
{t("share.whatStaysPrivate")}
|
|
130
|
+
</h4>
|
|
131
|
+
<ul className="space-y-1">
|
|
132
|
+
<li>{t("share.speakerNotes")}</li>
|
|
133
|
+
<li>{t("share.otherPresentations")}</li>
|
|
134
|
+
<li>{t("share.editingAccess")}</li>
|
|
135
|
+
</ul>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</details>
|
|
136
139
|
|
|
137
140
|
{error && (
|
|
138
141
|
<p className="text-xs text-red-400 bg-red-400/10 px-3 py-2 rounded-lg">
|
|
@@ -194,7 +197,7 @@ export default function ShareDialog({ deck, children }: ShareDialogProps) {
|
|
|
194
197
|
{t("share.openSharedLink")}
|
|
195
198
|
</a>
|
|
196
199
|
|
|
197
|
-
<p className="text-[11px] text-muted-foreground/70 text-center">
|
|
200
|
+
<p className="sr-only text-[11px] text-muted-foreground/70 text-center">
|
|
198
201
|
{t("share.anyoneWithLink")}
|
|
199
202
|
</p>
|
|
200
203
|
</>
|
|
@@ -36,6 +36,8 @@ import {
|
|
|
36
36
|
IconStackBack,
|
|
37
37
|
IconStackFront,
|
|
38
38
|
IconUnderline,
|
|
39
|
+
IconZoomIn,
|
|
40
|
+
IconZoomOut,
|
|
39
41
|
} from "@tabler/icons-react";
|
|
40
42
|
import type { ReactNode } from "react";
|
|
41
43
|
|
|
@@ -150,6 +152,7 @@ export function SlideContextToolbar({
|
|
|
150
152
|
objectSelectionCount = 0,
|
|
151
153
|
onAlignObjects,
|
|
152
154
|
onDistributeObjects,
|
|
155
|
+
zoomControls,
|
|
153
156
|
}: {
|
|
154
157
|
snapshot: SlideStyleSnapshot | null;
|
|
155
158
|
background: string | undefined;
|
|
@@ -164,6 +167,13 @@ export function SlideContextToolbar({
|
|
|
164
167
|
objectSelectionCount?: number;
|
|
165
168
|
onAlignObjects?: (alignment: SlideObjectAlignment) => void;
|
|
166
169
|
onDistributeObjects?: (distribution: SlideObjectDistribution) => void;
|
|
170
|
+
zoomControls?: {
|
|
171
|
+
value: number;
|
|
172
|
+
onZoomOut: () => void;
|
|
173
|
+
onZoomIn: () => void;
|
|
174
|
+
canZoomOut: boolean;
|
|
175
|
+
canZoomIn: boolean;
|
|
176
|
+
};
|
|
167
177
|
}) {
|
|
168
178
|
const t = useT();
|
|
169
179
|
const documentColors = tokenPalette(designSystem, t).map(
|
|
@@ -914,6 +924,46 @@ export function SlideContextToolbar({
|
|
|
914
924
|
</Popover>
|
|
915
925
|
</>
|
|
916
926
|
)}
|
|
927
|
+
{zoomControls && (
|
|
928
|
+
<>
|
|
929
|
+
<div className={TOOLBAR_DIVIDER} />
|
|
930
|
+
<Tooltip>
|
|
931
|
+
<TooltipTrigger asChild>
|
|
932
|
+
<Button
|
|
933
|
+
type="button"
|
|
934
|
+
variant="ghost"
|
|
935
|
+
size="icon"
|
|
936
|
+
className={MENU_BUTTON_CLASS}
|
|
937
|
+
onClick={zoomControls.onZoomOut}
|
|
938
|
+
disabled={!zoomControls.canZoomOut}
|
|
939
|
+
aria-label={t("raw.zoomOut")}
|
|
940
|
+
>
|
|
941
|
+
<IconZoomOut className="size-3.5" />
|
|
942
|
+
</Button>
|
|
943
|
+
</TooltipTrigger>
|
|
944
|
+
<TooltipContent>{t("raw.zoomOut")}</TooltipContent>
|
|
945
|
+
</Tooltip>
|
|
946
|
+
<span className="w-11 shrink-0 text-center text-xs tabular-nums text-muted-foreground">
|
|
947
|
+
{zoomControls.value}%
|
|
948
|
+
</span>
|
|
949
|
+
<Tooltip>
|
|
950
|
+
<TooltipTrigger asChild>
|
|
951
|
+
<Button
|
|
952
|
+
type="button"
|
|
953
|
+
variant="ghost"
|
|
954
|
+
size="icon"
|
|
955
|
+
className={MENU_BUTTON_CLASS}
|
|
956
|
+
onClick={zoomControls.onZoomIn}
|
|
957
|
+
disabled={!zoomControls.canZoomIn}
|
|
958
|
+
aria-label={t("raw.zoomIn")}
|
|
959
|
+
>
|
|
960
|
+
<IconZoomIn className="size-3.5" />
|
|
961
|
+
</Button>
|
|
962
|
+
</TooltipTrigger>
|
|
963
|
+
<TooltipContent>{t("raw.zoomIn")}</TooltipContent>
|
|
964
|
+
</Tooltip>
|
|
965
|
+
</>
|
|
966
|
+
)}
|
|
917
967
|
</div>
|
|
918
968
|
);
|
|
919
969
|
}
|
|
@@ -21,13 +21,10 @@ import {
|
|
|
21
21
|
IconBrush,
|
|
22
22
|
IconClipboard,
|
|
23
23
|
IconCopy,
|
|
24
|
-
IconMaximize,
|
|
25
24
|
IconStackBack,
|
|
26
25
|
IconStackFront,
|
|
27
26
|
IconTrash,
|
|
28
27
|
IconX,
|
|
29
|
-
IconZoomIn,
|
|
30
|
-
IconZoomOut,
|
|
31
28
|
} from "@tabler/icons-react";
|
|
32
29
|
import {
|
|
33
30
|
useState,
|
|
@@ -1252,10 +1249,6 @@ export default function SlideEditor({
|
|
|
1252
1249
|
.find((preset) => preset < canvasZoom);
|
|
1253
1250
|
setManualCanvasZoom(previous ?? CANVAS_ZOOM_PRESETS[0]);
|
|
1254
1251
|
}, [canvasZoom, setManualCanvasZoom]);
|
|
1255
|
-
const fitCanvasToScreen = useCallback(() => {
|
|
1256
|
-
userSetCanvasZoomRef.current = false;
|
|
1257
|
-
setCanvasZoom(fitCanvasZoom);
|
|
1258
|
-
}, [fitCanvasZoom]);
|
|
1259
1252
|
|
|
1260
1253
|
usePinchZoom({
|
|
1261
1254
|
containerRef: scrollContainerRef,
|
|
@@ -5010,6 +5003,16 @@ export default function SlideEditor({
|
|
|
5010
5003
|
const objectOperationSelection = getObjectOperationSelection();
|
|
5011
5004
|
const hasObjectSelection = objectOperationSelection !== null;
|
|
5012
5005
|
const objectSelectionCount = objectOperationSelection?.elements.length ?? 0;
|
|
5006
|
+
const zoomControls = slide.excalidrawData
|
|
5007
|
+
? undefined
|
|
5008
|
+
: {
|
|
5009
|
+
value: canvasZoom,
|
|
5010
|
+
onZoomOut: canvasZoomOut,
|
|
5011
|
+
onZoomIn: canvasZoomIn,
|
|
5012
|
+
canZoomOut: canvasZoom > MIN_CANVAS_ZOOM,
|
|
5013
|
+
canZoomIn:
|
|
5014
|
+
canvasZoom < CANVAS_ZOOM_PRESETS[CANVAS_ZOOM_PRESETS.length - 1],
|
|
5015
|
+
};
|
|
5013
5016
|
|
|
5014
5017
|
// Excalidraw slides have no selectable slide content, so the row collapses
|
|
5015
5018
|
// to its slide-level state — but that state owns the background picker, and
|
|
@@ -5038,6 +5041,7 @@ export default function SlideEditor({
|
|
|
5038
5041
|
objectSelectionCount={objectSelectionCount}
|
|
5039
5042
|
onAlignObjects={handleAlignSelectedObjects}
|
|
5040
5043
|
onDistributeObjects={handleDistributeSelectedObjects}
|
|
5044
|
+
zoomControls={zoomControls}
|
|
5041
5045
|
/>
|
|
5042
5046
|
</div>
|
|
5043
5047
|
) : null;
|
|
@@ -5061,6 +5065,7 @@ export default function SlideEditor({
|
|
|
5061
5065
|
objectSelectionCount={objectSelectionCount}
|
|
5062
5066
|
onAlignObjects={handleAlignSelectedObjects}
|
|
5063
5067
|
onDistributeObjects={handleDistributeSelectedObjects}
|
|
5068
|
+
zoomControls={zoomControls}
|
|
5064
5069
|
className="slide-context-toolbar--top-row"
|
|
5065
5070
|
/>
|
|
5066
5071
|
</div>
|
|
@@ -5102,59 +5107,6 @@ export default function SlideEditor({
|
|
|
5102
5107
|
</div>
|
|
5103
5108
|
) : (
|
|
5104
5109
|
<div className="relative h-full bg-[var(--slides-editor-surface)]">
|
|
5105
|
-
<div className="absolute right-3 top-3 z-20 flex h-8 items-center gap-0.5 rounded-md bg-popover/95 px-1">
|
|
5106
|
-
<Tooltip>
|
|
5107
|
-
<TooltipTrigger asChild>
|
|
5108
|
-
<Button
|
|
5109
|
-
variant="ghost"
|
|
5110
|
-
size="icon"
|
|
5111
|
-
className="h-6 w-6 cursor-pointer"
|
|
5112
|
-
onClick={canvasZoomOut}
|
|
5113
|
-
disabled={canvasZoom <= MIN_CANVAS_ZOOM}
|
|
5114
|
-
aria-label={t("raw.zoomOut")}
|
|
5115
|
-
>
|
|
5116
|
-
<IconZoomOut className="h-3.5 w-3.5" />
|
|
5117
|
-
</Button>
|
|
5118
|
-
</TooltipTrigger>
|
|
5119
|
-
<TooltipContent>{t("raw.zoomOut")}</TooltipContent>
|
|
5120
|
-
</Tooltip>
|
|
5121
|
-
<span className="w-11 text-center text-xs tabular-nums text-muted-foreground">
|
|
5122
|
-
{canvasZoom}%
|
|
5123
|
-
</span>
|
|
5124
|
-
<Tooltip>
|
|
5125
|
-
<TooltipTrigger asChild>
|
|
5126
|
-
<Button
|
|
5127
|
-
variant="ghost"
|
|
5128
|
-
size="icon"
|
|
5129
|
-
className="h-6 w-6 cursor-pointer"
|
|
5130
|
-
onClick={canvasZoomIn}
|
|
5131
|
-
disabled={
|
|
5132
|
-
canvasZoom >=
|
|
5133
|
-
CANVAS_ZOOM_PRESETS[CANVAS_ZOOM_PRESETS.length - 1]
|
|
5134
|
-
}
|
|
5135
|
-
aria-label={t("raw.zoomIn")}
|
|
5136
|
-
>
|
|
5137
|
-
<IconZoomIn className="h-3.5 w-3.5" />
|
|
5138
|
-
</Button>
|
|
5139
|
-
</TooltipTrigger>
|
|
5140
|
-
<TooltipContent>{t("raw.zoomIn")}</TooltipContent>
|
|
5141
|
-
</Tooltip>
|
|
5142
|
-
<div className="mx-0.5 h-4 w-px bg-border" />
|
|
5143
|
-
<Tooltip>
|
|
5144
|
-
<TooltipTrigger asChild>
|
|
5145
|
-
<Button
|
|
5146
|
-
variant="ghost"
|
|
5147
|
-
size="icon"
|
|
5148
|
-
className="h-6 w-6 cursor-pointer"
|
|
5149
|
-
onClick={fitCanvasToScreen}
|
|
5150
|
-
aria-label={t("raw.fitSlideToScreen")}
|
|
5151
|
-
>
|
|
5152
|
-
<IconMaximize className="h-3.5 w-3.5" />
|
|
5153
|
-
</Button>
|
|
5154
|
-
</TooltipTrigger>
|
|
5155
|
-
<TooltipContent>{t("raw.fitToScreen")}</TooltipContent>
|
|
5156
|
-
</Tooltip>
|
|
5157
|
-
</div>
|
|
5158
5110
|
<div
|
|
5159
5111
|
ref={scrollContainerRef}
|
|
5160
5112
|
className={`h-full overflow-auto ${
|
|
@@ -373,6 +373,14 @@ export interface ProductionAgentOptions {
|
|
|
373
373
|
};
|
|
374
374
|
}
|
|
375
375
|
export declare function resolveAgentOwnerEmail(options: Pick<ProductionAgentOptions, "resolveOwnerEmail">, event: any): Promise<string | null>;
|
|
376
|
+
/**
|
|
377
|
+
* A transient database failure in the completion callback used to prevent a
|
|
378
|
+
* background continuation from ever being handed off. Keep the retry bounded
|
|
379
|
+
* and database-only so a permanent persistence error still fails loudly.
|
|
380
|
+
*/
|
|
381
|
+
export declare function runCompletionCallbackWithDatabaseRetry(callback: () => void | Promise<void>, options?: {
|
|
382
|
+
sleep?: (ms: number) => Promise<void>;
|
|
383
|
+
}): Promise<void>;
|
|
376
384
|
declare function generateRunId(): string;
|
|
377
385
|
/** Check if an error is transient and should be retried
|
|
378
386
|
* @internal exported for unit tests only
|
|
@@ -5,7 +5,7 @@ import { parseA2AAgentActivityPart } from "../a2a/activity.js";
|
|
|
5
5
|
import { describeToolParameterSignature, isAgentActionStopError, } from "../action.js";
|
|
6
6
|
import { readAppState } from "../application-state/script-helpers.js";
|
|
7
7
|
import { isReadOnlyShellCommand } from "../coding-tools/index.js";
|
|
8
|
-
import { getDbExec } from "../db/client.js";
|
|
8
|
+
import { getDbExec, isTransientDatabaseError } from "../db/client.js";
|
|
9
9
|
import { extensionIdFromPathname } from "../extensions/path.js";
|
|
10
10
|
import { preUploadAttachments } from "../file-upload/pre-upload-attachments.js";
|
|
11
11
|
import { isMcpActionResult } from "../mcp-client/app-result.js";
|
|
@@ -620,6 +620,29 @@ export async function resolveAgentOwnerEmail(options, event) {
|
|
|
620
620
|
return ownerEmail ?? getRequestUserEmail() ?? null;
|
|
621
621
|
}
|
|
622
622
|
const MAX_RETRIES = 3;
|
|
623
|
+
const COMPLETION_DATABASE_RETRY_DELAYS_MS = [250, 750, 1_500];
|
|
624
|
+
/**
|
|
625
|
+
* A transient database failure in the completion callback used to prevent a
|
|
626
|
+
* background continuation from ever being handed off. Keep the retry bounded
|
|
627
|
+
* and database-only so a permanent persistence error still fails loudly.
|
|
628
|
+
*/
|
|
629
|
+
export async function runCompletionCallbackWithDatabaseRetry(callback, options) {
|
|
630
|
+
const sleep = options?.sleep ??
|
|
631
|
+
((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
632
|
+
for (let attempt = 0;; attempt += 1) {
|
|
633
|
+
try {
|
|
634
|
+
await callback();
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
catch (error) {
|
|
638
|
+
const retryDelay = COMPLETION_DATABASE_RETRY_DELAYS_MS[attempt];
|
|
639
|
+
if (!isTransientDatabaseError(error) || retryDelay === undefined) {
|
|
640
|
+
throw error;
|
|
641
|
+
}
|
|
642
|
+
await sleep(retryDelay);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
623
646
|
/**
|
|
624
647
|
* Retry budget override for `builder_gateway_error` — the no-detail Builder
|
|
625
648
|
* gateway fallback. Production data shows this code is almost never
|
|
@@ -6981,7 +7004,7 @@ export function createProductionAgentHandler(options) {
|
|
|
6981
7004
|
const baseHandleRunComplete = options.onRunComplete || trackedProgressRunId
|
|
6982
7005
|
? async (run) => {
|
|
6983
7006
|
try {
|
|
6984
|
-
await options.onRunComplete?.(run, threadId);
|
|
7007
|
+
await runCompletionCallbackWithDatabaseRetry(() => options.onRunComplete?.(run, threadId));
|
|
6985
7008
|
}
|
|
6986
7009
|
catch (err) {
|
|
6987
7010
|
await completeTrackedProgressRun(run, err);
|
|
@@ -448,6 +448,11 @@ function terminalEventForcesErroredStatus(event) {
|
|
|
448
448
|
return event?.type === "error" || event?.type === "missing_api_key";
|
|
449
449
|
}
|
|
450
450
|
function terminalReasonForRun(finalStatus, terminalEvent, abortReason, completionError) {
|
|
451
|
+
if (finalStatus !== "aborted" &&
|
|
452
|
+
completionError &&
|
|
453
|
+
terminalEvent?.type === "auto_continue") {
|
|
454
|
+
return "completion_error";
|
|
455
|
+
}
|
|
451
456
|
if (terminalEvent?.type === "auto_continue") {
|
|
452
457
|
return terminalEvent.reason || "auto_continue";
|
|
453
458
|
}
|
|
@@ -1295,13 +1300,18 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
|
|
|
1295
1300
|
: terminalEventForCompletion?.event.type === "error" ||
|
|
1296
1301
|
terminalEventForCompletion?.event.type === "missing_api_key"
|
|
1297
1302
|
? terminalEventForCompletion.event
|
|
1298
|
-
: terminalEventForCompletion?.event.type === "auto_continue"
|
|
1303
|
+
: terminalEventForCompletion?.event.type === "auto_continue" &&
|
|
1304
|
+
run.continuationTerminalEvent
|
|
1299
1305
|
? // The run was checkpointed at a soft-timeout/loop boundary and
|
|
1300
1306
|
// is recoverable: the partial turn is in agent_run_events and
|
|
1301
|
-
// the continuation run will re-attempt the
|
|
1307
|
+
// the handed-off continuation run will re-attempt the
|
|
1308
|
+
// thread_data save.
|
|
1302
1309
|
// Even though the completion save failed (finalStatus stays
|
|
1303
1310
|
// "errored" for SQL/diagnostics), re-emit the auto_continue so
|
|
1304
|
-
// the client resumes instead of seeing a dead chat.
|
|
1311
|
+
// the client resumes instead of seeing a dead chat. A
|
|
1312
|
+
// pending auto_continue without this handoff marker is not
|
|
1313
|
+
// recoverable: advertising it makes the client poll a dead
|
|
1314
|
+
// run until it reports background_run_lost.
|
|
1305
1315
|
terminalEventForCompletion.event
|
|
1306
1316
|
: {
|
|
1307
1317
|
type: "error",
|
|
@@ -22,7 +22,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
22
22
|
* <AgentChatSurface mode="page" className="h-screen" />
|
|
23
23
|
*/
|
|
24
24
|
import { Tooltip as DesignSystemTooltip } from "@agent-native/toolkit/design-system";
|
|
25
|
-
import { IconMessageCircle, IconMessageDots, IconTerminal2, IconSettings, IconLayoutSidebarRightCollapse, IconLayoutGrid, IconCheck, IconPlus, IconX, IconDotsVertical, IconHistory, IconArrowsHorizontal, IconArrowsMaximize, IconExternalLink, } from "@tabler/icons-react";
|
|
25
|
+
import { IconMessageCircle, IconMessageDots, IconTerminal2, IconSettings, IconLayoutSidebarRightCollapse, IconLayoutGrid, IconCheck, IconPlus, IconX, IconDotsVertical, IconHistory, IconArrowsHorizontal, IconArrowsMaximize, IconExternalLink, IconShare3, } from "@tabler/icons-react";
|
|
26
26
|
import React, { useState, useEffect, useRef, useCallback, useMemo, lazy, Suspense, startTransition, } from "react";
|
|
27
27
|
import { flushSync } from "react-dom";
|
|
28
28
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger, } from "./components/ui/dropdown-menu.js";
|
|
@@ -666,6 +666,7 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
|
|
|
666
666
|
: "text-muted-foreground hover:bg-accent/50 hover:text-foreground"), style: AGENT_PANEL_CONTROL_STYLE, children: [_jsx(IconLayoutGrid, { size: 14 }), t("agentPanel.workspace")] }), content: t("agentPanel.workspaceMode"), delayMs: 200 })] })), [codeAccessEnabled, codeUnavailableDescription, showCliMode, t]);
|
|
667
667
|
const [headerMenuOpen, setHeaderMenuOpen] = useState(false);
|
|
668
668
|
const [feedbackOpen, setFeedbackOpen] = useState(false);
|
|
669
|
+
const [shareFromMenuOpen, setShareFromMenuOpen] = useState(false);
|
|
669
670
|
const getChatThreadShareUrl = useCallback((threadId) => {
|
|
670
671
|
if (typeof window === "undefined")
|
|
671
672
|
return undefined;
|
|
@@ -700,7 +701,7 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
|
|
|
700
701
|
document.addEventListener("keydown", handleKeyDown);
|
|
701
702
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
702
703
|
}, [mode, onCollapse, wideDrawerAction]);
|
|
703
|
-
const renderHeaderActions = useCallback(({ activeChatSessionId, activeTabId, activeTabMessageCount, addTab, clearActiveTab, closeAllTabs, closeOtherTabs, closeTab, showHistory, tabs, toggleHistory, }) => (_jsxs("div", { className: "relative flex shrink-0 items-center gap-0.5", children: [!onCollapse && SHOW_ONBOARDING && (_jsx(Suspense, { fallback: null, children: _jsx(SetupButton, {}) })), !onCollapse &&
|
|
704
|
+
const renderHeaderActions = useCallback(({ activeChatSessionId, activeTabId, activeTabMessageCount, addTab, clearActiveTab, closeAllTabs, closeOtherTabs, closeTab, showHistory, tabs, toggleHistory, }) => (_jsxs("div", { className: "relative flex shrink-0 items-center gap-0.5", children: [!onCollapse && SHOW_ONBOARDING && (_jsx(Suspense, { fallback: null, children: _jsx(SetupButton, {}) })), (!onCollapse || shareFromMenuOpen) &&
|
|
704
705
|
(() => {
|
|
705
706
|
const activeTab = mode === "chat" && activeChatSessionId
|
|
706
707
|
? tabs.find((tab) => tab.id === activeChatSessionId)
|
|
@@ -709,7 +710,7 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
|
|
|
709
710
|
(activeTabMessageCount <= 0 && activeTab.status === "idle")) {
|
|
710
711
|
return null;
|
|
711
712
|
}
|
|
712
|
-
return (_jsx(ShareButton, { resourceType: "chat_thread", resourceId: activeTab.id, allowedRoles: ["viewer", "editor", "admin"], resourceTitle: activeTab.label || "Chat", shareUrl: getChatThreadShareUrl(activeTab.id), trigger: "icon", triggerClassName: "h-7 w-7" }));
|
|
713
|
+
return (_jsx(ShareButton, { resourceType: "chat_thread", resourceId: activeTab.id, allowedRoles: ["viewer", "editor", "admin"], resourceTitle: activeTab.label || "Chat", shareUrl: getChatThreadShareUrl(activeTab.id), trigger: "icon", triggerClassName: "h-7 w-7", defaultOpen: onCollapse && shareFromMenuOpen, onOpenChange: onCollapse ? setShareFromMenuOpen : undefined }));
|
|
713
714
|
})(), feedbackEnabled ? (_jsx(FeedbackButton, { variant: "icon", side: "bottom", align: "end", chatSessionId: activeChatSessionId, chatStorageKey: storageKey, open: feedbackOpen, onOpenChange: setFeedbackOpen, trigger: _jsx("button", { type: "button", tabIndex: -1, "aria-hidden": "true", className: "pointer-events-none absolute end-0 top-full h-px w-px opacity-0" }) })) : null, mode === "chat" && (_jsx(IconTooltip, { content: t("agentPanel.newChat"), children: _jsx("button", { onClick: addTab, "aria-label": t("agentPanel.newChat"), className: "flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50", children: _jsx(IconPlus, { size: 14 }) }) })), !onCollapse && mode === "cli" && canUseCodeTools && (_jsx(IconTooltip, { content: t("agentPanel.newTerminal"), children: _jsx("button", { onClick: addCliTab, "aria-label": t("agentPanel.newTerminal"), className: "flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50", children: _jsx(IconPlus, { size: 14 }) }) })), _jsxs(DropdownMenu, { open: headerMenuOpen, onOpenChange: setHeaderMenuOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { className: cn("flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50", (headerMenuOpen || mode === "settings") &&
|
|
714
715
|
"bg-accent text-foreground"), "aria-label": t("agentPanel.panelOptions"), children: _jsx(IconDotsVertical, { size: 14 }) }) }), _jsxs(DropdownMenuContent, { align: "end", sideOffset: 6, className: "w-48", children: [onCollapse && (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuItem, { onSelect: onCollapse, children: [_jsx(IconLayoutSidebarRightCollapse, { size: 14, className: "shrink-0" }), t("agentPanel.collapseSidebar"), _jsx(DropdownMenuShortcut, { children: toggleSidebarHint })] }), _jsx(DropdownMenuSeparator, {})] })), onCollapse && mode === "chat" && wideDrawerAction ? (_jsxs(DropdownMenuItem, { onSelect: wideDrawerAction, children: [_jsx(IconArrowsHorizontal, { size: 14, className: "shrink-0" }), wideDrawerLabel, _jsx(DropdownMenuShortcut, { children: widenChatHint })] })) : null, fullViewAction?.kind === "callback" && onFullViewRequest ? (_jsxs(DropdownMenuItem, { onSelect: onFullViewRequest, "aria-label": t("agentPanel.openFullView"), children: [_jsx(IconArrowsMaximize, { size: 14, className: "shrink-0" }), t("agentPanel.openFullView")] })) : fullViewAction?.kind === "link" ? (_jsx(DropdownMenuItem, { asChild: true, children: _jsxs(Link, { to: fullViewAction.href, "aria-label": t("agentPanel.openFullView"), children: [_jsx(IconArrowsMaximize, { size: 14, className: "shrink-0" }), t("agentPanel.openFullView")] }) })) : null, (onCollapse && mode === "chat" && wideDrawerAction) ||
|
|
715
716
|
fullViewAction ? (_jsx(DropdownMenuSeparator, {})) : null, onCollapse && mode === "chat" && (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuItem, { onSelect: addTab, children: [_jsx(IconPlus, { size: 14, className: "shrink-0" }), t("agentPanel.newChat")] }), (() => {
|
|
@@ -720,7 +721,7 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
|
|
|
720
721
|
(activeTabMessageCount <= 0 && activeTab.status === "idle")) {
|
|
721
722
|
return null;
|
|
722
723
|
}
|
|
723
|
-
return (
|
|
724
|
+
return (_jsxs(DropdownMenuItem, { onSelect: (event) => deferAgentPanelOverlayOpen(event, () => setHeaderMenuOpen(false), () => setShareFromMenuOpen(true)), children: [_jsx(IconShare3, { size: 14, className: "shrink-0" }), "Share"] }));
|
|
724
725
|
})()] })), mode === "chat" && toggleHistory && (_jsxs(DropdownMenuItem, { onSelect: (event) => deferAgentPanelOverlayOpen(event, () => setHeaderMenuOpen(false), toggleHistory), children: [_jsx(IconHistory, { size: 14, className: "shrink-0" }), showHistory
|
|
725
726
|
? t("agentPanel.hideChats")
|
|
726
727
|
: t("agentPanel.allChats")] })), mode === "chat" && (_jsx(RunsTrayMenuItem, { pollMs: 0, limit: 12, showRecent: true, onOpenThread: openRunThread })), mode === "chat" && _jsx(DropdownMenuSeparator, {}), mode === "cli" && availableClis.length > 0 && (_jsxs(_Fragment, { children: [availableClis.map((cli) => (_jsxs(DropdownMenuItem, { onSelect: () => selectCli(cli.command), className: cn(cli.command === selectedCli
|
|
@@ -752,6 +753,7 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
|
|
|
752
753
|
openRunThread,
|
|
753
754
|
selectCli,
|
|
754
755
|
selectedCli,
|
|
756
|
+
shareFromMenuOpen,
|
|
755
757
|
storageKey,
|
|
756
758
|
switchMode,
|
|
757
759
|
t,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { IconExternalLink } from "@tabler/icons-react";
|
|
2
|
+
import { IconAlertTriangle, IconExternalLink } from "@tabler/icons-react";
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { isLocalDevelopmentOrigin, shouldOfferWorkspace, } from "../../org/workspace-url.js";
|
|
5
5
|
import { useOrg } from "./hooks.js";
|
|
6
|
+
import { isWorkspaceAppEnvironment } from "./workspace-app-links.js";
|
|
6
7
|
const DISMISS_KEY_PREFIX = "agent-native:workspace-notice-dismissed:";
|
|
7
8
|
function dismissKey(workspaceUrl) {
|
|
8
9
|
return `${DISMISS_KEY_PREFIX}${workspaceUrl}`;
|
|
@@ -38,14 +39,16 @@ export function WorkspaceNotice({ className }) {
|
|
|
38
39
|
const workspaceUrl = org?.workspaceUrl ?? null;
|
|
39
40
|
if (typeof window === "undefined")
|
|
40
41
|
return null;
|
|
41
|
-
if (isLocalDevelopmentOrigin(window.location.href)
|
|
42
|
+
if (isLocalDevelopmentOrigin(window.location.href) ||
|
|
43
|
+
isWorkspaceAppEnvironment()) {
|
|
42
44
|
return null;
|
|
45
|
+
}
|
|
43
46
|
if (!workspaceUrl || dismissedNow || readDismissed(workspaceUrl))
|
|
44
47
|
return null;
|
|
45
48
|
if (!shouldOfferWorkspace(window.location.href, workspaceUrl))
|
|
46
49
|
return null;
|
|
47
50
|
const workspaceHost = new URL(workspaceUrl).host;
|
|
48
|
-
return (_jsx("div", { className: `border-b border-
|
|
51
|
+
return (_jsx("div", { role: "status", "aria-live": "polite", className: `border-b border-amber-500/30 bg-amber-500/10 px-3 py-3 text-amber-900 dark:text-amber-200 sm:px-4 ${className ?? ""}`, children: _jsxs("div", { className: "flex flex-wrap items-start gap-x-3 gap-y-2 text-sm", children: [_jsx(IconAlertTriangle, { "aria-hidden": "true", className: "mt-0.5 size-4 shrink-0 text-amber-600 dark:text-amber-400" }), _jsxs("span", { className: "min-w-0 flex-1 leading-5", children: [_jsx("span", { className: "font-medium", children: org?.orgName }), " has its own workspace at ", _jsx("span", { className: "font-medium", children: workspaceHost }), ". Your team's apps live there, not here."] }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [_jsxs("a", { href: workspaceUrl, className: "inline-flex items-center gap-1.5 rounded-md border border-amber-600 bg-amber-600 px-3 py-1.5 text-xs font-medium text-primary-foreground no-underline hover:bg-amber-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: [_jsx(IconExternalLink, { className: "h-3 w-3" }), "Go there"] }), _jsx("button", { type: "button", onClick: () => {
|
|
49
52
|
try {
|
|
50
53
|
localStorage.setItem(dismissKey(workspaceUrl), "1");
|
|
51
54
|
}
|
|
@@ -53,5 +56,5 @@ export function WorkspaceNotice({ className }) {
|
|
|
53
56
|
// Private-mode storage failure only costs a repeat banner.
|
|
54
57
|
}
|
|
55
58
|
setDismissedNow(true);
|
|
56
|
-
}, className: "rounded-md border border-
|
|
59
|
+
}, className: "cursor-pointer rounded-md border border-amber-500/40 bg-background/60 px-3 py-1.5 text-xs font-medium text-amber-900 hover:bg-amber-500/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 dark:border-amber-800 dark:bg-amber-950/20 dark:text-amber-100 dark:hover:bg-amber-900/40", children: "Stay here" })] })] }) }));
|
|
57
60
|
}
|
|
@@ -29,8 +29,8 @@ export interface ShareButtonProps {
|
|
|
29
29
|
shareUrlLabel?: string;
|
|
30
30
|
/** Optional helper text for the primary copyable link section. */
|
|
31
31
|
shareUrlDescription?: ReactNode;
|
|
32
|
-
/** Where to render share links in the popover. Defaults to the
|
|
33
|
-
*
|
|
32
|
+
/** Where to render share links in the popover. Defaults to the top,
|
|
33
|
+
* keeping the copyable link as the primary share action. */
|
|
34
34
|
shareUrlPlacement?: "top" | "bottom";
|
|
35
35
|
/** Whether to render copyable share URL fields. Defaults to true. */
|
|
36
36
|
showShareLinks?: boolean;
|