@agent-native/core 0.84.22 → 0.84.24
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 +14 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/model-config.ts +1 -1
- package/corpus/core/src/cli/skills.ts +17 -3
- package/corpus/core/src/client/AssistantChat.tsx +45 -12
- package/corpus/core/src/client/active-run-state.ts +22 -0
- package/corpus/core/src/client/chat/message-components.tsx +2 -1
- package/corpus/core/src/client/chat/tool-call-display.tsx +18 -2
- package/corpus/core/src/client/sse-event-processor.ts +157 -40
- package/corpus/core/src/notifications/index.ts +1 -0
- package/corpus/core/src/notifications/registry.ts +13 -1
- package/corpus/core/src/templates/default/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +15 -13
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +18 -1
- package/corpus/templates/analytics/server/db/schema.ts +1 -1
- package/corpus/templates/analytics/server/jobs/analytics-alerts.ts +4 -1
- package/corpus/templates/analytics/server/lib/analytics-alerts.ts +76 -7
- package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/clips/.agents/skills/recording/SKILL.md +30 -1
- package/corpus/templates/clips/actions/finalize-recording.ts +84 -4
- package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +256 -0
- package/corpus/templates/clips/actions/reprocess-recording.ts +160 -0
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +2 -2
- package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/clips/app/routes/embed.$shareId.tsx +31 -3
- package/corpus/templates/clips/changelog/2026-07-01-clip-embeds-now-render-as-a-clean-video-only-player-without-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-shared-clips-now-start-playing-and-scrub-instantly-instead-o.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/chrome-extension/src/github-preview-content.ts +2 -2
- package/corpus/templates/clips/chrome-extension/src/github-preview.ts +41 -31
- package/corpus/templates/clips/server/lib/video-remux.ts +222 -0
- package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +75 -19
- package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +11 -1
- package/corpus/templates/design/changelog/2026-07-01-design-chat-no-longer-shows-an-empty-state-selection-chip-in-the-composer.md +6 -0
- package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +2 -0
- package/corpus/templates/videos/.agents/skills/frontend-design/SKILL.md +2 -0
- package/dist/agent/model-config.js +1 -1
- package/dist/agent/model-config.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +17 -3
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +2 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +37 -15
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/active-run-state.d.ts +2 -0
- package/dist/client/active-run-state.d.ts.map +1 -1
- package/dist/client/active-run-state.js +17 -0
- package/dist/client/active-run-state.js.map +1 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +3 -1
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +3 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +8 -8
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +1 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +136 -35
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +1 -1
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/registry.d.ts +5 -0
- package/dist/notifications/registry.d.ts.map +1 -1
- package/dist/notifications/registry.js +4 -1
- package/dist/notifications/registry.js.map +1 -1
- package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +2 -0
- package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +2 -0
- package/package.json +1 -1
- package/src/templates/default/.agents/skills/frontend-design/SKILL.md +2 -0
- package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +2 -0
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.84.24
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d88a57f: Fix chat tool-call rows shrinking, keep running chats visibly thinking, stream reconnect content incrementally, and collapse exact repeated tool rows.
|
|
8
|
+
|
|
9
|
+
## 0.84.23
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- fde28eb: Expose notification delivery channel results so callers can distinguish custom-channel delivery from inbox persistence.
|
|
14
|
+
- fde28eb: Re-enable Claude Sonnet 5 as the visible and default Sonnet model now that the Builder gateway supports it.
|
|
15
|
+
- fde28eb: Sharpen the exported Design skill's quality bar to defeat "AI slop": frame the variant flow as separating taste/exploration/spec/code, pair every banned default with a concrete alternative, warn about second-order convergence, and add explicit guidance for building on an existing design system or codebase.
|
|
16
|
+
|
|
3
17
|
## 0.84.22
|
|
4
18
|
|
|
5
19
|
### 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.24",
|
|
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": {
|
|
@@ -120,7 +120,7 @@ export function getContextWindowForModel(modelId: string): number {
|
|
|
120
120
|
return DEFAULT_CONTEXT_WINDOW;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
const ENABLE_CLAUDE_SONNET_5 =
|
|
123
|
+
const ENABLE_CLAUDE_SONNET_5 = true;
|
|
124
124
|
|
|
125
125
|
export const CLAUDE_SONNET_MODEL_ID = ENABLE_CLAUDE_SONNET_5
|
|
126
126
|
? "claude-sonnet-5"
|
|
@@ -363,14 +363,28 @@ iteration, or a human-in-the-loop choice among design directions.
|
|
|
363
363
|
|
|
364
364
|
## Design Quality Bar
|
|
365
365
|
|
|
366
|
+
Generic "AI slop" comes from letting one prompt set taste, explore, and emit code
|
|
367
|
+
at once — so the model returns the training-average (Inter, an indigo/violet
|
|
368
|
+
gradient, a centered hero, three rounded cards). The variant flow above exists to
|
|
369
|
+
separate those jobs; use it, and hold this bar:
|
|
370
|
+
|
|
366
371
|
- Before generating, name the concrete audience, the screen's primary job, and
|
|
367
372
|
the visual thesis. If the brief is vague, make a reasonable choice and state
|
|
368
373
|
it instead of producing a generic dashboard/landing-page default.
|
|
369
|
-
-
|
|
370
|
-
|
|
374
|
+
- Refuse the defaults, and pair every "don't" with a "do" (banning Inter alone
|
|
375
|
+
just makes you reach for Roboto). Avoid Inter/Roboto/system fonts, the
|
|
376
|
+
indigo/violet slop palette (\`#6366F1\`/\`#8B5CF6\`/\`#A855F7\`) and purple-on-white
|
|
377
|
+
gradients, and centered-hero + three-icon-card layouts; instead pick a
|
|
378
|
+
distinctive font pairing, one non-default palette family with a single decisive
|
|
379
|
+
accent, and an asymmetric layout with a clear focal point.
|
|
371
380
|
- Make each direction distinct in structure and behavior, not just palette.
|
|
372
381
|
Give every variant one memorable signature choice, then keep the surrounding
|
|
373
|
-
chrome disciplined.
|
|
382
|
+
chrome disciplined. Even your creative picks converge (Space Grotesk
|
|
383
|
+
everywhere) — vary deliberately so two directions never share a fingerprint.
|
|
384
|
+
- For existing products, inspect the current screen, design system, tokens, and
|
|
385
|
+
component language before inventing a new direction. Treat any drift back to a
|
|
386
|
+
default as a missing token to pin, and vary layout per screen so on-brand does
|
|
387
|
+
not become same-in-your-colors.
|
|
374
388
|
- Treat copy, data, and imagery as design material. Use realistic domain
|
|
375
389
|
content and first-party/generated assets when images matter; avoid lorem
|
|
376
390
|
ipsum, vague SaaS filler, and decorative placeholder boxes.
|
|
@@ -37,10 +37,13 @@ import React, {
|
|
|
37
37
|
import { LLM_MISSING_CREDENTIALS_MESSAGE } from "../agent/engine/credential-errors.js";
|
|
38
38
|
import type { ReasoningEffort } from "../shared/reasoning-effort.js";
|
|
39
39
|
import {
|
|
40
|
+
ACTIVE_RUN_STATE_EVENT,
|
|
41
|
+
clearActiveRunIfMatches,
|
|
40
42
|
getActiveRunActivityTool,
|
|
41
43
|
getActiveRun,
|
|
42
44
|
resolveReconnectAfterSeq,
|
|
43
45
|
setActiveRun,
|
|
46
|
+
type ActiveRunState,
|
|
44
47
|
updateActiveRunActivity,
|
|
45
48
|
updateActiveRunSeq,
|
|
46
49
|
} from "./active-run-state.js";
|
|
@@ -282,6 +285,13 @@ export function reconnectProgressTimedOut(args: {
|
|
|
282
285
|
return args.now - args.lastProgressAt >= threshold;
|
|
283
286
|
}
|
|
284
287
|
|
|
288
|
+
function activeRunMatchesThread(
|
|
289
|
+
state: ActiveRunState | null,
|
|
290
|
+
threadId: string | undefined,
|
|
291
|
+
): boolean {
|
|
292
|
+
return Boolean(threadId && state?.threadId === threadId && state.runId);
|
|
293
|
+
}
|
|
294
|
+
|
|
285
295
|
function isAssistantUiDuplicateMessageIdError(error: unknown): boolean {
|
|
286
296
|
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
287
297
|
return (
|
|
@@ -627,15 +637,21 @@ export function resolveAssistantChatRunningState({
|
|
|
627
637
|
isReconnecting,
|
|
628
638
|
optimisticRunning,
|
|
629
639
|
isAutoResuming,
|
|
640
|
+
hasActiveServerRun,
|
|
630
641
|
}: {
|
|
631
642
|
forceStopped: boolean;
|
|
632
643
|
isRuntimeRunning: boolean;
|
|
633
644
|
isReconnecting: boolean;
|
|
634
645
|
optimisticRunning: boolean;
|
|
635
646
|
isAutoResuming: boolean;
|
|
647
|
+
hasActiveServerRun?: boolean;
|
|
636
648
|
}): { isRunning: boolean; showRunningInUI: boolean } {
|
|
637
649
|
const isRunning =
|
|
638
|
-
!forceStopped &&
|
|
650
|
+
!forceStopped &&
|
|
651
|
+
(isRuntimeRunning ||
|
|
652
|
+
isReconnecting ||
|
|
653
|
+
optimisticRunning ||
|
|
654
|
+
Boolean(hasActiveServerRun));
|
|
639
655
|
return {
|
|
640
656
|
isRunning,
|
|
641
657
|
// During auto-continuation, assistant-ui can briefly mark the message done
|
|
@@ -1440,13 +1456,33 @@ const AssistantChatInner = forwardRef<
|
|
|
1440
1456
|
// When stop is clicked during reconnect, keep content visible (don't wipe it)
|
|
1441
1457
|
const [reconnectFrozen, setReconnectFrozen] = useState(false);
|
|
1442
1458
|
const reconnectRunIdRef = useRef<string | null>(null);
|
|
1443
|
-
const [reconnectAfterSeq, setReconnectAfterSeq] = useState(0);
|
|
1444
1459
|
const reconnectAbortRef = useRef<AbortController | null>(null);
|
|
1445
1460
|
// Nuclear stop: user clicked stop. Clears the stop button/indicator AND
|
|
1446
1461
|
// lets new submissions go through immediately — prevents the "stuck
|
|
1447
1462
|
// queueing forever" state where isReconnecting or isRuntimeRunning gets
|
|
1448
1463
|
// wedged (e.g. after a tab refresh + stop during reconnect).
|
|
1449
1464
|
const [forceStopped, setForceStopped] = useState(false);
|
|
1465
|
+
const [hasActiveServerRun, setHasActiveServerRun] = useState(() =>
|
|
1466
|
+
activeRunMatchesThread(getActiveRun(), threadId),
|
|
1467
|
+
);
|
|
1468
|
+
useEffect(() => {
|
|
1469
|
+
const syncActiveRun = (state = getActiveRun()) => {
|
|
1470
|
+
setHasActiveServerRun(activeRunMatchesThread(state, threadId));
|
|
1471
|
+
};
|
|
1472
|
+
syncActiveRun();
|
|
1473
|
+
const handler = (event: Event) => {
|
|
1474
|
+
const state = (event as CustomEvent<{ state?: ActiveRunState | null }>)
|
|
1475
|
+
.detail?.state;
|
|
1476
|
+
syncActiveRun(state ?? null);
|
|
1477
|
+
};
|
|
1478
|
+
const storageHandler = () => syncActiveRun();
|
|
1479
|
+
window.addEventListener(ACTIVE_RUN_STATE_EVENT, handler);
|
|
1480
|
+
window.addEventListener("storage", storageHandler);
|
|
1481
|
+
return () => {
|
|
1482
|
+
window.removeEventListener(ACTIVE_RUN_STATE_EVENT, handler);
|
|
1483
|
+
window.removeEventListener("storage", storageHandler);
|
|
1484
|
+
};
|
|
1485
|
+
}, [threadId]);
|
|
1450
1486
|
// Real running state drives submission/queue gating; UI running also covers
|
|
1451
1487
|
// short auto-continuation gaps so the latest assistant message does not flash
|
|
1452
1488
|
// into a done state while the agent is still working.
|
|
@@ -1456,14 +1492,14 @@ const AssistantChatInner = forwardRef<
|
|
|
1456
1492
|
isReconnecting,
|
|
1457
1493
|
optimisticRunning,
|
|
1458
1494
|
isAutoResuming,
|
|
1495
|
+
hasActiveServerRun,
|
|
1459
1496
|
});
|
|
1460
1497
|
const textStreaming = showRunningInUI || externalStreaming;
|
|
1461
1498
|
// A revealed activity label wins; otherwise stay a steady "Thinking" by
|
|
1462
1499
|
// default. We only surface "Reconnecting" while we are actively replaying
|
|
1463
1500
|
// recovered content (reconnectContent populated). A bare reconnect with no
|
|
1464
|
-
// replayed content
|
|
1465
|
-
//
|
|
1466
|
-
// ongoing work, so it must read as "Thinking", never a perpetual "Working".
|
|
1501
|
+
// replayed content is normal ongoing work, so it must read as "Thinking",
|
|
1502
|
+
// never a perpetual "Working".
|
|
1467
1503
|
const runningStatusLabel = runningActivityLabel
|
|
1468
1504
|
? runningActivityLabel
|
|
1469
1505
|
: isAutoResuming
|
|
@@ -1740,7 +1776,6 @@ const AssistantChatInner = forwardRef<
|
|
|
1740
1776
|
const afterSeq = resolveReconnectAfterSeq(threadId, runId);
|
|
1741
1777
|
const storedActivityTool = getActiveRunActivityTool(threadId, runId);
|
|
1742
1778
|
setRunningActivityTool(storedActivityTool);
|
|
1743
|
-
setReconnectAfterSeq(afterSeq);
|
|
1744
1779
|
setActiveRun({
|
|
1745
1780
|
threadId,
|
|
1746
1781
|
runId,
|
|
@@ -1830,7 +1865,6 @@ const AssistantChatInner = forwardRef<
|
|
|
1830
1865
|
let rafPending = false;
|
|
1831
1866
|
let latestSnapshot: ContentPart[] = [];
|
|
1832
1867
|
const scheduleUpdate = (snapshot: ContentPart[]) => {
|
|
1833
|
-
if (afterSeq > 0) return;
|
|
1834
1868
|
latestSnapshot = snapshot;
|
|
1835
1869
|
if (rafPending) return;
|
|
1836
1870
|
rafPending = true;
|
|
@@ -1918,10 +1952,10 @@ const AssistantChatInner = forwardRef<
|
|
|
1918
1952
|
runId,
|
|
1919
1953
|
});
|
|
1920
1954
|
setDismissedRunErrorKey(null);
|
|
1955
|
+
clearActiveRunIfMatches(threadId, runId);
|
|
1921
1956
|
reconnectAbortRef.current = null;
|
|
1922
1957
|
setIsReconnecting(false);
|
|
1923
1958
|
reconnectRunIdRef.current = null;
|
|
1924
|
-
setReconnectAfterSeq(0);
|
|
1925
1959
|
window.dispatchEvent(
|
|
1926
1960
|
new CustomEvent("agentNative.chatRunning", {
|
|
1927
1961
|
detail: { isRunning: false, tabId: tabId || threadId },
|
|
@@ -1945,10 +1979,10 @@ const AssistantChatInner = forwardRef<
|
|
|
1945
1979
|
}
|
|
1946
1980
|
|
|
1947
1981
|
if (reconnectRunIdRef.current === runId) {
|
|
1982
|
+
clearActiveRunIfMatches(threadId, runId);
|
|
1948
1983
|
reconnectAbortRef.current = null;
|
|
1949
1984
|
setIsReconnecting(false);
|
|
1950
1985
|
reconnectRunIdRef.current = null;
|
|
1951
|
-
setReconnectAfterSeq(0);
|
|
1952
1986
|
window.dispatchEvent(
|
|
1953
1987
|
new CustomEvent("agentNative.chatRunning", {
|
|
1954
1988
|
detail: { isRunning: false, tabId: tabId || threadId },
|
|
@@ -2748,6 +2782,7 @@ const AssistantChatInner = forwardRef<
|
|
|
2748
2782
|
setRunErrorInfo(null);
|
|
2749
2783
|
setDismissedRunErrorKey(null);
|
|
2750
2784
|
if (runIdToAbort) {
|
|
2785
|
+
if (threadId) clearActiveRunIfMatches(threadId, runIdToAbort);
|
|
2751
2786
|
fetch(`${apiUrl}/runs/${encodeURIComponent(runIdToAbort)}/abort`, {
|
|
2752
2787
|
method: "POST",
|
|
2753
2788
|
}).catch(() => {});
|
|
@@ -2756,7 +2791,6 @@ const AssistantChatInner = forwardRef<
|
|
|
2756
2791
|
reconnectAbortRef.current?.abort();
|
|
2757
2792
|
reconnectAbortRef.current = null;
|
|
2758
2793
|
reconnectRunIdRef.current = null;
|
|
2759
|
-
setReconnectAfterSeq(0);
|
|
2760
2794
|
setIsReconnecting(false);
|
|
2761
2795
|
setReconnectFrozen(reconnectContent.length > 0);
|
|
2762
2796
|
}
|
|
@@ -3610,12 +3644,11 @@ const AssistantChatInner = forwardRef<
|
|
|
3610
3644
|
/>
|
|
3611
3645
|
)}
|
|
3612
3646
|
{(isReconnecting || reconnectFrozen) &&
|
|
3613
|
-
reconnectAfterSeq === 0 &&
|
|
3614
3647
|
reconnectContent.length > 0 && (
|
|
3615
3648
|
<ReconnectStreamMessage content={reconnectContent} />
|
|
3616
3649
|
)}
|
|
3617
3650
|
{(isReconnecting || reconnectFrozen) &&
|
|
3618
|
-
|
|
3651
|
+
reconnectContent.length === 0 &&
|
|
3619
3652
|
reconnectActivityContent.length > 0 && (
|
|
3620
3653
|
<ReconnectStreamMessage
|
|
3621
3654
|
content={reconnectActivityContent}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const STORAGE_KEY = "agent-chat-active-run";
|
|
2
|
+
export const ACTIVE_RUN_STATE_EVENT = "agent-chat:active-run-state-change";
|
|
2
3
|
|
|
3
4
|
export interface ActiveRunState {
|
|
4
5
|
threadId: string;
|
|
@@ -7,6 +8,19 @@ export interface ActiveRunState {
|
|
|
7
8
|
activityTool?: string | null;
|
|
8
9
|
}
|
|
9
10
|
|
|
11
|
+
function notifyActiveRunStateChanged(state: ActiveRunState | null): void {
|
|
12
|
+
if (
|
|
13
|
+
typeof window === "undefined" ||
|
|
14
|
+
typeof window.dispatchEvent !== "function" ||
|
|
15
|
+
typeof CustomEvent === "undefined"
|
|
16
|
+
) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
window.dispatchEvent(
|
|
20
|
+
new CustomEvent(ACTIVE_RUN_STATE_EVENT, { detail: { state } }),
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
10
24
|
function normalizeActivityTool(toolName: unknown): string | null {
|
|
11
25
|
if (typeof toolName !== "string") return null;
|
|
12
26
|
const tool = toolName.trim();
|
|
@@ -17,6 +31,7 @@ export function setActiveRun(state: ActiveRunState): void {
|
|
|
17
31
|
try {
|
|
18
32
|
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(state));
|
|
19
33
|
} catch {}
|
|
34
|
+
notifyActiveRunStateChanged(state);
|
|
20
35
|
}
|
|
21
36
|
|
|
22
37
|
export function getActiveRun(): ActiveRunState | null {
|
|
@@ -64,6 +79,13 @@ export function clearActiveRun(): void {
|
|
|
64
79
|
try {
|
|
65
80
|
sessionStorage.removeItem(STORAGE_KEY);
|
|
66
81
|
} catch {}
|
|
82
|
+
notifyActiveRunStateChanged(null);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function clearActiveRunIfMatches(threadId: string, runId: string): void {
|
|
86
|
+
const state = getActiveRun();
|
|
87
|
+
if (state?.threadId !== threadId || state.runId !== runId) return;
|
|
88
|
+
clearActiveRun();
|
|
67
89
|
}
|
|
68
90
|
|
|
69
91
|
/** Resume reconnect SSE after the last seen event (0 = replay from the start). */
|
|
@@ -807,6 +807,7 @@ export function shouldShowAssistantMessageFooter({
|
|
|
807
807
|
hasUnresolvedTool?: boolean;
|
|
808
808
|
}): boolean {
|
|
809
809
|
if (!hasRenderableContent) return false;
|
|
810
|
+
if (chatRunning) return false;
|
|
810
811
|
if (!isLast) return true;
|
|
811
812
|
if (hasUnresolvedTool) return false;
|
|
812
813
|
return !chatRunning && statusIsTerminal;
|
|
@@ -909,7 +910,7 @@ export function AssistantMessage() {
|
|
|
909
910
|
className="group relative"
|
|
910
911
|
style={{ contentVisibility: isComplete ? "auto" : "visible" }}
|
|
911
912
|
>
|
|
912
|
-
<div className="max-w-[95%] text-sm leading-relaxed text-foreground">
|
|
913
|
+
<div className="w-full max-w-[95%] text-sm leading-relaxed text-foreground">
|
|
913
914
|
<MessagePrimitive.Parts
|
|
914
915
|
components={{
|
|
915
916
|
Text: MarkdownText,
|
|
@@ -418,6 +418,7 @@ export function ToolCallDisplay({
|
|
|
418
418
|
isRunning,
|
|
419
419
|
structuredMeta,
|
|
420
420
|
approval,
|
|
421
|
+
repeatCount,
|
|
421
422
|
}: {
|
|
422
423
|
toolName: string;
|
|
423
424
|
argsText?: string;
|
|
@@ -428,6 +429,7 @@ export function ToolCallDisplay({
|
|
|
428
429
|
isRunning: boolean;
|
|
429
430
|
structuredMeta?: Record<string, unknown>;
|
|
430
431
|
approval?: { approvalKey: string; dismissed?: boolean };
|
|
432
|
+
repeatCount?: number;
|
|
431
433
|
}) {
|
|
432
434
|
// Delegate to bespoke cells when structured metadata is present.
|
|
433
435
|
// These must be separate components so hook order in ToolCallDisplayGeneric
|
|
@@ -474,6 +476,7 @@ export function ToolCallDisplay({
|
|
|
474
476
|
chatUI={chatUI}
|
|
475
477
|
isRunning={isRunning}
|
|
476
478
|
approval={approval}
|
|
479
|
+
repeatCount={repeatCount}
|
|
477
480
|
/>
|
|
478
481
|
);
|
|
479
482
|
}
|
|
@@ -487,6 +490,7 @@ function ToolCallDisplayGeneric({
|
|
|
487
490
|
chatUI,
|
|
488
491
|
isRunning,
|
|
489
492
|
approval,
|
|
493
|
+
repeatCount,
|
|
490
494
|
}: {
|
|
491
495
|
toolName: string;
|
|
492
496
|
argsText?: string;
|
|
@@ -496,6 +500,7 @@ function ToolCallDisplayGeneric({
|
|
|
496
500
|
chatUI?: ActionChatUIConfig;
|
|
497
501
|
isRunning: boolean;
|
|
498
502
|
approval?: { approvalKey: string; dismissed?: boolean };
|
|
503
|
+
repeatCount?: number;
|
|
499
504
|
}) {
|
|
500
505
|
const streamRef = useRef<HTMLDivElement>(null);
|
|
501
506
|
|
|
@@ -614,7 +619,7 @@ function ToolCallDisplayGeneric({
|
|
|
614
619
|
const isExpanded = isAgentCall ? hasStreamText && expanded : expanded;
|
|
615
620
|
|
|
616
621
|
return (
|
|
617
|
-
<div className="my-1 overflow-hidden">
|
|
622
|
+
<div className="my-1 w-full overflow-hidden">
|
|
618
623
|
{mcpApp && <McpAppRenderer app={mcpApp} className="mb-1.5" />}
|
|
619
624
|
<button
|
|
620
625
|
onClick={() => canExpand && setExpanded(!isExpanded)}
|
|
@@ -640,6 +645,14 @@ function ToolCallDisplayGeneric({
|
|
|
640
645
|
<span className="truncate min-w-0">
|
|
641
646
|
<span className="font-medium">{displayName}</span>
|
|
642
647
|
</span>
|
|
648
|
+
{repeatCount && repeatCount > 1 && (
|
|
649
|
+
<span
|
|
650
|
+
className="shrink-0 rounded border border-border/60 px-1.5 py-0.5 text-[10px] leading-none text-muted-foreground"
|
|
651
|
+
title={`Repeated ${repeatCount} times`}
|
|
652
|
+
>
|
|
653
|
+
{repeatCount}x
|
|
654
|
+
</span>
|
|
655
|
+
)}
|
|
643
656
|
{canExpand && (
|
|
644
657
|
<IconChevronDown
|
|
645
658
|
className={cn(
|
|
@@ -694,6 +707,7 @@ export function ToolCallFallback({
|
|
|
694
707
|
structuredMeta?: Record<string, unknown>;
|
|
695
708
|
activity?: boolean;
|
|
696
709
|
approval?: { approvalKey: string; dismissed?: boolean };
|
|
710
|
+
repeatCount?: number;
|
|
697
711
|
}) {
|
|
698
712
|
const chatRunning = React.useContext(ChatRunningContext);
|
|
699
713
|
const isRunning =
|
|
@@ -715,6 +729,7 @@ export function ToolCallFallback({
|
|
|
715
729
|
structuredMeta={rest.structuredMeta}
|
|
716
730
|
isRunning={isRunning}
|
|
717
731
|
approval={rest.approval}
|
|
732
|
+
repeatCount={rest.repeatCount}
|
|
718
733
|
/>
|
|
719
734
|
);
|
|
720
735
|
}
|
|
@@ -734,7 +749,7 @@ export function ReconnectStreamMessage({
|
|
|
734
749
|
|
|
735
750
|
return (
|
|
736
751
|
<div className="flex justify-start">
|
|
737
|
-
<div className="max-w-[95%] text-sm leading-relaxed text-foreground space-y-1">
|
|
752
|
+
<div className="w-full max-w-[95%] text-sm leading-relaxed text-foreground space-y-1">
|
|
738
753
|
{content.map((part, i) => {
|
|
739
754
|
if (part.type === "text") {
|
|
740
755
|
const partStreaming = chatRunning && i === streamingTextPartIndex;
|
|
@@ -764,6 +779,7 @@ export function ReconnectStreamMessage({
|
|
|
764
779
|
(chatRunning || part.activity === true)
|
|
765
780
|
}
|
|
766
781
|
approval={part.approval}
|
|
782
|
+
repeatCount={part.repeatCount}
|
|
767
783
|
/>
|
|
768
784
|
);
|
|
769
785
|
}
|