@botconnector/bccli 0.4.0 → 0.4.2

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.
Files changed (173) hide show
  1. package/README.en.md +13 -2
  2. package/README.md +13 -2
  3. package/dist/abort.d.ts +5 -0
  4. package/dist/{acp-ATIL4JOZ.js → acp-UYC6APR5.js} +30 -6
  5. package/dist/acp.d.ts +28 -0
  6. package/dist/agent.d.ts +147 -0
  7. package/dist/agents.d.ts +4 -0
  8. package/dist/args-NRTZWWEV.js +13 -0
  9. package/dist/args.d.ts +53 -0
  10. package/dist/budget.d.ts +23 -0
  11. package/dist/checkpoints.d.ts +35 -0
  12. package/dist/{chunk-6M5X2KZE.js → chunk-266SY3MC.js} +1 -1
  13. package/dist/{chunk-7F3IWBPQ.js → chunk-3I5BW3ZT.js} +20 -9
  14. package/dist/{chunk-UZWJQF67.js → chunk-3WHI4LN3.js} +2 -1
  15. package/dist/{chunk-PUN4HMAG.js → chunk-5YJ3ZQVR.js} +73 -10
  16. package/dist/{chunk-L7QI6JN2.js → chunk-6M55UPES.js} +7 -7
  17. package/dist/{chunk-KEMYIGCP.js → chunk-6UO5GX55.js} +26 -12
  18. package/dist/{chunk-TLPFQIS2.js → chunk-C64KM37J.js} +36 -6
  19. package/dist/{chunk-JL5ZCSP2.js → chunk-H7XJDKJY.js} +4 -4
  20. package/dist/{chunk-SPRNLS7K.js → chunk-HQW4BROS.js} +4 -5
  21. package/dist/{chunk-XBO5GACQ.js → chunk-IYR5PYA3.js} +4 -4
  22. package/dist/{chunk-45SDLMTV.js → chunk-JXM6REER.js} +1 -1
  23. package/dist/{chunk-BLOYFIAD.js → chunk-KPTML72Q.js} +1 -1
  24. package/dist/{chunk-BQ2IGPBR.js → chunk-NQOGVP3I.js} +2 -2
  25. package/dist/{chunk-QZKLZELZ.js → chunk-OK2USAXX.js} +28 -4
  26. package/dist/{chunk-TVALKHFB.js → chunk-R72OV7RF.js} +421 -108
  27. package/dist/{chunk-LACUWBWS.js → chunk-RX4UQX43.js} +25 -10
  28. package/dist/{chunk-QDQU6VY2.js → chunk-SVD4LHJC.js} +73 -3
  29. package/dist/{chunk-BNA74HPN.js → chunk-TIMSYZIR.js} +7 -7
  30. package/dist/{chunk-O7M3TGIW.js → chunk-WFZKJY3R.js} +43 -4
  31. package/dist/{chunk-M7CALSP3.js → chunk-ZEPTUKNW.js} +120 -32
  32. package/dist/cli.d.ts +1 -0
  33. package/dist/cli.js +61 -45
  34. package/dist/clipboardImage.d.ts +17 -0
  35. package/dist/commands.d.ts +16 -0
  36. package/dist/{completion-HPPFDAPB.js → completion-D2BSBTQO.js} +1 -1
  37. package/dist/completion.d.ts +14 -0
  38. package/dist/{config-COPNMMV6.js → config-ENDRMOTP.js} +3 -3
  39. package/dist/{config-RZ5JONMX.js → config-LAINORU5.js} +8 -4
  40. package/dist/config.d.ts +99 -0
  41. package/dist/context.d.ts +13 -0
  42. package/dist/contextWindow.d.ts +3 -0
  43. package/dist/diff.d.ts +7 -0
  44. package/dist/exit.d.ts +9 -0
  45. package/dist/extensions.d.ts +34 -0
  46. package/dist/{ghBotCli-IBCZIWT5.js → ghBotCli-TSLRORJJ.js} +21 -20
  47. package/dist/ghBotCli.d.ts +9 -0
  48. package/dist/ghBotDispatch.d.ts +68 -0
  49. package/dist/ghBotRunner.d.ts +82 -0
  50. package/dist/ghBotServe.d.ts +21 -0
  51. package/dist/ghBotWebhook.d.ts +45 -0
  52. package/dist/hooks.d.ts +27 -0
  53. package/dist/i18n/id.d.ts +1 -0
  54. package/dist/i18n.d.ts +16 -0
  55. package/dist/{integrationCli-P75R2VSC.js → integrationCli-HXCSEWH2.js} +2 -2
  56. package/dist/integrationCli.d.ts +9 -0
  57. package/dist/keybinds.d.ts +22 -0
  58. package/dist/{login-SYIQP46J.js → login-RYESFZG6.js} +4 -4
  59. package/dist/login.d.ts +19 -0
  60. package/dist/lsp.d.ts +50 -0
  61. package/dist/mcp/catalog.d.ts +14 -0
  62. package/dist/mcp/config.d.ts +36 -0
  63. package/dist/mcp/manager.d.ts +52 -0
  64. package/dist/mcp/oauth.d.ts +82 -0
  65. package/dist/{mcpCli-QUV5SD64.js → mcpCli-SETTCL3K.js} +6 -6
  66. package/dist/mcpCli.d.ts +3 -0
  67. package/dist/mcpServer/serve.d.ts +8 -0
  68. package/dist/mcpServer/server.d.ts +19 -0
  69. package/dist/models.d.ts +13 -0
  70. package/dist/{modelsCli-TRRB5LX4.js → modelsCli-OP7CDZC7.js} +6 -6
  71. package/dist/modelsCli.d.ts +15 -0
  72. package/dist/npmRunner.d.ts +21 -0
  73. package/dist/{oauthLogin-6FWQABJ7.js → oauthLogin-XWY7OHCG.js} +1 -1
  74. package/dist/oauthLogin.d.ts +47 -0
  75. package/dist/permissions.d.ts +30 -0
  76. package/dist/pluginSpec.d.ts +25 -0
  77. package/dist/plugins.d.ts +45 -0
  78. package/dist/{pluginsCli-GNW4TPVV.js → pluginsCli-ZQNKWZHJ.js} +2 -2
  79. package/dist/pluginsCli.d.ts +16 -0
  80. package/dist/{prCli-5IMV3VNQ.js → prCli-6PUWTMTP.js} +20 -4
  81. package/dist/prCli.d.ts +46 -0
  82. package/dist/presets.d.ts +8 -0
  83. package/dist/print.d.ts +16 -0
  84. package/dist/provider.d.ts +145 -0
  85. package/dist/providerCli-5OZI2GQQ.js +15 -0
  86. package/dist/providerCli.d.ts +17 -0
  87. package/dist/providers.d.ts +6 -0
  88. package/dist/reasoning.d.ts +12 -0
  89. package/dist/repomap.d.ts +10 -0
  90. package/dist/sdk.d.ts +61 -0
  91. package/dist/sdk.js +829 -191
  92. package/dist/{serve-JG7RLIUD.js → serve-4J7HA4JO.js} +24 -18
  93. package/dist/{serve-6QLBJUYW.js → serve-K5AH4MGO.js} +3 -3
  94. package/dist/serve.d.ts +37 -0
  95. package/dist/serveHeartbeat.d.ts +14 -0
  96. package/dist/serveProtocol.d.ts +47 -0
  97. package/dist/session-YQXCCKXI.js +12 -0
  98. package/dist/session.d.ts +33 -0
  99. package/dist/setup-S4AJT6XE.js +24 -0
  100. package/dist/setup.d.ts +74 -0
  101. package/dist/slash/copy.d.ts +1 -0
  102. package/dist/slash/diff.d.ts +1 -0
  103. package/dist/slash/doctor.d.ts +18 -0
  104. package/dist/slash/export.d.ts +2 -0
  105. package/dist/slash/info.d.ts +46 -0
  106. package/dist/slash/memory.d.ts +10 -0
  107. package/dist/{statsCli-CF3R7ATL.js → statsCli-XR6BEZQQ.js} +2 -2
  108. package/dist/statsCli.d.ts +62 -0
  109. package/dist/textToolCalls.d.ts +6 -0
  110. package/dist/thinking.d.ts +17 -0
  111. package/dist/tokenBudget.d.ts +22 -0
  112. package/dist/tools/bash.d.ts +52 -0
  113. package/dist/tools/diagnostics.d.ts +5 -0
  114. package/dist/tools/edit.d.ts +12 -0
  115. package/dist/tools/fetch.d.ts +41 -0
  116. package/dist/tools/glob.d.ts +5 -0
  117. package/dist/tools/globSafety.d.ts +7 -0
  118. package/dist/tools/grep.d.ts +25 -0
  119. package/dist/tools/ignore.d.ts +2 -0
  120. package/dist/tools/index.d.ts +12 -0
  121. package/dist/tools/memory.d.ts +13 -0
  122. package/dist/tools/paths.d.ts +4 -0
  123. package/dist/tools/plan.d.ts +10 -0
  124. package/dist/tools/read.d.ts +16 -0
  125. package/dist/tools/repomap.d.ts +2 -0
  126. package/dist/tools/sandbox.d.ts +12 -0
  127. package/dist/tools/shell.d.ts +18 -0
  128. package/dist/tools/skill.d.ts +3 -0
  129. package/dist/tools/task.d.ts +28 -0
  130. package/dist/tools/todo.d.ts +13 -0
  131. package/dist/tools/types.d.ts +52 -0
  132. package/dist/tools/websearch.d.ts +9 -0
  133. package/dist/tools/write.d.ts +5 -0
  134. package/dist/{transferCli-TK2GSVRK.js → transferCli-R2KEIWQR.js} +8 -6
  135. package/dist/transferCli.d.ts +23 -0
  136. package/dist/ui/App.d.ts +7 -0
  137. package/dist/ui/DiffView.d.ts +3 -0
  138. package/dist/ui/EditableText.d.ts +7 -0
  139. package/dist/ui/LinePrompt.d.ts +6 -0
  140. package/dist/ui/ListPicker.d.ts +11 -0
  141. package/dist/ui/Markdown.d.ts +8 -0
  142. package/dist/ui/McpMenu.d.ts +11 -0
  143. package/dist/ui/ModelPicker.d.ts +6 -0
  144. package/dist/ui/PermissionPrompt.d.ts +5 -0
  145. package/dist/ui/PlanApproval.d.ts +5 -0
  146. package/dist/ui/PromptInput.d.ts +18 -0
  147. package/dist/ui/ProviderMenu.d.ts +10 -0
  148. package/dist/ui/Spinner.d.ts +4 -0
  149. package/dist/ui/StatusBar.d.ts +9 -0
  150. package/dist/ui/TodoList.d.ts +4 -0
  151. package/dist/ui/ToolBlock.d.ts +14 -0
  152. package/dist/ui/complete.d.ts +6 -0
  153. package/dist/ui/focusReport.d.ts +1 -0
  154. package/dist/ui/index.d.ts +7 -0
  155. package/dist/ui/lineEdit.d.ts +42 -0
  156. package/dist/ui/replay.d.ts +4 -0
  157. package/dist/ui/table.d.ts +20 -0
  158. package/dist/ui/theme.d.ts +5 -0
  159. package/dist/ui/thinkingMouse.d.ts +33 -0
  160. package/dist/ui/transcript.d.ts +55 -0
  161. package/dist/ui/vim.d.ts +26 -0
  162. package/dist/{ui-B3CJ6TGE.js → ui-36ZEK65V.js} +179 -54
  163. package/dist/{update-K5IQ2MYR.js → update-FSS7X5WV.js} +2 -2
  164. package/dist/update.d.ts +30 -0
  165. package/dist/verify.d.ts +16 -0
  166. package/dist/version.d.ts +1 -0
  167. package/dist/webBudget.d.ts +25 -0
  168. package/dist/worktree.d.ts +12 -0
  169. package/package.json +26 -13
  170. package/dist/args-J5ONVGIF.js +0 -13
  171. package/dist/providerCli-FAOZHOKW.js +0 -15
  172. package/dist/session-KBAQ4BZ5.js +0 -12
  173. package/dist/setup-FJAWDWV4.js +0 -24
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  reasoningPayload
4
- } from "./chunk-BLOYFIAD.js";
4
+ } from "./chunk-KPTML72Q.js";
5
5
  import {
6
6
  t
7
- } from "./chunk-UZWJQF67.js";
7
+ } from "./chunk-3WHI4LN3.js";
8
8
 
9
9
  // src/provider.ts
10
10
  import OpenAI from "openai";
@@ -171,7 +171,7 @@ async function readStream(res, onText, onThinking) {
171
171
  }
172
172
  current = target;
173
173
  if (tc.id) target.id = tc.id;
174
- if (tc.function?.name && !target.name) target.name = tc.function.name;
174
+ if (tc.function?.name && target.name !== tc.function.name) target.name += tc.function.name;
175
175
  if (tc.function?.arguments) target.arguments += tc.function.arguments;
176
176
  }
177
177
  };
@@ -378,7 +378,16 @@ function responsesFinishReason(items, toolCallCount, incompleteReason) {
378
378
  function hasPendingProgrammaticReplay(messages) {
379
379
  let pending = false;
380
380
  for (const message of messages) {
381
+ if (message.role === "user" && typeof message.content === "string") {
382
+ pending = false;
383
+ continue;
384
+ }
385
+ if (message.role === "tool" && message.content === "Cancelled by the user.") {
386
+ pending = false;
387
+ continue;
388
+ }
381
389
  if (message.role !== "assistant") continue;
390
+ if (message.content) pending = false;
382
391
  for (const item of message.responses_output_items ?? []) {
383
392
  if (item.type === "program") pending = true;
384
393
  else if (item.type === "message" && pending) pending = false;
@@ -452,7 +461,7 @@ async function readResponsesStream(res, onText, onThinking) {
452
461
  calls.set(index, {
453
462
  id: String(event.item.call_id || current?.id || event.item.id || `call_${index}`),
454
463
  name: String(event.item.name || current?.name || ""),
455
- arguments: String(event.item.arguments ?? current?.arguments ?? ""),
464
+ arguments: String(event.item.arguments || current?.arguments || ""),
456
465
  ...programCaller(event.item) ?? current?.caller ? { caller: programCaller(event.item) ?? current?.caller } : {}
457
466
  });
458
467
  }
@@ -688,7 +697,7 @@ async function readResponsesWebSocketTurn(events, ws, onText, onThinking, signal
688
697
  calls.set(index, {
689
698
  id: String(event.item.call_id || current?.id || event.item.id || `call_${index}`),
690
699
  name: String(event.item.name || current?.name || ""),
691
- arguments: String(event.item.arguments ?? current?.arguments ?? ""),
700
+ arguments: String(event.item.arguments || current?.arguments || ""),
692
701
  ...programCaller(event.item) ?? current?.caller ? { caller: programCaller(event.item) ?? current?.caller } : {}
693
702
  });
694
703
  }
@@ -749,7 +758,7 @@ function createProvider(options) {
749
758
  const isBotConnectorCloud = /^https:\/\/api\.botconnector\.id\/v1\/?$/.test(baseURL) && apiKey?.startsWith("bc_live_");
750
759
  if (isBotConnectorCloud) {
751
760
  headers["x-botconnector-client"] = "bccli";
752
- headers["x-botconnector-client-version"] = "0.4.0";
761
+ headers["x-botconnector-client-version"] = "0.4.2";
753
762
  }
754
763
  const useNativeResponses = isBotConnectorCloud && providerId === "bc-cloud" && model === "gpt-6-luna";
755
764
  const useResponsesWebSocket = useNativeResponses && Boolean(apiKey) && (fetchOverride === void 0 || responsesWebSocketFactory !== void 0);
@@ -780,7 +789,7 @@ function createProvider(options) {
780
789
  baseURL: baseURL.replace(/\/+$/, ""),
781
790
  defaultHeaders: {
782
791
  "x-botconnector-client": "bccli",
783
- "x-botconnector-client-version": "0.4.0"
792
+ "x-botconnector-client-version": "0.4.2"
784
793
  }
785
794
  });
786
795
  lunaWs = responsesWebSocketFactory ? responsesWebSocketFactory(client) : new ResponsesWS(client, { reconnect: null });
@@ -843,8 +852,13 @@ function createProvider(options) {
843
852
  } catch (error) {
844
853
  const err = error instanceof ProviderError ? error : new ProviderError(error.message);
845
854
  const lostPrevious = err.code === "previous_response_not_found";
855
+ const handshakeStatus = /unexpected server response:\s*(\d{3})\b/i.exec(err.message);
856
+ const handshakeRejected = Boolean(handshakeStatus && Number(handshakeStatus[1]) >= 400);
846
857
  const connectionLost = /websocket|connection closed|socket|network/i.test(err.message) || err.code === "websocket_connection_limit_reached";
847
858
  resetLunaWebSocket();
859
+ if (!emittedText && !signal?.aborted && handshakeRejected) {
860
+ throw new ProviderError(err.message, Number(handshakeStatus?.[1]), "bc_ws_http_fallback");
861
+ }
848
862
  if (attempt === 0 && !emittedText && !signal?.aborted && (plan.incremental || lostPrevious || connectionLost)) {
849
863
  continue;
850
864
  }
@@ -858,8 +872,13 @@ function createProvider(options) {
858
872
  }
859
873
  async function post(path, body, signal) {
860
874
  let lastError;
875
+ let retryAfterWaitMs;
861
876
  for (let attempt = 0; attempt < 4; attempt++) {
862
- if (attempt > 0) await sleep(retryDelayMs * 2 ** (attempt - 1), signal);
877
+ if (attempt > 0) {
878
+ const delay = retryAfterWaitMs ?? retryDelayMs * 2 ** (attempt - 1);
879
+ retryAfterWaitMs = void 0;
880
+ await sleep(delay, signal);
881
+ }
863
882
  let res;
864
883
  try {
865
884
  res = await doFetch(`${baseURL}${path}`, { method: "POST", headers, body: JSON.stringify(body), signal });
@@ -869,9 +888,15 @@ function createProvider(options) {
869
888
  continue;
870
889
  }
871
890
  if (res.ok) return res;
891
+ const retryHeader = res.headers.get("retry-after");
892
+ if (retryHeader) {
893
+ const secs = Number(retryHeader);
894
+ if (Number.isFinite(secs) && secs > 0 && secs <= 30) retryAfterWaitMs = Math.ceil(secs * 1e3);
895
+ }
872
896
  const text = await res.text().catch(() => "");
873
897
  lastError = new ProviderError(t("{status} from {url}: {message}", { status: res.status, url: baseURL, message: errorMessage(text) }), res.status);
874
- if (!retryable(res.status)) throw lastError;
898
+ const deterministicQuota429 = res.status === 429 && /"code"\s*:\s*"(?:luna_monthly_quota_exhausted|plan_allowance_exhausted)"/i.test(text);
899
+ if (!retryable(res.status) || deterministicQuota429) throw lastError;
875
900
  }
876
901
  throw lastError ?? new ProviderError(t("Request failed"));
877
902
  }
@@ -930,11 +955,49 @@ function createProvider(options) {
930
955
  }
931
956
  };
932
957
  }
958
+ function withFallbackProvider(primary, fallbacks) {
959
+ return {
960
+ async chat(req) {
961
+ let emitted = false;
962
+ const wrappedReq = {
963
+ ...req,
964
+ onText: req.onText ? (delta) => {
965
+ emitted = true;
966
+ req.onText?.(delta);
967
+ } : () => {
968
+ emitted = true;
969
+ }
970
+ };
971
+ try {
972
+ return await primary.chat(wrappedReq);
973
+ } catch (primaryError) {
974
+ if (emitted || req.signal?.aborted) throw primaryError;
975
+ const status = primaryError instanceof ProviderError ? primaryError.status : void 0;
976
+ const canFallback = status === void 0 || status === 429 || status === 408 || status >= 500;
977
+ if (!canFallback) throw primaryError;
978
+ const chain = fallbacks();
979
+ if (!chain.length) throw primaryError;
980
+ let lastError = primaryError;
981
+ for (const fb of chain) {
982
+ if (req.signal?.aborted) throw lastError;
983
+ try {
984
+ return await fb.chat(req);
985
+ } catch (fbError) {
986
+ lastError = fbError;
987
+ }
988
+ }
989
+ throw lastError;
990
+ }
991
+ },
992
+ listModels: () => primary.listModels()
993
+ };
994
+ }
933
995
 
934
996
  export {
935
997
  contentText,
936
998
  imageUserMessage,
937
999
  hasPendingProgrammaticReplay,
938
1000
  completionAssistantMessage,
939
- createProvider
1001
+ createProvider,
1002
+ withFallbackProvider
940
1003
  };
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- ConfigError
4
- } from "./chunk-LACUWBWS.js";
3
+ ConfigError,
4
+ writeFileAtomic
5
+ } from "./chunk-RX4UQX43.js";
5
6
  import {
6
7
  t
7
- } from "./chunk-UZWJQF67.js";
8
+ } from "./chunk-3WHI4LN3.js";
8
9
 
9
10
  // src/mcp/config.ts
10
11
  import { createHash } from "crypto";
11
- import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
12
+ import { existsSync, mkdirSync, readFileSync } from "fs";
12
13
  import { dirname, join, resolve } from "path";
13
14
  var ENV_REF = /\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g;
14
15
  function expandEnvVars(config, env) {
@@ -41,9 +42,8 @@ function readJson(path, fallback) {
41
42
  }
42
43
  function writePrivate(path, data) {
43
44
  mkdirSync(dirname(path), { recursive: true });
44
- writeFileSync(path, `${JSON.stringify(data, null, 2)}
45
- `, { mode: 384 });
46
- chmodSync(path, 384);
45
+ writeFileAtomic(path, `${JSON.stringify(data, null, 2)}
46
+ `, 384);
47
47
  }
48
48
  function readMcpFile(path) {
49
49
  return readJson(path, {}).mcpServers ?? {};
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  MODE_ORDER
4
- } from "./chunk-O7M3TGIW.js";
4
+ } from "./chunk-WFZKJY3R.js";
5
5
  import {
6
6
  REASONING_LEVELS,
7
7
  isReasoningLevel
8
- } from "./chunk-BLOYFIAD.js";
8
+ } from "./chunk-KPTML72Q.js";
9
9
  import {
10
10
  ConfigError
11
- } from "./chunk-LACUWBWS.js";
11
+ } from "./chunk-RX4UQX43.js";
12
12
  import {
13
13
  parseLang,
14
14
  t
15
- } from "./chunk-UZWJQF67.js";
15
+ } from "./chunk-3WHI4LN3.js";
16
16
 
17
17
  // src/args.ts
18
18
  import { parseArgs } from "util";
@@ -41,9 +41,18 @@ var LOOPBACK_BIND = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1"]);
41
41
  function trustFence(headers, port) {
42
42
  const host = headers.host;
43
43
  if (!host) return "missing Host header";
44
- const sep = host.lastIndexOf(":");
45
- const hostname = sep > -1 ? host.slice(0, sep) : host;
46
- const hostPort = sep > -1 ? Number(host.slice(sep + 1)) : 80;
44
+ let hostname;
45
+ let hostPort;
46
+ if (host.startsWith("[")) {
47
+ const close = host.indexOf("]");
48
+ if (close === -1) return `non-loopback Host: ${host}`;
49
+ hostname = host.slice(0, close + 1);
50
+ hostPort = host[close + 1] === ":" ? Number(host.slice(close + 2)) : 80;
51
+ } else {
52
+ const sep = host.lastIndexOf(":");
53
+ hostname = sep > -1 ? host.slice(0, sep) : host;
54
+ hostPort = sep > -1 ? Number(host.slice(sep + 1)) : 80;
55
+ }
47
56
  if (!LOOPBACK_HOSTS.has(hostname)) return `non-loopback Host: ${hostname}`;
48
57
  if (hostPort !== port) return `Host port ${hostPort} does not match listener ${port}`;
49
58
  if (headers.origin) {
@@ -111,9 +120,12 @@ Options:
111
120
  -h, --help this help`;
112
121
  var helpText = () => t(HELP);
113
122
  function parseCliArgs(argv) {
114
- const expanded = argv.map(
115
- (a, i) => a === "--models" && (i + 1 >= argv.length || argv[i + 1].startsWith("-")) ? "--models=all" : a
116
- );
123
+ const expanded = argv.map((a, i) => {
124
+ const nextIsFlagOrEnd = i + 1 >= argv.length || argv[i + 1].startsWith("-");
125
+ if (a === "--models" && nextIsFlagOrEnd) return "--models=all";
126
+ if (a === "--project" && nextIsFlagOrEnd) return "--project=";
127
+ return a;
128
+ });
117
129
  const { values, positionals } = parseArgs({
118
130
  args: expanded,
119
131
  allowPositionals: true,
@@ -145,7 +157,8 @@ function parseCliArgs(argv) {
145
157
  tools: { type: "string" },
146
158
  models: { type: "string" },
147
159
  project: { type: "string" },
148
- repo: { type: "string" }
160
+ repo: { type: "string" },
161
+ "fallback-model": { type: "string" }
149
162
  }
150
163
  });
151
164
  const reasoning = values.reasoning;
@@ -219,7 +232,8 @@ function parseCliArgs(argv) {
219
232
  tools,
220
233
  models,
221
234
  project: values.project,
222
- repo: values.repo
235
+ repo: values.repo,
236
+ fallbackModel: values["fallback-model"]
223
237
  };
224
238
  }
225
239
 
@@ -2,13 +2,13 @@
2
2
  import {
3
3
  runCommand,
4
4
  runHooks
5
- } from "./chunk-M7CALSP3.js";
5
+ } from "./chunk-ZEPTUKNW.js";
6
6
  import {
7
7
  ConfigError
8
- } from "./chunk-LACUWBWS.js";
8
+ } from "./chunk-RX4UQX43.js";
9
9
  import {
10
10
  t
11
- } from "./chunk-UZWJQF67.js";
11
+ } from "./chunk-3WHI4LN3.js";
12
12
 
13
13
  // src/verify.ts
14
14
  var MAX_VERIFY_ROUNDS = 2;
@@ -40,10 +40,19 @@ function toolLabel(tool) {
40
40
  if (tool === "web_search") return "Search";
41
41
  return tool.charAt(0).toUpperCase() + tool.slice(1);
42
42
  }
43
+ function resolvePrintPrompt(prompt, stdinText) {
44
+ const cleanStdin = stdinText?.trim();
45
+ const cleanPrompt = prompt?.trim();
46
+ if (cleanStdin && cleanPrompt) return `${cleanStdin}
47
+
48
+ ${cleanPrompt}`;
49
+ return cleanPrompt || cleanStdin || void 0;
50
+ }
43
51
  async function runPrint(rt, prompt, io = { out: process.stdout, err: process.stderr }, opts = {}) {
44
52
  const format = opts.outputFormat ?? "text";
45
53
  const emit = (line) => io.out.write(`${JSON.stringify(line)}
46
54
  `);
55
+ const startedMs = Date.now();
47
56
  let result = "";
48
57
  const toolNames = /* @__PURE__ */ new Map();
49
58
  rt.interaction.approvePlan = async () => {
@@ -65,6 +74,9 @@ async function runPrint(rt, prompt, io = { out: process.stdout, err: process.std
65
74
  result += event.delta;
66
75
  }
67
76
  break;
77
+ case "textReplace":
78
+ result = event.text;
79
+ break;
68
80
  case "toolStart":
69
81
  toolNames.set(event.id, event.tool);
70
82
  io.err.write(`\u23BF ${toolLabel(event.tool)} ${event.target}
@@ -142,12 +154,29 @@ async function runPrint(rt, prompt, io = { out: process.stdout, err: process.std
142
154
  for (const warning of stop.warnings) io.err.write(`${warning}
143
155
  `);
144
156
  await rt.emitPlugins("Stop");
157
+ const durationMs = Math.max(0, Date.now() - startedMs);
145
158
  if (format === "json") {
146
- io.out.write(`${JSON.stringify({ status: failed ? "failed" : "ok", model: rt.modelRef, result })}
147
- `);
159
+ io.out.write(
160
+ `${JSON.stringify({
161
+ status: failed ? "failed" : "ok",
162
+ model: rt.modelRef,
163
+ session_id: rt.session.id,
164
+ usage: rt.agent.totalUsage,
165
+ duration_ms: durationMs,
166
+ result
167
+ })}
168
+ `
169
+ );
148
170
  } else if (format === "stream-json") {
149
171
  if (result) emit({ type: "message", text: result });
150
- emit({ type: "result", status: failed ? "failed" : "ok", model: rt.modelRef });
172
+ emit({
173
+ type: "result",
174
+ status: failed ? "failed" : "ok",
175
+ model: rt.modelRef,
176
+ session_id: rt.session.id,
177
+ usage: rt.agent.totalUsage,
178
+ duration_ms: durationMs
179
+ });
151
180
  } else if (wroteText) {
152
181
  io.out.write("\n");
153
182
  }
@@ -225,6 +254,7 @@ export {
225
254
  runVerify,
226
255
  verifyFollowup,
227
256
  toolLabel,
257
+ resolvePrintPrompt,
228
258
  runPrint,
229
259
  resolveWorktree,
230
260
  worktreeListText
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  listAllModels
4
- } from "./chunk-XBO5GACQ.js";
4
+ } from "./chunk-IYR5PYA3.js";
5
5
  import {
6
6
  hasKey,
7
7
  providerName,
8
8
  removeGlobalProvider,
9
9
  writeGlobalConfig
10
- } from "./chunk-SPRNLS7K.js";
10
+ } from "./chunk-HQW4BROS.js";
11
11
  import {
12
12
  PRESETS,
13
13
  loadConfig,
14
14
  saveCredential
15
- } from "./chunk-LACUWBWS.js";
15
+ } from "./chunk-RX4UQX43.js";
16
16
  import {
17
17
  t
18
- } from "./chunk-UZWJQF67.js";
18
+ } from "./chunk-3WHI4LN3.js";
19
19
 
20
20
  // src/providerCli.ts
21
21
  async function addProviderKey(providerId, key, deps) {
@@ -4,11 +4,11 @@ import {
4
4
  bccliHome,
5
5
  readCredentials,
6
6
  readJsonConfig,
7
- removeCredential
8
- } from "./chunk-LACUWBWS.js";
7
+ removeCredential,
8
+ writeFileAtomic
9
+ } from "./chunk-RX4UQX43.js";
9
10
 
10
11
  // src/providers.ts
11
- import { mkdirSync, writeFileSync } from "fs";
12
12
  import { join } from "path";
13
13
  function providerName(config, id) {
14
14
  return PRESETS.find((p) => p.id === id)?.name ?? config.providers[id]?.name ?? id;
@@ -24,8 +24,7 @@ function readGlobalConfigFile(env = process.env) {
24
24
  return readJsonConfig(globalPath(env));
25
25
  }
26
26
  function save(data, env) {
27
- mkdirSync(bccliHome(env), { recursive: true });
28
- writeFileSync(globalPath(env), `${JSON.stringify(data, null, 2)}
27
+ writeFileAtomic(globalPath(env), `${JSON.stringify(data, null, 2)}
29
28
  `);
30
29
  }
31
30
  function writeGlobalConfig(patch, env = process.env) {
@@ -2,17 +2,17 @@
2
2
  import {
3
3
  hasKey,
4
4
  providerName
5
- } from "./chunk-SPRNLS7K.js";
5
+ } from "./chunk-HQW4BROS.js";
6
6
  import {
7
7
  createProvider
8
- } from "./chunk-PUN4HMAG.js";
8
+ } from "./chunk-5YJ3ZQVR.js";
9
9
  import {
10
10
  PRESETS,
11
11
  readCredentials
12
- } from "./chunk-LACUWBWS.js";
12
+ } from "./chunk-RX4UQX43.js";
13
13
  import {
14
14
  t
15
- } from "./chunk-UZWJQF67.js";
15
+ } from "./chunk-3WHI4LN3.js";
16
16
 
17
17
  // src/models.ts
18
18
  function order(ids) {
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-PSO7R5AD.js";
5
5
  import {
6
6
  t
7
- } from "./chunk-UZWJQF67.js";
7
+ } from "./chunk-3WHI4LN3.js";
8
8
 
9
9
  // src/tools/websearch.ts
10
10
  import { z } from "zod";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  t
4
- } from "./chunk-UZWJQF67.js";
4
+ } from "./chunk-3WHI4LN3.js";
5
5
 
6
6
  // src/reasoning.ts
7
7
  var REASONING_LEVELS = ["auto", "off", "low", "medium", "high", "max"];
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigError
4
- } from "./chunk-LACUWBWS.js";
4
+ } from "./chunk-RX4UQX43.js";
5
5
  import {
6
6
  t
7
- } from "./chunk-UZWJQF67.js";
7
+ } from "./chunk-3WHI4LN3.js";
8
8
 
9
9
  // src/mcp/catalog.ts
10
10
  var CATALOG = [
@@ -1,12 +1,36 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/tokenBudget.ts
4
+ var IMAGE_TOKEN_COST = 1e3;
4
5
  var estimateTokens = (text) => Math.ceil(text.length / 4);
5
- var estimateMessages = (messages) => estimateTokens(JSON.stringify(messages));
6
+ function isImageAttachmentMessage(m) {
7
+ if (m.role !== "user" || !Array.isArray(m.content)) return false;
8
+ const hasImage = m.content.some((p) => p.type === "image_url");
9
+ const firstText = m.content.find((p) => p.type === "text");
10
+ return hasImage && !!firstText && firstText.type === "text" && firstText.text.startsWith("Image read from ");
11
+ }
12
+ function isUserTurnBoundary(m) {
13
+ return m.role === "user" && !isImageAttachmentMessage(m);
14
+ }
15
+ function estimateMessages(messages) {
16
+ let total = 0;
17
+ for (const m of messages) {
18
+ if (m.role === "user" && Array.isArray(m.content)) {
19
+ for (const part of m.content) {
20
+ if (part.type === "image_url") total += IMAGE_TOKEN_COST;
21
+ else total += estimateTokens(part.text);
22
+ }
23
+ total += 8;
24
+ } else {
25
+ total += estimateTokens(JSON.stringify(m));
26
+ }
27
+ }
28
+ return total;
29
+ }
6
30
  function trimToFit(messages, maxTokens) {
7
31
  let out = messages;
8
32
  while (estimateMessages(out) > maxTokens) {
9
- const boundary = out.findIndex((m, i) => i > 0 && m.role === "user");
33
+ const boundary = out.findIndex((m, i) => i > 0 && isUserTurnBoundary(m));
10
34
  if (boundary === -1) return out;
11
35
  out = out.slice(boundary);
12
36
  }
@@ -16,13 +40,13 @@ function splitKeepRecent(messages, keepTokens) {
16
40
  if (messages.length === 0) return [[], []];
17
41
  let start = messages.length;
18
42
  for (let i = messages.length - 1; i >= 0; i--) {
19
- if (i > 0 && messages[i].role !== "user") continue;
43
+ if (i > 0 && !isUserTurnBoundary(messages[i])) continue;
20
44
  if (estimateMessages(messages.slice(i)) <= keepTokens) start = i;
21
45
  else break;
22
46
  }
23
47
  if (start === messages.length) {
24
48
  for (let i = messages.length - 1; i >= 0; i--) {
25
- if (messages[i].role === "user") {
49
+ if (isUserTurnBoundary(messages[i])) {
26
50
  start = i;
27
51
  break;
28
52
  }