stitchkit 0.68.11 → 0.69.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 (120) hide show
  1. package/README.md +30 -1
  2. package/dist/agent-runtime/coding-tool-contract.d.ts +155 -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 +19 -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 +31 -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 +706 -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 +700 -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 +106 -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 +8 -6
  85. package/dist/{index-8vpzxg55.js → index-10gbbbaa.js} +103 -2
  86. package/dist/index-3eqrkqhg.js +2565 -0
  87. package/dist/{index-hcx9yypn.js → index-444747ww.js} +7 -7
  88. package/dist/{index-hxh98zbm.js → index-d1jsvkyk.js} +1 -1
  89. package/dist/index-es0h4w26.js +63 -0
  90. package/dist/{index-grgvpbch.js → index-gqdfpv4n.js} +4 -2
  91. package/dist/{index-bt5179zb.js → index-h7ctj1kp.js} +5 -3
  92. package/dist/{index-y2ctppmg.js → index-h8vn1jcr.js} +7 -73
  93. package/dist/{index-hqza5nde.js → index-hsabxjz0.js} +78 -77
  94. package/dist/index-kg9xx84n.js +73 -0
  95. package/dist/{index-hb0mncmj.js → index-kxxyvkka.js} +2 -2
  96. package/dist/{index-2t6zt5cg.js → index-mbxds404.js} +7 -5
  97. package/dist/{index-f24xg2cw.js → index-p1be103g.js} +1 -1
  98. package/dist/{index-xyvxez9r.js → index-q7gdep2c.js} +5 -5
  99. package/dist/{index-t23p2b68.js → index-t4dpby6e.js} +1 -1
  100. package/dist/index-tg3m2ec5.js +157 -0
  101. package/dist/index-v3dpzpjw.js +92 -0
  102. package/dist/{index-eqqyd9v8.js → index-wwst0td5.js} +2 -46
  103. package/dist/{index-ysphyxax.js → index-x1th9s8c.js} +23 -2
  104. package/dist/index-xxsq3ca1.js +51 -0
  105. package/dist/{index-22tjt2rk.js → index-zh459sfj.js} +1 -1
  106. package/dist/index.js +51 -37
  107. package/dist/internal/ai-sdk-typed.d.ts +14 -0
  108. package/dist/internal/ai-sdk-typed.d.ts.map +1 -0
  109. package/dist/node.js +5 -5
  110. package/dist/observability/index.js +4 -4
  111. package/dist/realtime/request.d.ts +5 -0
  112. package/dist/realtime/request.d.ts.map +1 -1
  113. package/dist/remote.js +6 -6
  114. package/dist/server/index.js +9 -9
  115. package/dist/testing.js +5 -5
  116. package/dist/tool-invoker.js +6 -5
  117. package/dist/tools.js +21 -17
  118. package/llms-full.txt +342 -27
  119. package/package.json +10 -2
  120. package/dist/{index-pgsyp3xh.js → index-x18ndp4p.js} +3 -3
@@ -0,0 +1,706 @@
1
+ import {
2
+ readContainedUtf8File,
3
+ scanContainedFiles
4
+ } from "./index-v3dpzpjw.js";
5
+ import {
6
+ defineRuntimeTool
7
+ } from "./index-wwst0td5.js";
8
+ import"./index-22by16v6.js";
9
+ import"./index-cby4ar3v.js";
10
+ import"./index-qyrqwr4c.js";
11
+
12
+ // src/agent-runtime/coding-tools.ts
13
+ import path3 from "node:path";
14
+ import { z as z3 } from "zod";
15
+
16
+ // src/agent-runtime/coding-tool-contract.ts
17
+ import { z } from "zod";
18
+ var AgentCodingToolLimitsSchema = z.object({
19
+ maxPathBytes: z.int().positive(),
20
+ maxReadBytes: z.int().positive(),
21
+ maxWriteBytes: z.int().positive(),
22
+ maxShellArguments: z.int().positive(),
23
+ maxShellArgumentBytes: z.int().positive(),
24
+ maxShellOutputBytes: z.int().positive(),
25
+ maxArtifactBytes: z.int().positive(),
26
+ maxSearchResults: z.int().positive(),
27
+ maxSearchFiles: z.int().positive(),
28
+ maxSearchDepth: z.int().nonnegative(),
29
+ shellTimeoutMs: z.int().positive()
30
+ }).strict();
31
+ var AgentCodingToolAuthorizationSchema = z.discriminatedUnion("operation", [
32
+ z.object({ operation: z.literal("read"), path: z.string().min(1) }).strict(),
33
+ z.object({
34
+ operation: z.literal("write"),
35
+ path: z.string().min(1),
36
+ bytes: z.int().nonnegative(),
37
+ overwrite: z.boolean()
38
+ }).strict(),
39
+ z.object({
40
+ operation: z.literal("search"),
41
+ query: z.string().min(1),
42
+ mode: z.enum(["path", "content"])
43
+ }).strict(),
44
+ z.object({
45
+ operation: z.literal("patch"),
46
+ path: z.string().min(1),
47
+ baseSha256: z.string().length(64),
48
+ resultSha256: z.string().length(64),
49
+ resultBytes: z.int().nonnegative(),
50
+ replacements: z.int().positive(),
51
+ dryRun: z.boolean()
52
+ }).strict(),
53
+ z.object({
54
+ operation: z.literal("artifact-read"),
55
+ reference: z.string().min(1),
56
+ offset: z.int().nonnegative(),
57
+ maxBytes: z.int().positive()
58
+ }).strict(),
59
+ z.object({
60
+ operation: z.literal("shell"),
61
+ executable: z.string().min(1),
62
+ args: z.array(z.string()),
63
+ cwd: z.string().min(1)
64
+ }).strict()
65
+ ]);
66
+ var FileReadInputSchema = z.object({
67
+ path: z.string().min(1),
68
+ offset: z.int().nonnegative().default(0),
69
+ maxBytes: z.int().positive().optional()
70
+ }).strict();
71
+ var FileReadOutputSchema = z.object({
72
+ path: z.string().min(1),
73
+ text: z.string(),
74
+ bytes: z.int().nonnegative(),
75
+ sha256: z.string().length(64),
76
+ truncated: z.boolean(),
77
+ nextOffset: z.int().nonnegative().optional()
78
+ }).strict();
79
+ var FileWriteInputSchema = z.object({
80
+ path: z.string().min(1),
81
+ content: z.string(),
82
+ overwrite: z.boolean().default(false)
83
+ }).strict();
84
+ var FileWriteOutputSchema = z.object({ path: z.string().min(1), bytes: z.int().nonnegative() }).strict();
85
+ function createShellInputSchema(executableNames) {
86
+ if (executableNames.length === 0) {
87
+ throw new Error("A command tool requires at least one executable alias");
88
+ }
89
+ return z.object({
90
+ executable: z.enum(executableNames),
91
+ args: z.array(z.string()).default([]),
92
+ cwd: z.string().min(1).default(".")
93
+ }).strict();
94
+ }
95
+ var ShellInputSchema = z.object({
96
+ executable: z.string().min(1),
97
+ args: z.array(z.string()).default([]),
98
+ cwd: z.string().min(1).default(".")
99
+ }).strict();
100
+ var ShellOutputSchema = z.object({
101
+ executable: z.string().min(1),
102
+ exitCode: z.int().nullable(),
103
+ signal: z.string().nullable(),
104
+ stdout: z.string(),
105
+ stderr: z.string(),
106
+ outcome: z.enum(["exited", "timeout", "output-limit", "cancelled"]),
107
+ artifact: z.object({
108
+ reference: z.string().min(1),
109
+ bytes: z.int().nonnegative(),
110
+ truncated: z.boolean()
111
+ }).strict().optional()
112
+ }).strict();
113
+ var DEFAULT_LIMITS = {
114
+ maxPathBytes: 4096,
115
+ maxReadBytes: 262144,
116
+ maxWriteBytes: 262144,
117
+ maxShellArguments: 128,
118
+ maxShellArgumentBytes: 65536,
119
+ maxShellOutputBytes: 262144,
120
+ maxArtifactBytes: 4194304,
121
+ maxSearchResults: 100,
122
+ maxSearchFiles: 1e4,
123
+ maxSearchDepth: 32,
124
+ shellTimeoutMs: 30000
125
+ };
126
+ function resolveCodingToolLimits(input) {
127
+ return AgentCodingToolLimitsSchema.parse({ ...DEFAULT_LIMITS, ...input });
128
+ }
129
+
130
+ // src/agent-runtime/coding-tool-files.ts
131
+ import { createHash } from "node:crypto";
132
+ import { constants } from "node:fs";
133
+ import { open, realpath as realpath2, stat, writeFile as writeFile2 } from "node:fs/promises";
134
+
135
+ // src/agent-runtime/coding-tool-paths.ts
136
+ import { lstat, realpath, rename, unlink, writeFile } from "node:fs/promises";
137
+ import path from "node:path";
138
+ function within(root, target) {
139
+ const relative = path.relative(root, target);
140
+ return relative === "" || !relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative);
141
+ }
142
+ function inputPath(root, requested, maxPathBytes) {
143
+ if (new TextEncoder().encode(requested).byteLength > maxPathBytes) {
144
+ throw new Error("Coding tool path exceeds maxPathBytes");
145
+ }
146
+ if (path.isAbsolute(requested))
147
+ throw new Error("Coding tool paths must be relative");
148
+ const resolved = path.resolve(root, requested);
149
+ if (!within(root, resolved))
150
+ throw new Error("Coding tool path escapes its root");
151
+ return resolved;
152
+ }
153
+ async function existingCodingPath(root, requested, maxPathBytes) {
154
+ const resolved = await realpath(inputPath(root, requested, maxPathBytes));
155
+ if (!within(root, resolved))
156
+ throw new Error("Coding tool path resolves outside its root");
157
+ return { absolute: resolved, relative: path.relative(root, resolved) || "." };
158
+ }
159
+ async function writableCodingPath(root, requested, maxPathBytes) {
160
+ const candidate = inputPath(root, requested, maxPathBytes);
161
+ const parent = await realpath(path.dirname(candidate));
162
+ if (!within(root, parent))
163
+ throw new Error("Coding tool parent resolves outside its root");
164
+ const target = path.join(parent, path.basename(candidate));
165
+ const metadata = await lstat(target).catch((error) => {
166
+ if (error.code === "ENOENT")
167
+ return null;
168
+ throw error;
169
+ });
170
+ if (metadata?.isSymbolicLink())
171
+ throw new Error("Coding tools do not write through symlinks");
172
+ return { absolute: target, relative: path.relative(root, target) || "." };
173
+ }
174
+ async function editableCodingPath(root, requested, maxPathBytes) {
175
+ const candidate = inputPath(root, requested, maxPathBytes);
176
+ const metadata = await lstat(candidate);
177
+ if (metadata.isSymbolicLink())
178
+ throw new Error("Coding tools do not edit symlinks");
179
+ const resolved = await realpath(candidate);
180
+ if (!within(root, resolved))
181
+ throw new Error("Coding tool path resolves outside its root");
182
+ return { absolute: resolved, relative: path.relative(root, resolved) || "." };
183
+ }
184
+ async function authorizeCodingTool(config, request) {
185
+ const parsed = AgentCodingToolAuthorizationSchema.parse(request);
186
+ if (!await config.authorize(parsed))
187
+ throw new Error("Coding tool permission denied");
188
+ }
189
+ async function atomicCodingReplace(target, content, mode) {
190
+ const temporary = path.join(path.dirname(target), `.${path.basename(target)}.${process.pid}.${crypto.randomUUID()}.tmp`);
191
+ try {
192
+ await writeFile(temporary, content, { flag: "wx", ...mode !== undefined && { mode } });
193
+ await rename(temporary, target);
194
+ } catch (error) {
195
+ await unlink(temporary).catch(() => {
196
+ return;
197
+ });
198
+ throw error;
199
+ }
200
+ }
201
+ var codingPathLocks = new Map;
202
+ async function withCodingPathLock(target, work) {
203
+ const previous = codingPathLocks.get(target) ?? Promise.resolve();
204
+ let release = () => {
205
+ return;
206
+ };
207
+ const current = new Promise((resolve) => {
208
+ release = resolve;
209
+ });
210
+ const queued = previous.then(() => current);
211
+ codingPathLocks.set(target, queued);
212
+ await previous;
213
+ try {
214
+ return await work();
215
+ } finally {
216
+ release();
217
+ if (codingPathLocks.get(target) === queued)
218
+ codingPathLocks.delete(target);
219
+ }
220
+ }
221
+ function textOccurrences(source, search) {
222
+ let count = 0;
223
+ let offset = 0;
224
+ while (offset <= source.length - search.length) {
225
+ const found = source.indexOf(search, offset);
226
+ if (found < 0)
227
+ break;
228
+ count += 1;
229
+ offset = found + search.length;
230
+ }
231
+ return count;
232
+ }
233
+
234
+ // src/agent-runtime/coding-tool-files.ts
235
+ async function digestFile(handle, size) {
236
+ const hash = createHash("sha256");
237
+ const chunk = Buffer.alloc(Math.min(64 * 1024, Math.max(1, size)));
238
+ for (let position = 0;position < size; ) {
239
+ const length = Math.min(chunk.byteLength, size - position);
240
+ const { bytesRead } = await handle.read(chunk, 0, length, position);
241
+ if (bytesRead === 0)
242
+ break;
243
+ hash.update(chunk.subarray(0, bytesRead));
244
+ position += bytesRead;
245
+ }
246
+ return hash.digest("hex");
247
+ }
248
+ function createFileCodingTools(config, limits) {
249
+ const read = defineRuntimeTool({
250
+ name: "read_file",
251
+ description: "Read a bounded UTF-8 slice from a host-authorized workspace file.",
252
+ identity: { serviceName: "coding", action: "read-file", method: "POST" },
253
+ input: FileReadInputSchema,
254
+ output: FileReadOutputSchema,
255
+ transports: ["AGENT"],
256
+ handler: async ({ input }) => {
257
+ const root = await realpath2(config.root);
258
+ const target = await existingCodingPath(root, input.path, limits.maxPathBytes);
259
+ await authorizeCodingTool(config, { operation: "read", path: target.relative });
260
+ const maximum = Math.min(input.maxBytes ?? limits.maxReadBytes, limits.maxReadBytes);
261
+ const handle = await open(target.absolute, constants.O_RDONLY | constants.O_NOFOLLOW);
262
+ let selected;
263
+ let size;
264
+ let sha256;
265
+ try {
266
+ const metadata = await handle.stat();
267
+ if (!metadata.isFile())
268
+ throw new Error("Coding read path is not a regular file");
269
+ size = metadata.size;
270
+ const length = Math.min(maximum, Math.max(0, size - input.offset));
271
+ selected = Buffer.alloc(length);
272
+ const { bytesRead } = await handle.read(selected, 0, length, input.offset);
273
+ selected = selected.subarray(0, bytesRead);
274
+ sha256 = await digestFile(handle, size);
275
+ } finally {
276
+ await handle.close();
277
+ }
278
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(selected);
279
+ const end = input.offset + selected.byteLength;
280
+ const truncated = end < size;
281
+ return {
282
+ path: target.relative,
283
+ text,
284
+ bytes: selected.byteLength,
285
+ sha256,
286
+ truncated,
287
+ ...truncated && { nextOffset: end }
288
+ };
289
+ }
290
+ });
291
+ const write = defineRuntimeTool({
292
+ name: "write_file",
293
+ description: "Write bounded UTF-8 content to a host-authorized workspace file.",
294
+ identity: { serviceName: "coding", action: "write-file", method: "POST" },
295
+ input: FileWriteInputSchema,
296
+ output: FileWriteOutputSchema,
297
+ transports: ["AGENT"],
298
+ handler: async ({ input }) => {
299
+ const root = await realpath2(config.root);
300
+ const target = await writableCodingPath(root, input.path, limits.maxPathBytes);
301
+ const bytes = Buffer.byteLength(input.content);
302
+ if (bytes > limits.maxWriteBytes)
303
+ throw new Error("Coding tool write exceeds maxWriteBytes");
304
+ await authorizeCodingTool(config, {
305
+ operation: "write",
306
+ path: target.relative,
307
+ bytes,
308
+ overwrite: input.overwrite
309
+ });
310
+ if (input.overwrite) {
311
+ const current = await stat(target.absolute).catch(() => null);
312
+ await atomicCodingReplace(target.absolute, input.content, current?.mode);
313
+ } else {
314
+ await writeFile2(target.absolute, input.content, { flag: "wx" });
315
+ }
316
+ return { path: target.relative, bytes };
317
+ }
318
+ });
319
+ return [read, write];
320
+ }
321
+
322
+ // src/agent-runtime/coding-tool-search-patch.ts
323
+ import { createHash as createHash2 } from "node:crypto";
324
+ import { realpath as realpath3 } from "node:fs/promises";
325
+ import path2 from "node:path";
326
+ import { z as z2 } from "zod";
327
+ var DEFAULT_EXCLUDED_DIRECTORIES = [
328
+ ".git",
329
+ ".next",
330
+ ".stitchkit",
331
+ "build",
332
+ "coverage",
333
+ "dist",
334
+ "node_modules"
335
+ ];
336
+ var SearchInputSchema = z2.object({ query: z2.string().min(1), mode: z2.enum(["path", "content"]).default("content") }).strict();
337
+ var SearchOutputSchema = z2.object({
338
+ matches: z2.array(z2.object({ path: z2.string().min(1), line: z2.int().positive().optional() }).strict()),
339
+ truncated: z2.boolean(),
340
+ scannedFiles: z2.int().nonnegative(),
341
+ skippedDirectories: z2.int().nonnegative(),
342
+ skippedSymlinks: z2.int().nonnegative()
343
+ }).strict();
344
+ var PatchInputSchema = z2.object({
345
+ path: z2.string().min(1),
346
+ baseSha256: z2.string().length(64),
347
+ oldText: z2.string().min(1),
348
+ newText: z2.string(),
349
+ replaceAll: z2.boolean().default(false),
350
+ dryRun: z2.boolean().default(true)
351
+ }).strict();
352
+ var PatchOutputSchema = z2.object({
353
+ path: z2.string().min(1),
354
+ baseSha256: z2.string().length(64),
355
+ resultSha256: z2.string().length(64),
356
+ replacements: z2.int().positive(),
357
+ bytes: z2.int().nonnegative(),
358
+ applied: z2.boolean()
359
+ }).strict();
360
+ function sha256(value) {
361
+ return createHash2("sha256").update(value).digest("hex");
362
+ }
363
+ function createSearchAndPatchCodingTools(config, limits) {
364
+ const search = defineRuntimeTool({
365
+ name: "search_files",
366
+ description: "Search bounded relative paths or UTF-8 file content under the workspace root.",
367
+ identity: { serviceName: "coding", action: "search", method: "POST" },
368
+ input: SearchInputSchema,
369
+ output: SearchOutputSchema,
370
+ transports: ["AGENT"],
371
+ handler: async ({ input }) => {
372
+ await authorizeCodingTool(config, {
373
+ operation: "search",
374
+ query: input.query,
375
+ mode: input.mode
376
+ });
377
+ const root = await realpath3(config.root);
378
+ const excluded = new Set(config.search?.excludeDirectories ?? DEFAULT_EXCLUDED_DIRECTORIES);
379
+ const scan = await scanContainedFiles({
380
+ root,
381
+ maxDepth: limits.maxSearchDepth,
382
+ maxFiles: limits.maxSearchFiles,
383
+ symlinks: "skip",
384
+ excludeDirectory: (relative) => relative.split(path2.sep).some((segment) => excluded.has(segment))
385
+ });
386
+ const matches = [];
387
+ let truncated = false;
388
+ for (const file of scan.files) {
389
+ if (input.mode === "path") {
390
+ if (file.relative.includes(input.query))
391
+ matches.push({ path: file.relative });
392
+ } else {
393
+ let text;
394
+ try {
395
+ text = (await readContainedUtf8File(file.absolute, limits.maxReadBytes)).text;
396
+ } catch {
397
+ continue;
398
+ }
399
+ const lines = text.split(`
400
+ `);
401
+ for (let index = 0;index < lines.length; index += 1) {
402
+ if (lines[index]?.includes(input.query)) {
403
+ matches.push({ path: file.relative, line: index + 1 });
404
+ if (matches.length >= limits.maxSearchResults)
405
+ break;
406
+ }
407
+ }
408
+ }
409
+ if (matches.length >= limits.maxSearchResults) {
410
+ truncated = true;
411
+ break;
412
+ }
413
+ }
414
+ return {
415
+ matches,
416
+ truncated: truncated || scan.truncated,
417
+ scannedFiles: scan.files.length,
418
+ skippedDirectories: scan.skippedDirectories,
419
+ skippedSymlinks: scan.skippedSymlinks
420
+ };
421
+ }
422
+ });
423
+ const patch = defineRuntimeTool({
424
+ name: "apply_patch",
425
+ description: "Dry-run or atomically apply one exact guarded UTF-8 file patch.",
426
+ identity: { serviceName: "coding", action: "patch-file", method: "POST" },
427
+ input: PatchInputSchema,
428
+ output: PatchOutputSchema,
429
+ transports: ["AGENT"],
430
+ handler: async ({ input }) => {
431
+ const root = await realpath3(config.root);
432
+ const target = await editableCodingPath(root, input.path, limits.maxPathBytes);
433
+ const read = await readContainedUtf8File(target.absolute, limits.maxWriteBytes);
434
+ const source = read.text;
435
+ const baseSha256 = sha256(source);
436
+ if (baseSha256 !== input.baseSha256)
437
+ throw new Error("Coding patch base digest is stale");
438
+ const count = textOccurrences(source, input.oldText);
439
+ if (count === 0)
440
+ throw new Error("Coding patch text was not found");
441
+ if (!input.replaceAll && count !== 1)
442
+ throw new Error("Coding patch text is not unique");
443
+ const replacements = input.replaceAll ? count : 1;
444
+ const changed = input.replaceAll ? source.replaceAll(input.oldText, input.newText) : source.replace(input.oldText, input.newText);
445
+ const bytes = Buffer.byteLength(changed);
446
+ const resultSha256 = sha256(changed);
447
+ if (bytes > limits.maxWriteBytes)
448
+ throw new Error("Coding patch exceeds maxWriteBytes");
449
+ await authorizeCodingTool(config, {
450
+ operation: "patch",
451
+ path: target.relative,
452
+ baseSha256,
453
+ resultSha256,
454
+ resultBytes: bytes,
455
+ replacements,
456
+ dryRun: input.dryRun
457
+ });
458
+ if (!input.dryRun) {
459
+ await withCodingPathLock(target.absolute, async () => {
460
+ const current = await readContainedUtf8File(target.absolute, limits.maxWriteBytes);
461
+ if (sha256(current.text) !== baseSha256)
462
+ throw new Error("Coding patch base digest became stale before apply");
463
+ await atomicCodingReplace(target.absolute, changed, current.mode);
464
+ });
465
+ }
466
+ return {
467
+ path: target.relative,
468
+ baseSha256,
469
+ resultSha256,
470
+ replacements,
471
+ bytes,
472
+ applied: !input.dryRun
473
+ };
474
+ }
475
+ });
476
+ return [search, patch];
477
+ }
478
+
479
+ // src/agent-runtime/coding-tool-shell.ts
480
+ import { spawn } from "node:child_process";
481
+ import { realpath as realpath4, stat as stat2 } from "node:fs/promises";
482
+ async function runShell(input) {
483
+ const stdoutHeader = Buffer.from(`--- stdout ---
484
+ `);
485
+ const stderrHeader = Buffer.from(`
486
+ --- stderr ---
487
+ `);
488
+ const artifactPayloadLimit = input.maxArtifactBytes - stdoutHeader.byteLength - stderrHeader.byteLength;
489
+ if (input.artifacts && artifactPayloadLimit < 0)
490
+ throw new Error("maxArtifactBytes is smaller than the shell artifact envelope");
491
+ return await new Promise((resolve, reject) => {
492
+ const child = spawn(input.executable, input.args, {
493
+ cwd: input.cwd,
494
+ env: input.environment,
495
+ stdio: ["ignore", "pipe", "pipe"]
496
+ });
497
+ const stdout = [];
498
+ const stderr = [];
499
+ const artifactStdout = [];
500
+ const artifactStderr = [];
501
+ let retained = 0;
502
+ let artifactBytes = 0;
503
+ let outcome = "exited";
504
+ let settled = false;
505
+ const terminate = (reason) => {
506
+ if (settled || outcome !== "exited")
507
+ return;
508
+ outcome = reason;
509
+ child.kill("SIGKILL");
510
+ };
511
+ const timer = setTimeout(() => terminate("timeout"), input.timeoutMs);
512
+ timer.unref();
513
+ const cancel = () => terminate("cancelled");
514
+ const retain = (target, artifactTarget, chunk) => {
515
+ const remaining = Math.max(0, input.maxOutputBytes - retained);
516
+ if (remaining > 0) {
517
+ const kept = chunk.subarray(0, remaining);
518
+ target.push(kept);
519
+ retained += kept.byteLength;
520
+ }
521
+ if (input.artifacts) {
522
+ const artifactRemaining = Math.max(0, artifactPayloadLimit - artifactBytes);
523
+ if (artifactRemaining > 0) {
524
+ const kept = chunk.subarray(0, artifactRemaining);
525
+ artifactTarget.push(kept);
526
+ artifactBytes += kept.byteLength;
527
+ }
528
+ if (chunk.byteLength > artifactRemaining)
529
+ terminate("output-limit");
530
+ } else if (chunk.byteLength > remaining)
531
+ terminate("output-limit");
532
+ };
533
+ child.stdout.on("data", (chunk) => retain(stdout, artifactStdout, chunk));
534
+ child.stderr.on("data", (chunk) => retain(stderr, artifactStderr, chunk));
535
+ child.on("error", (error) => {
536
+ if (settled)
537
+ return;
538
+ settled = true;
539
+ clearTimeout(timer);
540
+ input.signal?.removeEventListener("abort", cancel);
541
+ reject(error);
542
+ });
543
+ child.on("close", async (exitCode, signal) => {
544
+ if (settled)
545
+ return;
546
+ settled = true;
547
+ clearTimeout(timer);
548
+ input.signal?.removeEventListener("abort", cancel);
549
+ try {
550
+ const hasArtifact = input.artifacts && artifactBytes > input.maxOutputBytes;
551
+ const artifactData = hasArtifact ? Buffer.concat([stdoutHeader, ...artifactStdout, stderrHeader, ...artifactStderr]) : undefined;
552
+ const persisted = hasArtifact ? await input.artifacts?.write({
553
+ mediaType: "text/plain; charset=utf-8",
554
+ data: artifactData ?? new Uint8Array
555
+ }) : undefined;
556
+ resolve(ShellOutputSchema.parse({
557
+ executable: input.executableName,
558
+ exitCode,
559
+ signal,
560
+ stdout: Buffer.concat(stdout).toString("utf8"),
561
+ stderr: Buffer.concat(stderr).toString("utf8"),
562
+ outcome,
563
+ ...persisted && {
564
+ artifact: {
565
+ reference: persisted.reference,
566
+ bytes: artifactData?.byteLength ?? 0,
567
+ truncated: outcome === "output-limit"
568
+ }
569
+ }
570
+ }));
571
+ } catch (error) {
572
+ reject(error);
573
+ }
574
+ });
575
+ input.signal?.addEventListener("abort", cancel, { once: true });
576
+ if (input.signal?.aborted)
577
+ cancel();
578
+ });
579
+ }
580
+ function createShellCodingTool(config, limits) {
581
+ const executables = config.executables ?? {};
582
+ const executableNames = Object.keys(executables).sort();
583
+ return defineRuntimeTool({
584
+ name: "run_command",
585
+ description: "Run a finite host-declared executable with bounded arguments, time and output.",
586
+ identity: { serviceName: "coding", action: "shell", method: "POST" },
587
+ input: createShellInputSchema(executableNames),
588
+ output: ShellOutputSchema,
589
+ transports: ["AGENT"],
590
+ handler: async ({ input, signal }) => {
591
+ if (input.args.length > limits.maxShellArguments) {
592
+ throw new Error("Coding tool shell arguments exceed maxShellArguments");
593
+ }
594
+ const argumentBytes = input.args.reduce((total, argument) => total + Buffer.byteLength(argument), 0);
595
+ if (argumentBytes > limits.maxShellArgumentBytes) {
596
+ throw new Error("Coding tool shell arguments exceed maxShellArgumentBytes");
597
+ }
598
+ const executable = executables[input.executable];
599
+ if (!executable)
600
+ throw new Error("Coding tool executable is not declared");
601
+ const root = await realpath4(config.root);
602
+ const cwd = await existingCodingPath(root, input.cwd, limits.maxPathBytes);
603
+ if (!(await stat2(cwd.absolute)).isDirectory()) {
604
+ throw new Error("Coding tool cwd is not a directory");
605
+ }
606
+ await authorizeCodingTool(config, {
607
+ operation: "shell",
608
+ executable: input.executable,
609
+ args: input.args,
610
+ cwd: cwd.relative
611
+ });
612
+ return runShell({
613
+ executableName: input.executable,
614
+ executable,
615
+ args: input.args,
616
+ cwd: cwd.absolute,
617
+ environment: config.environment ?? {},
618
+ ...signal && { signal },
619
+ timeoutMs: limits.shellTimeoutMs,
620
+ maxOutputBytes: limits.maxShellOutputBytes,
621
+ maxArtifactBytes: limits.maxArtifactBytes,
622
+ ...config.artifacts && { artifacts: config.artifacts }
623
+ });
624
+ }
625
+ });
626
+ }
627
+
628
+ // src/agent-runtime/coding-tools.ts
629
+ var AGENT_CODING_TOOL_NAMES = {
630
+ applyPatch: "apply_patch",
631
+ readFile: "read_file",
632
+ readOutput: "read_output",
633
+ runCommand: "run_command",
634
+ searchFiles: "search_files",
635
+ writeFile: "write_file"
636
+ };
637
+ function createAgentCodingTools(config) {
638
+ if (!path3.isAbsolute(config.root))
639
+ throw new Error("Coding tool root must be absolute");
640
+ for (const executable of Object.values(config.executables ?? {})) {
641
+ if (!path3.isAbsolute(executable)) {
642
+ throw new Error("Coding tool executables must be absolute paths");
643
+ }
644
+ }
645
+ const limits = resolveCodingToolLimits(config.limits);
646
+ const artifactTools = [];
647
+ if (config.artifacts) {
648
+ const artifacts = config.artifacts;
649
+ artifactTools.push(defineRuntimeTool({
650
+ name: AGENT_CODING_TOOL_NAMES.readOutput,
651
+ description: "Read a bounded byte slice from one opaque coding output artifact.",
652
+ identity: { serviceName: "coding", action: "read-artifact", method: "POST" },
653
+ input: z3.object({
654
+ reference: z3.string().min(1),
655
+ offset: z3.int().nonnegative().default(0),
656
+ maxBytes: z3.int().positive().optional()
657
+ }).strict(),
658
+ output: z3.object({
659
+ reference: z3.string().min(1),
660
+ offset: z3.int().nonnegative(),
661
+ text: z3.string(),
662
+ bytes: z3.int().nonnegative(),
663
+ totalBytes: z3.int().nonnegative().optional()
664
+ }).strict(),
665
+ transports: ["AGENT"],
666
+ handler: async ({ input }) => {
667
+ const maxBytes = Math.min(input.maxBytes ?? limits.maxReadBytes, limits.maxReadBytes);
668
+ await authorizeCodingTool(config, {
669
+ operation: "artifact-read",
670
+ reference: input.reference,
671
+ offset: input.offset,
672
+ maxBytes
673
+ });
674
+ const result = await artifacts.read({
675
+ reference: input.reference,
676
+ offset: input.offset,
677
+ maxBytes
678
+ });
679
+ if (result.data.byteLength > maxBytes)
680
+ throw new Error("Coding artifact store exceeded the requested byte budget");
681
+ if (result.totalBytes !== undefined && result.totalBytes < input.offset + result.data.byteLength) {
682
+ throw new Error("Coding artifact store returned inconsistent totalBytes");
683
+ }
684
+ return {
685
+ reference: input.reference,
686
+ offset: input.offset,
687
+ text: new TextDecoder("utf-8", { fatal: true }).decode(result.data),
688
+ bytes: result.data.byteLength,
689
+ ...result.totalBytes !== undefined && { totalBytes: result.totalBytes }
690
+ };
691
+ }
692
+ }));
693
+ }
694
+ const commandTools = Object.keys(config.executables ?? {}).length ? [createShellCodingTool(config, limits)] : [];
695
+ return [
696
+ ...createFileCodingTools(config, limits),
697
+ ...createSearchAndPatchCodingTools(config, limits),
698
+ ...commandTools,
699
+ ...artifactTools
700
+ ];
701
+ }
702
+ export {
703
+ AgentCodingToolAuthorizationSchema,
704
+ AgentCodingToolLimitsSchema,
705
+ createAgentCodingTools
706
+ };