@agent-native/core 0.84.12 → 0.84.14
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 +19 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/durable-background.ts +42 -5
- package/corpus/core/src/agent/production-agent.ts +72 -10
- package/corpus/core/src/agent/run-manager.ts +84 -4
- package/corpus/core/src/agent/run-store.ts +55 -11
- package/corpus/core/src/chat-threads/store.ts +2 -0
- package/corpus/core/src/cli/skills.ts +10 -6
- package/corpus/core/src/client/AssistantChat.tsx +40 -9
- package/corpus/core/src/client/agent-chat-adapter.ts +44 -5
- package/corpus/core/src/client/blocks/library/diagram.tsx +3 -3
- package/corpus/core/src/client/chat/message-components.tsx +17 -0
- package/corpus/core/src/client/chat/run-recovery.tsx +52 -44
- package/corpus/core/src/client/chat/tool-call-display.tsx +7 -2
- package/corpus/core/src/observability/traces.ts +17 -3
- package/corpus/core/src/server/agent-chat-plugin.ts +12 -1
- package/corpus/core/src/shared/streaming-text-smoothing.ts +10 -5
- package/corpus/core/src/styles/blocks.css +44 -1
- package/corpus/templates/clips/app/components/library/recording-card.tsx +14 -4
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +10 -12
- package/corpus/templates/clips/app/hooks/use-library.ts +7 -4
- package/corpus/templates/clips/app/lib/recording-status.ts +32 -0
- package/corpus/templates/clips/changelog/2026-07-01-chrome-extension-recordings-stream-uploads-while-recording.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-dictation-keeps-listening-through-natural-pauses-and-gives-c.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-redoing-a-paused-desktop-recording-no-longer-leaves-the-recording-controls-disabled.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-stalled-clip-uploads-now-update-in-the-library-and-surface-as-failed.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/background.ts +8 -0
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +61 -16
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +36 -18
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +99 -27
- package/corpus/templates/clips/desktop/src/overlays/flow-bar.tsx +4 -43
- package/corpus/templates/clips/desktop/src/overlays/toolbar.tsx +3 -0
- package/corpus/templates/clips/desktop/src/styles.css +5 -48
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +4 -5
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +15 -6
- package/corpus/templates/design/AGENTS.md +8 -4
- package/corpus/templates/design/actions/create-design.ts +1 -0
- package/corpus/templates/design/actions/duplicate-design.ts +3 -1
- package/corpus/templates/design/actions/edit-design.ts +43 -13
- package/corpus/templates/design/actions/generate-design.ts +5 -1
- package/corpus/templates/design/actions/present-design-variants.ts +17 -12
- package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
- package/corpus/templates/design/app/pages/DesignEditor.tsx +2 -2
- package/corpus/templates/design/changelog/2026-07-01-org-members-can-see-design-projects-created-in-their-workspace.md +6 -0
- package/corpus/templates/design/server/plugins/db.ts +9 -0
- package/corpus/templates/plan/app/components/plan/wireframe/html-artboard.css +44 -1
- package/corpus/templates/plan/changelog/2026-07-01-visual-plan-and-recap-diagram-labels-now-wrap-inside-their-b.md +6 -0
- package/dist/agent/durable-background.d.ts +3 -0
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +33 -3
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/production-agent.d.ts +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +55 -9
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +2 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +68 -5
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +9 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +47 -11
- package/dist/agent/run-store.js.map +1 -1
- package/dist/chat-threads/store.d.ts +1 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +2 -0
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +10 -6
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +10 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +26 -8
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +40 -5
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/blocks/library/diagram.js +3 -3
- package/dist/client/blocks/library/diagram.js.map +1 -1
- package/dist/client/chat/message-components.d.ts +3 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +16 -2
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/run-recovery.d.ts.map +1 -1
- package/dist/client/chat/run-recovery.js +15 -10
- package/dist/client/chat/run-recovery.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +1 -0
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +3 -2
- package/dist/client/chat/tool-call-display.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 +6 -6
- package/dist/observability/traces.d.ts +5 -0
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +9 -3
- package/dist/observability/traces.js.map +1 -1
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/server/agent-chat-plugin.d.ts +2 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +8 -3
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/streaming-text-smoothing.d.ts.map +1 -1
- package/dist/shared/streaming-text-smoothing.js +8 -5
- package/dist/shared/streaming-text-smoothing.js.map +1 -1
- package/dist/styles/blocks.css +44 -1
- package/package.json +1 -1
|
@@ -116,6 +116,7 @@ async function ensureRunTables(): Promise<void> {
|
|
|
116
116
|
turn_id TEXT,
|
|
117
117
|
error_code TEXT,
|
|
118
118
|
error_detail TEXT,
|
|
119
|
+
terminal_reason TEXT,
|
|
119
120
|
dispatch_mode TEXT,
|
|
120
121
|
diag_stage TEXT
|
|
121
122
|
)
|
|
@@ -187,6 +188,7 @@ async function ensureRunTables(): Promise<void> {
|
|
|
187
188
|
["turn_id", "TEXT"],
|
|
188
189
|
["error_code", "TEXT"],
|
|
189
190
|
["error_detail", "TEXT"],
|
|
191
|
+
["terminal_reason", "TEXT"],
|
|
190
192
|
["dispatch_mode", "TEXT"],
|
|
191
193
|
["diag_stage", "TEXT"],
|
|
192
194
|
["worker_stage", "TEXT"],
|
|
@@ -264,6 +266,7 @@ async function ensureRunTables(): Promise<void> {
|
|
|
264
266
|
"turn_id",
|
|
265
267
|
"error_code",
|
|
266
268
|
"error_detail",
|
|
269
|
+
"terminal_reason",
|
|
267
270
|
"dispatch_mode",
|
|
268
271
|
"diag_stage",
|
|
269
272
|
"worker_stage",
|
|
@@ -621,6 +624,28 @@ export async function setRunError(
|
|
|
621
624
|
}
|
|
622
625
|
}
|
|
623
626
|
|
|
627
|
+
/**
|
|
628
|
+
* Record why a run reached its terminal status. Unlike error_code/error_detail,
|
|
629
|
+
* this is set for successful checkpoint boundaries too (for example
|
|
630
|
+
* status='completed' + terminal_reason='run_timeout').
|
|
631
|
+
*/
|
|
632
|
+
export async function setRunTerminalReason(
|
|
633
|
+
runId: string,
|
|
634
|
+
terminalReason: string | undefined,
|
|
635
|
+
): Promise<void> {
|
|
636
|
+
if (!terminalReason) return;
|
|
637
|
+
try {
|
|
638
|
+
await ensureRunTables();
|
|
639
|
+
const client = getDbExec();
|
|
640
|
+
await client.execute({
|
|
641
|
+
sql: `UPDATE agent_runs SET terminal_reason = ? WHERE id = ?`,
|
|
642
|
+
args: [terminalReason.slice(0, 200), runId],
|
|
643
|
+
});
|
|
644
|
+
} catch {
|
|
645
|
+
// Diagnostics are best-effort; never let them break completion.
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
624
649
|
/**
|
|
625
650
|
* Diagnostic stage names recorded onto a background run as it moves through the
|
|
626
651
|
* `_process-run` worker pipeline. Each value is the LAST stage successfully
|
|
@@ -765,7 +790,8 @@ export async function reapIfStale(
|
|
|
765
790
|
SET status = 'errored',
|
|
766
791
|
completed_at = ?,
|
|
767
792
|
error_code = ?,
|
|
768
|
-
error_detail =
|
|
793
|
+
error_detail = ?,
|
|
794
|
+
terminal_reason = ?
|
|
769
795
|
WHERE id = ?
|
|
770
796
|
AND status = 'running'
|
|
771
797
|
AND ${staleClause}`,
|
|
@@ -773,6 +799,7 @@ export async function reapIfStale(
|
|
|
773
799
|
completedAt,
|
|
774
800
|
STALE_RUN_ERROR_EVENT.errorCode,
|
|
775
801
|
STALE_RUN_ERROR_EVENT.details,
|
|
802
|
+
STALE_RUN_ERROR_EVENT.errorCode,
|
|
776
803
|
runId,
|
|
777
804
|
...staleArgs,
|
|
778
805
|
],
|
|
@@ -821,7 +848,8 @@ export async function reapUnclaimedBackgroundRun(
|
|
|
821
848
|
SET status = 'errored',
|
|
822
849
|
completed_at = ?,
|
|
823
850
|
error_code = ?,
|
|
824
|
-
error_detail =
|
|
851
|
+
error_detail = ?,
|
|
852
|
+
terminal_reason = ?
|
|
825
853
|
WHERE id = ?
|
|
826
854
|
AND status = 'running'
|
|
827
855
|
AND dispatch_mode = 'background'
|
|
@@ -830,6 +858,7 @@ export async function reapUnclaimedBackgroundRun(
|
|
|
830
858
|
completedAt,
|
|
831
859
|
UNCLAIMED_BACKGROUND_RUN_ERROR_EVENT.errorCode,
|
|
832
860
|
UNCLAIMED_BACKGROUND_RUN_ERROR_EVENT.details,
|
|
861
|
+
UNCLAIMED_BACKGROUND_RUN_ERROR_EVENT.errorCode,
|
|
833
862
|
runId,
|
|
834
863
|
cutoff,
|
|
835
864
|
],
|
|
@@ -902,8 +931,8 @@ export async function markRunAborted(
|
|
|
902
931
|
await ensureRunTables();
|
|
903
932
|
const client = getDbExec();
|
|
904
933
|
await client.execute({
|
|
905
|
-
sql: `UPDATE agent_runs SET status = 'aborted', abort_reason = ?, completed_at = ? WHERE id = ?`,
|
|
906
|
-
args: [reason ?? "user", Date.now(), runId],
|
|
934
|
+
sql: `UPDATE agent_runs SET status = 'aborted', abort_reason = ?, completed_at = ?, terminal_reason = ? WHERE id = ?`,
|
|
935
|
+
args: [reason ?? "user", Date.now(), `aborted:${reason ?? "user"}`, runId],
|
|
907
936
|
});
|
|
908
937
|
await safeAppendTerminalRunEvent(runId, { type: "done" }, "mark-aborted");
|
|
909
938
|
}
|
|
@@ -1005,13 +1034,14 @@ export async function getRunByThread(
|
|
|
1005
1034
|
completedAt: number | null;
|
|
1006
1035
|
lastProgressAt: number | null;
|
|
1007
1036
|
dispatchMode: string | null;
|
|
1037
|
+
terminalReason: string | null;
|
|
1008
1038
|
diagStage: string | null;
|
|
1009
1039
|
} | null> {
|
|
1010
1040
|
await ensureRunTables();
|
|
1011
1041
|
const client = getDbExec();
|
|
1012
1042
|
const sql = options?.includeTerminal
|
|
1013
|
-
? `SELECT id, thread_id, turn_id, status, started_at, heartbeat_at, completed_at, last_progress_at, dispatch_mode, diag_stage FROM agent_runs WHERE thread_id = ? ORDER BY started_at DESC LIMIT 1`
|
|
1014
|
-
: `SELECT id, thread_id, turn_id, status, started_at, heartbeat_at, completed_at, last_progress_at, dispatch_mode, diag_stage FROM agent_runs WHERE thread_id = ? AND status = 'running' ORDER BY started_at DESC LIMIT 1`;
|
|
1043
|
+
? `SELECT id, thread_id, turn_id, status, started_at, heartbeat_at, completed_at, last_progress_at, dispatch_mode, terminal_reason, diag_stage FROM agent_runs WHERE thread_id = ? ORDER BY started_at DESC LIMIT 1`
|
|
1044
|
+
: `SELECT id, thread_id, turn_id, status, started_at, heartbeat_at, completed_at, last_progress_at, dispatch_mode, terminal_reason, diag_stage FROM agent_runs WHERE thread_id = ? AND status = 'running' ORDER BY started_at DESC LIMIT 1`;
|
|
1015
1045
|
const { rows } = await client.execute({ sql, args: [threadId] });
|
|
1016
1046
|
if (rows.length === 0) return null;
|
|
1017
1047
|
const r = rows[0] as {
|
|
@@ -1024,6 +1054,7 @@ export async function getRunByThread(
|
|
|
1024
1054
|
completed_at: number | string | null;
|
|
1025
1055
|
last_progress_at: number | string | null;
|
|
1026
1056
|
dispatch_mode?: string | null;
|
|
1057
|
+
terminal_reason?: string | null;
|
|
1027
1058
|
diag_stage?: string | null;
|
|
1028
1059
|
};
|
|
1029
1060
|
return {
|
|
@@ -1037,6 +1068,7 @@ export async function getRunByThread(
|
|
|
1037
1068
|
lastProgressAt:
|
|
1038
1069
|
r.last_progress_at == null ? null : Number(r.last_progress_at),
|
|
1039
1070
|
dispatchMode: r.dispatch_mode ?? null,
|
|
1071
|
+
terminalReason: r.terminal_reason ?? null,
|
|
1040
1072
|
diagStage: r.diag_stage ?? null,
|
|
1041
1073
|
};
|
|
1042
1074
|
}
|
|
@@ -1053,6 +1085,7 @@ export interface AgentRunSummary {
|
|
|
1053
1085
|
errorCode: string | null;
|
|
1054
1086
|
abortReason: string | null;
|
|
1055
1087
|
dispatchMode: string | null;
|
|
1088
|
+
terminalReason: string | null;
|
|
1056
1089
|
/** Last reached `_process-run` worker stage (JSON `{stage,detail?,at}`). */
|
|
1057
1090
|
diagStage: string | null;
|
|
1058
1091
|
}
|
|
@@ -1065,7 +1098,7 @@ export async function listRunsForThread(
|
|
|
1065
1098
|
const limit = Math.min(Math.max(options.limit ?? 10, 1), 50);
|
|
1066
1099
|
const client = getDbExec();
|
|
1067
1100
|
const { rows } = await client.execute({
|
|
1068
|
-
sql: `SELECT id, thread_id, turn_id, status, started_at, heartbeat_at, completed_at, last_progress_at, error_code, abort_reason, dispatch_mode, diag_stage
|
|
1101
|
+
sql: `SELECT id, thread_id, turn_id, status, started_at, heartbeat_at, completed_at, last_progress_at, error_code, abort_reason, dispatch_mode, terminal_reason, diag_stage
|
|
1069
1102
|
FROM agent_runs
|
|
1070
1103
|
WHERE thread_id = ?
|
|
1071
1104
|
ORDER BY started_at DESC
|
|
@@ -1085,6 +1118,7 @@ export async function listRunsForThread(
|
|
|
1085
1118
|
error_code?: string | null;
|
|
1086
1119
|
abort_reason?: string | null;
|
|
1087
1120
|
dispatch_mode?: string | null;
|
|
1121
|
+
terminal_reason?: string | null;
|
|
1088
1122
|
diag_stage?: string | null;
|
|
1089
1123
|
};
|
|
1090
1124
|
return {
|
|
@@ -1100,6 +1134,7 @@ export async function listRunsForThread(
|
|
|
1100
1134
|
errorCode: row.error_code ?? null,
|
|
1101
1135
|
abortReason: row.abort_reason ?? null,
|
|
1102
1136
|
dispatchMode: row.dispatch_mode ?? null,
|
|
1137
|
+
terminalReason: row.terminal_reason ?? null,
|
|
1103
1138
|
diagStage: row.diag_stage ?? null,
|
|
1104
1139
|
};
|
|
1105
1140
|
});
|
|
@@ -1183,13 +1218,15 @@ export async function reapAllStaleRuns(): Promise<number> {
|
|
|
1183
1218
|
SET status = 'errored',
|
|
1184
1219
|
completed_at = ?,
|
|
1185
1220
|
error_code = ?,
|
|
1186
|
-
error_detail =
|
|
1221
|
+
error_detail = ?,
|
|
1222
|
+
terminal_reason = ?
|
|
1187
1223
|
WHERE status = 'running'
|
|
1188
1224
|
AND ${livenessBasisSql()} < ${backgroundAwareStaleCutoffSql()}`,
|
|
1189
1225
|
args: [
|
|
1190
1226
|
completedAt,
|
|
1191
1227
|
STALE_RUN_ERROR_EVENT.errorCode,
|
|
1192
1228
|
STALE_RUN_ERROR_EVENT.details,
|
|
1229
|
+
STALE_RUN_ERROR_EVENT.errorCode,
|
|
1193
1230
|
completedAt,
|
|
1194
1231
|
],
|
|
1195
1232
|
});
|
|
@@ -1241,12 +1278,14 @@ export async function cleanupOldRuns(
|
|
|
1241
1278
|
SET status = 'errored',
|
|
1242
1279
|
completed_at = ?,
|
|
1243
1280
|
error_code = ?,
|
|
1244
|
-
error_detail =
|
|
1281
|
+
error_detail = ?,
|
|
1282
|
+
terminal_reason = ?
|
|
1245
1283
|
WHERE status = 'running' AND started_at < ?`,
|
|
1246
1284
|
args: [
|
|
1247
1285
|
completedAt,
|
|
1248
1286
|
STALE_RUN_ERROR_EVENT.errorCode,
|
|
1249
1287
|
STALE_RUN_ERROR_EVENT.details,
|
|
1288
|
+
STALE_RUN_ERROR_EVENT.errorCode,
|
|
1250
1289
|
cutoff,
|
|
1251
1290
|
],
|
|
1252
1291
|
});
|
|
@@ -1257,13 +1296,15 @@ export async function cleanupOldRuns(
|
|
|
1257
1296
|
SET status = 'errored',
|
|
1258
1297
|
completed_at = ?,
|
|
1259
1298
|
error_code = ?,
|
|
1260
|
-
error_detail =
|
|
1299
|
+
error_detail = ?,
|
|
1300
|
+
terminal_reason = ?
|
|
1261
1301
|
WHERE status = 'running'
|
|
1262
1302
|
AND ${livenessBasisSql()} < ${backgroundAwareStaleCutoffSql()}`,
|
|
1263
1303
|
args: [
|
|
1264
1304
|
completedAt,
|
|
1265
1305
|
STALE_RUN_ERROR_EVENT.errorCode,
|
|
1266
1306
|
STALE_RUN_ERROR_EVENT.details,
|
|
1307
|
+
STALE_RUN_ERROR_EVENT.errorCode,
|
|
1267
1308
|
completedAt,
|
|
1268
1309
|
],
|
|
1269
1310
|
});
|
|
@@ -1312,6 +1353,7 @@ export async function listErroredRuns(options?: {
|
|
|
1312
1353
|
status: string;
|
|
1313
1354
|
errorCode: string | null;
|
|
1314
1355
|
errorDetail: string | null;
|
|
1356
|
+
terminalReason: string | null;
|
|
1315
1357
|
startedAt: number;
|
|
1316
1358
|
completedAt: number | null;
|
|
1317
1359
|
durationMs: number | null;
|
|
@@ -1323,7 +1365,7 @@ export async function listErroredRuns(options?: {
|
|
|
1323
1365
|
const since =
|
|
1324
1366
|
options?.sinceMs && options.sinceMs > 0 ? Date.now() - options.sinceMs : 0;
|
|
1325
1367
|
const { rows } = await client.execute({
|
|
1326
|
-
sql: `SELECT id, thread_id, turn_id, status, error_code, error_detail, started_at, completed_at
|
|
1368
|
+
sql: `SELECT id, thread_id, turn_id, status, error_code, error_detail, terminal_reason, started_at, completed_at
|
|
1327
1369
|
FROM agent_runs
|
|
1328
1370
|
WHERE status IN ('errored', 'aborted')
|
|
1329
1371
|
AND COALESCE(completed_at, started_at) >= ?
|
|
@@ -1339,6 +1381,7 @@ export async function listErroredRuns(options?: {
|
|
|
1339
1381
|
status: string;
|
|
1340
1382
|
error_code: string | null;
|
|
1341
1383
|
error_detail: string | null;
|
|
1384
|
+
terminal_reason: string | null;
|
|
1342
1385
|
started_at: number | string;
|
|
1343
1386
|
completed_at: number | string | null;
|
|
1344
1387
|
};
|
|
@@ -1352,6 +1395,7 @@ export async function listErroredRuns(options?: {
|
|
|
1352
1395
|
status: row.status,
|
|
1353
1396
|
errorCode: row.error_code ?? null,
|
|
1354
1397
|
errorDetail: row.error_detail ?? null,
|
|
1398
|
+
terminalReason: row.terminal_reason ?? null,
|
|
1355
1399
|
startedAt,
|
|
1356
1400
|
completedAt,
|
|
1357
1401
|
durationMs: completedAt == null ? null : completedAt - startedAt,
|
|
@@ -712,6 +712,7 @@ export interface UpdateThreadDataOptions {
|
|
|
712
712
|
preserveExistingQueuedMessages?: boolean;
|
|
713
713
|
preserveExistingTopLevelKeys?: boolean;
|
|
714
714
|
maxAttempts?: number;
|
|
715
|
+
ignoreConflicts?: boolean;
|
|
715
716
|
}
|
|
716
717
|
|
|
717
718
|
function parseThreadData(value: string): any {
|
|
@@ -792,6 +793,7 @@ export async function updateThreadData(
|
|
|
792
793
|
}
|
|
793
794
|
|
|
794
795
|
if (lastConflict) {
|
|
796
|
+
if (options.ignoreConflicts) return;
|
|
795
797
|
throw new Error(
|
|
796
798
|
`Failed to update chat thread ${id} after concurrent write conflicts.`,
|
|
797
799
|
);
|
|
@@ -329,15 +329,18 @@ iteration, or a human-in-the-loop choice among design directions.
|
|
|
329
329
|
overview board and renders an inline chat choice with one button per screen
|
|
330
330
|
name. After the user picks, delete the unchosen variant screens and continue
|
|
331
331
|
from the kept screen by first calling \`get-design-snapshot\` with that
|
|
332
|
-
screen's \`fileId\`, then
|
|
332
|
+
screen's \`fileId\`, then calling \`edit-design\` on that same \`fileId\` in a
|
|
333
|
+
bounded single-file pass. Use \`mode: "replace-file"\` when expanding the
|
|
334
|
+
representative placeholder into the full chosen direction. Do not call
|
|
335
|
+
\`generate-design\` after a variant pick.
|
|
333
336
|
- If the chat choice buttons are not available in the host, ask the user to
|
|
334
337
|
tell you the screen name they prefer. The variants are already real screens
|
|
335
338
|
on the board, so do not ask them to paste HTML or copy a generated handoff
|
|
336
339
|
summary.
|
|
337
340
|
- For direct refinements to an already chosen direction, call
|
|
338
341
|
\`get-design-snapshot\`, edit from the current tuned HTML, and use
|
|
339
|
-
\`edit-design\` for surgical changes
|
|
340
|
-
|
|
342
|
+
\`edit-design\` for surgical changes or \`mode: "replace-file"\` for a bounded
|
|
343
|
+
selected-file replacement. Use \`generate-design\` for new files only.
|
|
341
344
|
- Use \`export-coding-handoff\` when the user wants to implement the chosen
|
|
342
345
|
design in a codebase.
|
|
343
346
|
|
|
@@ -353,9 +356,10 @@ iteration, or a human-in-the-loop choice among design directions.
|
|
|
353
356
|
5. After \`present-design-variants\`, wait for the user's pick before
|
|
354
357
|
generating the next version. Keep the chosen screen, delete the other
|
|
355
358
|
variant screens, call \`get-design-snapshot\` with \`fileId\` for the kept
|
|
356
|
-
screen, then
|
|
357
|
-
\`
|
|
358
|
-
|
|
359
|
+
screen, then call \`edit-design\` on that same \`fileId\` in a bounded pass.
|
|
360
|
+
Use \`mode: "replace-file"\` when expanding the representative placeholder
|
|
361
|
+
into the full chosen direction. Do not call \`generate-design\` after a
|
|
362
|
+
variant pick. Stop after the first successful \`edit-design\` save.
|
|
359
363
|
|
|
360
364
|
## Design Quality Bar
|
|
361
365
|
|
|
@@ -601,6 +601,31 @@ export function resolveAssistantChatSubmitIntent({
|
|
|
601
601
|
return requestedIntent ?? "immediate";
|
|
602
602
|
}
|
|
603
603
|
|
|
604
|
+
export function resolveAssistantChatRunningState({
|
|
605
|
+
forceStopped,
|
|
606
|
+
isRuntimeRunning,
|
|
607
|
+
isReconnecting,
|
|
608
|
+
optimisticRunning,
|
|
609
|
+
isAutoResuming,
|
|
610
|
+
}: {
|
|
611
|
+
forceStopped: boolean;
|
|
612
|
+
isRuntimeRunning: boolean;
|
|
613
|
+
isReconnecting: boolean;
|
|
614
|
+
optimisticRunning: boolean;
|
|
615
|
+
isAutoResuming: boolean;
|
|
616
|
+
}): { isRunning: boolean; showRunningInUI: boolean } {
|
|
617
|
+
const isRunning =
|
|
618
|
+
!forceStopped && (isRuntimeRunning || isReconnecting || optimisticRunning);
|
|
619
|
+
return {
|
|
620
|
+
isRunning,
|
|
621
|
+
// During auto-continuation, assistant-ui can briefly mark the message done
|
|
622
|
+
// between chunks even though the adapter is about to POST the next run.
|
|
623
|
+
// Keep the visible chat state running so the latest assistant message shows
|
|
624
|
+
// Thinking/Resuming and does not expose footer actions prematurely.
|
|
625
|
+
showRunningInUI: !forceStopped && (isRunning || isAutoResuming),
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
604
629
|
type QueuedMessage = {
|
|
605
630
|
id: string;
|
|
606
631
|
text: string;
|
|
@@ -1378,14 +1403,17 @@ const AssistantChatInner = forwardRef<
|
|
|
1378
1403
|
// queueing forever" state where isReconnecting or isRuntimeRunning gets
|
|
1379
1404
|
// wedged (e.g. after a tab refresh + stop during reconnect).
|
|
1380
1405
|
const [forceStopped, setForceStopped] = useState(false);
|
|
1381
|
-
// Real running state
|
|
1382
|
-
//
|
|
1383
|
-
//
|
|
1384
|
-
const isRunning =
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1406
|
+
// Real running state drives submission/queue gating; UI running also covers
|
|
1407
|
+
// short auto-continuation gaps so the latest assistant message does not flash
|
|
1408
|
+
// into a done state while the agent is still working.
|
|
1409
|
+
const { isRunning, showRunningInUI } = resolveAssistantChatRunningState({
|
|
1410
|
+
forceStopped,
|
|
1411
|
+
isRuntimeRunning,
|
|
1412
|
+
isReconnecting,
|
|
1413
|
+
optimisticRunning,
|
|
1414
|
+
isAutoResuming,
|
|
1415
|
+
});
|
|
1416
|
+
const textStreaming = showRunningInUI || externalStreaming;
|
|
1389
1417
|
// A revealed activity label wins; otherwise stay a steady "Thinking" by
|
|
1390
1418
|
// default. We only surface "Reconnecting" while we are actively replaying
|
|
1391
1419
|
// recovered content (reconnectContent populated). A bare reconnect with no
|
|
@@ -2265,6 +2293,8 @@ const AssistantChatInner = forwardRef<
|
|
|
2265
2293
|
|
|
2266
2294
|
useEffect(() => {
|
|
2267
2295
|
if (!authError) return;
|
|
2296
|
+
const shouldCaptureStuckAuthCard =
|
|
2297
|
+
authSessionAvailable || authError.sessionExpired;
|
|
2268
2298
|
// Auto-recovery (`checkAuthSession`) runs immediately + at 250ms. If the
|
|
2269
2299
|
// card is still showing 3 seconds later, recovery failed and the user
|
|
2270
2300
|
// is about to hit "Refresh chat" — that's the "Reload UI required"
|
|
@@ -2273,6 +2303,7 @@ const AssistantChatInner = forwardRef<
|
|
|
2273
2303
|
void (async () => {
|
|
2274
2304
|
const hasSession = await checkAuthSession();
|
|
2275
2305
|
if (hasSession) return;
|
|
2306
|
+
if (!shouldCaptureStuckAuthCard) return;
|
|
2276
2307
|
captureError(new Error("agent-chat:auth_error_card_stuck"), {
|
|
2277
2308
|
tags: {
|
|
2278
2309
|
context: "agent-native-chat",
|
|
@@ -3211,7 +3242,7 @@ const AssistantChatInner = forwardRef<
|
|
|
3211
3242
|
<CheckpointContext.Provider value={checkpointCtx}>
|
|
3212
3243
|
<MessageActionsContext.Provider value={messageActionsCtx}>
|
|
3213
3244
|
<ApprovalContext.Provider value={approvalCtx}>
|
|
3214
|
-
<ChatRunningContext.Provider value={
|
|
3245
|
+
<ChatRunningContext.Provider value={showRunningInUI}>
|
|
3215
3246
|
<TextStreamingContext.Provider value={textStreaming}>
|
|
3216
3247
|
<div
|
|
3217
3248
|
data-agent-empty-state={
|
|
@@ -807,6 +807,21 @@ function lastActivityTool(
|
|
|
807
807
|
return undefined;
|
|
808
808
|
}
|
|
809
809
|
|
|
810
|
+
function formatActivityTrail(
|
|
811
|
+
trail: readonly AgentActivityTrailEntry[],
|
|
812
|
+
): string | undefined {
|
|
813
|
+
const items = trail
|
|
814
|
+
.slice(-8)
|
|
815
|
+
.map((entry) => {
|
|
816
|
+
const label = entry.label.replace(/\s+/g, " ").trim();
|
|
817
|
+
const tool = entry.tool?.replace(/\s+/g, " ").trim();
|
|
818
|
+
if (label && tool && label !== tool) return `${label} (${tool})`;
|
|
819
|
+
return label || tool || "";
|
|
820
|
+
})
|
|
821
|
+
.filter(Boolean);
|
|
822
|
+
return items.length > 0 ? items.join(" > ") : undefined;
|
|
823
|
+
}
|
|
824
|
+
|
|
810
825
|
function lastUnresolvedToolActivity(
|
|
811
826
|
content: ContentPart[],
|
|
812
827
|
): string | undefined {
|
|
@@ -928,9 +943,9 @@ function incrementalActionGuidance(tool: string): string | undefined {
|
|
|
928
943
|
return "create a compact working v1 with `create-extension`, then use focused `update-extension` edits for refinements";
|
|
929
944
|
case "generate-design":
|
|
930
945
|
case "update-design":
|
|
931
|
-
return
|
|
946
|
+
return 'if an existing design file or snapshot is already in history, especially after a Design variant pick, stop retrying `generate-design`: call `get-design-snapshot` for the selected file, then call `edit-design` once on that same `fileId` (`mode: "replace-file"` for a compact full-file replacement, or search/replace for smaller edits). Use `generate-design` only for a brand-new compact first file when no target file exists yet';
|
|
932
947
|
case "present-design-variants":
|
|
933
|
-
return
|
|
948
|
+
return 'call `present-design-variants` with concise labels, descriptions, accent colors, and feature bullets; omit large `content` HTML when needed so the action can render compact representative screens. After the user picks a direction, delete unchosen screens, snapshot the selected `fileId`, and refine that same file with `edit-design` (`mode: "replace-file"` for placeholder expansion). Do not call `generate-design` after the variant pick';
|
|
934
949
|
case "create-visual-plan":
|
|
935
950
|
case "create-ui-plan":
|
|
936
951
|
case "create-plan-design":
|
|
@@ -1439,14 +1454,36 @@ export function createAgentChatAdapter(
|
|
|
1439
1454
|
let visibleContinuationPrefix: ContentPart[] = [];
|
|
1440
1455
|
let lastAutoContinueReason: string | null = null;
|
|
1441
1456
|
let lastRecoverableRunError: AgentAutoContinueErrorInfo | null = null;
|
|
1457
|
+
let lastActivityTrail: AgentActivityTrailEntry[] = [];
|
|
1442
1458
|
const attemptedRunIds: string[] = [];
|
|
1443
1459
|
let authRecoveryAttempted = false;
|
|
1444
1460
|
let continuationToolCallCounter = 0;
|
|
1445
1461
|
const nextContinuationToolCallId = () =>
|
|
1446
1462
|
`continuation_tc_${++continuationToolCallCounter}`;
|
|
1447
1463
|
|
|
1464
|
+
const runDebugContextDetails = (): string => {
|
|
1465
|
+
const pageOrigin =
|
|
1466
|
+
typeof window !== "undefined" && window.location?.origin
|
|
1467
|
+
? window.location.origin
|
|
1468
|
+
: "";
|
|
1469
|
+
return [
|
|
1470
|
+
`api_url: ${apiUrl}`,
|
|
1471
|
+
pageOrigin ? `page_origin: ${pageOrigin}` : "",
|
|
1472
|
+
tabId ? `tab_id: ${tabId}` : "",
|
|
1473
|
+
threadId ? `thread_id: ${threadId}` : "",
|
|
1474
|
+
`turn_id: ${turnId}`,
|
|
1475
|
+
runId ? `current_run: ${runId}` : "",
|
|
1476
|
+
attemptedRunIds.length > 0
|
|
1477
|
+
? `attempted_runs: ${attemptedRunIds.join(", ")}`
|
|
1478
|
+
: "",
|
|
1479
|
+
]
|
|
1480
|
+
.filter(Boolean)
|
|
1481
|
+
.join("\n");
|
|
1482
|
+
};
|
|
1483
|
+
|
|
1448
1484
|
const connectionRecoveryDetails = (): string => {
|
|
1449
1485
|
return [
|
|
1486
|
+
runDebugContextDetails(),
|
|
1450
1487
|
lastAutoContinueReason
|
|
1451
1488
|
? `last_auto_continue_reason: ${lastAutoContinueReason}`
|
|
1452
1489
|
: "",
|
|
@@ -1468,10 +1505,10 @@ export function createAgentChatAdapter(
|
|
|
1468
1505
|
lastPreparingToolName
|
|
1469
1506
|
? `last_preparing_tool: ${lastPreparingToolName}`
|
|
1470
1507
|
: "",
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
? `attempted_runs: ${attemptedRunIds.join(", ")}`
|
|
1508
|
+
formatActivityTrail(lastActivityTrail)
|
|
1509
|
+
? `activity_trail: ${formatActivityTrail(lastActivityTrail)}`
|
|
1474
1510
|
: "",
|
|
1511
|
+
`total_transient_continuations: ${totalTransientContinuationAttempts}`,
|
|
1475
1512
|
]
|
|
1476
1513
|
.filter(Boolean)
|
|
1477
1514
|
.join("\n");
|
|
@@ -1559,6 +1596,7 @@ export function createAgentChatAdapter(
|
|
|
1559
1596
|
lastSeq,
|
|
1560
1597
|
contentParts: content.length,
|
|
1561
1598
|
attemptedRunIds: [...attemptedRunIds],
|
|
1599
|
+
activityTrail: [...lastActivityTrail],
|
|
1562
1600
|
startupRecoveryAttempts,
|
|
1563
1601
|
staleRunContinuationAttempts,
|
|
1564
1602
|
stalledTransientContinuationAttempts,
|
|
@@ -1867,6 +1905,7 @@ export function createAgentChatAdapter(
|
|
|
1867
1905
|
completedToolName?: string;
|
|
1868
1906
|
} => {
|
|
1869
1907
|
lastAutoContinueReason = signal.reason;
|
|
1908
|
+
lastActivityTrail = [...signal.activityTrail];
|
|
1870
1909
|
if (signal.errorInfo) {
|
|
1871
1910
|
lastRecoverableRunError = signal.errorInfo;
|
|
1872
1911
|
}
|
|
@@ -291,7 +291,7 @@ function PositionedDiagram({
|
|
|
291
291
|
return (
|
|
292
292
|
<span
|
|
293
293
|
key={`${edge.from}-${edge.to}-${index}-label`}
|
|
294
|
-
className="absolute z-10 max-w-[130px] -translate-x-1/2 -translate-y-1/2 rounded-full border border-border bg-background px-2 py-0.5 text-center text-[11px] font-semibold text-muted-foreground shadow-sm"
|
|
294
|
+
className="absolute z-10 max-w-[130px] -translate-x-1/2 -translate-y-1/2 break-words rounded-full border border-border bg-background px-2 py-0.5 text-center text-[11px] font-semibold text-muted-foreground shadow-sm [overflow-wrap:anywhere]"
|
|
295
295
|
style={{
|
|
296
296
|
left: `${(from.displayX + to.displayX) / 2}%`,
|
|
297
297
|
top: `${(from.displayY + to.displayY) / 2}%`,
|
|
@@ -305,7 +305,7 @@ function PositionedDiagram({
|
|
|
305
305
|
{displayNodesForDirection.map((node, index) => (
|
|
306
306
|
<article
|
|
307
307
|
key={node.id}
|
|
308
|
-
className="absolute z-20 -translate-x-1/2 -translate-y-1/2 rounded-xl border-2 border-border bg-background p-3 text-foreground shadow-sm"
|
|
308
|
+
className="absolute z-20 -translate-x-1/2 -translate-y-1/2 break-words rounded-xl border-2 border-border bg-background p-3 text-foreground shadow-sm [overflow-wrap:anywhere]"
|
|
309
309
|
style={{
|
|
310
310
|
left: `${node.displayX}%`,
|
|
311
311
|
top: `${node.displayY}%`,
|
|
@@ -485,7 +485,7 @@ function SequenceDiagram({
|
|
|
485
485
|
{next && (
|
|
486
486
|
<div className="grid min-w-[72px] justify-items-center gap-1 text-muted-foreground">
|
|
487
487
|
{edge?.label && (
|
|
488
|
-
<span className="max-w-[96px]
|
|
488
|
+
<span className="max-w-[96px] whitespace-normal break-words rounded-full border border-border px-2 py-0.5 text-center text-[11px] font-semibold [overflow-wrap:anywhere]">
|
|
489
489
|
{edge.label}
|
|
490
490
|
</span>
|
|
491
491
|
)}
|
|
@@ -784,19 +784,31 @@ function assistantMessageStatusIsTerminal(message: {
|
|
|
784
784
|
return statusType === "complete" || statusType === "incomplete";
|
|
785
785
|
}
|
|
786
786
|
|
|
787
|
+
export function assistantMessageHasUnresolvedTool(content: unknown): boolean {
|
|
788
|
+
if (!Array.isArray(content)) return false;
|
|
789
|
+
return content.some((part): boolean => {
|
|
790
|
+
if (!part || typeof part !== "object") return false;
|
|
791
|
+
const record = part as { type?: unknown; result?: unknown };
|
|
792
|
+
return record.type === "tool-call" && record.result === undefined;
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
|
|
787
796
|
export function shouldShowAssistantMessageFooter({
|
|
788
797
|
isLast,
|
|
789
798
|
chatRunning,
|
|
790
799
|
hasRenderableContent,
|
|
791
800
|
statusIsTerminal,
|
|
801
|
+
hasUnresolvedTool,
|
|
792
802
|
}: {
|
|
793
803
|
isLast: boolean;
|
|
794
804
|
chatRunning: boolean;
|
|
795
805
|
hasRenderableContent: boolean;
|
|
796
806
|
statusIsTerminal: boolean;
|
|
807
|
+
hasUnresolvedTool?: boolean;
|
|
797
808
|
}): boolean {
|
|
798
809
|
if (!hasRenderableContent) return false;
|
|
799
810
|
if (!isLast) return true;
|
|
811
|
+
if (hasUnresolvedTool) return false;
|
|
800
812
|
return !chatRunning && statusIsTerminal;
|
|
801
813
|
}
|
|
802
814
|
|
|
@@ -813,11 +825,13 @@ export function AssistantMessage() {
|
|
|
813
825
|
thread.messages.length > 0 &&
|
|
814
826
|
thread.messages[thread.messages.length - 1].id === msg.id;
|
|
815
827
|
const hasRenderableContent = assistantMessageHasRenderableContent(msg);
|
|
828
|
+
const hasUnresolvedTool = assistantMessageHasUnresolvedTool(msg.content);
|
|
816
829
|
const isComplete = shouldShowAssistantMessageFooter({
|
|
817
830
|
isLast,
|
|
818
831
|
chatRunning,
|
|
819
832
|
hasRenderableContent,
|
|
820
833
|
statusIsTerminal: assistantMessageStatusIsTerminal(msg),
|
|
834
|
+
hasUnresolvedTool,
|
|
821
835
|
});
|
|
822
836
|
const cpCtx = React.useContext(CheckpointContext);
|
|
823
837
|
|
|
@@ -907,6 +921,9 @@ export function AssistantMessage() {
|
|
|
907
921
|
{isComplete && hasCodeAgentTools && msgContent && (
|
|
908
922
|
<FilesChangedSummary parts={msgContent} />
|
|
909
923
|
)}
|
|
924
|
+
{isLast && hasUnresolvedTool && !chatRunning && (
|
|
925
|
+
<RunningActivityStatus label="Thinking" />
|
|
926
|
+
)}
|
|
910
927
|
</div>
|
|
911
928
|
{isComplete && (
|
|
912
929
|
<div className="mt-1 flex items-center justify-between">
|