@bunny-agent/runner-cli 0.9.50 → 0.9.51-beta.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 (2) hide show
  1. package/dist/bundle.mjs +296 -74
  2. package/package.json +9 -8
package/dist/bundle.mjs CHANGED
@@ -700,7 +700,11 @@ function createSDKOptions(options) {
700
700
  ],
701
701
  cwd: options.cwd,
702
702
  env: options.env,
703
- resume: options.resume,
703
+ // forkFrom snapshot-clones the source session into a new session id;
704
+ // plain resume continues the same session.
705
+ resume: options.forkFrom ?? options.resume,
706
+ forkSession: options.forkFrom != null ? true : void 0,
707
+ ...options.skills ? { skills: options.skills } : {},
704
708
  settingSources: ["project", "user"],
705
709
  canUseTool: createCanUseToolCallback(options),
706
710
  permissionMode: isRoot || options.yolo ? "bypassPermissions" : "default",
@@ -894,18 +898,33 @@ function toToolEndPayload(event) {
894
898
  }
895
899
  return null;
896
900
  }
897
- function toAssistantText(event) {
898
- if (event.type === "item.completed" && event.item.type === "agent_message") {
899
- return event.item.text;
900
- }
901
- if (event.type === "item.completed" && event.item.type === "reasoning") {
902
- return `[Reasoning] ${event.item.text}`;
901
+ function sseData(obj) {
902
+ return `data: ${JSON.stringify(obj)}
903
+
904
+ `;
905
+ }
906
+ var ItemTextTracker = class {
907
+ emittedLength = /* @__PURE__ */ new Map();
908
+ started = /* @__PURE__ */ new Set();
909
+ /** Returns the new delta for this item, or empty string if nothing new. */
910
+ delta(itemId, fullText) {
911
+ const emitted = this.emittedLength.get(itemId) ?? 0;
912
+ if (fullText.length <= emitted)
913
+ return "";
914
+ this.emittedLength.set(itemId, fullText.length);
915
+ return fullText.slice(emitted);
916
+ }
917
+ /** Returns true the first time an item id is seen (caller emits text-start). */
918
+ markStarted(itemId) {
919
+ if (this.started.has(itemId))
920
+ return false;
921
+ this.started.add(itemId);
922
+ return true;
903
923
  }
904
- if (event.type === "item.completed" && event.item.type === "error") {
905
- return `[Error] ${event.item.message}`;
924
+ isStarted(itemId) {
925
+ return this.started.has(itemId);
906
926
  }
907
- return null;
908
- }
927
+ };
909
928
  function createCodexRunner(options) {
910
929
  const codex = new Codex({
911
930
  apiKey: process.env.CODEX_API_KEY || process.env.OPENAI_API_KEY,
@@ -954,62 +973,170 @@ function createCodexRunner(options) {
954
973
  }
955
974
  } catch (_e) {
956
975
  }
976
+ if (options.systemPrompt && !options.resume) {
977
+ if (typeof inputToCodex === "string") {
978
+ inputToCodex = `${options.systemPrompt}
979
+
980
+ ${inputToCodex}`;
981
+ } else {
982
+ inputToCodex = [
983
+ { type: "text", text: options.systemPrompt },
984
+ ...inputToCodex
985
+ ];
986
+ }
987
+ }
957
988
  const streamedTurn = await thread.runStreamed(inputToCodex, {
958
989
  signal: options.abortController?.signal
959
990
  });
991
+ let sessionId = options.resume ?? void 0;
992
+ let startEmitted = false;
993
+ let finished = false;
994
+ const textTracker = new ItemTextTracker();
995
+ const reasoningTracker = new ItemTextTracker();
996
+ const ensureStart = () => {
997
+ if (startEmitted)
998
+ return [];
999
+ startEmitted = true;
1000
+ return [
1001
+ sseData({ type: "start" }),
1002
+ sseData({
1003
+ type: "message-metadata",
1004
+ messageMetadata: { sessionId: sessionId ?? thread.id ?? void 0 }
1005
+ })
1006
+ ];
1007
+ };
960
1008
  for await (const event of streamedTurn.events) {
961
- const assistantText = toAssistantText(event);
962
- if (assistantText) {
963
- yield `data: ${JSON.stringify({ type: "text-delta", delta: assistantText })}
964
-
965
- `;
1009
+ if (event.type === "thread.started") {
1010
+ sessionId = event.thread_id;
1011
+ yield* ensureStart();
1012
+ continue;
1013
+ }
1014
+ yield* ensureStart();
1015
+ if ((event.type === "item.started" || event.type === "item.updated" || event.type === "item.completed") && event.item.type === "agent_message") {
1016
+ const item = event.item;
1017
+ if (textTracker.markStarted(item.id)) {
1018
+ yield sseData({ type: "text-start", id: item.id });
1019
+ }
1020
+ const delta = textTracker.delta(item.id, item.text);
1021
+ if (delta) {
1022
+ yield sseData({ type: "text-delta", id: item.id, delta });
1023
+ }
1024
+ if (event.type === "item.completed") {
1025
+ yield sseData({ type: "text-end", id: item.id });
1026
+ }
1027
+ continue;
1028
+ }
1029
+ if ((event.type === "item.updated" || event.type === "item.completed") && event.item.type === "reasoning") {
1030
+ const delta = reasoningTracker.delta(event.item.id, event.item.text);
1031
+ if (delta) {
1032
+ yield sseData({ type: "reasoning", text: delta });
1033
+ }
1034
+ continue;
1035
+ }
1036
+ if (event.type === "item.completed" && event.item.type === "error") {
1037
+ const id = `error-item-${event.item.id}`;
1038
+ yield sseData({ type: "text-start", id });
1039
+ yield sseData({
1040
+ type: "text-delta",
1041
+ id,
1042
+ delta: `[Error] ${event.item.message}`
1043
+ });
1044
+ yield sseData({ type: "text-end", id });
1045
+ continue;
1046
+ }
1047
+ if (event.type === "item.completed" && event.item.type === "file_change") {
1048
+ const item = event.item;
1049
+ yield sseData({
1050
+ type: "tool-input-start",
1051
+ toolCallId: item.id,
1052
+ toolName: "apply_patch"
1053
+ });
1054
+ yield sseData({
1055
+ type: "tool-input-available",
1056
+ toolCallId: item.id,
1057
+ toolName: "apply_patch",
1058
+ input: { changes: item.changes }
1059
+ });
1060
+ yield sseData({
1061
+ type: "tool-output-available",
1062
+ toolCallId: item.id,
1063
+ output: { status: item.status, changes: item.changes },
1064
+ isError: item.status === "failed"
1065
+ });
1066
+ continue;
966
1067
  }
967
1068
  const toolStart = toToolStartPayload(event);
968
1069
  if (toolStart) {
969
- yield `data: ${JSON.stringify({ type: "tool-input-start", toolCallId: toolStart.toolCallId, toolName: toolStart.toolName })}
970
-
971
- `;
972
- yield `data: ${JSON.stringify({ type: "tool-input-available", toolCallId: toolStart.toolCallId, toolName: toolStart.toolName, input: toolStart.args })}
973
-
974
- `;
1070
+ yield sseData({
1071
+ type: "tool-input-start",
1072
+ toolCallId: toolStart.toolCallId,
1073
+ toolName: toolStart.toolName
1074
+ });
1075
+ yield sseData({
1076
+ type: "tool-input-available",
1077
+ toolCallId: toolStart.toolCallId,
1078
+ toolName: toolStart.toolName,
1079
+ input: toolStart.args
1080
+ });
975
1081
  }
976
1082
  const toolEnd = toToolEndPayload(event);
977
1083
  if (toolEnd) {
978
- yield `data: ${JSON.stringify({ type: "tool-output-available", toolCallId: toolEnd.toolCallId, output: toolEnd.result, isError: toolEnd.isError })}
979
-
980
- `;
1084
+ yield sseData({
1085
+ type: "tool-output-available",
1086
+ toolCallId: toolEnd.toolCallId,
1087
+ output: toolEnd.result,
1088
+ isError: toolEnd.isError
1089
+ });
981
1090
  }
982
1091
  if (event.type === "turn.completed") {
983
- yield `data: ${JSON.stringify({ type: "finish", finishReason: "stop", usage: event.usage })}
984
-
985
- `;
1092
+ yield sseData({
1093
+ type: "finish",
1094
+ finishReason: "stop",
1095
+ messageMetadata: {
1096
+ sessionId: sessionId ?? thread.id ?? void 0,
1097
+ usage: {
1098
+ input_tokens: event.usage.input_tokens,
1099
+ output_tokens: event.usage.output_tokens,
1100
+ cache_read_input_tokens: event.usage.cached_input_tokens
1101
+ }
1102
+ }
1103
+ });
986
1104
  yield `data: [DONE]
987
1105
 
988
1106
  `;
1107
+ finished = true;
989
1108
  }
990
1109
  if (event.type === "turn.failed") {
991
- yield `data: ${JSON.stringify({ type: "error", errorText: event.error.message })}
992
-
993
- `;
994
- yield `data: ${JSON.stringify({ type: "finish", finishReason: "error" })}
995
-
996
- `;
1110
+ yield sseData({ type: "error", errorText: event.error.message });
1111
+ yield sseData({ type: "finish", finishReason: "error" });
997
1112
  yield `data: [DONE]
998
1113
 
999
1114
  `;
1115
+ finished = true;
1000
1116
  }
1001
1117
  if (event.type === "error") {
1002
- yield `data: ${JSON.stringify({ type: "error", errorText: stringifyUnknown(event.message) })}
1003
-
1004
- `;
1005
- yield `data: ${JSON.stringify({ type: "finish", finishReason: "error" })}
1006
-
1007
- `;
1118
+ yield sseData({
1119
+ type: "error",
1120
+ errorText: stringifyUnknown(event.message)
1121
+ });
1122
+ yield sseData({ type: "finish", finishReason: "error" });
1008
1123
  yield `data: [DONE]
1009
1124
 
1010
1125
  `;
1126
+ finished = true;
1011
1127
  }
1012
1128
  }
1129
+ if (!finished) {
1130
+ yield* ensureStart();
1131
+ yield sseData({
1132
+ type: "error",
1133
+ errorText: "Codex stream ended unexpectedly before completing."
1134
+ });
1135
+ yield sseData({ type: "finish", finishReason: "error" });
1136
+ yield `data: [DONE]
1137
+
1138
+ `;
1139
+ }
1013
1140
  for (const tmpFile of tempFiles) {
1014
1141
  try {
1015
1142
  fs.unlinkSync(tmpFile);
@@ -1303,7 +1430,7 @@ function createOpenCodeRunner(options = {}) {
1303
1430
  // ../../packages/runner-pi/dist/pi-runner.js
1304
1431
  import { appendFileSync as appendFileSync2, existsSync as existsSync5, unlinkSync as unlinkSync3 } from "node:fs";
1305
1432
  import { join as join8 } from "node:path";
1306
- import { getModel } from "@earendil-works/pi-ai";
1433
+ import { getBuiltinModel } from "@earendil-works/pi-ai/providers/all";
1307
1434
  import { AuthStorage, createAgentSession, ModelRegistry, SessionManager as SessionManager2 } from "@earendil-works/pi-coding-agent";
1308
1435
 
1309
1436
  // ../../packages/runner-pi/dist/bunny-agent-resource-loader.js
@@ -2077,7 +2204,7 @@ function extractToolResultText(result) {
2077
2204
  return String(result);
2078
2205
  }
2079
2206
  }
2080
- function sseData(obj) {
2207
+ function sseData2(obj) {
2081
2208
  return "data: " + JSON.stringify(obj) + "\n\n";
2082
2209
  }
2083
2210
  var PiAISDKStreamConverter = class {
@@ -2121,13 +2248,13 @@ var PiAISDKStreamConverter = class {
2121
2248
  }
2122
2249
  if (event.type === "tool_execution_start") {
2123
2250
  chunks.push(...this.endTextStreamIfOpen());
2124
- chunks.push(sseData({
2251
+ chunks.push(sseData2({
2125
2252
  type: "tool-input-start",
2126
2253
  toolCallId: event.toolCallId,
2127
2254
  toolName: event.toolName,
2128
2255
  dynamic: true,
2129
2256
  providerExecuted: true
2130
- }), sseData({
2257
+ }), sseData2({
2131
2258
  type: "tool-input-available",
2132
2259
  toolCallId: event.toolCallId,
2133
2260
  toolName: event.toolName,
@@ -2143,7 +2270,7 @@ var PiAISDKStreamConverter = class {
2143
2270
  if (raw != null)
2144
2271
  accumulateToolUsage(this.toolUsageTally, raw);
2145
2272
  if (event.isError) {
2146
- chunks.push(sseData({
2273
+ chunks.push(sseData2({
2147
2274
  type: "tool-output-error",
2148
2275
  toolCallId: event.toolCallId,
2149
2276
  errorText: output,
@@ -2151,7 +2278,7 @@ var PiAISDKStreamConverter = class {
2151
2278
  providerExecuted: true
2152
2279
  }));
2153
2280
  } else {
2154
- chunks.push(sseData({
2281
+ chunks.push(sseData2({
2155
2282
  type: "tool-output-available",
2156
2283
  toolCallId: event.toolCallId,
2157
2284
  output,
@@ -2180,8 +2307,8 @@ var PiAISDKStreamConverter = class {
2180
2307
  return [];
2181
2308
  this.hasStarted = true;
2182
2309
  return [
2183
- sseData({ type: "start", messageId: this.messageId }),
2184
- sseData({
2310
+ sseData2({ type: "start", messageId: this.messageId }),
2311
+ sseData2({
2185
2312
  type: "message-metadata",
2186
2313
  messageMetadata: { sessionId: this.options.sessionId }
2187
2314
  })
@@ -2192,7 +2319,7 @@ var PiAISDKStreamConverter = class {
2192
2319
  }
2193
2320
  openTextStream() {
2194
2321
  this.activeTextPartId = this.newTextPartId();
2195
- return [sseData({ type: "text-start", id: this.activeTextPartId })];
2322
+ return [sseData2({ type: "text-start", id: this.activeTextPartId })];
2196
2323
  }
2197
2324
  emitTextDelta(rawDelta) {
2198
2325
  if (!rawDelta)
@@ -2200,7 +2327,7 @@ var PiAISDKStreamConverter = class {
2200
2327
  const startChunk = this.activeTextPartId == null ? this.openTextStream() : [];
2201
2328
  return [
2202
2329
  ...startChunk,
2203
- sseData({
2330
+ sseData2({
2204
2331
  type: "text-delta",
2205
2332
  id: this.activeTextPartId,
2206
2333
  delta: rawDelta
@@ -2212,7 +2339,7 @@ var PiAISDKStreamConverter = class {
2212
2339
  return [];
2213
2340
  const id = this.activeTextPartId;
2214
2341
  this.activeTextPartId = null;
2215
- return [sseData({ type: "text-end", id })];
2342
+ return [sseData2({ type: "text-end", id })];
2216
2343
  }
2217
2344
  finishSuccess(usage) {
2218
2345
  const chunks = [...this.endTextStreamIfOpen()];
@@ -2236,7 +2363,7 @@ var PiAISDKStreamConverter = class {
2236
2363
  if (usage) {
2237
2364
  finishPayload.messageMetadata = { usage: { ...chatUsage, raw } };
2238
2365
  }
2239
- chunks.push(sseData(finishPayload), "data: [DONE]\n\n");
2366
+ chunks.push(sseData2(finishPayload), "data: [DONE]\n\n");
2240
2367
  this.hasFinished = true;
2241
2368
  return chunks;
2242
2369
  }
@@ -5349,12 +5476,79 @@ function serializeResult(result) {
5349
5476
 
5350
5477
  // ../../packages/runner-pi/dist/pi-runner.js
5351
5478
  var LOG_PREFIX2 = "[bunny-agent:pi]";
5479
+ var LLM_THOUGHT_SIGNATURE_SEPARATOR = "__thought__";
5352
5480
  function applyAllowedTools(tools, allowedTools) {
5353
5481
  if (!allowedTools)
5354
5482
  return tools;
5355
5483
  const allowed = new Set(allowedTools);
5356
5484
  return tools.filter((tool) => allowed.has(tool.name));
5357
5485
  }
5486
+ function shouldStripLLMThoughtSignaturesForModel(model) {
5487
+ const id = (model.id ?? "").toLowerCase();
5488
+ return !id.includes("gemini");
5489
+ }
5490
+ function stripLLMThoughtSignatureFromId(id) {
5491
+ const separatorIndex = id.indexOf(LLM_THOUGHT_SIGNATURE_SEPARATOR);
5492
+ if (separatorIndex === -1)
5493
+ return id;
5494
+ return id.slice(0, separatorIndex);
5495
+ }
5496
+ function stripLLMThoughtSignaturesFromSessionManager(sessionManager, model) {
5497
+ if (!shouldStripLLMThoughtSignaturesForModel(model))
5498
+ return;
5499
+ const manager = sessionManager;
5500
+ const entries = manager.getEntries?.();
5501
+ if (Array.isArray(entries)) {
5502
+ for (const entry of entries) {
5503
+ if (entry == null || typeof entry !== "object")
5504
+ continue;
5505
+ const message = entry.message;
5506
+ if (entry.type === "message") {
5507
+ stripLLMThoughtSignaturesFromMessage(message);
5508
+ }
5509
+ }
5510
+ return;
5511
+ }
5512
+ const context = sessionManager.buildSessionContext?.();
5513
+ const messages = context?.messages;
5514
+ if (!Array.isArray(messages))
5515
+ return;
5516
+ for (const message of messages) {
5517
+ stripLLMThoughtSignaturesFromMessage(message);
5518
+ }
5519
+ }
5520
+ function stripLLMThoughtSignaturesFromMessage(message) {
5521
+ if (message == null || typeof message !== "object")
5522
+ return;
5523
+ const msg = message;
5524
+ if (typeof msg.toolCallId === "string") {
5525
+ msg.toolCallId = stripLLMThoughtSignatureFromId(msg.toolCallId);
5526
+ }
5527
+ if (typeof msg.tool_call_id === "string") {
5528
+ msg.tool_call_id = stripLLMThoughtSignatureFromId(msg.tool_call_id);
5529
+ }
5530
+ if (!Array.isArray(msg.content))
5531
+ return;
5532
+ for (const block of msg.content) {
5533
+ if (block == null || typeof block !== "object")
5534
+ continue;
5535
+ const contentBlock = block;
5536
+ if (contentBlock.type !== "toolCall")
5537
+ continue;
5538
+ if (typeof contentBlock.id === "string") {
5539
+ contentBlock.id = stripLLMThoughtSignatureFromId(contentBlock.id);
5540
+ }
5541
+ if (typeof contentBlock.toolCallId === "string") {
5542
+ contentBlock.toolCallId = stripLLMThoughtSignatureFromId(contentBlock.toolCallId);
5543
+ }
5544
+ if (typeof contentBlock.tool_call_id === "string") {
5545
+ contentBlock.tool_call_id = stripLLMThoughtSignatureFromId(contentBlock.tool_call_id);
5546
+ }
5547
+ if (typeof contentBlock.call_id === "string") {
5548
+ contentBlock.call_id = stripLLMThoughtSignatureFromId(contentBlock.call_id);
5549
+ }
5550
+ }
5551
+ }
5358
5552
  function parseModelSpec(model) {
5359
5553
  const trimmed = model.trim();
5360
5554
  const separator = trimmed.indexOf(":");
@@ -5434,7 +5628,7 @@ function createPiRunner(options = {}) {
5434
5628
  const apiKeyEnvKey = `${provider.toUpperCase().replace(/-/g, "_")}_API_KEY`;
5435
5629
  const inlineApiKey = typeof options.env?.[apiKeyEnvKey] === "string" && options.env[apiKeyEnvKey].length > 0 ? options.env[apiKeyEnvKey] : void 0;
5436
5630
  const modelRegistry = ModelRegistry.inMemory(AuthStorage.create());
5437
- const defaultModel = getModel(provider, modelName);
5631
+ const defaultModel = getBuiltinModel(provider, modelName);
5438
5632
  let model = defaultModel ?? modelRegistry.find(provider, modelName);
5439
5633
  if (model == null) {
5440
5634
  const baseUrlEnvKey = `${provider.toUpperCase().replace(/-/g, "_")}_BASE_URL`;
@@ -5504,7 +5698,8 @@ function createPiRunner(options = {}) {
5504
5698
  }
5505
5699
  return SessionManager2.create(cwd);
5506
5700
  })();
5507
- const resourceLoader = options.skillPaths ? new BunnyAgentResourceLoader({
5701
+ stripLLMThoughtSignaturesFromSessionManager(sessionManager, model);
5702
+ const resourceLoader = options.skillPaths || options.systemPrompt ? new BunnyAgentResourceLoader({
5508
5703
  cwd,
5509
5704
  skillPaths: options.skillPaths,
5510
5705
  appendSystemPrompt: options.systemPrompt
@@ -5537,7 +5732,9 @@ function createPiRunner(options = {}) {
5537
5732
  ]
5538
5733
  });
5539
5734
  const eventQueue = [];
5540
- let isComplete = false;
5735
+ let promptSettled = false;
5736
+ let promptError;
5737
+ let finalAgentEnd;
5541
5738
  let aborted = false;
5542
5739
  let wakeConsumer = null;
5543
5740
  const notify = () => {
@@ -5546,15 +5743,11 @@ function createPiRunner(options = {}) {
5546
5743
  };
5547
5744
  const unsubscribe = session.subscribe((e) => {
5548
5745
  eventQueue.push(e);
5549
- if (e.type === "agent_end") {
5550
- isComplete = true;
5551
- }
5552
5746
  notify();
5553
5747
  });
5554
5748
  const abortSignal = options.abortController?.signal;
5555
5749
  const abortHandler = () => {
5556
5750
  aborted = true;
5557
- isComplete = true;
5558
5751
  void session.abort();
5559
5752
  notify();
5560
5753
  };
@@ -5568,6 +5761,14 @@ function createPiRunner(options = {}) {
5568
5761
  traceRawMessage(cwd, null, true, options.env);
5569
5762
  const promptText = userInput;
5570
5763
  const promptPromise = session.prompt(promptText);
5764
+ void promptPromise.then(() => {
5765
+ promptSettled = true;
5766
+ notify();
5767
+ }, (error) => {
5768
+ promptError = error;
5769
+ promptSettled = true;
5770
+ notify();
5771
+ });
5571
5772
  const streamConverter = new PiAISDKStreamConverter({
5572
5773
  sessionId: session.sessionId,
5573
5774
  model,
@@ -5575,10 +5776,14 @@ function createPiRunner(options = {}) {
5575
5776
  getUsageFromAgentEndMessages,
5576
5777
  getErrorFromAgentEndMessages
5577
5778
  });
5578
- while (!isComplete || eventQueue.length > 0) {
5779
+ while (!promptSettled && !aborted || eventQueue.length > 0) {
5579
5780
  while (eventQueue.length > 0) {
5580
5781
  const event = eventQueue.shift();
5581
5782
  traceRawMessage(cwd, event, false, options.env);
5783
+ if (event.type === "agent_end") {
5784
+ finalAgentEnd = event;
5785
+ continue;
5786
+ }
5582
5787
  const chunks = streamConverter.handleEvent(event, aborted);
5583
5788
  for (const chunk of chunks) {
5584
5789
  yield chunk;
@@ -5590,30 +5795,35 @@ function createPiRunner(options = {}) {
5590
5795
  }
5591
5796
  break;
5592
5797
  }
5593
- if (!isComplete && eventQueue.length === 0) {
5798
+ if (!promptSettled && !aborted && eventQueue.length === 0) {
5594
5799
  await new Promise((resolve4) => {
5595
5800
  wakeConsumer = resolve4;
5596
5801
  });
5597
5802
  }
5598
5803
  }
5599
- if (streamConverter.finished) {
5804
+ if (aborted) {
5600
5805
  return;
5601
5806
  }
5602
- try {
5603
- await promptPromise;
5604
- } catch (error) {
5605
- if (!streamConverter.finished) {
5606
- const message = error instanceof Error ? error.message : "Pi agent run failed.";
5607
- for (const chunk of streamConverter.forceError(message)) {
5608
- yield chunk;
5609
- }
5807
+ if (promptError !== void 0) {
5808
+ const message = promptError instanceof Error ? promptError.message : "Pi agent run failed.";
5809
+ for (const chunk of streamConverter.forceError(message)) {
5810
+ yield chunk;
5610
5811
  }
5611
5812
  return;
5612
5813
  }
5613
- if (!streamConverter.finished && session.agent.state.errorMessage) {
5814
+ if (finalAgentEnd) {
5815
+ const chunks = streamConverter.handleEvent(finalAgentEnd, false);
5816
+ for (const chunk of chunks) {
5817
+ yield chunk;
5818
+ }
5819
+ } else if (session.agent.state.errorMessage) {
5614
5820
  for (const chunk of streamConverter.forceError(session.agent.state.errorMessage)) {
5615
5821
  yield chunk;
5616
5822
  }
5823
+ } else {
5824
+ for (const chunk of streamConverter.forceError("Pi agent run completed without an agent_end event.")) {
5825
+ yield chunk;
5826
+ }
5617
5827
  }
5618
5828
  } finally {
5619
5829
  if (abortSignal) {
@@ -5707,10 +5917,21 @@ function dispatchRunner(runner, base, cwd, options) {
5707
5917
  const _env = base.env;
5708
5918
  switch (runner) {
5709
5919
  case "claude":
5710
- return createClaudeRunner(base).run(options.userInput);
5920
+ return createClaudeRunner({
5921
+ ...base,
5922
+ cwd,
5923
+ forkFrom: options.forkFrom
5924
+ }).run(options.userInput);
5711
5925
  case "codex": {
5712
5926
  const { outputFormat: _of, ...codexBase } = base;
5713
- return createCodexRunner({ ...codexBase, cwd }).run(options.userInput);
5927
+ const codexEfforts = ["minimal", "low", "medium", "high", "xhigh"];
5928
+ return createCodexRunner({
5929
+ ...codexBase,
5930
+ cwd,
5931
+ ...options.effort && codexEfforts.includes(options.effort) ? {
5932
+ modelReasoningEffort: options.effort
5933
+ } : {}
5934
+ }).run(options.userInput);
5714
5935
  }
5715
5936
  case "gemini":
5716
5937
  return createGeminiRunner({
@@ -5756,7 +5977,8 @@ async function* captureSessionId(stream, cwd) {
5756
5977
  writeSessionId(cwd, sessionId);
5757
5978
  }
5758
5979
  }
5759
- } catch {
5980
+ } catch (error) {
5981
+ console.debug("[captureSessionId] failed to parse chunk", error);
5760
5982
  }
5761
5983
  }
5762
5984
  yield chunk;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunny-agent/runner-cli",
3
- "version": "0.9.50",
3
+ "version": "0.9.51-beta.2",
4
4
  "description": "BunnyAgent Runner CLI - Like gemini-cli or claude-code, runs in your local terminal with AI SDK UI streaming",
5
5
  "type": "module",
6
6
  "bin": {
@@ -40,11 +40,12 @@
40
40
  "anthropic"
41
41
  ],
42
42
  "dependencies": {
43
- "@anthropic-ai/claude-agent-sdk": "0.2.70",
44
- "@earendil-works/pi-agent-core": "0.78.0",
45
- "@earendil-works/pi-ai": "0.78.0",
46
- "@earendil-works/pi-coding-agent": "0.78.0",
47
- "@openai/codex-sdk": "^0.110.0",
43
+ "@anthropic-ai/claude-agent-sdk": "0.3.211",
44
+ "@anthropic-ai/sdk": "0.111.0",
45
+ "@earendil-works/pi-agent-core": "0.80.7",
46
+ "@earendil-works/pi-ai": "0.80.7",
47
+ "@earendil-works/pi-coding-agent": "0.80.7",
48
+ "@openai/codex-sdk": "^0.144.5",
48
49
  "dotenv": "^17.3.1",
49
50
  "zod": "^4.0.0"
50
51
  },
@@ -53,11 +54,11 @@
53
54
  "esbuild": "^0.27.2",
54
55
  "typescript": "^5.3.0",
55
56
  "vitest": "^1.6.1",
57
+ "@bunny-agent/runner-claude": "0.6.2",
58
+ "@bunny-agent/runner-codex": "0.6.2",
56
59
  "@bunny-agent/runner-harness": "0.1.1-beta.0",
57
60
  "@bunny-agent/runner-gemini": "0.6.2",
58
- "@bunny-agent/runner-claude": "0.6.2",
59
61
  "@bunny-agent/runner-opencode": "0.6.2",
60
- "@bunny-agent/runner-codex": "0.6.2",
61
62
  "@bunny-agent/runner-pi": "0.6.4-beta.0"
62
63
  },
63
64
  "scripts": {