@agent-native/core 0.84.2 → 0.84.3
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 +8 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +9 -2
- package/corpus/core/src/agent/run-loop-with-resume.ts +38 -8
- package/corpus/core/src/agent/thread-data-builder.ts +6 -0
- package/corpus/core/src/client/agent-chat-adapter.ts +208 -9
- package/corpus/core/src/client/sharing/ShareButton.tsx +70 -21
- package/corpus/core/src/client/sse-event-processor.ts +15 -0
- package/corpus/templates/analytics/AGENTS.md +1 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +129 -100
- package/corpus/templates/design/changelog/2026-06-30-agent-chat-now-reports-saved-design-generations-clearly-when.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-apply-styles-now-appears-only-for-localhost-visual-edit-scre.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-share-options-now-make-export-and-agent-handoff-easier-to-no.md +6 -0
- package/corpus/templates/design/changelog/2026-07-01-share-general-access-menu-stays-open-when-choosing-organization.md +6 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +9 -2
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts +2 -2
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +31 -8
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +2 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +5 -0
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +179 -8
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +6 -0
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +25 -11
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +4 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +13 -0
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/package.json +1 -1
|
@@ -529,6 +529,34 @@ function hasContinuationProgress(content) {
|
|
|
529
529
|
? part.text.trim().length > 0
|
|
530
530
|
: part.result !== undefined);
|
|
531
531
|
}
|
|
532
|
+
function lastCompletedSideEffectTool(content) {
|
|
533
|
+
for (let i = content.length - 1; i >= 0; i--) {
|
|
534
|
+
const part = content[i];
|
|
535
|
+
if (part.type === "tool-call" &&
|
|
536
|
+
part.activity !== true &&
|
|
537
|
+
part.result !== undefined &&
|
|
538
|
+
part.isError !== true &&
|
|
539
|
+
part.completedSideEffect === true) {
|
|
540
|
+
return part;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return undefined;
|
|
544
|
+
}
|
|
545
|
+
function humanizeActionName(toolName) {
|
|
546
|
+
return toolName
|
|
547
|
+
.replace(/^agent:/, "")
|
|
548
|
+
.replace(/[-_]+/g, " ")
|
|
549
|
+
.trim();
|
|
550
|
+
}
|
|
551
|
+
function completedToolTimeoutMessage(toolName) {
|
|
552
|
+
if (toolName === "generate-design" || toolName === "update-design") {
|
|
553
|
+
return "The design was saved, but the assistant timed out before sending its final note. You can open the generated design from the completed tool card above.";
|
|
554
|
+
}
|
|
555
|
+
if (toolName === "present-design-variants") {
|
|
556
|
+
return "The design variants were saved, but the assistant timed out before sending its final note. You can review the completed variants from the tool card above.";
|
|
557
|
+
}
|
|
558
|
+
return `The ${humanizeActionName(toolName)} action completed, but the assistant timed out before sending its final response. The saved result is in the completed tool card above.`;
|
|
559
|
+
}
|
|
532
560
|
/**
|
|
533
561
|
* Signature of the *unique* sentence-like segments in a continuation's newly
|
|
534
562
|
* streamed text, used to detect a degenerate repetition loop. A stuck model
|
|
@@ -603,6 +631,8 @@ function toolContinuationKey(part) {
|
|
|
603
631
|
stableJson(part.args),
|
|
604
632
|
part.result === undefined ? "pending" : "done",
|
|
605
633
|
part.result ?? "",
|
|
634
|
+
part.isError === true ? "error" : "",
|
|
635
|
+
part.completedSideEffect === true ? "side-effect" : "",
|
|
606
636
|
part.activity === true ? "activity" : "tool",
|
|
607
637
|
part.mcpApp ? "mcp-app" : "",
|
|
608
638
|
].join("\u0000");
|
|
@@ -657,6 +687,8 @@ function incrementalActionGuidance(tool) {
|
|
|
657
687
|
case "generate-design":
|
|
658
688
|
case "update-design":
|
|
659
689
|
return "persist a minimal first version (fewer files) with `generate-design`, then refine individual files with `edit-design` search/replace instead of resending everything";
|
|
690
|
+
case "present-design-variants":
|
|
691
|
+
return "save compact but complete variant screens with `present-design-variants` first, keeping each HTML direction focused enough to finish, then refine the chosen direction with `generate-design` or `edit-design`";
|
|
660
692
|
case "create-visual-plan":
|
|
661
693
|
case "create-ui-plan":
|
|
662
694
|
case "create-plan-design":
|
|
@@ -1039,6 +1071,7 @@ export function createAgentChatAdapter(options) {
|
|
|
1039
1071
|
// its own budget rather than inheriting the prior payload's.
|
|
1040
1072
|
let lastInFlightToolSignature;
|
|
1041
1073
|
let repeatedInFlightToolCount = 0;
|
|
1074
|
+
let recoveryGaveUpOnInFlightTool = false;
|
|
1042
1075
|
const MAX_REPEATED_INFLIGHT_TOOL_STALLS = 3;
|
|
1043
1076
|
const continuationHistoryFragments = [];
|
|
1044
1077
|
const structuredContinuationFragments = [];
|
|
@@ -1077,6 +1110,9 @@ export function createAgentChatAdapter(options) {
|
|
|
1077
1110
|
.join("\n");
|
|
1078
1111
|
};
|
|
1079
1112
|
const exhaustedRecoveryMessage = (reason) => {
|
|
1113
|
+
if (recoveryGaveUpOnInFlightTool) {
|
|
1114
|
+
return "The agent got stuck waiting for the same tool to finish, so I stopped the automatic retries. The tool did not report a completed result.";
|
|
1115
|
+
}
|
|
1080
1116
|
if (recoveryGaveUpOnRepetition) {
|
|
1081
1117
|
return "The agent got stuck repeating the same response without finishing, so I stopped the automatic retries. This often happens when it tries to re-type a large pasted file into one action — starting a new chat, or asking for a smaller first step, usually gets it unstuck.";
|
|
1082
1118
|
}
|
|
@@ -1309,6 +1345,73 @@ export function createAgentChatAdapter(options) {
|
|
|
1309
1345
|
}
|
|
1310
1346
|
return false;
|
|
1311
1347
|
};
|
|
1348
|
+
const reconnectBackgroundContinuationForRunTimeout = async function* () {
|
|
1349
|
+
if (!threadId || !runId)
|
|
1350
|
+
return false;
|
|
1351
|
+
const interruptedRunId = runId;
|
|
1352
|
+
const interruptedLastSeq = lastSeq;
|
|
1353
|
+
let lastActiveRunError = null;
|
|
1354
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
1355
|
+
if (attempt > 0) {
|
|
1356
|
+
await delay(500, abortSignal);
|
|
1357
|
+
}
|
|
1358
|
+
if (abortSignal.aborted)
|
|
1359
|
+
return true;
|
|
1360
|
+
try {
|
|
1361
|
+
const activeRes = await fetch(`${apiUrl}/runs/active?threadId=${encodeURIComponent(threadId)}`, { signal: abortSignal });
|
|
1362
|
+
if (!activeRes.ok) {
|
|
1363
|
+
if (activeRes.status === 404)
|
|
1364
|
+
return false;
|
|
1365
|
+
lastActiveRunError = new Error(`Active run lookup failed: ${activeRes.status}`);
|
|
1366
|
+
continue;
|
|
1367
|
+
}
|
|
1368
|
+
const active = await activeRes.json();
|
|
1369
|
+
if (!active?.active || !active.runId)
|
|
1370
|
+
return false;
|
|
1371
|
+
const activeRunId = String(active.runId);
|
|
1372
|
+
const dispatchMode = typeof active.dispatchMode === "string"
|
|
1373
|
+
? active.dispatchMode
|
|
1374
|
+
: "";
|
|
1375
|
+
if (activeRunId === interruptedRunId) {
|
|
1376
|
+
if (dispatchMode.startsWith("background"))
|
|
1377
|
+
continue;
|
|
1378
|
+
return false;
|
|
1379
|
+
}
|
|
1380
|
+
const activeTurnId = typeof active.turnId === "string" ? active.turnId : "";
|
|
1381
|
+
const activeStatus = typeof active.status === "string" ? active.status : "";
|
|
1382
|
+
if (!dispatchMode.startsWith("background"))
|
|
1383
|
+
return false;
|
|
1384
|
+
if (activeTurnId && activeTurnId !== turnId)
|
|
1385
|
+
return false;
|
|
1386
|
+
if (activeStatus !== "running" && activeStatus !== "starting") {
|
|
1387
|
+
return false;
|
|
1388
|
+
}
|
|
1389
|
+
runId = activeRunId;
|
|
1390
|
+
if (!attemptedRunIds.includes(activeRunId)) {
|
|
1391
|
+
attemptedRunIds.push(activeRunId);
|
|
1392
|
+
}
|
|
1393
|
+
lastSeq = -1;
|
|
1394
|
+
setActiveRun({ threadId, runId: activeRunId, lastSeq: -1 });
|
|
1395
|
+
const reconnected = yield* reconnectCurrentRun();
|
|
1396
|
+
if (reconnected)
|
|
1397
|
+
return true;
|
|
1398
|
+
}
|
|
1399
|
+
catch (activeErr) {
|
|
1400
|
+
if (activeErr instanceof Error &&
|
|
1401
|
+
activeErr.name === "AbortError") {
|
|
1402
|
+
clearActiveRun();
|
|
1403
|
+
return true;
|
|
1404
|
+
}
|
|
1405
|
+
lastActiveRunError = activeErr;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
if (lastActiveRunError) {
|
|
1409
|
+
captureChatClientError(lastActiveRunError, "reconnect-background-continuation-failed");
|
|
1410
|
+
}
|
|
1411
|
+
runId = interruptedRunId;
|
|
1412
|
+
lastSeq = interruptedLastSeq;
|
|
1413
|
+
return false;
|
|
1414
|
+
};
|
|
1312
1415
|
const visibleContentForContinuation = () => {
|
|
1313
1416
|
return contentAfterContinuationPrefix(content, visibleContinuationPrefix);
|
|
1314
1417
|
};
|
|
@@ -1331,6 +1434,7 @@ export function createAgentChatAdapter(options) {
|
|
|
1331
1434
|
// before tool_start; treating it as progress caused silent retry
|
|
1332
1435
|
// loops when the LLM timed out while assembling a large tool input.
|
|
1333
1436
|
const hasInFlightTool = hasInFlightToolCall(visibleContent);
|
|
1437
|
+
const completedSideEffectTool = lastCompletedSideEffectTool(content);
|
|
1334
1438
|
// Either real output or an actively-running tool counts as progress
|
|
1335
1439
|
// for the stalled/empty caps.
|
|
1336
1440
|
const madeProgress = madeContentProgress || hasInFlightTool;
|
|
@@ -1343,11 +1447,11 @@ export function createAgentChatAdapter(options) {
|
|
|
1343
1447
|
// MAX_REPEATED_INFLIGHT_TOOL_STALLS consecutive stream_ended events,
|
|
1344
1448
|
// bail with a clear message.
|
|
1345
1449
|
//
|
|
1346
|
-
//
|
|
1347
|
-
//
|
|
1348
|
-
//
|
|
1349
|
-
//
|
|
1350
|
-
//
|
|
1450
|
+
// Count broken streams (connection drop / reconnect failed) and
|
|
1451
|
+
// no_progress stalls (the client aborts a stream that stayed open but
|
|
1452
|
+
// stopped producing events). Do NOT count run_timeout: with an
|
|
1453
|
+
// in-flight tool that means the server is still actively executing a
|
|
1454
|
+
// slow action and reconnection may recover the result.
|
|
1351
1455
|
const currentInFlightToolPart = visibleContent.find((p) => p.type === "tool-call" &&
|
|
1352
1456
|
p.result === undefined &&
|
|
1353
1457
|
p.activity !== true);
|
|
@@ -1355,8 +1459,8 @@ export function createAgentChatAdapter(options) {
|
|
|
1355
1459
|
const currentInFlightToolSignature = currentInFlightToolPart
|
|
1356
1460
|
? inFlightToolInputSignature(currentInFlightToolPart)
|
|
1357
1461
|
: undefined;
|
|
1358
|
-
const
|
|
1359
|
-
if (currentInFlightToolName &&
|
|
1462
|
+
const isBrokenInFlightTool = signal.reason === "stream_ended" || signal.reason === "no_progress";
|
|
1463
|
+
if (currentInFlightToolName && isBrokenInFlightTool) {
|
|
1360
1464
|
if (currentInFlightToolName === lastInFlightToolName &&
|
|
1361
1465
|
currentInFlightToolSignature === lastInFlightToolSignature) {
|
|
1362
1466
|
repeatedInFlightToolCount += 1;
|
|
@@ -1393,11 +1497,27 @@ export function createAgentChatAdapter(options) {
|
|
|
1393
1497
|
}
|
|
1394
1498
|
else {
|
|
1395
1499
|
totalTransientContinuationAttempts += 1;
|
|
1500
|
+
// If a mutating action already completed, do not turn a missing
|
|
1501
|
+
// closing sentence into a scary connection failure. Give the model
|
|
1502
|
+
// one continuation opportunity (the completed tool itself counts
|
|
1503
|
+
// as progress on the first timeout); if the follow-up produces no
|
|
1504
|
+
// new content, stop locally with a clear "saved, final note timed
|
|
1505
|
+
// out" message.
|
|
1506
|
+
if (completedSideEffectTool &&
|
|
1507
|
+
!hasInFlightToolCall(content) &&
|
|
1508
|
+
!madeContentProgress &&
|
|
1509
|
+
!hasInFlightTool) {
|
|
1510
|
+
return {
|
|
1511
|
+
ok: false,
|
|
1512
|
+
resetVisibleContent: false,
|
|
1513
|
+
completedToolName: completedSideEffectTool.toolName,
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1396
1516
|
// Bail when the same write tool is stuck in-flight across too many
|
|
1397
1517
|
// consecutive continuations. Checked before the text-repeat guard
|
|
1398
1518
|
// because hasInFlightTool=true would mask the repeat as progress.
|
|
1399
1519
|
if (repeatedInFlightToolCount >= MAX_REPEATED_INFLIGHT_TOOL_STALLS) {
|
|
1400
|
-
|
|
1520
|
+
recoveryGaveUpOnInFlightTool = true;
|
|
1401
1521
|
return { ok: false, resetVisibleContent: false };
|
|
1402
1522
|
}
|
|
1403
1523
|
// Bail fast on a non-advancing repetition loop, well before the
|
|
@@ -1760,6 +1880,11 @@ export function createAgentChatAdapter(options) {
|
|
|
1760
1880
|
if (err.reason === "no_progress") {
|
|
1761
1881
|
await abortCurrentRun();
|
|
1762
1882
|
}
|
|
1883
|
+
if (err.reason === "run_timeout" && !err.errorInfo) {
|
|
1884
|
+
const reconnected = yield* reconnectBackgroundContinuationForRunTimeout();
|
|
1885
|
+
if (reconnected)
|
|
1886
|
+
return;
|
|
1887
|
+
}
|
|
1763
1888
|
if (err.reason === "stream_ended") {
|
|
1764
1889
|
const reconnected = yield* reconnectCurrentRun();
|
|
1765
1890
|
if (reconnected)
|
|
@@ -1770,6 +1895,29 @@ export function createAgentChatAdapter(options) {
|
|
|
1770
1895
|
}
|
|
1771
1896
|
const continuation = prepareAutoContinuation(err);
|
|
1772
1897
|
if (!continuation.ok) {
|
|
1898
|
+
if (continuation.completedToolName) {
|
|
1899
|
+
const message = completedToolTimeoutMessage(continuation.completedToolName);
|
|
1900
|
+
content.push({ type: "text", text: message });
|
|
1901
|
+
yield {
|
|
1902
|
+
content: [...content],
|
|
1903
|
+
status: {
|
|
1904
|
+
type: "complete",
|
|
1905
|
+
reason: "stop",
|
|
1906
|
+
},
|
|
1907
|
+
metadata: {
|
|
1908
|
+
custom: {
|
|
1909
|
+
...(runId ? { runId } : {}),
|
|
1910
|
+
runWarning: {
|
|
1911
|
+
message,
|
|
1912
|
+
errorCode: "final_response_timeout_after_tool",
|
|
1913
|
+
recoverable: true,
|
|
1914
|
+
},
|
|
1915
|
+
},
|
|
1916
|
+
},
|
|
1917
|
+
};
|
|
1918
|
+
clearActiveRun();
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1773
1921
|
const preservedError = err.errorInfo ?? lastRecoverableRunError ?? null;
|
|
1774
1922
|
const message = preservedError?.message ??
|
|
1775
1923
|
exhaustedRecoveryMessage(err.reason);
|
|
@@ -1920,6 +2068,29 @@ export function createAgentChatAdapter(options) {
|
|
|
1920
2068
|
if (content.length > 0) {
|
|
1921
2069
|
const continuation = prepareAutoContinuation(new AgentAutoContinueSignal({ reason: "stream_ended" }));
|
|
1922
2070
|
if (!continuation.ok) {
|
|
2071
|
+
if (continuation.completedToolName) {
|
|
2072
|
+
const message = completedToolTimeoutMessage(continuation.completedToolName);
|
|
2073
|
+
content.push({ type: "text", text: message });
|
|
2074
|
+
yield {
|
|
2075
|
+
content: [...content],
|
|
2076
|
+
status: {
|
|
2077
|
+
type: "complete",
|
|
2078
|
+
reason: "stop",
|
|
2079
|
+
},
|
|
2080
|
+
metadata: {
|
|
2081
|
+
custom: {
|
|
2082
|
+
...(runId ? { runId } : {}),
|
|
2083
|
+
runWarning: {
|
|
2084
|
+
message,
|
|
2085
|
+
errorCode: "final_response_timeout_after_tool",
|
|
2086
|
+
recoverable: true,
|
|
2087
|
+
},
|
|
2088
|
+
},
|
|
2089
|
+
},
|
|
2090
|
+
};
|
|
2091
|
+
clearActiveRun();
|
|
2092
|
+
return;
|
|
2093
|
+
}
|
|
1923
2094
|
const message = exhaustedRecoveryMessage("stream_ended");
|
|
1924
2095
|
captureChatClientError(err, "recovery-exhausted");
|
|
1925
2096
|
const runError = {
|