@agent-native/core 0.84.24 → 0.84.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +18 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +12 -8
- package/corpus/core/src/agent/run-manager.ts +58 -6
- package/corpus/core/src/agent/types.ts +9 -2
- package/corpus/core/src/client/AssistantChat.tsx +51 -3
- package/corpus/core/src/client/FeedbackButton.tsx +2 -2
- package/corpus/core/src/client/components/PresenceBar.tsx +7 -2
- package/corpus/core/src/client/composer/ComposerPlusMenu.tsx +23 -9
- package/corpus/core/src/client/sharing/ShareButton.tsx +22 -3
- package/corpus/core/src/client/sharing/ShareDialog.tsx +13 -1
- package/corpus/core/src/client/sse-event-processor.ts +83 -26
- package/corpus/core/src/client/tool-display.ts +1 -1
- package/corpus/core/src/sharing/actions/set-resource-visibility.ts +3 -3
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +4 -2
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +9 -0
- package/corpus/templates/clips/app/i18n/ar-SA.ts +3 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +3 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +3 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +3 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +3 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +3 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +3 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +3 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +3 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +3 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/clips/changelog/2026-07-01-shared-clips-can-now-copy-a-ready-to-send-prompt-that-tells-.md +6 -0
- package/corpus/templates/design/actions/insert-asset.ts +14 -2
- package/corpus/templates/design/actions/insert-design-native-asset.ts +16 -4
- package/corpus/templates/design/actions/insert-figma-library-asset.ts +14 -2
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +14 -0
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +54 -10
- package/corpus/templates/design/app/components/design/EditPanel.tsx +23 -16
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +7 -2
- package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +9 -5
- package/corpus/templates/design/app/components/layout/Header.tsx +44 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +267 -105
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +15 -1
- package/corpus/templates/design/app/pages/Index.tsx +20 -9
- package/corpus/templates/design/app/routes/settings.tsx +1 -1
- package/corpus/templates/design/changelog/2026-07-01-design-editor-polish-makes-zoom-assets-sharing-and-agent-handoffs-clearer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-01-design-qa-fixes-make-exports-permissions-and-tools-clearer.md +6 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +12 -9
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +56 -4
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/types.d.ts +3 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +52 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/FeedbackButton.js +2 -2
- package/dist/client/FeedbackButton.js.map +1 -1
- package/dist/client/components/PresenceBar.d.ts.map +1 -1
- package/dist/client/components/PresenceBar.js +6 -2
- package/dist/client/components/PresenceBar.js.map +1 -1
- package/dist/client/composer/ComposerPlusMenu.d.ts.map +1 -1
- package/dist/client/composer/ComposerPlusMenu.js +15 -7
- package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/client/sharing/ShareButton.js +15 -2
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/sharing/ShareDialog.js +16 -1
- package/dist/client/sharing/ShareDialog.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +64 -21
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/tool-display.js +1 -1
- package/dist/client/tool-display.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/sharing/actions/set-resource-visibility.js +3 -3
- package/dist/sharing/actions/set-resource-visibility.js.map +1 -1
- package/package.json +1 -1
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.84.27
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 6fb954b: Require admin access for shareable resource visibility changes.
|
|
8
|
+
|
|
9
|
+
## 0.84.26
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- f041345: Track action-preparation progress by streamed tool-input id so parallel same-name tool calls do not hide real progress.
|
|
14
|
+
|
|
15
|
+
## 0.84.25
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- a11293f: Keep tail-resume reconnect content display-only, recover zero-byte action-prep stalls, and polish Design screen tool labels.
|
|
20
|
+
|
|
3
21
|
## 0.84.24
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.84.
|
|
3
|
+
"version": "0.84.27",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -2795,6 +2795,7 @@ export async function runAgentLoop(opts: {
|
|
|
2795
2795
|
let lastToolInputActivityAt = 0;
|
|
2796
2796
|
const sendToolInputActivity = (
|
|
2797
2797
|
toolName: string | undefined,
|
|
2798
|
+
toolInputId?: string,
|
|
2798
2799
|
progressBytes?: number,
|
|
2799
2800
|
force = false,
|
|
2800
2801
|
) => {
|
|
@@ -2810,6 +2811,7 @@ export async function runAgentLoop(opts: {
|
|
|
2810
2811
|
type: "activity",
|
|
2811
2812
|
label: toolInputActivityLabel(toolName),
|
|
2812
2813
|
...(toolName ? { tool: toolName } : {}),
|
|
2814
|
+
...(toolInputId ? { id: toolInputId } : {}),
|
|
2813
2815
|
...(typeof progressBytes === "number" ? { progressBytes } : {}),
|
|
2814
2816
|
});
|
|
2815
2817
|
};
|
|
@@ -2843,24 +2845,26 @@ export async function runAgentLoop(opts: {
|
|
|
2843
2845
|
// reasons, then collapse it when content arrives.
|
|
2844
2846
|
send({ type: "thinking", text: event.text });
|
|
2845
2847
|
} else if (event.type === "tool-input-start") {
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2848
|
+
const key = event.id ?? event.name;
|
|
2849
|
+
if (key && event.name) {
|
|
2850
|
+
toolInputNames.set(key, event.name);
|
|
2851
|
+
toolInputBytes.set(key, 0);
|
|
2849
2852
|
}
|
|
2850
|
-
sendToolInputActivity(event.name, undefined, true);
|
|
2853
|
+
sendToolInputActivity(event.name, key, undefined, true);
|
|
2851
2854
|
} else if (event.type === "tool-input-delta") {
|
|
2855
|
+
const key = event.id ?? event.name;
|
|
2852
2856
|
const toolName =
|
|
2853
2857
|
event.name ??
|
|
2854
2858
|
(event.id ? toolInputNames.get(event.id) : undefined);
|
|
2855
2859
|
let progressBytes: number | undefined;
|
|
2856
|
-
if (
|
|
2857
|
-
const previous = toolInputBytes.get(
|
|
2860
|
+
if (key) {
|
|
2861
|
+
const previous = toolInputBytes.get(key) ?? 0;
|
|
2858
2862
|
progressBytes =
|
|
2859
2863
|
previous +
|
|
2860
2864
|
new TextEncoder().encode(event.text ?? "").byteLength;
|
|
2861
|
-
toolInputBytes.set(
|
|
2865
|
+
toolInputBytes.set(key, progressBytes);
|
|
2862
2866
|
}
|
|
2863
|
-
sendToolInputActivity(toolName, progressBytes);
|
|
2867
|
+
sendToolInputActivity(toolName, key, progressBytes);
|
|
2864
2868
|
} else if (event.type === "gateway-heartbeat") {
|
|
2865
2869
|
send({ type: "stream_keepalive" });
|
|
2866
2870
|
} else if (event.type === "tool-call") {
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
bumpRunProgress,
|
|
17
17
|
reapIfStale,
|
|
18
18
|
reapUnclaimedBackgroundRun,
|
|
19
|
+
reconcileTerminalRunFromEvents,
|
|
19
20
|
ensureTerminalRunEvent,
|
|
20
21
|
setRunError,
|
|
21
22
|
setRunTerminalReason,
|
|
@@ -125,6 +126,12 @@ export const TERMINAL_RUN_RECONNECT_WINDOW_MS = 10 * 60 * 1000;
|
|
|
125
126
|
|
|
126
127
|
const PROVIDER_RATE_LIMITED_ERROR_CODE = "provider_rate_limited";
|
|
127
128
|
|
|
129
|
+
function isPreparingActionActivityEvent(event: AgentChatEvent): boolean {
|
|
130
|
+
if (event.type !== "activity") return false;
|
|
131
|
+
const label = event.label.trim().toLowerCase();
|
|
132
|
+
return label.startsWith("preparing ") && label.includes(" action");
|
|
133
|
+
}
|
|
134
|
+
|
|
128
135
|
function getRunErrorMessage(err: unknown): string {
|
|
129
136
|
if (
|
|
130
137
|
typeof err === "object" &&
|
|
@@ -423,6 +430,7 @@ export function startRun(
|
|
|
423
430
|
// chunk. The stuck-detector threshold is on the order of tens of seconds,
|
|
424
431
|
// so 1s resolution is plenty.
|
|
425
432
|
let lastProgressBumpAt = 0;
|
|
433
|
+
const preparingActivityBytes = new Map<string, number>();
|
|
426
434
|
let eventPersistenceErrorCaptured = false;
|
|
427
435
|
const bumpProgressIfDue = () => {
|
|
428
436
|
const now = Date.now();
|
|
@@ -430,6 +438,42 @@ export function startRun(
|
|
|
430
438
|
lastProgressBumpAt = now;
|
|
431
439
|
bumpRunProgress(runId).catch(() => {});
|
|
432
440
|
};
|
|
441
|
+
const shouldBumpProgressForEvent = (event: AgentChatEvent): boolean => {
|
|
442
|
+
if (event.type === "stream_keepalive") return false;
|
|
443
|
+
if (event.type === "activity" && isPreparingActionActivityEvent(event)) {
|
|
444
|
+
const toolKey =
|
|
445
|
+
event.id?.trim() || event.tool?.trim() || event.label.trim();
|
|
446
|
+
const progressBytes =
|
|
447
|
+
typeof event.progressBytes === "number" &&
|
|
448
|
+
Number.isFinite(event.progressBytes) &&
|
|
449
|
+
event.progressBytes >= 0
|
|
450
|
+
? Math.floor(event.progressBytes)
|
|
451
|
+
: undefined;
|
|
452
|
+
if (progressBytes === undefined) return false;
|
|
453
|
+
if (!event.id?.trim()) return progressBytes > 0;
|
|
454
|
+
const previousBytes = preparingActivityBytes.get(toolKey) ?? 0;
|
|
455
|
+
if (progressBytes <= previousBytes) {
|
|
456
|
+
preparingActivityBytes.set(
|
|
457
|
+
toolKey,
|
|
458
|
+
Math.max(previousBytes, progressBytes),
|
|
459
|
+
);
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
preparingActivityBytes.set(toolKey, progressBytes);
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
if (event.type === "tool_start" || event.type === "tool_done") {
|
|
466
|
+
preparingActivityBytes.clear();
|
|
467
|
+
}
|
|
468
|
+
if (
|
|
469
|
+
event.type === "clear" ||
|
|
470
|
+
event.type === "done" ||
|
|
471
|
+
event.type === "error"
|
|
472
|
+
) {
|
|
473
|
+
preparingActivityBytes.clear();
|
|
474
|
+
}
|
|
475
|
+
return true;
|
|
476
|
+
};
|
|
433
477
|
|
|
434
478
|
// Periodic SQL abort check interval (for cross-isolate abort on Workers).
|
|
435
479
|
// Also self-aborts when our row is no longer status='running' — catches the
|
|
@@ -539,8 +583,12 @@ export function startRun(
|
|
|
539
583
|
// Bump the durable progress timestamp. Distinct from the heartbeat:
|
|
540
584
|
// heartbeat = "process is up", progress = "real work is happening." The
|
|
541
585
|
// gap between them is what the client-side stuck-detector reads to tell
|
|
542
|
-
// a hung run from a healthy one.
|
|
543
|
-
|
|
586
|
+
// a hung run from a healthy one. Keepalive and zero-byte action prep are
|
|
587
|
+
// liveness only; streamed input bytes, text, and tool lifecycle events are
|
|
588
|
+
// real progress.
|
|
589
|
+
if (shouldBumpProgressForEvent(runEvent.event)) {
|
|
590
|
+
bumpProgressIfDue();
|
|
591
|
+
}
|
|
544
592
|
|
|
545
593
|
// Persist event to SQL. Events are chained through persistenceChain so
|
|
546
594
|
// inserts commit in seq order — an out-of-order commit would advance the
|
|
@@ -733,12 +781,16 @@ export function startRun(
|
|
|
733
781
|
try {
|
|
734
782
|
await insertRunPromise;
|
|
735
783
|
if (!terminalPersistenceError) {
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
finalStatus
|
|
739
|
-
|
|
784
|
+
let statusUpdated = false;
|
|
785
|
+
try {
|
|
786
|
+
statusUpdated = await updateRunStatusIfRunning(runId, finalStatus);
|
|
787
|
+
} catch {
|
|
788
|
+
statusUpdated = false;
|
|
789
|
+
}
|
|
740
790
|
if (statusUpdated) {
|
|
741
791
|
await setRunTerminalReason(runId, terminalReason);
|
|
792
|
+
} else {
|
|
793
|
+
await reconcileTerminalRunFromEvents(runId).catch(() => false);
|
|
742
794
|
}
|
|
743
795
|
}
|
|
744
796
|
} catch {
|
|
@@ -212,12 +212,19 @@ export type AgentToolInput = Record<string, unknown>;
|
|
|
212
212
|
export type AgentChatEvent =
|
|
213
213
|
| { type: "text"; text: string }
|
|
214
214
|
| { type: "thinking"; text: string }
|
|
215
|
-
| {
|
|
215
|
+
| {
|
|
216
|
+
type: "activity";
|
|
217
|
+
label: string;
|
|
218
|
+
tool?: string;
|
|
219
|
+
id?: string;
|
|
220
|
+
progressBytes?: number;
|
|
221
|
+
}
|
|
216
222
|
| { type: "stream_keepalive" }
|
|
217
|
-
| { type: "tool_start"; tool: string; input: AgentToolInput }
|
|
223
|
+
| { type: "tool_start"; tool: string; id?: string; input: AgentToolInput }
|
|
218
224
|
| {
|
|
219
225
|
type: "tool_done";
|
|
220
226
|
tool: string;
|
|
227
|
+
id?: string;
|
|
221
228
|
input?: AgentToolInput;
|
|
222
229
|
result: string;
|
|
223
230
|
isError?: boolean;
|
|
@@ -1456,6 +1456,8 @@ const AssistantChatInner = forwardRef<
|
|
|
1456
1456
|
// When stop is clicked during reconnect, keep content visible (don't wipe it)
|
|
1457
1457
|
const [reconnectFrozen, setReconnectFrozen] = useState(false);
|
|
1458
1458
|
const reconnectRunIdRef = useRef<string | null>(null);
|
|
1459
|
+
const reconnectTailOnlyRef = useRef(false);
|
|
1460
|
+
const reconnectCanMaterializeRef = useRef(false);
|
|
1459
1461
|
const reconnectAbortRef = useRef<AbortController | null>(null);
|
|
1460
1462
|
// Nuclear stop: user clicked stop. Clears the stop button/indicator AND
|
|
1461
1463
|
// lets new submissions go through immediately — prevents the "stuck
|
|
@@ -1774,6 +1776,8 @@ const AssistantChatInner = forwardRef<
|
|
|
1774
1776
|
|
|
1775
1777
|
reconnectRunIdRef.current = runId;
|
|
1776
1778
|
const afterSeq = resolveReconnectAfterSeq(threadId, runId);
|
|
1779
|
+
reconnectTailOnlyRef.current = afterSeq > 0;
|
|
1780
|
+
reconnectCanMaterializeRef.current = afterSeq === 0;
|
|
1777
1781
|
const storedActivityTool = getActiveRunActivityTool(threadId, runId);
|
|
1778
1782
|
setRunningActivityTool(storedActivityTool);
|
|
1779
1783
|
setActiveRun({
|
|
@@ -1939,10 +1943,12 @@ const AssistantChatInner = forwardRef<
|
|
|
1939
1943
|
await refreshThreadFromServer();
|
|
1940
1944
|
setReconnectContent([]);
|
|
1941
1945
|
setReconnectFrozen(false);
|
|
1946
|
+
reconnectTailOnlyRef.current = false;
|
|
1942
1947
|
} else {
|
|
1943
1948
|
settleInterruptedToolCalls(latestContent);
|
|
1944
1949
|
setReconnectContent([...latestContent]);
|
|
1945
1950
|
setReconnectFrozen(latestContent.length > 0);
|
|
1951
|
+
reconnectCanMaterializeRef.current = latestContent.length > 0;
|
|
1946
1952
|
}
|
|
1947
1953
|
setRunErrorInfo({
|
|
1948
1954
|
message:
|
|
@@ -1956,6 +1962,10 @@ const AssistantChatInner = forwardRef<
|
|
|
1956
1962
|
reconnectAbortRef.current = null;
|
|
1957
1963
|
setIsReconnecting(false);
|
|
1958
1964
|
reconnectRunIdRef.current = null;
|
|
1965
|
+
reconnectTailOnlyRef.current = false;
|
|
1966
|
+
if (afterSeq > 0) {
|
|
1967
|
+
reconnectCanMaterializeRef.current = false;
|
|
1968
|
+
}
|
|
1959
1969
|
window.dispatchEvent(
|
|
1960
1970
|
new CustomEvent("agentNative.chatRunning", {
|
|
1961
1971
|
detail: { isRunning: false, tabId: tabId || threadId },
|
|
@@ -1964,7 +1974,7 @@ const AssistantChatInner = forwardRef<
|
|
|
1964
1974
|
return;
|
|
1965
1975
|
}
|
|
1966
1976
|
|
|
1967
|
-
setReconnectFrozen(
|
|
1977
|
+
setReconnectFrozen(afterSeq === 0);
|
|
1968
1978
|
let loaded = false;
|
|
1969
1979
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
1970
1980
|
await new Promise((r) => setTimeout(r, 500));
|
|
@@ -1973,16 +1983,25 @@ const AssistantChatInner = forwardRef<
|
|
|
1973
1983
|
if (repoHasAssistantMessage(repo)) {
|
|
1974
1984
|
setReconnectContent([]);
|
|
1975
1985
|
setReconnectFrozen(false);
|
|
1986
|
+
reconnectCanMaterializeRef.current = false;
|
|
1976
1987
|
loaded = true;
|
|
1977
1988
|
break;
|
|
1978
1989
|
}
|
|
1979
1990
|
}
|
|
1980
1991
|
|
|
1981
1992
|
if (reconnectRunIdRef.current === runId) {
|
|
1993
|
+
if (afterSeq > 0) {
|
|
1994
|
+
setReconnectContent([]);
|
|
1995
|
+
setReconnectFrozen(false);
|
|
1996
|
+
}
|
|
1982
1997
|
clearActiveRunIfMatches(threadId, runId);
|
|
1983
1998
|
reconnectAbortRef.current = null;
|
|
1984
1999
|
setIsReconnecting(false);
|
|
1985
2000
|
reconnectRunIdRef.current = null;
|
|
2001
|
+
reconnectTailOnlyRef.current = false;
|
|
2002
|
+
if (loaded || afterSeq > 0 || latestContent.length === 0) {
|
|
2003
|
+
reconnectCanMaterializeRef.current = false;
|
|
2004
|
+
}
|
|
1986
2005
|
window.dispatchEvent(
|
|
1987
2006
|
new CustomEvent("agentNative.chatRunning", {
|
|
1988
2007
|
detail: { isRunning: false, tabId: tabId || threadId },
|
|
@@ -1990,7 +2009,12 @@ const AssistantChatInner = forwardRef<
|
|
|
1990
2009
|
);
|
|
1991
2010
|
}
|
|
1992
2011
|
if (!loaded) {
|
|
1993
|
-
await refreshThreadFromServer();
|
|
2012
|
+
const repo = await refreshThreadFromServer();
|
|
2013
|
+
if (afterSeq > 0 || repoHasAssistantMessage(repo)) {
|
|
2014
|
+
setReconnectContent([]);
|
|
2015
|
+
setReconnectFrozen(false);
|
|
2016
|
+
reconnectCanMaterializeRef.current = false;
|
|
2017
|
+
}
|
|
1994
2018
|
}
|
|
1995
2019
|
};
|
|
1996
2020
|
|
|
@@ -2004,6 +2028,7 @@ const AssistantChatInner = forwardRef<
|
|
|
2004
2028
|
useCallback(async (): Promise<boolean> => {
|
|
2005
2029
|
if (!threadId) return false;
|
|
2006
2030
|
try {
|
|
2031
|
+
const storedActiveRun = getActiveRun();
|
|
2007
2032
|
const runRes = await fetch(
|
|
2008
2033
|
`${apiUrl}/runs/active?threadId=${encodeURIComponent(threadId)}`,
|
|
2009
2034
|
);
|
|
@@ -2014,6 +2039,11 @@ const AssistantChatInner = forwardRef<
|
|
|
2014
2039
|
runInfo.status !== "running" ||
|
|
2015
2040
|
activeRunLooksStale(runInfo)
|
|
2016
2041
|
) {
|
|
2042
|
+
if (storedActiveRun?.threadId === threadId) {
|
|
2043
|
+
clearActiveRunIfMatches(threadId, storedActiveRun.runId);
|
|
2044
|
+
} else if (runInfo.runId) {
|
|
2045
|
+
clearActiveRunIfMatches(threadId, String(runInfo.runId));
|
|
2046
|
+
}
|
|
2017
2047
|
await refreshThreadFromServer();
|
|
2018
2048
|
return false;
|
|
2019
2049
|
}
|
|
@@ -2684,6 +2714,7 @@ const AssistantChatInner = forwardRef<
|
|
|
2684
2714
|
if (reconnectFrozen) {
|
|
2685
2715
|
setReconnectFrozen(false);
|
|
2686
2716
|
setReconnectContent([]);
|
|
2717
|
+
reconnectCanMaterializeRef.current = false;
|
|
2687
2718
|
}
|
|
2688
2719
|
if (forceStopped) {
|
|
2689
2720
|
setForceStopped(false);
|
|
@@ -2704,6 +2735,11 @@ const AssistantChatInner = forwardRef<
|
|
|
2704
2735
|
|
|
2705
2736
|
const materializeFrozenReconnectContent = useCallback(() => {
|
|
2706
2737
|
if (!reconnectFrozen || reconnectContent.length === 0) return;
|
|
2738
|
+
if (!reconnectCanMaterializeRef.current) {
|
|
2739
|
+
setReconnectFrozen(false);
|
|
2740
|
+
setReconnectContent([]);
|
|
2741
|
+
return;
|
|
2742
|
+
}
|
|
2707
2743
|
try {
|
|
2708
2744
|
const frozenContent = cloneContentParts(reconnectContent);
|
|
2709
2745
|
settleInterruptedToolCalls(frozenContent);
|
|
@@ -2744,6 +2780,7 @@ const AssistantChatInner = forwardRef<
|
|
|
2744
2780
|
threadRuntime.import(ensureMessageMetadata(repo));
|
|
2745
2781
|
setReconnectFrozen(false);
|
|
2746
2782
|
setReconnectContent([]);
|
|
2783
|
+
reconnectCanMaterializeRef.current = false;
|
|
2747
2784
|
} catch (err) {
|
|
2748
2785
|
captureError(err, {
|
|
2749
2786
|
tags: {
|
|
@@ -2792,7 +2829,18 @@ const AssistantChatInner = forwardRef<
|
|
|
2792
2829
|
reconnectAbortRef.current = null;
|
|
2793
2830
|
reconnectRunIdRef.current = null;
|
|
2794
2831
|
setIsReconnecting(false);
|
|
2795
|
-
|
|
2832
|
+
const shouldFreezeReconnectContent =
|
|
2833
|
+
!reconnectTailOnlyRef.current &&
|
|
2834
|
+
reconnectCanMaterializeRef.current &&
|
|
2835
|
+
reconnectContent.length > 0;
|
|
2836
|
+
if (shouldFreezeReconnectContent) {
|
|
2837
|
+
setReconnectFrozen(true);
|
|
2838
|
+
} else {
|
|
2839
|
+
setReconnectFrozen(false);
|
|
2840
|
+
setReconnectContent([]);
|
|
2841
|
+
reconnectCanMaterializeRef.current = false;
|
|
2842
|
+
}
|
|
2843
|
+
reconnectTailOnlyRef.current = false;
|
|
2796
2844
|
}
|
|
2797
2845
|
threadRuntime.cancelRun();
|
|
2798
2846
|
if (typeof window !== "undefined") {
|
|
@@ -429,7 +429,7 @@ export function FeedbackButton({
|
|
|
429
429
|
<TooltipPrimitive.Portal>
|
|
430
430
|
<TooltipPrimitive.Content
|
|
431
431
|
sideOffset={6}
|
|
432
|
-
className="z-[
|
|
432
|
+
className="z-[100040] overflow-hidden rounded-md border border-border bg-popover px-2 py-1 text-[11px] text-foreground shadow-md animate-in fade-in-0 zoom-in-95"
|
|
433
433
|
>
|
|
434
434
|
{resolvedLabel}
|
|
435
435
|
</TooltipPrimitive.Content>
|
|
@@ -482,7 +482,7 @@ export function FeedbackButton({
|
|
|
482
482
|
align={align}
|
|
483
483
|
sideOffset={8}
|
|
484
484
|
collisionPadding={16}
|
|
485
|
-
className="z-[
|
|
485
|
+
className="z-[100040] overflow-hidden rounded-lg border border-border bg-popover shadow-xl outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2"
|
|
486
486
|
style={surfaceStyle}
|
|
487
487
|
>
|
|
488
488
|
{submitted ? (
|
|
@@ -113,7 +113,8 @@ function UserAvatar({
|
|
|
113
113
|
: `0 0 0 2px #fff`,
|
|
114
114
|
}}
|
|
115
115
|
aria-label={`${name} (${user.email})${isFollowing ? " — following" : ""}`}
|
|
116
|
-
tabIndex={0}
|
|
116
|
+
tabIndex={onClick ? 0 : undefined}
|
|
117
|
+
role={onClick ? "button" : undefined}
|
|
117
118
|
onClick={onClick}
|
|
118
119
|
onKeyDown={(e) => {
|
|
119
120
|
if (e.key === "Enter" || e.key === " ") onClick?.();
|
|
@@ -123,7 +124,11 @@ function UserAvatar({
|
|
|
123
124
|
</div>
|
|
124
125
|
</TooltipTrigger>
|
|
125
126
|
<TooltipContent side="bottom">
|
|
126
|
-
{isFollowing
|
|
127
|
+
{isFollowing
|
|
128
|
+
? `Following ${name} — click to stop`
|
|
129
|
+
: onClick
|
|
130
|
+
? `${name} — click to follow`
|
|
131
|
+
: user.email}
|
|
127
132
|
</TooltipContent>
|
|
128
133
|
</Tooltip>
|
|
129
134
|
);
|
|
@@ -12,7 +12,13 @@ import {
|
|
|
12
12
|
IconArrowLeft,
|
|
13
13
|
IconX,
|
|
14
14
|
} from "@tabler/icons-react";
|
|
15
|
-
import React, {
|
|
15
|
+
import React, {
|
|
16
|
+
useState,
|
|
17
|
+
useRef,
|
|
18
|
+
useEffect,
|
|
19
|
+
useMemo,
|
|
20
|
+
useCallback,
|
|
21
|
+
} from "react";
|
|
16
22
|
import { createPortal } from "react-dom";
|
|
17
23
|
|
|
18
24
|
import { setAgentChatContextItem } from "../agent-chat.js";
|
|
@@ -740,6 +746,16 @@ function AssetsPickerModal({
|
|
|
740
746
|
const sourceUrl = useMemo(() => assetPickerUrl(), []);
|
|
741
747
|
const iframeUrl = useMemo(() => withEmbeddedParams(sourceUrl), [sourceUrl]);
|
|
742
748
|
const targetOrigin = useMemo(() => assetPickerOrigin(iframeUrl), [iframeUrl]);
|
|
749
|
+
const configurePicker = useCallback(() => {
|
|
750
|
+
if (!targetOrigin) return;
|
|
751
|
+
iframeRef.current?.contentWindow?.postMessage(
|
|
752
|
+
embedEnvelope("message", {
|
|
753
|
+
name: "configure",
|
|
754
|
+
payload: { mediaType: "image", count: 3 },
|
|
755
|
+
}),
|
|
756
|
+
targetOrigin,
|
|
757
|
+
);
|
|
758
|
+
}, [targetOrigin]);
|
|
743
759
|
|
|
744
760
|
useEffect(() => {
|
|
745
761
|
if (open) setPickerReady(false);
|
|
@@ -755,13 +771,7 @@ function AssetsPickerModal({
|
|
|
755
771
|
|
|
756
772
|
if (event.data.type === "ready") {
|
|
757
773
|
setPickerReady(true);
|
|
758
|
-
|
|
759
|
-
embedEnvelope("message", {
|
|
760
|
-
name: "configure",
|
|
761
|
-
payload: { mediaType: "image", count: 3 },
|
|
762
|
-
}),
|
|
763
|
-
targetOrigin,
|
|
764
|
-
);
|
|
774
|
+
configurePicker();
|
|
765
775
|
return;
|
|
766
776
|
}
|
|
767
777
|
|
|
@@ -801,7 +811,7 @@ function AssetsPickerModal({
|
|
|
801
811
|
window.removeEventListener("message", handleMessage);
|
|
802
812
|
window.removeEventListener("keydown", handleKey);
|
|
803
813
|
};
|
|
804
|
-
}, [onOpenChange, open, targetOrigin]);
|
|
814
|
+
}, [configurePicker, onOpenChange, open, targetOrigin]);
|
|
805
815
|
|
|
806
816
|
if (!open || typeof document === "undefined") return null;
|
|
807
817
|
|
|
@@ -846,6 +856,10 @@ function AssetsPickerModal({
|
|
|
846
856
|
sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-downloads"
|
|
847
857
|
allow="clipboard-read; clipboard-write; microphone; fullscreen"
|
|
848
858
|
referrerPolicy="strict-origin-when-cross-origin"
|
|
859
|
+
onLoad={() => {
|
|
860
|
+
configurePicker();
|
|
861
|
+
setPickerReady(true);
|
|
862
|
+
}}
|
|
849
863
|
/>
|
|
850
864
|
</div>
|
|
851
865
|
) : (
|
|
@@ -693,6 +693,10 @@ function SharePanel(
|
|
|
693
693
|
|
|
694
694
|
const handleVisibility = (next: Visibility) => {
|
|
695
695
|
if (next === visibility) return;
|
|
696
|
+
if (!canManage) {
|
|
697
|
+
setShareError("Only owners and admins can change access.");
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
696
700
|
setShareError(null);
|
|
697
701
|
void onVisibilityChange(next).catch((err) => {
|
|
698
702
|
setShareError(extractShareErrorMessage(err));
|
|
@@ -1025,6 +1029,15 @@ function SharePanel(
|
|
|
1025
1029
|
</div>
|
|
1026
1030
|
</div>
|
|
1027
1031
|
|
|
1032
|
+
{shareError && !canManage ? (
|
|
1033
|
+
<div
|
|
1034
|
+
role="alert"
|
|
1035
|
+
className="mb-4 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive"
|
|
1036
|
+
>
|
|
1037
|
+
{shareError}
|
|
1038
|
+
</div>
|
|
1039
|
+
) : null}
|
|
1040
|
+
|
|
1028
1041
|
{props.accessNote ? (
|
|
1029
1042
|
<div className="mb-4 rounded-md border border-border bg-muted/35 p-3 text-xs text-muted-foreground">
|
|
1030
1043
|
{props.accessNote}
|
|
@@ -1589,14 +1602,20 @@ function VisibilitySelect(props: {
|
|
|
1589
1602
|
onChange: (v: Visibility) => void;
|
|
1590
1603
|
disabled?: boolean;
|
|
1591
1604
|
visibilityCopy?: ShareButtonProps["visibilityCopy"];
|
|
1605
|
+
/** When false, the "Private" option is omitted unless currently selected. */
|
|
1606
|
+
allowPrivate?: boolean;
|
|
1592
1607
|
/** When false, the "Public" option is omitted. Default: true. */
|
|
1593
1608
|
allowPublic?: boolean;
|
|
1594
1609
|
}) {
|
|
1610
|
+
const allowPrivate = props.allowPrivate !== false;
|
|
1595
1611
|
const allowPublic = props.allowPublic !== false;
|
|
1596
1612
|
const current = visibilityMeta(props.value, props.visibilityCopy);
|
|
1597
|
-
const options = (Object.keys(VIS_META) as Visibility[]).filter(
|
|
1598
|
-
(k
|
|
1599
|
-
|
|
1613
|
+
const options = (Object.keys(VIS_META) as Visibility[]).filter((k) => {
|
|
1614
|
+
if (k === props.value) return true;
|
|
1615
|
+
if (k === "private" && !allowPrivate) return false;
|
|
1616
|
+
if (k === "public" && !allowPublic) return false;
|
|
1617
|
+
return true;
|
|
1618
|
+
});
|
|
1600
1619
|
return (
|
|
1601
1620
|
<Select.Root
|
|
1602
1621
|
value={props.value}
|
|
@@ -376,6 +376,7 @@ function LinkTab(props: {
|
|
|
376
376
|
|
|
377
377
|
const handleVisibility = (next: Visibility) => {
|
|
378
378
|
if (next === visibility) return;
|
|
379
|
+
if (!canManage) return;
|
|
379
380
|
setVisibility.mutate(
|
|
380
381
|
{ resourceType, resourceId, visibility: next } as any,
|
|
381
382
|
{ onSuccess: () => sharesQuery.refetch() },
|
|
@@ -490,6 +491,7 @@ function InviteTab(props: {
|
|
|
490
491
|
|
|
491
492
|
const handleVisibility = (next: Visibility) => {
|
|
492
493
|
if (next === visibility) return;
|
|
494
|
+
if (!canManage) return;
|
|
493
495
|
setVisibility.mutate(
|
|
494
496
|
{ resourceType, resourceId, visibility: next } as any,
|
|
495
497
|
{ onSuccess: () => sharesQuery.refetch() },
|
|
@@ -762,10 +764,20 @@ function VisibilitySelect(props: {
|
|
|
762
764
|
value: Visibility;
|
|
763
765
|
onChange: (v: Visibility) => void;
|
|
764
766
|
disabled?: boolean;
|
|
767
|
+
allowPrivate?: boolean;
|
|
768
|
+
allowPublic?: boolean;
|
|
765
769
|
}) {
|
|
766
770
|
const t = useT();
|
|
767
771
|
const visibilityMeta = useVisibilityMeta();
|
|
768
772
|
const current = visibilityMeta[props.value];
|
|
773
|
+
const allowPrivate = props.allowPrivate !== false;
|
|
774
|
+
const allowPublic = props.allowPublic !== false;
|
|
775
|
+
const options = (Object.keys(VIS_ICONS) as Visibility[]).filter((k) => {
|
|
776
|
+
if (k === props.value) return true;
|
|
777
|
+
if (k === "private" && !allowPrivate) return false;
|
|
778
|
+
if (k === "public" && !allowPublic) return false;
|
|
779
|
+
return true;
|
|
780
|
+
});
|
|
769
781
|
return (
|
|
770
782
|
<Select.Root
|
|
771
783
|
value={props.value}
|
|
@@ -792,7 +804,7 @@ function VisibilitySelect(props: {
|
|
|
792
804
|
>
|
|
793
805
|
<Select.Viewport>
|
|
794
806
|
<SelectItems
|
|
795
|
-
items={
|
|
807
|
+
items={options.map((k) => ({
|
|
796
808
|
value: k,
|
|
797
809
|
label: visibilityMeta[k].label,
|
|
798
810
|
description: visibilityMeta[k].description,
|