tnega 0.3.0 → 0.4.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.
package/dist/index.js CHANGED
@@ -9096,6 +9096,8 @@ __export(src_exports2, {
9096
9096
  SESSION_FORMAT_VERSION: () => SESSION_FORMAT_VERSION,
9097
9097
  SessionFormatError: () => SessionFormatError,
9098
9098
  SessionLog: () => SessionLog,
9099
+ assistantStreams: () => assistantStreams,
9100
+ checkAssistantAttempts: () => checkAssistantAttempts,
9099
9101
  checkBalancedSteps: () => checkBalancedSteps,
9100
9102
  checkBalancedToolCalls: () => checkBalancedToolCalls,
9101
9103
  checkBalancedTurns: () => checkBalancedTurns,
@@ -9226,17 +9228,88 @@ function checkMonotonicSeq(events) {
9226
9228
  }
9227
9229
  return failures;
9228
9230
  }
9231
+ function isRecord(value) {
9232
+ return typeof value === "object" && value !== null && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
9233
+ }
9234
+ function isNonNegativeInteger(value) {
9235
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
9236
+ }
9237
+ function isJsonValue(value, ancestors = /* @__PURE__ */ new Set()) {
9238
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
9239
+ if (typeof value === "number") return Number.isFinite(value);
9240
+ if (typeof value !== "object" || ancestors.has(value)) return false;
9241
+ if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) return false;
9242
+ ancestors.add(value);
9243
+ const valid = (Array.isArray(value) ? Array.from(value) : Object.values(value)).every((member) => isJsonValue(member, ancestors));
9244
+ ancestors.delete(value);
9245
+ return valid;
9246
+ }
9247
+ function isStreamRecord(value) {
9248
+ if (!isRecord(value) || !isNonNegativeInteger(value.time) || !isRecord(value.chunk)) return false;
9249
+ const chunk = value.chunk;
9250
+ if (chunk.type === "stream_error") {
9251
+ return isRecord(chunk.error) && typeof chunk.error.message === "string" && (chunk.error.name === void 0 || typeof chunk.error.name === "string") && (chunk.error.stack === void 0 || typeof chunk.error.stack === "string");
9252
+ }
9253
+ if (typeof chunk.id !== "string") return false;
9254
+ switch (chunk.type) {
9255
+ case "message_start":
9256
+ return chunk.model === void 0 || typeof chunk.model === "string";
9257
+ case "message_delta":
9258
+ return typeof chunk.delta === "string";
9259
+ case "toolcall_start":
9260
+ case "toolcall_end":
9261
+ return isNonNegativeInteger(chunk.index) && typeof chunk.name === "string" && (chunk.type === "toolcall_start" || isJsonValue(chunk.arguments));
9262
+ case "message_stop":
9263
+ return chunk.finishReason === "stop" || chunk.finishReason === "tool_calls" || chunk.finishReason === "length" || chunk.finishReason === "max_turns" || chunk.finishReason === "max_steps" || chunk.finishReason === "error" || chunk.finishReason === "cancelled";
9264
+ default:
9265
+ return false;
9266
+ }
9267
+ }
9268
+ function checkAssistantAttempts(events) {
9269
+ const turns = /* @__PURE__ */ new Set();
9270
+ const steps = /* @__PURE__ */ new Map();
9271
+ const failures = [];
9272
+ for (const event of events) {
9273
+ if (event.type === "turn/start") turns.add(event.payload.turn);
9274
+ if (event.type === "turn/end") {
9275
+ turns.delete(event.payload.turn);
9276
+ for (const [key, turn] of steps) {
9277
+ if (turn === event.payload.turn) steps.delete(key);
9278
+ }
9279
+ }
9280
+ if (event.type === "step/start" && turns.has(event.payload.turn)) {
9281
+ steps.set(`${event.payload.turn}:${event.payload.step}`, event.payload.turn);
9282
+ }
9283
+ if (event.type === "step/end") steps.delete(`${event.payload.turn}:${event.payload.step}`);
9284
+ if (event.type !== "assistant/attempt") continue;
9285
+ const payload = event.payload;
9286
+ if (!isRecord(payload) || !isNonNegativeInteger(payload.turn) || !isNonNegativeInteger(payload.step) || !turns.has(payload.turn) || !steps.has(`${payload.turn}:${payload.step}`)) {
9287
+ failures.push({
9288
+ name: "assistant/attempt-without-open-step",
9289
+ detail: `assistant/attempt at seq ${event.seq} has no matching open turn and step`
9290
+ });
9291
+ }
9292
+ if (!isRecord(payload) || !Array.isArray(payload.stream) || !payload.stream.every(isStreamRecord)) {
9293
+ failures.push({
9294
+ name: "assistant/attempt-invalid-stream",
9295
+ detail: `assistant/attempt at seq ${event.seq} must contain an ordered array of normalized stream records`
9296
+ });
9297
+ }
9298
+ }
9299
+ return failures;
9300
+ }
9229
9301
  function checkSessionInvariants(events) {
9230
9302
  return [
9231
9303
  ...checkBalancedTurns(events),
9232
9304
  ...checkBalancedSteps(events),
9233
9305
  ...checkBalancedToolCalls(events),
9234
- ...checkMonotonicSeq(events)
9306
+ ...checkMonotonicSeq(events),
9307
+ ...checkAssistantAttempts(events)
9235
9308
  ];
9236
9309
  }
9237
9310
 
9238
9311
  // packages/session/src/index.ts
9239
- var SESSION_FORMAT_VERSION = 7;
9312
+ var SESSION_FORMAT_VERSION = 10;
9240
9313
  var SessionFormatError = class extends Error {
9241
9314
  name = "SessionFormatError";
9242
9315
  };
@@ -9256,6 +9329,12 @@ function isSessionEvent(value) {
9256
9329
  const record3 = value;
9257
9330
  return typeof record3.id === "string" && typeof record3.seq === "number" && typeof record3.ts === "number" && typeof record3.type === "string" && "payload" in record3;
9258
9331
  }
9332
+ function assistantStreams(events) {
9333
+ return events.flatMap((event) => {
9334
+ if (event.type !== "assistant/message" && event.type !== "assistant/attempt") return [];
9335
+ return event.payload.stream === void 0 ? [] : [clone(event.payload.stream)];
9336
+ });
9337
+ }
9259
9338
  function clone(value) {
9260
9339
  return structuredClone(value);
9261
9340
  }
@@ -9411,7 +9490,7 @@ function transcriptEvents(events) {
9411
9490
  const structure = events.filter((event) => !inSkeleton.has(event.id)).filter((event) => event.type !== "compaction/start" && event.type !== "compaction/end").filter((event) => event.type !== "checkpoint").filter((event) => !(event.type === "tool/call" && declaredCalls.has(event.payload.id)));
9412
9491
  const ordered = [...skeleton];
9413
9492
  for (const event of structure) {
9414
- let index = ordered.length;
9493
+ let index = 0;
9415
9494
  for (let cursor = ordered.length - 1; cursor >= 0; cursor -= 1) {
9416
9495
  if (ordered[cursor].seq <= event.seq) {
9417
9496
  index = cursor + 1;
@@ -9511,6 +9590,7 @@ function estimateEventTokens(event) {
9511
9590
  return Math.ceil(raw.length / 4);
9512
9591
  }
9513
9592
  case "plan":
9593
+ case "assistant/attempt":
9514
9594
  return 0;
9515
9595
  case "checkpoint":
9516
9596
  return estimateMessageTokens(event.payload.messages);
@@ -9667,9 +9747,15 @@ function estimateContextUsage(messages, limit = DEFAULT_CONTEXT_LIMIT) {
9667
9747
  };
9668
9748
  }
9669
9749
  var SessionLog = class {
9670
- constructor(file, _broadcast) {
9750
+ constructor(file, _broadcast, publicationContext) {
9671
9751
  this.file = file;
9672
9752
  this._broadcast = _broadcast;
9753
+ if (!this._broadcast && publicationContext) {
9754
+ this._broadcast = (type, payload) => {
9755
+ if (type === "event") publicationContext.emit("session/event", payload);
9756
+ else publicationContext.emit("session/flush", payload);
9757
+ };
9758
+ }
9673
9759
  }
9674
9760
  file;
9675
9761
  _broadcast;
@@ -9838,7 +9924,7 @@ var SessionLog = class {
9838
9924
  selected.push(clone(event));
9839
9925
  continue;
9840
9926
  }
9841
- if (event.type === "meta" || event.type === "llm/retry" || event.type === "llm/retry-started" || event.type.startsWith("turn/") || event.type.startsWith("step/")) {
9927
+ if (event.type === "meta" || event.type === "llm/retry" || event.type === "llm/retry-started" || event.type === "assistant/attempt" || event.type.startsWith("turn/") || event.type.startsWith("step/")) {
9842
9928
  continue;
9843
9929
  }
9844
9930
  if (event.type === "checkpoint") {
@@ -11511,7 +11597,7 @@ function copyMessages(messages) {
11511
11597
  copy.tool_calls = message3.tool_calls.map((call) => ({
11512
11598
  id: call.id,
11513
11599
  name: call.name,
11514
- arguments: call.arguments
11600
+ arguments: structuredClone(call.arguments)
11515
11601
  }));
11516
11602
  }
11517
11603
  return copy;
@@ -11542,6 +11628,75 @@ function copyCompletion(completion) {
11542
11628
  }
11543
11629
  return copy;
11544
11630
  }
11631
+ function canonicalMessages(messages) {
11632
+ return JSON.stringify(messages.map((message3) => ({
11633
+ role: message3.role,
11634
+ content: message3.content,
11635
+ ...message3.name ? { name: message3.name } : {},
11636
+ ...message3.tool_call_id ? { tool_call_id: message3.tool_call_id } : {},
11637
+ ...message3.tool_calls?.length ? { tool_calls: message3.tool_calls } : {},
11638
+ ...message3.toolOk !== void 0 ? { toolOk: message3.toolOk } : {},
11639
+ ...message3.toolError ? {
11640
+ toolError: { name: message3.toolError.name, message: message3.toolError.message }
11641
+ } : {}
11642
+ })));
11643
+ }
11644
+ function freezeRequestData(data) {
11645
+ const visited = /* @__PURE__ */ new Set();
11646
+ const freeze = (value) => {
11647
+ if (typeof value !== "object" || value === null || visited.has(value)) return;
11648
+ visited.add(value);
11649
+ for (const child of Object.values(value)) freeze(child);
11650
+ Object.freeze(value);
11651
+ };
11652
+ freeze(data);
11653
+ }
11654
+ function lockStreamRequest(request) {
11655
+ freezeRequestData(request.messages);
11656
+ request.tools = Object.freeze(request.tools.map((tool) => {
11657
+ const schema = structuredClone(tool.schema);
11658
+ freezeRequestData(schema);
11659
+ return Object.freeze({ ...tool, schema });
11660
+ }));
11661
+ Object.freeze(request.options);
11662
+ Object.freeze(request);
11663
+ }
11664
+ function requestHeaderFor(request, input) {
11665
+ const system = input.find((message3) => message3.role === "system")?.content;
11666
+ const tools2 = request.tools.map((tool) => ({
11667
+ name: tool.schema.name,
11668
+ description: tool.schema.description,
11669
+ ...tool.schema.parameters ? { parameters: tool.schema.parameters } : {}
11670
+ }));
11671
+ const config2 = {
11672
+ ...request.options.provider ? { provider: request.options.provider } : {},
11673
+ ...request.options.model ? { model: request.options.model } : {},
11674
+ ...request.options.temperature !== void 0 ? { temperature: request.options.temperature } : {}
11675
+ };
11676
+ return {
11677
+ ...Object.keys(config2).length ? { config: config2 } : {},
11678
+ ...system !== void 0 ? { system } : {},
11679
+ ...tools2.length ? { tools: tools2 } : {}
11680
+ };
11681
+ }
11682
+ async function* completeAsStream(llm, messages, tools2, options) {
11683
+ const id = randomUUID2();
11684
+ yield { type: "message_start", id };
11685
+ const completion = await llm.complete(messages, tools2, options);
11686
+ if (completion.content) yield { type: "message_delta", id, delta: completion.content };
11687
+ for (let index = 0; index < (completion.toolCalls?.length ?? 0); index += 1) {
11688
+ const call = completion.toolCalls[index];
11689
+ yield { type: "toolcall_start", id: call.id, index, name: call.name };
11690
+ yield {
11691
+ type: "toolcall_end",
11692
+ id: call.id,
11693
+ index,
11694
+ name: call.name,
11695
+ arguments: call.arguments
11696
+ };
11697
+ }
11698
+ yield { type: "message_stop", id, finishReason: completion.finishReason };
11699
+ }
11545
11700
  function isCancelCause(value) {
11546
11701
  if (typeof value !== "object" || value === null) return false;
11547
11702
  const record3 = value;
@@ -11562,6 +11717,13 @@ function cancelCauseFromSignal(signal) {
11562
11717
  if (isCancelCause(reason)) return reason;
11563
11718
  return { type: "abort" };
11564
11719
  }
11720
+ function abortedToolError(signal) {
11721
+ const cause = cancelCauseFromSignal(signal);
11722
+ return {
11723
+ name: "AbortError",
11724
+ message: `tool call aborted: ${cause?.type ?? "abort"}`
11725
+ };
11726
+ }
11565
11727
  function toTurnEndReason(fields) {
11566
11728
  const { cancelled, turnError, finishReason, cancelCause } = fields;
11567
11729
  if (cancelled || finishReason === "cancelled") {
@@ -11606,6 +11768,78 @@ async function cancellableDelay(ms, signal) {
11606
11768
  signal?.addEventListener("abort", onAbort, { once: true });
11607
11769
  });
11608
11770
  }
11771
+ var AssistantStreamAttempt = class {
11772
+ constructor(session2, turn, step, nextRevision) {
11773
+ this.session = session2;
11774
+ this.turn = turn;
11775
+ this.step = step;
11776
+ this.nextRevision = nextRevision;
11777
+ }
11778
+ session;
11779
+ turn;
11780
+ step;
11781
+ nextRevision;
11782
+ attemptId = randomUUID2();
11783
+ records = [];
11784
+ ended = false;
11785
+ start() {
11786
+ return { type: "assistant/stream", frame: {
11787
+ type: "start",
11788
+ attemptId: this.attemptId,
11789
+ revision: this.nextRevision(),
11790
+ turn: this.turn,
11791
+ step: this.step
11792
+ } };
11793
+ }
11794
+ push(chunk) {
11795
+ if (this.ended) throw new AgentError("assistant attempt is already settled");
11796
+ const record3 = { time: Date.now(), chunk: structuredClone(chunk) };
11797
+ const index = this.records.length;
11798
+ this.records.push(record3);
11799
+ return { type: "assistant/stream", frame: {
11800
+ type: "chunk",
11801
+ attemptId: this.attemptId,
11802
+ revision: this.nextRevision(),
11803
+ index,
11804
+ ...structuredClone(record3)
11805
+ } };
11806
+ }
11807
+ async settle(message3) {
11808
+ if (this.ended) throw new AgentError("assistant attempt is already settled");
11809
+ const eventType = message3 ? "assistant/message" : "assistant/attempt";
11810
+ let event;
11811
+ try {
11812
+ event = message3 ? await this.session.append("assistant/message", { ...message3, stream: this.records }) : await this.session.append("assistant/attempt", { turn: this.turn, step: this.step, stream: this.records });
11813
+ } catch (cause) {
11814
+ this.ended = true;
11815
+ const frame = { type: "assistant/stream", frame: {
11816
+ type: "end",
11817
+ attemptId: this.attemptId,
11818
+ revision: this.nextRevision(),
11819
+ index: this.records.length,
11820
+ outcome: { kind: "abandoned" }
11821
+ } };
11822
+ throw new AttemptSettlementError(cause, frame);
11823
+ }
11824
+ this.ended = true;
11825
+ return { type: "assistant/stream", frame: {
11826
+ type: "end",
11827
+ attemptId: this.attemptId,
11828
+ revision: this.nextRevision(),
11829
+ index: this.records.length,
11830
+ outcome: { kind: "committed", eventType, seq: event.seq }
11831
+ } };
11832
+ }
11833
+ };
11834
+ var AttemptSettlementError = class extends Error {
11835
+ constructor(cause, frame) {
11836
+ super("assistant attempt settlement failed");
11837
+ this.cause = cause;
11838
+ this.frame = frame;
11839
+ }
11840
+ cause;
11841
+ frame;
11842
+ };
11609
11843
  var AgentService = class {
11610
11844
  constructor(ctx, config2 = {}) {
11611
11845
  this.ctx = ctx;
@@ -11614,9 +11848,24 @@ var AgentService = class {
11614
11848
  }
11615
11849
  ctx;
11616
11850
  config;
11851
+ async *settleAttempt(attempt, message3) {
11852
+ try {
11853
+ yield await attempt.settle(message3);
11854
+ } catch (error2) {
11855
+ if (error2 instanceof AttemptSettlementError) {
11856
+ yield error2.frame;
11857
+ throw error2.cause;
11858
+ }
11859
+ throw error2;
11860
+ }
11861
+ }
11617
11862
  inbox;
11618
11863
  _seriesStarted = false;
11619
11864
  _persistedRequest = false;
11865
+ _streamRevision = 0;
11866
+ _attempt(session2, turn, step) {
11867
+ return new AssistantStreamAttempt(session2, turn, step, () => ++this._streamRevision);
11868
+ }
11620
11869
  /** Call when a request begins an explicit new model-message series. */
11621
11870
  startSeries() {
11622
11871
  this._seriesStarted = true;
@@ -11630,7 +11879,7 @@ var AgentService = class {
11630
11879
  return this._persistedRequest;
11631
11880
  }
11632
11881
  async run(input, options = {}) {
11633
- return consumeAgentStream(this._stream(input, options, false));
11882
+ return consumeAgentStream(this._stream(input, options));
11634
11883
  }
11635
11884
  async *runStream(input, options = {}) {
11636
11885
  const iterator = this._stream(input, options)[Symbol.asyncIterator]();
@@ -11640,7 +11889,7 @@ var AgentService = class {
11640
11889
  yield next.value;
11641
11890
  }
11642
11891
  }
11643
- async *_stream(input, options = {}, useStream = true) {
11892
+ async *_stream(input, options = {}) {
11644
11893
  const claimed = input ?? this.inbox.claim();
11645
11894
  if (!claimed) throw new AgentError("no agent input available");
11646
11895
  await this.config.hooks?.beforeRun?.(claimed, options);
@@ -11648,26 +11897,32 @@ var AgentService = class {
11648
11897
  const tools2 = this._tools();
11649
11898
  const llm = this._llm();
11650
11899
  if (!llm) throw new AgentError("no LLM adapter available");
11900
+ const streamAdapter = llm.stream;
11901
+ const persistChunks = streamAdapter !== void 0;
11651
11902
  const maxTurns = options.maxTurns ?? this.config.maxTurns ?? 64;
11652
11903
  const maxSteps = options.maxSteps ?? this.config.maxSteps ?? 64;
11653
11904
  const contextBudget = options.contextBudget ?? this.config.contextBudget;
11654
11905
  const injected = this.inbox.injected();
11655
11906
  this.ctx.emit("agent/start", { input: claimed, options, injected });
11656
11907
  let messages = await this._initialMessages(claimed);
11908
+ let claimedMessages = messages.filter((message3) => message3.role === "user");
11657
11909
  const steps = [];
11658
11910
  let output = "";
11659
11911
  let finishReason = "stop";
11912
+ let lengthReached = false;
11660
11913
  this.ctx.emit("agent/turn-start", {
11661
11914
  input: claimed,
11662
11915
  messages: copyMessages(messages),
11663
11916
  injected
11664
11917
  });
11665
- const turn = await session2.nextTurn();
11666
- await session2.append("turn/start", {
11667
- turn,
11668
- input: claimed.text ?? claimed,
11669
- reason: "user"
11670
- });
11918
+ const turn = options.turn ?? await session2.nextTurn();
11919
+ if (options.turn === void 0) {
11920
+ await session2.append("turn/start", {
11921
+ turn,
11922
+ input: claimed.text ?? claimed,
11923
+ reason: "user"
11924
+ });
11925
+ }
11671
11926
  let index = 0;
11672
11927
  let finalTurnGranted = false;
11673
11928
  let currentStepIndex;
@@ -11686,20 +11941,22 @@ var AgentService = class {
11686
11941
  messages = await this._enforceContextBudget(session2, contextBudget, messages);
11687
11942
  }
11688
11943
  const stepInput = copyMessages(messages);
11689
- const preStep = this.ctx.waterfall("agent/pre-step", {
11944
+ const preStep = await this.ctx.waterfallAsync("agent/pre-step", {
11690
11945
  index,
11691
11946
  turn,
11692
11947
  step: index,
11693
11948
  messages: stepInput,
11949
+ claimedMessages: copyMessages(claimedMessages),
11694
11950
  ...options.signal ? { signal: options.signal } : {}
11695
11951
  }, (payload) => payload);
11696
- if (!preStep || !Array.isArray(preStep.messages) || !preStep.messages.length) {
11952
+ if (!preStep || preStep.admission === "reject" || !Array.isArray(preStep.messages) || !preStep.messages.length) {
11697
11953
  break;
11698
11954
  }
11699
11955
  if (preStep.startsRequestSeries) this._seriesStarted = true;
11700
11956
  await session2.append("step/start", { turn, step: index });
11701
11957
  currentStepIndex = index;
11702
11958
  const requestedInput = copyMessages(preStep.messages);
11959
+ const admittedHistory = await session2.deriveMessages();
11703
11960
  this.ctx.emit("agent/step", {
11704
11961
  index,
11705
11962
  turn,
@@ -11711,93 +11968,85 @@ var AgentService = class {
11711
11968
  maxSteps: maxSteps - steps.length
11712
11969
  };
11713
11970
  if (options.signal) completeOptions.signal = options.signal;
11714
- const request = this.ctx.waterfall("agent/request", {
11715
- index,
11716
- messages: requestedInput,
11717
- tools: availableTools,
11718
- options: completeOptions
11719
- }, (payload) => payload);
11720
- if (!request || !request.options || !request.tools) {
11721
- throw new AgentError("agent/request must return a request payload");
11722
- }
11723
11971
  let llmMessages = copyMessages(requestedInput);
11724
11972
  let completion;
11725
- const streamMethod = useStream ? llm.stream : void 0;
11973
+ let activeAttempt;
11726
11974
  let attempt = 0;
11727
- let persistedStepInput = false;
11728
11975
  while (true) {
11729
11976
  if (options.signal?.aborted) {
11730
11977
  finishReason = "cancelled";
11731
11978
  break;
11732
11979
  }
11733
11980
  const streamEvents = [];
11981
+ activeAttempt = this._attempt(session2, turn, index);
11982
+ yield activeAttempt.start();
11983
+ const streamRequest = {
11984
+ index,
11985
+ messages: copyMessages(requestedInput),
11986
+ tools: [...availableTools],
11987
+ options: { ...completeOptions }
11988
+ };
11734
11989
  try {
11735
- if (streamMethod) {
11736
- const streamRequest = {
11737
- index,
11738
- messages: llmMessages,
11739
- tools: request.tools,
11740
- options: request.options
11741
- };
11742
- const stream = await this.ctx.waterfallAsync(
11743
- "llm/stream",
11744
- streamRequest,
11745
- async (payload) => {
11746
- if (!streamMethod) {
11747
- throw new AgentError("llm.stream is required for streaming runs");
11748
- }
11749
- return streamMethod(payload.messages, payload.tools, payload.options);
11750
- }
11751
- );
11752
- if (!stream || typeof stream[Symbol.asyncIterator] !== "function") {
11753
- throw new AgentError("llm/stream must return an async iterable stream");
11754
- }
11990
+ const request = await this.ctx.waterfallAsync("agent/request", {
11991
+ index,
11992
+ messages: copyMessages(requestedInput),
11993
+ tools: streamRequest.tools,
11994
+ options: streamRequest.options
11995
+ }, (payload) => payload);
11996
+ if (!request || !request.options || !request.tools) {
11997
+ throw new AgentError("agent/request must return a request payload");
11998
+ }
11999
+ streamRequest.tools = request.tools;
12000
+ streamRequest.options = request.options;
12001
+ let preparation;
12002
+ const prepareRequest = () => preparation ??= (async () => {
12003
+ lockStreamRequest(streamRequest);
11755
12004
  llmMessages = copyMessages(streamRequest.messages);
11756
- if (!persistedStepInput) {
11757
- await this._persistStepInput(session2, request, llmMessages);
11758
- if (this.config.assertReplayable) {
11759
- await this._assertReplayable(session2, llmMessages);
11760
- }
11761
- persistedStepInput = true;
11762
- }
11763
- let chunkIndex = 0;
11764
- for await (const event of stream) {
11765
- streamEvents.push(event);
11766
- if (event.type === "message_delta") {
11767
- await session2.append("assistant/chunk", {
11768
- id: event.id,
11769
- content: event.delta,
11770
- index: chunkIndex
11771
- });
11772
- chunkIndex += 1;
11773
- }
11774
- yield event;
11775
- }
11776
- completion = completionFromStreamEvents(streamEvents);
11777
- } else {
11778
- if (!persistedStepInput) {
11779
- await this._persistStepInput(session2, request, llmMessages);
11780
- if (this.config.assertReplayable) {
11781
- await this._assertReplayable(session2, llmMessages);
11782
- }
11783
- persistedStepInput = true;
12005
+ await this._persistStepInput(session2, streamRequest, llmMessages, admittedHistory);
12006
+ await this._assertReplayable(session2, streamRequest, llmMessages);
12007
+ })();
12008
+ const stream = await this.ctx.waterfallAsync(
12009
+ "llm/stream",
12010
+ streamRequest,
12011
+ (payload) => (async function* () {
12012
+ await prepareRequest();
12013
+ yield* streamAdapter ? streamAdapter.call(llm, payload.messages, payload.tools, payload.options) : completeAsStream(llm, payload.messages, payload.tools, payload.options);
12014
+ })()
12015
+ );
12016
+ if (!stream || typeof stream[Symbol.asyncIterator] !== "function") {
12017
+ throw new AgentError("llm/stream must return an async iterable stream");
12018
+ }
12019
+ await prepareRequest();
12020
+ let chunkIndex = 0;
12021
+ for await (const event of stream) {
12022
+ streamEvents.push(structuredClone(event));
12023
+ yield activeAttempt.push(event);
12024
+ if (persistChunks && event.type === "message_delta") {
12025
+ await session2.append("assistant/chunk", {
12026
+ id: event.id,
12027
+ content: event.delta,
12028
+ index: chunkIndex
12029
+ });
12030
+ chunkIndex += 1;
11784
12031
  }
11785
- completion = await llm.complete(llmMessages, request.tools, request.options);
12032
+ yield event;
12033
+ }
12034
+ completion = completionFromStreamEvents(streamEvents);
12035
+ if (completion.finishReason === "error" || completion.finishReason === "cancelled") {
12036
+ const terminalReason = completion.finishReason;
12037
+ completion = void 0;
12038
+ throw new AgentError(`LLM stream finished with ${terminalReason}`);
11786
12039
  }
11787
12040
  break;
11788
12041
  } catch (error2) {
11789
- const content = partialStreamContent(streamEvents);
11790
- if (content) {
11791
- await session2.append("assistant/message", {
11792
- content,
11793
- interrupted: true
11794
- });
11795
- llmMessages = await session2.deriveMessages();
11796
- }
12042
+ yield activeAttempt.push({ type: "stream_error", error: toToolError2(error2) });
11797
12043
  if (options.signal?.aborted) {
12044
+ const content = partialStreamContent(streamEvents);
12045
+ yield* this.settleAttempt(activeAttempt, content ? { content, interrupted: true } : void 0);
11798
12046
  finishReason = "cancelled";
11799
12047
  break;
11800
12048
  }
12049
+ yield* this.settleAttempt(activeAttempt);
11801
12050
  attempt += 1;
11802
12051
  const failure = toToolError2(error2);
11803
12052
  const decision = await this.ctx.waterfallAsync(
@@ -11806,14 +12055,14 @@ var AgentService = class {
11806
12055
  index,
11807
12056
  turn,
11808
12057
  step: index,
11809
- messages: copyMessages(llmMessages),
11810
- tools: request.tools,
11811
- options: request.options,
12058
+ messages: copyMessages(streamRequest.messages),
12059
+ tools: [...streamRequest.tools],
12060
+ options: { ...streamRequest.options },
11812
12061
  attempt,
11813
12062
  error: error2,
11814
12063
  failure,
11815
- ...request.options.provider ? { provider: request.options.provider } : {},
11816
- ...request.options.model ? { model: request.options.model } : {},
12064
+ ...streamRequest.options.provider ? { provider: streamRequest.options.provider } : {},
12065
+ ...streamRequest.options.model ? { model: streamRequest.options.model } : {},
11817
12066
  ...options.signal ? { signal: options.signal } : {}
11818
12067
  },
11819
12068
  () => void 0
@@ -11838,16 +12087,30 @@ var AgentService = class {
11838
12087
  await session2.append("llm/retry-started", { retryId, retry: attempt });
11839
12088
  }
11840
12089
  }
11841
- if (options.signal?.aborted) break;
11842
- if (!completion) throw new AgentError("LLM adapter did not produce a completion");
12090
+ if (!completion || !activeAttempt) {
12091
+ if (options.signal?.aborted) {
12092
+ finishReason = "cancelled";
12093
+ break;
12094
+ }
12095
+ throw new AgentError("LLM adapter did not produce a completion");
12096
+ }
11843
12097
  const toolCalls = completion.toolCalls ?? [];
11844
- if (finalTurnGranted && toolCalls.length) {
12098
+ if (completion.finishReason === "length") lengthReached = true;
12099
+ if (options.signal?.aborted && toolCalls.length === 0) {
12100
+ yield activeAttempt.push({ type: "stream_error", error: { name: "AbortError", message: "stream aborted" } });
12101
+ yield* this.settleAttempt(activeAttempt);
12102
+ finishReason = "cancelled";
12103
+ break;
12104
+ }
12105
+ if (finalTurnGranted && toolCalls.length && !options.signal?.aborted) {
12106
+ yield activeAttempt.push({ type: "stream_error", error: { name: "AgentError", message: "maximum turns reached" } });
12107
+ yield* this.settleAttempt(activeAttempt);
11845
12108
  finishReason = "max_turns";
11846
12109
  break;
11847
12110
  }
11848
12111
  const toolResults = [];
11849
12112
  if (toolCalls.length) {
11850
- await session2.append("assistant/message", {
12113
+ yield* this.settleAttempt(activeAttempt, {
11851
12114
  content: completion.content ?? "",
11852
12115
  toolCalls: toolCalls.map((call) => ({
11853
12116
  id: call.id,
@@ -11868,18 +12131,30 @@ var AgentService = class {
11868
12131
  if (options.signal) toolOptions.signal = options.signal;
11869
12132
  const startedAt = Date.now();
11870
12133
  let result;
11871
- try {
11872
- result = await tools2.execute(call.name, call.arguments, toolOptions);
11873
- } catch (error2) {
12134
+ if (options.signal?.aborted) {
11874
12135
  result = {
11875
12136
  ok: false,
11876
12137
  name: call.name,
11877
12138
  callId: call.id,
11878
12139
  input: call.arguments,
11879
- error: toToolError2(error2),
12140
+ error: abortedToolError(options.signal),
11880
12141
  startedAt,
11881
12142
  durationMs: Date.now() - startedAt
11882
12143
  };
12144
+ } else {
12145
+ try {
12146
+ result = await tools2.execute(call.name, call.arguments, toolOptions);
12147
+ } catch (error2) {
12148
+ result = {
12149
+ ok: false,
12150
+ name: call.name,
12151
+ callId: call.id,
12152
+ input: call.arguments,
12153
+ error: toToolError2(error2),
12154
+ startedAt,
12155
+ durationMs: Date.now() - startedAt
12156
+ };
12157
+ }
11883
12158
  }
11884
12159
  toolResults.push(result);
11885
12160
  const toolResultPayload = {
@@ -11921,7 +12196,7 @@ var AgentService = class {
11921
12196
  output = completion.content;
11922
12197
  }
11923
12198
  if (!toolCalls.length) {
11924
- await session2.append("assistant/message", { content: completion.content ?? "" });
12199
+ yield* this.settleAttempt(activeAttempt, { content: completion.content ?? "" });
11925
12200
  }
11926
12201
  await session2.append("step/end", {
11927
12202
  turn,
@@ -11932,19 +12207,19 @@ var AgentService = class {
11932
12207
  currentStepIndex = void 0;
11933
12208
  const nextMessages = this._extendMessages(llmMessages, completion, toolResults);
11934
12209
  const concludesTurn = toolResults.some((result) => result.concludesTurn === true);
11935
- if (concludesTurn) {
11936
- finishReason = "stop";
11937
- break;
11938
- }
11939
- if (toolCalls.length === 0) {
11940
- finishReason = completion.finishReason === "length" ? "length" : completion.finishReason === "error" ? "error" : "stop";
12210
+ let nextStepMessages = await this._claimNextStepMessages();
12211
+ if ((toolCalls.length === 0 || concludesTurn) && nextStepMessages.length === 0) {
12212
+ finishReason = lengthReached ? "length" : completion.finishReason === "error" ? "error" : "stop";
11941
12213
  const keepGoing = await this.ctx.serial("agent/turn-stopping", {
11942
12214
  index,
11943
12215
  turn,
11944
12216
  steps: copySteps(steps),
11945
12217
  finishReason
11946
12218
  });
11947
- if (!keepGoing) break;
12219
+ if (!keepGoing) {
12220
+ nextStepMessages = await this._claimNextStepMessages();
12221
+ if (nextStepMessages.length === 0) break;
12222
+ }
11948
12223
  }
11949
12224
  if (index + 1 >= maxTurns && !finalTurnGranted) {
11950
12225
  finalTurnGranted = true;
@@ -11954,7 +12229,8 @@ var AgentService = class {
11954
12229
  }
11955
12230
  if (toolCalls.length) finishReason = "tool_calls";
11956
12231
  index += 1;
11957
- messages = nextMessages;
12232
+ messages = [...nextMessages, ...nextStepMessages];
12233
+ claimedMessages = nextStepMessages.filter((message3) => message3.role === "user");
11958
12234
  }
11959
12235
  } catch (error2) {
11960
12236
  turnError = error2;
@@ -12040,23 +12316,22 @@ var AgentService = class {
12040
12316
  });
12041
12317
  return compactMessages.map((message3) => copyMessages([message3])[0]);
12042
12318
  }
12043
- async _persistStepInput(session2, request, input) {
12044
- const system = input.find((message3) => message3.role === "system")?.content;
12045
- const tools2 = request.tools.map((tool) => ({
12046
- name: tool.schema.name,
12047
- description: tool.schema.description,
12048
- ...tool.schema.parameters ? { parameters: tool.schema.parameters } : {}
12049
- }));
12050
- const config2 = {
12051
- ...request.options.provider ? { provider: request.options.provider } : {},
12052
- ...request.options.model ? { model: request.options.model } : {},
12053
- ...request.options.temperature !== void 0 ? { temperature: request.options.temperature } : {}
12054
- };
12055
- const nextHeader = {
12056
- ...Object.keys(config2).length ? { config: config2 } : {},
12057
- ...system !== void 0 ? { system } : {},
12058
- ...tools2.length ? { tools: tools2 } : {}
12059
- };
12319
+ async _persistStepInput(session2, request, input, admittedHistory) {
12320
+ const surface = await session2.deriveMessages();
12321
+ const owned = admittedHistory.filter((message3) => !isUserOrSystemMessage(message3));
12322
+ let ownerIndex = 0;
12323
+ for (const message3 of input) {
12324
+ if (isUserOrSystemMessage(message3)) continue;
12325
+ const actual = canonicalMessages([message3]);
12326
+ while (ownerIndex < owned.length && canonicalMessages([owned[ownerIndex]]) !== actual) {
12327
+ ownerIndex += 1;
12328
+ }
12329
+ if (ownerIndex === owned.length) {
12330
+ throw new AgentError("request is not reconstructable from session log: unowned or reordered assistant/tool message");
12331
+ }
12332
+ ownerIndex += 1;
12333
+ }
12334
+ const nextHeader = requestHeaderFor(request, input);
12060
12335
  const previousHeader = session2.requestHeader();
12061
12336
  const isFirstRequest = !this._persistedRequest;
12062
12337
  const isResume = isFirstRequest && previousHeader !== void 0;
@@ -12086,19 +12361,23 @@ var AgentService = class {
12086
12361
  ...request.options.provider ? { provider: request.options.provider } : {},
12087
12362
  ...request.options.model ? { model: request.options.model } : {}
12088
12363
  };
12364
+ if (request.options.contextWindow !== void 0) {
12365
+ nextContext.contextWindow = request.options.contextWindow;
12366
+ }
12089
12367
  const llmService2 = this.ctx.reflect.get("llm", false);
12090
12368
  const routeCapacity = llmService2?.routeCapacity?.();
12091
- if (routeCapacity?.contextWindow !== void 0) {
12369
+ if (nextContext.contextWindow === void 0 && routeCapacity?.contextWindow !== void 0) {
12092
12370
  nextContext.contextWindow = routeCapacity.contextWindow;
12093
12371
  }
12094
12372
  const previousContext = session2.requestContext();
12095
12373
  if (previousContext?.provider !== nextContext.provider || previousContext?.model !== nextContext.model || previousContext?.contextWindow !== nextContext.contextWindow || previousContext === void 0) {
12096
12374
  await session2.append("request/context", nextContext);
12097
12375
  }
12098
- const requested = input.filter(isUserOrSystemMessage);
12099
- const surface = await session2.deriveMessages();
12100
- const appendDurable = async (messages) => {
12101
- for (const message3 of messages) {
12376
+ const isSurfacePrefix = (messages) => canonicalMessages(messages.slice(0, surface.length)) === canonicalMessages(surface);
12377
+ const requested = surface.length > 0 && input[0]?.role === "system" && input[0].content === nextHeader.system && isSurfacePrefix(input.slice(1)) ? input.slice(1) : input;
12378
+ const tail = requested.slice(surface.length);
12379
+ if (isSurfacePrefix(requested) && tail.every(isUserOrSystemMessage)) {
12380
+ for (const message3 of tail) {
12102
12381
  if (message3.role === "user") {
12103
12382
  await session2.append("user/message", {
12104
12383
  content: message3.content,
@@ -12111,45 +12390,37 @@ var AgentService = class {
12111
12390
  });
12112
12391
  }
12113
12392
  }
12114
- };
12115
- if (surface.length === 0) {
12116
- await appendDurable(requested);
12117
12393
  return;
12118
12394
  }
12119
- const durableUsers = surface.filter((message3) => message3.role === "user").length;
12120
- let seenUsers = 0;
12121
- const newTail = [];
12122
- for (const message3 of requested) {
12123
- if (message3.role === "user") seenUsers += 1;
12124
- if (seenUsers <= durableUsers) continue;
12125
- newTail.push(message3);
12126
- }
12127
- await appendDurable(newTail);
12395
+ const nodes = await session2.surfaceEvents();
12396
+ await session2.append("checkpoint", {
12397
+ messages: copyMessages(requested),
12398
+ surfaceOp: { op: "replace", start: nodes[0].seq, end: nodes.at(-1).seq }
12399
+ });
12128
12400
  }
12129
- async _assertReplayable(session2, input) {
12401
+ async _assertReplayable(session2, request, input) {
12130
12402
  const reconstructed = await session2.deriveMessages();
12131
- const canonical = (messages) => messages.filter((message3) => message3.role !== "system").map((message3) => ({
12132
- role: message3.role,
12133
- content: message3.content,
12134
- ...message3.tool_call_id ? { tool_call_id: message3.tool_call_id } : {},
12135
- ...message3.name ? { name: message3.name } : {},
12136
- ...message3.tool_calls?.length ? { tool_calls: message3.tool_calls.map((call) => ({
12137
- id: call.id,
12138
- name: call.name,
12139
- args: JSON.stringify(call.arguments ?? {})
12140
- })) } : {}
12141
- }));
12142
- const actual = canonical(input);
12143
- const replay = canonical(reconstructed);
12144
- if (JSON.stringify(actual) !== JSON.stringify(replay)) {
12403
+ const actual = canonicalMessages(input);
12404
+ const replay = canonicalMessages(reconstructed);
12405
+ const header = session2.requestHeader();
12406
+ const headerOwnsPrefix = input[0]?.role === "system" && input[0].content === header?.system && canonicalMessages(input.slice(1)) === replay;
12407
+ if (actual !== replay && !headerOwnsPrefix) {
12145
12408
  throw new AgentError(
12146
- `request is not reconstructable from session log: actual=${JSON.stringify(actual)} replay=${JSON.stringify(replay)}`
12409
+ `request is not reconstructable from session log: actual=${actual} replay=${replay}`
12147
12410
  );
12148
12411
  }
12412
+ const expected = requestHeaderFor(request, input);
12413
+ if (header?.system !== expected.system || JSON.stringify(header?.tools ?? []) !== JSON.stringify(expected.tools ?? []) || JSON.stringify(header?.config ?? {}) !== JSON.stringify(expected.config ?? {})) {
12414
+ throw new AgentError("request envelope is not reconstructable from session log");
12415
+ }
12416
+ }
12417
+ async _claimNextStepMessages() {
12418
+ const inputs = await this.config.claimNextStep?.() ?? [];
12419
+ return inputs.flatMap((input) => input.messages?.length ? copyMessages(input.messages) : input.text ? [{ role: "user", content: input.text }] : input.context !== void 0 ? [{ role: "user", content: stringify2(input.context) }] : []);
12149
12420
  }
12150
12421
  async _initialMessages(input) {
12151
12422
  const promptService = this.ctx.reflect.get("systemPrompt", false);
12152
- const assembled = promptService ? (await promptService.assemble()).text.trim() : void 0;
12423
+ const assembled = promptService ? (await promptService.assemble({}, this.ctx)).text.trim() : void 0;
12153
12424
  const systemPrompt3 = assembled || this.inbox.injected().get("agentSystem");
12154
12425
  const systemMessage = typeof systemPrompt3 === "string" && systemPrompt3 ? [{ role: "system", content: systemPrompt3 }] : [];
12155
12426
  if (input.messages?.length) {
@@ -12174,7 +12445,7 @@ var AgentService = class {
12174
12445
  arguments: call.arguments
12175
12446
  }));
12176
12447
  }
12177
- next.push(assistant);
12448
+ if (assistant.content || toolCalls.length) next.push(assistant);
12178
12449
  for (const call of toolCalls) {
12179
12450
  const result = toolResults.find((candidate) => candidate.callId === call.id);
12180
12451
  const content = result ? result.ok ? stringify2(result.output) : `error: ${result.error?.message ?? "unknown"}` : "error: missing tool result";
@@ -12204,7 +12475,8 @@ var AgentService = class {
12204
12475
  return session2;
12205
12476
  }
12206
12477
  _tools() {
12207
- const tools2 = this.ctx.tools;
12478
+ const scoped = this.ctx.reflect.get("tools", false);
12479
+ const tools2 = scoped ?? this.ctx.tools;
12208
12480
  if (!tools2) throw new AgentError("tools service is required");
12209
12481
  return tools2;
12210
12482
  }
@@ -12388,67 +12660,77 @@ var DurableInbox = class _DurableInbox {
12388
12660
  _session;
12389
12661
  _nextTurn = [];
12390
12662
  _nextStep = [];
12663
+ _operationTail = Promise.resolve();
12391
12664
  get size() {
12392
12665
  return this._nextTurn.length + this._nextStep.length;
12393
12666
  }
12394
12667
  async insert(input, target = "next-turn") {
12395
- const message3 = {
12396
- id: randomUUID3(),
12397
- ...input.text !== void 0 ? { text: input.text } : {},
12398
- ...input.content !== void 0 ? { content: input.content } : {}
12399
- };
12400
- const list = target === "next-step" ? this._nextStep : this._nextTurn;
12401
- list.push(message3);
12402
- await this._session.append("agent/inbox/spliced", {
12403
- target,
12404
- index: list.length - 1,
12405
- inserted: [{
12406
- id: message3.id,
12407
- content: contentOf(message3),
12408
- ...payloadOf(message3) !== void 0 ? { payload: payloadOf(message3) } : {}
12409
- }]
12668
+ return this._run(async () => {
12669
+ const message3 = {
12670
+ id: randomUUID3(),
12671
+ ...input.text !== void 0 ? { text: input.text } : {},
12672
+ ...input.content !== void 0 ? { content: input.content } : {}
12673
+ };
12674
+ const list = target === "next-step" ? this._nextStep : this._nextTurn;
12675
+ const index = list.length;
12676
+ await this._session.append("agent/inbox/spliced", {
12677
+ target,
12678
+ index,
12679
+ inserted: [{
12680
+ id: message3.id,
12681
+ content: contentOf(message3),
12682
+ ...payloadOf(message3) !== void 0 ? { payload: payloadOf(message3) } : {}
12683
+ }]
12684
+ });
12685
+ list.push(message3);
12686
+ return message3;
12410
12687
  });
12411
- return message3;
12412
12688
  }
12413
12689
  async steer(input) {
12414
- const message3 = {
12415
- id: randomUUID3(),
12416
- ...input.text !== void 0 ? { text: input.text } : {},
12417
- ...input.content !== void 0 ? { content: input.content } : {}
12418
- };
12419
- this._nextStep.unshift(message3);
12420
- await this._session.append("agent/inbox/spliced", {
12421
- target: "next-step",
12422
- index: 0,
12423
- inserted: [{
12424
- id: message3.id,
12425
- content: contentOf(message3),
12426
- ...payloadOf(message3) !== void 0 ? { payload: payloadOf(message3) } : {},
12427
- mode: "steer"
12428
- }]
12429
- });
12430
- return message3;
12431
- }
12432
- async claim() {
12433
- const steered = this._nextStep.shift();
12434
- if (steered) {
12690
+ return this._run(async () => {
12691
+ const message3 = {
12692
+ id: randomUUID3(),
12693
+ ...input.text !== void 0 ? { text: input.text } : {},
12694
+ ...input.content !== void 0 ? { content: input.content } : {}
12695
+ };
12435
12696
  await this._session.append("agent/inbox/spliced", {
12436
12697
  target: "next-step",
12437
- index: 0,
12438
- deleteCount: 1
12698
+ index: this._nextStep.length,
12699
+ inserted: [{
12700
+ id: message3.id,
12701
+ content: contentOf(message3),
12702
+ ...payloadOf(message3) !== void 0 ? { payload: payloadOf(message3) } : {},
12703
+ mode: "steer"
12704
+ }]
12439
12705
  });
12440
- return steered;
12441
- }
12442
- const next = this._nextTurn.shift();
12443
- if (next) {
12444
- await this._session.append("agent/inbox/spliced", {
12445
- target: "next-turn",
12446
- index: 0,
12447
- deleteCount: 1
12448
- });
12449
- return next;
12450
- }
12451
- return void 0;
12706
+ this._nextStep.push(message3);
12707
+ return message3;
12708
+ });
12709
+ }
12710
+ async claim() {
12711
+ return this._run(async () => {
12712
+ const steered = this._nextStep[0];
12713
+ if (steered) {
12714
+ await this._session.append("agent/inbox/spliced", {
12715
+ target: "next-step",
12716
+ index: 0,
12717
+ deleteCount: 1
12718
+ });
12719
+ this._nextStep.shift();
12720
+ return steered;
12721
+ }
12722
+ const next = this._nextTurn[0];
12723
+ if (next) {
12724
+ await this._session.append("agent/inbox/spliced", {
12725
+ target: "next-turn",
12726
+ index: 0,
12727
+ deleteCount: 1
12728
+ });
12729
+ this._nextTurn.shift();
12730
+ return next;
12731
+ }
12732
+ return void 0;
12733
+ });
12452
12734
  }
12453
12735
  /**
12454
12736
  * Claim the complete proposed step batch: every pending next-step input plus
@@ -12456,105 +12738,108 @@ var DurableInbox = class _DurableInbox {
12456
12738
  * nothing is pending.
12457
12739
  */
12458
12740
  async claimBatch() {
12459
- const claimed = [];
12460
- if (this._nextStep.length) {
12461
- const removed = this._nextStep.splice(0);
12462
- claimed.push(...removed);
12741
+ return this._run(async () => {
12742
+ const claimed = [...this._nextStep];
12743
+ const nextTurn = this._nextTurn[0];
12744
+ if (nextTurn) claimed.push(nextTurn);
12745
+ if (!claimed.length) return [];
12746
+ const deleteCounts = { nextStep: this._nextStep.length, nextTurn: nextTurn ? 1 : 0 };
12747
+ await this._session.append("agent/inbox/spliced", { target: "all", deleteCounts });
12748
+ this._nextStep.splice(0, deleteCounts.nextStep);
12749
+ this._nextTurn.splice(0, deleteCounts.nextTurn);
12750
+ return claimed;
12751
+ });
12752
+ }
12753
+ /** Claim every input waiting for the next step, without consuming a turn. */
12754
+ async claimNextStep() {
12755
+ return this._run(async () => {
12756
+ if (!this._nextStep.length) return [];
12757
+ const claimed = [...this._nextStep];
12463
12758
  await this._session.append("agent/inbox/spliced", {
12464
12759
  target: "next-step",
12465
12760
  index: 0,
12466
- deleteCount: removed.length
12761
+ deleteCount: claimed.length
12467
12762
  });
12468
- }
12469
- const nextTurn = this._nextTurn.shift();
12470
- if (nextTurn) {
12471
- claimed.push(nextTurn);
12472
- await this._session.append("agent/inbox/spliced", {
12473
- target: "next-turn",
12474
- index: 0,
12475
- deleteCount: 1
12476
- });
12477
- }
12478
- return claimed;
12763
+ this._nextStep.splice(0, claimed.length);
12764
+ return claimed;
12765
+ });
12479
12766
  }
12480
12767
  async clear() {
12481
- if (this._nextTurn.length) {
12482
- this._nextTurn = [];
12768
+ return this._run(async () => {
12769
+ if (!this._nextTurn.length && !this._nextStep.length) return;
12483
12770
  await this._session.append("agent/inbox/spliced", {
12484
- target: "next-turn",
12485
- index: 0,
12486
- deleteCount: Number.POSITIVE_INFINITY
12771
+ target: "all"
12487
12772
  });
12488
- }
12489
- if (this._nextStep.length) {
12773
+ this._nextTurn = [];
12490
12774
  this._nextStep = [];
12491
- await this._session.append("agent/inbox/spliced", {
12492
- target: "next-step",
12493
- index: 0,
12494
- deleteCount: Number.POSITIVE_INFINITY
12495
- });
12496
- }
12775
+ });
12497
12776
  }
12498
12777
  /** Insert a message at an explicit target/index boundary. */
12499
12778
  async insertAt(input, target, index, mode = "followup") {
12500
- const message3 = {
12501
- id: randomUUID3(),
12502
- ...input.text !== void 0 ? { text: input.text } : {},
12503
- ...input.content !== void 0 ? { content: input.content } : {}
12504
- };
12505
- const list = target === "next-step" ? this._nextStep : this._nextTurn;
12506
- const safeIndex = Math.max(0, Math.min(index, list.length));
12507
- list.splice(safeIndex, 0, message3);
12508
- await this._session.append("agent/inbox/spliced", {
12509
- target,
12510
- index: safeIndex,
12511
- inserted: [{
12512
- id: message3.id,
12513
- content: contentOf(message3),
12514
- ...payloadOf(message3) !== void 0 ? { payload: payloadOf(message3) } : {},
12515
- mode
12516
- }]
12779
+ return this._run(async () => {
12780
+ const message3 = {
12781
+ id: randomUUID3(),
12782
+ ...input.text !== void 0 ? { text: input.text } : {},
12783
+ ...input.content !== void 0 ? { content: input.content } : {}
12784
+ };
12785
+ const list = target === "next-step" ? this._nextStep : this._nextTurn;
12786
+ const safeIndex = Math.max(0, Math.min(index, list.length));
12787
+ await this._session.append("agent/inbox/spliced", {
12788
+ target,
12789
+ index: safeIndex,
12790
+ inserted: [{
12791
+ id: message3.id,
12792
+ content: contentOf(message3),
12793
+ ...payloadOf(message3) !== void 0 ? { payload: payloadOf(message3) } : {},
12794
+ mode
12795
+ }]
12796
+ });
12797
+ list.splice(safeIndex, 0, message3);
12798
+ return message3;
12517
12799
  });
12518
- return message3;
12519
12800
  }
12520
12801
  /**
12521
12802
  * Replace one pending message by id. The old message is emitted as
12522
12803
  * discarded and the new one as inserted, matching the DSH inbox contract.
12523
12804
  */
12524
12805
  async replace(messageId, input) {
12525
- const entry = this._find(messageId);
12526
- if (!entry) return void 0;
12527
- const replacement = {
12528
- id: randomUUID3(),
12529
- ...input.text !== void 0 ? { text: input.text } : {},
12530
- ...input.content !== void 0 ? { content: input.content } : {}
12531
- };
12532
- const target = entry.target;
12533
- const index = entry.index;
12534
- entry.list.splice(index, 1, replacement);
12535
- await this._session.append("agent/inbox/spliced", {
12536
- target,
12537
- index,
12538
- deleteCount: 1,
12539
- inserted: [{
12540
- id: replacement.id,
12541
- content: contentOf(replacement),
12542
- ...payloadOf(replacement) !== void 0 ? { payload: payloadOf(replacement) } : {}
12543
- }]
12806
+ return this._run(async () => {
12807
+ const entry = this._find(messageId);
12808
+ if (!entry) return void 0;
12809
+ const replacement = {
12810
+ id: randomUUID3(),
12811
+ ...input.text !== void 0 ? { text: input.text } : {},
12812
+ ...input.content !== void 0 ? { content: input.content } : {}
12813
+ };
12814
+ const target = entry.target;
12815
+ const index = entry.index;
12816
+ await this._session.append("agent/inbox/spliced", {
12817
+ target,
12818
+ index,
12819
+ deleteCount: 1,
12820
+ inserted: [{
12821
+ id: replacement.id,
12822
+ content: contentOf(replacement),
12823
+ ...payloadOf(replacement) !== void 0 ? { payload: payloadOf(replacement) } : {}
12824
+ }]
12825
+ });
12826
+ entry.list.splice(index, 1, replacement);
12827
+ return replacement;
12544
12828
  });
12545
- return replacement;
12546
12829
  }
12547
12830
  /** Remove one pending message by id. */
12548
12831
  async remove(messageId) {
12549
- const entry = this._find(messageId);
12550
- if (!entry) return void 0;
12551
- entry.list.splice(entry.index, 1);
12552
- await this._session.append("agent/inbox/spliced", {
12553
- target: entry.target,
12554
- index: entry.index,
12555
- deleteCount: 1
12832
+ return this._run(async () => {
12833
+ const entry = this._find(messageId);
12834
+ if (!entry) return void 0;
12835
+ await this._session.append("agent/inbox/spliced", {
12836
+ target: entry.target,
12837
+ index: entry.index,
12838
+ deleteCount: 1
12839
+ });
12840
+ entry.list.splice(entry.index, 1);
12841
+ return entry.message;
12556
12842
  });
12557
- return entry.message;
12558
12843
  }
12559
12844
  /** Return the first pending message by id across both queues. */
12560
12845
  get(messageId) {
@@ -12566,6 +12851,12 @@ var DurableInbox = class _DurableInbox {
12566
12851
  nextStep: [...this._nextStep]
12567
12852
  };
12568
12853
  }
12854
+ /** Keep the entire read → append → apply transition ordered, including claims. */
12855
+ _run(operation) {
12856
+ const result = this._operationTail.then(operation);
12857
+ this._operationTail = result.then(() => void 0, () => void 0);
12858
+ return result;
12859
+ }
12569
12860
  _find(messageId) {
12570
12861
  for (const target of ["next-step", "next-turn"]) {
12571
12862
  const list = target === "next-step" ? this._nextStep : this._nextTurn;
@@ -12583,6 +12874,11 @@ var DurableInbox = class _DurableInbox {
12583
12874
  for (const event of events) {
12584
12875
  if (event.type !== "agent/inbox/spliced") continue;
12585
12876
  const payload = event.payload;
12877
+ if (payload.target === "all") {
12878
+ this._nextTurn.splice(0, payload.deleteCounts?.nextTurn ?? this._nextTurn.length);
12879
+ this._nextStep.splice(0, payload.deleteCounts?.nextStep ?? this._nextStep.length);
12880
+ continue;
12881
+ }
12586
12882
  const list = payload.target === "next-step" ? this._nextStep : this._nextTurn;
12587
12883
  const count = payload.deleteCount ?? 0;
12588
12884
  if (count === Number.POSITIVE_INFINITY) {
@@ -12612,8 +12908,29 @@ var DurableInbox = class _DurableInbox {
12612
12908
  };
12613
12909
 
12614
12910
  // packages/agent/src/live.ts
12911
+ function createAgentRuntimeContext(agentCtx) {
12912
+ const runtimeCtx = agentCtx.extend();
12913
+ Object.defineProperty(runtimeCtx, Context.filter, {
12914
+ value: (target) => {
12915
+ const targetScope = target[symbols.isolate].agentScope;
12916
+ let ancestor = agentCtx;
12917
+ while (true) {
12918
+ if (targetScope === ancestor[symbols.isolate].agentScope) return true;
12919
+ if (ancestor.fiber.parent.fiber === ancestor.fiber) return false;
12920
+ ancestor = ancestor.fiber.parent;
12921
+ }
12922
+ }
12923
+ });
12924
+ const events = agentCtx.events;
12925
+ for (const method of ["emit", "parallel", "serial", "bail", "waterfall", "waterfallAsync"]) {
12926
+ Object.defineProperty(runtimeCtx, method, {
12927
+ value: (...args) => events[method](runtimeCtx, ...args)
12928
+ });
12929
+ }
12930
+ return runtimeCtx;
12931
+ }
12615
12932
  var LiveAgentImpl = class {
12616
- constructor(_ctx, _agentCtx, _service, id, inbox, session2, durable, meta2, manualStreaming = false) {
12933
+ constructor(_ctx, _agentCtx, _service, id, inbox, session2, durable, meta2, manualStreaming = false, wakeReserved = false) {
12617
12934
  this._ctx = _ctx;
12618
12935
  this._agentCtx = _agentCtx;
12619
12936
  this._service = _service;
@@ -12623,6 +12940,7 @@ var LiveAgentImpl = class {
12623
12940
  this.meta = meta2;
12624
12941
  this._durable = durable;
12625
12942
  this._manualStreaming = manualStreaming;
12943
+ this._wakeReserved = wakeReserved;
12626
12944
  }
12627
12945
  _ctx;
12628
12946
  _agentCtx;
@@ -12644,8 +12962,9 @@ var LiveAgentImpl = class {
12644
12962
  _setupDisposers = [];
12645
12963
  _scopeClosed = false;
12646
12964
  _durable;
12647
- _agentSystem;
12648
12965
  _manualStreaming = false;
12966
+ _wakeReserved = false;
12967
+ _loop;
12649
12968
  get agentType() {
12650
12969
  return this.meta.agentType;
12651
12970
  }
@@ -12672,13 +12991,41 @@ var LiveAgentImpl = class {
12672
12991
  return this._ctx;
12673
12992
  }
12674
12993
  followup(input) {
12675
- this._send(input, "followup");
12994
+ return this._send(input, "followup");
12995
+ }
12996
+ setLoop(loop) {
12997
+ this._loop = loop;
12998
+ }
12999
+ async _closeCustomTurn(turn, run) {
13000
+ const events = await this.session.read();
13001
+ if (events.some((event) => event.type === "turn/end" && event.payload.turn === turn)) return;
13002
+ const reason = run.finishReason === "length" ? { kind: "max-tokens" } : run.finishReason === "max_steps" ? { kind: "max-steps" } : run.finishReason === "max_turns" ? { kind: "max-turns" } : run.finishReason === "cancelled" ? { kind: "interrupted" } : run.finishReason === "error" ? { kind: "interrupted" } : { kind: "completed" };
13003
+ await this.session.append("turn/end", {
13004
+ turn,
13005
+ finishReason: run.finishReason,
13006
+ reason,
13007
+ ...run.output ? { output: run.output } : {},
13008
+ ...run.steps.length ? { steps: run.steps.length } : {}
13009
+ });
13010
+ }
13011
+ async _closeFailedCustomTurn(turn, error2) {
13012
+ const events = await this.session.read();
13013
+ if (events.some((event) => event.type === "turn/end" && event.payload.turn === turn)) return;
13014
+ const failure = error2 instanceof Error ? { name: error2.name, message: error2.message, ...error2.stack ? { stack: error2.stack } : {} } : { name: "Error", message: String(error2) };
13015
+ await this.session.append("turn/end", {
13016
+ turn,
13017
+ finishReason: "error",
13018
+ reason: { kind: "error", error: failure },
13019
+ interrupted: true,
13020
+ error: failure
13021
+ });
12676
13022
  }
12677
13023
  /** Replace one pending inbox message by durable message id. */
12678
13024
  replaceMessage(messageId, input) {
12679
- this._mutatePending(async () => {
13025
+ return this._mutatePending(async () => {
12680
13026
  const previous = this._durable.get(messageId);
12681
- if (!previous) return;
13027
+ const target = this._findTarget(messageId);
13028
+ if (!previous || !target) return;
12682
13029
  const replacement = await this._durable.replace(messageId, {
12683
13030
  text: input.text ?? "",
12684
13031
  ...input.messages !== void 0 || input.context !== void 0 ? { content: input.messages ?? input.context } : {}
@@ -12689,18 +13036,19 @@ var LiveAgentImpl = class {
12689
13036
  agent: this,
12690
13037
  message: previous
12691
13038
  });
12692
- this._ctx.emit("agent/inbox/inserted", {
13039
+ const event = {
12693
13040
  id: this.id,
12694
13041
  agent: this,
12695
- target: this._findTarget(messageId) ?? "next-turn",
13042
+ target,
12696
13043
  input: replacement,
12697
13044
  inboxSeq: this._durable.size
12698
- });
13045
+ };
13046
+ this._ctx.emit("agent/inbox/inserted", event);
12699
13047
  });
12700
13048
  }
12701
13049
  /** Remove one pending inbox message by durable message id. */
12702
13050
  removeMessage(messageId) {
12703
- this._mutatePending(async () => {
13051
+ return this._mutatePending(async () => {
12704
13052
  const removed = await this._durable.remove(messageId);
12705
13053
  if (!removed) return;
12706
13054
  this._ctx.emit("agent/inbox/discarded", {
@@ -12711,48 +13059,54 @@ var LiveAgentImpl = class {
12711
13059
  });
12712
13060
  }
12713
13061
  steer(input) {
12714
- this._send(input, "steer");
13062
+ return this._send(input, "steer");
12715
13063
  }
12716
- inject(key, value) {
12717
- if (key === "agentSystem" && typeof value === "string") {
12718
- this._agentSystem = value;
12719
- }
13064
+ inject(input) {
13065
+ return this._send(input, "inject");
12720
13066
  }
12721
13067
  send(input, options = {}) {
12722
- this._send(input, options.mode === "steer" ? "steer" : "followup");
13068
+ return this._send(input, options.mode === "steer" ? "steer" : "followup");
12723
13069
  }
12724
13070
  _send(input, target) {
12725
- if (this._disposed) throw new Error(`agent disposed: ${this.id}`);
12726
13071
  const text = input.text ?? "";
12727
13072
  const content = input.messages ?? input.context;
12728
- const task = this._writeTail.then(async () => {
12729
- const message3 = target === "steer" ? await this._durable.steer({ text, ...content !== void 0 ? { content } : {} }) : await this._durable.insert({ text, ...content !== void 0 ? { content } : {} });
12730
- this._ctx.emit("agent/inbox/inserted", {
13073
+ const steeredAfterAbort = target === "steer" && this._controller?.signal.aborted === true;
13074
+ return this._mutatePending(async () => {
13075
+ const message3 = target === "followup" ? await this._durable.insert({ text, ...content !== void 0 ? { content } : {} }) : target === "steer" ? steeredAfterAbort ? await this._durable.insert({ text, ...content !== void 0 ? { content } : {} }) : await this._durable.steer({ text, ...content !== void 0 ? { content } : {} }) : await this._durable.insert({ text, ...content !== void 0 ? { content } : {} }, "next-step");
13076
+ if (target !== "inject") this._wakeReserved = true;
13077
+ const event = {
12731
13078
  id: this.id,
13079
+ agent: this,
12732
13080
  target,
12733
13081
  input: message3,
12734
13082
  inboxSeq: this._durable.size
12735
- });
12736
- }).catch((error2) => {
12737
- this._ctx.emit("agent/error", { id: this.id, error: error2 });
12738
- });
12739
- this._writeTail = task;
12740
- this._pendingWrite = task;
12741
- task.finally(() => {
12742
- if (this._pendingWrite === task) this._pendingWrite = void 0;
12743
- queueMicrotask(() => this._wake());
12744
- });
13083
+ };
13084
+ this._ctx.emit("agent/inbox/inserted", event);
13085
+ }, target !== "inject");
12745
13086
  }
12746
- _mutatePending(task) {
12747
- if (this._disposed) throw new Error(`agent disposed: ${this.id}`);
12748
- const chained = this._writeTail.then(task).catch((error2) => {
12749
- this._ctx.emit("agent/error", { id: this.id, error: error2 });
12750
- });
12751
- this._writeTail = chained;
12752
- this._pendingWrite = chained;
12753
- chained.finally(() => {
12754
- if (this._pendingWrite === chained) this._pendingWrite = void 0;
13087
+ _mutatePending(task, wakes = false, allowDisposed = false) {
13088
+ if (this._disposed && !allowDisposed) {
13089
+ return Promise.reject(new Error(`agent disposed: ${this.id}`));
13090
+ }
13091
+ const result = this._writeTail.then(task);
13092
+ const recovered = result.catch((error2) => {
13093
+ try {
13094
+ this._ctx.emit("agent/error", { id: this.id, error: error2 });
13095
+ } catch {
13096
+ }
12755
13097
  });
13098
+ this._writeTail = recovered;
13099
+ this._pendingWrite = recovered;
13100
+ void result.then(
13101
+ () => {
13102
+ if (this._pendingWrite === recovered) this._pendingWrite = void 0;
13103
+ if (wakes) queueMicrotask(() => this._wake());
13104
+ },
13105
+ () => {
13106
+ if (this._pendingWrite === recovered) this._pendingWrite = void 0;
13107
+ }
13108
+ );
13109
+ return result;
12756
13110
  }
12757
13111
  _findTarget(messageId) {
12758
13112
  const snapshot = this._durable.snapshot();
@@ -12762,27 +13116,21 @@ var LiveAgentImpl = class {
12762
13116
  }
12763
13117
  cancel(cause, options = {}) {
12764
13118
  if (!options.keepInbox) {
12765
- const task = this._writeTail.then(async () => {
13119
+ void this._mutatePending(async () => {
12766
13120
  const before = this._durable.snapshot();
13121
+ await this._durable.clear();
12767
13122
  for (const message3 of [...before.nextStep, ...before.nextTurn]) {
12768
13123
  this._ctx.emit("agent/inbox/discarded", { id: this.id, message: message3 });
12769
13124
  }
12770
- await this._durable.clear();
12771
- }).catch((error2) => {
12772
- this._ctx.emit("agent/error", { id: this.id, error: error2 });
12773
- });
12774
- this._writeTail = task;
12775
- this._pendingWrite = task;
12776
- task.finally(() => {
12777
- if (this._pendingWrite === task) this._pendingWrite = void 0;
12778
- });
13125
+ this._wakeReserved = false;
13126
+ }, false, true);
12779
13127
  }
12780
13128
  this._controller?.abort(cause);
12781
13129
  this._maintenance?.controller.abort(cause);
12782
13130
  }
12783
13131
  async whenIdle() {
12784
- while (this._pendingWrite) await this._pendingWrite;
12785
13132
  while (true) {
13133
+ while (this._pendingWrite) await this._pendingWrite;
12786
13134
  const maintenance = this._maintenance;
12787
13135
  if (maintenance) {
12788
13136
  await maintenance.promise.catch(() => void 0);
@@ -12793,7 +13141,7 @@ var LiveAgentImpl = class {
12793
13141
  await drain.catch(() => void 0);
12794
13142
  continue;
12795
13143
  }
12796
- if (!this._durable.size) break;
13144
+ if (!this._hasQueuedWork()) break;
12797
13145
  await new Promise((resolve12) => setTimeout(resolve12, 0));
12798
13146
  }
12799
13147
  }
@@ -12835,6 +13183,7 @@ var LiveAgentImpl = class {
12835
13183
  this._pendingWrite = void 0;
12836
13184
  await writeTail.catch(() => void 0);
12837
13185
  }
13186
+ await this.session.close();
12838
13187
  this._setStatus("idle");
12839
13188
  }
12840
13189
  /** Publish the agent lifecycle session-start notification once. */
@@ -12864,13 +13213,23 @@ var LiveAgentImpl = class {
12864
13213
  if (this._disposed || this._manualStreaming || this._drainPromise || this._busy || this._maintenanceBusy) return;
12865
13214
  const task = this._drainAll();
12866
13215
  this._drainPromise = task;
12867
- task.finally(() => {
12868
- if (this._drainPromise === task) this._drainPromise = void 0;
12869
- });
13216
+ void task.then(
13217
+ () => {
13218
+ if (this._drainPromise === task) this._drainPromise = void 0;
13219
+ if (this._hasQueuedWork()) queueMicrotask(() => this._wake());
13220
+ },
13221
+ (error2) => {
13222
+ if (this._drainPromise === task) this._drainPromise = void 0;
13223
+ try {
13224
+ this._ctx.emit("agent/error", { id: this.id, agent: this, error: error2 });
13225
+ } catch {
13226
+ }
13227
+ }
13228
+ );
12870
13229
  }
12871
13230
  async _drainAll() {
12872
13231
  await this._writeTail.catch(() => void 0);
12873
- if (this._durable.size) {
13232
+ if (this._hasQueuedWork()) {
12874
13233
  for await (const _ of this._streamTurns()) void _;
12875
13234
  }
12876
13235
  }
@@ -12886,10 +13245,13 @@ var LiveAgentImpl = class {
12886
13245
  this._setStatus("running");
12887
13246
  try {
12888
13247
  while (!this._disposed) {
12889
- const batch = await this._durable.claimBatch();
12890
- if (!batch.length) break;
12891
- const input = await this._inputForBatch(batch);
13248
+ const snapshot = this._durable.snapshot();
13249
+ if (!snapshot.nextTurn.length && !this._wakeReserved) break;
12892
13250
  const turn = await this._nextTurnNumber();
13251
+ await this.session.append("turn/start", { turn, reason: "user" });
13252
+ const batch = await this._durable.claimBatch();
13253
+ this._wakeReserved = false;
13254
+ const input = batch.length ? await this._inputForBatch(batch, true) : { text: "" };
12893
13255
  for (const message3 of batch) {
12894
13256
  this._ctx.emit("agent/inbox/claimed", {
12895
13257
  id: this.id,
@@ -12906,11 +13268,21 @@ var LiveAgentImpl = class {
12906
13268
  else signal?.addEventListener("abort", forward, { once: true });
12907
13269
  try {
12908
13270
  if (this._manualStreaming) {
12909
- yield* this._service.runStream(input, { signal: controller.signal });
13271
+ if (this._loop) {
13272
+ const run = await this._loop(input, { signal: controller.signal, turn });
13273
+ await this._closeCustomTurn(turn, run);
13274
+ yield { type: "run/end", run };
13275
+ } else {
13276
+ yield* this._service.runStream(input, { signal: controller.signal, turn });
13277
+ }
12910
13278
  } else {
12911
- await this._service.run(input, { signal: controller.signal });
13279
+ if (this._loop) {
13280
+ const run = await this._loop(input, { signal: controller.signal, turn });
13281
+ await this._closeCustomTurn(turn, run);
13282
+ } else await this._service.run(input, { signal: controller.signal, turn });
12912
13283
  }
12913
13284
  } catch (error2) {
13285
+ if (this._loop) await this._closeFailedCustomTurn(turn, error2);
12914
13286
  if (!controller.signal.aborted) {
12915
13287
  const step = await this._failedStep();
12916
13288
  this._ctx.emit("agent/error", {
@@ -12928,26 +13300,37 @@ var LiveAgentImpl = class {
12928
13300
  }
12929
13301
  } finally {
12930
13302
  this._busy = false;
13303
+ if (!this._durable.size) this._wakeReserved = false;
12931
13304
  this._setStatus("idle");
12932
13305
  }
12933
13306
  }
12934
- async _inputForBatch(batch) {
12935
- const history = await this.session.deriveMessages();
12936
- const structured = batch.filter((message3) => message3.content !== void 0);
12937
- const plain = batch.filter((message3) => message3.content === void 0);
12938
- const plainMessages = plain.filter((message3) => message3.text !== void 0).map((message3) => ({ role: "user", content: message3.text ?? "" }));
12939
- if (structured.length) {
12940
- return {
12941
- messages: [
12942
- ...history,
12943
- ...structured.flatMap((message3) => Array.isArray(message3.content) ? message3.content : [{ role: "user", content: String(message3.content ?? "") }])
12944
- ]
12945
- };
12946
- }
12947
- if (history.length || batch.length > 1) {
12948
- return { messages: [...history, ...plainMessages] };
13307
+ _hasQueuedWork() {
13308
+ const snapshot = this._durable.snapshot();
13309
+ return snapshot.nextTurn.length > 0 || this._wakeReserved && snapshot.nextStep.length > 0;
13310
+ }
13311
+ async _inputForBatch(batch, includeHistory) {
13312
+ const history = includeHistory ? await this.session.deriveMessages() : [];
13313
+ const messages = batch.flatMap((message3) => message3.content !== void 0 ? Array.isArray(message3.content) ? message3.content : [{ role: "user", content: String(message3.content ?? "") }] : message3.text !== void 0 ? [{ role: "user", content: message3.text }] : []);
13314
+ if (history.length || messages.length > 1) return { messages: [...history, ...messages] };
13315
+ return { text: messages[0]?.content ?? "" };
13316
+ }
13317
+ async claimNextStepInputs() {
13318
+ await this._writeTail;
13319
+ const batch = await this._durable.claimNextStep();
13320
+ this._wakeReserved = false;
13321
+ if (batch.length) {
13322
+ const turn = await this._nextTurnNumber() - 1;
13323
+ for (const message3 of batch) {
13324
+ this._ctx.emit("agent/inbox/claimed", {
13325
+ id: this.id,
13326
+ agent: this,
13327
+ message: message3,
13328
+ input: { text: message3.text ?? "" },
13329
+ turn
13330
+ });
13331
+ }
12949
13332
  }
12950
- return { text: plainMessages[0]?.content ?? "" };
13333
+ return batch.length ? [await this._inputForBatch(batch, false)] : [];
12951
13334
  }
12952
13335
  _setStatus(status) {
12953
13336
  const previous = this._status;
@@ -13043,112 +13426,156 @@ async function buildHandle(ctx, options, resume = false) {
13043
13426
  }
13044
13427
  const agentId = fileMeta?.agentId ?? requestedId;
13045
13428
  if (!agentId) throw new AgentError("agent requires a stable identity");
13046
- const log = new SessionLog(
13047
- options.file,
13048
- (type, payload) => {
13049
- if (type === "event") ctx.emit("session/event", payload);
13050
- else ctx.emit("session/flush", payload);
13051
- }
13052
- );
13053
- await log.init();
13054
- const fileEvents = await log.read();
13055
- const durableMeta = readDurableAgentMeta(fileEvents);
13056
- if (durableMeta) {
13057
- if (durableMeta.agentId !== agentId) {
13058
- throw new AgentError(
13059
- `agent id mismatch: session belongs to ${durableMeta.agentId}, requested ${agentId}`
13060
- );
13061
- }
13062
- if (options.agentType && durableMeta.agentType !== options.agentType) {
13063
- throw new AgentError(
13064
- `agent type mismatch: session is ${durableMeta.agentType ?? "untyped"}, requested ${options.agentType}`
13065
- );
13066
- }
13067
- if (options.mode && durableMeta.mode !== options.mode) {
13068
- throw new AgentError(
13069
- `agent mode mismatch: session is ${durableMeta.mode ?? "unset"}, requested ${options.mode}`
13070
- );
13071
- }
13072
- }
13073
- const boundMeta = {
13074
- ...fileMeta,
13075
- ...durableMeta ?? {},
13076
- ...options.title ? { title: options.title } : {},
13077
- ...options.owner ? { owner: options.owner } : {},
13078
- ...options.parentSessionId ? { parentSessionId: options.parentSessionId } : {},
13079
- ...options.forkedAtMessageId ? { forkedAtMessageId: options.forkedAtMessageId } : {},
13080
- agentId
13081
- };
13082
- const resolvedAgentType = options.agentType ?? durableMeta?.agentType;
13083
- if (resolvedAgentType) boundMeta.agentType = resolvedAgentType;
13084
- const resolvedMode = options.mode ?? durableMeta?.mode;
13085
- if (resolvedMode) boundMeta.mode = resolvedMode;
13086
- if (!durableMeta) {
13087
- await log.append("meta", {
13088
- kind: "agent",
13089
- agentId,
13090
- ...boundMeta.agentType ? { agentType: boundMeta.agentType } : {},
13091
- ...boundMeta.mode ? { mode: boundMeta.mode } : {},
13092
- ...boundMeta.title ? { title: boundMeta.title } : {},
13093
- ...boundMeta.owner ? { owner: boundMeta.owner } : {},
13094
- ...boundMeta.parentSessionId ? { parentSessionId: boundMeta.parentSessionId } : {},
13095
- ...boundMeta.forkedAtMessageId ? { forkedAtMessageId: boundMeta.forkedAtMessageId } : {},
13096
- ...boundMeta.createdAt !== void 0 ? { createdAt: boundMeta.createdAt } : {}
13097
- });
13098
- }
13099
- const durable = resume ? await DurableInbox.restore(log) : new DurableInbox(log);
13100
- const injected = new AgentInbox();
13101
- const inboxView = {
13102
- get size() {
13103
- return durable.size;
13104
- },
13105
- snapshot: () => durable.snapshot()
13106
- };
13107
- const service = new AgentService(ctx, {
13108
- session: log,
13109
- inbox: injected,
13110
- ...options.llm ? { llm: options.llm } : {},
13111
- ...options.maxTurns !== void 0 ? { maxTurns: options.maxTurns } : {},
13112
- ...options.maxSteps !== void 0 ? { maxSteps: options.maxSteps } : {},
13113
- ...options.contextBudget ? { contextBudget: options.contextBudget } : {},
13114
- ...options.hooks ? { hooks: options.hooks } : {}
13115
- });
13116
- if (options.system) injected.inject("agentSystem", options.system);
13429
+ let agentCtx;
13117
13430
  const agentScope = await ctx.inject([], (scopeCtx) => {
13118
- scopeCtx.isolate("agentScope").provide("agentScope", scopeCtx);
13431
+ agentCtx = scopeCtx.isolate("agentScope").isolate("llm").isolate("tools").isolate("systemPrompt");
13432
+ agentCtx.provide("agentScope", agentCtx);
13119
13433
  });
13120
13434
  await agentScope;
13121
- const agentCtx = agentScope.ctx;
13122
- const agent2 = new LiveAgentImpl(
13123
- ctx,
13124
- agentCtx,
13125
- service,
13126
- agentId,
13127
- inboxView,
13128
- log,
13129
- durable,
13130
- boundMeta,
13131
- options.manualStreaming === true
13132
- );
13133
- agent2.trackSetupDispose(() => agentScope.dispose());
13134
- if (options.system) agent2.inject("agentSystem", options.system);
13135
- let setupResult;
13435
+ const runtimeCtx = createAgentRuntimeContext(agentCtx);
13436
+ const log = new SessionLog(options.file, void 0, runtimeCtx);
13136
13437
  try {
13137
- setupResult = await options.setup?.(agentCtx);
13138
- setupResult?.commit();
13438
+ await log.init();
13439
+ const fileEvents = await log.read();
13440
+ const durableMeta = readDurableAgentMeta(fileEvents);
13441
+ if (durableMeta) {
13442
+ if (durableMeta.agentId !== agentId) {
13443
+ throw new AgentError(
13444
+ `agent id mismatch: session belongs to ${durableMeta.agentId}, requested ${agentId}`
13445
+ );
13446
+ }
13447
+ if (options.agentType && durableMeta.agentType !== options.agentType) {
13448
+ throw new AgentError(
13449
+ `agent type mismatch: session is ${durableMeta.agentType ?? "untyped"}, requested ${options.agentType}`
13450
+ );
13451
+ }
13452
+ if (options.mode && durableMeta.mode !== options.mode) {
13453
+ throw new AgentError(
13454
+ `agent mode mismatch: session is ${durableMeta.mode ?? "unset"}, requested ${options.mode}`
13455
+ );
13456
+ }
13457
+ }
13458
+ const boundMeta = {
13459
+ ...fileMeta,
13460
+ ...durableMeta ?? {},
13461
+ ...options.title ? { title: options.title } : {},
13462
+ ...options.owner ? { owner: options.owner } : {},
13463
+ ...options.parentSessionId ? { parentSessionId: options.parentSessionId } : {},
13464
+ ...options.forkedAtMessageId ? { forkedAtMessageId: options.forkedAtMessageId } : {},
13465
+ agentId
13466
+ };
13467
+ const resolvedAgentType = options.agentType ?? durableMeta?.agentType;
13468
+ if (resolvedAgentType) boundMeta.agentType = resolvedAgentType;
13469
+ const resolvedMode = options.mode ?? durableMeta?.mode;
13470
+ if (resolvedMode) boundMeta.mode = resolvedMode;
13471
+ if (!durableMeta) {
13472
+ await log.append("meta", {
13473
+ kind: "agent",
13474
+ agentId,
13475
+ ...boundMeta.agentType ? { agentType: boundMeta.agentType } : {},
13476
+ ...boundMeta.mode ? { mode: boundMeta.mode } : {},
13477
+ ...boundMeta.title ? { title: boundMeta.title } : {},
13478
+ ...boundMeta.owner ? { owner: boundMeta.owner } : {},
13479
+ ...boundMeta.parentSessionId ? { parentSessionId: boundMeta.parentSessionId } : {},
13480
+ ...boundMeta.forkedAtMessageId ? { forkedAtMessageId: boundMeta.forkedAtMessageId } : {},
13481
+ ...boundMeta.createdAt !== void 0 ? { createdAt: boundMeta.createdAt } : {}
13482
+ });
13483
+ }
13484
+ const durable = resume ? await DurableInbox.restore(log) : new DurableInbox(log);
13485
+ const restoredWakeReservation = resume && hasRestoredWakeReservation(fileEvents);
13486
+ const injected = new AgentInbox();
13487
+ const inboxView = {
13488
+ get size() {
13489
+ return durable.size;
13490
+ },
13491
+ snapshot: () => durable.snapshot()
13492
+ };
13493
+ let claimNextStep = async () => [];
13494
+ const service = new AgentService(runtimeCtx, {
13495
+ session: log,
13496
+ inbox: injected,
13497
+ ...options.llm ? { llm: options.llm } : {},
13498
+ ...options.maxTurns !== void 0 ? { maxTurns: options.maxTurns } : {},
13499
+ ...options.maxSteps !== void 0 ? { maxSteps: options.maxSteps } : {},
13500
+ ...options.contextBudget ? { contextBudget: options.contextBudget } : {},
13501
+ ...options.hooks ? { hooks: options.hooks } : {},
13502
+ claimNextStep: () => claimNextStep()
13503
+ });
13504
+ if (options.system) injected.inject("agentSystem", options.system);
13505
+ const agent2 = new LiveAgentImpl(
13506
+ runtimeCtx,
13507
+ agentCtx,
13508
+ service,
13509
+ agentId,
13510
+ inboxView,
13511
+ log,
13512
+ durable,
13513
+ boundMeta,
13514
+ options.manualStreaming === true,
13515
+ restoredWakeReservation
13516
+ );
13517
+ claimNextStep = () => agent2.claimNextStepInputs();
13518
+ agent2.trackSetupDispose(() => agentScope.dispose());
13519
+ let setupResult;
13520
+ try {
13521
+ setupResult = await options.setup?.(agentCtx);
13522
+ for (const name7 of ["llm", "systemPrompt"]) {
13523
+ if (agentCtx.reflect.get(name7, false) !== void 0) continue;
13524
+ const inherited = ctx.reflect.get(name7, false);
13525
+ if (inherited !== void 0) agentCtx.provide(name7, inherited);
13526
+ }
13527
+ const loop = agentCtx.reflect.get("agentLoop", false);
13528
+ agent2.setLoop(loop);
13529
+ setupResult?.commit();
13530
+ } catch (error2) {
13531
+ await agent2.dispose().catch(() => void 0);
13532
+ await log.close().catch(() => void 0);
13533
+ throw error2;
13534
+ }
13535
+ await agent2.publishSessionStart(resume ? "resume" : "startup");
13536
+ for (const input of options.initial ?? []) {
13537
+ if (!resume) await durable.insert({ text: input.text ?? "" });
13538
+ }
13539
+ if (resume && (durable.snapshot().nextTurn.length || restoredWakeReservation) && options.manualStreaming !== true) {
13540
+ setTimeout(() => agent2.wakeNow(), 0);
13541
+ }
13542
+ return { agent: agent2, dispose: () => agent2.dispose() };
13139
13543
  } catch (error2) {
13140
- await agent2.dispose().catch(() => void 0);
13141
- await log.close().catch(() => void 0);
13544
+ await Promise.resolve().then(() => agentScope.dispose()).catch(() => void 0);
13142
13545
  throw error2;
13143
13546
  }
13144
- await agent2.publishSessionStart(resume ? "resume" : "startup");
13145
- for (const input of options.initial ?? []) {
13146
- if (!resume) await durable.insert({ text: input.text ?? "" });
13147
- }
13148
- if (resume && durable.size && options.manualStreaming !== true) {
13149
- setTimeout(() => agent2.wakeNow(), 0);
13547
+ }
13548
+ function hasRestoredWakeReservation(events) {
13549
+ const nextTurn = [];
13550
+ const nextStep = [];
13551
+ for (const event of events) {
13552
+ if (event.type !== "agent/inbox/spliced") continue;
13553
+ const payload = event.payload;
13554
+ if (payload.target === "all") {
13555
+ nextTurn.splice(0, payload.deleteCounts?.nextTurn ?? nextTurn.length);
13556
+ nextStep.splice(0, payload.deleteCounts?.nextStep ?? nextStep.length);
13557
+ continue;
13558
+ }
13559
+ const list = payload.target === "next-step" ? nextStep : nextTurn;
13560
+ const count = payload.deleteCount ?? 0;
13561
+ if (count === Number.POSITIVE_INFINITY) {
13562
+ list.length = 0;
13563
+ } else if (count > 0 && payload.index !== void 0) {
13564
+ list.splice(payload.index, count);
13565
+ }
13566
+ let insertedOffset = 0;
13567
+ for (const inserted of payload.inserted ?? []) {
13568
+ const entry = { wakes: payload.target === "next-step" && inserted.mode === "steer" };
13569
+ if (inserted.mode === "steer") {
13570
+ list.unshift(entry);
13571
+ } else {
13572
+ const index = payload.index === void 0 ? list.length : Math.min(list.length, payload.index + insertedOffset);
13573
+ list.splice(index, 0, entry);
13574
+ insertedOffset += 1;
13575
+ }
13576
+ }
13150
13577
  }
13151
- return { agent: agent2, dispose: () => agent2.dispose() };
13578
+ return nextStep.some((entry) => entry.wakes);
13152
13579
  }
13153
13580
  async function readAgentMeta(file) {
13154
13581
  const lines = await import("node:fs/promises").then((fs2) => fs2.readFile(file, "utf8")).catch((error2) => {
@@ -26283,7 +26710,9 @@ function anthropicMessagesAdapter(config2 = {}) {
26283
26710
  }
26284
26711
  let sawEvent = false;
26285
26712
  try {
26286
- for await (const event of parseAnthropicStream(response.body)) {
26713
+ const contentType2 = response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase();
26714
+ const events = contentType2 === "application/json" ? parseJSONCompletionStream(response) : parseAnthropicStream(response.body);
26715
+ for await (const event of events) {
26287
26716
  sawEvent = true;
26288
26717
  yield event;
26289
26718
  }
@@ -26324,7 +26753,7 @@ function buildRequest(messages, tools2, config2, signal, stream = false) {
26324
26753
  headers: {
26325
26754
  "content-type": "application/json",
26326
26755
  "anthropic-version": ANTHROPIC_VERSION,
26327
- ...config2.apiKey ? { "x-api-key": config2.apiKey } : {}
26756
+ ...config2.apiKey ? { [config2.apiKeyHeader ?? "x-api-key"]: config2.apiKey } : {}
26328
26757
  },
26329
26758
  body: JSON.stringify(body),
26330
26759
  ...signal ? { signal } : {}
@@ -26616,6 +27045,26 @@ async function* parseAnthropicStream(body) {
26616
27045
  function toIndex(block) {
26617
27046
  return typeof block.index === "number" ? block.index : 0;
26618
27047
  }
27048
+ async function* parseJSONCompletionStream(response) {
27049
+ const payload = await parseJson(response);
27050
+ const completion = parseCompletion(payload);
27051
+ const record3 = payload;
27052
+ const id = typeof record3?.id === "string" && record3.id ? record3.id : randomUUID8();
27053
+ const model = typeof record3?.model === "string" && record3.model ? record3.model : void 0;
27054
+ yield { type: "message_start", id, ...model ? { model } : {} };
27055
+ if (completion.content) yield { type: "message_delta", id, delta: completion.content };
27056
+ for (const [index, call] of (completion.toolCalls ?? []).entries()) {
27057
+ yield { type: "toolcall_start", id: call.id, index, name: call.name };
27058
+ yield {
27059
+ type: "toolcall_end",
27060
+ id: call.id,
27061
+ index,
27062
+ name: call.name,
27063
+ arguments: call.arguments
27064
+ };
27065
+ }
27066
+ yield { type: "message_stop", id, finishReason: completion.finishReason };
27067
+ }
26619
27068
  function toStreamError(error2, timeoutMs) {
26620
27069
  if (isAbortLike(error2)) {
26621
27070
  return new OpenAICompatibleError(
@@ -26704,7 +27153,9 @@ function openaiCompatAdapter(config2 = {}) {
26704
27153
  }
26705
27154
  let sawEvent = false;
26706
27155
  try {
26707
- for await (const event of parseOpenAIStream(response.body)) {
27156
+ const contentType2 = response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase();
27157
+ const events = contentType2 === "application/json" ? parseJSONCompletionStream2(response) : parseOpenAIStream(response.body);
27158
+ for await (const event of events) {
26708
27159
  sawEvent = true;
26709
27160
  yield event;
26710
27161
  }
@@ -26922,6 +27373,26 @@ async function* parseOpenAIStream(body) {
26922
27373
  }
26923
27374
  yield* finalizeStreamEvents(messageId, content, calls, finishReason);
26924
27375
  }
27376
+ async function* parseJSONCompletionStream2(response) {
27377
+ const payload = await parseJson(response);
27378
+ const completion = parseCompletion2(payload);
27379
+ const record3 = payload;
27380
+ const id = typeof record3?.id === "string" && record3.id ? record3.id : randomUUID9();
27381
+ const model = typeof record3?.model === "string" && record3.model ? record3.model : void 0;
27382
+ yield { type: "message_start", id, ...model ? { model } : {} };
27383
+ if (completion.content) yield { type: "message_delta", id, delta: completion.content };
27384
+ for (const [index, call] of (completion.toolCalls ?? []).entries()) {
27385
+ yield { type: "toolcall_start", id: call.id, index, name: call.name };
27386
+ yield {
27387
+ type: "toolcall_end",
27388
+ id: call.id,
27389
+ index,
27390
+ name: call.name,
27391
+ arguments: call.arguments
27392
+ };
27393
+ }
27394
+ yield { type: "message_stop", id, finishReason: completion.finishReason };
27395
+ }
26925
27396
  function processStreamChunk(payload, state, progress, calls) {
26926
27397
  const record3 = payload;
26927
27398
  const events = [];
@@ -31186,7 +31657,7 @@ async function updateSystemConfig(patch, file = systemConfigPath()) {
31186
31657
  }
31187
31658
  function effectiveLlmConfig(config2, env = process.env) {
31188
31659
  const envConfig = resolveLlmEnv(env);
31189
- const apiKey = envConfig.apiKey ?? config2.apiKey;
31660
+ const apiKey = effectiveApiKey(config2, env);
31190
31661
  const baseUrl = envConfig.baseUrl ?? config2.baseUrl ?? DEFAULT_OPENCODE_GO_BASE_URL;
31191
31662
  const model = envConfig.model ?? config2.model ?? DEFAULT_MODEL;
31192
31663
  const result = {
@@ -31195,15 +31666,18 @@ function effectiveLlmConfig(config2, env = process.env) {
31195
31666
  model
31196
31667
  };
31197
31668
  if (config2.protocol) result.protocol = config2.protocol;
31669
+ if (config2.apiKeyHeader) result.apiKeyHeader = config2.apiKeyHeader;
31198
31670
  if (config2.temperature !== void 0) result.temperature = config2.temperature;
31199
31671
  return result;
31200
31672
  }
31201
31673
  function effectiveApiKey(config2, env = process.env) {
31674
+ if (env.TNEGA_API_KEY) return env.TNEGA_API_KEY;
31675
+ if (config2.protocol && config2.apiKey) return config2.apiKey;
31202
31676
  return resolveLlmEnv(env).apiKey ?? config2.apiKey;
31203
31677
  }
31204
31678
  function resolveLlmEnv(env = process.env) {
31205
31679
  const config2 = {};
31206
- const apiKey = env.OPENCODE_GO_API_KEY || env.OPENAI_API_KEY || env.DEEPSEEK_API_KEY;
31680
+ const apiKey = env.TNEGA_API_KEY || env.OPENCODE_GO_API_KEY || env.OPENAI_API_KEY || env.DEEPSEEK_API_KEY;
31207
31681
  if (apiKey) config2.apiKey = apiKey;
31208
31682
  if (env.OPENCODE_GO_BASE_URL) config2.baseUrl = env.OPENCODE_GO_BASE_URL;
31209
31683
  if (env.OPENCODE_GO_MODEL) config2.model = env.OPENCODE_GO_MODEL;
@@ -31219,6 +31693,9 @@ function normalizeConfig2(value) {
31219
31693
  if (record3.protocol === "anthropic" || record3.protocol === "openai") {
31220
31694
  config2.protocol = record3.protocol;
31221
31695
  }
31696
+ if (record3.apiKeyHeader === "x-api-key" || record3.apiKeyHeader === "api-key") {
31697
+ config2.apiKeyHeader = record3.apiKeyHeader;
31698
+ }
31222
31699
  if (typeof record3.temperature === "number" && Number.isFinite(record3.temperature)) {
31223
31700
  config2.temperature = record3.temperature;
31224
31701
  }
@@ -31542,10 +32019,10 @@ async function runCommand3(options) {
31542
32019
  if (candidate.coding) {
31543
32020
  const systemConfig = await readSystemConfig();
31544
32021
  const envConfig = resolveLlmEnv(process.env);
31545
- const apiKey = envConfig.apiKey ?? systemConfig.apiKey;
32022
+ const apiKey = effectiveApiKey(systemConfig);
31546
32023
  if (!apiKey) {
31547
32024
  throw new CliError(
31548
- "missing LLM API key for coding eval; set OPENCODE_GO_API_KEY (or OPENAI_API_KEY / DEEPSEEK_API_KEY) or configure it in the tnega config file"
32025
+ "missing LLM API key for coding eval; set TNEGA_API_KEY (or OPENCODE_GO_API_KEY / OPENAI_API_KEY / DEEPSEEK_API_KEY) or configure it in the tnega config file"
31549
32026
  );
31550
32027
  }
31551
32028
  const model = options.model ?? envConfig.model ?? systemConfig.model ?? candidate.coding.model;
@@ -31555,6 +32032,7 @@ async function runCommand3(options) {
31555
32032
  ...model ? { model } : {},
31556
32033
  ...baseUrl ? { baseUrl } : {},
31557
32034
  ...systemConfig.protocol ? { protocol: systemConfig.protocol } : {},
32035
+ ...systemConfig.apiKeyHeader ? { apiKeyHeader: systemConfig.apiKeyHeader } : {},
31558
32036
  ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
31559
32037
  ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
31560
32038
  ...systemConfig.temperature !== void 0 && options.temperature === void 0 ? { temperature: systemConfig.temperature } : {},
@@ -31622,10 +32100,10 @@ async function runAgentCommand(options) {
31622
32100
  const profileOptions = profile?.options ?? {};
31623
32101
  const systemConfig = await readSystemConfig(configFile);
31624
32102
  const envConfig = resolveLlmEnv(process.env);
31625
- const apiKey = envConfig.apiKey ?? systemConfig.apiKey;
32103
+ const apiKey = effectiveApiKey(systemConfig);
31626
32104
  if (!apiKey) {
31627
32105
  throw new CliError(
31628
- "missing LLM API key; set OPENCODE_GO_API_KEY (or OPENAI_API_KEY / DEEPSEEK_API_KEY) or configure it in the tnega config file"
32106
+ "missing LLM API key; set TNEGA_API_KEY (or OPENCODE_GO_API_KEY / OPENAI_API_KEY / DEEPSEEK_API_KEY) or configure it in the tnega config file"
31629
32107
  );
31630
32108
  }
31631
32109
  const model = options.model ?? profileOptions.model ?? envConfig.model ?? systemConfig.model;
@@ -31636,6 +32114,7 @@ async function runAgentCommand(options) {
31636
32114
  ...typeof model === "string" && model ? { model } : {},
31637
32115
  ...baseUrl ? { baseUrl } : {},
31638
32116
  ...protocolFrom(profileOptions) ?? systemConfig.protocol ? { protocol: protocolFrom(profileOptions) ?? systemConfig.protocol } : {},
32117
+ ...systemConfig.apiKeyHeader ? { apiKeyHeader: systemConfig.apiKeyHeader } : {},
31639
32118
  ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
31640
32119
  ...options.maxTokens === void 0 && typeof profileOptions.maxTokens === "number" ? { maxTokens: profileOptions.maxTokens } : {},
31641
32120
  ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
@@ -31766,10 +32245,10 @@ async function runEvolveCommand(options) {
31766
32245
  const configFile = options.configFile ?? systemConfigPath();
31767
32246
  const systemConfig = await readSystemConfig(configFile);
31768
32247
  const envConfig = resolveLlmEnv(process.env);
31769
- const apiKey = envConfig.apiKey ?? systemConfig.apiKey;
32248
+ const apiKey = effectiveApiKey(systemConfig);
31770
32249
  if (!apiKey) {
31771
32250
  throw new CliError(
31772
- "missing LLM API key; set OPENCODE_GO_API_KEY (or OPENAI_API_KEY / DEEPSEEK_API_KEY) or configure it in the tnega config file"
32251
+ "missing LLM API key; set TNEGA_API_KEY (or OPENCODE_GO_API_KEY / OPENAI_API_KEY / DEEPSEEK_API_KEY) or configure it in the tnega config file"
31773
32252
  );
31774
32253
  }
31775
32254
  const model = options.model ?? envConfig.model ?? systemConfig.model;
@@ -31779,6 +32258,7 @@ async function runEvolveCommand(options) {
31779
32258
  ...model ? { model } : {},
31780
32259
  ...baseUrl ? { baseUrl } : {},
31781
32260
  ...systemConfig.protocol ? { protocol: systemConfig.protocol } : {},
32261
+ ...systemConfig.apiKeyHeader ? { apiKeyHeader: systemConfig.apiKeyHeader } : {},
31782
32262
  ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
31783
32263
  ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
31784
32264
  ...systemConfig.temperature !== void 0 && options.temperature === void 0 ? { temperature: systemConfig.temperature } : {},
@@ -33145,6 +33625,7 @@ function adapterFromConfig(effective, apiKey) {
33145
33625
  model: effective.model
33146
33626
  };
33147
33627
  if (effective.protocol) options.protocol = effective.protocol;
33628
+ if (effective.apiKeyHeader) options.apiKeyHeader = effective.apiKeyHeader;
33148
33629
  if (effective.temperature !== void 0) options.temperature = effective.temperature;
33149
33630
  return createLlmAdapter(options);
33150
33631
  }
@@ -33827,6 +34308,7 @@ export {
33827
34308
  anthropicMessagesAdapter,
33828
34309
  assertInvariants,
33829
34310
  assertStrategy,
34311
+ assistantStreams,
33830
34312
  bootAgentRuntime,
33831
34313
  builtinTools,
33832
34314
  candidateKey,