@agent-native/core 0.84.34 → 0.84.36
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/README.md +15 -51
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +20 -0
- package/corpus/core/docs/content/actions.mdx +17 -24
- package/corpus/core/docs/content/agent-surfaces.mdx +59 -56
- package/corpus/core/docs/content/cloneable-saas.mdx +3 -3
- package/corpus/core/docs/content/faq.mdx +4 -4
- package/corpus/core/docs/content/getting-started.mdx +246 -204
- package/corpus/core/docs/content/key-concepts.mdx +9 -9
- package/corpus/core/docs/content/locales/ar-SA/cloneable-saas.mdx +3 -3
- package/corpus/core/docs/content/locales/ar-SA/faq.mdx +4 -4
- package/corpus/core/docs/content/locales/ar-SA/getting-started.mdx +148 -214
- package/corpus/core/docs/content/locales/ar-SA/pure-agent-apps.mdx +4 -9
- package/corpus/core/docs/content/locales/ar-SA/template-chat.mdx +2 -2
- package/corpus/core/docs/content/locales/ar-SA/using-your-agent.mdx +2 -2
- package/corpus/core/docs/content/locales/de-DE/cloneable-saas.mdx +3 -3
- package/corpus/core/docs/content/locales/de-DE/faq.mdx +4 -4
- package/corpus/core/docs/content/locales/de-DE/getting-started.mdx +145 -211
- package/corpus/core/docs/content/locales/de-DE/pure-agent-apps.mdx +4 -9
- package/corpus/core/docs/content/locales/de-DE/template-chat.mdx +2 -2
- package/corpus/core/docs/content/locales/de-DE/using-your-agent.mdx +2 -2
- package/corpus/core/docs/content/locales/es-ES/cloneable-saas.mdx +3 -3
- package/corpus/core/docs/content/locales/es-ES/faq.mdx +4 -4
- package/corpus/core/docs/content/locales/es-ES/getting-started.mdx +148 -214
- package/corpus/core/docs/content/locales/es-ES/pure-agent-apps.mdx +4 -9
- package/corpus/core/docs/content/locales/es-ES/template-chat.mdx +2 -2
- package/corpus/core/docs/content/locales/es-ES/using-your-agent.mdx +2 -2
- package/corpus/core/docs/content/locales/fr-FR/cloneable-saas.mdx +2 -2
- package/corpus/core/docs/content/locales/fr-FR/faq.mdx +4 -4
- package/corpus/core/docs/content/locales/fr-FR/getting-started.mdx +146 -212
- package/corpus/core/docs/content/locales/fr-FR/pure-agent-apps.mdx +4 -9
- package/corpus/core/docs/content/locales/fr-FR/template-chat.mdx +2 -2
- package/corpus/core/docs/content/locales/fr-FR/using-your-agent.mdx +2 -2
- package/corpus/core/docs/content/locales/hi-IN/cloneable-saas.mdx +3 -3
- package/corpus/core/docs/content/locales/hi-IN/faq.mdx +4 -4
- package/corpus/core/docs/content/locales/hi-IN/getting-started.mdx +149 -215
- package/corpus/core/docs/content/locales/hi-IN/pure-agent-apps.mdx +8 -13
- package/corpus/core/docs/content/locales/hi-IN/template-chat.mdx +2 -2
- package/corpus/core/docs/content/locales/hi-IN/using-your-agent.mdx +2 -2
- package/corpus/core/docs/content/locales/ja-JP/cloneable-saas.mdx +2 -2
- package/corpus/core/docs/content/locales/ja-JP/faq.mdx +4 -4
- package/corpus/core/docs/content/locales/ja-JP/getting-started.mdx +144 -210
- package/corpus/core/docs/content/locales/ja-JP/pure-agent-apps.mdx +8 -13
- package/corpus/core/docs/content/locales/ja-JP/template-chat.mdx +2 -2
- package/corpus/core/docs/content/locales/ja-JP/using-your-agent.mdx +2 -2
- package/corpus/core/docs/content/locales/ko-KR/cloneable-saas.mdx +3 -3
- package/corpus/core/docs/content/locales/ko-KR/faq.mdx +4 -4
- package/corpus/core/docs/content/locales/ko-KR/getting-started.mdx +146 -212
- package/corpus/core/docs/content/locales/ko-KR/pure-agent-apps.mdx +8 -13
- package/corpus/core/docs/content/locales/ko-KR/template-chat.mdx +2 -2
- package/corpus/core/docs/content/locales/ko-KR/using-your-agent.mdx +2 -2
- package/corpus/core/docs/content/locales/pt-BR/cloneable-saas.mdx +3 -3
- package/corpus/core/docs/content/locales/pt-BR/faq.mdx +4 -4
- package/corpus/core/docs/content/locales/pt-BR/getting-started.mdx +145 -211
- package/corpus/core/docs/content/locales/pt-BR/pure-agent-apps.mdx +4 -9
- package/corpus/core/docs/content/locales/pt-BR/template-chat.mdx +2 -2
- package/corpus/core/docs/content/locales/pt-BR/using-your-agent.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-CN/cloneable-saas.mdx +3 -3
- package/corpus/core/docs/content/locales/zh-CN/faq.mdx +4 -4
- package/corpus/core/docs/content/locales/zh-CN/getting-started.mdx +158 -209
- package/corpus/core/docs/content/locales/zh-CN/pure-agent-apps.mdx +4 -9
- package/corpus/core/docs/content/locales/zh-CN/template-chat.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-CN/using-your-agent.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-TW/cloneable-saas.mdx +3 -3
- package/corpus/core/docs/content/locales/zh-TW/faq.mdx +4 -4
- package/corpus/core/docs/content/locales/zh-TW/getting-started.mdx +158 -212
- package/corpus/core/docs/content/locales/zh-TW/pure-agent-apps.mdx +4 -9
- package/corpus/core/docs/content/locales/zh-TW/template-chat.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-TW/using-your-agent.mdx +2 -2
- package/corpus/core/docs/content/native-chat-ui.mdx +17 -32
- package/corpus/core/docs/content/pure-agent-apps.mdx +46 -26
- package/corpus/core/docs/content/template-chat.mdx +8 -8
- package/corpus/core/docs/content/using-your-agent.mdx +2 -2
- package/corpus/core/docs/content/what-is-agent-native.mdx +6 -6
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/response-text.ts +10 -1
- package/corpus/core/src/agent/production-agent.ts +23 -20
- package/corpus/core/src/agent/run-manager.ts +46 -1
- package/corpus/core/src/agent/thread-data-builder.ts +15 -2
- package/corpus/core/src/cli/skills.ts +7 -9
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +2 -0
- package/corpus/core/src/client/RunStuckBanner.tsx +172 -18
- package/corpus/core/src/client/blocks/library/wireframe-kit.tsx +57 -9
- package/corpus/core/src/client/blocks/library/wireframe.tsx +3 -18
- package/corpus/core/src/client/chat/widgets/builtin-tool-renderers.tsx +28 -32
- package/corpus/core/src/client/code-agent-chat-adapter.ts +8 -1
- package/corpus/core/src/client/sse-event-processor.ts +21 -3
- package/corpus/core/src/code-agents/transcript-normalizer.ts +31 -0
- package/corpus/core/src/db/schema.ts +5 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +8 -2
- package/corpus/core/src/server/agent-teams.ts +24 -2
- package/corpus/core/src/styles/blocks.css +27 -6
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -4
- package/corpus/templates/analytics/changelog/2026-07-01-smoother-chat-streaming.md +6 -0
- package/corpus/templates/content/actions/list-trashed-content-databases.ts +1 -1
- package/corpus/templates/plan/.agents/skills/visual-plan/references/wireframe.md +7 -9
- package/corpus/templates/plan/.agents/skills/visual-recap/references/wireframe.md +7 -9
- package/dist/a2a/response-text.d.ts +1 -0
- package/dist/a2a/response-text.d.ts.map +1 -1
- package/dist/a2a/response-text.js +8 -2
- package/dist/a2a/response-text.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +31 -27
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +12 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +32 -1
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +15 -2
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/cli/skills.d.ts +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +7 -9
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +1 -1
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/RunStuckBanner.d.ts +13 -1
- package/dist/client/RunStuckBanner.d.ts.map +1 -1
- package/dist/client/RunStuckBanner.js +123 -16
- package/dist/client/RunStuckBanner.js.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.d.ts +6 -6
- package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.js +52 -5
- package/dist/client/blocks/library/wireframe-kit.js.map +1 -1
- package/dist/client/blocks/library/wireframe.d.ts.map +1 -1
- package/dist/client/blocks/library/wireframe.js +1 -11
- package/dist/client/blocks/library/wireframe.js.map +1 -1
- package/dist/client/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
- package/dist/client/chat/widgets/builtin-tool-renderers.js +27 -31
- package/dist/client/chat/widgets/builtin-tool-renderers.js.map +1 -1
- package/dist/client/code-agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/code-agent-chat-adapter.js +5 -1
- package/dist/client/code-agent-chat-adapter.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +21 -3
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/code-agents/transcript-normalizer.d.ts.map +1 -1
- package/dist/code-agents/transcript-normalizer.js +24 -0
- package/dist/code-agents/transcript-normalizer.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 +2 -2
- package/dist/db/schema.d.ts +2 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +3 -2
- package/dist/db/schema.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +2 -3
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-teams.d.ts.map +1 -1
- package/dist/server/agent-teams.js +17 -2
- package/dist/server/agent-teams.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/styles/blocks.css +27 -6
- package/docs/content/actions.mdx +17 -24
- package/docs/content/agent-surfaces.mdx +59 -56
- package/docs/content/cloneable-saas.mdx +3 -3
- package/docs/content/faq.mdx +4 -4
- package/docs/content/getting-started.mdx +246 -204
- package/docs/content/key-concepts.mdx +9 -9
- package/docs/content/locales/ar-SA/cloneable-saas.mdx +3 -3
- package/docs/content/locales/ar-SA/faq.mdx +4 -4
- package/docs/content/locales/ar-SA/getting-started.mdx +148 -214
- package/docs/content/locales/ar-SA/pure-agent-apps.mdx +4 -9
- package/docs/content/locales/ar-SA/template-chat.mdx +2 -2
- package/docs/content/locales/ar-SA/using-your-agent.mdx +2 -2
- package/docs/content/locales/de-DE/cloneable-saas.mdx +3 -3
- package/docs/content/locales/de-DE/faq.mdx +4 -4
- package/docs/content/locales/de-DE/getting-started.mdx +145 -211
- package/docs/content/locales/de-DE/pure-agent-apps.mdx +4 -9
- package/docs/content/locales/de-DE/template-chat.mdx +2 -2
- package/docs/content/locales/de-DE/using-your-agent.mdx +2 -2
- package/docs/content/locales/es-ES/cloneable-saas.mdx +3 -3
- package/docs/content/locales/es-ES/faq.mdx +4 -4
- package/docs/content/locales/es-ES/getting-started.mdx +148 -214
- package/docs/content/locales/es-ES/pure-agent-apps.mdx +4 -9
- package/docs/content/locales/es-ES/template-chat.mdx +2 -2
- package/docs/content/locales/es-ES/using-your-agent.mdx +2 -2
- package/docs/content/locales/fr-FR/cloneable-saas.mdx +2 -2
- package/docs/content/locales/fr-FR/faq.mdx +4 -4
- package/docs/content/locales/fr-FR/getting-started.mdx +146 -212
- package/docs/content/locales/fr-FR/pure-agent-apps.mdx +4 -9
- package/docs/content/locales/fr-FR/template-chat.mdx +2 -2
- package/docs/content/locales/fr-FR/using-your-agent.mdx +2 -2
- package/docs/content/locales/hi-IN/cloneable-saas.mdx +3 -3
- package/docs/content/locales/hi-IN/faq.mdx +4 -4
- package/docs/content/locales/hi-IN/getting-started.mdx +149 -215
- package/docs/content/locales/hi-IN/pure-agent-apps.mdx +8 -13
- package/docs/content/locales/hi-IN/template-chat.mdx +2 -2
- package/docs/content/locales/hi-IN/using-your-agent.mdx +2 -2
- package/docs/content/locales/ja-JP/cloneable-saas.mdx +2 -2
- package/docs/content/locales/ja-JP/faq.mdx +4 -4
- package/docs/content/locales/ja-JP/getting-started.mdx +144 -210
- package/docs/content/locales/ja-JP/pure-agent-apps.mdx +8 -13
- package/docs/content/locales/ja-JP/template-chat.mdx +2 -2
- package/docs/content/locales/ja-JP/using-your-agent.mdx +2 -2
- package/docs/content/locales/ko-KR/cloneable-saas.mdx +3 -3
- package/docs/content/locales/ko-KR/faq.mdx +4 -4
- package/docs/content/locales/ko-KR/getting-started.mdx +146 -212
- package/docs/content/locales/ko-KR/pure-agent-apps.mdx +8 -13
- package/docs/content/locales/ko-KR/template-chat.mdx +2 -2
- package/docs/content/locales/ko-KR/using-your-agent.mdx +2 -2
- package/docs/content/locales/pt-BR/cloneable-saas.mdx +3 -3
- package/docs/content/locales/pt-BR/faq.mdx +4 -4
- package/docs/content/locales/pt-BR/getting-started.mdx +145 -211
- package/docs/content/locales/pt-BR/pure-agent-apps.mdx +4 -9
- package/docs/content/locales/pt-BR/template-chat.mdx +2 -2
- package/docs/content/locales/pt-BR/using-your-agent.mdx +2 -2
- package/docs/content/locales/zh-CN/cloneable-saas.mdx +3 -3
- package/docs/content/locales/zh-CN/faq.mdx +4 -4
- package/docs/content/locales/zh-CN/getting-started.mdx +158 -209
- package/docs/content/locales/zh-CN/pure-agent-apps.mdx +4 -9
- package/docs/content/locales/zh-CN/template-chat.mdx +2 -2
- package/docs/content/locales/zh-CN/using-your-agent.mdx +2 -2
- package/docs/content/locales/zh-TW/cloneable-saas.mdx +3 -3
- package/docs/content/locales/zh-TW/faq.mdx +4 -4
- package/docs/content/locales/zh-TW/getting-started.mdx +158 -212
- package/docs/content/locales/zh-TW/pure-agent-apps.mdx +4 -9
- package/docs/content/locales/zh-TW/template-chat.mdx +2 -2
- package/docs/content/locales/zh-TW/using-your-agent.mdx +2 -2
- package/docs/content/native-chat-ui.mdx +17 -32
- package/docs/content/pure-agent-apps.mdx +46 -26
- package/docs/content/template-chat.mdx +8 -8
- package/docs/content/using-your-agent.mdx +2 -2
- package/docs/content/what-is-agent-native.mdx +6 -6
- package/package.json +1 -1
|
@@ -38,21 +38,119 @@ export interface RunStuckBannerProps {
|
|
|
38
38
|
* observability events (Sentry, PostHog) at the call site.
|
|
39
39
|
*/
|
|
40
40
|
onStuckStateChange?: (state: RunStuckState) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Automatically abort and retry once when the server reports a run is
|
|
43
|
+
* alive but has stopped making real progress. Manual controls remain visible
|
|
44
|
+
* as the fallback if the automatic recovery cannot clear the run.
|
|
45
|
+
*/
|
|
46
|
+
autoRetry?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Stable browser-tab/window id used to coordinate automatic retries across
|
|
49
|
+
* multiple mounted chat views for the same thread. A local id is generated
|
|
50
|
+
* when omitted.
|
|
51
|
+
*/
|
|
52
|
+
autoRetryOwnerId?: string;
|
|
41
53
|
/** Extra class on the outer container. */
|
|
42
54
|
className?: string;
|
|
43
55
|
}
|
|
44
56
|
|
|
57
|
+
const AUTO_RETRY_CLAIM_TTL_MS = 5 * 60 * 1000;
|
|
58
|
+
|
|
59
|
+
type BusyState = { type: "none" } | { type: "cancel" | "retry"; runId: string };
|
|
60
|
+
|
|
61
|
+
type MaybeLockManager = {
|
|
62
|
+
request<T>(
|
|
63
|
+
name: string,
|
|
64
|
+
options: { mode?: "exclusive" | "shared"; ifAvailable?: boolean },
|
|
65
|
+
callback: (lock: unknown) => T | Promise<T>,
|
|
66
|
+
): Promise<T>;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
function createAutoRetryOwnerId() {
|
|
70
|
+
const cryptoApi =
|
|
71
|
+
typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
|
|
72
|
+
? crypto
|
|
73
|
+
: null;
|
|
74
|
+
return cryptoApi?.randomUUID() ?? `owner-${Math.random().toString(36)}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function autoRetryClaimKey(threadId: string, runId: string) {
|
|
78
|
+
return `agent-native:stuck-auto-retry:${threadId}:${runId}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function markAutoRetryClaim(key: string, ownerId: string) {
|
|
82
|
+
if (typeof window === "undefined") return true;
|
|
83
|
+
const now = Date.now();
|
|
84
|
+
try {
|
|
85
|
+
const raw = window.localStorage.getItem(key);
|
|
86
|
+
if (raw) {
|
|
87
|
+
const existing = JSON.parse(raw) as {
|
|
88
|
+
ownerId?: unknown;
|
|
89
|
+
expiresAt?: unknown;
|
|
90
|
+
};
|
|
91
|
+
const expiresAt =
|
|
92
|
+
typeof existing.expiresAt === "number" ? existing.expiresAt : 0;
|
|
93
|
+
if (expiresAt > now && existing.ownerId !== ownerId) return false;
|
|
94
|
+
}
|
|
95
|
+
window.localStorage.setItem(
|
|
96
|
+
key,
|
|
97
|
+
JSON.stringify({ ownerId, expiresAt: now + AUTO_RETRY_CLAIM_TTL_MS }),
|
|
98
|
+
);
|
|
99
|
+
const confirmed = JSON.parse(window.localStorage.getItem(key) ?? "{}") as {
|
|
100
|
+
ownerId?: unknown;
|
|
101
|
+
};
|
|
102
|
+
return confirmed.ownerId === ownerId;
|
|
103
|
+
} catch {
|
|
104
|
+
// Private browsing or disabled storage: fall back to in-tab retry. The
|
|
105
|
+
// server abort is idempotent, and manual controls remain available.
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function claimAutoRetryAttempt(
|
|
111
|
+
threadId: string | null | undefined,
|
|
112
|
+
runId: string,
|
|
113
|
+
ownerId: string,
|
|
114
|
+
) {
|
|
115
|
+
if (!threadId) return true;
|
|
116
|
+
const key = autoRetryClaimKey(threadId, runId);
|
|
117
|
+
const locks =
|
|
118
|
+
typeof navigator !== "undefined"
|
|
119
|
+
? (navigator as Navigator & { locks?: MaybeLockManager }).locks
|
|
120
|
+
: undefined;
|
|
121
|
+
if (locks?.request) {
|
|
122
|
+
try {
|
|
123
|
+
return await locks.request(
|
|
124
|
+
key,
|
|
125
|
+
{ mode: "exclusive", ifAvailable: true },
|
|
126
|
+
(lock) => (lock ? markAutoRetryClaim(key, ownerId) : false),
|
|
127
|
+
);
|
|
128
|
+
} catch {
|
|
129
|
+
return markAutoRetryClaim(key, ownerId);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return markAutoRetryClaim(key, ownerId);
|
|
133
|
+
}
|
|
134
|
+
|
|
45
135
|
export function RunStuckBanner({
|
|
46
136
|
threadId,
|
|
47
137
|
apiUrl,
|
|
48
138
|
stuckThresholdMs,
|
|
49
139
|
onRetry,
|
|
50
140
|
onStuckStateChange,
|
|
141
|
+
autoRetry = false,
|
|
142
|
+
autoRetryOwnerId,
|
|
51
143
|
className,
|
|
52
144
|
}: RunStuckBannerProps) {
|
|
53
145
|
const state = useRunStuckDetection({ threadId, stuckThresholdMs, apiUrl });
|
|
54
146
|
const abortRun = useAbortRun(apiUrl);
|
|
55
|
-
const [busy, setBusy] = useState<
|
|
147
|
+
const [busy, setBusy] = useState<BusyState>({ type: "none" });
|
|
148
|
+
const autoRetriedRunIdsRef = useRef<Set<string>>(new Set());
|
|
149
|
+
const generatedOwnerIdRef = useRef<string | null>(null);
|
|
150
|
+
if (!generatedOwnerIdRef.current) {
|
|
151
|
+
generatedOwnerIdRef.current = createAutoRetryOwnerId();
|
|
152
|
+
}
|
|
153
|
+
const ownerId = autoRetryOwnerId ?? generatedOwnerIdRef.current;
|
|
56
154
|
|
|
57
155
|
const lastReportedRef = useRef<{
|
|
58
156
|
isStuck: boolean;
|
|
@@ -77,39 +175,92 @@ export function RunStuckBanner({
|
|
|
77
175
|
}
|
|
78
176
|
}, [state, onStuckStateChange, threadId]);
|
|
79
177
|
|
|
80
|
-
// Reset the busy spinner once the underlying run
|
|
81
|
-
//
|
|
178
|
+
// Reset the busy spinner once the underlying run is no longer the one we
|
|
179
|
+
// acted on. A recovery continuation can start quickly enough that polling
|
|
180
|
+
// never observes an idle state between run ids.
|
|
82
181
|
useEffect(() => {
|
|
83
|
-
|
|
84
|
-
|
|
182
|
+
setBusy((current) => {
|
|
183
|
+
if (current.type === "none") return current;
|
|
184
|
+
if (state.status !== "running") return { type: "none" };
|
|
185
|
+
if (state.runId && state.runId !== current.runId) return { type: "none" };
|
|
186
|
+
return current;
|
|
187
|
+
});
|
|
188
|
+
}, [state.runId, state.status]);
|
|
189
|
+
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
if (
|
|
192
|
+
!autoRetry ||
|
|
193
|
+
!state.isStuck ||
|
|
194
|
+
!state.runId ||
|
|
195
|
+
busy.type !== "none" ||
|
|
196
|
+
autoRetriedRunIdsRef.current.has(state.runId)
|
|
197
|
+
) {
|
|
198
|
+
return;
|
|
85
199
|
}
|
|
86
|
-
|
|
200
|
+
|
|
201
|
+
const runId = state.runId;
|
|
202
|
+
void claimAutoRetryAttempt(threadId, runId, ownerId).then((claimed) => {
|
|
203
|
+
autoRetriedRunIdsRef.current.add(runId);
|
|
204
|
+
if (!claimed) return;
|
|
205
|
+
setBusy({ type: "retry", runId });
|
|
206
|
+
trackEvent("agent_chat_stuck_auto_retry", {
|
|
207
|
+
runId,
|
|
208
|
+
threadId: threadId ?? null,
|
|
209
|
+
stuckSinceMs: state.stuckSinceMs ?? null,
|
|
210
|
+
});
|
|
211
|
+
void abortRun(runId, "auto_stuck_retry").then((aborted) => {
|
|
212
|
+
if (aborted) {
|
|
213
|
+
onRetry?.(aborted);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
setBusy((current) =>
|
|
217
|
+
current.type !== "none" && current.runId === runId
|
|
218
|
+
? { type: "none" }
|
|
219
|
+
: current,
|
|
220
|
+
);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
}, [
|
|
224
|
+
abortRun,
|
|
225
|
+
autoRetry,
|
|
226
|
+
busy,
|
|
227
|
+
onRetry,
|
|
228
|
+
ownerId,
|
|
229
|
+
state.isStuck,
|
|
230
|
+
state.runId,
|
|
231
|
+
state.stuckSinceMs,
|
|
232
|
+
threadId,
|
|
233
|
+
]);
|
|
87
234
|
|
|
88
235
|
if (!state.isStuck || !state.runId) return null;
|
|
89
236
|
|
|
90
237
|
const handleCancel = async () => {
|
|
91
|
-
if (!state.runId || busy !== "none") return;
|
|
92
|
-
|
|
238
|
+
if (!state.runId || busy.type !== "none") return;
|
|
239
|
+
const runId = state.runId;
|
|
240
|
+
setBusy({ type: "cancel", runId });
|
|
93
241
|
trackEvent("agent_chat_stuck_cancel", {
|
|
94
|
-
runId
|
|
242
|
+
runId,
|
|
95
243
|
threadId: threadId ?? null,
|
|
96
244
|
stuckSinceMs: state.stuckSinceMs ?? null,
|
|
97
245
|
});
|
|
98
|
-
await abortRun(
|
|
246
|
+
await abortRun(runId, "user_stuck_cancel");
|
|
99
247
|
};
|
|
100
248
|
|
|
101
249
|
const handleRetry = async () => {
|
|
102
|
-
if (!state.runId || busy !== "none") return;
|
|
103
|
-
|
|
250
|
+
if (!state.runId || busy.type !== "none") return;
|
|
251
|
+
const runId = state.runId;
|
|
252
|
+
setBusy({ type: "retry", runId });
|
|
104
253
|
trackEvent("agent_chat_stuck_retry", {
|
|
105
|
-
runId
|
|
254
|
+
runId,
|
|
106
255
|
threadId: threadId ?? null,
|
|
107
256
|
stuckSinceMs: state.stuckSinceMs ?? null,
|
|
108
257
|
});
|
|
109
|
-
const aborted = await abortRun(
|
|
258
|
+
const aborted = await abortRun(runId, "user_stuck_retry");
|
|
110
259
|
if (aborted) onRetry?.(aborted);
|
|
111
260
|
};
|
|
112
261
|
|
|
262
|
+
const busyType = busy.type;
|
|
263
|
+
|
|
113
264
|
const stuckSeconds =
|
|
114
265
|
state.stuckSinceMs != null ? Math.floor(state.stuckSinceMs / 1000) : null;
|
|
115
266
|
|
|
@@ -134,16 +285,19 @@ export function RunStuckBanner({
|
|
|
134
285
|
No progress
|
|
135
286
|
{stuckSeconds != null ? ` for ${stuckSeconds}s` : ""}. The agent may
|
|
136
287
|
have hit a server timeout or lost its connection.
|
|
288
|
+
{autoRetry && busyType === "retry"
|
|
289
|
+
? " Retrying automatically now."
|
|
290
|
+
: ""}
|
|
137
291
|
</span>
|
|
138
292
|
</div>
|
|
139
293
|
<div className="flex flex-wrap items-center gap-2">
|
|
140
294
|
<button
|
|
141
295
|
type="button"
|
|
142
296
|
onClick={handleRetry}
|
|
143
|
-
disabled={
|
|
297
|
+
disabled={busyType !== "none"}
|
|
144
298
|
className="inline-flex h-7 cursor-pointer items-center gap-1.5 rounded-md bg-foreground px-2.5 text-[11px] font-medium text-background transition-colors hover:bg-foreground/90 disabled:cursor-not-allowed disabled:opacity-60"
|
|
145
299
|
>
|
|
146
|
-
{
|
|
300
|
+
{busyType === "retry" ? (
|
|
147
301
|
<IconLoader2
|
|
148
302
|
size={12}
|
|
149
303
|
className="animate-spin"
|
|
@@ -155,10 +309,10 @@ export function RunStuckBanner({
|
|
|
155
309
|
<button
|
|
156
310
|
type="button"
|
|
157
311
|
onClick={handleCancel}
|
|
158
|
-
disabled={
|
|
312
|
+
disabled={busyType !== "none"}
|
|
159
313
|
className="inline-flex h-7 cursor-pointer items-center gap-1.5 rounded-md border border-border bg-background px-2.5 text-[11px] font-medium text-foreground transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-60"
|
|
160
314
|
>
|
|
161
|
-
{
|
|
315
|
+
{busyType === "cancel" ? (
|
|
162
316
|
<IconLoader2
|
|
163
317
|
size={12}
|
|
164
318
|
className="animate-spin"
|
|
@@ -26,10 +26,10 @@ import type {
|
|
|
26
26
|
* DECOUPLING: the only behavioral change from the plan copy is theme detection —
|
|
27
27
|
* core blocks read `document.documentElement.classList.contains("dark")` (the
|
|
28
28
|
* MermaidBlock precedent) instead of importing `next-themes`. Everything else
|
|
29
|
-
* (the `.plan-wf` / `[data-rough]` class contract the rough overlay
|
|
30
|
-
* the `--wf-*` / `--ink` / `--paper` token names every primitive
|
|
31
|
-
* preserved exactly, so the kit looks identical in plan and renders
|
|
32
|
-
* any app once the matching tokens exist in `core/styles/blocks.css`.
|
|
29
|
+
* (the `.plan-wf` / `.wf-*` / `[data-rough]` class contract the rough overlay
|
|
30
|
+
* measures, the `--wf-*` / `--ink` / `--paper` token names every primitive
|
|
31
|
+
* reads) is preserved exactly, so the kit looks identical in plan and renders
|
|
32
|
+
* correctly in any app once the matching tokens exist in `core/styles/blocks.css`.
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
35
|
/* ========================================================================== */
|
|
@@ -211,7 +211,7 @@ export function Screen({
|
|
|
211
211
|
// clipping. The frame shell (`ArtboardFrame`) owns the height policy; a
|
|
212
212
|
// caller can still override via `style.height` for a fixed canvas.
|
|
213
213
|
minHeight: "100%",
|
|
214
|
-
background:
|
|
214
|
+
background: "transparent",
|
|
215
215
|
color: V.ink,
|
|
216
216
|
fontFamily: V.hand,
|
|
217
217
|
fontSize: V.fs,
|
|
@@ -1425,9 +1425,9 @@ const gen = rough.generator();
|
|
|
1425
1425
|
|
|
1426
1426
|
type RoughPath = { d: string; stroke: string; strokeWidth: number };
|
|
1427
1427
|
|
|
1428
|
-
/** The default selector used for HTML mockups:
|
|
1428
|
+
/** The default selector used for HTML mockups: standard wireframe primitives plus explicit opt-ins. */
|
|
1429
1429
|
export const HTML_ROUGH_SELECTOR =
|
|
1430
|
-
"[data-rough],button,input,textarea,select,hr";
|
|
1430
|
+
"[data-rough],button,input,textarea,select,hr,.wf-btn,.wf-card,.wf-box,.wf-pill,.wf-chip,.wf-icon-fallback,[style*='border:'],[style*='border-top:'],[style*='border-right:'],[style*='border-bottom:'],[style*='border-left:']";
|
|
1431
1431
|
|
|
1432
1432
|
/** Stable per-element seed so a frame doesn't re-wobble on every measure. */
|
|
1433
1433
|
function seedFrom(...parts: Array<string | number>): number {
|
|
@@ -1528,6 +1528,50 @@ function elementStroke(node: Element, fallback: string): string {
|
|
|
1528
1528
|
return fallback;
|
|
1529
1529
|
}
|
|
1530
1530
|
|
|
1531
|
+
function visibleBorderSides(
|
|
1532
|
+
node: Element,
|
|
1533
|
+
): Array<"top" | "right" | "bottom" | "left"> {
|
|
1534
|
+
const cs = getComputedStyle(node);
|
|
1535
|
+
const sides: Array<"top" | "right" | "bottom" | "left"> = [];
|
|
1536
|
+
for (const [side, widthProp, colorProp] of [
|
|
1537
|
+
["top", "borderTopWidth", "borderTopColor"],
|
|
1538
|
+
["right", "borderRightWidth", "borderRightColor"],
|
|
1539
|
+
["bottom", "borderBottomWidth", "borderBottomColor"],
|
|
1540
|
+
["left", "borderLeftWidth", "borderLeftColor"],
|
|
1541
|
+
] as const) {
|
|
1542
|
+
const width = parseFloat(cs[widthProp]);
|
|
1543
|
+
const color = cs[colorProp];
|
|
1544
|
+
if (
|
|
1545
|
+
width > 0 &&
|
|
1546
|
+
color &&
|
|
1547
|
+
color !== "rgba(0, 0, 0, 0)" &&
|
|
1548
|
+
color !== "transparent"
|
|
1549
|
+
) {
|
|
1550
|
+
sides.push(side);
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
return sides;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
function inferRoughKind(node: HTMLElement): string {
|
|
1557
|
+
const explicit = node.getAttribute("data-rough");
|
|
1558
|
+
if (explicit) return explicit;
|
|
1559
|
+
if (node.tagName === "HR") return "line:middle";
|
|
1560
|
+
const sides = visibleBorderSides(node);
|
|
1561
|
+
if (sides.length !== 1) return "rect";
|
|
1562
|
+
switch (sides[0]) {
|
|
1563
|
+
case "top":
|
|
1564
|
+
return "line:top";
|
|
1565
|
+
case "right":
|
|
1566
|
+
return "line:right";
|
|
1567
|
+
case "bottom":
|
|
1568
|
+
return "line:bottom";
|
|
1569
|
+
case "left":
|
|
1570
|
+
return "line:left";
|
|
1571
|
+
}
|
|
1572
|
+
return "rect";
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1531
1575
|
function build(
|
|
1532
1576
|
scope: HTMLElement,
|
|
1533
1577
|
opts: {
|
|
@@ -1602,7 +1646,7 @@ function build(
|
|
|
1602
1646
|
const w = r.width / zoom;
|
|
1603
1647
|
const h = r.height / zoom;
|
|
1604
1648
|
if (w < 2 || h < 2) return;
|
|
1605
|
-
const kind = node
|
|
1649
|
+
const kind = inferRoughKind(node);
|
|
1606
1650
|
const rawStroke = elementStroke(node, sketch);
|
|
1607
1651
|
const stroke =
|
|
1608
1652
|
sameColor(rawStroke, ink) || (line !== "" && sameColor(rawStroke, line))
|
|
@@ -1621,11 +1665,15 @@ function build(
|
|
|
1621
1665
|
let drawable: unknown;
|
|
1622
1666
|
if (kind === "ellipse") {
|
|
1623
1667
|
drawable = gen.ellipse(x + w / 2, y + h / 2, w, h, o);
|
|
1668
|
+
} else if (kind === "line:left") {
|
|
1669
|
+
drawable = gen.line(x, y, x, y + h, o);
|
|
1624
1670
|
} else if (kind === "line:right") {
|
|
1625
1671
|
drawable = gen.line(x + w, y, x + w, y + h, o);
|
|
1626
1672
|
} else if (kind === "line:bottom") {
|
|
1627
1673
|
drawable = gen.line(x, y + h, x + w, y + h, o);
|
|
1628
|
-
} else if (kind === "line:top"
|
|
1674
|
+
} else if (kind === "line:top") {
|
|
1675
|
+
drawable = gen.line(x, y, x + w, y, o);
|
|
1676
|
+
} else if (kind === "line:middle") {
|
|
1629
1677
|
drawable = gen.line(x, y + h / 2, x + w, y + h / 2, o);
|
|
1630
1678
|
} else {
|
|
1631
1679
|
const cr = parseFloat(getComputedStyle(node).borderTopLeftRadius) || 0;
|
|
@@ -56,8 +56,8 @@ import {
|
|
|
56
56
|
* - The plan-only prototype runtime, design-element selection, and legacy region
|
|
57
57
|
* fallback are intentionally NOT ported; those are plan-canvas features, not
|
|
58
58
|
* part of the document-block render. The kit element vocabulary, the `--wf-*`
|
|
59
|
-
* token contract, and the `.plan-wf` / `[data-rough]` classes the
|
|
60
|
-
* measures are preserved exactly.
|
|
59
|
+
* token contract, and the `.plan-wf` / `.wf-*` / `[data-rough]` classes the
|
|
60
|
+
* overlay measures are preserved exactly.
|
|
61
61
|
*
|
|
62
62
|
* The section carries the app-neutral `an-block` class plus the legacy
|
|
63
63
|
* `plan-block` class so plan renders byte-identically while any other app gets
|
|
@@ -164,12 +164,6 @@ function ArtboardFrame({
|
|
|
164
164
|
const designMode = renderMode === "design";
|
|
165
165
|
const sketchy = !designMode && style === "sketchy" && !skeleton;
|
|
166
166
|
const roughEnabled = sketchy && roughOverlay;
|
|
167
|
-
const paper = designMode
|
|
168
|
-
? "hsl(var(--background))"
|
|
169
|
-
: "var(--plan-document, hsl(var(--background)))";
|
|
170
|
-
const frameBorder = skeleton
|
|
171
|
-
? "var(--plan-placeholder-line, var(--plan-line, hsl(var(--border))))"
|
|
172
|
-
: "var(--plan-line, hsl(var(--border)))";
|
|
173
167
|
|
|
174
168
|
useEffect(() => {
|
|
175
169
|
const element = fitRef.current;
|
|
@@ -243,7 +237,6 @@ function ArtboardFrame({
|
|
|
243
237
|
// a fixed `canvasSize` locks the height for canvas artboards.
|
|
244
238
|
...(fixedHeight != null ? { height: fixedHeight } : { minHeight }),
|
|
245
239
|
borderRadius: preset.radius,
|
|
246
|
-
background: paper,
|
|
247
240
|
...(fitScale !== 1
|
|
248
241
|
? {
|
|
249
242
|
transform: `scale(${fitScale})`,
|
|
@@ -266,18 +259,10 @@ function ArtboardFrame({
|
|
|
266
259
|
>
|
|
267
260
|
{render({ theme, style })}
|
|
268
261
|
</div>
|
|
269
|
-
{!roughEnabled && (
|
|
270
|
-
<div
|
|
271
|
-
className="pointer-events-none absolute inset-0"
|
|
272
|
-
style={{
|
|
273
|
-
borderRadius: preset.radius,
|
|
274
|
-
border: `1.5px solid ${frameBorder}`,
|
|
275
|
-
}}
|
|
276
|
-
/>
|
|
277
|
-
)}
|
|
278
262
|
<RoughOverlay
|
|
279
263
|
scopeRef={ref}
|
|
280
264
|
enabled={roughEnabled}
|
|
265
|
+
drawFrame={false}
|
|
281
266
|
frameRadius={preset.radius}
|
|
282
267
|
selector={selector}
|
|
283
268
|
/>
|
|
@@ -34,10 +34,37 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
34
34
|
function normalizeActionDataWidgetResult(
|
|
35
35
|
context: ToolRendererContext,
|
|
36
36
|
): DataWidgetResult | null {
|
|
37
|
+
const renderer = context.chatUI?.renderer;
|
|
38
|
+
if (isRecord(context.resultJson)) {
|
|
39
|
+
if (renderer === ACTION_CHAT_UI_DATA_TABLE_RENDERER) {
|
|
40
|
+
return normalizeDataWidgetResult({
|
|
41
|
+
...context.resultJson,
|
|
42
|
+
widget: DATA_TABLE_WIDGET,
|
|
43
|
+
table: isRecord(context.resultJson.table)
|
|
44
|
+
? context.resultJson.table
|
|
45
|
+
: context.resultJson,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (renderer === ACTION_CHAT_UI_DATA_CHART_RENDERER) {
|
|
49
|
+
return normalizeDataWidgetResult({
|
|
50
|
+
...context.resultJson,
|
|
51
|
+
widget: DATA_CHART_WIDGET,
|
|
52
|
+
chartSeries: isRecord(context.resultJson.chartSeries)
|
|
53
|
+
? context.resultJson.chartSeries
|
|
54
|
+
: context.resultJson,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (renderer === ACTION_CHAT_UI_DATA_INSIGHTS_RENDERER) {
|
|
58
|
+
return normalizeDataWidgetResult({
|
|
59
|
+
...context.resultJson,
|
|
60
|
+
widget: DATA_INSIGHTS_WIDGET,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
37
65
|
const result = normalizeDataWidgetResult(context.resultJson);
|
|
38
66
|
if (result) return result;
|
|
39
67
|
|
|
40
|
-
const renderer = context.chatUI?.renderer;
|
|
41
68
|
if (
|
|
42
69
|
renderer === ACTION_CHAT_UI_DATA_WIDGET_RENDERER ||
|
|
43
70
|
context.toolName === "render-data-widget"
|
|
@@ -46,37 +73,6 @@ function normalizeActionDataWidgetResult(
|
|
|
46
73
|
if (argsResult) return argsResult;
|
|
47
74
|
}
|
|
48
75
|
|
|
49
|
-
if (!isRecord(context.resultJson)) return null;
|
|
50
|
-
|
|
51
|
-
if (renderer === ACTION_CHAT_UI_DATA_TABLE_RENDERER) {
|
|
52
|
-
return normalizeDataWidgetResult({
|
|
53
|
-
widget: DATA_TABLE_WIDGET,
|
|
54
|
-
table: isRecord(context.resultJson.table)
|
|
55
|
-
? context.resultJson.table
|
|
56
|
-
: context.resultJson,
|
|
57
|
-
display: isRecord(context.resultJson.display)
|
|
58
|
-
? context.resultJson.display
|
|
59
|
-
: undefined,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
if (renderer === ACTION_CHAT_UI_DATA_CHART_RENDERER) {
|
|
63
|
-
return normalizeDataWidgetResult({
|
|
64
|
-
widget: DATA_CHART_WIDGET,
|
|
65
|
-
chartSeries: isRecord(context.resultJson.chartSeries)
|
|
66
|
-
? context.resultJson.chartSeries
|
|
67
|
-
: context.resultJson,
|
|
68
|
-
display: isRecord(context.resultJson.display)
|
|
69
|
-
? context.resultJson.display
|
|
70
|
-
: undefined,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
if (renderer === ACTION_CHAT_UI_DATA_INSIGHTS_RENDERER) {
|
|
74
|
-
return normalizeDataWidgetResult({
|
|
75
|
-
...context.resultJson,
|
|
76
|
-
widget: DATA_INSIGHTS_WIDGET,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
76
|
return null;
|
|
81
77
|
}
|
|
82
78
|
|
|
@@ -180,7 +180,8 @@ export function createCodeAgentChatAdapter(
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
const content = codeAgentTranscriptEventsToContent(tailedEvents);
|
|
183
|
-
|
|
183
|
+
const sawClear = nextEvents.some(isAgentChatClearTranscriptEvent);
|
|
184
|
+
if ((content.length > 0 || sawClear) && nextEvents.length > 0) {
|
|
184
185
|
yieldedContent = true;
|
|
185
186
|
yield withRunMetadata({ content: [...content] }, runId);
|
|
186
187
|
}
|
|
@@ -249,6 +250,12 @@ export function codeAgentTranscriptEventsToContent(
|
|
|
249
250
|
return content;
|
|
250
251
|
}
|
|
251
252
|
|
|
253
|
+
function isAgentChatClearTranscriptEvent(
|
|
254
|
+
event: CodeAgentChatTranscriptEvent | undefined,
|
|
255
|
+
): boolean {
|
|
256
|
+
return event?.metadata?.agentChatEventType === "clear";
|
|
257
|
+
}
|
|
258
|
+
|
|
252
259
|
function latestUserMessage(
|
|
253
260
|
messages: ChatModelRunOptions["messages"],
|
|
254
261
|
): ChatModelRunOptions["messages"][number] | undefined {
|
|
@@ -698,11 +698,15 @@ export function processEvent(
|
|
|
698
698
|
};
|
|
699
699
|
} {
|
|
700
700
|
if (ev.type === "clear") {
|
|
701
|
-
// Server is retrying — discard
|
|
702
|
-
|
|
701
|
+
// Server is retrying — discard rejected draft text and unfinished tool
|
|
702
|
+
// output while keeping completed tool results visible.
|
|
703
|
+
clearAssistantDraftContent(content);
|
|
703
704
|
resetProcessEventState(state);
|
|
704
705
|
dispatchActivityClear(tabId);
|
|
705
|
-
return {
|
|
706
|
+
return {
|
|
707
|
+
action: "yield",
|
|
708
|
+
result: { content: contentSnapshot(content) } as ChatModelRunResult,
|
|
709
|
+
};
|
|
706
710
|
}
|
|
707
711
|
|
|
708
712
|
if (ev.type === "text") {
|
|
@@ -1159,6 +1163,20 @@ export function processEvent(
|
|
|
1159
1163
|
return { action: "continue" };
|
|
1160
1164
|
}
|
|
1161
1165
|
|
|
1166
|
+
function clearAssistantDraftContent(content: ContentPart[]): void {
|
|
1167
|
+
for (let index = content.length - 1; index >= 0; index--) {
|
|
1168
|
+
const part = content[index];
|
|
1169
|
+
if (!part) continue;
|
|
1170
|
+
if (part.type === "text") {
|
|
1171
|
+
content.splice(index, 1);
|
|
1172
|
+
continue;
|
|
1173
|
+
}
|
|
1174
|
+
if (part.type === "tool-call" && part.result === undefined) {
|
|
1175
|
+
content.splice(index, 1);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1162
1180
|
/**
|
|
1163
1181
|
* Read and process SSE events from a ReadableStream response body.
|
|
1164
1182
|
* Yields ChatModelRunResult for each meaningful event.
|
|
@@ -92,6 +92,12 @@ export function normalizeCodeAgentTranscript(
|
|
|
92
92
|
|
|
93
93
|
for (const event of events) {
|
|
94
94
|
const currentTurnIndex = Math.max(turnIndex, 0);
|
|
95
|
+
if (isAgentChatClearEvent(event)) {
|
|
96
|
+
clearNormalizedAgentDraftItems(items, currentTurnIndex);
|
|
97
|
+
hiddenEvents.push(event);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
95
101
|
if (event.kind === "user") {
|
|
96
102
|
turnIndex = turnIndex < 0 ? (items.length === 0 ? 0 : 1) : turnIndex + 1;
|
|
97
103
|
items.push(createUserTurn(event, turnIndex));
|
|
@@ -359,6 +365,31 @@ function findOpenToolEvent(
|
|
|
359
365
|
return null;
|
|
360
366
|
}
|
|
361
367
|
|
|
368
|
+
function isAgentChatClearEvent(event: CodeAgentTranscriptEvent): boolean {
|
|
369
|
+
return stringMetadata(event.metadata, "agentChatEventType") === "clear";
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function clearNormalizedAgentDraftItems(
|
|
373
|
+
items: NormalizedCodeAgentTranscriptItem[],
|
|
374
|
+
turnIndex: number,
|
|
375
|
+
): void {
|
|
376
|
+
for (let index = items.length - 1; index >= 0; index--) {
|
|
377
|
+
const item = items[index];
|
|
378
|
+
if (!item || item.turnIndex !== turnIndex) continue;
|
|
379
|
+
if (
|
|
380
|
+
item.type === "assistant" ||
|
|
381
|
+
item.type === "thinking" ||
|
|
382
|
+
item.type === "status"
|
|
383
|
+
) {
|
|
384
|
+
items.splice(index, 1);
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
if (item.type === "tool" && item.state !== "completed") {
|
|
388
|
+
items.splice(index, 1);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
362
393
|
function suppressDuplicateFinalAssistantText(
|
|
363
394
|
items: readonly NormalizedCodeAgentTranscriptItem[],
|
|
364
395
|
hiddenEvents: CodeAgentTranscriptEvent[],
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { sql } from "drizzle-orm";
|
|
20
20
|
import {
|
|
21
21
|
pgTable,
|
|
22
|
+
alias as pgAlias,
|
|
22
23
|
index as pgIndex,
|
|
23
24
|
text as pgText,
|
|
24
25
|
integer as pgInteger,
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
} from "drizzle-orm/pg-core";
|
|
29
30
|
import {
|
|
30
31
|
sqliteTable,
|
|
32
|
+
alias as sqliteAlias,
|
|
31
33
|
index as sqliteIndex,
|
|
32
34
|
text as sqliteText,
|
|
33
35
|
integer as sqliteInteger,
|
|
@@ -50,6 +52,9 @@ function pg(): boolean {
|
|
|
50
52
|
export const table: typeof sqliteTable = ((...args: any[]) =>
|
|
51
53
|
pg() ? (pgTable as any)(...args) : (sqliteTable as any)(...args)) as any;
|
|
52
54
|
|
|
55
|
+
export const alias: typeof sqliteAlias = ((...args: any[]) =>
|
|
56
|
+
pg() ? (pgAlias as any)(...args) : (sqliteAlias as any)(...args)) as any;
|
|
57
|
+
|
|
53
58
|
export const index: typeof sqliteIndex = ((...args: any[]) =>
|
|
54
59
|
pg() ? (pgIndex as any)(...args) : (sqliteIndex as any)(...args)) as any;
|
|
55
60
|
|
|
@@ -21,7 +21,10 @@ import {
|
|
|
21
21
|
hasConfiguredA2ASecret,
|
|
22
22
|
isA2AProductionRuntime,
|
|
23
23
|
} from "../a2a/auth-policy.js";
|
|
24
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
applyAgentTextEventToBuffer,
|
|
26
|
+
collectFinalResponseTextFromAgentEvents,
|
|
27
|
+
} from "../a2a/response-text.js";
|
|
25
28
|
import { updateTaskStatusMessage } from "../a2a/task-store.js";
|
|
26
29
|
import { ACTION_CHAT_UI_DATA_WIDGET_RENDERER } from "../action-ui.js";
|
|
27
30
|
import type { ActionHttpConfig } from "../action.js";
|
|
@@ -5179,7 +5182,10 @@ export function createAgentChatPlugin(
|
|
|
5179
5182
|
],
|
|
5180
5183
|
actions: mcpActions,
|
|
5181
5184
|
send: (event) => {
|
|
5182
|
-
|
|
5185
|
+
accumulatedText = applyAgentTextEventToBuffer(
|
|
5186
|
+
accumulatedText,
|
|
5187
|
+
event,
|
|
5188
|
+
);
|
|
5183
5189
|
},
|
|
5184
5190
|
signal: controller.signal,
|
|
5185
5191
|
},
|