@bike4mind/cli 2.0.0 → 3.0.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/README.md +1 -1
- package/dist/{AgentHistoryStore-DIMzvf-y.mjs → AgentHistoryStore-DVxv1kSO.mjs} +7940 -7014
- package/dist/{ConfigStore-DIs-K9oD.mjs → ConfigStore-51UaP9pJ.mjs} +12333 -7008
- package/dist/{ImageStore-kVo-oHoS.mjs → ImageStore-DuI3tgwQ.mjs} +8 -6
- package/dist/SandboxRuntimeAdapter-Bt1wPpld.mjs +2 -0
- package/dist/{WorkItemsClient-Cow6nXx7.mjs → WorkItemsClient-BMY0Qq1s.mjs} +78 -9
- package/dist/{buildAgent-DOvEp92a.mjs → buildAgent-CrSRKYU_.mjs} +106 -43
- package/dist/commands/acpCommand.mjs +26 -35
- package/dist/commands/apiCommand.mjs +1 -1
- package/dist/commands/doctorCommand.mjs +3 -3
- package/dist/commands/envCommand.mjs +1 -1
- package/dist/commands/headlessCommand.mjs +15 -24
- package/dist/commands/mcpCommand.mjs +3 -3
- package/dist/commands/pluginCommand.mjs +2 -2
- package/dist/commands/updateCommand.mjs +5 -15
- package/dist/dist-BsG2Hlp-.mjs +739 -0
- package/dist/index.mjs +670 -340
- package/dist/{package-Dl_F2Fah.mjs → package-DEsNSP54.mjs} +1 -1
- package/dist/serve-BaHzVbcm.mjs +1511 -0
- package/dist/store-BkMur6uk.mjs +2 -0
- package/dist/{store-CvjTpQPs.mjs → store-BxmPEI3v.mjs} +10 -0
- package/dist/types-I45lMh7Q.mjs +2 -0
- package/dist/{updateChecker-CQW8bxo6.mjs → updateChecker-CCLVVdH0.mjs} +36 -13
- package/package.json +27 -26
- package/dist/ApiClient-BTI0-m0g.mjs +0 -316
- package/dist/SandboxRuntimeAdapter-BmHELLuM.mjs +0 -3
- package/dist/serve-Y2zMbfGs.mjs +0 -938
- package/dist/store-BG3e54c8.mjs +0 -3
- package/dist/types-CdIKgWWe.mjs +0 -3
- package/dist/{bashExecute-CrdPpBqk-DCATrE-D.mjs → bashExecute-CrdPpBqk-CgK1gZa2.mjs} +1 -1
- package/dist/{grepSearch-BgoOOwGe-DtlV8Gn-.mjs → grepSearch-BgoOOwGe-B388GxRD.mjs} +2 -2
|
@@ -139,12 +139,14 @@ var ImageStore = class ImageStore {
|
|
|
139
139
|
const maxDimension = 2048;
|
|
140
140
|
let width = metadata.width;
|
|
141
141
|
let height = metadata.height;
|
|
142
|
-
if (width > maxDimension || height > maxDimension)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
142
|
+
if (width > maxDimension || height > maxDimension) {
|
|
143
|
+
if (width > height) {
|
|
144
|
+
height = Math.round(height * maxDimension / width);
|
|
145
|
+
width = maxDimension;
|
|
146
|
+
} else {
|
|
147
|
+
width = Math.round(width * maxDimension / height);
|
|
148
|
+
height = maxDimension;
|
|
149
|
+
}
|
|
148
150
|
}
|
|
149
151
|
image = image.resize(width, height);
|
|
150
152
|
let quality = 85;
|
|
@@ -1,25 +1,73 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { kt as tokenEstimateMultiplier } from "./ConfigStore-51UaP9pJ.mjs";
|
|
2
3
|
import { v4 } from "uuid";
|
|
3
4
|
import { get_encoding } from "tiktoken";
|
|
5
|
+
//#region ../../b4m-core/utils/dist/calibratedTokenizer.mjs
|
|
6
|
+
/**
|
|
7
|
+
* Scale a raw cl100k_base count by `multiplier`, rounded up to the nearest whole token. Paired with
|
|
8
|
+
* tokenEstimateMultiplier (@bike4mind/common) to bring a raw count up to a Claude model's tokenizer.
|
|
9
|
+
* The sync counterpart of withTokenEstimateMultiplier, for callers whose counter is synchronous.
|
|
10
|
+
*/
|
|
11
|
+
function scaleTokenEstimate(count, multiplier) {
|
|
12
|
+
if (multiplier === 1) return count;
|
|
13
|
+
const hundredths = Math.round(multiplier * 100);
|
|
14
|
+
return Math.ceil(count * hundredths / 100);
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
4
17
|
//#region src/utils/tokenCounter.ts
|
|
5
18
|
const DEFAULT_CONTEXT_WINDOW = 2e5;
|
|
6
19
|
const IMAGE_BLOCK_TOKEN_ESTIMATE = 1600;
|
|
20
|
+
const TOKEN_CACHE_MIN_CHARS = 256;
|
|
21
|
+
const DEFAULT_TOKEN_CACHE_MAX_CHARS = 4e6;
|
|
7
22
|
/**
|
|
8
23
|
* Token counting utility for context window management.
|
|
9
|
-
* Uses tiktoken (cl100k_base encoding)
|
|
24
|
+
* Uses tiktoken (cl100k_base encoding), the encoding GPT-4 reports. Claude's tokenizers spend more
|
|
25
|
+
* tokens on the same text, so call `forModel(modelId)` before comparing a count against a model's
|
|
26
|
+
* context window (see tokenEstimateMultiplier in @bike4mind/common).
|
|
10
27
|
*/
|
|
11
28
|
var TokenCounter = class {
|
|
12
|
-
constructor() {
|
|
29
|
+
constructor(cacheMaxChars = DEFAULT_TOKEN_CACHE_MAX_CHARS) {
|
|
30
|
+
this.cacheMaxChars = cacheMaxChars;
|
|
13
31
|
this.encoder = null;
|
|
32
|
+
this.countCache = /* @__PURE__ */ new Map();
|
|
33
|
+
this.cachedChars = 0;
|
|
14
34
|
}
|
|
15
35
|
getEncoder() {
|
|
16
36
|
if (!this.encoder) this.encoder = get_encoding("cl100k_base");
|
|
17
37
|
return this.encoder;
|
|
18
38
|
}
|
|
19
39
|
/**
|
|
40
|
+
* A view of this counter whose counts are scaled to `modelId`'s own tokenizer, or `this` when the
|
|
41
|
+
* model needs no scaling (non-Claude, or a Claude version tokenEstimateMultiplier leaves at 1). Use
|
|
42
|
+
* it wherever a count is compared against a model's context window, so a Claude session compacts
|
|
43
|
+
* and reports against real token usage rather than a cl100k_base undercount.
|
|
44
|
+
*/
|
|
45
|
+
forModel(modelId) {
|
|
46
|
+
const multiplier = tokenEstimateMultiplier(modelId);
|
|
47
|
+
return multiplier === 1 ? this : new CalibratedTokenCounter(this, multiplier);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
20
50
|
* Count tokens in a text string
|
|
21
51
|
*/
|
|
22
52
|
countTokens(text) {
|
|
53
|
+
if (text.length < TOKEN_CACHE_MIN_CHARS || text.length > this.cacheMaxChars) return this.encode(text);
|
|
54
|
+
const cached = this.countCache.get(text);
|
|
55
|
+
if (cached !== void 0) {
|
|
56
|
+
this.countCache.delete(text);
|
|
57
|
+
this.countCache.set(text, cached);
|
|
58
|
+
return cached;
|
|
59
|
+
}
|
|
60
|
+
const count = this.encode(text);
|
|
61
|
+
this.countCache.set(text, count);
|
|
62
|
+
this.cachedChars += text.length;
|
|
63
|
+
for (const [oldest] of this.countCache) {
|
|
64
|
+
if (this.cachedChars <= this.cacheMaxChars) break;
|
|
65
|
+
this.countCache.delete(oldest);
|
|
66
|
+
this.cachedChars -= oldest.length;
|
|
67
|
+
}
|
|
68
|
+
return count;
|
|
69
|
+
}
|
|
70
|
+
encode(text) {
|
|
23
71
|
return this.getEncoder().encode_ordinary(text).length;
|
|
24
72
|
}
|
|
25
73
|
/**
|
|
@@ -74,12 +122,32 @@ var TokenCounter = class {
|
|
|
74
122
|
* Free encoder resources when done
|
|
75
123
|
*/
|
|
76
124
|
dispose() {
|
|
125
|
+
this.countCache.clear();
|
|
126
|
+
this.cachedChars = 0;
|
|
77
127
|
if (this.encoder) {
|
|
78
128
|
this.encoder.free();
|
|
79
129
|
this.encoder = null;
|
|
80
130
|
}
|
|
81
131
|
}
|
|
82
132
|
};
|
|
133
|
+
/**
|
|
134
|
+
* A calibrated view over a raw `TokenCounter`. `countTokens` is the one primitive every other count
|
|
135
|
+
* routes through, so overriding it calibrates message, session and tool-schema counts at once. The
|
|
136
|
+
* flat image estimate stays unscaled because it is not a tiktoken count. The view holds no encoder of
|
|
137
|
+
* its own - it reads through `raw` - so `dispose` is a no-op and disposing the view never frees the
|
|
138
|
+
* shared encoder.
|
|
139
|
+
*/
|
|
140
|
+
var CalibratedTokenCounter = class extends TokenCounter {
|
|
141
|
+
constructor(raw, multiplier) {
|
|
142
|
+
super();
|
|
143
|
+
this.raw = raw;
|
|
144
|
+
this.multiplier = multiplier;
|
|
145
|
+
}
|
|
146
|
+
countTokens(text) {
|
|
147
|
+
return scaleTokenEstimate(this.raw.countTokens(text), this.multiplier);
|
|
148
|
+
}
|
|
149
|
+
dispose() {}
|
|
150
|
+
};
|
|
83
151
|
let tokenCounter = null;
|
|
84
152
|
/**
|
|
85
153
|
* Get the singleton TokenCounter instance
|
|
@@ -151,7 +219,8 @@ var ConversationContext = class ConversationContext {
|
|
|
151
219
|
* rendering each turn's bounded tool trace - plus the system prompt, and
|
|
152
220
|
* compares against `thresholdRatio` of the context window. Rich-content aware:
|
|
153
221
|
* replaces the old string-only `content` sum so a session whose weight lives
|
|
154
|
-
* in tool traces still triggers compaction.
|
|
222
|
+
* in tool traces still triggers compaction. Counts are calibrated to
|
|
223
|
+
* `opts.model`, so `systemPromptTokens` must be too (the caller owns it).
|
|
155
224
|
*/
|
|
156
225
|
needsCompaction(systemPromptTokens, opts, thresholdRatio = .8) {
|
|
157
226
|
return this.estimateSessionTokens(systemPromptTokens, opts) >= opts.contextWindow * thresholdRatio;
|
|
@@ -162,7 +231,7 @@ var ConversationContext = class ConversationContext {
|
|
|
162
231
|
* system prompt. Used by `needsCompaction`.
|
|
163
232
|
*/
|
|
164
233
|
estimateSessionTokens(systemPromptTokens, opts) {
|
|
165
|
-
return systemPromptTokens + this.messages.filter((m) => m.role === "user" || m.role === "assistant").reduce((sum, m) => sum + this.estimateTokens(this.renderHistoryMessage(m, opts)), 0);
|
|
234
|
+
return systemPromptTokens + this.messages.filter((m) => m.role === "user" || m.role === "assistant").reduce((sum, m) => sum + this.estimateTokens(this.renderHistoryMessage(m, opts), opts), 0);
|
|
166
235
|
}
|
|
167
236
|
/** Serialize back to a Session for on-disk persistence (rich content preserved). */
|
|
168
237
|
toSession() {
|
|
@@ -181,12 +250,12 @@ var ConversationContext = class ConversationContext {
|
|
|
181
250
|
*/
|
|
182
251
|
windowedHistory(newInput, opts) {
|
|
183
252
|
const reserved = opts.reservedTokens ?? 8e3;
|
|
184
|
-
const currentTokens = this.estimateTokens(this.inputToIMessage(newInput));
|
|
253
|
+
const currentTokens = this.estimateTokens(this.inputToIMessage(newInput), opts);
|
|
185
254
|
const history = this.messages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => this.renderHistoryMessage(m, opts));
|
|
186
255
|
let remaining = opts.contextWindow - reserved - currentTokens;
|
|
187
256
|
const kept = [];
|
|
188
257
|
for (let i = history.length - 1; i >= 0; i--) {
|
|
189
|
-
const cost = this.estimateTokens(history[i]);
|
|
258
|
+
const cost = this.estimateTokens(history[i], opts);
|
|
190
259
|
if (cost > remaining) break;
|
|
191
260
|
remaining -= cost;
|
|
192
261
|
kept.unshift(history[i]);
|
|
@@ -250,7 +319,7 @@ var ConversationContext = class ConversationContext {
|
|
|
250
319
|
if (b.type !== "tool_use") continue;
|
|
251
320
|
const resultText = b.id && results.get(b.id) || "";
|
|
252
321
|
const line = `[tool ${b.name}] ${this.brief(JSON.stringify(b.input ?? {}))} -> ${this.brief(resultText)}`;
|
|
253
|
-
const cost = this.counter.countTokens(line);
|
|
322
|
+
const cost = this.counter.forModel(opts.model).countTokens(line);
|
|
254
323
|
if (used + cost > budget) {
|
|
255
324
|
omitted++;
|
|
256
325
|
continue;
|
|
@@ -272,8 +341,8 @@ var ConversationContext = class ConversationContext {
|
|
|
272
341
|
content: input
|
|
273
342
|
};
|
|
274
343
|
}
|
|
275
|
-
estimateTokens(message) {
|
|
276
|
-
return this.counter.countMessageContent(message.content);
|
|
344
|
+
estimateTokens(message, opts) {
|
|
345
|
+
return this.counter.forModel(opts.model).countMessageContent(message.content);
|
|
277
346
|
}
|
|
278
347
|
};
|
|
279
348
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { J as getPlanModeFilePath, L as generateCliTools, N as ReActAgent, R as wrapTools, Y as buildSystemPrompt, a as loadProjectContext, c as createSseBackend, i as buildProjectAgentStore, l as ServerLlmBackend, lt as Logger, n as BackgroundAgentManager, o as McpManager, q as setWebSocketToolExecutor, r as SubagentOrchestrator, t as AgentHistoryStore } from "./AgentHistoryStore-DVxv1kSO.mjs";
|
|
3
|
+
import { D as ARTIFACT_REMOVED_PLACEHOLDER, Dt as stripToolArtifactMarkup, E as ARTIFACT_DELIVERED_PLACEHOLDER, Et as stripDeliveredArtifactBlocks, F as ChatModels, G as ModelBackend, I as CompletionReasoningEffortSchema, N as CONTEXT_WINDOW_SAFETY_BUFFER_TOKENS, U as MODEL_INFO_FIELD_GROUP_OF, X as PermissionDeniedError, _ as logger, ct as escapeThinkMarkers, ft as getQuestErrorCode, lt as filterToolArtifactMarkup, mt as hasDeliverablePinnedArtifact, st as createThinkMarkerEscaper, vt as isUserInitiatedAbort } from "./ConfigStore-51UaP9pJ.mjs";
|
|
4
4
|
import "crypto";
|
|
5
5
|
import { v4 } from "uuid";
|
|
6
6
|
import { z as z$1 } from "zod";
|
|
@@ -20,10 +20,28 @@ import WebSocket from "ws";
|
|
|
20
20
|
* A tool failure that must end the turn rather than be fed back to the model as a
|
|
21
21
|
* recoverable observation: a declined tool (PermissionDeniedError) or an out-of-credits
|
|
22
22
|
* error (tagged via `getQuestErrorCode`), neither of which any retry can satisfy.
|
|
23
|
+
* Also exempts these from the services tool failure guard (toolFailureGuard.ts).
|
|
23
24
|
*/
|
|
24
25
|
function isTerminalToolError(error) {
|
|
25
26
|
return error instanceof PermissionDeniedError || getQuestErrorCode(error) !== void 0;
|
|
26
27
|
}
|
|
28
|
+
/** Whether the named tool is flagged `endsTurnAfterText` in the offered tool list. */
|
|
29
|
+
function isTurnEndingTool(name, tools) {
|
|
30
|
+
return tools?.find((tool) => tool.toolSchema.name === name)?.endsTurnAfterText === true;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Whether the tool loop should end the turn after this round's tool batch instead of making
|
|
34
|
+
* the follow-up model call. True only when the round already streamed non-empty answer text
|
|
35
|
+
* AND every tool it called is flagged `endsTurnAfterText`: the follow-up round would only
|
|
36
|
+
* repeat the answer. A tool-only round still recurses so the model gets to answer.
|
|
37
|
+
*
|
|
38
|
+
* The single decision point for every adapter's tool loop - keep them from drifting.
|
|
39
|
+
*/
|
|
40
|
+
function shouldEndTurnAfterTools(calledToolNames, tools, roundText) {
|
|
41
|
+
const names = calledToolNames.filter((name) => Boolean(name));
|
|
42
|
+
if (names.length === 0 || !roundText?.trim()) return false;
|
|
43
|
+
return names.every((name) => isTurnEndingTool(name, tools));
|
|
44
|
+
}
|
|
27
45
|
/**
|
|
28
46
|
* Execute an array of async tasks either in parallel (with concurrency limiting)
|
|
29
47
|
* or sequentially, returning outcomes in the original order.
|
|
@@ -243,6 +261,8 @@ const stripToolDependentMessages = (messages) => messages.filter((message) => !m
|
|
|
243
261
|
* WHOLE request with `ValidationException: A maximum of 4 blocks with cache_control may be
|
|
244
262
|
* provided`, which is non-retryable - so an over-budget request loses the turn outright,
|
|
245
263
|
* after the user has already waited for it.
|
|
264
|
+
* See https://docs.claude.com/en/docs/build-with-claude/prompt-caching (tools, system and messages
|
|
265
|
+
* all count toward the same four).
|
|
246
266
|
*/
|
|
247
267
|
const MAX_CACHE_CONTROL_BLOCKS = 4;
|
|
248
268
|
/**
|
|
@@ -261,18 +281,44 @@ function hasMarker(block) {
|
|
|
261
281
|
* shareable-prefix breakpoint) - so this adapter's budget is whatever they left, not the full four.
|
|
262
282
|
*/
|
|
263
283
|
function censusMarkers(params) {
|
|
264
|
-
const tools =
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
284
|
+
const { tools, system, messages } = markerSites(params);
|
|
285
|
+
return {
|
|
286
|
+
tools: tools.length,
|
|
287
|
+
system: system.length,
|
|
288
|
+
messages: messages.length,
|
|
289
|
+
total: tools.length + system.length + messages.length
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function markerSites(params) {
|
|
293
|
+
const sitesIn = (region, value) => Array.isArray(value) ? value.flatMap((block, index) => hasMarker(block) ? [{
|
|
294
|
+
region,
|
|
295
|
+
index
|
|
296
|
+
}] : []) : [];
|
|
297
|
+
const messages = [];
|
|
298
|
+
if (Array.isArray(params.messages)) params.messages.forEach((message, messageIndex) => {
|
|
268
299
|
const content = message?.content;
|
|
269
|
-
if (Array.isArray(content))
|
|
270
|
-
|
|
300
|
+
if (!Array.isArray(content)) return;
|
|
301
|
+
content.forEach((block, index) => {
|
|
302
|
+
const inner = block?.content;
|
|
303
|
+
if (Array.isArray(inner)) inner.forEach((innerBlock, nested) => {
|
|
304
|
+
if (hasMarker(innerBlock)) messages.push({
|
|
305
|
+
region: "messages",
|
|
306
|
+
index,
|
|
307
|
+
message: messageIndex,
|
|
308
|
+
nested
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
if (hasMarker(block)) messages.push({
|
|
312
|
+
region: "messages",
|
|
313
|
+
index,
|
|
314
|
+
message: messageIndex
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
});
|
|
271
318
|
return {
|
|
272
|
-
tools,
|
|
273
|
-
system,
|
|
274
|
-
messages
|
|
275
|
-
total: tools + system + messages
|
|
319
|
+
tools: sitesIn("tools", params.tools),
|
|
320
|
+
system: sitesIn("system", params.system),
|
|
321
|
+
messages
|
|
276
322
|
};
|
|
277
323
|
}
|
|
278
324
|
/**
|
|
@@ -371,14 +417,14 @@ var AnthropicCachingAdapter = class {
|
|
|
371
417
|
else console.info(message, JSON.stringify(census));
|
|
372
418
|
} else if (logger) logger.debug("[PromptCache] cache_control census", census);
|
|
373
419
|
if (outbound.total > MAX_CACHE_CONTROL_BLOCKS) {
|
|
374
|
-
const message = `[PromptCache] request exceeds the ${MAX_CACHE_CONTROL_BLOCKS}-block cache_control limit on arrival (${outbound.total}); the
|
|
420
|
+
const message = `[PromptCache] request exceeds the ${MAX_CACHE_CONTROL_BLOCKS}-block cache_control limit on arrival (${outbound.total}); capCacheControlMarkers will strip the excess before send`;
|
|
375
421
|
const detail = {
|
|
376
422
|
inbound,
|
|
377
423
|
outbound,
|
|
378
424
|
limit: MAX_CACHE_CONTROL_BLOCKS
|
|
379
425
|
};
|
|
380
|
-
if (logger) logger.
|
|
381
|
-
else console.
|
|
426
|
+
if (logger) logger.warn(message, detail);
|
|
427
|
+
else console.warn(message, JSON.stringify(detail));
|
|
382
428
|
} else if (dropped.length > 0) {
|
|
383
429
|
const message = `[PromptCache] cache_control budget exhausted (limit ${MAX_CACHE_CONTROL_BLOCKS}); skipped breakpoints: ${dropped.join(", ")}`;
|
|
384
430
|
const detail = {
|
|
@@ -479,6 +525,29 @@ var GeminiCachingAdapter = class {
|
|
|
479
525
|
}
|
|
480
526
|
};
|
|
481
527
|
/**
|
|
528
|
+
* Cached prompt tokens from a raw provider usage object, across every spelling in use:
|
|
529
|
+
* OpenAI Chat Completions nests them under `prompt_tokens_details`, the OpenAI
|
|
530
|
+
* Responses API under `input_tokens_details`, Moonshot publishes a flat
|
|
531
|
+
* `cached_tokens` alongside the OpenAI-shaped nesting, and DeepSeek its own flat
|
|
532
|
+
* `prompt_cache_hit_tokens`. Reading only one spelling silently bills every cache
|
|
533
|
+
* hit on the other transports at the full input rate.
|
|
534
|
+
*
|
|
535
|
+
* DeepSeek's own spelling leads, because it is the number its invoice is computed
|
|
536
|
+
* from; the OpenAI-shaped ones it also sends are the fallback for a proxy that
|
|
537
|
+
* forwards only those.
|
|
538
|
+
*/
|
|
539
|
+
function cachedTokensFromUsage(usage) {
|
|
540
|
+
if (!usage) return 0;
|
|
541
|
+
const candidates = [
|
|
542
|
+
usage.prompt_cache_hit_tokens,
|
|
543
|
+
usage.cached_tokens,
|
|
544
|
+
usage.prompt_tokens_details?.cached_tokens,
|
|
545
|
+
usage.input_tokens_details?.cached_tokens
|
|
546
|
+
];
|
|
547
|
+
for (const value of candidates) if (typeof value === "number" && Number.isFinite(value) && value > 0) return value;
|
|
548
|
+
return 0;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
482
551
|
* xAI Grok-specific caching adapter
|
|
483
552
|
* Caching is automatic with optional conversation ID for cache affinity
|
|
484
553
|
*/
|
|
@@ -495,7 +564,7 @@ var XAICachingAdapter = class {
|
|
|
495
564
|
const usage = response.usage;
|
|
496
565
|
if (!usage) return void 0;
|
|
497
566
|
const totalInputTokens = usage.prompt_tokens || 0;
|
|
498
|
-
const cachedTokens = usage
|
|
567
|
+
const cachedTokens = cachedTokensFromUsage(usage);
|
|
499
568
|
const cacheHitRate = totalInputTokens > 0 ? cachedTokens / totalInputTokens * 100 : 0;
|
|
500
569
|
const costSavingsPercent = cacheHitRate * .65;
|
|
501
570
|
const estimatedLatencyReduction = cacheHitRate * .7;
|
|
@@ -553,29 +622,6 @@ var KimiCachingAdapter = class {
|
|
|
553
622
|
}
|
|
554
623
|
};
|
|
555
624
|
/**
|
|
556
|
-
* Cached prompt tokens from a raw provider usage object, across every spelling in use:
|
|
557
|
-
* OpenAI Chat Completions nests them under `prompt_tokens_details`, the OpenAI
|
|
558
|
-
* Responses API under `input_tokens_details`, Moonshot publishes a flat
|
|
559
|
-
* `cached_tokens` alongside the OpenAI-shaped nesting, and DeepSeek its own flat
|
|
560
|
-
* `prompt_cache_hit_tokens`. Reading only one spelling silently bills every cache
|
|
561
|
-
* hit on the other transports at the full input rate.
|
|
562
|
-
*
|
|
563
|
-
* DeepSeek's own spelling leads, because it is the number its invoice is computed
|
|
564
|
-
* from; the OpenAI-shaped ones it also sends are the fallback for a proxy that
|
|
565
|
-
* forwards only those.
|
|
566
|
-
*/
|
|
567
|
-
function cachedTokensFromUsage(usage) {
|
|
568
|
-
if (!usage) return 0;
|
|
569
|
-
const candidates = [
|
|
570
|
-
usage.prompt_cache_hit_tokens,
|
|
571
|
-
usage.cached_tokens,
|
|
572
|
-
usage.prompt_tokens_details?.cached_tokens,
|
|
573
|
-
usage.input_tokens_details?.cached_tokens
|
|
574
|
-
];
|
|
575
|
-
for (const value of candidates) if (typeof value === "number" && Number.isFinite(value) && value > 0) return value;
|
|
576
|
-
return 0;
|
|
577
|
-
}
|
|
578
|
-
/**
|
|
579
625
|
* DeepSeek context caching. Automatic, like Moonshot's and xAI's: no parameter,
|
|
580
626
|
* no header, no explicit cache-creation call. The adapter exists only to read
|
|
581
627
|
* the counters back out.
|
|
@@ -617,7 +663,8 @@ var NoOpCachingAdapter = class {
|
|
|
617
663
|
extractCacheStats() {}
|
|
618
664
|
};
|
|
619
665
|
ModelBackend.Anthropic, new AnthropicCachingAdapter(), ModelBackend.OpenAI, new OpenAICachingAdapter(), ModelBackend.Gemini, new GeminiCachingAdapter(), ModelBackend.Bedrock, new AnthropicCachingAdapter(), ModelBackend.XAI, new XAICachingAdapter(), ModelBackend.Kimi, new KimiCachingAdapter(), ModelBackend.DeepSeek, new DeepSeekCachingAdapter(), ModelBackend.Ollama, new NoOpCachingAdapter(), ModelBackend.BFL, new NoOpCachingAdapter(), ModelBackend.VoyageAI, new NoOpCachingAdapter(), ModelBackend.AWS, new NoOpCachingAdapter(), ModelBackend.LocalImage, new NoOpCachingAdapter();
|
|
620
|
-
ChatModels.KIMI_K2_THINKING_BEDROCK, ChatModels.KIMI_K2_5_BEDROCK, ChatModels.DEEPSEEK_R1_BEDROCK, ChatModels.DEEPSEEK_FLASH, ChatModels.DEEPSEEK_V4_PRO;
|
|
666
|
+
ChatModels.KIMI_K2_THINKING_BEDROCK, ChatModels.KIMI_K2_5_BEDROCK, ChatModels.DEEPSEEK_R1_BEDROCK, ChatModels.DEEPSEEK_FLASH, ChatModels.DEEPSEEK_V4_PRO, ChatModels.KIMI_K3;
|
|
667
|
+
ChatModels.CLAUDE_4_7_OPUS, ChatModels.CLAUDE_4_7_OPUS_BEDROCK, ChatModels.CLAUDE_4_8_OPUS, ChatModels.CLAUDE_4_8_OPUS_BEDROCK, ChatModels.CLAUDE_5_SONNET, ChatModels.CLAUDE_5_SONNET_BEDROCK, ChatModels.CLAUDE_5_OPUS, ChatModels.CLAUDE_5_OPUS_BEDROCK, ChatModels.CLAUDE_5_5_HAIKU, ChatModels.CLAUDE_5_5_HAIKU_BEDROCK, ChatModels.CLAUDE_5_5_SONNET_BEDROCK;
|
|
621
668
|
new Logger();
|
|
622
669
|
ChatModels.CLAUDE_4_5_SONNET, ChatModels.CLAUDE_4_1_OPUS, ChatModels.CLAUDE_4_5_HAIKU, ChatModels.CLAUDE_4_5_OPUS, ChatModels.CLAUDE_4_6_SONNET, ChatModels.CLAUDE_4_6_OPUS;
|
|
623
670
|
ChatModels.CLAUDE_4_5_SONNET_BEDROCK, ChatModels.CLAUDE_4_5_HAIKU_BEDROCK, ChatModels.CLAUDE_4_5_OPUS_BEDROCK, ChatModels.CLAUDE_4_6_SONNET_BEDROCK, ChatModels.CLAUDE_4_6_OPUS_BEDROCK;
|
|
@@ -994,6 +1041,20 @@ var OllamaBackend = class OllamaBackend {
|
|
|
994
1041
|
if (!inheritedArtifactGuard && artifactGuard) await artifactGuard.flush();
|
|
995
1042
|
return;
|
|
996
1043
|
}
|
|
1044
|
+
if (round.toolCalls.length > 0 && shouldEndTurnAfterTools(toolCalls.map((tc) => tc.name), options.tools, round.content)) {
|
|
1045
|
+
this._logger.info("[Tool Execution] Ending turn: answer already produced, only end-of-turn tools ran", {
|
|
1046
|
+
model,
|
|
1047
|
+
toolsExecuted: toolCalls.map((tc) => tc.name)
|
|
1048
|
+
});
|
|
1049
|
+
await (artifactGuard?.callback ?? callback)([round.content], {
|
|
1050
|
+
inputTokens,
|
|
1051
|
+
outputTokens,
|
|
1052
|
+
toolsUsed: executedToolsUsed,
|
|
1053
|
+
stopReason: "tool_use"
|
|
1054
|
+
});
|
|
1055
|
+
if (!inheritedArtifactGuard && artifactGuard) await artifactGuard.flush();
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
997
1058
|
await this.complete(model, messages, {
|
|
998
1059
|
...options,
|
|
999
1060
|
_internal: {
|
|
@@ -1285,9 +1346,10 @@ var OllamaBackend = class OllamaBackend {
|
|
|
1285
1346
|
const texts = [];
|
|
1286
1347
|
const images = [];
|
|
1287
1348
|
for (const block of msg.content) if (block.type === "text" && typeof block.text === "string") texts.push(block.text);
|
|
1288
|
-
else if (block.type === "image")
|
|
1289
|
-
|
|
1290
|
-
|
|
1349
|
+
else if (block.type === "image") {
|
|
1350
|
+
if (block.source.type === "base64") images.push(block.source.data);
|
|
1351
|
+
else this._logger.debug("[OllamaBackend] Dropping non-base64 image block; Ollama requires inline base64.");
|
|
1352
|
+
} else if (block.type === "image_url") {
|
|
1291
1353
|
const dataUrl = block.image_url.url.match(/^data:[^,]*;base64,(.+)$/s);
|
|
1292
1354
|
if (dataUrl) images.push(dataUrl[1]);
|
|
1293
1355
|
else this._logger.debug("[OllamaBackend] Dropping non-data image_url; Ollama requires inline base64.");
|
|
@@ -1324,6 +1386,7 @@ ModelBackend.Bedrock, ModelBackend.AWS, ModelBackend.OpenAI, ModelBackend.Anthro
|
|
|
1324
1386
|
Object.values(ModelBackend);
|
|
1325
1387
|
Object.entries(MODEL_INFO_FIELD_GROUP_OF);
|
|
1326
1388
|
ModelBackend.Anthropic, ModelBackend.Gemini, ModelBackend.XAI, ModelBackend.Ollama, ModelBackend.BFL, ModelBackend.LocalImage, ModelBackend.AWS;
|
|
1389
|
+
CompletionReasoningEffortSchema.options;
|
|
1327
1390
|
ModelBackend.Ollama, ModelBackend.LocalImage;
|
|
1328
1391
|
//#endregion
|
|
1329
1392
|
//#region src/llm/NotifyingLlmBackend.ts
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { _ as
|
|
4
|
-
import { a as deferredToolRegistry, i as createToolSearchTool, n as buildSupportingStores, o as NotifyingLlmBackend, r as buildLlmBackend, t as buildAgent } from "../buildAgent-
|
|
5
|
-
import {
|
|
2
|
+
import { A as createAgentDelegateTool, C as createTodoStore, D as createCoordinateTaskTool, F as PermissionManager, I as buildCliToolList, O as createResumeAgentTool, Q as CustomCommandStore, S as createFindDefinitionTool, T as createSkillTool, Z as RemoteSkillSource, b as createGetFileStructureTool, j as FallbackLlmBackend, k as createBackgroundAgentTools, nt as SessionStore, q as setWebSocketToolExecutor, s as buildSandbox, tt as CheckpointStore, w as createWriteTodosTool } from "../AgentHistoryStore-DVxv1kSO.mjs";
|
|
3
|
+
import { _ as logger, o as isReadOnlyTool, t as ConfigStore, u as requireApiUrl } from "../ConfigStore-51UaP9pJ.mjs";
|
|
4
|
+
import { a as deferredToolRegistry, i as createToolSearchTool, n as buildSupportingStores, o as NotifyingLlmBackend, r as buildLlmBackend, t as buildAgent } from "../buildAgent-CrSRKYU_.mjs";
|
|
5
|
+
import { a as ApiClient } from "../dist-BsG2Hlp-.mjs";
|
|
6
6
|
import { randomUUID } from "crypto";
|
|
7
7
|
import { existsSync, realpathSync, statSync } from "fs";
|
|
8
8
|
import { isAbsolute } from "path";
|
|
@@ -604,10 +604,7 @@ var AcpServer = class {
|
|
|
604
604
|
checkpointStore
|
|
605
605
|
});
|
|
606
606
|
const additionalDirectories = await this.resolveAdditionalDirectories();
|
|
607
|
-
const agentContext = {
|
|
608
|
-
currentAgent: null,
|
|
609
|
-
observationQueue: []
|
|
610
|
-
};
|
|
607
|
+
const agentContext = { currentAgent: null };
|
|
611
608
|
const { agentStore, contextResult, loadedB4mTools, orchestrator, backgroundManager, historyStore, mcpManager } = await buildSupportingStores({
|
|
612
609
|
config,
|
|
613
610
|
llm,
|
|
@@ -629,6 +626,11 @@ var AcpServer = class {
|
|
|
629
626
|
});
|
|
630
627
|
const llmWithFallback = config.fallbackModels && config.fallbackModels.length > 0 ? new FallbackLlmBackend(llm, config.fallbackModels, (from, to, error) => logger.debug(`[acp] Model "${from}" failed (${error.message}); falling back to "${to}"`)) : llm;
|
|
631
628
|
const notifyingLlm = new NotifyingLlmBackend(llmWithFallback, backgroundManager);
|
|
629
|
+
const agentToolsRef = { current: null };
|
|
630
|
+
const toolSearchTool = deferredToolRegistry.size() > 0 ? createToolSearchTool(() => {
|
|
631
|
+
if (!agentToolsRef.current) throw new Error("tool_search invoked before agent context was wired");
|
|
632
|
+
return agentToolsRef.current;
|
|
633
|
+
}) : null;
|
|
632
634
|
const cliTools = this.buildCliTools({
|
|
633
635
|
config,
|
|
634
636
|
orchestrator,
|
|
@@ -640,18 +642,10 @@ var AcpServer = class {
|
|
|
640
642
|
agentContext,
|
|
641
643
|
apiClient,
|
|
642
644
|
sandboxOrchestrator: sandboxOrchestrator ?? void 0,
|
|
643
|
-
allowedDirectories: additionalDirectories
|
|
645
|
+
allowedDirectories: additionalDirectories,
|
|
646
|
+
toolSearchTool
|
|
644
647
|
});
|
|
645
|
-
const
|
|
646
|
-
const toolSearchTool = deferredToolRegistry.size() > 0 ? createToolSearchTool(() => {
|
|
647
|
-
if (!agentToolsRef.current) throw new Error("tool_search invoked before agent context was wired");
|
|
648
|
-
return agentToolsRef.current;
|
|
649
|
-
}) : null;
|
|
650
|
-
const allTools = [
|
|
651
|
-
...loadedB4mTools,
|
|
652
|
-
...toolSearchTool ? [toolSearchTool] : [],
|
|
653
|
-
...cliTools
|
|
654
|
-
];
|
|
648
|
+
const allTools = [...loadedB4mTools, ...cliTools];
|
|
655
649
|
const { agent, buildPromptForMode } = buildAgent({
|
|
656
650
|
config,
|
|
657
651
|
modelId: modelInfo.id,
|
|
@@ -684,7 +678,7 @@ var AcpServer = class {
|
|
|
684
678
|
};
|
|
685
679
|
}
|
|
686
680
|
buildCliTools(input) {
|
|
687
|
-
const { config, orchestrator, agentStore, backgroundManager, historyStore, sessionId, permissionManager, agentContext, apiClient, sandboxOrchestrator, allowedDirectories } = input;
|
|
681
|
+
const { config, orchestrator, agentStore, backgroundManager, historyStore, sessionId, permissionManager, agentContext, apiClient, sandboxOrchestrator, allowedDirectories, toolSearchTool } = input;
|
|
688
682
|
const cliWrapDeps = {
|
|
689
683
|
permissionManager,
|
|
690
684
|
showPermissionPrompt: this.promptFn,
|
|
@@ -694,10 +688,15 @@ var AcpServer = class {
|
|
|
694
688
|
sandboxOrchestrator,
|
|
695
689
|
allowedDirectories
|
|
696
690
|
};
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
691
|
+
return buildCliToolList({
|
|
692
|
+
toolSearchTool,
|
|
693
|
+
agentDelegateTool: createAgentDelegateTool(orchestrator, agentStore, sessionId, backgroundManager),
|
|
694
|
+
backgroundTools: createBackgroundAgentTools(backgroundManager),
|
|
695
|
+
resumeAgentTool: createResumeAgentTool(orchestrator, historyStore, backgroundManager),
|
|
696
|
+
writeTodosTool: createWriteTodosTool(createTodoStore()),
|
|
697
|
+
findDefinitionTool: createFindDefinitionTool(allowedDirectories),
|
|
698
|
+
getFileStructureTool: createGetFileStructureTool(allowedDirectories),
|
|
699
|
+
skillTool: config.preferences.enableSkillTool !== false ? createSkillTool({
|
|
701
700
|
customCommandStore: this.customCommandStore,
|
|
702
701
|
subagentOrchestrator: orchestrator,
|
|
703
702
|
sessionId,
|
|
@@ -706,17 +705,9 @@ var AcpServer = class {
|
|
|
706
705
|
promptFn: this.promptFn
|
|
707
706
|
},
|
|
708
707
|
allowedDirectories
|
|
709
|
-
})
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
createAgentDelegateTool(orchestrator, agentStore, sessionId, backgroundManager),
|
|
713
|
-
...createBackgroundAgentTools(backgroundManager),
|
|
714
|
-
createResumeAgentTool(orchestrator, historyStore, backgroundManager),
|
|
715
|
-
createWriteTodosTool(createTodoStore()),
|
|
716
|
-
...wrappedSecurityTools
|
|
717
|
-
];
|
|
718
|
-
if (config.preferences.enableCoordinatorMode === true) tools.push(createCoordinateTaskTool(orchestrator, agentStore, sessionId));
|
|
719
|
-
return tools;
|
|
708
|
+
}) : null,
|
|
709
|
+
coordinateTaskTool: config.preferences.enableCoordinatorMode === true ? createCoordinateTaskTool(orchestrator, agentStore, sessionId) : null
|
|
710
|
+
}, cliWrapDeps);
|
|
720
711
|
}
|
|
721
712
|
async loadCustomCommands() {
|
|
722
713
|
this.customCommandStore.setProjectTrusted(this.configStore.isProjectTrusted());
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { d as resolveApiEndpoint, g as parseApiUrl, t as ConfigStore } from "../ConfigStore-51UaP9pJ.mjs";
|
|
3
3
|
//#region src/commands/apiCommand.ts
|
|
4
4
|
/**
|
|
5
5
|
* External API config command (--api-url / --reset-api)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as version } from "../package-
|
|
3
|
-
import {
|
|
2
|
+
import { t as version } from "../package-DEsNSP54.mjs";
|
|
3
|
+
import { c as isNpmPrefixWritable, i as fetchLatestVersion, r as compareSemver } from "../updateChecker-CCLVVdH0.mjs";
|
|
4
4
|
import { t as checkRipgrep } from "../ripgrepCheck-BmkyTK2i.mjs";
|
|
5
|
-
import { execSync } from "child_process";
|
|
6
5
|
import { existsSync } from "fs";
|
|
7
6
|
import { homedir } from "os";
|
|
8
7
|
import path from "path";
|
|
8
|
+
import { execSync } from "child_process";
|
|
9
9
|
//#region src/commands/doctorCommand.ts
|
|
10
10
|
/**
|
|
11
11
|
* External doctor command (b4m doctor)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { _ as
|
|
4
|
-
import { t as
|
|
5
|
-
import {
|
|
2
|
+
import { A as createAgentDelegateTool, B as SHELL_LIKE_TOOL_COMMAND_FIELDS, C as createTodoStore, D as createCoordinateTaskTool, F as PermissionManager, I as buildCliToolList, K as classifyCommandRisk, L as generateCliTools, N as ReActAgent, O as createResumeAgentTool, Q as CustomCommandStore, R as wrapTools, S as createFindDefinitionTool, T as createSkillTool, Y as buildSystemPrompt, Z as RemoteSkillSource, a as loadProjectContext, b as createGetFileStructureTool, c as createSseBackend, i as buildProjectAgentStore, j as FallbackLlmBackend, k as createBackgroundAgentTools, n as BackgroundAgentManager, nt as SessionStore, o as McpManager, q as setWebSocketToolExecutor, r as SubagentOrchestrator, s as buildSandbox, t as AgentHistoryStore, tt as CheckpointStore, w as createWriteTodosTool, x as createWorkItemTools } from "../AgentHistoryStore-DVxv1kSO.mjs";
|
|
3
|
+
import { _ as logger, o as isReadOnlyTool, t as ConfigStore, u as requireApiUrl } from "../ConfigStore-51UaP9pJ.mjs";
|
|
4
|
+
import { r as reconstructTurnBlocks, t as WorkItemsClient } from "../WorkItemsClient-BMY0Qq1s.mjs";
|
|
5
|
+
import { a as ApiClient } from "../dist-BsG2Hlp-.mjs";
|
|
6
6
|
import { randomBytes } from "crypto";
|
|
7
7
|
import { v4 } from "uuid";
|
|
8
8
|
import { readFile } from "fs/promises";
|
|
@@ -360,10 +360,7 @@ async function handleHeadlessCommand(options) {
|
|
|
360
360
|
checkpointStore,
|
|
361
361
|
log: headlessSandboxLog
|
|
362
362
|
});
|
|
363
|
-
const agentContext = {
|
|
364
|
-
currentAgent: null,
|
|
365
|
-
observationQueue: []
|
|
366
|
-
};
|
|
363
|
+
const agentContext = { currentAgent: null };
|
|
367
364
|
const { tools: b4mTools } = await generateCliTools(config.userId, llm, modelInfo.id, permissionManager, promptFn, agentContext, configStore, apiClient, void 0, userQuestionFn, checkpointStore, sandboxOrchestrator, additionalDirectories);
|
|
368
365
|
const mcpManager = new McpManager(config);
|
|
369
366
|
const builtinAgentsDir = new URL("../agents/defaults/", import.meta.url).pathname;
|
|
@@ -407,11 +404,9 @@ async function handleHeadlessCommand(options) {
|
|
|
407
404
|
const resumeAgentTool = createResumeAgentTool(orchestrator, historyStore, backgroundManager);
|
|
408
405
|
const todoStore = createTodoStore();
|
|
409
406
|
const writeTodosTool = createWriteTodosTool(todoStore);
|
|
410
|
-
const findDefinitionTool = wrapTools([createFindDefinitionTool(additionalDirectories)], cliWrapDeps)[0];
|
|
411
|
-
const getFileStructureTool = wrapTools([createGetFileStructureTool(additionalDirectories)], cliWrapDeps)[0];
|
|
412
407
|
const workItemTools = config.preferences.enableWorkItemTools ? createWorkItemTools(new WorkItemsClient(apiClient)) : [];
|
|
413
408
|
const enableSkillTool = config.preferences.enableSkillTool !== false;
|
|
414
|
-
const skillTool = enableSkillTool ?
|
|
409
|
+
const skillTool = enableSkillTool ? createSkillTool({
|
|
415
410
|
customCommandStore,
|
|
416
411
|
subagentOrchestrator: orchestrator,
|
|
417
412
|
sessionId: session.id,
|
|
@@ -420,21 +415,18 @@ async function handleHeadlessCommand(options) {
|
|
|
420
415
|
promptFn
|
|
421
416
|
},
|
|
422
417
|
allowedDirectories: additionalDirectories
|
|
423
|
-
})
|
|
424
|
-
const cliTools =
|
|
418
|
+
}) : null;
|
|
419
|
+
const cliTools = buildCliToolList({
|
|
425
420
|
agentDelegateTool,
|
|
426
|
-
|
|
421
|
+
backgroundTools,
|
|
427
422
|
resumeAgentTool,
|
|
428
423
|
writeTodosTool,
|
|
429
|
-
findDefinitionTool,
|
|
430
|
-
getFileStructureTool,
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const coordinateTaskTool = createCoordinateTaskTool(orchestrator, agentStore, session.id);
|
|
436
|
-
cliTools.push(coordinateTaskTool);
|
|
437
|
-
}
|
|
424
|
+
findDefinitionTool: createFindDefinitionTool(additionalDirectories),
|
|
425
|
+
getFileStructureTool: createGetFileStructureTool(additionalDirectories),
|
|
426
|
+
workItemTools,
|
|
427
|
+
skillTool,
|
|
428
|
+
coordinateTaskTool: config.preferences.enableCoordinatorMode === true ? createCoordinateTaskTool(orchestrator, agentStore, session.id) : null
|
|
429
|
+
}, cliWrapDeps);
|
|
438
430
|
const allTools = [
|
|
439
431
|
...b4mTools,
|
|
440
432
|
...mcpTools,
|
|
@@ -461,7 +453,6 @@ async function handleHeadlessCommand(options) {
|
|
|
461
453
|
systemPrompt
|
|
462
454
|
});
|
|
463
455
|
agentContext.currentAgent = agent;
|
|
464
|
-
agent.observationQueue = agentContext.observationQueue;
|
|
465
456
|
if (streaming) {
|
|
466
457
|
agent.on("thought", (step) => {
|
|
467
458
|
emit({
|