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/agent.js CHANGED
@@ -107,17 +107,88 @@ function checkMonotonicSeq(events) {
107
107
  }
108
108
  return failures;
109
109
  }
110
+ function isRecord(value) {
111
+ return typeof value === "object" && value !== null && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
112
+ }
113
+ function isNonNegativeInteger(value) {
114
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
115
+ }
116
+ function isJsonValue(value, ancestors = /* @__PURE__ */ new Set()) {
117
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
118
+ if (typeof value === "number") return Number.isFinite(value);
119
+ if (typeof value !== "object" || ancestors.has(value)) return false;
120
+ if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) return false;
121
+ ancestors.add(value);
122
+ const valid = (Array.isArray(value) ? Array.from(value) : Object.values(value)).every((member) => isJsonValue(member, ancestors));
123
+ ancestors.delete(value);
124
+ return valid;
125
+ }
126
+ function isStreamRecord(value) {
127
+ if (!isRecord(value) || !isNonNegativeInteger(value.time) || !isRecord(value.chunk)) return false;
128
+ const chunk = value.chunk;
129
+ if (chunk.type === "stream_error") {
130
+ 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");
131
+ }
132
+ if (typeof chunk.id !== "string") return false;
133
+ switch (chunk.type) {
134
+ case "message_start":
135
+ return chunk.model === void 0 || typeof chunk.model === "string";
136
+ case "message_delta":
137
+ return typeof chunk.delta === "string";
138
+ case "toolcall_start":
139
+ case "toolcall_end":
140
+ return isNonNegativeInteger(chunk.index) && typeof chunk.name === "string" && (chunk.type === "toolcall_start" || isJsonValue(chunk.arguments));
141
+ case "message_stop":
142
+ 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";
143
+ default:
144
+ return false;
145
+ }
146
+ }
147
+ function checkAssistantAttempts(events) {
148
+ const turns = /* @__PURE__ */ new Set();
149
+ const steps = /* @__PURE__ */ new Map();
150
+ const failures = [];
151
+ for (const event of events) {
152
+ if (event.type === "turn/start") turns.add(event.payload.turn);
153
+ if (event.type === "turn/end") {
154
+ turns.delete(event.payload.turn);
155
+ for (const [key, turn] of steps) {
156
+ if (turn === event.payload.turn) steps.delete(key);
157
+ }
158
+ }
159
+ if (event.type === "step/start" && turns.has(event.payload.turn)) {
160
+ steps.set(`${event.payload.turn}:${event.payload.step}`, event.payload.turn);
161
+ }
162
+ if (event.type === "step/end") steps.delete(`${event.payload.turn}:${event.payload.step}`);
163
+ if (event.type !== "assistant/attempt") continue;
164
+ const payload = event.payload;
165
+ if (!isRecord(payload) || !isNonNegativeInteger(payload.turn) || !isNonNegativeInteger(payload.step) || !turns.has(payload.turn) || !steps.has(`${payload.turn}:${payload.step}`)) {
166
+ failures.push({
167
+ name: "assistant/attempt-without-open-step",
168
+ detail: `assistant/attempt at seq ${event.seq} has no matching open turn and step`
169
+ });
170
+ }
171
+ if (!isRecord(payload) || !Array.isArray(payload.stream) || !payload.stream.every(isStreamRecord)) {
172
+ failures.push({
173
+ name: "assistant/attempt-invalid-stream",
174
+ detail: `assistant/attempt at seq ${event.seq} must contain an ordered array of normalized stream records`
175
+ });
176
+ }
177
+ }
178
+ return failures;
179
+ }
110
180
  function checkSessionInvariants(events) {
111
181
  return [
112
182
  ...checkBalancedTurns(events),
113
183
  ...checkBalancedSteps(events),
114
184
  ...checkBalancedToolCalls(events),
115
- ...checkMonotonicSeq(events)
185
+ ...checkMonotonicSeq(events),
186
+ ...checkAssistantAttempts(events)
116
187
  ];
117
188
  }
118
189
 
119
190
  // packages/session/src/index.ts
120
- var SESSION_FORMAT_VERSION = 7;
191
+ var SESSION_FORMAT_VERSION = 10;
121
192
  var SessionFormatError = class extends Error {
122
193
  name = "SessionFormatError";
123
194
  };
@@ -332,6 +403,7 @@ function estimateEventTokens(event) {
332
403
  return Math.ceil(raw.length / 4);
333
404
  }
334
405
  case "plan":
406
+ case "assistant/attempt":
335
407
  return 0;
336
408
  case "checkpoint":
337
409
  return estimateMessageTokens(event.payload.messages);
@@ -466,9 +538,15 @@ function estimateContextUsage(messages, limit = DEFAULT_CONTEXT_LIMIT) {
466
538
  };
467
539
  }
468
540
  var SessionLog = class {
469
- constructor(file, _broadcast) {
541
+ constructor(file, _broadcast, publicationContext) {
470
542
  this.file = file;
471
543
  this._broadcast = _broadcast;
544
+ if (!this._broadcast && publicationContext) {
545
+ this._broadcast = (type, payload) => {
546
+ if (type === "event") publicationContext.emit("session/event", payload);
547
+ else publicationContext.emit("session/flush", payload);
548
+ };
549
+ }
472
550
  }
473
551
  file;
474
552
  _broadcast;
@@ -637,7 +715,7 @@ var SessionLog = class {
637
715
  selected.push(clone(event));
638
716
  continue;
639
717
  }
640
- if (event.type === "meta" || event.type === "llm/retry" || event.type === "llm/retry-started" || event.type.startsWith("turn/") || event.type.startsWith("step/")) {
718
+ 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/")) {
641
719
  continue;
642
720
  }
643
721
  if (event.type === "checkpoint") {
@@ -975,7 +1053,7 @@ function copyMessages(messages) {
975
1053
  copy.tool_calls = message.tool_calls.map((call) => ({
976
1054
  id: call.id,
977
1055
  name: call.name,
978
- arguments: call.arguments
1056
+ arguments: structuredClone(call.arguments)
979
1057
  }));
980
1058
  }
981
1059
  return copy;
@@ -1006,6 +1084,75 @@ function copyCompletion(completion) {
1006
1084
  }
1007
1085
  return copy;
1008
1086
  }
1087
+ function canonicalMessages(messages) {
1088
+ return JSON.stringify(messages.map((message) => ({
1089
+ role: message.role,
1090
+ content: message.content,
1091
+ ...message.name ? { name: message.name } : {},
1092
+ ...message.tool_call_id ? { tool_call_id: message.tool_call_id } : {},
1093
+ ...message.tool_calls?.length ? { tool_calls: message.tool_calls } : {},
1094
+ ...message.toolOk !== void 0 ? { toolOk: message.toolOk } : {},
1095
+ ...message.toolError ? {
1096
+ toolError: { name: message.toolError.name, message: message.toolError.message }
1097
+ } : {}
1098
+ })));
1099
+ }
1100
+ function freezeRequestData(data) {
1101
+ const visited = /* @__PURE__ */ new Set();
1102
+ const freeze = (value) => {
1103
+ if (typeof value !== "object" || value === null || visited.has(value)) return;
1104
+ visited.add(value);
1105
+ for (const child of Object.values(value)) freeze(child);
1106
+ Object.freeze(value);
1107
+ };
1108
+ freeze(data);
1109
+ }
1110
+ function lockStreamRequest(request) {
1111
+ freezeRequestData(request.messages);
1112
+ request.tools = Object.freeze(request.tools.map((tool) => {
1113
+ const schema = structuredClone(tool.schema);
1114
+ freezeRequestData(schema);
1115
+ return Object.freeze({ ...tool, schema });
1116
+ }));
1117
+ Object.freeze(request.options);
1118
+ Object.freeze(request);
1119
+ }
1120
+ function requestHeaderFor(request, input) {
1121
+ const system = input.find((message) => message.role === "system")?.content;
1122
+ const tools = request.tools.map((tool) => ({
1123
+ name: tool.schema.name,
1124
+ description: tool.schema.description,
1125
+ ...tool.schema.parameters ? { parameters: tool.schema.parameters } : {}
1126
+ }));
1127
+ const config = {
1128
+ ...request.options.provider ? { provider: request.options.provider } : {},
1129
+ ...request.options.model ? { model: request.options.model } : {},
1130
+ ...request.options.temperature !== void 0 ? { temperature: request.options.temperature } : {}
1131
+ };
1132
+ return {
1133
+ ...Object.keys(config).length ? { config } : {},
1134
+ ...system !== void 0 ? { system } : {},
1135
+ ...tools.length ? { tools } : {}
1136
+ };
1137
+ }
1138
+ async function* completeAsStream(llm, messages, tools, options) {
1139
+ const id = randomUUID2();
1140
+ yield { type: "message_start", id };
1141
+ const completion = await llm.complete(messages, tools, options);
1142
+ if (completion.content) yield { type: "message_delta", id, delta: completion.content };
1143
+ for (let index = 0; index < (completion.toolCalls?.length ?? 0); index += 1) {
1144
+ const call = completion.toolCalls[index];
1145
+ yield { type: "toolcall_start", id: call.id, index, name: call.name };
1146
+ yield {
1147
+ type: "toolcall_end",
1148
+ id: call.id,
1149
+ index,
1150
+ name: call.name,
1151
+ arguments: call.arguments
1152
+ };
1153
+ }
1154
+ yield { type: "message_stop", id, finishReason: completion.finishReason };
1155
+ }
1009
1156
  function isCancelCause(value) {
1010
1157
  if (typeof value !== "object" || value === null) return false;
1011
1158
  const record = value;
@@ -1026,6 +1173,13 @@ function cancelCauseFromSignal(signal) {
1026
1173
  if (isCancelCause(reason)) return reason;
1027
1174
  return { type: "abort" };
1028
1175
  }
1176
+ function abortedToolError(signal) {
1177
+ const cause = cancelCauseFromSignal(signal);
1178
+ return {
1179
+ name: "AbortError",
1180
+ message: `tool call aborted: ${cause?.type ?? "abort"}`
1181
+ };
1182
+ }
1029
1183
  function toTurnEndReason(fields) {
1030
1184
  const { cancelled, turnError, finishReason, cancelCause } = fields;
1031
1185
  if (cancelled || finishReason === "cancelled") {
@@ -1070,6 +1224,78 @@ async function cancellableDelay(ms, signal) {
1070
1224
  signal?.addEventListener("abort", onAbort, { once: true });
1071
1225
  });
1072
1226
  }
1227
+ var AssistantStreamAttempt = class {
1228
+ constructor(session, turn, step, nextRevision) {
1229
+ this.session = session;
1230
+ this.turn = turn;
1231
+ this.step = step;
1232
+ this.nextRevision = nextRevision;
1233
+ }
1234
+ session;
1235
+ turn;
1236
+ step;
1237
+ nextRevision;
1238
+ attemptId = randomUUID2();
1239
+ records = [];
1240
+ ended = false;
1241
+ start() {
1242
+ return { type: "assistant/stream", frame: {
1243
+ type: "start",
1244
+ attemptId: this.attemptId,
1245
+ revision: this.nextRevision(),
1246
+ turn: this.turn,
1247
+ step: this.step
1248
+ } };
1249
+ }
1250
+ push(chunk) {
1251
+ if (this.ended) throw new AgentError("assistant attempt is already settled");
1252
+ const record = { time: Date.now(), chunk: structuredClone(chunk) };
1253
+ const index = this.records.length;
1254
+ this.records.push(record);
1255
+ return { type: "assistant/stream", frame: {
1256
+ type: "chunk",
1257
+ attemptId: this.attemptId,
1258
+ revision: this.nextRevision(),
1259
+ index,
1260
+ ...structuredClone(record)
1261
+ } };
1262
+ }
1263
+ async settle(message) {
1264
+ if (this.ended) throw new AgentError("assistant attempt is already settled");
1265
+ const eventType = message ? "assistant/message" : "assistant/attempt";
1266
+ let event;
1267
+ try {
1268
+ event = message ? await this.session.append("assistant/message", { ...message, stream: this.records }) : await this.session.append("assistant/attempt", { turn: this.turn, step: this.step, stream: this.records });
1269
+ } catch (cause) {
1270
+ this.ended = true;
1271
+ const frame = { type: "assistant/stream", frame: {
1272
+ type: "end",
1273
+ attemptId: this.attemptId,
1274
+ revision: this.nextRevision(),
1275
+ index: this.records.length,
1276
+ outcome: { kind: "abandoned" }
1277
+ } };
1278
+ throw new AttemptSettlementError(cause, frame);
1279
+ }
1280
+ this.ended = true;
1281
+ return { type: "assistant/stream", frame: {
1282
+ type: "end",
1283
+ attemptId: this.attemptId,
1284
+ revision: this.nextRevision(),
1285
+ index: this.records.length,
1286
+ outcome: { kind: "committed", eventType, seq: event.seq }
1287
+ } };
1288
+ }
1289
+ };
1290
+ var AttemptSettlementError = class extends Error {
1291
+ constructor(cause, frame) {
1292
+ super("assistant attempt settlement failed");
1293
+ this.cause = cause;
1294
+ this.frame = frame;
1295
+ }
1296
+ cause;
1297
+ frame;
1298
+ };
1073
1299
  var AgentService = class {
1074
1300
  constructor(ctx, config = {}) {
1075
1301
  this.ctx = ctx;
@@ -1078,9 +1304,24 @@ var AgentService = class {
1078
1304
  }
1079
1305
  ctx;
1080
1306
  config;
1307
+ async *settleAttempt(attempt, message) {
1308
+ try {
1309
+ yield await attempt.settle(message);
1310
+ } catch (error) {
1311
+ if (error instanceof AttemptSettlementError) {
1312
+ yield error.frame;
1313
+ throw error.cause;
1314
+ }
1315
+ throw error;
1316
+ }
1317
+ }
1081
1318
  inbox;
1082
1319
  _seriesStarted = false;
1083
1320
  _persistedRequest = false;
1321
+ _streamRevision = 0;
1322
+ _attempt(session, turn, step) {
1323
+ return new AssistantStreamAttempt(session, turn, step, () => ++this._streamRevision);
1324
+ }
1084
1325
  /** Call when a request begins an explicit new model-message series. */
1085
1326
  startSeries() {
1086
1327
  this._seriesStarted = true;
@@ -1094,7 +1335,7 @@ var AgentService = class {
1094
1335
  return this._persistedRequest;
1095
1336
  }
1096
1337
  async run(input, options = {}) {
1097
- return consumeAgentStream(this._stream(input, options, false));
1338
+ return consumeAgentStream(this._stream(input, options));
1098
1339
  }
1099
1340
  async *runStream(input, options = {}) {
1100
1341
  const iterator = this._stream(input, options)[Symbol.asyncIterator]();
@@ -1104,7 +1345,7 @@ var AgentService = class {
1104
1345
  yield next.value;
1105
1346
  }
1106
1347
  }
1107
- async *_stream(input, options = {}, useStream = true) {
1348
+ async *_stream(input, options = {}) {
1108
1349
  const claimed = input ?? this.inbox.claim();
1109
1350
  if (!claimed) throw new AgentError("no agent input available");
1110
1351
  await this.config.hooks?.beforeRun?.(claimed, options);
@@ -1112,26 +1353,32 @@ var AgentService = class {
1112
1353
  const tools = this._tools();
1113
1354
  const llm = this._llm();
1114
1355
  if (!llm) throw new AgentError("no LLM adapter available");
1356
+ const streamAdapter = llm.stream;
1357
+ const persistChunks = streamAdapter !== void 0;
1115
1358
  const maxTurns = options.maxTurns ?? this.config.maxTurns ?? 64;
1116
1359
  const maxSteps = options.maxSteps ?? this.config.maxSteps ?? 64;
1117
1360
  const contextBudget = options.contextBudget ?? this.config.contextBudget;
1118
1361
  const injected = this.inbox.injected();
1119
1362
  this.ctx.emit("agent/start", { input: claimed, options, injected });
1120
1363
  let messages = await this._initialMessages(claimed);
1364
+ let claimedMessages = messages.filter((message) => message.role === "user");
1121
1365
  const steps = [];
1122
1366
  let output = "";
1123
1367
  let finishReason = "stop";
1368
+ let lengthReached = false;
1124
1369
  this.ctx.emit("agent/turn-start", {
1125
1370
  input: claimed,
1126
1371
  messages: copyMessages(messages),
1127
1372
  injected
1128
1373
  });
1129
- const turn = await session.nextTurn();
1130
- await session.append("turn/start", {
1131
- turn,
1132
- input: claimed.text ?? claimed,
1133
- reason: "user"
1134
- });
1374
+ const turn = options.turn ?? await session.nextTurn();
1375
+ if (options.turn === void 0) {
1376
+ await session.append("turn/start", {
1377
+ turn,
1378
+ input: claimed.text ?? claimed,
1379
+ reason: "user"
1380
+ });
1381
+ }
1135
1382
  let index = 0;
1136
1383
  let finalTurnGranted = false;
1137
1384
  let currentStepIndex;
@@ -1150,20 +1397,22 @@ var AgentService = class {
1150
1397
  messages = await this._enforceContextBudget(session, contextBudget, messages);
1151
1398
  }
1152
1399
  const stepInput = copyMessages(messages);
1153
- const preStep = this.ctx.waterfall("agent/pre-step", {
1400
+ const preStep = await this.ctx.waterfallAsync("agent/pre-step", {
1154
1401
  index,
1155
1402
  turn,
1156
1403
  step: index,
1157
1404
  messages: stepInput,
1405
+ claimedMessages: copyMessages(claimedMessages),
1158
1406
  ...options.signal ? { signal: options.signal } : {}
1159
1407
  }, (payload) => payload);
1160
- if (!preStep || !Array.isArray(preStep.messages) || !preStep.messages.length) {
1408
+ if (!preStep || preStep.admission === "reject" || !Array.isArray(preStep.messages) || !preStep.messages.length) {
1161
1409
  break;
1162
1410
  }
1163
1411
  if (preStep.startsRequestSeries) this._seriesStarted = true;
1164
1412
  await session.append("step/start", { turn, step: index });
1165
1413
  currentStepIndex = index;
1166
1414
  const requestedInput = copyMessages(preStep.messages);
1415
+ const admittedHistory = await session.deriveMessages();
1167
1416
  this.ctx.emit("agent/step", {
1168
1417
  index,
1169
1418
  turn,
@@ -1175,93 +1424,85 @@ var AgentService = class {
1175
1424
  maxSteps: maxSteps - steps.length
1176
1425
  };
1177
1426
  if (options.signal) completeOptions.signal = options.signal;
1178
- const request = this.ctx.waterfall("agent/request", {
1179
- index,
1180
- messages: requestedInput,
1181
- tools: availableTools,
1182
- options: completeOptions
1183
- }, (payload) => payload);
1184
- if (!request || !request.options || !request.tools) {
1185
- throw new AgentError("agent/request must return a request payload");
1186
- }
1187
1427
  let llmMessages = copyMessages(requestedInput);
1188
1428
  let completion;
1189
- const streamMethod = useStream ? llm.stream : void 0;
1429
+ let activeAttempt;
1190
1430
  let attempt = 0;
1191
- let persistedStepInput = false;
1192
1431
  while (true) {
1193
1432
  if (options.signal?.aborted) {
1194
1433
  finishReason = "cancelled";
1195
1434
  break;
1196
1435
  }
1197
1436
  const streamEvents = [];
1437
+ activeAttempt = this._attempt(session, turn, index);
1438
+ yield activeAttempt.start();
1439
+ const streamRequest = {
1440
+ index,
1441
+ messages: copyMessages(requestedInput),
1442
+ tools: [...availableTools],
1443
+ options: { ...completeOptions }
1444
+ };
1198
1445
  try {
1199
- if (streamMethod) {
1200
- const streamRequest = {
1201
- index,
1202
- messages: llmMessages,
1203
- tools: request.tools,
1204
- options: request.options
1205
- };
1206
- const stream = await this.ctx.waterfallAsync(
1207
- "llm/stream",
1208
- streamRequest,
1209
- async (payload) => {
1210
- if (!streamMethod) {
1211
- throw new AgentError("llm.stream is required for streaming runs");
1212
- }
1213
- return streamMethod(payload.messages, payload.tools, payload.options);
1214
- }
1215
- );
1216
- if (!stream || typeof stream[Symbol.asyncIterator] !== "function") {
1217
- throw new AgentError("llm/stream must return an async iterable stream");
1218
- }
1446
+ const request = await this.ctx.waterfallAsync("agent/request", {
1447
+ index,
1448
+ messages: copyMessages(requestedInput),
1449
+ tools: streamRequest.tools,
1450
+ options: streamRequest.options
1451
+ }, (payload) => payload);
1452
+ if (!request || !request.options || !request.tools) {
1453
+ throw new AgentError("agent/request must return a request payload");
1454
+ }
1455
+ streamRequest.tools = request.tools;
1456
+ streamRequest.options = request.options;
1457
+ let preparation;
1458
+ const prepareRequest = () => preparation ??= (async () => {
1459
+ lockStreamRequest(streamRequest);
1219
1460
  llmMessages = copyMessages(streamRequest.messages);
1220
- if (!persistedStepInput) {
1221
- await this._persistStepInput(session, request, llmMessages);
1222
- if (this.config.assertReplayable) {
1223
- await this._assertReplayable(session, llmMessages);
1224
- }
1225
- persistedStepInput = true;
1226
- }
1227
- let chunkIndex = 0;
1228
- for await (const event of stream) {
1229
- streamEvents.push(event);
1230
- if (event.type === "message_delta") {
1231
- await session.append("assistant/chunk", {
1232
- id: event.id,
1233
- content: event.delta,
1234
- index: chunkIndex
1235
- });
1236
- chunkIndex += 1;
1237
- }
1238
- yield event;
1239
- }
1240
- completion = completionFromStreamEvents(streamEvents);
1241
- } else {
1242
- if (!persistedStepInput) {
1243
- await this._persistStepInput(session, request, llmMessages);
1244
- if (this.config.assertReplayable) {
1245
- await this._assertReplayable(session, llmMessages);
1246
- }
1247
- persistedStepInput = true;
1461
+ await this._persistStepInput(session, streamRequest, llmMessages, admittedHistory);
1462
+ await this._assertReplayable(session, streamRequest, llmMessages);
1463
+ })();
1464
+ const stream = await this.ctx.waterfallAsync(
1465
+ "llm/stream",
1466
+ streamRequest,
1467
+ (payload) => (async function* () {
1468
+ await prepareRequest();
1469
+ yield* streamAdapter ? streamAdapter.call(llm, payload.messages, payload.tools, payload.options) : completeAsStream(llm, payload.messages, payload.tools, payload.options);
1470
+ })()
1471
+ );
1472
+ if (!stream || typeof stream[Symbol.asyncIterator] !== "function") {
1473
+ throw new AgentError("llm/stream must return an async iterable stream");
1474
+ }
1475
+ await prepareRequest();
1476
+ let chunkIndex = 0;
1477
+ for await (const event of stream) {
1478
+ streamEvents.push(structuredClone(event));
1479
+ yield activeAttempt.push(event);
1480
+ if (persistChunks && event.type === "message_delta") {
1481
+ await session.append("assistant/chunk", {
1482
+ id: event.id,
1483
+ content: event.delta,
1484
+ index: chunkIndex
1485
+ });
1486
+ chunkIndex += 1;
1248
1487
  }
1249
- completion = await llm.complete(llmMessages, request.tools, request.options);
1488
+ yield event;
1489
+ }
1490
+ completion = completionFromStreamEvents(streamEvents);
1491
+ if (completion.finishReason === "error" || completion.finishReason === "cancelled") {
1492
+ const terminalReason = completion.finishReason;
1493
+ completion = void 0;
1494
+ throw new AgentError(`LLM stream finished with ${terminalReason}`);
1250
1495
  }
1251
1496
  break;
1252
1497
  } catch (error) {
1253
- const content = partialStreamContent(streamEvents);
1254
- if (content) {
1255
- await session.append("assistant/message", {
1256
- content,
1257
- interrupted: true
1258
- });
1259
- llmMessages = await session.deriveMessages();
1260
- }
1498
+ yield activeAttempt.push({ type: "stream_error", error: toToolError(error) });
1261
1499
  if (options.signal?.aborted) {
1500
+ const content = partialStreamContent(streamEvents);
1501
+ yield* this.settleAttempt(activeAttempt, content ? { content, interrupted: true } : void 0);
1262
1502
  finishReason = "cancelled";
1263
1503
  break;
1264
1504
  }
1505
+ yield* this.settleAttempt(activeAttempt);
1265
1506
  attempt += 1;
1266
1507
  const failure = toToolError(error);
1267
1508
  const decision = await this.ctx.waterfallAsync(
@@ -1270,14 +1511,14 @@ var AgentService = class {
1270
1511
  index,
1271
1512
  turn,
1272
1513
  step: index,
1273
- messages: copyMessages(llmMessages),
1274
- tools: request.tools,
1275
- options: request.options,
1514
+ messages: copyMessages(streamRequest.messages),
1515
+ tools: [...streamRequest.tools],
1516
+ options: { ...streamRequest.options },
1276
1517
  attempt,
1277
1518
  error,
1278
1519
  failure,
1279
- ...request.options.provider ? { provider: request.options.provider } : {},
1280
- ...request.options.model ? { model: request.options.model } : {},
1520
+ ...streamRequest.options.provider ? { provider: streamRequest.options.provider } : {},
1521
+ ...streamRequest.options.model ? { model: streamRequest.options.model } : {},
1281
1522
  ...options.signal ? { signal: options.signal } : {}
1282
1523
  },
1283
1524
  () => void 0
@@ -1302,16 +1543,30 @@ var AgentService = class {
1302
1543
  await session.append("llm/retry-started", { retryId, retry: attempt });
1303
1544
  }
1304
1545
  }
1305
- if (options.signal?.aborted) break;
1306
- if (!completion) throw new AgentError("LLM adapter did not produce a completion");
1546
+ if (!completion || !activeAttempt) {
1547
+ if (options.signal?.aborted) {
1548
+ finishReason = "cancelled";
1549
+ break;
1550
+ }
1551
+ throw new AgentError("LLM adapter did not produce a completion");
1552
+ }
1307
1553
  const toolCalls = completion.toolCalls ?? [];
1308
- if (finalTurnGranted && toolCalls.length) {
1554
+ if (completion.finishReason === "length") lengthReached = true;
1555
+ if (options.signal?.aborted && toolCalls.length === 0) {
1556
+ yield activeAttempt.push({ type: "stream_error", error: { name: "AbortError", message: "stream aborted" } });
1557
+ yield* this.settleAttempt(activeAttempt);
1558
+ finishReason = "cancelled";
1559
+ break;
1560
+ }
1561
+ if (finalTurnGranted && toolCalls.length && !options.signal?.aborted) {
1562
+ yield activeAttempt.push({ type: "stream_error", error: { name: "AgentError", message: "maximum turns reached" } });
1563
+ yield* this.settleAttempt(activeAttempt);
1309
1564
  finishReason = "max_turns";
1310
1565
  break;
1311
1566
  }
1312
1567
  const toolResults = [];
1313
1568
  if (toolCalls.length) {
1314
- await session.append("assistant/message", {
1569
+ yield* this.settleAttempt(activeAttempt, {
1315
1570
  content: completion.content ?? "",
1316
1571
  toolCalls: toolCalls.map((call) => ({
1317
1572
  id: call.id,
@@ -1332,18 +1587,30 @@ var AgentService = class {
1332
1587
  if (options.signal) toolOptions.signal = options.signal;
1333
1588
  const startedAt = Date.now();
1334
1589
  let result;
1335
- try {
1336
- result = await tools.execute(call.name, call.arguments, toolOptions);
1337
- } catch (error) {
1590
+ if (options.signal?.aborted) {
1338
1591
  result = {
1339
1592
  ok: false,
1340
1593
  name: call.name,
1341
1594
  callId: call.id,
1342
1595
  input: call.arguments,
1343
- error: toToolError(error),
1596
+ error: abortedToolError(options.signal),
1344
1597
  startedAt,
1345
1598
  durationMs: Date.now() - startedAt
1346
1599
  };
1600
+ } else {
1601
+ try {
1602
+ result = await tools.execute(call.name, call.arguments, toolOptions);
1603
+ } catch (error) {
1604
+ result = {
1605
+ ok: false,
1606
+ name: call.name,
1607
+ callId: call.id,
1608
+ input: call.arguments,
1609
+ error: toToolError(error),
1610
+ startedAt,
1611
+ durationMs: Date.now() - startedAt
1612
+ };
1613
+ }
1347
1614
  }
1348
1615
  toolResults.push(result);
1349
1616
  const toolResultPayload = {
@@ -1385,7 +1652,7 @@ var AgentService = class {
1385
1652
  output = completion.content;
1386
1653
  }
1387
1654
  if (!toolCalls.length) {
1388
- await session.append("assistant/message", { content: completion.content ?? "" });
1655
+ yield* this.settleAttempt(activeAttempt, { content: completion.content ?? "" });
1389
1656
  }
1390
1657
  await session.append("step/end", {
1391
1658
  turn,
@@ -1396,19 +1663,19 @@ var AgentService = class {
1396
1663
  currentStepIndex = void 0;
1397
1664
  const nextMessages = this._extendMessages(llmMessages, completion, toolResults);
1398
1665
  const concludesTurn = toolResults.some((result) => result.concludesTurn === true);
1399
- if (concludesTurn) {
1400
- finishReason = "stop";
1401
- break;
1402
- }
1403
- if (toolCalls.length === 0) {
1404
- finishReason = completion.finishReason === "length" ? "length" : completion.finishReason === "error" ? "error" : "stop";
1666
+ let nextStepMessages = await this._claimNextStepMessages();
1667
+ if ((toolCalls.length === 0 || concludesTurn) && nextStepMessages.length === 0) {
1668
+ finishReason = lengthReached ? "length" : completion.finishReason === "error" ? "error" : "stop";
1405
1669
  const keepGoing = await this.ctx.serial("agent/turn-stopping", {
1406
1670
  index,
1407
1671
  turn,
1408
1672
  steps: copySteps(steps),
1409
1673
  finishReason
1410
1674
  });
1411
- if (!keepGoing) break;
1675
+ if (!keepGoing) {
1676
+ nextStepMessages = await this._claimNextStepMessages();
1677
+ if (nextStepMessages.length === 0) break;
1678
+ }
1412
1679
  }
1413
1680
  if (index + 1 >= maxTurns && !finalTurnGranted) {
1414
1681
  finalTurnGranted = true;
@@ -1418,7 +1685,8 @@ var AgentService = class {
1418
1685
  }
1419
1686
  if (toolCalls.length) finishReason = "tool_calls";
1420
1687
  index += 1;
1421
- messages = nextMessages;
1688
+ messages = [...nextMessages, ...nextStepMessages];
1689
+ claimedMessages = nextStepMessages.filter((message) => message.role === "user");
1422
1690
  }
1423
1691
  } catch (error) {
1424
1692
  turnError = error;
@@ -1504,23 +1772,22 @@ var AgentService = class {
1504
1772
  });
1505
1773
  return compactMessages.map((message) => copyMessages([message])[0]);
1506
1774
  }
1507
- async _persistStepInput(session, request, input) {
1508
- const system = input.find((message) => message.role === "system")?.content;
1509
- const tools = request.tools.map((tool) => ({
1510
- name: tool.schema.name,
1511
- description: tool.schema.description,
1512
- ...tool.schema.parameters ? { parameters: tool.schema.parameters } : {}
1513
- }));
1514
- const config = {
1515
- ...request.options.provider ? { provider: request.options.provider } : {},
1516
- ...request.options.model ? { model: request.options.model } : {},
1517
- ...request.options.temperature !== void 0 ? { temperature: request.options.temperature } : {}
1518
- };
1519
- const nextHeader = {
1520
- ...Object.keys(config).length ? { config } : {},
1521
- ...system !== void 0 ? { system } : {},
1522
- ...tools.length ? { tools } : {}
1523
- };
1775
+ async _persistStepInput(session, request, input, admittedHistory) {
1776
+ const surface = await session.deriveMessages();
1777
+ const owned = admittedHistory.filter((message) => !isUserOrSystemMessage(message));
1778
+ let ownerIndex = 0;
1779
+ for (const message of input) {
1780
+ if (isUserOrSystemMessage(message)) continue;
1781
+ const actual = canonicalMessages([message]);
1782
+ while (ownerIndex < owned.length && canonicalMessages([owned[ownerIndex]]) !== actual) {
1783
+ ownerIndex += 1;
1784
+ }
1785
+ if (ownerIndex === owned.length) {
1786
+ throw new AgentError("request is not reconstructable from session log: unowned or reordered assistant/tool message");
1787
+ }
1788
+ ownerIndex += 1;
1789
+ }
1790
+ const nextHeader = requestHeaderFor(request, input);
1524
1791
  const previousHeader = session.requestHeader();
1525
1792
  const isFirstRequest = !this._persistedRequest;
1526
1793
  const isResume = isFirstRequest && previousHeader !== void 0;
@@ -1550,19 +1817,23 @@ var AgentService = class {
1550
1817
  ...request.options.provider ? { provider: request.options.provider } : {},
1551
1818
  ...request.options.model ? { model: request.options.model } : {}
1552
1819
  };
1820
+ if (request.options.contextWindow !== void 0) {
1821
+ nextContext.contextWindow = request.options.contextWindow;
1822
+ }
1553
1823
  const llmService2 = this.ctx.reflect.get("llm", false);
1554
1824
  const routeCapacity = llmService2?.routeCapacity?.();
1555
- if (routeCapacity?.contextWindow !== void 0) {
1825
+ if (nextContext.contextWindow === void 0 && routeCapacity?.contextWindow !== void 0) {
1556
1826
  nextContext.contextWindow = routeCapacity.contextWindow;
1557
1827
  }
1558
1828
  const previousContext = session.requestContext();
1559
1829
  if (previousContext?.provider !== nextContext.provider || previousContext?.model !== nextContext.model || previousContext?.contextWindow !== nextContext.contextWindow || previousContext === void 0) {
1560
1830
  await session.append("request/context", nextContext);
1561
1831
  }
1562
- const requested = input.filter(isUserOrSystemMessage);
1563
- const surface = await session.deriveMessages();
1564
- const appendDurable = async (messages) => {
1565
- for (const message of messages) {
1832
+ const isSurfacePrefix = (messages) => canonicalMessages(messages.slice(0, surface.length)) === canonicalMessages(surface);
1833
+ const requested = surface.length > 0 && input[0]?.role === "system" && input[0].content === nextHeader.system && isSurfacePrefix(input.slice(1)) ? input.slice(1) : input;
1834
+ const tail = requested.slice(surface.length);
1835
+ if (isSurfacePrefix(requested) && tail.every(isUserOrSystemMessage)) {
1836
+ for (const message of tail) {
1566
1837
  if (message.role === "user") {
1567
1838
  await session.append("user/message", {
1568
1839
  content: message.content,
@@ -1575,45 +1846,37 @@ var AgentService = class {
1575
1846
  });
1576
1847
  }
1577
1848
  }
1578
- };
1579
- if (surface.length === 0) {
1580
- await appendDurable(requested);
1581
1849
  return;
1582
1850
  }
1583
- const durableUsers = surface.filter((message) => message.role === "user").length;
1584
- let seenUsers = 0;
1585
- const newTail = [];
1586
- for (const message of requested) {
1587
- if (message.role === "user") seenUsers += 1;
1588
- if (seenUsers <= durableUsers) continue;
1589
- newTail.push(message);
1590
- }
1591
- await appendDurable(newTail);
1851
+ const nodes = await session.surfaceEvents();
1852
+ await session.append("checkpoint", {
1853
+ messages: copyMessages(requested),
1854
+ surfaceOp: { op: "replace", start: nodes[0].seq, end: nodes.at(-1).seq }
1855
+ });
1592
1856
  }
1593
- async _assertReplayable(session, input) {
1857
+ async _assertReplayable(session, request, input) {
1594
1858
  const reconstructed = await session.deriveMessages();
1595
- const canonical = (messages) => messages.filter((message) => message.role !== "system").map((message) => ({
1596
- role: message.role,
1597
- content: message.content,
1598
- ...message.tool_call_id ? { tool_call_id: message.tool_call_id } : {},
1599
- ...message.name ? { name: message.name } : {},
1600
- ...message.tool_calls?.length ? { tool_calls: message.tool_calls.map((call) => ({
1601
- id: call.id,
1602
- name: call.name,
1603
- args: JSON.stringify(call.arguments ?? {})
1604
- })) } : {}
1605
- }));
1606
- const actual = canonical(input);
1607
- const replay = canonical(reconstructed);
1608
- if (JSON.stringify(actual) !== JSON.stringify(replay)) {
1859
+ const actual = canonicalMessages(input);
1860
+ const replay = canonicalMessages(reconstructed);
1861
+ const header = session.requestHeader();
1862
+ const headerOwnsPrefix = input[0]?.role === "system" && input[0].content === header?.system && canonicalMessages(input.slice(1)) === replay;
1863
+ if (actual !== replay && !headerOwnsPrefix) {
1609
1864
  throw new AgentError(
1610
- `request is not reconstructable from session log: actual=${JSON.stringify(actual)} replay=${JSON.stringify(replay)}`
1865
+ `request is not reconstructable from session log: actual=${actual} replay=${replay}`
1611
1866
  );
1612
1867
  }
1868
+ const expected = requestHeaderFor(request, input);
1869
+ if (header?.system !== expected.system || JSON.stringify(header?.tools ?? []) !== JSON.stringify(expected.tools ?? []) || JSON.stringify(header?.config ?? {}) !== JSON.stringify(expected.config ?? {})) {
1870
+ throw new AgentError("request envelope is not reconstructable from session log");
1871
+ }
1872
+ }
1873
+ async _claimNextStepMessages() {
1874
+ const inputs = await this.config.claimNextStep?.() ?? [];
1875
+ 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) }] : []);
1613
1876
  }
1614
1877
  async _initialMessages(input) {
1615
1878
  const promptService = this.ctx.reflect.get("systemPrompt", false);
1616
- const assembled = promptService ? (await promptService.assemble()).text.trim() : void 0;
1879
+ const assembled = promptService ? (await promptService.assemble({}, this.ctx)).text.trim() : void 0;
1617
1880
  const systemPrompt2 = assembled || this.inbox.injected().get("agentSystem");
1618
1881
  const systemMessage = typeof systemPrompt2 === "string" && systemPrompt2 ? [{ role: "system", content: systemPrompt2 }] : [];
1619
1882
  if (input.messages?.length) {
@@ -1638,7 +1901,7 @@ var AgentService = class {
1638
1901
  arguments: call.arguments
1639
1902
  }));
1640
1903
  }
1641
- next.push(assistant);
1904
+ if (assistant.content || toolCalls.length) next.push(assistant);
1642
1905
  for (const call of toolCalls) {
1643
1906
  const result = toolResults.find((candidate) => candidate.callId === call.id);
1644
1907
  const content = result ? result.ok ? stringify2(result.output) : `error: ${result.error?.message ?? "unknown"}` : "error: missing tool result";
@@ -1668,7 +1931,8 @@ var AgentService = class {
1668
1931
  return session;
1669
1932
  }
1670
1933
  _tools() {
1671
- const tools = this.ctx.tools;
1934
+ const scoped = this.ctx.reflect.get("tools", false);
1935
+ const tools = scoped ?? this.ctx.tools;
1672
1936
  if (!tools) throw new AgentError("tools service is required");
1673
1937
  return tools;
1674
1938
  }
@@ -1837,206 +2101,1500 @@ function defineAgent(definition) {
1837
2101
  // packages/agent/src/live.ts
1838
2102
  import { randomUUID as randomUUID4 } from "node:crypto";
1839
2103
 
1840
- // packages/agent/src/inbox-durable.ts
1841
- import { randomUUID as randomUUID3 } from "node:crypto";
1842
- function contentOf(input) {
1843
- return input.text ?? "";
1844
- }
1845
- function payloadOf(input) {
1846
- return input.content !== void 0 ? input.content : void 0;
1847
- }
1848
- var DurableInbox = class _DurableInbox {
1849
- constructor(_session) {
1850
- this._session = _session;
2104
+ // packages/core/src/utils.ts
2105
+ var DisposableList = class {
2106
+ sn = 0;
2107
+ map = /* @__PURE__ */ new Map();
2108
+ weak = /* @__PURE__ */ new WeakMap();
2109
+ get length() {
2110
+ return this.map.size;
2111
+ }
2112
+ push(value) {
2113
+ const sn = ++this.sn;
2114
+ this.map.set(sn, value);
2115
+ this.weak.set(value, sn);
2116
+ return () => this.map.delete(sn);
2117
+ }
2118
+ unshift(value) {
2119
+ const sn = ++this.sn;
2120
+ const entries = [...this.map.entries()];
2121
+ this.map.clear();
2122
+ this.map.set(sn, value);
2123
+ this.weak.set(value, sn);
2124
+ for (const [oldSn, oldValue] of entries) {
2125
+ this.map.set(oldSn, oldValue);
2126
+ }
2127
+ return () => this.map.delete(sn);
1851
2128
  }
1852
- _session;
1853
- _nextTurn = [];
1854
- _nextStep = [];
1855
- get size() {
1856
- return this._nextTurn.length + this._nextStep.length;
2129
+ delete(value) {
2130
+ const sn = this.weak.get(value);
2131
+ if (!sn) return false;
2132
+ return this.map.delete(sn);
1857
2133
  }
1858
- async insert(input, target = "next-turn") {
1859
- const message = {
1860
- id: randomUUID3(),
1861
- ...input.text !== void 0 ? { text: input.text } : {},
1862
- ...input.content !== void 0 ? { content: input.content } : {}
1863
- };
1864
- const list = target === "next-step" ? this._nextStep : this._nextTurn;
1865
- list.push(message);
1866
- await this._session.append("agent/inbox/spliced", {
1867
- target,
1868
- index: list.length - 1,
1869
- inserted: [{
1870
- id: message.id,
1871
- content: contentOf(message),
1872
- ...payloadOf(message) !== void 0 ? { payload: payloadOf(message) } : {}
1873
- }]
1874
- });
1875
- return message;
2134
+ clear() {
2135
+ const values = [...this.map.values()];
2136
+ this.map.clear();
2137
+ return values.reverse();
1876
2138
  }
1877
- async steer(input) {
1878
- const message = {
1879
- id: randomUUID3(),
1880
- ...input.text !== void 0 ? { text: input.text } : {},
1881
- ...input.content !== void 0 ? { content: input.content } : {}
1882
- };
1883
- this._nextStep.unshift(message);
1884
- await this._session.append("agent/inbox/spliced", {
1885
- target: "next-step",
1886
- index: 0,
1887
- inserted: [{
1888
- id: message.id,
1889
- content: contentOf(message),
1890
- ...payloadOf(message) !== void 0 ? { payload: payloadOf(message) } : {},
1891
- mode: "steer"
1892
- }]
1893
- });
1894
- return message;
2139
+ [Symbol.iterator]() {
2140
+ return this.map.values();
1895
2141
  }
1896
- async claim() {
1897
- const steered = this._nextStep.shift();
1898
- if (steered) {
1899
- await this._session.append("agent/inbox/spliced", {
1900
- target: "next-step",
1901
- index: 0,
1902
- deleteCount: 1
1903
- });
1904
- return steered;
1905
- }
1906
- const next = this._nextTurn.shift();
1907
- if (next) {
1908
- await this._session.append("agent/inbox/spliced", {
1909
- target: "next-turn",
1910
- index: 0,
1911
- deleteCount: 1
1912
- });
1913
- return next;
1914
- }
1915
- return void 0;
2142
+ [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
2143
+ return [...this];
1916
2144
  }
1917
- /**
1918
- * Claim the complete proposed step batch: every pending next-step input plus
1919
- * one next-turn message at a turn boundary. Returns an empty array when
1920
- * nothing is pending.
1921
- */
1922
- async claimBatch() {
1923
- const claimed = [];
1924
- if (this._nextStep.length) {
1925
- const removed = this._nextStep.splice(0);
1926
- claimed.push(...removed);
1927
- await this._session.append("agent/inbox/spliced", {
1928
- target: "next-step",
1929
- index: 0,
1930
- deleteCount: removed.length
1931
- });
2145
+ };
2146
+ var symbols = {
2147
+ shadow: /* @__PURE__ */ Symbol.for("tnega.shadow"),
2148
+ caller: /* @__PURE__ */ Symbol.for("tnega.caller"),
2149
+ receiver: /* @__PURE__ */ Symbol.for("tnega.receiver"),
2150
+ original: /* @__PURE__ */ Symbol.for("tnega.original"),
2151
+ metadata: /* @__PURE__ */ Symbol.for("tnega.metadata"),
2152
+ initHooks: /* @__PURE__ */ Symbol.for("tnega.initHooks"),
2153
+ checkProto: /* @__PURE__ */ Symbol.for("tnega.checkProto"),
2154
+ effect: /* @__PURE__ */ Symbol.for("tnega.effect"),
2155
+ filter: /* @__PURE__ */ Symbol.for("tnega.filter"),
2156
+ isolate: /* @__PURE__ */ Symbol.for("tnega.isolate"),
2157
+ intercept: /* @__PURE__ */ Symbol.for("tnega.intercept"),
2158
+ init: /* @__PURE__ */ Symbol.for("tnega.init"),
2159
+ check: /* @__PURE__ */ Symbol.for("tnega.check"),
2160
+ config: /* @__PURE__ */ Symbol.for("tnega.config"),
2161
+ invoke: /* @__PURE__ */ Symbol.for("tnega.invoke"),
2162
+ extend: /* @__PURE__ */ Symbol.for("tnega.extend"),
2163
+ tracker: /* @__PURE__ */ Symbol.for("tnega.tracker"),
2164
+ resolveConfig: /* @__PURE__ */ Symbol.for("tnega.resolveConfig")
2165
+ };
2166
+ var GeneratorFunction = function* () {
2167
+ }.constructor;
2168
+ var AsyncGeneratorFunction = async function* () {
2169
+ }.constructor;
2170
+ function isConstructor(func) {
2171
+ if (typeof func !== "function") return false;
2172
+ if (func instanceof GeneratorFunction) return false;
2173
+ if (AsyncGeneratorFunction !== Function && func instanceof AsyncGeneratorFunction) return false;
2174
+ return /^class\s/.test(Function.prototype.toString.call(func));
2175
+ }
2176
+ function isObject(value) {
2177
+ return !!value && (typeof value === "object" || typeof value === "function");
2178
+ }
2179
+ function isNullable(value) {
2180
+ return value == null;
2181
+ }
2182
+ function joinPrototype(proto1, proto2) {
2183
+ if (proto1 === Object.prototype) return proto2;
2184
+ const result = Object.create(joinPrototype(Object.getPrototypeOf(proto1), proto2));
2185
+ for (const key of Reflect.ownKeys(proto1)) {
2186
+ Object.defineProperty(result, key, Reflect.getOwnPropertyDescriptor(proto1, key));
2187
+ }
2188
+ return result;
2189
+ }
2190
+ function getPropertyDescriptor(target, prop) {
2191
+ let proto = target;
2192
+ while (proto) {
2193
+ const desc = Reflect.getOwnPropertyDescriptor(proto, prop);
2194
+ if (desc) return desc;
2195
+ proto = Object.getPrototypeOf(proto);
2196
+ }
2197
+ }
2198
+ function getTraceable(ctx, value) {
2199
+ if (!isObject(value)) return value;
2200
+ if (Object.hasOwn(value, symbols.shadow)) {
2201
+ return Object.getPrototypeOf(value);
2202
+ }
2203
+ const tracker = value[symbols.tracker];
2204
+ if (!tracker) return value;
2205
+ return createTraceable(ctx, value, tracker);
2206
+ }
2207
+ function withProps(target, props) {
2208
+ if (!props) return target;
2209
+ return new Proxy(target, {
2210
+ get: (target2, prop, receiver) => {
2211
+ if (prop in props && prop !== "constructor") return Reflect.get(props, prop, receiver);
2212
+ return Reflect.get(target2, prop, receiver);
2213
+ },
2214
+ set: (target2, prop, value, receiver) => {
2215
+ if (prop in props && prop !== "constructor") return Reflect.set(props, prop, value, receiver);
2216
+ return Reflect.set(target2, prop, value, receiver);
1932
2217
  }
1933
- const nextTurn = this._nextTurn.shift();
1934
- if (nextTurn) {
1935
- claimed.push(nextTurn);
1936
- await this._session.append("agent/inbox/spliced", {
1937
- target: "next-turn",
1938
- index: 0,
1939
- deleteCount: 1
1940
- });
2218
+ });
2219
+ }
2220
+ function withProp(target, prop, value) {
2221
+ return withProps(target, Object.defineProperty(/* @__PURE__ */ Object.create(null), prop, {
2222
+ value,
2223
+ writable: false
2224
+ }));
2225
+ }
2226
+ function createShadow(ctx, target, property, receiver) {
2227
+ if (!property) return receiver;
2228
+ const origin = getPropertyDescriptor(target, property)?.value;
2229
+ if (!origin) return receiver;
2230
+ return withProp(receiver, property, ctx.extend({ [symbols.shadow]: origin }));
2231
+ }
2232
+ function createShadowMethod(ctx, value, outer, shadow) {
2233
+ const proxy = new Proxy(value, {
2234
+ apply: (target, thisArg, args) => {
2235
+ if (thisArg === outer) thisArg = shadow;
2236
+ return getTraceable(ctx, Reflect.apply(target, thisArg, args));
1941
2237
  }
1942
- return claimed;
1943
- }
1944
- async clear() {
1945
- if (this._nextTurn.length) {
1946
- this._nextTurn = [];
1947
- await this._session.append("agent/inbox/spliced", {
1948
- target: "next-turn",
1949
- index: 0,
1950
- deleteCount: Number.POSITIVE_INFINITY
1951
- });
2238
+ });
2239
+ return proxy;
2240
+ }
2241
+ function createTraceable(ctx, value, tracker) {
2242
+ const caller = ctx[symbols.shadow] ?? ctx;
2243
+ if (ctx[symbols.shadow]) {
2244
+ ctx = Object.getPrototypeOf(ctx);
2245
+ }
2246
+ const proxy = new Proxy(value, {
2247
+ get: (target, prop, receiver) => {
2248
+ if (prop === symbols.original) return target;
2249
+ if (prop === symbols.caller) return caller;
2250
+ if (prop === tracker.property) return ctx;
2251
+ if (typeof prop === "symbol") {
2252
+ return Reflect.get(target, prop, receiver);
2253
+ }
2254
+ if (tracker.associate && ctx.reflect.props[`${tracker.associate}.${prop}`]) {
2255
+ return Reflect.get(ctx, `${tracker.associate}.${prop}`, withProp(ctx, symbols.receiver, receiver));
2256
+ }
2257
+ let shadow;
2258
+ let innerValue;
2259
+ const desc = getPropertyDescriptor(target, prop);
2260
+ if (desc?.value !== void 0) {
2261
+ innerValue = desc.value;
2262
+ } else {
2263
+ shadow = createShadow(ctx, target, tracker.property, receiver);
2264
+ innerValue = Reflect.get(target, prop, shadow);
2265
+ }
2266
+ const innerTracker = innerValue?.[symbols.tracker];
2267
+ if (innerTracker) {
2268
+ return createTraceable(ctx, innerValue, innerTracker);
2269
+ } else if (!tracker.noShadow && typeof innerValue === "function") {
2270
+ shadow ??= createShadow(ctx, target, tracker.property, receiver);
2271
+ return createShadowMethod(ctx, innerValue, receiver, shadow);
2272
+ } else {
2273
+ return innerValue;
2274
+ }
2275
+ },
2276
+ set: (target, prop, value2, receiver) => {
2277
+ if (prop === symbols.original || prop === symbols.caller || prop === tracker.property) return false;
2278
+ if (typeof prop === "symbol") {
2279
+ return Reflect.set(target, prop, value2, receiver);
2280
+ }
2281
+ if (tracker.associate && ctx.reflect.props[`${tracker.associate}.${prop}`]) {
2282
+ return Reflect.set(ctx, `${tracker.associate}.${prop}`, value2, withProp(ctx, symbols.receiver, receiver));
2283
+ }
2284
+ const shadow = createShadow(ctx, target, tracker.property, receiver);
2285
+ return Reflect.set(target, prop, value2, shadow);
2286
+ },
2287
+ apply: (target, thisArg, args) => {
2288
+ const receiver = tracker.noShadow ? proxy : createShadow(ctx, target, tracker.property, proxy);
2289
+ return applyTraceable(receiver, target, thisArg, args);
1952
2290
  }
1953
- if (this._nextStep.length) {
1954
- this._nextStep = [];
1955
- await this._session.append("agent/inbox/spliced", {
1956
- target: "next-step",
1957
- index: 0,
1958
- deleteCount: Number.POSITIVE_INFINITY
1959
- });
2291
+ });
2292
+ return proxy;
2293
+ }
2294
+ function applyTraceable(proxy, value, thisArg, args) {
2295
+ if (!value[symbols.invoke]) return Reflect.apply(value, thisArg, args);
2296
+ return value[symbols.invoke].apply(proxy, args);
2297
+ }
2298
+ function createCallable(name2, proto, tracker) {
2299
+ const self = function(...args) {
2300
+ const proxy = createTraceable(self.ctx, self, tracker);
2301
+ return Reflect.apply(proxy, this, args);
2302
+ };
2303
+ Object.defineProperty(self, "name", { value: name2, configurable: true, writable: true });
2304
+ return Object.setPrototypeOf(self, proto);
2305
+ }
2306
+ function handleError(info, reason, getOuterStack) {
2307
+ const innerLines = info.error.stack.split("\n");
2308
+ if (typeof reason?.stack !== "string") {
2309
+ const outerError = new Error(reason);
2310
+ const lines2 = outerError.stack.split("\n");
2311
+ lines2.splice(1, Infinity, ...getOuterStack());
2312
+ outerError.stack = lines2.join("\n");
2313
+ throw outerError;
2314
+ }
2315
+ const lines = reason.stack.split("\n");
2316
+ const index = lines.indexOf(innerLines[2]);
2317
+ if (index === -1) throw reason;
2318
+ lines.splice(index - info.offset, Infinity, ...getOuterStack());
2319
+ reason.stack = lines.join("\n");
2320
+ throw reason;
2321
+ }
2322
+ function composeError(callback, getOuterStack = buildOuterStack()) {
2323
+ const info = { offset: 1, error: new Error() };
2324
+ try {
2325
+ const result = callback(info);
2326
+ if (isObject(result) && "then" in result) {
2327
+ return result.then(void 0, (reason) => handleError(info, reason, getOuterStack));
1960
2328
  }
2329
+ return result;
2330
+ } catch (reason) {
2331
+ handleError(info, reason, getOuterStack);
1961
2332
  }
1962
- /** Insert a message at an explicit target/index boundary. */
1963
- async insertAt(input, target, index, mode = "followup") {
1964
- const message = {
1965
- id: randomUUID3(),
1966
- ...input.text !== void 0 ? { text: input.text } : {},
1967
- ...input.content !== void 0 ? { content: input.content } : {}
1968
- };
1969
- const list = target === "next-step" ? this._nextStep : this._nextTurn;
1970
- const safeIndex = Math.max(0, Math.min(index, list.length));
1971
- list.splice(safeIndex, 0, message);
1972
- await this._session.append("agent/inbox/spliced", {
1973
- target,
1974
- index: safeIndex,
1975
- inserted: [{
1976
- id: message.id,
1977
- content: contentOf(message),
1978
- ...payloadOf(message) !== void 0 ? { payload: payloadOf(message) } : {},
1979
- mode
1980
- }]
2333
+ }
2334
+ function buildOuterStack(offset = 0) {
2335
+ const outerError = new Error();
2336
+ return () => outerError.stack.split("\n").slice(3 + offset);
2337
+ }
2338
+
2339
+ // packages/core/src/events.ts
2340
+ function isBailed(value) {
2341
+ return value !== null && value !== false && value !== void 0;
2342
+ }
2343
+ var EventsService = class {
2344
+ constructor(ctx) {
2345
+ this.ctx = ctx;
2346
+ Object.defineProperty(this, symbols.tracker, {
2347
+ value: { property: "ctx", noShadow: true }
1981
2348
  });
1982
- return message;
1983
- }
1984
- /**
1985
- * Replace one pending message by id. The old message is emitted as
1986
- * discarded and the new one as inserted, matching the DSH inbox contract.
1987
- */
1988
- async replace(messageId, input) {
1989
- const entry = this._find(messageId);
1990
- if (!entry) return void 0;
1991
- const replacement = {
1992
- id: randomUUID3(),
1993
- ...input.text !== void 0 ? { text: input.text } : {},
1994
- ...input.content !== void 0 ? { content: input.content } : {}
1995
- };
1996
- const target = entry.target;
1997
- const index = entry.index;
1998
- entry.list.splice(index, 1, replacement);
1999
- await this._session.append("agent/inbox/spliced", {
2000
- target,
2001
- index,
2002
- deleteCount: 1,
2003
- inserted: [{
2004
- id: replacement.id,
2005
- content: contentOf(replacement),
2006
- ...payloadOf(replacement) !== void 0 ? { payload: payloadOf(replacement) } : {}
2007
- }]
2349
+ this.on("internal/listener", function(name2, listener, options) {
2350
+ if (name2 === "internal/update" && !options.global) {
2351
+ const hooks = this.fiber._hooks["internal/update"] ??= new DisposableList();
2352
+ const method = options.prepend ? "unshift" : "push";
2353
+ return hooks[method](listener);
2354
+ }
2008
2355
  });
2009
- return replacement;
2356
+ this.on("internal/update", function(config, noSave, next) {
2357
+ const cbs = [...this.fiber._hooks["internal/update"] ?? []];
2358
+ const _next = () => {
2359
+ const cb = cbs.shift() ?? next;
2360
+ return cb.call(this, config, noSave, _next);
2361
+ };
2362
+ return _next();
2363
+ }, { global: true, prepend: true });
2010
2364
  }
2011
- /** Remove one pending message by id. */
2012
- async remove(messageId) {
2013
- const entry = this._find(messageId);
2014
- if (!entry) return void 0;
2015
- entry.list.splice(entry.index, 1);
2016
- await this._session.append("agent/inbox/spliced", {
2017
- target: entry.target,
2018
- index: entry.index,
2019
- deleteCount: 1
2020
- });
2021
- return entry.message;
2365
+ ctx;
2366
+ _hooks = /* @__PURE__ */ Object.create(null);
2367
+ _resolve(type, args) {
2368
+ const thisArg = typeof args[0] === "object" || typeof args[0] === "function" ? args.shift() : null;
2369
+ const name2 = args.shift();
2370
+ if (!name2.startsWith("internal/") && this._hooks["internal/dispatch"]?.length) {
2371
+ this.emit("internal/dispatch", type, name2, args, thisArg);
2372
+ }
2373
+ const filter = thisArg?.[Context.filter];
2374
+ return [thisArg, (this._hooks[name2] || []).filter((hook) => hook.global || !filter || filter.call(thisArg, hook.ctx)).map((hook) => hook.callback)];
2375
+ }
2376
+ dispatch(type, args) {
2377
+ const [thisArg, callbacks] = this._resolve(type, args);
2378
+ return callbacks.map((callback) => callback.bind(thisArg));
2379
+ }
2380
+ async parallel(...args) {
2381
+ const [thisArg, callbacks] = this._resolve("parallel", args);
2382
+ const results = await Promise.allSettled(callbacks.map(async (callback) => Reflect.apply(callback, thisArg, args)));
2383
+ const errors = results.filter((result) => result.status === "rejected").map((result) => result.reason);
2384
+ if (errors.length) throw new AggregateError(errors);
2385
+ }
2386
+ emit(...args) {
2387
+ const [thisArg, callbacks] = this._resolve("emit", args);
2388
+ for (const callback of callbacks) Reflect.apply(callback, thisArg, args);
2389
+ }
2390
+ async serial(...args) {
2391
+ const [thisArg, callbacks] = this._resolve("serial", args);
2392
+ for (const callback of callbacks) {
2393
+ const result = await Reflect.apply(callback, thisArg, args);
2394
+ if (isBailed(result)) return result;
2395
+ }
2022
2396
  }
2023
- /** Return the first pending message by id across both queues. */
2024
- get(messageId) {
2025
- return this._find(messageId)?.message;
2397
+ bail(...args) {
2398
+ const [thisArg, callbacks] = this._resolve("bail", args);
2399
+ for (const callback of callbacks) {
2400
+ const result = Reflect.apply(callback, thisArg, args);
2401
+ if (isBailed(result)) return result;
2402
+ }
2026
2403
  }
2027
- snapshot() {
2028
- return {
2029
- nextTurn: [...this._nextTurn],
2030
- nextStep: [...this._nextStep]
2404
+ waterfall(...args) {
2405
+ const [thisArg, callbacks] = this._resolve("waterfall", args);
2406
+ const inner = args.pop();
2407
+ const next = () => {
2408
+ const callback = callbacks.shift();
2409
+ return callback ? Reflect.apply(callback, thisArg, args) : inner(...args);
2031
2410
  };
2411
+ args.push(next);
2412
+ return next();
2413
+ }
2414
+ async waterfallAsync(...args) {
2415
+ const [thisArg, callbacks] = this._resolve("waterfallAsync", args);
2416
+ const inner = args.pop();
2417
+ const next = async () => {
2418
+ const callback = callbacks.shift();
2419
+ return callback ? await Reflect.apply(callback, thisArg, args) : inner(...args);
2420
+ };
2421
+ args.push(next);
2422
+ return next();
2423
+ }
2424
+ register(label, hooks, callback, options) {
2425
+ const method = options.prepend ? "unshift" : "push";
2426
+ return this.ctx.fiber.effect(() => {
2427
+ hooks[method]({ ctx: this.ctx, callback, ...options });
2428
+ return () => this.unregister(hooks, callback);
2429
+ }, label);
2430
+ }
2431
+ unregister(hooks, callback) {
2432
+ const index = hooks.findIndex((hook) => hook.callback === callback);
2433
+ if (index >= 0) {
2434
+ hooks.splice(index, 1);
2435
+ return true;
2436
+ }
2032
2437
  }
2033
- _find(messageId) {
2034
- for (const target of ["next-step", "next-turn"]) {
2035
- const list = target === "next-step" ? this._nextStep : this._nextTurn;
2036
- const index = list.findIndex((message) => message.id === messageId);
2037
- if (index >= 0) return { list, target, index, message: list[index] };
2438
+ on(name2, listener, options = {}) {
2439
+ if (typeof options !== "object") {
2440
+ options = { prepend: options };
2038
2441
  }
2039
- return void 0;
2442
+ this.ctx.fiber.assertActive();
2443
+ listener = this.ctx.reflect.bind(listener);
2444
+ const result = this.bail(this.ctx, "internal/listener", name2, listener, options);
2445
+ if (result) return result;
2446
+ const hooks = this._hooks[name2] ??= [];
2447
+ return this.register(`ctx.on(${JSON.stringify(name2)})`, hooks, listener, options);
2448
+ }
2449
+ once(name2, listener, options = {}) {
2450
+ const dispose = this.on(name2, function(...args) {
2451
+ dispose();
2452
+ return listener.apply(this, args);
2453
+ }, options);
2454
+ return dispose;
2455
+ }
2456
+ };
2457
+
2458
+ // packages/core/src/fiber.ts
2459
+ var INACTIVE = "__INACTIVE__";
2460
+ var ValidationError = class extends TypeError {
2461
+ name = "ValidationError";
2462
+ constructor(issues) {
2463
+ super("invalid config:\n" + issues.map((issue) => {
2464
+ if (issue.path) {
2465
+ return ` - ${issue.message} (at ${issue.path.join(".")})`;
2466
+ }
2467
+ return ` - ${issue.message}`;
2468
+ }).join("\n"));
2469
+ }
2470
+ };
2471
+ function resolveConfig(runtime, config) {
2472
+ if (!runtime.Config) return config;
2473
+ const schema = runtime.Config;
2474
+ if (typeof schema === "function") return schema(config);
2475
+ const validate = schema?.["~standard"]?.validate;
2476
+ if (typeof validate === "function") {
2477
+ const result = validate(config);
2478
+ if ("then" in result) {
2479
+ throw new TypeError("Async config validation is not supported");
2480
+ }
2481
+ if (result.issues) {
2482
+ throw new ValidationError(result.issues);
2483
+ }
2484
+ return result.value;
2485
+ }
2486
+ return config;
2487
+ }
2488
+ var CordisErrorCode = {
2489
+ INACTIVE_EFFECT: "cannot create effect on inactive context"
2490
+ };
2491
+ var CordisError = class extends Error {
2492
+ constructor(code, message) {
2493
+ super(message ?? CordisErrorCode[code]);
2494
+ this.code = code;
2495
+ }
2496
+ code;
2497
+ };
2498
+ var Fiber = class {
2499
+ constructor(parent, config, inject, runtime, getOuterStack) {
2500
+ this.parent = parent;
2501
+ this.inject = inject;
2502
+ this.runtime = runtime;
2503
+ const collect = (dispose) => {
2504
+ this._disposables.push(dispose);
2505
+ };
2506
+ if (runtime) {
2507
+ this.uid = parent.registry.counter;
2508
+ this.ctx = this.context = parent.extend({ fiber: this });
2509
+ const injectEntries = Object.entries(this.inject);
2510
+ if (injectEntries.length) {
2511
+ this.ctx[symbols.intercept] = Object.create(parent[symbols.intercept]);
2512
+ for (const [name2, config2] of injectEntries) {
2513
+ if (isNullable(config2)) continue;
2514
+ this.ctx[symbols.intercept][name2] = config2;
2515
+ }
2516
+ }
2517
+ this._runner = {
2518
+ epoch: INACTIVE,
2519
+ getOuterStack,
2520
+ execute: () => {
2521
+ if (isConstructor(runtime.callback)) {
2522
+ const instance = new runtime.callback(this.ctx, this.config);
2523
+ for (const hook of instance?.[symbols.initHooks] ?? []) {
2524
+ hook();
2525
+ }
2526
+ return instance?.[symbols.init]?.();
2527
+ } else {
2528
+ return runtime.callback(this.ctx, this.config);
2529
+ }
2530
+ },
2531
+ collect
2532
+ };
2533
+ this.context.emit(this, "internal/plugin");
2534
+ for (const name2 of Object.keys(this.inject)) {
2535
+ this._checkImpl(name2);
2536
+ }
2537
+ this.dispose = parent.fiber.effect(() => {
2538
+ const remove = runtime.fibers.push(this);
2539
+ try {
2540
+ this.config = resolveConfig(runtime, config);
2541
+ this._refresh();
2542
+ } catch (error) {
2543
+ this.ctx.logger.error(error);
2544
+ this._error = error;
2545
+ }
2546
+ return async () => {
2547
+ this.uid = null;
2548
+ this.context.emit(this, "internal/plugin");
2549
+ if (this.ctx.registry.has(runtime.callback)) {
2550
+ remove();
2551
+ if (!runtime.fibers.length) {
2552
+ this.ctx.registry.delete(runtime.callback);
2553
+ }
2554
+ }
2555
+ this._setEpoch(INACTIVE);
2556
+ while (this.inertia) {
2557
+ await this.inertia;
2558
+ }
2559
+ };
2560
+ }, "ctx.plugin()");
2561
+ } else {
2562
+ this.uid = 0;
2563
+ this.ctx = this.context = parent;
2564
+ this.state = 2 /* ACTIVE */;
2565
+ this.store = /* @__PURE__ */ Object.create(null);
2566
+ this._runner = {
2567
+ epoch: "",
2568
+ getOuterStack,
2569
+ execute: () => {
2570
+ },
2571
+ collect
2572
+ };
2573
+ this.dispose = () => this.restart();
2574
+ }
2575
+ }
2576
+ parent;
2577
+ inject;
2578
+ runtime;
2579
+ uid;
2580
+ ctx;
2581
+ config;
2582
+ state = 0 /* PENDING */;
2583
+ dispose;
2584
+ store;
2585
+ inertia;
2586
+ _hooks = /* @__PURE__ */ Object.create(null);
2587
+ _disposables = new DisposableList();
2588
+ context;
2589
+ _error = void 0;
2590
+ _runner;
2591
+ _store = /* @__PURE__ */ Object.create(null);
2592
+ _restarting;
2593
+ get name() {
2594
+ if (this.runtime?.name) return this.runtime.name;
2595
+ let fiber = this.parent.fiber;
2596
+ while (fiber !== fiber.parent.fiber) {
2597
+ if (fiber.runtime?.name) return fiber.runtime.name;
2598
+ fiber = fiber.parent.fiber;
2599
+ }
2600
+ return "root";
2601
+ }
2602
+ assertActive() {
2603
+ if (this.uid !== null) return;
2604
+ throw new CordisError("INACTIVE_EFFECT");
2605
+ }
2606
+ _execute(runner) {
2607
+ const oldEpoch = runner.epoch;
2608
+ return composeError((info) => {
2609
+ const safeCollect = (dispose) => {
2610
+ if (typeof dispose === "function") {
2611
+ runner.collect(dispose);
2612
+ } else if (!isNullable(dispose)) {
2613
+ throw new TypeError("Invalid effect");
2614
+ }
2615
+ };
2616
+ const effect = runner.execute.call(this);
2617
+ if (typeof effect === "function") {
2618
+ return runner.collect(effect);
2619
+ } else if (isNullable(effect)) {
2620
+ return;
2621
+ } else if (!isObject(effect)) {
2622
+ throw new TypeError("Invalid effect");
2623
+ } else if ("then" in effect) {
2624
+ return effect.then(safeCollect);
2625
+ } else if (Symbol.iterator in effect) {
2626
+ info.error = new Error();
2627
+ const iter = effect[Symbol.iterator]();
2628
+ while (true) {
2629
+ const result = iter.next();
2630
+ safeCollect(result.value);
2631
+ if (result.done) return;
2632
+ }
2633
+ } else if (Symbol.asyncIterator in effect) {
2634
+ const iter = effect[Symbol.asyncIterator]();
2635
+ return (async () => {
2636
+ await Promise.resolve();
2637
+ info.error = new Error();
2638
+ while (true) {
2639
+ if (runner.epoch !== oldEpoch) return;
2640
+ const result = await iter.next();
2641
+ safeCollect(result.value);
2642
+ if (result.done) return;
2643
+ }
2644
+ })();
2645
+ } else {
2646
+ throw new TypeError("Invalid effect");
2647
+ }
2648
+ }, runner.getOuterStack);
2649
+ }
2650
+ effect(execute, label = "anonymous") {
2651
+ this.assertActive();
2652
+ const disposables = [];
2653
+ const dispose = () => {
2654
+ let task2;
2655
+ for (const dispose2 of disposables.splice(0).reverse()) {
2656
+ if (task2) {
2657
+ task2 = task2.then(dispose2);
2658
+ } else {
2659
+ const result = dispose2();
2660
+ if (isObject(result) && "then" in result) {
2661
+ task2 = result;
2662
+ }
2663
+ }
2664
+ }
2665
+ return task2;
2666
+ };
2667
+ const meta = { label, children: [] };
2668
+ const runner = {
2669
+ execute,
2670
+ epoch: true,
2671
+ collect: (dispose2) => {
2672
+ disposables.push(dispose2);
2673
+ this._disposables.delete(dispose2);
2674
+ if (dispose2[symbols.effect]) {
2675
+ meta.children.push(dispose2[symbols.effect]);
2676
+ }
2677
+ },
2678
+ getOuterStack: buildOuterStack()
2679
+ };
2680
+ let task;
2681
+ try {
2682
+ task = this._execute(runner);
2683
+ } catch (reason) {
2684
+ dispose();
2685
+ throw reason;
2686
+ }
2687
+ task?.catch(dispose).catch((error) => this.ctx.logger.error(error));
2688
+ const wrapper = Object.defineProperty(() => {
2689
+ if (!runner.epoch) return;
2690
+ runner.epoch = false;
2691
+ return task ? task.then(dispose) : dispose();
2692
+ }, symbols.effect, { value: meta });
2693
+ const disposeAsync = () => {
2694
+ if (!runner.epoch) return;
2695
+ runner.epoch = false;
2696
+ return dispose();
2697
+ };
2698
+ wrapper.then = async (onFulfilled, onRejected) => {
2699
+ return Promise.resolve(task).then(() => disposeAsync).then(onFulfilled, onRejected);
2700
+ };
2701
+ disposables.push(this._disposables.push(wrapper));
2702
+ return wrapper;
2703
+ }
2704
+ getEffects() {
2705
+ return [...this._disposables].map((dispose) => dispose[symbols.effect]).filter(Boolean);
2706
+ }
2707
+ _getState() {
2708
+ if (this.uid === null) return 4 /* DISPOSED */;
2709
+ if (this._error) return 3 /* FAILED */;
2710
+ if (this._runner.epoch !== INACTIVE) return 2 /* ACTIVE */;
2711
+ return 0 /* PENDING */;
2712
+ }
2713
+ _updateState(callback) {
2714
+ const oldState = this.state;
2715
+ this.state = callback?.() ?? this._getState();
2716
+ if (oldState === this.state) return;
2717
+ this.context.emit(this, "internal/status", oldState);
2718
+ if (oldState !== 2 /* ACTIVE */ && this.state !== 2 /* ACTIVE */) return;
2719
+ for (const key of Reflect.ownKeys(this.ctx.reflect.store)) {
2720
+ const impl = this.ctx.reflect.store[key];
2721
+ if (!impl) continue;
2722
+ if (impl.fiber !== this) continue;
2723
+ this.ctx.reflect.notify([impl.name]);
2724
+ }
2725
+ }
2726
+ _checkImpl(name2) {
2727
+ const impl = this.ctx.reflect._getImpl(name2, true);
2728
+ if (!impl) return delete this._store[name2];
2729
+ try {
2730
+ if (impl.check && !impl.check.call(getTraceable(this.ctx, impl.value))) {
2731
+ return delete this._store[name2];
2732
+ }
2733
+ } catch (error) {
2734
+ impl.fiber.ctx.logger.error(error);
2735
+ return delete this._store[name2];
2736
+ }
2737
+ this._store[name2] = impl;
2738
+ }
2739
+ _refresh() {
2740
+ let epoch = "";
2741
+ for (const name2 of Object.keys(this.inject)) {
2742
+ const impl = this._store[name2];
2743
+ if (!impl) {
2744
+ epoch = INACTIVE;
2745
+ break;
2746
+ }
2747
+ epoch += ":" + impl.fiber.uid;
2748
+ }
2749
+ this._setEpoch(epoch);
2750
+ }
2751
+ _setEpoch(epoch) {
2752
+ const oldEpoch = this._runner.epoch;
2753
+ if (epoch === oldEpoch) return;
2754
+ this._runner.epoch = epoch;
2755
+ if (this.inertia) return;
2756
+ this._updateState(() => {
2757
+ if (epoch !== INACTIVE && oldEpoch === INACTIVE) {
2758
+ this.inertia = this._reload();
2759
+ return 1 /* LOADING */;
2760
+ } else {
2761
+ this.inertia = this._unload();
2762
+ return 5 /* UNLOADING */;
2763
+ }
2764
+ });
2765
+ }
2766
+ async _reload() {
2767
+ this.store = { ...this._store };
2768
+ const oldEpoch = this._runner.epoch;
2769
+ try {
2770
+ await Promise.resolve();
2771
+ await this._execute(this._runner);
2772
+ } catch (reason) {
2773
+ this.ctx.logger.error(reason);
2774
+ this._error = reason;
2775
+ this._runner.epoch = INACTIVE;
2776
+ }
2777
+ this._updateState(() => {
2778
+ if (this._runner.epoch === oldEpoch) {
2779
+ this.inertia = void 0;
2780
+ } else {
2781
+ this.inertia = this._unload();
2782
+ return 5 /* UNLOADING */;
2783
+ }
2784
+ });
2785
+ }
2786
+ async _unload() {
2787
+ await Promise.all(this._disposables.clear().map(async (dispose) => {
2788
+ try {
2789
+ await composeError(async (info) => {
2790
+ await Promise.resolve();
2791
+ info.error = new Error();
2792
+ await dispose();
2793
+ }, this._runner.getOuterStack);
2794
+ } catch (reason) {
2795
+ this.ctx.logger.error(reason);
2796
+ }
2797
+ }));
2798
+ this.store = void 0;
2799
+ this._updateState(() => {
2800
+ if (this._runner.epoch === INACTIVE) {
2801
+ this.inertia = void 0;
2802
+ } else {
2803
+ this.inertia = this._reload();
2804
+ return 1 /* LOADING */;
2805
+ }
2806
+ });
2807
+ }
2808
+ async await() {
2809
+ while (this.inertia || this._restarting) {
2810
+ await Promise.all([this.inertia, this._restarting].filter((task) => !!task));
2811
+ }
2812
+ if (this._error) throw this._error;
2813
+ return this;
2814
+ }
2815
+ async restart() {
2816
+ const fiber = this.ctx.fiber;
2817
+ fiber.assertActive();
2818
+ if (fiber._restarting) return fiber._restarting.then(() => void 0);
2819
+ let resolve2;
2820
+ const restarting = new Promise((resolvePromise) => {
2821
+ resolve2 = resolvePromise;
2822
+ });
2823
+ fiber._restarting = restarting;
2824
+ try {
2825
+ await this._runRestart(fiber);
2826
+ } finally {
2827
+ fiber._restarting = void 0;
2828
+ resolve2();
2829
+ }
2830
+ }
2831
+ async _runRestart(fiber) {
2832
+ if (fiber.inertia) {
2833
+ fiber._setEpoch(INACTIVE);
2834
+ while (fiber.inertia) await fiber.inertia;
2835
+ }
2836
+ fiber._setEpoch(INACTIVE);
2837
+ fiber._refresh();
2838
+ while (fiber.inertia) await fiber.inertia;
2839
+ }
2840
+ update(config, noSave = false) {
2841
+ const fiber = this.ctx.fiber;
2842
+ fiber.assertActive();
2843
+ config = resolveConfig(fiber.runtime, config);
2844
+ fiber.context.waterfall(fiber.context, "internal/update", config, noSave, () => {
2845
+ fiber.config = config;
2846
+ fiber._error = void 0;
2847
+ return fiber.restart();
2848
+ });
2849
+ }
2850
+ };
2851
+
2852
+ // packages/core/src/logger.ts
2853
+ var Logger = class {
2854
+ constructor(options, service) {
2855
+ this.service = service;
2856
+ this.name = options.name;
2857
+ this.level = options.level;
2858
+ this.error = this._method("error");
2859
+ this.info = this._method("info");
2860
+ this.warn = this._method("warn");
2861
+ this.debug = this._method("debug");
2862
+ }
2863
+ service;
2864
+ name;
2865
+ level;
2866
+ error;
2867
+ info;
2868
+ warn;
2869
+ debug;
2870
+ _method(type) {
2871
+ return (...args) => this.service._log({ name: this.name, type, args });
2872
+ }
2873
+ };
2874
+ var LoggerService = class {
2875
+ buffer = [];
2876
+ ctx;
2877
+ _snMessage = 0;
2878
+ _snExporter = 0;
2879
+ exporters = /* @__PURE__ */ new Map();
2880
+ constructor(ctx) {
2881
+ const tracker = {
2882
+ property: "ctx",
2883
+ noShadow: true
2884
+ };
2885
+ const self = createCallable("logger", joinPrototype(Object.getPrototypeOf(this), Function.prototype), tracker);
2886
+ Object.assign(self, this);
2887
+ self.ctx = ctx;
2888
+ Object.defineProperty(self, symbols.tracker, { value: tracker });
2889
+ self.exporter({
2890
+ export: (message) => {
2891
+ self.buffer.push(message);
2892
+ }
2893
+ });
2894
+ ctx.reflect.provide("logger", self);
2895
+ return self;
2896
+ }
2897
+ exporter(exporter) {
2898
+ return this.ctx.fiber.effect(() => {
2899
+ const id = ++this._snExporter;
2900
+ this.exporters.set(id, exporter);
2901
+ return () => this.exporters.delete(id);
2902
+ }, "ctx.logger.exporter()");
2903
+ }
2904
+ [symbols.invoke](name2) {
2905
+ const caller = this[symbols.caller];
2906
+ const fiber = (caller ?? this.ctx).fiber;
2907
+ return new Logger({
2908
+ name: name2 ?? fiber.name
2909
+ }, this);
2910
+ }
2911
+ _log(message) {
2912
+ const sn = ++this._snMessage;
2913
+ const ts = Date.now();
2914
+ for (const exporter of this.exporters.values()) {
2915
+ if (exporter.level != null && exporter.level < 0) continue;
2916
+ exporter.export({ sn, ts, ...message });
2917
+ }
2918
+ }
2919
+ error(...args) {
2920
+ return this().error(...args);
2921
+ }
2922
+ info(...args) {
2923
+ return this().info(...args);
2924
+ }
2925
+ warn(...args) {
2926
+ return this().warn(...args);
2927
+ }
2928
+ debug(...args) {
2929
+ return this().debug(...args);
2930
+ }
2931
+ };
2932
+
2933
+ // packages/core/src/reflect.ts
2934
+ function enhanceError(error) {
2935
+ const lines = error.stack.split("\n");
2936
+ lines.splice(0, 2, `Error: ${error.message}`);
2937
+ error.stack = lines.join("\n");
2938
+ return error;
2939
+ }
2940
+ var RESERVED_WORDS = ["prototype", "then"];
2941
+ function isSpecialProperty(prop) {
2942
+ return typeof prop === "symbol" || RESERVED_WORDS.includes(prop) || parseInt(prop).toString() === prop || prop.startsWith("_");
2943
+ }
2944
+ var ReflectService = class {
2945
+ constructor(ctx) {
2946
+ this.ctx = ctx;
2947
+ Object.defineProperty(this, symbols.tracker, {
2948
+ value: { property: "ctx", noShadow: true }
2949
+ });
2950
+ this.mixin("reflect", ["get", "set", "provide", "accessor", "mixin"]);
2951
+ this.mixin("fiber", ["runtime", "effect"]);
2952
+ this.mixin("registry", ["inject", "plugin"]);
2953
+ this.mixin("events", [
2954
+ "on",
2955
+ "once",
2956
+ "parallel",
2957
+ "emit",
2958
+ "serial",
2959
+ "bail",
2960
+ "waterfall",
2961
+ "waterfallAsync"
2962
+ ]);
2963
+ }
2964
+ ctx;
2965
+ static handler = {
2966
+ get: (target, prop, ctx) => {
2967
+ if (isSpecialProperty(prop)) {
2968
+ return Reflect.get(target, prop, ctx);
2969
+ }
2970
+ if (Reflect.has(target, prop)) {
2971
+ return getTraceable(ctx, Reflect.get(target, prop, ctx));
2972
+ }
2973
+ const error = new Error(`cannot get property "${String(prop)}" without inject`);
2974
+ try {
2975
+ const def = target.reflect.props[prop];
2976
+ if (def?.type === "accessor") {
2977
+ return def.get.call(ctx, ctx[symbols.receiver], error);
2978
+ }
2979
+ if (!ctx.fiber.runtime) return ctx.reflect.get(prop, false);
2980
+ return ctx.events.waterfall(ctx, "internal/get", prop, error, () => {
2981
+ const key = target[symbols.isolate][prop];
2982
+ let fiber = (ctx[symbols.shadow] ?? ctx).fiber;
2983
+ while (true) {
2984
+ const impl = fiber.store?.[prop];
2985
+ if (impl) return getTraceable(ctx, impl.value);
2986
+ if (prop in fiber.inject) {
2987
+ error.message = `cannot get required service "${String(prop)}" in inactive context`;
2988
+ throw error;
2989
+ }
2990
+ if (!fiber.runtime) throw error;
2991
+ if (fiber.parent[symbols.isolate][prop] !== key) throw error;
2992
+ fiber = fiber.parent.fiber;
2993
+ }
2994
+ });
2995
+ } catch (e) {
2996
+ throw e === error ? enhanceError(e) : e;
2997
+ }
2998
+ },
2999
+ set: (target, prop, value, ctx) => {
3000
+ if (isSpecialProperty(prop)) {
3001
+ return Reflect.set(target, prop, value, ctx);
3002
+ }
3003
+ const error = new Error(`cannot set property "${String(prop)}" without provide`);
3004
+ const def = target.reflect.props[prop];
3005
+ try {
3006
+ if (def?.type === "accessor") {
3007
+ if (!def.set) return false;
3008
+ return def.set.call(ctx, value, ctx[symbols.receiver], error);
3009
+ }
3010
+ if (!def && !ctx.fiber.runtime) {
3011
+ return Reflect.set(target, prop, value, ctx);
3012
+ }
3013
+ return ctx.events.waterfall(ctx, "internal/set", prop, value, error, () => {
3014
+ if (!def) throw error;
3015
+ return ctx.reflect.set(prop, value);
3016
+ });
3017
+ } catch (e) {
3018
+ throw e === error ? enhanceError(e) : e;
3019
+ }
3020
+ },
3021
+ has: (target, prop) => {
3022
+ if (isSpecialProperty(prop)) {
3023
+ return Reflect.has(target, prop);
3024
+ }
3025
+ if (Reflect.has(target, prop)) return true;
3026
+ return !!target.reflect.props[prop];
3027
+ }
3028
+ };
3029
+ store = /* @__PURE__ */ Object.create(null);
3030
+ props = /* @__PURE__ */ Object.create(null);
3031
+ get(name2, strict = true) {
3032
+ return getTraceable(this.ctx, this._getImpl(name2, strict)?.value);
3033
+ }
3034
+ _getImpl(name2, strict = true) {
3035
+ const key = this.ctx[symbols.isolate][name2];
3036
+ const impl = key && this.store[key];
3037
+ if (!impl) return;
3038
+ if (strict && impl.fiber.state !== 2 /* ACTIVE */) return;
3039
+ return impl;
3040
+ }
3041
+ set(name2, value) {
3042
+ const key = this.ctx[symbols.isolate][name2];
3043
+ const impl = key && this.store[key];
3044
+ if (!impl) {
3045
+ throw new Error(`cannot set property "${name2}" without provide`);
3046
+ }
3047
+ if (impl.fiber !== this.ctx.fiber) {
3048
+ throw new Error(`cannot set property "${name2}" in multiple fibers`);
3049
+ }
3050
+ impl.value = value;
3051
+ return true;
3052
+ }
3053
+ provide(name2, value, check) {
3054
+ return this.ctx.fiber.effect(() => {
3055
+ if (!this.props[name2]) {
3056
+ this.props[name2] = { type: "service" };
3057
+ } else if (this.props[name2].type !== "service") {
3058
+ throw new Error(`property "${name2}" is already declared as ${this.props[name2].type}`);
3059
+ }
3060
+ this.props[name2] = { type: "service" };
3061
+ this.ctx.root[symbols.isolate][name2] ??= Symbol(name2);
3062
+ const key = this.ctx[symbols.isolate][name2];
3063
+ const impl = {
3064
+ name: name2,
3065
+ value,
3066
+ fiber: this.ctx.fiber,
3067
+ ...check ? { check } : {}
3068
+ };
3069
+ if (key && this.store[key]) {
3070
+ throw new Error(`service "${name2}" has been registered at <${this.store[key].fiber.name}>`);
3071
+ }
3072
+ this.store[key] = impl;
3073
+ this.ctx.fiber.store[name2] = impl;
3074
+ if (this.ctx.fiber.state === 2 /* ACTIVE */) {
3075
+ this.notify([name2]);
3076
+ }
3077
+ return async () => {
3078
+ delete this.store[key];
3079
+ const fibers = this.notify([name2]);
3080
+ await Promise.allSettled(fibers.map((fiber) => fiber.await()));
3081
+ delete this.ctx.fiber.store[name2];
3082
+ };
3083
+ }, `ctx.provide(${JSON.stringify(name2)})`);
3084
+ }
3085
+ notify(names, filter = (ctx, name2) => ctx[symbols.isolate][name2] === this.ctx[symbols.isolate][name2]) {
3086
+ const fibers = [];
3087
+ for (const runtime of this.ctx.registry.values()) {
3088
+ for (const fiber of runtime.fibers) {
3089
+ let hasUpdate = false;
3090
+ for (const name2 of names) {
3091
+ if (!(name2 in fiber.inject)) continue;
3092
+ if (!filter(fiber.ctx, name2)) continue;
3093
+ hasUpdate = true;
3094
+ fiber._checkImpl(name2);
3095
+ }
3096
+ if (!hasUpdate) continue;
3097
+ fiber._refresh();
3098
+ fibers.push(fiber);
3099
+ }
3100
+ }
3101
+ for (const name2 of names) {
3102
+ const self = Object.create(this.ctx);
3103
+ self[symbols.filter] = (target) => filter(target, name2);
3104
+ this.ctx.events.emit(self, "internal/service", name2, this._getImpl(name2, false)?.value);
3105
+ }
3106
+ return fibers;
3107
+ }
3108
+ accessor(name2, options) {
3109
+ return this.ctx.fiber.effect(() => {
3110
+ if (name2 in this.props) {
3111
+ throw new Error(`property "${name2}" is already declared as ${this.props[name2].type}`);
3112
+ }
3113
+ this.props[name2] = { type: "accessor", ...options };
3114
+ return () => delete this.props[name2];
3115
+ }, `ctx.accessor(${JSON.stringify(name2)})`);
3116
+ }
3117
+ mixin(source, mixins) {
3118
+ return this.ctx.fiber.effect(() => {
3119
+ const entries = Array.isArray(mixins) ? mixins.map((key) => [key, key]) : Object.entries(mixins);
3120
+ const getTarget = (target) => target[source];
3121
+ return entries.map(([key, value]) => {
3122
+ return this.accessor(value, {
3123
+ get(receiver) {
3124
+ const service = getTarget(this);
3125
+ if (service == null) return service;
3126
+ const mixin = receiver ? withProps(receiver, service) : service;
3127
+ const result = Reflect.get(service, key, mixin);
3128
+ if (typeof result !== "function") return result;
3129
+ return result.bind(mixin ?? service);
3130
+ },
3131
+ set(value2, receiver) {
3132
+ const service = getTarget(this);
3133
+ const mixin = receiver ? withProps(receiver, service) : service;
3134
+ return Reflect.set(service, key, value2, mixin);
3135
+ }
3136
+ });
3137
+ });
3138
+ }, `ctx.mixin(${JSON.stringify(source)})`);
3139
+ }
3140
+ trace(value) {
3141
+ return getTraceable(this.ctx, value);
3142
+ }
3143
+ bind(callback) {
3144
+ return new Proxy(callback, {
3145
+ apply: (target, thisArg, args) => {
3146
+ return Reflect.apply(target, this.trace(thisArg), args.map((arg) => this.trace(arg)));
3147
+ },
3148
+ construct: (target, args, newTarget) => {
3149
+ return Reflect.construct(target, args.map((arg) => this.trace(arg)), newTarget);
3150
+ }
3151
+ });
3152
+ }
3153
+ };
3154
+
3155
+ // packages/core/src/registry.ts
3156
+ function isApplicable(object) {
3157
+ return object && typeof object === "object" && typeof object.apply === "function";
3158
+ }
3159
+ function resolveInject(inject, result = /* @__PURE__ */ Object.create(null)) {
3160
+ if (!inject) return result;
3161
+ if (Array.isArray(inject)) {
3162
+ for (const name2 of inject) {
3163
+ result[name2] = null;
3164
+ }
3165
+ } else if (Reflect.has(inject, symbols.checkProto)) {
3166
+ Object.assign(result, resolveInject(Object.getPrototypeOf(inject)));
3167
+ for (const name2 of Object.keys(inject)) {
3168
+ result[name2] = inject[name2] ?? null;
3169
+ }
3170
+ } else {
3171
+ for (const name2 of Object.keys(inject)) {
3172
+ result[name2] = inject[name2] ?? null;
3173
+ }
3174
+ }
3175
+ return result;
3176
+ }
3177
+ var RegistryService = class {
3178
+ constructor(ctx) {
3179
+ this.ctx = ctx;
3180
+ Object.defineProperty(this, symbols.tracker, {
3181
+ value: { property: "ctx", noShadow: true }
3182
+ });
3183
+ }
3184
+ ctx;
3185
+ _counter = 0;
3186
+ _internal = /* @__PURE__ */ new Map();
3187
+ get counter() {
3188
+ return ++this._counter;
3189
+ }
3190
+ get size() {
3191
+ return this._internal.size;
3192
+ }
3193
+ resolve(plugin) {
3194
+ try {
3195
+ if (typeof plugin === "function") return plugin;
3196
+ if (isApplicable(plugin)) return plugin.apply;
3197
+ } catch {
3198
+ }
3199
+ }
3200
+ get(plugin) {
3201
+ const key = this.resolve(plugin);
3202
+ return key && this._internal.get(key);
3203
+ }
3204
+ has(plugin) {
3205
+ const key = this.resolve(plugin);
3206
+ return !!key && this._internal.has(key);
3207
+ }
3208
+ delete(plugin) {
3209
+ const key = this.resolve(plugin);
3210
+ const runtime = key && this._internal.get(key);
3211
+ if (!runtime) return;
3212
+ this._internal.delete(key);
3213
+ for (const fiber of [...runtime.fibers]) {
3214
+ fiber.dispose();
3215
+ }
3216
+ return runtime;
3217
+ }
3218
+ keys() {
3219
+ return this._internal.keys();
3220
+ }
3221
+ values() {
3222
+ return this._internal.values();
3223
+ }
3224
+ entries() {
3225
+ return this._internal.entries();
3226
+ }
3227
+ forEach(callback) {
3228
+ return this._internal.forEach(callback);
3229
+ }
3230
+ inject(inject, callback) {
3231
+ const name2 = callback.name || void 0;
3232
+ return this.plugin(Object.assign(
3233
+ { inject, apply: callback },
3234
+ name2 ? { name: name2 } : {}
3235
+ ));
3236
+ }
3237
+ plugin(plugin, config, getOuterStack = buildOuterStack()) {
3238
+ const callback = this.resolve(plugin);
3239
+ if (!callback) throw new Error('invalid plugin, expect function or object with an "apply" method, received ' + typeof plugin);
3240
+ this.ctx.fiber.assertActive();
3241
+ let runtime = this._internal.get(callback);
3242
+ if (!runtime) {
3243
+ let name2 = plugin.name;
3244
+ if (name2 === "apply") name2 = void 0;
3245
+ runtime = {
3246
+ callback,
3247
+ fibers: new DisposableList(),
3248
+ ...name2 ? { name: name2 } : {},
3249
+ ...plugin.Config ? { Config: plugin.Config } : {}
3250
+ };
3251
+ this._internal.set(callback, runtime);
3252
+ }
3253
+ const fiber = new Fiber(this.ctx, config, resolveInject(plugin.inject), runtime, getOuterStack);
3254
+ const wrapped = Object.create(fiber);
3255
+ wrapped.then = (onFulfilled, onRejected) => {
3256
+ return fiber.await().then(onFulfilled, onRejected);
3257
+ };
3258
+ return wrapped;
3259
+ }
3260
+ };
3261
+
3262
+ // packages/core/src/context.ts
3263
+ var Context = class _Context {
3264
+ static effect = symbols.effect;
3265
+ static filter = symbols.filter;
3266
+ static isolate = symbols.isolate;
3267
+ static intercept = symbols.intercept;
3268
+ static marker = /* @__PURE__ */ Symbol.for("tnega.is");
3269
+ static is(value) {
3270
+ return !!value?.[_Context.marker];
3271
+ }
3272
+ static {
3273
+ _Context.prototype[_Context.marker] = true;
3274
+ }
3275
+ constructor() {
3276
+ this[symbols.isolate] = /* @__PURE__ */ Object.create(null);
3277
+ this[symbols.intercept] = /* @__PURE__ */ Object.create(null);
3278
+ const self = new Proxy(this, ReflectService.handler);
3279
+ this.root = self;
3280
+ this.baseUrl = void 0;
3281
+ this.fiber = new Fiber(self, {}, /* @__PURE__ */ Object.create(null), null, () => []);
3282
+ this.reflect = new ReflectService(self);
3283
+ this.registry = new RegistryService(self);
3284
+ this.events = new EventsService(self);
3285
+ this.logger = new LoggerService(self);
3286
+ this.fiber._disposables.clear();
3287
+ return self;
3288
+ }
3289
+ [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
3290
+ return `Context <${this.fiber.name}>`;
3291
+ }
3292
+ extend(meta = {}) {
3293
+ const shadow = Reflect.getOwnPropertyDescriptor(this, symbols.shadow)?.value;
3294
+ const self = Object.create(getTraceable(this, this));
3295
+ for (const prop of Reflect.ownKeys(meta)) {
3296
+ Object.defineProperty(self, prop, Reflect.getOwnPropertyDescriptor(meta, prop));
3297
+ }
3298
+ if (!shadow) return self;
3299
+ return Object.assign(Object.create(self), { [symbols.shadow]: shadow });
3300
+ }
3301
+ isolate(name2, label) {
3302
+ const shadow = Object.create(this[symbols.isolate]);
3303
+ shadow[name2] = label ?? Symbol(name2);
3304
+ return this.extend({ [symbols.isolate]: shadow });
3305
+ }
3306
+ intercept(name2, config) {
3307
+ const intercept = Object.create(this[symbols.intercept]);
3308
+ intercept[name2] = config;
3309
+ return this.extend({ [symbols.intercept]: intercept });
3310
+ }
3311
+ };
3312
+
3313
+ // packages/core/src/service.ts
3314
+ var Service = class _Service {
3315
+ constructor(ctx, name2) {
3316
+ this.ctx = ctx;
3317
+ name2 ??= this.constructor.provide;
3318
+ const tracker = {
3319
+ associate: name2,
3320
+ property: "ctx"
3321
+ };
3322
+ const self = this[symbols.invoke] ? createCallable(name2, joinPrototype(Object.getPrototypeOf(this), Function.prototype), tracker) : this;
3323
+ self.ctx = ctx;
3324
+ self.name = name2;
3325
+ Object.defineProperty(self, symbols.tracker, { value: tracker });
3326
+ self.ctx.reflect.provide(name2, self, this[symbols.check]);
3327
+ return self;
3328
+ }
3329
+ ctx;
3330
+ static init = symbols.init;
3331
+ static check = symbols.check;
3332
+ static config = symbols.config;
3333
+ static invoke = symbols.invoke;
3334
+ static extend = symbols.extend;
3335
+ static tracker = symbols.tracker;
3336
+ static resolveConfig = symbols.resolveConfig;
3337
+ name;
3338
+ [symbols.filter](ctx) {
3339
+ return ctx[symbols.isolate][this.name] === this.ctx[symbols.isolate][this.name];
3340
+ }
3341
+ [symbols.extend](props) {
3342
+ let self;
3343
+ if (this[_Service.invoke]) {
3344
+ self = createCallable(this.name, this, this[symbols.tracker]);
3345
+ } else {
3346
+ self = Object.create(this);
3347
+ }
3348
+ return Object.assign(self, props);
3349
+ }
3350
+ [symbols.resolveConfig](base, head) {
3351
+ let intercept = this.ctx[symbols.intercept];
3352
+ const configs = [];
3353
+ while (this.name in intercept) {
3354
+ if (Object.hasOwn(intercept, this.name)) {
3355
+ configs.unshift(intercept[this.name]);
3356
+ }
3357
+ intercept = Object.getPrototypeOf(intercept);
3358
+ }
3359
+ if (base) configs.unshift(base);
3360
+ if (head) configs.push(head);
3361
+ if (this["Config"]?.merge) {
3362
+ return this["Config"].merge(...configs);
3363
+ } else {
3364
+ return Object.assign({}, ...configs);
3365
+ }
3366
+ }
3367
+ static [Symbol.hasInstance](instance) {
3368
+ if (!instance) return false;
3369
+ let constructor = instance.constructor;
3370
+ while (constructor) {
3371
+ constructor = constructor.prototype?.constructor;
3372
+ if (constructor === this) return true;
3373
+ constructor &&= Object.getPrototypeOf(constructor);
3374
+ }
3375
+ return false;
3376
+ }
3377
+ };
3378
+
3379
+ // packages/agent/src/inbox-durable.ts
3380
+ import { randomUUID as randomUUID3 } from "node:crypto";
3381
+ function contentOf(input) {
3382
+ return input.text ?? "";
3383
+ }
3384
+ function payloadOf(input) {
3385
+ return input.content !== void 0 ? input.content : void 0;
3386
+ }
3387
+ var DurableInbox = class _DurableInbox {
3388
+ constructor(_session) {
3389
+ this._session = _session;
3390
+ }
3391
+ _session;
3392
+ _nextTurn = [];
3393
+ _nextStep = [];
3394
+ _operationTail = Promise.resolve();
3395
+ get size() {
3396
+ return this._nextTurn.length + this._nextStep.length;
3397
+ }
3398
+ async insert(input, target = "next-turn") {
3399
+ return this._run(async () => {
3400
+ const message = {
3401
+ id: randomUUID3(),
3402
+ ...input.text !== void 0 ? { text: input.text } : {},
3403
+ ...input.content !== void 0 ? { content: input.content } : {}
3404
+ };
3405
+ const list = target === "next-step" ? this._nextStep : this._nextTurn;
3406
+ const index = list.length;
3407
+ await this._session.append("agent/inbox/spliced", {
3408
+ target,
3409
+ index,
3410
+ inserted: [{
3411
+ id: message.id,
3412
+ content: contentOf(message),
3413
+ ...payloadOf(message) !== void 0 ? { payload: payloadOf(message) } : {}
3414
+ }]
3415
+ });
3416
+ list.push(message);
3417
+ return message;
3418
+ });
3419
+ }
3420
+ async steer(input) {
3421
+ return this._run(async () => {
3422
+ const message = {
3423
+ id: randomUUID3(),
3424
+ ...input.text !== void 0 ? { text: input.text } : {},
3425
+ ...input.content !== void 0 ? { content: input.content } : {}
3426
+ };
3427
+ await this._session.append("agent/inbox/spliced", {
3428
+ target: "next-step",
3429
+ index: this._nextStep.length,
3430
+ inserted: [{
3431
+ id: message.id,
3432
+ content: contentOf(message),
3433
+ ...payloadOf(message) !== void 0 ? { payload: payloadOf(message) } : {},
3434
+ mode: "steer"
3435
+ }]
3436
+ });
3437
+ this._nextStep.push(message);
3438
+ return message;
3439
+ });
3440
+ }
3441
+ async claim() {
3442
+ return this._run(async () => {
3443
+ const steered = this._nextStep[0];
3444
+ if (steered) {
3445
+ await this._session.append("agent/inbox/spliced", {
3446
+ target: "next-step",
3447
+ index: 0,
3448
+ deleteCount: 1
3449
+ });
3450
+ this._nextStep.shift();
3451
+ return steered;
3452
+ }
3453
+ const next = this._nextTurn[0];
3454
+ if (next) {
3455
+ await this._session.append("agent/inbox/spliced", {
3456
+ target: "next-turn",
3457
+ index: 0,
3458
+ deleteCount: 1
3459
+ });
3460
+ this._nextTurn.shift();
3461
+ return next;
3462
+ }
3463
+ return void 0;
3464
+ });
3465
+ }
3466
+ /**
3467
+ * Claim the complete proposed step batch: every pending next-step input plus
3468
+ * one next-turn message at a turn boundary. Returns an empty array when
3469
+ * nothing is pending.
3470
+ */
3471
+ async claimBatch() {
3472
+ return this._run(async () => {
3473
+ const claimed = [...this._nextStep];
3474
+ const nextTurn = this._nextTurn[0];
3475
+ if (nextTurn) claimed.push(nextTurn);
3476
+ if (!claimed.length) return [];
3477
+ const deleteCounts = { nextStep: this._nextStep.length, nextTurn: nextTurn ? 1 : 0 };
3478
+ await this._session.append("agent/inbox/spliced", { target: "all", deleteCounts });
3479
+ this._nextStep.splice(0, deleteCounts.nextStep);
3480
+ this._nextTurn.splice(0, deleteCounts.nextTurn);
3481
+ return claimed;
3482
+ });
3483
+ }
3484
+ /** Claim every input waiting for the next step, without consuming a turn. */
3485
+ async claimNextStep() {
3486
+ return this._run(async () => {
3487
+ if (!this._nextStep.length) return [];
3488
+ const claimed = [...this._nextStep];
3489
+ await this._session.append("agent/inbox/spliced", {
3490
+ target: "next-step",
3491
+ index: 0,
3492
+ deleteCount: claimed.length
3493
+ });
3494
+ this._nextStep.splice(0, claimed.length);
3495
+ return claimed;
3496
+ });
3497
+ }
3498
+ async clear() {
3499
+ return this._run(async () => {
3500
+ if (!this._nextTurn.length && !this._nextStep.length) return;
3501
+ await this._session.append("agent/inbox/spliced", {
3502
+ target: "all"
3503
+ });
3504
+ this._nextTurn = [];
3505
+ this._nextStep = [];
3506
+ });
3507
+ }
3508
+ /** Insert a message at an explicit target/index boundary. */
3509
+ async insertAt(input, target, index, mode = "followup") {
3510
+ return this._run(async () => {
3511
+ const message = {
3512
+ id: randomUUID3(),
3513
+ ...input.text !== void 0 ? { text: input.text } : {},
3514
+ ...input.content !== void 0 ? { content: input.content } : {}
3515
+ };
3516
+ const list = target === "next-step" ? this._nextStep : this._nextTurn;
3517
+ const safeIndex = Math.max(0, Math.min(index, list.length));
3518
+ await this._session.append("agent/inbox/spliced", {
3519
+ target,
3520
+ index: safeIndex,
3521
+ inserted: [{
3522
+ id: message.id,
3523
+ content: contentOf(message),
3524
+ ...payloadOf(message) !== void 0 ? { payload: payloadOf(message) } : {},
3525
+ mode
3526
+ }]
3527
+ });
3528
+ list.splice(safeIndex, 0, message);
3529
+ return message;
3530
+ });
3531
+ }
3532
+ /**
3533
+ * Replace one pending message by id. The old message is emitted as
3534
+ * discarded and the new one as inserted, matching the DSH inbox contract.
3535
+ */
3536
+ async replace(messageId, input) {
3537
+ return this._run(async () => {
3538
+ const entry = this._find(messageId);
3539
+ if (!entry) return void 0;
3540
+ const replacement = {
3541
+ id: randomUUID3(),
3542
+ ...input.text !== void 0 ? { text: input.text } : {},
3543
+ ...input.content !== void 0 ? { content: input.content } : {}
3544
+ };
3545
+ const target = entry.target;
3546
+ const index = entry.index;
3547
+ await this._session.append("agent/inbox/spliced", {
3548
+ target,
3549
+ index,
3550
+ deleteCount: 1,
3551
+ inserted: [{
3552
+ id: replacement.id,
3553
+ content: contentOf(replacement),
3554
+ ...payloadOf(replacement) !== void 0 ? { payload: payloadOf(replacement) } : {}
3555
+ }]
3556
+ });
3557
+ entry.list.splice(index, 1, replacement);
3558
+ return replacement;
3559
+ });
3560
+ }
3561
+ /** Remove one pending message by id. */
3562
+ async remove(messageId) {
3563
+ return this._run(async () => {
3564
+ const entry = this._find(messageId);
3565
+ if (!entry) return void 0;
3566
+ await this._session.append("agent/inbox/spliced", {
3567
+ target: entry.target,
3568
+ index: entry.index,
3569
+ deleteCount: 1
3570
+ });
3571
+ entry.list.splice(entry.index, 1);
3572
+ return entry.message;
3573
+ });
3574
+ }
3575
+ /** Return the first pending message by id across both queues. */
3576
+ get(messageId) {
3577
+ return this._find(messageId)?.message;
3578
+ }
3579
+ snapshot() {
3580
+ return {
3581
+ nextTurn: [...this._nextTurn],
3582
+ nextStep: [...this._nextStep]
3583
+ };
3584
+ }
3585
+ /** Keep the entire read → append → apply transition ordered, including claims. */
3586
+ _run(operation) {
3587
+ const result = this._operationTail.then(operation);
3588
+ this._operationTail = result.then(() => void 0, () => void 0);
3589
+ return result;
3590
+ }
3591
+ _find(messageId) {
3592
+ for (const target of ["next-step", "next-turn"]) {
3593
+ const list = target === "next-step" ? this._nextStep : this._nextTurn;
3594
+ const index = list.findIndex((message) => message.id === messageId);
3595
+ if (index >= 0) return { list, target, index, message: list[index] };
3596
+ }
3597
+ return void 0;
2040
3598
  }
2041
3599
  static async restore(session) {
2042
3600
  const inbox = new _DurableInbox(session);
@@ -2047,6 +3605,11 @@ var DurableInbox = class _DurableInbox {
2047
3605
  for (const event of events) {
2048
3606
  if (event.type !== "agent/inbox/spliced") continue;
2049
3607
  const payload = event.payload;
3608
+ if (payload.target === "all") {
3609
+ this._nextTurn.splice(0, payload.deleteCounts?.nextTurn ?? this._nextTurn.length);
3610
+ this._nextStep.splice(0, payload.deleteCounts?.nextStep ?? this._nextStep.length);
3611
+ continue;
3612
+ }
2050
3613
  const list = payload.target === "next-step" ? this._nextStep : this._nextTurn;
2051
3614
  const count = payload.deleteCount ?? 0;
2052
3615
  if (count === Number.POSITIVE_INFINITY) {
@@ -2076,8 +3639,29 @@ var DurableInbox = class _DurableInbox {
2076
3639
  };
2077
3640
 
2078
3641
  // packages/agent/src/live.ts
3642
+ function createAgentRuntimeContext(agentCtx) {
3643
+ const runtimeCtx = agentCtx.extend();
3644
+ Object.defineProperty(runtimeCtx, Context.filter, {
3645
+ value: (target) => {
3646
+ const targetScope = target[symbols.isolate].agentScope;
3647
+ let ancestor = agentCtx;
3648
+ while (true) {
3649
+ if (targetScope === ancestor[symbols.isolate].agentScope) return true;
3650
+ if (ancestor.fiber.parent.fiber === ancestor.fiber) return false;
3651
+ ancestor = ancestor.fiber.parent;
3652
+ }
3653
+ }
3654
+ });
3655
+ const events = agentCtx.events;
3656
+ for (const method of ["emit", "parallel", "serial", "bail", "waterfall", "waterfallAsync"]) {
3657
+ Object.defineProperty(runtimeCtx, method, {
3658
+ value: (...args) => events[method](runtimeCtx, ...args)
3659
+ });
3660
+ }
3661
+ return runtimeCtx;
3662
+ }
2079
3663
  var LiveAgentImpl = class {
2080
- constructor(_ctx, _agentCtx, _service, id, inbox, session, durable, meta, manualStreaming = false) {
3664
+ constructor(_ctx, _agentCtx, _service, id, inbox, session, durable, meta, manualStreaming = false, wakeReserved = false) {
2081
3665
  this._ctx = _ctx;
2082
3666
  this._agentCtx = _agentCtx;
2083
3667
  this._service = _service;
@@ -2087,6 +3671,7 @@ var LiveAgentImpl = class {
2087
3671
  this.meta = meta;
2088
3672
  this._durable = durable;
2089
3673
  this._manualStreaming = manualStreaming;
3674
+ this._wakeReserved = wakeReserved;
2090
3675
  }
2091
3676
  _ctx;
2092
3677
  _agentCtx;
@@ -2108,8 +3693,9 @@ var LiveAgentImpl = class {
2108
3693
  _setupDisposers = [];
2109
3694
  _scopeClosed = false;
2110
3695
  _durable;
2111
- _agentSystem;
2112
3696
  _manualStreaming = false;
3697
+ _wakeReserved = false;
3698
+ _loop;
2113
3699
  get agentType() {
2114
3700
  return this.meta.agentType;
2115
3701
  }
@@ -2136,13 +3722,41 @@ var LiveAgentImpl = class {
2136
3722
  return this._ctx;
2137
3723
  }
2138
3724
  followup(input) {
2139
- this._send(input, "followup");
3725
+ return this._send(input, "followup");
3726
+ }
3727
+ setLoop(loop) {
3728
+ this._loop = loop;
3729
+ }
3730
+ async _closeCustomTurn(turn, run) {
3731
+ const events = await this.session.read();
3732
+ if (events.some((event) => event.type === "turn/end" && event.payload.turn === turn)) return;
3733
+ 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" };
3734
+ await this.session.append("turn/end", {
3735
+ turn,
3736
+ finishReason: run.finishReason,
3737
+ reason,
3738
+ ...run.output ? { output: run.output } : {},
3739
+ ...run.steps.length ? { steps: run.steps.length } : {}
3740
+ });
3741
+ }
3742
+ async _closeFailedCustomTurn(turn, error) {
3743
+ const events = await this.session.read();
3744
+ if (events.some((event) => event.type === "turn/end" && event.payload.turn === turn)) return;
3745
+ const failure = error instanceof Error ? { name: error.name, message: error.message, ...error.stack ? { stack: error.stack } : {} } : { name: "Error", message: String(error) };
3746
+ await this.session.append("turn/end", {
3747
+ turn,
3748
+ finishReason: "error",
3749
+ reason: { kind: "error", error: failure },
3750
+ interrupted: true,
3751
+ error: failure
3752
+ });
2140
3753
  }
2141
3754
  /** Replace one pending inbox message by durable message id. */
2142
3755
  replaceMessage(messageId, input) {
2143
- this._mutatePending(async () => {
3756
+ return this._mutatePending(async () => {
2144
3757
  const previous = this._durable.get(messageId);
2145
- if (!previous) return;
3758
+ const target = this._findTarget(messageId);
3759
+ if (!previous || !target) return;
2146
3760
  const replacement = await this._durable.replace(messageId, {
2147
3761
  text: input.text ?? "",
2148
3762
  ...input.messages !== void 0 || input.context !== void 0 ? { content: input.messages ?? input.context } : {}
@@ -2153,18 +3767,19 @@ var LiveAgentImpl = class {
2153
3767
  agent: this,
2154
3768
  message: previous
2155
3769
  });
2156
- this._ctx.emit("agent/inbox/inserted", {
3770
+ const event = {
2157
3771
  id: this.id,
2158
3772
  agent: this,
2159
- target: this._findTarget(messageId) ?? "next-turn",
3773
+ target,
2160
3774
  input: replacement,
2161
3775
  inboxSeq: this._durable.size
2162
- });
3776
+ };
3777
+ this._ctx.emit("agent/inbox/inserted", event);
2163
3778
  });
2164
3779
  }
2165
3780
  /** Remove one pending inbox message by durable message id. */
2166
3781
  removeMessage(messageId) {
2167
- this._mutatePending(async () => {
3782
+ return this._mutatePending(async () => {
2168
3783
  const removed = await this._durable.remove(messageId);
2169
3784
  if (!removed) return;
2170
3785
  this._ctx.emit("agent/inbox/discarded", {
@@ -2175,48 +3790,54 @@ var LiveAgentImpl = class {
2175
3790
  });
2176
3791
  }
2177
3792
  steer(input) {
2178
- this._send(input, "steer");
3793
+ return this._send(input, "steer");
2179
3794
  }
2180
- inject(key, value) {
2181
- if (key === "agentSystem" && typeof value === "string") {
2182
- this._agentSystem = value;
2183
- }
3795
+ inject(input) {
3796
+ return this._send(input, "inject");
2184
3797
  }
2185
3798
  send(input, options = {}) {
2186
- this._send(input, options.mode === "steer" ? "steer" : "followup");
3799
+ return this._send(input, options.mode === "steer" ? "steer" : "followup");
2187
3800
  }
2188
3801
  _send(input, target) {
2189
- if (this._disposed) throw new Error(`agent disposed: ${this.id}`);
2190
3802
  const text = input.text ?? "";
2191
3803
  const content = input.messages ?? input.context;
2192
- const task = this._writeTail.then(async () => {
2193
- const message = target === "steer" ? await this._durable.steer({ text, ...content !== void 0 ? { content } : {} }) : await this._durable.insert({ text, ...content !== void 0 ? { content } : {} });
2194
- this._ctx.emit("agent/inbox/inserted", {
3804
+ const steeredAfterAbort = target === "steer" && this._controller?.signal.aborted === true;
3805
+ return this._mutatePending(async () => {
3806
+ const message = 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");
3807
+ if (target !== "inject") this._wakeReserved = true;
3808
+ const event = {
2195
3809
  id: this.id,
3810
+ agent: this,
2196
3811
  target,
2197
3812
  input: message,
2198
3813
  inboxSeq: this._durable.size
2199
- });
2200
- }).catch((error) => {
2201
- this._ctx.emit("agent/error", { id: this.id, error });
2202
- });
2203
- this._writeTail = task;
2204
- this._pendingWrite = task;
2205
- task.finally(() => {
2206
- if (this._pendingWrite === task) this._pendingWrite = void 0;
2207
- queueMicrotask(() => this._wake());
2208
- });
3814
+ };
3815
+ this._ctx.emit("agent/inbox/inserted", event);
3816
+ }, target !== "inject");
2209
3817
  }
2210
- _mutatePending(task) {
2211
- if (this._disposed) throw new Error(`agent disposed: ${this.id}`);
2212
- const chained = this._writeTail.then(task).catch((error) => {
2213
- this._ctx.emit("agent/error", { id: this.id, error });
2214
- });
2215
- this._writeTail = chained;
2216
- this._pendingWrite = chained;
2217
- chained.finally(() => {
2218
- if (this._pendingWrite === chained) this._pendingWrite = void 0;
3818
+ _mutatePending(task, wakes = false, allowDisposed = false) {
3819
+ if (this._disposed && !allowDisposed) {
3820
+ return Promise.reject(new Error(`agent disposed: ${this.id}`));
3821
+ }
3822
+ const result = this._writeTail.then(task);
3823
+ const recovered = result.catch((error) => {
3824
+ try {
3825
+ this._ctx.emit("agent/error", { id: this.id, error });
3826
+ } catch {
3827
+ }
2219
3828
  });
3829
+ this._writeTail = recovered;
3830
+ this._pendingWrite = recovered;
3831
+ void result.then(
3832
+ () => {
3833
+ if (this._pendingWrite === recovered) this._pendingWrite = void 0;
3834
+ if (wakes) queueMicrotask(() => this._wake());
3835
+ },
3836
+ () => {
3837
+ if (this._pendingWrite === recovered) this._pendingWrite = void 0;
3838
+ }
3839
+ );
3840
+ return result;
2220
3841
  }
2221
3842
  _findTarget(messageId) {
2222
3843
  const snapshot = this._durable.snapshot();
@@ -2226,27 +3847,21 @@ var LiveAgentImpl = class {
2226
3847
  }
2227
3848
  cancel(cause, options = {}) {
2228
3849
  if (!options.keepInbox) {
2229
- const task = this._writeTail.then(async () => {
3850
+ void this._mutatePending(async () => {
2230
3851
  const before = this._durable.snapshot();
3852
+ await this._durable.clear();
2231
3853
  for (const message of [...before.nextStep, ...before.nextTurn]) {
2232
3854
  this._ctx.emit("agent/inbox/discarded", { id: this.id, message });
2233
3855
  }
2234
- await this._durable.clear();
2235
- }).catch((error) => {
2236
- this._ctx.emit("agent/error", { id: this.id, error });
2237
- });
2238
- this._writeTail = task;
2239
- this._pendingWrite = task;
2240
- task.finally(() => {
2241
- if (this._pendingWrite === task) this._pendingWrite = void 0;
2242
- });
3856
+ this._wakeReserved = false;
3857
+ }, false, true);
2243
3858
  }
2244
3859
  this._controller?.abort(cause);
2245
3860
  this._maintenance?.controller.abort(cause);
2246
3861
  }
2247
3862
  async whenIdle() {
2248
- while (this._pendingWrite) await this._pendingWrite;
2249
3863
  while (true) {
3864
+ while (this._pendingWrite) await this._pendingWrite;
2250
3865
  const maintenance = this._maintenance;
2251
3866
  if (maintenance) {
2252
3867
  await maintenance.promise.catch(() => void 0);
@@ -2257,7 +3872,7 @@ var LiveAgentImpl = class {
2257
3872
  await drain.catch(() => void 0);
2258
3873
  continue;
2259
3874
  }
2260
- if (!this._durable.size) break;
3875
+ if (!this._hasQueuedWork()) break;
2261
3876
  await new Promise((resolve2) => setTimeout(resolve2, 0));
2262
3877
  }
2263
3878
  }
@@ -2299,6 +3914,7 @@ var LiveAgentImpl = class {
2299
3914
  this._pendingWrite = void 0;
2300
3915
  await writeTail.catch(() => void 0);
2301
3916
  }
3917
+ await this.session.close();
2302
3918
  this._setStatus("idle");
2303
3919
  }
2304
3920
  /** Publish the agent lifecycle session-start notification once. */
@@ -2328,13 +3944,23 @@ var LiveAgentImpl = class {
2328
3944
  if (this._disposed || this._manualStreaming || this._drainPromise || this._busy || this._maintenanceBusy) return;
2329
3945
  const task = this._drainAll();
2330
3946
  this._drainPromise = task;
2331
- task.finally(() => {
2332
- if (this._drainPromise === task) this._drainPromise = void 0;
2333
- });
3947
+ void task.then(
3948
+ () => {
3949
+ if (this._drainPromise === task) this._drainPromise = void 0;
3950
+ if (this._hasQueuedWork()) queueMicrotask(() => this._wake());
3951
+ },
3952
+ (error) => {
3953
+ if (this._drainPromise === task) this._drainPromise = void 0;
3954
+ try {
3955
+ this._ctx.emit("agent/error", { id: this.id, agent: this, error });
3956
+ } catch {
3957
+ }
3958
+ }
3959
+ );
2334
3960
  }
2335
3961
  async _drainAll() {
2336
3962
  await this._writeTail.catch(() => void 0);
2337
- if (this._durable.size) {
3963
+ if (this._hasQueuedWork()) {
2338
3964
  for await (const _ of this._streamTurns()) void _;
2339
3965
  }
2340
3966
  }
@@ -2350,10 +3976,13 @@ var LiveAgentImpl = class {
2350
3976
  this._setStatus("running");
2351
3977
  try {
2352
3978
  while (!this._disposed) {
2353
- const batch = await this._durable.claimBatch();
2354
- if (!batch.length) break;
2355
- const input = await this._inputForBatch(batch);
3979
+ const snapshot = this._durable.snapshot();
3980
+ if (!snapshot.nextTurn.length && !this._wakeReserved) break;
2356
3981
  const turn = await this._nextTurnNumber();
3982
+ await this.session.append("turn/start", { turn, reason: "user" });
3983
+ const batch = await this._durable.claimBatch();
3984
+ this._wakeReserved = false;
3985
+ const input = batch.length ? await this._inputForBatch(batch, true) : { text: "" };
2357
3986
  for (const message of batch) {
2358
3987
  this._ctx.emit("agent/inbox/claimed", {
2359
3988
  id: this.id,
@@ -2370,11 +3999,21 @@ var LiveAgentImpl = class {
2370
3999
  else signal?.addEventListener("abort", forward, { once: true });
2371
4000
  try {
2372
4001
  if (this._manualStreaming) {
2373
- yield* this._service.runStream(input, { signal: controller.signal });
4002
+ if (this._loop) {
4003
+ const run = await this._loop(input, { signal: controller.signal, turn });
4004
+ await this._closeCustomTurn(turn, run);
4005
+ yield { type: "run/end", run };
4006
+ } else {
4007
+ yield* this._service.runStream(input, { signal: controller.signal, turn });
4008
+ }
2374
4009
  } else {
2375
- await this._service.run(input, { signal: controller.signal });
4010
+ if (this._loop) {
4011
+ const run = await this._loop(input, { signal: controller.signal, turn });
4012
+ await this._closeCustomTurn(turn, run);
4013
+ } else await this._service.run(input, { signal: controller.signal, turn });
2376
4014
  }
2377
4015
  } catch (error) {
4016
+ if (this._loop) await this._closeFailedCustomTurn(turn, error);
2378
4017
  if (!controller.signal.aborted) {
2379
4018
  const step = await this._failedStep();
2380
4019
  this._ctx.emit("agent/error", {
@@ -2392,26 +4031,37 @@ var LiveAgentImpl = class {
2392
4031
  }
2393
4032
  } finally {
2394
4033
  this._busy = false;
4034
+ if (!this._durable.size) this._wakeReserved = false;
2395
4035
  this._setStatus("idle");
2396
4036
  }
2397
4037
  }
2398
- async _inputForBatch(batch) {
2399
- const history = await this.session.deriveMessages();
2400
- const structured = batch.filter((message) => message.content !== void 0);
2401
- const plain = batch.filter((message) => message.content === void 0);
2402
- const plainMessages = plain.filter((message) => message.text !== void 0).map((message) => ({ role: "user", content: message.text ?? "" }));
2403
- if (structured.length) {
2404
- return {
2405
- messages: [
2406
- ...history,
2407
- ...structured.flatMap((message) => Array.isArray(message.content) ? message.content : [{ role: "user", content: String(message.content ?? "") }])
2408
- ]
2409
- };
2410
- }
2411
- if (history.length || batch.length > 1) {
2412
- return { messages: [...history, ...plainMessages] };
4038
+ _hasQueuedWork() {
4039
+ const snapshot = this._durable.snapshot();
4040
+ return snapshot.nextTurn.length > 0 || this._wakeReserved && snapshot.nextStep.length > 0;
4041
+ }
4042
+ async _inputForBatch(batch, includeHistory) {
4043
+ const history = includeHistory ? await this.session.deriveMessages() : [];
4044
+ const messages = batch.flatMap((message) => message.content !== void 0 ? Array.isArray(message.content) ? message.content : [{ role: "user", content: String(message.content ?? "") }] : message.text !== void 0 ? [{ role: "user", content: message.text }] : []);
4045
+ if (history.length || messages.length > 1) return { messages: [...history, ...messages] };
4046
+ return { text: messages[0]?.content ?? "" };
4047
+ }
4048
+ async claimNextStepInputs() {
4049
+ await this._writeTail;
4050
+ const batch = await this._durable.claimNextStep();
4051
+ this._wakeReserved = false;
4052
+ if (batch.length) {
4053
+ const turn = await this._nextTurnNumber() - 1;
4054
+ for (const message of batch) {
4055
+ this._ctx.emit("agent/inbox/claimed", {
4056
+ id: this.id,
4057
+ agent: this,
4058
+ message,
4059
+ input: { text: message.text ?? "" },
4060
+ turn
4061
+ });
4062
+ }
2413
4063
  }
2414
- return { text: plainMessages[0]?.content ?? "" };
4064
+ return batch.length ? [await this._inputForBatch(batch, false)] : [];
2415
4065
  }
2416
4066
  _setStatus(status) {
2417
4067
  const previous = this._status;
@@ -2507,112 +4157,156 @@ async function buildHandle(ctx, options, resume = false) {
2507
4157
  }
2508
4158
  const agentId = fileMeta?.agentId ?? requestedId;
2509
4159
  if (!agentId) throw new AgentError("agent requires a stable identity");
2510
- const log = new SessionLog(
2511
- options.file,
2512
- (type, payload) => {
2513
- if (type === "event") ctx.emit("session/event", payload);
2514
- else ctx.emit("session/flush", payload);
2515
- }
2516
- );
2517
- await log.init();
2518
- const fileEvents = await log.read();
2519
- const durableMeta = readDurableAgentMeta(fileEvents);
2520
- if (durableMeta) {
2521
- if (durableMeta.agentId !== agentId) {
2522
- throw new AgentError(
2523
- `agent id mismatch: session belongs to ${durableMeta.agentId}, requested ${agentId}`
2524
- );
2525
- }
2526
- if (options.agentType && durableMeta.agentType !== options.agentType) {
2527
- throw new AgentError(
2528
- `agent type mismatch: session is ${durableMeta.agentType ?? "untyped"}, requested ${options.agentType}`
2529
- );
2530
- }
2531
- if (options.mode && durableMeta.mode !== options.mode) {
2532
- throw new AgentError(
2533
- `agent mode mismatch: session is ${durableMeta.mode ?? "unset"}, requested ${options.mode}`
2534
- );
2535
- }
2536
- }
2537
- const boundMeta = {
2538
- ...fileMeta,
2539
- ...durableMeta ?? {},
2540
- ...options.title ? { title: options.title } : {},
2541
- ...options.owner ? { owner: options.owner } : {},
2542
- ...options.parentSessionId ? { parentSessionId: options.parentSessionId } : {},
2543
- ...options.forkedAtMessageId ? { forkedAtMessageId: options.forkedAtMessageId } : {},
2544
- agentId
2545
- };
2546
- const resolvedAgentType = options.agentType ?? durableMeta?.agentType;
2547
- if (resolvedAgentType) boundMeta.agentType = resolvedAgentType;
2548
- const resolvedMode = options.mode ?? durableMeta?.mode;
2549
- if (resolvedMode) boundMeta.mode = resolvedMode;
2550
- if (!durableMeta) {
2551
- await log.append("meta", {
2552
- kind: "agent",
2553
- agentId,
2554
- ...boundMeta.agentType ? { agentType: boundMeta.agentType } : {},
2555
- ...boundMeta.mode ? { mode: boundMeta.mode } : {},
2556
- ...boundMeta.title ? { title: boundMeta.title } : {},
2557
- ...boundMeta.owner ? { owner: boundMeta.owner } : {},
2558
- ...boundMeta.parentSessionId ? { parentSessionId: boundMeta.parentSessionId } : {},
2559
- ...boundMeta.forkedAtMessageId ? { forkedAtMessageId: boundMeta.forkedAtMessageId } : {},
2560
- ...boundMeta.createdAt !== void 0 ? { createdAt: boundMeta.createdAt } : {}
2561
- });
2562
- }
2563
- const durable = resume ? await DurableInbox.restore(log) : new DurableInbox(log);
2564
- const injected = new AgentInbox();
2565
- const inboxView = {
2566
- get size() {
2567
- return durable.size;
2568
- },
2569
- snapshot: () => durable.snapshot()
2570
- };
2571
- const service = new AgentService(ctx, {
2572
- session: log,
2573
- inbox: injected,
2574
- ...options.llm ? { llm: options.llm } : {},
2575
- ...options.maxTurns !== void 0 ? { maxTurns: options.maxTurns } : {},
2576
- ...options.maxSteps !== void 0 ? { maxSteps: options.maxSteps } : {},
2577
- ...options.contextBudget ? { contextBudget: options.contextBudget } : {},
2578
- ...options.hooks ? { hooks: options.hooks } : {}
2579
- });
2580
- if (options.system) injected.inject("agentSystem", options.system);
4160
+ let agentCtx;
2581
4161
  const agentScope = await ctx.inject([], (scopeCtx) => {
2582
- scopeCtx.isolate("agentScope").provide("agentScope", scopeCtx);
4162
+ agentCtx = scopeCtx.isolate("agentScope").isolate("llm").isolate("tools").isolate("systemPrompt");
4163
+ agentCtx.provide("agentScope", agentCtx);
2583
4164
  });
2584
4165
  await agentScope;
2585
- const agentCtx = agentScope.ctx;
2586
- const agent2 = new LiveAgentImpl(
2587
- ctx,
2588
- agentCtx,
2589
- service,
2590
- agentId,
2591
- inboxView,
2592
- log,
2593
- durable,
2594
- boundMeta,
2595
- options.manualStreaming === true
2596
- );
2597
- agent2.trackSetupDispose(() => agentScope.dispose());
2598
- if (options.system) agent2.inject("agentSystem", options.system);
2599
- let setupResult;
4166
+ const runtimeCtx = createAgentRuntimeContext(agentCtx);
4167
+ const log = new SessionLog(options.file, void 0, runtimeCtx);
2600
4168
  try {
2601
- setupResult = await options.setup?.(agentCtx);
2602
- setupResult?.commit();
4169
+ await log.init();
4170
+ const fileEvents = await log.read();
4171
+ const durableMeta = readDurableAgentMeta(fileEvents);
4172
+ if (durableMeta) {
4173
+ if (durableMeta.agentId !== agentId) {
4174
+ throw new AgentError(
4175
+ `agent id mismatch: session belongs to ${durableMeta.agentId}, requested ${agentId}`
4176
+ );
4177
+ }
4178
+ if (options.agentType && durableMeta.agentType !== options.agentType) {
4179
+ throw new AgentError(
4180
+ `agent type mismatch: session is ${durableMeta.agentType ?? "untyped"}, requested ${options.agentType}`
4181
+ );
4182
+ }
4183
+ if (options.mode && durableMeta.mode !== options.mode) {
4184
+ throw new AgentError(
4185
+ `agent mode mismatch: session is ${durableMeta.mode ?? "unset"}, requested ${options.mode}`
4186
+ );
4187
+ }
4188
+ }
4189
+ const boundMeta = {
4190
+ ...fileMeta,
4191
+ ...durableMeta ?? {},
4192
+ ...options.title ? { title: options.title } : {},
4193
+ ...options.owner ? { owner: options.owner } : {},
4194
+ ...options.parentSessionId ? { parentSessionId: options.parentSessionId } : {},
4195
+ ...options.forkedAtMessageId ? { forkedAtMessageId: options.forkedAtMessageId } : {},
4196
+ agentId
4197
+ };
4198
+ const resolvedAgentType = options.agentType ?? durableMeta?.agentType;
4199
+ if (resolvedAgentType) boundMeta.agentType = resolvedAgentType;
4200
+ const resolvedMode = options.mode ?? durableMeta?.mode;
4201
+ if (resolvedMode) boundMeta.mode = resolvedMode;
4202
+ if (!durableMeta) {
4203
+ await log.append("meta", {
4204
+ kind: "agent",
4205
+ agentId,
4206
+ ...boundMeta.agentType ? { agentType: boundMeta.agentType } : {},
4207
+ ...boundMeta.mode ? { mode: boundMeta.mode } : {},
4208
+ ...boundMeta.title ? { title: boundMeta.title } : {},
4209
+ ...boundMeta.owner ? { owner: boundMeta.owner } : {},
4210
+ ...boundMeta.parentSessionId ? { parentSessionId: boundMeta.parentSessionId } : {},
4211
+ ...boundMeta.forkedAtMessageId ? { forkedAtMessageId: boundMeta.forkedAtMessageId } : {},
4212
+ ...boundMeta.createdAt !== void 0 ? { createdAt: boundMeta.createdAt } : {}
4213
+ });
4214
+ }
4215
+ const durable = resume ? await DurableInbox.restore(log) : new DurableInbox(log);
4216
+ const restoredWakeReservation = resume && hasRestoredWakeReservation(fileEvents);
4217
+ const injected = new AgentInbox();
4218
+ const inboxView = {
4219
+ get size() {
4220
+ return durable.size;
4221
+ },
4222
+ snapshot: () => durable.snapshot()
4223
+ };
4224
+ let claimNextStep = async () => [];
4225
+ const service = new AgentService(runtimeCtx, {
4226
+ session: log,
4227
+ inbox: injected,
4228
+ ...options.llm ? { llm: options.llm } : {},
4229
+ ...options.maxTurns !== void 0 ? { maxTurns: options.maxTurns } : {},
4230
+ ...options.maxSteps !== void 0 ? { maxSteps: options.maxSteps } : {},
4231
+ ...options.contextBudget ? { contextBudget: options.contextBudget } : {},
4232
+ ...options.hooks ? { hooks: options.hooks } : {},
4233
+ claimNextStep: () => claimNextStep()
4234
+ });
4235
+ if (options.system) injected.inject("agentSystem", options.system);
4236
+ const agent2 = new LiveAgentImpl(
4237
+ runtimeCtx,
4238
+ agentCtx,
4239
+ service,
4240
+ agentId,
4241
+ inboxView,
4242
+ log,
4243
+ durable,
4244
+ boundMeta,
4245
+ options.manualStreaming === true,
4246
+ restoredWakeReservation
4247
+ );
4248
+ claimNextStep = () => agent2.claimNextStepInputs();
4249
+ agent2.trackSetupDispose(() => agentScope.dispose());
4250
+ let setupResult;
4251
+ try {
4252
+ setupResult = await options.setup?.(agentCtx);
4253
+ for (const name2 of ["llm", "systemPrompt"]) {
4254
+ if (agentCtx.reflect.get(name2, false) !== void 0) continue;
4255
+ const inherited = ctx.reflect.get(name2, false);
4256
+ if (inherited !== void 0) agentCtx.provide(name2, inherited);
4257
+ }
4258
+ const loop = agentCtx.reflect.get("agentLoop", false);
4259
+ agent2.setLoop(loop);
4260
+ setupResult?.commit();
4261
+ } catch (error) {
4262
+ await agent2.dispose().catch(() => void 0);
4263
+ await log.close().catch(() => void 0);
4264
+ throw error;
4265
+ }
4266
+ await agent2.publishSessionStart(resume ? "resume" : "startup");
4267
+ for (const input of options.initial ?? []) {
4268
+ if (!resume) await durable.insert({ text: input.text ?? "" });
4269
+ }
4270
+ if (resume && (durable.snapshot().nextTurn.length || restoredWakeReservation) && options.manualStreaming !== true) {
4271
+ setTimeout(() => agent2.wakeNow(), 0);
4272
+ }
4273
+ return { agent: agent2, dispose: () => agent2.dispose() };
2603
4274
  } catch (error) {
2604
- await agent2.dispose().catch(() => void 0);
2605
- await log.close().catch(() => void 0);
4275
+ await Promise.resolve().then(() => agentScope.dispose()).catch(() => void 0);
2606
4276
  throw error;
2607
4277
  }
2608
- await agent2.publishSessionStart(resume ? "resume" : "startup");
2609
- for (const input of options.initial ?? []) {
2610
- if (!resume) await durable.insert({ text: input.text ?? "" });
2611
- }
2612
- if (resume && durable.size && options.manualStreaming !== true) {
2613
- setTimeout(() => agent2.wakeNow(), 0);
4278
+ }
4279
+ function hasRestoredWakeReservation(events) {
4280
+ const nextTurn = [];
4281
+ const nextStep = [];
4282
+ for (const event of events) {
4283
+ if (event.type !== "agent/inbox/spliced") continue;
4284
+ const payload = event.payload;
4285
+ if (payload.target === "all") {
4286
+ nextTurn.splice(0, payload.deleteCounts?.nextTurn ?? nextTurn.length);
4287
+ nextStep.splice(0, payload.deleteCounts?.nextStep ?? nextStep.length);
4288
+ continue;
4289
+ }
4290
+ const list = payload.target === "next-step" ? nextStep : nextTurn;
4291
+ const count = payload.deleteCount ?? 0;
4292
+ if (count === Number.POSITIVE_INFINITY) {
4293
+ list.length = 0;
4294
+ } else if (count > 0 && payload.index !== void 0) {
4295
+ list.splice(payload.index, count);
4296
+ }
4297
+ let insertedOffset = 0;
4298
+ for (const inserted of payload.inserted ?? []) {
4299
+ const entry = { wakes: payload.target === "next-step" && inserted.mode === "steer" };
4300
+ if (inserted.mode === "steer") {
4301
+ list.unshift(entry);
4302
+ } else {
4303
+ const index = payload.index === void 0 ? list.length : Math.min(list.length, payload.index + insertedOffset);
4304
+ list.splice(index, 0, entry);
4305
+ insertedOffset += 1;
4306
+ }
4307
+ }
2614
4308
  }
2615
- return { agent: agent2, dispose: () => agent2.dispose() };
4309
+ return nextStep.some((entry) => entry.wakes);
2616
4310
  }
2617
4311
  async function readAgentMeta(file) {
2618
4312
  const lines = await import("node:fs/promises").then((fs) => fs.readFile(file, "utf8")).catch((error) => {