@agent-native/core 0.85.3 → 0.85.4
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 +6 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/run-manager.ts +20 -10
- package/corpus/core/src/client/AssistantChat.tsx +13 -1
- package/corpus/templates/design/AGENTS.md +6 -4
- package/corpus/templates/design/actions/present-design-variants.ts +10 -5
- package/corpus/templates/design/changelog/2026-07-03-variant-picks-build-the-selected-app.md +5 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +19 -11
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +11 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/observability/routes.d.ts +7 -7
- package/dist/progress/routes.d.ts +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/package.json +1 -1
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.85.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 00c6f16: Keep chat restore and active-run polling on the durable background timeout budget so healthy long-running background turns do not look stale to the browser.
|
|
8
|
+
|
|
3
9
|
## 0.85.3
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.85.
|
|
3
|
+
"version": "0.85.4",
|
|
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": {
|
|
@@ -1362,20 +1362,32 @@ export async function getActiveRunForThreadAsync(threadId: string): Promise<{
|
|
|
1362
1362
|
// the full conversation from completed runs via SSE.
|
|
1363
1363
|
const memRun = getActiveRunForThread(threadId);
|
|
1364
1364
|
if (memRun && (memRun.status === "running" || memRun.events.length > 0)) {
|
|
1365
|
+
const sqlSnapshot = await fetchRunThreadSnapshot(memRun.runId, threadId);
|
|
1366
|
+
const status = sqlSnapshot?.status ?? memRun.status;
|
|
1367
|
+
const heartbeatAt =
|
|
1368
|
+
status === "running"
|
|
1369
|
+
? Date.now()
|
|
1370
|
+
: (sqlSnapshot?.heartbeatAt ?? memRun.startedAt);
|
|
1365
1371
|
return {
|
|
1366
1372
|
runId: memRun.runId,
|
|
1367
1373
|
threadId: memRun.threadId,
|
|
1368
1374
|
turnId: memRun.turnId,
|
|
1369
|
-
status
|
|
1375
|
+
status,
|
|
1370
1376
|
// In-memory means this isolate is the producer. By definition, the
|
|
1371
|
-
// heartbeat is fresh as of "now"
|
|
1372
|
-
|
|
1377
|
+
// heartbeat is fresh as of "now" while the run is still running. Once
|
|
1378
|
+
// SQL has terminal truth, prefer that timestamp so a stale in-memory
|
|
1379
|
+
// buffer cannot keep the browser believing a finished background run is
|
|
1380
|
+
// still alive.
|
|
1381
|
+
heartbeatAt,
|
|
1373
1382
|
// For an in-memory run we don't have a separate "last event emit"
|
|
1374
1383
|
// timestamp tracked in JS — the SQL bump is throttled per-second.
|
|
1375
1384
|
// Read it back from SQL on demand. For the common case the SQL row
|
|
1376
1385
|
// is well under 1s old; if it isn't, the stuck-detector will pick
|
|
1377
1386
|
// it up on the next poll cycle.
|
|
1378
|
-
lastProgressAt:
|
|
1387
|
+
lastProgressAt: sqlSnapshot?.lastProgressAt ?? null,
|
|
1388
|
+
dispatchMode: sqlSnapshot?.dispatchMode ?? null,
|
|
1389
|
+
terminalReason: sqlSnapshot?.terminalReason ?? null,
|
|
1390
|
+
diagStage: sqlSnapshot?.diagStage ?? null,
|
|
1379
1391
|
};
|
|
1380
1392
|
}
|
|
1381
1393
|
// Fall back to SQL — also surface recently terminated runs so the client
|
|
@@ -1454,16 +1466,14 @@ export async function getActiveRunForThreadAsync(threadId: string): Promise<{
|
|
|
1454
1466
|
return null;
|
|
1455
1467
|
}
|
|
1456
1468
|
|
|
1457
|
-
async function
|
|
1469
|
+
async function fetchRunThreadSnapshot(runId: string, threadId: string) {
|
|
1458
1470
|
try {
|
|
1459
|
-
const run = await getRunById(runId);
|
|
1460
|
-
if (!run) return null;
|
|
1461
1471
|
// `getRunById` returns a narrow projection today; ask for the row via
|
|
1462
|
-
// the thread lookup which carries
|
|
1463
|
-
const byThread = await getRunByThread(
|
|
1472
|
+
// the thread lookup which carries dispatch/terminal/progress fields.
|
|
1473
|
+
const byThread = await getRunByThread(threadId, {
|
|
1464
1474
|
includeTerminal: true,
|
|
1465
1475
|
});
|
|
1466
|
-
if (byThread && byThread.id === runId) return byThread
|
|
1476
|
+
if (byThread && byThread.id === runId) return byThread;
|
|
1467
1477
|
return null;
|
|
1468
1478
|
} catch {
|
|
1469
1479
|
return null;
|
|
@@ -239,6 +239,7 @@ function createUserMessageRunConfig(
|
|
|
239
239
|
const PENDING_SELECTION_KEY = "pending-selection-context";
|
|
240
240
|
const ACTIVE_RUN_CLEAR_TIMEOUT_MS = 5_000;
|
|
241
241
|
const ACTIVE_RUN_STUCK_THRESHOLD_MS = 90_000;
|
|
242
|
+
const BACKGROUND_ACTIVE_RUN_STUCK_THRESHOLD_MS = 13 * 60_000;
|
|
242
243
|
const ACTIVE_RUN_POLL_INTERVAL_MS = 150;
|
|
243
244
|
const AUTO_RESUME_STATUS_TIMEOUT_MS = 30_000;
|
|
244
245
|
// How long a single activity (model call, tool prep, long tool) must stay
|
|
@@ -271,15 +272,24 @@ function isReplayableTerminalRun(runInfo: ActiveRunLookup): boolean {
|
|
|
271
272
|
);
|
|
272
273
|
}
|
|
273
274
|
|
|
275
|
+
function activeRunStuckThresholdMs(runInfo: ActiveRunLookup): number {
|
|
276
|
+
const dispatchMode =
|
|
277
|
+
typeof runInfo.dispatchMode === "string" ? runInfo.dispatchMode : "";
|
|
278
|
+
return dispatchMode.startsWith("background")
|
|
279
|
+
? BACKGROUND_ACTIVE_RUN_STUCK_THRESHOLD_MS
|
|
280
|
+
: ACTIVE_RUN_STUCK_THRESHOLD_MS;
|
|
281
|
+
}
|
|
282
|
+
|
|
274
283
|
function activeRunLooksStale(runInfo: ActiveRunLookup): boolean {
|
|
275
284
|
const lastProgressAt =
|
|
276
285
|
typeof runInfo.lastProgressAt === "number" ? runInfo.lastProgressAt : null;
|
|
277
286
|
const nowMs =
|
|
278
287
|
typeof runInfo.serverNow === "number" ? runInfo.serverNow : Date.now();
|
|
288
|
+
const thresholdMs = activeRunStuckThresholdMs(runInfo);
|
|
279
289
|
return (
|
|
280
290
|
runInfo.status === "running" &&
|
|
281
291
|
lastProgressAt != null &&
|
|
282
|
-
nowMs - lastProgressAt >
|
|
292
|
+
nowMs - lastProgressAt > thresholdMs
|
|
283
293
|
);
|
|
284
294
|
}
|
|
285
295
|
|
|
@@ -1894,6 +1904,7 @@ const AssistantChatInner = forwardRef<
|
|
|
1894
1904
|
reconnectAbortRef.current = abortCtrl;
|
|
1895
1905
|
let reconnectTerminalReason: AgentAutoContinueSignal["reason"] | null =
|
|
1896
1906
|
null;
|
|
1907
|
+
const reconnectStuckThresholdMs = activeRunStuckThresholdMs(runInfo);
|
|
1897
1908
|
|
|
1898
1909
|
const watchdog = setInterval(async () => {
|
|
1899
1910
|
try {
|
|
@@ -1937,6 +1948,7 @@ const AssistantChatInner = forwardRef<
|
|
|
1937
1948
|
!reconnectProgressTimedOut({
|
|
1938
1949
|
lastProgressAt: lastReconnectProgressAt,
|
|
1939
1950
|
now: Date.now(),
|
|
1951
|
+
thresholdMs: reconnectStuckThresholdMs,
|
|
1940
1952
|
})
|
|
1941
1953
|
) {
|
|
1942
1954
|
return;
|
|
@@ -243,10 +243,12 @@ patterns live in `.agents/skills/`.
|
|
|
243
243
|
wait for the user to pick one in chat, delete each other generated variant
|
|
244
244
|
screen with `delete-file` at most once, call `get-design-snapshot` exactly
|
|
245
245
|
once with the selected screen's `fileId`, then call `edit-design` exactly once
|
|
246
|
-
on that same `fileId` for follow-up refinement.
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
a variant
|
|
246
|
+
on that same `fileId` for follow-up refinement. The kept variant screen is a
|
|
247
|
+
representative direction, not the final deliverable: use `mode:
|
|
248
|
+
"replace-file"` to replace it with the actual requested app/product UI in the
|
|
249
|
+
chosen visual style. Do not leave a direction board, variant brief, summary
|
|
250
|
+
card, or prose description as the final screen. Do not repeat delete/snapshot
|
|
251
|
+
cycles, and do not call `generate-design` after a variant pick.
|
|
250
252
|
- If inline chat choice buttons are unavailable, the user can tell you the
|
|
251
253
|
preferred screen name. Do not show a separate variant picker or ask them to
|
|
252
254
|
paste a copyable handoff summary.
|
|
@@ -42,13 +42,18 @@ const FALLBACK_INSTRUCTIONS =
|
|
|
42
42
|
"a screen by name if the inline buttons are not available; after they pick, " +
|
|
43
43
|
"delete each other variant screen at most once, call get-design-snapshot with fileId for " +
|
|
44
44
|
"the kept screen once, then call edit-design on that same fileId in a bounded pass. " +
|
|
45
|
-
'Use mode "replace-file"
|
|
45
|
+
'Use mode "replace-file" to replace the representative direction screen with ' +
|
|
46
|
+
"the actual requested product UI; do not leave a direction board, summary card, " +
|
|
47
|
+
"or variant brief as the final result. " +
|
|
46
48
|
"Do not call generate-design after a variant pick.";
|
|
47
49
|
|
|
48
50
|
const VARIANT_PICK_SUBMIT_MESSAGE =
|
|
49
51
|
"Use this design direction. Keep the selected screen, clean up each other " +
|
|
50
52
|
"variant screen at most once, read only the kept screen, then update that " +
|
|
51
|
-
"same screen in one bounded pass
|
|
53
|
+
"same screen in one bounded pass into the full requested app/product UI. " +
|
|
54
|
+
"The selected screen is only a representative direction; the final saved " +
|
|
55
|
+
"screen must not be a direction board, variant brief, or summary card. " +
|
|
56
|
+
"If a cleanup action reports a screen was " +
|
|
52
57
|
"already missing, continue. Use the exact file ids and tool instructions in " +
|
|
53
58
|
"the selected answer below. Do not repeat cleanup/read cycles, do not create " +
|
|
54
59
|
"a new index.html, and stop after the first successful screen update.";
|
|
@@ -580,7 +585,7 @@ export default defineAction({
|
|
|
580
585
|
await writeAppStateForCurrentTab("guided-questions", {
|
|
581
586
|
title: prompt ?? "Pick a direction",
|
|
582
587
|
description:
|
|
583
|
-
"All options are on the board. Choose one to keep; I will delete the others, read only the kept screen, and
|
|
588
|
+
"All options are on the board. Choose one to keep; I will delete the others, read only the kept screen, and turn that direction into the final requested screen.",
|
|
584
589
|
submitLabel: "Use selected direction",
|
|
585
590
|
submitMessage: VARIANT_PICK_SUBMIT_MESSAGE,
|
|
586
591
|
skipLabel: "Show another set",
|
|
@@ -608,7 +613,7 @@ export default defineAction({
|
|
|
608
613
|
value:
|
|
609
614
|
`Keep "${screen.label}" (${screen.filename}, file id ${screen.id}) ` +
|
|
610
615
|
`from variant set ${variantSetId}. Delete each other variant screen at most once: ${otherScreens}. If delete-file says a screen is already missing, continue. ` +
|
|
611
|
-
`Then call get-design-snapshot exactly once with designId ${designId} and fileId ${screen.id} (filename ${screen.filename}), then call edit-design with fileId ${screen.id} on that same kept file in a bounded single-file pass. Use mode "replace-file"
|
|
616
|
+
`Then call get-design-snapshot exactly once with designId ${designId} and fileId ${screen.id} (filename ${screen.filename}), then call edit-design with fileId ${screen.id} on that same kept file in a bounded single-file pass. Use mode "replace-file" to replace the representative direction screen with the full requested app/product UI in the chosen visual style. The final saved screen must be the actual usable UI requested by the user, not a direction board, variant brief, summary card, or description of the direction. Do not call generate-design after this variant pick, do not repeat delete/snapshot cycles, do not create index.html, and do not resend a huge payload. Stop after the first successful edit-design save.`,
|
|
612
617
|
};
|
|
613
618
|
}),
|
|
614
619
|
},
|
|
@@ -626,7 +631,7 @@ export default defineAction({
|
|
|
626
631
|
embed: true,
|
|
627
632
|
fallbackInstructions: FALLBACK_INSTRUCTIONS,
|
|
628
633
|
nextRequiredAction:
|
|
629
|
-
'Wait for the user to pick a screen in chat. Then delete each unchosen variant screen with delete-file at most once, call get-design-snapshot exactly once with fileId for the chosen screen, and call edit-design with that same fileId in a bounded pass. Use mode "replace-file"
|
|
634
|
+
'Wait for the user to pick a screen in chat. Then delete each unchosen variant screen with delete-file at most once, call get-design-snapshot exactly once with fileId for the chosen screen, and call edit-design with that same fileId in a bounded pass. Use mode "replace-file" to replace the representative direction screen with the full requested app/product UI in the chosen visual style. Do not leave a direction board, variant brief, or summary card as the final result. Do not repeat delete/snapshot cycles. Do not call generate-design after a variant pick. Stop after the first successful edit-design save.',
|
|
630
635
|
};
|
|
631
636
|
},
|
|
632
637
|
link: ({ result }) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-manager.d.ts","sourceRoot":"","sources":["../../src/agent/run-manager.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"run-manager.d.ts","sourceRoot":"","sources":["../../src/agent/run-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -1118,20 +1118,31 @@ export async function getActiveRunForThreadAsync(threadId) {
|
|
|
1118
1118
|
// the full conversation from completed runs via SSE.
|
|
1119
1119
|
const memRun = getActiveRunForThread(threadId);
|
|
1120
1120
|
if (memRun && (memRun.status === "running" || memRun.events.length > 0)) {
|
|
1121
|
+
const sqlSnapshot = await fetchRunThreadSnapshot(memRun.runId, threadId);
|
|
1122
|
+
const status = sqlSnapshot?.status ?? memRun.status;
|
|
1123
|
+
const heartbeatAt = status === "running"
|
|
1124
|
+
? Date.now()
|
|
1125
|
+
: (sqlSnapshot?.heartbeatAt ?? memRun.startedAt);
|
|
1121
1126
|
return {
|
|
1122
1127
|
runId: memRun.runId,
|
|
1123
1128
|
threadId: memRun.threadId,
|
|
1124
1129
|
turnId: memRun.turnId,
|
|
1125
|
-
status
|
|
1130
|
+
status,
|
|
1126
1131
|
// In-memory means this isolate is the producer. By definition, the
|
|
1127
|
-
// heartbeat is fresh as of "now"
|
|
1128
|
-
|
|
1132
|
+
// heartbeat is fresh as of "now" while the run is still running. Once
|
|
1133
|
+
// SQL has terminal truth, prefer that timestamp so a stale in-memory
|
|
1134
|
+
// buffer cannot keep the browser believing a finished background run is
|
|
1135
|
+
// still alive.
|
|
1136
|
+
heartbeatAt,
|
|
1129
1137
|
// For an in-memory run we don't have a separate "last event emit"
|
|
1130
1138
|
// timestamp tracked in JS — the SQL bump is throttled per-second.
|
|
1131
1139
|
// Read it back from SQL on demand. For the common case the SQL row
|
|
1132
1140
|
// is well under 1s old; if it isn't, the stuck-detector will pick
|
|
1133
1141
|
// it up on the next poll cycle.
|
|
1134
|
-
lastProgressAt:
|
|
1142
|
+
lastProgressAt: sqlSnapshot?.lastProgressAt ?? null,
|
|
1143
|
+
dispatchMode: sqlSnapshot?.dispatchMode ?? null,
|
|
1144
|
+
terminalReason: sqlSnapshot?.terminalReason ?? null,
|
|
1145
|
+
diagStage: sqlSnapshot?.diagStage ?? null,
|
|
1135
1146
|
};
|
|
1136
1147
|
}
|
|
1137
1148
|
// Fall back to SQL — also surface recently terminated runs so the client
|
|
@@ -1211,18 +1222,15 @@ export async function getActiveRunForThreadAsync(threadId) {
|
|
|
1211
1222
|
}
|
|
1212
1223
|
return null;
|
|
1213
1224
|
}
|
|
1214
|
-
async function
|
|
1225
|
+
async function fetchRunThreadSnapshot(runId, threadId) {
|
|
1215
1226
|
try {
|
|
1216
|
-
const run = await getRunById(runId);
|
|
1217
|
-
if (!run)
|
|
1218
|
-
return null;
|
|
1219
1227
|
// `getRunById` returns a narrow projection today; ask for the row via
|
|
1220
|
-
// the thread lookup which carries
|
|
1221
|
-
const byThread = await getRunByThread(
|
|
1228
|
+
// the thread lookup which carries dispatch/terminal/progress fields.
|
|
1229
|
+
const byThread = await getRunByThread(threadId, {
|
|
1222
1230
|
includeTerminal: true,
|
|
1223
1231
|
});
|
|
1224
1232
|
if (byThread && byThread.id === runId)
|
|
1225
|
-
return byThread
|
|
1233
|
+
return byThread;
|
|
1226
1234
|
return null;
|
|
1227
1235
|
}
|
|
1228
1236
|
catch {
|