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