@bunny-agent/runner-cli 0.10.1-beta.2 → 0.10.3-beta1

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 +192 -20
  2. package/package.json +2 -2
package/dist/bundle.mjs CHANGED
@@ -39690,6 +39690,89 @@ function createOpenCodeRunner(options = {}) {
39690
39690
  // ../../packages/runner-pi/dist/live-control.js
39691
39691
  import { AsyncLocalStorage } from "node:async_hooks";
39692
39692
  import { randomBytes as randomBytes2 } from "node:crypto";
39693
+ import { createAssistantMessageEventStream as createAssistantMessageEventStream2 } from "@earendil-works/pi-ai";
39694
+
39695
+ // ../../packages/runner-pi/dist/generation-stream.js
39696
+ import { createAssistantMessageEventStream } from "@earendil-works/pi-ai";
39697
+ function fenceGenerationStream(source, model, signal, onSettled) {
39698
+ const target = createAssistantMessageEventStream();
39699
+ let partial3;
39700
+ let closed = false;
39701
+ const close = () => {
39702
+ closed = true;
39703
+ signal.removeEventListener("abort", abort);
39704
+ onSettled();
39705
+ };
39706
+ const finishFailure = (error109) => {
39707
+ if (closed)
39708
+ return;
39709
+ const message = partial3 ? structuredClone(partial3) : {
39710
+ role: "assistant",
39711
+ content: [],
39712
+ api: model.api,
39713
+ provider: model.provider,
39714
+ model: model.id,
39715
+ timestamp: Date.now(),
39716
+ stopReason: "aborted",
39717
+ usage: {
39718
+ input: 0,
39719
+ output: 0,
39720
+ cacheRead: 0,
39721
+ cacheWrite: 0,
39722
+ totalTokens: 0,
39723
+ cost: {
39724
+ input: 0,
39725
+ output: 0,
39726
+ cacheRead: 0,
39727
+ cacheWrite: 0,
39728
+ total: 0
39729
+ }
39730
+ }
39731
+ };
39732
+ message.stopReason = error109 === void 0 ? "aborted" : "error";
39733
+ if (error109 === void 0)
39734
+ delete message.errorMessage;
39735
+ else
39736
+ message.errorMessage = error109 instanceof Error ? error109.message : String(error109);
39737
+ close();
39738
+ target.push({ type: "error", reason: message.stopReason, error: message });
39739
+ target.end();
39740
+ source.end(message);
39741
+ };
39742
+ const abort = () => finishFailure();
39743
+ signal.addEventListener("abort", abort, { once: true });
39744
+ if (signal.aborted)
39745
+ abort();
39746
+ void (async () => {
39747
+ try {
39748
+ for await (const event of source) {
39749
+ if (closed)
39750
+ break;
39751
+ if ("partial" in event)
39752
+ partial3 = event.partial;
39753
+ if (event.type === "done" || event.type === "error") {
39754
+ close();
39755
+ target.push(event);
39756
+ target.end();
39757
+ return;
39758
+ }
39759
+ target.push(event);
39760
+ }
39761
+ if (!closed) {
39762
+ const message = await source.result();
39763
+ if (closed)
39764
+ return;
39765
+ close();
39766
+ target.end(message);
39767
+ }
39768
+ } catch (error109) {
39769
+ finishFailure(error109);
39770
+ }
39771
+ })();
39772
+ return target;
39773
+ }
39774
+
39775
+ // ../../packages/runner-pi/dist/live-control.js
39693
39776
  function createPiLiveController(session, onFirstStreamInvocation) {
39694
39777
  const originalSteeringMode = session.agent.steeringMode;
39695
39778
  session.agent.steeringMode = "one-at-a-time";
@@ -39699,6 +39782,7 @@ function createPiLiveController(session, onFirstStreamInvocation) {
39699
39782
  let consumedCallback;
39700
39783
  let disposed = false;
39701
39784
  let firstStreamInvocationReported = false;
39785
+ let activeGeneration;
39702
39786
  const reportIfConfirmed = (dispatch) => {
39703
39787
  if (!disposed && dispatch.outcome === "queued" && dispatch.captured.length === 1 && dispatch.observed && !dispatch.reported) {
39704
39788
  dispatch.reported = true;
@@ -39720,40 +39804,93 @@ function createPiLiveController(session, onFirstStreamInvocation) {
39720
39804
  session.agent.steer = installedAgentSteer;
39721
39805
  const originalStreamFunction = session.agent.streamFunction;
39722
39806
  const wrappedStreamFunction = (model, context, streamOptions) => {
39807
+ const generation = new AbortController();
39808
+ activeGeneration = generation;
39809
+ const parentSignal = streamOptions?.signal;
39810
+ const abortFromParent = () => generation.abort(parentSignal?.reason);
39811
+ parentSignal?.addEventListener("abort", abortFromParent, { once: true });
39812
+ if (parentSignal?.aborted)
39813
+ abortFromParent();
39814
+ const settle2 = () => {
39815
+ parentSignal?.removeEventListener("abort", abortFromParent);
39816
+ if (activeGeneration === generation)
39817
+ activeGeneration = void 0;
39818
+ };
39723
39819
  const observed = /* @__PURE__ */ new Set();
39724
- const sanitizedMessages = context.messages.map((candidate) => {
39820
+ const sanitizedMessages = context.messages.flatMap((candidate) => {
39821
+ if (candidate.role === "assistant" && candidate.stopReason === "aborted") {
39822
+ const text = candidate.content.filter((part) => part.type === "text" && part.text.trim().length > 0);
39823
+ return text.length ? [{ ...candidate, content: text, stopReason: "stop" }] : [];
39824
+ }
39725
39825
  if (!Object.getOwnPropertyDescriptor(candidate, "__bunnyLiveControl"))
39726
- return candidate;
39826
+ return [candidate];
39727
39827
  const { __bunnyLiveControl: marker, ...rest } = candidate;
39728
39828
  if (marker && marker.controllerNonce === controllerNonce && candidate.role === "user") {
39729
39829
  const dispatch = dispatches.get(marker.stableId);
39730
39830
  if (dispatch)
39731
39831
  observed.add(dispatch);
39732
39832
  }
39733
- return rest;
39833
+ return [rest];
39734
39834
  });
39735
39835
  const providerContext = {
39736
39836
  ...context,
39737
39837
  messages: sanitizedMessages
39738
39838
  };
39739
- const result = originalStreamFunction.call(session.agent, model, providerContext, streamOptions);
39740
- return Promise.resolve(result).then((stream) => {
39839
+ let result;
39840
+ try {
39841
+ result = originalStreamFunction.call(session.agent, model, providerContext, { ...streamOptions, signal: generation.signal });
39842
+ } catch (error109) {
39843
+ settle2();
39844
+ throw error109;
39845
+ }
39846
+ let abortStartup;
39847
+ const startupAborted = new Promise((resolve6) => {
39848
+ abortStartup = () => resolve6(fenceGenerationStream(createAssistantMessageEventStream2(), model, generation.signal, settle2));
39849
+ generation.signal.addEventListener("abort", abortStartup, {
39850
+ once: true
39851
+ });
39852
+ if (generation.signal.aborted)
39853
+ abortStartup();
39854
+ });
39855
+ const started = Promise.resolve(result).then((stream) => {
39856
+ generation.signal.removeEventListener("abort", abortStartup);
39857
+ if (generation.signal.aborted)
39858
+ return fenceGenerationStream(stream, model, generation.signal, settle2);
39741
39859
  if (!disposed && !firstStreamInvocationReported) {
39742
39860
  firstStreamInvocationReported = true;
39743
39861
  onFirstStreamInvocation?.();
39744
39862
  }
39745
- for (const dispatch of observed) {
39746
- dispatch.observed = true;
39747
- reportIfConfirmed(dispatch);
39863
+ if (!generation.signal.aborted) {
39864
+ for (const dispatch of observed) {
39865
+ dispatch.observed = true;
39866
+ reportIfConfirmed(dispatch);
39867
+ }
39868
+ const waiting = [...dispatches.values()].filter((dispatch) => dispatch.outcome === "queued" && dispatch.captured.length === 1 && !dispatch.observed && !dispatch.preemptionRequested);
39869
+ if (waiting.length) {
39870
+ for (const dispatch of waiting)
39871
+ dispatch.preemptionRequested = true;
39872
+ generation.abort();
39873
+ }
39748
39874
  }
39749
- return stream;
39875
+ return fenceGenerationStream(stream, model, generation.signal, settle2);
39876
+ }, (error109) => {
39877
+ generation.signal.removeEventListener("abort", abortStartup);
39878
+ settle2();
39879
+ throw error109;
39750
39880
  });
39881
+ return Promise.race([started, startupAborted]);
39751
39882
  };
39752
39883
  session.agent.streamFunction = wrappedStreamFunction;
39753
39884
  return {
39885
+ supportedPiSteerPreempt: true,
39754
39886
  async steer(message) {
39755
39887
  if (disposed)
39756
39888
  return { disposition: "rejected", reason: "controller disposed" };
39889
+ if (session.isStreaming === false)
39890
+ return {
39891
+ disposition: "rejected",
39892
+ reason: "session is no longer running"
39893
+ };
39757
39894
  if (dispatches.has(message.id)) {
39758
39895
  return {
39759
39896
  disposition: "rejected",
@@ -39764,7 +39901,8 @@ function createPiLiveController(session, onFirstStreamInvocation) {
39764
39901
  id: message.id,
39765
39902
  captured: [],
39766
39903
  observed: false,
39767
- reported: false
39904
+ reported: false,
39905
+ preemptionRequested: false
39768
39906
  };
39769
39907
  dispatches.set(message.id, dispatch);
39770
39908
  const images = message.images?.map((image) => ({
@@ -39785,6 +39923,10 @@ function createPiLiveController(session, onFirstStreamInvocation) {
39785
39923
  if (dispatch.outcome === "handled")
39786
39924
  return { disposition: "handled" };
39787
39925
  reportIfConfirmed(dispatch);
39926
+ if (dispatch.captured.length === 1 && !dispatch.observed && activeGeneration) {
39927
+ dispatch.preemptionRequested = true;
39928
+ activeGeneration.abort();
39929
+ }
39788
39930
  return { disposition: "queued" };
39789
39931
  },
39790
39932
  onConsumed(callback) {
@@ -41735,12 +41877,34 @@ function accumulateToolUsage(tally, raw) {
41735
41877
  }
41736
41878
  }
41737
41879
  function getUsageFromAgentEndMessages(messages) {
41738
- for (let i = messages.length - 1; i >= 0; i--) {
41739
- const m = messages[i];
41740
- if (m.role === "assistant" && m.usage != null)
41741
- return m.usage;
41880
+ let total;
41881
+ for (const message of messages) {
41882
+ if (message.role !== "assistant" || !message.usage)
41883
+ continue;
41884
+ const usage = message.usage;
41885
+ if (!total) {
41886
+ total = { ...usage, cost: { ...usage.cost } };
41887
+ continue;
41888
+ }
41889
+ total.input += usage.input;
41890
+ total.output += usage.output;
41891
+ total.cacheRead += usage.cacheRead;
41892
+ total.cacheWrite += usage.cacheWrite;
41893
+ total.totalTokens += usage.totalTokens;
41894
+ if (usage.reasoning !== void 0)
41895
+ total.reasoning = (total.reasoning ?? 0) + usage.reasoning;
41896
+ if (usage.cacheWrite1h !== void 0)
41897
+ total.cacheWrite1h = (total.cacheWrite1h ?? 0) + usage.cacheWrite1h;
41898
+ for (const key of [
41899
+ "input",
41900
+ "output",
41901
+ "cacheRead",
41902
+ "cacheWrite",
41903
+ "total"
41904
+ ])
41905
+ total.cost[key] += usage.cost[key];
41742
41906
  }
41743
- return void 0;
41907
+ return total;
41744
41908
  }
41745
41909
 
41746
41910
  // ../../packages/runner-pi/dist/stream-converter.js
@@ -45724,8 +45888,8 @@ function serializeResult2(result) {
45724
45888
  // ../../packages/runner-pi/dist/pi-runner.js
45725
45889
  var LOG_PREFIX = "[bunny-agent:pi]";
45726
45890
  var DEFAULT_DYNAMIC_MODEL_PROFILE = {
45727
- contextWindow: 128e3,
45728
- maxTokens: 16384,
45891
+ contextWindow: 2e5,
45892
+ maxTokens: 64e3,
45729
45893
  thinkingLevelMap: { off: null, xhigh: "xhigh" }
45730
45894
  };
45731
45895
  var DYNAMIC_MODEL_PROFILES = {
@@ -45838,8 +46002,10 @@ function applyModelOverrides(model, provider, optionsEnv) {
45838
46002
  function getErrorFromAgentEndMessages(messages) {
45839
46003
  for (let i = messages.length - 1; i >= 0; i--) {
45840
46004
  const m = messages[i];
45841
- if (m.role === "assistant" && m.errorMessage) {
45842
- return m.errorMessage;
46005
+ if (m.role === "assistant") {
46006
+ if (m.stopReason === "aborted")
46007
+ return m.errorMessage ?? "Generation interrupted before pending input could be consumed.";
46008
+ return m.stopReason === "error" ? m.errorMessage : void 0;
45843
46009
  }
45844
46010
  }
45845
46011
  return void 0;
@@ -46063,6 +46229,7 @@ function createPiRunner(options = {}) {
46063
46229
  let promptSettled = false;
46064
46230
  let promptError;
46065
46231
  let finalAgentEnd;
46232
+ const assistantCompletions = [];
46066
46233
  let aborted3 = false;
46067
46234
  let wakeConsumer = null;
46068
46235
  const notify = () => {
@@ -46129,6 +46296,8 @@ function createPiRunner(options = {}) {
46129
46296
  finalAgentEnd = event;
46130
46297
  continue;
46131
46298
  }
46299
+ if (event.type === "message_end" && event.message.role === "assistant")
46300
+ assistantCompletions.push(event.message);
46132
46301
  const chunks = streamConverter.handleEvent(event, aborted3);
46133
46302
  for (const chunk of chunks) {
46134
46303
  yield chunk;
@@ -46157,7 +46326,10 @@ function createPiRunner(options = {}) {
46157
46326
  return;
46158
46327
  }
46159
46328
  if (finalAgentEnd) {
46160
- const chunks = streamConverter.handleEvent(finalAgentEnd, false);
46329
+ const chunks = streamConverter.handleEvent({
46330
+ ...finalAgentEnd,
46331
+ messages: assistantCompletions.length ? assistantCompletions : finalAgentEnd.messages
46332
+ }, false);
46161
46333
  for (const chunk of chunks) {
46162
46334
  yield chunk;
46163
46335
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunny-agent/runner-cli",
3
- "version": "0.10.1-beta.2",
3
+ "version": "0.10.3-beta1",
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": {
@@ -60,8 +60,8 @@
60
60
  "typescript": "^5.3.0",
61
61
  "vitest": "^1.6.1",
62
62
  "@bunny-agent/apply-patch": "0.1.0",
63
+ "@bunny-agent/manager": "0.10.3-beta1",
63
64
  "@bunny-agent/runner-claude": "0.6.2",
64
- "@bunny-agent/manager": "0.10.1-beta.2",
65
65
  "@bunny-agent/runner-codex": "0.6.2",
66
66
  "@bunny-agent/runner-copilot": "0.1.0",
67
67
  "@bunny-agent/runner-gemini": "0.6.2",