tnega 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +115 -100
  2. package/dist/agent.js +2510 -134
  3. package/dist/agent.js.map +4 -4
  4. package/dist/bin.js +30723 -5163
  5. package/dist/bin.js.map +4 -4
  6. package/dist/cli-runtime.js +30690 -5094
  7. package/dist/cli-runtime.js.map +4 -4
  8. package/dist/coding-agent.js +19088 -0
  9. package/dist/coding-agent.js.map +7 -0
  10. package/dist/core.js +74 -1
  11. package/dist/core.js.map +4 -4
  12. package/dist/eval.js +23159 -1990
  13. package/dist/eval.js.map +4 -4
  14. package/dist/events.js +22 -1
  15. package/dist/events.js.map +2 -2
  16. package/dist/evolve.js +17631 -1815
  17. package/dist/evolve.js.map +4 -4
  18. package/dist/index.js +31848 -6076
  19. package/dist/index.js.map +4 -4
  20. package/dist/llm.js +8 -0
  21. package/dist/llm.js.map +2 -2
  22. package/dist/services.js +12 -1
  23. package/dist/services.js.map +2 -2
  24. package/dist/session.js +793 -122
  25. package/dist/session.js.map +4 -4
  26. package/dist/tools.js +242 -135
  27. package/dist/tools.js.map +4 -4
  28. package/dist/types/packages/agent/src/inbox-durable.d.ts +54 -0
  29. package/dist/types/packages/agent/src/index.d.ts +4 -0
  30. package/dist/types/packages/agent/src/live.d.ts +115 -0
  31. package/dist/types/packages/agent/src/llm-service.d.ts +32 -0
  32. package/dist/types/packages/agent/src/prompt.d.ts +55 -0
  33. package/dist/types/packages/agent/src/service.d.ts +24 -0
  34. package/dist/types/packages/agent/src/types.d.ts +75 -1
  35. package/dist/types/packages/benchmark/src/bigcodebench.d.ts +6 -0
  36. package/dist/types/packages/benchmark/src/humaneval.d.ts +11 -0
  37. package/dist/types/packages/benchmark/src/index.d.ts +9 -0
  38. package/dist/types/packages/benchmark/src/manifest.d.ts +10 -0
  39. package/dist/types/packages/benchmark/src/mbpp.d.ts +10 -0
  40. package/dist/types/packages/benchmark/src/parquet.d.ts +2 -0
  41. package/dist/types/packages/benchmark/src/stdlib.d.ts +3 -0
  42. package/dist/types/packages/benchmark/src/swebench.d.ts +2 -0
  43. package/dist/types/packages/benchmark/src/types.d.ts +58 -0
  44. package/dist/types/packages/cli/src/commands.d.ts +26 -2
  45. package/dist/types/packages/cli/src/config.d.ts +2 -0
  46. package/dist/types/packages/cli/src/index.d.ts +4 -2
  47. package/dist/types/packages/cli/src/profile-file.d.ts +26 -0
  48. package/dist/types/packages/cli/src/profile.d.ts +9 -0
  49. package/dist/types/packages/cli/src/server.d.ts +2 -0
  50. package/dist/types/packages/cli/src/store.d.ts +10 -9
  51. package/dist/types/packages/coding-agent/src/codingAgent.d.ts +25 -0
  52. package/dist/types/packages/coding-agent/src/index.d.ts +6 -0
  53. package/dist/types/packages/coding-agent/src/mcp.d.ts +24 -0
  54. package/dist/types/packages/coding-agent/src/plan.d.ts +13 -0
  55. package/dist/types/packages/coding-agent/src/skills.d.ts +12 -0
  56. package/dist/types/packages/coding-agent/src/slash.d.ts +28 -0
  57. package/dist/types/packages/coding-agent/src/types.d.ts +41 -0
  58. package/dist/types/packages/core/src/events.d.ts +4 -1
  59. package/dist/types/packages/core/src/index.d.ts +1 -0
  60. package/dist/types/packages/core/src/invariant.d.ts +45 -0
  61. package/dist/types/packages/eval/src/codingRuntime.d.ts +18 -0
  62. package/dist/types/packages/eval/src/policy.d.ts +8 -0
  63. package/dist/types/packages/eval/src/runner.d.ts +4 -0
  64. package/dist/types/packages/eval/src/strategies.d.ts +10 -0
  65. package/dist/types/packages/eval/src/trace.d.ts +7 -0
  66. package/dist/types/packages/eval/src/types.d.ts +97 -1
  67. package/dist/types/packages/eval/src/workspace.d.ts +17 -0
  68. package/dist/types/packages/llm/src/types.d.ts +2 -0
  69. package/dist/types/packages/session/src/index.d.ts +397 -12
  70. package/dist/types/packages/session/src/invariant.d.ts +33 -0
  71. package/dist/types/packages/tools/src/builtins.d.ts +2 -0
  72. package/dist/types/packages/tools/src/execution.d.ts +31 -0
  73. package/dist/types/packages/tools/src/index.d.ts +17 -0
  74. package/dist/types/src/index.d.ts +5 -5
  75. package/dist/web/assets/index-45pokiBH.js +270 -0
  76. package/dist/web/assets/index-DCLL69Kz.css +1 -0
  77. package/dist/web/index.html +2 -2
  78. package/package.json +92 -88
  79. package/dist/web/assets/index-C3jFlR4V.css +0 -1
  80. package/dist/web/assets/index-DeFGh2h4.js +0 -268
package/dist/agent.js CHANGED
@@ -1,5 +1,302 @@
1
+ import { createRequire as __tnegaCreateRequire } from 'node:module'; const require = __tnegaCreateRequire(import.meta.url);
2
+
3
+ // packages/agent/src/service.ts
4
+ import { randomUUID as randomUUID2 } from "node:crypto";
5
+
6
+ // packages/session/src/index.ts
7
+ import { randomUUID } from "node:crypto";
8
+ import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
9
+ import { dirname, resolve } from "node:path";
10
+
11
+ // packages/session/src/invariant.ts
12
+ function checkBalancedTurns(events) {
13
+ const opens = /* @__PURE__ */ new Map();
14
+ const failures = [];
15
+ for (const event of events) {
16
+ if (event.type === "turn/start") {
17
+ opens.set(event.payload.turn, (opens.get(event.payload.turn) ?? 0) + 1);
18
+ } else if (event.type === "turn/end") {
19
+ const remaining = opens.get(event.payload.turn) ?? 0;
20
+ if (remaining <= 0) {
21
+ failures.push({
22
+ name: "turn/end-without-start",
23
+ detail: `turn/end for turn ${event.payload.turn} has no matching turn/start`
24
+ });
25
+ } else {
26
+ opens.set(event.payload.turn, remaining - 1);
27
+ }
28
+ }
29
+ }
30
+ for (const [turn, count] of opens) {
31
+ for (let index = 0; index < count; index += 1) {
32
+ failures.push({
33
+ name: "turn/start-without-end",
34
+ detail: `turn/start ${turn} has no matching turn/end`
35
+ });
36
+ }
37
+ }
38
+ return failures;
39
+ }
40
+ function checkBalancedSteps(events) {
41
+ const open = /* @__PURE__ */ new Set();
42
+ const failures = [];
43
+ for (const event of events) {
44
+ if (event.type === "step/start") {
45
+ const key = `${event.payload.turn}:${event.payload.step}`;
46
+ if (open.has(key)) {
47
+ failures.push({
48
+ name: "duplicate-step-start",
49
+ detail: `step/start for ${key} appears twice without a matching step/end`
50
+ });
51
+ }
52
+ open.add(key);
53
+ } else if (event.type === "step/end") {
54
+ const key = `${event.payload.turn}:${event.payload.step}`;
55
+ if (!open.delete(key)) {
56
+ failures.push({
57
+ name: "step/end-without-start",
58
+ detail: `step/end for ${key} has no matching step/start`
59
+ });
60
+ }
61
+ }
62
+ }
63
+ for (const key of open) {
64
+ failures.push({
65
+ name: "step/start-without-end",
66
+ detail: `step/start ${key} has no matching step/end`
67
+ });
68
+ }
69
+ return failures;
70
+ }
71
+ function checkBalancedToolCalls(events) {
72
+ const open = /* @__PURE__ */ new Set();
73
+ const failures = [];
74
+ for (const event of events) {
75
+ if (event.type === "tool/call") {
76
+ open.add(event.payload.id);
77
+ } else if (event.type === "tool/result") {
78
+ const payload = event.payload;
79
+ const id = payload.toolCallId;
80
+ if (!open.delete(id)) {
81
+ failures.push({
82
+ name: "tool/result-without-call",
83
+ detail: `tool/result for call ${id} has no matching tool/call`
84
+ });
85
+ }
86
+ }
87
+ }
88
+ for (const id of open) {
89
+ failures.push({
90
+ name: "tool/call-without-result",
91
+ detail: `tool/call ${id} has no matching tool/result`
92
+ });
93
+ }
94
+ return failures;
95
+ }
96
+ function checkMonotonicSeq(events) {
97
+ const failures = [];
98
+ for (let index = 1; index < events.length; index += 1) {
99
+ const previous = events[index - 1];
100
+ const current = events[index];
101
+ if (current && previous && current.seq <= previous.seq) {
102
+ failures.push({
103
+ name: "non-monotonic-seq",
104
+ detail: `seq ${current.seq} follows ${previous.seq} out of order at index ${index}`
105
+ });
106
+ }
107
+ }
108
+ return failures;
109
+ }
110
+ function checkSessionInvariants(events) {
111
+ return [
112
+ ...checkBalancedTurns(events),
113
+ ...checkBalancedSteps(events),
114
+ ...checkBalancedToolCalls(events),
115
+ ...checkMonotonicSeq(events)
116
+ ];
117
+ }
118
+
1
119
  // packages/session/src/index.ts
120
+ var SESSION_FORMAT_VERSION = 7;
121
+ var SessionFormatError = class extends Error {
122
+ name = "SessionFormatError";
123
+ };
124
+ var liveSessions = /* @__PURE__ */ new Map();
125
+ function sessionFileKey(file) {
126
+ return resolve(file);
127
+ }
128
+ function isMessageEventType(type) {
129
+ return type === "user/message" || type === "assistant/message" || type === "system/message";
130
+ }
131
+ function isMessageEvent(event) {
132
+ return isMessageEventType(event.type);
133
+ }
2
134
  var DEFAULT_CONTEXT_LIMIT = 128e3;
135
+ function isSessionEvent(value) {
136
+ if (typeof value !== "object" || value === null) return false;
137
+ const record = value;
138
+ return typeof record.id === "string" && typeof record.seq === "number" && typeof record.ts === "number" && typeof record.type === "string" && "payload" in record;
139
+ }
140
+ function clone(value) {
141
+ return structuredClone(value);
142
+ }
143
+ function previousSurfaceSeq(nodes) {
144
+ return nodes.at(-1);
145
+ }
146
+ function isSurfaceEventType(type) {
147
+ return type === "user/message" || type === "system/message" || type === "assistant/message" || type === "tool/result";
148
+ }
149
+ function checkpointShadowRange(event) {
150
+ if (event.type !== "checkpoint") return void 0;
151
+ const op = event.payload.surfaceOp;
152
+ if (typeof op === "object" && op !== null && op.op === "replace") {
153
+ return { start: op.start, end: op.end };
154
+ }
155
+ return void 0;
156
+ }
157
+ function foldSurface(events) {
158
+ const nodes = [];
159
+ const replacements = [];
160
+ const applyReplace = (seq, start, end) => {
161
+ const startIndex = nodes.indexOf(start);
162
+ if (startIndex < 0) {
163
+ nodes.push(seq);
164
+ return;
165
+ }
166
+ const endIndex = nodes.indexOf(end, startIndex);
167
+ if (endIndex < 0) {
168
+ nodes.push(seq);
169
+ return;
170
+ }
171
+ const shadowed = nodes.splice(startIndex, endIndex - startIndex + 1);
172
+ nodes.splice(startIndex, 0, seq);
173
+ replacements.push({ seq, start, end, shadowedSeqs: shadowed });
174
+ };
175
+ for (const event of events) {
176
+ if (event.type === "checkpoint") {
177
+ const range = checkpointShadowRange(event);
178
+ if (!range) {
179
+ nodes.push(event.seq);
180
+ continue;
181
+ }
182
+ applyReplace(event.seq, range.start, range.end);
183
+ continue;
184
+ }
185
+ if (!isSurfaceEventType(event.type)) continue;
186
+ const op = event.surfaceOp ?? "append";
187
+ if (op === "append") {
188
+ nodes.push(event.seq);
189
+ continue;
190
+ }
191
+ applyReplace(event.seq, op.start, op.end);
192
+ }
193
+ return { nodes, replacements };
194
+ }
195
+ function deriveEventMessage(event) {
196
+ switch (event.type) {
197
+ case "user/message": {
198
+ const message = { role: "user", content: event.payload.content };
199
+ if (event.payload.name) message.name = event.payload.name;
200
+ return message;
201
+ }
202
+ case "system/message": {
203
+ const message = { role: "system", content: event.payload.content };
204
+ if (event.payload.name) message.name = event.payload.name;
205
+ return message;
206
+ }
207
+ case "assistant/message": {
208
+ const toolCalls = event.payload.toolCalls;
209
+ if (!event.payload.content && !(toolCalls && toolCalls.length)) return null;
210
+ const message = { role: "assistant", content: event.payload.content };
211
+ if (event.payload.name) message.name = event.payload.name;
212
+ if (toolCalls?.length) message.tool_calls = clone(toolCalls);
213
+ return message;
214
+ }
215
+ case "tool/result": {
216
+ const failed = !event.payload.ok;
217
+ const message = {
218
+ role: "tool",
219
+ content: failed ? `error: ${event.payload.error?.message ?? "unknown"}` : stringify(event.payload.output),
220
+ tool_call_id: event.payload.toolCallId
221
+ };
222
+ message.name = event.payload.name;
223
+ if (failed) {
224
+ message.toolOk = false;
225
+ if (event.payload.error) message.toolError = event.payload.error;
226
+ }
227
+ return message;
228
+ }
229
+ default:
230
+ return null;
231
+ }
232
+ }
233
+ function deriveSurfaceMessages(events) {
234
+ const { nodes } = foldSurface(events);
235
+ const bySeq = new Map(events.map((event) => [event.seq, event]));
236
+ const messages = [];
237
+ for (const seq of nodes) {
238
+ const event = bySeq.get(seq);
239
+ if (!event) continue;
240
+ if (event.type === "checkpoint") {
241
+ messages.splice(0, messages.length, ...clone(event.payload.messages));
242
+ continue;
243
+ }
244
+ const message = deriveEventMessage(event);
245
+ if (message) messages.push(message);
246
+ }
247
+ return messages;
248
+ }
249
+ function foldRequestHeader(events) {
250
+ let header;
251
+ for (const event of events) {
252
+ if (event.type === "request/header") header = clone(event.payload);
253
+ }
254
+ return header;
255
+ }
256
+ function foldRequestContext(events) {
257
+ let context;
258
+ for (const event of events) {
259
+ if (event.type === "request/context") context = clone(event.payload);
260
+ }
261
+ return context;
262
+ }
263
+ function foldSessionMeta(events) {
264
+ const meta = {};
265
+ for (const event of events) {
266
+ if (event.type === "meta") {
267
+ const payload = event.payload;
268
+ if (typeof payload.title === "string") meta.title = payload.title;
269
+ if (payload.agentType === "general" || payload.agentType === "coding") {
270
+ meta.agentType = payload.agentType;
271
+ }
272
+ if (payload.mode === "auto" || payload.mode === "plan" || payload.mode === "execute") {
273
+ meta.mode = payload.mode;
274
+ }
275
+ continue;
276
+ }
277
+ if (event.type === "meta/patch") {
278
+ const patch = event.payload;
279
+ if (patch.fields.includes("title") && typeof patch.title === "string") {
280
+ meta.title = patch.title;
281
+ }
282
+ if (patch.fields.includes("agentType") && patch.agentType !== void 0) {
283
+ meta.agentType = patch.agentType;
284
+ }
285
+ if (patch.fields.includes("mode") && patch.mode !== void 0) {
286
+ meta.mode = patch.mode;
287
+ }
288
+ }
289
+ }
290
+ return meta;
291
+ }
292
+ function stringify(value) {
293
+ if (typeof value === "string") return value;
294
+ try {
295
+ return JSON.stringify(value) ?? String(value);
296
+ } catch {
297
+ return String(value);
298
+ }
299
+ }
3
300
  function estimateMessageTokens(messages) {
4
301
  let tokens = 0;
5
302
  for (const message of messages) {
@@ -11,6 +308,155 @@ function estimateMessageTokens(messages) {
11
308
  }
12
309
  return tokens;
13
310
  }
311
+ function estimateEventTokens(event) {
312
+ switch (event.type) {
313
+ case "user/message":
314
+ case "system/message":
315
+ return Math.ceil(event.payload.content.length / 4);
316
+ case "assistant/message": {
317
+ let tokens = Math.ceil(event.payload.content.length / 4);
318
+ for (const call of event.payload.toolCalls ?? []) {
319
+ const raw = JSON.stringify(call.arguments ?? {}) ?? "";
320
+ tokens += Math.ceil(raw.length / 4);
321
+ }
322
+ return tokens;
323
+ }
324
+ case "assistant/chunk":
325
+ return Math.ceil(event.payload.content.length / 4);
326
+ case "tool/call": {
327
+ const raw = JSON.stringify(event.payload.arguments ?? {}) ?? "";
328
+ return Math.ceil(raw.length / 4);
329
+ }
330
+ case "tool/result": {
331
+ const raw = event.payload.ok ? stringify(event.payload.output) : event.payload.error?.message ?? "error";
332
+ return Math.ceil(raw.length / 4);
333
+ }
334
+ case "plan":
335
+ return 0;
336
+ case "checkpoint":
337
+ return estimateMessageTokens(event.payload.messages);
338
+ case "compaction/start":
339
+ case "compaction/end":
340
+ case "meta":
341
+ case "meta/patch":
342
+ return 0;
343
+ case "llm/retry":
344
+ case "llm/retry-started":
345
+ return 0;
346
+ case "request/header":
347
+ case "request/context":
348
+ case "agent/inbox/spliced":
349
+ return 0;
350
+ case "turn/start":
351
+ case "turn/end":
352
+ case "step/start":
353
+ case "step/end":
354
+ return 0;
355
+ }
356
+ }
357
+ function suffixStartIndexForTokens(events, targetTokens) {
358
+ if (!events.length || targetTokens <= 0) return 0;
359
+ let tokens = 0;
360
+ let candidate = events.length;
361
+ for (let index = events.length - 1; index >= 0; index -= 1) {
362
+ tokens += estimateEventTokens(events[index]);
363
+ if (tokens >= targetTokens) {
364
+ candidate = index;
365
+ break;
366
+ }
367
+ }
368
+ if (candidate === events.length) return 0;
369
+ let cut = candidate;
370
+ while (cut < events.length) {
371
+ const event = events[cut];
372
+ if (event?.type === "user/message") return cut;
373
+ cut += 1;
374
+ }
375
+ return candidate;
376
+ }
377
+ function repairUnclosed(events) {
378
+ const openCalls = [];
379
+ const openSteps = [];
380
+ const openTurns = [];
381
+ for (const event of events) {
382
+ switch (event.type) {
383
+ case "tool/call":
384
+ openCalls.push(event);
385
+ break;
386
+ case "tool/result": {
387
+ const callIndex = openCalls.findIndex(
388
+ (call) => call.payload.id === event.payload.toolCallId
389
+ );
390
+ if (callIndex >= 0) openCalls.splice(callIndex, 1);
391
+ else openCalls.pop();
392
+ break;
393
+ }
394
+ case "step/start":
395
+ openSteps.push(event);
396
+ break;
397
+ case "step/end":
398
+ openSteps.pop();
399
+ break;
400
+ case "turn/start":
401
+ openTurns.push(event);
402
+ break;
403
+ case "turn/end":
404
+ openTurns.pop();
405
+ break;
406
+ }
407
+ }
408
+ if (!openCalls.length && !openSteps.length && !openTurns.length) return [];
409
+ const synthetic = [];
410
+ let nextSeq = (events.at(-1)?.seq ?? 0) + 1;
411
+ let nextTs = (events.at(-1)?.ts ?? Date.now()) + 1;
412
+ const push = (type, payload) => {
413
+ synthetic.push({
414
+ id: randomUUID(),
415
+ seq: nextSeq,
416
+ ts: nextTs,
417
+ type,
418
+ payload: clone(payload)
419
+ });
420
+ nextSeq += 1;
421
+ nextTs += 1;
422
+ };
423
+ for (const call of openCalls) {
424
+ push("tool/result", {
425
+ id: call.payload.id,
426
+ toolCallId: call.payload.id,
427
+ name: call.payload.name,
428
+ ok: false,
429
+ error: {
430
+ name: "SessionInterruptedError",
431
+ message: "session interrupted before tool result was recorded"
432
+ }
433
+ });
434
+ }
435
+ for (const step of openSteps) {
436
+ push("step/end", {
437
+ turn: step.payload.turn,
438
+ step: step.payload.step,
439
+ finishReason: "interrupted",
440
+ interrupted: true
441
+ });
442
+ }
443
+ for (let i = 0; i < openTurns.length; i++) {
444
+ const turn = openTurns[i].payload.turn;
445
+ push("turn/end", {
446
+ turn,
447
+ finishReason: "interrupted",
448
+ interrupted: true
449
+ });
450
+ }
451
+ return synthetic;
452
+ }
453
+ function resolveCompactKeep(events, options) {
454
+ if (typeof options.keepTokens === "number" && Number.isFinite(options.keepTokens) && options.keepTokens > 0) {
455
+ return events.length - suffixStartIndexForTokens(events, options.keepTokens);
456
+ }
457
+ const keep = typeof options.keep === "number" && Number.isFinite(options.keep) && options.keep > 0 ? Math.floor(options.keep) : 0;
458
+ return Math.min(keep, events.length);
459
+ }
14
460
  function estimateContextUsage(messages, limit = DEFAULT_CONTEXT_LIMIT) {
15
461
  const tokens = estimateMessageTokens(messages);
16
462
  return {
@@ -19,6 +465,459 @@ function estimateContextUsage(messages, limit = DEFAULT_CONTEXT_LIMIT) {
19
465
  ratio: limit > 0 ? tokens / limit : 0
20
466
  };
21
467
  }
468
+ var SessionLog = class {
469
+ constructor(file, _broadcast) {
470
+ this.file = file;
471
+ this._broadcast = _broadcast;
472
+ }
473
+ file;
474
+ _broadcast;
475
+ _events = [];
476
+ _surface = [];
477
+ _surfaceNodes = [];
478
+ _requestHeader;
479
+ _requestContext;
480
+ _loaded = false;
481
+ _nextSeq = 1;
482
+ _queue = Promise.resolve();
483
+ _writeTail = Promise.resolve();
484
+ _pending = [];
485
+ _drainScheduled = false;
486
+ _writeError;
487
+ init() {
488
+ return this._run(async () => {
489
+ const key = sessionFileKey(this.file);
490
+ if (!liveSessions.has(key)) liveSessions.set(key, this);
491
+ try {
492
+ await this._ensureLoaded();
493
+ await this._drainWrite();
494
+ if (this._writeError) {
495
+ const error = this._writeError;
496
+ this._writeError = void 0;
497
+ throw error;
498
+ }
499
+ } catch (error) {
500
+ if (liveSessions.get(key) === this) liveSessions.delete(key);
501
+ throw error;
502
+ }
503
+ });
504
+ }
505
+ append(type, payload) {
506
+ return this._run(async () => {
507
+ await this._ensureLoaded();
508
+ const event = this._buildEvent(type, payload);
509
+ this._commitEvent(event);
510
+ return event;
511
+ });
512
+ }
513
+ _buildEvent(type, payload) {
514
+ const eventPayload = clone(payload);
515
+ const event = {
516
+ id: randomUUID(),
517
+ seq: this._nextSeq,
518
+ ts: Date.now(),
519
+ type,
520
+ payload: eventPayload
521
+ };
522
+ if (isMessageEventType(type)) {
523
+ for (let index = this._events.length - 1; index >= 0; index -= 1) {
524
+ const previous2 = this._events[index];
525
+ if (previous2 && isMessageEvent(previous2)) {
526
+ const messagePayload = eventPayload;
527
+ messagePayload.parentId ??= previous2.id;
528
+ break;
529
+ }
530
+ }
531
+ event.surfaceOp ??= "append";
532
+ const previous = previousSurfaceSeq(this._surfaceNodes);
533
+ event.sourceEventSeqs ??= previous === void 0 ? [] : [previous];
534
+ } else if (type === "request/header") {
535
+ event.sourceEventSeqs ??= [];
536
+ } else if (type === "request/context") {
537
+ event.sourceEventSeqs ??= [];
538
+ } else if (type === "agent/inbox/spliced") {
539
+ event.sourceEventSeqs ??= [];
540
+ }
541
+ this._nextSeq += 1;
542
+ return event;
543
+ }
544
+ _commitEvent(event) {
545
+ this._events.push(event);
546
+ this._refreshSurface();
547
+ if (event.type === "request/header") this._requestHeader = clone(event.payload);
548
+ if (event.type === "request/context") this._requestContext = clone(event.payload);
549
+ this._broadcast?.("event", event);
550
+ this._enqueue(event);
551
+ }
552
+ /**
553
+ * Rebuild both surface views from the committed log. The nodes come from the
554
+ * positional {@link foldSurface} fold; the messages come from
555
+ * {@link deriveSurfaceMessages}, which walks those nodes in surface order —
556
+ * the model view is a pure function of the surface, never of raw file order.
557
+ */
558
+ _refreshSurface() {
559
+ this._surfaceNodes = foldSurface(this._events).nodes;
560
+ this._surface = deriveSurfaceMessages(this._events);
561
+ }
562
+ _enqueue(event) {
563
+ this._pending.push(event);
564
+ if (this._drainScheduled) return;
565
+ this._drainScheduled = true;
566
+ setImmediate(() => {
567
+ this._drainScheduled = false;
568
+ void this._drainWrite();
569
+ });
570
+ }
571
+ _drainWrite() {
572
+ const events = this._pending.splice(0);
573
+ if (!events.length) return this._writeTail;
574
+ const write = this._writeTail.then(() => appendFile(
575
+ this.file,
576
+ `${events.map((event) => JSON.stringify(event)).join("\n")}
577
+ `,
578
+ "utf8"
579
+ ));
580
+ this._writeTail = write.catch((error) => {
581
+ this._writeError ??= error;
582
+ });
583
+ return write.then(() => void 0, () => void 0);
584
+ }
585
+ flush() {
586
+ return this._run(async () => {
587
+ await this._ensureLoaded();
588
+ await this._drainWrite();
589
+ if (this._writeError) {
590
+ const error = this._writeError;
591
+ this._writeError = void 0;
592
+ throw error;
593
+ }
594
+ const seq = this._nextSeq - 1;
595
+ this._broadcast?.("flush", { file: this.file, seq });
596
+ return seq;
597
+ });
598
+ }
599
+ async lineage(messageId) {
600
+ return this._run(async () => {
601
+ await this._ensureLoaded();
602
+ return this._resolveLineage(messageId);
603
+ });
604
+ }
605
+ /** The next turn number: one more than the highest recorded `turn/start`. */
606
+ async nextTurn() {
607
+ return this._run(async () => {
608
+ await this._ensureLoaded();
609
+ let max = 0;
610
+ for (const event of this._events) {
611
+ if (event.type === "turn/start") max = Math.max(max, event.payload.turn);
612
+ }
613
+ return max + 1;
614
+ });
615
+ }
616
+ async forkAt(messageId) {
617
+ return this._run(async () => {
618
+ await this._ensureLoaded();
619
+ const lineage = this._resolveLineage(messageId);
620
+ const lineageIds = new Set(lineage.map((event) => event.id));
621
+ const targetIndex = this._events.findIndex(
622
+ (event) => event.id === messageId && isMessageEvent(event)
623
+ );
624
+ if (targetIndex < 0) {
625
+ throw new Error(`message not found: ${messageId}`);
626
+ }
627
+ const selected = [];
628
+ let skippedMessage = false;
629
+ for (let index = 0; index <= targetIndex; index += 1) {
630
+ const event = this._events[index];
631
+ if (isMessageEvent(event)) {
632
+ if (!lineageIds.has(event.id)) {
633
+ skippedMessage = true;
634
+ continue;
635
+ }
636
+ skippedMessage = false;
637
+ selected.push(clone(event));
638
+ continue;
639
+ }
640
+ if (event.type === "meta" || event.type === "llm/retry" || event.type === "llm/retry-started" || event.type.startsWith("turn/") || event.type.startsWith("step/")) {
641
+ continue;
642
+ }
643
+ if (event.type === "checkpoint") {
644
+ selected.push(clone(event));
645
+ continue;
646
+ }
647
+ if (!skippedMessage) {
648
+ selected.push(clone(event));
649
+ }
650
+ }
651
+ return selected;
652
+ });
653
+ }
654
+ read() {
655
+ return this._run(async () => {
656
+ await this._ensureLoaded();
657
+ return this._events.map((event) => clone(event));
658
+ });
659
+ }
660
+ deriveMessages() {
661
+ return this._run(async () => {
662
+ await this._ensureLoaded();
663
+ return clone(this._surface);
664
+ });
665
+ }
666
+ /** The ordered surface events, after compaction replacements are applied. */
667
+ surfaceEvents() {
668
+ return this._run(async () => {
669
+ await this._ensureLoaded();
670
+ const bySeq = new Map(this._events.map((event) => [event.seq, event]));
671
+ return this._surfaceNodes.map((seq) => bySeq.get(seq)).filter((event) => event !== void 0).map((event) => clone(event));
672
+ });
673
+ }
674
+ /** The latest `request/header` snapshot, or undefined before the first one. */
675
+ requestHeader() {
676
+ return this._requestHeader ? clone(this._requestHeader) : void 0;
677
+ }
678
+ /** The latest resolved route metadata, or undefined before the first one. */
679
+ requestContext() {
680
+ return this._requestContext ? clone(this._requestContext) : void 0;
681
+ }
682
+ /** Display metadata folded from the durable `meta` + `meta/patch` events. */
683
+ meta() {
684
+ return this._run(async () => {
685
+ await this._ensureLoaded();
686
+ return foldSessionMeta(this._events);
687
+ });
688
+ }
689
+ estimateContext(limit = DEFAULT_CONTEXT_LIMIT) {
690
+ return this._run(async () => {
691
+ await this._ensureLoaded();
692
+ return estimateContextUsage(this._surface, limit);
693
+ });
694
+ }
695
+ replay(reduce, initial) {
696
+ return this.read().then((events) => {
697
+ if (!reduce) return events;
698
+ let state = initial;
699
+ let pending = Promise.resolve();
700
+ for (const event of events) {
701
+ pending = pending.then(async () => {
702
+ state = await reduce(state, event);
703
+ });
704
+ }
705
+ return pending.then(() => state);
706
+ });
707
+ }
708
+ /** Structural violations in the loaded event stream (empty = balanced). */
709
+ runInvariants() {
710
+ return this._run(async () => {
711
+ await this._ensureLoaded();
712
+ return checkSessionInvariants(this._events);
713
+ });
714
+ }
715
+ /**
716
+ * Compact the session without ever rewriting history.
717
+ *
718
+ * The surface (not the raw log) is the unit of compaction. A kept suffix —
719
+ * measured in tokens via `keepTokens`, or in nodes via `keep`, and always
720
+ * snapped to start on a fresh `user/message` turn so a tool result never
721
+ * outlives the assistant call it answers — stays visible; every earlier
722
+ * surface node is shadowed by the checkpoint's `surfaceOp.replace` range.
723
+ * The three compaction events are appended like any other event, so seqs
724
+ * stay immutable, live observers see the transition, and a crash can only
725
+ * leave an orphaned `compaction/start` rather than a torn rewrite.
726
+ *
727
+ * Returns the new event count, or the unchanged count when there is no
728
+ * useful surface span to shadow.
729
+ */
730
+ compact(options = {}) {
731
+ return this._run(async () => {
732
+ await this._ensureLoaded();
733
+ const events = this._events;
734
+ const bySeq = new Map(events.map((event) => [event.seq, event]));
735
+ const surfaceEvents = foldSurface(events).nodes.map((seq) => bySeq.get(seq)).filter((event) => event !== void 0);
736
+ const keep = resolveCompactKeep(surfaceEvents, options);
737
+ let split = Math.max(0, surfaceEvents.length - keep);
738
+ if (split < surfaceEvents.length && surfaceEvents[split].type !== "user/message") {
739
+ let next = split;
740
+ while (next < surfaceEvents.length && surfaceEvents[next].type !== "user/message") next += 1;
741
+ if (next < surfaceEvents.length) {
742
+ split = next;
743
+ } else {
744
+ let lastUser = surfaceEvents.length - 1;
745
+ while (lastUser >= 0 && surfaceEvents[lastUser].type !== "user/message") lastUser -= 1;
746
+ split = lastUser >= 0 ? lastUser : surfaceEvents.length;
747
+ }
748
+ }
749
+ const shadowed = surfaceEvents.slice(0, split);
750
+ if (!options.messages?.length) return this._events.length;
751
+ const range = shadowed.length ? { start: shadowed[0].seq, end: shadowed[shadowed.length - 1].seq } : void 0;
752
+ const boundary = split;
753
+ this._commitEvent(this._buildEvent("compaction/start", {
754
+ ...boundary > 0 ? { boundary } : {},
755
+ ...options.keep !== void 0 ? { keep: options.keep } : {},
756
+ ...options.tokensBefore !== void 0 ? { tokensBefore: options.tokensBefore } : {}
757
+ }));
758
+ const checkpoint = this._buildEvent("checkpoint", {
759
+ messages: clone(options.messages),
760
+ ...range ? { surfaceOp: { op: "replace", start: range.start, end: range.end } } : {},
761
+ ...options.summary ? { summary: options.summary } : {},
762
+ ...options.tokensBefore !== void 0 ? { tokensBefore: options.tokensBefore } : {}
763
+ });
764
+ this._commitEvent(checkpoint);
765
+ this._commitEvent(this._buildEvent("compaction/end", {
766
+ checkpointId: checkpoint.id,
767
+ ...options.keep !== void 0 ? { keep: options.keep } : {}
768
+ }));
769
+ return this._events.length;
770
+ });
771
+ }
772
+ close() {
773
+ return this._queue.then(async () => {
774
+ await this._drainWrite();
775
+ if (this._writeError) {
776
+ const error = this._writeError;
777
+ this._writeError = void 0;
778
+ throw error;
779
+ }
780
+ const key = sessionFileKey(this.file);
781
+ if (liveSessions.get(key) === this) liveSessions.delete(key);
782
+ });
783
+ }
784
+ _run(task) {
785
+ const next = this._queue.then(task, task);
786
+ this._queue = next.then(() => void 0, () => void 0);
787
+ return next;
788
+ }
789
+ async _ensureLoaded() {
790
+ if (this._loaded) return;
791
+ const key = sessionFileKey(this.file);
792
+ const owner = liveSessions.get(key);
793
+ if (owner && owner !== this && owner._loaded) {
794
+ this._events = owner._events.map((event) => clone(event));
795
+ this._surface = clone(owner._surface);
796
+ this._surfaceNodes = [...owner._surfaceNodes];
797
+ this._requestHeader = owner._requestHeader ? clone(owner._requestHeader) : void 0;
798
+ this._requestContext = owner._requestContext ? clone(owner._requestContext) : void 0;
799
+ this._nextSeq = owner._nextSeq;
800
+ this._loaded = true;
801
+ return;
802
+ }
803
+ await mkdir(dirname(resolve(this.file)), { recursive: true });
804
+ const read = await this._read();
805
+ if (!read.existing) {
806
+ const meta = {
807
+ id: randomUUID(),
808
+ seq: 1,
809
+ ts: Date.now(),
810
+ type: "meta",
811
+ payload: { formatVersion: SESSION_FORMAT_VERSION }
812
+ };
813
+ await writeFile(this.file, `${JSON.stringify(meta)}
814
+ `, "utf8");
815
+ this._events = [meta];
816
+ this._refreshSurface();
817
+ this._nextSeq = 2;
818
+ this._loaded = true;
819
+ return;
820
+ }
821
+ this._events = read.events;
822
+ this._refreshSurface();
823
+ this._requestHeader = foldRequestHeader(this._events);
824
+ this._requestContext = foldRequestContext(this._events);
825
+ this._nextSeq = (this._events.at(-1)?.seq ?? 0) + 1;
826
+ this._loaded = true;
827
+ }
828
+ async _read() {
829
+ let text;
830
+ try {
831
+ text = await readFile(this.file, "utf8");
832
+ } catch (error) {
833
+ if (error.code === "ENOENT") {
834
+ return { existing: false, events: [] };
835
+ }
836
+ throw error;
837
+ }
838
+ if (!text.trim()) return { existing: false, events: [] };
839
+ const lines = text.split("\n");
840
+ const events = [];
841
+ let torn = false;
842
+ for (const line of lines) {
843
+ if (!line.trim()) continue;
844
+ if (torn) continue;
845
+ let parsed;
846
+ try {
847
+ parsed = JSON.parse(line);
848
+ } catch {
849
+ torn = true;
850
+ continue;
851
+ }
852
+ if (!isSessionEvent(parsed)) {
853
+ torn = true;
854
+ continue;
855
+ }
856
+ events.push(parsed);
857
+ }
858
+ this._assertFormat(events);
859
+ const key = sessionFileKey(this.file);
860
+ const owner = liveSessions.get(key);
861
+ if (owner && owner !== this) {
862
+ return { existing: true, events };
863
+ }
864
+ const synthetic = repairUnclosed(events);
865
+ if (!torn && !synthetic.length) return { existing: true, events };
866
+ const repaired = [...events, ...synthetic];
867
+ await writeFile(
868
+ this.file,
869
+ `${repaired.map((event) => JSON.stringify(event)).join("\n")}
870
+ `,
871
+ "utf8"
872
+ );
873
+ return { existing: true, events: repaired };
874
+ }
875
+ _assertFormat(events) {
876
+ let version;
877
+ for (const event of events) {
878
+ if (event.type !== "meta") continue;
879
+ const value = event.payload.formatVersion;
880
+ if (typeof value === "number") version = value;
881
+ }
882
+ if (version === void 0) {
883
+ throw new SessionFormatError(
884
+ `session log is missing formatVersion; expected ${SESSION_FORMAT_VERSION}`
885
+ );
886
+ }
887
+ if (version !== SESSION_FORMAT_VERSION) {
888
+ throw new SessionFormatError(
889
+ `unsupported session format version ${version}; expected ${SESSION_FORMAT_VERSION}`
890
+ );
891
+ }
892
+ }
893
+ _resolveLineage(messageId) {
894
+ const messages = this._events.filter(
895
+ (event) => isMessageEvent(event)
896
+ );
897
+ const byId = new Map(
898
+ messages.map((event) => [event.id, event])
899
+ );
900
+ if (!byId.has(messageId)) {
901
+ throw new Error(`message not found: ${messageId}`);
902
+ }
903
+ const chain = [];
904
+ const seen = /* @__PURE__ */ new Set();
905
+ let cursorId = messageId;
906
+ while (cursorId && byId.has(cursorId) && !seen.has(cursorId)) {
907
+ seen.add(cursorId);
908
+ const event = byId.get(cursorId);
909
+ chain.unshift(clone(event));
910
+ const parentId = event.payload.parentId;
911
+ if (parentId && byId.has(parentId)) {
912
+ cursorId = parentId;
913
+ continue;
914
+ }
915
+ const index = messages.findIndex((message) => message.id === cursorId);
916
+ cursorId = index > 0 ? messages[index - 1].id : void 0;
917
+ }
918
+ return chain;
919
+ }
920
+ };
22
921
 
23
922
  // packages/agent/src/service.ts
24
923
  var AgentError = class extends Error {
@@ -26,19 +925,34 @@ var AgentError = class extends Error {
26
925
  };
27
926
  var AgentInbox = class {
28
927
  _queue = [];
928
+ _steerQueue = [];
29
929
  _injected = /* @__PURE__ */ new Map();
30
930
  get size() {
31
- return this._queue.length;
931
+ return this._queue.length + this._steerQueue.length;
932
+ }
933
+ get steerSize() {
934
+ return this._steerQueue.length;
32
935
  }
33
936
  push(input) {
34
937
  this._queue.push(input);
35
938
  return input;
36
939
  }
940
+ followup(input) {
941
+ return this.push(input);
942
+ }
943
+ steer(input) {
944
+ this._steerQueue.push(input);
945
+ return input;
946
+ }
947
+ send(input, options = {}) {
948
+ if (options.mode === "steer") return this.steer(input);
949
+ return this.followup(input);
950
+ }
37
951
  claim() {
38
- return this._queue.shift();
952
+ return this._steerQueue.shift() ?? this._queue.shift();
39
953
  }
40
954
  peek() {
41
- return this._queue[0];
955
+ return this._steerQueue[0] ?? this._queue[0];
42
956
  }
43
957
  inject(key, value) {
44
958
  this._injected.set(key, value);
@@ -67,7 +981,10 @@ function copyMessages(messages) {
67
981
  return copy;
68
982
  });
69
983
  }
70
- function stringify(value) {
984
+ function isUserOrSystemMessage(message) {
985
+ return message.role === "system" || message.role === "user";
986
+ }
987
+ function stringify2(value) {
71
988
  if (typeof value === "string") return value;
72
989
  try {
73
990
  return JSON.stringify(value) ?? String(value);
@@ -89,6 +1006,70 @@ function copyCompletion(completion) {
89
1006
  }
90
1007
  return copy;
91
1008
  }
1009
+ function isCancelCause(value) {
1010
+ if (typeof value !== "object" || value === null) return false;
1011
+ const record = value;
1012
+ if (record.type === "user" || record.type === "parent" || record.type === "disposed") {
1013
+ return true;
1014
+ }
1015
+ if (record.type === "abort" && (record.message === void 0 || typeof record.message === "string")) {
1016
+ return true;
1017
+ }
1018
+ if (record.type === "timeout" && typeof record.timeoutMs === "number") {
1019
+ return true;
1020
+ }
1021
+ return false;
1022
+ }
1023
+ function cancelCauseFromSignal(signal) {
1024
+ if (!signal?.aborted) return void 0;
1025
+ const reason = signal.reason;
1026
+ if (isCancelCause(reason)) return reason;
1027
+ return { type: "abort" };
1028
+ }
1029
+ function toTurnEndReason(fields) {
1030
+ const { cancelled, turnError, finishReason, cancelCause } = fields;
1031
+ if (cancelled || finishReason === "cancelled") {
1032
+ return { kind: "aborted", cause: cancelCause ?? { type: "user" } };
1033
+ }
1034
+ if (turnError || finishReason === "error") {
1035
+ return {
1036
+ kind: "error",
1037
+ error: turnError ? toToolError(turnError) : { name: "TurnError", message: finishReason }
1038
+ };
1039
+ }
1040
+ switch (finishReason) {
1041
+ case "length":
1042
+ return { kind: "max-tokens" };
1043
+ case "max_steps":
1044
+ return { kind: "max-steps" };
1045
+ case "max_turns":
1046
+ return { kind: "max-turns" };
1047
+ default:
1048
+ return { kind: "completed" };
1049
+ }
1050
+ }
1051
+ function partialStreamContent(events) {
1052
+ let content = "";
1053
+ for (const event of events) {
1054
+ if (event.type === "message_delta") content += event.delta;
1055
+ }
1056
+ return content;
1057
+ }
1058
+ async function cancellableDelay(ms, signal) {
1059
+ if (ms <= 0) return !signal?.aborted;
1060
+ if (signal?.aborted) return false;
1061
+ return new Promise((resolve2) => {
1062
+ const timer = setTimeout(() => {
1063
+ signal?.removeEventListener("abort", onAbort);
1064
+ resolve2(true);
1065
+ }, ms);
1066
+ const onAbort = () => {
1067
+ clearTimeout(timer);
1068
+ resolve2(false);
1069
+ };
1070
+ signal?.addEventListener("abort", onAbort, { once: true });
1071
+ });
1072
+ }
92
1073
  var AgentService = class {
93
1074
  constructor(ctx, config = {}) {
94
1075
  this.ctx = ctx;
@@ -98,6 +1079,20 @@ var AgentService = class {
98
1079
  ctx;
99
1080
  config;
100
1081
  inbox;
1082
+ _seriesStarted = false;
1083
+ _persistedRequest = false;
1084
+ /** Call when a request begins an explicit new model-message series. */
1085
+ startSeries() {
1086
+ this._seriesStarted = true;
1087
+ }
1088
+ /** Clear any pending series marker after a completed run. */
1089
+ endSeriesBoundary() {
1090
+ this._seriesStarted = false;
1091
+ }
1092
+ /** True when this service instance has already persisted a model-visible step. */
1093
+ get hasPersistedRequest() {
1094
+ return this._persistedRequest;
1095
+ }
101
1096
  async run(input, options = {}) {
102
1097
  return consumeAgentStream(this._stream(input, options, false));
103
1098
  }
@@ -122,14 +1117,7 @@ var AgentService = class {
122
1117
  const contextBudget = options.contextBudget ?? this.config.contextBudget;
123
1118
  const injected = this.inbox.injected();
124
1119
  this.ctx.emit("agent/start", { input: claimed, options, injected });
125
- let messages = this._initialMessages(claimed);
126
- const systemPrompt = this.inbox.injected().get("agentSystem");
127
- if (typeof systemPrompt === "string" && systemPrompt) {
128
- await session.append("message", { role: "system", content: systemPrompt });
129
- }
130
- if (claimed.text) {
131
- await session.append("message", { role: "user", content: claimed.text });
132
- }
1120
+ let messages = await this._initialMessages(claimed);
133
1121
  const steps = [];
134
1122
  let output = "";
135
1123
  let finishReason = "stop";
@@ -138,141 +1126,336 @@ var AgentService = class {
138
1126
  messages: copyMessages(messages),
139
1127
  injected
140
1128
  });
1129
+ const turn = await session.nextTurn();
1130
+ await session.append("turn/start", {
1131
+ turn,
1132
+ input: claimed.text ?? claimed,
1133
+ reason: "user"
1134
+ });
141
1135
  let index = 0;
142
1136
  let finalTurnGranted = false;
143
- while (index < maxTurns + (finalTurnGranted ? 1 : 0)) {
144
- if (steps.length >= maxSteps) {
145
- finishReason = "max_steps";
146
- break;
147
- }
148
- if (contextBudget) {
149
- messages = await this._enforceContextBudget(session, contextBudget, messages);
150
- }
151
- const stepInput = copyMessages(messages);
152
- this.ctx.emit("agent/step", { index, input: copyMessages(stepInput) });
153
- const availableTools = finalTurnGranted ? [] : tools.list();
154
- const completeOptions = {
155
- maxSteps: maxSteps - steps.length
156
- };
157
- if (options.signal) completeOptions.signal = options.signal;
158
- let completion;
159
- const streamMethod = useStream ? llm.stream : void 0;
160
- if (streamMethod) {
161
- const streamEvents = [];
162
- let cancelled = false;
163
- try {
164
- for await (const event of streamMethod(stepInput, availableTools, completeOptions)) {
165
- streamEvents.push(event);
166
- yield event;
167
- }
168
- } catch (error) {
169
- if (!options.signal?.aborted) throw error;
1137
+ let currentStepIndex;
1138
+ let turnError;
1139
+ try {
1140
+ while (index < maxTurns + (finalTurnGranted ? 1 : 0)) {
1141
+ if (options.signal?.aborted) {
170
1142
  finishReason = "cancelled";
171
- cancelled = true;
1143
+ break;
172
1144
  }
173
- if (cancelled) break;
174
- completion = completionFromStreamEvents(streamEvents);
175
- } else {
176
- try {
177
- completion = await llm.complete(stepInput, availableTools, completeOptions);
178
- } catch (error) {
179
- if (!options.signal?.aborted) throw error;
180
- finishReason = "cancelled";
1145
+ if (steps.length >= maxSteps) {
1146
+ finishReason = "max_steps";
181
1147
  break;
182
1148
  }
183
- }
184
- const toolCalls = completion.toolCalls ?? [];
185
- if (finalTurnGranted && toolCalls.length) {
186
- finishReason = "max_turns";
187
- break;
188
- }
189
- const toolResults = [];
190
- if (toolCalls.length) {
191
- await session.append("message", {
192
- role: "assistant",
193
- content: completion.content ?? ""
194
- });
195
- }
196
- for (const call of toolCalls) {
197
- this.ctx.emit("agent/tool-call", { index, call });
198
- yield { type: "tool/start", index, call };
199
- await session.append("tool-call", {
200
- id: call.id,
201
- name: call.name,
202
- arguments: call.arguments
1149
+ if (contextBudget) {
1150
+ messages = await this._enforceContextBudget(session, contextBudget, messages);
1151
+ }
1152
+ const stepInput = copyMessages(messages);
1153
+ const preStep = this.ctx.waterfall("agent/pre-step", {
1154
+ index,
1155
+ turn,
1156
+ step: index,
1157
+ messages: stepInput,
1158
+ ...options.signal ? { signal: options.signal } : {}
1159
+ }, (payload) => payload);
1160
+ if (!preStep || !Array.isArray(preStep.messages) || !preStep.messages.length) {
1161
+ break;
1162
+ }
1163
+ if (preStep.startsRequestSeries) this._seriesStarted = true;
1164
+ await session.append("step/start", { turn, step: index });
1165
+ currentStepIndex = index;
1166
+ const requestedInput = copyMessages(preStep.messages);
1167
+ this.ctx.emit("agent/step", {
1168
+ index,
1169
+ turn,
1170
+ step: index,
1171
+ input: copyMessages(requestedInput)
203
1172
  });
204
- const toolOptions = { callId: call.id };
205
- if (options.signal) toolOptions.signal = options.signal;
206
- const startedAt = Date.now();
207
- let result;
208
- try {
209
- result = await tools.execute(call.name, call.arguments, toolOptions);
210
- } catch (error) {
211
- result = {
212
- ok: false,
1173
+ const availableTools = finalTurnGranted ? [] : await this._resolveAvailableTools();
1174
+ const completeOptions = {
1175
+ maxSteps: maxSteps - steps.length
1176
+ };
1177
+ 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
+ let llmMessages = copyMessages(requestedInput);
1188
+ let completion;
1189
+ const streamMethod = useStream ? llm.stream : void 0;
1190
+ let attempt = 0;
1191
+ let persistedStepInput = false;
1192
+ while (true) {
1193
+ if (options.signal?.aborted) {
1194
+ finishReason = "cancelled";
1195
+ break;
1196
+ }
1197
+ const streamEvents = [];
1198
+ 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
+ }
1219
+ 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;
1248
+ }
1249
+ completion = await llm.complete(llmMessages, request.tools, request.options);
1250
+ }
1251
+ break;
1252
+ } 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
+ }
1261
+ if (options.signal?.aborted) {
1262
+ finishReason = "cancelled";
1263
+ break;
1264
+ }
1265
+ attempt += 1;
1266
+ const failure = toToolError(error);
1267
+ const decision = await this.ctx.waterfallAsync(
1268
+ "agent/request-error",
1269
+ {
1270
+ index,
1271
+ turn,
1272
+ step: index,
1273
+ messages: copyMessages(llmMessages),
1274
+ tools: request.tools,
1275
+ options: request.options,
1276
+ attempt,
1277
+ error,
1278
+ failure,
1279
+ ...request.options.provider ? { provider: request.options.provider } : {},
1280
+ ...request.options.model ? { model: request.options.model } : {},
1281
+ ...options.signal ? { signal: options.signal } : {}
1282
+ },
1283
+ () => void 0
1284
+ );
1285
+ if (options.signal?.aborted) {
1286
+ finishReason = "cancelled";
1287
+ break;
1288
+ }
1289
+ if (decision?.kind !== "retry") throw error;
1290
+ const retryId = randomUUID2();
1291
+ const delayMs = decision.delayMs ?? 0;
1292
+ await session.append("llm/retry", {
1293
+ retryId,
1294
+ retry: attempt,
1295
+ ...delayMs > 0 ? { delayMs } : {},
1296
+ failure: toToolError(error)
1297
+ });
1298
+ if (!await cancellableDelay(delayMs, options.signal)) {
1299
+ finishReason = "cancelled";
1300
+ break;
1301
+ }
1302
+ await session.append("llm/retry-started", { retryId, retry: attempt });
1303
+ }
1304
+ }
1305
+ if (options.signal?.aborted) break;
1306
+ if (!completion) throw new AgentError("LLM adapter did not produce a completion");
1307
+ const toolCalls = completion.toolCalls ?? [];
1308
+ if (finalTurnGranted && toolCalls.length) {
1309
+ finishReason = "max_turns";
1310
+ break;
1311
+ }
1312
+ const toolResults = [];
1313
+ if (toolCalls.length) {
1314
+ await session.append("assistant/message", {
1315
+ content: completion.content ?? "",
1316
+ toolCalls: toolCalls.map((call) => ({
1317
+ id: call.id,
1318
+ name: call.name,
1319
+ arguments: call.arguments
1320
+ }))
1321
+ });
1322
+ }
1323
+ for (const call of toolCalls) {
1324
+ this.ctx.emit("agent/tool-call", { index, turn, step: index, call });
1325
+ yield { type: "tool/start", index, turn, step: index, call };
1326
+ await session.append("tool/call", {
1327
+ id: call.id,
1328
+ name: call.name,
1329
+ arguments: call.arguments
1330
+ });
1331
+ const toolOptions = { callId: call.id };
1332
+ if (options.signal) toolOptions.signal = options.signal;
1333
+ const startedAt = Date.now();
1334
+ let result;
1335
+ try {
1336
+ result = await tools.execute(call.name, call.arguments, toolOptions);
1337
+ } catch (error) {
1338
+ result = {
1339
+ ok: false,
1340
+ name: call.name,
1341
+ callId: call.id,
1342
+ input: call.arguments,
1343
+ error: toToolError(error),
1344
+ startedAt,
1345
+ durationMs: Date.now() - startedAt
1346
+ };
1347
+ }
1348
+ toolResults.push(result);
1349
+ const toolResultPayload = {
1350
+ id: call.id,
1351
+ toolCallId: call.id,
213
1352
  name: call.name,
214
- callId: call.id,
215
- input: call.arguments,
216
- error: toToolError(error),
217
- startedAt,
218
- durationMs: Date.now() - startedAt
1353
+ ok: result.ok,
1354
+ durationMs: result.durationMs
219
1355
  };
1356
+ if (result.output !== void 0) toolResultPayload.output = result.output;
1357
+ if (result.error) {
1358
+ toolResultPayload.error = {
1359
+ name: result.error.name,
1360
+ message: result.error.message
1361
+ };
1362
+ if (result.error.stack) toolResultPayload.error.stack = result.error.stack;
1363
+ }
1364
+ await session.append("tool/result", toolResultPayload);
1365
+ yield { type: "tool/end", index, turn, step: index, call, result };
1366
+ this.ctx.emit("agent/tool-result", {
1367
+ index,
1368
+ turn,
1369
+ step: index,
1370
+ call,
1371
+ result
1372
+ });
220
1373
  }
221
- toolResults.push(result);
222
- const toolResultPayload = {
223
- id: call.id,
224
- toolCallId: call.id,
225
- name: call.name,
226
- ok: result.ok,
227
- durationMs: result.durationMs
228
- };
229
- if (result.output !== void 0) toolResultPayload.output = result.output;
230
- if (result.error) {
231
- toolResultPayload.error = {
232
- name: result.error.name,
233
- message: result.error.message
234
- };
235
- if (result.error.stack) toolResultPayload.error.stack = result.error.stack;
1374
+ if (options.signal?.aborted) {
1375
+ finishReason = "cancelled";
1376
+ break;
1377
+ }
1378
+ steps.push({
1379
+ index,
1380
+ input: copyMessages(llmMessages),
1381
+ completion,
1382
+ toolResults
1383
+ });
1384
+ if (completion.content) {
1385
+ output = completion.content;
1386
+ }
1387
+ if (!toolCalls.length) {
1388
+ await session.append("assistant/message", { content: completion.content ?? "" });
236
1389
  }
237
- await session.append("tool-result", toolResultPayload);
238
- yield { type: "tool/end", index, call, result };
239
- this.ctx.emit("agent/tool-result", { index, call, result });
1390
+ await session.append("step/end", {
1391
+ turn,
1392
+ step: index,
1393
+ finishReason: completion.finishReason,
1394
+ toolCalls: toolCalls.length
1395
+ });
1396
+ currentStepIndex = void 0;
1397
+ const nextMessages = this._extendMessages(llmMessages, completion, toolResults);
1398
+ 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";
1405
+ const keepGoing = await this.ctx.serial("agent/turn-stopping", {
1406
+ index,
1407
+ turn,
1408
+ steps: copySteps(steps),
1409
+ finishReason
1410
+ });
1411
+ if (!keepGoing) break;
1412
+ }
1413
+ if (index + 1 >= maxTurns && !finalTurnGranted) {
1414
+ finalTurnGranted = true;
1415
+ } else if (index + 1 >= maxTurns + (finalTurnGranted ? 1 : 0)) {
1416
+ finishReason = "max_turns";
1417
+ break;
1418
+ }
1419
+ if (toolCalls.length) finishReason = "tool_calls";
1420
+ index += 1;
1421
+ messages = nextMessages;
240
1422
  }
241
- if (options.signal?.aborted) {
242
- finishReason = "cancelled";
243
- break;
1423
+ } catch (error) {
1424
+ turnError = error;
1425
+ throw error;
1426
+ } finally {
1427
+ const cancelCause = cancelCauseFromSignal(options.signal);
1428
+ if (currentStepIndex !== void 0) {
1429
+ const cancelled = options.signal?.aborted;
1430
+ await session.append("step/end", {
1431
+ turn,
1432
+ step: currentStepIndex,
1433
+ finishReason: cancelled ? "cancelled" : turnError ? "error" : "interrupted",
1434
+ interrupted: true,
1435
+ ...cancelCause ? { cancelCause } : {},
1436
+ ...turnError ? { error: toToolError(turnError) } : {}
1437
+ });
244
1438
  }
245
- steps.push({
246
- index,
247
- input: stepInput,
248
- completion,
249
- toolResults
1439
+ await session.append("turn/end", {
1440
+ turn,
1441
+ finishReason: turnError ? options.signal?.aborted ? "cancelled" : "error" : finishReason,
1442
+ reason: toTurnEndReason({
1443
+ cancelled: options.signal?.aborted ?? false,
1444
+ turnError,
1445
+ finishReason: turnError ? options.signal?.aborted ? "cancelled" : "error" : finishReason,
1446
+ ...cancelCause ? { cancelCause } : {}
1447
+ }),
1448
+ ...cancelCause ? { cancelCause } : {},
1449
+ ...output ? { output } : {},
1450
+ ...steps.length ? { steps: steps.length } : {},
1451
+ ...turnError ? { interrupted: true, error: toToolError(turnError) } : {}
250
1452
  });
251
- if (completion.content) {
252
- output = completion.content;
253
- }
254
- if (!toolCalls.length) {
255
- await session.append("message", { role: "assistant", content: completion.content ?? "" });
256
- }
257
- const nextMessages = this._extendMessages(messages, completion, toolResults);
258
- if (toolCalls.length === 0) {
259
- finishReason = completion.finishReason === "length" ? "length" : completion.finishReason === "error" ? "error" : "stop";
260
- break;
261
- }
262
- if (index + 1 >= maxTurns && !finalTurnGranted) {
263
- finalTurnGranted = true;
264
- } else if (index + 1 >= maxTurns + (finalTurnGranted ? 1 : 0)) {
265
- finishReason = "max_turns";
266
- break;
267
- }
268
- finishReason = "tool_calls";
269
- index += 1;
270
- messages = nextMessages;
271
1453
  }
272
1454
  const runResult = {
273
1455
  input: claimed,
274
1456
  output,
275
1457
  finishReason,
1458
+ turn,
276
1459
  steps,
277
1460
  messages: copyMessages(messages)
278
1461
  };
@@ -321,9 +1504,118 @@ var AgentService = class {
321
1504
  });
322
1505
  return compactMessages.map((message) => copyMessages([message])[0]);
323
1506
  }
324
- _initialMessages(input) {
325
- const systemPrompt = this.inbox.injected().get("agentSystem");
326
- const systemMessage = typeof systemPrompt === "string" && systemPrompt ? [{ role: "system", content: systemPrompt }] : [];
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
+ };
1524
+ const previousHeader = session.requestHeader();
1525
+ const isFirstRequest = !this._persistedRequest;
1526
+ const isResume = isFirstRequest && previousHeader !== void 0;
1527
+ this._persistedRequest = true;
1528
+ const changedHeader = previousHeader && (previousHeader.system !== nextHeader.system || JSON.stringify(previousHeader.tools ?? []) !== JSON.stringify(nextHeader.tools ?? []) || JSON.stringify(previousHeader.config ?? {}) !== JSON.stringify(nextHeader.config ?? {}));
1529
+ if (isFirstRequest) {
1530
+ await session.append("request/header", {
1531
+ reason: this._seriesStarted ? "series" : isResume ? "resume" : "initial",
1532
+ ...nextHeader,
1533
+ ...this._seriesStarted ? { startsSeries: true } : {}
1534
+ });
1535
+ } else if (changedHeader) {
1536
+ await session.append("request/header", {
1537
+ reason: this._seriesStarted ? "change-series" : "change",
1538
+ ...nextHeader,
1539
+ startsSeries: true
1540
+ });
1541
+ } else if (this._seriesStarted) {
1542
+ await session.append("request/header", {
1543
+ reason: "series",
1544
+ ...nextHeader,
1545
+ startsSeries: true
1546
+ });
1547
+ }
1548
+ this._seriesStarted = false;
1549
+ const nextContext = {
1550
+ ...request.options.provider ? { provider: request.options.provider } : {},
1551
+ ...request.options.model ? { model: request.options.model } : {}
1552
+ };
1553
+ const llmService2 = this.ctx.reflect.get("llm", false);
1554
+ const routeCapacity = llmService2?.routeCapacity?.();
1555
+ if (routeCapacity?.contextWindow !== void 0) {
1556
+ nextContext.contextWindow = routeCapacity.contextWindow;
1557
+ }
1558
+ const previousContext = session.requestContext();
1559
+ if (previousContext?.provider !== nextContext.provider || previousContext?.model !== nextContext.model || previousContext?.contextWindow !== nextContext.contextWindow || previousContext === void 0) {
1560
+ await session.append("request/context", nextContext);
1561
+ }
1562
+ const requested = input.filter(isUserOrSystemMessage);
1563
+ const surface = await session.deriveMessages();
1564
+ const appendDurable = async (messages) => {
1565
+ for (const message of messages) {
1566
+ if (message.role === "user") {
1567
+ await session.append("user/message", {
1568
+ content: message.content,
1569
+ ...message.name ? { name: message.name } : {}
1570
+ });
1571
+ } else {
1572
+ await session.append("system/message", {
1573
+ content: message.content,
1574
+ ...message.name ? { name: message.name } : {}
1575
+ });
1576
+ }
1577
+ }
1578
+ };
1579
+ if (surface.length === 0) {
1580
+ await appendDurable(requested);
1581
+ return;
1582
+ }
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);
1592
+ }
1593
+ async _assertReplayable(session, input) {
1594
+ 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)) {
1609
+ throw new AgentError(
1610
+ `request is not reconstructable from session log: actual=${JSON.stringify(actual)} replay=${JSON.stringify(replay)}`
1611
+ );
1612
+ }
1613
+ }
1614
+ async _initialMessages(input) {
1615
+ const promptService = this.ctx.reflect.get("systemPrompt", false);
1616
+ const assembled = promptService ? (await promptService.assemble()).text.trim() : void 0;
1617
+ const systemPrompt2 = assembled || this.inbox.injected().get("agentSystem");
1618
+ const systemMessage = typeof systemPrompt2 === "string" && systemPrompt2 ? [{ role: "system", content: systemPrompt2 }] : [];
327
1619
  if (input.messages?.length) {
328
1620
  const first = input.messages[0];
329
1621
  if (first?.role === "system") return copyMessages(input.messages);
@@ -349,7 +1641,7 @@ var AgentService = class {
349
1641
  next.push(assistant);
350
1642
  for (const call of toolCalls) {
351
1643
  const result = toolResults.find((candidate) => candidate.callId === call.id);
352
- const content = result ? result.ok ? stringify(result.output) : `error: ${result.error?.message ?? "unknown"}` : "error: missing tool result";
1644
+ const content = result ? result.ok ? stringify2(result.output) : `error: ${result.error?.message ?? "unknown"}` : "error: missing tool result";
353
1645
  const message = {
354
1646
  role: "tool",
355
1647
  content,
@@ -370,6 +1662,7 @@ var AgentService = class {
370
1662
  return next;
371
1663
  }
372
1664
  _session() {
1665
+ if (this.config.session) return this.config.session;
373
1666
  const session = this.ctx.session;
374
1667
  if (!session) throw new AgentError("session service is required");
375
1668
  return session;
@@ -379,8 +1672,19 @@ var AgentService = class {
379
1672
  if (!tools) throw new AgentError("tools service is required");
380
1673
  return tools;
381
1674
  }
1675
+ async _resolveAvailableTools() {
1676
+ const executable = this._tools().list();
1677
+ const promptService = this.ctx.reflect.get("systemPrompt", false);
1678
+ if (!promptService) return executable;
1679
+ const schemas = await promptService.toolSchemas();
1680
+ if (!schemas.length) return executable;
1681
+ const byName = new Map(executable.map((tool) => [tool.schema.name, tool]));
1682
+ return schemas.map((schema) => byName.get(schema.name)).filter((tool) => tool !== void 0);
1683
+ }
382
1684
  _llm() {
383
- return this.config.llm;
1685
+ if (this.config.llm) return this.config.llm;
1686
+ const service = this.ctx.reflect.get("llm", false);
1687
+ return service?.current();
384
1688
  }
385
1689
  };
386
1690
  function defaultContextSummary(messages, usage) {
@@ -506,6 +1810,15 @@ function defineAgent(definition) {
506
1810
  agentService = ctx.reflect.get("agent");
507
1811
  if (system) agentService.inbox.inject("agentSystem", system);
508
1812
  }
1813
+ if (system) {
1814
+ const prompt = ctx.reflect.get("systemPrompt");
1815
+ if (prompt) {
1816
+ ctx.fiber.effect(() => prompt.registerSection({
1817
+ name: `${name2}:persona`,
1818
+ content: system
1819
+ }), `ctx.systemPrompt.section(${JSON.stringify(name2)})`);
1820
+ }
1821
+ }
509
1822
  for (const tool of definition.tools ?? []) {
510
1823
  dynamic.tools.register(tool);
511
1824
  }
@@ -520,12 +1833,1075 @@ function defineAgent(definition) {
520
1833
  }
521
1834
  };
522
1835
  }
1836
+
1837
+ // packages/agent/src/live.ts
1838
+ import { randomUUID as randomUUID4 } from "node:crypto";
1839
+
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;
1851
+ }
1852
+ _session;
1853
+ _nextTurn = [];
1854
+ _nextStep = [];
1855
+ get size() {
1856
+ return this._nextTurn.length + this._nextStep.length;
1857
+ }
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;
1876
+ }
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;
1895
+ }
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;
1916
+ }
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
+ });
1932
+ }
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
+ });
1941
+ }
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
+ });
1952
+ }
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
+ });
1960
+ }
1961
+ }
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
+ }]
1981
+ });
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
+ }]
2008
+ });
2009
+ return replacement;
2010
+ }
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;
2022
+ }
2023
+ /** Return the first pending message by id across both queues. */
2024
+ get(messageId) {
2025
+ return this._find(messageId)?.message;
2026
+ }
2027
+ snapshot() {
2028
+ return {
2029
+ nextTurn: [...this._nextTurn],
2030
+ nextStep: [...this._nextStep]
2031
+ };
2032
+ }
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] };
2038
+ }
2039
+ return void 0;
2040
+ }
2041
+ static async restore(session) {
2042
+ const inbox = new _DurableInbox(session);
2043
+ inbox._applyAll(await session.read());
2044
+ return inbox;
2045
+ }
2046
+ _applyAll(events) {
2047
+ for (const event of events) {
2048
+ if (event.type !== "agent/inbox/spliced") continue;
2049
+ const payload = event.payload;
2050
+ const list = payload.target === "next-step" ? this._nextStep : this._nextTurn;
2051
+ const count = payload.deleteCount ?? 0;
2052
+ if (count === Number.POSITIVE_INFINITY) {
2053
+ list.length = 0;
2054
+ } else if (count > 0 && payload.index === 0) {
2055
+ list.splice(0, Math.min(count, list.length));
2056
+ } else if (count > 0 && payload.index !== void 0) {
2057
+ list.splice(payload.index, Math.min(count, list.length - payload.index));
2058
+ }
2059
+ let insertedOffset = 0;
2060
+ for (const item of payload.inserted ?? []) {
2061
+ const message = {
2062
+ id: item.id,
2063
+ ...item.content !== void 0 ? { text: item.content } : {},
2064
+ ...item.payload !== void 0 ? { content: item.payload } : {}
2065
+ };
2066
+ if (item.mode === "steer") {
2067
+ list.unshift(message);
2068
+ } else {
2069
+ const at = payload.index === void 0 ? list.length : Math.min(list.length, payload.index + insertedOffset);
2070
+ list.splice(at, 0, message);
2071
+ insertedOffset += 1;
2072
+ }
2073
+ }
2074
+ }
2075
+ }
2076
+ };
2077
+
2078
+ // packages/agent/src/live.ts
2079
+ var LiveAgentImpl = class {
2080
+ constructor(_ctx, _agentCtx, _service, id, inbox, session, durable, meta, manualStreaming = false) {
2081
+ this._ctx = _ctx;
2082
+ this._agentCtx = _agentCtx;
2083
+ this._service = _service;
2084
+ this.id = id;
2085
+ this.inbox = inbox;
2086
+ this.session = session;
2087
+ this.meta = meta;
2088
+ this._durable = durable;
2089
+ this._manualStreaming = manualStreaming;
2090
+ }
2091
+ _ctx;
2092
+ _agentCtx;
2093
+ _service;
2094
+ id;
2095
+ inbox;
2096
+ session;
2097
+ meta;
2098
+ _status = "idle";
2099
+ _disposed = false;
2100
+ _controller;
2101
+ _drainPromise;
2102
+ _busy = false;
2103
+ _writeTail = Promise.resolve();
2104
+ _pendingWrite;
2105
+ _sessionStarted = false;
2106
+ _maintenance;
2107
+ _maintenanceBusy = false;
2108
+ _setupDisposers = [];
2109
+ _scopeClosed = false;
2110
+ _durable;
2111
+ _agentSystem;
2112
+ _manualStreaming = false;
2113
+ get agentType() {
2114
+ return this.meta.agentType;
2115
+ }
2116
+ get mode() {
2117
+ return this.meta.mode;
2118
+ }
2119
+ get owner() {
2120
+ return this.meta.owner;
2121
+ }
2122
+ get parentSessionId() {
2123
+ return this.meta.parentSessionId;
2124
+ }
2125
+ get agentCtx() {
2126
+ return this._agentCtx;
2127
+ }
2128
+ trackSetupDispose(dispose) {
2129
+ if (this._scopeClosed) void dispose();
2130
+ else this._setupDisposers.push(dispose);
2131
+ }
2132
+ get status() {
2133
+ return this._status;
2134
+ }
2135
+ get ctx() {
2136
+ return this._ctx;
2137
+ }
2138
+ followup(input) {
2139
+ this._send(input, "followup");
2140
+ }
2141
+ /** Replace one pending inbox message by durable message id. */
2142
+ replaceMessage(messageId, input) {
2143
+ this._mutatePending(async () => {
2144
+ const previous = this._durable.get(messageId);
2145
+ if (!previous) return;
2146
+ const replacement = await this._durable.replace(messageId, {
2147
+ text: input.text ?? "",
2148
+ ...input.messages !== void 0 || input.context !== void 0 ? { content: input.messages ?? input.context } : {}
2149
+ });
2150
+ if (!replacement) return;
2151
+ this._ctx.emit("agent/inbox/discarded", {
2152
+ id: this.id,
2153
+ agent: this,
2154
+ message: previous
2155
+ });
2156
+ this._ctx.emit("agent/inbox/inserted", {
2157
+ id: this.id,
2158
+ agent: this,
2159
+ target: this._findTarget(messageId) ?? "next-turn",
2160
+ input: replacement,
2161
+ inboxSeq: this._durable.size
2162
+ });
2163
+ });
2164
+ }
2165
+ /** Remove one pending inbox message by durable message id. */
2166
+ removeMessage(messageId) {
2167
+ this._mutatePending(async () => {
2168
+ const removed = await this._durable.remove(messageId);
2169
+ if (!removed) return;
2170
+ this._ctx.emit("agent/inbox/discarded", {
2171
+ id: this.id,
2172
+ agent: this,
2173
+ message: removed
2174
+ });
2175
+ });
2176
+ }
2177
+ steer(input) {
2178
+ this._send(input, "steer");
2179
+ }
2180
+ inject(key, value) {
2181
+ if (key === "agentSystem" && typeof value === "string") {
2182
+ this._agentSystem = value;
2183
+ }
2184
+ }
2185
+ send(input, options = {}) {
2186
+ this._send(input, options.mode === "steer" ? "steer" : "followup");
2187
+ }
2188
+ _send(input, target) {
2189
+ if (this._disposed) throw new Error(`agent disposed: ${this.id}`);
2190
+ const text = input.text ?? "";
2191
+ 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", {
2195
+ id: this.id,
2196
+ target,
2197
+ input: message,
2198
+ 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
+ });
2209
+ }
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;
2219
+ });
2220
+ }
2221
+ _findTarget(messageId) {
2222
+ const snapshot = this._durable.snapshot();
2223
+ if (snapshot.nextStep.some((message) => message.id === messageId)) return "next-step";
2224
+ if (snapshot.nextTurn.some((message) => message.id === messageId)) return "next-turn";
2225
+ return void 0;
2226
+ }
2227
+ cancel(cause, options = {}) {
2228
+ if (!options.keepInbox) {
2229
+ const task = this._writeTail.then(async () => {
2230
+ const before = this._durable.snapshot();
2231
+ for (const message of [...before.nextStep, ...before.nextTurn]) {
2232
+ this._ctx.emit("agent/inbox/discarded", { id: this.id, message });
2233
+ }
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
+ });
2243
+ }
2244
+ this._controller?.abort(cause);
2245
+ this._maintenance?.controller.abort(cause);
2246
+ }
2247
+ async whenIdle() {
2248
+ while (this._pendingWrite) await this._pendingWrite;
2249
+ while (true) {
2250
+ const maintenance = this._maintenance;
2251
+ if (maintenance) {
2252
+ await maintenance.promise.catch(() => void 0);
2253
+ continue;
2254
+ }
2255
+ const drain = this._drainPromise;
2256
+ if (drain) {
2257
+ await drain.catch(() => void 0);
2258
+ continue;
2259
+ }
2260
+ if (!this._durable.size) break;
2261
+ await new Promise((resolve2) => setTimeout(resolve2, 0));
2262
+ }
2263
+ }
2264
+ /** Run one non-turn maintenance task from the true idle phase. */
2265
+ runMaintenance(task) {
2266
+ if (this._disposed) throw new Error(`agent disposed: ${this.id}`);
2267
+ if (this._maintenanceBusy || this._drainPromise || this._pendingWrite) {
2268
+ throw new Error("agent is busy; maintenance can only run while idle");
2269
+ }
2270
+ this._maintenanceBusy = true;
2271
+ const controller = new AbortController();
2272
+ const result = Promise.resolve().then(async () => {
2273
+ const value = await task(controller.signal);
2274
+ return value;
2275
+ }).finally(() => {
2276
+ this._maintenanceBusy = false;
2277
+ if (this._maintenance?.controller === controller) {
2278
+ this._maintenance = void 0;
2279
+ }
2280
+ queueMicrotask(() => this._wake());
2281
+ });
2282
+ this._maintenance = { promise: result.then(() => void 0, () => void 0), controller };
2283
+ return result;
2284
+ }
2285
+ async dispose() {
2286
+ if (this._disposed) return;
2287
+ this._disposed = true;
2288
+ this.cancel({ type: "disposed" });
2289
+ await this._writeTail.catch(() => void 0);
2290
+ await this._drainPromise?.catch(() => void 0);
2291
+ if (this._maintenance) this._maintenance.controller.abort({ type: "disposed" });
2292
+ await this._maintenance?.promise.catch(() => void 0);
2293
+ if (!this._scopeClosed) {
2294
+ this._scopeClosed = true;
2295
+ await Promise.all([...this._setupDisposers].reverse().map((dispose) => dispose()));
2296
+ }
2297
+ const writeTail = this._writeTail;
2298
+ if (writeTail !== this._drainPromise && this._pendingWrite) {
2299
+ this._pendingWrite = void 0;
2300
+ await writeTail.catch(() => void 0);
2301
+ }
2302
+ this._setStatus("idle");
2303
+ }
2304
+ /** Publish the agent lifecycle session-start notification once. */
2305
+ async publishSessionStart(source = "startup") {
2306
+ if (this._sessionStarted) return;
2307
+ this._sessionStarted = true;
2308
+ this._ctx.emit("agent/session-start", {
2309
+ id: this.id,
2310
+ agent: this,
2311
+ source
2312
+ });
2313
+ }
2314
+ /** Current durable turn number for a newly claimed inbox item. */
2315
+ async _nextTurnNumber() {
2316
+ const events = await this.session.read();
2317
+ let max = 0;
2318
+ for (const event of events) {
2319
+ if (event.type === "turn/start") max = Math.max(max, event.payload.turn);
2320
+ }
2321
+ return max + 1;
2322
+ }
2323
+ /** Start draining currently pending durable work without a new input. */
2324
+ wakeNow() {
2325
+ this._wake();
2326
+ }
2327
+ _wake() {
2328
+ if (this._disposed || this._manualStreaming || this._drainPromise || this._busy || this._maintenanceBusy) return;
2329
+ const task = this._drainAll();
2330
+ this._drainPromise = task;
2331
+ task.finally(() => {
2332
+ if (this._drainPromise === task) this._drainPromise = void 0;
2333
+ });
2334
+ }
2335
+ async _drainAll() {
2336
+ await this._writeTail.catch(() => void 0);
2337
+ if (this._durable.size) {
2338
+ for await (const _ of this._streamTurns()) void _;
2339
+ }
2340
+ }
2341
+ /** Public drain over queued durable work (manual-streaming mode). */
2342
+ async *runTurns(signal) {
2343
+ if (this._manualStreaming) await this._writeTail.catch(() => void 0);
2344
+ if (this._busy || this._disposed) return;
2345
+ yield* this._streamTurns(signal);
2346
+ }
2347
+ async *_streamTurns(signal) {
2348
+ if (this._busy || this._disposed) return;
2349
+ this._busy = true;
2350
+ this._setStatus("running");
2351
+ try {
2352
+ while (!this._disposed) {
2353
+ const batch = await this._durable.claimBatch();
2354
+ if (!batch.length) break;
2355
+ const input = await this._inputForBatch(batch);
2356
+ const turn = await this._nextTurnNumber();
2357
+ for (const message of batch) {
2358
+ this._ctx.emit("agent/inbox/claimed", {
2359
+ id: this.id,
2360
+ agent: this,
2361
+ message,
2362
+ input: { text: message.text ?? "" },
2363
+ turn
2364
+ });
2365
+ }
2366
+ const controller = new AbortController();
2367
+ this._controller = controller;
2368
+ const forward = () => controller.abort(signal?.reason);
2369
+ if (signal?.aborted) controller.abort(signal.reason);
2370
+ else signal?.addEventListener("abort", forward, { once: true });
2371
+ try {
2372
+ if (this._manualStreaming) {
2373
+ yield* this._service.runStream(input, { signal: controller.signal });
2374
+ } else {
2375
+ await this._service.run(input, { signal: controller.signal });
2376
+ }
2377
+ } catch (error) {
2378
+ if (!controller.signal.aborted) {
2379
+ const step = await this._failedStep();
2380
+ this._ctx.emit("agent/error", {
2381
+ id: this.id,
2382
+ agent: this,
2383
+ error,
2384
+ ...step !== void 0 ? { turn, step } : {}
2385
+ });
2386
+ if (this._manualStreaming) throw error;
2387
+ }
2388
+ } finally {
2389
+ signal?.removeEventListener("abort", forward);
2390
+ if (this._controller === controller) this._controller = void 0;
2391
+ }
2392
+ }
2393
+ } finally {
2394
+ this._busy = false;
2395
+ this._setStatus("idle");
2396
+ }
2397
+ }
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] };
2413
+ }
2414
+ return { text: plainMessages[0]?.content ?? "" };
2415
+ }
2416
+ _setStatus(status) {
2417
+ const previous = this._status;
2418
+ if (previous === status) return;
2419
+ this._status = status;
2420
+ this._ctx.emit("agent/status", { id: this.id, status, previous });
2421
+ }
2422
+ async _failedStep() {
2423
+ const events = await this.session.read();
2424
+ for (let index = events.length - 1; index >= 0; index -= 1) {
2425
+ const event = events[index];
2426
+ if (event?.type === "step/end" && (event.payload.finishReason === "error" || event.payload.interrupted === true)) {
2427
+ return event.payload.step;
2428
+ }
2429
+ }
2430
+ return void 0;
2431
+ }
2432
+ };
2433
+ var AgentRegistry = class {
2434
+ constructor(_ctx) {
2435
+ this._ctx = _ctx;
2436
+ }
2437
+ _ctx;
2438
+ _agents = /* @__PURE__ */ new Map();
2439
+ _owners = /* @__PURE__ */ new Map();
2440
+ _factory;
2441
+ setFactory(factory) {
2442
+ this._factory = factory;
2443
+ return () => {
2444
+ if (this._factory === factory) this._factory = void 0;
2445
+ };
2446
+ }
2447
+ async create(options) {
2448
+ if (!this._factory) throw new Error("no agent factory registered");
2449
+ const id = options.id ?? options.sessionId ?? randomUUID4();
2450
+ if (this._agents.has(id)) throw new Error(`agent already registered: ${id}`);
2451
+ const handle = await this._factory.create({ ...options, id });
2452
+ this._agents.set(id, handle.agent);
2453
+ this._owners.set(id, handle.agent.owner);
2454
+ this._ctx.emit("agent/created", { id, agent: handle.agent });
2455
+ return {
2456
+ agent: handle.agent,
2457
+ dispose: () => this._dispose(id, handle)
2458
+ };
2459
+ }
2460
+ async resume(options) {
2461
+ if (!this._factory) throw new Error("no agent factory registered");
2462
+ if (!options.id && !options.sessionId) {
2463
+ throw new AgentError("agent resume requires a stable session id");
2464
+ }
2465
+ const id = options.id ?? options.sessionId;
2466
+ if (this._agents.has(id)) throw new Error(`agent already registered: ${id}`);
2467
+ const handle = await this._factory.resume({ ...options, id });
2468
+ this._agents.set(id, handle.agent);
2469
+ this._owners.set(id, handle.agent.owner);
2470
+ this._ctx.emit("agent/created", { id, agent: handle.agent });
2471
+ return {
2472
+ agent: handle.agent,
2473
+ dispose: () => this._dispose(id, handle)
2474
+ };
2475
+ }
2476
+ get(id) {
2477
+ return this._agents.get(id);
2478
+ }
2479
+ list() {
2480
+ return [...this._agents.values()];
2481
+ }
2482
+ roots() {
2483
+ return this.list().filter((agent2) => this._owners.get(agent2.id) === void 0);
2484
+ }
2485
+ isOwnedBy(id, owner) {
2486
+ return this._owners.get(id) === owner;
2487
+ }
2488
+ async _dispose(id, handle) {
2489
+ const agent2 = this._agents.get(id);
2490
+ if (!agent2) return;
2491
+ await handle.dispose();
2492
+ this._agents.delete(id);
2493
+ this._owners.delete(id);
2494
+ this._ctx.emit("agent/disposed", { id });
2495
+ }
2496
+ };
2497
+ async function buildHandle(ctx, options, resume = false) {
2498
+ const fileMeta = await readAgentMeta(options.file);
2499
+ if (resume && !fileMeta) {
2500
+ throw new AgentError(`no agent session meta found for resume: ${options.file}`);
2501
+ }
2502
+ const requestedId = options.id ?? options.sessionId ?? randomUUID4();
2503
+ if (fileMeta?.agentId && fileMeta.agentId !== requestedId) {
2504
+ throw new AgentError(
2505
+ `agent id mismatch: session belongs to ${fileMeta.agentId}, requested ${requestedId}`
2506
+ );
2507
+ }
2508
+ const agentId = fileMeta?.agentId ?? requestedId;
2509
+ 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);
2581
+ const agentScope = await ctx.inject([], (scopeCtx) => {
2582
+ scopeCtx.isolate("agentScope").provide("agentScope", scopeCtx);
2583
+ });
2584
+ 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;
2600
+ try {
2601
+ setupResult = await options.setup?.(agentCtx);
2602
+ setupResult?.commit();
2603
+ } catch (error) {
2604
+ await agent2.dispose().catch(() => void 0);
2605
+ await log.close().catch(() => void 0);
2606
+ throw error;
2607
+ }
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);
2614
+ }
2615
+ return { agent: agent2, dispose: () => agent2.dispose() };
2616
+ }
2617
+ async function readAgentMeta(file) {
2618
+ const lines = await import("node:fs/promises").then((fs) => fs.readFile(file, "utf8")).catch((error) => {
2619
+ if (error.code === "ENOENT") return void 0;
2620
+ throw error;
2621
+ });
2622
+ if (lines === void 0) return void 0;
2623
+ for (const line of lines.split("\n")) {
2624
+ if (!line.trim()) continue;
2625
+ let record;
2626
+ try {
2627
+ const parsed = JSON.parse(line);
2628
+ record = parsed && typeof parsed === "object" ? parsed : {};
2629
+ } catch {
2630
+ continue;
2631
+ }
2632
+ if (record.type !== "meta") continue;
2633
+ const payload = record.payload;
2634
+ if (!payload || payload.kind !== "agent") continue;
2635
+ return {
2636
+ ...typeof payload.agentId === "string" ? { agentId: payload.agentId } : {},
2637
+ ...typeof payload.createdAt === "number" ? { createdAt: payload.createdAt } : {}
2638
+ };
2639
+ }
2640
+ return void 0;
2641
+ }
2642
+ function readDurableAgentMeta(events) {
2643
+ for (let index = events.length - 1; index >= 0; index -= 1) {
2644
+ const event = events[index];
2645
+ if (event?.type !== "meta") continue;
2646
+ const payload = event.payload;
2647
+ if (payload.kind !== "agent") continue;
2648
+ const meta = {
2649
+ agentId: typeof payload.agentId === "string" ? payload.agentId : ""
2650
+ };
2651
+ if (payload.agentType === "general" || payload.agentType === "coding") {
2652
+ meta.agentType = payload.agentType;
2653
+ }
2654
+ if (payload.mode === "auto" || payload.mode === "plan" || payload.mode === "execute") {
2655
+ meta.mode = payload.mode;
2656
+ }
2657
+ if (typeof payload.title === "string") meta.title = payload.title;
2658
+ if (typeof payload.owner === "string") meta.owner = payload.owner;
2659
+ if (typeof payload.parentSessionId === "string") {
2660
+ meta.parentSessionId = payload.parentSessionId;
2661
+ }
2662
+ if (typeof payload.forkedAtMessageId === "string") {
2663
+ meta.forkedAtMessageId = payload.forkedAtMessageId;
2664
+ }
2665
+ if (typeof payload.createdAt === "number") meta.createdAt = payload.createdAt;
2666
+ return meta;
2667
+ }
2668
+ return void 0;
2669
+ }
2670
+ var agents = {
2671
+ name: "agents",
2672
+ inject: ["tools"],
2673
+ apply: (ctx) => {
2674
+ const registry = new AgentRegistry(ctx);
2675
+ ctx.provide("agents", registry);
2676
+ registry.setFactory({
2677
+ create: (options) => buildHandle(ctx, options),
2678
+ resume: (options) => buildHandle(ctx, options, true)
2679
+ });
2680
+ return () => {
2681
+ for (const agent2 of registry.list()) {
2682
+ void agent2.dispose();
2683
+ }
2684
+ };
2685
+ }
2686
+ };
2687
+
2688
+ // packages/agent/src/prompt.ts
2689
+ var SystemPromptService = class {
2690
+ _sections = /* @__PURE__ */ new Map();
2691
+ _toolProviders = [];
2692
+ _variables = /* @__PURE__ */ new Map();
2693
+ _contexts = [];
2694
+ _ctx;
2695
+ constructor(ctx) {
2696
+ this._ctx = ctx;
2697
+ }
2698
+ registerSection(section) {
2699
+ const name2 = section.name.trim();
2700
+ if (!name2) throw new TypeError("prompt section requires a non-empty name");
2701
+ if (this._sections.has(name2)) {
2702
+ throw new Error(`prompt section already registered: ${name2}`);
2703
+ }
2704
+ this._sections.set(name2, { ...section, name: name2 });
2705
+ this._emitChange();
2706
+ return () => {
2707
+ this._sections.delete(name2);
2708
+ this._emitChange();
2709
+ };
2710
+ }
2711
+ sections() {
2712
+ return [...this._sections.values()].sort((left, right) => (left.order ?? 0) - (right.order ?? 0));
2713
+ }
2714
+ registerTools(provider) {
2715
+ if (typeof provider !== "function") throw new TypeError("tool provider must be a function");
2716
+ this._toolProviders.push(provider);
2717
+ this._emitChange();
2718
+ return () => {
2719
+ const index = this._toolProviders.indexOf(provider);
2720
+ if (index >= 0) this._toolProviders.splice(index, 1);
2721
+ this._emitChange();
2722
+ };
2723
+ }
2724
+ async toolSchemas(options = {}) {
2725
+ const seen = /* @__PURE__ */ new Set();
2726
+ const tools = [];
2727
+ for (const provider of this._toolProviders) {
2728
+ for (const schema of await provider(options)) {
2729
+ if (!seen.has(schema.name)) {
2730
+ seen.add(schema.name);
2731
+ tools.push(schema);
2732
+ }
2733
+ }
2734
+ }
2735
+ return tools;
2736
+ }
2737
+ registerVariable(name2, provider) {
2738
+ const key = name2.trim();
2739
+ if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(key)) {
2740
+ throw new TypeError(`invalid prompt variable name: ${name2}`);
2741
+ }
2742
+ if (typeof provider !== "function") throw new TypeError("variable provider must be a function");
2743
+ if (this._variables.has(key)) throw new Error(`prompt variable already registered: ${key}`);
2744
+ this._variables.set(key, provider);
2745
+ this._emitChange();
2746
+ return () => {
2747
+ this._variables.delete(key);
2748
+ this._emitChange();
2749
+ };
2750
+ }
2751
+ registerContext(context) {
2752
+ const name2 = context.name.trim();
2753
+ if (!name2) throw new TypeError("prompt context requires a non-empty name");
2754
+ if (this._contexts.some((entry) => entry.name === name2)) {
2755
+ throw new Error(`prompt context already registered: ${name2}`);
2756
+ }
2757
+ this._contexts.push({ ...context, name: name2 });
2758
+ this._emitChange();
2759
+ return () => {
2760
+ this._contexts = this._contexts.filter((entry) => entry.name !== name2);
2761
+ this._emitChange();
2762
+ };
2763
+ }
2764
+ async assemble(options = {}, ctx) {
2765
+ const activeCtx = ctx ?? this._ctx;
2766
+ const sections = this.sections();
2767
+ const variables = await this._resolveVariables(options);
2768
+ const renderedSections = sections.map((section) => ({
2769
+ ...section,
2770
+ content: renderTemplate(section.content, variables)
2771
+ }));
2772
+ const dynamicContexts = await this._resolveContexts(options);
2773
+ const contextText = dynamicContexts.map((context) => renderTemplate(context.content, variables)).filter(Boolean);
2774
+ const base = {
2775
+ sections: renderedSections,
2776
+ text: [
2777
+ ...renderedSections.map((section) => section.content).filter(Boolean),
2778
+ ...contextText
2779
+ ].join("\n\n"),
2780
+ ...Object.keys(variables).length ? { variables } : {},
2781
+ ...Object.keys(options).length ? { context: options } : {}
2782
+ };
2783
+ const tools = await this.toolSchemas(options);
2784
+ if (tools.length) base.tools = tools;
2785
+ if (!activeCtx) return base;
2786
+ const resolved = await activeCtx.waterfallAsync(
2787
+ "system-prompt/assemble",
2788
+ base,
2789
+ async (assembly) => assembly
2790
+ );
2791
+ return resolved ?? base;
2792
+ }
2793
+ async _resolveVariables(options) {
2794
+ const result = {};
2795
+ for (const [name2, provider] of this._variables) {
2796
+ const value = await provider(options);
2797
+ if (value !== void 0) result[name2] = value;
2798
+ }
2799
+ return result;
2800
+ }
2801
+ async _resolveContexts(options) {
2802
+ const contexts = this._contexts.slice().sort((left, right) => (left.order ?? 0) - (right.order ?? 0));
2803
+ const result = [];
2804
+ for (const context of contexts) {
2805
+ const value = await context.content(options);
2806
+ if (value) result.push({ name: context.name, content: value });
2807
+ }
2808
+ return result;
2809
+ }
2810
+ _emitChange() {
2811
+ this._ctx?.emit("system-prompt/change");
2812
+ }
2813
+ };
2814
+ function renderTemplate(text, variables) {
2815
+ return text.replace(/\$\{([a-zA-Z][a-zA-Z0-9_]*)\}/g, (match, name2) => {
2816
+ return variables[name2] ?? match;
2817
+ });
2818
+ }
2819
+ var systemPrompt = {
2820
+ name: "system-prompt",
2821
+ apply: (ctx) => {
2822
+ const service = new SystemPromptService(ctx);
2823
+ ctx.provide("systemPrompt", service);
2824
+ return () => {
2825
+ };
2826
+ }
2827
+ };
2828
+
2829
+ // packages/agent/src/llm-service.ts
2830
+ var LlmService = class {
2831
+ _adapters = /* @__PURE__ */ new Map();
2832
+ _current;
2833
+ _defaultName;
2834
+ register(name2, adapter) {
2835
+ const key = name2.trim();
2836
+ if (!key) throw new TypeError("llm provider requires a non-empty name");
2837
+ if (this._adapters.has(key)) throw new Error(`llm provider already registered: ${key}`);
2838
+ this._adapters.set(key, adapter);
2839
+ if (!this._current) {
2840
+ this._current = adapter;
2841
+ this._defaultName = key;
2842
+ }
2843
+ return () => {
2844
+ this._adapters.delete(key);
2845
+ if (this._defaultName === key) {
2846
+ this._defaultName = void 0;
2847
+ this._current = this._adapters.values().next().value;
2848
+ }
2849
+ };
2850
+ }
2851
+ setCurrent(name2) {
2852
+ const adapter = this._adapters.get(name2.trim());
2853
+ if (!adapter) throw new Error(`llm provider not found: ${name2}`);
2854
+ this._current = adapter;
2855
+ this._defaultName = name2.trim();
2856
+ return adapter;
2857
+ }
2858
+ current() {
2859
+ return this._current;
2860
+ }
2861
+ list() {
2862
+ return [...this._adapters.entries()].map(([name2, adapter]) => ({ name: name2, adapter }));
2863
+ }
2864
+ /**
2865
+ * Report provider/model/contextWindow for the current route. The adapter
2866
+ * may be registered with an optional capacity provider by callers that own
2867
+ * the model catalog (for example the CLI runtime).
2868
+ */
2869
+ routeCapacity() {
2870
+ if (!this._current || !this._defaultName) return void 0;
2871
+ const provider = this._defaultName;
2872
+ const model = this._current.model;
2873
+ const contextWindow = this._capacityFor?.(model);
2874
+ const result = { provider, model: model ?? "" };
2875
+ if (contextWindow !== void 0) result.contextWindow = contextWindow;
2876
+ return result;
2877
+ }
2878
+ _capacityFor;
2879
+ /** Install a catalog-backed contextWindow resolver (optional). */
2880
+ setCapacityResolver(resolver) {
2881
+ this._capacityFor = resolver;
2882
+ }
2883
+ };
2884
+ var llmService = {
2885
+ name: "llm-service",
2886
+ apply: (ctx) => {
2887
+ ctx.provide("llm", new LlmService());
2888
+ return () => {
2889
+ };
2890
+ }
2891
+ };
523
2892
  export {
524
2893
  AgentError,
525
2894
  AgentInbox,
2895
+ AgentRegistry,
526
2896
  AgentService,
2897
+ DurableInbox,
2898
+ LlmService,
2899
+ SystemPromptService,
527
2900
  agent,
2901
+ agents,
528
2902
  defineAgent,
529
- name
2903
+ llmService,
2904
+ name,
2905
+ systemPrompt
530
2906
  };
531
2907
  //# sourceMappingURL=agent.js.map