@bike4mind/cli 0.10.0 → 0.10.1

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.
@@ -1002,6 +1002,8 @@ let ApiKeyScope = /* @__PURE__ */ function(ApiKeyScope) {
1002
1002
  * radius of a sprite-spawning credential, not a billable AI key. */
1003
1003
  ApiKeyScope["CC_BRIDGE"] = "cc-bridge:connect";
1004
1004
  ApiKeyScope["ADMIN"] = "admin:*";
1005
+ ApiKeyScope["MARKETING_REPORTS_READ"] = "marketing-reports:read";
1006
+ ApiKeyScope["MARKETING_REPORTS_WRITE"] = "marketing-reports:write";
1005
1007
  return ApiKeyScope;
1006
1008
  }({});
1007
1009
  /** Valid document types that can be favorited */
@@ -3667,6 +3669,8 @@ z.enum([
3667
3669
  "EnableRapidReplyDefault",
3668
3670
  "EnableLattice",
3669
3671
  "EnableLatticeDefault",
3672
+ "EnableDataLakes",
3673
+ "EnableDataLakesDefault",
3670
3674
  "RapidReplySettings",
3671
3675
  "EnableResearchEngine",
3672
3676
  "EnableResearchEngineDefault",
@@ -5039,6 +5043,25 @@ const settingsMap = {
5039
5043
  group: API_SERVICE_GROUPS.NOTEBOOK.id,
5040
5044
  order: 1
5041
5045
  }),
5046
+ EnableDataLakes: makeBooleanSetting({
5047
+ key: "EnableDataLakes",
5048
+ name: "Enable Data Lakes",
5049
+ defaultValue: false,
5050
+ description: "Server-side gate for the Data Lake capability (bulk folder ingestion). Off by default — turn on to expose the data-lake APIs and wizard.",
5051
+ category: "Experimental",
5052
+ group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
5053
+ order: 88
5054
+ }),
5055
+ EnableDataLakesDefault: makeBooleanSetting({
5056
+ key: "EnableDataLakesDefault",
5057
+ name: "Data Lakes: On by default for users",
5058
+ defaultValue: false,
5059
+ description: "When enabled, Data Lakes is active for users who have never explicitly toggled it.",
5060
+ category: "Experimental",
5061
+ group: API_SERVICE_GROUPS.EXPERIMENTAL.id,
5062
+ order: 89,
5063
+ dependsOn: "EnableDataLakes"
5064
+ }),
5042
5065
  EnableQuestMaster: makeBooleanSetting({
5043
5066
  key: "EnableQuestMaster",
5044
5067
  name: "Enable Quest Master",
@@ -7661,18 +7684,36 @@ z$1.object({
7661
7684
  slug: z$1.string().min(2).max(60).regex(slugRegex, "Slug must be lowercase alphanumeric with hyphens (e.g. \"my-data-lake\")"),
7662
7685
  description: z$1.string().max(2e3).optional(),
7663
7686
  fileTagPrefix: z$1.string().min(2).max(30).refine((s) => s.endsWith(":"), "Tag prefix must end with \":\" (e.g. \"acme:\")"),
7664
- requiredUserTag: z$1.string().min(1).max(100).optional(),
7665
- organizationId: z$1.string().optional()
7687
+ requiredUserTag: z$1.string().min(1).max(100).optional()
7666
7688
  });
7667
7689
  z$1.object({
7668
7690
  name: z$1.string().min(1).max(200).optional(),
7669
7691
  description: z$1.string().max(2e3).optional(),
7670
- requiredUserTag: z$1.string().min(1).max(100).optional(),
7671
- status: z$1.enum(["active", "archived"]).optional()
7692
+ requiredUserTag: z$1.string().min(1).max(100).optional()
7672
7693
  });
7673
7694
  z$1.object({
7674
7695
  organizationId: z$1.string().optional(),
7675
- status: z$1.enum(["active", "archived"]).optional()
7696
+ status: z$1.enum([
7697
+ "draft",
7698
+ "active",
7699
+ "archived",
7700
+ "deleted"
7701
+ ]).optional()
7702
+ });
7703
+ const ConflictResolutionSchema = z$1.enum([
7704
+ "skip",
7705
+ "update",
7706
+ "duplicate"
7707
+ ]);
7708
+ z$1.object({
7709
+ dataLakeId: z$1.string(),
7710
+ totalFiles: z$1.number().positive(),
7711
+ totalSizeBytes: z$1.number().nonnegative(),
7712
+ conflictResolution: ConflictResolutionSchema.optional(),
7713
+ appliedTags: z$1.array(z$1.object({
7714
+ name: z$1.string(),
7715
+ strength: z$1.number()
7716
+ })).optional()
7676
7717
  });
7677
7718
  const BatchPresignedUrlFileItem = z$1.object({
7678
7719
  fileName: z$1.string().min(1),
@@ -7687,7 +7728,13 @@ const BatchPresignedUrlFileItem = z$1.object({
7687
7728
  });
7688
7729
  z$1.object({
7689
7730
  files: z$1.array(BatchPresignedUrlFileItem).min(1).max(100),
7690
- dataLakeSlug: z$1.string().optional()
7731
+ dataLakeSlug: z$1.string().optional(),
7732
+ /**
7733
+ * When uploading into a data lake batch, the batch id so each created FabFile is
7734
+ * correlated to the batch (stamped with batchId) AND appended to the batch
7735
+ * manifest. Without it the pipeline can't track batch progress.
7736
+ */
7737
+ batchId: z$1.string().optional()
7691
7738
  });
7692
7739
  const InferTaxonomyFolderEntry = z$1.object({
7693
7740
  relativePath: z$1.string(),
@@ -7713,7 +7760,9 @@ const SyncDeltaFileEntry = z$1.object({
7713
7760
  });
7714
7761
  z$1.object({
7715
7762
  dataLakeSlug: z$1.string(),
7716
- currentFiles: z$1.array(SyncDeltaFileEntry).min(1).max(1e4)
7763
+ currentFiles: z$1.array(SyncDeltaFileEntry).min(1).max(1e4),
7764
+ /** Per-request dedup policy for files whose content hash already exists. Defaults to 'skip'. */
7765
+ conflictResolution: ConflictResolutionSchema.optional()
7717
7766
  });
7718
7767
  z$1.object({
7719
7768
  dataLakeSlug: z$1.string(),
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as ConfigStore } from "../ConfigStore-DPWN3-0c.mjs";
2
+ import { t as ConfigStore } from "../ConfigStore-aeJGqjKm.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 { a as version, n as compareSemver, r as fetchLatestVersion } from "../updateChecker-C3DYG0Gn.mjs";
2
+ import { a as version, n as compareSemver, r as fetchLatestVersion } from "../updateChecker-CP_jeER9.mjs";
3
3
  import { t as checkRipgrep } from "../ripgrepCheck-BmkyTK2i.mjs";
4
4
  import { execSync } from "child_process";
5
5
  import { constants, existsSync, promises } from "fs";
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as PermissionManager, I as generateCliTools, M as loadContextFiles, N as getApiUrl, O as McpManager, Q as CheckpointStore, S as ApiClient, T as FallbackLlmBackend, W as setWebSocketToolExecutor, X as ReActAgent, Y as isReadOnlyTool, Z as CustomCommandStore, _ as createAgentDelegateTool, b as createSkillTool, d as createFindDefinitionTool, et as SessionStore, f as createTodoStore, g as BackgroundAgentManager, h as createBackgroundAgentTools, m as createCoordinateTaskTool, p as createWriteTodosTool, q as buildSystemPrompt, u as createGetFileStructureTool, v as AgentStore, w as WebSocketConnectionManager, y as SubagentOrchestrator } from "../tools-_X4rUM4L.mjs";
3
- import { n as logger, t as ConfigStore } from "../ConfigStore-DPWN3-0c.mjs";
2
+ import { C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as PermissionManager, I as generateCliTools, M as loadContextFiles, N as getApiUrl, O as McpManager, Q as CheckpointStore, S as ApiClient, T as FallbackLlmBackend, W as setWebSocketToolExecutor, X as ReActAgent, Y as isReadOnlyTool, Z as CustomCommandStore, _ as createAgentDelegateTool, b as createSkillTool, d as createFindDefinitionTool, et as SessionStore, f as createTodoStore, g as BackgroundAgentManager, h as createBackgroundAgentTools, m as createCoordinateTaskTool, p as createWriteTodosTool, q as buildSystemPrompt, u as createGetFileStructureTool, v as AgentStore, w as WebSocketConnectionManager, y as SubagentOrchestrator } from "../tools-RdGu37Lw.mjs";
3
+ import { n as logger, t as ConfigStore } from "../ConfigStore-aeJGqjKm.mjs";
4
4
  import { t as DEFAULT_SANDBOX_CONFIG } from "../types-LyRNHOiS.mjs";
5
5
  import { t as createSandboxRuntime } from "../SandboxRuntimeAdapter-ChGlxSGQ.mjs";
6
6
  import { t as SandboxOrchestrator } from "../SandboxOrchestrator-BoINxbX4.mjs";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as ConfigStore } from "../ConfigStore-DPWN3-0c.mjs";
2
+ import { t as ConfigStore } from "../ConfigStore-aeJGqjKm.mjs";
3
3
  //#region src/commands/mcpCommand.ts
4
4
  /**
5
5
  * External MCP commands (b4m mcp list, b4m mcp add, etc.)
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as version, i as forceCheckForUpdate } from "../updateChecker-C3DYG0Gn.mjs";
2
+ import { a as version, i as forceCheckForUpdate } from "../updateChecker-CP_jeER9.mjs";
3
3
  import { t as checkRipgrep } from "../ripgrepCheck-BmkyTK2i.mjs";
4
4
  import { execSync } from "child_process";
5
5
  //#region src/commands/updateCommand.ts
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { $ as CommandHistoryStore, A as formatStep, B as DEFAULT_RETRY_CONFIG, C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as PermissionManager, G as OllamaBackend, H as clearFeatureModuleTools, I as generateCliTools, J as buildSkillsPromptSection, K as getPlanModeFilePath, L as ALWAYS_DENIED_FOR_AGENTS, M as loadContextFiles, N as getApiUrl, O as McpManager, P as getEnvironmentName, Q as CheckpointStore, R as DEFAULT_AGENT_MODEL, S as ApiClient, T as FallbackLlmBackend, U as registerFeatureModuleTools, V as DEFAULT_THOROUGHNESS, W as setWebSocketToolExecutor, X as ReActAgent, Y as isReadOnlyTool, Z as CustomCommandStore, _ as createAgentDelegateTool, a as createBlockerTools, at as mergeCommands, b as createSkillTool, c as createDecisionStore, ct as warmFileCache, d as createFindDefinitionTool, et as SessionStore, f as createTodoStore, g as BackgroundAgentManager, h as createBackgroundAgentTools, i as createBlockerStore, it as searchCommands, j as extractCompactInstructions, k as substituteArguments, l as formatDecisionsOutput, m as createCoordinateTaskTool, n as createReviewGateTool, nt as hasFileReferences, o as formatBlockersOutput, ot as formatFileSize, p as createWriteTodosTool, q as buildSystemPrompt, r as formatReviewGatesOutput, rt as processFileReferences, s as createDecisionLogTool, st as searchFiles, t as createReviewGateStore, tt as OAuthClient, u as createGetFileStructureTool, v as AgentStore, w as WebSocketConnectionManager, x as parseAgentConfig, y as SubagentOrchestrator, z as DEFAULT_MAX_ITERATIONS } from "./tools-_X4rUM4L.mjs";
2
+ import { $ as CommandHistoryStore, A as formatStep, B as DEFAULT_RETRY_CONFIG, C as WebSocketToolExecutor, D as ServerLlmBackend, E as WebSocketLlmBackend, F as PermissionManager, G as OllamaBackend, H as clearFeatureModuleTools, I as generateCliTools, J as buildSkillsPromptSection, K as getPlanModeFilePath, L as ALWAYS_DENIED_FOR_AGENTS, M as loadContextFiles, N as getApiUrl, O as McpManager, P as getEnvironmentName, Q as CheckpointStore, R as DEFAULT_AGENT_MODEL, S as ApiClient, T as FallbackLlmBackend, U as registerFeatureModuleTools, V as DEFAULT_THOROUGHNESS, W as setWebSocketToolExecutor, X as ReActAgent, Y as isReadOnlyTool, Z as CustomCommandStore, _ as createAgentDelegateTool, a as createBlockerTools, at as mergeCommands, b as createSkillTool, c as createDecisionStore, ct as warmFileCache, d as createFindDefinitionTool, et as SessionStore, f as createTodoStore, g as BackgroundAgentManager, h as createBackgroundAgentTools, i as createBlockerStore, it as searchCommands, j as extractCompactInstructions, k as substituteArguments, l as formatDecisionsOutput, m as createCoordinateTaskTool, n as createReviewGateTool, nt as hasFileReferences, o as formatBlockersOutput, ot as formatFileSize, p as createWriteTodosTool, q as buildSystemPrompt, r as formatReviewGatesOutput, rt as processFileReferences, s as createDecisionLogTool, st as searchFiles, t as createReviewGateStore, tt as OAuthClient, u as createGetFileStructureTool, v as AgentStore, w as WebSocketConnectionManager, x as parseAgentConfig, y as SubagentOrchestrator, z as DEFAULT_MAX_ITERATIONS } from "./tools-RdGu37Lw.mjs";
3
3
  import { n as useCliStore, t as selectActiveBackgroundAgents } from "./store-DV5s-qni.mjs";
4
- import { Xt as validateNotebookPath$1, Yt as validateJupyterKernelName, g as CREDIT_DEDUCT_TRANSACTION_TYPES, n as logger, t as ConfigStore, v as ChatModels } from "./ConfigStore-DPWN3-0c.mjs";
5
- import { a as version, t as checkForUpdate } from "./updateChecker-C3DYG0Gn.mjs";
4
+ import { Xt as validateNotebookPath$1, Yt as validateJupyterKernelName, g as CREDIT_DEDUCT_TRANSACTION_TYPES, n as logger, t as ConfigStore, v as ChatModels } from "./ConfigStore-aeJGqjKm.mjs";
5
+ import { a as version, t as checkForUpdate } from "./updateChecker-CP_jeER9.mjs";
6
6
  import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
7
7
  import { Box, Static, Text, render, useApp, useInput, usePaste, useStdout } from "ink";
8
8
  import { execSync } from "child_process";
@@ -3761,6 +3761,196 @@ Unlike agent_delegate (which uses pre-defined agents), this tool lets you compos
3761
3761
  };
3762
3762
  }
3763
3763
  //#endregion
3764
+ //#region src/tools/deferredToolRegistry.ts
3765
+ /**
3766
+ * Registry of tool schemas that are NOT loaded into the model's initial tool
3767
+ * list. The model sees only the names (via the system prompt directory) and
3768
+ * must call the `tool_search` meta-tool to load schemas on demand.
3769
+ *
3770
+ * This mirrors Claude Code's deferred-tool pattern. The win is large for
3771
+ * heavy MCP integrations (e.g. 41 GitHub MCP tools at ~250-350 tokens of
3772
+ * JSONSchema each = ~10-15k tokens per turn that's now ~1-1.5k of names).
3773
+ */
3774
+ var DeferredToolRegistry = class {
3775
+ constructor() {
3776
+ this.byName = /* @__PURE__ */ new Map();
3777
+ }
3778
+ /** Replace registry contents with the supplied tools. Idempotent. */
3779
+ register(tools) {
3780
+ this.byName.clear();
3781
+ for (const tool of tools) this.byName.set(tool.toolSchema.name, tool);
3782
+ logger.debug(`[DeferredToolRegistry] Registered ${tools.length} deferred tool(s)`);
3783
+ }
3784
+ clear() {
3785
+ this.byName.clear();
3786
+ }
3787
+ size() {
3788
+ return this.byName.size;
3789
+ }
3790
+ has(name) {
3791
+ return this.byName.has(name);
3792
+ }
3793
+ get(name) {
3794
+ return this.byName.get(name);
3795
+ }
3796
+ getAll() {
3797
+ return Array.from(this.byName.values());
3798
+ }
3799
+ /** Return tools whose names appear in the supplied list, in input order. */
3800
+ getByNames(names) {
3801
+ const found = [];
3802
+ for (const name of names) {
3803
+ const tool = this.byName.get(name);
3804
+ if (tool) found.push(tool);
3805
+ }
3806
+ return found;
3807
+ }
3808
+ /**
3809
+ * Rank-search deferred tools by query terms. Name matches outrank
3810
+ * description matches; exact substring on name wins ties.
3811
+ */
3812
+ searchByKeywords(query, maxResults) {
3813
+ const terms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 0);
3814
+ if (terms.length === 0) return [];
3815
+ const scored = [];
3816
+ for (const tool of this.byName.values()) {
3817
+ const name = tool.toolSchema.name.toLowerCase();
3818
+ const desc = (tool.toolSchema.description || "").toLowerCase();
3819
+ let score = 0;
3820
+ for (const term of terms) {
3821
+ if (name.includes(term)) score += 10;
3822
+ if (desc.includes(term)) score += 1;
3823
+ }
3824
+ if (score > 0) scored.push({
3825
+ tool,
3826
+ score
3827
+ });
3828
+ }
3829
+ scored.sort((a, b) => b.score - a.score);
3830
+ return scored.slice(0, maxResults).map((s) => s.tool);
3831
+ }
3832
+ /** Return the directory entries used to render the system-prompt reminder. */
3833
+ getDirectoryNames() {
3834
+ return Array.from(this.byName.keys()).sort();
3835
+ }
3836
+ };
3837
+ const deferredToolRegistry = new DeferredToolRegistry();
3838
+ //#endregion
3839
+ //#region src/tools/toolSearchTool.ts
3840
+ /**
3841
+ * Default number of tools returned for a keyword search. Matches Claude
3842
+ * Code's ToolSearch convention. 5 keeps the response payload small while
3843
+ * surfacing enough alternatives for the model to refine its query.
3844
+ */
3845
+ const DEFAULT_MAX_RESULTS = 5;
3846
+ /**
3847
+ * Runtime validation for tool_search params. The LLM produces these
3848
+ * values, so we validate at this boundary rather than trusting the
3849
+ * shape. Coerces `max_results` from string→number for models that emit
3850
+ * numeric-looking strings.
3851
+ */
3852
+ const ToolSearchParamsSchema = z.object({
3853
+ query: z.string().min(1, "query must be a non-empty string"),
3854
+ max_results: z.coerce.number().int().min(1).max(20).optional()
3855
+ });
3856
+ /**
3857
+ * Parse the query string. Two forms:
3858
+ * - `select:name1,name2,...` — exact-name selection
3859
+ * - free text — keyword search across name + description
3860
+ */
3861
+ function parseQuery(query) {
3862
+ const trimmed = query.trim();
3863
+ const selectMatch = trimmed.match(/^select:(.+)$/i);
3864
+ if (selectMatch) return {
3865
+ mode: "select",
3866
+ names: selectMatch[1].split(",").map((n) => n.trim()).filter((n) => n.length > 0)
3867
+ };
3868
+ return {
3869
+ mode: "search",
3870
+ text: trimmed
3871
+ };
3872
+ }
3873
+ /**
3874
+ * Format the loaded-tools response. Mirrors Claude Code's convention:
3875
+ * one <function>{...}</function> line per matched tool. The model has
3876
+ * already seen this format in its tool-registration system messages, so
3877
+ * it parses without additional explanation.
3878
+ *
3879
+ * Note: the schemas are *also* injected into context.tools by the caller,
3880
+ * so on the next iteration the model gets them as native tool definitions.
3881
+ * The text response here is for in-turn awareness and audit trail.
3882
+ */
3883
+ function renderToolsBlock(tools) {
3884
+ if (tools.length === 0) return "";
3885
+ return `<functions>\n${tools.map((tool) => {
3886
+ const schema = {
3887
+ description: tool.toolSchema.description,
3888
+ name: tool.toolSchema.name,
3889
+ parameters: tool.toolSchema.parameters
3890
+ };
3891
+ return `<function>${JSON.stringify(schema)}</function>`;
3892
+ }).join("\n")}\n</functions>`;
3893
+ }
3894
+ /**
3895
+ * Build the tool_search meta-tool. The returned tool has a closure over
3896
+ * the supplied `toolListAccessor`, which it uses to push newly-resolved
3897
+ * tool schemas into the live agent context.
3898
+ *
3899
+ * Idempotent: re-loading a tool that's already in the context is a no-op.
3900
+ */
3901
+ function createToolSearchTool(toolListAccessor) {
3902
+ return {
3903
+ toolSchema: {
3904
+ name: "tool_search",
3905
+ 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.",
3906
+ parameters: {
3907
+ type: "object",
3908
+ properties: {
3909
+ query: {
3910
+ type: "string",
3911
+ description: "Either 'select:<comma-separated names>' to fetch specific tools, or free-text keywords (e.g. 'github pull request') to rank-search deferred tools."
3912
+ },
3913
+ max_results: {
3914
+ type: "number",
3915
+ description: `Maximum number of tools to return for keyword search. Defaults to ${DEFAULT_MAX_RESULTS}. Ignored for 'select:' queries.`
3916
+ }
3917
+ },
3918
+ required: ["query"]
3919
+ }
3920
+ },
3921
+ toolFn: async (params) => {
3922
+ const parsedParams = ToolSearchParamsSchema.safeParse(params ?? {});
3923
+ if (!parsedParams.success) {
3924
+ const issue = parsedParams.error.issues[0];
3925
+ return `tool_search: invalid parameters — ${issue.path.join(".") || "params"}: ${issue.message}`;
3926
+ }
3927
+ const { query, max_results } = parsedParams.data;
3928
+ const parsed = parseQuery(query);
3929
+ let matched;
3930
+ let unmatched = [];
3931
+ if (parsed.mode === "select") {
3932
+ matched = deferredToolRegistry.getByNames(parsed.names);
3933
+ const foundNames = new Set(matched.map((t) => t.toolSchema.name));
3934
+ unmatched = parsed.names.filter((n) => !foundNames.has(n));
3935
+ } else {
3936
+ const max = max_results ?? DEFAULT_MAX_RESULTS;
3937
+ matched = deferredToolRegistry.searchByKeywords(parsed.text, max);
3938
+ }
3939
+ 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}".`;
3940
+ const liveTools = toolListAccessor();
3941
+ const liveNames = new Set(liveTools.map((t) => t.toolSchema.name));
3942
+ let added = 0;
3943
+ for (const tool of matched) if (!liveNames.has(tool.toolSchema.name)) {
3944
+ liveTools.push(tool);
3945
+ added++;
3946
+ }
3947
+ logger.debug(`[tool_search] query="${query}" matched=${matched.length} added=${added} alreadyLoaded=${matched.length - added}`);
3948
+ const block = renderToolsBlock(matched);
3949
+ 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}`;
3950
+ }
3951
+ };
3952
+ }
3953
+ //#endregion
3764
3954
  //#region src/features/FeatureModuleRegistry.ts
3765
3955
  /**
3766
3956
  * Manages the lifecycle of opt-in CLI feature modules.
@@ -5976,10 +6166,23 @@ function CliApp() {
5976
6166
  loadContextFiles(agentProjectDir)
5977
6167
  ]);
5978
6168
  const mcpTools = mcpManager.getTools();
6169
+ const deferredB4mToolNames = new Set([
6170
+ "math_evaluate",
6171
+ "dice_roll",
6172
+ "current_datetime",
6173
+ "recent_changes",
6174
+ "prompt_enhancement"
6175
+ ]);
6176
+ const deferredB4mTools = b4mTools.filter((t) => deferredB4mToolNames.has(t.toolSchema.name));
6177
+ const loadedB4mTools = b4mTools.filter((t) => !deferredB4mToolNames.has(t.toolSchema.name));
6178
+ deferredToolRegistry.register([...mcpTools, ...deferredB4mTools]);
5979
6179
  if (mcpTools.length > 0) {
5980
6180
  const serverSummaries = mcpManager.getToolCount().map((s) => `${s.serverName} (${s.count})`).join(", ");
5981
- startupLog.push(`🛠️ Loaded ${b4mTools.length} B4M + ${mcpTools.length} MCP tool(s): ${serverSummaries}`);
5982
- } else startupLog.push(`🛠️ Loaded ${b4mTools.length} B4M tool(s), no MCP tools`);
6181
+ startupLog.push(`🛠️ Loaded ${loadedB4mTools.length} B4M + ${mcpTools.length} MCP tool(s, ${deferredB4mTools.length + mcpTools.length} deferred): ${serverSummaries}`);
6182
+ } else {
6183
+ const suffix = deferredB4mTools.length > 0 ? ` (${deferredB4mTools.length} deferred)` : "";
6184
+ startupLog.push(`🛠️ Loaded ${loadedB4mTools.length} B4M tool(s)${suffix}, no MCP tools`);
6185
+ }
5983
6186
  const agentSummary = agentStore.getSummary();
5984
6187
  startupLog.push(`🤖 Loaded ${agentSummary.total} agent(s): ${agentSummary.builtin} built-in, ${agentSummary.global} global, ${agentSummary.project} project`);
5985
6188
  const orchestrator = new SubagentOrchestrator({
@@ -6053,9 +6256,14 @@ function CliApp() {
6053
6256
  cliTools.push(coordinateTaskTool);
6054
6257
  }
6055
6258
  const featureTools = featureRegistry.getAllTools();
6259
+ const agentToolsRef = { current: null };
6260
+ const toolSearchTool = deferredToolRegistry.size() > 0 ? createToolSearchTool(() => {
6261
+ if (!agentToolsRef.current) throw new Error("tool_search invoked before agent context was wired");
6262
+ return agentToolsRef.current;
6263
+ }) : null;
6056
6264
  const allTools = [
6057
- ...b4mTools,
6058
- ...mcpTools,
6265
+ ...loadedB4mTools,
6266
+ ...toolSearchTool ? [toolSearchTool] : [],
6059
6267
  ...cliTools,
6060
6268
  ...featureTools
6061
6269
  ];
@@ -6070,7 +6278,7 @@ function CliApp() {
6070
6278
  const moduleNames = featureRegistry.getModuleNames().join(", ");
6071
6279
  startupLog.push(`🏰 Feature modules: ${moduleNames} (${featureTools.length} tools)`);
6072
6280
  }
6073
- logger.debug(`Total tools available to agent: ${allTools.length} (${b4mTools.length} B4M + ${mcpTools.length} MCP + ${cliTools.length} CLI + ${featureTools.length} feature)`);
6281
+ logger.debug(`Total tools available to agent: ${allTools.length} (${loadedB4mTools.length} B4M loaded + ${cliTools.length} CLI + ${featureTools.length} feature + ${toolSearchTool ? 1 : 0} tool_search, ${deferredB4mTools.length} B4M + ${mcpTools.length} MCP deferred)`);
6074
6282
  if (contextResult.globalContext) startupLog.push(`📄 Global context: ${contextResult.globalContext.filename}`);
6075
6283
  if (contextResult.projectContext) startupLog.push(`📄 Project context: ${contextResult.projectContext.filename}`);
6076
6284
  for (const error of contextResult.errors) startupLog.push(`⚠️ Context file error: ${error}`);
@@ -6084,7 +6292,8 @@ function CliApp() {
6084
6292
  enableDynamicAgentCreation: config.preferences.enableDynamicAgentCreation === true,
6085
6293
  additionalDirectories,
6086
6294
  featureModulePrompts: featureModulePrompts || void 0,
6087
- planModeFilePath: mode === "plan" ? getPlanModeFilePath(newSession.id) : void 0
6295
+ planModeFilePath: mode === "plan" ? getPlanModeFilePath(newSession.id) : void 0,
6296
+ deferredToolNames: deferredToolRegistry.getDirectoryNames()
6088
6297
  });
6089
6298
  const cliSystemPrompt = buildPromptForMode(useCliStore.getState().interactionMode);
6090
6299
  const maxIterations = config.preferences.maxIterations === null ? 999999 : config.preferences.maxIterations;
@@ -6097,16 +6306,17 @@ function CliApp() {
6097
6306
  maxIterations,
6098
6307
  maxTokens: config.preferences.maxTokens,
6099
6308
  temperature: config.preferences.temperature,
6100
- systemPrompt: cliSystemPrompt
6309
+ systemPrompt: cliSystemPrompt,
6310
+ unknownToolResolver: async (toolName) => deferredToolRegistry.get(toolName) ?? null
6101
6311
  });
6312
+ agentToolsRef.current = agent.getTools();
6102
6313
  agentContext.currentAgent = agent;
6103
- const agentInternalContext = agent.context;
6104
6314
  let lastInteractionMode = useCliStore.getState().interactionMode;
6105
6315
  useCliStore.subscribe((s) => {
6106
6316
  if (s.interactionMode === lastInteractionMode) return;
6107
6317
  lastInteractionMode = s.interactionMode;
6108
6318
  if (agentContext.currentAgent !== agent) return;
6109
- agentInternalContext.systemPrompt = buildPromptForMode(s.interactionMode);
6319
+ agent.setSystemPrompt(buildPromptForMode(s.interactionMode));
6110
6320
  });
6111
6321
  agent.observationQueue = agentContext.observationQueue;
6112
6322
  const stepHandler = (step) => {
@@ -6562,7 +6772,8 @@ function CliApp() {
6562
6772
  customCommands: state.customCommandStore.getAllCommands(),
6563
6773
  enableSkillTool: config?.preferences.enableSkillTool !== false,
6564
6774
  additionalDirectories: state.additionalDirectories,
6565
- featureModulePrompts: state.featureRegistry?.getSystemPromptSections() || void 0
6775
+ featureModulePrompts: state.featureRegistry?.getSystemPromptSections() || void 0,
6776
+ deferredToolNames: deferredToolRegistry.getDirectoryNames()
6566
6777
  });
6567
6778
  if (tokenCounter.countSessionTokens(activeSession, systemPrompt).totalTokens >= threshold) {
6568
6779
  console.log("\n⚠️ Context window 80% full. Auto-compacting...\n");
@@ -7753,7 +7964,7 @@ Multi-line Input:
7753
7964
  const skillsTokens = skillsSection ? tokenCounter.countTokens(skillsSection) : 0;
7754
7965
  const agentDirectoryTokens = state.agentStore ? tokenCounter.countTokens(state.agentStore.getDirectoryContext()) : 0;
7755
7966
  const mcpTools = state.mcpManager?.getTools() || [];
7756
- const mcpToolsTokens = tokenCounter.countToolSchemaTokens(mcpTools);
7967
+ const deferredNames = deferredToolRegistry.getDirectoryNames();
7757
7968
  const mcpToolCount = state.mcpManager?.getToolCount() || [];
7758
7969
  const systemPrompt = buildSystemPrompt(variantForCount, {
7759
7970
  contextContent: state.contextContent,
@@ -7761,10 +7972,13 @@ Multi-line Input:
7761
7972
  customCommands: commands,
7762
7973
  enableSkillTool: state.config?.preferences.enableSkillTool !== false,
7763
7974
  additionalDirectories: state.additionalDirectories,
7764
- featureModulePrompts: state.featureRegistry?.getSystemPromptSections() || void 0
7975
+ featureModulePrompts: state.featureRegistry?.getSystemPromptSections() || void 0,
7976
+ deferredToolNames: deferredNames
7765
7977
  });
7766
7978
  const usage = tokenCounter.countSessionTokens(state.session, systemPrompt);
7767
- const totalWithTools = usage.totalTokens + mcpToolsTokens;
7979
+ const agentTools = state.agent?.getTools() ?? [];
7980
+ const agentToolsTokens = tokenCounter.countToolSchemaTokens(agentTools);
7981
+ const totalWithTools = usage.totalTokens + agentToolsTokens;
7768
7982
  const usagePercent = totalWithTools / contextWindow * 100;
7769
7983
  const BAR_WIDTH = 40;
7770
7984
  const filledWidth = Math.min(Math.round(usagePercent / 100 * BAR_WIDTH), BAR_WIDTH);
@@ -7781,10 +7995,14 @@ Multi-line Input:
7781
7995
  console.log(` Agent Directory: ${agentDirectoryTokens.toLocaleString()} tokens (${agentCount} agents)`);
7782
7996
  }
7783
7997
  if (mcpTools.length > 0) {
7784
- console.log("\nMCP Tools:");
7785
- console.log(` Total: ${mcpToolsTokens.toLocaleString()} tokens (${mcpTools.length} tools)`);
7998
+ console.log("\nMCP Tools (deferred):");
7999
+ console.log(` Schemas: load on demand via tool_search (${mcpTools.length} tools available)`);
7786
8000
  for (const { serverName, count } of mcpToolCount) console.log(` ${serverName}: ${count} tools`);
7787
8001
  }
8002
+ if (agentTools.length > 0) {
8003
+ console.log("\nLoaded Tool Schemas:");
8004
+ console.log(` Schemas: ${agentToolsTokens.toLocaleString()} tokens (${agentTools.length} tools active)`);
8005
+ }
7788
8006
  console.log("\nConversation:");
7789
8007
  console.log(` Messages: ${usage.messageTokens.toLocaleString()} tokens (${state.session.messages.length} messages)`);
7790
8008
  if (usagePercent >= 80) {
@@ -8320,14 +8538,13 @@ Multi-line Input:
8320
8538
  const newToolNames = newFeatureRegistry.getAllToolNames();
8321
8539
  if (newToolNames.length > 0) registerFeatureModuleTools(newToolNames);
8322
8540
  if (state.wsManager && newFeatureRegistry.hasModules) newFeatureRegistry.registerAllWsHandlers(state.wsManager);
8323
- const agentContext = state.agent.context;
8324
8541
  const oldFeatureToolNames = new Set(state.featureRegistry?.getAllToolNames() ?? []);
8325
- const baseTools = agentContext.tools.filter((t) => !oldFeatureToolNames.has(t.toolSchema.name));
8542
+ const baseTools = state.agent.getTools().filter((t) => !oldFeatureToolNames.has(t.toolSchema.name));
8326
8543
  const newFeatureTools = newFeatureRegistry.getAllTools();
8327
- agentContext.tools = [...baseTools, ...newFeatureTools];
8544
+ state.agent.setTools([...baseTools, ...newFeatureTools]);
8328
8545
  const newFeaturePrompts = newFeatureRegistry.getSystemPromptSections();
8329
8546
  const planFilePathForRebuild = useCliStore.getState().interactionMode === "plan" && state.session ? getPlanModeFilePath(state.session.id) : void 0;
8330
- agentContext.systemPrompt = buildSystemPrompt(updatedConfig.preferences.promptVariant ?? "current", {
8547
+ state.agent.setSystemPrompt(buildSystemPrompt(updatedConfig.preferences.promptVariant ?? "current", {
8331
8548
  contextContent: state.contextContent,
8332
8549
  agentStore: state.agentStore || void 0,
8333
8550
  customCommands: state.customCommandStore.getAllCommands(),
@@ -8335,8 +8552,9 @@ Multi-line Input:
8335
8552
  enableDynamicAgentCreation: updatedConfig.preferences.enableDynamicAgentCreation === true,
8336
8553
  additionalDirectories: state.additionalDirectories,
8337
8554
  featureModulePrompts: newFeaturePrompts || void 0,
8338
- planModeFilePath: planFilePathForRebuild
8339
- });
8555
+ planModeFilePath: planFilePathForRebuild,
8556
+ deferredToolNames: deferredToolRegistry.getDirectoryNames()
8557
+ }));
8340
8558
  const moduleNames = newFeatureRegistry.getModuleNames();
8341
8559
  if (moduleNames.length > 0) console.error(`\n\x1b[36m🏰 Feature modules hot-reloaded: ${moduleNames.join(", ")}\x1b[0m`);
8342
8560
  else console.error(`\n\x1b[36m🏰 Feature modules disabled\x1b[0m`);
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { $ as PermissionDeniedError, $t as isNearLimit, A as GEMINI_IMAGE_MODELS, At as VideoGenerationUsageTransaction, B as InternalServerError, Bt as isGPTImage2Model, C as ElabsEvents, Ct as TooManyRequestsError, D as FileEvents, Dt as UnauthorizedError, E as FeedbackEvents, Et as UiNavigationEvents, F as HttpStatus, Ft as dayjsConfig_default, G as MiscEvents, Gt as resolveNavigationIntents, H as InviteType, Ht as isModelDeprecated, I as ImageEditUsageTransaction, It as getAccessibleDataLakes, J as NO_TEMPERATURE_MODELS, Jt as settingsMap, K as ModalEvents, Kt as sanitizeTelemetryError, L as ImageGenerationUsageTransaction, Lt as getDataLakeTags, M as GenericCreditAddTransaction, Mt as VoyageAIEmbeddingModel, N as GenericCreditDeductTransaction, Nt as XAI_IMAGE_MODELS, O as ForbiddenError, Ot as UnprocessableEntityError, P as HTTPError, Pt as b4mLLMTools, Q as Permission, Qt as extractSnippetMeta, R as ImageModels, Rt as getMcpProviderMetadata, S as DashboardParamsSchema, St as TextGenerationUsageTransaction, T as FavoriteDocumentType, Tt as TransferCreditTransaction, U as KnowledgeType, Ut as isZodError, V as InviteEvents, Vt as isGPTImageModel, W as LLMEvents, Wt as obfuscateApiKey, X as OpenAIEmbeddingModel, Y as NotFoundError, Z as OpenAIImageGenerationInput, Zt as buildRateLimitLogEntry, _ as ChatCompletionCreateInputSchema, _t as SpeechToTextUsageTransaction, a as ApiKeyEvents, at as QuestMasterParamsSchema, b as CompletionApiUsageTransaction, bt as TagType, c as AppFileEvents, ct as ReceivedCreditTransaction, d as BEDROCK_NO_PROMPT_CACHING_MODELS, dt as ResearchModeParamsSchema, en as parseRateLimitHeaders, et as ProfileEvents, f as BFL_IMAGE_MODELS, ft as ResearchTaskExecutionType, gt as SpeechToTextModels, h as BedrockEmbeddingModel, ht as SessionEvents, i as AiEvents, it as PurchaseTransaction, j as GenerateImageToolCallSchema, jt as VideoModels, k as FriendshipEvents, kt as VIDEO_SIZE_CONSTRAINTS, l as ArtifactTypeSchema, lt as RechartsChartTypeList, m as BadRequestError, mt as ResearchTaskType, n as logger, nt as PromptIntentSchema, o as ApiKeyScope, ot as REASONING_SUPPORTED_MODELS, p as BFL_SAFETY_TOLERANCE, pt as ResearchTaskPeriodicFrequencyType, q as ModelBackend, qt as secureParameters, r as ALERT_THRESHOLDS, rt as PromptMetaZodSchema, s as ApiKeyType, st as RealtimeVoiceUsageTransaction, t as ConfigStore, tn as CollectionType, tt as ProjectEvents, u as AuthEvents, ut as RegInviteEvents, v as ChatModels, vt as SubscriptionCreditTransaction, w as FIXED_TEMPERATURE_MODELS, wt as ToolUsageTransaction, x as CorruptedFileError, xt as TaskScheduleHandler, y as ClaudeArtifactMimeTypes, yt as SupportedFabFileMimeTypes, z as InboxEvents, zt as getViewById } from "./ConfigStore-DPWN3-0c.mjs";
2
+ import { $ as PermissionDeniedError, $t as isNearLimit, A as GEMINI_IMAGE_MODELS, At as VideoGenerationUsageTransaction, B as InternalServerError, Bt as isGPTImage2Model, C as ElabsEvents, Ct as TooManyRequestsError, D as FileEvents, Dt as UnauthorizedError, E as FeedbackEvents, Et as UiNavigationEvents, F as HttpStatus, Ft as dayjsConfig_default, G as MiscEvents, Gt as resolveNavigationIntents, H as InviteType, Ht as isModelDeprecated, I as ImageEditUsageTransaction, It as getAccessibleDataLakes, J as NO_TEMPERATURE_MODELS, Jt as settingsMap, K as ModalEvents, Kt as sanitizeTelemetryError, L as ImageGenerationUsageTransaction, Lt as getDataLakeTags, M as GenericCreditAddTransaction, Mt as VoyageAIEmbeddingModel, N as GenericCreditDeductTransaction, Nt as XAI_IMAGE_MODELS, O as ForbiddenError, Ot as UnprocessableEntityError, P as HTTPError, Pt as b4mLLMTools, Q as Permission, Qt as extractSnippetMeta, R as ImageModels, Rt as getMcpProviderMetadata, S as DashboardParamsSchema, St as TextGenerationUsageTransaction, T as FavoriteDocumentType, Tt as TransferCreditTransaction, U as KnowledgeType, Ut as isZodError, V as InviteEvents, Vt as isGPTImageModel, W as LLMEvents, Wt as obfuscateApiKey, X as OpenAIEmbeddingModel, Y as NotFoundError, Z as OpenAIImageGenerationInput, Zt as buildRateLimitLogEntry, _ as ChatCompletionCreateInputSchema, _t as SpeechToTextUsageTransaction, a as ApiKeyEvents, at as QuestMasterParamsSchema, b as CompletionApiUsageTransaction, bt as TagType, c as AppFileEvents, ct as ReceivedCreditTransaction, d as BEDROCK_NO_PROMPT_CACHING_MODELS, dt as ResearchModeParamsSchema, en as parseRateLimitHeaders, et as ProfileEvents, f as BFL_IMAGE_MODELS, ft as ResearchTaskExecutionType, gt as SpeechToTextModels, h as BedrockEmbeddingModel, ht as SessionEvents, i as AiEvents, it as PurchaseTransaction, j as GenerateImageToolCallSchema, jt as VideoModels, k as FriendshipEvents, kt as VIDEO_SIZE_CONSTRAINTS, l as ArtifactTypeSchema, lt as RechartsChartTypeList, m as BadRequestError, mt as ResearchTaskType, n as logger, nt as PromptIntentSchema, o as ApiKeyScope, ot as REASONING_SUPPORTED_MODELS, p as BFL_SAFETY_TOLERANCE, pt as ResearchTaskPeriodicFrequencyType, q as ModelBackend, qt as secureParameters, r as ALERT_THRESHOLDS, rt as PromptMetaZodSchema, s as ApiKeyType, st as RealtimeVoiceUsageTransaction, t as ConfigStore, tn as CollectionType, tt as ProjectEvents, u as AuthEvents, ut as RegInviteEvents, v as ChatModels, vt as SubscriptionCreditTransaction, w as FIXED_TEMPERATURE_MODELS, wt as ToolUsageTransaction, x as CorruptedFileError, xt as TaskScheduleHandler, y as ClaudeArtifactMimeTypes, yt as SupportedFabFileMimeTypes, z as InboxEvents, zt as getViewById } from "./ConfigStore-aeJGqjKm.mjs";
3
3
  import { a as isUserLockedOut, c as userCanDisableMFA, d as userRequiresMFA, f as verifyBackupCode, i as getLockoutTimeRemaining, l as userEligibleForMFA, n as generateBackupCodes, o as recordFailedAttempt, p as verifyTOTPToken, r as generateTOTPSetup, s as shouldResetFailedAttempts, t as clearFailedAttempts, u as userHasMFAConfigured } from "./utils-PpNti-tY.mjs";
4
4
  import { n as isPathAllowed, t as assertPathAllowed } from "./pathValidation-D8tjkQXE-1HwvsuYT.mjs";
5
5
  import { execFile, execFileSync, spawn } from "child_process";
@@ -1945,6 +1945,35 @@ var ReActAgent = class extends EventEmitter {
1945
1945
  };
1946
1946
  }
1947
1947
  /**
1948
+ * Return the live tools array used by this agent. Mutations (push/splice)
1949
+ * are reflected in subsequent ReAct iterations because the array is read
1950
+ * each turn at the top of the loop. Intended for hosts that load tool
1951
+ * schemas lazily (see `unknownToolResolver` and the CLI's tool_search
1952
+ * meta-tool). Prefer this over reaching into `agent.context.tools` via a
1953
+ * type cast.
1954
+ */
1955
+ getTools() {
1956
+ return this.context.tools;
1957
+ }
1958
+ /**
1959
+ * Replace the live tools array. Used by hosts that need to swap the
1960
+ * tool set wholesale (e.g. hot-reload of feature module tools). Prefer
1961
+ * `getTools()` + array mutation for incremental additions — replacing
1962
+ * the reference invalidates any closures that captured the previous
1963
+ * array (e.g. `createToolSearchTool`'s `toolListAccessor`).
1964
+ */
1965
+ setTools(tools) {
1966
+ this.context.tools = tools;
1967
+ }
1968
+ /**
1969
+ * Update the system prompt. Used to hot-swap prompts when the user
1970
+ * cycles interaction modes (e.g. into plan mode) or when feature
1971
+ * modules reload.
1972
+ */
1973
+ setSystemPrompt(systemPrompt) {
1974
+ this.context.systemPrompt = systemPrompt;
1975
+ }
1976
+ /**
1948
1977
  * Run the agent to completion
1949
1978
  *
1950
1979
  * @param query - The user's question or task (can be text or multimodal content with images)
@@ -2826,7 +2855,20 @@ Remember: You are an autonomous AGENT. Act independently and solve problems proa
2826
2855
  return typeof result === "string" ? result : JSON.stringify(result);
2827
2856
  }
2828
2857
  const tool = this.context.tools.find((t) => t.toolSchema.name === toolUse.name);
2829
- if (!tool) return `Error: Tool ${toolUse.name} not found in agent context`;
2858
+ if (!tool) {
2859
+ if (this.context.unknownToolResolver) {
2860
+ const resolved = await this.context.unknownToolResolver(toolUse.name);
2861
+ if (resolved) {
2862
+ this.context.tools.push(resolved);
2863
+ return `Tool '${toolUse.name}' was deferred and its schema is now loaded. The previous call was made without the schema visible — please re-issue the call with valid parameters per the schema below.\n\n<function>${JSON.stringify({
2864
+ description: resolved.toolSchema.description,
2865
+ name: resolved.toolSchema.name,
2866
+ parameters: resolved.toolSchema.parameters
2867
+ })}</function>`;
2868
+ }
2869
+ }
2870
+ return `Error: Tool ${toolUse.name} not found in agent context`;
2871
+ }
2830
2872
  try {
2831
2873
  const params = this.parseToolArguments(toolUse.arguments);
2832
2874
  const result = await tool.toolFn(params);
@@ -3253,6 +3295,28 @@ const TOOL_WRITE_TODOS = "write_todos";
3253
3295
  const TOOL_CREATE_DYNAMIC_AGENT = "create_dynamic_agent";
3254
3296
  const EXPLORE_SUBAGENT_TYPE = "explore";
3255
3297
  /**
3298
+ * Render the deferred-tool directory: names only, no descriptions.
3299
+ * Following Claude Code's pattern, MCP tools have self-describing names
3300
+ * (e.g. mcp__github__create_pull_request) that the model parses without
3301
+ * needing extra text. The model can also call `tool_search` with free-text
3302
+ * keywords for non-obvious names.
3303
+ */
3304
+ function buildDeferredToolDirectory(names) {
3305
+ if (names.length === 0) return "";
3306
+ const sorted = [...names].sort();
3307
+ return `
3308
+
3309
+ ## Deferred tool schemas
3310
+
3311
+ The following ${sorted.length} tool(s) are available but their parameter schemas are NOT loaded by default to save context. Calling them directly will fail with "Tool not found". Use the \`tool_search\` tool to load schemas on demand:
3312
+ - Exact selection: \`tool_search(query="select:<name>[,<name>...]")\`
3313
+ - Keyword search: \`tool_search(query="<free text>")\`
3314
+
3315
+ Once a tool's schema is loaded, it becomes callable in subsequent turns. Deferred tool names:
3316
+
3317
+ ${sorted.join("\n")}`;
3318
+ }
3319
+ /**
3256
3320
  * Plan-mode section: tells the model that write tools are blocked and where to put the plan.
3257
3321
  * Appended dynamically when the user cycles into plan mode via Shift+Tab.
3258
3322
  *
@@ -3343,6 +3407,7 @@ function buildCoreSystemPrompt(contextSection, config) {
3343
3407
  let directoriesSection = "";
3344
3408
  let featureModulesSection = "";
3345
3409
  let planModeSection = "";
3410
+ let deferredToolSection = "";
3346
3411
  if (typeof contextSection === "string") {
3347
3412
  projectContextSection = contextSection;
3348
3413
  if (config) {
@@ -3351,6 +3416,7 @@ function buildCoreSystemPrompt(contextSection, config) {
3351
3416
  if (config.enableDynamicAgentCreation) dynamicAgentSection = buildDynamicAgentPromptSection();
3352
3417
  if (config.featureModulePrompts) featureModulesSection = config.featureModulePrompts;
3353
3418
  if (config.planModeFilePath) planModeSection = buildPlanModePromptSection(config.planModeFilePath);
3419
+ if (config.deferredToolNames && config.deferredToolNames.length > 0) deferredToolSection = buildDeferredToolDirectory(config.deferredToolNames);
3354
3420
  }
3355
3421
  } else if (contextSection && typeof contextSection === "object") {
3356
3422
  config = contextSection;
@@ -3361,6 +3427,7 @@ function buildCoreSystemPrompt(contextSection, config) {
3361
3427
  if (config.additionalDirectories && config.additionalDirectories.length > 0) directoriesSection = `\n\n## Additional Allowed Directories\n\nIn addition to the working directory (${process.cwd()}), you have read/write access to these directories:\n${config.additionalDirectories.map((d) => `- ${d}`).join("\n")}\n\nTo access files in additional directories, pass the full path to the 'dir_path' parameter of file tools:\n- ${TOOL_GREP_SEARCH}(pattern="...", dir_path="/path/to/additional/dir")\n- ${TOOL_GLOB_FILES}(pattern="**/*.ts", dir_path="/path/to/additional/dir")\n- ${TOOL_FILE_READ}(path="/path/to/additional/dir/file.ts")\n\nWhen the user asks about content in an additional directory, search there first using the dir_path parameter.`;
3362
3428
  if (config.featureModulePrompts) featureModulesSection = config.featureModulePrompts;
3363
3429
  if (config.planModeFilePath) planModeSection = buildPlanModePromptSection(config.planModeFilePath);
3430
+ if (config.deferredToolNames && config.deferredToolNames.length > 0) deferredToolSection = buildDeferredToolDirectory(config.deferredToolNames);
3364
3431
  }
3365
3432
  return `You are an autonomous AI assistant with access to tools. Your job is to help users by taking action and solving problems proactively.
3366
3433
 
@@ -3478,7 +3545,7 @@ These tools are lightweight — use them naturally as part of your work, not as
3478
3545
 
3479
3546
  ## Working Directory
3480
3547
 
3481
- The current working directory is \`${process.cwd()}\`. All relative paths in tool calls resolve from here. When using \`${TOOL_GLOB_FILES}\` or \`${TOOL_GREP_SEARCH}\` without an explicit \`dir_path\`, they search from this directory.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}${planModeSection}`;
3548
+ The current working directory is \`${process.cwd()}\`. All relative paths in tool calls resolve from here. When using \`${TOOL_GLOB_FILES}\` or \`${TOOL_GREP_SEARCH}\` without an explicit \`dir_path\`, they search from this directory.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}${planModeSection}${deferredToolSection}`;
3482
3549
  }
3483
3550
  /**
3484
3551
  * Build the minimal-variant system prompt. Reuses the project context,
@@ -3492,11 +3559,13 @@ function buildMinimalSystemPrompt(config = {}) {
3492
3559
  let directoriesSection = "";
3493
3560
  let featureModulesSection = "";
3494
3561
  let planModeSection = "";
3562
+ let deferredToolSection = "";
3495
3563
  if (config.contextContent) projectContextSection = `\n\n## Project Context\n\nFollow these project-specific instructions:\n\n${config.contextContent}`;
3496
3564
  if (config.enableSkillTool !== false && config.customCommands && config.customCommands.length > 0) skillsSection = buildSkillsPromptSection(config.customCommands);
3497
3565
  if (config.additionalDirectories && config.additionalDirectories.length > 0) directoriesSection = `\n\n## Additional Allowed Directories\n\nIn addition to the working directory (${process.cwd()}), you have read/write access to these directories:\n${config.additionalDirectories.map((d) => `- ${d}`).join("\n")}\n\nPass full paths to file tools' \`dir_path\` parameter to access these directories.`;
3498
3566
  if (config.featureModulePrompts) featureModulesSection = config.featureModulePrompts;
3499
3567
  if (config.planModeFilePath) planModeSection = buildPlanModePromptSection(config.planModeFilePath);
3568
+ if (config.deferredToolNames && config.deferredToolNames.length > 0) deferredToolSection = buildDeferredToolDirectory(config.deferredToolNames);
3500
3569
  return `You are an expert coding assistant. You help users by reading files, executing commands, editing code, and writing new files using the tools available to you.
3501
3570
 
3502
3571
  Guidelines:
@@ -3506,7 +3575,7 @@ Guidelines:
3506
3575
 
3507
3576
  ## Working Directory
3508
3577
 
3509
- The current working directory is \`${process.cwd()}\`. All relative paths in tool calls resolve from here. When using \`${TOOL_GLOB_FILES}\` or \`${TOOL_GREP_SEARCH}\` without an explicit \`dir_path\`, they search from this directory.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}${planModeSection}`;
3578
+ The current working directory is \`${process.cwd()}\`. All relative paths in tool calls resolve from here. When using \`${TOOL_GLOB_FILES}\` or \`${TOOL_GREP_SEARCH}\` without an explicit \`dir_path\`, they search from this directory.${directoriesSection}${projectContextSection}${skillsSection}${featureModulesSection}${planModeSection}${deferredToolSection}`;
3510
3579
  }
3511
3580
  /**
3512
3581
  * Pick a system prompt by variant. The dispatch point for the
@@ -16332,13 +16401,12 @@ var BFLImageService = class extends AIImageService {
16332
16401
  throw pollError;
16333
16402
  }
16334
16403
  const elapsedSeconds = Math.round((Date.now() - startTime) / 1e3);
16335
- const timeoutError = `Image generation is taking longer than expected (timed out after ${elapsedSeconds}s). The image may still be processing on BFL's servers. Request ID: ${requestId}`;
16336
16404
  console.error(`[DEBUG] ❌ Polling timeout:`, {
16337
16405
  requestId,
16338
16406
  maxAttempts,
16339
16407
  totalTime: `${elapsedSeconds}s`
16340
16408
  });
16341
- throw new Error(timeoutError);
16409
+ throw new Error("Image generation is taking longer than usual and your request could not be completed. Please try again in a few minutes.");
16342
16410
  }
16343
16411
  };
16344
16412
  var XAIImageService = class extends AIImageService {
@@ -18449,7 +18517,10 @@ const webFetchTool = {
18449
18517
  if (error instanceof FirecrawlError && [
18450
18518
  400,
18451
18519
  403,
18452
- 408
18520
+ 408,
18521
+ 502,
18522
+ 503,
18523
+ 504
18453
18524
  ].includes(error.statusCode)) {
18454
18525
  const { userMessage, statusLabel } = {
18455
18526
  400: {
@@ -18463,6 +18534,18 @@ const webFetchTool = {
18463
18534
  408: {
18464
18535
  userMessage: `The page timed out before loading completely — the site may be slow or require heavy JavaScript rendering. Try using web_search to find the information instead.`,
18465
18536
  statusLabel: "scrape timed out"
18537
+ },
18538
+ 502: {
18539
+ userMessage: `The website returned a Bad Gateway error — it may be temporarily unavailable. Try again later or use web_search to find the information instead.`,
18540
+ statusLabel: "bad gateway"
18541
+ },
18542
+ 503: {
18543
+ userMessage: `The website is temporarily unavailable (Service Unavailable). Try again later or use web_search to find the information instead.`,
18544
+ statusLabel: "service unavailable"
18545
+ },
18546
+ 504: {
18547
+ userMessage: `The website took too long to respond (Gateway Timeout). Try again later or use web_search to find the information instead.`,
18548
+ statusLabel: "gateway timeout"
18466
18549
  }
18467
18550
  }[error.statusCode];
18468
18551
  context.logger.warn("WebFetch expected Firecrawl failure", {
@@ -20651,7 +20734,7 @@ No markdown, no explanation, no code blocks — just the raw JSON object.`;
20651
20734
  "Minimize: sum(error^2)"
20652
20735
  ].join("\n");
20653
20736
  //#endregion
20654
- //#region ../../b4m-core/services/dist/tools-DeOV9w_P.mjs
20737
+ //#region ../../b4m-core/services/dist/tools-DtGzruvF.mjs
20655
20738
  async function performDeepResearch(context, params, config) {
20656
20739
  const maxDepth = config.maxDepth || 7;
20657
20740
  const duration = config.duration || 4.5;
@@ -21463,6 +21546,11 @@ async function evaluateMath(params) {
21463
21546
  } catch (error) {
21464
21547
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
21465
21548
  console.error(`❌ Math Tool: Evaluation error for expression "${params.expression}":`, errorMessage);
21549
+ const undefinedSymbol = errorMessage.match(/Undefined symbol (\w+)/);
21550
+ if (undefinedSymbol) {
21551
+ const symbol = undefinedSymbol[1];
21552
+ return `Error: Failed to evaluate math expression "${params.expression}". Reason: the symbol "${symbol}" is used but never assigned a value. Assign it first with a semicolon-separated statement (e.g. "${symbol} = <value>; ...") before referencing it, then try again.`;
21553
+ }
21466
21554
  return `Error: Failed to evaluate math expression "${params.expression}". Reason: ${errorMessage}. Please provide a valid mathematical expression using numbers and operators only (no natural language).`;
21467
21555
  }
21468
21556
  }
@@ -24269,7 +24357,7 @@ const knowledgeBaseRetrieveTool = {
24269
24357
  if (ownedFile) files = [ownedFile];
24270
24358
  else {
24271
24359
  const sharedFile = await context.db.fabfiles.findById(file_id);
24272
- if (sharedFile && !sharedFile.deletedAt) {
24360
+ if (sharedFile && !sharedFile.deletedAt && !sharedFile.archivedAt) {
24273
24361
  const { dataLakeTags, dataLakeTagPrefixes } = await getDynamicDataLakeAccess(context);
24274
24362
  const fileTags = sharedFile.tags?.map((t) => t.name) || [];
24275
24363
  const hasMetaTagAccess = dataLakeTags.some((dlt) => fileTags.includes(dlt));
@@ -27975,6 +28063,7 @@ z.object({
27975
28063
  embargoDetected: z.boolean().prefault(false),
27976
28064
  suggestedTags: z.array(z.string()).prefault([])
27977
28065
  });
28066
+ ImageModels.GPT_IMAGE_1, ImageModels.GPT_IMAGE_1_5, ImageModels.GPT_IMAGE_1_MINI, ImageModels.GPT_IMAGE_2;
27978
28067
  /**
27979
28068
  * Regex patterns for extracting GitHub entities from text
27980
28069
  */
@@ -4,7 +4,7 @@ import { homedir } from "os";
4
4
  import path from "path";
5
5
  import axios from "axios";
6
6
  //#region package.json
7
- var version = "0.10.0";
7
+ var version = "0.10.1";
8
8
  //#endregion
9
9
  //#region src/utils/updateChecker.ts
10
10
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bike4mind/cli",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "type": "module",
5
5
  "description": "Interactive CLI tool for Bike4Mind with ReAct agents",
6
6
  "license": "UNLICENSED",
@@ -121,11 +121,11 @@
121
121
  "tsx": "^4.22.3",
122
122
  "typescript": "^5.9.3",
123
123
  "vitest": "^4.1.7",
124
- "@bike4mind/agents": "0.11.1",
125
- "@bike4mind/common": "2.102.0",
126
- "@bike4mind/mcp": "1.37.16",
127
- "@bike4mind/services": "2.90.1",
128
- "@bike4mind/utils": "2.23.3"
124
+ "@bike4mind/agents": "0.11.3",
125
+ "@bike4mind/common": "2.103.0",
126
+ "@bike4mind/mcp": "1.37.17",
127
+ "@bike4mind/services": "2.90.3",
128
+ "@bike4mind/utils": "2.23.5"
129
129
  },
130
130
  "optionalDependencies": {
131
131
  "@vscode/ripgrep": "^1.18.0"