@agent-native/core 0.41.0 → 0.42.0
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/dist/action.d.ts +13 -1
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js.map +1 -1
- package/dist/agent/production-agent.d.ts +8 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +93 -0
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/app-skill.d.ts +16 -0
- package/dist/cli/app-skill.d.ts.map +1 -1
- package/dist/cli/app-skill.js +33 -3
- package/dist/cli/app-skill.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +57 -0
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +1 -1
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +14 -3
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills.d.ts +34 -3
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +172 -48
- package/dist/cli/skills.js.map +1 -1
- package/dist/cli/workspacify.d.ts.map +1 -1
- package/dist/cli/workspacify.js +19 -4
- package/dist/cli/workspacify.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +2 -2
- 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 +172 -5
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts +19 -0
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +5 -57
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
- package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/ApiEndpointBlock.js +116 -7
- package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -1
- package/dist/client/blocks/library/DataModelBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/DataModelBlock.js +75 -9
- package/dist/client/blocks/library/DataModelBlock.js.map +1 -1
- package/dist/client/blocks/library/DiffBlock.d.ts +1 -1
- package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/DiffBlock.js +195 -34
- package/dist/client/blocks/library/DiffBlock.js.map +1 -1
- package/dist/client/blocks/library/HighlightedCode.d.ts +1 -1
- package/dist/client/blocks/library/HighlightedCode.js +1 -1
- package/dist/client/blocks/library/HighlightedCode.js.map +1 -1
- package/dist/client/blocks/library/annotation-rail.d.ts +96 -0
- package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -0
- package/dist/client/blocks/library/annotation-rail.js +120 -0
- package/dist/client/blocks/library/annotation-rail.js.map +1 -0
- package/dist/client/blocks/library/api-endpoint.config.d.ts +31 -6
- package/dist/client/blocks/library/api-endpoint.config.d.ts.map +1 -1
- package/dist/client/blocks/library/api-endpoint.config.js +30 -6
- package/dist/client/blocks/library/api-endpoint.config.js.map +1 -1
- package/dist/client/blocks/library/code.d.ts.map +1 -1
- package/dist/client/blocks/library/code.js +32 -15
- package/dist/client/blocks/library/code.js.map +1 -1
- package/dist/client/blocks/library/columns.d.ts.map +1 -1
- package/dist/client/blocks/library/columns.js +56 -35
- package/dist/client/blocks/library/columns.js.map +1 -1
- package/dist/client/blocks/library/data-model.config.d.ts +17 -0
- package/dist/client/blocks/library/data-model.config.d.ts.map +1 -1
- package/dist/client/blocks/library/data-model.config.js +15 -0
- package/dist/client/blocks/library/data-model.config.js.map +1 -1
- package/dist/client/blocks/library/diff.config.d.ts +28 -6
- package/dist/client/blocks/library/diff.config.d.ts.map +1 -1
- package/dist/client/blocks/library/diff.config.js +30 -6
- package/dist/client/blocks/library/diff.config.js.map +1 -1
- package/dist/client/blocks/types.d.ts +2 -2
- package/dist/client/blocks/types.d.ts.map +1 -1
- package/dist/client/blocks/types.js.map +1 -1
- package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/DragHandle.js +75 -9
- package/dist/client/rich-markdown-editor/DragHandle.js.map +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts +25 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js +29 -6
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +8 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.js +5 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +159 -12
- package/dist/extensions/actions.js.map +1 -1
- package/dist/extensions/store.d.ts +21 -0
- package/dist/extensions/store.d.ts.map +1 -1
- package/dist/extensions/store.js +33 -1
- package/dist/extensions/store.js.map +1 -1
- package/dist/server/recap-image-route.d.ts.map +1 -1
- package/dist/server/recap-image-route.js +12 -3
- package/dist/server/recap-image-route.js.map +1 -1
- package/dist/templates/default/pnpm-workspace.yaml +7 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +30 -5
- package/dist/templates/workspace-root/package.json +0 -5
- package/dist/templates/workspace-root/pnpm-workspace.yaml +14 -0
- package/docs/content/plan-plugin.md +107 -0
- package/docs/content/skills-guide.md +8 -0
- package/docs/content/visual-plans.md +2 -0
- package/package.json +5 -1
- package/src/templates/default/pnpm-workspace.yaml +7 -0
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +30 -5
- package/src/templates/workspace-root/package.json +0 -5
- package/src/templates/workspace-root/pnpm-workspace.yaml +14 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-chat-adapter.d.ts","sourceRoot":"","sources":["../../src/client/agent-chat-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,qBAAqB,CAAC;AAgBhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAKrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,oBAAoB;AAC9B;;;;GAIG;AACD,KAAK;AACP,0EAA0E;GACxE,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-chat-adapter.d.ts","sourceRoot":"","sources":["../../src/client/agent-chat-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,qBAAqB,CAAC;AAgBhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAKrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,oBAAoB;AAC9B;;;;GAIG;AACD,KAAK;AACP,0EAA0E;GACxE,WAAW,CAAC;AAm+BhB;;;;GAIG;AACH;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC3C,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC5C,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,eAAe,GAAG,SAAS,CAAA;KAAE,CAAC;IACrD,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAAA;KAAE,CAAC;IAC3D,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC,GAAG,gBAAgB,CA4nCnB"}
|
|
@@ -29,9 +29,29 @@ const MAX_TOTAL_TRANSIENT_CONTINUATIONS = 32;
|
|
|
29
29
|
// times lets a transient slow start recover, while the cap still terminates a
|
|
30
30
|
// genuinely stuck turn with a clear message instead of looping forever.
|
|
31
31
|
const MAX_EMPTY_TRANSIENT_CONTINUATIONS = 3;
|
|
32
|
+
// How many consecutive continuations that re-stream the SAME narration without
|
|
33
|
+
// advancing (no in-flight tool, no completed tool) we tolerate before giving
|
|
34
|
+
// up. A model that degenerates into repeating one phrase ("I have the full
|
|
35
|
+
// HTML. Creating the extension now!") emits "new" text every continuation,
|
|
36
|
+
// which keeps resetting the stalled/empty budgets — so without this guard the
|
|
37
|
+
// stuck turn burns the entire MAX_TOTAL_TRANSIENT_CONTINUATIONS budget (each
|
|
38
|
+
// round re-sending any large pasted payload) before bailing. Catching the
|
|
39
|
+
// repeat ends it in a few rounds with a clear, actionable message instead.
|
|
40
|
+
const MAX_REPEATED_TRANSIENT_CONTINUATIONS = 3;
|
|
32
41
|
const RETRY_BASE_DELAY_MS = 500;
|
|
33
42
|
const RETRY_MAX_DELAY_MS = 8_000;
|
|
34
43
|
const MAX_HISTORY_ATTACHMENT_CHARS = 60_000;
|
|
44
|
+
// The attachment submitted with the CURRENT turn gets a much larger cap than
|
|
45
|
+
// prior-history embedding. The server threads this turn's attachments into each
|
|
46
|
+
// action's ActionRunContext, and `create-extension`/`update-extension` host a
|
|
47
|
+
// pasted file verbatim from it via `contentFromAttachment` — a feature whose
|
|
48
|
+
// whole point is large pastes. Truncating the outbound payload to the 60K
|
|
49
|
+
// history cap would silently cut a >60K HTML/Alpine file before the server ever
|
|
50
|
+
// reads it, hosting a broken extension. Mirror the large-input tool-arg cap
|
|
51
|
+
// (MAX_HISTORY_LARGE_TOOL_ARGS_CHARS) so realistic pasted files survive intact;
|
|
52
|
+
// the trailing truncation notice still makes a pathological multi-MB paste
|
|
53
|
+
// visibly (not silently) capped.
|
|
54
|
+
const MAX_OUTBOUND_ATTACHMENT_CHARS = 200_000;
|
|
35
55
|
const MAX_HISTORY_MESSAGES = 24;
|
|
36
56
|
const MAX_HISTORY_TOTAL_CHARS = 64_000;
|
|
37
57
|
const MAX_HISTORY_MESSAGE_CHARS = 12_000;
|
|
@@ -216,10 +236,10 @@ function truncateHistoryAttachment(text) {
|
|
|
216
236
|
return `${text.slice(0, MAX_HISTORY_ATTACHMENT_CHARS)}\n\n[Attachment truncated after ${MAX_HISTORY_ATTACHMENT_CHARS.toLocaleString()} characters; ${omitted.toLocaleString()} characters omitted from prior chat history.]`;
|
|
217
237
|
}
|
|
218
238
|
function truncateOutboundAttachment(text) {
|
|
219
|
-
if (text.length <=
|
|
239
|
+
if (text.length <= MAX_OUTBOUND_ATTACHMENT_CHARS)
|
|
220
240
|
return text;
|
|
221
|
-
const omitted = text.length -
|
|
222
|
-
return `${text.slice(0,
|
|
241
|
+
const omitted = text.length - MAX_OUTBOUND_ATTACHMENT_CHARS;
|
|
242
|
+
return `${text.slice(0, MAX_OUTBOUND_ATTACHMENT_CHARS)}\n\n[Attachment truncated after ${MAX_OUTBOUND_ATTACHMENT_CHARS.toLocaleString()} characters; ${omitted.toLocaleString()} characters omitted from the submitted attachment.]`;
|
|
223
243
|
}
|
|
224
244
|
function attachmentHistoryText(attachment) {
|
|
225
245
|
if (typeof attachment.text === "string" && attachment.text.length > 0) {
|
|
@@ -448,6 +468,30 @@ function hasContinuationProgress(content) {
|
|
|
448
468
|
? part.text.trim().length > 0
|
|
449
469
|
: part.result !== undefined);
|
|
450
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* Signature of the *unique* sentence-like segments in a continuation's newly
|
|
473
|
+
* streamed text, used to detect a degenerate repetition loop. A stuck model
|
|
474
|
+
* re-emits the same phrase ("I have the full HTML. Creating the extension
|
|
475
|
+
* now!") an arbitrary number of times per run, so the set of unique segments
|
|
476
|
+
* is small and stable across continuations regardless of how many times any
|
|
477
|
+
* single run repeated it. An empty signature (no visible text) is never a
|
|
478
|
+
* repeat — the stalled/empty budgets handle no-output stalls instead.
|
|
479
|
+
*/
|
|
480
|
+
function continuationRepeatSignature(content) {
|
|
481
|
+
const text = content
|
|
482
|
+
.map((part) => (part.type === "text" ? part.text : ""))
|
|
483
|
+
.join(" ")
|
|
484
|
+
.toLowerCase();
|
|
485
|
+
const segments = text
|
|
486
|
+
// Split after sentence punctuation even when runs are concatenated without
|
|
487
|
+
// a following space ("...now!I have..."), and on newlines.
|
|
488
|
+
.split(/(?<=[.!?])|\n+/)
|
|
489
|
+
.map((segment) => segment.replace(/[^a-z0-9]+/g, " ").trim())
|
|
490
|
+
.filter((segment) => segment.length > 0);
|
|
491
|
+
if (segments.length === 0)
|
|
492
|
+
return "";
|
|
493
|
+
return Array.from(new Set(segments)).sort().join("");
|
|
494
|
+
}
|
|
451
495
|
/**
|
|
452
496
|
* True when an action was streamed but never returned a result yet — i.e. a
|
|
453
497
|
* `tool_start` with no matching `tool_done`. The server is still executing it,
|
|
@@ -479,6 +523,17 @@ function stableJson(value) {
|
|
|
479
523
|
return String(value ?? "");
|
|
480
524
|
}
|
|
481
525
|
}
|
|
526
|
+
// Bounded signature of an in-flight tool call's input, used by the stall guard
|
|
527
|
+
// to tell a genuinely-stuck retry (same tool, same payload, connection keeps
|
|
528
|
+
// dropping) apart from a legitimate retry with a CHANGED payload (e.g. the
|
|
529
|
+
// `create-extension` cutoff nudge tells the model to re-send a smaller body).
|
|
530
|
+
// A changed payload yields a different signature, so the guard resets that
|
|
531
|
+
// tool's stall budget instead of aborting the new attempt as a repeat. Bounded
|
|
532
|
+
// to length + head so we never retain a large pasted payload across rounds.
|
|
533
|
+
function inFlightToolInputSignature(part) {
|
|
534
|
+
const raw = part.argsText ?? stableJson(part.args);
|
|
535
|
+
return `${raw.length}${raw.slice(0, 256)}`;
|
|
536
|
+
}
|
|
482
537
|
function toolContinuationKey(part) {
|
|
483
538
|
return [
|
|
484
539
|
part.toolCallId,
|
|
@@ -535,8 +590,13 @@ function autoContinueMessage(signal) {
|
|
|
535
590
|
: signal.reason === "stream_ended"
|
|
536
591
|
? "The previous stream ended before the agent sent a final completion signal."
|
|
537
592
|
: "The previous run reached an internal execution budget.";
|
|
538
|
-
|
|
539
|
-
|
|
593
|
+
// A run_timeout or a mid-stream cutoff while assembling one large action
|
|
594
|
+
// payload (e.g. inlining a big pasted HTML file into `create-extension`) is
|
|
595
|
+
// the classic trigger for the repetition/cutoff loop. Nudge the model toward
|
|
596
|
+
// a compact first version it can actually finish in a single run.
|
|
597
|
+
const cutoffPreparingAction = signal.reason === "run_timeout" || signal.reason === "stream_ended";
|
|
598
|
+
const actionInputNote = cutoffPreparingAction && tool
|
|
599
|
+
? `\n\nThe previous run was cut off while preparing the \`${tool}\` action input before the action could finish. Avoid spending another whole run assembling one large tool payload. If this is \`create-extension\`, create a compact working v1 first, then use focused \`update-extension\` edits for refinements.`
|
|
540
600
|
: "";
|
|
541
601
|
return `${AUTO_CONTINUE_PROMPT}\n\nInternal note: ${reason}${actionInputNote}`;
|
|
542
602
|
}
|
|
@@ -841,6 +901,22 @@ export function createAgentChatAdapter(options) {
|
|
|
841
901
|
let stalledTransientContinuationAttempts = 0;
|
|
842
902
|
let emptyTransientContinuationAttempts = 0;
|
|
843
903
|
let totalTransientContinuationAttempts = 0;
|
|
904
|
+
let repeatedTransientContinuationAttempts = 0;
|
|
905
|
+
let lastContinuationRepeatSignature = null;
|
|
906
|
+
let recoveryGaveUpOnRepetition = false;
|
|
907
|
+
// Track when the same write tool is stuck in-flight across consecutive
|
|
908
|
+
// continuations (connection keeps dropping mid-execution). This is
|
|
909
|
+
// orthogonal to the text-repeat guard — in-flight tools reset
|
|
910
|
+
// madeProgress=true so the stalled/empty budgets never fire, but the tool
|
|
911
|
+
// never actually completes. After MAX_REPEATED_INFLIGHT_TOOL_STALLS
|
|
912
|
+
// consecutive interruptions of the same tool, bail with a clear message.
|
|
913
|
+
let lastInFlightToolName;
|
|
914
|
+
// Signature of the stuck tool's input. A retry with a changed payload
|
|
915
|
+
// (different signature) resets the stall count so the new attempt gets
|
|
916
|
+
// its own budget rather than inheriting the prior payload's.
|
|
917
|
+
let lastInFlightToolSignature;
|
|
918
|
+
let repeatedInFlightToolCount = 0;
|
|
919
|
+
const MAX_REPEATED_INFLIGHT_TOOL_STALLS = 3;
|
|
844
920
|
const continuationHistoryFragments = [];
|
|
845
921
|
const structuredContinuationFragments = [];
|
|
846
922
|
let visibleContinuationPrefix = [];
|
|
@@ -857,6 +933,11 @@ export function createAgentChatAdapter(options) {
|
|
|
857
933
|
`stale_run_continuations: ${staleRunContinuationAttempts}`,
|
|
858
934
|
`stalled_transient_continuations: ${stalledTransientContinuationAttempts}`,
|
|
859
935
|
`empty_transient_continuations: ${emptyTransientContinuationAttempts}`,
|
|
936
|
+
`repeated_transient_continuations: ${repeatedTransientContinuationAttempts}`,
|
|
937
|
+
`repeated_inflight_tool_stalls: ${repeatedInFlightToolCount}`,
|
|
938
|
+
lastInFlightToolName
|
|
939
|
+
? `last_inflight_tool: ${lastInFlightToolName}`
|
|
940
|
+
: "",
|
|
860
941
|
`total_transient_continuations: ${totalTransientContinuationAttempts}`,
|
|
861
942
|
attemptedRunIds.length > 0
|
|
862
943
|
? `attempted_runs: ${attemptedRunIds.join(", ")}`
|
|
@@ -866,6 +947,9 @@ export function createAgentChatAdapter(options) {
|
|
|
866
947
|
.join("\n");
|
|
867
948
|
};
|
|
868
949
|
const exhaustedRecoveryMessage = (reason) => {
|
|
950
|
+
if (recoveryGaveUpOnRepetition) {
|
|
951
|
+
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.";
|
|
952
|
+
}
|
|
869
953
|
if (content.length === 0 &&
|
|
870
954
|
(reason === "run_timeout" ||
|
|
871
955
|
reason === "no_progress" ||
|
|
@@ -927,6 +1011,9 @@ export function createAgentChatAdapter(options) {
|
|
|
927
1011
|
staleRunContinuationAttempts,
|
|
928
1012
|
stalledTransientContinuationAttempts,
|
|
929
1013
|
emptyTransientContinuationAttempts,
|
|
1014
|
+
repeatedTransientContinuationAttempts,
|
|
1015
|
+
repeatedInFlightToolCount,
|
|
1016
|
+
lastInFlightToolName,
|
|
930
1017
|
totalTransientContinuationAttempts,
|
|
931
1018
|
...extra,
|
|
932
1019
|
},
|
|
@@ -941,6 +1028,9 @@ export function createAgentChatAdapter(options) {
|
|
|
941
1028
|
staleRunContinuationAttempts,
|
|
942
1029
|
stalledTransientContinuationAttempts,
|
|
943
1030
|
emptyTransientContinuationAttempts,
|
|
1031
|
+
repeatedTransientContinuationAttempts,
|
|
1032
|
+
repeatedInFlightToolCount,
|
|
1033
|
+
lastInFlightToolName,
|
|
944
1034
|
totalTransientContinuationAttempts,
|
|
945
1035
|
},
|
|
946
1036
|
},
|
|
@@ -1104,12 +1194,89 @@ export function createAgentChatAdapter(options) {
|
|
|
1104
1194
|
// for the stalled/empty caps.
|
|
1105
1195
|
const madeProgress = madeContentProgress || hasInFlightTool;
|
|
1106
1196
|
const madeDurableToolProgress = visibleContent.some((part) => part.type === "tool-call" && part.result !== undefined);
|
|
1197
|
+
// In-flight tool stall guard. When the same write tool is stuck
|
|
1198
|
+
// in-flight because the connection keeps dropping (stream_ended),
|
|
1199
|
+
// hasInFlightTool=true keeps madeProgress=true and completely
|
|
1200
|
+
// bypasses the stalled/empty budgets. Track the last in-flight tool
|
|
1201
|
+
// name; when the same tool is still unresolved after
|
|
1202
|
+
// MAX_REPEATED_INFLIGHT_TOOL_STALLS consecutive stream_ended events,
|
|
1203
|
+
// bail with a clear message.
|
|
1204
|
+
//
|
|
1205
|
+
// Only count stream_ended (connection drop / reconnect failed), NOT
|
|
1206
|
+
// run_timeout (server legitimately still executing a slow tool). A
|
|
1207
|
+
// run_timeout with an in-flight tool means the server is actively
|
|
1208
|
+
// working and reconnection may still recover the result; a repeated
|
|
1209
|
+
// stream_ended means the connection keeps breaking under that payload.
|
|
1210
|
+
const currentInFlightToolPart = visibleContent.find((p) => p.type === "tool-call" &&
|
|
1211
|
+
p.result === undefined &&
|
|
1212
|
+
p.activity !== true);
|
|
1213
|
+
const currentInFlightToolName = currentInFlightToolPart?.toolName;
|
|
1214
|
+
const currentInFlightToolSignature = currentInFlightToolPart
|
|
1215
|
+
? inFlightToolInputSignature(currentInFlightToolPart)
|
|
1216
|
+
: undefined;
|
|
1217
|
+
const isConnectionDrop = signal.reason === "stream_ended";
|
|
1218
|
+
if (currentInFlightToolName && isConnectionDrop) {
|
|
1219
|
+
if (currentInFlightToolName === lastInFlightToolName &&
|
|
1220
|
+
currentInFlightToolSignature === lastInFlightToolSignature) {
|
|
1221
|
+
repeatedInFlightToolCount += 1;
|
|
1222
|
+
}
|
|
1223
|
+
else {
|
|
1224
|
+
// New tool, or the same tool retried with a CHANGED (e.g.
|
|
1225
|
+
// smaller) payload — give the changed input a fresh stall budget
|
|
1226
|
+
// instead of aborting it as a repeat of the prior payload.
|
|
1227
|
+
repeatedInFlightToolCount = 0;
|
|
1228
|
+
lastInFlightToolName = currentInFlightToolName;
|
|
1229
|
+
lastInFlightToolSignature = currentInFlightToolSignature;
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
else if (!currentInFlightToolName) {
|
|
1233
|
+
repeatedInFlightToolCount = 0;
|
|
1234
|
+
}
|
|
1235
|
+
// Degenerate repetition guard. When the model gets stuck re-streaming
|
|
1236
|
+
// the SAME narration every continuation without ever starting or
|
|
1237
|
+
// finishing a tool, each round is "new" text — so madeProgress stays
|
|
1238
|
+
// true and the stalled/empty budgets never trip. Compare this round's
|
|
1239
|
+
// unique-sentence signature to the previous round's; a match with no
|
|
1240
|
+
// tool progress is a non-advancing loop. Tracked by its own counter
|
|
1241
|
+
// so it bails after a few rounds instead of the full transient budget,
|
|
1242
|
+
// without perturbing the stalled/empty/stale accounting.
|
|
1243
|
+
const repeatSignature = continuationRepeatSignature(visibleContent);
|
|
1244
|
+
const isNonAdvancingRepeat = signal.reason !== "loop_limit" &&
|
|
1245
|
+
repeatSignature !== "" &&
|
|
1246
|
+
repeatSignature === lastContinuationRepeatSignature &&
|
|
1247
|
+
!hasInFlightTool &&
|
|
1248
|
+
!madeDurableToolProgress;
|
|
1107
1249
|
if (signal.reason === "loop_limit") {
|
|
1108
1250
|
stalledTransientContinuationAttempts = 0;
|
|
1109
1251
|
emptyTransientContinuationAttempts = 0;
|
|
1110
1252
|
}
|
|
1111
1253
|
else {
|
|
1112
1254
|
totalTransientContinuationAttempts += 1;
|
|
1255
|
+
// Bail when the same write tool is stuck in-flight across too many
|
|
1256
|
+
// consecutive continuations. Checked before the text-repeat guard
|
|
1257
|
+
// because hasInFlightTool=true would mask the repeat as progress.
|
|
1258
|
+
if (repeatedInFlightToolCount >= MAX_REPEATED_INFLIGHT_TOOL_STALLS) {
|
|
1259
|
+
recoveryGaveUpOnRepetition = true;
|
|
1260
|
+
return { ok: false, resetVisibleContent: false };
|
|
1261
|
+
}
|
|
1262
|
+
// Bail fast on a non-advancing repetition loop, well before the
|
|
1263
|
+
// stalled/empty/total budgets would (each round otherwise re-sends
|
|
1264
|
+
// the whole pasted payload). Tracked separately so it never trips
|
|
1265
|
+
// on legitimately-progressing runs that happen to be slow.
|
|
1266
|
+
if (isNonAdvancingRepeat) {
|
|
1267
|
+
repeatedTransientContinuationAttempts += 1;
|
|
1268
|
+
if (repeatedTransientContinuationAttempts >
|
|
1269
|
+
MAX_REPEATED_TRANSIENT_CONTINUATIONS) {
|
|
1270
|
+
recoveryGaveUpOnRepetition = true;
|
|
1271
|
+
return { ok: false, resetVisibleContent: false };
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
else {
|
|
1275
|
+
repeatedTransientContinuationAttempts = 0;
|
|
1276
|
+
}
|
|
1277
|
+
if (repeatSignature) {
|
|
1278
|
+
lastContinuationRepeatSignature = repeatSignature;
|
|
1279
|
+
}
|
|
1113
1280
|
// A run_timeout that produced nothing visible and no activity (the
|
|
1114
1281
|
// model spent the whole soft-timeout window thinking before its
|
|
1115
1282
|
// first output) is NOT an immediate give-up: a transient slow start
|