@bike4mind/cli 1.2.0 → 2.1.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.
@@ -1,12 +1,27 @@
1
1
  #!/usr/bin/env node
2
+ import { bt as tokenEstimateMultiplier } from "./ConfigStore-BygzEI6H.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;
7
20
  /**
8
21
  * Token counting utility for context window management.
9
- * Uses tiktoken (cl100k_base encoding) which works for Claude and GPT-4.
22
+ * Uses tiktoken (cl100k_base encoding), the encoding GPT-4 reports. Claude's tokenizers spend more
23
+ * tokens on the same text, so call `forModel(modelId)` before comparing a count against a model's
24
+ * context window (see tokenEstimateMultiplier in @bike4mind/common).
10
25
  */
11
26
  var TokenCounter = class {
12
27
  constructor() {
@@ -17,6 +32,16 @@ var TokenCounter = class {
17
32
  return this.encoder;
18
33
  }
19
34
  /**
35
+ * A view of this counter whose counts are scaled to `modelId`'s own tokenizer, or `this` when the
36
+ * model needs no scaling (non-Claude, or a Claude version tokenEstimateMultiplier leaves at 1). Use
37
+ * it wherever a count is compared against a model's context window, so a Claude session compacts
38
+ * and reports against real token usage rather than a cl100k_base undercount.
39
+ */
40
+ forModel(modelId) {
41
+ const multiplier = tokenEstimateMultiplier(modelId);
42
+ return multiplier === 1 ? this : new CalibratedTokenCounter(this, multiplier);
43
+ }
44
+ /**
20
45
  * Count tokens in a text string
21
46
  */
22
47
  countTokens(text) {
@@ -80,6 +105,24 @@ var TokenCounter = class {
80
105
  }
81
106
  }
82
107
  };
108
+ /**
109
+ * A calibrated view over a raw `TokenCounter`. `countTokens` is the one primitive every other count
110
+ * routes through, so overriding it calibrates message, session and tool-schema counts at once. The
111
+ * flat image estimate stays unscaled because it is not a tiktoken count. The view holds no encoder of
112
+ * its own - it reads through `raw` - so `dispose` is a no-op and disposing the view never frees the
113
+ * shared encoder.
114
+ */
115
+ var CalibratedTokenCounter = class extends TokenCounter {
116
+ constructor(raw, multiplier) {
117
+ super();
118
+ this.raw = raw;
119
+ this.multiplier = multiplier;
120
+ }
121
+ countTokens(text) {
122
+ return scaleTokenEstimate(this.raw.countTokens(text), this.multiplier);
123
+ }
124
+ dispose() {}
125
+ };
83
126
  let tokenCounter = null;
84
127
  /**
85
128
  * Get the singleton TokenCounter instance
@@ -151,7 +194,8 @@ var ConversationContext = class ConversationContext {
151
194
  * rendering each turn's bounded tool trace - plus the system prompt, and
152
195
  * compares against `thresholdRatio` of the context window. Rich-content aware:
153
196
  * replaces the old string-only `content` sum so a session whose weight lives
154
- * in tool traces still triggers compaction.
197
+ * in tool traces still triggers compaction. Counts are calibrated to
198
+ * `opts.model`, so `systemPromptTokens` must be too (the caller owns it).
155
199
  */
156
200
  needsCompaction(systemPromptTokens, opts, thresholdRatio = .8) {
157
201
  return this.estimateSessionTokens(systemPromptTokens, opts) >= opts.contextWindow * thresholdRatio;
@@ -162,7 +206,7 @@ var ConversationContext = class ConversationContext {
162
206
  * system prompt. Used by `needsCompaction`.
163
207
  */
164
208
  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);
209
+ 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
210
  }
167
211
  /** Serialize back to a Session for on-disk persistence (rich content preserved). */
168
212
  toSession() {
@@ -181,12 +225,12 @@ var ConversationContext = class ConversationContext {
181
225
  */
182
226
  windowedHistory(newInput, opts) {
183
227
  const reserved = opts.reservedTokens ?? 8e3;
184
- const currentTokens = this.estimateTokens(this.inputToIMessage(newInput));
228
+ const currentTokens = this.estimateTokens(this.inputToIMessage(newInput), opts);
185
229
  const history = this.messages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => this.renderHistoryMessage(m, opts));
186
230
  let remaining = opts.contextWindow - reserved - currentTokens;
187
231
  const kept = [];
188
232
  for (let i = history.length - 1; i >= 0; i--) {
189
- const cost = this.estimateTokens(history[i]);
233
+ const cost = this.estimateTokens(history[i], opts);
190
234
  if (cost > remaining) break;
191
235
  remaining -= cost;
192
236
  kept.unshift(history[i]);
@@ -250,7 +294,7 @@ var ConversationContext = class ConversationContext {
250
294
  if (b.type !== "tool_use") continue;
251
295
  const resultText = b.id && results.get(b.id) || "";
252
296
  const line = `[tool ${b.name}] ${this.brief(JSON.stringify(b.input ?? {}))} -> ${this.brief(resultText)}`;
253
- const cost = this.counter.countTokens(line);
297
+ const cost = this.counter.forModel(opts.model).countTokens(line);
254
298
  if (used + cost > budget) {
255
299
  omitted++;
256
300
  continue;
@@ -272,8 +316,8 @@ var ConversationContext = class ConversationContext {
272
316
  content: input
273
317
  };
274
318
  }
275
- estimateTokens(message) {
276
- return this.counter.countMessageContent(message.content);
319
+ estimateTokens(message, opts) {
320
+ return this.counter.forModel(opts.model).countMessageContent(message.content);
277
321
  }
278
322
  };
279
323
  /**
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { I as generateCliTools, J as setWebSocketToolExecutor, L as wrapTools, N as ReActAgent, X as buildSystemPrompt, Y as getPlanModeFilePath, a as loadProjectContext, c as createSseBackend, i as buildProjectAgentStore, it as Logger, l as ServerLlmBackend, n as BackgroundAgentManager, o as McpManager, r as SubagentOrchestrator, t as AgentHistoryStore } from "./AgentHistoryStore-B2NEOvSW.mjs";
3
- import { D as MODEL_INFO_FIELD_GROUP_OF, H as escapeThinkMarkers, P as PermissionDeniedError, S as CONTEXT_WINDOW_SAFETY_BUFFER_TOKENS, V as createThinkMarkerEscaper, W as getQuestErrorCode, Y as isUserInitiatedAbort, k as ModelBackend, n as logger, w as ChatModels } from "./ConfigStore-qV7NrCgZ.mjs";
2
+ import { I as generateCliTools, J as setWebSocketToolExecutor, L as wrapTools, N as ReActAgent, X as buildSystemPrompt, Y as getPlanModeFilePath, a as loadProjectContext, c as createSseBackend, i as buildProjectAgentStore, it as Logger, l as ServerLlmBackend, n as BackgroundAgentManager, o as McpManager, r as SubagentOrchestrator, t as AgentHistoryStore } from "./AgentHistoryStore-DdYKfx7C.mjs";
3
+ import { $ as escapeThinkMarkers, A as ChatModels, C as ARTIFACT_REMOVED_PLACEHOLDER, L as MODEL_INFO_FIELD_GROUP_OF, O as CONTEXT_WINDOW_SAFETY_BUFFER_TOKENS, Q as createThinkMarkerEscaper, S as ARTIFACT_DELIVERED_PLACEHOLDER, W as PermissionDeniedError, _t as stripDeliveredArtifactBlocks, at as hasDeliverablePinnedArtifact, et as filterToolArtifactMarkup, lt as isUserInitiatedAbort, n as logger, rt as getQuestErrorCode, vt as stripToolArtifactMarkup, z as ModelBackend } from "./ConfigStore-BygzEI6H.mjs";
4
4
  import "crypto";
5
5
  import { v4 } from "uuid";
6
6
  import { z as z$1 } from "zod";
@@ -24,6 +24,23 @@ import WebSocket from "ws";
24
24
  function isTerminalToolError(error) {
25
25
  return error instanceof PermissionDeniedError || getQuestErrorCode(error) !== void 0;
26
26
  }
27
+ /** Whether the named tool is flagged `endsTurnAfterText` in the offered tool list. */
28
+ function isTurnEndingTool(name, tools) {
29
+ return tools?.find((tool) => tool.toolSchema.name === name)?.endsTurnAfterText === true;
30
+ }
31
+ /**
32
+ * Whether the tool loop should end the turn after this round's tool batch instead of making
33
+ * the follow-up model call. True only when the round already streamed non-empty answer text
34
+ * AND every tool it called is flagged `endsTurnAfterText`: the follow-up round would only
35
+ * repeat the answer. A tool-only round still recurses so the model gets to answer.
36
+ *
37
+ * The single decision point for every adapter's tool loop - keep them from drifting.
38
+ */
39
+ function shouldEndTurnAfterTools(calledToolNames, tools, roundText) {
40
+ const names = calledToolNames.filter((name) => Boolean(name));
41
+ if (names.length === 0 || !roundText?.trim()) return false;
42
+ return names.every((name) => isTurnEndingTool(name, tools));
43
+ }
27
44
  /**
28
45
  * Execute an array of async tasks either in parallel (with concurrency limiting)
29
46
  * or sequentially, returning outcomes in the original order.
@@ -135,6 +152,96 @@ function attachFullToolResult(entry, observation) {
135
152
  writable: true
136
153
  });
137
154
  }
155
+ /** The `artifactType` the named tool declared at registration, for the helpers below. */
156
+ function declaredArtifactType(tools, toolName) {
157
+ return tools?.find((t) => t.toolSchema.name === toolName)?.artifactType;
158
+ }
159
+ /**
160
+ * Helper function to handle tool result streaming for artifact-generating tools
161
+ * This ensures tools like recharts that generate artifacts are streamed immediately
162
+ * rather than waiting for recursive completion calls.
163
+ *
164
+ * Only the emitters in TOOL_ARTIFACT_EMITTERS (or a tool that declared `artifactType`, see
165
+ * declaredArtifactType) stream, and only their pinned artifact type: streamed text is parsed
166
+ * into reply artifacts, so any other tool's markup would render.
167
+ *
168
+ * What does stream is still a raw tool artifact rather than reply prose, so `streamCallback`
169
+ * receives the channel tag and a public surface drops the text on that tag alone.
170
+ */
171
+ async function handleToolResultStreaming(toolName, toolResult, streamCallback, artifactType) {
172
+ const filtered = filterToolArtifactMarkup(toolName, String(toolResult), artifactType);
173
+ if (filtered !== null) await streamCallback([filtered], { channel: "tool-artifact" });
174
+ }
175
+ /**
176
+ * Strips artifact markup from a tool result before it re-enters history, so the model cannot echo
177
+ * it into a second card. Live-emitting backends pass `delivered` = whether their tool-artifact
178
+ * emit fired. The `hasDeliverablePinnedArtifact` fallback is for the OpenAI Responses path, whose
179
+ * artifact reaches the client through sharedToolBuilder extraction instead.
180
+ *
181
+ * The delivered/removed choice is made once per result and stamped on every block it contains.
182
+ * That holds because each emitter emits one pinned block; a result mixing a foreign-type block
183
+ * with a pinned one would label both DELIVERED.
184
+ */
185
+ function stripUnstreamedToolResult(toolName, result, delivered) {
186
+ return stripToolArtifactMarkup(result, delivered ?? hasDeliverablePinnedArtifact(toolName, result) ? ARTIFACT_DELIVERED_PLACEHOLDER : ARTIFACT_REMOVED_PLACEHOLDER);
187
+ }
188
+ /**
189
+ * Buffers a recursive completion's text instead of streaming it live, so an <artifact> tag the
190
+ * model echoes back (after seeing its own tool result, even sanitized) can be stripped before the
191
+ * client ever renders a second, empty card for it. Only removes blocks whose `identifier` matches
192
+ * one already delivered this turn (stripDeliveredArtifactBlocks) - a stray "<artifact" the model's
193
+ * prose merely mentions, or a genuinely NEW artifact the model composes with a different
194
+ * identifier, must not cost the rest of a legitimate reply, unlike the tool-output-facing
195
+ * stripToolArtifactMarkup.
196
+ *
197
+ * One instance is created lazily by whichever level first streams an artifact in a recursive
198
+ * chain, and reused UNCHANGED by every deeper level (never recreated per level) - see each
199
+ * backend's `_internal.artifactGuard`. A chained tool call's artifact goes through `emitArtifact`
200
+ * on this same shared instance, which flushes any text already buffered ahead of it before
201
+ * sending the artifact through - so ordering survives even when the chained call happens several
202
+ * recursion levels below where the guard was created.
203
+ */
204
+ function createRecursiveArtifactGuard(cb) {
205
+ let buffer = "";
206
+ let deliveredMarkup = "";
207
+ let meta = {};
208
+ const callback = (async (text, info) => {
209
+ for (const chunk of text) if (chunk != null) buffer += chunk;
210
+ if (info?.inputTokens || info?.outputTokens) meta = { ...info };
211
+ });
212
+ const emitArtifact = async (results, info) => {
213
+ if (buffer) {
214
+ const pending = buffer;
215
+ buffer = "";
216
+ const stripped = stripDeliveredArtifactBlocks(pending, deliveredMarkup);
217
+ if (stripped.trim()) await cb([stripped], {});
218
+ }
219
+ deliveredMarkup += results.join("");
220
+ await cb(results, info);
221
+ };
222
+ const markDelivered = (markup) => {
223
+ deliveredMarkup += markup;
224
+ };
225
+ let flushPromise = null;
226
+ const flush = () => {
227
+ if (flushPromise) return flushPromise;
228
+ const cleaned = stripDeliveredArtifactBlocks(buffer, deliveredMarkup).trim();
229
+ let resolve;
230
+ let reject;
231
+ flushPromise = new Promise((res, rej) => {
232
+ resolve = res;
233
+ reject = rej;
234
+ });
235
+ (async () => cb(cleaned ? [cleaned] : [], meta))().then(() => resolve(), reject);
236
+ return flushPromise;
237
+ };
238
+ return {
239
+ callback,
240
+ emitArtifact,
241
+ markDelivered,
242
+ flush
243
+ };
244
+ }
138
245
  /**
139
246
  * Drops the messages whose instructions assume a tool is attached (see `IMessage.requiresTool`).
140
247
  *
@@ -850,20 +957,32 @@ var OllamaBackend = class OllamaBackend {
850
957
  parallel: options.parallelToolExecution !== false,
851
958
  maxConcurrency: options.maxParallelTools
852
959
  });
960
+ const inheritedArtifactGuard = options._internal?.artifactGuard;
961
+ let artifactGuard = inheritedArtifactGuard;
853
962
  const observations = [];
854
- outcomes.forEach((outcome, i) => {
963
+ for (let i = 0; i < outcomes.length; i++) {
964
+ const outcome = outcomes[i];
855
965
  const { tc } = resolved[i];
856
966
  const params = tc.arguments || "{}";
857
967
  if (outcome.ok) {
858
- observations[i] = outcome.result;
968
+ let emitted = false;
969
+ await handleToolResultStreaming(tc.name, outcome.result, async (results, artifactInfo) => {
970
+ emitted = true;
971
+ if (!artifactGuard) artifactGuard = createRecursiveArtifactGuard(callback);
972
+ await artifactGuard.emitArtifact(results, {
973
+ toolsUsed,
974
+ ...artifactInfo
975
+ });
976
+ }, declaredArtifactType(options.tools, tc.name));
977
+ observations[i] = stripUnstreamedToolResult(tc.name, outcome.result, emitted);
859
978
  this.pushToolMessages(messages, {
860
979
  id: tc.id,
861
980
  name: tc.name,
862
981
  parameters: params
863
- }, outcome.result);
982
+ }, observations[i]);
864
983
  } else {
865
984
  if (outcome.error instanceof PermissionDeniedError) throw outcome.error;
866
- const errorMsg = `Error running ${tc.name}: ${outcome.error instanceof Error ? outcome.error.message : "Unknown error"}`;
985
+ const errorMsg = stripToolArtifactMarkup(`Error running ${tc.name}: ${outcome.error instanceof Error ? outcome.error.message : "Unknown error"}`, ARTIFACT_REMOVED_PLACEHOLDER);
867
986
  observations[i] = errorMsg;
868
987
  this.pushToolMessages(messages, {
869
988
  id: tc.id,
@@ -871,7 +990,7 @@ var OllamaBackend = class OllamaBackend {
871
990
  parameters: params
872
991
  }, errorMsg);
873
992
  }
874
- });
993
+ }
875
994
  const executedToolsUsed = [...priorToolsUsed, ...resolved.map(({ tc }, i) => {
876
995
  const entry = {
877
996
  name: tc.name,
@@ -884,11 +1003,26 @@ var OllamaBackend = class OllamaBackend {
884
1003
  return entry;
885
1004
  })];
886
1005
  if (options.abortSignal?.aborted) {
887
- await callback([""], {
1006
+ await (artifactGuard?.callback ?? callback)([""], {
888
1007
  inputTokens,
889
1008
  outputTokens,
890
1009
  toolsUsed: executedToolsUsed
891
1010
  });
1011
+ if (!inheritedArtifactGuard && artifactGuard) await artifactGuard.flush();
1012
+ return;
1013
+ }
1014
+ if (round.toolCalls.length > 0 && shouldEndTurnAfterTools(toolCalls.map((tc) => tc.name), options.tools, round.content)) {
1015
+ this._logger.info("[Tool Execution] Ending turn: answer already produced, only end-of-turn tools ran", {
1016
+ model,
1017
+ toolsExecuted: toolCalls.map((tc) => tc.name)
1018
+ });
1019
+ await (artifactGuard?.callback ?? callback)([round.content], {
1020
+ inputTokens,
1021
+ outputTokens,
1022
+ toolsUsed: executedToolsUsed,
1023
+ stopReason: "tool_use"
1024
+ });
1025
+ if (!inheritedArtifactGuard && artifactGuard) await artifactGuard.flush();
892
1026
  return;
893
1027
  }
894
1028
  await this.complete(model, messages, {
@@ -898,9 +1032,11 @@ var OllamaBackend = class OllamaBackend {
898
1032
  toolCallCount: toolCallCount + 1,
899
1033
  accumToolsUsed: executedToolsUsed,
900
1034
  accumInputTokens: inputTokens,
901
- accumOutputTokens: outputTokens
1035
+ accumOutputTokens: outputTokens,
1036
+ artifactGuard
902
1037
  }
903
- }, callback);
1038
+ }, artifactGuard?.callback ?? callback);
1039
+ if (!inheritedArtifactGuard && artifactGuard) await artifactGuard.flush();
904
1040
  } catch (error) {
905
1041
  if (error instanceof Error && isUserInitiatedAbort(error, options.abortSignal)) this._logger.debug("[OllamaBackend] Ollama request cancelled by the caller");
906
1042
  else this._logger.error("[OllamaBackend] Error during Ollama API call:", error);
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { $ as CustomCommandStore, A as createAgentDelegateTool, C as createTodoStore, D as createCoordinateTaskTool, F as PermissionManager, J as setWebSocketToolExecutor, L as wrapTools, O as createResumeAgentTool, Q as RemoteSkillSource, S as createFindDefinitionTool, T as createSkillTool, b as createGetFileStructureTool, et as CheckpointStore, j as FallbackLlmBackend, k as createBackgroundAgentTools, s as buildSandbox, tt as SessionStore, w as createWriteTodosTool } from "../AgentHistoryStore-B2NEOvSW.mjs";
3
- import { h as requireApiUrl, n as logger, s as isReadOnlyTool, t as ConfigStore } from "../ConfigStore-qV7NrCgZ.mjs";
4
- import { a as deferredToolRegistry, i as createToolSearchTool, n as buildSupportingStores, o as NotifyingLlmBackend, r as buildLlmBackend, t as buildAgent } from "../buildAgent-C-C-VGff.mjs";
5
- import { t as ApiClient } from "../ApiClient-D0fQ2FT6.mjs";
2
+ import { $ as CustomCommandStore, A as createAgentDelegateTool, C as createTodoStore, D as createCoordinateTaskTool, F as PermissionManager, J as setWebSocketToolExecutor, L as wrapTools, O as createResumeAgentTool, Q as RemoteSkillSource, S as createFindDefinitionTool, T as createSkillTool, b as createGetFileStructureTool, et as CheckpointStore, j as FallbackLlmBackend, k as createBackgroundAgentTools, s as buildSandbox, tt as SessionStore, w as createWriteTodosTool } from "../AgentHistoryStore-DdYKfx7C.mjs";
3
+ import { _ as requireApiUrl, d as isReadOnlyTool, n as logger, t as ConfigStore } from "../ConfigStore-BygzEI6H.mjs";
4
+ import { a as deferredToolRegistry, i as createToolSearchTool, n as buildSupportingStores, o as NotifyingLlmBackend, r as buildLlmBackend, t as buildAgent } from "../buildAgent-B7cMwRAy.mjs";
5
+ import { t as ApiClient } from "../ApiClient-dIB3YQc2.mjs";
6
6
  import { randomUUID } from "crypto";
7
7
  import { existsSync, realpathSync, statSync } from "fs";
8
8
  import { isAbsolute } from "path";
@@ -575,6 +575,7 @@ var AcpServer = class {
575
575
  }
576
576
  async buildStack() {
577
577
  const config = await this.configStore.load();
578
+ this.configStore.warnPendingMcpApprovals();
578
579
  await this.loadCustomCommands();
579
580
  const authTokens = await this.configStore.getAuthTokens();
580
581
  if (!authTokens) throw RequestError.authRequired(void 0, "Not authenticated. Run `b4m /login` first.");
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { g as resolveApiEndpoint, m as parseApiUrl, t as ConfigStore } from "../ConfigStore-qV7NrCgZ.mjs";
2
+ import { g as parseApiUrl, t as ConfigStore, v as resolveApiEndpoint } from "../ConfigStore-BygzEI6H.mjs";
3
3
  //#region src/commands/apiCommand.ts
4
4
  /**
5
5
  * External API config command (--api-url / --reset-api)
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as version } from "../package-CGZIoxcs.mjs";
2
+ import { t as version } from "../package-CRNnNXxq.mjs";
3
3
  import { a as fetchLatestVersion, c as isNpmPrefixWritable, i as compareSemver } from "../updateChecker-CQW8bxo6.mjs";
4
4
  import { t as checkRipgrep } from "../ripgrepCheck-BmkyTK2i.mjs";
5
5
  import { execSync } from "child_process";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as ConfigStore } from "../ConfigStore-qV7NrCgZ.mjs";
2
+ import { t as ConfigStore } from "../ConfigStore-BygzEI6H.mjs";
3
3
  //#region src/commands/envCommand.ts
4
4
  /**
5
5
  * Environment switching for the `--dev` / `--prod` launch flags.
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { $ as CustomCommandStore, A as createAgentDelegateTool, C as createTodoStore, D as createCoordinateTaskTool, F as PermissionManager, G as classifyCommandRisk, I as generateCliTools, J as setWebSocketToolExecutor, L as wrapTools, N as ReActAgent, O as createResumeAgentTool, Q as RemoteSkillSource, S as createFindDefinitionTool, T as createSkillTool, X as buildSystemPrompt, a as loadProjectContext, b as createGetFileStructureTool, c as createSseBackend, et as CheckpointStore, i as buildProjectAgentStore, j as FallbackLlmBackend, k as createBackgroundAgentTools, n as BackgroundAgentManager, o as McpManager, r as SubagentOrchestrator, s as buildSandbox, t as AgentHistoryStore, tt as SessionStore, w as createWriteTodosTool, x as createWorkItemTools, z as SHELL_LIKE_TOOL_COMMAND_FIELDS } from "../AgentHistoryStore-B2NEOvSW.mjs";
3
- import { h as requireApiUrl, n as logger, s as isReadOnlyTool, t as ConfigStore } from "../ConfigStore-qV7NrCgZ.mjs";
4
- import { t as ApiClient } from "../ApiClient-D0fQ2FT6.mjs";
5
- import { r as reconstructTurnBlocks, t as WorkItemsClient } from "../WorkItemsClient-Cow6nXx7.mjs";
2
+ import { $ as CustomCommandStore, A as createAgentDelegateTool, C as createTodoStore, D as createCoordinateTaskTool, F as PermissionManager, G as classifyCommandRisk, I as generateCliTools, J as setWebSocketToolExecutor, L as wrapTools, N as ReActAgent, O as createResumeAgentTool, Q as RemoteSkillSource, S as createFindDefinitionTool, T as createSkillTool, X as buildSystemPrompt, a as loadProjectContext, b as createGetFileStructureTool, c as createSseBackend, et as CheckpointStore, i as buildProjectAgentStore, j as FallbackLlmBackend, k as createBackgroundAgentTools, n as BackgroundAgentManager, o as McpManager, r as SubagentOrchestrator, s as buildSandbox, t as AgentHistoryStore, tt as SessionStore, w as createWriteTodosTool, x as createWorkItemTools, z as SHELL_LIKE_TOOL_COMMAND_FIELDS } from "../AgentHistoryStore-DdYKfx7C.mjs";
3
+ import { _ as requireApiUrl, d as isReadOnlyTool, n as logger, t as ConfigStore } from "../ConfigStore-BygzEI6H.mjs";
4
+ import { r as reconstructTurnBlocks, t as WorkItemsClient } from "../WorkItemsClient-ClBC49y0.mjs";
5
+ import { t as ApiClient } from "../ApiClient-dIB3YQc2.mjs";
6
6
  import { randomBytes } from "crypto";
7
7
  import { v4 } from "uuid";
8
8
  import { readFile } from "fs/promises";
@@ -250,6 +250,7 @@ async function handleHeadlessCommand(options) {
250
250
  const runId = v4();
251
251
  try {
252
252
  const config = await configStore.load();
253
+ configStore.warnPendingMcpApprovals();
253
254
  const configDirs = await configStore.getAdditionalDirectories();
254
255
  const flagDirs = process.env.B4M_ADDITIONAL_DIRS ? parseStringArray(process.env.B4M_ADDITIONAL_DIRS, "B4M_ADDITIONAL_DIRS") : [];
255
256
  const additionalDirectories = [.../* @__PURE__ */ new Set([
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { t as ConfigStore } from "../ConfigStore-qV7NrCgZ.mjs";
3
- import { t as version } from "../package-CGZIoxcs.mjs";
2
+ import { t as ConfigStore } from "../ConfigStore-BygzEI6H.mjs";
3
+ import { t as version } from "../package-CRNnNXxq.mjs";
4
4
  //#region src/commands/mcpCommand.ts
5
5
  /**
6
6
  * External MCP commands (b4m mcp list, b4m mcp add, etc.)
@@ -70,7 +70,7 @@ async function handleMcpCommand(subcommand, argv) {
70
70
  await handleDisable(config, argv.name, configStore);
71
71
  break;
72
72
  case "serve": {
73
- const { handleMcpServeCommand } = await import("../serve-CPXqcEZr.mjs");
73
+ const { handleMcpServeCommand } = await import("../serve-DM7ld3-o.mjs");
74
74
  await handleMcpServeCommand({
75
75
  http: Boolean(argv.http),
76
76
  port: typeof argv.port === "number" ? argv.port : void 0,
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { n as getDefaultPluginsDir, r as isFeatureEnabled, t as PluginStore } from "../PluginStore-DwvOJ-G3.mjs";
3
- import { t as ConfigStore } from "../ConfigStore-qV7NrCgZ.mjs";
3
+ import { t as ConfigStore } from "../ConfigStore-BygzEI6H.mjs";
4
4
  import { execFileSync } from "child_process";
5
5
  import { promises } from "fs";
6
6
  import path from "path";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as version } from "../package-CGZIoxcs.mjs";
2
+ import { t as version } from "../package-CRNnNXxq.mjs";
3
3
  import { c as isNpmPrefixWritable, l as setAutoUpdatePreference, n as REEXEC_GUARD_ENV, o as forceCheckForUpdate, r as checkForUpdate, s as getAutoUpdatePreference, t as INSTALL_CMD, u as shouldAttemptAutoUpdate } from "../updateChecker-CQW8bxo6.mjs";
4
4
  import { t as checkRipgrep } from "../ripgrepCheck-BmkyTK2i.mjs";
5
5
  import { execSync, spawnSync } from "child_process";