@earendil-works/pi-durable 1.4.0 → 1.5.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 (221) hide show
  1. package/CHANGELOG.md +186 -0
  2. package/README.md +181 -84
  3. package/dist/codemode/codemode.d.ts +4 -4
  4. package/dist/codemode/codemode.d.ts.map +1 -1
  5. package/dist/codemode/codemode.js +9 -12
  6. package/dist/codemode/codemode.js.map +1 -1
  7. package/dist/codemode/models.js +4 -4
  8. package/dist/codemode/models.js.map +1 -1
  9. package/dist/env/index.d.ts +12 -10
  10. package/dist/env/index.d.ts.map +1 -1
  11. package/dist/env/index.js.map +1 -1
  12. package/dist/env/node-watch.d.ts +3 -3
  13. package/dist/env/node-watch.d.ts.map +1 -1
  14. package/dist/env/node-watch.js +18 -16
  15. package/dist/env/node-watch.js.map +1 -1
  16. package/dist/env/node.d.ts +2 -2
  17. package/dist/env/node.d.ts.map +1 -1
  18. package/dist/env/node.js +1 -1
  19. package/dist/env/node.js.map +1 -1
  20. package/dist/errors.d.ts +18 -2
  21. package/dist/errors.d.ts.map +1 -1
  22. package/dist/errors.js +26 -3
  23. package/dist/errors.js.map +1 -1
  24. package/dist/harness/agent.d.ts.map +1 -1
  25. package/dist/harness/agent.js +10 -2
  26. package/dist/harness/agent.js.map +1 -1
  27. package/dist/harness/compaction.d.ts.map +1 -1
  28. package/dist/harness/compaction.js +19 -2
  29. package/dist/harness/compaction.js.map +1 -1
  30. package/dist/harness/context.d.ts +23 -7
  31. package/dist/harness/context.d.ts.map +1 -1
  32. package/dist/harness/context.js +71 -25
  33. package/dist/harness/context.js.map +1 -1
  34. package/dist/harness/define.d.ts +7 -3
  35. package/dist/harness/define.d.ts.map +1 -1
  36. package/dist/harness/define.js +4 -0
  37. package/dist/harness/define.js.map +1 -1
  38. package/dist/harness/generation.d.ts +38 -5
  39. package/dist/harness/generation.d.ts.map +1 -1
  40. package/dist/harness/generation.js +445 -96
  41. package/dist/harness/generation.js.map +1 -1
  42. package/dist/harness/harness.d.ts +2 -0
  43. package/dist/harness/harness.d.ts.map +1 -1
  44. package/dist/harness/harness.js +87 -62
  45. package/dist/harness/harness.js.map +1 -1
  46. package/dist/harness/inbox.d.ts +5 -1
  47. package/dist/harness/inbox.d.ts.map +1 -1
  48. package/dist/harness/inbox.js +4 -1
  49. package/dist/harness/inbox.js.map +1 -1
  50. package/dist/harness/json.d.ts +20 -4
  51. package/dist/harness/json.d.ts.map +1 -1
  52. package/dist/harness/json.js +23 -5
  53. package/dist/harness/json.js.map +1 -1
  54. package/dist/harness/live.d.ts +122 -16
  55. package/dist/harness/live.d.ts.map +1 -1
  56. package/dist/harness/live.js +93 -12
  57. package/dist/harness/live.js.map +1 -1
  58. package/dist/harness/partial-changes.d.ts +54 -0
  59. package/dist/harness/partial-changes.d.ts.map +1 -0
  60. package/dist/harness/partial-changes.js +162 -0
  61. package/dist/harness/partial-changes.js.map +1 -0
  62. package/dist/harness/registry-reader.d.ts +11 -0
  63. package/dist/harness/registry-reader.d.ts.map +1 -0
  64. package/dist/harness/registry-reader.js +25 -0
  65. package/dist/harness/registry-reader.js.map +1 -0
  66. package/dist/harness/scheduler.d.ts +46 -19
  67. package/dist/harness/scheduler.d.ts.map +1 -1
  68. package/dist/harness/scheduler.js +973 -376
  69. package/dist/harness/scheduler.js.map +1 -1
  70. package/dist/harness/submissions.d.ts +17 -10
  71. package/dist/harness/submissions.d.ts.map +1 -1
  72. package/dist/harness/submissions.js +62 -25
  73. package/dist/harness/submissions.js.map +1 -1
  74. package/dist/harness/task-graph.d.ts +12 -9
  75. package/dist/harness/task-graph.d.ts.map +1 -1
  76. package/dist/harness/task-graph.js +38 -28
  77. package/dist/harness/task-graph.js.map +1 -1
  78. package/dist/harness/telemetry.d.ts +11 -11
  79. package/dist/harness/telemetry.d.ts.map +1 -1
  80. package/dist/harness/telemetry.js +7 -7
  81. package/dist/harness/telemetry.js.map +1 -1
  82. package/dist/harness/tool.d.ts +2 -0
  83. package/dist/harness/tool.d.ts.map +1 -1
  84. package/dist/harness/tool.js +171 -55
  85. package/dist/harness/tool.js.map +1 -1
  86. package/dist/harness/transcript.d.ts +72 -0
  87. package/dist/harness/transcript.d.ts.map +1 -0
  88. package/dist/harness/transcript.js +264 -0
  89. package/dist/harness/transcript.js.map +1 -0
  90. package/dist/harness/types.d.ts +218 -59
  91. package/dist/harness/types.d.ts.map +1 -1
  92. package/dist/harness/types.js.map +1 -1
  93. package/dist/harness/util.d.ts +3 -1
  94. package/dist/harness/util.d.ts.map +1 -1
  95. package/dist/harness/util.js +9 -0
  96. package/dist/harness/util.js.map +1 -1
  97. package/dist/harness/view.d.ts +43 -22
  98. package/dist/harness/view.d.ts.map +1 -1
  99. package/dist/harness/view.js +130 -71
  100. package/dist/harness/view.js.map +1 -1
  101. package/dist/index.d.ts +9 -7
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +4 -3
  104. package/dist/index.js.map +1 -1
  105. package/dist/search.d.ts +19 -3
  106. package/dist/search.d.ts.map +1 -1
  107. package/dist/search.js +107 -44
  108. package/dist/search.js.map +1 -1
  109. package/dist/session/cached-storage.d.ts +71 -0
  110. package/dist/session/cached-storage.d.ts.map +1 -0
  111. package/dist/session/cached-storage.js +269 -0
  112. package/dist/session/cached-storage.js.map +1 -0
  113. package/dist/session/conversation-tails.d.ts +33 -0
  114. package/dist/session/conversation-tails.d.ts.map +1 -0
  115. package/dist/session/conversation-tails.js +101 -0
  116. package/dist/session/conversation-tails.js.map +1 -0
  117. package/dist/session/keys.d.ts +68 -0
  118. package/dist/session/keys.d.ts.map +1 -0
  119. package/dist/session/keys.js +161 -0
  120. package/dist/session/keys.js.map +1 -0
  121. package/dist/session/session.d.ts +167 -26
  122. package/dist/session/session.d.ts.map +1 -1
  123. package/dist/session/session.js +1029 -171
  124. package/dist/session/session.js.map +1 -1
  125. package/dist/session/transaction.d.ts +81 -3
  126. package/dist/session/transaction.d.ts.map +1 -1
  127. package/dist/session/transaction.js +224 -37
  128. package/dist/session/transaction.js.map +1 -1
  129. package/dist/session/writer.d.ts +74 -0
  130. package/dist/session/writer.d.ts.map +1 -0
  131. package/dist/session/writer.js +294 -0
  132. package/dist/session/writer.js.map +1 -0
  133. package/dist/storage/indexeddb.d.ts +10 -2
  134. package/dist/storage/indexeddb.d.ts.map +1 -1
  135. package/dist/storage/indexeddb.js +91 -41
  136. package/dist/storage/indexeddb.js.map +1 -1
  137. package/dist/storage/jsonl/storage.d.ts +14 -2
  138. package/dist/storage/jsonl/storage.d.ts.map +1 -1
  139. package/dist/storage/jsonl/storage.js +88 -24
  140. package/dist/storage/jsonl/storage.js.map +1 -1
  141. package/dist/storage/memory.d.ts +7 -2
  142. package/dist/storage/memory.d.ts.map +1 -1
  143. package/dist/storage/memory.js +28 -2
  144. package/dist/storage/memory.js.map +1 -1
  145. package/dist/storage/sqlite/storage.d.ts +14 -2
  146. package/dist/storage/sqlite/storage.d.ts.map +1 -1
  147. package/dist/storage/sqlite/storage.js +110 -36
  148. package/dist/storage/sqlite/storage.js.map +1 -1
  149. package/dist/storage/store/commit.d.ts +33 -0
  150. package/dist/storage/store/commit.d.ts.map +1 -0
  151. package/dist/storage/store/commit.js +286 -0
  152. package/dist/storage/store/commit.js.map +1 -0
  153. package/dist/storage/store/documents.d.ts +43 -0
  154. package/dist/storage/store/documents.d.ts.map +1 -0
  155. package/dist/storage/store/documents.js +102 -0
  156. package/dist/storage/store/documents.js.map +1 -0
  157. package/dist/storage/store/index.d.ts +2 -0
  158. package/dist/storage/store/index.d.ts.map +1 -0
  159. package/dist/storage/store/index.js +2 -0
  160. package/dist/storage/store/index.js.map +1 -0
  161. package/dist/storage/store/queries.d.ts +49 -0
  162. package/dist/storage/store/queries.d.ts.map +1 -0
  163. package/dist/storage/store/queries.js +93 -0
  164. package/dist/storage/store/queries.js.map +1 -0
  165. package/dist/storage/store/storage.d.ts +59 -0
  166. package/dist/storage/store/storage.d.ts.map +1 -0
  167. package/dist/storage/store/storage.js +438 -0
  168. package/dist/storage/store/storage.js.map +1 -0
  169. package/dist/storage/store/tables.d.ts +54 -0
  170. package/dist/storage/store/tables.d.ts.map +1 -0
  171. package/dist/storage/store/tables.js +108 -0
  172. package/dist/storage/store/tables.js.map +1 -0
  173. package/dist/testing/commit-writes.d.ts +5 -0
  174. package/dist/testing/commit-writes.d.ts.map +1 -0
  175. package/dist/testing/commit-writes.js +12 -0
  176. package/dist/testing/commit-writes.js.map +1 -0
  177. package/dist/testing/env-conformance.js +7 -7
  178. package/dist/testing/env-conformance.js.map +1 -1
  179. package/dist/testing/index.d.ts +1 -0
  180. package/dist/testing/index.d.ts.map +1 -1
  181. package/dist/testing/index.js +1 -0
  182. package/dist/testing/index.js.map +1 -1
  183. package/dist/testing/storage-benchmark.d.ts.map +1 -1
  184. package/dist/testing/storage-benchmark.js +18 -17
  185. package/dist/testing/storage-benchmark.js.map +1 -1
  186. package/dist/testing/storage-conformance.d.ts.map +1 -1
  187. package/dist/testing/storage-conformance.js +354 -69
  188. package/dist/testing/storage-conformance.js.map +1 -1
  189. package/dist/tool-search/tool-search.d.ts +6 -12
  190. package/dist/tool-search/tool-search.d.ts.map +1 -1
  191. package/dist/tool-search/tool-search.js +7 -7
  192. package/dist/tool-search/tool-search.js.map +1 -1
  193. package/dist/tools/bash.d.ts +12 -12
  194. package/dist/tools/bash.d.ts.map +1 -1
  195. package/dist/tools/bash.js +16 -15
  196. package/dist/tools/bash.js.map +1 -1
  197. package/dist/tools/edit.d.ts +7 -7
  198. package/dist/tools/edit.d.ts.map +1 -1
  199. package/dist/tools/edit.js +9 -9
  200. package/dist/tools/edit.js.map +1 -1
  201. package/dist/tools/index.d.ts +32 -32
  202. package/dist/tools/read.d.ts +6 -6
  203. package/dist/tools/read.d.ts.map +1 -1
  204. package/dist/tools/read.js +19 -8
  205. package/dist/tools/read.js.map +1 -1
  206. package/dist/tools/write.d.ts +4 -4
  207. package/dist/tools/write.d.ts.map +1 -1
  208. package/dist/tools/write.js +5 -5
  209. package/dist/tools/write.js.map +1 -1
  210. package/dist/types.d.ts +223 -77
  211. package/dist/types.d.ts.map +1 -1
  212. package/dist/types.js.map +1 -1
  213. package/package.json +19 -12
  214. package/dist/harness/events.d.ts +0 -186
  215. package/dist/harness/events.d.ts.map +0 -1
  216. package/dist/harness/events.js +0 -373
  217. package/dist/harness/events.js.map +0 -1
  218. package/dist/session/observation.d.ts +0 -42
  219. package/dist/session/observation.d.ts.map +0 -1
  220. package/dist/session/observation.js +0 -281
  221. package/dist/session/observation.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport { characterEnd } from \"@earendil-works/chord/output\";\nimport { type Static, Type } from \"typebox\";\nimport {\n\ttype BinaryReader,\n\ttype FileInfo,\n\tgetOrThrow,\n\ttype LineScan,\n\trangeDecoder,\n\tstartsWithBom,\n} from \"../env/index.ts\";\nimport { defineTool } from \"../harness/define.ts\";\nimport type { ToolDiagnostic, ToolExecutionApi, ToolExecutionResult, ToolRegistration } from \"../harness/types.ts\";\nimport {\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\tformatSize,\n\ttype TruncationResult,\n\ttruncateHeadOf,\n\tutf8ByteLength,\n} from \"../truncate.ts\";\nimport { requireEnv } from \"./env.ts\";\nimport { detectSupportedImageMimeTypeOf, imageDimensions } from \"./image.ts\";\nimport {\n\tbase64Length,\n\tDEFAULT_IMAGE_LIMITS,\n\ttype ImageLimits,\n\ttype ImageProcessor,\n\tINLINE_IMAGE_TYPES,\n\ttoBase64,\n} from \"./image-processor.ts\";\nimport { resolveReadToolPath } from \"./path-utils.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 type ReadToolDetails = {\n\t/** How the shown text was cut; the text itself is the result content. */\n\ttruncation?: Omit<TruncationResult, \"content\">;\n};\n\nconst READ_CHUNK = 64 * 1024;\n\n/** `Array.prototype.slice`'s conversion of an index: NaN is 0, other values truncate toward zero. */\nfunction sliceIndex(value: number): number {\n\treturn Number.isNaN(value) ? 0 : Math.trunc(value);\n}\n\n/**\n * The decoded start of bytes `[start, end)` of the file, decoded as part of the whole file: all of it, or enough for\n * `truncateHeadOf` (more than `DEFAULT_MAX_BYTES + 1` bytes, or `DEFAULT_MAX_LINES` newlines).\n */\nasync function readHead(\n\treader: BinaryReader,\n\tstart: number,\n\tend: number,\n\tskipBom: boolean,\n\tcontext: Context,\n): Promise<string> {\n\tconst decoder = rangeDecoder();\n\tlet text = \"\";\n\tlet newlines = 0;\n\tfor (let position = skipBom && start === 0 ? 3 : start; position < end; ) {\n\t\tconst bytes = getOrThrow(await reader.read(position, Math.min(READ_CHUNK, end - position), context));\n\t\tif (bytes.length === 0) break;\n\t\tposition += bytes.length;\n\t\tconst decoded = decoder.decode(bytes, { stream: true });\n\t\ttext += decoded;\n\t\tfor (let index = decoded.indexOf(\"\\n\"); index !== -1; index = decoded.indexOf(\"\\n\", index + 1)) newlines++;\n\t\tif (newlines >= DEFAULT_MAX_LINES || utf8ByteLength(text) > DEFAULT_MAX_BYTES + 1) return text;\n\t}\n\treturn text + decoder.decode();\n}\n\nexport type ReadToolOptions = {\n\t/** Resizes and converts images; without one, images within `DEFAULT_IMAGE_LIMITS.maxBytes` pass through as they are. */\n\treadonly images?: ImageProcessor;\n};\n\n/**\n * Reads text files and images. Remarks about truncation, continuation, and image processing are diagnostics; the content\n * is only file text, or one image, which programs get as its `ImageContent`.\n */\nexport function createReadTool(options: ReadToolOptions = {}): ToolRegistration<typeof readSchema, ReadToolDetails> {\n\tconst formats = options.images === undefined ? \"jpg, png, gif, webp\" : \"jpg, png, gif, webp, bmp\";\n\treturn defineTool({\n\t\tname: \"read\",\n\t\tdescription: `Read the contents of a file. Supports text files and images (${formats}). 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(args, api, context) {\n\t\t\tconst { path, offset, limit } = args;\n\t\t\tconst env = requireEnv(api);\n\t\t\tconst absolutePath = await resolveReadToolPath(env, path, context);\n\t\t\tconst reader = getOrThrow(await env.openBinaryReader(absolutePath, undefined, context));\n\t\t\ttry {\n\t\t\t\t// A concurrent writer can change the file between the scan and the reads. Appending (a growing log) leaves\n\t\t\t\t// the scanned text as it was; an image must be whole. Otherwise the file is read again once.\n\t\t\t\tfor (let attempt = 0; ; attempt++) {\n\t\t\t\t\tconst before = getOrThrow(await reader.info(context));\n\t\t\t\t\tconst mimeType = await detectSupportedImageMimeTypeOf({\n\t\t\t\t\t\tsize: before.size,\n\t\t\t\t\t\tread: async (position, length) => getOrThrow(await reader.read(position, length, context)),\n\t\t\t\t\t});\n\t\t\t\t\tconst result =\n\t\t\t\t\t\tmimeType === undefined\n\t\t\t\t\t\t\t? await readText(reader, path, offset, limit, context)\n\t\t\t\t\t\t\t: await readImage(\n\t\t\t\t\t\t\t\t\treader,\n\t\t\t\t\t\t\t\t\tbefore,\n\t\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\t\tmimeType,\n\t\t\t\t\t\t\t\t\toptions.images,\n\t\t\t\t\t\t\t\t\tawait imageModel(api, context),\n\t\t\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\tconst after = getOrThrow(await reader.info(context));\n\t\t\t\t\tconst unchanged = after.size === before.size && after.mtimeMs === before.mtimeMs;\n\t\t\t\t\tif (unchanged || (mimeType === undefined && after.size > before.size)) return result;\n\t\t\t\t\tif (attempt === 1) throw new Error(`${path} changed while it was read`);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tawait reader.close(context);\n\t\t\t}\n\t\t},\n\t});\n}\n\n/**\n * One image block, prepared by `processor` or, without one, as it is when its format is inline and it fits the limits\n * (its size is checked before reading it, its dimensions from its header); otherwise an error result saying why. What happened to the image is an `info` diagnostic, so programs get the\n * bare `ImageContent`. A model without image input sees pi-ai's placeholder instead; a diagnostic says so.\n */\nasync function readImage(\n\treader: BinaryReader,\n\tinfo: FileInfo,\n\tpath: string,\n\tmimeType: string,\n\tprocessor: ImageProcessor | undefined,\n\t{ vision, limits }: ImageModel,\n\tcontext: Context,\n): Promise<ToolExecutionResult<ReadToolDetails>> {\n\tlet image: { readonly data: string; readonly mimeType: string };\n\tconst notes: string[] = [];\n\tif (processor !== undefined) {\n\t\tconst bytes = getOrThrow(await reader.read(0, info.size, context));\n\t\tconst prepared = await processor.prepare(bytes, mimeType, limits);\n\t\tif (prepared === undefined)\n\t\t\treturn imageError(`${path} is an image (${mimeType}) that cannot be prepared for the model`);\n\t\timage = prepared;\n\t\tif (prepared.convertedFrom !== undefined)\n\t\t\tnotes.push(`Converted from ${prepared.convertedFrom} to ${prepared.mimeType}.`);\n\t\tif (prepared.resized !== undefined) {\n\t\t\tconst { from, to } = prepared.resized;\n\t\t\tconst scale = (from.width / to.width).toFixed(2);\n\t\t\tnotes.push(\n\t\t\t\t`Resized from ${from.width}x${from.height} to ${to.width}x${to.height}. Multiply coordinates by ${scale} to map them to the original.`,\n\t\t\t);\n\t\t}\n\t} else {\n\t\tif (!INLINE_IMAGE_TYPES.has(mimeType)) {\n\t\t\treturn imageError(\n\t\t\t\t`${path} is an image (${mimeType}) that needs converting, and no image processor is configured`,\n\t\t\t);\n\t\t}\n\t\tconst unshrinkable = \"and no image processor is configured to shrink it\";\n\t\tif (base64Length(info.size) > limits.maxBytes) {\n\t\t\treturn imageError(\n\t\t\t\t`${path} is an image (${mimeType}) of ${formatSize(info.size)}, too large to send, ${unshrinkable}`,\n\t\t\t);\n\t\t}\n\t\tconst bytes = getOrThrow(await reader.read(0, info.size, context));\n\t\tconst size = imageDimensions(bytes, mimeType);\n\t\tif (size === undefined) return imageError(`${path} is an image (${mimeType}) whose size cannot be read`);\n\t\tif (size.width > limits.maxWidth || size.height > limits.maxHeight) {\n\t\t\treturn imageError(\n\t\t\t\t`${path} is an image (${mimeType}) of ${size.width}x${size.height}, larger than ${limits.maxWidth}x${limits.maxHeight}, ${unshrinkable}`,\n\t\t\t);\n\t\t}\n\t\timage = { data: toBase64(bytes), mimeType };\n\t}\n\tif (!vision) notes.push(\"The current model does not support images; it sees a placeholder instead.\");\n\tconst diagnostics: ToolDiagnostic[] = [\n\t\t{ severity: \"info\", code: \"image\", message: [`Read image file [${image.mimeType}].`, ...notes].join(\" \") },\n\t];\n\treturn { output: [{ type: \"image\", data: image.data, mimeType: image.mimeType }], diagnostics };\n}\n\n/** What the conversation's model takes: whether images, and within which limits. */\ntype ImageModel = { readonly vision: boolean; readonly limits: ImageLimits };\n\n/**\n * The conversation model's image input: `DEFAULT_IMAGE_LIMITS` overridden by its `inputLimits.images.resize`. Without\n * a resolvable model, it counts as one that takes images within the defaults.\n */\nasync function imageModel(api: ToolExecutionApi, context: Context): Promise<ImageModel> {\n\tconst ref = (await api.agent(context)).model;\n\tconst model = ref === undefined ? undefined : api.models.getModel(ref.provider, ref.modelId);\n\tconst resize = model?.inputLimits?.images?.resize;\n\treturn {\n\t\tvision: model?.input.includes(\"image\") ?? true,\n\t\tlimits: {\n\t\t\tmaxWidth: resize?.maxWidth ?? DEFAULT_IMAGE_LIMITS.maxWidth,\n\t\t\tmaxHeight: resize?.maxHeight ?? DEFAULT_IMAGE_LIMITS.maxHeight,\n\t\t\tmaxBytes: resize?.maxBytes ?? DEFAULT_IMAGE_LIMITS.maxBytes,\n\t\t},\n\t};\n}\n\nfunction imageError(message: string): ToolExecutionResult<ReadToolDetails> {\n\treturn { output: [], isError: true, diagnostics: [{ severity: \"error\", code: \"unsupported_image\", message }] };\n}\n\n/**\n * The read result for the opened file. It equals decoding the whole file with `TextDecoder`, splitting it on `\\n`,\n * and bounding the selected lines with `truncateHead`, while reading only one scan's worth of the file plus the head.\n */\nasync function readText(\n\treader: BinaryReader,\n\tpath: string,\n\toffset: number | undefined,\n\tlimit: number | undefined,\n\tcontext: Context,\n) {\n\tconst startLine = offset ? Math.max(0, offset - 1) : 0;\n\tconst startLineDisplay = startLine + 1;\n\t// Lines are selected like `allLines.slice(startLine, endLine)`, which truncates fractional indices.\n\tconst sliceStart = sliceIndex(startLine);\n\t// One pass finds the line count and the selection; a selection past the last line ends with it, as `slice` does,\n\t// and an empty one (a zero or negative limit) is scanned as one line and then ignored.\n\t// A start beyond any file is scanned from 0 only to count lines; the offset check below then fails as before.\n\tconst scanStart = Number.isSafeInteger(sliceStart) ? sliceStart : 0;\n\tconst requestedEnd = limit === undefined ? undefined : Math.max(scanStart + 1, sliceIndex(startLine + limit));\n\tconst scanEnd = requestedEnd !== undefined && Number.isSafeInteger(requestedEnd) ? requestedEnd : undefined;\n\tconst scanOf = async (endLine: number | undefined): Promise<LineScan> =>\n\t\tgetOrThrow(\n\t\t\tawait reader.scanLines({ startLine: scanStart, ...(endLine === undefined ? {} : { endLine }) }, context),\n\t\t);\n\tlet scan = await scanOf(scanEnd);\n\tconst totalFileLines = scan.newlines + 1;\n\tif (startLine >= totalFileLines) {\n\t\tthrow new Error(`Offset ${offset} is beyond end of file (${totalFileLines} lines total)`);\n\t}\n\n\tlet userLimitedLines: number | undefined;\n\tlet selectedLineCount = totalFileLines - sliceStart;\n\tif (limit !== undefined) {\n\t\tconst endLine = Math.min(startLine + limit, totalFileLines);\n\t\tuserLimitedLines = endLine - startLine;\n\t\t// `slice` counts a negative end from the end of the lines, which only the line count tells; scan again for it.\n\t\tconst relativeEnd = sliceIndex(endLine);\n\t\tconst sliceEnd = relativeEnd < 0 ? Math.max(totalFileLines + relativeEnd, 0) : relativeEnd;\n\t\tselectedLineCount = Math.max(0, sliceEnd - sliceStart);\n\t\tif (selectedLineCount > 0 && relativeEnd < 0) scan = await scanOf(sliceEnd);\n\t}\n\tconst empty = selectedLineCount === 0;\n\t// Counted like `truncateHead`: a trailing newline adds no line, and empty text has none.\n\tconst endsWithNewline = !empty && scan.lastLineStart === scan.end && scan.lastLineStart > scan.start;\n\tconst totals = {\n\t\tlines: empty || scan.selectedBytes === 0 ? 0 : selectedLineCount - (endsWithNewline ? 1 : 0),\n\t\tbytes: empty ? 0 : scan.selectedBytes,\n\t};\n\tconst firstBytes = getOrThrow(await reader.read(0, 3, context));\n\tconst head = empty ? \"\" : await readHead(reader, scan.start, scan.end, startsWithBom(firstBytes), context);\n\n\tconst { content: headText, ...truncation } = truncateHeadOf(head, totals);\n\tconst diagnostics: ToolDiagnostic[] = [];\n\tlet outputText = headText;\n\tlet details: ReadToolDetails | undefined;\n\tif (truncation.firstLineExceedsLimit) {\n\t\t// Show the start of the line, cut at the byte limit on a character boundary. Like `allLines[startLine]`, a\n\t\t// fractional start line names no line.\n\t\tconst integral = Number.isInteger(startLine);\n\t\tconst lineBytes = new TextEncoder().encode(integral ? (head.split(\"\\n\")[0] ?? \"\") : \"\");\n\t\tconst lineSize = integral ? scan.firstLineBytes : 0;\n\t\tconst end = characterEnd(lineBytes, DEFAULT_MAX_BYTES);\n\t\toutputText = new TextDecoder().decode(lineBytes.subarray(0, end));\n\t\tdiagnostics.push({\n\t\t\tseverity: \"warn\",\n\t\t\tcode: \"truncated\",\n\t\t\tmessage: `Line ${startLineDisplay} is ${formatSize(lineSize)}, exceeds the ${formatSize(DEFAULT_MAX_BYTES)} limit; showing its first ${formatSize(end)}. Use bash: sed -n '${startLineDisplay}p' ${path} | tail -c +${end + 1}`,\n\t\t});\n\t\tdetails = { truncation: { ...truncation, outputBytes: end, outputLines: 1 } };\n\t} else if (truncation.truncated) {\n\t\tconst endLineDisplay = startLineDisplay + truncation.outputLines - 1;\n\t\tconst nextOffset = endLineDisplay + 1;\n\t\tconst limitText = truncation.truncatedBy === \"lines\" ? \"\" : ` (${formatSize(DEFAULT_MAX_BYTES)} limit)`;\n\t\tdiagnostics.push({\n\t\t\tseverity: \"info\",\n\t\t\tcode: \"truncated\",\n\t\t\tmessage: `Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}${limitText}. Use offset=${nextOffset} to continue.`,\n\t\t});\n\t\tdetails = { truncation };\n\t} else if (userLimitedLines !== undefined && startLine + userLimitedLines < totalFileLines) {\n\t\tconst remaining = totalFileLines - (startLine + userLimitedLines);\n\t\tconst nextOffset = startLine + userLimitedLines + 1;\n\t\tdiagnostics.push({\n\t\t\tseverity: \"info\",\n\t\t\tmessage: `${remaining} more lines in file. Use offset=${nextOffset} to continue.`,\n\t\t});\n\t}\n\n\treturn {\n\t\toutput: outputText === \"\" ? [] : [{ type: \"text\" as const, text: outputText }],\n\t\t...(details === undefined ? {} : { details }),\n\t\tdiagnostics,\n\t};\n}\n"]}
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport { characterEnd } from \"@earendil-works/chord/output\";\nimport { lit, num, obj, opt, type Static, str, union } from \"@earendil-works/pi-schema\";\nimport {\n\ttype BinaryReader,\n\ttype FileInfo,\n\tgetOrThrow,\n\ttype LineScan,\n\trangeDecoder,\n\tstartsWithBom,\n} from \"../env/index.ts\";\nimport { defineTool } from \"../harness/define.ts\";\nimport type { ToolDiagnostic, ToolExecutionApi, ToolExecutionResult, ToolRegistration } from \"../harness/types.ts\";\nimport {\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\tformatSize,\n\ttype TruncationResult,\n\ttruncateHeadOf,\n\tutf8ByteLength,\n} from \"../truncate.ts\";\nimport { requireEnv } from \"./env.ts\";\nimport { detectSupportedImageMimeTypeOf, imageDimensions } from \"./image.ts\";\nimport {\n\tbase64Length,\n\tDEFAULT_IMAGE_LIMITS,\n\ttype ImageLimits,\n\ttype ImageProcessor,\n\tINLINE_IMAGE_TYPES,\n\ttoBase64,\n} from \"./image-processor.ts\";\nimport { resolveReadToolPath } from \"./path-utils.ts\";\n\nconst readSchema = obj(\n\t{\n\t\tpath: str({ description: \"Path to the file to read (relative or absolute)\" }),\n\t\toffset: opt(num({ description: \"Line number to start reading from (1-indexed)\" })),\n\t\tlimit: opt(num({ description: \"Maximum number of lines to read\" })),\n\t},\n\t{ open: true },\n);\n\nexport type ReadToolInput = Static<typeof readSchema>;\n\n/**\n * What programs such as code mode scripts get: the text of a text file, or the image block of an image, which code\n * mode's `image()` shows.\n */\nconst readStructuredOutputSchema = union([\n\tstr(),\n\tobj({ type: lit(\"image\"), data: str(), mimeType: str() }, { open: true }),\n]);\n\nexport type ReadToolDetails = {\n\t/** How the shown text was cut; the text itself is the result content. */\n\ttruncation?: Omit<TruncationResult, \"content\">;\n};\n\nconst READ_CHUNK = 64 * 1024;\n\n/** `Array.prototype.slice`'s conversion of an index: NaN is 0, other values truncate toward zero. */\nfunction sliceIndex(value: number): number {\n\treturn Number.isNaN(value) ? 0 : Math.trunc(value);\n}\n\n/**\n * The decoded start of bytes `[start, end)` of the file, decoded as part of the whole file: all of it, or enough for\n * `truncateHeadOf` (more than `DEFAULT_MAX_BYTES + 1` bytes, or `DEFAULT_MAX_LINES` newlines).\n */\nasync function readHead(\n\treader: BinaryReader,\n\tstart: number,\n\tend: number,\n\tskipBom: boolean,\n\tcontext: Context,\n): Promise<string> {\n\tconst decoder = rangeDecoder();\n\tlet text = \"\";\n\tlet newlines = 0;\n\tfor (let position = skipBom && start === 0 ? 3 : start; position < end; ) {\n\t\tconst bytes = getOrThrow(await reader.read(position, Math.min(READ_CHUNK, end - position), context));\n\t\tif (bytes.length === 0) break;\n\t\tposition += bytes.length;\n\t\tconst decoded = decoder.decode(bytes, { stream: true });\n\t\ttext += decoded;\n\t\tfor (let index = decoded.indexOf(\"\\n\"); index !== -1; index = decoded.indexOf(\"\\n\", index + 1)) newlines++;\n\t\tif (newlines >= DEFAULT_MAX_LINES || utf8ByteLength(text) > DEFAULT_MAX_BYTES + 1) return text;\n\t}\n\treturn text + decoder.decode();\n}\n\nexport type ReadToolOptions = {\n\t/** Resizes and converts images; without one, images within `DEFAULT_IMAGE_LIMITS.maxBytes` pass through as they are. */\n\treadonly images?: ImageProcessor;\n};\n\n/**\n * Reads text files and images. Remarks about truncation, continuation, and image processing are diagnostics; the content\n * is only file text, or one image. Programs get the text, or the image's `ImageContent`.\n */\nexport function createReadTool(options: ReadToolOptions = {}): ToolRegistration<typeof readSchema, ReadToolDetails> {\n\tconst formats = options.images === undefined ? \"jpg, png, gif, webp\" : \"jpg, png, gif, webp, bmp\";\n\treturn defineTool({\n\t\tname: \"read\",\n\t\tdescription: `Read the contents of a file. Supports text files and images (${formats}). 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\tstructuredOutputSchema: readStructuredOutputSchema,\n\t\tasync execute(args, api, context) {\n\t\t\tconst { path, offset, limit } = args;\n\t\t\tconst env = requireEnv(api);\n\t\t\tconst absolutePath = await resolveReadToolPath(env, path, context);\n\t\t\tconst reader = getOrThrow(await env.openBinaryReader(absolutePath, undefined, context));\n\t\t\ttry {\n\t\t\t\t// A concurrent writer can change the file between the scan and the reads. Appending (a growing log) leaves\n\t\t\t\t// the scanned text as it was; an image must be whole. Otherwise the file is read again once.\n\t\t\t\tfor (let attempt = 0; ; attempt++) {\n\t\t\t\t\tconst before = getOrThrow(await reader.info(context));\n\t\t\t\t\tconst mimeType = await detectSupportedImageMimeTypeOf({\n\t\t\t\t\t\tsize: before.size,\n\t\t\t\t\t\tread: async (position, length) => getOrThrow(await reader.read(position, length, context)),\n\t\t\t\t\t});\n\t\t\t\t\tconst result =\n\t\t\t\t\t\tmimeType === undefined\n\t\t\t\t\t\t\t? await readText(reader, path, offset, limit, context)\n\t\t\t\t\t\t\t: await readImage(\n\t\t\t\t\t\t\t\t\treader,\n\t\t\t\t\t\t\t\t\tbefore,\n\t\t\t\t\t\t\t\t\tpath,\n\t\t\t\t\t\t\t\t\tmimeType,\n\t\t\t\t\t\t\t\t\toptions.images,\n\t\t\t\t\t\t\t\t\tawait imageModel(api, context),\n\t\t\t\t\t\t\t\t\tcontext,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\tconst after = getOrThrow(await reader.info(context));\n\t\t\t\t\tconst unchanged = after.size === before.size && after.mtimeMs === before.mtimeMs;\n\t\t\t\t\tif (unchanged || (mimeType === undefined && after.size > before.size)) return result;\n\t\t\t\t\tif (attempt === 1) throw new Error(`${path} changed while it was read`);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tawait reader.close(context);\n\t\t\t}\n\t\t},\n\t});\n}\n\n/**\n * One image block, prepared by `processor` or, without one, as it is when its format is inline and it fits the limits\n * (its size is checked before reading it, its dimensions from its header); otherwise an error result saying why. What happened to the image is an `info` diagnostic, so programs get the\n * bare `ImageContent`. A model without image input sees pi-ai's placeholder instead; a diagnostic says so.\n */\nasync function readImage(\n\treader: BinaryReader,\n\tinfo: FileInfo,\n\tpath: string,\n\tmimeType: string,\n\tprocessor: ImageProcessor | undefined,\n\t{ vision, limits }: ImageModel,\n\tcontext: Context,\n): Promise<ToolExecutionResult<ReadToolDetails>> {\n\tlet image: { readonly data: string; readonly mimeType: string };\n\tconst notes: string[] = [];\n\tif (processor !== undefined) {\n\t\tconst bytes = getOrThrow(await reader.read(0, info.size, context));\n\t\tconst prepared = await processor.prepare(bytes, mimeType, limits);\n\t\tif (prepared === undefined)\n\t\t\treturn imageError(`${path} is an image (${mimeType}) that cannot be prepared for the model`);\n\t\timage = prepared;\n\t\tif (prepared.convertedFrom !== undefined)\n\t\t\tnotes.push(`Converted from ${prepared.convertedFrom} to ${prepared.mimeType}.`);\n\t\tif (prepared.resized !== undefined) {\n\t\t\tconst { from, to } = prepared.resized;\n\t\t\tconst scale = (from.width / to.width).toFixed(2);\n\t\t\tnotes.push(\n\t\t\t\t`Resized from ${from.width}x${from.height} to ${to.width}x${to.height}. Multiply coordinates by ${scale} to map them to the original.`,\n\t\t\t);\n\t\t}\n\t} else {\n\t\tif (!INLINE_IMAGE_TYPES.has(mimeType)) {\n\t\t\treturn imageError(\n\t\t\t\t`${path} is an image (${mimeType}) that needs converting, and no image processor is configured`,\n\t\t\t);\n\t\t}\n\t\tconst unshrinkable = \"and no image processor is configured to shrink it\";\n\t\tif (base64Length(info.size) > limits.maxBytes) {\n\t\t\treturn imageError(\n\t\t\t\t`${path} is an image (${mimeType}) of ${formatSize(info.size)}, too large to send, ${unshrinkable}`,\n\t\t\t);\n\t\t}\n\t\tconst bytes = getOrThrow(await reader.read(0, info.size, context));\n\t\tconst size = imageDimensions(bytes, mimeType);\n\t\tif (size === undefined) return imageError(`${path} is an image (${mimeType}) whose size cannot be read`);\n\t\tif (size.width > limits.maxWidth || size.height > limits.maxHeight) {\n\t\t\treturn imageError(\n\t\t\t\t`${path} is an image (${mimeType}) of ${size.width}x${size.height}, larger than ${limits.maxWidth}x${limits.maxHeight}, ${unshrinkable}`,\n\t\t\t);\n\t\t}\n\t\timage = { data: toBase64(bytes), mimeType };\n\t}\n\tif (!vision) notes.push(\"The current model does not support images; it sees a placeholder instead.\");\n\tconst diagnostics: ToolDiagnostic[] = [\n\t\t{ severity: \"info\", code: \"image\", message: [`Read image file [${image.mimeType}].`, ...notes].join(\" \") },\n\t];\n\tconst block = { type: \"image\" as const, data: image.data, mimeType: image.mimeType };\n\treturn { output: [block], structuredOutput: block, diagnostics };\n}\n\n/** What the conversation's model takes: whether images, and within which limits. */\ntype ImageModel = { readonly vision: boolean; readonly limits: ImageLimits };\n\n/**\n * The conversation model's image input: `DEFAULT_IMAGE_LIMITS` overridden by its `inputLimits.images.resize`. Without\n * a resolvable model, it counts as one that takes images within the defaults.\n */\nasync function imageModel(api: ToolExecutionApi, context: Context): Promise<ImageModel> {\n\tconst ref = (await api.agent(context)).model;\n\tconst model = ref === undefined ? undefined : api.models.getModel(ref.provider, ref.modelId);\n\tconst resize = model?.inputLimits?.images?.resize;\n\treturn {\n\t\tvision: model?.input.includes(\"image\") ?? true,\n\t\tlimits: {\n\t\t\tmaxWidth: resize?.maxWidth ?? DEFAULT_IMAGE_LIMITS.maxWidth,\n\t\t\tmaxHeight: resize?.maxHeight ?? DEFAULT_IMAGE_LIMITS.maxHeight,\n\t\t\tmaxBytes: resize?.maxBytes ?? DEFAULT_IMAGE_LIMITS.maxBytes,\n\t\t},\n\t};\n}\n\nfunction imageError(message: string): ToolExecutionResult<ReadToolDetails> {\n\treturn { output: [], isError: true, diagnostics: [{ severity: \"error\", code: \"unsupported_image\", message }] };\n}\n\n/**\n * The read result for the opened file. It equals decoding the whole file with `TextDecoder`, splitting it on `\\n`,\n * and bounding the selected lines with `truncateHead`, while reading only one scan's worth of the file plus the head.\n */\nasync function readText(\n\treader: BinaryReader,\n\tpath: string,\n\toffset: number | undefined,\n\tlimit: number | undefined,\n\tcontext: Context,\n) {\n\tconst startLine = offset ? Math.max(0, offset - 1) : 0;\n\tconst startLineDisplay = startLine + 1;\n\t// Lines are selected like `allLines.slice(startLine, endLine)`, which truncates fractional indices.\n\tconst sliceStart = sliceIndex(startLine);\n\t// One pass finds the line count and the selection; a selection past the last line ends with it, as `slice` does,\n\t// and an empty one (a zero or negative limit) is scanned as one line and then ignored.\n\t// A start beyond any file is scanned from 0 only to count lines; the offset check below then fails as before.\n\tconst scanStart = Number.isSafeInteger(sliceStart) ? sliceStart : 0;\n\tconst requestedEnd = limit === undefined ? undefined : Math.max(scanStart + 1, sliceIndex(startLine + limit));\n\tconst scanEnd = requestedEnd !== undefined && Number.isSafeInteger(requestedEnd) ? requestedEnd : undefined;\n\tconst scanOf = async (endLine: number | undefined): Promise<LineScan> =>\n\t\tgetOrThrow(\n\t\t\tawait reader.scanLines({ startLine: scanStart, ...(endLine === undefined ? {} : { endLine }) }, context),\n\t\t);\n\tlet scan = await scanOf(scanEnd);\n\tconst totalFileLines = scan.newlines + 1;\n\tif (startLine >= totalFileLines) {\n\t\tthrow new Error(`Offset ${offset} is beyond end of file (${totalFileLines} lines total)`);\n\t}\n\n\tlet userLimitedLines: number | undefined;\n\tlet selectedLineCount = totalFileLines - sliceStart;\n\tif (limit !== undefined) {\n\t\tconst endLine = Math.min(startLine + limit, totalFileLines);\n\t\tuserLimitedLines = endLine - startLine;\n\t\t// `slice` counts a negative end from the end of the lines, which only the line count tells; scan again for it.\n\t\tconst relativeEnd = sliceIndex(endLine);\n\t\tconst sliceEnd = relativeEnd < 0 ? Math.max(totalFileLines + relativeEnd, 0) : relativeEnd;\n\t\tselectedLineCount = Math.max(0, sliceEnd - sliceStart);\n\t\tif (selectedLineCount > 0 && relativeEnd < 0) scan = await scanOf(sliceEnd);\n\t}\n\tconst empty = selectedLineCount === 0;\n\t// Counted like `truncateHead`: a trailing newline adds no line, and empty text has none.\n\tconst endsWithNewline = !empty && scan.lastLineStart === scan.end && scan.lastLineStart > scan.start;\n\tconst totals = {\n\t\tlines: empty || scan.selectedBytes === 0 ? 0 : selectedLineCount - (endsWithNewline ? 1 : 0),\n\t\tbytes: empty ? 0 : scan.selectedBytes,\n\t};\n\tconst firstBytes = getOrThrow(await reader.read(0, 3, context));\n\tconst head = empty ? \"\" : await readHead(reader, scan.start, scan.end, startsWithBom(firstBytes), context);\n\n\tconst { content: headText, ...truncation } = truncateHeadOf(head, totals);\n\tconst diagnostics: ToolDiagnostic[] = [];\n\tlet outputText = headText;\n\tlet details: ReadToolDetails | undefined;\n\tif (truncation.firstLineExceedsLimit) {\n\t\t// Show the start of the line, cut at the byte limit on a character boundary. Like `allLines[startLine]`, a\n\t\t// fractional start line names no line.\n\t\tconst integral = Number.isInteger(startLine);\n\t\tconst lineBytes = new TextEncoder().encode(integral ? (head.split(\"\\n\")[0] ?? \"\") : \"\");\n\t\tconst lineSize = integral ? scan.firstLineBytes : 0;\n\t\tconst end = characterEnd(lineBytes, DEFAULT_MAX_BYTES);\n\t\toutputText = new TextDecoder().decode(lineBytes.subarray(0, end));\n\t\tdiagnostics.push({\n\t\t\tseverity: \"warn\",\n\t\t\tcode: \"truncated\",\n\t\t\tmessage: `Line ${startLineDisplay} is ${formatSize(lineSize)}, exceeds the ${formatSize(DEFAULT_MAX_BYTES)} limit; showing its first ${formatSize(end)}. Use bash: sed -n '${startLineDisplay}p' ${path} | tail -c +${end + 1}`,\n\t\t});\n\t\tdetails = { truncation: { ...truncation, outputBytes: end, outputLines: 1 } };\n\t} else if (truncation.truncated) {\n\t\tconst endLineDisplay = startLineDisplay + truncation.outputLines - 1;\n\t\tconst nextOffset = endLineDisplay + 1;\n\t\tconst limitText = truncation.truncatedBy === \"lines\" ? \"\" : ` (${formatSize(DEFAULT_MAX_BYTES)} limit)`;\n\t\tdiagnostics.push({\n\t\t\tseverity: \"info\",\n\t\t\tcode: \"truncated\",\n\t\t\tmessage: `Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}${limitText}. Use offset=${nextOffset} to continue.`,\n\t\t});\n\t\tdetails = { truncation };\n\t} else if (userLimitedLines !== undefined && startLine + userLimitedLines < totalFileLines) {\n\t\tconst remaining = totalFileLines - (startLine + userLimitedLines);\n\t\tconst nextOffset = startLine + userLimitedLines + 1;\n\t\tdiagnostics.push({\n\t\t\tseverity: \"info\",\n\t\t\tmessage: `${remaining} more lines in file. Use offset=${nextOffset} to continue.`,\n\t\t});\n\t}\n\n\treturn {\n\t\toutput: outputText === \"\" ? [] : [{ type: \"text\" as const, text: outputText }],\n\t\tstructuredOutput: outputText,\n\t\t...(details === undefined ? {} : { details }),\n\t\tdiagnostics,\n\t};\n}\n"]}
@@ -1,8 +1,8 @@
1
- import { type Static, Type } from "typebox";
1
+ import { type Static } from "@earendil-works/pi-schema";
2
2
  import type { ToolRegistration } from "../harness/types.ts";
3
- declare const writeSchema: Type.TObject<{
4
- path: Type.TString;
5
- content: Type.TString;
3
+ declare const writeSchema: import("@earendil-works/pi-schema").Schema<{
4
+ path: string;
5
+ content: string;
6
6
  }>;
7
7
  export type WriteToolInput = Static<typeof writeSchema>;
8
8
  export declare function createWriteTool(): ToolRegistration<typeof writeSchema>;
@@ -1 +1 @@
1
- {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/tools/write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAK5D,QAAA,MAAM,WAAW;;;EAGf,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AAExD,wBAAgB,eAAe,IAAI,gBAAgB,CAAC,OAAO,WAAW,CAAC,CAuBtE"}
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/tools/write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,MAAM,EAAO,MAAM,2BAA2B,CAAC;AAGlE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAK5D,QAAA,MAAM,WAAW;;;EAMhB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,WAAW,CAAC,CAAC;AAExD,wBAAgB,eAAe,IAAI,gBAAgB,CAAC,OAAO,WAAW,CAAC,CAuBtE"}
@@ -1,13 +1,13 @@
1
- import { Type } from "typebox";
1
+ import { obj, str } from "@earendil-works/pi-schema";
2
2
  import { getOrThrow } from "../env/index.js";
3
3
  import { defineTool } from "../harness/define.js";
4
4
  import { requireEnv } from "./env.js";
5
5
  import { withFileMutationQueue } from "./file-mutation-queue.js";
6
6
  import { resolveToolPath } from "./path-utils.js";
7
- const writeSchema = Type.Object({
8
- path: Type.String({ description: "Path to the file to write (relative or absolute)" }),
9
- content: Type.String({ description: "Content to write to the file" }),
10
- });
7
+ const writeSchema = obj({
8
+ path: str({ description: "Path to the file to write (relative or absolute)" }),
9
+ content: str({ description: "Content to write to the file" }),
10
+ }, { open: true });
11
11
  export function createWriteTool() {
12
12
  return defineTool({
13
13
  name: "write",
@@ -1 +1 @@
1
- {"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/tools/write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC;IAC/B,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC;IACtF,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;CACrE,CAAC,CAAC;AAIH,MAAM,UAAU,eAAe;IAC9B,OAAO,UAAU,CAAC;QACjB,IAAI,EAAE,OAAO;QACb,WAAW,EACV,iIAAiI;QAClI,UAAU,EAAE,WAAW;QACvB,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO;YAC/B,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;YAC/B,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;YAC5B,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;YAC/D,OAAO,qBAAqB,CAC3B,GAAG,EACH,YAAY,EACZ,KAAK,IAAI,EAAE;gBACV,IAAI,OAAO,CAAC,WAAW,EAAE,OAAO;oBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;gBACvE,UAAU,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;gBAChE,IAAI,OAAO,CAAC,WAAW,EAAE,OAAO;oBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;gBACvE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;YAC9E,CAAC,EACD,OAAO,CACP,CAAC;QACH,CAAC;KACD,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { type Static, Type } from \"typebox\";\nimport { getOrThrow } from \"../env/index.ts\";\nimport { defineTool } from \"../harness/define.ts\";\nimport type { ToolRegistration } from \"../harness/types.ts\";\nimport { requireEnv } from \"./env.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport { resolveToolPath } from \"./path-utils.ts\";\n\nconst writeSchema = Type.Object({\n\tpath: Type.String({ description: \"Path to the file to write (relative or absolute)\" }),\n\tcontent: Type.String({ description: \"Content to write to the file\" }),\n});\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\nexport function createWriteTool(): ToolRegistration<typeof writeSchema> {\n\treturn defineTool({\n\t\tname: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tparameters: writeSchema,\n\t\tasync execute(args, api, context) {\n\t\t\tconst { path, content } = args;\n\t\t\tconst env = requireEnv(api);\n\t\t\tconst absolutePath = await resolveToolPath(env, path, context);\n\t\t\treturn withFileMutationQueue(\n\t\t\t\tenv,\n\t\t\t\tabsolutePath,\n\t\t\t\tasync () => {\n\t\t\t\t\tif (context.abortSignal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\t\t\tgetOrThrow(await env.writeFile(absolutePath, content, context));\n\t\t\t\t\tif (context.abortSignal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\t\t\treturn { output: [{ type: \"text\", text: `Successfully wrote to ${path}` }] };\n\t\t\t\t},\n\t\t\t\tcontext,\n\t\t\t);\n\t\t},\n\t});\n}\n"]}
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/tools/write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAe,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,WAAW,GAAG,GAAG,CACtB;IACC,IAAI,EAAE,GAAG,CAAC,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC;IAC9E,OAAO,EAAE,GAAG,CAAC,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;CAC7D,EACD,EAAE,IAAI,EAAE,IAAI,EAAE,CACd,CAAC;AAIF,MAAM,UAAU,eAAe;IAC9B,OAAO,UAAU,CAAC;QACjB,IAAI,EAAE,OAAO;QACb,WAAW,EACV,iIAAiI;QAClI,UAAU,EAAE,WAAW;QACvB,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO;YAC/B,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;YAC/B,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;YAC5B,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;YAC/D,OAAO,qBAAqB,CAC3B,GAAG,EACH,YAAY,EACZ,KAAK,IAAI,EAAE;gBACV,IAAI,OAAO,CAAC,WAAW,EAAE,OAAO;oBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;gBACvE,UAAU,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;gBAChE,IAAI,OAAO,CAAC,WAAW,EAAE,OAAO;oBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;gBACvE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;YAC9E,CAAC,EACD,OAAO,CACP,CAAC;QACH,CAAC;KACD,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { obj, type Static, str } from \"@earendil-works/pi-schema\";\nimport { getOrThrow } from \"../env/index.ts\";\nimport { defineTool } from \"../harness/define.ts\";\nimport type { ToolRegistration } from \"../harness/types.ts\";\nimport { requireEnv } from \"./env.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport { resolveToolPath } from \"./path-utils.ts\";\n\nconst writeSchema = obj(\n\t{\n\t\tpath: str({ description: \"Path to the file to write (relative or absolute)\" }),\n\t\tcontent: str({ description: \"Content to write to the file\" }),\n\t},\n\t{ open: true },\n);\n\nexport type WriteToolInput = Static<typeof writeSchema>;\n\nexport function createWriteTool(): ToolRegistration<typeof writeSchema> {\n\treturn defineTool({\n\t\tname: \"write\",\n\t\tdescription:\n\t\t\t\"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.\",\n\t\tparameters: writeSchema,\n\t\tasync execute(args, api, context) {\n\t\t\tconst { path, content } = args;\n\t\t\tconst env = requireEnv(api);\n\t\t\tconst absolutePath = await resolveToolPath(env, path, context);\n\t\t\treturn withFileMutationQueue(\n\t\t\t\tenv,\n\t\t\t\tabsolutePath,\n\t\t\t\tasync () => {\n\t\t\t\t\tif (context.abortSignal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\t\t\tgetOrThrow(await env.writeFile(absolutePath, content, context));\n\t\t\t\t\tif (context.abortSignal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\t\t\treturn { output: [{ type: \"text\", text: `Successfully wrote to ${path}` }] };\n\t\t\t\t},\n\t\t\t\tcontext,\n\t\t\t);\n\t\t},\n\t});\n}\n"]}