@cryer/star-cli 0.3.10 → 0.3.11
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/{chunk-MNVCCOI2.js → chunk-63CGUF3F.js} +209 -11
- package/dist/chunk-63CGUF3F.js.map +1 -0
- package/dist/{chunk-PDFC7QVW.js → chunk-GYXF6NPC.js} +28 -6
- package/dist/chunk-GYXF6NPC.js.map +1 -0
- package/dist/{chunk-A5PXPWHB.js → chunk-LSYGHLRU.js} +13 -1
- package/dist/chunk-LSYGHLRU.js.map +1 -0
- package/dist/{chunk-IJGUW4PM.js → chunk-N4MIQSNZ.js} +2 -2
- package/dist/chunk-N4MIQSNZ.js.map +1 -0
- package/dist/{chunk-URC7JXKX.js → chunk-Q5FJN4LN.js} +7 -3
- package/dist/{chunk-URC7JXKX.js.map → chunk-Q5FJN4LN.js.map} +1 -1
- package/dist/{loop-TW2FHL7J.js → loop-AY4PXRVP.js} +5 -5
- package/dist/main.js +7 -8
- package/dist/main.js.map +1 -1
- package/dist/{repl-GBVIW5QR.js → repl-YBNTJOSB.js} +287 -51
- package/dist/repl-YBNTJOSB.js.map +1 -0
- package/dist/{tools-7EBGCYYE.js → tools-BH55BVIS.js} +3 -3
- package/package.json +25 -16
- package/dist/chunk-A5PXPWHB.js.map +0 -1
- package/dist/chunk-IJGUW4PM.js.map +0 -1
- package/dist/chunk-MNVCCOI2.js.map +0 -1
- package/dist/chunk-PDFC7QVW.js.map +0 -1
- package/dist/repl-GBVIW5QR.js.map +0 -1
- /package/dist/{loop-TW2FHL7J.js.map → loop-AY4PXRVP.js.map} +0 -0
- /package/dist/{tools-7EBGCYYE.js.map → tools-BH55BVIS.js.map} +0 -0
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
toCoreMessages,
|
|
13
13
|
toStarMessage,
|
|
14
14
|
trackTree
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-N4MIQSNZ.js";
|
|
16
16
|
import {
|
|
17
17
|
createModel,
|
|
18
18
|
streamActivity
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
ToolRegistry,
|
|
25
25
|
createDefaultRegistry,
|
|
26
26
|
debugStreamLog
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-Q5FJN4LN.js";
|
|
28
28
|
import {
|
|
29
29
|
TodoStore,
|
|
30
30
|
beginTurn,
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
stripOversizedImages,
|
|
44
44
|
undoTurnSnapshots,
|
|
45
45
|
userMemoryPath
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-LSYGHLRU.js";
|
|
47
47
|
|
|
48
48
|
// src/agent/loop.ts
|
|
49
49
|
import { tool as aiTool, generateText as generateText3 } from "ai";
|
|
@@ -76,6 +76,9 @@ function textWeight(text) {
|
|
|
76
76
|
}
|
|
77
77
|
return text.length + cjk * (CHARS_PER_TOKEN - 1);
|
|
78
78
|
}
|
|
79
|
+
function roughTextTokens(text) {
|
|
80
|
+
return Math.ceil(textWeight(text) / CHARS_PER_TOKEN);
|
|
81
|
+
}
|
|
79
82
|
function contentTokenLength(message) {
|
|
80
83
|
if (message.role === "tool") {
|
|
81
84
|
let tokens2 = 0;
|
|
@@ -241,6 +244,7 @@ ${transcript}`,
|
|
|
241
244
|
}
|
|
242
245
|
function compactMessages(messages, maxTokens, opts = {}) {
|
|
243
246
|
const limit = opts.force ? 0 : maxTokens;
|
|
247
|
+
const minKept = opts.force ? 1 : MIN_KEPT_MESSAGES;
|
|
244
248
|
const overhead = opts.overheadTokens ?? 0;
|
|
245
249
|
const tokens = messages.map((star) => estimateMessageTokens(star.message));
|
|
246
250
|
let total = overhead;
|
|
@@ -269,7 +273,7 @@ function compactMessages(messages, maxTokens, opts = {}) {
|
|
|
269
273
|
let droppedCount = 0;
|
|
270
274
|
let droppedTokens = 0;
|
|
271
275
|
for (const turn of turns) {
|
|
272
|
-
if (restCount - droppedCount - turn.length <
|
|
276
|
+
if (restCount - droppedCount - turn.length < minKept) {
|
|
273
277
|
break;
|
|
274
278
|
}
|
|
275
279
|
droppedCount += turn.length;
|
|
@@ -299,6 +303,7 @@ var KEEP_RECENT_MESSAGES = 24;
|
|
|
299
303
|
var MIN_ELISION_TOKENS = 250;
|
|
300
304
|
var ELIDED_TOOL_RESULT_TEXT = "[stale tool output elided to free context \u2014 it was shown in full earlier; re-run the tool if you need it again]";
|
|
301
305
|
var ELIDED_IMAGE_TEXT = "[image elided to free context \u2014 re-attach it if it is still needed]";
|
|
306
|
+
var ELIDED_TOOL_CALL_ARGS_TEXT = "arguments elided to free context \u2014 the call already ran; this placeholder is not valid tool input, re-read the file if you need the old content";
|
|
302
307
|
function elideStaleContent(messages, thresholdTokens, overheadTokens = 0) {
|
|
303
308
|
let total = overheadTokens;
|
|
304
309
|
for (const star of messages) total += estimateMessageTokens(star.message);
|
|
@@ -324,6 +329,19 @@ function elideStaleContent(messages, thresholdTokens, overheadTokens = 0) {
|
|
|
324
329
|
meta: { ...star.meta, elided: true }
|
|
325
330
|
};
|
|
326
331
|
elidedCount++;
|
|
332
|
+
} else if (message.role === "assistant" && Array.isArray(message.content)) {
|
|
333
|
+
if (!message.content.some((part) => part.type === "tool-call")) continue;
|
|
334
|
+
if (estimateMessageTokens(message) < MIN_ELISION_TOKENS) continue;
|
|
335
|
+
out[i] = {
|
|
336
|
+
message: {
|
|
337
|
+
...message,
|
|
338
|
+
content: message.content.map(
|
|
339
|
+
(part) => part.type === "tool-call" ? { ...part, args: { elided: ELIDED_TOOL_CALL_ARGS_TEXT } } : part
|
|
340
|
+
)
|
|
341
|
+
},
|
|
342
|
+
meta: { ...star.meta, elided: true }
|
|
343
|
+
};
|
|
344
|
+
elidedCount++;
|
|
327
345
|
} else if (message.role === "user" && Array.isArray(message.content)) {
|
|
328
346
|
if (!message.content.some((part) => part.type === "image" || part.type === "file")) continue;
|
|
329
347
|
out[i] = {
|
|
@@ -709,7 +727,7 @@ function summarizeStreamError(error) {
|
|
|
709
727
|
}
|
|
710
728
|
|
|
711
729
|
// src/llm/stream.ts
|
|
712
|
-
import { streamText } from "ai";
|
|
730
|
+
import { InvalidToolArgumentsError, streamText } from "ai";
|
|
713
731
|
var DEFAULT_IDLE_TIMEOUT_MS = 6e4;
|
|
714
732
|
var DEFAULT_FIRST_PART_TIMEOUT_MS = 12e4;
|
|
715
733
|
var CONTENT_PART_TYPES = /* @__PURE__ */ new Set(["text-delta", "tool-call", "tool-call-delta"]);
|
|
@@ -791,6 +809,7 @@ async function* streamChat(opts) {
|
|
|
791
809
|
let deltas = 0;
|
|
792
810
|
let hasContent = false;
|
|
793
811
|
const argsProgress = /* @__PURE__ */ new Map();
|
|
812
|
+
let invalidArgsCounter = 0;
|
|
794
813
|
let lastPartAt = Date.now();
|
|
795
814
|
debugStreamLog("request", {
|
|
796
815
|
messages: opts.messages.length,
|
|
@@ -863,6 +882,7 @@ async function* streamChat(opts) {
|
|
|
863
882
|
break;
|
|
864
883
|
case "tool-call":
|
|
865
884
|
hasContent = true;
|
|
885
|
+
argsProgress.delete(part.toolCallId);
|
|
866
886
|
yield {
|
|
867
887
|
type: "tool-call",
|
|
868
888
|
id: part.toolCallId,
|
|
@@ -902,6 +922,22 @@ async function* streamChat(opts) {
|
|
|
902
922
|
usage: part.usage,
|
|
903
923
|
deltas
|
|
904
924
|
});
|
|
925
|
+
for (const [id, progress] of argsProgress) {
|
|
926
|
+
debugStreamLog("truncated-tool-call", {
|
|
927
|
+
toolCallId: id,
|
|
928
|
+
toolName: progress.name,
|
|
929
|
+
chars: progress.chars,
|
|
930
|
+
finishReason: part.finishReason
|
|
931
|
+
});
|
|
932
|
+
yield {
|
|
933
|
+
type: "tool-call",
|
|
934
|
+
id,
|
|
935
|
+
name: progress.name,
|
|
936
|
+
args: {},
|
|
937
|
+
invalidArgs: `the arguments were cut off mid-stream after ${progress.chars} characters (finish reason: ${part.finishReason}) \u2014 the reply hit the output or context limit before the JSON completed. Reissue ${progress.name} with complete arguments; for large content, split it into several smaller calls.`
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
argsProgress.clear();
|
|
905
941
|
yield {
|
|
906
942
|
type: "finish",
|
|
907
943
|
finishReason: part.finishReason,
|
|
@@ -916,6 +952,29 @@ async function* streamChat(opts) {
|
|
|
916
952
|
}
|
|
917
953
|
case "error": {
|
|
918
954
|
if (opts.abortSignal?.aborted) return;
|
|
955
|
+
if (InvalidToolArgumentsError.isInstance(part.error)) {
|
|
956
|
+
const invalid = part.error;
|
|
957
|
+
let args = invalid.toolArgs;
|
|
958
|
+
try {
|
|
959
|
+
args = JSON.parse(invalid.toolArgs);
|
|
960
|
+
} catch {
|
|
961
|
+
}
|
|
962
|
+
const started = [...argsProgress.entries()].reverse().find(([, progress]) => progress.name === invalid.toolName);
|
|
963
|
+
debugStreamLog("invalid-tool-args", {
|
|
964
|
+
toolName: invalid.toolName,
|
|
965
|
+
recoveredId: started?.[0] ?? null
|
|
966
|
+
});
|
|
967
|
+
yield {
|
|
968
|
+
type: "tool-call",
|
|
969
|
+
id: started?.[0] ?? `invalid-args-${++invalidArgsCounter}`,
|
|
970
|
+
name: invalid.toolName,
|
|
971
|
+
args,
|
|
972
|
+
// The zod detail tells the model exactly which field to fix;
|
|
973
|
+
// capped because the message embeds the full rejected value.
|
|
974
|
+
invalidArgs: invalid.message.length > 2e3 ? `${invalid.message.slice(0, 2e3)}\u2026` : invalid.message
|
|
975
|
+
};
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
919
978
|
const error = toStreamErrorInfo(part.error);
|
|
920
979
|
debugStreamLog("error-part", { seenContent, deltas, error: summarizeStreamError(error) });
|
|
921
980
|
yield { type: "error", error };
|
|
@@ -2087,7 +2146,7 @@ function findModelPricing(config, name, modelId) {
|
|
|
2087
2146
|
return config.models.find((m) => m.model === modelId);
|
|
2088
2147
|
}
|
|
2089
2148
|
async function runSubagent(deps, args, cwd, signal, agentIdRef) {
|
|
2090
|
-
const { AgentLoop: AgentLoop2 } = await import("./loop-
|
|
2149
|
+
const { AgentLoop: AgentLoop2 } = await import("./loop-AY4PXRVP.js");
|
|
2091
2150
|
const child = new AgentLoop2({
|
|
2092
2151
|
model: deps.model,
|
|
2093
2152
|
registry: createDefaultRegistry(),
|
|
@@ -2284,6 +2343,7 @@ function finalNudgeWarning(autoContinues, maxAutoContinues) {
|
|
|
2284
2343
|
return autoContinues >= maxAutoContinues ? " This is the final automatic continuation \u2014 if your next reply again has no tool calls, nudging stops and the turn is handed back to the user. Finish the work now or summarize what remains." : "";
|
|
2285
2344
|
}
|
|
2286
2345
|
var COMPLETION_CHECK_TIMEOUT_MS = 3e4;
|
|
2346
|
+
var TODO_STALE_NUDGE_STEPS = 20;
|
|
2287
2347
|
function attachTurnContext(messages, context) {
|
|
2288
2348
|
if (!context) return messages;
|
|
2289
2349
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -2422,6 +2482,29 @@ var AgentLoop = class {
|
|
|
2422
2482
|
// (the tool map's share of the request window) is computed alongside since
|
|
2423
2483
|
// it depends on the same inputs.
|
|
2424
2484
|
aiToolsCache = null;
|
|
2485
|
+
// Server-calibration baseline for the context estimate: the latest finish
|
|
2486
|
+
// event's provider-reported promptTokens paired with the local estimate of
|
|
2487
|
+
// the exact request that produced it. Non-OpenAI tokenizers can count the
|
|
2488
|
+
// same history far higher than the local cl100k estimate (a qwen endpoint
|
|
2489
|
+
// counted ~30% more on a Chinese-heavy code session), which let the ctx%
|
|
2490
|
+
// display and the auto-compaction trigger lag the real window fill until
|
|
2491
|
+
// the provider itself started truncating replies. The delta refreshes on
|
|
2492
|
+
// every step's finish, so drift is bounded to one step's new content.
|
|
2493
|
+
serverTokenBaseline = null;
|
|
2494
|
+
// Text streamed by the in-flight reply (visible text + reasoning +
|
|
2495
|
+
// completed tool-call args). estimateContextTokens folds a rough token
|
|
2496
|
+
// estimate of it into the status-bar ctx% so the display tracks the
|
|
2497
|
+
// server's real-time occupancy — prompt PLUS generated-so-far, which is
|
|
2498
|
+
// what llama.cpp-style monitors show — instead of lagging it by the whole
|
|
2499
|
+
// in-flight generation (10k reasoning tokens ≈ 8% of a 128k window).
|
|
2500
|
+
// Reset when each stream attempt ends: the persisted reply is then counted
|
|
2501
|
+
// through the history estimate, and reasoning — never resent — drops out.
|
|
2502
|
+
// User steering prompts submitted while a turn is running (the REPL's
|
|
2503
|
+
// "insert now" choice). Drained into the history as synthetic user
|
|
2504
|
+
// messages at the next step boundary — the in-flight model reply and its
|
|
2505
|
+
// tool calls always finish first, so a steer never interrupts mid-step.
|
|
2506
|
+
pendingSteers = [];
|
|
2507
|
+
inflightStreamText = "";
|
|
2425
2508
|
// Lazily resolved auxiliary model (config.smallModel) for cheap side calls
|
|
2426
2509
|
// — compaction summaries, session titles, completion checks. Undefined =
|
|
2427
2510
|
// use the main model; a resolution failure is surfaced once as a notice.
|
|
@@ -2487,12 +2570,56 @@ var AgentLoop = class {
|
|
|
2487
2570
|
getStarMessages() {
|
|
2488
2571
|
return this.messages;
|
|
2489
2572
|
}
|
|
2573
|
+
// Queue a user steering prompt for the running turn. Delivery happens at
|
|
2574
|
+
// the next step boundary (see drainSteers); if the turn ends first the
|
|
2575
|
+
// text stays queued and takeUndeliveredSteers hands it back to the caller.
|
|
2576
|
+
injectUserMessage(text) {
|
|
2577
|
+
const trimmed = text.trim();
|
|
2578
|
+
if (trimmed.length > 0) this.pendingSteers.push(trimmed);
|
|
2579
|
+
}
|
|
2580
|
+
// Steers that never reached a step boundary (turn ended, aborted or
|
|
2581
|
+
// errored first). The caller decides their fate — the REPL re-queues them
|
|
2582
|
+
// as ordinary prompts so user input is never dropped.
|
|
2583
|
+
takeUndeliveredSteers() {
|
|
2584
|
+
return this.pendingSteers.splice(0);
|
|
2585
|
+
}
|
|
2586
|
+
// Append queued steers to the history as synthetic user messages: they
|
|
2587
|
+
// belong to the turn in progress, so /undo, /rewind and compaction must
|
|
2588
|
+
// not treat them as turn boundaries. Returns the number delivered.
|
|
2589
|
+
async drainSteers() {
|
|
2590
|
+
const steers = this.pendingSteers.splice(0);
|
|
2591
|
+
for (const text of steers) {
|
|
2592
|
+
const message = { role: "user", content: text };
|
|
2593
|
+
const meta = { synthetic: "steer" };
|
|
2594
|
+
this.messages.push({ message, meta });
|
|
2595
|
+
await this.persist(message, meta);
|
|
2596
|
+
}
|
|
2597
|
+
return steers.length;
|
|
2598
|
+
}
|
|
2490
2599
|
// Wire-schema token overhead of the active tool set (memoized by
|
|
2491
2600
|
// buildAiTools). The auto-compact budget includes it; surfaced here so the
|
|
2492
2601
|
// status bar ctx% can show the same total the compaction decision uses.
|
|
2493
2602
|
getToolSchemaTokens() {
|
|
2494
2603
|
return this.buildAiTools().schemaTokens;
|
|
2495
2604
|
}
|
|
2605
|
+
// Additive correction from serverTokenBaseline, clamped ≥ 0: an
|
|
2606
|
+
// under-reading provider estimate must pull compaction earlier (the bug
|
|
2607
|
+
// this fixes), but an over-reading one must never push it later than the
|
|
2608
|
+
// local estimate alone would — compacting early is safe, late is fatal.
|
|
2609
|
+
calibrationDelta() {
|
|
2610
|
+
const baseline = this.serverTokenBaseline;
|
|
2611
|
+
if (!baseline) return 0;
|
|
2612
|
+
return Math.max(0, baseline.serverTokens - baseline.localTokens);
|
|
2613
|
+
}
|
|
2614
|
+
// Effective context-window usage in tokens: local BPE estimate + tool
|
|
2615
|
+
// schemas + the server-calibration delta + the in-flight reply's streamed
|
|
2616
|
+
// output. This is the status bar's live ctx% view; the elision/compaction
|
|
2617
|
+
// budgets deliberately use only the first three (history + schemas +
|
|
2618
|
+
// calibration), since in-flight reasoning is never resent and must not
|
|
2619
|
+
// trigger a compaction the next request won't need.
|
|
2620
|
+
estimateContextTokens() {
|
|
2621
|
+
return estimateTokens(this.getMessages()) + this.getToolSchemaTokens() + this.calibrationDelta() + (this.inflightStreamText.length > 0 ? roughTextTokens(this.inflightStreamText) : 0);
|
|
2622
|
+
}
|
|
2496
2623
|
// Auxiliary model for cheap side calls: config.smallModel, resolved once.
|
|
2497
2624
|
// An unresolvable name/key degrades to the main model instead of breaking
|
|
2498
2625
|
// turns; the failure rides out once as a notice at the next turn start.
|
|
@@ -2832,6 +2959,9 @@ var AgentLoop = class {
|
|
|
2832
2959
|
let usedToolsThisTurn = false;
|
|
2833
2960
|
let streamCutContinues = 0;
|
|
2834
2961
|
let openTodos = [];
|
|
2962
|
+
let todoRevision = this.opts.registry.todos.revision;
|
|
2963
|
+
let stepsSinceTodoChange = 0;
|
|
2964
|
+
const todoStaleNudgeSteps = this.opts.todoStaleNudgeSteps ?? TODO_STALE_NUDGE_STEPS;
|
|
2835
2965
|
const maxSteps = config.maxSteps;
|
|
2836
2966
|
let step = 0;
|
|
2837
2967
|
let toolExecutedSinceCheckpoint = false;
|
|
@@ -2868,16 +2998,47 @@ ${finished.result}`
|
|
|
2868
2998
|
await this.persist(note, meta);
|
|
2869
2999
|
}
|
|
2870
3000
|
}
|
|
3001
|
+
const steered = await this.drainSteers();
|
|
3002
|
+
if (steered > 0) {
|
|
3003
|
+
yield {
|
|
3004
|
+
type: "notice",
|
|
3005
|
+
message: `${steered} steering message(s) delivered to the model; taking effect from this step.`
|
|
3006
|
+
};
|
|
3007
|
+
}
|
|
3008
|
+
const currentTodoRevision = registry.todos.revision;
|
|
3009
|
+
if (currentTodoRevision !== todoRevision) {
|
|
3010
|
+
todoRevision = currentTodoRevision;
|
|
3011
|
+
stepsSinceTodoChange = 0;
|
|
3012
|
+
} else if (stepsSinceTodoChange >= todoStaleNudgeSteps) {
|
|
3013
|
+
const open = registry.todos.list().filter((t) => t.status !== "done");
|
|
3014
|
+
if (open.length > 0) {
|
|
3015
|
+
stepsSinceTodoChange = 0;
|
|
3016
|
+
yield {
|
|
3017
|
+
type: "notice",
|
|
3018
|
+
message: `todo list unchanged for ${todoStaleNudgeSteps} steps with ${open.length} open item(s); reminding the model to update it.`
|
|
3019
|
+
};
|
|
3020
|
+
const reminder = {
|
|
3021
|
+
role: "user",
|
|
3022
|
+
content: `[todo reminder] Your todo list still has ${open.length} open item(s), unchanged for ${todoStaleNudgeSteps} steps: ${open.slice(0, 5).map((t) => `"${t.title}"`).join(
|
|
3023
|
+
", "
|
|
3024
|
+
)}${open.length > 5 ? ", \u2026" : ""}. Update the list with todo_write so it reflects what is done and what you are working on now, then continue working.`
|
|
3025
|
+
};
|
|
3026
|
+
const meta = { synthetic: "nudge" };
|
|
3027
|
+
this.messages.push({ message: reminder, meta });
|
|
3028
|
+
await this.persist(reminder, meta);
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
2871
3031
|
const maxTokens = this.opts.contextMaxTokens ?? config.contextMaxTokens;
|
|
2872
3032
|
const threshold = resolveCompactThreshold(config, maxTokens);
|
|
2873
|
-
const
|
|
3033
|
+
const calibration = this.calibrationDelta();
|
|
3034
|
+
const elided = elideStaleContent(this.messages, threshold, toolSchemaTokens + calibration);
|
|
2874
3035
|
if (elided) {
|
|
2875
3036
|
this.messages = elided.messages;
|
|
2876
3037
|
await this.opts.sessionStore?.replaceMessages([...this.messages]);
|
|
2877
3038
|
this.opts.registry?.resetVolatileState();
|
|
2878
3039
|
}
|
|
2879
3040
|
const compacted = compactMessages(this.messages, threshold, {
|
|
2880
|
-
overheadTokens: toolSchemaTokens
|
|
3041
|
+
overheadTokens: toolSchemaTokens + calibration
|
|
2881
3042
|
});
|
|
2882
3043
|
if (compacted.compacted) {
|
|
2883
3044
|
this.messages = await this.applyCompactionSummary(compacted, signal);
|
|
@@ -2885,6 +3046,7 @@ ${finished.result}`
|
|
|
2885
3046
|
this.turnMarkers = [];
|
|
2886
3047
|
this.opts.registry?.resetVolatileState();
|
|
2887
3048
|
}
|
|
3049
|
+
const requestLocalTokens = estimateTokens(this.getMessages()) + toolSchemaTokens;
|
|
2888
3050
|
const maxRetries = Math.max(0, config.streamMaxRetries);
|
|
2889
3051
|
const baseDelay = this.opts.retryDelayMs ?? STREAM_RETRY_BASE_DELAY_MS;
|
|
2890
3052
|
let text = "";
|
|
@@ -2905,22 +3067,37 @@ ${finished.result}`
|
|
|
2905
3067
|
lastUsage = void 0;
|
|
2906
3068
|
sawReasoning = false;
|
|
2907
3069
|
wasTruncated = false;
|
|
3070
|
+
this.inflightStreamText = "";
|
|
2908
3071
|
const timeoutScale = Math.min(attempt + 1, 3);
|
|
2909
3072
|
try {
|
|
2910
3073
|
for await (const event of this.streamOnce(aiTools, signal, timeoutScale)) {
|
|
2911
3074
|
if (event.type === "text-delta") {
|
|
2912
3075
|
text += event.text;
|
|
3076
|
+
this.inflightStreamText += event.text;
|
|
2913
3077
|
yield event;
|
|
2914
3078
|
} else if (event.type === "reasoning") {
|
|
2915
3079
|
sawReasoning = true;
|
|
3080
|
+
this.inflightStreamText += event.text;
|
|
2916
3081
|
yield event;
|
|
2917
3082
|
} else if (event.type === "tool-call") {
|
|
2918
|
-
|
|
3083
|
+
this.inflightStreamText += JSON.stringify(event.args ?? null);
|
|
3084
|
+
toolCalls.push({
|
|
3085
|
+
id: event.id,
|
|
3086
|
+
name: event.name,
|
|
3087
|
+
args: event.args,
|
|
3088
|
+
...event.invalidArgs !== void 0 ? { invalidArgs: event.invalidArgs } : {}
|
|
3089
|
+
});
|
|
2919
3090
|
yield event;
|
|
2920
3091
|
} else if (event.type === "finish") {
|
|
2921
3092
|
lastFinishReason = event.finishReason;
|
|
2922
3093
|
if (event.truncated) wasTruncated = true;
|
|
2923
3094
|
lastUsage = event.usage;
|
|
3095
|
+
if (event.usage && event.usage.promptTokens > 0) {
|
|
3096
|
+
this.serverTokenBaseline = {
|
|
3097
|
+
localTokens: requestLocalTokens,
|
|
3098
|
+
serverTokens: event.usage.promptTokens
|
|
3099
|
+
};
|
|
3100
|
+
}
|
|
2924
3101
|
yield this.foldSubagentUsage(event);
|
|
2925
3102
|
} else if (event.type === "error") {
|
|
2926
3103
|
failure = event.error;
|
|
@@ -2933,6 +3110,7 @@ ${finished.result}`
|
|
|
2933
3110
|
failure = toStreamErrorInfo(error);
|
|
2934
3111
|
}
|
|
2935
3112
|
}
|
|
3113
|
+
this.inflightStreamText = "";
|
|
2936
3114
|
if (signal.aborted) {
|
|
2937
3115
|
yield* this.persistInterrupted(text, toolCalls);
|
|
2938
3116
|
await this.runStopHooks("aborted");
|
|
@@ -2985,6 +3163,14 @@ ${finished.result}`
|
|
|
2985
3163
|
return;
|
|
2986
3164
|
}
|
|
2987
3165
|
if (!hasOutput()) {
|
|
3166
|
+
const steered2 = await this.drainSteers();
|
|
3167
|
+
if (steered2 > 0) {
|
|
3168
|
+
yield {
|
|
3169
|
+
type: "notice",
|
|
3170
|
+
message: `${steered2} steering message(s) delivered to the model after an empty reply.`
|
|
3171
|
+
};
|
|
3172
|
+
continue;
|
|
3173
|
+
}
|
|
2988
3174
|
if (isSteerableEmptyFinish(lastFinishReason) && config.permissionMode !== "plan" && autoContinues < config.maxAutoContinues) {
|
|
2989
3175
|
autoContinues++;
|
|
2990
3176
|
lastWasNudge = true;
|
|
@@ -3026,6 +3212,16 @@ ${finished.result}`
|
|
|
3026
3212
|
this.messages.push({ message: assistantMessage });
|
|
3027
3213
|
await this.persist(assistantMessage);
|
|
3028
3214
|
if (toolCalls.length === 0) {
|
|
3215
|
+
const steered2 = await this.drainSteers();
|
|
3216
|
+
if (steered2 > 0) {
|
|
3217
|
+
autoContinues = 0;
|
|
3218
|
+
lastWasNudge = false;
|
|
3219
|
+
yield {
|
|
3220
|
+
type: "notice",
|
|
3221
|
+
message: `${steered2} steering message(s) delivered to the model; the turn continues.`
|
|
3222
|
+
};
|
|
3223
|
+
continue;
|
|
3224
|
+
}
|
|
3029
3225
|
if (wasTruncated) {
|
|
3030
3226
|
if (config.permissionMode !== "plan" && streamCutContinues < config.streamMaxRetries) {
|
|
3031
3227
|
streamCutContinues++;
|
|
@@ -3114,7 +3310,8 @@ ${finished.result}`
|
|
|
3114
3310
|
try {
|
|
3115
3311
|
for (const call of toolCalls) {
|
|
3116
3312
|
if (signal.aborted) break;
|
|
3117
|
-
const refusal =
|
|
3313
|
+
const refusal = call.invalidArgs !== void 0 ? `Tool call not executed \u2014 ${call.invalidArgs}
|
|
3314
|
+
Reissue ${call.name} with valid arguments.` : this.doomLoopRefusal(call);
|
|
3118
3315
|
if (refusal !== null) {
|
|
3119
3316
|
yield { type: "notice", message: refusal };
|
|
3120
3317
|
} else {
|
|
@@ -3194,6 +3391,7 @@ ${finished.result}`
|
|
|
3194
3391
|
return;
|
|
3195
3392
|
}
|
|
3196
3393
|
step++;
|
|
3394
|
+
stepsSinceTodoChange++;
|
|
3197
3395
|
}
|
|
3198
3396
|
}
|
|
3199
3397
|
// The permission mode can change at runtime, so the system message is
|
|
@@ -3507,4 +3705,4 @@ export {
|
|
|
3507
3705
|
attachTurnContext,
|
|
3508
3706
|
AgentLoop
|
|
3509
3707
|
};
|
|
3510
|
-
//# sourceMappingURL=chunk-
|
|
3708
|
+
//# sourceMappingURL=chunk-63CGUF3F.js.map
|