@bike4mind/cli 3.0.0 → 3.2.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,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { kt as tokenEstimateMultiplier } from "./ConfigStore-51UaP9pJ.mjs";
2
+ import { Mt as tokenEstimateMultiplier } from "./ConfigStore-v0wgf0_j.mjs";
3
3
  import { v4 } from "uuid";
4
4
  import { get_encoding } from "tiktoken";
5
5
  //#region ../../b4m-core/utils/dist/calibratedTokenizer.mjs
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env node
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";
2
+ import { F as generateCliTools, G as setWebSocketToolExecutor, J as DeferredToolRegistry, U as wrapTools, V as buildSystemPrompt, Y as ReActAgent, a as loadProjectContext, c as createSseBackend, i as buildProjectAgentStore, l as ServerLlmBackend, n as BackgroundAgentManager, o as McpManager, q as getPlanModeFilePath, r as SubagentOrchestrator, t as AgentHistoryStore, ut as Logger } from "./AgentHistoryStore-BWko6txx.mjs";
3
+ import { $ as PermissionDeniedError, At as stripToolArtifactMarkup, F as CONTEXT_WINDOW_SAFETY_BUFFER_TOKENS, J as ModelBackend, K as MODEL_INFO_FIELD_GROUP_OF, L as ChatModels, O as ARTIFACT_DELIVERED_PLACEHOLDER, R as CompletionReasoningEffortSchema, _t as hasDeliverablePinnedArtifact, dt as escapeThinkMarkers, ft as filterToolArtifactMarkup, ht as getQuestErrorCode, k as ARTIFACT_REMOVED_PLACEHOLDER, kt as stripDeliveredArtifactBlocks, ut as createThinkMarkerEscaper, xt as isUserInitiatedAbort, y as logger } from "./ConfigStore-v0wgf0_j.mjs";
4
4
  import "crypto";
5
5
  import { v4 } from "uuid";
6
- import { z as z$1 } from "zod";
7
6
  import "axios";
8
7
  import "@aws-sdk/client-bedrock-runtime";
9
8
  import "openai";
@@ -394,12 +393,17 @@ var AnthropicCachingAdapter = class {
394
393
  }
395
394
  }
396
395
  const tools = modifiedParams.tools;
397
- if (strategy.cacheTools && Array.isArray(tools) && tools.length > 0) {
396
+ const markIndex = Array.isArray(tools) ? tools.findLastIndex((t) => {
397
+ const tool = t;
398
+ const isServerTool = typeof tool.type === "string" && !("input_schema" in tool);
399
+ return tool.defer_loading !== true && !isServerTool;
400
+ }) : -1;
401
+ if (strategy.cacheTools && Array.isArray(tools) && markIndex >= 0) {
398
402
  const toolsCopy = [...tools];
399
- const lastTool = toolsCopy[toolsCopy.length - 1];
400
- if (claim("tools", hasMarker(lastTool))) {
401
- toolsCopy[toolsCopy.length - 1] = {
402
- ...lastTool,
403
+ const markTool = toolsCopy[markIndex];
404
+ if (claim("tools", hasMarker(markTool))) {
405
+ toolsCopy[markIndex] = {
406
+ ...markTool,
403
407
  cache_control: cacheControl
404
408
  };
405
409
  modifiedParams.tools = toolsCopy;
@@ -1430,203 +1434,7 @@ var NotifyingLlmBackend = class {
1430
1434
  };
1431
1435
  //#endregion
1432
1436
  //#region src/tools/deferredToolRegistry.ts
1433
- /**
1434
- * Registry of tool schemas that are NOT loaded into the model's initial tool
1435
- * list. The model sees only the names (via the system prompt directory) and
1436
- * must call the `tool_search` meta-tool to load schemas on demand.
1437
- *
1438
- * This mirrors Claude Code's deferred-tool pattern. The win is large for
1439
- * heavy MCP integrations (e.g. 41 GitHub MCP tools at ~250-350 tokens of
1440
- * JSONSchema each = ~10-15k tokens per turn that's now ~1-1.5k of names).
1441
- */
1442
- var DeferredToolRegistry = class {
1443
- constructor() {
1444
- this.byName = /* @__PURE__ */ new Map();
1445
- this.directoryNames = [];
1446
- }
1447
- /** Replace registry contents with the supplied tools. Idempotent. */
1448
- register(tools) {
1449
- this.byName.clear();
1450
- for (const tool of tools) this.byName.set(tool.toolSchema.name, tool);
1451
- this.directoryNames = Object.freeze([...this.byName.keys()].sort());
1452
- logger.debug(`[DeferredToolRegistry] Registered ${tools.length} deferred tool(s)`);
1453
- }
1454
- clear() {
1455
- this.byName.clear();
1456
- this.directoryNames = Object.freeze([]);
1457
- }
1458
- size() {
1459
- return this.byName.size;
1460
- }
1461
- has(name) {
1462
- return this.byName.has(name);
1463
- }
1464
- get(name) {
1465
- return this.byName.get(name);
1466
- }
1467
- getAll() {
1468
- return Array.from(this.byName.values());
1469
- }
1470
- /** Return tools whose names appear in the supplied list, in input order. */
1471
- getByNames(names) {
1472
- const found = [];
1473
- for (const name of names) {
1474
- const tool = this.byName.get(name);
1475
- if (tool) found.push(tool);
1476
- }
1477
- return found;
1478
- }
1479
- /**
1480
- * Rank-search deferred tools by query terms. Name matches outrank
1481
- * description matches; exact substring on name wins ties.
1482
- */
1483
- searchByKeywords(query, maxResults) {
1484
- const terms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 0);
1485
- if (terms.length === 0) return [];
1486
- const scored = [];
1487
- for (const tool of this.byName.values()) {
1488
- const name = tool.toolSchema.name.toLowerCase();
1489
- const desc = (tool.toolSchema.description || "").toLowerCase();
1490
- let score = 0;
1491
- for (const term of terms) {
1492
- if (name.includes(term)) score += 10;
1493
- if (desc.includes(term)) score += 1;
1494
- }
1495
- if (score > 0) scored.push({
1496
- tool,
1497
- score
1498
- });
1499
- }
1500
- scored.sort((a, b) => b.score - a.score);
1501
- return scored.slice(0, maxResults).map((s) => s.tool);
1502
- }
1503
- /**
1504
- * Directory entries rendered into the cache-stamped system-prompt reminder.
1505
- * Returns the frozen snapshot captured at `register()`, NOT live `byName`
1506
- * keys, so loading a tool mid-session can never change a byte of the cached
1507
- * system block (issue #213). A loaded tool remaining listed here is
1508
- * harmless: re-selecting it via `tool_search` is an idempotent no-op.
1509
- */
1510
- getDirectoryNames() {
1511
- return [...this.directoryNames];
1512
- }
1513
- };
1514
- const deferredToolRegistry = new DeferredToolRegistry();
1515
- //#endregion
1516
- //#region src/tools/toolSearchTool.ts
1517
- /**
1518
- * Default number of tools returned for a keyword search. Matches Claude
1519
- * Code's ToolSearch convention. 5 keeps the response payload small while
1520
- * surfacing enough alternatives for the model to refine its query.
1521
- */
1522
- const DEFAULT_MAX_RESULTS = 5;
1523
- /**
1524
- * Runtime validation for tool_search params. The LLM produces these
1525
- * values, so we validate at this boundary rather than trusting the
1526
- * shape. Coerces `max_results` from string->number for models that emit
1527
- * numeric-looking strings.
1528
- */
1529
- const ToolSearchParamsSchema = z$1.object({
1530
- query: z$1.string().min(1, "query must be a non-empty string"),
1531
- max_results: z$1.coerce.number().int().min(1).max(20).optional()
1532
- });
1533
- /**
1534
- * Parse the query string. Two forms:
1535
- * - `select:name1,name2,...` - exact-name selection
1536
- * - free text - keyword search across name + description
1537
- */
1538
- function parseQuery(query) {
1539
- const trimmed = query.trim();
1540
- const selectMatch = trimmed.match(/^select:(.+)$/i);
1541
- if (selectMatch) return {
1542
- mode: "select",
1543
- names: selectMatch[1].split(",").map((n) => n.trim()).filter((n) => n.length > 0)
1544
- };
1545
- return {
1546
- mode: "search",
1547
- text: trimmed
1548
- };
1549
- }
1550
- /**
1551
- * Format the loaded-tools response. Mirrors Claude Code's convention:
1552
- * one <function>{...}</function> line per matched tool. The model has
1553
- * already seen this format in its tool-registration system messages, so
1554
- * it parses without additional explanation.
1555
- *
1556
- * Note: the schemas are *also* injected into context.tools by the caller,
1557
- * so on the next iteration the model gets them as native tool definitions.
1558
- * The text response here is for in-turn awareness and audit trail.
1559
- */
1560
- function renderToolsBlock(tools) {
1561
- if (tools.length === 0) return "";
1562
- return `<functions>\n${tools.map((tool) => {
1563
- const schema = {
1564
- description: tool.toolSchema.description,
1565
- name: tool.toolSchema.name,
1566
- parameters: tool.toolSchema.parameters
1567
- };
1568
- return `<function>${JSON.stringify(schema)}</function>`;
1569
- }).join("\n")}\n</functions>`;
1570
- }
1571
- /**
1572
- * Build the tool_search meta-tool. The returned tool has a closure over
1573
- * the supplied `toolListAccessor`, which it uses to push newly-resolved
1574
- * tool schemas into the live agent context.
1575
- *
1576
- * Idempotent: re-loading a tool that's already in the context is a no-op.
1577
- */
1578
- function createToolSearchTool(toolListAccessor) {
1579
- return {
1580
- toolSchema: {
1581
- name: "tool_search",
1582
- description: "Fetches full schema definitions for deferred tools so they can be called. Deferred tools appear by name only in a system reminder; their parameter schemas are NOT loaded by default. Use this tool to load schemas on demand. Query forms: 'select:name1,name2' for exact selection, or free-text keywords to search by name and description. Once a tool's schema is returned, it becomes callable in subsequent turns.",
1583
- parameters: {
1584
- type: "object",
1585
- properties: {
1586
- query: {
1587
- type: "string",
1588
- description: "Either 'select:<comma-separated names>' to fetch specific tools, or free-text keywords (e.g. 'github pull request') to rank-search deferred tools."
1589
- },
1590
- max_results: {
1591
- type: "number",
1592
- description: `Maximum number of tools to return for keyword search. Defaults to ${DEFAULT_MAX_RESULTS}. Ignored for 'select:' queries.`
1593
- }
1594
- },
1595
- required: ["query"]
1596
- }
1597
- },
1598
- toolFn: async (params) => {
1599
- const parsedParams = ToolSearchParamsSchema.safeParse(params ?? {});
1600
- if (!parsedParams.success) {
1601
- const issue = parsedParams.error.issues[0];
1602
- return `tool_search: invalid parameters — ${issue.path.join(".") || "params"}: ${issue.message}`;
1603
- }
1604
- const { query, max_results } = parsedParams.data;
1605
- const parsed = parseQuery(query);
1606
- let matched;
1607
- let unmatched = [];
1608
- if (parsed.mode === "select") {
1609
- matched = deferredToolRegistry.getByNames(parsed.names);
1610
- const foundNames = new Set(matched.map((t) => t.toolSchema.name));
1611
- unmatched = parsed.names.filter((n) => !foundNames.has(n));
1612
- } else {
1613
- const max = max_results ?? DEFAULT_MAX_RESULTS;
1614
- matched = deferredToolRegistry.searchByKeywords(parsed.text, max);
1615
- }
1616
- if (matched.length === 0) return parsed.mode === "select" ? `tool_search: no deferred tools matched ${parsed.names.join(", ")}. Use a free-text query to search.` : `tool_search: no deferred tools matched query "${parsed.text}".`;
1617
- const liveTools = toolListAccessor();
1618
- const liveNames = new Set(liveTools.map((t) => t.toolSchema.name));
1619
- let added = 0;
1620
- for (const tool of matched) if (!liveNames.has(tool.toolSchema.name)) {
1621
- liveTools.push(tool);
1622
- added++;
1623
- }
1624
- logger.debug(`[tool_search] query="${query}" matched=${matched.length} added=${added} alreadyLoaded=${matched.length - added}`);
1625
- const block = renderToolsBlock(matched);
1626
- return `${`Loaded ${added} new tool schema(s)${added < matched.length ? ` (${matched.length - added} already loaded)` : ""}. These are now callable in your next message.${unmatched.length > 0 ? `\n\nNot found: ${unmatched.join(", ")}` : ""}`}\n\n${block}`;
1627
- }
1628
- };
1629
- }
1437
+ const deferredToolRegistry = new DeferredToolRegistry({ logger });
1630
1438
  //#endregion
1631
1439
  //#region src/llm/MultiLlmBackend.ts
1632
1440
  /**
@@ -2166,4 +1974,4 @@ function buildAgent(input) {
2166
1974
  };
2167
1975
  }
2168
1976
  //#endregion
2169
- export { deferredToolRegistry as a, createToolSearchTool as i, buildSupportingStores as n, NotifyingLlmBackend as o, buildLlmBackend as r, buildAgent as t };
1977
+ export { NotifyingLlmBackend as a, deferredToolRegistry as i, buildSupportingStores as n, buildLlmBackend as r, buildAgent as t };
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
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";
2
+ import { $ as CustomCommandStore, A as createAgentDelegateTool, C as createTodoStore, D as createCoordinateTaskTool, G as setWebSocketToolExecutor, N as PermissionManager, O as createResumeAgentTool, P as buildCliToolList, Q as RemoteSkillSource, S as createFindDefinitionTool, T as createSkillTool, X as createToolSearchTool, b as createGetFileStructureTool, j as FallbackLlmBackend, k as createBackgroundAgentTools, nt as CheckpointStore, rt as SessionStore, s as buildSandbox, w as createWriteTodosTool } from "../AgentHistoryStore-BWko6txx.mjs";
3
+ import { f as requireApiUrl, i as ToolCatalog, t as ConfigStore, y as logger } from "../ConfigStore-v0wgf0_j.mjs";
4
+ import { a as NotifyingLlmBackend, i as deferredToolRegistry, n as buildSupportingStores, r as buildLlmBackend, t as buildAgent } from "../buildAgent-BZnmDQIr.mjs";
5
+ import { a as ApiClient } from "../dist-5DDaX8tB.mjs";
6
6
  import { randomUUID } from "crypto";
7
7
  import { existsSync, realpathSync, statSync } from "fs";
8
8
  import { isAbsolute } from "path";
@@ -424,7 +424,7 @@ var AcpServer = class {
424
424
  result = await stack.agent.run(userText, {
425
425
  signal: abortController.signal,
426
426
  previousMessages: session.history,
427
- isReadOnlyTool,
427
+ isReadOnlyTool: (toolName) => stack.permissionManager.catalog.isReadOnly(toolName),
428
428
  maxHistoryIterations: 4
429
429
  });
430
430
  } catch (error) {
@@ -594,7 +594,7 @@ var AcpServer = class {
594
594
  tokenGetter,
595
595
  startupLog
596
596
  });
597
- const permissionManager = new PermissionManager(config.trustedTools ?? [], void 0, config.tools.disabled ?? []);
597
+ const permissionManager = new PermissionManager(new ToolCatalog(), config.trustedTools ?? [], config.tools.disabled ?? []);
598
598
  const checkpointStore = new CheckpointStore(this.configStore.getProjectConfigDir() ?? process.cwd());
599
599
  const stackSessionId = randomUUID();
600
600
  const { sandboxOrchestrator } = await buildSandbox({
@@ -627,9 +627,13 @@ var AcpServer = class {
627
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;
628
628
  const notifyingLlm = new NotifyingLlmBackend(llmWithFallback, backgroundManager);
629
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;
630
+ const toolSearchTool = deferredToolRegistry.size() > 0 ? createToolSearchTool({
631
+ registry: deferredToolRegistry,
632
+ logger,
633
+ toolListAccessor: () => {
634
+ if (!agentToolsRef.current) throw new Error("tool_search invoked before agent context was wired");
635
+ return agentToolsRef.current;
636
+ }
633
637
  }) : null;
634
638
  const cliTools = this.buildCliTools({
635
639
  config,
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { d as resolveApiEndpoint, g as parseApiUrl, t as ConfigStore } from "../ConfigStore-51UaP9pJ.mjs";
2
+ import { p as resolveApiEndpoint, t as ConfigStore, v as parseApiUrl } from "../ConfigStore-v0wgf0_j.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-DEsNSP54.mjs";
2
+ import { t as version } from "../package-COr17yKT.mjs";
3
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
5
  import { existsSync } from "fs";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as ConfigStore } from "../ConfigStore-51UaP9pJ.mjs";
2
+ import { t as ConfigStore } from "../ConfigStore-v0wgf0_j.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 { 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";
2
+ import { $ as CustomCommandStore, A as createAgentDelegateTool, C as createTodoStore, D as createCoordinateTaskTool, F as generateCliTools, G as setWebSocketToolExecutor, K as classifyCommandRisk, N as PermissionManager, O as createResumeAgentTool, P as buildCliToolList, Q as RemoteSkillSource, S as createFindDefinitionTool, T as createSkillTool, U as wrapTools, V as buildSystemPrompt, Y as ReActAgent, a as loadProjectContext, b as createGetFileStructureTool, c as createSseBackend, i as buildProjectAgentStore, j as FallbackLlmBackend, k as createBackgroundAgentTools, n as BackgroundAgentManager, nt as CheckpointStore, o as McpManager, r as SubagentOrchestrator, rt as SessionStore, s as buildSandbox, t as AgentHistoryStore, w as createWriteTodosTool, x as createWorkItemTools } from "../AgentHistoryStore-BWko6txx.mjs";
3
+ import { a as commandTextOf, f as requireApiUrl, i as ToolCatalog, s as toolCategory, t as ConfigStore, y as logger } from "../ConfigStore-v0wgf0_j.mjs";
4
+ import { r as reconstructTurnBlocks, t as WorkItemsClient } from "../WorkItemsClient-BZL44N12.mjs";
5
+ import { a as ApiClient } from "../dist-5DDaX8tB.mjs";
6
6
  import { randomBytes } from "crypto";
7
7
  import { v4 } from "uuid";
8
8
  import { readFile } from "fs/promises";
@@ -87,15 +87,15 @@ function parseStringArray(raw, inputType) {
87
87
  }
88
88
  /**
89
89
  * Classify the risk of a single tool invocation for the permission protocol.
90
- * Shell-like tools are classified from their actual command text (reusing the
91
- * shared command-risk tokenizer); every other tool falls back to a level derived
92
- * from its permission category (auto_approve -> low, prompt_always -> high, else
93
- * medium). Never throws: a classifier failure fails closed at `high`.
90
+ * Shell-like tools (a descriptor commandArg) are classified from their actual
91
+ * command text, reusing the shared command-risk tokenizer, exactly as the
92
+ * gateway's command-risk stage does; every other tool falls back to a level
93
+ * derived from its permission category (auto_approve -> low, prompt_always ->
94
+ * high, else medium). Never throws: a classifier failure fails closed at `high`.
94
95
  */
95
- function classifyToolRisk(toolName, args, category) {
96
- const field = SHELL_LIKE_TOOL_COMMAND_FIELDS[toolName];
97
- const command = field && args !== null && typeof args === "object" ? args[field] : void 0;
98
- if (typeof command === "string") try {
96
+ function classifyToolRisk(descriptor, args) {
97
+ const command = commandTextOf(descriptor, args);
98
+ if (command !== void 0) try {
99
99
  return classifyCommandRisk(command);
100
100
  } catch {
101
101
  return {
@@ -103,6 +103,7 @@ function classifyToolRisk(toolName, args, category) {
103
103
  reasons: ["command risk analysis failed (fail closed)"]
104
104
  };
105
105
  }
106
+ const category = toolCategory(descriptor);
106
107
  return {
107
108
  level: category === "auto_approve" ? "low" : category === "prompt_always" ? "high" : "medium",
108
109
  reasons: []
@@ -319,11 +320,11 @@ async function handleHeadlessCommand(options) {
319
320
  }
320
321
  };
321
322
  await logger.initialize(session.id);
322
- const permissionManager = new PermissionManager(config.trustedTools ?? [], void 0, config.tools.disabled);
323
+ const permissionManager = new PermissionManager(new ToolCatalog(), config.trustedTools ?? [], config.tools.disabled);
323
324
  const emit = createHeadlessEmitter(runId, (line) => process.stdout.write(line));
324
325
  const streaming = outputFormat === "stream-json";
325
326
  const promptFn = (toolName, args, _preview, kind) => {
326
- const risk = classifyToolRisk(toolName, args, permissionManager.getCategory(toolName));
327
+ const risk = classifyToolRisk(permissionManager.catalog.resolve(toolName), args);
327
328
  if (streaming) emit({
328
329
  type: "permission_request",
329
330
  toolName,
@@ -482,7 +483,7 @@ async function handleHeadlessCommand(options) {
482
483
  try {
483
484
  result = await agent.run(fullPrompt, {
484
485
  parallelExecution: config.preferences.enableParallelToolExecution === true,
485
- isReadOnlyTool,
486
+ isReadOnlyTool: (toolName) => permissionManager.catalog.isReadOnly(toolName),
486
487
  maxHistoryIterations: 4
487
488
  });
488
489
  } finally {
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { t as ConfigStore } from "../ConfigStore-51UaP9pJ.mjs";
3
- import { t as version } from "../package-DEsNSP54.mjs";
2
+ import { t as ConfigStore } from "../ConfigStore-v0wgf0_j.mjs";
3
+ import { t as version } from "../package-COr17yKT.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-BaHzVbcm.mjs");
73
+ const { handleMcpServeCommand } = await import("../serve-D6Eu3oXv.mjs");
74
74
  await handleMcpServeCommand({
75
75
  http: Boolean(argv.http),
76
76
  port: typeof argv.port === "number" ? argv.port : void 0,
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as ConfigStore } from "../ConfigStore-51UaP9pJ.mjs";
2
+ import { t as ConfigStore } from "../ConfigStore-v0wgf0_j.mjs";
3
3
  import { n as getDefaultPluginsDir, r as isFeatureEnabled, t as PluginStore } from "../PluginStore-DwvOJ-G3.mjs";
4
4
  import { promises } from "fs";
5
5
  import path from "path";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as version } from "../package-DEsNSP54.mjs";
2
+ import { t as version } from "../package-COr17yKT.mjs";
3
3
  import { a as forceCheckForUpdate, c as isNpmPrefixWritable, d as shouldAttemptAutoUpdate, l as readCachedUpdate, n as REEXEC_GUARD_ENV, o as getAutoUpdatePreference, t as INSTALL_CMD, u as setAutoUpdatePreference } from "../updateChecker-CCLVVdH0.mjs";
4
4
  import { t as checkRipgrep } from "../ripgrepCheck-BmkyTK2i.mjs";
5
5
  import { execSync, spawnSync } from "child_process";
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { _ as logger, f as AuthenticatedApiClient, p as DeviceFlowClient, t as ConfigStore } from "./ConfigStore-51UaP9pJ.mjs";
3
- import { t as version } from "./package-DEsNSP54.mjs";
2
+ import { h as DeviceFlowClient, m as AuthenticatedApiClient, t as ConfigStore, y as logger } from "./ConfigStore-v0wgf0_j.mjs";
3
+ import { t as version } from "./package-COr17yKT.mjs";
4
4
  import { isAxiosError } from "axios";
5
5
  import { Readable } from "node:stream";
6
6
  import { createParser } from "eventsource-parser";
package/dist/index.mjs CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { $ as processFileReferences, A as createAgentDelegateTool, C as createTodoStore, D as createCoordinateTaskTool, E as parseAgentConfig, F as PermissionManager, G as DEFAULT_THOROUGHNESS, H as DEFAULT_AGENT_MODEL, I as buildCliToolList, J as getPlanModeFilePath, M as substituteArguments, O as createResumeAgentTool, P as findIterationBoundary, Q as CustomCommandStore, S as createFindDefinitionTool, T as createSkillTool, U as DEFAULT_MAX_ITERATIONS, V as ALWAYS_DENIED_FOR_AGENTS, W as DEFAULT_RETRY_CONFIG, X as buildSkillsPromptSection, Y as buildSystemPrompt, Z as RemoteSkillSource, _ as createDecisionLogTool, at as rewireReservedNames, b as createGetFileStructureTool, ct as warmFileCache, d as createReviewGateStore, et as hasFileReferences, f as createReviewGateTool, ft as MAX_PASTE_SIZE, g as formatBlockersOutput, h as createBlockerTools, it as mergeCommands, j as FallbackLlmBackend, k as createBackgroundAgentTools, m as createBlockerStore, nt as SessionStore, ot as formatFileSize, p as formatReviewGatesOutput, q as setWebSocketToolExecutor, rt as searchCommands, s as buildSandbox, st as searchFiles, tt as CheckpointStore, u as isConnectionDrop, ut as COMPACTION_SUMMARY_MARKER, v as createDecisionStore, w as createWriteTodosTool, x as createWorkItemTools, y as formatDecisionsOutput, z as getProcessHooks } from "./AgentHistoryStore-DVxv1kSO.mjs";
2
+ import { $ as CustomCommandStore, A as createAgentDelegateTool, B as DEFAULT_THOROUGHNESS, C as createTodoStore, D as createCoordinateTaskTool, E as parseAgentConfig, G as setWebSocketToolExecutor, H as buildSkillsPromptSection, I as ALWAYS_DENIED_FOR_AGENTS, L as DEFAULT_AGENT_MODEL, M as substituteArguments, N as PermissionManager, O as createResumeAgentTool, P as buildCliToolList, Q as RemoteSkillSource, R as DEFAULT_MAX_ITERATIONS, S as createFindDefinitionTool, T as createSkillTool, V as buildSystemPrompt, W as getProcessHooks, X as createToolSearchTool, Z as findIterationBoundary, _ as createDecisionLogTool, at as mergeCommands, b as createGetFileStructureTool, ct as searchFiles, d as createReviewGateStore, dt as COMPACTION_SUMMARY_MARKER, et as processFileReferences, f as createReviewGateTool, g as formatBlockersOutput, h as createBlockerTools, it as searchCommands, j as FallbackLlmBackend, k as createBackgroundAgentTools, lt as warmFileCache, m as createBlockerStore, nt as CheckpointStore, ot as rewireReservedNames, p as formatReviewGatesOutput, pt as MAX_PASTE_SIZE, q as getPlanModeFilePath, rt as SessionStore, s as buildSandbox, st as formatFileSize, tt as hasFileReferences, u as isConnectionDrop, v as createDecisionStore, w as createWriteTodosTool, x as createWorkItemTools, y as formatDecisionsOutput, z as DEFAULT_RETRY_CONFIG } from "./AgentHistoryStore-BWko6txx.mjs";
3
3
  import { a as selectLiveSubagentTokens, i as selectLiveSubagentCredits, n as selectActiveBackgroundShells, o as useCliStore, r as selectCompletedBackgroundShells, t as selectActiveBackgroundAgents } from "./store-BxmPEI3v.mjs";
4
- import { Bt as selfClaimedActorKindSchema, F as ChatModels, Ft as DELEGATION_PAYLOAD_SCHEMA_NAME, It as actorColorIndex, Lt as actorKindMarker, P as CREDIT_DEDUCT_TRANSACTION_TYPES, Pt as ACTOR_COLOR_SLOTS, Rt as actorKindSchema, Z as REVIEW_GATE_STATUS_VALUES, _ as logger, c as getCreditsUrl, d as resolveApiEndpoint, et as SUBQUEST_STATUS_VALUES, g as parseApiUrl, ht as isEarlyStop, kt as tokenEstimateMultiplier, l as getEnvironmentName, m as LOCAL_DEV_URL, n as extractCompactInstructions, o as isReadOnlyTool, s as ApiEndpointUnconfiguredError, t as ConfigStore, u as requireApiUrl, v as SESSION_ID_PATTERN, y as isValidSessionId, zt as delegationPayloadSchema } from "./ConfigStore-51UaP9pJ.mjs";
4
+ import { Bt as actorKindMarker, Ht as delegationPayloadSchema, I as CREDIT_DEDUCT_TRANSACTION_TYPES, L as ChatModels, Lt as ACTOR_COLOR_SLOTS, Mt as tokenEstimateMultiplier, Rt as DELEGATION_PAYLOAD_SCHEMA_NAME, Ut as selfClaimedActorKindSchema, Vt as actorKindSchema, b as SESSION_ID_PATTERN, d as getEnvironmentName, et as REVIEW_GATE_STATUS_VALUES, f as requireApiUrl, g as LOCAL_DEV_URL, i as ToolCatalog, l as ApiEndpointUnconfiguredError, n as extractCompactInstructions, p as resolveApiEndpoint, rt as SUBQUEST_STATUS_VALUES, t as ConfigStore, u as getCreditsUrl, v as parseApiUrl, vt as isEarlyStop, x as isValidSessionId, y as logger, zt as actorColorIndex } from "./ConfigStore-v0wgf0_j.mjs";
5
5
  import { r as isFeatureEnabled, t as PluginStore } from "./PluginStore-DwvOJ-G3.mjs";
6
- import { i as getTokenCounter, n as ConversationContext, r as reconstructTurnBlocks, t as WorkItemsClient } from "./WorkItemsClient-BMY0Qq1s.mjs";
7
- import { a as deferredToolRegistry, i as createToolSearchTool, n as buildSupportingStores, o as NotifyingLlmBackend, r as buildLlmBackend, t as buildAgent } from "./buildAgent-CrSRKYU_.mjs";
6
+ import { i as getTokenCounter, n as ConversationContext, r as reconstructTurnBlocks, t as WorkItemsClient } from "./WorkItemsClient-BZL44N12.mjs";
7
+ import { a as NotifyingLlmBackend, i as deferredToolRegistry, n as buildSupportingStores, r as buildLlmBackend, t as buildAgent } from "./buildAgent-BZnmDQIr.mjs";
8
8
  import { t as getShellSessionManager } from "./ShellSessionManager-6o8KZzl1-vrbPAUTq.mjs";
9
- import { a as ApiClient, s as OAuthClient } from "./dist-BsG2Hlp-.mjs";
10
- import { t as version } from "./package-DEsNSP54.mjs";
9
+ import { a as ApiClient, s as OAuthClient } from "./dist-5DDaX8tB.mjs";
10
+ import { t as version } from "./package-COr17yKT.mjs";
11
11
  import { s as getStartupUpdateStatus } from "./updateChecker-CCLVVdH0.mjs";
12
12
  import React, { useCallback, useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
13
13
  import { Box, Static, Text, measureElement, render, useApp, useInput, usePaste, useStdout } from "ink";
@@ -5768,7 +5768,7 @@ const HandoffSchema = z$1.object({
5768
5768
  updatedAt: z$1.string()
5769
5769
  });
5770
5770
  z$1.object({
5771
- _id: z$1.string(),
5771
+ id: z$1.string(),
5772
5772
  notebookId: z$1.string(),
5773
5773
  goal: z$1.string(),
5774
5774
  quests: z$1.array(QuestDataSchema),
@@ -8405,7 +8405,7 @@ async function runTurn(message, ctx) {
8405
8405
  }
8406
8406
  }
8407
8407
  async function runHeldTurn(message, ctx) {
8408
- const { agent, sessionStore, configStore, commandHistoryStore, customCommandStore, messageBuilder, config, availableModels, agentStore, contextContent, additionalDirectories, featureRegistry, backgroundManager, todoStore, decisionStore, blockerStore, postEditDiagnostics, workflowStores, setCommandHistory, setAbortController } = ctx;
8408
+ const { agent, sessionStore, configStore, commandHistoryStore, customCommandStore, messageBuilder, config, availableModels, agentStore, contextContent, additionalDirectories, featureRegistry, backgroundManager, toolCatalog, todoStore, decisionStore, blockerStore, postEditDiagnostics, workflowStores, setCommandHistory, setAbortController } = ctx;
8409
8409
  const storeSession = useCliStore.getState().session;
8410
8410
  if (!agent || !storeSession) {
8411
8411
  console.error("❌ CLI failed to initialize. Try restarting b4m.\n");
@@ -8523,7 +8523,7 @@ async function runHeldTurn(message, ctx) {
8523
8523
  previousMessages: previousMessages.length > 0 ? previousMessages : void 0,
8524
8524
  signal: abortController.signal,
8525
8525
  parallelExecution: cliConfig.preferences.enableParallelToolExecution === true,
8526
- isReadOnlyTool,
8526
+ isReadOnlyTool: (toolName) => toolCatalog.isReadOnly(toolName),
8527
8527
  workflowReminder,
8528
8528
  drainFeedback: diagnostics ? (phase) => diagnostics.drain(DRAIN_BUDGET_MS[phase], abortController.signal) : void 0,
8529
8529
  onContextLimit: createReactiveCompactionHandler(agent, activeSession)
@@ -8957,6 +8957,7 @@ function CliApp() {
8957
8957
  agent: null,
8958
8958
  mcpManager: null,
8959
8959
  permissionManager: null,
8960
+ toolCatalog: new ToolCatalog(),
8960
8961
  permissionPrompt: null,
8961
8962
  trustLocationSelector: null,
8962
8963
  folderTrustPrompt: null,
@@ -9214,7 +9215,7 @@ function CliApp() {
9214
9215
  error: () => {},
9215
9216
  debug: () => {}
9216
9217
  };
9217
- const permissionManager = new PermissionManager(config.trustedTools || [], void 0, config.tools.disabled || []);
9218
+ const permissionManager = new PermissionManager(state.toolCatalog, config.trustedTools || [], config.tools.disabled || []);
9218
9219
  const checkpointProjectDir = state.configStore.getProjectConfigDir() || process.cwd();
9219
9220
  const checkpointStore = new CheckpointStore(checkpointProjectDir);
9220
9221
  const { sandboxOrchestrator } = await buildSandbox({
@@ -9374,9 +9375,13 @@ function CliApp() {
9374
9375
  }) : null;
9375
9376
  const workItemTools = config.preferences.enableWorkItemTools ? createWorkItemTools(new WorkItemsClient(apiClient)) : [];
9376
9377
  const agentToolsRef = { current: null };
9377
- const toolSearchTool = deferredToolRegistry.size() > 0 ? createToolSearchTool(() => {
9378
- if (!agentToolsRef.current) throw new Error("tool_search invoked before agent context was wired");
9379
- return agentToolsRef.current;
9378
+ const toolSearchTool = deferredToolRegistry.size() > 0 ? createToolSearchTool({
9379
+ registry: deferredToolRegistry,
9380
+ logger,
9381
+ toolListAccessor: () => {
9382
+ if (!agentToolsRef.current) throw new Error("tool_search invoked before agent context was wired");
9383
+ return agentToolsRef.current;
9384
+ }
9380
9385
  }) : null;
9381
9386
  const cliTools = buildCliToolList({
9382
9387
  toolSearchTool,
@@ -9662,7 +9667,7 @@ function CliApp() {
9662
9667
  previousMessages: previousMessages.length > 0 ? previousMessages : void 0,
9663
9668
  signal: abortController.signal,
9664
9669
  parallelExecution: cliConfig.preferences.enableParallelToolExecution === true,
9665
- isReadOnlyTool,
9670
+ isReadOnlyTool: (toolName) => state.toolCatalog.isReadOnly(toolName),
9666
9671
  onContextLimit: createReactiveCompactionHandler(state.agent, session)
9667
9672
  });
9668
9673
  } finally {
@@ -9797,6 +9802,7 @@ function CliApp() {
9797
9802
  additionalDirectories: state.additionalDirectories,
9798
9803
  featureRegistry: state.featureRegistry,
9799
9804
  backgroundManager: state.backgroundManager,
9805
+ toolCatalog: state.toolCatalog,
9800
9806
  todoStore: todoStoreRef.current,
9801
9807
  decisionStore: decisionStoreRef.current,
9802
9808
  blockerStore: blockerStoreRef.current,
@@ -9864,7 +9870,7 @@ function CliApp() {
9864
9870
  previousMessages: previousMessages.length > 0 ? previousMessages : void 0,
9865
9871
  signal: abortController.signal,
9866
9872
  parallelExecution: cliConfig.preferences.enableParallelToolExecution === true,
9867
- isReadOnlyTool
9873
+ isReadOnlyTool: (toolName) => state.toolCatalog.isReadOnly(toolName)
9868
9874
  });
9869
9875
  const successfulToolCalls = result.steps.filter((s) => s.type === "observation").length;
9870
9876
  const currentSession = useCliStore.getState().session;
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  //#region package.json
3
- var version = "3.0.0";
3
+ var version = "3.2.0";
4
4
  //#endregion
5
5
  export { version as t };