@agent-native/core 0.84.5 → 0.84.7
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 +12 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +97 -38
- package/corpus/core/src/agent/run-store.ts +17 -0
- package/corpus/core/src/client/agent-chat-adapter.ts +74 -2
- package/corpus/core/src/server/agent-chat-plugin.ts +57 -12
- package/corpus/templates/design/AGENTS.md +9 -7
- package/corpus/templates/design/actions/present-design-variants.ts +202 -10
- package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
- package/corpus/templates/design/app/pages/DesignEditor.tsx +1 -1
- package/corpus/templates/design/changelog/2026-07-01-design-variants-can-now-be-generated-from-compact-directions.md +6 -0
- package/dist/agent/production-agent.d.ts +20 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +65 -32
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-store.d.ts +14 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +14 -0
- package/dist/agent/run-store.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +63 -2
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/server/agent-chat-plugin.d.ts +9 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +20 -8
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/package.json +1 -1
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.84.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- ab1e410: Stop repeated agent-chat action-preparation loops with a clear terminal warning, and let Design agents present compact variant directions without streaming large HTML payloads.
|
|
8
|
+
|
|
9
|
+
## 0.84.6
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 126ccac: Claim durable background agent-chat runs before expensive worker setup so hosted apps stay on the 15-minute background-function path instead of falling back to 40-second inline chunks.
|
|
14
|
+
|
|
3
15
|
## 0.84.5
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.84.
|
|
3
|
+
"version": "0.84.7",
|
|
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": {
|
|
@@ -4025,6 +4025,58 @@ export function shouldChainBackgroundContinuation(opts: {
|
|
|
4025
4025
|
);
|
|
4026
4026
|
}
|
|
4027
4027
|
|
|
4028
|
+
export async function claimBackgroundWorkerRunEarly(opts: {
|
|
4029
|
+
runId: string;
|
|
4030
|
+
threadId?: string | null;
|
|
4031
|
+
markerTurnId?: string | null;
|
|
4032
|
+
requestTurnId?: string | null;
|
|
4033
|
+
continuationCount: number;
|
|
4034
|
+
runsInBackgroundFunction: boolean;
|
|
4035
|
+
deps?: {
|
|
4036
|
+
recordRunDiagnostic?: typeof recordRunDiagnostic;
|
|
4037
|
+
insertRun?: typeof insertRun;
|
|
4038
|
+
claimBackgroundRun?: typeof claimBackgroundRun;
|
|
4039
|
+
updateRunHeartbeat?: typeof updateRunHeartbeat;
|
|
4040
|
+
};
|
|
4041
|
+
}): Promise<{ claimed: true } | { claimed: false; skipped: string }> {
|
|
4042
|
+
const record = opts.deps?.recordRunDiagnostic ?? recordRunDiagnostic;
|
|
4043
|
+
const insert = opts.deps?.insertRun ?? insertRun;
|
|
4044
|
+
const claim = opts.deps?.claimBackgroundRun ?? claimBackgroundRun;
|
|
4045
|
+
const heartbeat = opts.deps?.updateRunHeartbeat ?? updateRunHeartbeat;
|
|
4046
|
+
const threadId =
|
|
4047
|
+
typeof opts.threadId === "string" && opts.threadId.trim()
|
|
4048
|
+
? opts.threadId.trim()
|
|
4049
|
+
: opts.runId;
|
|
4050
|
+
const turnId =
|
|
4051
|
+
typeof opts.markerTurnId === "string" && opts.markerTurnId.trim()
|
|
4052
|
+
? opts.markerTurnId.trim()
|
|
4053
|
+
: typeof opts.requestTurnId === "string" && opts.requestTurnId.trim()
|
|
4054
|
+
? opts.requestTurnId.trim()
|
|
4055
|
+
: opts.runId;
|
|
4056
|
+
|
|
4057
|
+
await record(
|
|
4058
|
+
opts.runId,
|
|
4059
|
+
RUN_DIAG_STAGE.workerEntered,
|
|
4060
|
+
`runsInBackgroundFunction=${opts.runsInBackgroundFunction} continuationCount=${opts.continuationCount}`,
|
|
4061
|
+
).catch(() => {});
|
|
4062
|
+
|
|
4063
|
+
if (opts.continuationCount > 0) {
|
|
4064
|
+
await insert(opts.runId, threadId, turnId, {
|
|
4065
|
+
dispatchMode: "background",
|
|
4066
|
+
}).catch(() => {});
|
|
4067
|
+
}
|
|
4068
|
+
|
|
4069
|
+
const won = await claim(opts.runId);
|
|
4070
|
+
if (!won) {
|
|
4071
|
+
await record(opts.runId, RUN_DIAG_STAGE.workerClaimLost).catch(() => {});
|
|
4072
|
+
return { claimed: false, skipped: "already-claimed" };
|
|
4073
|
+
}
|
|
4074
|
+
|
|
4075
|
+
await record(opts.runId, RUN_DIAG_STAGE.workerClaimed).catch(() => {});
|
|
4076
|
+
await heartbeat(opts.runId).catch(() => {});
|
|
4077
|
+
return { claimed: true };
|
|
4078
|
+
}
|
|
4079
|
+
|
|
4028
4080
|
function progressStepFromAgentChatEvent(event: AgentChatEvent): string | null {
|
|
4029
4081
|
switch (event.type) {
|
|
4030
4082
|
case "activity":
|
|
@@ -4175,6 +4227,24 @@ export function createProductionAgentHandler(
|
|
|
4175
4227
|
Number.isFinite(backgroundRunMarker.continuationCount)
|
|
4176
4228
|
? Math.max(0, Math.floor(backgroundRunMarker.continuationCount))
|
|
4177
4229
|
: 0;
|
|
4230
|
+
let backgroundRunClaimedEarly = false;
|
|
4231
|
+
if (isBackgroundWorker && bgRunId) {
|
|
4232
|
+
const earlyClaim = await claimBackgroundWorkerRunEarly({
|
|
4233
|
+
runId: bgRunId,
|
|
4234
|
+
threadId,
|
|
4235
|
+
markerTurnId:
|
|
4236
|
+
typeof backgroundRunMarker?.turnId === "string"
|
|
4237
|
+
? backgroundRunMarker.turnId
|
|
4238
|
+
: null,
|
|
4239
|
+
requestTurnId,
|
|
4240
|
+
continuationCount: backgroundContinuationCount,
|
|
4241
|
+
runsInBackgroundFunction,
|
|
4242
|
+
});
|
|
4243
|
+
if (!earlyClaim.claimed) {
|
|
4244
|
+
return { ok: true, skipped: earlyClaim.skipped };
|
|
4245
|
+
}
|
|
4246
|
+
backgroundRunClaimedEarly = true;
|
|
4247
|
+
}
|
|
4178
4248
|
// The foreground POST decides whether to dispatch into a background
|
|
4179
4249
|
// function. The background worker itself never re-dispatches.
|
|
4180
4250
|
const dispatchToBackground =
|
|
@@ -5254,48 +5324,37 @@ export function createProductionAgentHandler(
|
|
|
5254
5324
|
}
|
|
5255
5325
|
: undefined;
|
|
5256
5326
|
|
|
5257
|
-
// Background worker:
|
|
5258
|
-
//
|
|
5259
|
-
//
|
|
5260
|
-
//
|
|
5261
|
-
//
|
|
5327
|
+
// Background worker: the run was claimed immediately after the authenticated
|
|
5328
|
+
// `_process-run` body was parsed, before owner/model/prompt/tool setup. That
|
|
5329
|
+
// early claim is what lets the foreground subscribe to the real background
|
|
5330
|
+
// worker instead of racing slow setup and falling back to the 40s inline
|
|
5331
|
+
// path. This late block is a defensive fallback for older/custom callers
|
|
5332
|
+
// that somehow reach here without the early claim.
|
|
5262
5333
|
if (isBackgroundWorker) {
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
}
|
|
5284
|
-
const won = await claimBackgroundRun(runId);
|
|
5285
|
-
if (!won) {
|
|
5286
|
-
// Already claimed by an earlier delivery — return a benign ack so
|
|
5287
|
-
// Netlify doesn't retry a successful handoff.
|
|
5288
|
-
await recordRunDiagnostic(runId, RUN_DIAG_STAGE.workerClaimLost).catch(
|
|
5334
|
+
if (!backgroundRunClaimedEarly) {
|
|
5335
|
+
await recordRunDiagnostic(
|
|
5336
|
+
runId,
|
|
5337
|
+
RUN_DIAG_STAGE.workerEntered,
|
|
5338
|
+
`runsInBackgroundFunction=${runsInBackgroundFunction} continuationCount=${backgroundContinuationCount}`,
|
|
5339
|
+
).catch(() => {});
|
|
5340
|
+
if (isChainedBackgroundContinuation) {
|
|
5341
|
+
await insertRun(runId, effectiveThreadId, effectiveTurnId, {
|
|
5342
|
+
dispatchMode: "background",
|
|
5343
|
+
}).catch(() => {});
|
|
5344
|
+
}
|
|
5345
|
+
const won = await claimBackgroundRun(runId);
|
|
5346
|
+
if (!won) {
|
|
5347
|
+
await recordRunDiagnostic(
|
|
5348
|
+
runId,
|
|
5349
|
+
RUN_DIAG_STAGE.workerClaimLost,
|
|
5350
|
+
).catch(() => {});
|
|
5351
|
+
return { ok: true, skipped: "already-claimed" };
|
|
5352
|
+
}
|
|
5353
|
+
await recordRunDiagnostic(runId, RUN_DIAG_STAGE.workerClaimed).catch(
|
|
5289
5354
|
() => {},
|
|
5290
5355
|
);
|
|
5291
|
-
|
|
5356
|
+
await updateRunHeartbeat(runId).catch(() => {});
|
|
5292
5357
|
}
|
|
5293
|
-
// DIAGNOSTIC: this worker won the claim and now OWNS the run. If a run
|
|
5294
|
-
// ever stalls at this stage it means the loop below failed to start.
|
|
5295
|
-
await recordRunDiagnostic(runId, RUN_DIAG_STAGE.workerClaimed).catch(
|
|
5296
|
-
() => {},
|
|
5297
|
-
);
|
|
5298
|
-
await updateRunHeartbeat(runId).catch(() => {});
|
|
5299
5358
|
}
|
|
5300
5359
|
|
|
5301
5360
|
// DIAGNOSTIC-ONLY: build the pre-startRun setup-timing breakdown now (so the
|
|
@@ -64,6 +64,23 @@ export const UNCLAIMED_BACKGROUND_RUN_ERROR_EVENT = {
|
|
|
64
64
|
"A background-dispatched run was acknowledged (HTTP 202) but its worker never claimed the run, so no progress was produced. The run was reaped early (it had no live worker to protect) so the turn can be retried.",
|
|
65
65
|
} as const;
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Terminal error for a background worker that DID claim the run, then failed
|
|
69
|
+
* during route/handler setup before `startRun` could emit its own error event.
|
|
70
|
+
* Claimed runs are no longer eligible for foreground inline recovery, so the
|
|
71
|
+
* route boundary must fail them loudly instead of leaving subscribers to wait
|
|
72
|
+
* for stale-run recovery.
|
|
73
|
+
*/
|
|
74
|
+
export const CLAIMED_BACKGROUND_WORKER_FAILED_ERROR_EVENT = {
|
|
75
|
+
type: "error",
|
|
76
|
+
error:
|
|
77
|
+
"The background agent worker stopped before it could start the turn. You can retry from the preserved chat context.",
|
|
78
|
+
errorCode: "background_worker_failed",
|
|
79
|
+
recoverable: true,
|
|
80
|
+
details:
|
|
81
|
+
"The durable background worker claimed the run but threw during setup before it could emit agent events.",
|
|
82
|
+
} as const;
|
|
83
|
+
|
|
67
84
|
/**
|
|
68
85
|
* Grace period before a never-claimed background run (dispatch_mode still
|
|
69
86
|
* 'background', no worker claim) is treated as a dead handoff and reaped.
|
|
@@ -93,6 +93,12 @@ const MAX_EMPTY_TRANSIENT_CONTINUATIONS = 3;
|
|
|
93
93
|
// round re-sending any large pasted payload) before bailing. Catching the
|
|
94
94
|
// repeat ends it in a few rounds with a clear, actionable message instead.
|
|
95
95
|
const MAX_REPEATED_TRANSIENT_CONTINUATIONS = 3;
|
|
96
|
+
// How many consecutive continuations that only reach the SAME "preparing
|
|
97
|
+
// action" activity card we tolerate before giving up. This catches runs that
|
|
98
|
+
// keep timing out while assembling a large tool payload: they are not empty,
|
|
99
|
+
// and the narration may vary enough to bypass the text-repeat guard, but the
|
|
100
|
+
// real tool never starts.
|
|
101
|
+
const MAX_REPEATED_ACTION_PREPARATION_CONTINUATIONS = 3;
|
|
96
102
|
const RETRY_BASE_DELAY_MS = 500;
|
|
97
103
|
const RETRY_MAX_DELAY_MS = 8_000;
|
|
98
104
|
const MAX_HISTORY_ATTACHMENT_CHARS = 60_000;
|
|
@@ -782,6 +788,22 @@ function lastActivityTool(
|
|
|
782
788
|
return undefined;
|
|
783
789
|
}
|
|
784
790
|
|
|
791
|
+
function lastUnresolvedToolActivity(
|
|
792
|
+
content: ContentPart[],
|
|
793
|
+
): string | undefined {
|
|
794
|
+
for (let i = content.length - 1; i >= 0; i--) {
|
|
795
|
+
const part = content[i];
|
|
796
|
+
if (
|
|
797
|
+
part.type === "tool-call" &&
|
|
798
|
+
part.activity === true &&
|
|
799
|
+
part.result === undefined
|
|
800
|
+
) {
|
|
801
|
+
return part.toolName;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
return undefined;
|
|
805
|
+
}
|
|
806
|
+
|
|
785
807
|
function snapshotContent(content: ContentPart[]): ContentPart[] {
|
|
786
808
|
return content.map((part) =>
|
|
787
809
|
part.type === "text" ? { ...part } : { ...part, args: { ...part.args } },
|
|
@@ -889,7 +911,7 @@ function incrementalActionGuidance(tool: string): string | undefined {
|
|
|
889
911
|
case "update-design":
|
|
890
912
|
return "persist a minimal first version (fewer files) with `generate-design`, then refine individual files with `edit-design` search/replace instead of resending everything";
|
|
891
913
|
case "present-design-variants":
|
|
892
|
-
return "
|
|
914
|
+
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, then refine the chosen direction with `generate-design` or `edit-design`";
|
|
893
915
|
case "create-visual-plan":
|
|
894
916
|
case "create-ui-plan":
|
|
895
917
|
case "create-plan-design":
|
|
@@ -1390,6 +1412,9 @@ export function createAgentChatAdapter(
|
|
|
1390
1412
|
let repeatedInFlightToolCount = 0;
|
|
1391
1413
|
let recoveryGaveUpOnInFlightTool = false;
|
|
1392
1414
|
const MAX_REPEATED_INFLIGHT_TOOL_STALLS = 3;
|
|
1415
|
+
let lastPreparingToolName: string | undefined;
|
|
1416
|
+
let repeatedActionPreparationCount = 0;
|
|
1417
|
+
let recoveryGaveUpOnActionPreparation = false;
|
|
1393
1418
|
const continuationHistoryFragments: string[] = [];
|
|
1394
1419
|
const structuredContinuationFragments: AgentChatStructuredMessage[] = [];
|
|
1395
1420
|
let visibleContinuationPrefix: ContentPart[] = [];
|
|
@@ -1420,6 +1445,10 @@ export function createAgentChatAdapter(
|
|
|
1420
1445
|
lastInFlightToolName
|
|
1421
1446
|
? `last_inflight_tool: ${lastInFlightToolName}`
|
|
1422
1447
|
: "",
|
|
1448
|
+
`repeated_action_preparation_stalls: ${repeatedActionPreparationCount}`,
|
|
1449
|
+
lastPreparingToolName
|
|
1450
|
+
? `last_preparing_tool: ${lastPreparingToolName}`
|
|
1451
|
+
: "",
|
|
1423
1452
|
`total_transient_continuations: ${totalTransientContinuationAttempts}`,
|
|
1424
1453
|
attemptedRunIds.length > 0
|
|
1425
1454
|
? `attempted_runs: ${attemptedRunIds.join(", ")}`
|
|
@@ -1436,6 +1465,12 @@ export function createAgentChatAdapter(
|
|
|
1436
1465
|
if (recoveryGaveUpOnRepetition) {
|
|
1437
1466
|
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.";
|
|
1438
1467
|
}
|
|
1468
|
+
if (recoveryGaveUpOnActionPreparation) {
|
|
1469
|
+
const tool = lastPreparingToolName
|
|
1470
|
+
? ` the ${humanizeActionName(lastPreparingToolName)} action`
|
|
1471
|
+
: " the same action";
|
|
1472
|
+
return `The agent got stuck preparing${tool} input and never started the tool, so I stopped the automatic retries. Try a smaller first step or a more compact version of the request.`;
|
|
1473
|
+
}
|
|
1439
1474
|
if (
|
|
1440
1475
|
content.length === 0 &&
|
|
1441
1476
|
(reason === "run_timeout" ||
|
|
@@ -1512,6 +1547,8 @@ export function createAgentChatAdapter(
|
|
|
1512
1547
|
repeatedTransientContinuationAttempts,
|
|
1513
1548
|
repeatedInFlightToolCount,
|
|
1514
1549
|
lastInFlightToolName,
|
|
1550
|
+
repeatedActionPreparationCount,
|
|
1551
|
+
lastPreparingToolName,
|
|
1515
1552
|
totalTransientContinuationAttempts,
|
|
1516
1553
|
...extra,
|
|
1517
1554
|
},
|
|
@@ -1529,6 +1566,8 @@ export function createAgentChatAdapter(
|
|
|
1529
1566
|
repeatedTransientContinuationAttempts,
|
|
1530
1567
|
repeatedInFlightToolCount,
|
|
1531
1568
|
lastInFlightToolName,
|
|
1569
|
+
repeatedActionPreparationCount,
|
|
1570
|
+
lastPreparingToolName,
|
|
1532
1571
|
totalTransientContinuationAttempts,
|
|
1533
1572
|
},
|
|
1534
1573
|
},
|
|
@@ -1832,8 +1871,13 @@ export function createAgentChatAdapter(
|
|
|
1832
1871
|
// for the stalled/empty caps.
|
|
1833
1872
|
const madeProgress = madeContentProgress || hasInFlightTool;
|
|
1834
1873
|
const madeDurableToolProgress = visibleContent.some(
|
|
1835
|
-
(part) =>
|
|
1874
|
+
(part) =>
|
|
1875
|
+
part.type === "tool-call" &&
|
|
1876
|
+
part.activity !== true &&
|
|
1877
|
+
part.result !== undefined,
|
|
1836
1878
|
);
|
|
1879
|
+
const currentPreparingToolName =
|
|
1880
|
+
lastUnresolvedToolActivity(visibleContent);
|
|
1837
1881
|
// In-flight tool stall guard. When the same write tool is stuck
|
|
1838
1882
|
// in-flight because the connection keeps dropping (stream_ended),
|
|
1839
1883
|
// hasInFlightTool=true keeps madeProgress=true and completely
|
|
@@ -1877,6 +1921,27 @@ export function createAgentChatAdapter(
|
|
|
1877
1921
|
repeatedInFlightToolCount = 0;
|
|
1878
1922
|
}
|
|
1879
1923
|
|
|
1924
|
+
const isRepeatedActionPreparationCandidate =
|
|
1925
|
+
signal.reason !== "loop_limit" &&
|
|
1926
|
+
currentPreparingToolName !== undefined &&
|
|
1927
|
+
!hasInFlightTool &&
|
|
1928
|
+
!madeDurableToolProgress;
|
|
1929
|
+
if (isRepeatedActionPreparationCandidate) {
|
|
1930
|
+
if (currentPreparingToolName === lastPreparingToolName) {
|
|
1931
|
+
repeatedActionPreparationCount += 1;
|
|
1932
|
+
} else {
|
|
1933
|
+
repeatedActionPreparationCount = 0;
|
|
1934
|
+
lastPreparingToolName = currentPreparingToolName;
|
|
1935
|
+
}
|
|
1936
|
+
} else if (
|
|
1937
|
+
!currentPreparingToolName ||
|
|
1938
|
+
hasInFlightTool ||
|
|
1939
|
+
madeDurableToolProgress
|
|
1940
|
+
) {
|
|
1941
|
+
repeatedActionPreparationCount = 0;
|
|
1942
|
+
lastPreparingToolName = undefined;
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1880
1945
|
// Degenerate repetition guard. When the model gets stuck re-streaming
|
|
1881
1946
|
// the SAME narration every continuation without ever starting or
|
|
1882
1947
|
// finishing a tool, each round is "new" text — so madeProgress stays
|
|
@@ -1925,6 +1990,13 @@ export function createAgentChatAdapter(
|
|
|
1925
1990
|
recoveryGaveUpOnInFlightTool = true;
|
|
1926
1991
|
return { ok: false, resetVisibleContent: false };
|
|
1927
1992
|
}
|
|
1993
|
+
if (
|
|
1994
|
+
repeatedActionPreparationCount >
|
|
1995
|
+
MAX_REPEATED_ACTION_PREPARATION_CONTINUATIONS
|
|
1996
|
+
) {
|
|
1997
|
+
recoveryGaveUpOnActionPreparation = true;
|
|
1998
|
+
return { ok: false, resetVisibleContent: false };
|
|
1999
|
+
}
|
|
1928
2000
|
// Bail fast on a non-advancing repetition loop, well before the
|
|
1929
2001
|
// stalled/empty/total budgets would (each round otherwise re-sends
|
|
1930
2002
|
// the whole pasted payload). Tracked separately so it never trips
|
|
@@ -62,7 +62,15 @@ import {
|
|
|
62
62
|
import { runAgentLoopDirectWithSoftTimeout } from "../agent/run-loop-with-resume.js";
|
|
63
63
|
import { callerOwnsRun, callerOwnsThread } from "../agent/run-ownership.js";
|
|
64
64
|
import type { AgentRunSummary } from "../agent/run-store.js";
|
|
65
|
-
import {
|
|
65
|
+
import {
|
|
66
|
+
CLAIMED_BACKGROUND_WORKER_FAILED_ERROR_EVENT,
|
|
67
|
+
ensureTerminalRunEvent,
|
|
68
|
+
readBackgroundRunClaim,
|
|
69
|
+
recordRunDiagnostic,
|
|
70
|
+
RUN_DIAG_STAGE,
|
|
71
|
+
setRunError,
|
|
72
|
+
updateRunStatusIfRunning,
|
|
73
|
+
} from "../agent/run-store.js";
|
|
66
74
|
import { buildRuntimeContextPrompt } from "../agent/runtime-context.js";
|
|
67
75
|
import {
|
|
68
76
|
buildAssistantMessage,
|
|
@@ -3864,6 +3872,50 @@ export function shouldDisableRecurringJobsRuntime(
|
|
|
3864
3872
|
return isLocalRuntime;
|
|
3865
3873
|
}
|
|
3866
3874
|
|
|
3875
|
+
type AgentChatProcessRunFailureDeps = {
|
|
3876
|
+
readBackgroundRunClaim?: typeof readBackgroundRunClaim;
|
|
3877
|
+
recordRunDiagnostic?: typeof recordRunDiagnostic;
|
|
3878
|
+
setRunError?: typeof setRunError;
|
|
3879
|
+
updateRunStatusIfRunning?: typeof updateRunStatusIfRunning;
|
|
3880
|
+
ensureTerminalRunEvent?: typeof ensureTerminalRunEvent;
|
|
3881
|
+
};
|
|
3882
|
+
|
|
3883
|
+
export async function finalizeClaimedAgentChatProcessRunFailure(
|
|
3884
|
+
runId: string,
|
|
3885
|
+
err: unknown,
|
|
3886
|
+
deps: AgentChatProcessRunFailureDeps = {},
|
|
3887
|
+
): Promise<boolean> {
|
|
3888
|
+
const readClaim = deps.readBackgroundRunClaim ?? readBackgroundRunClaim;
|
|
3889
|
+
const record = deps.recordRunDiagnostic ?? recordRunDiagnostic;
|
|
3890
|
+
const setError = deps.setRunError ?? setRunError;
|
|
3891
|
+
const updateStatus =
|
|
3892
|
+
deps.updateRunStatusIfRunning ?? updateRunStatusIfRunning;
|
|
3893
|
+
const ensureTerminal = deps.ensureTerminalRunEvent ?? ensureTerminalRunEvent;
|
|
3894
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3895
|
+
|
|
3896
|
+
await record(runId, RUN_DIAG_STAGE.routeThrew, message).catch(() => {});
|
|
3897
|
+
|
|
3898
|
+
const claim = await readClaim(runId).catch(() => null);
|
|
3899
|
+
if (
|
|
3900
|
+
claim?.status !== "running" ||
|
|
3901
|
+
claim.dispatchMode !== "background-processing"
|
|
3902
|
+
) {
|
|
3903
|
+
return false;
|
|
3904
|
+
}
|
|
3905
|
+
|
|
3906
|
+
await setError(
|
|
3907
|
+
runId,
|
|
3908
|
+
CLAIMED_BACKGROUND_WORKER_FAILED_ERROR_EVENT.errorCode,
|
|
3909
|
+
`${CLAIMED_BACKGROUND_WORKER_FAILED_ERROR_EVENT.details} setupError=${message}`,
|
|
3910
|
+
).catch(() => {});
|
|
3911
|
+
await updateStatus(runId, "errored").catch(() => {});
|
|
3912
|
+
await ensureTerminal(
|
|
3913
|
+
runId,
|
|
3914
|
+
CLAIMED_BACKGROUND_WORKER_FAILED_ERROR_EVENT,
|
|
3915
|
+
).catch(() => {});
|
|
3916
|
+
return true;
|
|
3917
|
+
}
|
|
3918
|
+
|
|
3867
3919
|
export function createAgentChatPlugin(
|
|
3868
3920
|
options?: AgentChatPluginOptions,
|
|
3869
3921
|
): NitroPluginDef {
|
|
@@ -8030,17 +8082,10 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
8030
8082
|
runId: prepared.runId,
|
|
8031
8083
|
},
|
|
8032
8084
|
});
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
.record(
|
|
8038
|
-
prepared.runId,
|
|
8039
|
-
diag.stages.routeThrew,
|
|
8040
|
-
err instanceof Error ? err.message : String(err),
|
|
8041
|
-
)
|
|
8042
|
-
.catch(() => {});
|
|
8043
|
-
}
|
|
8085
|
+
await finalizeClaimedAgentChatProcessRunFailure(
|
|
8086
|
+
prepared.runId,
|
|
8087
|
+
err,
|
|
8088
|
+
);
|
|
8044
8089
|
setResponseStatus(event, 500);
|
|
8045
8090
|
return { error: "process-run failed" };
|
|
8046
8091
|
}
|
|
@@ -133,9 +133,11 @@ patterns live in `.agents/skills/`.
|
|
|
133
133
|
write-back remains a localhost/fusion follow-up capability.
|
|
134
134
|
- For multi-variant work, use `present-design-variants` so every candidate is
|
|
135
135
|
saved as a normal overview-board screen and the user gets one inline chat
|
|
136
|
-
button per screen name. Keep each variant compact:
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
button per screen name. Keep each variant compact: prefer concise labels,
|
|
137
|
+
descriptions, accent colors, and feature bullets, and omit full HTML when it
|
|
138
|
+
would make the tool input too large. The action can render representative
|
|
139
|
+
screens from direction data. After the user picks, delete the unchosen
|
|
140
|
+
variant screens before continuing from the kept screen.
|
|
139
141
|
- Use framework sharing actions for design and design-system visibility/grants.
|
|
140
142
|
- `/visual-edit` is a public entry route and public `/design/:id` links may
|
|
141
143
|
render read-only public designs without a session. Do not open anonymous write
|
|
@@ -218,10 +220,10 @@ patterns live in `.agents/skills/`.
|
|
|
218
220
|
register that manifest with `connect-localhost`, call `add-localhost-screens`,
|
|
219
221
|
and open the editor in overview mode.
|
|
220
222
|
- For human-in-the-loop UI exploration, create a design shell, call
|
|
221
|
-
`present-design-variants` with 2-5
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
`present-design-variants` with 2-5 concise directions (three by default),
|
|
224
|
+
wait for the user to pick one in chat, delete the other generated variant
|
|
225
|
+
screens with `delete-file`, then use `get-design-snapshot` and
|
|
226
|
+
`generate-design` or `edit-design` for follow-up refinements.
|
|
225
227
|
- If inline chat choice buttons are unavailable, the user can tell you the
|
|
226
228
|
preferred screen name. Do not show a separate variant picker or ask them to
|
|
227
229
|
paste a copyable handoff summary.
|