@code-yeongyu/senpi-agent-core 2026.7.25-2

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 (142) hide show
  1. package/README.md +508 -0
  2. package/dist/agent-loop.d.ts +31 -0
  3. package/dist/agent-loop.d.ts.map +1 -0
  4. package/dist/agent-loop.js +851 -0
  5. package/dist/agent-loop.js.map +1 -0
  6. package/dist/agent.d.ts +140 -0
  7. package/dist/agent.d.ts.map +1 -0
  8. package/dist/agent.js +464 -0
  9. package/dist/agent.js.map +1 -0
  10. package/dist/harness/agent-harness.d.ts +94 -0
  11. package/dist/harness/agent-harness.d.ts.map +1 -0
  12. package/dist/harness/agent-harness.js +988 -0
  13. package/dist/harness/agent-harness.js.map +1 -0
  14. package/dist/harness/compaction/branch-summarization.d.ts +55 -0
  15. package/dist/harness/compaction/branch-summarization.d.ts.map +1 -0
  16. package/dist/harness/compaction/branch-summarization.js +172 -0
  17. package/dist/harness/compaction/branch-summarization.js.map +1 -0
  18. package/dist/harness/compaction/compaction.d.ts +107 -0
  19. package/dist/harness/compaction/compaction.d.ts.map +1 -0
  20. package/dist/harness/compaction/compaction.js +588 -0
  21. package/dist/harness/compaction/compaction.js.map +1 -0
  22. package/dist/harness/compaction/utils.d.ts +36 -0
  23. package/dist/harness/compaction/utils.d.ts.map +1 -0
  24. package/dist/harness/compaction/utils.js +136 -0
  25. package/dist/harness/compaction/utils.js.map +1 -0
  26. package/dist/harness/env/nodejs.d.ts +45 -0
  27. package/dist/harness/env/nodejs.d.ts.map +1 -0
  28. package/dist/harness/env/nodejs.js +620 -0
  29. package/dist/harness/env/nodejs.js.map +1 -0
  30. package/dist/harness/messages.d.ts +52 -0
  31. package/dist/harness/messages.d.ts.map +1 -0
  32. package/dist/harness/messages.js +102 -0
  33. package/dist/harness/messages.js.map +1 -0
  34. package/dist/harness/prompt-templates.d.ts +48 -0
  35. package/dist/harness/prompt-templates.d.ts.map +1 -0
  36. package/dist/harness/prompt-templates.js +230 -0
  37. package/dist/harness/prompt-templates.js.map +1 -0
  38. package/dist/harness/session/jsonl-repo.d.ts +26 -0
  39. package/dist/harness/session/jsonl-repo.d.ts.map +1 -0
  40. package/dist/harness/session/jsonl-repo.js +100 -0
  41. package/dist/harness/session/jsonl-repo.js.map +1 -0
  42. package/dist/harness/session/jsonl-storage.d.ts +42 -0
  43. package/dist/harness/session/jsonl-storage.d.ts.map +1 -0
  44. package/dist/harness/session/jsonl-storage.js +286 -0
  45. package/dist/harness/session/jsonl-storage.js.map +1 -0
  46. package/dist/harness/session/memory-repo.d.ts +18 -0
  47. package/dist/harness/session/memory-repo.d.ts.map +1 -0
  48. package/dist/harness/session/memory-repo.js +44 -0
  49. package/dist/harness/session/memory-repo.js.map +1 -0
  50. package/dist/harness/session/memory-storage.d.ts +33 -0
  51. package/dist/harness/session/memory-storage.d.ts.map +1 -0
  52. package/dist/harness/session/memory-storage.js +163 -0
  53. package/dist/harness/session/memory-storage.js.map +1 -0
  54. package/dist/harness/session/repo-utils.d.ts +11 -0
  55. package/dist/harness/session/repo-utils.d.ts.map +1 -0
  56. package/dist/harness/session/repo-utils.js +39 -0
  57. package/dist/harness/session/repo-utils.js.map +1 -0
  58. package/dist/harness/session/session.d.ts +49 -0
  59. package/dist/harness/session/session.d.ts.map +1 -0
  60. package/dist/harness/session/session.js +261 -0
  61. package/dist/harness/session/session.js.map +1 -0
  62. package/dist/harness/skills.d.ts +44 -0
  63. package/dist/harness/skills.d.ts.map +1 -0
  64. package/dist/harness/skills.js +311 -0
  65. package/dist/harness/skills.js.map +1 -0
  66. package/dist/harness/system-prompt.d.ts +3 -0
  67. package/dist/harness/system-prompt.d.ts.map +1 -0
  68. package/dist/harness/system-prompt.js +30 -0
  69. package/dist/harness/system-prompt.js.map +1 -0
  70. package/dist/harness/tools/bash.d.ts +27 -0
  71. package/dist/harness/tools/bash.d.ts.map +1 -0
  72. package/dist/harness/tools/bash.js +132 -0
  73. package/dist/harness/tools/bash.js.map +1 -0
  74. package/dist/harness/tools/edit-diff.d.ts +89 -0
  75. package/dist/harness/tools/edit-diff.d.ts.map +1 -0
  76. package/dist/harness/tools/edit-diff.js +386 -0
  77. package/dist/harness/tools/edit-diff.js.map +1 -0
  78. package/dist/harness/tools/edit.d.ts +19 -0
  79. package/dist/harness/tools/edit.d.ts.map +1 -0
  80. package/dist/harness/tools/edit.js +95 -0
  81. package/dist/harness/tools/edit.js.map +1 -0
  82. package/dist/harness/tools/file-mutation-queue.d.ts +4 -0
  83. package/dist/harness/tools/file-mutation-queue.d.ts.map +1 -0
  84. package/dist/harness/tools/file-mutation-queue.js +46 -0
  85. package/dist/harness/tools/file-mutation-queue.js.map +1 -0
  86. package/dist/harness/tools/image.d.ts +3 -0
  87. package/dist/harness/tools/image.d.ts.map +1 -0
  88. package/dist/harness/tools/image.js +106 -0
  89. package/dist/harness/tools/image.js.map +1 -0
  90. package/dist/harness/tools/index.d.ts +6 -0
  91. package/dist/harness/tools/index.d.ts.map +1 -0
  92. package/dist/harness/tools/index.js +5 -0
  93. package/dist/harness/tools/index.js.map +1 -0
  94. package/dist/harness/tools/path-utils.d.ts +4 -0
  95. package/dist/harness/tools/path-utils.d.ts.map +1 -0
  96. package/dist/harness/tools/path-utils.js +26 -0
  97. package/dist/harness/tools/path-utils.js.map +1 -0
  98. package/dist/harness/tools/read.d.ts +34 -0
  99. package/dist/harness/tools/read.d.ts.map +1 -0
  100. package/dist/harness/tools/read.js +110 -0
  101. package/dist/harness/tools/read.js.map +1 -0
  102. package/dist/harness/tools/tool-context.d.ts +6 -0
  103. package/dist/harness/tools/tool-context.d.ts.map +1 -0
  104. package/dist/harness/tools/tool-context.js +2 -0
  105. package/dist/harness/tools/tool-context.js.map +1 -0
  106. package/dist/harness/tools/write.d.ts +11 -0
  107. package/dist/harness/tools/write.d.ts.map +1 -0
  108. package/dist/harness/tools/write.js +31 -0
  109. package/dist/harness/tools/write.js.map +1 -0
  110. package/dist/harness/types.d.ts +682 -0
  111. package/dist/harness/types.d.ts.map +1 -0
  112. package/dist/harness/types.js +83 -0
  113. package/dist/harness/types.js.map +1 -0
  114. package/dist/harness/utils/shell-output.d.ts +22 -0
  115. package/dist/harness/utils/shell-output.d.ts.map +1 -0
  116. package/dist/harness/utils/shell-output.js +181 -0
  117. package/dist/harness/utils/shell-output.js.map +1 -0
  118. package/dist/harness/utils/truncate.d.ts +70 -0
  119. package/dist/harness/utils/truncate.d.ts.map +1 -0
  120. package/dist/harness/utils/truncate.js +296 -0
  121. package/dist/harness/utils/truncate.js.map +1 -0
  122. package/dist/index.d.ts +24 -0
  123. package/dist/index.d.ts.map +1 -0
  124. package/dist/index.js +30 -0
  125. package/dist/index.js.map +1 -0
  126. package/dist/node.d.ts +3 -0
  127. package/dist/node.d.ts.map +1 -0
  128. package/dist/node.js +3 -0
  129. package/dist/node.js.map +1 -0
  130. package/dist/proxy.d.ts +71 -0
  131. package/dist/proxy.d.ts.map +1 -0
  132. package/dist/proxy.js +280 -0
  133. package/dist/proxy.js.map +1 -0
  134. package/dist/stream-fn.d.ts +10 -0
  135. package/dist/stream-fn.d.ts.map +1 -0
  136. package/dist/stream-fn.js +17 -0
  137. package/dist/stream-fn.js.map +1 -0
  138. package/dist/types.d.ts +415 -0
  139. package/dist/types.d.ts.map +1 -0
  140. package/dist/types.js +2 -0
  141. package/dist/types.js.map +1 -0
  142. package/package.json +61 -0
@@ -0,0 +1,95 @@
1
+ import { Type } from "typebox";
2
+ import { applyEditsToNormalizedContent, detectLineEnding, generateDiffString, generateUnifiedPatch, normalizeToLF, restoreLineEndings, stripBom, } from "./edit-diff.js";
3
+ import { withFileMutationQueue } from "./file-mutation-queue.js";
4
+ import { resolveToolPath } from "./path-utils.js";
5
+ const replaceEditSchema = Type.Object({
6
+ oldText: Type.String({
7
+ description: "Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call.",
8
+ }),
9
+ newText: Type.String({ description: "Replacement text for this targeted edit." }),
10
+ }, {});
11
+ const editSchema = Type.Object({
12
+ path: Type.String({ description: "Path to the file to edit (relative or absolute)" }),
13
+ edits: Type.Array(replaceEditSchema, {
14
+ description: "One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.",
15
+ }),
16
+ }, {});
17
+ function prepareEditArguments(input) {
18
+ if (!input || typeof input !== "object")
19
+ return input;
20
+ const args = input;
21
+ if (typeof args.edits === "string") {
22
+ try {
23
+ const parsed = JSON.parse(args.edits);
24
+ if (Array.isArray(parsed))
25
+ args.edits = parsed;
26
+ }
27
+ catch { }
28
+ }
29
+ const legacy = args;
30
+ if (typeof legacy.oldText !== "string" || typeof legacy.newText !== "string")
31
+ return args;
32
+ const edits = Array.isArray(legacy.edits) ? [...legacy.edits] : [];
33
+ edits.push({ oldText: legacy.oldText, newText: legacy.newText });
34
+ const { oldText: _oldText, newText: _newText, ...rest } = legacy;
35
+ return { ...rest, edits };
36
+ }
37
+ function validateEditInput(input) {
38
+ if (!Array.isArray(input.edits) || input.edits.length === 0) {
39
+ throw new Error("Edit tool input is invalid. edits must contain at least one replacement.");
40
+ }
41
+ return { path: input.path, edits: input.edits };
42
+ }
43
+ function editAccessError(path, error) {
44
+ return new Error(`Could not edit file: ${path}. Error code: ${error.code}.`, { cause: error });
45
+ }
46
+ export function createEditTool() {
47
+ return {
48
+ name: "edit",
49
+ label: "edit",
50
+ description: "Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.",
51
+ parameters: editSchema,
52
+ prepareArguments: prepareEditArguments,
53
+ async execute(_toolCallId, input, signal, _onUpdate, { env }) {
54
+ const { path, edits } = validateEditInput(input);
55
+ const absolutePath = await resolveToolPath(env, path, signal);
56
+ return withFileMutationQueue(env, absolutePath, async () => {
57
+ if (signal?.aborted)
58
+ throw new Error("Operation aborted");
59
+ const info = await env.fileInfo(absolutePath, signal);
60
+ if (!info.ok)
61
+ throw editAccessError(path, info.error);
62
+ if (info.value.kind !== "file" && info.value.kind !== "symlink") {
63
+ throw new Error(`Could not edit file: ${path}. Path is not a file.`);
64
+ }
65
+ const readResult = await env.readTextFile(absolutePath, signal);
66
+ if (!readResult.ok)
67
+ throw editAccessError(path, readResult.error);
68
+ if (signal?.aborted)
69
+ throw new Error("Operation aborted");
70
+ const { bom, text: content } = stripBom(readResult.value);
71
+ const originalEnding = detectLineEnding(content);
72
+ const normalizedContent = normalizeToLF(content);
73
+ const { baseContent, newContent } = applyEditsToNormalizedContent(normalizedContent, edits, path);
74
+ if (signal?.aborted)
75
+ throw new Error("Operation aborted");
76
+ const finalContent = bom + restoreLineEndings(newContent, originalEnding);
77
+ const writeResult = await env.writeFile(absolutePath, finalContent, signal);
78
+ if (!writeResult.ok)
79
+ throw editAccessError(path, writeResult.error);
80
+ if (signal?.aborted)
81
+ throw new Error("Operation aborted");
82
+ const diffResult = generateDiffString(baseContent, newContent);
83
+ return {
84
+ content: [{ type: "text", text: `Successfully replaced ${edits.length} block(s) in ${path}.` }],
85
+ details: {
86
+ diff: diffResult.diff,
87
+ patch: generateUnifiedPatch(path, baseContent, newContent),
88
+ firstChangedLine: diffResult.firstChangedLine,
89
+ },
90
+ };
91
+ });
92
+ },
93
+ };
94
+ }
95
+ //# sourceMappingURL=edit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/harness/tools/edit.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Static, Type } from \"typebox\";\nimport type { AgentHarnessTool, FileError } from \"../types.ts\";\nimport {\n\tapplyEditsToNormalizedContent,\n\tdetectLineEnding,\n\ttype Edit,\n\tgenerateDiffString,\n\tgenerateUnifiedPatch,\n\tnormalizeToLF,\n\trestoreLineEndings,\n\tstripBom,\n} from \"./edit-diff.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport { resolveToolPath } from \"./path-utils.ts\";\nimport type { ExecutionToolContext } from \"./tool-context.ts\";\n\nconst replaceEditSchema = Type.Object(\n\t{\n\t\toldText: Type.String({\n\t\t\tdescription:\n\t\t\t\t\"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call.\",\n\t\t}),\n\t\tnewText: Type.String({ description: \"Replacement text for this targeted edit.\" }),\n\t},\n\t{},\n);\n\nconst editSchema = Type.Object(\n\t{\n\t\tpath: Type.String({ description: \"Path to the file to edit (relative or absolute)\" }),\n\t\tedits: Type.Array(replaceEditSchema, {\n\t\t\tdescription:\n\t\t\t\t\"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.\",\n\t\t}),\n\t},\n\t{},\n);\n\nexport type EditToolInput = Static<typeof editSchema>;\ntype LegacyEditToolInput = EditToolInput & { oldText?: unknown; newText?: unknown };\n\nexport interface EditToolDetails {\n\tdiff: string;\n\tpatch: string;\n\tfirstChangedLine?: number;\n}\n\nfunction prepareEditArguments(input: unknown): EditToolInput {\n\tif (!input || typeof input !== \"object\") return input as EditToolInput;\n\tconst args = input as Record<string, unknown>;\n\tif (typeof args.edits === \"string\") {\n\t\ttry {\n\t\t\tconst parsed: unknown = JSON.parse(args.edits);\n\t\t\tif (Array.isArray(parsed)) args.edits = parsed;\n\t\t} catch {}\n\t}\n\n\tconst legacy = args as LegacyEditToolInput;\n\tif (typeof legacy.oldText !== \"string\" || typeof legacy.newText !== \"string\") return args as EditToolInput;\n\tconst edits = Array.isArray(legacy.edits) ? [...legacy.edits] : [];\n\tedits.push({ oldText: legacy.oldText, newText: legacy.newText });\n\tconst { oldText: _oldText, newText: _newText, ...rest } = legacy;\n\treturn { ...rest, edits } as EditToolInput;\n}\n\nfunction validateEditInput(input: EditToolInput): { path: string; edits: Edit[] } {\n\tif (!Array.isArray(input.edits) || input.edits.length === 0) {\n\t\tthrow new Error(\"Edit tool input is invalid. edits must contain at least one replacement.\");\n\t}\n\treturn { path: input.path, edits: input.edits };\n}\n\nfunction editAccessError(path: string, error: FileError): Error {\n\treturn new Error(`Could not edit file: ${path}. Error code: ${error.code}.`, { cause: error });\n}\n\nexport function createEditTool<TContext extends ExecutionToolContext = ExecutionToolContext>(): AgentHarnessTool<\n\tTContext,\n\ttypeof editSchema,\n\tEditToolDetails | undefined\n> {\n\treturn {\n\t\tname: \"edit\",\n\t\tlabel: \"edit\",\n\t\tdescription:\n\t\t\t\"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.\",\n\t\tparameters: editSchema,\n\t\tprepareArguments: prepareEditArguments,\n\t\tasync execute(_toolCallId, input, signal, _onUpdate, { env }) {\n\t\t\tconst { path, edits } = validateEditInput(input);\n\t\t\tconst absolutePath = await resolveToolPath(env, path, signal);\n\t\t\treturn withFileMutationQueue(env, absolutePath, async () => {\n\t\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\t\tconst info = await env.fileInfo(absolutePath, signal);\n\t\t\t\tif (!info.ok) throw editAccessError(path, info.error);\n\t\t\t\tif (info.value.kind !== \"file\" && info.value.kind !== \"symlink\") {\n\t\t\t\t\tthrow new Error(`Could not edit file: ${path}. Path is not a file.`);\n\t\t\t\t}\n\n\t\t\t\tconst readResult = await env.readTextFile(absolutePath, signal);\n\t\t\t\tif (!readResult.ok) throw editAccessError(path, readResult.error);\n\t\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\n\t\t\t\tconst { bom, text: content } = stripBom(readResult.value);\n\t\t\t\tconst originalEnding = detectLineEnding(content);\n\t\t\t\tconst normalizedContent = normalizeToLF(content);\n\t\t\t\tconst { baseContent, newContent } = applyEditsToNormalizedContent(normalizedContent, edits, path);\n\t\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\n\t\t\t\tconst finalContent = bom + restoreLineEndings(newContent, originalEnding);\n\t\t\t\tconst writeResult = await env.writeFile(absolutePath, finalContent, signal);\n\t\t\t\tif (!writeResult.ok) throw editAccessError(path, writeResult.error);\n\t\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\n\t\t\t\tconst diffResult = generateDiffString(baseContent, newContent);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Successfully replaced ${edits.length} block(s) in ${path}.` }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tdiff: diffResult.diff,\n\t\t\t\t\t\tpatch: generateUnifiedPatch(path, baseContent, newContent),\n\t\t\t\t\t\tfirstChangedLine: diffResult.firstChangedLine,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { ExecutionEnv } from "../types.ts";
2
+ /** Serialize file mutations targeting the same environment and canonical path. */
3
+ export declare function withFileMutationQueue<T>(env: ExecutionEnv, path: string, fn: () => Promise<T>): Promise<T>;
4
+ //# sourceMappingURL=file-mutation-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-mutation-queue.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/file-mutation-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA2BhD,kFAAkF;AAClF,wBAAsB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CA2BhH"}
@@ -0,0 +1,46 @@
1
+ import { getOrThrow } from "../types.js";
2
+ const states = new WeakMap();
3
+ function getState(env) {
4
+ let state = states.get(env);
5
+ if (!state) {
6
+ state = { queues: new Map(), registration: Promise.resolve() };
7
+ states.set(env, state);
8
+ }
9
+ return state;
10
+ }
11
+ async function getMutationQueueKey(env, path) {
12
+ const absolutePath = getOrThrow(await env.absolutePath(path));
13
+ const canonicalPath = await env.canonicalPath(absolutePath);
14
+ if (canonicalPath.ok)
15
+ return canonicalPath.value;
16
+ if (canonicalPath.error.code === "not_found" || canonicalPath.error.code === "not_supported")
17
+ return absolutePath;
18
+ throw canonicalPath.error;
19
+ }
20
+ /** Serialize file mutations targeting the same environment and canonical path. */
21
+ export async function withFileMutationQueue(env, path, fn) {
22
+ const state = getState(env);
23
+ const registration = state.registration.then(async () => {
24
+ const key = await getMutationQueueKey(env, path);
25
+ const currentQueue = state.queues.get(key) ?? Promise.resolve();
26
+ let releaseNext = () => { };
27
+ const nextQueue = new Promise((resolve) => {
28
+ releaseNext = resolve;
29
+ });
30
+ const chainedQueue = currentQueue.then(() => nextQueue);
31
+ state.queues.set(key, chainedQueue);
32
+ return { key, currentQueue, chainedQueue, releaseNext };
33
+ });
34
+ state.registration = registration.then(() => undefined, () => undefined);
35
+ const { key, currentQueue, chainedQueue, releaseNext } = await registration;
36
+ await currentQueue;
37
+ try {
38
+ return await fn();
39
+ }
40
+ finally {
41
+ releaseNext();
42
+ if (state.queues.get(key) === chainedQueue)
43
+ state.queues.delete(key);
44
+ }
45
+ }
46
+ //# sourceMappingURL=file-mutation-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-mutation-queue.js","sourceRoot":"","sources":["../../../src/harness/tools/file-mutation-queue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAOzC,MAAM,MAAM,GAAG,IAAI,OAAO,EAAoC,CAAC;AAE/D,SAAS,QAAQ,CAAC,GAAiB;IAClC,IAAI,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,KAAK,GAAG,EAAE,MAAM,EAAE,IAAI,GAAG,EAAE,EAAE,YAAY,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/D,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,GAAiB,EAAE,IAAY;IACjE,MAAM,YAAY,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9D,MAAM,aAAa,GAAG,MAAM,GAAG,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IAC5D,IAAI,aAAa,CAAC,EAAE;QAAE,OAAO,aAAa,CAAC,KAAK,CAAC;IACjD,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,eAAe;QAAE,OAAO,YAAY,CAAC;IAClH,MAAM,aAAa,CAAC,KAAK,CAAC;AAC3B,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAI,GAAiB,EAAE,IAAY,EAAE,EAAoB;IACnG,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;QACvD,MAAM,GAAG,GAAG,MAAM,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACjD,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAEhE,IAAI,WAAW,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC;QAC3B,MAAM,SAAS,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAC/C,WAAW,GAAG,OAAO,CAAC;QACvB,CAAC,CAAC,CAAC;QACH,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACxD,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QACpC,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;IACzD,CAAC,CAAC,CAAC;IACH,KAAK,CAAC,YAAY,GAAG,YAAY,CAAC,IAAI,CACrC,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CACf,CAAC;IAEF,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,MAAM,YAAY,CAAC;IAC5E,MAAM,YAAY,CAAC;IACnB,IAAI,CAAC;QACJ,OAAO,MAAM,EAAE,EAAE,CAAC;IACnB,CAAC;YAAS,CAAC;QACV,WAAW,EAAE,CAAC;QACd,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,YAAY;YAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACtE,CAAC;AACF,CAAC","sourcesContent":["import type { ExecutionEnv } from \"../types.ts\";\nimport { getOrThrow } from \"../types.ts\";\n\ntype MutationQueueState = {\n\tqueues: Map<string, Promise<void>>;\n\tregistration: Promise<void>;\n};\n\nconst states = new WeakMap<ExecutionEnv, MutationQueueState>();\n\nfunction getState(env: ExecutionEnv): MutationQueueState {\n\tlet state = states.get(env);\n\tif (!state) {\n\t\tstate = { queues: new Map(), registration: Promise.resolve() };\n\t\tstates.set(env, state);\n\t}\n\treturn state;\n}\n\nasync function getMutationQueueKey(env: ExecutionEnv, path: string): Promise<string> {\n\tconst absolutePath = getOrThrow(await env.absolutePath(path));\n\tconst canonicalPath = await env.canonicalPath(absolutePath);\n\tif (canonicalPath.ok) return canonicalPath.value;\n\tif (canonicalPath.error.code === \"not_found\" || canonicalPath.error.code === \"not_supported\") return absolutePath;\n\tthrow canonicalPath.error;\n}\n\n/** Serialize file mutations targeting the same environment and canonical path. */\nexport async function withFileMutationQueue<T>(env: ExecutionEnv, path: string, fn: () => Promise<T>): Promise<T> {\n\tconst state = getState(env);\n\tconst registration = state.registration.then(async () => {\n\t\tconst key = await getMutationQueueKey(env, path);\n\t\tconst currentQueue = state.queues.get(key) ?? Promise.resolve();\n\n\t\tlet releaseNext = () => {};\n\t\tconst nextQueue = new Promise<void>((resolve) => {\n\t\t\treleaseNext = resolve;\n\t\t});\n\t\tconst chainedQueue = currentQueue.then(() => nextQueue);\n\t\tstate.queues.set(key, chainedQueue);\n\t\treturn { key, currentQueue, chainedQueue, releaseNext };\n\t});\n\tstate.registration = registration.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\n\tconst { key, currentQueue, chainedQueue, releaseNext } = await registration;\n\tawait currentQueue;\n\ttry {\n\t\treturn await fn();\n\t} finally {\n\t\treleaseNext();\n\t\tif (state.queues.get(key) === chainedQueue) state.queues.delete(key);\n\t}\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export declare function detectSupportedImageMimeType(buffer: Uint8Array): string | undefined;
2
+ export declare function encodeBase64(bytes: Uint8Array): string;
3
+ //# sourceMappingURL=image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/image.ts"],"names":[],"mappings":"AAEA,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAOnF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAatD"}
@@ -0,0 +1,106 @@
1
+ const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
2
+ export function detectSupportedImageMimeType(buffer) {
3
+ if (startsWith(buffer, [0xff, 0xd8, 0xff]))
4
+ return buffer[3] === 0xf7 ? undefined : "image/jpeg";
5
+ if (startsWith(buffer, PNG_SIGNATURE))
6
+ return isPng(buffer) && !isAnimatedPng(buffer) ? "image/png" : undefined;
7
+ if (startsWithAscii(buffer, 0, "GIF"))
8
+ return "image/gif";
9
+ if (startsWithAscii(buffer, 0, "RIFF") && startsWithAscii(buffer, 8, "WEBP"))
10
+ return "image/webp";
11
+ if (startsWithAscii(buffer, 0, "BM") && isBmp(buffer))
12
+ return "image/bmp";
13
+ return undefined;
14
+ }
15
+ export function encodeBase64(bytes) {
16
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
17
+ let output = "";
18
+ for (let index = 0; index < bytes.length; index += 3) {
19
+ const first = bytes[index] ?? 0;
20
+ const second = bytes[index + 1];
21
+ const third = bytes[index + 2];
22
+ output += alphabet[first >> 2];
23
+ output += alphabet[((first & 0x03) << 4) | ((second ?? 0) >> 4)];
24
+ output += second === undefined ? "=" : alphabet[((second & 0x0f) << 2) | ((third ?? 0) >> 6)];
25
+ output += third === undefined ? "=" : alphabet[third & 0x3f];
26
+ }
27
+ return output;
28
+ }
29
+ function isPng(buffer) {
30
+ return (buffer.length >= 16 && readUint32BE(buffer, PNG_SIGNATURE.length) === 13 && startsWithAscii(buffer, 12, "IHDR"));
31
+ }
32
+ function isAnimatedPng(buffer) {
33
+ let offset = PNG_SIGNATURE.length;
34
+ while (offset + 8 <= buffer.length) {
35
+ const chunkLength = readUint32BE(buffer, offset);
36
+ const chunkTypeOffset = offset + 4;
37
+ if (startsWithAscii(buffer, chunkTypeOffset, "acTL"))
38
+ return true;
39
+ if (startsWithAscii(buffer, chunkTypeOffset, "IDAT"))
40
+ return false;
41
+ const nextOffset = offset + 8 + chunkLength + 4;
42
+ if (nextOffset <= offset || nextOffset > buffer.length)
43
+ return false;
44
+ offset = nextOffset;
45
+ }
46
+ return false;
47
+ }
48
+ function isBmp(buffer) {
49
+ if (buffer.length < 26)
50
+ return false;
51
+ const declaredFileSize = readUint32LE(buffer, 2);
52
+ const pixelDataOffset = readUint32LE(buffer, 10);
53
+ const dibHeaderSize = readUint32LE(buffer, 14);
54
+ if (declaredFileSize !== 0 && declaredFileSize < 26)
55
+ return false;
56
+ if (pixelDataOffset < 14 + dibHeaderSize)
57
+ return false;
58
+ if (declaredFileSize !== 0 && pixelDataOffset >= declaredFileSize)
59
+ return false;
60
+ let colorPlanes;
61
+ let bitsPerPixel;
62
+ if (dibHeaderSize === 12) {
63
+ colorPlanes = readUint16LE(buffer, 22);
64
+ bitsPerPixel = readUint16LE(buffer, 24);
65
+ }
66
+ else if (dibHeaderSize >= 40 && dibHeaderSize <= 124) {
67
+ if (buffer.length < 30)
68
+ return false;
69
+ colorPlanes = readUint16LE(buffer, 26);
70
+ bitsPerPixel = readUint16LE(buffer, 28);
71
+ }
72
+ else {
73
+ return false;
74
+ }
75
+ return colorPlanes === 1 && [1, 4, 8, 16, 24, 32].includes(bitsPerPixel);
76
+ }
77
+ function readUint16LE(buffer, offset) {
78
+ return (buffer[offset] ?? 0) + ((buffer[offset + 1] ?? 0) << 8);
79
+ }
80
+ function readUint32BE(buffer, offset) {
81
+ return ((buffer[offset] ?? 0) * 0x1000000 +
82
+ ((buffer[offset + 1] ?? 0) << 16) +
83
+ ((buffer[offset + 2] ?? 0) << 8) +
84
+ (buffer[offset + 3] ?? 0));
85
+ }
86
+ function readUint32LE(buffer, offset) {
87
+ return ((buffer[offset] ?? 0) +
88
+ ((buffer[offset + 1] ?? 0) << 8) +
89
+ ((buffer[offset + 2] ?? 0) << 16) +
90
+ (buffer[offset + 3] ?? 0) * 0x1000000);
91
+ }
92
+ function startsWith(buffer, bytes) {
93
+ if (buffer.length < bytes.length)
94
+ return false;
95
+ return bytes.every((byte, index) => buffer[index] === byte);
96
+ }
97
+ function startsWithAscii(buffer, offset, text) {
98
+ if (buffer.length < offset + text.length)
99
+ return false;
100
+ for (let index = 0; index < text.length; index++) {
101
+ if (buffer[offset + index] !== text.charCodeAt(index))
102
+ return false;
103
+ }
104
+ return true;
105
+ }
106
+ //# sourceMappingURL=image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../../../src/harness/tools/image.ts"],"names":[],"mappings":"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","sourcesContent":["const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\nexport function detectSupportedImageMimeType(buffer: Uint8Array): string | undefined {\n\tif (startsWith(buffer, [0xff, 0xd8, 0xff])) return buffer[3] === 0xf7 ? undefined : \"image/jpeg\";\n\tif (startsWith(buffer, PNG_SIGNATURE)) return isPng(buffer) && !isAnimatedPng(buffer) ? \"image/png\" : undefined;\n\tif (startsWithAscii(buffer, 0, \"GIF\")) return \"image/gif\";\n\tif (startsWithAscii(buffer, 0, \"RIFF\") && startsWithAscii(buffer, 8, \"WEBP\")) return \"image/webp\";\n\tif (startsWithAscii(buffer, 0, \"BM\") && isBmp(buffer)) return \"image/bmp\";\n\treturn undefined;\n}\n\nexport function encodeBase64(bytes: Uint8Array): string {\n\tconst alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\tlet output = \"\";\n\tfor (let index = 0; index < bytes.length; index += 3) {\n\t\tconst first = bytes[index] ?? 0;\n\t\tconst second = bytes[index + 1];\n\t\tconst third = bytes[index + 2];\n\t\toutput += alphabet[first >> 2];\n\t\toutput += alphabet[((first & 0x03) << 4) | ((second ?? 0) >> 4)];\n\t\toutput += second === undefined ? \"=\" : alphabet[((second & 0x0f) << 2) | ((third ?? 0) >> 6)];\n\t\toutput += third === undefined ? \"=\" : alphabet[third & 0x3f];\n\t}\n\treturn output;\n}\n\nfunction isPng(buffer: Uint8Array): boolean {\n\treturn (\n\t\tbuffer.length >= 16 && readUint32BE(buffer, PNG_SIGNATURE.length) === 13 && startsWithAscii(buffer, 12, \"IHDR\")\n\t);\n}\n\nfunction isAnimatedPng(buffer: Uint8Array): boolean {\n\tlet offset = PNG_SIGNATURE.length;\n\twhile (offset + 8 <= buffer.length) {\n\t\tconst chunkLength = readUint32BE(buffer, offset);\n\t\tconst chunkTypeOffset = offset + 4;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"acTL\")) return true;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"IDAT\")) return false;\n\t\tconst nextOffset = offset + 8 + chunkLength + 4;\n\t\tif (nextOffset <= offset || nextOffset > buffer.length) return false;\n\t\toffset = nextOffset;\n\t}\n\treturn false;\n}\n\nfunction isBmp(buffer: Uint8Array): boolean {\n\tif (buffer.length < 26) return false;\n\tconst declaredFileSize = readUint32LE(buffer, 2);\n\tconst pixelDataOffset = readUint32LE(buffer, 10);\n\tconst dibHeaderSize = readUint32LE(buffer, 14);\n\tif (declaredFileSize !== 0 && declaredFileSize < 26) return false;\n\tif (pixelDataOffset < 14 + dibHeaderSize) return false;\n\tif (declaredFileSize !== 0 && pixelDataOffset >= declaredFileSize) return false;\n\n\tlet colorPlanes: number;\n\tlet bitsPerPixel: number;\n\tif (dibHeaderSize === 12) {\n\t\tcolorPlanes = readUint16LE(buffer, 22);\n\t\tbitsPerPixel = readUint16LE(buffer, 24);\n\t} else if (dibHeaderSize >= 40 && dibHeaderSize <= 124) {\n\t\tif (buffer.length < 30) return false;\n\t\tcolorPlanes = readUint16LE(buffer, 26);\n\t\tbitsPerPixel = readUint16LE(buffer, 28);\n\t} else {\n\t\treturn false;\n\t}\n\treturn colorPlanes === 1 && [1, 4, 8, 16, 24, 32].includes(bitsPerPixel);\n}\n\nfunction readUint16LE(buffer: Uint8Array, offset: number): number {\n\treturn (buffer[offset] ?? 0) + ((buffer[offset + 1] ?? 0) << 8);\n}\n\nfunction readUint32BE(buffer: Uint8Array, offset: number): number {\n\treturn (\n\t\t(buffer[offset] ?? 0) * 0x1000000 +\n\t\t((buffer[offset + 1] ?? 0) << 16) +\n\t\t((buffer[offset + 2] ?? 0) << 8) +\n\t\t(buffer[offset + 3] ?? 0)\n\t);\n}\n\nfunction readUint32LE(buffer: Uint8Array, offset: number): number {\n\treturn (\n\t\t(buffer[offset] ?? 0) +\n\t\t((buffer[offset + 1] ?? 0) << 8) +\n\t\t((buffer[offset + 2] ?? 0) << 16) +\n\t\t(buffer[offset + 3] ?? 0) * 0x1000000\n\t);\n}\n\nfunction startsWith(buffer: Uint8Array, bytes: number[]): boolean {\n\tif (buffer.length < bytes.length) return false;\n\treturn bytes.every((byte, index) => buffer[index] === byte);\n}\n\nfunction startsWithAscii(buffer: Uint8Array, offset: number, text: string): boolean {\n\tif (buffer.length < offset + text.length) return false;\n\tfor (let index = 0; index < text.length; index++) {\n\t\tif (buffer[offset + index] !== text.charCodeAt(index)) return false;\n\t}\n\treturn true;\n}\n"]}
@@ -0,0 +1,6 @@
1
+ export { type BashExecution, type BashPrepare, type BashToolDetails, type BashToolInput, type BashToolOptions, createBashTool, } from "./bash.ts";
2
+ export { createEditTool, type EditToolDetails, type EditToolInput, } from "./edit.ts";
3
+ export { createReadTool, type ReadImageProcessor, type ReadImageProcessorResult, type ReadToolDetails, type ReadToolInput, type ReadToolOptions, } from "./read.ts";
4
+ export type { ExecutionToolContext } from "./tool-context.ts";
5
+ export { createWriteTool, type WriteToolInput } from "./write.ts";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,cAAc,GACd,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,aAAa,GAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,eAAe,GACpB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { createBashTool, } from "./bash.js";
2
+ export { createEditTool, } from "./edit.js";
3
+ export { createReadTool, } from "./read.js";
4
+ export { createWriteTool } from "./write.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/harness/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAMN,cAAc,GACd,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,cAAc,GAGd,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,cAAc,GAMd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,eAAe,EAAuB,MAAM,YAAY,CAAC","sourcesContent":["export {\n\ttype BashExecution,\n\ttype BashPrepare,\n\ttype BashToolDetails,\n\ttype BashToolInput,\n\ttype BashToolOptions,\n\tcreateBashTool,\n} from \"./bash.ts\";\nexport {\n\tcreateEditTool,\n\ttype EditToolDetails,\n\ttype EditToolInput,\n} from \"./edit.ts\";\nexport {\n\tcreateReadTool,\n\ttype ReadImageProcessor,\n\ttype ReadImageProcessorResult,\n\ttype ReadToolDetails,\n\ttype ReadToolInput,\n\ttype ReadToolOptions,\n} from \"./read.ts\";\nexport type { ExecutionToolContext } from \"./tool-context.ts\";\nexport { createWriteTool, type WriteToolInput } from \"./write.ts\";\n"]}
@@ -0,0 +1,4 @@
1
+ import type { ExecutionEnv } from "../types.ts";
2
+ export declare function resolveToolPath(env: ExecutionEnv, path: string, signal?: AbortSignal): Promise<string>;
3
+ export declare function resolveReadToolPath(env: ExecutionEnv, path: string, signal?: AbortSignal): Promise<string>;
4
+ //# sourceMappingURL=path-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/path-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAWhD,wBAAsB,eAAe,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAE5G;AAED,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAchH"}
@@ -0,0 +1,26 @@
1
+ import { getOrThrow } from "../types.js";
2
+ const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
3
+ const NARROW_NO_BREAK_SPACE = "\u202F";
4
+ function normalizeToolPath(path) {
5
+ const normalized = path.replace(UNICODE_SPACES, " ");
6
+ return normalized.startsWith("@") ? normalized.slice(1) : normalized;
7
+ }
8
+ export async function resolveToolPath(env, path, signal) {
9
+ return getOrThrow(await env.absolutePath(normalizeToolPath(path), signal));
10
+ }
11
+ export async function resolveReadToolPath(env, path, signal) {
12
+ const resolved = await resolveToolPath(env, path, signal);
13
+ const variants = [
14
+ resolved,
15
+ resolved.replace(/ (AM|PM)\./gi, `${NARROW_NO_BREAK_SPACE}$1.`),
16
+ resolved.normalize("NFD"),
17
+ resolved.replace(/'/g, "\u2019"),
18
+ resolved.normalize("NFD").replace(/'/g, "\u2019"),
19
+ ];
20
+ for (const variant of new Set(variants)) {
21
+ if (getOrThrow(await env.exists(variant, signal)))
22
+ return variant;
23
+ }
24
+ return resolved;
25
+ }
26
+ //# sourceMappingURL=path-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-utils.js","sourceRoot":"","sources":["../../../src/harness/tools/path-utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,cAAc,GAAG,0CAA0C,CAAC;AAClE,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAEvC,SAAS,iBAAiB,CAAC,IAAY;IACtC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IACrD,OAAO,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;AACtE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAiB,EAAE,IAAY,EAAE,MAAoB;IAC1F,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,YAAY,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,GAAiB,EAAE,IAAY,EAAE,MAAoB;IAC9F,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG;QAChB,QAAQ;QACR,QAAQ,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,qBAAqB,KAAK,CAAC;QAC/D,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC;QACzB,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;QAChC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;KACjD,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAAE,OAAO,OAAO,CAAC;IACnE,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC","sourcesContent":["import type { ExecutionEnv } from \"../types.ts\";\nimport { getOrThrow } from \"../types.ts\";\n\nconst UNICODE_SPACES = /[\\u00A0\\u2000-\\u200A\\u202F\\u205F\\u3000]/g;\nconst NARROW_NO_BREAK_SPACE = \"\\u202F\";\n\nfunction normalizeToolPath(path: string): string {\n\tconst normalized = path.replace(UNICODE_SPACES, \" \");\n\treturn normalized.startsWith(\"@\") ? normalized.slice(1) : normalized;\n}\n\nexport async function resolveToolPath(env: ExecutionEnv, path: string, signal?: AbortSignal): Promise<string> {\n\treturn getOrThrow(await env.absolutePath(normalizeToolPath(path), signal));\n}\n\nexport async function resolveReadToolPath(env: ExecutionEnv, path: string, signal?: AbortSignal): Promise<string> {\n\tconst resolved = await resolveToolPath(env, path, signal);\n\tconst variants = [\n\t\tresolved,\n\t\tresolved.replace(/ (AM|PM)\\./gi, `${NARROW_NO_BREAK_SPACE}$1.`),\n\t\tresolved.normalize(\"NFD\"),\n\t\tresolved.replace(/'/g, \"\\u2019\"),\n\t\tresolved.normalize(\"NFD\").replace(/'/g, \"\\u2019\"),\n\t];\n\n\tfor (const variant of new Set(variants)) {\n\t\tif (getOrThrow(await env.exists(variant, signal))) return variant;\n\t}\n\treturn resolved;\n}\n"]}
@@ -0,0 +1,34 @@
1
+ import { type Static, Type } from "typebox";
2
+ import type { AgentHarnessTool } from "../types.ts";
3
+ import { type TruncationResult } from "../utils/truncate.ts";
4
+ import type { ExecutionToolContext } from "./tool-context.ts";
5
+ declare const readSchema: Type.TObject<{
6
+ path: Type.TString;
7
+ offset: Type.TOptional<Type.TNumber>;
8
+ limit: Type.TOptional<Type.TNumber>;
9
+ }>;
10
+ export type ReadToolInput = Static<typeof readSchema>;
11
+ export interface ReadToolDetails {
12
+ truncation?: TruncationResult;
13
+ }
14
+ export type ReadImageProcessorResult = {
15
+ ok: true;
16
+ data: string;
17
+ mimeType: string;
18
+ hints: string[];
19
+ } | {
20
+ ok: false;
21
+ message: string;
22
+ };
23
+ export type ReadImageProcessor = (bytes: Uint8Array, mimeType: string, options: {
24
+ autoResizeImages: boolean;
25
+ }) => Promise<ReadImageProcessorResult>;
26
+ export interface ReadToolOptions {
27
+ /** Whether an injected image processor should resize images. Default: true. */
28
+ autoResizeImages?: boolean;
29
+ /** Optional image conversion/resizing implementation. */
30
+ imageProcessor?: ReadImageProcessor;
31
+ }
32
+ export declare function createReadTool<TContext extends ExecutionToolContext = ExecutionToolContext>(options?: ReadToolOptions): AgentHarnessTool<TContext, typeof readSchema, ReadToolDetails | undefined>;
33
+ export {};
34
+ //# sourceMappingURL=read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/read.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,OAAO,EAIN,KAAK,gBAAgB,EAErB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,QAAA,MAAM,UAAU;;;;EAId,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC9B;AAED,MAAM,MAAM,wBAAwB,GACjC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GAC7D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,MAAM,MAAM,kBAAkB,GAAG,CAChC,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;IAAE,gBAAgB,EAAE,OAAO,CAAA;CAAE,KAClC,OAAO,CAAC,wBAAwB,CAAC,CAAC;AAEvC,MAAM,WAAW,eAAe;IAC/B,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yDAAyD;IACzD,cAAc,CAAC,EAAE,kBAAkB,CAAC;CACpC;AAED,wBAAgB,cAAc,CAAC,QAAQ,SAAS,oBAAoB,GAAG,oBAAoB,EAC1F,OAAO,CAAC,EAAE,eAAe,GACvB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC,CAiG5E"}
@@ -0,0 +1,110 @@
1
+ import { Type } from "typebox";
2
+ import { getOrThrow } from "../types.js";
3
+ import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, truncateHead, } from "../utils/truncate.js";
4
+ import { detectSupportedImageMimeType, encodeBase64 } from "./image.js";
5
+ import { resolveReadToolPath } from "./path-utils.js";
6
+ const readSchema = Type.Object({
7
+ path: Type.String({ description: "Path to the file to read (relative or absolute)" }),
8
+ offset: Type.Optional(Type.Number({ description: "Line number to start reading from (1-indexed)" })),
9
+ limit: Type.Optional(Type.Number({ description: "Maximum number of lines to read" })),
10
+ });
11
+ export function createReadTool(options) {
12
+ return {
13
+ name: "read",
14
+ label: "read",
15
+ description: `Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.`,
16
+ parameters: readSchema,
17
+ async execute(_toolCallId, { path, offset, limit }, signal, _onUpdate, { env }) {
18
+ const absolutePath = await resolveReadToolPath(env, path, signal);
19
+ const bytes = getOrThrow(await env.readBinaryFile(absolutePath, signal));
20
+ const mimeType = detectSupportedImageMimeType(bytes);
21
+ if (mimeType) {
22
+ if (options?.imageProcessor) {
23
+ const processed = await options.imageProcessor(bytes, mimeType, {
24
+ autoResizeImages: options.autoResizeImages ?? true,
25
+ });
26
+ if (!processed.ok) {
27
+ return {
28
+ content: [{ type: "text", text: `Read image file [${mimeType}]\n${processed.message}` }],
29
+ details: undefined,
30
+ };
31
+ }
32
+ const hints = processed.hints.length > 0 ? `\n${processed.hints.join("\n")}` : "";
33
+ return {
34
+ content: [
35
+ { type: "text", text: `Read image file [${processed.mimeType}]${hints}` },
36
+ { type: "image", data: processed.data, mimeType: processed.mimeType },
37
+ ],
38
+ details: undefined,
39
+ };
40
+ }
41
+ if (mimeType === "image/bmp") {
42
+ return {
43
+ content: [
44
+ {
45
+ type: "text",
46
+ text: "Read image file [image/bmp]\n[Image omitted: configure an imageProcessor to convert BMP images.]",
47
+ },
48
+ ],
49
+ details: undefined,
50
+ };
51
+ }
52
+ return {
53
+ content: [
54
+ { type: "text", text: `Read image file [${mimeType}]` },
55
+ { type: "image", data: encodeBase64(bytes), mimeType },
56
+ ],
57
+ details: undefined,
58
+ };
59
+ }
60
+ const textContent = new TextDecoder().decode(bytes);
61
+ const allLines = textContent.split("\n");
62
+ const totalFileLines = allLines.length;
63
+ const startLine = offset ? Math.max(0, offset - 1) : 0;
64
+ const startLineDisplay = startLine + 1;
65
+ if (startLine >= allLines.length) {
66
+ throw new Error(`Offset ${offset} is beyond end of file (${allLines.length} lines total)`);
67
+ }
68
+ let selectedContent;
69
+ let userLimitedLines;
70
+ if (limit !== undefined) {
71
+ const endLine = Math.min(startLine + limit, allLines.length);
72
+ selectedContent = allLines.slice(startLine, endLine).join("\n");
73
+ userLimitedLines = endLine - startLine;
74
+ }
75
+ else {
76
+ selectedContent = allLines.slice(startLine).join("\n");
77
+ }
78
+ const truncation = truncateHead(selectedContent);
79
+ let outputText;
80
+ let details;
81
+ if (truncation.firstLineExceedsLimit) {
82
+ const firstLineSize = formatSize(new TextEncoder().encode(allLines[startLine]).byteLength);
83
+ outputText = `[Line ${startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLineDisplay}p' ${path} | head -c ${DEFAULT_MAX_BYTES}]`;
84
+ details = { truncation };
85
+ }
86
+ else if (truncation.truncated) {
87
+ const endLineDisplay = startLineDisplay + truncation.outputLines - 1;
88
+ const nextOffset = endLineDisplay + 1;
89
+ outputText = truncation.content;
90
+ if (truncation.truncatedBy === "lines") {
91
+ outputText += `\n\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}. Use offset=${nextOffset} to continue.]`;
92
+ }
93
+ else {
94
+ outputText += `\n\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;
95
+ }
96
+ details = { truncation };
97
+ }
98
+ else if (userLimitedLines !== undefined && startLine + userLimitedLines < allLines.length) {
99
+ const remaining = allLines.length - (startLine + userLimitedLines);
100
+ const nextOffset = startLine + userLimitedLines + 1;
101
+ outputText = `${truncation.content}\n\n[${remaining} more lines in file. Use offset=${nextOffset} to continue.]`;
102
+ }
103
+ else {
104
+ outputText = truncation.content;
105
+ }
106
+ return { content: [{ type: "text", text: outputText }], details };
107
+ },
108
+ };
109
+ }
110
+ //# sourceMappingURL=read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/harness/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { type Static, Type } from \"typebox\";\nimport type { AgentHarnessTool } from \"../types.ts\";\nimport { getOrThrow } from \"../types.ts\";\nimport {\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\tformatSize,\n\ttype TruncationResult,\n\ttruncateHead,\n} from \"../utils/truncate.ts\";\nimport { detectSupportedImageMimeType, encodeBase64 } from \"./image.ts\";\nimport { resolveReadToolPath } from \"./path-utils.ts\";\nimport type { ExecutionToolContext } from \"./tool-context.ts\";\n\nconst readSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to read (relative or absolute)\" }),\n\toffset: Type.Optional(Type.Number({ description: \"Line number to start reading from (1-indexed)\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"Maximum number of lines to read\" })),\n});\n\nexport type ReadToolInput = Static<typeof readSchema>;\n\nexport interface ReadToolDetails {\n\ttruncation?: TruncationResult;\n}\n\nexport type ReadImageProcessorResult =\n\t| { ok: true; data: string; mimeType: string; hints: string[] }\n\t| { ok: false; message: string };\n\nexport type ReadImageProcessor = (\n\tbytes: Uint8Array,\n\tmimeType: string,\n\toptions: { autoResizeImages: boolean },\n) => Promise<ReadImageProcessorResult>;\n\nexport interface ReadToolOptions {\n\t/** Whether an injected image processor should resize images. Default: true. */\n\tautoResizeImages?: boolean;\n\t/** Optional image conversion/resizing implementation. */\n\timageProcessor?: ReadImageProcessor;\n}\n\nexport function createReadTool<TContext extends ExecutionToolContext = ExecutionToolContext>(\n\toptions?: ReadToolOptions,\n): AgentHarnessTool<TContext, typeof readSchema, ReadToolDetails | undefined> {\n\treturn {\n\t\tname: \"read\",\n\t\tlabel: \"read\",\n\t\tdescription: `Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.`,\n\t\tparameters: readSchema,\n\t\tasync execute(_toolCallId, { path, offset, limit }, signal, _onUpdate, { env }) {\n\t\t\tconst absolutePath = await resolveReadToolPath(env, path, signal);\n\t\t\tconst bytes = getOrThrow(await env.readBinaryFile(absolutePath, signal));\n\t\t\tconst mimeType = detectSupportedImageMimeType(bytes);\n\t\t\tif (mimeType) {\n\t\t\t\tif (options?.imageProcessor) {\n\t\t\t\t\tconst processed = await options.imageProcessor(bytes, mimeType, {\n\t\t\t\t\t\tautoResizeImages: options.autoResizeImages ?? true,\n\t\t\t\t\t});\n\t\t\t\t\tif (!processed.ok) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: `Read image file [${mimeType}]\\n${processed.message}` }],\n\t\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tconst hints = processed.hints.length > 0 ? `\\n${processed.hints.join(\"\\n\")}` : \"\";\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{ type: \"text\", text: `Read image file [${processed.mimeType}]${hints}` },\n\t\t\t\t\t\t\t{ type: \"image\", data: processed.data, mimeType: processed.mimeType },\n\t\t\t\t\t\t] satisfies Array<TextContent | ImageContent>,\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tif (mimeType === \"image/bmp\") {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\ttext: \"Read image file [image/bmp]\\n[Image omitted: configure an imageProcessor to convert BMP images.]\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tdetails: undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{ type: \"text\", text: `Read image file [${mimeType}]` },\n\t\t\t\t\t\t{ type: \"image\", data: encodeBase64(bytes), mimeType },\n\t\t\t\t\t] satisfies Array<TextContent | ImageContent>,\n\t\t\t\t\tdetails: undefined,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst textContent = new TextDecoder().decode(bytes);\n\t\t\tconst allLines = textContent.split(\"\\n\");\n\t\t\tconst totalFileLines = allLines.length;\n\t\t\tconst startLine = offset ? Math.max(0, offset - 1) : 0;\n\t\t\tconst startLineDisplay = startLine + 1;\n\t\t\tif (startLine >= allLines.length) {\n\t\t\t\tthrow new Error(`Offset ${offset} is beyond end of file (${allLines.length} lines total)`);\n\t\t\t}\n\n\t\t\tlet selectedContent: string;\n\t\t\tlet userLimitedLines: number | undefined;\n\t\t\tif (limit !== undefined) {\n\t\t\t\tconst endLine = Math.min(startLine + limit, allLines.length);\n\t\t\t\tselectedContent = allLines.slice(startLine, endLine).join(\"\\n\");\n\t\t\t\tuserLimitedLines = endLine - startLine;\n\t\t\t} else {\n\t\t\t\tselectedContent = allLines.slice(startLine).join(\"\\n\");\n\t\t\t}\n\n\t\t\tconst truncation = truncateHead(selectedContent);\n\t\t\tlet outputText: string;\n\t\t\tlet details: ReadToolDetails | undefined;\n\t\t\tif (truncation.firstLineExceedsLimit) {\n\t\t\t\tconst firstLineSize = formatSize(new TextEncoder().encode(allLines[startLine]).byteLength);\n\t\t\t\toutputText = `[Line ${startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLineDisplay}p' ${path} | head -c ${DEFAULT_MAX_BYTES}]`;\n\t\t\t\tdetails = { truncation };\n\t\t\t} else if (truncation.truncated) {\n\t\t\t\tconst endLineDisplay = startLineDisplay + truncation.outputLines - 1;\n\t\t\t\tconst nextOffset = endLineDisplay + 1;\n\t\t\t\toutputText = truncation.content;\n\t\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}. Use offset=${nextOffset} to continue.]`;\n\t\t\t\t} else {\n\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue.]`;\n\t\t\t\t}\n\t\t\t\tdetails = { truncation };\n\t\t\t} else if (userLimitedLines !== undefined && startLine + userLimitedLines < allLines.length) {\n\t\t\t\tconst remaining = allLines.length - (startLine + userLimitedLines);\n\t\t\t\tconst nextOffset = startLine + userLimitedLines + 1;\n\t\t\t\toutputText = `${truncation.content}\\n\\n[${remaining} more lines in file. Use offset=${nextOffset} to continue.]`;\n\t\t\t} else {\n\t\t\t\toutputText = truncation.content;\n\t\t\t}\n\n\t\t\treturn { content: [{ type: \"text\", text: outputText }], details };\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { ExecutionEnv } from "../types.ts";
2
+ /** Filesystem and shell context required by the built-in execution tools. */
3
+ export interface ExecutionToolContext {
4
+ env: ExecutionEnv;
5
+ }
6
+ //# sourceMappingURL=tool-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-context.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/tool-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,6EAA6E;AAC7E,MAAM,WAAW,oBAAoB;IACpC,GAAG,EAAE,YAAY,CAAC;CAClB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tool-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-context.js","sourceRoot":"","sources":["../../../src/harness/tools/tool-context.ts"],"names":[],"mappings":"","sourcesContent":["import type { ExecutionEnv } from \"../types.ts\";\n\n/** Filesystem and shell context required by the built-in execution tools. */\nexport interface ExecutionToolContext {\n\tenv: ExecutionEnv;\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import { type Static, Type } from "typebox";
2
+ import type { AgentHarnessTool } from "../types.ts";
3
+ import type { ExecutionToolContext } from "./tool-context.ts";
4
+ declare const writeSchema: Type.TObject<{
5
+ path: Type.TString;
6
+ content: Type.TString;
7
+ }>;
8
+ export type WriteToolInput = Static<typeof writeSchema>;
9
+ export declare function createWriteTool<TContext extends ExecutionToolContext = ExecutionToolContext>(): AgentHarnessTool<TContext, typeof writeSchema, undefined>;
10
+ export {};
11
+ //# sourceMappingURL=write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../src/harness/tools/write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,QAAA,MAAM,WAAW;;;EAGf,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AAExD,wBAAgB,eAAe,CAAC,QAAQ,SAAS,oBAAoB,GAAG,oBAAoB,KAAK,gBAAgB,CAChH,QAAQ,EACR,OAAO,WAAW,EAClB,SAAS,CACT,CAoBA"}