@bike4mind/cli 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { I as generateCliTools, J as setWebSocketToolExecutor, L as wrapTools, N as ReActAgent, X as buildSystemPrompt, Y as getPlanModeFilePath, a as loadProjectContext, c as createSseBackend, i as buildProjectAgentStore, it as Logger, l as ServerLlmBackend, n as BackgroundAgentManager, o as McpManager, r as SubagentOrchestrator, t as AgentHistoryStore } from "./AgentHistoryStore-B2NEOvSW.mjs";
3
- import { D as MODEL_INFO_FIELD_GROUP_OF, H as escapeThinkMarkers, P as PermissionDeniedError, S as CONTEXT_WINDOW_SAFETY_BUFFER_TOKENS, V as createThinkMarkerEscaper, W as getQuestErrorCode, Y as isUserInitiatedAbort, k as ModelBackend, n as logger, w as ChatModels } from "./ConfigStore-qV7NrCgZ.mjs";
2
+ import { I as generateCliTools, J as setWebSocketToolExecutor, L as wrapTools, N as ReActAgent, X as buildSystemPrompt, Y as getPlanModeFilePath, a as loadProjectContext, c as createSseBackend, i as buildProjectAgentStore, it as Logger, l as ServerLlmBackend, n as BackgroundAgentManager, o as McpManager, r as SubagentOrchestrator, t as AgentHistoryStore } from "./AgentHistoryStore-DIMzvf-y.mjs";
3
+ import { $ as hasDeliverablePinnedArtifact, C as ARTIFACT_REMOVED_PLACEHOLDER, E as CONTEXT_WINDOW_SAFETY_BUFFER_TOKENS, J as filterToolArtifactMarkup, K as createThinkMarkerEscaper, M as MODEL_INFO_FIELD_GROUP_OF, O as ChatModels, P as ModelBackend, S as ARTIFACT_DELIVERED_PLACEHOLDER, Z as getQuestErrorCode, ct as stripDeliveredArtifactBlocks, lt as stripToolArtifactMarkup, n as logger, q as escapeThinkMarkers, rt as isUserInitiatedAbort, z as PermissionDeniedError } from "./ConfigStore-DIs-K9oD.mjs";
4
4
  import "crypto";
5
5
  import { v4 } from "uuid";
6
6
  import { z as z$1 } from "zod";
@@ -135,6 +135,96 @@ function attachFullToolResult(entry, observation) {
135
135
  writable: true
136
136
  });
137
137
  }
138
+ /** The `artifactType` the named tool declared at registration, for the helpers below. */
139
+ function declaredArtifactType(tools, toolName) {
140
+ return tools?.find((t) => t.toolSchema.name === toolName)?.artifactType;
141
+ }
142
+ /**
143
+ * Helper function to handle tool result streaming for artifact-generating tools
144
+ * This ensures tools like recharts that generate artifacts are streamed immediately
145
+ * rather than waiting for recursive completion calls.
146
+ *
147
+ * Only the emitters in TOOL_ARTIFACT_EMITTERS (or a tool that declared `artifactType`, see
148
+ * declaredArtifactType) stream, and only their pinned artifact type: streamed text is parsed
149
+ * into reply artifacts, so any other tool's markup would render.
150
+ *
151
+ * What does stream is still a raw tool artifact rather than reply prose, so `streamCallback`
152
+ * receives the channel tag and a public surface drops the text on that tag alone.
153
+ */
154
+ async function handleToolResultStreaming(toolName, toolResult, streamCallback, artifactType) {
155
+ const filtered = filterToolArtifactMarkup(toolName, String(toolResult), artifactType);
156
+ if (filtered !== null) await streamCallback([filtered], { channel: "tool-artifact" });
157
+ }
158
+ /**
159
+ * Strips artifact markup from a tool result before it re-enters history, so the model cannot echo
160
+ * it into a second card. Live-emitting backends pass `delivered` = whether their tool-artifact
161
+ * emit fired. The `hasDeliverablePinnedArtifact` fallback is for the OpenAI Responses path, whose
162
+ * artifact reaches the client through sharedToolBuilder extraction instead.
163
+ *
164
+ * The delivered/removed choice is made once per result and stamped on every block it contains.
165
+ * That holds because each emitter emits one pinned block; a result mixing a foreign-type block
166
+ * with a pinned one would label both DELIVERED.
167
+ */
168
+ function stripUnstreamedToolResult(toolName, result, delivered) {
169
+ return stripToolArtifactMarkup(result, delivered ?? hasDeliverablePinnedArtifact(toolName, result) ? ARTIFACT_DELIVERED_PLACEHOLDER : ARTIFACT_REMOVED_PLACEHOLDER);
170
+ }
171
+ /**
172
+ * Buffers a recursive completion's text instead of streaming it live, so an <artifact> tag the
173
+ * model echoes back (after seeing its own tool result, even sanitized) can be stripped before the
174
+ * client ever renders a second, empty card for it. Only removes blocks whose `identifier` matches
175
+ * one already delivered this turn (stripDeliveredArtifactBlocks) - a stray "<artifact" the model's
176
+ * prose merely mentions, or a genuinely NEW artifact the model composes with a different
177
+ * identifier, must not cost the rest of a legitimate reply, unlike the tool-output-facing
178
+ * stripToolArtifactMarkup.
179
+ *
180
+ * One instance is created lazily by whichever level first streams an artifact in a recursive
181
+ * chain, and reused UNCHANGED by every deeper level (never recreated per level) - see each
182
+ * backend's `_internal.artifactGuard`. A chained tool call's artifact goes through `emitArtifact`
183
+ * on this same shared instance, which flushes any text already buffered ahead of it before
184
+ * sending the artifact through - so ordering survives even when the chained call happens several
185
+ * recursion levels below where the guard was created.
186
+ */
187
+ function createRecursiveArtifactGuard(cb) {
188
+ let buffer = "";
189
+ let deliveredMarkup = "";
190
+ let meta = {};
191
+ const callback = (async (text, info) => {
192
+ for (const chunk of text) if (chunk != null) buffer += chunk;
193
+ if (info?.inputTokens || info?.outputTokens) meta = { ...info };
194
+ });
195
+ const emitArtifact = async (results, info) => {
196
+ if (buffer) {
197
+ const pending = buffer;
198
+ buffer = "";
199
+ const stripped = stripDeliveredArtifactBlocks(pending, deliveredMarkup);
200
+ if (stripped.trim()) await cb([stripped], {});
201
+ }
202
+ deliveredMarkup += results.join("");
203
+ await cb(results, info);
204
+ };
205
+ const markDelivered = (markup) => {
206
+ deliveredMarkup += markup;
207
+ };
208
+ let flushPromise = null;
209
+ const flush = () => {
210
+ if (flushPromise) return flushPromise;
211
+ const cleaned = stripDeliveredArtifactBlocks(buffer, deliveredMarkup).trim();
212
+ let resolve;
213
+ let reject;
214
+ flushPromise = new Promise((res, rej) => {
215
+ resolve = res;
216
+ reject = rej;
217
+ });
218
+ (async () => cb(cleaned ? [cleaned] : [], meta))().then(() => resolve(), reject);
219
+ return flushPromise;
220
+ };
221
+ return {
222
+ callback,
223
+ emitArtifact,
224
+ markDelivered,
225
+ flush
226
+ };
227
+ }
138
228
  /**
139
229
  * Drops the messages whose instructions assume a tool is attached (see `IMessage.requiresTool`).
140
230
  *
@@ -850,20 +940,32 @@ var OllamaBackend = class OllamaBackend {
850
940
  parallel: options.parallelToolExecution !== false,
851
941
  maxConcurrency: options.maxParallelTools
852
942
  });
943
+ const inheritedArtifactGuard = options._internal?.artifactGuard;
944
+ let artifactGuard = inheritedArtifactGuard;
853
945
  const observations = [];
854
- outcomes.forEach((outcome, i) => {
946
+ for (let i = 0; i < outcomes.length; i++) {
947
+ const outcome = outcomes[i];
855
948
  const { tc } = resolved[i];
856
949
  const params = tc.arguments || "{}";
857
950
  if (outcome.ok) {
858
- observations[i] = outcome.result;
951
+ let emitted = false;
952
+ await handleToolResultStreaming(tc.name, outcome.result, async (results, artifactInfo) => {
953
+ emitted = true;
954
+ if (!artifactGuard) artifactGuard = createRecursiveArtifactGuard(callback);
955
+ await artifactGuard.emitArtifact(results, {
956
+ toolsUsed,
957
+ ...artifactInfo
958
+ });
959
+ }, declaredArtifactType(options.tools, tc.name));
960
+ observations[i] = stripUnstreamedToolResult(tc.name, outcome.result, emitted);
859
961
  this.pushToolMessages(messages, {
860
962
  id: tc.id,
861
963
  name: tc.name,
862
964
  parameters: params
863
- }, outcome.result);
965
+ }, observations[i]);
864
966
  } else {
865
967
  if (outcome.error instanceof PermissionDeniedError) throw outcome.error;
866
- const errorMsg = `Error running ${tc.name}: ${outcome.error instanceof Error ? outcome.error.message : "Unknown error"}`;
968
+ const errorMsg = stripToolArtifactMarkup(`Error running ${tc.name}: ${outcome.error instanceof Error ? outcome.error.message : "Unknown error"}`, ARTIFACT_REMOVED_PLACEHOLDER);
867
969
  observations[i] = errorMsg;
868
970
  this.pushToolMessages(messages, {
869
971
  id: tc.id,
@@ -871,7 +973,7 @@ var OllamaBackend = class OllamaBackend {
871
973
  parameters: params
872
974
  }, errorMsg);
873
975
  }
874
- });
976
+ }
875
977
  const executedToolsUsed = [...priorToolsUsed, ...resolved.map(({ tc }, i) => {
876
978
  const entry = {
877
979
  name: tc.name,
@@ -884,11 +986,12 @@ var OllamaBackend = class OllamaBackend {
884
986
  return entry;
885
987
  })];
886
988
  if (options.abortSignal?.aborted) {
887
- await callback([""], {
989
+ await (artifactGuard?.callback ?? callback)([""], {
888
990
  inputTokens,
889
991
  outputTokens,
890
992
  toolsUsed: executedToolsUsed
891
993
  });
994
+ if (!inheritedArtifactGuard && artifactGuard) await artifactGuard.flush();
892
995
  return;
893
996
  }
894
997
  await this.complete(model, messages, {
@@ -898,9 +1001,11 @@ var OllamaBackend = class OllamaBackend {
898
1001
  toolCallCount: toolCallCount + 1,
899
1002
  accumToolsUsed: executedToolsUsed,
900
1003
  accumInputTokens: inputTokens,
901
- accumOutputTokens: outputTokens
1004
+ accumOutputTokens: outputTokens,
1005
+ artifactGuard
902
1006
  }
903
- }, callback);
1007
+ }, artifactGuard?.callback ?? callback);
1008
+ if (!inheritedArtifactGuard && artifactGuard) await artifactGuard.flush();
904
1009
  } catch (error) {
905
1010
  if (error instanceof Error && isUserInitiatedAbort(error, options.abortSignal)) this._logger.debug("[OllamaBackend] Ollama request cancelled by the caller");
906
1011
  else this._logger.error("[OllamaBackend] Error during Ollama API call:", error);
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { $ as CustomCommandStore, A as createAgentDelegateTool, C as createTodoStore, D as createCoordinateTaskTool, F as PermissionManager, J as setWebSocketToolExecutor, L as wrapTools, O as createResumeAgentTool, Q as RemoteSkillSource, S as createFindDefinitionTool, T as createSkillTool, b as createGetFileStructureTool, et as CheckpointStore, j as FallbackLlmBackend, k as createBackgroundAgentTools, s as buildSandbox, tt as SessionStore, w as createWriteTodosTool } from "../AgentHistoryStore-B2NEOvSW.mjs";
3
- import { h as requireApiUrl, n as logger, s as isReadOnlyTool, t as ConfigStore } from "../ConfigStore-qV7NrCgZ.mjs";
4
- import { a as deferredToolRegistry, i as createToolSearchTool, n as buildSupportingStores, o as NotifyingLlmBackend, r as buildLlmBackend, t as buildAgent } from "../buildAgent-C-C-VGff.mjs";
5
- import { t as ApiClient } from "../ApiClient-D0fQ2FT6.mjs";
2
+ import { $ as CustomCommandStore, A as createAgentDelegateTool, C as createTodoStore, D as createCoordinateTaskTool, F as PermissionManager, J as setWebSocketToolExecutor, L as wrapTools, O as createResumeAgentTool, Q as RemoteSkillSource, S as createFindDefinitionTool, T as createSkillTool, b as createGetFileStructureTool, et as CheckpointStore, j as FallbackLlmBackend, k as createBackgroundAgentTools, s as buildSandbox, tt as SessionStore, w as createWriteTodosTool } from "../AgentHistoryStore-DIMzvf-y.mjs";
3
+ import { _ as requireApiUrl, d as isReadOnlyTool, n as logger, t as ConfigStore } from "../ConfigStore-DIs-K9oD.mjs";
4
+ import { a as deferredToolRegistry, i as createToolSearchTool, n as buildSupportingStores, o as NotifyingLlmBackend, r as buildLlmBackend, t as buildAgent } from "../buildAgent-DOvEp92a.mjs";
5
+ import { t as ApiClient } from "../ApiClient-BTI0-m0g.mjs";
6
6
  import { randomUUID } from "crypto";
7
7
  import { existsSync, realpathSync, statSync } from "fs";
8
8
  import { isAbsolute } from "path";
@@ -575,6 +575,7 @@ var AcpServer = class {
575
575
  }
576
576
  async buildStack() {
577
577
  const config = await this.configStore.load();
578
+ this.configStore.warnPendingMcpApprovals();
578
579
  await this.loadCustomCommands();
579
580
  const authTokens = await this.configStore.getAuthTokens();
580
581
  if (!authTokens) throw RequestError.authRequired(void 0, "Not authenticated. Run `b4m /login` first.");
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { g as resolveApiEndpoint, m as parseApiUrl, t as ConfigStore } from "../ConfigStore-qV7NrCgZ.mjs";
2
+ import { g as parseApiUrl, t as ConfigStore, v as resolveApiEndpoint } from "../ConfigStore-DIs-K9oD.mjs";
3
3
  //#region src/commands/apiCommand.ts
4
4
  /**
5
5
  * External API config command (--api-url / --reset-api)
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as version } from "../package-CGZIoxcs.mjs";
2
+ import { t as version } from "../package-Dl_F2Fah.mjs";
3
3
  import { a as fetchLatestVersion, c as isNpmPrefixWritable, i as compareSemver } from "../updateChecker-CQW8bxo6.mjs";
4
4
  import { t as checkRipgrep } from "../ripgrepCheck-BmkyTK2i.mjs";
5
5
  import { execSync } from "child_process";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as ConfigStore } from "../ConfigStore-qV7NrCgZ.mjs";
2
+ import { t as ConfigStore } from "../ConfigStore-DIs-K9oD.mjs";
3
3
  //#region src/commands/envCommand.ts
4
4
  /**
5
5
  * Environment switching for the `--dev` / `--prod` launch flags.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as CustomCommandStore, A as createAgentDelegateTool, C as createTodoStore, D as createCoordinateTaskTool, F as PermissionManager, G as classifyCommandRisk, I as generateCliTools, J as setWebSocketToolExecutor, L as wrapTools, N as ReActAgent, O as createResumeAgentTool, Q as RemoteSkillSource, S as createFindDefinitionTool, T as createSkillTool, X as buildSystemPrompt, a as loadProjectContext, b as createGetFileStructureTool, c as createSseBackend, et as CheckpointStore, i as buildProjectAgentStore, j as FallbackLlmBackend, k as createBackgroundAgentTools, n as BackgroundAgentManager, o as McpManager, r as SubagentOrchestrator, s as buildSandbox, t as AgentHistoryStore, tt as SessionStore, w as createWriteTodosTool, x as createWorkItemTools, z as SHELL_LIKE_TOOL_COMMAND_FIELDS } from "../AgentHistoryStore-B2NEOvSW.mjs";
3
- import { h as requireApiUrl, n as logger, s as isReadOnlyTool, t as ConfigStore } from "../ConfigStore-qV7NrCgZ.mjs";
4
- import { t as ApiClient } from "../ApiClient-D0fQ2FT6.mjs";
2
+ import { $ as CustomCommandStore, A as createAgentDelegateTool, C as createTodoStore, D as createCoordinateTaskTool, F as PermissionManager, G as classifyCommandRisk, I as generateCliTools, J as setWebSocketToolExecutor, L as wrapTools, N as ReActAgent, O as createResumeAgentTool, Q as RemoteSkillSource, S as createFindDefinitionTool, T as createSkillTool, X as buildSystemPrompt, a as loadProjectContext, b as createGetFileStructureTool, c as createSseBackend, et as CheckpointStore, i as buildProjectAgentStore, j as FallbackLlmBackend, k as createBackgroundAgentTools, n as BackgroundAgentManager, o as McpManager, r as SubagentOrchestrator, s as buildSandbox, t as AgentHistoryStore, tt as SessionStore, w as createWriteTodosTool, x as createWorkItemTools, z as SHELL_LIKE_TOOL_COMMAND_FIELDS } from "../AgentHistoryStore-DIMzvf-y.mjs";
3
+ import { _ as requireApiUrl, d as isReadOnlyTool, n as logger, t as ConfigStore } from "../ConfigStore-DIs-K9oD.mjs";
4
+ import { t as ApiClient } from "../ApiClient-BTI0-m0g.mjs";
5
5
  import { r as reconstructTurnBlocks, t as WorkItemsClient } from "../WorkItemsClient-Cow6nXx7.mjs";
6
6
  import { randomBytes } from "crypto";
7
7
  import { v4 } from "uuid";
@@ -250,6 +250,7 @@ async function handleHeadlessCommand(options) {
250
250
  const runId = v4();
251
251
  try {
252
252
  const config = await configStore.load();
253
+ configStore.warnPendingMcpApprovals();
253
254
  const configDirs = await configStore.getAdditionalDirectories();
254
255
  const flagDirs = process.env.B4M_ADDITIONAL_DIRS ? parseStringArray(process.env.B4M_ADDITIONAL_DIRS, "B4M_ADDITIONAL_DIRS") : [];
255
256
  const additionalDirectories = [.../* @__PURE__ */ new Set([
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { t as ConfigStore } from "../ConfigStore-qV7NrCgZ.mjs";
3
- import { t as version } from "../package-CGZIoxcs.mjs";
2
+ import { t as ConfigStore } from "../ConfigStore-DIs-K9oD.mjs";
3
+ import { t as version } from "../package-Dl_F2Fah.mjs";
4
4
  //#region src/commands/mcpCommand.ts
5
5
  /**
6
6
  * External MCP commands (b4m mcp list, b4m mcp add, etc.)
@@ -70,7 +70,7 @@ async function handleMcpCommand(subcommand, argv) {
70
70
  await handleDisable(config, argv.name, configStore);
71
71
  break;
72
72
  case "serve": {
73
- const { handleMcpServeCommand } = await import("../serve-CPXqcEZr.mjs");
73
+ const { handleMcpServeCommand } = await import("../serve-Y2zMbfGs.mjs");
74
74
  await handleMcpServeCommand({
75
75
  http: Boolean(argv.http),
76
76
  port: typeof argv.port === "number" ? argv.port : void 0,
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { n as getDefaultPluginsDir, r as isFeatureEnabled, t as PluginStore } from "../PluginStore-DwvOJ-G3.mjs";
3
- import { t as ConfigStore } from "../ConfigStore-qV7NrCgZ.mjs";
3
+ import { t as ConfigStore } from "../ConfigStore-DIs-K9oD.mjs";
4
4
  import { execFileSync } from "child_process";
5
5
  import { promises } from "fs";
6
6
  import path from "path";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as version } from "../package-CGZIoxcs.mjs";
2
+ import { t as version } from "../package-Dl_F2Fah.mjs";
3
3
  import { c as isNpmPrefixWritable, l as setAutoUpdatePreference, n as REEXEC_GUARD_ENV, o as forceCheckForUpdate, r as checkForUpdate, s as getAutoUpdatePreference, t as INSTALL_CMD, u as shouldAttemptAutoUpdate } from "../updateChecker-CQW8bxo6.mjs";
4
4
  import { t as checkRipgrep } from "../ripgrepCheck-BmkyTK2i.mjs";
5
5
  import { execSync, spawnSync } from "child_process";