stitchkit 0.68.11 → 0.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.md +30 -1
  2. package/dist/agent-runtime/coding-tool-contract.d.ts +156 -0
  3. package/dist/agent-runtime/coding-tool-contract.d.ts.map +1 -0
  4. package/dist/agent-runtime/coding-tool-files.d.ts +3 -0
  5. package/dist/agent-runtime/coding-tool-files.d.ts.map +1 -0
  6. package/dist/agent-runtime/coding-tool-paths.d.ts +20 -0
  7. package/dist/agent-runtime/coding-tool-paths.d.ts.map +1 -0
  8. package/dist/agent-runtime/coding-tool-search-patch.d.ts +3 -0
  9. package/dist/agent-runtime/coding-tool-search-patch.d.ts.map +1 -0
  10. package/dist/agent-runtime/coding-tool-shell.d.ts +3 -0
  11. package/dist/agent-runtime/coding-tool-shell.d.ts.map +1 -0
  12. package/dist/agent-runtime/coding-tools.d.ts +17 -0
  13. package/dist/agent-runtime/coding-tools.d.ts.map +1 -0
  14. package/dist/agent-runtime/compaction.d.ts +2 -0
  15. package/dist/agent-runtime/compaction.d.ts.map +1 -1
  16. package/dist/agent-runtime/contained-files.d.ts +56 -0
  17. package/dist/agent-runtime/contained-files.d.ts.map +1 -0
  18. package/dist/agent-runtime/control-schema.d.ts +1074 -0
  19. package/dist/agent-runtime/control-schema.d.ts.map +1 -0
  20. package/dist/agent-runtime/conversations.d.ts +128 -0
  21. package/dist/agent-runtime/conversations.d.ts.map +1 -0
  22. package/dist/agent-runtime/event-schema.d.ts +96 -0
  23. package/dist/agent-runtime/event-schema.d.ts.map +1 -1
  24. package/dist/agent-runtime/harness-contract.d.ts +169 -0
  25. package/dist/agent-runtime/harness-contract.d.ts.map +1 -0
  26. package/dist/agent-runtime/harness-control.d.ts +25 -0
  27. package/dist/agent-runtime/harness-control.d.ts.map +1 -0
  28. package/dist/agent-runtime/harness-file-resources.d.ts +33 -0
  29. package/dist/agent-runtime/harness-file-resources.d.ts.map +1 -0
  30. package/dist/agent-runtime/harness-resources.d.ts +5 -0
  31. package/dist/agent-runtime/harness-resources.d.ts.map +1 -0
  32. package/dist/agent-runtime/harness.d.ts +12 -0
  33. package/dist/agent-runtime/harness.d.ts.map +1 -0
  34. package/dist/agent-runtime/history.d.ts +2 -0
  35. package/dist/agent-runtime/history.d.ts.map +1 -1
  36. package/dist/agent-runtime/models.d.ts +199 -0
  37. package/dist/agent-runtime/models.d.ts.map +1 -1
  38. package/dist/agent-runtime/prompt.d.ts +2 -0
  39. package/dist/agent-runtime/prompt.d.ts.map +1 -1
  40. package/dist/agent-runtime/run-execution.d.ts.map +1 -1
  41. package/dist/agent-runtime/runtime.d.ts +11 -3
  42. package/dist/agent-runtime/runtime.d.ts.map +1 -1
  43. package/dist/agent-runtime/schemas.d.ts +49 -0
  44. package/dist/agent-runtime/schemas.d.ts.map +1 -1
  45. package/dist/agent-runtime/sqlite.d.ts +2 -0
  46. package/dist/agent-runtime/sqlite.d.ts.map +1 -1
  47. package/dist/agent-runtime/store-driver.d.ts +61 -212
  48. package/dist/agent-runtime/store-driver.d.ts.map +1 -1
  49. package/dist/agent-runtime/store.d.ts +368 -1
  50. package/dist/agent-runtime/store.d.ts.map +1 -1
  51. package/dist/agent-runtime/terminal-commit.d.ts +12 -1
  52. package/dist/agent-runtime/terminal-commit.d.ts.map +1 -1
  53. package/dist/agent-runtime/terminal-status.d.ts +8 -0
  54. package/dist/agent-runtime/terminal-status.d.ts.map +1 -1
  55. package/dist/agent-runtime-browser.d.ts +1 -0
  56. package/dist/agent-runtime-browser.d.ts.map +1 -1
  57. package/dist/agent-runtime-browser.js +217 -1
  58. package/dist/agent-runtime-coding-tools.d.ts +2 -0
  59. package/dist/agent-runtime-coding-tools.d.ts.map +1 -0
  60. package/dist/agent-runtime-coding-tools.js +766 -0
  61. package/dist/agent-runtime-harness.d.ts +2 -0
  62. package/dist/agent-runtime-harness.d.ts.map +1 -0
  63. package/dist/agent-runtime-harness.js +704 -0
  64. package/dist/agent-runtime-openrouter.d.ts +14 -1
  65. package/dist/agent-runtime-openrouter.d.ts.map +1 -1
  66. package/dist/agent-runtime-openrouter.js +211 -0
  67. package/dist/agent-runtime-sqlite-bun.js +4 -3
  68. package/dist/agent-runtime-sqlite-node.js +4 -3
  69. package/dist/agent-runtime.d.ts +5 -3
  70. package/dist/agent-runtime.d.ts.map +1 -1
  71. package/dist/agent-runtime.js +108 -2580
  72. package/dist/application/diagnostic-journal-contract.d.ts +164 -0
  73. package/dist/application/diagnostic-journal-contract.d.ts.map +1 -0
  74. package/dist/application/diagnostic-journal-manager.d.ts +12 -0
  75. package/dist/application/diagnostic-journal-manager.d.ts.map +1 -0
  76. package/dist/application/diagnostic-journal-storage.d.ts +29 -0
  77. package/dist/application/diagnostic-journal-storage.d.ts.map +1 -0
  78. package/dist/application/diagnostic-journal.d.ts +11 -0
  79. package/dist/application/diagnostic-journal.d.ts.map +1 -0
  80. package/dist/application.d.ts +2 -0
  81. package/dist/application.d.ts.map +1 -1
  82. package/dist/application.js +561 -17
  83. package/dist/browser/socket-io.d.ts.map +1 -1
  84. package/dist/cli.js +9 -6
  85. package/dist/{index-t23p2b68.js → index-0yphgata.js} +1 -1
  86. package/dist/{index-8vpzxg55.js → index-10gbbbaa.js} +103 -2
  87. package/dist/{index-hcx9yypn.js → index-38hs3a58.js} +13 -11
  88. package/dist/index-3xnq72rz.js +21 -0
  89. package/dist/index-7rey2b8s.js +73 -0
  90. package/dist/{index-22tjt2rk.js → index-aczggrty.js} +1 -1
  91. package/dist/index-bd17ytae.js +192 -0
  92. package/dist/{index-xyvxez9r.js → index-da1aqnhb.js} +5 -5
  93. package/dist/index-es0h4w26.js +63 -0
  94. package/dist/{index-hqza5nde.js → index-hsabxjz0.js} +78 -77
  95. package/dist/{index-f24xg2cw.js → index-ktsps64f.js} +4 -2
  96. package/dist/{index-2t6zt5cg.js → index-mjx0wt4c.js} +11 -7
  97. package/dist/{index-y2ctppmg.js → index-qzbg46b6.js} +10 -74
  98. package/dist/{index-bt5179zb.js → index-s8nt8c22.js} +16 -11
  99. package/dist/index-tg3m2ec5.js +157 -0
  100. package/dist/{index-hxh98zbm.js → index-vbf2p6me.js} +1 -1
  101. package/dist/index-vc498pst.js +51 -0
  102. package/dist/{index-pgsyp3xh.js → index-vj3vvpaa.js} +1 -192
  103. package/dist/index-vy5bjy07.js +2569 -0
  104. package/dist/{index-hb0mncmj.js → index-wc80at9n.js} +2 -2
  105. package/dist/{index-eqqyd9v8.js → index-wwst0td5.js} +2 -46
  106. package/dist/{index-ysphyxax.js → index-x1th9s8c.js} +23 -2
  107. package/dist/index-xbtcszs7.js +202 -0
  108. package/dist/{index-grgvpbch.js → index-y3w12g8d.js} +4 -2
  109. package/dist/index.js +51 -37
  110. package/dist/internal/ai-sdk-typed.d.ts +14 -0
  111. package/dist/internal/ai-sdk-typed.d.ts.map +1 -0
  112. package/dist/node.js +6 -5
  113. package/dist/observability/index.js +5 -4
  114. package/dist/realtime/request.d.ts +5 -0
  115. package/dist/realtime/request.d.ts.map +1 -1
  116. package/dist/remote.js +7 -6
  117. package/dist/server/index.js +13 -11
  118. package/dist/testing.js +6 -5
  119. package/dist/tool-invoker.js +7 -5
  120. package/dist/tools/agent-tool-error.d.ts +14 -0
  121. package/dist/tools/agent-tool-error.d.ts.map +1 -0
  122. package/dist/tools/agent.d.ts.map +1 -1
  123. package/dist/tools.js +24 -17
  124. package/llms-full.txt +386 -29
  125. package/package.json +10 -2
@@ -0,0 +1,704 @@
1
+ import {
2
+ AgentRuntimeEventSchema,
3
+ composeAgentPrompt,
4
+ createAgentRuntime,
5
+ createAgentRuntimeEventSink
6
+ } from "./index-vy5bjy07.js";
7
+ import {
8
+ walkContainedFiles
9
+ } from "./index-xbtcszs7.js";
10
+ import {
11
+ AgentModelDescriptorSchema
12
+ } from "./index-tg3m2ec5.js";
13
+ import"./index-es0h4w26.js";
14
+ import"./index-a59da114.js";
15
+ import"./index-3xnq72rz.js";
16
+ import {
17
+ defineRuntimeTool
18
+ } from "./index-wwst0td5.js";
19
+ import"./index-qzbg46b6.js";
20
+ import"./index-ktsps64f.js";
21
+ import {
22
+ AgentMessagePartSchema,
23
+ AgentSnapshotSchema
24
+ } from "./index-x1th9s8c.js";
25
+ import"./index-22by16v6.js";
26
+ import"./index-cby4ar3v.js";
27
+ import"./index-bd17ytae.js";
28
+ import"./index-vj3vvpaa.js";
29
+ import"./index-0w9abg87.js";
30
+ import"./index-qyrqwr4c.js";
31
+ import"./index-6k1937bx.js";
32
+
33
+ // src/agent-runtime/harness-contract.ts
34
+ import { z } from "zod";
35
+ var AgentHarnessResourceKindSchema = z.enum(["instruction", "skill", "resource"]);
36
+ var AgentHarnessResourceSchema = z.object({
37
+ kind: AgentHarnessResourceKindSchema,
38
+ name: z.string().min(1),
39
+ text: z.string(),
40
+ provenance: z.string().min(1)
41
+ }).strict();
42
+ var AgentHarnessResourceDiagnosticSchema = z.object({
43
+ resource: z.string().min(1),
44
+ severity: z.enum(["warning", "error"]),
45
+ message: z.string().min(1)
46
+ }).strict();
47
+ var AgentHarnessLimitsSchema = z.object({
48
+ maxResources: z.int().positive(),
49
+ maxResourceBytes: z.int().positive(),
50
+ maxDiagnostics: z.int().nonnegative()
51
+ }).strict();
52
+ var AgentHarnessAppliedResourceSchema = AgentHarnessResourceSchema.omit({
53
+ text: true
54
+ });
55
+ var AgentHarnessProfileEventSchema = z.object({
56
+ schemaVersion: z.literal(1),
57
+ type: z.literal("profile-applied"),
58
+ conversationId: z.string().min(1),
59
+ runId: z.string().min(1),
60
+ model: AgentModelDescriptorSchema,
61
+ resources: z.array(AgentHarnessAppliedResourceSchema),
62
+ diagnostics: z.array(AgentHarnessResourceDiagnosticSchema),
63
+ toolNames: z.array(z.string().min(1))
64
+ }).strict();
65
+
66
+ // src/agent-runtime/harness-resources.ts
67
+ var DEFAULT_LIMITS = {
68
+ maxResources: 64,
69
+ maxResourceBytes: 1048576,
70
+ maxDiagnostics: 128
71
+ };
72
+ function resolveHarnessLimits(input) {
73
+ return AgentHarnessLimitsSchema.parse({ ...DEFAULT_LIMITS, ...input });
74
+ }
75
+ function utf8Bytes(value) {
76
+ return new TextEncoder().encode(value).byteLength;
77
+ }
78
+ function validateHarnessResources(result, limits) {
79
+ if (result.resources.length > limits.maxResources) {
80
+ throw new Error("Agent harness resource count exceeds maxResources");
81
+ }
82
+ if (result.diagnostics.length > limits.maxDiagnostics) {
83
+ throw new Error("Agent harness diagnostic count exceeds maxDiagnostics");
84
+ }
85
+ const resources = AgentHarnessResourceSchema.array().parse(result.resources);
86
+ const diagnostics = AgentHarnessResourceDiagnosticSchema.array().parse(result.diagnostics);
87
+ const names = new Set;
88
+ let bytes = 0;
89
+ for (const resource of resources) {
90
+ if (names.has(resource.name)) {
91
+ throw new Error(`Duplicate Agent harness resource: ${resource.name}`);
92
+ }
93
+ names.add(resource.name);
94
+ bytes += utf8Bytes(resource.text);
95
+ if (bytes > limits.maxResourceBytes) {
96
+ throw new Error("Agent harness resources exceed maxResourceBytes");
97
+ }
98
+ }
99
+ return { resources, diagnostics };
100
+ }
101
+ function xmlAttribute(value) {
102
+ return value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
103
+ }
104
+ function renderHarnessResources(resources) {
105
+ return resources.map((resource) => `<resource kind="${resource.kind}" name="${xmlAttribute(resource.name)}" provenance="${xmlAttribute(resource.provenance)}">
106
+ ${resource.text}
107
+ </resource>`).join(`
108
+ `);
109
+ }
110
+ // src/agent-runtime/harness-file-resources.ts
111
+ import { realpath } from "node:fs/promises";
112
+ import path from "node:path";
113
+ import { z as z2 } from "zod";
114
+ var AgentHarnessFileRootSchema = z2.object({
115
+ id: z2.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/),
116
+ path: z2.string().min(1),
117
+ kind: z2.enum(["instruction", "skill", "resource"])
118
+ }).strict();
119
+ var AgentHarnessFileLimitsSchema = z2.object({
120
+ maxFiles: z2.int().positive(),
121
+ maxDepth: z2.int().nonnegative(),
122
+ maxFileBytes: z2.int().positive(),
123
+ maxTotalBytes: z2.int().positive()
124
+ }).strict();
125
+ var DEFAULT_LIMITS2 = {
126
+ maxFiles: 128,
127
+ maxDepth: 8,
128
+ maxFileBytes: 262144,
129
+ maxTotalBytes: 1048576
130
+ };
131
+ function metadata(text, fallbackName) {
132
+ const match = /^---\s*\n([\s\S]*?)\n---(?:\s*\n|$)/.exec(text.replaceAll(`\r
133
+ `, `
134
+ `));
135
+ if (!match)
136
+ throw new Error(`Skill ${fallbackName} requires YAML frontmatter`);
137
+ const fields = new Map;
138
+ const lines = (match[1] ?? "").split(`
139
+ `);
140
+ for (let index = 0;index < lines.length; index += 1) {
141
+ const line = lines[index] ?? "";
142
+ const field = /^(name|description):\s*(.*)$/.exec(line);
143
+ if (!field)
144
+ continue;
145
+ const key = field[1] ?? "";
146
+ if (fields.has(key))
147
+ throw new Error(`Skill ${fallbackName} repeats ${key} metadata`);
148
+ const raw = field[2] ?? "";
149
+ if (raw === ">" || raw === "|") {
150
+ const block = [];
151
+ while ((lines[index + 1] ?? "").match(/^\s+/)) {
152
+ index += 1;
153
+ block.push((lines[index] ?? "").replace(/^\s+/, ""));
154
+ }
155
+ fields.set(key, raw === ">" ? block.join(" ").trim() : block.join(`
156
+ `).trim());
157
+ } else if (raw.startsWith('"')) {
158
+ let parsed;
159
+ try {
160
+ parsed = JSON.parse(raw);
161
+ } catch {
162
+ throw new Error(`Skill ${fallbackName} has invalid quoted ${key} metadata`);
163
+ }
164
+ if (typeof parsed !== "string")
165
+ throw new Error(`Skill ${fallbackName} has non-string ${key} metadata`);
166
+ fields.set(key, parsed);
167
+ } else if (raw.startsWith("'") && raw.endsWith("'")) {
168
+ fields.set(key, raw.slice(1, -1).replaceAll("''", "'"));
169
+ } else {
170
+ fields.set(key, raw.trim());
171
+ }
172
+ }
173
+ const name = fields.get("name");
174
+ const description = fields.get("description");
175
+ if (!name || !description) {
176
+ throw new Error(`Skill ${fallbackName} requires name and description metadata`);
177
+ }
178
+ return { name, description };
179
+ }
180
+ function safeName(relative) {
181
+ return relative.split(path.sep).join("/");
182
+ }
183
+ function createAgentHarnessFileResources(config) {
184
+ const roots = AgentHarnessFileRootSchema.array().min(1).parse(config.roots);
185
+ const ids = new Set;
186
+ for (const root of roots) {
187
+ if (!path.isAbsolute(root.path))
188
+ throw new Error("Agent harness resource roots must be absolute");
189
+ if (ids.has(root.id))
190
+ throw new Error(`Duplicate Agent harness resource root id: ${root.id}`);
191
+ ids.add(root.id);
192
+ }
193
+ const limits = AgentHarnessFileLimitsSchema.parse({ ...DEFAULT_LIMITS2, ...config.limits });
194
+ let exact = new Map;
195
+ let loaded;
196
+ const discover = async () => {
197
+ const next = new Map;
198
+ const projected = [];
199
+ let totalBytes = 0;
200
+ let fileCount = 0;
201
+ for (const root of roots) {
202
+ const resolvedRoot = await realpath(root.path);
203
+ const files = await walkContainedFiles({
204
+ root: resolvedRoot,
205
+ maxDepth: limits.maxDepth,
206
+ maxFiles: limits.maxFiles - fileCount,
207
+ readMaxBytes: limits.maxFileBytes
208
+ });
209
+ fileCount += files.length;
210
+ for (const file of files) {
211
+ if (root.kind === "skill" && path.basename(file.absolute) !== "SKILL.md")
212
+ continue;
213
+ const read = file.content;
214
+ if (!read)
215
+ throw new Error("Agent harness resource content is unavailable");
216
+ totalBytes += read.bytes;
217
+ if (totalBytes > limits.maxTotalBytes) {
218
+ throw new Error("Agent harness resources exceed maxTotalBytes");
219
+ }
220
+ const text = read.text;
221
+ const provenance = `${root.id}:${safeName(file.relative)}`;
222
+ const skill = root.kind === "skill" ? metadata(text, provenance) : undefined;
223
+ const name = skill?.name ?? safeName(file.relative);
224
+ if (next.has(name))
225
+ throw new Error(`Duplicate Agent harness resource: ${name}`);
226
+ const resource = {
227
+ kind: root.kind,
228
+ name,
229
+ text,
230
+ provenance
231
+ };
232
+ next.set(name, resource);
233
+ projected.push(root.kind === "instruction" ? resource : {
234
+ ...resource,
235
+ text: root.kind === "skill" ? `Available skill: ${name} — ${skill?.description ?? ""}. Read it by exact name when needed.` : `Available resource: ${name}. Read it by exact name when needed.`
236
+ });
237
+ }
238
+ }
239
+ exact = new Map(next);
240
+ return { resources: Object.freeze([...projected]), diagnostics: [] };
241
+ };
242
+ const load = () => {
243
+ loaded ??= discover();
244
+ return loaded;
245
+ };
246
+ const readResource = defineRuntimeTool({
247
+ name: "read_resource",
248
+ description: "Read one exact discovered harness skill or resource by canonical name.",
249
+ identity: { serviceName: "harness", action: "read-resource", method: "POST" },
250
+ input: z2.object({ name: z2.string().min(1) }).strict(),
251
+ output: z2.object({
252
+ kind: z2.enum(["instruction", "skill", "resource"]),
253
+ name: z2.string().min(1),
254
+ text: z2.string(),
255
+ provenance: z2.string().min(1)
256
+ }).strict(),
257
+ transports: ["AGENT"],
258
+ handler: ({ input }) => {
259
+ const resource = exact.get(input.name);
260
+ if (!resource)
261
+ throw new Error("Harness resource is missing or stale; refresh discovery");
262
+ return resource;
263
+ }
264
+ });
265
+ return { load, runtimeTools: [readResource] };
266
+ }
267
+ // src/agent-runtime/control-schema.ts
268
+ import { z as z3 } from "zod";
269
+ var AgentControlIdentitySchema = z3.object({ schemaVersion: z3.literal(1), requestId: z3.string().min(1) }).strict();
270
+ var AgentControlRequestSchema = z3.discriminatedUnion("operation", [
271
+ AgentControlIdentitySchema.extend({
272
+ operation: z3.literal("attach"),
273
+ conversationId: z3.string().min(1),
274
+ access: z3.enum(["observe", "control"])
275
+ }),
276
+ AgentControlIdentitySchema.extend({
277
+ operation: z3.literal("detach"),
278
+ conversationId: z3.string().min(1)
279
+ }),
280
+ AgentControlIdentitySchema.extend({
281
+ operation: z3.literal("snapshot"),
282
+ conversationId: z3.string().min(1)
283
+ }),
284
+ AgentControlIdentitySchema.extend({
285
+ operation: z3.literal("submit"),
286
+ conversationId: z3.string().min(1),
287
+ idempotencyKey: z3.string().min(1),
288
+ context: z3.unknown(),
289
+ parts: z3.array(AgentMessagePartSchema).min(1),
290
+ metadata: z3.unknown().optional()
291
+ }),
292
+ AgentControlIdentitySchema.extend({
293
+ operation: z3.literal("interrupt"),
294
+ conversationId: z3.string().min(1),
295
+ runId: z3.string().min(1)
296
+ }),
297
+ AgentControlIdentitySchema.extend({
298
+ operation: z3.literal("respond-approval"),
299
+ conversationId: z3.string().min(1),
300
+ approvalId: z3.string().min(1),
301
+ approved: z3.boolean(),
302
+ reason: z3.string().min(1).optional(),
303
+ context: z3.unknown(),
304
+ metadata: z3.unknown().optional()
305
+ })
306
+ ]);
307
+ var AgentControlResponseIdentitySchema = z3.object({
308
+ schemaVersion: z3.literal(1),
309
+ requestId: z3.string().min(1)
310
+ });
311
+ var AgentControlResponseSchema = z3.discriminatedUnion("outcome", [
312
+ AgentControlResponseIdentitySchema.extend({
313
+ outcome: z3.literal("ok"),
314
+ snapshot: AgentSnapshotSchema.optional(),
315
+ runId: z3.string().min(1).optional()
316
+ }).strict(),
317
+ AgentControlResponseIdentitySchema.extend({
318
+ outcome: z3.literal("error"),
319
+ error: z3.object({ code: z3.string().min(1), message: z3.string().min(1) }).strict()
320
+ }).strict()
321
+ ]);
322
+ var AgentControlDeliverySchema = z3.discriminatedUnion("type", [
323
+ z3.object({
324
+ schemaVersion: z3.literal(1),
325
+ type: z3.literal("event"),
326
+ event: AgentRuntimeEventSchema
327
+ }).strict(),
328
+ z3.object({
329
+ schemaVersion: z3.literal(1),
330
+ type: z3.literal("resync-required"),
331
+ conversationId: z3.string().min(1),
332
+ reason: z3.literal("overflow")
333
+ }).strict()
334
+ ]);
335
+ var AgentMultiSessionCursorSchema = z3.object({
336
+ conversations: z3.record(z3.string(), z3.object({
337
+ snapshotVersion: z3.int().nonnegative().optional(),
338
+ durableEventIds: z3.array(z3.string().min(1)).optional(),
339
+ runs: z3.record(z3.string(), z3.object({ runtimeEpoch: z3.string().min(1), sequence: z3.int().nonnegative() }).strict())
340
+ }).strict())
341
+ }).strict();
342
+
343
+ // src/agent-runtime/harness-control.ts
344
+ function createAgentHarnessControlServer(harness, config = {}) {
345
+ const connections = new Map;
346
+ const controllers = new Map;
347
+ let closed = false;
348
+ const unsubscribe = harness.subscribe((event) => {
349
+ for (const connection of connections.values()) {
350
+ if (connection.attached.has(event.conversationId) && !connection.overflowed) {
351
+ connection.sink.publish(event);
352
+ }
353
+ }
354
+ });
355
+ const detach = (connectionId) => {
356
+ const connection = connections.get(connectionId);
357
+ if (!connection)
358
+ return;
359
+ for (const [conversationId, access] of connection.attached) {
360
+ if (access === "control" && controllers.get(conversationId) === connectionId) {
361
+ controllers.delete(conversationId);
362
+ }
363
+ }
364
+ connection.sink.close();
365
+ connections.delete(connectionId);
366
+ };
367
+ return {
368
+ connect({ id, deliver, onOverflow }) {
369
+ if (closed)
370
+ throw new Error("Agent harness control server is closed");
371
+ if (connections.has(id))
372
+ throw new Error("Agent harness control connection id is duplicate");
373
+ const state = {
374
+ attached: new Map,
375
+ overflowed: false,
376
+ sink: createAgentRuntimeEventSink({
377
+ write: (event) => deliver({ schemaVersion: 1, type: "event", event }),
378
+ maxPending: config.maxPendingEvents ?? 128,
379
+ onDrop: ({ event }) => {
380
+ if (state.overflowed)
381
+ return;
382
+ state.overflowed = true;
383
+ onOverflow({
384
+ schemaVersion: 1,
385
+ type: "resync-required",
386
+ conversationId: event.conversationId,
387
+ reason: "overflow"
388
+ });
389
+ detach(id);
390
+ }
391
+ })
392
+ };
393
+ connections.set(id, state);
394
+ return {
395
+ async request(raw) {
396
+ const request = AgentControlRequestSchema.parse(raw);
397
+ const fail = (code, message) => AgentControlResponseSchema.parse({
398
+ schemaVersion: 1,
399
+ requestId: request.requestId,
400
+ outcome: "error",
401
+ error: { code, message }
402
+ });
403
+ if (closed || connections.get(id) !== state)
404
+ return fail("CONNECTION_CLOSED", "Connection is closed");
405
+ try {
406
+ if (request.operation === "attach") {
407
+ const owner = controllers.get(request.conversationId);
408
+ if (request.access === "control" && owner && owner !== id) {
409
+ return fail("LEASE_CONFLICT", "Conversation already has a controller");
410
+ }
411
+ const snapshot = await harness.snapshot(request.conversationId);
412
+ if (request.access === "control")
413
+ controllers.set(request.conversationId, id);
414
+ else if (controllers.get(request.conversationId) === id)
415
+ controllers.delete(request.conversationId);
416
+ state.attached.set(request.conversationId, request.access);
417
+ return AgentControlResponseSchema.parse({
418
+ schemaVersion: 1,
419
+ requestId: request.requestId,
420
+ outcome: "ok",
421
+ snapshot
422
+ });
423
+ }
424
+ if (request.operation === "detach") {
425
+ if (controllers.get(request.conversationId) === id)
426
+ controllers.delete(request.conversationId);
427
+ state.attached.delete(request.conversationId);
428
+ return AgentControlResponseSchema.parse({
429
+ schemaVersion: 1,
430
+ requestId: request.requestId,
431
+ outcome: "ok"
432
+ });
433
+ }
434
+ if (!state.attached.has(request.conversationId))
435
+ return fail("NOT_ATTACHED", "Attach before requesting conversation state");
436
+ if (request.operation === "snapshot") {
437
+ const snapshot = await harness.snapshot(request.conversationId);
438
+ return AgentControlResponseSchema.parse({
439
+ schemaVersion: 1,
440
+ requestId: request.requestId,
441
+ outcome: "ok",
442
+ snapshot
443
+ });
444
+ }
445
+ if (state.attached.get(request.conversationId) !== "control" || controllers.get(request.conversationId) !== id) {
446
+ return fail("LEASE_REQUIRED", "An active controller lease is required");
447
+ }
448
+ if (request.operation === "interrupt") {
449
+ await harness.interrupt({
450
+ conversationId: request.conversationId,
451
+ runId: request.runId
452
+ });
453
+ return AgentControlResponseSchema.parse({
454
+ schemaVersion: 1,
455
+ requestId: request.requestId,
456
+ outcome: "ok",
457
+ snapshot: await harness.snapshot(request.conversationId)
458
+ });
459
+ }
460
+ if (request.operation === "respond-approval") {
461
+ const ticket2 = await harness.respondToApproval({
462
+ conversationId: request.conversationId,
463
+ approvalId: request.approvalId,
464
+ approved: request.approved,
465
+ ...request.reason && { reason: request.reason },
466
+ context: request.context,
467
+ ...request.metadata !== undefined && { metadata: request.metadata }
468
+ });
469
+ const admission2 = await ticket2.admission;
470
+ return AgentControlResponseSchema.parse({
471
+ schemaVersion: 1,
472
+ requestId: request.requestId,
473
+ outcome: "ok",
474
+ runId: admission2.runId,
475
+ snapshot: await harness.snapshot(request.conversationId)
476
+ });
477
+ }
478
+ const ticket = harness.submit({
479
+ conversationId: request.conversationId,
480
+ idempotencyKey: request.idempotencyKey,
481
+ context: request.context,
482
+ parts: request.parts,
483
+ ...request.metadata !== undefined && { metadata: request.metadata }
484
+ });
485
+ const admission = await ticket.admission;
486
+ return AgentControlResponseSchema.parse({
487
+ schemaVersion: 1,
488
+ requestId: request.requestId,
489
+ outcome: "ok",
490
+ runId: admission.runId,
491
+ snapshot: await harness.snapshot(request.conversationId)
492
+ });
493
+ } catch {
494
+ return fail("REQUEST_REJECTED", "Control request was rejected");
495
+ }
496
+ },
497
+ close() {
498
+ detach(id);
499
+ }
500
+ };
501
+ },
502
+ close() {
503
+ if (closed)
504
+ return;
505
+ closed = true;
506
+ unsubscribe();
507
+ for (const id of [...connections.keys()])
508
+ detach(id);
509
+ }
510
+ };
511
+ }
512
+
513
+ // src/agent-runtime/harness.ts
514
+ function activeRunId(snapshot) {
515
+ const active = snapshot.runs.filter((run2) => run2.state === "running" || run2.state === "interrupt_requested");
516
+ if (active.length !== 1) {
517
+ throw new Error("Agent harness prompt requires exactly one active run");
518
+ }
519
+ const run = active[0];
520
+ if (!run)
521
+ throw new Error("Agent harness active run is unavailable");
522
+ return run.id;
523
+ }
524
+ function createHeadlessAgentHarness(config) {
525
+ const {
526
+ models,
527
+ resources,
528
+ tools,
529
+ promptBudget,
530
+ estimateResourceTokens,
531
+ limits: inputLimits,
532
+ onProfile,
533
+ onProfileError,
534
+ publish: applicationPublish,
535
+ ...runtimeConfig
536
+ } = config;
537
+ const limits = resolveHarnessLimits(inputLimits);
538
+ const pending = new Map;
539
+ const subscribers = new Set;
540
+ const emitProfile = async (runId) => {
541
+ const entry = pending.get(runId);
542
+ if (!entry?.prompt || !entry.toolNames || entry.emitting)
543
+ return entry?.emitting;
544
+ const event = AgentHarnessProfileEventSchema.parse({
545
+ schemaVersion: 1,
546
+ type: "profile-applied",
547
+ conversationId: entry.prompt.conversationId,
548
+ runId,
549
+ model: entry.prompt.model.descriptor,
550
+ resources: entry.prompt.resources.map(({ kind, name, provenance }) => ({
551
+ kind,
552
+ name,
553
+ provenance
554
+ })),
555
+ diagnostics: entry.prompt.diagnostics,
556
+ toolNames: entry.toolNames
557
+ });
558
+ const emitting = (async () => {
559
+ try {
560
+ await onProfile?.(event);
561
+ } catch (error) {
562
+ try {
563
+ await onProfileError?.({ event, error });
564
+ } catch {}
565
+ } finally {
566
+ pending.delete(runId);
567
+ }
568
+ })();
569
+ entry.emitting = emitting;
570
+ return emitting;
571
+ };
572
+ const publish = async (event) => {
573
+ try {
574
+ await applicationPublish?.(event);
575
+ } finally {
576
+ for (const subscriber of subscribers) {
577
+ Promise.resolve(subscriber(event)).catch(() => {});
578
+ }
579
+ if (event.type === "terminal")
580
+ pending.delete(event.runId);
581
+ }
582
+ };
583
+ const runtime = createAgentRuntime({
584
+ ...runtimeConfig,
585
+ models,
586
+ publish,
587
+ prompt: async ({ context, signal, model, snapshot }) => {
588
+ const loaded = validateHarnessResources(await resources.load({ context, signal, model, snapshot }), limits);
589
+ try {
590
+ await resources.onDiagnostics?.({ context, diagnostics: loaded.diagnostics });
591
+ } catch {}
592
+ const runId = activeRunId(snapshot);
593
+ const entry = pending.get(runId) ?? {};
594
+ entry.prompt = {
595
+ conversationId: snapshot.conversationId,
596
+ model,
597
+ resources: loaded.resources,
598
+ diagnostics: loaded.diagnostics
599
+ };
600
+ pending.set(runId, entry);
601
+ await emitProfile(runId);
602
+ const prompt = composeAgentPrompt([
603
+ {
604
+ name: "harness-resources",
605
+ stability: "dynamic",
606
+ render: () => renderHarnessResources(loaded.resources),
607
+ ...estimateResourceTokens && { estimateTokens: estimateResourceTokens }
608
+ }
609
+ ]);
610
+ return prompt({
611
+ context,
612
+ signal,
613
+ budget: await promptBudget({
614
+ context,
615
+ contextWindow: model.descriptor.contextWindow
616
+ })
617
+ });
618
+ },
619
+ tools: async (runContext) => {
620
+ const mounted = await tools(runContext);
621
+ const entry = pending.get(runContext.run.id) ?? {};
622
+ entry.toolNames = Object.keys(mounted).sort();
623
+ pending.set(runContext.run.id, entry);
624
+ await emitProfile(runContext.run.id);
625
+ return mounted;
626
+ }
627
+ });
628
+ const pendingApprovals = async (conversationId) => {
629
+ const snapshot = await config.store.loadSnapshot(conversationId);
630
+ const responded = new Set(snapshot.messages.flatMap((message) => message.parts.filter((part) => part.type === "tool-approval-response").map((part) => part.approvalId)));
631
+ const pendingApprovals2 = [];
632
+ for (const message of snapshot.messages) {
633
+ const calls = new Map(message.parts.filter((part) => part.type === "tool-call").map((part) => [part.callId, part]));
634
+ for (const request of message.parts.filter((part) => part.type === "tool-approval-request")) {
635
+ if (responded.has(request.approvalId))
636
+ continue;
637
+ const call = calls.get(request.callId);
638
+ if (!call || !message.runId)
639
+ continue;
640
+ pendingApprovals2.push({
641
+ conversationId,
642
+ runId: message.runId,
643
+ messageId: message.id,
644
+ approvalId: request.approvalId,
645
+ callId: request.callId,
646
+ toolName: call.toolName,
647
+ input: call.input,
648
+ ...request.signature && { signature: request.signature }
649
+ });
650
+ }
651
+ }
652
+ return pendingApprovals2;
653
+ };
654
+ return {
655
+ ...runtime,
656
+ snapshot: (conversationId) => config.store.loadSnapshot(conversationId),
657
+ subscribe(listener) {
658
+ subscribers.add(listener);
659
+ return () => subscribers.delete(listener);
660
+ },
661
+ pendingApprovals,
662
+ async respondToApproval(input) {
663
+ const pending2 = (await pendingApprovals(input.conversationId)).filter((approval) => approval.approvalId === input.approvalId);
664
+ if (pending2.length !== 1) {
665
+ throw new Error("Approval request is missing, stale or already answered");
666
+ }
667
+ const ticket = runtime.submit({
668
+ conversationId: input.conversationId,
669
+ idempotencyKey: `tool-approval:${input.approvalId}`,
670
+ context: input.context,
671
+ role: "tool",
672
+ ...input.metadata !== undefined && { metadata: input.metadata },
673
+ parts: [
674
+ {
675
+ type: "tool-approval-response",
676
+ approvalId: input.approvalId,
677
+ approved: input.approved,
678
+ ...input.reason && { reason: input.reason }
679
+ }
680
+ ]
681
+ });
682
+ const checkedAdmission = ticket.admission.then(async (admission) => {
683
+ const response = admission.input.parts.find((part) => part.type === "tool-approval-response");
684
+ if (!response || response.approvalId !== input.approvalId || response.approved !== input.approved || response.reason !== input.reason) {
685
+ throw new Error("Approval decision conflicts with the durable decision");
686
+ }
687
+ return admission;
688
+ });
689
+ return { ...ticket, admission: checkedAdmission };
690
+ }
691
+ };
692
+ }
693
+ export {
694
+ AgentHarnessFileLimitsSchema,
695
+ AgentHarnessFileRootSchema,
696
+ AgentHarnessLimitsSchema,
697
+ AgentHarnessProfileEventSchema,
698
+ AgentHarnessResourceDiagnosticSchema,
699
+ AgentHarnessResourceKindSchema,
700
+ AgentHarnessResourceSchema,
701
+ createAgentHarnessControlServer,
702
+ createAgentHarnessFileResources,
703
+ createHeadlessAgentHarness
704
+ };