@agent-native/core 0.84.25 → 0.84.28
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 +4 -2
- package/corpus/core/src/agent/run-manager.ts +4 -3
- package/corpus/core/src/agent/types.ts +9 -2
- package/corpus/core/src/client/AssistantChat.tsx +15 -1
- package/corpus/core/src/client/FeedbackButton.tsx +2 -2
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +17 -2
- package/corpus/core/src/client/agent-chat.ts +118 -0
- 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/index.ts +4 -0
- 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 +49 -20
- package/corpus/core/src/sharing/actions/set-resource-visibility.ts +3 -3
- 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 +38 -2
- package/corpus/templates/design/app/pages/DesignEditor.tsx +281 -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/corpus/templates/design/changelog/2026-07-01-zoom-menus-in-the-design-editor-now-open-independently-from-.md +6 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +4 -3
- 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 +4 -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 +16 -1
- 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/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +14 -2
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts +23 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +79 -0
- package/dist/client/agent-chat.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/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.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 +42 -16
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.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.28
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- ffd5b99: Make agent chat thread handoffs robust when opening the panel from external UI controls.
|
|
8
|
+
|
|
9
|
+
## 0.84.27
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 6fb954b: Require admin access for shareable resource visibility changes.
|
|
14
|
+
|
|
15
|
+
## 0.84.26
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- f041345: Track action-preparation progress by streamed tool-input id so parallel same-name tool calls do not hide real progress.
|
|
20
|
+
|
|
3
21
|
## 0.84.25
|
|
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.28",
|
|
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
|
};
|
|
@@ -2848,7 +2850,7 @@ export async function runAgentLoop(opts: {
|
|
|
2848
2850
|
toolInputNames.set(key, event.name);
|
|
2849
2851
|
toolInputBytes.set(key, 0);
|
|
2850
2852
|
}
|
|
2851
|
-
sendToolInputActivity(event.name, undefined, true);
|
|
2853
|
+
sendToolInputActivity(event.name, key, undefined, true);
|
|
2852
2854
|
} else if (event.type === "tool-input-delta") {
|
|
2853
2855
|
const key = event.id ?? event.name;
|
|
2854
2856
|
const toolName =
|
|
@@ -2862,7 +2864,7 @@ export async function runAgentLoop(opts: {
|
|
|
2862
2864
|
new TextEncoder().encode(event.text ?? "").byteLength;
|
|
2863
2865
|
toolInputBytes.set(key, progressBytes);
|
|
2864
2866
|
}
|
|
2865
|
-
sendToolInputActivity(toolName, progressBytes);
|
|
2867
|
+
sendToolInputActivity(toolName, key, progressBytes);
|
|
2866
2868
|
} else if (event.type === "gateway-heartbeat") {
|
|
2867
2869
|
send({ type: "stream_keepalive" });
|
|
2868
2870
|
} else if (event.type === "tool-call") {
|
|
@@ -441,7 +441,8 @@ export function startRun(
|
|
|
441
441
|
const shouldBumpProgressForEvent = (event: AgentChatEvent): boolean => {
|
|
442
442
|
if (event.type === "stream_keepalive") return false;
|
|
443
443
|
if (event.type === "activity" && isPreparingActionActivityEvent(event)) {
|
|
444
|
-
const toolKey =
|
|
444
|
+
const toolKey =
|
|
445
|
+
event.id?.trim() || event.tool?.trim() || event.label.trim();
|
|
445
446
|
const progressBytes =
|
|
446
447
|
typeof event.progressBytes === "number" &&
|
|
447
448
|
Number.isFinite(event.progressBytes) &&
|
|
@@ -449,6 +450,7 @@ export function startRun(
|
|
|
449
450
|
? Math.floor(event.progressBytes)
|
|
450
451
|
: undefined;
|
|
451
452
|
if (progressBytes === undefined) return false;
|
|
453
|
+
if (!event.id?.trim()) return progressBytes > 0;
|
|
452
454
|
const previousBytes = preparingActivityBytes.get(toolKey) ?? 0;
|
|
453
455
|
if (progressBytes <= previousBytes) {
|
|
454
456
|
preparingActivityBytes.set(
|
|
@@ -461,8 +463,7 @@ export function startRun(
|
|
|
461
463
|
return true;
|
|
462
464
|
}
|
|
463
465
|
if (event.type === "tool_start" || event.type === "tool_done") {
|
|
464
|
-
|
|
465
|
-
if (tool) preparingActivityBytes.delete(tool);
|
|
466
|
+
preparingActivityBytes.clear();
|
|
466
467
|
}
|
|
467
468
|
if (
|
|
468
469
|
event.type === "clear" ||
|
|
@@ -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,7 @@ 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);
|
|
1459
1460
|
const reconnectCanMaterializeRef = useRef(false);
|
|
1460
1461
|
const reconnectAbortRef = useRef<AbortController | null>(null);
|
|
1461
1462
|
// Nuclear stop: user clicked stop. Clears the stop button/indicator AND
|
|
@@ -1775,6 +1776,7 @@ const AssistantChatInner = forwardRef<
|
|
|
1775
1776
|
|
|
1776
1777
|
reconnectRunIdRef.current = runId;
|
|
1777
1778
|
const afterSeq = resolveReconnectAfterSeq(threadId, runId);
|
|
1779
|
+
reconnectTailOnlyRef.current = afterSeq > 0;
|
|
1778
1780
|
reconnectCanMaterializeRef.current = afterSeq === 0;
|
|
1779
1781
|
const storedActivityTool = getActiveRunActivityTool(threadId, runId);
|
|
1780
1782
|
setRunningActivityTool(storedActivityTool);
|
|
@@ -1941,6 +1943,7 @@ const AssistantChatInner = forwardRef<
|
|
|
1941
1943
|
await refreshThreadFromServer();
|
|
1942
1944
|
setReconnectContent([]);
|
|
1943
1945
|
setReconnectFrozen(false);
|
|
1946
|
+
reconnectTailOnlyRef.current = false;
|
|
1944
1947
|
} else {
|
|
1945
1948
|
settleInterruptedToolCalls(latestContent);
|
|
1946
1949
|
setReconnectContent([...latestContent]);
|
|
@@ -1959,6 +1962,7 @@ const AssistantChatInner = forwardRef<
|
|
|
1959
1962
|
reconnectAbortRef.current = null;
|
|
1960
1963
|
setIsReconnecting(false);
|
|
1961
1964
|
reconnectRunIdRef.current = null;
|
|
1965
|
+
reconnectTailOnlyRef.current = false;
|
|
1962
1966
|
if (afterSeq > 0) {
|
|
1963
1967
|
reconnectCanMaterializeRef.current = false;
|
|
1964
1968
|
}
|
|
@@ -1994,6 +1998,7 @@ const AssistantChatInner = forwardRef<
|
|
|
1994
1998
|
reconnectAbortRef.current = null;
|
|
1995
1999
|
setIsReconnecting(false);
|
|
1996
2000
|
reconnectRunIdRef.current = null;
|
|
2001
|
+
reconnectTailOnlyRef.current = false;
|
|
1997
2002
|
if (loaded || afterSeq > 0 || latestContent.length === 0) {
|
|
1998
2003
|
reconnectCanMaterializeRef.current = false;
|
|
1999
2004
|
}
|
|
@@ -2023,6 +2028,7 @@ const AssistantChatInner = forwardRef<
|
|
|
2023
2028
|
useCallback(async (): Promise<boolean> => {
|
|
2024
2029
|
if (!threadId) return false;
|
|
2025
2030
|
try {
|
|
2031
|
+
const storedActiveRun = getActiveRun();
|
|
2026
2032
|
const runRes = await fetch(
|
|
2027
2033
|
`${apiUrl}/runs/active?threadId=${encodeURIComponent(threadId)}`,
|
|
2028
2034
|
);
|
|
@@ -2033,6 +2039,11 @@ const AssistantChatInner = forwardRef<
|
|
|
2033
2039
|
runInfo.status !== "running" ||
|
|
2034
2040
|
activeRunLooksStale(runInfo)
|
|
2035
2041
|
) {
|
|
2042
|
+
if (storedActiveRun?.threadId === threadId) {
|
|
2043
|
+
clearActiveRunIfMatches(threadId, storedActiveRun.runId);
|
|
2044
|
+
} else if (runInfo.runId) {
|
|
2045
|
+
clearActiveRunIfMatches(threadId, String(runInfo.runId));
|
|
2046
|
+
}
|
|
2036
2047
|
await refreshThreadFromServer();
|
|
2037
2048
|
return false;
|
|
2038
2049
|
}
|
|
@@ -2819,7 +2830,9 @@ const AssistantChatInner = forwardRef<
|
|
|
2819
2830
|
reconnectRunIdRef.current = null;
|
|
2820
2831
|
setIsReconnecting(false);
|
|
2821
2832
|
const shouldFreezeReconnectContent =
|
|
2822
|
-
|
|
2833
|
+
!reconnectTailOnlyRef.current &&
|
|
2834
|
+
reconnectCanMaterializeRef.current &&
|
|
2835
|
+
reconnectContent.length > 0;
|
|
2823
2836
|
if (shouldFreezeReconnectContent) {
|
|
2824
2837
|
setReconnectFrozen(true);
|
|
2825
2838
|
} else {
|
|
@@ -2827,6 +2840,7 @@ const AssistantChatInner = forwardRef<
|
|
|
2827
2840
|
setReconnectContent([]);
|
|
2828
2841
|
reconnectCanMaterializeRef.current = false;
|
|
2829
2842
|
}
|
|
2843
|
+
reconnectTailOnlyRef.current = false;
|
|
2830
2844
|
}
|
|
2831
2845
|
threadRuntime.cancelRun();
|
|
2832
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 ? (
|
|
@@ -26,7 +26,9 @@ import {
|
|
|
26
26
|
AGENT_CHAT_REMOVE_CONTEXT_MESSAGE_TYPE,
|
|
27
27
|
AGENT_CHAT_SET_CONTEXT_MESSAGE_TYPE,
|
|
28
28
|
appendAgentChatContextToMessage,
|
|
29
|
+
claimAgentChatOpenRequest,
|
|
29
30
|
claimAgentChatSubmit,
|
|
31
|
+
drainBufferedAgentChatOpenRequests,
|
|
30
32
|
drainBufferedAgentChatSubmits,
|
|
31
33
|
normalizeAgentChatContextItem,
|
|
32
34
|
parseSubmitChatMessage,
|
|
@@ -2087,11 +2089,12 @@ export function MultiTabAssistantChat({
|
|
|
2087
2089
|
useEffect(() => {
|
|
2088
2090
|
const handleOpenThread = (event: Event) => {
|
|
2089
2091
|
const detail = (event as CustomEvent).detail as
|
|
2090
|
-
| { threadId?: unknown; newThread?: unknown }
|
|
2092
|
+
| { threadId?: unknown; newThread?: unknown; openRequestId?: unknown }
|
|
2091
2093
|
| undefined;
|
|
2092
2094
|
const threadId =
|
|
2093
2095
|
typeof detail?.threadId === "string" ? detail.threadId : "";
|
|
2094
|
-
if (!threadId) return;
|
|
2096
|
+
if (!detail || !threadId) return;
|
|
2097
|
+
if (!claimAgentChatOpenRequest(detail.openRequestId)) return;
|
|
2095
2098
|
|
|
2096
2099
|
if (detail?.newThread === true) {
|
|
2097
2100
|
newThreadIds.current.add(threadId);
|
|
@@ -2143,6 +2146,7 @@ export function MultiTabAssistantChat({
|
|
|
2143
2146
|
const detail = (e as CustomEvent).detail;
|
|
2144
2147
|
const threadId = detail?.threadId;
|
|
2145
2148
|
if (!threadId) return;
|
|
2149
|
+
if (!claimAgentChatOpenRequest(detail.openRequestId)) return;
|
|
2146
2150
|
dismissedSubAgentTabsRef.current.delete(threadId);
|
|
2147
2151
|
// Prefer an explicit parent (RunsTray/background hydration knows it);
|
|
2148
2152
|
// inline task cards fall back to the active orchestrator thread.
|
|
@@ -2196,6 +2200,17 @@ export function MultiTabAssistantChat({
|
|
|
2196
2200
|
return () => window.removeEventListener("agent-task-open", handleOpenTask);
|
|
2197
2201
|
}, [openTabIds, switchThread, refreshThreads, parentMap]);
|
|
2198
2202
|
|
|
2203
|
+
// Replay thread/task opens requested before this lazy panel's listeners
|
|
2204
|
+
// attached. Live events claim their id; replay drains only unclaimed requests.
|
|
2205
|
+
useEffect(() => {
|
|
2206
|
+
const buffered = drainBufferedAgentChatOpenRequests();
|
|
2207
|
+
for (const request of buffered) {
|
|
2208
|
+
window.dispatchEvent(
|
|
2209
|
+
new CustomEvent(request.eventType, { detail: request.detail }),
|
|
2210
|
+
);
|
|
2211
|
+
}
|
|
2212
|
+
}, []);
|
|
2213
|
+
|
|
2199
2214
|
// Watch for agent-issued chat-command in application-state
|
|
2200
2215
|
const lastChatCommandRef = useRef(0);
|
|
2201
2216
|
useEffect(() => {
|
|
@@ -130,6 +130,26 @@ export interface AgentChatContextState {
|
|
|
130
130
|
updatedAt: number;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
export interface AgentChatOpenThreadRequest {
|
|
134
|
+
threadId: string;
|
|
135
|
+
newThread?: boolean;
|
|
136
|
+
openRequestId?: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface AgentChatOpenTaskRequest {
|
|
140
|
+
threadId: string;
|
|
141
|
+
parentThreadId?: string;
|
|
142
|
+
description?: string;
|
|
143
|
+
name?: string;
|
|
144
|
+
openRequestId?: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export type BufferedAgentChatOpenRequest = {
|
|
148
|
+
id: string;
|
|
149
|
+
eventType: "agent-chat:open-thread" | "agent-task-open";
|
|
150
|
+
detail: AgentChatOpenThreadRequest | AgentChatOpenTaskRequest;
|
|
151
|
+
};
|
|
152
|
+
|
|
133
153
|
export interface AgentComposerReference {
|
|
134
154
|
label: string;
|
|
135
155
|
icon?: string;
|
|
@@ -243,6 +263,14 @@ const SELF_SUBMIT_BUFFER_TTL_MS = 8000;
|
|
|
243
263
|
const bufferedSelfSubmits: BufferedSelfSubmit[] = [];
|
|
244
264
|
const claimedSubmitIds = new Set<string>();
|
|
245
265
|
|
|
266
|
+
interface BufferedOpenRequest extends BufferedAgentChatOpenRequest {
|
|
267
|
+
at: number;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const OPEN_REQUEST_BUFFER_TTL_MS = 8000;
|
|
271
|
+
const bufferedOpenRequests: BufferedOpenRequest[] = [];
|
|
272
|
+
const claimedOpenRequestIds = new Set<string>();
|
|
273
|
+
|
|
246
274
|
function pruneSelfSubmitBuffer(now: number): void {
|
|
247
275
|
for (let i = bufferedSelfSubmits.length - 1; i >= 0; i -= 1) {
|
|
248
276
|
if (now - bufferedSelfSubmits[i].at > SELF_SUBMIT_BUFFER_TTL_MS) {
|
|
@@ -261,6 +289,32 @@ function bufferSelfSubmit(data: Record<string, unknown>): void {
|
|
|
261
289
|
bufferedSelfSubmits.push({ id, data, at: now });
|
|
262
290
|
}
|
|
263
291
|
|
|
292
|
+
function pruneOpenRequestBuffer(now: number): void {
|
|
293
|
+
for (let i = bufferedOpenRequests.length - 1; i >= 0; i -= 1) {
|
|
294
|
+
if (now - bufferedOpenRequests[i].at > OPEN_REQUEST_BUFFER_TTL_MS) {
|
|
295
|
+
const [removed] = bufferedOpenRequests.splice(i, 1);
|
|
296
|
+
if (removed) claimedOpenRequestIds.delete(removed.id);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function bufferOpenRequest(
|
|
302
|
+
eventType: BufferedAgentChatOpenRequest["eventType"],
|
|
303
|
+
detail: AgentChatOpenThreadRequest | AgentChatOpenTaskRequest,
|
|
304
|
+
): BufferedOpenRequest {
|
|
305
|
+
const now = Date.now();
|
|
306
|
+
pruneOpenRequestBuffer(now);
|
|
307
|
+
const id = `open-${now}-${Math.random().toString(36).slice(2, 8)}`;
|
|
308
|
+
const entry: BufferedOpenRequest = {
|
|
309
|
+
id,
|
|
310
|
+
eventType,
|
|
311
|
+
detail: { ...detail, openRequestId: id },
|
|
312
|
+
at: now,
|
|
313
|
+
};
|
|
314
|
+
bufferedOpenRequests.push(entry);
|
|
315
|
+
return entry;
|
|
316
|
+
}
|
|
317
|
+
|
|
264
318
|
/** Unclaimed self-submit payloads, for the panel to replay once it mounts. */
|
|
265
319
|
export function drainBufferedAgentChatSubmits(): Array<
|
|
266
320
|
Record<string, unknown>
|
|
@@ -279,10 +333,28 @@ export function claimAgentChatSubmit(id: string | undefined): boolean {
|
|
|
279
333
|
return true;
|
|
280
334
|
}
|
|
281
335
|
|
|
336
|
+
/** Unclaimed open-thread/task requests, for the panel to replay once it mounts. */
|
|
337
|
+
export function drainBufferedAgentChatOpenRequests(): BufferedAgentChatOpenRequest[] {
|
|
338
|
+
pruneOpenRequestBuffer(Date.now());
|
|
339
|
+
return bufferedOpenRequests
|
|
340
|
+
.filter((entry) => !claimedOpenRequestIds.has(entry.id))
|
|
341
|
+
.map(({ id, eventType, detail }) => ({ id, eventType, detail }));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** Claim an open-thread/task request; false if already handled. Idless events pass. */
|
|
345
|
+
export function claimAgentChatOpenRequest(id: unknown): boolean {
|
|
346
|
+
if (typeof id !== "string" || !id) return true;
|
|
347
|
+
if (claimedOpenRequestIds.has(id)) return false;
|
|
348
|
+
claimedOpenRequestIds.add(id);
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
|
|
282
352
|
/** Test-only: reset the self-submit buffer and claim set. */
|
|
283
353
|
export function _resetAgentChatSubmitBufferForTests(): void {
|
|
284
354
|
bufferedSelfSubmits.length = 0;
|
|
285
355
|
claimedSubmitIds.clear();
|
|
356
|
+
bufferedOpenRequests.length = 0;
|
|
357
|
+
claimedOpenRequestIds.clear();
|
|
286
358
|
}
|
|
287
359
|
|
|
288
360
|
export function normalizeAgentChatContextItem(
|
|
@@ -638,6 +710,52 @@ function postAgentChatReferenceMessage(
|
|
|
638
710
|
}
|
|
639
711
|
}
|
|
640
712
|
|
|
713
|
+
function openAgentPanelForChat(): void {
|
|
714
|
+
if (typeof window === "undefined") return;
|
|
715
|
+
window.dispatchEvent(
|
|
716
|
+
new CustomEvent("agent-panel:set-mode", {
|
|
717
|
+
detail: { mode: "chat" },
|
|
718
|
+
}),
|
|
719
|
+
);
|
|
720
|
+
window.dispatchEvent(new CustomEvent("agent-panel:open"));
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function dispatchBufferedOpenRequest(entry: BufferedOpenRequest): void {
|
|
724
|
+
if (typeof window === "undefined") return;
|
|
725
|
+
const dispatch = () => {
|
|
726
|
+
window.dispatchEvent(
|
|
727
|
+
new CustomEvent(entry.eventType, { detail: entry.detail }),
|
|
728
|
+
);
|
|
729
|
+
};
|
|
730
|
+
setTimeout(dispatch, 0);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
export function requestAgentChatThreadOpen(
|
|
734
|
+
detail: AgentChatOpenThreadRequest,
|
|
735
|
+
): void {
|
|
736
|
+
if (typeof window === "undefined" || !detail.threadId.trim()) return;
|
|
737
|
+
openAgentPanelForChat();
|
|
738
|
+
dispatchBufferedOpenRequest(
|
|
739
|
+
bufferOpenRequest("agent-chat:open-thread", {
|
|
740
|
+
...detail,
|
|
741
|
+
threadId: detail.threadId.trim(),
|
|
742
|
+
}),
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
export function requestAgentTaskOpen(detail: AgentChatOpenTaskRequest): void {
|
|
747
|
+
if (typeof window === "undefined" || !detail.threadId.trim()) return;
|
|
748
|
+
openAgentPanelForChat();
|
|
749
|
+
const parentThreadId = detail.parentThreadId?.trim();
|
|
750
|
+
dispatchBufferedOpenRequest(
|
|
751
|
+
bufferOpenRequest("agent-task-open", {
|
|
752
|
+
...detail,
|
|
753
|
+
threadId: detail.threadId.trim(),
|
|
754
|
+
...(parentThreadId ? { parentThreadId } : {}),
|
|
755
|
+
}),
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
|
|
641
759
|
function isMcpAppChatBridgeEnabled(): boolean {
|
|
642
760
|
if (typeof window === "undefined" || window.parent === window) return false;
|
|
643
761
|
if (readEmbedMcpChatBridgeFlagFromUrl()) markEmbedMcpChatBridgeActive();
|
|
@@ -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
|
) : (
|
|
@@ -16,12 +16,16 @@ export {
|
|
|
16
16
|
normalizeAgentComposerReference,
|
|
17
17
|
refreshAgentChatContext,
|
|
18
18
|
removeAgentChatContextItem,
|
|
19
|
+
requestAgentChatThreadOpen,
|
|
20
|
+
requestAgentTaskOpen,
|
|
19
21
|
sendToAgentChat,
|
|
20
22
|
parseSubmitChatMessage,
|
|
21
23
|
setAgentChatContextItem,
|
|
22
24
|
setContextToAgentChat,
|
|
23
25
|
generateTabId,
|
|
24
26
|
type ParsedSubmitChat,
|
|
27
|
+
type AgentChatOpenTaskRequest,
|
|
28
|
+
type AgentChatOpenThreadRequest,
|
|
25
29
|
type AgentChatContextItem,
|
|
26
30
|
type AgentChatContextMessage,
|
|
27
31
|
type AgentChatContextMutationOptions,
|
|
@@ -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,
|