@bubblebrain-ai/bubble 0.0.49 → 0.0.51
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/agent/abort-errors.d.ts +4 -0
- package/dist/agent/abort-errors.js +30 -0
- package/dist/agent/profiles.js +3 -4
- package/dist/agent/provider-fingerprint.d.ts +6 -0
- package/dist/agent/provider-fingerprint.js +89 -0
- package/dist/agent/resident-history.d.ts +7 -0
- package/dist/agent/resident-history.js +61 -0
- package/dist/agent/subtask-policy.js +4 -4
- package/dist/agent/transcript-helpers.d.ts +20 -0
- package/dist/agent/transcript-helpers.js +62 -0
- package/dist/agent/update-queue.d.ts +13 -0
- package/dist/agent/update-queue.js +69 -0
- package/dist/agent.d.ts +33 -26
- package/dist/agent.js +80 -334
- package/dist/debug-trace.js +0 -2
- package/dist/external-runtime/grok-runtime.js +1 -1
- package/dist/feishu/agent-host/run-driver.js +1 -8
- package/dist/feishu/card/run-state.js +0 -3
- package/dist/lsp/service.js +2 -2
- package/dist/main.js +0 -15
- package/dist/memory/phase1.js +0 -2
- package/dist/model-catalog.js +8 -6
- package/dist/permissions/settings.d.ts +0 -1
- package/dist/permissions/settings.js +0 -2
- package/dist/prompt/compose.js +0 -3
- package/dist/prompt/environment.js +0 -2
- package/dist/prompt/reminders.js +1 -1
- package/dist/provider-registry.js +13 -1
- package/dist/provider-transform.js +7 -8
- package/dist/sdk/index.d.ts +17 -2
- package/dist/sdk/index.js +37 -6
- package/dist/session-log.d.ts +2 -4
- package/dist/session-log.js +0 -23
- package/dist/session-types.d.ts +2 -6
- package/dist/session.d.ts +1 -3
- package/dist/session.js +0 -8
- package/dist/slash-commands/commands.js +13 -13
- package/dist/tools/child-tools.d.ts +1 -1
- package/dist/tools/child-tools.js +1 -1
- package/dist/tools/grep.js +1 -1
- package/dist/tools/index.d.ts +0 -3
- package/dist/tools/index.js +0 -3
- package/dist/tui-ink/app-helpers.d.ts +43 -0
- package/dist/tui-ink/app-helpers.js +150 -0
- package/dist/tui-ink/app.d.ts +4 -11
- package/dist/tui-ink/app.js +57 -657
- package/dist/tui-ink/command-palette.d.ts +32 -0
- package/dist/tui-ink/command-palette.js +158 -0
- package/dist/tui-ink/display-reconstruct.d.ts +3 -0
- package/dist/tui-ink/display-reconstruct.js +82 -0
- package/dist/tui-ink/message-list.js +0 -1
- package/dist/tui-ink/rewind-picker.d.ts +8 -0
- package/dist/tui-ink/rewind-picker.js +102 -0
- package/dist/tui-ink/session-picker.js +1 -1
- package/dist/tui-ink/stats-panel.d.ts +11 -0
- package/dist/tui-ink/stats-panel.js +65 -0
- package/dist/tui-ink/waiting-indicator.d.ts +12 -0
- package/dist/tui-ink/waiting-indicator.js +129 -0
- package/dist/types.d.ts +0 -9
- package/package.json +1 -1
- package/dist/tools/todo.d.ts +0 -12
- package/dist/tools/todo.js +0 -136
- package/dist/tui-ink/todos.d.ts +0 -7
- package/dist/tui-ink/todos.js +0 -46
package/dist/agent.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Agent - The core decision loop.
|
|
3
3
|
* It maintains message state, calls the LLM, executes tools, and auto-continues.
|
|
4
4
|
*/
|
|
5
|
-
import { compactCurrentTurnToolGroups, compactMessages
|
|
5
|
+
import { compactCurrentTurnToolGroups, compactMessages } from "./context/compact.js";
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
import { compactMessagesWithLLM } from "./context/compact-llm.js";
|
|
8
|
-
import {
|
|
8
|
+
import { getContextBudget } from "./context/budget.js";
|
|
9
9
|
import { buildContextUsageSnapshot } from "./context/usage.js";
|
|
10
10
|
import { isContextOverflowError } from "./context/overflow.js";
|
|
11
11
|
import { computeRetryDelayMs, isProviderStreamInterruption, MAX_STREAM_INTERRUPTION_RETRIES, sleepBeforeRetry, } from "./network/retry.js";
|
|
@@ -21,7 +21,12 @@ import { DEFAULT_AGENT_ROUTING, sanitizeAgentRouting } from "./agent/routing-cat
|
|
|
21
21
|
import { SubagentRouter } from "./agent/subagent/router.js";
|
|
22
22
|
import { normalizeWaitTimeout, SubagentRuntime } from "./agent/subagent/runtime.js";
|
|
23
23
|
import { WorkflowLedger } from "./agent/workflow/runs.js";
|
|
24
|
-
import {
|
|
24
|
+
import { EMPTY_ASSISTANT_FALLBACK, throwIfAborted, isAbortLikeError, isAbortError } from "./agent/abort-errors.js";
|
|
25
|
+
import { appendProviderContentBlock, buildProviderRequestFingerprint } from "./agent/provider-fingerprint.js";
|
|
26
|
+
import { countUserTurns, estimateResidentChars, estimateToolPayloadChars, getCurrentHeapUsed, RESIDENT_HISTORY_HEAP_HARD_LIMIT, RESIDENT_HISTORY_KEEP_RECENT_TURNS } from "./agent/resident-history.js";
|
|
27
|
+
import { cancelledToolResult, createModelInterruptedMessage, findMissingRequiredArgs, isSubagentLifecycleTool, lastProviderMessage, shouldAppendModelInterruptedBoundary } from "./agent/transcript-helpers.js";
|
|
28
|
+
import { createUpdateQueue } from "./agent/update-queue.js";
|
|
29
|
+
export { createUpdateQueue } from "./agent/update-queue.js";
|
|
25
30
|
import { isHiddenToolResult } from "./agent/tool-visibility.js";
|
|
26
31
|
import { createStreamingInternalReminderSanitizer, sanitizeAssistantProviderMetadata, sanitizeInternalReasoningText, sanitizeInternalReminderBlocks, } from "./agent/internal-reminder-sanitizer.js";
|
|
27
32
|
import { debugReasoningStream, summarizeDebugText } from "./reasoning-debug.js";
|
|
@@ -29,8 +34,6 @@ import { buildToolPromptOptions } from "./tools/prompt-metadata.js";
|
|
|
29
34
|
import { stopAutoServersForSession } from "./tools/server-manager.js";
|
|
30
35
|
import { summarizeAgentEventForTrace, summarizeTraceError, summarizeTraceMessage, summarizeTraceToolResult, summarizeTraceValue, traceEvent, } from "./debug-trace.js";
|
|
31
36
|
const MAX_CONSECUTIVE_OVERFLOW_RECOVERIES = 3;
|
|
32
|
-
const RESIDENT_HISTORY_KEEP_RECENT_TURNS = 3;
|
|
33
|
-
const RESIDENT_HISTORY_HEAP_HARD_LIMIT = 768 * 1024 * 1024;
|
|
34
37
|
const MAX_EMPTY_ASSISTANT_RECOVERIES = 1;
|
|
35
38
|
const EMPTY_ASSISTANT_RECOVERY_REMINDER = "The previous model response contained no user-visible assistant content and no tool calls. " +
|
|
36
39
|
"Respond now with a concise, user-visible answer, or call an available tool if more work is required. " +
|
|
@@ -74,7 +77,23 @@ function agentEventFromHookProgress(event) {
|
|
|
74
77
|
};
|
|
75
78
|
}
|
|
76
79
|
export class Agent {
|
|
77
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Backing store for the public `messages` accessor. All whole-array
|
|
82
|
+
* rewrites (clear/compact/rewind/session switch/subagent fork) go through
|
|
83
|
+
* the setter, which notifies `onContextChanged` subscribers — that push
|
|
84
|
+
* channel is what keeps derived UI (e.g. the footer usage readout) in sync
|
|
85
|
+
* without per-path refresh calls that are easy to forget.
|
|
86
|
+
*/
|
|
87
|
+
_messages = [];
|
|
88
|
+
contextChangedListeners = new Set();
|
|
89
|
+
notifyingContextChange = false;
|
|
90
|
+
get messages() {
|
|
91
|
+
return this._messages;
|
|
92
|
+
}
|
|
93
|
+
set messages(value) {
|
|
94
|
+
this._messages = value;
|
|
95
|
+
this.notifyContextChanged();
|
|
96
|
+
}
|
|
78
97
|
provider;
|
|
79
98
|
sessionID;
|
|
80
99
|
/**
|
|
@@ -101,9 +120,6 @@ export class Agent {
|
|
|
101
120
|
_modeVersion = 0;
|
|
102
121
|
onModeUpdate;
|
|
103
122
|
onCompactionApplied;
|
|
104
|
-
_todos;
|
|
105
|
-
_todosVersion = 0;
|
|
106
|
-
onTodosUpdate;
|
|
107
123
|
onMessageAppend;
|
|
108
124
|
onToolResult;
|
|
109
125
|
hookDefinitions;
|
|
@@ -151,10 +167,8 @@ export class Agent {
|
|
|
151
167
|
this.temperature = options.temperature ?? 0.2;
|
|
152
168
|
this.thinkingLevel = options.thinkingLevel ?? "off";
|
|
153
169
|
this._mode = options.mode ?? "default";
|
|
154
|
-
this._todos = options.todos ? [...options.todos] : [];
|
|
155
170
|
this.onMessageAppend = options.onMessageAppend;
|
|
156
171
|
this.onToolResult = options.onToolResult;
|
|
157
|
-
this.onTodosUpdate = options.onTodosUpdate;
|
|
158
172
|
this.onModeUpdate = options.onModeUpdate;
|
|
159
173
|
this.onCompactionApplied = options.onCompactionApplied;
|
|
160
174
|
this.hookDefinitions = options.hooks ?? [];
|
|
@@ -216,7 +230,7 @@ export class Agent {
|
|
|
216
230
|
persistDir,
|
|
217
231
|
});
|
|
218
232
|
if (options.systemPrompt) {
|
|
219
|
-
this.
|
|
233
|
+
this._messages.push({ role: "system", content: options.systemPrompt });
|
|
220
234
|
}
|
|
221
235
|
for (const tool of options.tools) {
|
|
222
236
|
this.tools.set(tool.name, tool);
|
|
@@ -362,6 +376,46 @@ export class Agent {
|
|
|
362
376
|
renderModelRoutingPromptFor(parent) {
|
|
363
377
|
return this.router.promptSectionFor(parent);
|
|
364
378
|
}
|
|
379
|
+
/**
|
|
380
|
+
* Push subscription for derived-context consumers (TUI footer, hosts).
|
|
381
|
+
* Fires whenever resident context mutates in a way that changes a
|
|
382
|
+
* `getContextUsageSnapshot()` reading: whole-array message rewrites,
|
|
383
|
+
* appended messages, system-prompt replacement, or model/provider switches
|
|
384
|
+
* (window size / estimation basis). Returns an unsubscribe function.
|
|
385
|
+
*/
|
|
386
|
+
onContextChanged(listener) {
|
|
387
|
+
this.contextChangedListeners.add(listener);
|
|
388
|
+
return () => {
|
|
389
|
+
this.contextChangedListeners.delete(listener);
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
notifyContextChanged() {
|
|
393
|
+
if (this.notifyingContextChange)
|
|
394
|
+
return;
|
|
395
|
+
this.notifyingContextChange = true;
|
|
396
|
+
try {
|
|
397
|
+
for (const listener of [...this.contextChangedListeners]) {
|
|
398
|
+
try {
|
|
399
|
+
listener();
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
// A broken subscriber must never corrupt agent state mid-mutation,
|
|
403
|
+
// but leave a trace — otherwise a throwing consumer (e.g. the footer
|
|
404
|
+
// readout hitting a pathological message) fails silently forever.
|
|
405
|
+
traceEvent("context_listener_error", {
|
|
406
|
+
error: error instanceof Error ? error.message : String(error),
|
|
407
|
+
}, {
|
|
408
|
+
sessionFile: this.sessionID,
|
|
409
|
+
provider: this._providerId || "none",
|
|
410
|
+
model: this.apiModel || "none",
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
finally {
|
|
416
|
+
this.notifyingContextChange = false;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
365
419
|
getContextUsageSnapshot() {
|
|
366
420
|
return buildContextUsageSnapshot({
|
|
367
421
|
providerId: this.providerId,
|
|
@@ -434,13 +488,21 @@ export class Agent {
|
|
|
434
488
|
return this._model;
|
|
435
489
|
}
|
|
436
490
|
set model(value) {
|
|
491
|
+
if (this._model === value)
|
|
492
|
+
return;
|
|
437
493
|
this._model = value;
|
|
494
|
+
// Context window (and thus usage readings) is model-derived.
|
|
495
|
+
this.notifyContextChanged();
|
|
438
496
|
}
|
|
439
497
|
get providerId() {
|
|
440
498
|
return this._providerId;
|
|
441
499
|
}
|
|
442
500
|
set providerId(value) {
|
|
501
|
+
if (this._providerId === value)
|
|
502
|
+
return;
|
|
443
503
|
this._providerId = value;
|
|
504
|
+
// Token estimation basis is provider-specific.
|
|
505
|
+
this.notifyContextChanged();
|
|
444
506
|
}
|
|
445
507
|
get apiModel() {
|
|
446
508
|
if (this._model.includes(":")) {
|
|
@@ -489,25 +551,17 @@ export class Agent {
|
|
|
489
551
|
get modeVersion() {
|
|
490
552
|
return this._modeVersion;
|
|
491
553
|
}
|
|
492
|
-
getTodos() {
|
|
493
|
-
return this._todos.map((todo) => ({ ...todo }));
|
|
494
|
-
}
|
|
495
|
-
setTodos(next) {
|
|
496
|
-
this._todos = next.map((todo) => ({ ...todo }));
|
|
497
|
-
this._todosVersion += 1;
|
|
498
|
-
this.onTodosUpdate?.(this.getTodos());
|
|
499
|
-
}
|
|
500
|
-
/** Internal: snapshot counter that bumps on every setTodos. Used by run loop to detect mutations. */
|
|
501
|
-
get todosVersion() {
|
|
502
|
-
return this._todosVersion;
|
|
503
|
-
}
|
|
504
554
|
setSystemPrompt(prompt) {
|
|
505
555
|
const systemMessage = { role: "system", content: prompt };
|
|
506
556
|
if (this.messages[0]?.role === "system") {
|
|
557
|
+
if (this.messages[0].content === prompt)
|
|
558
|
+
return;
|
|
507
559
|
this.messages[0] = systemMessage;
|
|
560
|
+
this.notifyContextChanged();
|
|
508
561
|
return;
|
|
509
562
|
}
|
|
510
563
|
this.messages.unshift(systemMessage);
|
|
564
|
+
this.notifyContextChanged();
|
|
511
565
|
}
|
|
512
566
|
async *run(userInput, cwd, options = {}) {
|
|
513
567
|
const abortSignal = options.abortSignal;
|
|
@@ -615,10 +669,6 @@ export class Agent {
|
|
|
615
669
|
this.injectSystemReminder(reminder);
|
|
616
670
|
}
|
|
617
671
|
};
|
|
618
|
-
if (this._todos.length > 0 && this._todos.every((t) => t.status === "completed")) {
|
|
619
|
-
this.setTodos([]);
|
|
620
|
-
yield emit({ type: "todos_updated", todos: [] });
|
|
621
|
-
}
|
|
622
672
|
if (!options.resumeWithoutInput) {
|
|
623
673
|
const promptHook = await this.runExternalHook({
|
|
624
674
|
eventName: "UserPromptSubmit",
|
|
@@ -1185,7 +1235,6 @@ export class Agent {
|
|
|
1185
1235
|
argsCorrupt: tc.argsCorrupt,
|
|
1186
1236
|
}, traceContext);
|
|
1187
1237
|
yield emit({ type: "tool_start", id: tc.id, name: tc.name, args: tc.parsedArgs });
|
|
1188
|
-
const todosVersionBefore = this._todosVersion;
|
|
1189
1238
|
const modeVersionBefore = this._modeVersion;
|
|
1190
1239
|
const updateQueue = createUpdateQueue();
|
|
1191
1240
|
let result;
|
|
@@ -1311,9 +1360,6 @@ export class Agent {
|
|
|
1311
1360
|
yield emit({ type: "tool_end", id: tc.id, name: tc.name, result });
|
|
1312
1361
|
for (const update of this.subagents.drainToolUpdates())
|
|
1313
1362
|
yield emit(update);
|
|
1314
|
-
if (this._todosVersion !== todosVersionBefore) {
|
|
1315
|
-
yield emit({ type: "todos_updated", todos: this.getTodos() });
|
|
1316
|
-
}
|
|
1317
1363
|
if (this._modeVersion !== modeVersionBefore) {
|
|
1318
1364
|
yield emit({ type: "mode_changed", mode: this._mode });
|
|
1319
1365
|
}
|
|
@@ -1385,15 +1431,10 @@ export class Agent {
|
|
|
1385
1431
|
catch (error) {
|
|
1386
1432
|
if (isAbortError(error, abortSignal)) {
|
|
1387
1433
|
const appendedBoundary = this.appendInterruptedAssistantBoundary(currentAssistantMsg, currentAssistantAppended);
|
|
1388
|
-
const clearedTodos = this.clearTodosAfterInterruptedRun();
|
|
1389
1434
|
traceEvent("agent_run_interrupted", {
|
|
1390
1435
|
appendedBoundary,
|
|
1391
|
-
clearedTodos,
|
|
1392
1436
|
messageCount: this.messages.length,
|
|
1393
1437
|
}, traceContext);
|
|
1394
|
-
if (clearedTodos) {
|
|
1395
|
-
yield emit({ type: "todos_updated", todos: this.getTodos() });
|
|
1396
|
-
}
|
|
1397
1438
|
}
|
|
1398
1439
|
else {
|
|
1399
1440
|
const stopFailureHook = await this.runExternalHook({
|
|
@@ -1836,6 +1877,7 @@ export class Agent {
|
|
|
1836
1877
|
model: this.apiModel || "none",
|
|
1837
1878
|
});
|
|
1838
1879
|
this.onMessageAppend?.(message);
|
|
1880
|
+
this.notifyContextChanged();
|
|
1839
1881
|
}
|
|
1840
1882
|
appendInterruptedAssistantBoundary(currentAssistant, currentAssistantAppended) {
|
|
1841
1883
|
const last = lastProviderMessage(this.messages);
|
|
@@ -1858,12 +1900,6 @@ export class Agent {
|
|
|
1858
1900
|
});
|
|
1859
1901
|
return true;
|
|
1860
1902
|
}
|
|
1861
|
-
clearTodosAfterInterruptedRun() {
|
|
1862
|
-
if (this._todos.length === 0)
|
|
1863
|
-
return false;
|
|
1864
|
-
this.setTodos([]);
|
|
1865
|
-
return true;
|
|
1866
|
-
}
|
|
1867
1903
|
async executeTool(toolCall, cwd, abortSignal, emitUpdate) {
|
|
1868
1904
|
throwIfAborted(abortSignal);
|
|
1869
1905
|
if (toolCall.name === "exit_plan_mode" && this._mode !== "plan") {
|
|
@@ -1882,7 +1918,7 @@ export class Agent {
|
|
|
1882
1918
|
if (this._mode === "plan" && !tool.readOnly) {
|
|
1883
1919
|
return {
|
|
1884
1920
|
content: `Error: Tool "${toolCall.name}" is not allowed in plan mode. ` +
|
|
1885
|
-
`In plan mode you may only use read-only tools (read, glob, grep, web_search, web_fetch, spawn_agent, wait_agent, send_input, skill_search, skill,
|
|
1921
|
+
`In plan mode you may only use read-only tools (read, glob, grep, web_search, web_fetch, spawn_agent, wait_agent, send_input, skill_search, skill, tool_search, question, exit_plan_mode). ` +
|
|
1886
1922
|
`To modify files or run commands, present your proposal and call exit_plan_mode so the user can review and approve it.`,
|
|
1887
1923
|
isError: true,
|
|
1888
1924
|
};
|
|
@@ -1946,293 +1982,3 @@ export class Agent {
|
|
|
1946
1982
|
}
|
|
1947
1983
|
}
|
|
1948
1984
|
}
|
|
1949
|
-
function findMissingRequiredArgs(schema, args) {
|
|
1950
|
-
const required = schema?.required;
|
|
1951
|
-
if (!required || required.length === 0)
|
|
1952
|
-
return [];
|
|
1953
|
-
const missing = [];
|
|
1954
|
-
for (const name of required) {
|
|
1955
|
-
const value = args ? args[name] : undefined;
|
|
1956
|
-
// Empty strings/arrays are intentionally allowed — writing an empty file
|
|
1957
|
-
// or passing an empty list can be legitimate. Only undefined/null counts
|
|
1958
|
-
// as "missing", because the observed failure mode is `finalArgs: "{}"`
|
|
1959
|
-
// where the field is entirely absent.
|
|
1960
|
-
if (value === undefined || value === null) {
|
|
1961
|
-
missing.push(name);
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
return missing;
|
|
1965
|
-
}
|
|
1966
|
-
function estimateResidentChars(messages) {
|
|
1967
|
-
let total = 0;
|
|
1968
|
-
for (const message of messages) {
|
|
1969
|
-
switch (message.role) {
|
|
1970
|
-
case "system":
|
|
1971
|
-
case "meta":
|
|
1972
|
-
total += message.content.length;
|
|
1973
|
-
break;
|
|
1974
|
-
case "tool":
|
|
1975
|
-
total += message.content.length + message.toolCallId.length;
|
|
1976
|
-
break;
|
|
1977
|
-
case "assistant":
|
|
1978
|
-
total += message.content.length + (message.reasoning?.length ?? 0);
|
|
1979
|
-
total += message.toolCalls?.reduce((sum, toolCall) => sum + toolCall.id.length + toolCall.name.length + toolCall.arguments.length, 0) ?? 0;
|
|
1980
|
-
break;
|
|
1981
|
-
case "user":
|
|
1982
|
-
if (typeof message.content === "string") {
|
|
1983
|
-
total += message.content.length;
|
|
1984
|
-
}
|
|
1985
|
-
else {
|
|
1986
|
-
total += message.content.reduce((sum, part) => {
|
|
1987
|
-
if (part.type === "text") {
|
|
1988
|
-
return sum + part.text.length;
|
|
1989
|
-
}
|
|
1990
|
-
return sum + part.image_url.url.length;
|
|
1991
|
-
}, 0);
|
|
1992
|
-
}
|
|
1993
|
-
break;
|
|
1994
|
-
}
|
|
1995
|
-
}
|
|
1996
|
-
return total;
|
|
1997
|
-
}
|
|
1998
|
-
function appendProviderContentBlock(message, provider, block) {
|
|
1999
|
-
const current = message.providerMetadata?.[provider]?.contentBlocks ?? [];
|
|
2000
|
-
message.providerMetadata = {
|
|
2001
|
-
...message.providerMetadata,
|
|
2002
|
-
[provider]: {
|
|
2003
|
-
...message.providerMetadata?.[provider],
|
|
2004
|
-
contentBlocks: [...current, cloneProviderRawContentBlock(block)],
|
|
2005
|
-
},
|
|
2006
|
-
};
|
|
2007
|
-
}
|
|
2008
|
-
function buildProviderRequestFingerprint(messages, tools, providerId, toolChoice) {
|
|
2009
|
-
const roleCounts = {};
|
|
2010
|
-
let contentChars = 0;
|
|
2011
|
-
let reasoningChars = 0;
|
|
2012
|
-
let toolResultChars = 0;
|
|
2013
|
-
let maxToolResultChars = 0;
|
|
2014
|
-
let assistantToolCalls = 0;
|
|
2015
|
-
let rawAnthropicBlocks = 0;
|
|
2016
|
-
let rawAnthropicThinkingBlocks = 0;
|
|
2017
|
-
let rawAnthropicSignatureChars = 0;
|
|
2018
|
-
for (const message of messages) {
|
|
2019
|
-
roleCounts[message.role] = (roleCounts[message.role] ?? 0) + 1;
|
|
2020
|
-
if (message.role === "assistant") {
|
|
2021
|
-
contentChars += message.content.length;
|
|
2022
|
-
reasoningChars += message.reasoning?.length ?? 0;
|
|
2023
|
-
assistantToolCalls += message.toolCalls?.length ?? 0;
|
|
2024
|
-
const blocks = message.providerMetadata?.anthropic?.contentBlocks ?? [];
|
|
2025
|
-
rawAnthropicBlocks += blocks.length;
|
|
2026
|
-
for (const block of blocks) {
|
|
2027
|
-
if (block.type === "thinking" || block.type === "redacted_thinking") {
|
|
2028
|
-
rawAnthropicThinkingBlocks += 1;
|
|
2029
|
-
}
|
|
2030
|
-
if (typeof block.signature === "string") {
|
|
2031
|
-
rawAnthropicSignatureChars += block.signature.length;
|
|
2032
|
-
}
|
|
2033
|
-
}
|
|
2034
|
-
}
|
|
2035
|
-
else if (message.role === "tool") {
|
|
2036
|
-
toolResultChars += message.content.length;
|
|
2037
|
-
maxToolResultChars = Math.max(maxToolResultChars, message.content.length);
|
|
2038
|
-
}
|
|
2039
|
-
else if (message.role === "user") {
|
|
2040
|
-
contentChars += typeof message.content === "string"
|
|
2041
|
-
? message.content.length
|
|
2042
|
-
: message.content.reduce((sum, part) => sum + (part.type === "text" ? part.text.length : part.image_url.url.length), 0);
|
|
2043
|
-
}
|
|
2044
|
-
else {
|
|
2045
|
-
contentChars += message.content.length;
|
|
2046
|
-
}
|
|
2047
|
-
}
|
|
2048
|
-
const systemMessages = messages.filter((message) => message.role === "system");
|
|
2049
|
-
const bodyMessages = messages.filter((message) => message.role !== "system");
|
|
2050
|
-
const systemJsonBytes = Buffer.byteLength(JSON.stringify(systemMessages), "utf8");
|
|
2051
|
-
const bodyJsonBytes = Buffer.byteLength(JSON.stringify(bodyMessages), "utf8");
|
|
2052
|
-
const toolSchemaJsonBytes = Buffer.byteLength(JSON.stringify(tools), "utf8");
|
|
2053
|
-
return {
|
|
2054
|
-
roleCounts,
|
|
2055
|
-
estimatedTokens: estimateContextTokens(messages, providerId),
|
|
2056
|
-
projectedJsonBytes: Buffer.byteLength(JSON.stringify(messages), "utf8"),
|
|
2057
|
-
systemJsonBytes,
|
|
2058
|
-
bodyJsonBytes,
|
|
2059
|
-
toolSchemaJsonBytes,
|
|
2060
|
-
staticPrefixJsonBytes: Buffer.byteLength(JSON.stringify({
|
|
2061
|
-
system: systemMessages,
|
|
2062
|
-
tools,
|
|
2063
|
-
tool_choice: toolChoice,
|
|
2064
|
-
}), "utf8"),
|
|
2065
|
-
toolChoice,
|
|
2066
|
-
contentChars,
|
|
2067
|
-
reasoningChars,
|
|
2068
|
-
toolResultChars,
|
|
2069
|
-
maxToolResultChars,
|
|
2070
|
-
assistantToolCalls,
|
|
2071
|
-
rawAnthropicBlocks,
|
|
2072
|
-
rawAnthropicThinkingBlocks,
|
|
2073
|
-
rawAnthropicSignatureChars,
|
|
2074
|
-
};
|
|
2075
|
-
}
|
|
2076
|
-
function cloneProviderRawContentBlock(block) {
|
|
2077
|
-
return JSON.parse(JSON.stringify(block));
|
|
2078
|
-
}
|
|
2079
|
-
function throwIfAborted(signal) {
|
|
2080
|
-
if (!signal?.aborted)
|
|
2081
|
-
return;
|
|
2082
|
-
const reason = signal.reason;
|
|
2083
|
-
if (reason instanceof Error)
|
|
2084
|
-
throw reason;
|
|
2085
|
-
throw new AgentAbortError(typeof reason === "string" ? reason : undefined);
|
|
2086
|
-
}
|
|
2087
|
-
function isAbortLikeError(error, signal) {
|
|
2088
|
-
if (signal?.aborted)
|
|
2089
|
-
return true;
|
|
2090
|
-
if (error instanceof AgentAbortError)
|
|
2091
|
-
return true;
|
|
2092
|
-
if (error instanceof DOMException && error.name === "AbortError")
|
|
2093
|
-
return true;
|
|
2094
|
-
if (typeof error === "object" && error !== null && error.name === "AbortError")
|
|
2095
|
-
return true;
|
|
2096
|
-
return false;
|
|
2097
|
-
}
|
|
2098
|
-
function isAbortError(error, signal) {
|
|
2099
|
-
return isAbortLikeError(error, signal);
|
|
2100
|
-
}
|
|
2101
|
-
function shouldAppendModelInterruptedBoundary(messages) {
|
|
2102
|
-
return messages.at(-1)?.role === "tool";
|
|
2103
|
-
}
|
|
2104
|
-
function createModelInterruptedMessage(error, metadata) {
|
|
2105
|
-
return {
|
|
2106
|
-
role: "assistant",
|
|
2107
|
-
content: `[model request interrupted before a final answer was produced: ${summarizeInterruptError(error)}]`,
|
|
2108
|
-
model: metadata.model,
|
|
2109
|
-
providerId: metadata.providerId,
|
|
2110
|
-
modelId: metadata.modelId,
|
|
2111
|
-
};
|
|
2112
|
-
}
|
|
2113
|
-
function summarizeInterruptError(error) {
|
|
2114
|
-
const message = error instanceof Error
|
|
2115
|
-
? error.message
|
|
2116
|
-
: typeof error === "string"
|
|
2117
|
-
? error
|
|
2118
|
-
: String(error);
|
|
2119
|
-
return message.replace(/\s+/g, " ").trim().slice(0, 240) || "unknown error";
|
|
2120
|
-
}
|
|
2121
|
-
function lastProviderMessage(messages) {
|
|
2122
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2123
|
-
const message = messages[i];
|
|
2124
|
-
if (message.role === "system" || message.role === "meta")
|
|
2125
|
-
continue;
|
|
2126
|
-
return message;
|
|
2127
|
-
}
|
|
2128
|
-
return undefined;
|
|
2129
|
-
}
|
|
2130
|
-
function cancelledToolResult(toolName) {
|
|
2131
|
-
return {
|
|
2132
|
-
content: `Tool "${toolName}" was cancelled.`,
|
|
2133
|
-
isError: true,
|
|
2134
|
-
status: "cancelled",
|
|
2135
|
-
metadata: { reason: "cancelled" },
|
|
2136
|
-
};
|
|
2137
|
-
}
|
|
2138
|
-
/** Exported for tests: the wake latch below is easiest to pin directly. */
|
|
2139
|
-
export function createUpdateQueue() {
|
|
2140
|
-
const items = [];
|
|
2141
|
-
let waiter;
|
|
2142
|
-
let abortCleanup;
|
|
2143
|
-
// Latch for wakes that arrive with no parked waiter — a wake() fired while
|
|
2144
|
-
// the consuming generator is suspended at a yield would otherwise be lost.
|
|
2145
|
-
// Callers today each pair their wake with a synchronously-checked flag
|
|
2146
|
-
// (items / settled / pendingSubagentUpdates), so no wake is currently
|
|
2147
|
-
// dropped; the latch keeps that correctness inside the queue instead of
|
|
2148
|
-
// depending on every future caller repeating the pairing.
|
|
2149
|
-
let signaled = false;
|
|
2150
|
-
return {
|
|
2151
|
-
push(item) {
|
|
2152
|
-
items.push(item);
|
|
2153
|
-
this.wake();
|
|
2154
|
-
},
|
|
2155
|
-
drain() {
|
|
2156
|
-
return items.splice(0, items.length);
|
|
2157
|
-
},
|
|
2158
|
-
hasItems() {
|
|
2159
|
-
return items.length > 0;
|
|
2160
|
-
},
|
|
2161
|
-
wait(signal) {
|
|
2162
|
-
if (items.length > 0)
|
|
2163
|
-
return Promise.resolve("woken");
|
|
2164
|
-
if (signaled) {
|
|
2165
|
-
signaled = false;
|
|
2166
|
-
return Promise.resolve("woken");
|
|
2167
|
-
}
|
|
2168
|
-
if (signal?.aborted)
|
|
2169
|
-
return Promise.resolve("aborted");
|
|
2170
|
-
return new Promise((resolve) => {
|
|
2171
|
-
abortCleanup?.();
|
|
2172
|
-
abortCleanup = undefined;
|
|
2173
|
-
const finish = (status) => {
|
|
2174
|
-
if (waiter !== resolve)
|
|
2175
|
-
return;
|
|
2176
|
-
waiter = undefined;
|
|
2177
|
-
abortCleanup?.();
|
|
2178
|
-
abortCleanup = undefined;
|
|
2179
|
-
resolve(status);
|
|
2180
|
-
};
|
|
2181
|
-
if (signal) {
|
|
2182
|
-
const onAbort = () => finish("aborted");
|
|
2183
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
2184
|
-
abortCleanup = () => signal.removeEventListener("abort", onAbort);
|
|
2185
|
-
}
|
|
2186
|
-
waiter = resolve;
|
|
2187
|
-
});
|
|
2188
|
-
},
|
|
2189
|
-
wake() {
|
|
2190
|
-
const resolve = waiter;
|
|
2191
|
-
if (!resolve) {
|
|
2192
|
-
signaled = true;
|
|
2193
|
-
return;
|
|
2194
|
-
}
|
|
2195
|
-
waiter = undefined;
|
|
2196
|
-
abortCleanup?.();
|
|
2197
|
-
abortCleanup = undefined;
|
|
2198
|
-
resolve("woken");
|
|
2199
|
-
},
|
|
2200
|
-
};
|
|
2201
|
-
}
|
|
2202
|
-
function estimateToolPayloadChars(messages) {
|
|
2203
|
-
return messages.reduce((sum, message) => {
|
|
2204
|
-
if (message.role !== "tool") {
|
|
2205
|
-
return sum;
|
|
2206
|
-
}
|
|
2207
|
-
return sum + message.content.length;
|
|
2208
|
-
}, 0);
|
|
2209
|
-
}
|
|
2210
|
-
function countUserTurns(messages) {
|
|
2211
|
-
// Real user turns only: projected reminders and summary envelopes are
|
|
2212
|
-
// user-ROLE but not user TURNS — counting them used to shrink the keep
|
|
2213
|
-
// window and fire compaction almost every turn.
|
|
2214
|
-
return messages.reduce((count, message) => count + (isRealUserMessage(message) ? 1 : 0), 0);
|
|
2215
|
-
}
|
|
2216
|
-
function getCurrentHeapUsed() {
|
|
2217
|
-
try {
|
|
2218
|
-
return process.memoryUsage().heapUsed;
|
|
2219
|
-
}
|
|
2220
|
-
catch {
|
|
2221
|
-
return 0;
|
|
2222
|
-
}
|
|
2223
|
-
}
|
|
2224
|
-
function isSubagentLifecycleTool(name) {
|
|
2225
|
-
return name === "subagent"
|
|
2226
|
-
|| name === "spawn_agent"
|
|
2227
|
-
|| name === "wait_agent"
|
|
2228
|
-
|| name === "send_input"
|
|
2229
|
-
|| name === "close_agent"
|
|
2230
|
-
|| name === "list_agents"
|
|
2231
|
-
|| name === "run_workflow"
|
|
2232
|
-
|| name === "wait_workflow"
|
|
2233
|
-
// Legacy names: still present in transcripts recorded before the tools
|
|
2234
|
-
// were removed (2026-07-06); forked children must not inherit their
|
|
2235
|
-
// dangling tool_calls either.
|
|
2236
|
-
|| name === "agent_team"
|
|
2237
|
-
|| name === "agent_batch";
|
|
2238
|
-
}
|
package/dist/debug-trace.js
CHANGED
|
@@ -530,7 +530,7 @@ export class GrokRuntimeManager {
|
|
|
530
530
|
try {
|
|
531
531
|
if (!this.lock)
|
|
532
532
|
temporaryLock = await this.acquireProfileLock();
|
|
533
|
-
const {
|
|
533
|
+
const { env, binary } = await this.prepareAndVerify();
|
|
534
534
|
const result = await runGrokCommand(this.spawn, binary.path, ["--no-auto-update", "--cwd", this.workspace, "models"], { cwd: this.workspace, env, maxStdoutBytes: 64 * 1024 });
|
|
535
535
|
if (result.code !== 0) {
|
|
536
536
|
throw new GrokRuntimeError("protocol_error", "Grok could not list subscription models.", result.stderr);
|
|
@@ -70,12 +70,7 @@ export class RunDriver {
|
|
|
70
70
|
({ action: "reject", reason: "Plan approval over Feishu not implemented; use /mode default." }),
|
|
71
71
|
setMode: (mode) => agentForPlan?.setMode(mode),
|
|
72
72
|
};
|
|
73
|
-
const todoStore = {
|
|
74
|
-
getTodos: () => agentRef?.getTodos() ?? [],
|
|
75
|
-
setTodos: (todos) => agentRef?.setTodos(todos),
|
|
76
|
-
};
|
|
77
73
|
const tools = createAllTools(session.cwd, this.opts.deps.skillRegistry, {
|
|
78
|
-
todoStore,
|
|
79
74
|
planController,
|
|
80
75
|
approvalController,
|
|
81
76
|
lspService,
|
|
@@ -125,7 +120,6 @@ export class RunDriver {
|
|
|
125
120
|
temperature: 0.2,
|
|
126
121
|
thinkingLevel,
|
|
127
122
|
mode: initialMode,
|
|
128
|
-
todos: session.manager.getTodos(),
|
|
129
123
|
onMessageAppend: (message) => {
|
|
130
124
|
if (message.role === "system" || message.role === "meta")
|
|
131
125
|
return;
|
|
@@ -145,7 +139,6 @@ export class RunDriver {
|
|
|
145
139
|
if (match?.[1])
|
|
146
140
|
session.manager.appendMarker("skill_activated", match[1].trim());
|
|
147
141
|
},
|
|
148
|
-
onTodosUpdate: (todos) => session.manager.appendTodosSnapshot(todos),
|
|
149
142
|
onModeUpdate: (mode) => {
|
|
150
143
|
session.manager.appendMarker("mode_switch", mode);
|
|
151
144
|
this.opts.binder.setMode(req.scopeKey, mode);
|
|
@@ -301,7 +294,7 @@ export class RunDriver {
|
|
|
301
294
|
this.opts.approvalUI.cancelForChat(req.chatId, "Run ended");
|
|
302
295
|
}
|
|
303
296
|
}
|
|
304
|
-
async resolveProvider(
|
|
297
|
+
async resolveProvider(_session, promptCacheKey) {
|
|
305
298
|
const registry = this.opts.deps.providerRegistry;
|
|
306
299
|
const userConfig = this.opts.deps.userConfig;
|
|
307
300
|
// Read session metadata for an explicit model preference, fall back to
|
|
@@ -97,9 +97,6 @@ export function reduceRunState(state, event) {
|
|
|
97
97
|
case "mode_changed":
|
|
98
98
|
state.mode = event.mode;
|
|
99
99
|
return state;
|
|
100
|
-
case "todos_updated":
|
|
101
|
-
// Todos render is deferred (would need a todos block kind). v1 ignores.
|
|
102
|
-
return state;
|
|
103
100
|
case "context_recovered":
|
|
104
101
|
// Internal recovery — silent.
|
|
105
102
|
return state;
|
package/dist/lsp/service.js
CHANGED
|
@@ -436,7 +436,7 @@ const TypeScriptServer = {
|
|
|
436
436
|
extensions: TS_EXTENSIONS,
|
|
437
437
|
root: nearestRoot(NODE_ROOT_MARKERS, DENO_ROOT_MARKERS),
|
|
438
438
|
languageId: languageIdFor,
|
|
439
|
-
async spawn(root,
|
|
439
|
+
async spawn(root, _ctx) {
|
|
440
440
|
const requireFromRoot = createRequire(join(root, "package.json"));
|
|
441
441
|
const requireFromSelf = createRequire(import.meta.url);
|
|
442
442
|
const tsserverPath = resolveModule(requireFromRoot, "typescript/lib/tsserver.js")
|
|
@@ -495,7 +495,7 @@ const ESLintServer = {
|
|
|
495
495
|
extensions: [...TS_EXTENSIONS, ".vue"],
|
|
496
496
|
root: eslintRoot,
|
|
497
497
|
languageId: languageIdFor,
|
|
498
|
-
async spawn(root,
|
|
498
|
+
async spawn(root, _ctx) {
|
|
499
499
|
const requireFromRoot = createRequire(join(root, "package.json"));
|
|
500
500
|
const requireFromSelf = createRequire(import.meta.url);
|
|
501
501
|
const eslint = resolveModule(requireFromRoot, "eslint");
|