stitchkit 0.85.2 → 0.87.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 (118) hide show
  1. package/CHANGELOG.md +243 -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/conversations.d.ts +11 -0
  19. package/dist/agent-runtime/conversations.d.ts.map +1 -1
  20. package/dist/agent-runtime/event-schema.d.ts +34 -0
  21. package/dist/agent-runtime/event-schema.d.ts.map +1 -1
  22. package/dist/agent-runtime/event-search-tools.d.ts +53 -0
  23. package/dist/agent-runtime/event-search-tools.d.ts.map +1 -0
  24. package/dist/agent-runtime/event-search.d.ts +23 -0
  25. package/dist/agent-runtime/event-search.d.ts.map +1 -0
  26. package/dist/agent-runtime/fault-bench.d.ts +45 -0
  27. package/dist/agent-runtime/fault-bench.d.ts.map +1 -0
  28. package/dist/agent-runtime/observability.d.ts +6 -0
  29. package/dist/agent-runtime/observability.d.ts.map +1 -1
  30. package/dist/agent-runtime/projections.d.ts +69 -0
  31. package/dist/agent-runtime/projections.d.ts.map +1 -0
  32. package/dist/agent-runtime/provider-failure.d.ts +5 -1
  33. package/dist/agent-runtime/provider-failure.d.ts.map +1 -1
  34. package/dist/agent-runtime/provider-origin.d.ts +12 -0
  35. package/dist/agent-runtime/provider-origin.d.ts.map +1 -0
  36. package/dist/agent-runtime/retry-policy.d.ts +27 -0
  37. package/dist/agent-runtime/retry-policy.d.ts.map +1 -0
  38. package/dist/agent-runtime/run-execution.d.ts +0 -9
  39. package/dist/agent-runtime/run-execution.d.ts.map +1 -1
  40. package/dist/agent-runtime/run-operation-lifecycle.d.ts.map +1 -1
  41. package/dist/agent-runtime/runtime.d.ts +15 -0
  42. package/dist/agent-runtime/runtime.d.ts.map +1 -1
  43. package/dist/agent-runtime/sandbox.d.ts +66 -0
  44. package/dist/agent-runtime/sandbox.d.ts.map +1 -0
  45. package/dist/agent-runtime/schedule-tools.d.ts +35 -0
  46. package/dist/agent-runtime/schedule-tools.d.ts.map +1 -0
  47. package/dist/agent-runtime/schedules.d.ts +63 -0
  48. package/dist/agent-runtime/schedules.d.ts.map +1 -0
  49. package/dist/agent-runtime/schemas.d.ts +9 -0
  50. package/dist/agent-runtime/schemas.d.ts.map +1 -1
  51. package/dist/agent-runtime/spill.d.ts +24 -0
  52. package/dist/agent-runtime/spill.d.ts.map +1 -0
  53. package/dist/agent-runtime/sqlite-purge.d.ts.map +1 -1
  54. package/dist/agent-runtime/sqlite.d.ts +26 -0
  55. package/dist/agent-runtime/sqlite.d.ts.map +1 -1
  56. package/dist/agent-runtime/state-slots.d.ts +48 -0
  57. package/dist/agent-runtime/state-slots.d.ts.map +1 -0
  58. package/dist/agent-runtime/state-tools.d.ts +58 -0
  59. package/dist/agent-runtime/state-tools.d.ts.map +1 -0
  60. package/dist/agent-runtime/store-driver.d.ts +13 -0
  61. package/dist/agent-runtime/store-driver.d.ts.map +1 -1
  62. package/dist/agent-runtime/store-events.d.ts +829 -0
  63. package/dist/agent-runtime/store-events.d.ts.map +1 -0
  64. package/dist/agent-runtime/store-migrations/v1-to-v2.d.ts +6 -0
  65. package/dist/agent-runtime/store-migrations/v1-to-v2.d.ts.map +1 -0
  66. package/dist/agent-runtime/store.d.ts +45 -0
  67. package/dist/agent-runtime/store.d.ts.map +1 -1
  68. package/dist/agent-runtime/terminal-commit.d.ts +1 -1
  69. package/dist/agent-runtime-browser.js +5 -3
  70. package/dist/agent-runtime-coding-tools.js +144 -15
  71. package/dist/agent-runtime-harness.js +12 -11
  72. package/dist/agent-runtime-sqlite-bun.d.ts +1 -1
  73. package/dist/agent-runtime-sqlite-bun.d.ts.map +1 -1
  74. package/dist/agent-runtime-sqlite-bun.js +5 -4
  75. package/dist/agent-runtime-sqlite-node.d.ts +1 -1
  76. package/dist/agent-runtime-sqlite-node.d.ts.map +1 -1
  77. package/dist/agent-runtime-sqlite-node.js +5 -4
  78. package/dist/agent-runtime-testing.d.ts +3 -0
  79. package/dist/agent-runtime-testing.d.ts.map +1 -0
  80. package/dist/agent-runtime-testing.js +156 -0
  81. package/dist/agent-runtime.d.ts +16 -2
  82. package/dist/agent-runtime.d.ts.map +1 -1
  83. package/dist/agent-runtime.js +1632 -156
  84. package/dist/application.js +14 -14
  85. package/dist/cli.js +6 -6
  86. package/dist/{index-fwbkp2jn.js → index-0vfdh685.js} +2 -2
  87. package/dist/{index-5z8tezpx.js → index-0ydchz76.js} +3 -3
  88. package/dist/{index-3ds42w1w.js → index-1szetq6f.js} +33 -4
  89. package/dist/{index-2jg7b11j.js → index-35aefxby.js} +879 -424
  90. package/dist/index-3yrqvza8.js +964 -0
  91. package/dist/{index-fq492hg0.js → index-5wezxcxx.js} +238 -161
  92. package/dist/index-9553432s.js +26 -0
  93. package/dist/{index-27487ay5.js → index-9hw9eq48.js} +4 -4
  94. package/dist/index-cnyk6te3.js +77 -0
  95. package/dist/index-gskm1k5a.js +50 -0
  96. package/dist/{index-4hk633vz.js → index-hkm6wysp.js} +9 -2
  97. package/dist/{index-dx9xehwt.js → index-kc6h6hg0.js} +1 -1
  98. package/dist/{index-x15ss2dx.js → index-p1b1y93x.js} +3 -0
  99. package/dist/{index-mgtxfe6b.js → index-r159gjwy.js} +8 -1
  100. package/dist/{index-s4ckmrac.js → index-rcgsnrr6.js} +3 -3
  101. package/dist/{index-dggwnnjv.js → index-wn9cb82y.js} +1 -1
  102. package/dist/index-z1m86vc8.js +334 -0
  103. package/dist/{index-ff0kcqvp.js → index-z2452tsw.js} +3 -3
  104. package/dist/internal/sqlite.d.ts +13 -0
  105. package/dist/internal/sqlite.d.ts.map +1 -1
  106. package/dist/testing/agent-store-conformance.d.ts.map +1 -1
  107. package/dist/testing.js +89 -80
  108. package/dist/tool-invoker.js +4 -4
  109. package/dist/tools/internal/surface-projector.d.ts.map +1 -1
  110. package/dist/tools/manifest.d.ts +16 -0
  111. package/dist/tools/manifest.d.ts.map +1 -1
  112. package/dist/tools.d.ts +1 -1
  113. package/dist/tools.d.ts.map +1 -1
  114. package/dist/tools.js +10 -8
  115. package/llms-full.txt +373 -16
  116. package/package.json +6 -1
  117. package/dist/index-66nxrgy0.js +0 -554
  118. package/dist/index-y2s6h5bf.js +0 -83
@@ -1,55 +1,66 @@
1
1
  import {
2
- messageOf
3
- } from "./index-p13mwz16.js";
2
+ AgentConversationMessagePageSchema,
3
+ AgentConversationPageSchema,
4
+ AgentConversationSummarySchema
5
+ } from "./index-9553432s.js";
4
6
  import {
5
7
  ACTIVE_AGENT_RUN_STATES,
6
- AcceptInputAndAssignRunSchema,
7
- AcquireAgentRunSchema,
8
8
  AgentAdmissionReceiptSchema,
9
- AgentConversationMessagePageSchema,
10
- AgentConversationPageSchema,
11
9
  AgentConversationPurgeInputSchema,
12
10
  AgentConversationPurgeResultSchema,
13
11
  AgentConversationPurgedError,
14
- AgentConversationSummarySchema,
15
12
  AgentHistoryMutationSchema,
16
- AgentRecoverableDescriptorSchema,
17
- AgentRecoverablePageSchema,
18
- AgentRunViewSchema,
19
13
  AgentRuntimeHeadSchema,
20
- AgentStoreAppliedSchema,
21
- AgentStoreConflictSchema,
22
- AgentStoreDuplicateSchema,
23
- AgentStoreMutationResultSchema,
24
- AgentStoreNotFoundSchema,
25
14
  AgentStoredRunSchema,
26
- CheckpointRunAssistantSchema,
27
- CommitRunTerminalSchema,
28
- RecordRunOperationSchema,
29
- RecoverAgentRunSchema,
30
- ReplaceCompactedRangeSchema,
31
- RequestRunInterruptSchema,
32
15
  createAgentRuntimeStore,
33
16
  createMemoryAgentRuntimeStore,
34
17
  purgeAgentConversation
35
- } from "./index-fq492hg0.js";
18
+ } from "./index-5wezxcxx.js";
36
19
  import {
37
20
  AgentContextOverflowError,
21
+ AgentProviderStreamCutError,
38
22
  AgentRuntimeConflictError,
39
23
  AgentTokenCountSchema,
24
+ agentGoalStateSlot,
25
+ agentTodoStateSlot,
26
+ classifyProviderFailure,
40
27
  composeAgentPrompt,
41
28
  createAgentRuntime,
42
29
  createAgentRuntimeEventSink,
43
30
  createAgentSessionCoordinator,
31
+ createAgentStateSlotStore,
44
32
  createAgentToolFenceLifecycle,
45
33
  deferredToolCatalog,
34
+ defineStateSlot,
35
+ isToolResultFailure,
36
+ normalizeSdkUsage,
46
37
  projectAgentHistory,
47
38
  projectAgentHistoryDetailed,
39
+ recordAgentRetryDecision,
48
40
  registerDeferredToolCatalog,
49
41
  registerDeferredToolRepair,
42
+ renderAgentStateSlots,
50
43
  repairedSearchCall,
51
44
  selectAgentHistory
52
- } from "./index-2jg7b11j.js";
45
+ } from "./index-35aefxby.js";
46
+ import"./index-p13mwz16.js";
47
+ import {
48
+ AgentAdmissionEventSchema,
49
+ AgentAttemptResetEventSchema,
50
+ AgentCheckpointEventSchema,
51
+ AgentReasoningDeltaEventSchema,
52
+ AgentReasoningEndEventSchema,
53
+ AgentReasoningStartEventSchema,
54
+ AgentRunOperationEventSchema,
55
+ AgentRunStateEventSchema,
56
+ AgentRuntimeEventCursorSchema,
57
+ AgentRuntimeEventSchema,
58
+ AgentTerminalEventSchema,
59
+ AgentToolStatusEventSchema,
60
+ AgentTransientDeltaEventSchema,
61
+ advanceAgentRuntimeEventCursor,
62
+ agentDurableEventId
63
+ } from "./index-hkm6wysp.js";
53
64
  import {
54
65
  AgentModelCapabilitySchema,
55
66
  AgentModelCatalogEntrySchema,
@@ -68,28 +79,36 @@ import {
68
79
  validateAgentModelSnapshot
69
80
  } from "./index-tg3m2ec5.js";
70
81
  import {
82
+ AcceptInputAndAssignRunSchema,
83
+ AcquireAgentRunSchema,
84
+ AgentConversationArchiveSchema,
85
+ AgentRecoverableDescriptorSchema,
86
+ AgentRecoverablePageSchema,
87
+ AgentRunViewSchema,
88
+ AgentStoreAppliedSchema,
89
+ AgentStoreConflictSchema,
90
+ AgentStoreDuplicateSchema,
91
+ AgentStoreEventEnvelopeSchema,
92
+ AgentStoreEventKindSchema,
93
+ AgentStoreEventPageSchema,
94
+ AgentStoreMutationResultSchema,
95
+ AgentStoreNotFoundSchema,
96
+ AgentStoreTransitionSchema,
97
+ AppendAgentStoreEventSchema,
98
+ CheckpointRunAssistantSchema,
99
+ CommitRunTerminalSchema,
100
+ ReadAgentStoreEventsSchema,
101
+ RecordRunOperationSchema,
102
+ RecoverAgentRunSchema,
103
+ ReplaceCompactedRangeSchema,
104
+ RequestRunInterruptSchema,
105
+ canonicalAgentJson,
106
+ decodeAgentConversationArchive,
107
+ decodeAgentStoreEvent,
108
+ encodeAgentConversationArchive,
71
109
  isAssistantHistoryEvidence,
72
110
  isCompleteAgentHistoryTurn
73
- } from "./index-y2s6h5bf.js";
74
- 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";
111
+ } from "./index-z1m86vc8.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,582 @@ 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
+ var ClaimRowSchema = z9.object({
1775
+ state: z9.enum(["scheduled", "cancelled", "completed"]),
1776
+ claim_until: z9.string().nullable()
1777
+ });
1778
+ var FinalizeRowSchema = z9.object({
1779
+ state: z9.enum(["scheduled", "cancelled", "completed"]),
1780
+ claim_owner: z9.string().nullable()
1781
+ });
1782
+ var StateRowSchema = z9.object({
1783
+ state: z9.enum(["scheduled", "cancelled", "completed"])
1784
+ });
1785
+ function parseSchedule(raw) {
1786
+ const row = ScheduleRowSchema.parse(raw);
1787
+ return AgentScheduleSchema.parse({
1788
+ id: row.id,
1789
+ conversationId: row.conversation_id,
1790
+ kind: row.kind,
1791
+ nextAt: row.next_at,
1792
+ ...row.interval_ms !== null && { intervalMs: row.interval_ms },
1793
+ ...row.time_zone !== null && { timeZone: row.time_zone },
1794
+ input: JSON.parse(row.input_payload),
1795
+ state: row.state,
1796
+ occurrence: row.occurrence,
1797
+ createdAt: row.created_at,
1798
+ updatedAt: row.updated_at
1799
+ });
1800
+ }
1801
+ function createAgentScheduleService(input) {
1802
+ const onError = input.onError ?? ((error) => {
1803
+ console.error("[stitchkit] agent schedule tick failed", error);
1804
+ });
1805
+ const now = input.now ?? (() => new Date);
1806
+ const database = input.sqlite.database;
1807
+ const setTimer = input.setTimer ?? ((callback, delay) => setTimeout(callback, delay));
1808
+ const clearTimer = input.clearTimer ?? clearTimeout;
1809
+ let timer;
1810
+ let closed = false;
1811
+ const listSchedules = (conversationId) => database.prepare(`
1812
+ SELECT id, conversation_id, kind, next_at, interval_ms, time_zone,
1813
+ input_payload, state, occurrence, created_at, updated_at
1814
+ FROM stitchkit_agent_runtime_schedules
1815
+ WHERE conversation_id = ? ORDER BY created_at, id
1816
+ `).all(conversationId).map(parseSchedule);
1817
+ const arm = () => {
1818
+ if (timer)
1819
+ clearTimer(timer);
1820
+ if (closed)
1821
+ return;
1822
+ try {
1823
+ armNext();
1824
+ } catch (error) {
1825
+ onError(error);
1826
+ }
1827
+ };
1828
+ const armNext = () => {
1829
+ const raw = database.prepare(`
1830
+ SELECT MAX(next_at, COALESCE(claim_until, next_at)) AS due_at
1831
+ FROM stitchkit_agent_runtime_schedules
1832
+ WHERE state = 'scheduled' ORDER BY due_at, id LIMIT 1
1833
+ `).get();
1834
+ if (raw === null || raw === undefined || typeof raw.due_at !== "string")
1835
+ return;
1836
+ const delay = Math.max(0, new Date(raw.due_at).getTime() - now().getTime());
1837
+ timer = setTimer(() => void tick(), Math.min(delay, 2147483647));
1838
+ };
1839
+ const owner = randomUUID2();
1840
+ const CLAIM_MS = 60000;
1841
+ let ticking;
1842
+ const claim = (id, until, at) => input.sqlite.transaction(async (scope) => {
1843
+ const raw = scope.database.prepare(`
1844
+ SELECT state, claim_until FROM stitchkit_agent_runtime_schedules WHERE id = ?
1845
+ `).get(id);
1846
+ if (raw === null || raw === undefined)
1847
+ return false;
1848
+ const row = ClaimRowSchema.parse(raw);
1849
+ if (row.state !== "scheduled")
1850
+ return false;
1851
+ if (row.claim_until !== null && row.claim_until >= at)
1852
+ return false;
1853
+ scope.database.prepare(`
1854
+ UPDATE stitchkit_agent_runtime_schedules
1855
+ SET claim_owner = ?, claim_until = ?, updated_at = ?
1856
+ WHERE id = ?
1857
+ `).run(owner, until, at, id);
1858
+ return true;
1859
+ });
1860
+ const runTick = async () => {
1861
+ if (closed)
1862
+ return;
1863
+ const observedAt = now();
1864
+ const due = database.prepare(`
1865
+ SELECT id, conversation_id, kind, next_at, interval_ms, time_zone,
1866
+ input_payload, state, occurrence, created_at, updated_at
1867
+ FROM stitchkit_agent_runtime_schedules
1868
+ WHERE state = 'scheduled' AND next_at <= ? ORDER BY next_at, id
1869
+ `).all(observedAt.toISOString()).map(parseSchedule);
1870
+ for (const schedule of due) {
1871
+ const at = observedAt.toISOString();
1872
+ if (!await claim(schedule.id, new Date(observedAt.getTime() + CLAIM_MS).toISOString(), at)) {
1873
+ continue;
1874
+ }
1875
+ const occurrence = schedule.occurrence + 1;
1876
+ const lateByMs = Math.max(0, observedAt.getTime() - new Date(schedule.nextAt).getTime());
1877
+ try {
1878
+ await input.dispatch({
1879
+ conversationId: schedule.conversationId,
1880
+ idempotencyKey: `schedule:${schedule.id}:${occurrence}`,
1881
+ input: schedule.input,
1882
+ schedule: { id: schedule.id, occurrence, lateByMs }
1883
+ });
1884
+ } catch (error) {
1885
+ await input.sqlite.transaction(async (scope) => {
1886
+ scope.database.prepare(`
1887
+ UPDATE stitchkit_agent_runtime_schedules
1888
+ SET claim_owner = NULL, claim_until = NULL, updated_at = ?
1889
+ WHERE id = ? AND claim_owner = ?
1890
+ `).run(at, schedule.id, owner);
1891
+ await scope.appendEvent({
1892
+ conversationId: schedule.conversationId,
1893
+ kind: "schedule/failed",
1894
+ occurredAt: at,
1895
+ payload: {
1896
+ id: schedule.id,
1897
+ occurrence,
1898
+ message: error instanceof Error ? error.message : String(error)
1899
+ }
1900
+ });
1901
+ });
1902
+ continue;
1903
+ }
1904
+ let nextAt = schedule.nextAt;
1905
+ if (schedule.kind === "every" && schedule.intervalMs) {
1906
+ let next = new Date(schedule.nextAt).getTime() + schedule.intervalMs;
1907
+ while (next <= observedAt.getTime())
1908
+ next += schedule.intervalMs;
1909
+ nextAt = new Date(next).toISOString();
1910
+ }
1911
+ const state = schedule.kind === "every" ? "scheduled" : "completed";
1912
+ await input.sqlite.transaction(async (scope) => {
1913
+ const held = scope.database.prepare(`
1914
+ SELECT state, claim_owner FROM stitchkit_agent_runtime_schedules WHERE id = ?
1915
+ `).get(schedule.id);
1916
+ const settled = held !== null && held !== undefined && (() => {
1917
+ const row = FinalizeRowSchema.parse(held);
1918
+ return row.state === "scheduled" && row.claim_owner === owner;
1919
+ })();
1920
+ if (settled) {
1921
+ scope.database.prepare(`
1922
+ UPDATE stitchkit_agent_runtime_schedules
1923
+ SET state = ?, occurrence = ?, next_at = ?, updated_at = ?,
1924
+ claim_owner = NULL, claim_until = NULL
1925
+ WHERE id = ?
1926
+ `).run(state, occurrence, nextAt, at, schedule.id);
1927
+ }
1928
+ if (lateByMs > 0) {
1929
+ await scope.appendEvent({
1930
+ conversationId: schedule.conversationId,
1931
+ kind: "schedule/late",
1932
+ occurredAt: at,
1933
+ payload: { id: schedule.id, occurrence, lateByMs }
1934
+ });
1935
+ }
1936
+ await scope.appendEvent({
1937
+ conversationId: schedule.conversationId,
1938
+ kind: "schedule/fired",
1939
+ occurredAt: at,
1940
+ payload: {
1941
+ id: schedule.id,
1942
+ occurrence,
1943
+ lateByMs,
1944
+ nextAt,
1945
+ state: settled ? state : "cancelled"
1946
+ }
1947
+ });
1948
+ });
1949
+ }
1950
+ };
1951
+ const tick = () => {
1952
+ if (ticking)
1953
+ return ticking;
1954
+ ticking = runTick().catch(onError).finally(() => {
1955
+ ticking = undefined;
1956
+ arm();
1957
+ });
1958
+ return ticking;
1959
+ };
1960
+ const scheduleInput = async (request) => {
1961
+ const modes = [
1962
+ request.at !== undefined,
1963
+ request.afterMs !== undefined,
1964
+ request.everyMs !== undefined
1965
+ ];
1966
+ if (modes.filter(Boolean).length !== 1)
1967
+ throw new TypeError("Declare exactly one schedule mode");
1968
+ if (request.everyMs !== undefined && !request.timeZone) {
1969
+ throw new TypeError("Repeating schedules require an explicit timeZone");
1970
+ }
1971
+ if (request.timeZone) {
1972
+ try {
1973
+ new Intl.DateTimeFormat("en", { timeZone: request.timeZone }).format(new Date(0));
1974
+ } catch {
1975
+ throw new TypeError(`Unknown schedule timeZone: ${request.timeZone}`);
1976
+ }
1977
+ }
1978
+ const observedAt = now();
1979
+ const kind = request.at !== undefined ? "at" : request.afterMs !== undefined ? "after" : "every";
1980
+ const intervalMs = request.everyMs;
1981
+ const delay = request.afterMs ?? request.everyMs;
1982
+ if (delay !== undefined && (!Number.isSafeInteger(delay) || delay < 1)) {
1983
+ throw new TypeError("Schedule delay must be a positive safe integer");
1984
+ }
1985
+ const nextAt = request.at ? new Date(z9.iso.datetime({ offset: true }).parse(request.at)).toISOString() : new Date(observedAt.getTime() + (delay ?? 0)).toISOString();
1986
+ const schedule = AgentScheduleSchema.parse({
1987
+ id: randomUUID2(),
1988
+ conversationId: request.conversationId,
1989
+ kind,
1990
+ nextAt,
1991
+ ...intervalMs !== undefined && { intervalMs },
1992
+ ...request.timeZone && { timeZone: request.timeZone },
1993
+ input: z9.json().parse(request.input),
1994
+ state: "scheduled",
1995
+ occurrence: 0,
1996
+ createdAt: observedAt.toISOString(),
1997
+ updatedAt: observedAt.toISOString()
1998
+ });
1999
+ await input.sqlite.transaction(async (scope) => {
2000
+ scope.database.prepare(`
2001
+ INSERT INTO stitchkit_agent_runtime_schedules (
2002
+ id, conversation_id, kind, next_at, interval_ms, time_zone,
2003
+ input_payload, state, occurrence, created_at, updated_at
2004
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, 'scheduled', 0, ?, ?)
2005
+ `).run(schedule.id, schedule.conversationId, schedule.kind, schedule.nextAt, schedule.intervalMs ?? null, schedule.timeZone ?? null, JSON.stringify(schedule.input), schedule.createdAt, schedule.updatedAt);
2006
+ await scope.appendEvent({
2007
+ conversationId: schedule.conversationId,
2008
+ kind: "schedule/set",
2009
+ occurredAt: schedule.createdAt,
2010
+ payload: { id: schedule.id, kind: schedule.kind, nextAt: schedule.nextAt }
2011
+ });
2012
+ });
2013
+ arm();
2014
+ return schedule;
2015
+ };
2016
+ const cancelSchedule = async (conversationId, id) => {
2017
+ const observedAt = now().toISOString();
2018
+ const changed = await input.sqlite.transaction(async (scope) => {
2019
+ const raw = scope.database.prepare(`
2020
+ SELECT state FROM stitchkit_agent_runtime_schedules
2021
+ WHERE id = ? AND conversation_id = ?
2022
+ `).get(id, conversationId);
2023
+ if (raw === null || raw === undefined)
2024
+ return false;
2025
+ if (StateRowSchema.parse(raw).state !== "scheduled")
2026
+ return false;
2027
+ scope.database.prepare(`
2028
+ UPDATE stitchkit_agent_runtime_schedules SET state = 'cancelled', updated_at = ?
2029
+ WHERE id = ? AND conversation_id = ?
2030
+ `).run(observedAt, id, conversationId);
2031
+ await scope.appendEvent({
2032
+ conversationId,
2033
+ kind: "schedule/cancelled",
2034
+ occurredAt: observedAt,
2035
+ payload: { id }
2036
+ });
2037
+ return true;
2038
+ });
2039
+ if (changed)
2040
+ arm();
2041
+ return changed;
2042
+ };
2043
+ return {
2044
+ scheduleInput,
2045
+ cancelSchedule,
2046
+ listSchedules,
2047
+ tick,
2048
+ start: arm,
2049
+ close() {
2050
+ closed = true;
2051
+ if (timer)
2052
+ clearTimer(timer);
2053
+ }
2054
+ };
2055
+ }
2056
+ // src/agent-runtime/spill.ts
2057
+ import { createHash, randomUUID as randomUUID3 } from "node:crypto";
2058
+ import { z as z10 } from "zod";
2059
+ var SpillRowSchema = z10.object({
2060
+ payload: z10.instanceof(Uint8Array),
2061
+ bytes: z10.int().nonnegative(),
2062
+ authorization_payload: z10.string().nullable()
2063
+ });
2064
+ var SpillMetadataRowSchema = z10.object({
2065
+ reference_id: z10.string(),
2066
+ bytes: z10.int().nonnegative(),
2067
+ created_at: z10.string()
2068
+ });
2069
+ function decodeAuthorization(value) {
2070
+ if (value === null)
770
2071
  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);
2072
+ return JSON.parse(value);
778
2073
  }
779
- function classifyProviderFailure(error) {
780
- const status = statusOf(error);
781
- const byStatus = status === undefined ? undefined : BY_STATUS[status];
782
- if (byStatus) {
2074
+ function createSqliteAgentSpillStore(input) {
2075
+ const now = input.now ?? (() => new Date);
2076
+ const database = input.sqlite.database;
2077
+ const authorization = (reference) => {
2078
+ const raw = database.prepare(`
2079
+ SELECT payload, bytes, authorization_payload
2080
+ FROM stitchkit_agent_runtime_spills
2081
+ WHERE reference_id = ? AND conversation_id = ?
2082
+ `).get(reference, input.conversationId);
2083
+ if (raw === null || raw === undefined)
2084
+ throw new TypeError("Unknown spill reference");
2085
+ return decodeAuthorization(SpillRowSchema.parse(raw).authorization_payload);
2086
+ };
2087
+ const write = async (request) => {
2088
+ const reference = randomUUID3();
2089
+ const createdAt = now();
2090
+ const expiresAt = input.retentionMs ? new Date(createdAt.getTime() + input.retentionMs).toISOString() : null;
2091
+ const sha256 = createHash("sha256").update(request.data).digest("hex");
2092
+ await input.sqlite.transaction(async (scope) => {
2093
+ scope.database.prepare(`
2094
+ INSERT INTO stitchkit_agent_runtime_spills (
2095
+ reference_id, conversation_id, run_id, tool_call_id, media_type,
2096
+ bytes, sha256, created_at, expires_at, payload, authorization_payload
2097
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2098
+ `).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);
2099
+ await scope.appendEvent({
2100
+ conversationId: input.conversationId,
2101
+ kind: "spill/created",
2102
+ occurredAt: createdAt.toISOString(),
2103
+ payload: { reference, bytes: request.data.byteLength, sha256 }
2104
+ });
2105
+ });
2106
+ return { reference };
2107
+ };
2108
+ const read = async (request) => {
2109
+ const raw = database.prepare(`
2110
+ SELECT payload, bytes, authorization_payload
2111
+ FROM stitchkit_agent_runtime_spills
2112
+ WHERE reference_id = ? AND conversation_id = ?
2113
+ `).get(request.reference, input.conversationId);
2114
+ if (raw === null || raw === undefined)
2115
+ throw new TypeError("Unknown spill reference");
2116
+ const row = SpillRowSchema.parse(raw);
783
2117
  return {
784
- reason: byStatus,
785
- status,
786
- retryable: RETRYABLE.has(byStatus),
787
- evidence: "status"
2118
+ data: row.payload.slice(request.offset, request.offset + request.maxBytes),
2119
+ totalBytes: row.bytes,
2120
+ ...decodeAuthorization(row.authorization_payload) && {
2121
+ authorization: decodeAuthorization(row.authorization_payload)
2122
+ }
788
2123
  };
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
- };
2124
+ };
2125
+ const search = (request) => {
2126
+ const raw = database.prepare(`
2127
+ SELECT payload, bytes, authorization_payload
2128
+ FROM stitchkit_agent_runtime_spills
2129
+ WHERE reference_id = ? AND conversation_id = ?
2130
+ `).get(request.reference, input.conversationId);
2131
+ if (raw === null || raw === undefined)
2132
+ throw new TypeError("Unknown spill reference");
2133
+ const row = SpillRowSchema.parse(raw);
2134
+ const matches = [];
2135
+ new TextDecoder("utf-8", { fatal: true }).decode(row.payload).split(`
2136
+ `).forEach((text, index) => {
2137
+ if (matches.length < request.maxMatches && text.includes(request.query)) {
2138
+ matches.push({ line: index + 1, text });
2139
+ }
2140
+ });
2141
+ return matches;
2142
+ };
2143
+ const cleanup = async () => {
2144
+ const expired = database.prepare(`
2145
+ SELECT reference_id, bytes, created_at
2146
+ FROM stitchkit_agent_runtime_spills
2147
+ WHERE conversation_id = ? AND expires_at IS NOT NULL AND expires_at <= ?
2148
+ ORDER BY created_at, reference_id
2149
+ `).all(input.conversationId, now().toISOString()).map((row) => SpillMetadataRowSchema.parse(row));
2150
+ let bytes = 0;
2151
+ for (const spill of expired) {
2152
+ await input.sqlite.transaction(async (scope) => {
2153
+ scope.database.prepare("DELETE FROM stitchkit_agent_runtime_spills WHERE reference_id = ?").run(spill.reference_id);
2154
+ await scope.appendEvent({
2155
+ conversationId: input.conversationId,
2156
+ kind: "spill/deleted",
2157
+ payload: { reference: spill.reference_id, bytes: spill.bytes }
2158
+ });
2159
+ });
2160
+ bytes += spill.bytes;
799
2161
  }
800
- }
801
- return {
802
- reason: "unknown",
803
- ...status !== undefined && { status },
804
- retryable: false,
805
- evidence: "none"
2162
+ return { deleted: expired.length, bytes };
806
2163
  };
2164
+ return { authorization, write, read, search, cleanup };
807
2165
  }
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
- }
2166
+ // src/agent-runtime/state-tools.ts
2167
+ import { z as z11 } from "zod";
2168
+ var GoalSchema = z11.object({
2169
+ objective: z11.string().min(1),
2170
+ status: z11.enum(["active", "complete", "blocked"])
2171
+ }).strict();
2172
+ var TodoItemSchema = z11.object({
2173
+ step: z11.string().min(1),
2174
+ status: z11.enum(["pending", "in_progress", "completed"])
2175
+ }).strict();
2176
+ function createAgentStateTools(input) {
2177
+ const writeGoal = async (value) => {
2178
+ const stored = await input.state.set({
2179
+ conversationId: input.conversationId,
2180
+ name: "goal",
2181
+ value,
2182
+ actor: "agent"
2183
+ });
2184
+ return GoalSchema.parse(stored.value);
2185
+ };
2186
+ return [
2187
+ defineAgentTool({
2188
+ name: "create_goal",
2189
+ description: "Create the durable objective for this conversation.",
2190
+ identity: { serviceName: "agent-state", action: "create-goal", method: "POST" },
2191
+ input: z11.object({ objective: z11.string().min(1) }).strict(),
2192
+ output: GoalSchema,
2193
+ transports: ["AGENT"],
2194
+ handler: async ({ input: request }) => {
2195
+ if (await input.state.get(input.conversationId, "goal")) {
2196
+ throw new TypeError("A durable goal already exists for this conversation");
2197
+ }
2198
+ return writeGoal({ objective: request.objective, status: "active" });
2199
+ }
2200
+ }),
2201
+ defineAgentTool({
2202
+ name: "update_goal",
2203
+ description: "Update the durable objective or lifecycle status.",
2204
+ identity: { serviceName: "agent-state", action: "update-goal", method: "POST" },
2205
+ input: z11.object({
2206
+ objective: z11.string().min(1).optional(),
2207
+ status: z11.enum(["active", "complete", "blocked"]).optional()
2208
+ }).strict().refine((request) => request.objective !== undefined || request.status !== undefined, {
2209
+ message: "At least one goal field must be supplied"
2210
+ }),
2211
+ output: GoalSchema,
2212
+ transports: ["AGENT"],
2213
+ handler: async ({ input: request }) => {
2214
+ const current = await input.state.get(input.conversationId, "goal");
2215
+ if (!current)
2216
+ throw new TypeError("No durable goal exists for this conversation");
2217
+ const goal = GoalSchema.parse(current.value);
2218
+ return writeGoal({ ...goal, ...request });
2219
+ }
2220
+ }),
2221
+ defineAgentTool({
2222
+ name: "get_goal",
2223
+ description: "Read the durable objective for this conversation.",
2224
+ identity: { serviceName: "agent-state", action: "get-goal", method: "GET" },
2225
+ input: z11.object({}).strict(),
2226
+ output: z11.object({ goal: GoalSchema.optional() }).strict(),
2227
+ transports: ["AGENT"],
2228
+ handler: async () => {
2229
+ const current = await input.state.get(input.conversationId, "goal");
2230
+ return { ...current && { goal: GoalSchema.parse(current.value) } };
2231
+ }
2232
+ }),
2233
+ defineAgentTool({
2234
+ name: "todo_write",
2235
+ description: "Replace the durable ordered work plan for this conversation.",
2236
+ identity: { serviceName: "agent-state", action: "write-todo", method: "POST" },
2237
+ input: z11.object({ items: z11.array(TodoItemSchema) }).strict(),
2238
+ output: z11.object({ items: z11.array(TodoItemSchema) }).strict(),
2239
+ transports: ["AGENT"],
2240
+ handler: async ({ input: request }) => {
2241
+ const stored = await input.state.set({
2242
+ conversationId: input.conversationId,
2243
+ name: "todo",
2244
+ value: request,
2245
+ actor: "agent"
2246
+ });
2247
+ return z11.object({ items: z11.array(TodoItemSchema) }).strict().parse(stored.value);
2248
+ }
2249
+ })
2250
+ ];
822
2251
  }
823
2252
  export {
824
2253
  validateAgentModelSnapshot,
@@ -827,36 +2256,69 @@ export {
827
2256
  selectAgentHistory,
828
2257
  searchAgentModelCatalog,
829
2258
  runStateForTerminalReason,
2259
+ renderAgentStateSlots,
2260
+ recordAgentSandboxProbe,
2261
+ recordAgentRetryDecision,
830
2262
  purgeAgentConversation,
831
2263
  projectAgentHistoryDetailed,
832
2264
  projectAgentHistory,
2265
+ probeAgentProcessSandbox,
2266
+ missingSandboxRestrictions,
833
2267
  isToolResultFailure,
834
2268
  isAssistantHistoryEvidence,
835
2269
  hasAgentTerminalOutput,
2270
+ encodeAgentConversationArchive,
2271
+ defineStateSlot,
836
2272
  defineModelRegistry,
837
2273
  defineAgentProtocol,
2274
+ defineAgentProjection,
2275
+ decodeAgentStoreEvent,
2276
+ decodeAgentConversationArchive,
2277
+ createSqliteAgentSpillStore,
2278
+ createSqliteAgentProjectionStore,
2279
+ createSqliteAgentEventSearch,
2280
+ createSqliteAgentChildManager,
838
2281
  createMemoryAgentRuntimeStore,
839
2282
  createMemoryAgentModelSelectionStore,
840
2283
  createDeferredAgentToolSurface,
841
2284
  createAgentToolFenceLifecycle,
2285
+ createAgentStateTools,
2286
+ createAgentStateSlotStore,
842
2287
  createAgentSessionCoordinator,
2288
+ createAgentScheduleTools,
2289
+ createAgentScheduleService,
843
2290
  createAgentRuntimeStore,
844
2291
  createAgentRuntimeEventSink,
845
2292
  createAgentRuntime,
2293
+ createAgentProjectionRegistry,
846
2294
  createAgentObservability,
2295
+ createAgentEventSearchTools,
2296
+ createAgentChildTools,
847
2297
  composeAgentPrompt,
848
2298
  classifyProviderFailure,
2299
+ canonicalAgentJson,
2300
+ agentUsageProjection,
2301
+ agentTodoStateSlot,
2302
+ agentSummaryProjection,
2303
+ agentStateSlotsProjection,
2304
+ agentScheduleSummaryProjection,
2305
+ agentOutlineProjection,
2306
+ agentGoalStateSlot,
849
2307
  agentDurableEventId,
2308
+ agentConversationCardProjection,
2309
+ agentChildBudgetStopPolicy,
850
2310
  advanceAgentRuntimeEventCursor,
851
2311
  RequestRunInterruptSchema,
852
2312
  ReplaceCompactedRangeSchema,
853
2313
  RecoverAgentRunSchema,
854
2314
  RecordRunOperationSchema,
2315
+ ReadAgentStoreEventsSchema,
855
2316
  DeferredAgentToolSearchInputSchema,
856
2317
  DeferredAgentToolReceiptSchema,
857
2318
  DeferredAgentToolMatchSchema,
858
2319
  CommitRunTerminalSchema,
859
2320
  CheckpointRunAssistantSchema,
2321
+ AppendAgentStoreEventSchema,
860
2322
  AgentUsageValueSchema,
861
2323
  AgentUsageSchema,
862
2324
  AgentTransientDeltaEventSchema,
@@ -871,14 +2333,21 @@ export {
871
2333
  AgentTerminalReasonSchema,
872
2334
  AgentTerminalEventSchema,
873
2335
  AgentStoredRunSchema,
2336
+ AgentStoreTransitionSchema,
874
2337
  AgentStoreNotFoundSchema,
875
2338
  AgentStoreMutationResultSchema,
2339
+ AgentStoreEventPageSchema,
2340
+ AgentStoreEventKindSchema,
2341
+ AgentStoreEventEnvelopeSchema,
876
2342
  AgentStoreDuplicateSchema,
877
2343
  AgentStoreConflictSchema,
878
2344
  AgentStoreAppliedSchema,
879
2345
  AgentStepFinishedEventSchema,
880
2346
  AgentSourcePartSchema,
881
2347
  AgentSnapshotSchema,
2348
+ AgentScheduleSchema,
2349
+ AgentSandboxRestrictionSchema,
2350
+ AgentSandboxGradeSchema,
882
2351
  AgentRuntimeHeadSchema,
883
2352
  AgentRuntimeEventSchema,
884
2353
  AgentRuntimeEventCursorSchema,
@@ -904,6 +2373,7 @@ export {
904
2373
  AgentReasoningPartSchema,
905
2374
  AgentReasoningEndEventSchema,
906
2375
  AgentReasoningDeltaEventSchema,
2376
+ AgentProviderStreamCutError,
907
2377
  AgentProviderEnvelopeSchema,
908
2378
  AgentProvenanceSchema,
909
2379
  AgentOpaquePartSchema,
@@ -925,6 +2395,7 @@ export {
925
2395
  AgentJsonObjectSchema,
926
2396
  AgentHistoryMutationSchema,
927
2397
  AgentFilePartSchema,
2398
+ AgentEventSearchResultSchema,
928
2399
  AgentCostValueSchema,
929
2400
  AgentConversationSummarySchema,
930
2401
  AgentConversationPurgedError,
@@ -932,9 +2403,14 @@ export {
932
2403
  AgentConversationPurgeInputSchema,
933
2404
  AgentConversationPageSchema,
934
2405
  AgentConversationMessagePageSchema,
2406
+ AgentConversationArchiveSchema,
935
2407
  AgentControlPartSchema,
936
2408
  AgentContextOverflowError,
2409
+ AgentChildStateSchema,
2410
+ AgentChildRecordSchema,
2411
+ AgentChildBudgetSchema,
937
2412
  AgentCheckpointEventSchema,
2413
+ AgentAttemptResetEventSchema,
938
2414
  AgentAssistantPlaceholderSchema,
939
2415
  AgentAdmissionReceiptSchema,
940
2416
  AgentAdmissionEventSchema,