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.
- package/CHANGELOG.md +243 -0
- package/README.md +3 -0
- package/dist/agent-runtime/agent-tool.d.ts +25 -0
- package/dist/agent-runtime/agent-tool.d.ts.map +1 -0
- package/dist/agent-runtime/child-tools.d.ts +69 -0
- package/dist/agent-runtime/child-tools.d.ts.map +1 -0
- package/dist/agent-runtime/children.d.ts +113 -0
- package/dist/agent-runtime/children.d.ts.map +1 -0
- package/dist/agent-runtime/coding-tool-contract.d.ts +29 -0
- package/dist/agent-runtime/coding-tool-contract.d.ts.map +1 -1
- package/dist/agent-runtime/coding-tool-refusals.d.ts +2 -0
- package/dist/agent-runtime/coding-tool-refusals.d.ts.map +1 -1
- package/dist/agent-runtime/coding-tool-shell.d.ts.map +1 -1
- package/dist/agent-runtime/coding-tools.d.ts +1 -0
- package/dist/agent-runtime/coding-tools.d.ts.map +1 -1
- package/dist/agent-runtime/control-schema.d.ts +17 -0
- package/dist/agent-runtime/control-schema.d.ts.map +1 -1
- package/dist/agent-runtime/conversations.d.ts +11 -0
- package/dist/agent-runtime/conversations.d.ts.map +1 -1
- package/dist/agent-runtime/event-schema.d.ts +34 -0
- package/dist/agent-runtime/event-schema.d.ts.map +1 -1
- package/dist/agent-runtime/event-search-tools.d.ts +53 -0
- package/dist/agent-runtime/event-search-tools.d.ts.map +1 -0
- package/dist/agent-runtime/event-search.d.ts +23 -0
- package/dist/agent-runtime/event-search.d.ts.map +1 -0
- package/dist/agent-runtime/fault-bench.d.ts +45 -0
- package/dist/agent-runtime/fault-bench.d.ts.map +1 -0
- package/dist/agent-runtime/observability.d.ts +6 -0
- package/dist/agent-runtime/observability.d.ts.map +1 -1
- package/dist/agent-runtime/projections.d.ts +69 -0
- package/dist/agent-runtime/projections.d.ts.map +1 -0
- package/dist/agent-runtime/provider-failure.d.ts +5 -1
- package/dist/agent-runtime/provider-failure.d.ts.map +1 -1
- package/dist/agent-runtime/provider-origin.d.ts +12 -0
- package/dist/agent-runtime/provider-origin.d.ts.map +1 -0
- package/dist/agent-runtime/retry-policy.d.ts +27 -0
- package/dist/agent-runtime/retry-policy.d.ts.map +1 -0
- package/dist/agent-runtime/run-execution.d.ts +0 -9
- package/dist/agent-runtime/run-execution.d.ts.map +1 -1
- package/dist/agent-runtime/run-operation-lifecycle.d.ts.map +1 -1
- package/dist/agent-runtime/runtime.d.ts +15 -0
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/sandbox.d.ts +66 -0
- package/dist/agent-runtime/sandbox.d.ts.map +1 -0
- package/dist/agent-runtime/schedule-tools.d.ts +35 -0
- package/dist/agent-runtime/schedule-tools.d.ts.map +1 -0
- package/dist/agent-runtime/schedules.d.ts +63 -0
- package/dist/agent-runtime/schedules.d.ts.map +1 -0
- package/dist/agent-runtime/schemas.d.ts +9 -0
- package/dist/agent-runtime/schemas.d.ts.map +1 -1
- package/dist/agent-runtime/spill.d.ts +24 -0
- package/dist/agent-runtime/spill.d.ts.map +1 -0
- package/dist/agent-runtime/sqlite-purge.d.ts.map +1 -1
- package/dist/agent-runtime/sqlite.d.ts +26 -0
- package/dist/agent-runtime/sqlite.d.ts.map +1 -1
- package/dist/agent-runtime/state-slots.d.ts +48 -0
- package/dist/agent-runtime/state-slots.d.ts.map +1 -0
- package/dist/agent-runtime/state-tools.d.ts +58 -0
- package/dist/agent-runtime/state-tools.d.ts.map +1 -0
- package/dist/agent-runtime/store-driver.d.ts +13 -0
- package/dist/agent-runtime/store-driver.d.ts.map +1 -1
- package/dist/agent-runtime/store-events.d.ts +829 -0
- package/dist/agent-runtime/store-events.d.ts.map +1 -0
- package/dist/agent-runtime/store-migrations/v1-to-v2.d.ts +6 -0
- package/dist/agent-runtime/store-migrations/v1-to-v2.d.ts.map +1 -0
- package/dist/agent-runtime/store.d.ts +45 -0
- package/dist/agent-runtime/store.d.ts.map +1 -1
- package/dist/agent-runtime/terminal-commit.d.ts +1 -1
- package/dist/agent-runtime-browser.js +5 -3
- package/dist/agent-runtime-coding-tools.js +144 -15
- package/dist/agent-runtime-harness.js +12 -11
- package/dist/agent-runtime-sqlite-bun.d.ts +1 -1
- package/dist/agent-runtime-sqlite-bun.d.ts.map +1 -1
- package/dist/agent-runtime-sqlite-bun.js +5 -4
- package/dist/agent-runtime-sqlite-node.d.ts +1 -1
- package/dist/agent-runtime-sqlite-node.d.ts.map +1 -1
- package/dist/agent-runtime-sqlite-node.js +5 -4
- package/dist/agent-runtime-testing.d.ts +3 -0
- package/dist/agent-runtime-testing.d.ts.map +1 -0
- package/dist/agent-runtime-testing.js +156 -0
- package/dist/agent-runtime.d.ts +16 -2
- package/dist/agent-runtime.d.ts.map +1 -1
- package/dist/agent-runtime.js +1632 -156
- package/dist/application.js +14 -14
- package/dist/cli.js +6 -6
- package/dist/{index-fwbkp2jn.js → index-0vfdh685.js} +2 -2
- package/dist/{index-5z8tezpx.js → index-0ydchz76.js} +3 -3
- package/dist/{index-3ds42w1w.js → index-1szetq6f.js} +33 -4
- package/dist/{index-2jg7b11j.js → index-35aefxby.js} +879 -424
- package/dist/index-3yrqvza8.js +964 -0
- package/dist/{index-fq492hg0.js → index-5wezxcxx.js} +238 -161
- package/dist/index-9553432s.js +26 -0
- package/dist/{index-27487ay5.js → index-9hw9eq48.js} +4 -4
- package/dist/index-cnyk6te3.js +77 -0
- package/dist/index-gskm1k5a.js +50 -0
- package/dist/{index-4hk633vz.js → index-hkm6wysp.js} +9 -2
- package/dist/{index-dx9xehwt.js → index-kc6h6hg0.js} +1 -1
- package/dist/{index-x15ss2dx.js → index-p1b1y93x.js} +3 -0
- package/dist/{index-mgtxfe6b.js → index-r159gjwy.js} +8 -1
- package/dist/{index-s4ckmrac.js → index-rcgsnrr6.js} +3 -3
- package/dist/{index-dggwnnjv.js → index-wn9cb82y.js} +1 -1
- package/dist/index-z1m86vc8.js +334 -0
- package/dist/{index-ff0kcqvp.js → index-z2452tsw.js} +3 -3
- package/dist/internal/sqlite.d.ts +13 -0
- package/dist/internal/sqlite.d.ts.map +1 -1
- package/dist/testing/agent-store-conformance.d.ts.map +1 -1
- package/dist/testing.js +89 -80
- package/dist/tool-invoker.js +4 -4
- package/dist/tools/internal/surface-projector.d.ts.map +1 -1
- package/dist/tools/manifest.d.ts +16 -0
- package/dist/tools/manifest.d.ts.map +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +10 -8
- package/llms-full.txt +373 -16
- package/package.json +6 -1
- package/dist/index-66nxrgy0.js +0 -554
- package/dist/index-y2s6h5bf.js +0 -83
package/dist/agent-runtime.js
CHANGED
|
@@ -1,55 +1,66 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
161
|
+
} from "./index-1szetq6f.js";
|
|
133
162
|
import"./index-3xnq72rz.js";
|
|
134
|
-
import"./index-
|
|
163
|
+
import"./index-rcgsnrr6.js";
|
|
135
164
|
import {
|
|
136
165
|
defineRuntimeTool
|
|
137
|
-
} from "./index-
|
|
138
|
-
import"./index-
|
|
139
|
-
import"./index-
|
|
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-
|
|
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 =
|
|
266
|
-
query:
|
|
267
|
-
reason:
|
|
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 =
|
|
270
|
-
name:
|
|
271
|
-
description:
|
|
789
|
+
var DeferredAgentToolMatchSchema = z3.object({
|
|
790
|
+
name: z3.string(),
|
|
791
|
+
description: z3.string()
|
|
272
792
|
});
|
|
273
|
-
var DeferredAgentToolReceiptSchema =
|
|
274
|
-
schemaVersion:
|
|
275
|
-
kind:
|
|
276
|
-
status:
|
|
277
|
-
runId:
|
|
278
|
-
surfaceKey:
|
|
279
|
-
selected:
|
|
280
|
-
matches:
|
|
281
|
-
truncated:
|
|
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
|
|
639
|
-
var AgentRunEventBaseSchema =
|
|
640
|
-
schemaVersion:
|
|
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:
|
|
645
|
-
spanId:
|
|
646
|
-
parentSpanId:
|
|
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:
|
|
649
|
-
durationMs:
|
|
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:
|
|
654
|
-
queueWaitMs:
|
|
1343
|
+
type: z6.literal("run-started"),
|
|
1344
|
+
queueWaitMs: z6.number().nonnegative().optional()
|
|
655
1345
|
});
|
|
656
1346
|
var AgentStepFinishedEventSchema = AgentRunEventBaseSchema.extend({
|
|
657
|
-
type:
|
|
658
|
-
step:
|
|
659
|
-
ttftMs:
|
|
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:
|
|
1353
|
+
type: z6.literal("run-terminal"),
|
|
664
1354
|
terminalReason: AgentTerminalReasonSchema,
|
|
665
|
-
ttftMs:
|
|
666
|
-
internalCause:
|
|
1355
|
+
ttftMs: z6.number().nonnegative().optional(),
|
|
1356
|
+
internalCause: z6.unknown().optional(),
|
|
667
1357
|
usage: AgentUsageSchema
|
|
668
1358
|
});
|
|
669
|
-
var AgentRunEventSchema =
|
|
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/
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
};
|
|
752
|
-
|
|
753
|
-
[
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
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
|
-
|
|
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
|
|
780
|
-
const
|
|
781
|
-
const
|
|
782
|
-
|
|
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
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
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
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
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
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
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,
|