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.
@@ -9078,17 +9078,88 @@ function checkMonotonicSeq(events) {
9078
9078
  }
9079
9079
  return failures;
9080
9080
  }
9081
+ function isRecord(value) {
9082
+ return typeof value === "object" && value !== null && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
9083
+ }
9084
+ function isNonNegativeInteger(value) {
9085
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
9086
+ }
9087
+ function isJsonValue(value, ancestors = /* @__PURE__ */ new Set()) {
9088
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
9089
+ if (typeof value === "number") return Number.isFinite(value);
9090
+ if (typeof value !== "object" || ancestors.has(value)) return false;
9091
+ if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) return false;
9092
+ ancestors.add(value);
9093
+ const valid = (Array.isArray(value) ? Array.from(value) : Object.values(value)).every((member) => isJsonValue(member, ancestors));
9094
+ ancestors.delete(value);
9095
+ return valid;
9096
+ }
9097
+ function isStreamRecord(value) {
9098
+ if (!isRecord(value) || !isNonNegativeInteger(value.time) || !isRecord(value.chunk)) return false;
9099
+ const chunk = value.chunk;
9100
+ if (chunk.type === "stream_error") {
9101
+ 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");
9102
+ }
9103
+ if (typeof chunk.id !== "string") return false;
9104
+ switch (chunk.type) {
9105
+ case "message_start":
9106
+ return chunk.model === void 0 || typeof chunk.model === "string";
9107
+ case "message_delta":
9108
+ return typeof chunk.delta === "string";
9109
+ case "toolcall_start":
9110
+ case "toolcall_end":
9111
+ return isNonNegativeInteger(chunk.index) && typeof chunk.name === "string" && (chunk.type === "toolcall_start" || isJsonValue(chunk.arguments));
9112
+ case "message_stop":
9113
+ 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";
9114
+ default:
9115
+ return false;
9116
+ }
9117
+ }
9118
+ function checkAssistantAttempts(events) {
9119
+ const turns = /* @__PURE__ */ new Set();
9120
+ const steps = /* @__PURE__ */ new Map();
9121
+ const failures = [];
9122
+ for (const event of events) {
9123
+ if (event.type === "turn/start") turns.add(event.payload.turn);
9124
+ if (event.type === "turn/end") {
9125
+ turns.delete(event.payload.turn);
9126
+ for (const [key, turn] of steps) {
9127
+ if (turn === event.payload.turn) steps.delete(key);
9128
+ }
9129
+ }
9130
+ if (event.type === "step/start" && turns.has(event.payload.turn)) {
9131
+ steps.set(`${event.payload.turn}:${event.payload.step}`, event.payload.turn);
9132
+ }
9133
+ if (event.type === "step/end") steps.delete(`${event.payload.turn}:${event.payload.step}`);
9134
+ if (event.type !== "assistant/attempt") continue;
9135
+ const payload = event.payload;
9136
+ if (!isRecord(payload) || !isNonNegativeInteger(payload.turn) || !isNonNegativeInteger(payload.step) || !turns.has(payload.turn) || !steps.has(`${payload.turn}:${payload.step}`)) {
9137
+ failures.push({
9138
+ name: "assistant/attempt-without-open-step",
9139
+ detail: `assistant/attempt at seq ${event.seq} has no matching open turn and step`
9140
+ });
9141
+ }
9142
+ if (!isRecord(payload) || !Array.isArray(payload.stream) || !payload.stream.every(isStreamRecord)) {
9143
+ failures.push({
9144
+ name: "assistant/attempt-invalid-stream",
9145
+ detail: `assistant/attempt at seq ${event.seq} must contain an ordered array of normalized stream records`
9146
+ });
9147
+ }
9148
+ }
9149
+ return failures;
9150
+ }
9081
9151
  function checkSessionInvariants(events) {
9082
9152
  return [
9083
9153
  ...checkBalancedTurns(events),
9084
9154
  ...checkBalancedSteps(events),
9085
9155
  ...checkBalancedToolCalls(events),
9086
- ...checkMonotonicSeq(events)
9156
+ ...checkMonotonicSeq(events),
9157
+ ...checkAssistantAttempts(events)
9087
9158
  ];
9088
9159
  }
9089
9160
 
9090
9161
  // packages/session/src/index.ts
9091
- var SESSION_FORMAT_VERSION = 7;
9162
+ var SESSION_FORMAT_VERSION = 10;
9092
9163
  var SessionFormatError = class extends Error {
9093
9164
  name = "SessionFormatError";
9094
9165
  };
@@ -9260,7 +9331,7 @@ function transcriptEvents(events) {
9260
9331
  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)));
9261
9332
  const ordered = [...skeleton];
9262
9333
  for (const event of structure) {
9263
- let index = ordered.length;
9334
+ let index = 0;
9264
9335
  for (let cursor = ordered.length - 1; cursor >= 0; cursor -= 1) {
9265
9336
  if (ordered[cursor].seq <= event.seq) {
9266
9337
  index = cursor + 1;
@@ -9357,6 +9428,7 @@ function estimateEventTokens(event) {
9357
9428
  return Math.ceil(raw.length / 4);
9358
9429
  }
9359
9430
  case "plan":
9431
+ case "assistant/attempt":
9360
9432
  return 0;
9361
9433
  case "checkpoint":
9362
9434
  return estimateMessageTokens(event.payload.messages);
@@ -9491,9 +9563,15 @@ function estimateContextUsage(messages, limit = DEFAULT_CONTEXT_LIMIT) {
9491
9563
  };
9492
9564
  }
9493
9565
  var SessionLog = class {
9494
- constructor(file, _broadcast) {
9566
+ constructor(file, _broadcast, publicationContext) {
9495
9567
  this.file = file;
9496
9568
  this._broadcast = _broadcast;
9569
+ if (!this._broadcast && publicationContext) {
9570
+ this._broadcast = (type, payload) => {
9571
+ if (type === "event") publicationContext.emit("session/event", payload);
9572
+ else publicationContext.emit("session/flush", payload);
9573
+ };
9574
+ }
9497
9575
  }
9498
9576
  file;
9499
9577
  _broadcast;
@@ -9662,7 +9740,7 @@ var SessionLog = class {
9662
9740
  selected.push(clone(event));
9663
9741
  continue;
9664
9742
  }
9665
- if (event.type === "meta" || event.type === "llm/retry" || event.type === "llm/retry-started" || event.type.startsWith("turn/") || event.type.startsWith("step/")) {
9743
+ 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/")) {
9666
9744
  continue;
9667
9745
  }
9668
9746
  if (event.type === "checkpoint") {
@@ -10012,7 +10090,7 @@ function copyMessages(messages) {
10012
10090
  copy.tool_calls = message3.tool_calls.map((call) => ({
10013
10091
  id: call.id,
10014
10092
  name: call.name,
10015
- arguments: call.arguments
10093
+ arguments: structuredClone(call.arguments)
10016
10094
  }));
10017
10095
  }
10018
10096
  return copy;
@@ -10043,6 +10121,75 @@ function copyCompletion(completion) {
10043
10121
  }
10044
10122
  return copy;
10045
10123
  }
10124
+ function canonicalMessages(messages) {
10125
+ return JSON.stringify(messages.map((message3) => ({
10126
+ role: message3.role,
10127
+ content: message3.content,
10128
+ ...message3.name ? { name: message3.name } : {},
10129
+ ...message3.tool_call_id ? { tool_call_id: message3.tool_call_id } : {},
10130
+ ...message3.tool_calls?.length ? { tool_calls: message3.tool_calls } : {},
10131
+ ...message3.toolOk !== void 0 ? { toolOk: message3.toolOk } : {},
10132
+ ...message3.toolError ? {
10133
+ toolError: { name: message3.toolError.name, message: message3.toolError.message }
10134
+ } : {}
10135
+ })));
10136
+ }
10137
+ function freezeRequestData(data) {
10138
+ const visited = /* @__PURE__ */ new Set();
10139
+ const freeze = (value) => {
10140
+ if (typeof value !== "object" || value === null || visited.has(value)) return;
10141
+ visited.add(value);
10142
+ for (const child of Object.values(value)) freeze(child);
10143
+ Object.freeze(value);
10144
+ };
10145
+ freeze(data);
10146
+ }
10147
+ function lockStreamRequest(request) {
10148
+ freezeRequestData(request.messages);
10149
+ request.tools = Object.freeze(request.tools.map((tool) => {
10150
+ const schema = structuredClone(tool.schema);
10151
+ freezeRequestData(schema);
10152
+ return Object.freeze({ ...tool, schema });
10153
+ }));
10154
+ Object.freeze(request.options);
10155
+ Object.freeze(request);
10156
+ }
10157
+ function requestHeaderFor(request, input) {
10158
+ const system = input.find((message3) => message3.role === "system")?.content;
10159
+ const tools2 = request.tools.map((tool) => ({
10160
+ name: tool.schema.name,
10161
+ description: tool.schema.description,
10162
+ ...tool.schema.parameters ? { parameters: tool.schema.parameters } : {}
10163
+ }));
10164
+ const config2 = {
10165
+ ...request.options.provider ? { provider: request.options.provider } : {},
10166
+ ...request.options.model ? { model: request.options.model } : {},
10167
+ ...request.options.temperature !== void 0 ? { temperature: request.options.temperature } : {}
10168
+ };
10169
+ return {
10170
+ ...Object.keys(config2).length ? { config: config2 } : {},
10171
+ ...system !== void 0 ? { system } : {},
10172
+ ...tools2.length ? { tools: tools2 } : {}
10173
+ };
10174
+ }
10175
+ async function* completeAsStream(llm, messages, tools2, options) {
10176
+ const id = randomUUID2();
10177
+ yield { type: "message_start", id };
10178
+ const completion = await llm.complete(messages, tools2, options);
10179
+ if (completion.content) yield { type: "message_delta", id, delta: completion.content };
10180
+ for (let index = 0; index < (completion.toolCalls?.length ?? 0); index += 1) {
10181
+ const call = completion.toolCalls[index];
10182
+ yield { type: "toolcall_start", id: call.id, index, name: call.name };
10183
+ yield {
10184
+ type: "toolcall_end",
10185
+ id: call.id,
10186
+ index,
10187
+ name: call.name,
10188
+ arguments: call.arguments
10189
+ };
10190
+ }
10191
+ yield { type: "message_stop", id, finishReason: completion.finishReason };
10192
+ }
10046
10193
  function isCancelCause(value) {
10047
10194
  if (typeof value !== "object" || value === null) return false;
10048
10195
  const record3 = value;
@@ -10063,6 +10210,13 @@ function cancelCauseFromSignal(signal) {
10063
10210
  if (isCancelCause(reason)) return reason;
10064
10211
  return { type: "abort" };
10065
10212
  }
10213
+ function abortedToolError(signal) {
10214
+ const cause = cancelCauseFromSignal(signal);
10215
+ return {
10216
+ name: "AbortError",
10217
+ message: `tool call aborted: ${cause?.type ?? "abort"}`
10218
+ };
10219
+ }
10066
10220
  function toTurnEndReason(fields) {
10067
10221
  const { cancelled, turnError, finishReason, cancelCause } = fields;
10068
10222
  if (cancelled || finishReason === "cancelled") {
@@ -10107,6 +10261,78 @@ async function cancellableDelay(ms, signal) {
10107
10261
  signal?.addEventListener("abort", onAbort, { once: true });
10108
10262
  });
10109
10263
  }
10264
+ var AssistantStreamAttempt = class {
10265
+ constructor(session2, turn, step, nextRevision) {
10266
+ this.session = session2;
10267
+ this.turn = turn;
10268
+ this.step = step;
10269
+ this.nextRevision = nextRevision;
10270
+ }
10271
+ session;
10272
+ turn;
10273
+ step;
10274
+ nextRevision;
10275
+ attemptId = randomUUID2();
10276
+ records = [];
10277
+ ended = false;
10278
+ start() {
10279
+ return { type: "assistant/stream", frame: {
10280
+ type: "start",
10281
+ attemptId: this.attemptId,
10282
+ revision: this.nextRevision(),
10283
+ turn: this.turn,
10284
+ step: this.step
10285
+ } };
10286
+ }
10287
+ push(chunk) {
10288
+ if (this.ended) throw new AgentError("assistant attempt is already settled");
10289
+ const record3 = { time: Date.now(), chunk: structuredClone(chunk) };
10290
+ const index = this.records.length;
10291
+ this.records.push(record3);
10292
+ return { type: "assistant/stream", frame: {
10293
+ type: "chunk",
10294
+ attemptId: this.attemptId,
10295
+ revision: this.nextRevision(),
10296
+ index,
10297
+ ...structuredClone(record3)
10298
+ } };
10299
+ }
10300
+ async settle(message3) {
10301
+ if (this.ended) throw new AgentError("assistant attempt is already settled");
10302
+ const eventType = message3 ? "assistant/message" : "assistant/attempt";
10303
+ let event;
10304
+ try {
10305
+ 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 });
10306
+ } catch (cause) {
10307
+ this.ended = true;
10308
+ const frame = { type: "assistant/stream", frame: {
10309
+ type: "end",
10310
+ attemptId: this.attemptId,
10311
+ revision: this.nextRevision(),
10312
+ index: this.records.length,
10313
+ outcome: { kind: "abandoned" }
10314
+ } };
10315
+ throw new AttemptSettlementError(cause, frame);
10316
+ }
10317
+ this.ended = true;
10318
+ return { type: "assistant/stream", frame: {
10319
+ type: "end",
10320
+ attemptId: this.attemptId,
10321
+ revision: this.nextRevision(),
10322
+ index: this.records.length,
10323
+ outcome: { kind: "committed", eventType, seq: event.seq }
10324
+ } };
10325
+ }
10326
+ };
10327
+ var AttemptSettlementError = class extends Error {
10328
+ constructor(cause, frame) {
10329
+ super("assistant attempt settlement failed");
10330
+ this.cause = cause;
10331
+ this.frame = frame;
10332
+ }
10333
+ cause;
10334
+ frame;
10335
+ };
10110
10336
  var AgentService = class {
10111
10337
  constructor(ctx, config2 = {}) {
10112
10338
  this.ctx = ctx;
@@ -10115,9 +10341,24 @@ var AgentService = class {
10115
10341
  }
10116
10342
  ctx;
10117
10343
  config;
10344
+ async *settleAttempt(attempt, message3) {
10345
+ try {
10346
+ yield await attempt.settle(message3);
10347
+ } catch (error2) {
10348
+ if (error2 instanceof AttemptSettlementError) {
10349
+ yield error2.frame;
10350
+ throw error2.cause;
10351
+ }
10352
+ throw error2;
10353
+ }
10354
+ }
10118
10355
  inbox;
10119
10356
  _seriesStarted = false;
10120
10357
  _persistedRequest = false;
10358
+ _streamRevision = 0;
10359
+ _attempt(session2, turn, step) {
10360
+ return new AssistantStreamAttempt(session2, turn, step, () => ++this._streamRevision);
10361
+ }
10121
10362
  /** Call when a request begins an explicit new model-message series. */
10122
10363
  startSeries() {
10123
10364
  this._seriesStarted = true;
@@ -10131,7 +10372,7 @@ var AgentService = class {
10131
10372
  return this._persistedRequest;
10132
10373
  }
10133
10374
  async run(input, options = {}) {
10134
- return consumeAgentStream(this._stream(input, options, false));
10375
+ return consumeAgentStream(this._stream(input, options));
10135
10376
  }
10136
10377
  async *runStream(input, options = {}) {
10137
10378
  const iterator = this._stream(input, options)[Symbol.asyncIterator]();
@@ -10141,7 +10382,7 @@ var AgentService = class {
10141
10382
  yield next.value;
10142
10383
  }
10143
10384
  }
10144
- async *_stream(input, options = {}, useStream = true) {
10385
+ async *_stream(input, options = {}) {
10145
10386
  const claimed = input ?? this.inbox.claim();
10146
10387
  if (!claimed) throw new AgentError("no agent input available");
10147
10388
  await this.config.hooks?.beforeRun?.(claimed, options);
@@ -10149,26 +10390,32 @@ var AgentService = class {
10149
10390
  const tools2 = this._tools();
10150
10391
  const llm = this._llm();
10151
10392
  if (!llm) throw new AgentError("no LLM adapter available");
10393
+ const streamAdapter = llm.stream;
10394
+ const persistChunks = streamAdapter !== void 0;
10152
10395
  const maxTurns = options.maxTurns ?? this.config.maxTurns ?? 64;
10153
10396
  const maxSteps = options.maxSteps ?? this.config.maxSteps ?? 64;
10154
10397
  const contextBudget = options.contextBudget ?? this.config.contextBudget;
10155
10398
  const injected = this.inbox.injected();
10156
10399
  this.ctx.emit("agent/start", { input: claimed, options, injected });
10157
10400
  let messages = await this._initialMessages(claimed);
10401
+ let claimedMessages = messages.filter((message3) => message3.role === "user");
10158
10402
  const steps = [];
10159
10403
  let output = "";
10160
10404
  let finishReason = "stop";
10405
+ let lengthReached = false;
10161
10406
  this.ctx.emit("agent/turn-start", {
10162
10407
  input: claimed,
10163
10408
  messages: copyMessages(messages),
10164
10409
  injected
10165
10410
  });
10166
- const turn = await session2.nextTurn();
10167
- await session2.append("turn/start", {
10168
- turn,
10169
- input: claimed.text ?? claimed,
10170
- reason: "user"
10171
- });
10411
+ const turn = options.turn ?? await session2.nextTurn();
10412
+ if (options.turn === void 0) {
10413
+ await session2.append("turn/start", {
10414
+ turn,
10415
+ input: claimed.text ?? claimed,
10416
+ reason: "user"
10417
+ });
10418
+ }
10172
10419
  let index = 0;
10173
10420
  let finalTurnGranted = false;
10174
10421
  let currentStepIndex;
@@ -10187,20 +10434,22 @@ var AgentService = class {
10187
10434
  messages = await this._enforceContextBudget(session2, contextBudget, messages);
10188
10435
  }
10189
10436
  const stepInput = copyMessages(messages);
10190
- const preStep = this.ctx.waterfall("agent/pre-step", {
10437
+ const preStep = await this.ctx.waterfallAsync("agent/pre-step", {
10191
10438
  index,
10192
10439
  turn,
10193
10440
  step: index,
10194
10441
  messages: stepInput,
10442
+ claimedMessages: copyMessages(claimedMessages),
10195
10443
  ...options.signal ? { signal: options.signal } : {}
10196
10444
  }, (payload) => payload);
10197
- if (!preStep || !Array.isArray(preStep.messages) || !preStep.messages.length) {
10445
+ if (!preStep || preStep.admission === "reject" || !Array.isArray(preStep.messages) || !preStep.messages.length) {
10198
10446
  break;
10199
10447
  }
10200
10448
  if (preStep.startsRequestSeries) this._seriesStarted = true;
10201
10449
  await session2.append("step/start", { turn, step: index });
10202
10450
  currentStepIndex = index;
10203
10451
  const requestedInput = copyMessages(preStep.messages);
10452
+ const admittedHistory = await session2.deriveMessages();
10204
10453
  this.ctx.emit("agent/step", {
10205
10454
  index,
10206
10455
  turn,
@@ -10212,93 +10461,85 @@ var AgentService = class {
10212
10461
  maxSteps: maxSteps - steps.length
10213
10462
  };
10214
10463
  if (options.signal) completeOptions.signal = options.signal;
10215
- const request = this.ctx.waterfall("agent/request", {
10216
- index,
10217
- messages: requestedInput,
10218
- tools: availableTools,
10219
- options: completeOptions
10220
- }, (payload) => payload);
10221
- if (!request || !request.options || !request.tools) {
10222
- throw new AgentError("agent/request must return a request payload");
10223
- }
10224
10464
  let llmMessages = copyMessages(requestedInput);
10225
10465
  let completion;
10226
- const streamMethod = useStream ? llm.stream : void 0;
10466
+ let activeAttempt;
10227
10467
  let attempt = 0;
10228
- let persistedStepInput = false;
10229
10468
  while (true) {
10230
10469
  if (options.signal?.aborted) {
10231
10470
  finishReason = "cancelled";
10232
10471
  break;
10233
10472
  }
10234
10473
  const streamEvents = [];
10474
+ activeAttempt = this._attempt(session2, turn, index);
10475
+ yield activeAttempt.start();
10476
+ const streamRequest = {
10477
+ index,
10478
+ messages: copyMessages(requestedInput),
10479
+ tools: [...availableTools],
10480
+ options: { ...completeOptions }
10481
+ };
10235
10482
  try {
10236
- if (streamMethod) {
10237
- const streamRequest = {
10238
- index,
10239
- messages: llmMessages,
10240
- tools: request.tools,
10241
- options: request.options
10242
- };
10243
- const stream = await this.ctx.waterfallAsync(
10244
- "llm/stream",
10245
- streamRequest,
10246
- async (payload) => {
10247
- if (!streamMethod) {
10248
- throw new AgentError("llm.stream is required for streaming runs");
10249
- }
10250
- return streamMethod(payload.messages, payload.tools, payload.options);
10251
- }
10252
- );
10253
- if (!stream || typeof stream[Symbol.asyncIterator] !== "function") {
10254
- throw new AgentError("llm/stream must return an async iterable stream");
10255
- }
10483
+ const request = await this.ctx.waterfallAsync("agent/request", {
10484
+ index,
10485
+ messages: copyMessages(requestedInput),
10486
+ tools: streamRequest.tools,
10487
+ options: streamRequest.options
10488
+ }, (payload) => payload);
10489
+ if (!request || !request.options || !request.tools) {
10490
+ throw new AgentError("agent/request must return a request payload");
10491
+ }
10492
+ streamRequest.tools = request.tools;
10493
+ streamRequest.options = request.options;
10494
+ let preparation;
10495
+ const prepareRequest = () => preparation ??= (async () => {
10496
+ lockStreamRequest(streamRequest);
10256
10497
  llmMessages = copyMessages(streamRequest.messages);
10257
- if (!persistedStepInput) {
10258
- await this._persistStepInput(session2, request, llmMessages);
10259
- if (this.config.assertReplayable) {
10260
- await this._assertReplayable(session2, llmMessages);
10261
- }
10262
- persistedStepInput = true;
10263
- }
10264
- let chunkIndex = 0;
10265
- for await (const event of stream) {
10266
- streamEvents.push(event);
10267
- if (event.type === "message_delta") {
10268
- await session2.append("assistant/chunk", {
10269
- id: event.id,
10270
- content: event.delta,
10271
- index: chunkIndex
10272
- });
10273
- chunkIndex += 1;
10274
- }
10275
- yield event;
10276
- }
10277
- completion = completionFromStreamEvents(streamEvents);
10278
- } else {
10279
- if (!persistedStepInput) {
10280
- await this._persistStepInput(session2, request, llmMessages);
10281
- if (this.config.assertReplayable) {
10282
- await this._assertReplayable(session2, llmMessages);
10283
- }
10284
- persistedStepInput = true;
10498
+ await this._persistStepInput(session2, streamRequest, llmMessages, admittedHistory);
10499
+ await this._assertReplayable(session2, streamRequest, llmMessages);
10500
+ })();
10501
+ const stream = await this.ctx.waterfallAsync(
10502
+ "llm/stream",
10503
+ streamRequest,
10504
+ (payload) => (async function* () {
10505
+ await prepareRequest();
10506
+ yield* streamAdapter ? streamAdapter.call(llm, payload.messages, payload.tools, payload.options) : completeAsStream(llm, payload.messages, payload.tools, payload.options);
10507
+ })()
10508
+ );
10509
+ if (!stream || typeof stream[Symbol.asyncIterator] !== "function") {
10510
+ throw new AgentError("llm/stream must return an async iterable stream");
10511
+ }
10512
+ await prepareRequest();
10513
+ let chunkIndex = 0;
10514
+ for await (const event of stream) {
10515
+ streamEvents.push(structuredClone(event));
10516
+ yield activeAttempt.push(event);
10517
+ if (persistChunks && event.type === "message_delta") {
10518
+ await session2.append("assistant/chunk", {
10519
+ id: event.id,
10520
+ content: event.delta,
10521
+ index: chunkIndex
10522
+ });
10523
+ chunkIndex += 1;
10285
10524
  }
10286
- completion = await llm.complete(llmMessages, request.tools, request.options);
10525
+ yield event;
10526
+ }
10527
+ completion = completionFromStreamEvents(streamEvents);
10528
+ if (completion.finishReason === "error" || completion.finishReason === "cancelled") {
10529
+ const terminalReason = completion.finishReason;
10530
+ completion = void 0;
10531
+ throw new AgentError(`LLM stream finished with ${terminalReason}`);
10287
10532
  }
10288
10533
  break;
10289
10534
  } catch (error2) {
10290
- const content = partialStreamContent(streamEvents);
10291
- if (content) {
10292
- await session2.append("assistant/message", {
10293
- content,
10294
- interrupted: true
10295
- });
10296
- llmMessages = await session2.deriveMessages();
10297
- }
10535
+ yield activeAttempt.push({ type: "stream_error", error: toToolError(error2) });
10298
10536
  if (options.signal?.aborted) {
10537
+ const content = partialStreamContent(streamEvents);
10538
+ yield* this.settleAttempt(activeAttempt, content ? { content, interrupted: true } : void 0);
10299
10539
  finishReason = "cancelled";
10300
10540
  break;
10301
10541
  }
10542
+ yield* this.settleAttempt(activeAttempt);
10302
10543
  attempt += 1;
10303
10544
  const failure = toToolError(error2);
10304
10545
  const decision = await this.ctx.waterfallAsync(
@@ -10307,14 +10548,14 @@ var AgentService = class {
10307
10548
  index,
10308
10549
  turn,
10309
10550
  step: index,
10310
- messages: copyMessages(llmMessages),
10311
- tools: request.tools,
10312
- options: request.options,
10551
+ messages: copyMessages(streamRequest.messages),
10552
+ tools: [...streamRequest.tools],
10553
+ options: { ...streamRequest.options },
10313
10554
  attempt,
10314
10555
  error: error2,
10315
10556
  failure,
10316
- ...request.options.provider ? { provider: request.options.provider } : {},
10317
- ...request.options.model ? { model: request.options.model } : {},
10557
+ ...streamRequest.options.provider ? { provider: streamRequest.options.provider } : {},
10558
+ ...streamRequest.options.model ? { model: streamRequest.options.model } : {},
10318
10559
  ...options.signal ? { signal: options.signal } : {}
10319
10560
  },
10320
10561
  () => void 0
@@ -10339,16 +10580,30 @@ var AgentService = class {
10339
10580
  await session2.append("llm/retry-started", { retryId, retry: attempt });
10340
10581
  }
10341
10582
  }
10342
- if (options.signal?.aborted) break;
10343
- if (!completion) throw new AgentError("LLM adapter did not produce a completion");
10583
+ if (!completion || !activeAttempt) {
10584
+ if (options.signal?.aborted) {
10585
+ finishReason = "cancelled";
10586
+ break;
10587
+ }
10588
+ throw new AgentError("LLM adapter did not produce a completion");
10589
+ }
10344
10590
  const toolCalls = completion.toolCalls ?? [];
10345
- if (finalTurnGranted && toolCalls.length) {
10591
+ if (completion.finishReason === "length") lengthReached = true;
10592
+ if (options.signal?.aborted && toolCalls.length === 0) {
10593
+ yield activeAttempt.push({ type: "stream_error", error: { name: "AbortError", message: "stream aborted" } });
10594
+ yield* this.settleAttempt(activeAttempt);
10595
+ finishReason = "cancelled";
10596
+ break;
10597
+ }
10598
+ if (finalTurnGranted && toolCalls.length && !options.signal?.aborted) {
10599
+ yield activeAttempt.push({ type: "stream_error", error: { name: "AgentError", message: "maximum turns reached" } });
10600
+ yield* this.settleAttempt(activeAttempt);
10346
10601
  finishReason = "max_turns";
10347
10602
  break;
10348
10603
  }
10349
10604
  const toolResults = [];
10350
10605
  if (toolCalls.length) {
10351
- await session2.append("assistant/message", {
10606
+ yield* this.settleAttempt(activeAttempt, {
10352
10607
  content: completion.content ?? "",
10353
10608
  toolCalls: toolCalls.map((call) => ({
10354
10609
  id: call.id,
@@ -10369,18 +10624,30 @@ var AgentService = class {
10369
10624
  if (options.signal) toolOptions.signal = options.signal;
10370
10625
  const startedAt = Date.now();
10371
10626
  let result;
10372
- try {
10373
- result = await tools2.execute(call.name, call.arguments, toolOptions);
10374
- } catch (error2) {
10627
+ if (options.signal?.aborted) {
10375
10628
  result = {
10376
10629
  ok: false,
10377
10630
  name: call.name,
10378
10631
  callId: call.id,
10379
10632
  input: call.arguments,
10380
- error: toToolError(error2),
10633
+ error: abortedToolError(options.signal),
10381
10634
  startedAt,
10382
10635
  durationMs: Date.now() - startedAt
10383
10636
  };
10637
+ } else {
10638
+ try {
10639
+ result = await tools2.execute(call.name, call.arguments, toolOptions);
10640
+ } catch (error2) {
10641
+ result = {
10642
+ ok: false,
10643
+ name: call.name,
10644
+ callId: call.id,
10645
+ input: call.arguments,
10646
+ error: toToolError(error2),
10647
+ startedAt,
10648
+ durationMs: Date.now() - startedAt
10649
+ };
10650
+ }
10384
10651
  }
10385
10652
  toolResults.push(result);
10386
10653
  const toolResultPayload = {
@@ -10422,7 +10689,7 @@ var AgentService = class {
10422
10689
  output = completion.content;
10423
10690
  }
10424
10691
  if (!toolCalls.length) {
10425
- await session2.append("assistant/message", { content: completion.content ?? "" });
10692
+ yield* this.settleAttempt(activeAttempt, { content: completion.content ?? "" });
10426
10693
  }
10427
10694
  await session2.append("step/end", {
10428
10695
  turn,
@@ -10433,19 +10700,19 @@ var AgentService = class {
10433
10700
  currentStepIndex = void 0;
10434
10701
  const nextMessages = this._extendMessages(llmMessages, completion, toolResults);
10435
10702
  const concludesTurn = toolResults.some((result) => result.concludesTurn === true);
10436
- if (concludesTurn) {
10437
- finishReason = "stop";
10438
- break;
10439
- }
10440
- if (toolCalls.length === 0) {
10441
- finishReason = completion.finishReason === "length" ? "length" : completion.finishReason === "error" ? "error" : "stop";
10703
+ let nextStepMessages = await this._claimNextStepMessages();
10704
+ if ((toolCalls.length === 0 || concludesTurn) && nextStepMessages.length === 0) {
10705
+ finishReason = lengthReached ? "length" : completion.finishReason === "error" ? "error" : "stop";
10442
10706
  const keepGoing = await this.ctx.serial("agent/turn-stopping", {
10443
10707
  index,
10444
10708
  turn,
10445
10709
  steps: copySteps(steps),
10446
10710
  finishReason
10447
10711
  });
10448
- if (!keepGoing) break;
10712
+ if (!keepGoing) {
10713
+ nextStepMessages = await this._claimNextStepMessages();
10714
+ if (nextStepMessages.length === 0) break;
10715
+ }
10449
10716
  }
10450
10717
  if (index + 1 >= maxTurns && !finalTurnGranted) {
10451
10718
  finalTurnGranted = true;
@@ -10455,7 +10722,8 @@ var AgentService = class {
10455
10722
  }
10456
10723
  if (toolCalls.length) finishReason = "tool_calls";
10457
10724
  index += 1;
10458
- messages = nextMessages;
10725
+ messages = [...nextMessages, ...nextStepMessages];
10726
+ claimedMessages = nextStepMessages.filter((message3) => message3.role === "user");
10459
10727
  }
10460
10728
  } catch (error2) {
10461
10729
  turnError = error2;
@@ -10541,23 +10809,22 @@ var AgentService = class {
10541
10809
  });
10542
10810
  return compactMessages.map((message3) => copyMessages([message3])[0]);
10543
10811
  }
10544
- async _persistStepInput(session2, request, input) {
10545
- const system = input.find((message3) => message3.role === "system")?.content;
10546
- const tools2 = request.tools.map((tool) => ({
10547
- name: tool.schema.name,
10548
- description: tool.schema.description,
10549
- ...tool.schema.parameters ? { parameters: tool.schema.parameters } : {}
10550
- }));
10551
- const config2 = {
10552
- ...request.options.provider ? { provider: request.options.provider } : {},
10553
- ...request.options.model ? { model: request.options.model } : {},
10554
- ...request.options.temperature !== void 0 ? { temperature: request.options.temperature } : {}
10555
- };
10556
- const nextHeader = {
10557
- ...Object.keys(config2).length ? { config: config2 } : {},
10558
- ...system !== void 0 ? { system } : {},
10559
- ...tools2.length ? { tools: tools2 } : {}
10560
- };
10812
+ async _persistStepInput(session2, request, input, admittedHistory) {
10813
+ const surface = await session2.deriveMessages();
10814
+ const owned = admittedHistory.filter((message3) => !isUserOrSystemMessage(message3));
10815
+ let ownerIndex = 0;
10816
+ for (const message3 of input) {
10817
+ if (isUserOrSystemMessage(message3)) continue;
10818
+ const actual = canonicalMessages([message3]);
10819
+ while (ownerIndex < owned.length && canonicalMessages([owned[ownerIndex]]) !== actual) {
10820
+ ownerIndex += 1;
10821
+ }
10822
+ if (ownerIndex === owned.length) {
10823
+ throw new AgentError("request is not reconstructable from session log: unowned or reordered assistant/tool message");
10824
+ }
10825
+ ownerIndex += 1;
10826
+ }
10827
+ const nextHeader = requestHeaderFor(request, input);
10561
10828
  const previousHeader = session2.requestHeader();
10562
10829
  const isFirstRequest = !this._persistedRequest;
10563
10830
  const isResume = isFirstRequest && previousHeader !== void 0;
@@ -10587,19 +10854,23 @@ var AgentService = class {
10587
10854
  ...request.options.provider ? { provider: request.options.provider } : {},
10588
10855
  ...request.options.model ? { model: request.options.model } : {}
10589
10856
  };
10857
+ if (request.options.contextWindow !== void 0) {
10858
+ nextContext.contextWindow = request.options.contextWindow;
10859
+ }
10590
10860
  const llmService = this.ctx.reflect.get("llm", false);
10591
10861
  const routeCapacity = llmService?.routeCapacity?.();
10592
- if (routeCapacity?.contextWindow !== void 0) {
10862
+ if (nextContext.contextWindow === void 0 && routeCapacity?.contextWindow !== void 0) {
10593
10863
  nextContext.contextWindow = routeCapacity.contextWindow;
10594
10864
  }
10595
10865
  const previousContext = session2.requestContext();
10596
10866
  if (previousContext?.provider !== nextContext.provider || previousContext?.model !== nextContext.model || previousContext?.contextWindow !== nextContext.contextWindow || previousContext === void 0) {
10597
10867
  await session2.append("request/context", nextContext);
10598
10868
  }
10599
- const requested = input.filter(isUserOrSystemMessage);
10600
- const surface = await session2.deriveMessages();
10601
- const appendDurable = async (messages) => {
10602
- for (const message3 of messages) {
10869
+ const isSurfacePrefix = (messages) => canonicalMessages(messages.slice(0, surface.length)) === canonicalMessages(surface);
10870
+ const requested = surface.length > 0 && input[0]?.role === "system" && input[0].content === nextHeader.system && isSurfacePrefix(input.slice(1)) ? input.slice(1) : input;
10871
+ const tail = requested.slice(surface.length);
10872
+ if (isSurfacePrefix(requested) && tail.every(isUserOrSystemMessage)) {
10873
+ for (const message3 of tail) {
10603
10874
  if (message3.role === "user") {
10604
10875
  await session2.append("user/message", {
10605
10876
  content: message3.content,
@@ -10612,45 +10883,37 @@ var AgentService = class {
10612
10883
  });
10613
10884
  }
10614
10885
  }
10615
- };
10616
- if (surface.length === 0) {
10617
- await appendDurable(requested);
10618
10886
  return;
10619
10887
  }
10620
- const durableUsers = surface.filter((message3) => message3.role === "user").length;
10621
- let seenUsers = 0;
10622
- const newTail = [];
10623
- for (const message3 of requested) {
10624
- if (message3.role === "user") seenUsers += 1;
10625
- if (seenUsers <= durableUsers) continue;
10626
- newTail.push(message3);
10627
- }
10628
- await appendDurable(newTail);
10888
+ const nodes = await session2.surfaceEvents();
10889
+ await session2.append("checkpoint", {
10890
+ messages: copyMessages(requested),
10891
+ surfaceOp: { op: "replace", start: nodes[0].seq, end: nodes.at(-1).seq }
10892
+ });
10629
10893
  }
10630
- async _assertReplayable(session2, input) {
10894
+ async _assertReplayable(session2, request, input) {
10631
10895
  const reconstructed = await session2.deriveMessages();
10632
- const canonical = (messages) => messages.filter((message3) => message3.role !== "system").map((message3) => ({
10633
- role: message3.role,
10634
- content: message3.content,
10635
- ...message3.tool_call_id ? { tool_call_id: message3.tool_call_id } : {},
10636
- ...message3.name ? { name: message3.name } : {},
10637
- ...message3.tool_calls?.length ? { tool_calls: message3.tool_calls.map((call) => ({
10638
- id: call.id,
10639
- name: call.name,
10640
- args: JSON.stringify(call.arguments ?? {})
10641
- })) } : {}
10642
- }));
10643
- const actual = canonical(input);
10644
- const replay = canonical(reconstructed);
10645
- if (JSON.stringify(actual) !== JSON.stringify(replay)) {
10896
+ const actual = canonicalMessages(input);
10897
+ const replay = canonicalMessages(reconstructed);
10898
+ const header = session2.requestHeader();
10899
+ const headerOwnsPrefix = input[0]?.role === "system" && input[0].content === header?.system && canonicalMessages(input.slice(1)) === replay;
10900
+ if (actual !== replay && !headerOwnsPrefix) {
10646
10901
  throw new AgentError(
10647
- `request is not reconstructable from session log: actual=${JSON.stringify(actual)} replay=${JSON.stringify(replay)}`
10902
+ `request is not reconstructable from session log: actual=${actual} replay=${replay}`
10648
10903
  );
10649
10904
  }
10905
+ const expected = requestHeaderFor(request, input);
10906
+ if (header?.system !== expected.system || JSON.stringify(header?.tools ?? []) !== JSON.stringify(expected.tools ?? []) || JSON.stringify(header?.config ?? {}) !== JSON.stringify(expected.config ?? {})) {
10907
+ throw new AgentError("request envelope is not reconstructable from session log");
10908
+ }
10909
+ }
10910
+ async _claimNextStepMessages() {
10911
+ const inputs = await this.config.claimNextStep?.() ?? [];
10912
+ 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) }] : []);
10650
10913
  }
10651
10914
  async _initialMessages(input) {
10652
10915
  const promptService = this.ctx.reflect.get("systemPrompt", false);
10653
- const assembled = promptService ? (await promptService.assemble()).text.trim() : void 0;
10916
+ const assembled = promptService ? (await promptService.assemble({}, this.ctx)).text.trim() : void 0;
10654
10917
  const systemPrompt3 = assembled || this.inbox.injected().get("agentSystem");
10655
10918
  const systemMessage = typeof systemPrompt3 === "string" && systemPrompt3 ? [{ role: "system", content: systemPrompt3 }] : [];
10656
10919
  if (input.messages?.length) {
@@ -10675,7 +10938,7 @@ var AgentService = class {
10675
10938
  arguments: call.arguments
10676
10939
  }));
10677
10940
  }
10678
- next.push(assistant);
10941
+ if (assistant.content || toolCalls.length) next.push(assistant);
10679
10942
  for (const call of toolCalls) {
10680
10943
  const result = toolResults.find((candidate) => candidate.callId === call.id);
10681
10944
  const content = result ? result.ok ? stringify2(result.output) : `error: ${result.error?.message ?? "unknown"}` : "error: missing tool result";
@@ -10705,7 +10968,8 @@ var AgentService = class {
10705
10968
  return session2;
10706
10969
  }
10707
10970
  _tools() {
10708
- const tools2 = this.ctx.tools;
10971
+ const scoped = this.ctx.reflect.get("tools", false);
10972
+ const tools2 = scoped ?? this.ctx.tools;
10709
10973
  if (!tools2) throw new AgentError("tools service is required");
10710
10974
  return tools2;
10711
10975
  }
@@ -10888,67 +11152,77 @@ var DurableInbox = class _DurableInbox {
10888
11152
  _session;
10889
11153
  _nextTurn = [];
10890
11154
  _nextStep = [];
11155
+ _operationTail = Promise.resolve();
10891
11156
  get size() {
10892
11157
  return this._nextTurn.length + this._nextStep.length;
10893
11158
  }
10894
11159
  async insert(input, target = "next-turn") {
10895
- const message3 = {
10896
- id: randomUUID3(),
10897
- ...input.text !== void 0 ? { text: input.text } : {},
10898
- ...input.content !== void 0 ? { content: input.content } : {}
10899
- };
10900
- const list = target === "next-step" ? this._nextStep : this._nextTurn;
10901
- list.push(message3);
10902
- await this._session.append("agent/inbox/spliced", {
10903
- target,
10904
- index: list.length - 1,
10905
- inserted: [{
10906
- id: message3.id,
10907
- content: contentOf(message3),
10908
- ...payloadOf(message3) !== void 0 ? { payload: payloadOf(message3) } : {}
10909
- }]
11160
+ return this._run(async () => {
11161
+ const message3 = {
11162
+ id: randomUUID3(),
11163
+ ...input.text !== void 0 ? { text: input.text } : {},
11164
+ ...input.content !== void 0 ? { content: input.content } : {}
11165
+ };
11166
+ const list = target === "next-step" ? this._nextStep : this._nextTurn;
11167
+ const index = list.length;
11168
+ await this._session.append("agent/inbox/spliced", {
11169
+ target,
11170
+ index,
11171
+ inserted: [{
11172
+ id: message3.id,
11173
+ content: contentOf(message3),
11174
+ ...payloadOf(message3) !== void 0 ? { payload: payloadOf(message3) } : {}
11175
+ }]
11176
+ });
11177
+ list.push(message3);
11178
+ return message3;
10910
11179
  });
10911
- return message3;
10912
11180
  }
10913
11181
  async steer(input) {
10914
- const message3 = {
10915
- id: randomUUID3(),
10916
- ...input.text !== void 0 ? { text: input.text } : {},
10917
- ...input.content !== void 0 ? { content: input.content } : {}
10918
- };
10919
- this._nextStep.unshift(message3);
10920
- await this._session.append("agent/inbox/spliced", {
10921
- target: "next-step",
10922
- index: 0,
10923
- inserted: [{
10924
- id: message3.id,
10925
- content: contentOf(message3),
10926
- ...payloadOf(message3) !== void 0 ? { payload: payloadOf(message3) } : {},
10927
- mode: "steer"
10928
- }]
10929
- });
10930
- return message3;
10931
- }
10932
- async claim() {
10933
- const steered = this._nextStep.shift();
10934
- if (steered) {
11182
+ return this._run(async () => {
11183
+ const message3 = {
11184
+ id: randomUUID3(),
11185
+ ...input.text !== void 0 ? { text: input.text } : {},
11186
+ ...input.content !== void 0 ? { content: input.content } : {}
11187
+ };
10935
11188
  await this._session.append("agent/inbox/spliced", {
10936
11189
  target: "next-step",
10937
- index: 0,
10938
- deleteCount: 1
11190
+ index: this._nextStep.length,
11191
+ inserted: [{
11192
+ id: message3.id,
11193
+ content: contentOf(message3),
11194
+ ...payloadOf(message3) !== void 0 ? { payload: payloadOf(message3) } : {},
11195
+ mode: "steer"
11196
+ }]
10939
11197
  });
10940
- return steered;
10941
- }
10942
- const next = this._nextTurn.shift();
10943
- if (next) {
10944
- await this._session.append("agent/inbox/spliced", {
10945
- target: "next-turn",
10946
- index: 0,
10947
- deleteCount: 1
10948
- });
10949
- return next;
10950
- }
10951
- return void 0;
11198
+ this._nextStep.push(message3);
11199
+ return message3;
11200
+ });
11201
+ }
11202
+ async claim() {
11203
+ return this._run(async () => {
11204
+ const steered = this._nextStep[0];
11205
+ if (steered) {
11206
+ await this._session.append("agent/inbox/spliced", {
11207
+ target: "next-step",
11208
+ index: 0,
11209
+ deleteCount: 1
11210
+ });
11211
+ this._nextStep.shift();
11212
+ return steered;
11213
+ }
11214
+ const next = this._nextTurn[0];
11215
+ if (next) {
11216
+ await this._session.append("agent/inbox/spliced", {
11217
+ target: "next-turn",
11218
+ index: 0,
11219
+ deleteCount: 1
11220
+ });
11221
+ this._nextTurn.shift();
11222
+ return next;
11223
+ }
11224
+ return void 0;
11225
+ });
10952
11226
  }
10953
11227
  /**
10954
11228
  * Claim the complete proposed step batch: every pending next-step input plus
@@ -10956,105 +11230,108 @@ var DurableInbox = class _DurableInbox {
10956
11230
  * nothing is pending.
10957
11231
  */
10958
11232
  async claimBatch() {
10959
- const claimed = [];
10960
- if (this._nextStep.length) {
10961
- const removed = this._nextStep.splice(0);
10962
- claimed.push(...removed);
11233
+ return this._run(async () => {
11234
+ const claimed = [...this._nextStep];
11235
+ const nextTurn = this._nextTurn[0];
11236
+ if (nextTurn) claimed.push(nextTurn);
11237
+ if (!claimed.length) return [];
11238
+ const deleteCounts = { nextStep: this._nextStep.length, nextTurn: nextTurn ? 1 : 0 };
11239
+ await this._session.append("agent/inbox/spliced", { target: "all", deleteCounts });
11240
+ this._nextStep.splice(0, deleteCounts.nextStep);
11241
+ this._nextTurn.splice(0, deleteCounts.nextTurn);
11242
+ return claimed;
11243
+ });
11244
+ }
11245
+ /** Claim every input waiting for the next step, without consuming a turn. */
11246
+ async claimNextStep() {
11247
+ return this._run(async () => {
11248
+ if (!this._nextStep.length) return [];
11249
+ const claimed = [...this._nextStep];
10963
11250
  await this._session.append("agent/inbox/spliced", {
10964
11251
  target: "next-step",
10965
11252
  index: 0,
10966
- deleteCount: removed.length
10967
- });
10968
- }
10969
- const nextTurn = this._nextTurn.shift();
10970
- if (nextTurn) {
10971
- claimed.push(nextTurn);
10972
- await this._session.append("agent/inbox/spliced", {
10973
- target: "next-turn",
10974
- index: 0,
10975
- deleteCount: 1
11253
+ deleteCount: claimed.length
10976
11254
  });
10977
- }
10978
- return claimed;
11255
+ this._nextStep.splice(0, claimed.length);
11256
+ return claimed;
11257
+ });
10979
11258
  }
10980
11259
  async clear() {
10981
- if (this._nextTurn.length) {
10982
- this._nextTurn = [];
11260
+ return this._run(async () => {
11261
+ if (!this._nextTurn.length && !this._nextStep.length) return;
10983
11262
  await this._session.append("agent/inbox/spliced", {
10984
- target: "next-turn",
10985
- index: 0,
10986
- deleteCount: Number.POSITIVE_INFINITY
11263
+ target: "all"
10987
11264
  });
10988
- }
10989
- if (this._nextStep.length) {
11265
+ this._nextTurn = [];
10990
11266
  this._nextStep = [];
10991
- await this._session.append("agent/inbox/spliced", {
10992
- target: "next-step",
10993
- index: 0,
10994
- deleteCount: Number.POSITIVE_INFINITY
10995
- });
10996
- }
11267
+ });
10997
11268
  }
10998
11269
  /** Insert a message at an explicit target/index boundary. */
10999
11270
  async insertAt(input, target, index, mode = "followup") {
11000
- const message3 = {
11001
- id: randomUUID3(),
11002
- ...input.text !== void 0 ? { text: input.text } : {},
11003
- ...input.content !== void 0 ? { content: input.content } : {}
11004
- };
11005
- const list = target === "next-step" ? this._nextStep : this._nextTurn;
11006
- const safeIndex = Math.max(0, Math.min(index, list.length));
11007
- list.splice(safeIndex, 0, message3);
11008
- await this._session.append("agent/inbox/spliced", {
11009
- target,
11010
- index: safeIndex,
11011
- inserted: [{
11012
- id: message3.id,
11013
- content: contentOf(message3),
11014
- ...payloadOf(message3) !== void 0 ? { payload: payloadOf(message3) } : {},
11015
- mode
11016
- }]
11271
+ return this._run(async () => {
11272
+ const message3 = {
11273
+ id: randomUUID3(),
11274
+ ...input.text !== void 0 ? { text: input.text } : {},
11275
+ ...input.content !== void 0 ? { content: input.content } : {}
11276
+ };
11277
+ const list = target === "next-step" ? this._nextStep : this._nextTurn;
11278
+ const safeIndex = Math.max(0, Math.min(index, list.length));
11279
+ await this._session.append("agent/inbox/spliced", {
11280
+ target,
11281
+ index: safeIndex,
11282
+ inserted: [{
11283
+ id: message3.id,
11284
+ content: contentOf(message3),
11285
+ ...payloadOf(message3) !== void 0 ? { payload: payloadOf(message3) } : {},
11286
+ mode
11287
+ }]
11288
+ });
11289
+ list.splice(safeIndex, 0, message3);
11290
+ return message3;
11017
11291
  });
11018
- return message3;
11019
11292
  }
11020
11293
  /**
11021
11294
  * Replace one pending message by id. The old message is emitted as
11022
11295
  * discarded and the new one as inserted, matching the DSH inbox contract.
11023
11296
  */
11024
11297
  async replace(messageId, input) {
11025
- const entry = this._find(messageId);
11026
- if (!entry) return void 0;
11027
- const replacement = {
11028
- id: randomUUID3(),
11029
- ...input.text !== void 0 ? { text: input.text } : {},
11030
- ...input.content !== void 0 ? { content: input.content } : {}
11031
- };
11032
- const target = entry.target;
11033
- const index = entry.index;
11034
- entry.list.splice(index, 1, replacement);
11035
- await this._session.append("agent/inbox/spliced", {
11036
- target,
11037
- index,
11038
- deleteCount: 1,
11039
- inserted: [{
11040
- id: replacement.id,
11041
- content: contentOf(replacement),
11042
- ...payloadOf(replacement) !== void 0 ? { payload: payloadOf(replacement) } : {}
11043
- }]
11298
+ return this._run(async () => {
11299
+ const entry = this._find(messageId);
11300
+ if (!entry) return void 0;
11301
+ const replacement = {
11302
+ id: randomUUID3(),
11303
+ ...input.text !== void 0 ? { text: input.text } : {},
11304
+ ...input.content !== void 0 ? { content: input.content } : {}
11305
+ };
11306
+ const target = entry.target;
11307
+ const index = entry.index;
11308
+ await this._session.append("agent/inbox/spliced", {
11309
+ target,
11310
+ index,
11311
+ deleteCount: 1,
11312
+ inserted: [{
11313
+ id: replacement.id,
11314
+ content: contentOf(replacement),
11315
+ ...payloadOf(replacement) !== void 0 ? { payload: payloadOf(replacement) } : {}
11316
+ }]
11317
+ });
11318
+ entry.list.splice(index, 1, replacement);
11319
+ return replacement;
11044
11320
  });
11045
- return replacement;
11046
11321
  }
11047
11322
  /** Remove one pending message by id. */
11048
11323
  async remove(messageId) {
11049
- const entry = this._find(messageId);
11050
- if (!entry) return void 0;
11051
- entry.list.splice(entry.index, 1);
11052
- await this._session.append("agent/inbox/spliced", {
11053
- target: entry.target,
11054
- index: entry.index,
11055
- deleteCount: 1
11324
+ return this._run(async () => {
11325
+ const entry = this._find(messageId);
11326
+ if (!entry) return void 0;
11327
+ await this._session.append("agent/inbox/spliced", {
11328
+ target: entry.target,
11329
+ index: entry.index,
11330
+ deleteCount: 1
11331
+ });
11332
+ entry.list.splice(entry.index, 1);
11333
+ return entry.message;
11056
11334
  });
11057
- return entry.message;
11058
11335
  }
11059
11336
  /** Return the first pending message by id across both queues. */
11060
11337
  get(messageId) {
@@ -11066,6 +11343,12 @@ var DurableInbox = class _DurableInbox {
11066
11343
  nextStep: [...this._nextStep]
11067
11344
  };
11068
11345
  }
11346
+ /** Keep the entire read → append → apply transition ordered, including claims. */
11347
+ _run(operation) {
11348
+ const result = this._operationTail.then(operation);
11349
+ this._operationTail = result.then(() => void 0, () => void 0);
11350
+ return result;
11351
+ }
11069
11352
  _find(messageId) {
11070
11353
  for (const target of ["next-step", "next-turn"]) {
11071
11354
  const list = target === "next-step" ? this._nextStep : this._nextTurn;
@@ -11083,6 +11366,11 @@ var DurableInbox = class _DurableInbox {
11083
11366
  for (const event of events) {
11084
11367
  if (event.type !== "agent/inbox/spliced") continue;
11085
11368
  const payload = event.payload;
11369
+ if (payload.target === "all") {
11370
+ this._nextTurn.splice(0, payload.deleteCounts?.nextTurn ?? this._nextTurn.length);
11371
+ this._nextStep.splice(0, payload.deleteCounts?.nextStep ?? this._nextStep.length);
11372
+ continue;
11373
+ }
11086
11374
  const list = payload.target === "next-step" ? this._nextStep : this._nextTurn;
11087
11375
  const count = payload.deleteCount ?? 0;
11088
11376
  if (count === Number.POSITIVE_INFINITY) {
@@ -11112,8 +11400,29 @@ var DurableInbox = class _DurableInbox {
11112
11400
  };
11113
11401
 
11114
11402
  // packages/agent/src/live.ts
11403
+ function createAgentRuntimeContext(agentCtx) {
11404
+ const runtimeCtx = agentCtx.extend();
11405
+ Object.defineProperty(runtimeCtx, Context.filter, {
11406
+ value: (target) => {
11407
+ const targetScope = target[symbols.isolate].agentScope;
11408
+ let ancestor = agentCtx;
11409
+ while (true) {
11410
+ if (targetScope === ancestor[symbols.isolate].agentScope) return true;
11411
+ if (ancestor.fiber.parent.fiber === ancestor.fiber) return false;
11412
+ ancestor = ancestor.fiber.parent;
11413
+ }
11414
+ }
11415
+ });
11416
+ const events = agentCtx.events;
11417
+ for (const method of ["emit", "parallel", "serial", "bail", "waterfall", "waterfallAsync"]) {
11418
+ Object.defineProperty(runtimeCtx, method, {
11419
+ value: (...args) => events[method](runtimeCtx, ...args)
11420
+ });
11421
+ }
11422
+ return runtimeCtx;
11423
+ }
11115
11424
  var LiveAgentImpl = class {
11116
- constructor(_ctx, _agentCtx, _service, id, inbox, session2, durable, meta2, manualStreaming = false) {
11425
+ constructor(_ctx, _agentCtx, _service, id, inbox, session2, durable, meta2, manualStreaming = false, wakeReserved = false) {
11117
11426
  this._ctx = _ctx;
11118
11427
  this._agentCtx = _agentCtx;
11119
11428
  this._service = _service;
@@ -11123,6 +11432,7 @@ var LiveAgentImpl = class {
11123
11432
  this.meta = meta2;
11124
11433
  this._durable = durable;
11125
11434
  this._manualStreaming = manualStreaming;
11435
+ this._wakeReserved = wakeReserved;
11126
11436
  }
11127
11437
  _ctx;
11128
11438
  _agentCtx;
@@ -11144,8 +11454,9 @@ var LiveAgentImpl = class {
11144
11454
  _setupDisposers = [];
11145
11455
  _scopeClosed = false;
11146
11456
  _durable;
11147
- _agentSystem;
11148
11457
  _manualStreaming = false;
11458
+ _wakeReserved = false;
11459
+ _loop;
11149
11460
  get agentType() {
11150
11461
  return this.meta.agentType;
11151
11462
  }
@@ -11172,13 +11483,41 @@ var LiveAgentImpl = class {
11172
11483
  return this._ctx;
11173
11484
  }
11174
11485
  followup(input) {
11175
- this._send(input, "followup");
11486
+ return this._send(input, "followup");
11487
+ }
11488
+ setLoop(loop) {
11489
+ this._loop = loop;
11490
+ }
11491
+ async _closeCustomTurn(turn, run) {
11492
+ const events = await this.session.read();
11493
+ if (events.some((event) => event.type === "turn/end" && event.payload.turn === turn)) return;
11494
+ 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" };
11495
+ await this.session.append("turn/end", {
11496
+ turn,
11497
+ finishReason: run.finishReason,
11498
+ reason,
11499
+ ...run.output ? { output: run.output } : {},
11500
+ ...run.steps.length ? { steps: run.steps.length } : {}
11501
+ });
11502
+ }
11503
+ async _closeFailedCustomTurn(turn, error2) {
11504
+ const events = await this.session.read();
11505
+ if (events.some((event) => event.type === "turn/end" && event.payload.turn === turn)) return;
11506
+ const failure = error2 instanceof Error ? { name: error2.name, message: error2.message, ...error2.stack ? { stack: error2.stack } : {} } : { name: "Error", message: String(error2) };
11507
+ await this.session.append("turn/end", {
11508
+ turn,
11509
+ finishReason: "error",
11510
+ reason: { kind: "error", error: failure },
11511
+ interrupted: true,
11512
+ error: failure
11513
+ });
11176
11514
  }
11177
11515
  /** Replace one pending inbox message by durable message id. */
11178
11516
  replaceMessage(messageId, input) {
11179
- this._mutatePending(async () => {
11517
+ return this._mutatePending(async () => {
11180
11518
  const previous = this._durable.get(messageId);
11181
- if (!previous) return;
11519
+ const target = this._findTarget(messageId);
11520
+ if (!previous || !target) return;
11182
11521
  const replacement = await this._durable.replace(messageId, {
11183
11522
  text: input.text ?? "",
11184
11523
  ...input.messages !== void 0 || input.context !== void 0 ? { content: input.messages ?? input.context } : {}
@@ -11189,18 +11528,19 @@ var LiveAgentImpl = class {
11189
11528
  agent: this,
11190
11529
  message: previous
11191
11530
  });
11192
- this._ctx.emit("agent/inbox/inserted", {
11531
+ const event = {
11193
11532
  id: this.id,
11194
11533
  agent: this,
11195
- target: this._findTarget(messageId) ?? "next-turn",
11534
+ target,
11196
11535
  input: replacement,
11197
11536
  inboxSeq: this._durable.size
11198
- });
11537
+ };
11538
+ this._ctx.emit("agent/inbox/inserted", event);
11199
11539
  });
11200
11540
  }
11201
11541
  /** Remove one pending inbox message by durable message id. */
11202
11542
  removeMessage(messageId) {
11203
- this._mutatePending(async () => {
11543
+ return this._mutatePending(async () => {
11204
11544
  const removed = await this._durable.remove(messageId);
11205
11545
  if (!removed) return;
11206
11546
  this._ctx.emit("agent/inbox/discarded", {
@@ -11211,48 +11551,54 @@ var LiveAgentImpl = class {
11211
11551
  });
11212
11552
  }
11213
11553
  steer(input) {
11214
- this._send(input, "steer");
11554
+ return this._send(input, "steer");
11215
11555
  }
11216
- inject(key, value) {
11217
- if (key === "agentSystem" && typeof value === "string") {
11218
- this._agentSystem = value;
11219
- }
11556
+ inject(input) {
11557
+ return this._send(input, "inject");
11220
11558
  }
11221
11559
  send(input, options = {}) {
11222
- this._send(input, options.mode === "steer" ? "steer" : "followup");
11560
+ return this._send(input, options.mode === "steer" ? "steer" : "followup");
11223
11561
  }
11224
11562
  _send(input, target) {
11225
- if (this._disposed) throw new Error(`agent disposed: ${this.id}`);
11226
11563
  const text = input.text ?? "";
11227
11564
  const content = input.messages ?? input.context;
11228
- const task = this._writeTail.then(async () => {
11229
- const message3 = target === "steer" ? await this._durable.steer({ text, ...content !== void 0 ? { content } : {} }) : await this._durable.insert({ text, ...content !== void 0 ? { content } : {} });
11230
- this._ctx.emit("agent/inbox/inserted", {
11565
+ const steeredAfterAbort = target === "steer" && this._controller?.signal.aborted === true;
11566
+ return this._mutatePending(async () => {
11567
+ 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");
11568
+ if (target !== "inject") this._wakeReserved = true;
11569
+ const event = {
11231
11570
  id: this.id,
11571
+ agent: this,
11232
11572
  target,
11233
11573
  input: message3,
11234
11574
  inboxSeq: this._durable.size
11235
- });
11236
- }).catch((error2) => {
11237
- this._ctx.emit("agent/error", { id: this.id, error: error2 });
11238
- });
11239
- this._writeTail = task;
11240
- this._pendingWrite = task;
11241
- task.finally(() => {
11242
- if (this._pendingWrite === task) this._pendingWrite = void 0;
11243
- queueMicrotask(() => this._wake());
11244
- });
11575
+ };
11576
+ this._ctx.emit("agent/inbox/inserted", event);
11577
+ }, target !== "inject");
11245
11578
  }
11246
- _mutatePending(task) {
11247
- if (this._disposed) throw new Error(`agent disposed: ${this.id}`);
11248
- const chained = this._writeTail.then(task).catch((error2) => {
11249
- this._ctx.emit("agent/error", { id: this.id, error: error2 });
11250
- });
11251
- this._writeTail = chained;
11252
- this._pendingWrite = chained;
11253
- chained.finally(() => {
11254
- if (this._pendingWrite === chained) this._pendingWrite = void 0;
11579
+ _mutatePending(task, wakes = false, allowDisposed = false) {
11580
+ if (this._disposed && !allowDisposed) {
11581
+ return Promise.reject(new Error(`agent disposed: ${this.id}`));
11582
+ }
11583
+ const result = this._writeTail.then(task);
11584
+ const recovered = result.catch((error2) => {
11585
+ try {
11586
+ this._ctx.emit("agent/error", { id: this.id, error: error2 });
11587
+ } catch {
11588
+ }
11255
11589
  });
11590
+ this._writeTail = recovered;
11591
+ this._pendingWrite = recovered;
11592
+ void result.then(
11593
+ () => {
11594
+ if (this._pendingWrite === recovered) this._pendingWrite = void 0;
11595
+ if (wakes) queueMicrotask(() => this._wake());
11596
+ },
11597
+ () => {
11598
+ if (this._pendingWrite === recovered) this._pendingWrite = void 0;
11599
+ }
11600
+ );
11601
+ return result;
11256
11602
  }
11257
11603
  _findTarget(messageId) {
11258
11604
  const snapshot = this._durable.snapshot();
@@ -11262,27 +11608,21 @@ var LiveAgentImpl = class {
11262
11608
  }
11263
11609
  cancel(cause, options = {}) {
11264
11610
  if (!options.keepInbox) {
11265
- const task = this._writeTail.then(async () => {
11611
+ void this._mutatePending(async () => {
11266
11612
  const before = this._durable.snapshot();
11613
+ await this._durable.clear();
11267
11614
  for (const message3 of [...before.nextStep, ...before.nextTurn]) {
11268
11615
  this._ctx.emit("agent/inbox/discarded", { id: this.id, message: message3 });
11269
11616
  }
11270
- await this._durable.clear();
11271
- }).catch((error2) => {
11272
- this._ctx.emit("agent/error", { id: this.id, error: error2 });
11273
- });
11274
- this._writeTail = task;
11275
- this._pendingWrite = task;
11276
- task.finally(() => {
11277
- if (this._pendingWrite === task) this._pendingWrite = void 0;
11278
- });
11617
+ this._wakeReserved = false;
11618
+ }, false, true);
11279
11619
  }
11280
11620
  this._controller?.abort(cause);
11281
11621
  this._maintenance?.controller.abort(cause);
11282
11622
  }
11283
11623
  async whenIdle() {
11284
- while (this._pendingWrite) await this._pendingWrite;
11285
11624
  while (true) {
11625
+ while (this._pendingWrite) await this._pendingWrite;
11286
11626
  const maintenance = this._maintenance;
11287
11627
  if (maintenance) {
11288
11628
  await maintenance.promise.catch(() => void 0);
@@ -11293,7 +11633,7 @@ var LiveAgentImpl = class {
11293
11633
  await drain.catch(() => void 0);
11294
11634
  continue;
11295
11635
  }
11296
- if (!this._durable.size) break;
11636
+ if (!this._hasQueuedWork()) break;
11297
11637
  await new Promise((resolve12) => setTimeout(resolve12, 0));
11298
11638
  }
11299
11639
  }
@@ -11335,6 +11675,7 @@ var LiveAgentImpl = class {
11335
11675
  this._pendingWrite = void 0;
11336
11676
  await writeTail.catch(() => void 0);
11337
11677
  }
11678
+ await this.session.close();
11338
11679
  this._setStatus("idle");
11339
11680
  }
11340
11681
  /** Publish the agent lifecycle session-start notification once. */
@@ -11364,13 +11705,23 @@ var LiveAgentImpl = class {
11364
11705
  if (this._disposed || this._manualStreaming || this._drainPromise || this._busy || this._maintenanceBusy) return;
11365
11706
  const task = this._drainAll();
11366
11707
  this._drainPromise = task;
11367
- task.finally(() => {
11368
- if (this._drainPromise === task) this._drainPromise = void 0;
11369
- });
11708
+ void task.then(
11709
+ () => {
11710
+ if (this._drainPromise === task) this._drainPromise = void 0;
11711
+ if (this._hasQueuedWork()) queueMicrotask(() => this._wake());
11712
+ },
11713
+ (error2) => {
11714
+ if (this._drainPromise === task) this._drainPromise = void 0;
11715
+ try {
11716
+ this._ctx.emit("agent/error", { id: this.id, agent: this, error: error2 });
11717
+ } catch {
11718
+ }
11719
+ }
11720
+ );
11370
11721
  }
11371
11722
  async _drainAll() {
11372
11723
  await this._writeTail.catch(() => void 0);
11373
- if (this._durable.size) {
11724
+ if (this._hasQueuedWork()) {
11374
11725
  for await (const _ of this._streamTurns()) void _;
11375
11726
  }
11376
11727
  }
@@ -11386,10 +11737,13 @@ var LiveAgentImpl = class {
11386
11737
  this._setStatus("running");
11387
11738
  try {
11388
11739
  while (!this._disposed) {
11389
- const batch = await this._durable.claimBatch();
11390
- if (!batch.length) break;
11391
- const input = await this._inputForBatch(batch);
11740
+ const snapshot = this._durable.snapshot();
11741
+ if (!snapshot.nextTurn.length && !this._wakeReserved) break;
11392
11742
  const turn = await this._nextTurnNumber();
11743
+ await this.session.append("turn/start", { turn, reason: "user" });
11744
+ const batch = await this._durable.claimBatch();
11745
+ this._wakeReserved = false;
11746
+ const input = batch.length ? await this._inputForBatch(batch, true) : { text: "" };
11393
11747
  for (const message3 of batch) {
11394
11748
  this._ctx.emit("agent/inbox/claimed", {
11395
11749
  id: this.id,
@@ -11406,11 +11760,21 @@ var LiveAgentImpl = class {
11406
11760
  else signal?.addEventListener("abort", forward, { once: true });
11407
11761
  try {
11408
11762
  if (this._manualStreaming) {
11409
- yield* this._service.runStream(input, { signal: controller.signal });
11763
+ if (this._loop) {
11764
+ const run = await this._loop(input, { signal: controller.signal, turn });
11765
+ await this._closeCustomTurn(turn, run);
11766
+ yield { type: "run/end", run };
11767
+ } else {
11768
+ yield* this._service.runStream(input, { signal: controller.signal, turn });
11769
+ }
11410
11770
  } else {
11411
- await this._service.run(input, { signal: controller.signal });
11771
+ if (this._loop) {
11772
+ const run = await this._loop(input, { signal: controller.signal, turn });
11773
+ await this._closeCustomTurn(turn, run);
11774
+ } else await this._service.run(input, { signal: controller.signal, turn });
11412
11775
  }
11413
11776
  } catch (error2) {
11777
+ if (this._loop) await this._closeFailedCustomTurn(turn, error2);
11414
11778
  if (!controller.signal.aborted) {
11415
11779
  const step = await this._failedStep();
11416
11780
  this._ctx.emit("agent/error", {
@@ -11428,26 +11792,37 @@ var LiveAgentImpl = class {
11428
11792
  }
11429
11793
  } finally {
11430
11794
  this._busy = false;
11795
+ if (!this._durable.size) this._wakeReserved = false;
11431
11796
  this._setStatus("idle");
11432
11797
  }
11433
11798
  }
11434
- async _inputForBatch(batch) {
11435
- const history = await this.session.deriveMessages();
11436
- const structured = batch.filter((message3) => message3.content !== void 0);
11437
- const plain = batch.filter((message3) => message3.content === void 0);
11438
- const plainMessages = plain.filter((message3) => message3.text !== void 0).map((message3) => ({ role: "user", content: message3.text ?? "" }));
11439
- if (structured.length) {
11440
- return {
11441
- messages: [
11442
- ...history,
11443
- ...structured.flatMap((message3) => Array.isArray(message3.content) ? message3.content : [{ role: "user", content: String(message3.content ?? "") }])
11444
- ]
11445
- };
11446
- }
11447
- if (history.length || batch.length > 1) {
11448
- return { messages: [...history, ...plainMessages] };
11799
+ _hasQueuedWork() {
11800
+ const snapshot = this._durable.snapshot();
11801
+ return snapshot.nextTurn.length > 0 || this._wakeReserved && snapshot.nextStep.length > 0;
11802
+ }
11803
+ async _inputForBatch(batch, includeHistory) {
11804
+ const history = includeHistory ? await this.session.deriveMessages() : [];
11805
+ 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 }] : []);
11806
+ if (history.length || messages.length > 1) return { messages: [...history, ...messages] };
11807
+ return { text: messages[0]?.content ?? "" };
11808
+ }
11809
+ async claimNextStepInputs() {
11810
+ await this._writeTail;
11811
+ const batch = await this._durable.claimNextStep();
11812
+ this._wakeReserved = false;
11813
+ if (batch.length) {
11814
+ const turn = await this._nextTurnNumber() - 1;
11815
+ for (const message3 of batch) {
11816
+ this._ctx.emit("agent/inbox/claimed", {
11817
+ id: this.id,
11818
+ agent: this,
11819
+ message: message3,
11820
+ input: { text: message3.text ?? "" },
11821
+ turn
11822
+ });
11823
+ }
11449
11824
  }
11450
- return { text: plainMessages[0]?.content ?? "" };
11825
+ return batch.length ? [await this._inputForBatch(batch, false)] : [];
11451
11826
  }
11452
11827
  _setStatus(status) {
11453
11828
  const previous = this._status;
@@ -11543,112 +11918,156 @@ async function buildHandle(ctx, options, resume = false) {
11543
11918
  }
11544
11919
  const agentId = fileMeta?.agentId ?? requestedId;
11545
11920
  if (!agentId) throw new AgentError("agent requires a stable identity");
11546
- const log = new SessionLog(
11547
- options.file,
11548
- (type, payload) => {
11549
- if (type === "event") ctx.emit("session/event", payload);
11550
- else ctx.emit("session/flush", payload);
11551
- }
11552
- );
11553
- await log.init();
11554
- const fileEvents = await log.read();
11555
- const durableMeta = readDurableAgentMeta(fileEvents);
11556
- if (durableMeta) {
11557
- if (durableMeta.agentId !== agentId) {
11558
- throw new AgentError(
11559
- `agent id mismatch: session belongs to ${durableMeta.agentId}, requested ${agentId}`
11560
- );
11561
- }
11562
- if (options.agentType && durableMeta.agentType !== options.agentType) {
11563
- throw new AgentError(
11564
- `agent type mismatch: session is ${durableMeta.agentType ?? "untyped"}, requested ${options.agentType}`
11565
- );
11566
- }
11567
- if (options.mode && durableMeta.mode !== options.mode) {
11568
- throw new AgentError(
11569
- `agent mode mismatch: session is ${durableMeta.mode ?? "unset"}, requested ${options.mode}`
11570
- );
11571
- }
11572
- }
11573
- const boundMeta = {
11574
- ...fileMeta,
11575
- ...durableMeta ?? {},
11576
- ...options.title ? { title: options.title } : {},
11577
- ...options.owner ? { owner: options.owner } : {},
11578
- ...options.parentSessionId ? { parentSessionId: options.parentSessionId } : {},
11579
- ...options.forkedAtMessageId ? { forkedAtMessageId: options.forkedAtMessageId } : {},
11580
- agentId
11581
- };
11582
- const resolvedAgentType = options.agentType ?? durableMeta?.agentType;
11583
- if (resolvedAgentType) boundMeta.agentType = resolvedAgentType;
11584
- const resolvedMode = options.mode ?? durableMeta?.mode;
11585
- if (resolvedMode) boundMeta.mode = resolvedMode;
11586
- if (!durableMeta) {
11587
- await log.append("meta", {
11588
- kind: "agent",
11589
- agentId,
11590
- ...boundMeta.agentType ? { agentType: boundMeta.agentType } : {},
11591
- ...boundMeta.mode ? { mode: boundMeta.mode } : {},
11592
- ...boundMeta.title ? { title: boundMeta.title } : {},
11593
- ...boundMeta.owner ? { owner: boundMeta.owner } : {},
11594
- ...boundMeta.parentSessionId ? { parentSessionId: boundMeta.parentSessionId } : {},
11595
- ...boundMeta.forkedAtMessageId ? { forkedAtMessageId: boundMeta.forkedAtMessageId } : {},
11596
- ...boundMeta.createdAt !== void 0 ? { createdAt: boundMeta.createdAt } : {}
11597
- });
11598
- }
11599
- const durable = resume ? await DurableInbox.restore(log) : new DurableInbox(log);
11600
- const injected = new AgentInbox();
11601
- const inboxView = {
11602
- get size() {
11603
- return durable.size;
11604
- },
11605
- snapshot: () => durable.snapshot()
11606
- };
11607
- const service = new AgentService(ctx, {
11608
- session: log,
11609
- inbox: injected,
11610
- ...options.llm ? { llm: options.llm } : {},
11611
- ...options.maxTurns !== void 0 ? { maxTurns: options.maxTurns } : {},
11612
- ...options.maxSteps !== void 0 ? { maxSteps: options.maxSteps } : {},
11613
- ...options.contextBudget ? { contextBudget: options.contextBudget } : {},
11614
- ...options.hooks ? { hooks: options.hooks } : {}
11615
- });
11616
- if (options.system) injected.inject("agentSystem", options.system);
11921
+ let agentCtx;
11617
11922
  const agentScope = await ctx.inject([], (scopeCtx) => {
11618
- scopeCtx.isolate("agentScope").provide("agentScope", scopeCtx);
11923
+ agentCtx = scopeCtx.isolate("agentScope").isolate("llm").isolate("tools").isolate("systemPrompt");
11924
+ agentCtx.provide("agentScope", agentCtx);
11619
11925
  });
11620
11926
  await agentScope;
11621
- const agentCtx = agentScope.ctx;
11622
- const agent2 = new LiveAgentImpl(
11623
- ctx,
11624
- agentCtx,
11625
- service,
11626
- agentId,
11627
- inboxView,
11628
- log,
11629
- durable,
11630
- boundMeta,
11631
- options.manualStreaming === true
11632
- );
11633
- agent2.trackSetupDispose(() => agentScope.dispose());
11634
- if (options.system) agent2.inject("agentSystem", options.system);
11635
- let setupResult;
11927
+ const runtimeCtx = createAgentRuntimeContext(agentCtx);
11928
+ const log = new SessionLog(options.file, void 0, runtimeCtx);
11636
11929
  try {
11637
- setupResult = await options.setup?.(agentCtx);
11638
- setupResult?.commit();
11930
+ await log.init();
11931
+ const fileEvents = await log.read();
11932
+ const durableMeta = readDurableAgentMeta(fileEvents);
11933
+ if (durableMeta) {
11934
+ if (durableMeta.agentId !== agentId) {
11935
+ throw new AgentError(
11936
+ `agent id mismatch: session belongs to ${durableMeta.agentId}, requested ${agentId}`
11937
+ );
11938
+ }
11939
+ if (options.agentType && durableMeta.agentType !== options.agentType) {
11940
+ throw new AgentError(
11941
+ `agent type mismatch: session is ${durableMeta.agentType ?? "untyped"}, requested ${options.agentType}`
11942
+ );
11943
+ }
11944
+ if (options.mode && durableMeta.mode !== options.mode) {
11945
+ throw new AgentError(
11946
+ `agent mode mismatch: session is ${durableMeta.mode ?? "unset"}, requested ${options.mode}`
11947
+ );
11948
+ }
11949
+ }
11950
+ const boundMeta = {
11951
+ ...fileMeta,
11952
+ ...durableMeta ?? {},
11953
+ ...options.title ? { title: options.title } : {},
11954
+ ...options.owner ? { owner: options.owner } : {},
11955
+ ...options.parentSessionId ? { parentSessionId: options.parentSessionId } : {},
11956
+ ...options.forkedAtMessageId ? { forkedAtMessageId: options.forkedAtMessageId } : {},
11957
+ agentId
11958
+ };
11959
+ const resolvedAgentType = options.agentType ?? durableMeta?.agentType;
11960
+ if (resolvedAgentType) boundMeta.agentType = resolvedAgentType;
11961
+ const resolvedMode = options.mode ?? durableMeta?.mode;
11962
+ if (resolvedMode) boundMeta.mode = resolvedMode;
11963
+ if (!durableMeta) {
11964
+ await log.append("meta", {
11965
+ kind: "agent",
11966
+ agentId,
11967
+ ...boundMeta.agentType ? { agentType: boundMeta.agentType } : {},
11968
+ ...boundMeta.mode ? { mode: boundMeta.mode } : {},
11969
+ ...boundMeta.title ? { title: boundMeta.title } : {},
11970
+ ...boundMeta.owner ? { owner: boundMeta.owner } : {},
11971
+ ...boundMeta.parentSessionId ? { parentSessionId: boundMeta.parentSessionId } : {},
11972
+ ...boundMeta.forkedAtMessageId ? { forkedAtMessageId: boundMeta.forkedAtMessageId } : {},
11973
+ ...boundMeta.createdAt !== void 0 ? { createdAt: boundMeta.createdAt } : {}
11974
+ });
11975
+ }
11976
+ const durable = resume ? await DurableInbox.restore(log) : new DurableInbox(log);
11977
+ const restoredWakeReservation = resume && hasRestoredWakeReservation(fileEvents);
11978
+ const injected = new AgentInbox();
11979
+ const inboxView = {
11980
+ get size() {
11981
+ return durable.size;
11982
+ },
11983
+ snapshot: () => durable.snapshot()
11984
+ };
11985
+ let claimNextStep = async () => [];
11986
+ const service = new AgentService(runtimeCtx, {
11987
+ session: log,
11988
+ inbox: injected,
11989
+ ...options.llm ? { llm: options.llm } : {},
11990
+ ...options.maxTurns !== void 0 ? { maxTurns: options.maxTurns } : {},
11991
+ ...options.maxSteps !== void 0 ? { maxSteps: options.maxSteps } : {},
11992
+ ...options.contextBudget ? { contextBudget: options.contextBudget } : {},
11993
+ ...options.hooks ? { hooks: options.hooks } : {},
11994
+ claimNextStep: () => claimNextStep()
11995
+ });
11996
+ if (options.system) injected.inject("agentSystem", options.system);
11997
+ const agent2 = new LiveAgentImpl(
11998
+ runtimeCtx,
11999
+ agentCtx,
12000
+ service,
12001
+ agentId,
12002
+ inboxView,
12003
+ log,
12004
+ durable,
12005
+ boundMeta,
12006
+ options.manualStreaming === true,
12007
+ restoredWakeReservation
12008
+ );
12009
+ claimNextStep = () => agent2.claimNextStepInputs();
12010
+ agent2.trackSetupDispose(() => agentScope.dispose());
12011
+ let setupResult;
12012
+ try {
12013
+ setupResult = await options.setup?.(agentCtx);
12014
+ for (const name2 of ["llm", "systemPrompt"]) {
12015
+ if (agentCtx.reflect.get(name2, false) !== void 0) continue;
12016
+ const inherited = ctx.reflect.get(name2, false);
12017
+ if (inherited !== void 0) agentCtx.provide(name2, inherited);
12018
+ }
12019
+ const loop = agentCtx.reflect.get("agentLoop", false);
12020
+ agent2.setLoop(loop);
12021
+ setupResult?.commit();
12022
+ } catch (error2) {
12023
+ await agent2.dispose().catch(() => void 0);
12024
+ await log.close().catch(() => void 0);
12025
+ throw error2;
12026
+ }
12027
+ await agent2.publishSessionStart(resume ? "resume" : "startup");
12028
+ for (const input of options.initial ?? []) {
12029
+ if (!resume) await durable.insert({ text: input.text ?? "" });
12030
+ }
12031
+ if (resume && (durable.snapshot().nextTurn.length || restoredWakeReservation) && options.manualStreaming !== true) {
12032
+ setTimeout(() => agent2.wakeNow(), 0);
12033
+ }
12034
+ return { agent: agent2, dispose: () => agent2.dispose() };
11639
12035
  } catch (error2) {
11640
- await agent2.dispose().catch(() => void 0);
11641
- await log.close().catch(() => void 0);
12036
+ await Promise.resolve().then(() => agentScope.dispose()).catch(() => void 0);
11642
12037
  throw error2;
11643
12038
  }
11644
- await agent2.publishSessionStart(resume ? "resume" : "startup");
11645
- for (const input of options.initial ?? []) {
11646
- if (!resume) await durable.insert({ text: input.text ?? "" });
11647
- }
11648
- if (resume && durable.size && options.manualStreaming !== true) {
11649
- setTimeout(() => agent2.wakeNow(), 0);
12039
+ }
12040
+ function hasRestoredWakeReservation(events) {
12041
+ const nextTurn = [];
12042
+ const nextStep = [];
12043
+ for (const event of events) {
12044
+ if (event.type !== "agent/inbox/spliced") continue;
12045
+ const payload = event.payload;
12046
+ if (payload.target === "all") {
12047
+ nextTurn.splice(0, payload.deleteCounts?.nextTurn ?? nextTurn.length);
12048
+ nextStep.splice(0, payload.deleteCounts?.nextStep ?? nextStep.length);
12049
+ continue;
12050
+ }
12051
+ const list = payload.target === "next-step" ? nextStep : nextTurn;
12052
+ const count = payload.deleteCount ?? 0;
12053
+ if (count === Number.POSITIVE_INFINITY) {
12054
+ list.length = 0;
12055
+ } else if (count > 0 && payload.index !== void 0) {
12056
+ list.splice(payload.index, count);
12057
+ }
12058
+ let insertedOffset = 0;
12059
+ for (const inserted of payload.inserted ?? []) {
12060
+ const entry = { wakes: payload.target === "next-step" && inserted.mode === "steer" };
12061
+ if (inserted.mode === "steer") {
12062
+ list.unshift(entry);
12063
+ } else {
12064
+ const index = payload.index === void 0 ? list.length : Math.min(list.length, payload.index + insertedOffset);
12065
+ list.splice(index, 0, entry);
12066
+ insertedOffset += 1;
12067
+ }
12068
+ }
11650
12069
  }
11651
- return { agent: agent2, dispose: () => agent2.dispose() };
12070
+ return nextStep.some((entry) => entry.wakes);
11652
12071
  }
11653
12072
  async function readAgentMeta(file) {
11654
12073
  const lines = await import("node:fs/promises").then((fs2) => fs2.readFile(file, "utf8")).catch((error2) => {
@@ -12098,7 +12517,9 @@ function anthropicMessagesAdapter(config2 = {}) {
12098
12517
  }
12099
12518
  let sawEvent = false;
12100
12519
  try {
12101
- for await (const event of parseAnthropicStream(response.body)) {
12520
+ const contentType2 = response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase();
12521
+ const events = contentType2 === "application/json" ? parseJSONCompletionStream(response) : parseAnthropicStream(response.body);
12522
+ for await (const event of events) {
12102
12523
  sawEvent = true;
12103
12524
  yield event;
12104
12525
  }
@@ -12139,7 +12560,7 @@ function buildRequest(messages, tools2, config2, signal, stream = false) {
12139
12560
  headers: {
12140
12561
  "content-type": "application/json",
12141
12562
  "anthropic-version": ANTHROPIC_VERSION,
12142
- ...config2.apiKey ? { "x-api-key": config2.apiKey } : {}
12563
+ ...config2.apiKey ? { [config2.apiKeyHeader ?? "x-api-key"]: config2.apiKey } : {}
12143
12564
  },
12144
12565
  body: JSON.stringify(body),
12145
12566
  ...signal ? { signal } : {}
@@ -12431,6 +12852,26 @@ async function* parseAnthropicStream(body) {
12431
12852
  function toIndex(block) {
12432
12853
  return typeof block.index === "number" ? block.index : 0;
12433
12854
  }
12855
+ async function* parseJSONCompletionStream(response) {
12856
+ const payload = await parseJson(response);
12857
+ const completion = parseCompletion(payload);
12858
+ const record3 = payload;
12859
+ const id = typeof record3?.id === "string" && record3.id ? record3.id : randomUUID5();
12860
+ const model = typeof record3?.model === "string" && record3.model ? record3.model : void 0;
12861
+ yield { type: "message_start", id, ...model ? { model } : {} };
12862
+ if (completion.content) yield { type: "message_delta", id, delta: completion.content };
12863
+ for (const [index, call] of (completion.toolCalls ?? []).entries()) {
12864
+ yield { type: "toolcall_start", id: call.id, index, name: call.name };
12865
+ yield {
12866
+ type: "toolcall_end",
12867
+ id: call.id,
12868
+ index,
12869
+ name: call.name,
12870
+ arguments: call.arguments
12871
+ };
12872
+ }
12873
+ yield { type: "message_stop", id, finishReason: completion.finishReason };
12874
+ }
12434
12875
  function toStreamError(error2, timeoutMs) {
12435
12876
  if (isAbortLike(error2)) {
12436
12877
  return new OpenAICompatibleError(
@@ -12519,7 +12960,9 @@ function openaiCompatAdapter(config2 = {}) {
12519
12960
  }
12520
12961
  let sawEvent = false;
12521
12962
  try {
12522
- for await (const event of parseOpenAIStream(response.body)) {
12963
+ const contentType2 = response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase();
12964
+ const events = contentType2 === "application/json" ? parseJSONCompletionStream2(response) : parseOpenAIStream(response.body);
12965
+ for await (const event of events) {
12523
12966
  sawEvent = true;
12524
12967
  yield event;
12525
12968
  }
@@ -12711,6 +13154,26 @@ async function* parseOpenAIStream(body) {
12711
13154
  }
12712
13155
  yield* finalizeStreamEvents(messageId, content, calls, finishReason);
12713
13156
  }
13157
+ async function* parseJSONCompletionStream2(response) {
13158
+ const payload = await parseJson(response);
13159
+ const completion = parseCompletion2(payload);
13160
+ const record3 = payload;
13161
+ const id = typeof record3?.id === "string" && record3.id ? record3.id : randomUUID6();
13162
+ const model = typeof record3?.model === "string" && record3.model ? record3.model : void 0;
13163
+ yield { type: "message_start", id, ...model ? { model } : {} };
13164
+ if (completion.content) yield { type: "message_delta", id, delta: completion.content };
13165
+ for (const [index, call] of (completion.toolCalls ?? []).entries()) {
13166
+ yield { type: "toolcall_start", id: call.id, index, name: call.name };
13167
+ yield {
13168
+ type: "toolcall_end",
13169
+ id: call.id,
13170
+ index,
13171
+ name: call.name,
13172
+ arguments: call.arguments
13173
+ };
13174
+ }
13175
+ yield { type: "message_stop", id, finishReason: completion.finishReason };
13176
+ }
12714
13177
  function processStreamChunk(payload, state, progress, calls) {
12715
13178
  const record3 = payload;
12716
13179
  const events = [];
@@ -30049,7 +30512,7 @@ async function updateSystemConfig(patch, file = systemConfigPath()) {
30049
30512
  }
30050
30513
  function effectiveLlmConfig(config2, env = process.env) {
30051
30514
  const envConfig = resolveLlmEnv(env);
30052
- const apiKey = envConfig.apiKey ?? config2.apiKey;
30515
+ const apiKey = effectiveApiKey(config2, env);
30053
30516
  const baseUrl = envConfig.baseUrl ?? config2.baseUrl ?? DEFAULT_OPENCODE_GO_BASE_URL;
30054
30517
  const model = envConfig.model ?? config2.model ?? DEFAULT_MODEL;
30055
30518
  const result = {
@@ -30058,15 +30521,18 @@ function effectiveLlmConfig(config2, env = process.env) {
30058
30521
  model
30059
30522
  };
30060
30523
  if (config2.protocol) result.protocol = config2.protocol;
30524
+ if (config2.apiKeyHeader) result.apiKeyHeader = config2.apiKeyHeader;
30061
30525
  if (config2.temperature !== void 0) result.temperature = config2.temperature;
30062
30526
  return result;
30063
30527
  }
30064
30528
  function effectiveApiKey(config2, env = process.env) {
30529
+ if (env.TNEGA_API_KEY) return env.TNEGA_API_KEY;
30530
+ if (config2.protocol && config2.apiKey) return config2.apiKey;
30065
30531
  return resolveLlmEnv(env).apiKey ?? config2.apiKey;
30066
30532
  }
30067
30533
  function resolveLlmEnv(env = process.env) {
30068
30534
  const config2 = {};
30069
- const apiKey = env.OPENCODE_GO_API_KEY || env.OPENAI_API_KEY || env.DEEPSEEK_API_KEY;
30535
+ const apiKey = env.TNEGA_API_KEY || env.OPENCODE_GO_API_KEY || env.OPENAI_API_KEY || env.DEEPSEEK_API_KEY;
30070
30536
  if (apiKey) config2.apiKey = apiKey;
30071
30537
  if (env.OPENCODE_GO_BASE_URL) config2.baseUrl = env.OPENCODE_GO_BASE_URL;
30072
30538
  if (env.OPENCODE_GO_MODEL) config2.model = env.OPENCODE_GO_MODEL;
@@ -30082,6 +30548,9 @@ function normalizeConfig2(value) {
30082
30548
  if (record3.protocol === "anthropic" || record3.protocol === "openai") {
30083
30549
  config2.protocol = record3.protocol;
30084
30550
  }
30551
+ if (record3.apiKeyHeader === "x-api-key" || record3.apiKeyHeader === "api-key") {
30552
+ config2.apiKeyHeader = record3.apiKeyHeader;
30553
+ }
30085
30554
  if (typeof record3.temperature === "number" && Number.isFinite(record3.temperature)) {
30086
30555
  config2.temperature = record3.temperature;
30087
30556
  }
@@ -31141,10 +31610,10 @@ async function runCommand3(options) {
31141
31610
  if (candidate.coding) {
31142
31611
  const systemConfig = await readSystemConfig();
31143
31612
  const envConfig = resolveLlmEnv(process.env);
31144
- const apiKey = envConfig.apiKey ?? systemConfig.apiKey;
31613
+ const apiKey = effectiveApiKey(systemConfig);
31145
31614
  if (!apiKey) {
31146
31615
  throw new CliError(
31147
- "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"
31616
+ "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"
31148
31617
  );
31149
31618
  }
31150
31619
  const model = options.model ?? envConfig.model ?? systemConfig.model ?? candidate.coding.model;
@@ -31154,6 +31623,7 @@ async function runCommand3(options) {
31154
31623
  ...model ? { model } : {},
31155
31624
  ...baseUrl ? { baseUrl } : {},
31156
31625
  ...systemConfig.protocol ? { protocol: systemConfig.protocol } : {},
31626
+ ...systemConfig.apiKeyHeader ? { apiKeyHeader: systemConfig.apiKeyHeader } : {},
31157
31627
  ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
31158
31628
  ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
31159
31629
  ...systemConfig.temperature !== void 0 && options.temperature === void 0 ? { temperature: systemConfig.temperature } : {},
@@ -31221,10 +31691,10 @@ async function runAgentCommand(options) {
31221
31691
  const profileOptions = profile?.options ?? {};
31222
31692
  const systemConfig = await readSystemConfig(configFile);
31223
31693
  const envConfig = resolveLlmEnv(process.env);
31224
- const apiKey = envConfig.apiKey ?? systemConfig.apiKey;
31694
+ const apiKey = effectiveApiKey(systemConfig);
31225
31695
  if (!apiKey) {
31226
31696
  throw new CliError(
31227
- "missing LLM API key; set OPENCODE_GO_API_KEY (or OPENAI_API_KEY / DEEPSEEK_API_KEY) or configure it in the tnega config file"
31697
+ "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"
31228
31698
  );
31229
31699
  }
31230
31700
  const model = options.model ?? profileOptions.model ?? envConfig.model ?? systemConfig.model;
@@ -31235,6 +31705,7 @@ async function runAgentCommand(options) {
31235
31705
  ...typeof model === "string" && model ? { model } : {},
31236
31706
  ...baseUrl ? { baseUrl } : {},
31237
31707
  ...protocolFrom(profileOptions) ?? systemConfig.protocol ? { protocol: protocolFrom(profileOptions) ?? systemConfig.protocol } : {},
31708
+ ...systemConfig.apiKeyHeader ? { apiKeyHeader: systemConfig.apiKeyHeader } : {},
31238
31709
  ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
31239
31710
  ...options.maxTokens === void 0 && typeof profileOptions.maxTokens === "number" ? { maxTokens: profileOptions.maxTokens } : {},
31240
31711
  ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
@@ -31365,10 +31836,10 @@ async function runEvolveCommand(options) {
31365
31836
  const configFile = options.configFile ?? systemConfigPath();
31366
31837
  const systemConfig = await readSystemConfig(configFile);
31367
31838
  const envConfig = resolveLlmEnv(process.env);
31368
- const apiKey = envConfig.apiKey ?? systemConfig.apiKey;
31839
+ const apiKey = effectiveApiKey(systemConfig);
31369
31840
  if (!apiKey) {
31370
31841
  throw new CliError(
31371
- "missing LLM API key; set OPENCODE_GO_API_KEY (or OPENAI_API_KEY / DEEPSEEK_API_KEY) or configure it in the tnega config file"
31842
+ "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"
31372
31843
  );
31373
31844
  }
31374
31845
  const model = options.model ?? envConfig.model ?? systemConfig.model;
@@ -31378,6 +31849,7 @@ async function runEvolveCommand(options) {
31378
31849
  ...model ? { model } : {},
31379
31850
  ...baseUrl ? { baseUrl } : {},
31380
31851
  ...systemConfig.protocol ? { protocol: systemConfig.protocol } : {},
31852
+ ...systemConfig.apiKeyHeader ? { apiKeyHeader: systemConfig.apiKeyHeader } : {},
31381
31853
  ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
31382
31854
  ...options.temperature !== void 0 ? { temperature: options.temperature } : {},
31383
31855
  ...systemConfig.temperature !== void 0 && options.temperature === void 0 ? { temperature: systemConfig.temperature } : {},
@@ -32744,6 +33216,7 @@ function adapterFromConfig(effective, apiKey) {
32744
33216
  model: effective.model
32745
33217
  };
32746
33218
  if (effective.protocol) options.protocol = effective.protocol;
33219
+ if (effective.apiKeyHeader) options.apiKeyHeader = effective.apiKeyHeader;
32747
33220
  if (effective.temperature !== void 0) options.temperature = effective.temperature;
32748
33221
  return createLlmAdapter(options);
32749
33222
  }
@@ -33380,6 +33853,7 @@ export {
33380
33853
  bootAgentRuntimeFromFile,
33381
33854
  compareCommand,
33382
33855
  createAgentRuntime,
33856
+ effectiveApiKey,
33383
33857
  ensureProfileDir,
33384
33858
  formatAgentRun,
33385
33859
  formatBenchmarkImport,