stitchkit 0.85.2 → 0.86.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 (111) hide show
  1. package/CHANGELOG.md +160 -0
  2. package/README.md +3 -0
  3. package/dist/agent-runtime/agent-tool.d.ts +25 -0
  4. package/dist/agent-runtime/agent-tool.d.ts.map +1 -0
  5. package/dist/agent-runtime/child-tools.d.ts +69 -0
  6. package/dist/agent-runtime/child-tools.d.ts.map +1 -0
  7. package/dist/agent-runtime/children.d.ts +113 -0
  8. package/dist/agent-runtime/children.d.ts.map +1 -0
  9. package/dist/agent-runtime/coding-tool-contract.d.ts +29 -0
  10. package/dist/agent-runtime/coding-tool-contract.d.ts.map +1 -1
  11. package/dist/agent-runtime/coding-tool-refusals.d.ts +2 -0
  12. package/dist/agent-runtime/coding-tool-refusals.d.ts.map +1 -1
  13. package/dist/agent-runtime/coding-tool-shell.d.ts.map +1 -1
  14. package/dist/agent-runtime/coding-tools.d.ts +1 -0
  15. package/dist/agent-runtime/coding-tools.d.ts.map +1 -1
  16. package/dist/agent-runtime/control-schema.d.ts +17 -0
  17. package/dist/agent-runtime/control-schema.d.ts.map +1 -1
  18. package/dist/agent-runtime/event-schema.d.ts +34 -0
  19. package/dist/agent-runtime/event-schema.d.ts.map +1 -1
  20. package/dist/agent-runtime/event-search-tools.d.ts +53 -0
  21. package/dist/agent-runtime/event-search-tools.d.ts.map +1 -0
  22. package/dist/agent-runtime/event-search.d.ts +23 -0
  23. package/dist/agent-runtime/event-search.d.ts.map +1 -0
  24. package/dist/agent-runtime/fault-bench.d.ts +45 -0
  25. package/dist/agent-runtime/fault-bench.d.ts.map +1 -0
  26. package/dist/agent-runtime/observability.d.ts +6 -0
  27. package/dist/agent-runtime/observability.d.ts.map +1 -1
  28. package/dist/agent-runtime/projections.d.ts +69 -0
  29. package/dist/agent-runtime/projections.d.ts.map +1 -0
  30. package/dist/agent-runtime/provider-failure.d.ts +5 -1
  31. package/dist/agent-runtime/provider-failure.d.ts.map +1 -1
  32. package/dist/agent-runtime/provider-origin.d.ts +12 -0
  33. package/dist/agent-runtime/provider-origin.d.ts.map +1 -0
  34. package/dist/agent-runtime/retry-policy.d.ts +27 -0
  35. package/dist/agent-runtime/retry-policy.d.ts.map +1 -0
  36. package/dist/agent-runtime/run-execution.d.ts +0 -9
  37. package/dist/agent-runtime/run-execution.d.ts.map +1 -1
  38. package/dist/agent-runtime/run-operation-lifecycle.d.ts.map +1 -1
  39. package/dist/agent-runtime/runtime.d.ts +15 -0
  40. package/dist/agent-runtime/runtime.d.ts.map +1 -1
  41. package/dist/agent-runtime/sandbox.d.ts +66 -0
  42. package/dist/agent-runtime/sandbox.d.ts.map +1 -0
  43. package/dist/agent-runtime/schedule-tools.d.ts +35 -0
  44. package/dist/agent-runtime/schedule-tools.d.ts.map +1 -0
  45. package/dist/agent-runtime/schedules.d.ts +63 -0
  46. package/dist/agent-runtime/schedules.d.ts.map +1 -0
  47. package/dist/agent-runtime/schemas.d.ts +9 -0
  48. package/dist/agent-runtime/schemas.d.ts.map +1 -1
  49. package/dist/agent-runtime/spill.d.ts +24 -0
  50. package/dist/agent-runtime/spill.d.ts.map +1 -0
  51. package/dist/agent-runtime/sqlite-purge.d.ts.map +1 -1
  52. package/dist/agent-runtime/sqlite.d.ts +26 -0
  53. package/dist/agent-runtime/sqlite.d.ts.map +1 -1
  54. package/dist/agent-runtime/state-slots.d.ts +48 -0
  55. package/dist/agent-runtime/state-slots.d.ts.map +1 -0
  56. package/dist/agent-runtime/state-tools.d.ts +58 -0
  57. package/dist/agent-runtime/state-tools.d.ts.map +1 -0
  58. package/dist/agent-runtime/store-driver.d.ts +13 -0
  59. package/dist/agent-runtime/store-driver.d.ts.map +1 -1
  60. package/dist/agent-runtime/store-events.d.ts +829 -0
  61. package/dist/agent-runtime/store-events.d.ts.map +1 -0
  62. package/dist/agent-runtime/store-migrations/v1-to-v2.d.ts +6 -0
  63. package/dist/agent-runtime/store-migrations/v1-to-v2.d.ts.map +1 -0
  64. package/dist/agent-runtime/store.d.ts +45 -0
  65. package/dist/agent-runtime/store.d.ts.map +1 -1
  66. package/dist/agent-runtime/terminal-commit.d.ts +1 -1
  67. package/dist/agent-runtime-browser.js +5 -3
  68. package/dist/agent-runtime-coding-tools.js +144 -15
  69. package/dist/agent-runtime-harness.js +12 -10
  70. package/dist/agent-runtime-sqlite-bun.d.ts +1 -1
  71. package/dist/agent-runtime-sqlite-bun.d.ts.map +1 -1
  72. package/dist/agent-runtime-sqlite-bun.js +4 -3
  73. package/dist/agent-runtime-sqlite-node.d.ts +1 -1
  74. package/dist/agent-runtime-sqlite-node.d.ts.map +1 -1
  75. package/dist/agent-runtime-sqlite-node.js +4 -3
  76. package/dist/agent-runtime-testing.d.ts +3 -0
  77. package/dist/agent-runtime-testing.d.ts.map +1 -0
  78. package/dist/agent-runtime-testing.js +156 -0
  79. package/dist/agent-runtime.d.ts +16 -2
  80. package/dist/agent-runtime.d.ts.map +1 -1
  81. package/dist/agent-runtime.js +1591 -154
  82. package/dist/application.js +14 -14
  83. package/dist/cli.js +6 -6
  84. package/dist/{index-fwbkp2jn.js → index-0vfdh685.js} +2 -2
  85. package/dist/{index-5z8tezpx.js → index-0ydchz76.js} +3 -3
  86. package/dist/{index-3ds42w1w.js → index-1szetq6f.js} +33 -4
  87. package/dist/{index-2jg7b11j.js → index-33xckd7h.js} +879 -422
  88. package/dist/{index-27487ay5.js → index-9hw9eq48.js} +4 -4
  89. package/dist/{index-66nxrgy0.js → index-b7qwhbzq.js} +428 -46
  90. package/dist/index-cnyk6te3.js +77 -0
  91. package/dist/index-gskm1k5a.js +50 -0
  92. package/dist/{index-fq492hg0.js → index-hber83mk.js} +254 -157
  93. package/dist/{index-4hk633vz.js → index-hkm6wysp.js} +9 -2
  94. package/dist/{index-dx9xehwt.js → index-kc6h6hg0.js} +1 -1
  95. package/dist/{index-x15ss2dx.js → index-p1b1y93x.js} +3 -0
  96. package/dist/{index-mgtxfe6b.js → index-r159gjwy.js} +8 -1
  97. package/dist/{index-s4ckmrac.js → index-rcgsnrr6.js} +3 -3
  98. package/dist/index-vfgb58nf.js +252 -0
  99. package/dist/{index-dggwnnjv.js → index-wn9cb82y.js} +1 -1
  100. package/dist/{index-ff0kcqvp.js → index-z2452tsw.js} +3 -3
  101. package/dist/testing/agent-store-conformance.d.ts.map +1 -1
  102. package/dist/testing.js +54 -80
  103. package/dist/tool-invoker.js +4 -4
  104. package/dist/tools/internal/surface-projector.d.ts.map +1 -1
  105. package/dist/tools/manifest.d.ts +16 -0
  106. package/dist/tools/manifest.d.ts.map +1 -1
  107. package/dist/tools.d.ts +1 -1
  108. package/dist/tools.d.ts.map +1 -1
  109. package/dist/tools.js +10 -8
  110. package/llms-full.txt +321 -13
  111. package/package.json +6 -1
@@ -1,10 +1,5 @@
1
- import {
2
- messageOf
3
- } from "./index-p13mwz16.js";
4
1
  import {
5
2
  ACTIVE_AGENT_RUN_STATES,
6
- AcceptInputAndAssignRunSchema,
7
- AcquireAgentRunSchema,
8
3
  AgentAdmissionReceiptSchema,
9
4
  AgentConversationMessagePageSchema,
10
5
  AgentConversationPageSchema,
@@ -13,43 +8,57 @@ import {
13
8
  AgentConversationPurgedError,
14
9
  AgentConversationSummarySchema,
15
10
  AgentHistoryMutationSchema,
16
- AgentRecoverableDescriptorSchema,
17
- AgentRecoverablePageSchema,
18
- AgentRunViewSchema,
19
11
  AgentRuntimeHeadSchema,
20
- AgentStoreAppliedSchema,
21
- AgentStoreConflictSchema,
22
- AgentStoreDuplicateSchema,
23
- AgentStoreMutationResultSchema,
24
- AgentStoreNotFoundSchema,
25
12
  AgentStoredRunSchema,
26
- CheckpointRunAssistantSchema,
27
- CommitRunTerminalSchema,
28
- RecordRunOperationSchema,
29
- RecoverAgentRunSchema,
30
- ReplaceCompactedRangeSchema,
31
- RequestRunInterruptSchema,
32
13
  createAgentRuntimeStore,
33
14
  createMemoryAgentRuntimeStore,
34
15
  purgeAgentConversation
35
- } from "./index-fq492hg0.js";
16
+ } from "./index-hber83mk.js";
36
17
  import {
37
18
  AgentContextOverflowError,
19
+ AgentProviderStreamCutError,
38
20
  AgentRuntimeConflictError,
39
21
  AgentTokenCountSchema,
22
+ agentGoalStateSlot,
23
+ agentTodoStateSlot,
24
+ classifyProviderFailure,
40
25
  composeAgentPrompt,
41
26
  createAgentRuntime,
42
27
  createAgentRuntimeEventSink,
43
28
  createAgentSessionCoordinator,
29
+ createAgentStateSlotStore,
44
30
  createAgentToolFenceLifecycle,
45
31
  deferredToolCatalog,
32
+ defineStateSlot,
33
+ isToolResultFailure,
34
+ normalizeSdkUsage,
46
35
  projectAgentHistory,
47
36
  projectAgentHistoryDetailed,
37
+ recordAgentRetryDecision,
48
38
  registerDeferredToolCatalog,
49
39
  registerDeferredToolRepair,
40
+ renderAgentStateSlots,
50
41
  repairedSearchCall,
51
42
  selectAgentHistory
52
- } from "./index-2jg7b11j.js";
43
+ } from "./index-33xckd7h.js";
44
+ import"./index-p13mwz16.js";
45
+ import {
46
+ AgentAdmissionEventSchema,
47
+ AgentAttemptResetEventSchema,
48
+ AgentCheckpointEventSchema,
49
+ AgentReasoningDeltaEventSchema,
50
+ AgentReasoningEndEventSchema,
51
+ AgentReasoningStartEventSchema,
52
+ AgentRunOperationEventSchema,
53
+ AgentRunStateEventSchema,
54
+ AgentRuntimeEventCursorSchema,
55
+ AgentRuntimeEventSchema,
56
+ AgentTerminalEventSchema,
57
+ AgentToolStatusEventSchema,
58
+ AgentTransientDeltaEventSchema,
59
+ advanceAgentRuntimeEventCursor,
60
+ agentDurableEventId
61
+ } from "./index-hkm6wysp.js";
53
62
  import {
54
63
  AgentModelCapabilitySchema,
55
64
  AgentModelCatalogEntrySchema,
@@ -72,24 +81,34 @@ import {
72
81
  isCompleteAgentHistoryTurn
73
82
  } from "./index-y2s6h5bf.js";
74
83
  import {
75
- createBoundedSinkManager
76
- } from "./index-wnfk50r0.js";
77
- import {
78
- AgentAdmissionEventSchema,
79
- AgentCheckpointEventSchema,
80
- AgentReasoningDeltaEventSchema,
81
- AgentReasoningEndEventSchema,
82
- AgentReasoningStartEventSchema,
83
- AgentRunOperationEventSchema,
84
- AgentRunStateEventSchema,
85
- AgentRuntimeEventCursorSchema,
86
- AgentRuntimeEventSchema,
87
- AgentTerminalEventSchema,
88
- AgentToolStatusEventSchema,
89
- AgentTransientDeltaEventSchema,
90
- advanceAgentRuntimeEventCursor,
91
- agentDurableEventId
92
- } from "./index-4hk633vz.js";
84
+ AcceptInputAndAssignRunSchema,
85
+ AcquireAgentRunSchema,
86
+ AgentConversationArchiveSchema,
87
+ AgentRecoverableDescriptorSchema,
88
+ AgentRecoverablePageSchema,
89
+ AgentRunViewSchema,
90
+ AgentStoreAppliedSchema,
91
+ AgentStoreConflictSchema,
92
+ AgentStoreDuplicateSchema,
93
+ AgentStoreEventEnvelopeSchema,
94
+ AgentStoreEventKindSchema,
95
+ AgentStoreEventPageSchema,
96
+ AgentStoreMutationResultSchema,
97
+ AgentStoreNotFoundSchema,
98
+ AgentStoreTransitionSchema,
99
+ AppendAgentStoreEventSchema,
100
+ CheckpointRunAssistantSchema,
101
+ CommitRunTerminalSchema,
102
+ ReadAgentStoreEventsSchema,
103
+ RecordRunOperationSchema,
104
+ RecoverAgentRunSchema,
105
+ ReplaceCompactedRangeSchema,
106
+ RequestRunInterruptSchema,
107
+ canonicalAgentJson,
108
+ decodeAgentConversationArchive,
109
+ decodeAgentStoreEvent,
110
+ encodeAgentConversationArchive
111
+ } from "./index-vfgb58nf.js";
93
112
  import {
94
113
  AgentAssistantPlaceholderSchema,
95
114
  AgentControlPartSchema,
@@ -125,21 +144,31 @@ import {
125
144
  AgentUsageSchema,
126
145
  AgentUsageValueSchema,
127
146
  runStateForTerminalReason
128
- } from "./index-x15ss2dx.js";
147
+ } from "./index-p1b1y93x.js";
148
+ import {
149
+ createBoundedSinkManager
150
+ } from "./index-wnfk50r0.js";
151
+ import {
152
+ AgentSandboxGradeSchema,
153
+ AgentSandboxRestrictionSchema,
154
+ missingSandboxRestrictions,
155
+ probeAgentProcessSandbox,
156
+ recordAgentSandboxProbe
157
+ } from "./index-gskm1k5a.js";
129
158
  import {
130
159
  buildToolManifest,
131
160
  mountAgent
132
- } from "./index-3ds42w1w.js";
161
+ } from "./index-1szetq6f.js";
133
162
  import"./index-3xnq72rz.js";
134
- import"./index-s4ckmrac.js";
163
+ import"./index-rcgsnrr6.js";
135
164
  import {
136
165
  defineRuntimeTool
137
- } from "./index-dx9xehwt.js";
138
- import"./index-fwbkp2jn.js";
139
- import"./index-dggwnnjv.js";
166
+ } from "./index-kc6h6hg0.js";
167
+ import"./index-0vfdh685.js";
168
+ import"./index-wn9cb82y.js";
140
169
  import"./index-vsbzgd7b.js";
141
170
  import"./index-f9mb610r.js";
142
- import"./index-mgtxfe6b.js";
171
+ import"./index-r159gjwy.js";
143
172
  import"./index-cby4ar3v.js";
144
173
  import"./index-7zbps32p.js";
145
174
  import"./index-7evdsyez.js";
@@ -152,10 +181,501 @@ import {
152
181
  } from "./index-zcgf3gqf.js";
153
182
  import"./index-kzfs85xp.js";
154
183
  import {
155
- executableAgentRuntimeTools,
156
- isRecord
184
+ executableAgentRuntimeTools
157
185
  } from "./index-77fekveh.js";
158
186
 
187
+ // src/agent-runtime/child-tools.ts
188
+ import { z as z2 } from "zod";
189
+
190
+ // src/agent-runtime/agent-tool.ts
191
+ function defineAgentTool(definition) {
192
+ return {
193
+ ...definition,
194
+ handler: (context) => definition.handler(context)
195
+ };
196
+ }
197
+
198
+ // src/agent-runtime/children.ts
199
+ import { randomUUID } from "node:crypto";
200
+ import { z } from "zod";
201
+ var AgentChildBudgetSchema = z.object({
202
+ usd: z.number().positive().optional(),
203
+ tokens: z.int().positive().optional(),
204
+ milliseconds: z.int().positive().optional()
205
+ }).strict();
206
+ var AgentChildStateSchema = z.enum([
207
+ "spawned",
208
+ "running",
209
+ "finished",
210
+ "stopped",
211
+ "lost"
212
+ ]);
213
+ var AgentChildRecordSchema = z.object({
214
+ parentConversationId: z.string().min(1),
215
+ childConversationId: z.string().min(1),
216
+ seedUptoSeq: z.int().nonnegative(),
217
+ state: AgentChildStateSchema,
218
+ budget: AgentChildBudgetSchema,
219
+ usage: z.object({
220
+ tokens: z.int().nonnegative(),
221
+ usd: z.number().nonnegative(),
222
+ milliseconds: z.int().nonnegative()
223
+ }).strict(),
224
+ resultReference: z.string().optional(),
225
+ createdAt: z.string(),
226
+ updatedAt: z.string()
227
+ }).strict();
228
+ var ChildRowSchema = z.object({
229
+ parent_conversation_id: z.string(),
230
+ child_conversation_id: z.string(),
231
+ seed_upto_seq: z.int().nonnegative(),
232
+ state: AgentChildStateSchema,
233
+ budget_payload: z.string(),
234
+ usage_payload: z.string(),
235
+ result_reference: z.string().nullable(),
236
+ created_at: z.string(),
237
+ updated_at: z.string()
238
+ });
239
+ function parseChild(raw) {
240
+ const row = ChildRowSchema.parse(raw);
241
+ return AgentChildRecordSchema.parse({
242
+ parentConversationId: row.parent_conversation_id,
243
+ childConversationId: row.child_conversation_id,
244
+ seedUptoSeq: row.seed_upto_seq,
245
+ state: row.state,
246
+ budget: JSON.parse(row.budget_payload),
247
+ usage: JSON.parse(row.usage_payload),
248
+ ...row.result_reference && { resultReference: row.result_reference },
249
+ createdAt: row.created_at,
250
+ updatedAt: row.updated_at
251
+ });
252
+ }
253
+ function usageNumbers(usage) {
254
+ const inputTokens = usage.inputTokens.value;
255
+ const outputTokens = usage.outputTokens.value;
256
+ return {
257
+ ...inputTokens !== undefined && outputTokens !== undefined ? { tokens: inputTokens + outputTokens } : {},
258
+ ...usage.cost?.currency === "USD" && usage.cost.value !== undefined ? { usd: usage.cost.value } : {}
259
+ };
260
+ }
261
+ function createSqliteAgentChildManager(input) {
262
+ const now = input.now ?? (() => new Date);
263
+ const database = input.sqlite.database;
264
+ const store = input.sqlite.store;
265
+ const handles = new Map;
266
+ const settlements = new Map;
267
+ const currentState = (childConversationId) => {
268
+ const raw = database.prepare("SELECT state FROM stitchkit_agent_runtime_children WHERE child_conversation_id = ?").get(childConversationId);
269
+ return raw === null || raw === undefined ? undefined : AgentChildStateSchema.parse(raw.state);
270
+ };
271
+ const isLive = (childConversationId) => {
272
+ const state = currentState(childConversationId);
273
+ return state === "spawned" || state === "running";
274
+ };
275
+ const settleable = (childConversationId) => {
276
+ const state = currentState(childConversationId);
277
+ return state === undefined || state === "spawned" || state === "running";
278
+ };
279
+ const STOP_BOUND_MS = 1e4;
280
+ const bounded = async (work, fallback) => {
281
+ let timer;
282
+ try {
283
+ return await Promise.race([
284
+ Promise.resolve(work),
285
+ new Promise((resolve) => {
286
+ timer = setTimeout(() => resolve(fallback), STOP_BOUND_MS);
287
+ })
288
+ ]);
289
+ } finally {
290
+ if (timer)
291
+ clearTimeout(timer);
292
+ }
293
+ };
294
+ const listChildren = (parentConversationId) => database.prepare(`
295
+ SELECT parent_conversation_id, child_conversation_id, seed_upto_seq, state,
296
+ budget_payload, usage_payload, result_reference, created_at, updated_at
297
+ FROM stitchkit_agent_runtime_children
298
+ WHERE parent_conversation_id = ? ORDER BY created_at, child_conversation_id
299
+ `).all(parentConversationId).map(parseChild);
300
+ const updateState = async (record, state, resultReference, details = {}) => {
301
+ const updatedAt = now().toISOString();
302
+ const payload = {
303
+ childConversationId: record.childConversationId,
304
+ state,
305
+ ...resultReference && { resultReference },
306
+ ...details
307
+ };
308
+ const childPayload = {
309
+ parentConversationId: record.parentConversationId,
310
+ state,
311
+ ...resultReference && { resultReference },
312
+ ...details
313
+ };
314
+ const write = (audience) => input.sqlite.transaction(async (scope) => {
315
+ scope.database.prepare(`
316
+ UPDATE stitchkit_agent_runtime_children
317
+ SET state = ?, result_reference = ?, updated_at = ? WHERE child_conversation_id = ?
318
+ `).run(state, resultReference ?? null, updatedAt, record.childConversationId);
319
+ if (audience === "both") {
320
+ await scope.appendEvent({
321
+ conversationId: record.parentConversationId,
322
+ kind: "child/state",
323
+ occurredAt: updatedAt,
324
+ payload
325
+ });
326
+ }
327
+ if (audience !== "row") {
328
+ await scope.appendEvent({
329
+ conversationId: record.childConversationId,
330
+ kind: "child/state",
331
+ occurredAt: updatedAt,
332
+ payload: childPayload
333
+ });
334
+ }
335
+ });
336
+ try {
337
+ await write("both");
338
+ } catch (error) {
339
+ if (!(error instanceof AgentConversationPurgedError))
340
+ throw error;
341
+ try {
342
+ await write("child");
343
+ } catch (inner) {
344
+ if (!(inner instanceof AgentConversationPurgedError))
345
+ throw inner;
346
+ await write("row");
347
+ }
348
+ }
349
+ };
350
+ const spawnChild = async (request) => {
351
+ const requestedBudget = AgentChildBudgetSchema.parse(request.budget);
352
+ const remaining = input.remainingBudget ? AgentChildBudgetSchema.parse(await input.remainingBudget(request.parentConversationId)) : undefined;
353
+ const bounded2 = (requested, available) => {
354
+ if (requested === undefined)
355
+ return available;
356
+ if (available === undefined)
357
+ return requested;
358
+ return Math.min(requested, available);
359
+ };
360
+ const usd = bounded2(requestedBudget.usd, remaining?.usd);
361
+ const tokens = bounded2(requestedBudget.tokens, remaining?.tokens);
362
+ const milliseconds = bounded2(requestedBudget.milliseconds, remaining?.milliseconds);
363
+ const budget = AgentChildBudgetSchema.parse({
364
+ ...usd !== undefined && { usd },
365
+ ...tokens !== undefined && { tokens },
366
+ ...milliseconds !== undefined && { milliseconds }
367
+ });
368
+ const events = [];
369
+ let cursor = 1;
370
+ for (;; ) {
371
+ const page = await store.readEvents({
372
+ conversationId: request.parentConversationId,
373
+ fromSeq: cursor,
374
+ ...request.seedUptoSeq !== undefined && { toSeq: request.seedUptoSeq },
375
+ limit: 1000
376
+ });
377
+ events.push(...page.items);
378
+ if (page.nextSeq === undefined)
379
+ break;
380
+ cursor = page.nextSeq;
381
+ }
382
+ const seedUptoSeq = events.at(-1)?.seq ?? 0;
383
+ const childConversationId = request.childConversationId ?? randomUUID();
384
+ const createdAt = now().toISOString();
385
+ const record = AgentChildRecordSchema.parse({
386
+ parentConversationId: request.parentConversationId,
387
+ childConversationId,
388
+ seedUptoSeq,
389
+ state: "spawned",
390
+ budget,
391
+ usage: { tokens: 0, usd: 0, milliseconds: 0 },
392
+ createdAt,
393
+ updatedAt: createdAt
394
+ });
395
+ const seedArchive = encodeAgentConversationArchive({
396
+ format: "stitchkit.agent-conversation",
397
+ formatVersion: 1,
398
+ conversationId: request.parentConversationId,
399
+ events,
400
+ projections: [],
401
+ spills: []
402
+ });
403
+ const handle = await input.spawn({
404
+ parentConversationId: record.parentConversationId,
405
+ childConversationId,
406
+ seedArchive,
407
+ childInput: request.childInput,
408
+ budget
409
+ });
410
+ await input.sqlite.transaction(async (scope) => {
411
+ scope.database.prepare(`
412
+ INSERT INTO stitchkit_agent_runtime_children (
413
+ parent_conversation_id, child_conversation_id, seed_upto_seq, state,
414
+ budget_payload, usage_payload, result_reference, created_at, updated_at
415
+ ) VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?)
416
+ `).run(record.parentConversationId, record.childConversationId, record.seedUptoSeq, record.state, JSON.stringify(record.budget), JSON.stringify(record.usage), createdAt, createdAt);
417
+ await scope.appendEvent({
418
+ conversationId: record.parentConversationId,
419
+ kind: "child/spawned",
420
+ occurredAt: createdAt,
421
+ payload: { childConversationId, seedUptoSeq, budget }
422
+ });
423
+ });
424
+ handles.set(childConversationId, handle);
425
+ await updateState(record, "running");
426
+ const settlement = handle.result.then(async (result) => {
427
+ handles.delete(childConversationId);
428
+ if (settleable(childConversationId)) {
429
+ await updateState(record, "finished", result.resultReference);
430
+ }
431
+ }, async () => {
432
+ handles.delete(childConversationId);
433
+ if (settleable(childConversationId))
434
+ await updateState(record, "stopped");
435
+ });
436
+ settlements.set(childConversationId, settlement);
437
+ return { ...record, state: "running", updatedAt: now().toISOString() };
438
+ };
439
+ const recordStepUsage = async (request) => {
440
+ const raw = database.prepare(`
441
+ SELECT parent_conversation_id, child_conversation_id, seed_upto_seq, state,
442
+ budget_payload, usage_payload, result_reference, created_at, updated_at
443
+ FROM stitchkit_agent_runtime_children WHERE child_conversation_id = ?
444
+ `).get(request.childConversationId);
445
+ if (raw === null || raw === undefined)
446
+ throw new TypeError("Unknown child conversation");
447
+ const record = parseChild(raw);
448
+ const measured = usageNumbers(request.usage);
449
+ if (record.budget.tokens !== undefined && measured.tokens === undefined) {
450
+ throw new TypeError("Cannot enforce child token budget without measured token usage");
451
+ }
452
+ if (record.budget.usd !== undefined && measured.usd === undefined) {
453
+ throw new TypeError("Cannot enforce child USD budget without USD usage");
454
+ }
455
+ const usage = {
456
+ tokens: record.usage.tokens + (measured.tokens ?? 0),
457
+ usd: record.usage.usd + (measured.usd ?? 0),
458
+ milliseconds: record.usage.milliseconds + request.elapsedMs
459
+ };
460
+ const updatedAt = now().toISOString();
461
+ const overrun = {
462
+ tokens: Math.max(0, usage.tokens - (record.budget.tokens ?? Number.POSITIVE_INFINITY)),
463
+ usd: Math.max(0, usage.usd - (record.budget.usd ?? Number.POSITIVE_INFINITY)),
464
+ milliseconds: Math.max(0, usage.milliseconds - (record.budget.milliseconds ?? Number.POSITIVE_INFINITY))
465
+ };
466
+ const stop = record.budget.tokens !== undefined && usage.tokens >= record.budget.tokens || record.budget.usd !== undefined && usage.usd >= record.budget.usd || record.budget.milliseconds !== undefined && usage.milliseconds >= record.budget.milliseconds;
467
+ const handle = handles.get(request.childConversationId);
468
+ let enforced = false;
469
+ if (stop && handle) {
470
+ enforced = await bounded(Promise.resolve().then(() => handle.stopPolicy("child-budget")).then(() => true, () => false), false);
471
+ }
472
+ const payload = {
473
+ childConversationId: record.childConversationId,
474
+ state: "running",
475
+ usage,
476
+ overrun,
477
+ ...stop && { policyName: "child-budget", enforced }
478
+ };
479
+ await input.sqlite.transaction(async (scope) => {
480
+ scope.database.prepare("UPDATE stitchkit_agent_runtime_children SET usage_payload = ?, updated_at = ? WHERE child_conversation_id = ?").run(JSON.stringify(usage), updatedAt, request.childConversationId);
481
+ await scope.appendEvent({
482
+ conversationId: record.parentConversationId,
483
+ kind: "child/state",
484
+ occurredAt: updatedAt,
485
+ payload
486
+ });
487
+ await scope.appendEvent({
488
+ conversationId: record.childConversationId,
489
+ kind: "child/state",
490
+ occurredAt: updatedAt,
491
+ payload: { parentConversationId: record.parentConversationId, ...payload }
492
+ });
493
+ });
494
+ return { stop, enforced, ...stop && { policyName: "child-budget" }, overrun };
495
+ };
496
+ const stopChildren = async (parentConversationId) => {
497
+ const records = listChildren(parentConversationId).filter((record) => record.state === "spawned" || record.state === "running");
498
+ await Promise.all(records.map(async (record) => {
499
+ const startedAt = performance.now();
500
+ const handle = handles.get(record.childConversationId);
501
+ const reachable = handle ? handle.reachable ? await bounded(handle.reachable(), false) : true : false;
502
+ if (!reachable) {
503
+ if (isLive(record.childConversationId)) {
504
+ await updateState(record, "lost", undefined, {
505
+ stopDurationMs: Math.max(0, performance.now() - startedAt)
506
+ });
507
+ }
508
+ return;
509
+ }
510
+ await bounded(Promise.resolve(handle?.stopPolicy("parent-stopped")).then(() => true), false);
511
+ if (!isLive(record.childConversationId))
512
+ return;
513
+ await updateState(record, "stopped", undefined, {
514
+ stopDurationMs: Math.max(0, performance.now() - startedAt)
515
+ });
516
+ }));
517
+ };
518
+ const activeChild = (parentConversationId, childConversationId) => {
519
+ const child = listChildren(parentConversationId).find((record) => record.childConversationId === childConversationId);
520
+ if (!child)
521
+ throw new TypeError("Unknown child conversation for this parent");
522
+ if (child.state !== "running" && child.state !== "spawned") {
523
+ throw new TypeError("Child conversation is not active");
524
+ }
525
+ const handle = handles.get(childConversationId);
526
+ if (!handle)
527
+ throw new TypeError("Child host handle is unavailable");
528
+ return { child, handle };
529
+ };
530
+ const sendMessage = async (parentConversationId, childConversationId, message) => {
531
+ const { handle } = activeChild(parentConversationId, childConversationId);
532
+ if (!handle.sendMessage)
533
+ throw new TypeError("Child host does not support messages");
534
+ await handle.sendMessage(message);
535
+ await store.appendEvent({
536
+ conversationId: parentConversationId,
537
+ kind: "child/state",
538
+ payload: { childConversationId, action: "message-sent" }
539
+ });
540
+ };
541
+ const interruptChild = async (parentConversationId, childConversationId) => {
542
+ const { child, handle } = activeChild(parentConversationId, childConversationId);
543
+ const startedAt = performance.now();
544
+ await handle.stopPolicy("parent-interrupt");
545
+ await updateState(child, "stopped", undefined, {
546
+ policyName: "parent-interrupt",
547
+ stopDurationMs: Math.max(0, performance.now() - startedAt)
548
+ });
549
+ };
550
+ const waitChild = async (childConversationId) => {
551
+ await settlements.get(childConversationId);
552
+ };
553
+ return {
554
+ spawnChild,
555
+ listChildren,
556
+ recordStepUsage,
557
+ stopChildren,
558
+ sendMessage,
559
+ interruptChild,
560
+ waitChild
561
+ };
562
+ }
563
+ function agentChildBudgetStopPolicy(input) {
564
+ let stepsSeen = 0;
565
+ let lastBoundary = performance.now();
566
+ return {
567
+ name: "child-budget",
568
+ when: async ({ steps }) => {
569
+ const step = steps.at(-1);
570
+ if (!step || steps.length <= stepsSeen)
571
+ return false;
572
+ stepsSeen = steps.length;
573
+ const elapsedMs = Math.max(0, performance.now() - lastBoundary);
574
+ lastBoundary = performance.now();
575
+ const usage = normalizeSdkUsage(step.usage);
576
+ const cost = input.cost?.({ usage: step.usage });
577
+ const boundary = await input.manager.recordStepUsage({
578
+ childConversationId: input.childConversationId,
579
+ usage: { ...usage, ...cost && { cost } },
580
+ elapsedMs
581
+ });
582
+ return boundary.stop;
583
+ }
584
+ };
585
+ }
586
+
587
+ // src/agent-runtime/child-tools.ts
588
+ function childTool(manager, fork) {
589
+ return defineAgentTool({
590
+ name: fork ? "subagent_fork" : "subagent",
591
+ description: fork ? "Run a child conversation seeded from the parent through one exact event sequence." : "Run a child conversation seeded from the current durable parent history.",
592
+ identity: {
593
+ serviceName: "agent-children",
594
+ action: fork ? "fork" : "spawn",
595
+ method: "POST"
596
+ },
597
+ input: z2.object({
598
+ parentConversationId: z2.string().min(1),
599
+ childInput: z2.json(),
600
+ budget: AgentChildBudgetSchema,
601
+ seedUptoSeq: z2.int().positive().optional()
602
+ }).strict(),
603
+ output: z2.object({
604
+ childConversationId: z2.string(),
605
+ state: z2.string(),
606
+ resultReference: z2.string().optional()
607
+ }),
608
+ transports: ["AGENT"],
609
+ handler: async ({ input }) => {
610
+ if (fork && input.seedUptoSeq === undefined) {
611
+ throw new TypeError("subagent_fork requires seedUptoSeq");
612
+ }
613
+ const child = await manager.spawnChild({
614
+ parentConversationId: input.parentConversationId,
615
+ childInput: input.childInput,
616
+ budget: input.budget,
617
+ ...input.seedUptoSeq !== undefined && { seedUptoSeq: input.seedUptoSeq }
618
+ });
619
+ await manager.waitChild(child.childConversationId);
620
+ const finished = manager.listChildren(input.parentConversationId).find((record) => record.childConversationId === child.childConversationId);
621
+ if (!finished)
622
+ throw new Error("Child record disappeared after settlement");
623
+ return {
624
+ childConversationId: finished.childConversationId,
625
+ state: finished.state,
626
+ ...finished.resultReference && { resultReference: finished.resultReference }
627
+ };
628
+ }
629
+ });
630
+ }
631
+ function createAgentChildTools(manager) {
632
+ return [
633
+ childTool(manager, false),
634
+ childTool(manager, true),
635
+ defineAgentTool({
636
+ name: "list_agents",
637
+ description: "List the durable child graph for one parent conversation.",
638
+ identity: { serviceName: "agent-children", action: "list", method: "GET" },
639
+ input: z2.object({ parentConversationId: z2.string().min(1) }).strict(),
640
+ output: z2.object({ children: z2.array(AgentChildRecordSchema) }),
641
+ transports: ["AGENT"],
642
+ handler: ({ input }) => ({
643
+ children: [...manager.listChildren(input.parentConversationId)]
644
+ })
645
+ }),
646
+ defineAgentTool({
647
+ name: "send_message",
648
+ description: "Send one follow-up input to an active child conversation.",
649
+ identity: { serviceName: "agent-children", action: "message", method: "POST" },
650
+ input: z2.object({
651
+ parentConversationId: z2.string().min(1),
652
+ childConversationId: z2.string().min(1),
653
+ input: z2.json()
654
+ }).strict(),
655
+ output: z2.object({ sent: z2.literal(true) }),
656
+ transports: ["AGENT"],
657
+ handler: async ({ input }) => {
658
+ await manager.sendMessage(input.parentConversationId, input.childConversationId, input.input);
659
+ return { sent: true };
660
+ }
661
+ }),
662
+ defineAgentTool({
663
+ name: "interrupt_agent",
664
+ description: "Stop one active child conversation at a durable policy boundary.",
665
+ identity: { serviceName: "agent-children", action: "interrupt", method: "POST" },
666
+ input: z2.object({
667
+ parentConversationId: z2.string().min(1),
668
+ childConversationId: z2.string().min(1)
669
+ }).strict(),
670
+ output: z2.object({ interrupted: z2.literal(true) }),
671
+ transports: ["AGENT"],
672
+ handler: async ({ input }) => {
673
+ await manager.interruptChild(input.parentConversationId, input.childConversationId);
674
+ return { interrupted: true };
675
+ }
676
+ })
677
+ ];
678
+ }
159
679
  // src/agent-runtime/compaction.ts
160
680
  function groupProviderTurns(messages, evidencePolicy) {
161
681
  const turns = [];
@@ -261,24 +781,24 @@ function structuredCompaction(config) {
261
781
  };
262
782
  }
263
783
  // src/agent-runtime/deferred-tool-types.ts
264
- import { z } from "zod";
265
- var DeferredAgentToolSearchInputSchema = z.object({
266
- query: z.string().min(1).describe("An exact tool name, or words describing the tool. Matched as one phrase first, then word by word."),
267
- reason: z.enum(["inactive_call"]).optional()
784
+ import { z as z3 } from "zod";
785
+ var DeferredAgentToolSearchInputSchema = z3.object({
786
+ query: z3.string().min(1).describe("An exact tool name, or words describing the tool. Matched as one phrase first, then word by word."),
787
+ reason: z3.enum(["inactive_call"]).optional()
268
788
  });
269
- var DeferredAgentToolMatchSchema = z.object({
270
- name: z.string(),
271
- description: z.string()
789
+ var DeferredAgentToolMatchSchema = z3.object({
790
+ name: z3.string(),
791
+ description: z3.string()
272
792
  });
273
- var DeferredAgentToolReceiptSchema = z.object({
274
- schemaVersion: z.literal(1),
275
- kind: z.literal("stitchkit.deferred-tool-selection"),
276
- status: z.enum(["SELECTED", "SEARCH_REQUIRED", "NO_MATCH", "SELECTION_REFUSED"]),
277
- runId: z.string(),
278
- surfaceKey: z.string(),
279
- selected: z.array(z.string()),
280
- matches: z.array(DeferredAgentToolMatchSchema),
281
- truncated: z.boolean()
793
+ var DeferredAgentToolReceiptSchema = z3.object({
794
+ schemaVersion: z3.literal(1),
795
+ kind: z3.literal("stitchkit.deferred-tool-selection"),
796
+ status: z3.enum(["SELECTED", "SEARCH_REQUIRED", "NO_MATCH", "SELECTION_REFUSED"]),
797
+ runId: z3.string(),
798
+ surfaceKey: z3.string(),
799
+ selected: z3.array(z3.string()),
800
+ matches: z3.array(DeferredAgentToolMatchSchema),
801
+ truncated: z3.boolean()
282
802
  }).superRefine((receipt, context) => {
283
803
  const selected = receipt.status === "SELECTED" || receipt.status === "SEARCH_REQUIRED";
284
804
  if (selected !== receipt.selected.length > 0) {
@@ -634,39 +1154,209 @@ function createDeferredAgentToolSurface(config) {
634
1154
  }
635
1155
  };
636
1156
  }
1157
+ // src/agent-runtime/event-search.ts
1158
+ import { z as z4 } from "zod";
1159
+ var AgentEventSearchResultSchema = z4.object({
1160
+ conversationId: z4.string().min(1),
1161
+ seq: z4.int().positive(),
1162
+ occurredAt: z4.string(),
1163
+ snippet: z4.string()
1164
+ }).strict();
1165
+ var SearchRowSchema = z4.object({
1166
+ conversation_id: z4.string(),
1167
+ event_seq: z4.int().positive(),
1168
+ occurred_at: z4.string(),
1169
+ snippet: z4.string()
1170
+ });
1171
+ function literalFtsQuery(query) {
1172
+ return `"${query.replaceAll('"', '""')}"`;
1173
+ }
1174
+ function createSqliteAgentEventSearch(input) {
1175
+ return async (request) => {
1176
+ if (request.query.trim().length === 0)
1177
+ throw new TypeError("Event search query must not be empty");
1178
+ const limit = Math.min(request.limit ?? 20, 200);
1179
+ const target = request.conversationId ?? (input.authorizeConversation ? undefined : request.requestingConversationId);
1180
+ const values = [literalFtsQuery(request.query)];
1181
+ const targetClause = target ? "AND f.conversation_id = ?" : "";
1182
+ if (target)
1183
+ values.push(target);
1184
+ const sinceClause = request.since ? "AND f.occurred_at >= ?" : "";
1185
+ if (request.since)
1186
+ values.push(z4.iso.datetime({ offset: true }).parse(request.since));
1187
+ const page = limit * 4;
1188
+ const candidatesFrom = (offset2) => input.database.prepare(`
1189
+ SELECT f.conversation_id, f.event_seq, f.occurred_at,
1190
+ snippet(stitchkit_agent_runtime_events_fts, 3, '[', ']', ' … ', 24) AS snippet
1191
+ FROM stitchkit_agent_runtime_events_fts AS f
1192
+ WHERE stitchkit_agent_runtime_events_fts MATCH ? ${targetClause} ${sinceClause}
1193
+ ORDER BY bm25(stitchkit_agent_runtime_events_fts), f.occurred_at DESC, f.rowid
1194
+ LIMIT ? OFFSET ?
1195
+ `).all(...values, page, offset2).map((row) => SearchRowSchema.parse(row));
1196
+ const authorization = new Map;
1197
+ const results = [];
1198
+ let offset = 0;
1199
+ let candidates = candidatesFrom(offset);
1200
+ while (candidates.length > 0 && results.length < limit) {
1201
+ for (const row of candidates) {
1202
+ if (row.conversation_id !== request.requestingConversationId) {
1203
+ let allowed = authorization.get(row.conversation_id);
1204
+ if (allowed === undefined) {
1205
+ allowed = input.authorizeConversation ? await input.authorizeConversation({
1206
+ requestingConversationId: request.requestingConversationId,
1207
+ targetConversationId: row.conversation_id
1208
+ }) : false;
1209
+ authorization.set(row.conversation_id, allowed);
1210
+ }
1211
+ if (!allowed)
1212
+ continue;
1213
+ }
1214
+ results.push(AgentEventSearchResultSchema.parse({
1215
+ conversationId: row.conversation_id,
1216
+ seq: row.event_seq,
1217
+ occurredAt: row.occurred_at,
1218
+ snippet: row.snippet
1219
+ }));
1220
+ if (results.length === limit)
1221
+ break;
1222
+ }
1223
+ offset += page;
1224
+ candidates = results.length < limit ? candidatesFrom(offset) : [];
1225
+ }
1226
+ return results;
1227
+ };
1228
+ }
1229
+ // src/agent-runtime/event-search-tools.ts
1230
+ import { z as z5 } from "zod";
1231
+ function createAgentEventSearchTools(input) {
1232
+ const EventAddressSchema = z5.object({ conversationId: z5.string(), seq: z5.int().positive() }).strict();
1233
+ const EventOutputSchema = z5.object({
1234
+ conversationId: z5.string(),
1235
+ seq: z5.int().positive(),
1236
+ kind: z5.string(),
1237
+ occurredAt: z5.string(),
1238
+ payload: z5.json()
1239
+ }).strict();
1240
+ const readExact = async (address) => {
1241
+ if (address.conversationId !== input.conversationId) {
1242
+ const allowed = await input.authorizeConversation?.({
1243
+ requestingConversationId: input.conversationId,
1244
+ targetConversationId: address.conversationId
1245
+ });
1246
+ if (!allowed)
1247
+ throw new TypeError("Event address is outside the authorized conversation");
1248
+ }
1249
+ const page = await input.store.readEvents({
1250
+ conversationId: address.conversationId,
1251
+ fromSeq: address.seq,
1252
+ toSeq: address.seq,
1253
+ limit: 1
1254
+ });
1255
+ const event = page.items[0];
1256
+ if (!event || event.seq !== address.seq)
1257
+ throw new TypeError("Unknown event address");
1258
+ return {
1259
+ conversationId: event.conversationId,
1260
+ seq: event.seq,
1261
+ kind: event.kind,
1262
+ occurredAt: event.occurredAt,
1263
+ payload: event.payload
1264
+ };
1265
+ };
1266
+ return [
1267
+ defineAgentTool({
1268
+ name: "session_search",
1269
+ description: "Search authorized durable conversation events and return exact addresses.",
1270
+ identity: { serviceName: "agent-events", action: "search", method: "POST" },
1271
+ input: z5.object({
1272
+ query: z5.string().min(1),
1273
+ conversationId: z5.string().min(1).optional(),
1274
+ since: z5.iso.datetime({ offset: true }).optional(),
1275
+ limit: z5.int().positive().max(200).optional()
1276
+ }).strict(),
1277
+ output: z5.object({ results: z5.array(AgentEventSearchResultSchema) }).strict(),
1278
+ transports: ["AGENT"],
1279
+ handler: async ({ input: request }) => ({
1280
+ results: [
1281
+ ...await input.search({
1282
+ requestingConversationId: input.conversationId,
1283
+ ...request
1284
+ })
1285
+ ]
1286
+ })
1287
+ }),
1288
+ defineAgentTool({
1289
+ name: "session_trace",
1290
+ description: "Read a bounded contiguous slice of this conversation event trace.",
1291
+ identity: { serviceName: "agent-events", action: "trace", method: "GET" },
1292
+ input: z5.object({
1293
+ fromSeq: z5.int().positive().optional(),
1294
+ toSeq: z5.int().positive().optional(),
1295
+ limit: z5.int().positive().max(1000).default(100)
1296
+ }).strict(),
1297
+ output: z5.object({ events: z5.array(EventOutputSchema), nextSeq: z5.number().optional() }),
1298
+ transports: ["AGENT"],
1299
+ handler: async ({ input: request }) => {
1300
+ const page = await input.store.readEvents({
1301
+ conversationId: input.conversationId,
1302
+ ...request
1303
+ });
1304
+ return {
1305
+ events: page.items.map((event) => ({
1306
+ conversationId: event.conversationId,
1307
+ seq: event.seq,
1308
+ kind: event.kind,
1309
+ occurredAt: event.occurredAt,
1310
+ payload: event.payload
1311
+ })),
1312
+ ...page.nextSeq !== undefined && { nextSeq: page.nextSeq }
1313
+ };
1314
+ }
1315
+ }),
1316
+ defineAgentTool({
1317
+ name: "session_event",
1318
+ description: "Read one durable event by exact conversation and sequence address.",
1319
+ identity: { serviceName: "agent-events", action: "read", method: "GET" },
1320
+ input: EventAddressSchema,
1321
+ output: EventOutputSchema,
1322
+ transports: ["AGENT"],
1323
+ handler: ({ input: request }) => readExact(request)
1324
+ })
1325
+ ];
1326
+ }
637
1327
  // src/agent-runtime/observability.ts
638
- import { z as z2 } from "zod";
639
- var AgentRunEventBaseSchema = z2.object({
640
- schemaVersion: z2.literal(1),
1328
+ import { z as z6 } from "zod";
1329
+ var AgentRunEventBaseSchema = z6.object({
1330
+ schemaVersion: z6.literal(1),
641
1331
  eventId: AgentRecordIdSchema,
642
1332
  conversationId: AgentRecordIdSchema,
643
1333
  runId: AgentRecordIdSchema,
644
- traceId: z2.string().min(1),
645
- spanId: z2.string().min(1),
646
- parentSpanId: z2.string().min(1).optional(),
1334
+ traceId: z6.string().min(1),
1335
+ spanId: z6.string().min(1),
1336
+ parentSpanId: z6.string().min(1).optional(),
647
1337
  state: AgentRunStateSchema,
648
- modelId: z2.string().min(1).optional(),
649
- durationMs: z2.number().nonnegative().optional(),
1338
+ modelId: z6.string().min(1).optional(),
1339
+ durationMs: z6.number().nonnegative().optional(),
650
1340
  emittedAt: AgentTimestampSchema
651
1341
  });
652
1342
  var AgentRunStartedEventSchema = AgentRunEventBaseSchema.extend({
653
- type: z2.literal("run-started"),
654
- queueWaitMs: z2.number().nonnegative().optional()
1343
+ type: z6.literal("run-started"),
1344
+ queueWaitMs: z6.number().nonnegative().optional()
655
1345
  });
656
1346
  var AgentStepFinishedEventSchema = AgentRunEventBaseSchema.extend({
657
- type: z2.literal("step-finished"),
658
- step: z2.int().nonnegative(),
659
- ttftMs: z2.number().nonnegative().optional(),
1347
+ type: z6.literal("step-finished"),
1348
+ step: z6.int().nonnegative(),
1349
+ ttftMs: z6.number().nonnegative().optional(),
660
1350
  usage: AgentUsageSchema
661
1351
  });
662
1352
  var AgentRunTerminalEventSchema = AgentRunEventBaseSchema.extend({
663
- type: z2.literal("run-terminal"),
1353
+ type: z6.literal("run-terminal"),
664
1354
  terminalReason: AgentTerminalReasonSchema,
665
- ttftMs: z2.number().nonnegative().optional(),
666
- internalCause: z2.unknown().optional(),
1355
+ ttftMs: z6.number().nonnegative().optional(),
1356
+ internalCause: z6.unknown().optional(),
667
1357
  usage: AgentUsageSchema
668
1358
  });
669
- var AgentRunEventSchema = z2.discriminatedUnion("type", [
1359
+ var AgentRunEventSchema = z6.discriminatedUnion("type", [
670
1360
  AgentRunStartedEventSchema,
671
1361
  AgentStepFinishedEventSchema,
672
1362
  AgentRunTerminalEventSchema
@@ -714,6 +1404,247 @@ function createAgentObservability(config) {
714
1404
  close: (bound) => manager.close(bound)
715
1405
  };
716
1406
  }
1407
+ // src/agent-runtime/projections.ts
1408
+ import { z as z7 } from "zod";
1409
+ function defineAgentProjection(definition) {
1410
+ if (definition.name.length === 0)
1411
+ throw new TypeError("Projection name must not be empty");
1412
+ if (!Number.isInteger(definition.version) || definition.version < 1) {
1413
+ throw new TypeError("Projection version must be a positive integer");
1414
+ }
1415
+ definition.schema.parse(definition.initial());
1416
+ return definition;
1417
+ }
1418
+ function createAgentProjectionRegistry(definitions) {
1419
+ const registry = new Map;
1420
+ for (const definition of definitions) {
1421
+ const previous = registry.get(definition.name);
1422
+ if (previous)
1423
+ throw new TypeError(`Duplicate agent projection: ${definition.name}`);
1424
+ registry.set(definition.name, definition);
1425
+ }
1426
+ return registry;
1427
+ }
1428
+ var ProjectionRowSchema = z7.object({
1429
+ conversation_id: z7.string().optional(),
1430
+ version: z7.int().positive(),
1431
+ upto_seq: z7.int().nonnegative(),
1432
+ payload: z7.string(),
1433
+ updated_at: z7.string()
1434
+ });
1435
+ function projectionPayload(value) {
1436
+ return JSON.parse(value);
1437
+ }
1438
+ function createSqliteAgentProjectionStore(input) {
1439
+ const database = input.sqlite.database;
1440
+ const store = input.sqlite.store;
1441
+ const load = (conversationId, definition) => {
1442
+ const raw = database.prepare(`
1443
+ SELECT version, upto_seq, payload, updated_at
1444
+ FROM stitchkit_agent_runtime_projections
1445
+ WHERE conversation_id = ? AND name = ?
1446
+ `).get(conversationId, definition.name);
1447
+ if (raw === null || raw === undefined)
1448
+ return;
1449
+ const row = ProjectionRowSchema.parse(raw);
1450
+ if (row.version !== definition.version)
1451
+ return;
1452
+ return {
1453
+ name: definition.name,
1454
+ version: row.version,
1455
+ uptoSeq: row.upto_seq,
1456
+ updatedAt: row.updated_at,
1457
+ value: definition.schema.parse(projectionPayload(row.payload))
1458
+ };
1459
+ };
1460
+ const advance = async (conversationId, definition) => {
1461
+ const current = load(conversationId, definition);
1462
+ let value = current?.value ?? definition.initial();
1463
+ let uptoSeq = current?.uptoSeq ?? 0;
1464
+ let cursor = uptoSeq + 1;
1465
+ let updatedAt = current?.updatedAt ?? new Date(0).toISOString();
1466
+ for (;; ) {
1467
+ const page = await store.readEvents({
1468
+ conversationId,
1469
+ fromSeq: cursor,
1470
+ limit: 1000
1471
+ });
1472
+ for (const event of page.items) {
1473
+ value = definition.schema.parse(definition.fold(value, event));
1474
+ uptoSeq = event.seq;
1475
+ updatedAt = event.occurredAt;
1476
+ }
1477
+ if (page.nextSeq === undefined)
1478
+ break;
1479
+ cursor = page.nextSeq;
1480
+ }
1481
+ await input.sqlite.transaction(async (scope) => {
1482
+ scope.database.prepare(`
1483
+ INSERT INTO stitchkit_agent_runtime_projections
1484
+ (conversation_id, name, version, upto_seq, payload, updated_at)
1485
+ VALUES (?, ?, ?, ?, ?, ?)
1486
+ ON CONFLICT (conversation_id, name) DO UPDATE SET
1487
+ version = excluded.version,
1488
+ upto_seq = excluded.upto_seq,
1489
+ payload = excluded.payload,
1490
+ updated_at = excluded.updated_at
1491
+ `).run(conversationId, definition.name, definition.version, uptoSeq, JSON.stringify(value), updatedAt);
1492
+ });
1493
+ return { name: definition.name, version: definition.version, uptoSeq, updatedAt, value };
1494
+ };
1495
+ const list = (definition) => database.prepare(`
1496
+ SELECT conversation_id, version, upto_seq, payload, updated_at
1497
+ FROM stitchkit_agent_runtime_projections
1498
+ WHERE name = ? AND version = ? ORDER BY updated_at DESC, conversation_id
1499
+ `).all(definition.name, definition.version).map((raw) => {
1500
+ const row = ProjectionRowSchema.parse(raw);
1501
+ if (!row.conversation_id)
1502
+ throw new TypeError("Projection list row has no conversation id");
1503
+ return {
1504
+ conversationId: row.conversation_id,
1505
+ projection: {
1506
+ name: definition.name,
1507
+ version: row.version,
1508
+ uptoSeq: row.upto_seq,
1509
+ updatedAt: row.updated_at,
1510
+ value: definition.schema.parse(projectionPayload(row.payload))
1511
+ }
1512
+ };
1513
+ });
1514
+ return { load, advance, list };
1515
+ }
1516
+ var ConversationCardSchema = z7.object({
1517
+ eventCount: z7.int().nonnegative(),
1518
+ lastKind: z7.string().optional(),
1519
+ lastOccurredAt: z7.string().optional()
1520
+ });
1521
+ var agentConversationCardProjection = defineAgentProjection({
1522
+ name: "conversation-card",
1523
+ version: 1,
1524
+ schema: ConversationCardSchema,
1525
+ initial: () => ({ eventCount: 0 }),
1526
+ fold: (state, event) => ({
1527
+ eventCount: state.eventCount + 1,
1528
+ lastKind: event.kind,
1529
+ lastOccurredAt: event.occurredAt
1530
+ })
1531
+ });
1532
+ var StateSlotsProjectionSchema = z7.record(z7.string(), z7.json());
1533
+ var agentStateSlotsProjection = defineAgentProjection({
1534
+ name: "state-slots",
1535
+ version: 1,
1536
+ schema: StateSlotsProjectionSchema,
1537
+ initial: () => ({}),
1538
+ fold: (state, event) => {
1539
+ if (event.kind !== "state/set")
1540
+ return state;
1541
+ const payload = z7.object({ name: z7.string().min(1), value: z7.json() }).passthrough().parse(event.payload);
1542
+ return { ...state, [payload.name]: payload.value };
1543
+ }
1544
+ });
1545
+ var ScheduleSummarySchema = z7.object({
1546
+ scheduled: z7.int().nonnegative(),
1547
+ fired: z7.int().nonnegative(),
1548
+ cancelled: z7.int().nonnegative(),
1549
+ late: z7.int().nonnegative()
1550
+ });
1551
+ var agentScheduleSummaryProjection = defineAgentProjection({
1552
+ name: "schedule-summary",
1553
+ version: 1,
1554
+ schema: ScheduleSummarySchema,
1555
+ initial: () => ({ scheduled: 0, fired: 0, cancelled: 0, late: 0 }),
1556
+ fold: (state, event) => {
1557
+ if (event.kind === "schedule/set")
1558
+ return { ...state, scheduled: state.scheduled + 1 };
1559
+ if (event.kind === "schedule/fired")
1560
+ return { ...state, fired: state.fired + 1 };
1561
+ if (event.kind === "schedule/cancelled")
1562
+ return { ...state, cancelled: state.cancelled + 1 };
1563
+ if (event.kind === "schedule/late")
1564
+ return { ...state, late: state.late + 1 };
1565
+ return state;
1566
+ }
1567
+ });
1568
+ var RuntimeSummarySchema = z7.object({
1569
+ eventCount: z7.int().nonnegative(),
1570
+ lastSeq: z7.int().nonnegative(),
1571
+ lastKind: z7.string().optional(),
1572
+ lastOccurredAt: z7.string().optional(),
1573
+ lastModelId: z7.string().optional()
1574
+ }).strict();
1575
+ var agentSummaryProjection = defineAgentProjection({
1576
+ name: "summary",
1577
+ version: 1,
1578
+ schema: RuntimeSummarySchema,
1579
+ initial: () => ({ eventCount: 0, lastSeq: 0 }),
1580
+ fold: (state, event) => {
1581
+ const request = event.kind === "provider/request" ? z7.object({ modelId: z7.string().optional() }).passthrough().safeParse(event.payload) : undefined;
1582
+ return {
1583
+ eventCount: state.eventCount + 1,
1584
+ lastSeq: event.seq,
1585
+ lastKind: event.kind,
1586
+ lastOccurredAt: event.occurredAt,
1587
+ ...request?.success && request.data.modelId ? { lastModelId: request.data.modelId } : state.lastModelId ? { lastModelId: state.lastModelId } : {}
1588
+ };
1589
+ }
1590
+ });
1591
+ var RuntimeUsageSchema = z7.object({
1592
+ inputTokens: z7.number().nonnegative(),
1593
+ outputTokens: z7.number().nonnegative(),
1594
+ cost: z7.number().nonnegative(),
1595
+ sources: z7.array(z7.string())
1596
+ }).strict();
1597
+ var agentUsageProjection = defineAgentProjection({
1598
+ name: "usage",
1599
+ version: 1,
1600
+ schema: RuntimeUsageSchema,
1601
+ initial: () => ({ inputTokens: 0, outputTokens: 0, cost: 0, sources: [] }),
1602
+ fold: (state, event) => {
1603
+ const parsed = z7.object({
1604
+ inputTokens: z7.number().nonnegative().optional(),
1605
+ outputTokens: z7.number().nonnegative().optional(),
1606
+ cost: z7.number().nonnegative().optional(),
1607
+ source: z7.string().min(1).optional()
1608
+ }).passthrough().safeParse(event.payload);
1609
+ if (!parsed.success)
1610
+ return state;
1611
+ const source = parsed.data.source;
1612
+ return {
1613
+ inputTokens: state.inputTokens + (parsed.data.inputTokens ?? 0),
1614
+ outputTokens: state.outputTokens + (parsed.data.outputTokens ?? 0),
1615
+ cost: state.cost + (parsed.data.cost ?? 0),
1616
+ sources: source && !state.sources.includes(source) ? [...state.sources, source] : state.sources
1617
+ };
1618
+ }
1619
+ });
1620
+ var RuntimeOutlineSchema = z7.object({
1621
+ requests: z7.array(z7.object({
1622
+ seq: z7.int().positive(),
1623
+ occurredAt: z7.string(),
1624
+ runId: z7.string().optional()
1625
+ }).strict())
1626
+ }).strict();
1627
+ var agentOutlineProjection = defineAgentProjection({
1628
+ name: "outline",
1629
+ version: 1,
1630
+ schema: RuntimeOutlineSchema,
1631
+ initial: () => ({ requests: [] }),
1632
+ fold: (state, event) => {
1633
+ if (event.kind !== "provider/request")
1634
+ return state;
1635
+ const payload = z7.object({ runId: z7.string().optional() }).passthrough().parse(event.payload);
1636
+ return {
1637
+ requests: [
1638
+ ...state.requests,
1639
+ {
1640
+ seq: event.seq,
1641
+ occurredAt: event.occurredAt,
1642
+ ...payload.runId && { runId: payload.runId }
1643
+ }
1644
+ ]
1645
+ };
1646
+ }
1647
+ });
717
1648
  // src/agent-runtime/protocol.ts
718
1649
  function hasAgentTerminalOutput(input) {
719
1650
  for (const part of input.message.parts) {
@@ -741,84 +1672,543 @@ function defineAgentProtocol(config) {
741
1672
  }
742
1673
  };
743
1674
  }
744
- // src/agent-runtime/provider-failure.ts
745
- var BY_STATUS = {
746
- 402: "insufficient-credits",
747
- 404: "model-unavailable",
748
- 408: "timeout",
749
- 413: "context-overflow",
750
- 429: "rate-limited"
751
- };
752
- var BY_MESSAGE = [
753
- [/insufficient|not enough credit|payment required|quota exceeded/i, "insufficient-credits"],
754
- [/rate.?limit|too many requests|slow down/i, "rate-limited"],
755
- [
756
- /no endpoints found|model not found|unknown model|no allowed providers/i,
757
- "model-unavailable"
758
- ],
759
- [/context length|maximum context|too many tokens|prompt is too long/i, "context-overflow"],
760
- [/timed? ?out|deadline exceeded|etimedout/i, "timeout"],
761
- [/abort|cancell?ed/i, "cancelled"]
762
- ];
763
- var RETRYABLE = new Set([
764
- "rate-limited",
765
- "timeout",
766
- "insufficient-credits"
767
- ]);
768
- function statusOf(value) {
769
- if (!isRecord(value))
1675
+ // src/agent-runtime/schedule-tools.ts
1676
+ import { z as z8 } from "zod";
1677
+ var ScheduledOutputSchema = z8.object({ scheduleId: z8.string(), nextAt: z8.string() }).strict();
1678
+ var ConversationInputSchema = z8.object({ conversationId: z8.string().min(1) }).strict();
1679
+ function createAgentScheduleTools(service) {
1680
+ const schedule = async (request) => {
1681
+ const result = await service.scheduleInput(request);
1682
+ return { scheduleId: result.id, nextAt: result.nextAt };
1683
+ };
1684
+ return [
1685
+ defineAgentTool({
1686
+ name: "schedule_after",
1687
+ description: "Schedule durable input after a relative delay.",
1688
+ identity: { serviceName: "agent-schedules", action: "after", method: "POST" },
1689
+ input: z8.object({
1690
+ conversationId: z8.string().min(1),
1691
+ input: z8.json(),
1692
+ afterMs: z8.int().positive()
1693
+ }).strict(),
1694
+ output: ScheduledOutputSchema,
1695
+ transports: ["AGENT"],
1696
+ handler: ({ input }) => schedule(input)
1697
+ }),
1698
+ defineAgentTool({
1699
+ name: "schedule_at",
1700
+ description: "Schedule durable input at an absolute timestamp with an explicit offset.",
1701
+ identity: { serviceName: "agent-schedules", action: "at", method: "POST" },
1702
+ input: z8.object({ conversationId: z8.string().min(1), input: z8.json(), at: z8.string().min(1) }).strict(),
1703
+ output: ScheduledOutputSchema,
1704
+ transports: ["AGENT"],
1705
+ handler: ({ input }) => schedule(input)
1706
+ }),
1707
+ defineAgentTool({
1708
+ name: "schedule_every",
1709
+ description: "Schedule recurring durable input in an explicit conversation timezone.",
1710
+ identity: { serviceName: "agent-schedules", action: "every", method: "POST" },
1711
+ input: z8.object({
1712
+ conversationId: z8.string().min(1),
1713
+ input: z8.json(),
1714
+ everyMs: z8.int().positive(),
1715
+ timeZone: z8.string().min(1)
1716
+ }).strict(),
1717
+ output: ScheduledOutputSchema,
1718
+ transports: ["AGENT"],
1719
+ handler: ({ input }) => schedule(input)
1720
+ }),
1721
+ defineAgentTool({
1722
+ name: "schedule_list",
1723
+ description: "List durable schedules for one conversation.",
1724
+ identity: { serviceName: "agent-schedules", action: "list", method: "GET" },
1725
+ input: ConversationInputSchema,
1726
+ output: z8.object({ schedules: z8.array(z8.json()) }).strict(),
1727
+ transports: ["AGENT"],
1728
+ handler: ({ input }) => ({
1729
+ schedules: [...service.listSchedules(input.conversationId)]
1730
+ })
1731
+ }),
1732
+ defineAgentTool({
1733
+ name: "schedule_cancel",
1734
+ description: "Cancel one durable deferred-input schedule.",
1735
+ identity: { serviceName: "agent-schedules", action: "cancel", method: "POST" },
1736
+ input: z8.object({ conversationId: z8.string().min(1), scheduleId: z8.string().min(1) }).strict(),
1737
+ output: z8.object({ cancelled: z8.boolean() }).strict(),
1738
+ transports: ["AGENT"],
1739
+ handler: async ({ input }) => ({
1740
+ cancelled: await service.cancelSchedule(input.conversationId, input.scheduleId)
1741
+ })
1742
+ })
1743
+ ];
1744
+ }
1745
+ // src/agent-runtime/schedules.ts
1746
+ import { randomUUID as randomUUID2 } from "node:crypto";
1747
+ import { z as z9 } from "zod";
1748
+ var AgentScheduleSchema = z9.object({
1749
+ id: z9.string().min(1),
1750
+ conversationId: z9.string().min(1),
1751
+ kind: z9.enum(["at", "after", "every"]),
1752
+ nextAt: z9.iso.datetime({ offset: true }),
1753
+ intervalMs: z9.int().positive().optional(),
1754
+ timeZone: z9.string().min(1).optional(),
1755
+ input: z9.json(),
1756
+ state: z9.enum(["scheduled", "cancelled", "completed"]),
1757
+ occurrence: z9.int().nonnegative(),
1758
+ createdAt: z9.iso.datetime({ offset: true }),
1759
+ updatedAt: z9.iso.datetime({ offset: true })
1760
+ }).strict();
1761
+ var ScheduleRowSchema = z9.object({
1762
+ id: z9.string(),
1763
+ conversation_id: z9.string(),
1764
+ kind: z9.enum(["at", "after", "every"]),
1765
+ next_at: z9.string(),
1766
+ interval_ms: z9.number().int().positive().nullable(),
1767
+ time_zone: z9.string().nullable(),
1768
+ input_payload: z9.string(),
1769
+ state: z9.enum(["scheduled", "cancelled", "completed"]),
1770
+ occurrence: z9.int().nonnegative(),
1771
+ created_at: z9.string(),
1772
+ updated_at: z9.string()
1773
+ });
1774
+ function parseSchedule(raw) {
1775
+ const row = ScheduleRowSchema.parse(raw);
1776
+ return AgentScheduleSchema.parse({
1777
+ id: row.id,
1778
+ conversationId: row.conversation_id,
1779
+ kind: row.kind,
1780
+ nextAt: row.next_at,
1781
+ ...row.interval_ms !== null && { intervalMs: row.interval_ms },
1782
+ ...row.time_zone !== null && { timeZone: row.time_zone },
1783
+ input: JSON.parse(row.input_payload),
1784
+ state: row.state,
1785
+ occurrence: row.occurrence,
1786
+ createdAt: row.created_at,
1787
+ updatedAt: row.updated_at
1788
+ });
1789
+ }
1790
+ function createAgentScheduleService(input) {
1791
+ const onError = input.onError ?? ((error) => {
1792
+ console.error("[stitchkit] agent schedule tick failed", error);
1793
+ });
1794
+ const now = input.now ?? (() => new Date);
1795
+ const database = input.sqlite.database;
1796
+ const setTimer = input.setTimer ?? ((callback, delay) => setTimeout(callback, delay));
1797
+ const clearTimer = input.clearTimer ?? clearTimeout;
1798
+ let timer;
1799
+ let closed = false;
1800
+ const listSchedules = (conversationId) => database.prepare(`
1801
+ SELECT id, conversation_id, kind, next_at, interval_ms, time_zone,
1802
+ input_payload, state, occurrence, created_at, updated_at
1803
+ FROM stitchkit_agent_runtime_schedules
1804
+ WHERE conversation_id = ? ORDER BY created_at, id
1805
+ `).all(conversationId).map(parseSchedule);
1806
+ const arm = () => {
1807
+ if (timer)
1808
+ clearTimer(timer);
1809
+ if (closed)
1810
+ return;
1811
+ try {
1812
+ armNext();
1813
+ } catch (error) {
1814
+ onError(error);
1815
+ }
1816
+ };
1817
+ const armNext = () => {
1818
+ const raw = database.prepare(`
1819
+ SELECT MAX(next_at, COALESCE(claim_until, next_at)) AS due_at
1820
+ FROM stitchkit_agent_runtime_schedules
1821
+ WHERE state = 'scheduled' ORDER BY due_at, id LIMIT 1
1822
+ `).get();
1823
+ if (raw === null || raw === undefined || typeof raw.due_at !== "string")
1824
+ return;
1825
+ const delay = Math.max(0, new Date(raw.due_at).getTime() - now().getTime());
1826
+ timer = setTimer(() => void tick(), Math.min(delay, 2147483647));
1827
+ };
1828
+ const owner = randomUUID2();
1829
+ const CLAIM_MS = 60000;
1830
+ let ticking;
1831
+ const claim = (id, until, at) => input.sqlite.transaction(async (scope) => scope.database.prepare(`
1832
+ UPDATE stitchkit_agent_runtime_schedules
1833
+ SET claim_owner = ?, claim_until = ?, updated_at = ?
1834
+ WHERE id = ? AND state = 'scheduled' AND (claim_until IS NULL OR claim_until < ?)
1835
+ `).run(owner, until, at, id, at).changes === 1);
1836
+ const runTick = async () => {
1837
+ if (closed)
1838
+ return;
1839
+ const observedAt = now();
1840
+ const due = database.prepare(`
1841
+ SELECT id, conversation_id, kind, next_at, interval_ms, time_zone,
1842
+ input_payload, state, occurrence, created_at, updated_at
1843
+ FROM stitchkit_agent_runtime_schedules
1844
+ WHERE state = 'scheduled' AND next_at <= ? ORDER BY next_at, id
1845
+ `).all(observedAt.toISOString()).map(parseSchedule);
1846
+ for (const schedule of due) {
1847
+ const at = observedAt.toISOString();
1848
+ if (!await claim(schedule.id, new Date(observedAt.getTime() + CLAIM_MS).toISOString(), at)) {
1849
+ continue;
1850
+ }
1851
+ const occurrence = schedule.occurrence + 1;
1852
+ const lateByMs = Math.max(0, observedAt.getTime() - new Date(schedule.nextAt).getTime());
1853
+ try {
1854
+ await input.dispatch({
1855
+ conversationId: schedule.conversationId,
1856
+ idempotencyKey: `schedule:${schedule.id}:${occurrence}`,
1857
+ input: schedule.input,
1858
+ schedule: { id: schedule.id, occurrence, lateByMs }
1859
+ });
1860
+ } catch (error) {
1861
+ await input.sqlite.transaction(async (scope) => {
1862
+ scope.database.prepare(`
1863
+ UPDATE stitchkit_agent_runtime_schedules
1864
+ SET claim_owner = NULL, claim_until = NULL, updated_at = ?
1865
+ WHERE id = ? AND claim_owner = ?
1866
+ `).run(at, schedule.id, owner);
1867
+ await scope.appendEvent({
1868
+ conversationId: schedule.conversationId,
1869
+ kind: "schedule/failed",
1870
+ occurredAt: at,
1871
+ payload: {
1872
+ id: schedule.id,
1873
+ occurrence,
1874
+ message: error instanceof Error ? error.message : String(error)
1875
+ }
1876
+ });
1877
+ });
1878
+ continue;
1879
+ }
1880
+ let nextAt = schedule.nextAt;
1881
+ if (schedule.kind === "every" && schedule.intervalMs) {
1882
+ let next = new Date(schedule.nextAt).getTime() + schedule.intervalMs;
1883
+ while (next <= observedAt.getTime())
1884
+ next += schedule.intervalMs;
1885
+ nextAt = new Date(next).toISOString();
1886
+ }
1887
+ const state = schedule.kind === "every" ? "scheduled" : "completed";
1888
+ await input.sqlite.transaction(async (scope) => {
1889
+ const settled = scope.database.prepare(`
1890
+ UPDATE stitchkit_agent_runtime_schedules
1891
+ SET state = ?, occurrence = ?, next_at = ?, updated_at = ?,
1892
+ claim_owner = NULL, claim_until = NULL
1893
+ WHERE id = ? AND state = 'scheduled' AND claim_owner = ?
1894
+ `).run(state, occurrence, nextAt, at, schedule.id, owner).changes === 1;
1895
+ if (lateByMs > 0) {
1896
+ await scope.appendEvent({
1897
+ conversationId: schedule.conversationId,
1898
+ kind: "schedule/late",
1899
+ occurredAt: at,
1900
+ payload: { id: schedule.id, occurrence, lateByMs }
1901
+ });
1902
+ }
1903
+ await scope.appendEvent({
1904
+ conversationId: schedule.conversationId,
1905
+ kind: "schedule/fired",
1906
+ occurredAt: at,
1907
+ payload: {
1908
+ id: schedule.id,
1909
+ occurrence,
1910
+ lateByMs,
1911
+ nextAt,
1912
+ state: settled ? state : "cancelled"
1913
+ }
1914
+ });
1915
+ });
1916
+ }
1917
+ };
1918
+ const tick = () => {
1919
+ if (ticking)
1920
+ return ticking;
1921
+ ticking = runTick().catch(onError).finally(() => {
1922
+ ticking = undefined;
1923
+ arm();
1924
+ });
1925
+ return ticking;
1926
+ };
1927
+ const scheduleInput = async (request) => {
1928
+ const modes = [
1929
+ request.at !== undefined,
1930
+ request.afterMs !== undefined,
1931
+ request.everyMs !== undefined
1932
+ ];
1933
+ if (modes.filter(Boolean).length !== 1)
1934
+ throw new TypeError("Declare exactly one schedule mode");
1935
+ if (request.everyMs !== undefined && !request.timeZone) {
1936
+ throw new TypeError("Repeating schedules require an explicit timeZone");
1937
+ }
1938
+ if (request.timeZone) {
1939
+ try {
1940
+ new Intl.DateTimeFormat("en", { timeZone: request.timeZone }).format(new Date(0));
1941
+ } catch {
1942
+ throw new TypeError(`Unknown schedule timeZone: ${request.timeZone}`);
1943
+ }
1944
+ }
1945
+ const observedAt = now();
1946
+ const kind = request.at !== undefined ? "at" : request.afterMs !== undefined ? "after" : "every";
1947
+ const intervalMs = request.everyMs;
1948
+ const delay = request.afterMs ?? request.everyMs;
1949
+ if (delay !== undefined && (!Number.isSafeInteger(delay) || delay < 1)) {
1950
+ throw new TypeError("Schedule delay must be a positive safe integer");
1951
+ }
1952
+ const nextAt = request.at ? new Date(z9.iso.datetime({ offset: true }).parse(request.at)).toISOString() : new Date(observedAt.getTime() + (delay ?? 0)).toISOString();
1953
+ const schedule = AgentScheduleSchema.parse({
1954
+ id: randomUUID2(),
1955
+ conversationId: request.conversationId,
1956
+ kind,
1957
+ nextAt,
1958
+ ...intervalMs !== undefined && { intervalMs },
1959
+ ...request.timeZone && { timeZone: request.timeZone },
1960
+ input: z9.json().parse(request.input),
1961
+ state: "scheduled",
1962
+ occurrence: 0,
1963
+ createdAt: observedAt.toISOString(),
1964
+ updatedAt: observedAt.toISOString()
1965
+ });
1966
+ await input.sqlite.transaction(async (scope) => {
1967
+ scope.database.prepare(`
1968
+ INSERT INTO stitchkit_agent_runtime_schedules (
1969
+ id, conversation_id, kind, next_at, interval_ms, time_zone,
1970
+ input_payload, state, occurrence, created_at, updated_at
1971
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, 'scheduled', 0, ?, ?)
1972
+ `).run(schedule.id, schedule.conversationId, schedule.kind, schedule.nextAt, schedule.intervalMs ?? null, schedule.timeZone ?? null, JSON.stringify(schedule.input), schedule.createdAt, schedule.updatedAt);
1973
+ await scope.appendEvent({
1974
+ conversationId: schedule.conversationId,
1975
+ kind: "schedule/set",
1976
+ occurredAt: schedule.createdAt,
1977
+ payload: { id: schedule.id, kind: schedule.kind, nextAt: schedule.nextAt }
1978
+ });
1979
+ });
1980
+ arm();
1981
+ return schedule;
1982
+ };
1983
+ const cancelSchedule = async (conversationId, id) => {
1984
+ const observedAt = now().toISOString();
1985
+ const changed = await input.sqlite.transaction(async (scope) => {
1986
+ const count = scope.database.prepare(`
1987
+ UPDATE stitchkit_agent_runtime_schedules SET state = 'cancelled', updated_at = ?
1988
+ WHERE id = ? AND conversation_id = ? AND state = 'scheduled'
1989
+ `).run(observedAt, id, conversationId).changes;
1990
+ if (count > 0) {
1991
+ await scope.appendEvent({
1992
+ conversationId,
1993
+ kind: "schedule/cancelled",
1994
+ occurredAt: observedAt,
1995
+ payload: { id }
1996
+ });
1997
+ }
1998
+ return count;
1999
+ });
2000
+ if (changed > 0)
2001
+ arm();
2002
+ return changed > 0;
2003
+ };
2004
+ return {
2005
+ scheduleInput,
2006
+ cancelSchedule,
2007
+ listSchedules,
2008
+ tick,
2009
+ start: arm,
2010
+ close() {
2011
+ closed = true;
2012
+ if (timer)
2013
+ clearTimer(timer);
2014
+ }
2015
+ };
2016
+ }
2017
+ // src/agent-runtime/spill.ts
2018
+ import { createHash, randomUUID as randomUUID3 } from "node:crypto";
2019
+ import { z as z10 } from "zod";
2020
+ var SpillRowSchema = z10.object({
2021
+ payload: z10.instanceof(Uint8Array),
2022
+ bytes: z10.int().nonnegative(),
2023
+ authorization_payload: z10.string().nullable()
2024
+ });
2025
+ var SpillMetadataRowSchema = z10.object({
2026
+ reference_id: z10.string(),
2027
+ bytes: z10.int().nonnegative(),
2028
+ created_at: z10.string()
2029
+ });
2030
+ function decodeAuthorization(value) {
2031
+ if (value === null)
770
2032
  return;
771
- for (const key of ["statusCode", "status", "code"]) {
772
- const candidate = value[key];
773
- if (typeof candidate === "number" && candidate >= 400 && candidate < 600)
774
- return candidate;
775
- }
776
- const nested = value.cause ?? value.response ?? value.error;
777
- return nested === value ? undefined : statusOf(nested);
2033
+ return JSON.parse(value);
778
2034
  }
779
- function classifyProviderFailure(error) {
780
- const status = statusOf(error);
781
- const byStatus = status === undefined ? undefined : BY_STATUS[status];
782
- if (byStatus) {
2035
+ function createSqliteAgentSpillStore(input) {
2036
+ const now = input.now ?? (() => new Date);
2037
+ const database = input.sqlite.database;
2038
+ const authorization = (reference) => {
2039
+ const raw = database.prepare(`
2040
+ SELECT payload, bytes, authorization_payload
2041
+ FROM stitchkit_agent_runtime_spills
2042
+ WHERE reference_id = ? AND conversation_id = ?
2043
+ `).get(reference, input.conversationId);
2044
+ if (raw === null || raw === undefined)
2045
+ throw new TypeError("Unknown spill reference");
2046
+ return decodeAuthorization(SpillRowSchema.parse(raw).authorization_payload);
2047
+ };
2048
+ const write = async (request) => {
2049
+ const reference = randomUUID3();
2050
+ const createdAt = now();
2051
+ const expiresAt = input.retentionMs ? new Date(createdAt.getTime() + input.retentionMs).toISOString() : null;
2052
+ const sha256 = createHash("sha256").update(request.data).digest("hex");
2053
+ await input.sqlite.transaction(async (scope) => {
2054
+ scope.database.prepare(`
2055
+ INSERT INTO stitchkit_agent_runtime_spills (
2056
+ reference_id, conversation_id, run_id, tool_call_id, media_type,
2057
+ bytes, sha256, created_at, expires_at, payload, authorization_payload
2058
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2059
+ `).run(reference, input.conversationId, input.runId ?? null, input.toolCallId ?? null, request.mediaType, request.data.byteLength, sha256, createdAt.toISOString(), expiresAt, request.data, request.authorization ? JSON.stringify(request.authorization) : null);
2060
+ await scope.appendEvent({
2061
+ conversationId: input.conversationId,
2062
+ kind: "spill/created",
2063
+ occurredAt: createdAt.toISOString(),
2064
+ payload: { reference, bytes: request.data.byteLength, sha256 }
2065
+ });
2066
+ });
2067
+ return { reference };
2068
+ };
2069
+ const read = async (request) => {
2070
+ const raw = database.prepare(`
2071
+ SELECT payload, bytes, authorization_payload
2072
+ FROM stitchkit_agent_runtime_spills
2073
+ WHERE reference_id = ? AND conversation_id = ?
2074
+ `).get(request.reference, input.conversationId);
2075
+ if (raw === null || raw === undefined)
2076
+ throw new TypeError("Unknown spill reference");
2077
+ const row = SpillRowSchema.parse(raw);
783
2078
  return {
784
- reason: byStatus,
785
- status,
786
- retryable: RETRYABLE.has(byStatus),
787
- evidence: "status"
2079
+ data: row.payload.slice(request.offset, request.offset + request.maxBytes),
2080
+ totalBytes: row.bytes,
2081
+ ...decodeAuthorization(row.authorization_payload) && {
2082
+ authorization: decodeAuthorization(row.authorization_payload)
2083
+ }
788
2084
  };
789
- }
790
- const message = messageOf(error);
791
- for (const [pattern, reason] of BY_MESSAGE) {
792
- if (pattern.test(message)) {
793
- return {
794
- reason,
795
- ...status !== undefined && { status },
796
- retryable: RETRYABLE.has(reason),
797
- evidence: "message"
798
- };
2085
+ };
2086
+ const search = (request) => {
2087
+ const raw = database.prepare(`
2088
+ SELECT payload, bytes, authorization_payload
2089
+ FROM stitchkit_agent_runtime_spills
2090
+ WHERE reference_id = ? AND conversation_id = ?
2091
+ `).get(request.reference, input.conversationId);
2092
+ if (raw === null || raw === undefined)
2093
+ throw new TypeError("Unknown spill reference");
2094
+ const row = SpillRowSchema.parse(raw);
2095
+ const matches = [];
2096
+ new TextDecoder("utf-8", { fatal: true }).decode(row.payload).split(`
2097
+ `).forEach((text, index) => {
2098
+ if (matches.length < request.maxMatches && text.includes(request.query)) {
2099
+ matches.push({ line: index + 1, text });
2100
+ }
2101
+ });
2102
+ return matches;
2103
+ };
2104
+ const cleanup = async () => {
2105
+ const expired = database.prepare(`
2106
+ SELECT reference_id, bytes, created_at
2107
+ FROM stitchkit_agent_runtime_spills
2108
+ WHERE conversation_id = ? AND expires_at IS NOT NULL AND expires_at <= ?
2109
+ ORDER BY created_at, reference_id
2110
+ `).all(input.conversationId, now().toISOString()).map((row) => SpillMetadataRowSchema.parse(row));
2111
+ let bytes = 0;
2112
+ for (const spill of expired) {
2113
+ await input.sqlite.transaction(async (scope) => {
2114
+ scope.database.prepare("DELETE FROM stitchkit_agent_runtime_spills WHERE reference_id = ?").run(spill.reference_id);
2115
+ await scope.appendEvent({
2116
+ conversationId: input.conversationId,
2117
+ kind: "spill/deleted",
2118
+ payload: { reference: spill.reference_id, bytes: spill.bytes }
2119
+ });
2120
+ });
2121
+ bytes += spill.bytes;
799
2122
  }
800
- }
801
- return {
802
- reason: "unknown",
803
- ...status !== undefined && { status },
804
- retryable: false,
805
- evidence: "none"
806
- };
807
- }
808
- function isToolResultFailure(output) {
809
- const parsed = typeof output === "string" ? parseJson(output) : output;
810
- if (!isRecord(parsed))
811
- return false;
812
- if ("error" in parsed)
813
- return true;
814
- return isRecord(parsed.value) && "error" in parsed.value;
815
- }
816
- function parseJson(value) {
817
- try {
818
- return JSON.parse(value);
819
- } catch {
820
- return;
821
- }
2123
+ return { deleted: expired.length, bytes };
2124
+ };
2125
+ return { authorization, write, read, search, cleanup };
2126
+ }
2127
+ // src/agent-runtime/state-tools.ts
2128
+ import { z as z11 } from "zod";
2129
+ var GoalSchema = z11.object({
2130
+ objective: z11.string().min(1),
2131
+ status: z11.enum(["active", "complete", "blocked"])
2132
+ }).strict();
2133
+ var TodoItemSchema = z11.object({
2134
+ step: z11.string().min(1),
2135
+ status: z11.enum(["pending", "in_progress", "completed"])
2136
+ }).strict();
2137
+ function createAgentStateTools(input) {
2138
+ const writeGoal = async (value) => {
2139
+ const stored = await input.state.set({
2140
+ conversationId: input.conversationId,
2141
+ name: "goal",
2142
+ value,
2143
+ actor: "agent"
2144
+ });
2145
+ return GoalSchema.parse(stored.value);
2146
+ };
2147
+ return [
2148
+ defineAgentTool({
2149
+ name: "create_goal",
2150
+ description: "Create the durable objective for this conversation.",
2151
+ identity: { serviceName: "agent-state", action: "create-goal", method: "POST" },
2152
+ input: z11.object({ objective: z11.string().min(1) }).strict(),
2153
+ output: GoalSchema,
2154
+ transports: ["AGENT"],
2155
+ handler: async ({ input: request }) => {
2156
+ if (await input.state.get(input.conversationId, "goal")) {
2157
+ throw new TypeError("A durable goal already exists for this conversation");
2158
+ }
2159
+ return writeGoal({ objective: request.objective, status: "active" });
2160
+ }
2161
+ }),
2162
+ defineAgentTool({
2163
+ name: "update_goal",
2164
+ description: "Update the durable objective or lifecycle status.",
2165
+ identity: { serviceName: "agent-state", action: "update-goal", method: "POST" },
2166
+ input: z11.object({
2167
+ objective: z11.string().min(1).optional(),
2168
+ status: z11.enum(["active", "complete", "blocked"]).optional()
2169
+ }).strict().refine((request) => request.objective !== undefined || request.status !== undefined, {
2170
+ message: "At least one goal field must be supplied"
2171
+ }),
2172
+ output: GoalSchema,
2173
+ transports: ["AGENT"],
2174
+ handler: async ({ input: request }) => {
2175
+ const current = await input.state.get(input.conversationId, "goal");
2176
+ if (!current)
2177
+ throw new TypeError("No durable goal exists for this conversation");
2178
+ const goal = GoalSchema.parse(current.value);
2179
+ return writeGoal({ ...goal, ...request });
2180
+ }
2181
+ }),
2182
+ defineAgentTool({
2183
+ name: "get_goal",
2184
+ description: "Read the durable objective for this conversation.",
2185
+ identity: { serviceName: "agent-state", action: "get-goal", method: "GET" },
2186
+ input: z11.object({}).strict(),
2187
+ output: z11.object({ goal: GoalSchema.optional() }).strict(),
2188
+ transports: ["AGENT"],
2189
+ handler: async () => {
2190
+ const current = await input.state.get(input.conversationId, "goal");
2191
+ return { ...current && { goal: GoalSchema.parse(current.value) } };
2192
+ }
2193
+ }),
2194
+ defineAgentTool({
2195
+ name: "todo_write",
2196
+ description: "Replace the durable ordered work plan for this conversation.",
2197
+ identity: { serviceName: "agent-state", action: "write-todo", method: "POST" },
2198
+ input: z11.object({ items: z11.array(TodoItemSchema) }).strict(),
2199
+ output: z11.object({ items: z11.array(TodoItemSchema) }).strict(),
2200
+ transports: ["AGENT"],
2201
+ handler: async ({ input: request }) => {
2202
+ const stored = await input.state.set({
2203
+ conversationId: input.conversationId,
2204
+ name: "todo",
2205
+ value: request,
2206
+ actor: "agent"
2207
+ });
2208
+ return z11.object({ items: z11.array(TodoItemSchema) }).strict().parse(stored.value);
2209
+ }
2210
+ })
2211
+ ];
822
2212
  }
823
2213
  export {
824
2214
  validateAgentModelSnapshot,
@@ -827,36 +2217,69 @@ export {
827
2217
  selectAgentHistory,
828
2218
  searchAgentModelCatalog,
829
2219
  runStateForTerminalReason,
2220
+ renderAgentStateSlots,
2221
+ recordAgentSandboxProbe,
2222
+ recordAgentRetryDecision,
830
2223
  purgeAgentConversation,
831
2224
  projectAgentHistoryDetailed,
832
2225
  projectAgentHistory,
2226
+ probeAgentProcessSandbox,
2227
+ missingSandboxRestrictions,
833
2228
  isToolResultFailure,
834
2229
  isAssistantHistoryEvidence,
835
2230
  hasAgentTerminalOutput,
2231
+ encodeAgentConversationArchive,
2232
+ defineStateSlot,
836
2233
  defineModelRegistry,
837
2234
  defineAgentProtocol,
2235
+ defineAgentProjection,
2236
+ decodeAgentStoreEvent,
2237
+ decodeAgentConversationArchive,
2238
+ createSqliteAgentSpillStore,
2239
+ createSqliteAgentProjectionStore,
2240
+ createSqliteAgentEventSearch,
2241
+ createSqliteAgentChildManager,
838
2242
  createMemoryAgentRuntimeStore,
839
2243
  createMemoryAgentModelSelectionStore,
840
2244
  createDeferredAgentToolSurface,
841
2245
  createAgentToolFenceLifecycle,
2246
+ createAgentStateTools,
2247
+ createAgentStateSlotStore,
842
2248
  createAgentSessionCoordinator,
2249
+ createAgentScheduleTools,
2250
+ createAgentScheduleService,
843
2251
  createAgentRuntimeStore,
844
2252
  createAgentRuntimeEventSink,
845
2253
  createAgentRuntime,
2254
+ createAgentProjectionRegistry,
846
2255
  createAgentObservability,
2256
+ createAgentEventSearchTools,
2257
+ createAgentChildTools,
847
2258
  composeAgentPrompt,
848
2259
  classifyProviderFailure,
2260
+ canonicalAgentJson,
2261
+ agentUsageProjection,
2262
+ agentTodoStateSlot,
2263
+ agentSummaryProjection,
2264
+ agentStateSlotsProjection,
2265
+ agentScheduleSummaryProjection,
2266
+ agentOutlineProjection,
2267
+ agentGoalStateSlot,
849
2268
  agentDurableEventId,
2269
+ agentConversationCardProjection,
2270
+ agentChildBudgetStopPolicy,
850
2271
  advanceAgentRuntimeEventCursor,
851
2272
  RequestRunInterruptSchema,
852
2273
  ReplaceCompactedRangeSchema,
853
2274
  RecoverAgentRunSchema,
854
2275
  RecordRunOperationSchema,
2276
+ ReadAgentStoreEventsSchema,
855
2277
  DeferredAgentToolSearchInputSchema,
856
2278
  DeferredAgentToolReceiptSchema,
857
2279
  DeferredAgentToolMatchSchema,
858
2280
  CommitRunTerminalSchema,
859
2281
  CheckpointRunAssistantSchema,
2282
+ AppendAgentStoreEventSchema,
860
2283
  AgentUsageValueSchema,
861
2284
  AgentUsageSchema,
862
2285
  AgentTransientDeltaEventSchema,
@@ -871,14 +2294,21 @@ export {
871
2294
  AgentTerminalReasonSchema,
872
2295
  AgentTerminalEventSchema,
873
2296
  AgentStoredRunSchema,
2297
+ AgentStoreTransitionSchema,
874
2298
  AgentStoreNotFoundSchema,
875
2299
  AgentStoreMutationResultSchema,
2300
+ AgentStoreEventPageSchema,
2301
+ AgentStoreEventKindSchema,
2302
+ AgentStoreEventEnvelopeSchema,
876
2303
  AgentStoreDuplicateSchema,
877
2304
  AgentStoreConflictSchema,
878
2305
  AgentStoreAppliedSchema,
879
2306
  AgentStepFinishedEventSchema,
880
2307
  AgentSourcePartSchema,
881
2308
  AgentSnapshotSchema,
2309
+ AgentScheduleSchema,
2310
+ AgentSandboxRestrictionSchema,
2311
+ AgentSandboxGradeSchema,
882
2312
  AgentRuntimeHeadSchema,
883
2313
  AgentRuntimeEventSchema,
884
2314
  AgentRuntimeEventCursorSchema,
@@ -904,6 +2334,7 @@ export {
904
2334
  AgentReasoningPartSchema,
905
2335
  AgentReasoningEndEventSchema,
906
2336
  AgentReasoningDeltaEventSchema,
2337
+ AgentProviderStreamCutError,
907
2338
  AgentProviderEnvelopeSchema,
908
2339
  AgentProvenanceSchema,
909
2340
  AgentOpaquePartSchema,
@@ -925,6 +2356,7 @@ export {
925
2356
  AgentJsonObjectSchema,
926
2357
  AgentHistoryMutationSchema,
927
2358
  AgentFilePartSchema,
2359
+ AgentEventSearchResultSchema,
928
2360
  AgentCostValueSchema,
929
2361
  AgentConversationSummarySchema,
930
2362
  AgentConversationPurgedError,
@@ -932,9 +2364,14 @@ export {
932
2364
  AgentConversationPurgeInputSchema,
933
2365
  AgentConversationPageSchema,
934
2366
  AgentConversationMessagePageSchema,
2367
+ AgentConversationArchiveSchema,
935
2368
  AgentControlPartSchema,
936
2369
  AgentContextOverflowError,
2370
+ AgentChildStateSchema,
2371
+ AgentChildRecordSchema,
2372
+ AgentChildBudgetSchema,
937
2373
  AgentCheckpointEventSchema,
2374
+ AgentAttemptResetEventSchema,
938
2375
  AgentAssistantPlaceholderSchema,
939
2376
  AgentAdmissionReceiptSchema,
940
2377
  AgentAdmissionEventSchema,