@earendil-works/pi-durable 1.5.0 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +17 -4
- package/dist/codemode/codemode.d.ts +2 -1
- package/dist/codemode/codemode.d.ts.map +1 -1
- package/dist/codemode/codemode.js +1 -37
- package/dist/codemode/codemode.js.map +1 -1
- package/dist/codemode/models.d.ts +2 -1
- package/dist/codemode/models.d.ts.map +1 -1
- package/dist/codemode/models.js.map +1 -1
- package/dist/documents.d.ts +1 -1
- package/dist/documents.d.ts.map +1 -1
- package/dist/documents.js +1 -1
- package/dist/documents.js.map +1 -1
- package/dist/entries.d.ts +1 -1
- package/dist/entries.d.ts.map +1 -1
- package/dist/entries.js.map +1 -1
- package/dist/env/index.d.ts +1 -1
- package/dist/env/index.d.ts.map +1 -1
- package/dist/env/index.js.map +1 -1
- package/dist/env/node-watch.d.ts +1 -1
- package/dist/env/node-watch.d.ts.map +1 -1
- package/dist/env/node-watch.js.map +1 -1
- package/dist/env/node.d.ts +1 -1
- package/dist/env/node.d.ts.map +1 -1
- package/dist/env/node.js.map +1 -1
- package/dist/harness/agent.d.ts.map +1 -1
- package/dist/harness/agent.js +27 -1
- package/dist/harness/agent.js.map +1 -1
- package/dist/harness/compaction.d.ts +8 -3
- package/dist/harness/compaction.d.ts.map +1 -1
- package/dist/harness/compaction.js +21 -9
- package/dist/harness/compaction.js.map +1 -1
- package/dist/harness/context.d.ts +1 -1
- package/dist/harness/context.d.ts.map +1 -1
- package/dist/harness/context.js.map +1 -1
- package/dist/harness/define.d.ts +1 -1
- package/dist/harness/define.d.ts.map +1 -1
- package/dist/harness/define.js.map +1 -1
- package/dist/harness/generation.d.ts +1 -1
- package/dist/harness/generation.d.ts.map +1 -1
- package/dist/harness/generation.js +6 -4
- package/dist/harness/generation.js.map +1 -1
- package/dist/harness/harness.d.ts +1 -1
- package/dist/harness/harness.d.ts.map +1 -1
- package/dist/harness/harness.js.map +1 -1
- package/dist/harness/inbox.d.ts +2 -1
- package/dist/harness/inbox.d.ts.map +1 -1
- package/dist/harness/inbox.js +1 -1
- package/dist/harness/inbox.js.map +1 -1
- package/dist/harness/json.d.ts +1 -1
- package/dist/harness/json.d.ts.map +1 -1
- package/dist/harness/json.js.map +1 -1
- package/dist/harness/live.d.ts +2 -1
- package/dist/harness/live.d.ts.map +1 -1
- package/dist/harness/live.js.map +1 -1
- package/dist/harness/partial-changes.d.ts +1 -1
- package/dist/harness/partial-changes.d.ts.map +1 -1
- package/dist/harness/partial-changes.js +1 -0
- package/dist/harness/partial-changes.js.map +1 -1
- package/dist/harness/prompt.d.ts +1 -1
- package/dist/harness/prompt.d.ts.map +1 -1
- package/dist/harness/prompt.js.map +1 -1
- package/dist/harness/provider.d.ts +1 -1
- package/dist/harness/provider.d.ts.map +1 -1
- package/dist/harness/provider.js.map +1 -1
- package/dist/harness/registry-reader.d.ts +1 -1
- package/dist/harness/registry-reader.d.ts.map +1 -1
- package/dist/harness/registry-reader.js.map +1 -1
- package/dist/harness/scheduler.d.ts +2 -1
- package/dist/harness/scheduler.d.ts.map +1 -1
- package/dist/harness/scheduler.js +42 -13
- package/dist/harness/scheduler.js.map +1 -1
- package/dist/harness/submissions.d.ts +1 -1
- package/dist/harness/submissions.d.ts.map +1 -1
- package/dist/harness/submissions.js +1 -1
- package/dist/harness/submissions.js.map +1 -1
- package/dist/harness/task-graph.d.ts +3 -1
- package/dist/harness/task-graph.d.ts.map +1 -1
- package/dist/harness/task-graph.js +1 -1
- package/dist/harness/task-graph.js.map +1 -1
- package/dist/harness/tool.d.ts +1 -1
- package/dist/harness/tool.d.ts.map +1 -1
- package/dist/harness/tool.js +1 -1
- package/dist/harness/tool.js.map +1 -1
- package/dist/harness/transcript.d.ts +1 -1
- package/dist/harness/transcript.d.ts.map +1 -1
- package/dist/harness/transcript.js.map +1 -1
- package/dist/harness/types.d.ts +21 -4
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/usage.d.ts +2 -1
- package/dist/harness/usage.d.ts.map +1 -1
- package/dist/harness/usage.js +1 -1
- package/dist/harness/usage.js.map +1 -1
- package/dist/harness/util.d.ts +1 -1
- package/dist/harness/util.d.ts.map +1 -1
- package/dist/harness/util.js.map +1 -1
- package/dist/harness/view.d.ts +2 -1
- package/dist/harness/view.d.ts.map +1 -1
- package/dist/harness/view.js +1 -1
- package/dist/harness/view.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/session/cached-storage.d.ts +2 -1
- package/dist/session/cached-storage.d.ts.map +1 -1
- package/dist/session/cached-storage.js.map +1 -1
- package/dist/session/forks.d.ts +1 -1
- package/dist/session/forks.d.ts.map +1 -1
- package/dist/session/forks.js.map +1 -1
- package/dist/session/keys.d.ts +2 -1
- package/dist/session/keys.d.ts.map +1 -1
- package/dist/session/keys.js.map +1 -1
- package/dist/session/session.d.ts +3 -1
- package/dist/session/session.d.ts.map +1 -1
- package/dist/session/session.js +1 -1
- package/dist/session/session.js.map +1 -1
- package/dist/session/transaction.d.ts +4 -3
- package/dist/session/transaction.d.ts.map +1 -1
- package/dist/session/transaction.js +3 -3
- package/dist/session/transaction.js.map +1 -1
- package/dist/session/writer.d.ts +1 -1
- package/dist/session/writer.d.ts.map +1 -1
- package/dist/session/writer.js.map +1 -1
- package/dist/storage/indexeddb.d.ts +2 -1
- package/dist/storage/indexeddb.d.ts.map +1 -1
- package/dist/storage/indexeddb.js.map +1 -1
- package/dist/storage/jsonl/index.d.ts +1 -1
- package/dist/storage/jsonl/index.d.ts.map +1 -1
- package/dist/storage/jsonl/index.js +1 -1
- package/dist/storage/jsonl/index.js.map +1 -1
- package/dist/storage/jsonl/node.d.ts +3 -3
- package/dist/storage/jsonl/node.d.ts.map +1 -1
- package/dist/storage/jsonl/node.js +2 -2
- package/dist/storage/jsonl/node.js.map +1 -1
- package/dist/storage/jsonl/storage.d.ts +32 -3
- package/dist/storage/jsonl/storage.d.ts.map +1 -1
- package/dist/storage/jsonl/storage.js +70 -19
- package/dist/storage/jsonl/storage.js.map +1 -1
- package/dist/storage/memory.d.ts +2 -1
- package/dist/storage/memory.d.ts.map +1 -1
- package/dist/storage/memory.js.map +1 -1
- package/dist/storage/sqlite/commit.d.ts +52 -0
- package/dist/storage/sqlite/commit.d.ts.map +1 -0
- package/dist/storage/sqlite/commit.js +371 -0
- package/dist/storage/sqlite/commit.js.map +1 -0
- package/dist/storage/sqlite/rows.d.ts +33 -0
- package/dist/storage/sqlite/rows.d.ts.map +1 -0
- package/dist/storage/sqlite/rows.js +53 -0
- package/dist/storage/sqlite/rows.js.map +1 -0
- package/dist/storage/sqlite/statements.d.ts +13 -0
- package/dist/storage/sqlite/statements.d.ts.map +1 -0
- package/dist/storage/sqlite/statements.js +88 -0
- package/dist/storage/sqlite/statements.js.map +1 -0
- package/dist/storage/sqlite/storage.d.ts +12 -11
- package/dist/storage/sqlite/storage.d.ts.map +1 -1
- package/dist/storage/sqlite/storage.js +44 -259
- package/dist/storage/sqlite/storage.js.map +1 -1
- package/dist/storage/store/commit.d.ts +2 -2
- package/dist/storage/store/commit.d.ts.map +1 -1
- package/dist/storage/store/commit.js +1 -1
- package/dist/storage/store/commit.js.map +1 -1
- package/dist/storage/store/documents.d.ts +1 -1
- package/dist/storage/store/documents.d.ts.map +1 -1
- package/dist/storage/store/documents.js +1 -1
- package/dist/storage/store/documents.js.map +1 -1
- package/dist/storage/store/queries.d.ts +2 -2
- package/dist/storage/store/queries.d.ts.map +1 -1
- package/dist/storage/store/queries.js.map +1 -1
- package/dist/storage/store/storage.d.ts +3 -2
- package/dist/storage/store/storage.d.ts.map +1 -1
- package/dist/storage/store/storage.js +1 -1
- package/dist/storage/store/storage.js.map +1 -1
- package/dist/storage/store/tables.d.ts +31 -31
- package/dist/storage/store/tables.d.ts.map +1 -1
- package/dist/storage/store/tables.js +1 -1
- package/dist/storage/store/tables.js.map +1 -1
- package/dist/testing/commit-writes.d.ts +1 -1
- package/dist/testing/commit-writes.d.ts.map +1 -1
- package/dist/testing/commit-writes.js.map +1 -1
- package/dist/testing/env-conformance.js.map +1 -1
- package/dist/testing/storage-benchmark.js.map +1 -1
- package/dist/testing/storage-conformance.js.map +1 -1
- package/dist/tool-search/tool-search.d.ts +2 -2
- package/dist/tool-search/tool-search.d.ts.map +1 -1
- package/dist/tool-search/tool-search.js +18 -6
- package/dist/tool-search/tool-search.js.map +1 -1
- package/dist/tools/bash.d.ts +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/file-mutation-queue.d.ts +1 -1
- package/dist/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/tools/file-mutation-queue.js.map +1 -1
- package/dist/tools/path-utils.d.ts +1 -1
- package/dist/tools/path-utils.d.ts.map +1 -1
- package/dist/tools/path-utils.js.map +1 -1
- package/dist/tools/read.js.map +1 -1
- package/dist/types.d.ts +6 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +6 -6
|
@@ -8,10 +8,10 @@ export type ToolSearchToolDetails = {
|
|
|
8
8
|
export declare function createToolSearchTool(): ToolRegistration<import("@earendil-works/pi-schema").Schema<{
|
|
9
9
|
query: string;
|
|
10
10
|
limit?: number | undefined;
|
|
11
|
-
}>, import("@earendil-works/chord").JsonValue>;
|
|
11
|
+
}>, import("@earendil-works/chord/json").JsonValue>;
|
|
12
12
|
/** The `tool_search` extension: install it and select it where the model should find deferred tools. */
|
|
13
13
|
export declare function createToolSearchExtension(): import("../harness/types.ts").Extension<ToolRegistration<import("@earendil-works/pi-schema").Schema<{
|
|
14
14
|
query: string;
|
|
15
15
|
limit?: number | undefined;
|
|
16
|
-
}>, import("@earendil-works/chord").JsonValue>>;
|
|
16
|
+
}>, import("@earendil-works/chord/json").JsonValue>>;
|
|
17
17
|
//# sourceMappingURL=tool-search.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-search.d.ts","sourceRoot":"","sources":["../../src/tool-search/tool-search.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tool-search.d.ts","sourceRoot":"","sources":["../../src/tool-search/tool-search.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,eAAO,MAAM,qBAAqB,gBAAgB,CAAC;AACnD,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAa3C,yDAAyD;AACzD,MAAM,MAAM,qBAAqB,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAyCzD,wBAAgB,oBAAoB;;;oDAgCnC;AAED,wGAAwG;AACxG,wBAAgB,yBAAyB;;;qDAExC"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* and the matches are loaded with `control.addTools`, so the next request offers them. Loading writes the
|
|
4
4
|
* conversation's `modelTools` (its `add`), so loaded tools stay offered on that branch, also after a compaction.
|
|
5
5
|
*/
|
|
6
|
+
import { resolveSchemaRef } from "@earendil-works/pi-codemode/declarations";
|
|
6
7
|
import { num, obj, opt, str } from "@earendil-works/pi-schema";
|
|
7
8
|
import { defineExtension, defineTool } from "../harness/define.js";
|
|
8
9
|
import { rankDocuments } from "../search.js";
|
|
@@ -17,30 +18,41 @@ const toolSearchSchema = obj({
|
|
|
17
18
|
function isObject(value) {
|
|
18
19
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19
20
|
}
|
|
20
|
-
/**
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Schema descriptions and property names, recursively, following local `$ref`s into `root`. `seen` holds the
|
|
23
|
+
* subschemas already reached through a reference, so recursive and shared definitions are read once, and
|
|
24
|
+
* definitions no property references are not read.
|
|
25
|
+
*/
|
|
26
|
+
function schemaText(schema, root, parts, seen) {
|
|
22
27
|
if (!isObject(schema))
|
|
23
28
|
return;
|
|
29
|
+
if (typeof schema.$ref === "string") {
|
|
30
|
+
const target = resolveSchemaRef(schema.$ref, root);
|
|
31
|
+
if (isObject(target) && !seen.has(target)) {
|
|
32
|
+
seen.add(target);
|
|
33
|
+
schemaText(target, root, parts, seen);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
24
36
|
if (typeof schema.description === "string")
|
|
25
37
|
parts.push(schema.description);
|
|
26
38
|
if (isObject(schema.properties)) {
|
|
27
39
|
for (const [name, property] of Object.entries(schema.properties)) {
|
|
28
40
|
parts.push(name);
|
|
29
|
-
schemaText(property, parts);
|
|
41
|
+
schemaText(property, root, parts, seen);
|
|
30
42
|
}
|
|
31
43
|
}
|
|
32
|
-
schemaText(schema.items, parts);
|
|
44
|
+
schemaText(schema.items, root, parts, seen);
|
|
33
45
|
for (const key of ["anyOf", "oneOf", "allOf"]) {
|
|
34
46
|
const variants = schema[key];
|
|
35
47
|
if (Array.isArray(variants))
|
|
36
48
|
for (const variant of variants)
|
|
37
|
-
schemaText(variant, parts);
|
|
49
|
+
schemaText(variant, root, parts, seen);
|
|
38
50
|
}
|
|
39
51
|
}
|
|
40
52
|
/** Search text of a tool: its description and its parameters' names and descriptions; the ranker reads the name. */
|
|
41
53
|
function searchText(tool) {
|
|
42
54
|
const parts = [tool.description];
|
|
43
|
-
schemaText(tool.parameters, parts);
|
|
55
|
+
schemaText(tool.parameters, tool.parameters, parts, new Set([tool.parameters]));
|
|
44
56
|
return parts.filter((part) => part.trim() !== "").join(" ");
|
|
45
57
|
}
|
|
46
58
|
export function createToolSearchTool() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-search.js","sourceRoot":"","sources":["../../src/tool-search/tool-search.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEnE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC;AACnD,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAE3C,4GAA4G;AAC5G,MAAM,uBAAuB,GAAG,yPAAyP,qBAAqB,8EAA8E,qBAAqB,KAAK,CAAC;AAEvZ,MAAM,gBAAgB,GAAG,GAAG,CAC3B;IACC,KAAK,EAAE,GAAG,CAAC,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;IAC/D,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,kDAAkD,yBAAyB,GAAG,EAAE,CAAC,CAAC;CAChH,EACD,EAAE,IAAI,EAAE,IAAI,EAAE,CACd,CAAC;AAKF,SAAS,QAAQ,CAAC,KAAc;IAC/B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"tool-search.js","sourceRoot":"","sources":["../../src/tool-search/tool-search.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The `tool_search` tool, a copy of the coding agent's: the model searches the deferred tools it is not offered yet,\n * and the matches are loaded with `control.addTools`, so the next request offers them. Loading writes the\n * conversation's `modelTools` (its `add`), so loaded tools stay offered on that branch, also after a compaction.\n */\nimport { resolveSchemaRef } from \"@earendil-works/pi-codemode/declarations\";\nimport { num, obj, opt, str } from \"@earendil-works/pi-schema\";\nimport { defineExtension, defineTool } from \"../harness/define.ts\";\nimport type { ToolRegistration } from \"../harness/types.ts\";\nimport { rankDocuments } from \"../search.ts\";\n\nexport const TOOL_SEARCH_TOOL_NAME = \"tool_search\";\nexport const DEFAULT_TOOL_SEARCH_LIMIT = 8;\n\n/** Fixed, so it does not change while deferred tools are registered, for example as MCP servers connect. */\nconst TOOL_SEARCH_DESCRIPTION = `# Tool discovery\\n\\nSearches over deferred tool metadata with BM25 and exposes matching tools for the next model call.\\n\\nSome of the tools, such as tools of MCP servers, may not have been provided to you upfront, and you should use this tool (\\`${TOOL_SEARCH_TOOL_NAME}\\`) to search for the required tools. For MCP tool discovery, always use \\`${TOOL_SEARCH_TOOL_NAME}\\`.`;\n\nconst toolSearchSchema = obj(\n\t{\n\t\tquery: str({ description: \"Search query for deferred tools.\" }),\n\t\tlimit: opt(num({ description: `Maximum number of tools to return. Defaults to ${DEFAULT_TOOL_SEARCH_LIMIT}.` })),\n\t},\n\t{ open: true },\n);\n\n/** `tool_search` result details: the tools it loaded. */\nexport type ToolSearchToolDetails = { loaded: string[] };\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Schema descriptions and property names, recursively, following local `$ref`s into `root`. `seen` holds the\n * subschemas already reached through a reference, so recursive and shared definitions are read once, and\n * definitions no property references are not read.\n */\nfunction schemaText(schema: unknown, root: unknown, parts: string[], seen: Set<unknown>): void {\n\tif (!isObject(schema)) return;\n\tif (typeof schema.$ref === \"string\") {\n\t\tconst target = resolveSchemaRef(schema.$ref, root);\n\t\tif (isObject(target) && !seen.has(target)) {\n\t\t\tseen.add(target);\n\t\t\tschemaText(target, root, parts, seen);\n\t\t}\n\t}\n\tif (typeof schema.description === \"string\") parts.push(schema.description);\n\tif (isObject(schema.properties)) {\n\t\tfor (const [name, property] of Object.entries(schema.properties)) {\n\t\t\tparts.push(name);\n\t\t\tschemaText(property, root, parts, seen);\n\t\t}\n\t}\n\tschemaText(schema.items, root, parts, seen);\n\tfor (const key of [\"anyOf\", \"oneOf\", \"allOf\"]) {\n\t\tconst variants = schema[key];\n\t\tif (Array.isArray(variants)) for (const variant of variants) schemaText(variant, root, parts, seen);\n\t}\n}\n\n/** Search text of a tool: its description and its parameters' names and descriptions; the ranker reads the name. */\nfunction searchText(tool: ToolRegistration): string {\n\tconst parts = [tool.description];\n\tschemaText(tool.parameters, tool.parameters, parts, new Set([tool.parameters]));\n\treturn parts.filter((part) => part.trim() !== \"\").join(\" \");\n}\n\nexport function createToolSearchTool() {\n\treturn defineTool({\n\t\tname: TOOL_SEARCH_TOOL_NAME,\n\t\tdescription: TOOL_SEARCH_DESCRIPTION,\n\t\tparameters: toolSearchSchema,\n\t\t// Searching changes what the model is offered; scripts have their own `searchTools()`.\n\t\tcallers: [\"model\"],\n\t\treplay: \"safe\",\n\t\tasync execute({ query, limit }, api, context) {\n\t\t\tif (query.trim() === \"\") throw new Error(\"query must not be empty\");\n\t\t\tconst max = limit ?? DEFAULT_TOOL_SEARCH_LIMIT;\n\t\t\tif (!Number.isInteger(max) || max <= 0) throw new Error(\"limit must be a positive integer\");\n\t\t\tconst candidates = (await api.agent(context)).deferred;\n\t\t\tconst documents = candidates.map((tool) => ({ name: tool.name, text: searchText(tool) }));\n\t\t\tconst loaded = rankDocuments(query, documents, max);\n\t\t\tconst text =\n\t\t\t\tloaded.length === 0\n\t\t\t\t\t? \"No matching tools found.\"\n\t\t\t\t\t: `Loaded ${loaded.length} tool${loaded.length === 1 ? \"\" : \"s\"}. They are available from your next call:\\n${loaded\n\t\t\t\t\t\t\t.map((name) => {\n\t\t\t\t\t\t\t\tconst description = candidates.find((tool) => tool.name === name)?.description ?? \"\";\n\t\t\t\t\t\t\t\treturn `- ${name}: ${description.trim().split(/\\r?\\n/)[0]}`;\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.join(\"\\n\")}`;\n\t\t\tconst details: ToolSearchToolDetails = { loaded };\n\t\t\treturn {\n\t\t\t\toutput: [{ type: \"text\", text }],\n\t\t\t\tdetails,\n\t\t\t\t...(loaded.length === 0 ? {} : { control: { addTools: loaded } }),\n\t\t\t};\n\t\t},\n\t});\n}\n\n/** The `tool_search` extension: install it and select it where the model should find deferred tools. */\nexport function createToolSearchExtension() {\n\treturn defineExtension({ name: \"tool-search\", tools: [createToolSearchTool()] });\n}\n"]}
|
package/dist/tools/bash.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Context } from "@earendil-works/chord";
|
|
1
|
+
import type { Context } from "@earendil-works/chord/context";
|
|
2
2
|
import { type Static } from "@earendil-works/pi-schema";
|
|
3
3
|
import type { ToolExecutionApi, ToolRegistration } from "../harness/types.ts";
|
|
4
4
|
declare const bashSchema: import("@earendil-works/pi-schema").Schema<{
|
package/dist/tools/bash.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../src/tools/bash.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../src/tools/bash.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAuB,KAAK,MAAM,EAAO,MAAM,2BAA2B,CAAC;AAElF,OAAO,KAAK,EAAE,gBAAgB,EAAuB,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAOnG,QAAA,MAAM,UAAU;;;EAMf,CAAC;AAEF,QAAA,MAAM,gBAAgB;;;EAMrB,CAAC;AAEF,iEAAiE;AACjE,QAAA,MAAM,2BAA2B;;;;;EAQhC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AACtD,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAClE,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAE/E,6GAA6G;AAC7G,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,GAAG,CAAC,SAAS,EAAE,aAAa,EAAE,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEtH,MAAM,WAAW,eAAe;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACrC,qCAAqC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,sHAAsH;IACtH,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAqFD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,gBAAgB,CAAC,OAAO,UAAU,CAAC,CAa7F;AAMD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,gBAAgB,CAAC,OAAO,gBAAgB,CAAC,CAgB/G"}
|
package/dist/tools/bash.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../src/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport { bool, num, obj, opt, type Static, str } from \"@earendil-works/pi-schema\";\nimport { defineTool } from \"../harness/define.ts\";\nimport type { ToolExecutionApi, ToolExecutionResult, ToolRegistration } from \"../harness/types.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES } from \"../truncate.ts\";\nimport { requireEnv } from \"./env.ts\";\n\nconst MAX_TIMEOUT_SECONDS = 2_147_483_647 / 1000;\n\n// Open objects, as the tools accepted extra arguments when their schemas were TypeBox's.\nconst bashSchema = obj(\n\t{\n\t\tcommand: str({ description: \"Bash command to execute\" }),\n\t\ttimeout: opt(num({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n\t},\n\t{ open: true },\n);\n\nconst powershellSchema = obj(\n\t{\n\t\tcommand: str({ description: \"PowerShell command to execute\" }),\n\t\ttimeout: opt(num({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n\t},\n\t{ open: true },\n);\n\n/** What a program that calls `bash` or `powershell` receives. */\nconst shellStructuredOutputSchema = obj(\n\t{\n\t\toutput: str({ description: \"Combined stdout and stderr: the retained tail, as the model sees it\" }),\n\t\ttruncated: bool({ description: \"Whether earlier output was dropped\" }),\n\t\tfullOutputPath: opt(str({ description: \"File with the complete output, when it was spilled\" })),\n\t\texitCode: num(),\n\t},\n\t{ open: true },\n);\n\nexport type BashToolInput = Static<typeof bashSchema>;\nexport type PowerShellToolInput = Static<typeof powershellSchema>;\nexport type ShellStructuredOutput = Static<typeof shellStructuredOutputSchema>;\n\n/** A command about to run, which `prepare` may change: the script, its working directory and environment. */\nexport interface BashExecution {\n\tcommand: string;\n\tcwd: string;\n\tenv: Record<string, string>;\n\tinheritEnv: boolean;\n}\n\nexport type BashPrepare = (execution: BashExecution, api: ToolExecutionApi, context: Context) => void | Promise<void>;\n\nexport interface BashToolOptions {\n\tcommandPrefix?: string;\n\tprepare?: BashPrepare;\n}\n\nexport interface PowerShellToolOptions {\n\t/** Lines run before each command. */\n\tcommandPrefix?: string;\n\tprepare?: BashPrepare;\n\t/** PowerShell programs to try in order; default `pwsh`, then `powershell`. A program that cannot start is skipped. */\n\tprograms?: readonly string[];\n}\n\nfunction validateTimeout(timeout: number | undefined): void {\n\tif (timeout === undefined) return;\n\tif (!Number.isFinite(timeout) || timeout <= 0) {\n\t\tthrow new Error(\"Invalid timeout: must be a finite number of seconds\");\n\t}\n\tif (timeout > MAX_TIMEOUT_SECONDS) {\n\t\tthrow new Error(`Invalid timeout: maximum is ${MAX_TIMEOUT_SECONDS} seconds`);\n\t}\n}\n\n/** The execution of one call: the command with its prefix, in the environment's working directory, then `prepare`. */\nasync function prepareExecution(\n\tcommand: string,\n\toptions: { commandPrefix?: string; prepare?: BashPrepare } | undefined,\n\tapi: ToolExecutionApi,\n\tcontext: Context,\n): Promise<BashExecution> {\n\tconst execution: BashExecution = {\n\t\tcommand: options?.commandPrefix ? `${options.commandPrefix}\\n${command}` : command,\n\t\tcwd: requireEnv(api).cwd,\n\t\tenv: {},\n\t\tinheritEnv: true,\n\t};\n\tawait options?.prepare?.(execution, api, context);\n\treturn execution;\n}\n\n/**\n * Run each command form in turn until one starts, streaming output to `api.output()` within the retained window, and\n * turn the result into the tool's result: a spill diagnostic, an error result for a nonzero exit, and a thrown error for\n * a failure such as a timeout. The structured output repeats the retained output with the exit code.\n */\nasync function runCommand(\n\tcommands: readonly (string | readonly string[])[],\n\texecution: BashExecution,\n\ttimeout: number | undefined,\n\tapi: ToolExecutionApi,\n\tcontext: Context,\n): Promise<ToolExecutionResult> {\n\tconst env = requireEnv(api);\n\tlet result: Awaited<ReturnType<typeof env.exec>> | undefined;\n\tfor (const command of commands) {\n\t\tresult = await env.exec(\n\t\t\tcommand,\n\t\t\t{\n\t\t\t\tcwd: execution.cwd,\n\t\t\t\tenv: execution.env,\n\t\t\t\tinheritEnv: execution.inheritEnv,\n\t\t\t\t...(timeout === undefined ? {} : { timeout }),\n\t\t\t\tonOutput: (text, _context, info) => api.output(text, info.skipped),\n\t\t\t\tspill: { afterBytes: DEFAULT_MAX_BYTES, afterLines: DEFAULT_MAX_LINES },\n\t\t\t\t// An environment may then omit output outside the retained tail and report the omission.\n\t\t\t\t...(api.outputWindow === undefined ? {} : { window: api.outputWindow }),\n\t\t\t},\n\t\t\tcontext,\n\t\t);\n\t\t// A program that could not start produced no output; the next form may.\n\t\tif (result.ok || result.error.code !== \"spawn_error\") break;\n\t}\n\tif (result === undefined) throw new Error(\"No command to run\");\n\tconst spillPath = result.ok ? result.value.spillPath : result.error.spillPath;\n\tif (spillPath !== undefined) {\n\t\tapi.diagnostic({ severity: \"info\", code: \"full_output\", message: `Full output: ${spillPath}` });\n\t}\n\tif (!result.ok) {\n\t\tif (result.error.code === \"aborted\" && context.abortSignal?.aborted) throw result.error;\n\t\tif (result.error.code === \"timeout\") throw new Error(`Command timed out after ${timeout} seconds`);\n\t\tif (result.error.code === \"aborted\") throw new Error(\"Command aborted\");\n\t\tthrow result.error;\n\t}\n\tconst exitCode = result.value.exitCode;\n\tconst retained = api.retainedOutput();\n\tconst structuredOutput: ShellStructuredOutput = {\n\t\toutput: retained.text,\n\t\ttruncated: retained.truncated,\n\t\t...(spillPath === undefined ? {} : { fullOutputPath: spillPath }),\n\t\texitCode,\n\t};\n\tif (exitCode === 0) return { structuredOutput };\n\tconst exited = { severity: \"error\", code: \"exit_code\", message: `Command exited with code ${exitCode}` } as const;\n\treturn { structuredOutput, isError: true, diagnostics: [exited] };\n}\n\n/**\n * Runs a command through the environment's shell. Its output streams to `api.output()`, where the Harness keeps the\n * tail within the default limits; the result content is that retained output. The retained window goes to the\n * environment, which may omit output outside it and report how much it omitted, so dropped counts stay exact. Output\n * beyond the limits is spilled to a file whose path is reported as a diagnostic. A nonzero exit is an error result with\n * the exit code; a timeout throws, which makes an error result that still carries the output and diagnostics.\n */\nexport function createBashTool(options?: BashToolOptions): ToolRegistration<typeof bashSchema> {\n\treturn defineTool({\n\t\tname: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns combined stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tparameters: bashSchema,\n\t\tstructuredOutputSchema: shellStructuredOutputSchema,\n\t\toutputLimits: { retain: \"tail\" },\n\t\tasync execute(args, api, context) {\n\t\t\tvalidateTimeout(args.timeout);\n\t\t\tconst execution = await prepareExecution(args.command, options, api, context);\n\t\t\treturn runCommand([execution.command], execution, args.timeout, api, context);\n\t\t},\n\t});\n}\n\n/** Output in UTF-8 whatever the console's code page, as the coding agent's `powershell` tool does. */\nconst UTF8_OUTPUT = \"try { [Console]::OutputEncoding=[System.Text.Encoding]::UTF8 } catch {}\";\nconst POWERSHELL_ARGS = [\"-NoProfile\", \"-NonInteractive\", \"-ExecutionPolicy\", \"Bypass\", \"-Command\"] as const;\n\n/**\n * Runs a PowerShell command, like `bash` but through PowerShell instead of the environment's shell: `pwsh` (PowerShell\n * 7), else Windows PowerShell, started directly with the command as an argument, so no other shell parses it.\n */\nexport function createPowerShellTool(options?: PowerShellToolOptions): ToolRegistration<typeof powershellSchema> {\n\tconst programs = options?.programs ?? [\"pwsh\", \"powershell\"];\n\treturn defineTool({\n\t\tname: \"powershell\",\n\t\tdescription: `Execute a PowerShell command in the current working directory. Returns combined stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tparameters: powershellSchema,\n\t\tstructuredOutputSchema: shellStructuredOutputSchema,\n\t\toutputLimits: { retain: \"tail\" },\n\t\tasync execute(args, api, context) {\n\t\t\tvalidateTimeout(args.timeout);\n\t\t\tconst execution = await prepareExecution(args.command, options, api, context);\n\t\t\tconst script = `${UTF8_OUTPUT}\\n${execution.command}`;\n\t\t\tconst commands = programs.map((program) => [program, ...POWERSHELL_ARGS, script]);\n\t\t\treturn runCommand(commands, execution, args.timeout, api, context);\n\t\t},\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../src/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from \"@earendil-works/chord/context\";\nimport { bool, num, obj, opt, type Static, str } from \"@earendil-works/pi-schema\";\nimport { defineTool } from \"../harness/define.ts\";\nimport type { ToolExecutionApi, ToolExecutionResult, ToolRegistration } from \"../harness/types.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES } from \"../truncate.ts\";\nimport { requireEnv } from \"./env.ts\";\n\nconst MAX_TIMEOUT_SECONDS = 2_147_483_647 / 1000;\n\n// Open objects, as the tools accepted extra arguments when their schemas were TypeBox's.\nconst bashSchema = obj(\n\t{\n\t\tcommand: str({ description: \"Bash command to execute\" }),\n\t\ttimeout: opt(num({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n\t},\n\t{ open: true },\n);\n\nconst powershellSchema = obj(\n\t{\n\t\tcommand: str({ description: \"PowerShell command to execute\" }),\n\t\ttimeout: opt(num({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n\t},\n\t{ open: true },\n);\n\n/** What a program that calls `bash` or `powershell` receives. */\nconst shellStructuredOutputSchema = obj(\n\t{\n\t\toutput: str({ description: \"Combined stdout and stderr: the retained tail, as the model sees it\" }),\n\t\ttruncated: bool({ description: \"Whether earlier output was dropped\" }),\n\t\tfullOutputPath: opt(str({ description: \"File with the complete output, when it was spilled\" })),\n\t\texitCode: num(),\n\t},\n\t{ open: true },\n);\n\nexport type BashToolInput = Static<typeof bashSchema>;\nexport type PowerShellToolInput = Static<typeof powershellSchema>;\nexport type ShellStructuredOutput = Static<typeof shellStructuredOutputSchema>;\n\n/** A command about to run, which `prepare` may change: the script, its working directory and environment. */\nexport interface BashExecution {\n\tcommand: string;\n\tcwd: string;\n\tenv: Record<string, string>;\n\tinheritEnv: boolean;\n}\n\nexport type BashPrepare = (execution: BashExecution, api: ToolExecutionApi, context: Context) => void | Promise<void>;\n\nexport interface BashToolOptions {\n\tcommandPrefix?: string;\n\tprepare?: BashPrepare;\n}\n\nexport interface PowerShellToolOptions {\n\t/** Lines run before each command. */\n\tcommandPrefix?: string;\n\tprepare?: BashPrepare;\n\t/** PowerShell programs to try in order; default `pwsh`, then `powershell`. A program that cannot start is skipped. */\n\tprograms?: readonly string[];\n}\n\nfunction validateTimeout(timeout: number | undefined): void {\n\tif (timeout === undefined) return;\n\tif (!Number.isFinite(timeout) || timeout <= 0) {\n\t\tthrow new Error(\"Invalid timeout: must be a finite number of seconds\");\n\t}\n\tif (timeout > MAX_TIMEOUT_SECONDS) {\n\t\tthrow new Error(`Invalid timeout: maximum is ${MAX_TIMEOUT_SECONDS} seconds`);\n\t}\n}\n\n/** The execution of one call: the command with its prefix, in the environment's working directory, then `prepare`. */\nasync function prepareExecution(\n\tcommand: string,\n\toptions: { commandPrefix?: string; prepare?: BashPrepare } | undefined,\n\tapi: ToolExecutionApi,\n\tcontext: Context,\n): Promise<BashExecution> {\n\tconst execution: BashExecution = {\n\t\tcommand: options?.commandPrefix ? `${options.commandPrefix}\\n${command}` : command,\n\t\tcwd: requireEnv(api).cwd,\n\t\tenv: {},\n\t\tinheritEnv: true,\n\t};\n\tawait options?.prepare?.(execution, api, context);\n\treturn execution;\n}\n\n/**\n * Run each command form in turn until one starts, streaming output to `api.output()` within the retained window, and\n * turn the result into the tool's result: a spill diagnostic, an error result for a nonzero exit, and a thrown error for\n * a failure such as a timeout. The structured output repeats the retained output with the exit code.\n */\nasync function runCommand(\n\tcommands: readonly (string | readonly string[])[],\n\texecution: BashExecution,\n\ttimeout: number | undefined,\n\tapi: ToolExecutionApi,\n\tcontext: Context,\n): Promise<ToolExecutionResult> {\n\tconst env = requireEnv(api);\n\tlet result: Awaited<ReturnType<typeof env.exec>> | undefined;\n\tfor (const command of commands) {\n\t\tresult = await env.exec(\n\t\t\tcommand,\n\t\t\t{\n\t\t\t\tcwd: execution.cwd,\n\t\t\t\tenv: execution.env,\n\t\t\t\tinheritEnv: execution.inheritEnv,\n\t\t\t\t...(timeout === undefined ? {} : { timeout }),\n\t\t\t\tonOutput: (text, _context, info) => api.output(text, info.skipped),\n\t\t\t\tspill: { afterBytes: DEFAULT_MAX_BYTES, afterLines: DEFAULT_MAX_LINES },\n\t\t\t\t// An environment may then omit output outside the retained tail and report the omission.\n\t\t\t\t...(api.outputWindow === undefined ? {} : { window: api.outputWindow }),\n\t\t\t},\n\t\t\tcontext,\n\t\t);\n\t\t// A program that could not start produced no output; the next form may.\n\t\tif (result.ok || result.error.code !== \"spawn_error\") break;\n\t}\n\tif (result === undefined) throw new Error(\"No command to run\");\n\tconst spillPath = result.ok ? result.value.spillPath : result.error.spillPath;\n\tif (spillPath !== undefined) {\n\t\tapi.diagnostic({ severity: \"info\", code: \"full_output\", message: `Full output: ${spillPath}` });\n\t}\n\tif (!result.ok) {\n\t\tif (result.error.code === \"aborted\" && context.abortSignal?.aborted) throw result.error;\n\t\tif (result.error.code === \"timeout\") throw new Error(`Command timed out after ${timeout} seconds`);\n\t\tif (result.error.code === \"aborted\") throw new Error(\"Command aborted\");\n\t\tthrow result.error;\n\t}\n\tconst exitCode = result.value.exitCode;\n\tconst retained = api.retainedOutput();\n\tconst structuredOutput: ShellStructuredOutput = {\n\t\toutput: retained.text,\n\t\ttruncated: retained.truncated,\n\t\t...(spillPath === undefined ? {} : { fullOutputPath: spillPath }),\n\t\texitCode,\n\t};\n\tif (exitCode === 0) return { structuredOutput };\n\tconst exited = { severity: \"error\", code: \"exit_code\", message: `Command exited with code ${exitCode}` } as const;\n\treturn { structuredOutput, isError: true, diagnostics: [exited] };\n}\n\n/**\n * Runs a command through the environment's shell. Its output streams to `api.output()`, where the Harness keeps the\n * tail within the default limits; the result content is that retained output. The retained window goes to the\n * environment, which may omit output outside it and report how much it omitted, so dropped counts stay exact. Output\n * beyond the limits is spilled to a file whose path is reported as a diagnostic. A nonzero exit is an error result with\n * the exit code; a timeout throws, which makes an error result that still carries the output and diagnostics.\n */\nexport function createBashTool(options?: BashToolOptions): ToolRegistration<typeof bashSchema> {\n\treturn defineTool({\n\t\tname: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns combined stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tparameters: bashSchema,\n\t\tstructuredOutputSchema: shellStructuredOutputSchema,\n\t\toutputLimits: { retain: \"tail\" },\n\t\tasync execute(args, api, context) {\n\t\t\tvalidateTimeout(args.timeout);\n\t\t\tconst execution = await prepareExecution(args.command, options, api, context);\n\t\t\treturn runCommand([execution.command], execution, args.timeout, api, context);\n\t\t},\n\t});\n}\n\n/** Output in UTF-8 whatever the console's code page, as the coding agent's `powershell` tool does. */\nconst UTF8_OUTPUT = \"try { [Console]::OutputEncoding=[System.Text.Encoding]::UTF8 } catch {}\";\nconst POWERSHELL_ARGS = [\"-NoProfile\", \"-NonInteractive\", \"-ExecutionPolicy\", \"Bypass\", \"-Command\"] as const;\n\n/**\n * Runs a PowerShell command, like `bash` but through PowerShell instead of the environment's shell: `pwsh` (PowerShell\n * 7), else Windows PowerShell, started directly with the command as an argument, so no other shell parses it.\n */\nexport function createPowerShellTool(options?: PowerShellToolOptions): ToolRegistration<typeof powershellSchema> {\n\tconst programs = options?.programs ?? [\"pwsh\", \"powershell\"];\n\treturn defineTool({\n\t\tname: \"powershell\",\n\t\tdescription: `Execute a PowerShell command in the current working directory. Returns combined stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tparameters: powershellSchema,\n\t\tstructuredOutputSchema: shellStructuredOutputSchema,\n\t\toutputLimits: { retain: \"tail\" },\n\t\tasync execute(args, api, context) {\n\t\t\tvalidateTimeout(args.timeout);\n\t\t\tconst execution = await prepareExecution(args.command, options, api, context);\n\t\t\tconst script = `${UTF8_OUTPUT}\\n${execution.command}`;\n\t\t\tconst commands = programs.map((program) => [program, ...POWERSHELL_ARGS, script]);\n\t\t\treturn runCommand(commands, execution, args.timeout, api, context);\n\t\t},\n\t});\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Context } from "@earendil-works/chord";
|
|
1
|
+
import type { Context } from "@earendil-works/chord/context";
|
|
2
2
|
import { type ExecutionEnv } from "../env/index.ts";
|
|
3
3
|
/**
|
|
4
4
|
* Serialize `edit` and `write` mutations of one file within this process: same file system id and canonical path,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-mutation-queue.d.ts","sourceRoot":"","sources":["../../src/tools/file-mutation-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"file-mutation-queue.d.ts","sourceRoot":"","sources":["../../src/tools/file-mutation-queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,KAAK,YAAY,EAAc,MAAM,iBAAiB,CAAC;AA0BhE;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,EAC5C,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,CAAC,CAAC,CAiBZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-mutation-queue.js","sourceRoot":"","sources":["../../src/tools/file-mutation-queue.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEhE,2FAA2F;AAC3F,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;AAEhD,KAAK,UAAU,WAAW,CAAC,GAAiB,EAAE,IAAY,EAAE,OAAgB;IAC3E,MAAM,YAAY,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACvE,OAAO,GAAG,GAAG,CAAC,EAAE,KAAK,MAAM,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,CAAC;AACpE,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,SAAS,CAAC,GAAiB,EAAE,YAAoB,EAAE,OAAgB;IACjF,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,aAAa,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC9D,IAAI,MAAM,CAAC,EAAE;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC;IACnC,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,eAAe;QAAE,OAAO,YAAY,CAAC;IAC/D,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW;QAAE,MAAM,MAAM,CAAC,KAAK,CAAC;IAC1D,mGAAmG;IACnG,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC,YAAY,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7E,IAAI,MAAM,KAAK,YAAY,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,YAAY,CAAC;IACrF,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjF,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AAC/F,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAC1C,GAAiB,EACjB,IAAY,EACZ,EAAoB,EACpB,OAAgB;IAEhB,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAClD,2EAA2E;IAC3E,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IACtD,IAAI,OAAO,GAAG,GAAS,EAAE,GAAE,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC1C,OAAO,GAAG,OAAO,CAAC;IACnB,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACtB,MAAM,QAAQ,CAAC;IACf,IAAI,CAAC;QACJ,OAAO,MAAM,EAAE,EAAE,CAAC;IACnB,CAAC;YAAS,CAAC;QACV,OAAO,EAAE,CAAC;QACV,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI;YAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAClD,CAAC;AACF,CAAC","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport { type ExecutionEnv, getOrThrow } from \"../env/index.ts\";\n\n/** Tail of the mutation chain of each file, keyed by file system id and canonical path. */\nconst queues = new Map<string, Promise<void>>();\n\nasync function mutationKey(env: ExecutionEnv, path: string, context: Context): Promise<string> {\n\tconst absolutePath = getOrThrow(await env.absolutePath(path, context));\n\treturn `${env.id}\\0${await canonical(env, absolutePath, context)}`;\n}\n\n/**\n * The canonical path; for a file that does not exist yet, its canonical parent joined with its name, so a `write` that\n * creates a file and a later mutation of it share one key even under a symlinked directory.\n */\nasync function canonical(env: ExecutionEnv, absolutePath: string, context: Context): Promise<string> {\n\tconst result = await env.canonicalPath(absolutePath, context);\n\tif (result.ok) return result.value;\n\tif (result.error.code === \"not_supported\") return absolutePath;\n\tif (result.error.code !== \"not_found\") throw result.error;\n\t// The file system splits the path, so a name may contain characters that are separators elsewhere.\n\tconst parent = getOrThrow(await env.joinPath([absolutePath, \"..\"], context));\n\tif (parent === absolutePath || !absolutePath.startsWith(parent)) return absolutePath;\n\tconst name = absolutePath.slice(parent.length + (/[/\\\\]$/.test(parent) ? 0 : 1));\n\treturn getOrThrow(await env.joinPath([await canonical(env, parent, context), name], context));\n}\n\n/**\n * Serialize `edit` and `write` mutations of one file within this process: same file system id and canonical path,\n * whichever environment object the call got. Other files, and other file systems, never wait. Concurrent calls on one\n * file run in the order their keys resolve. Not a lock against `bash` or other processes.\n */\nexport async function withFileMutationQueue<T>(\n\tenv: ExecutionEnv,\n\tpath: string,\n\tfn: () => Promise<T>,\n\tcontext: Context,\n): Promise<T> {\n\tconst key = await mutationKey(env, path, context);\n\t// Take the slot without awaiting, so no other call can take it in between.\n\tconst previous = queues.get(key) ?? Promise.resolve();\n\tlet release = (): void => {};\n\tconst done = new Promise<void>((resolve) => {\n\t\trelease = resolve;\n\t});\n\tconst tail = previous.then(() => done);\n\tqueues.set(key, tail);\n\tawait previous;\n\ttry {\n\t\treturn await fn();\n\t} finally {\n\t\trelease();\n\t\tif (queues.get(key) === tail) queues.delete(key);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"file-mutation-queue.js","sourceRoot":"","sources":["../../src/tools/file-mutation-queue.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEhE,2FAA2F;AAC3F,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;AAEhD,KAAK,UAAU,WAAW,CAAC,GAAiB,EAAE,IAAY,EAAE,OAAgB;IAC3E,MAAM,YAAY,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACvE,OAAO,GAAG,GAAG,CAAC,EAAE,KAAK,MAAM,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,CAAC;AACpE,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,SAAS,CAAC,GAAiB,EAAE,YAAoB,EAAE,OAAgB;IACjF,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,aAAa,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC9D,IAAI,MAAM,CAAC,EAAE;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC;IACnC,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,eAAe;QAAE,OAAO,YAAY,CAAC;IAC/D,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW;QAAE,MAAM,MAAM,CAAC,KAAK,CAAC;IAC1D,mGAAmG;IACnG,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC,YAAY,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7E,IAAI,MAAM,KAAK,YAAY,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,YAAY,CAAC;IACrF,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjF,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AAC/F,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAC1C,GAAiB,EACjB,IAAY,EACZ,EAAoB,EACpB,OAAgB;IAEhB,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAClD,2EAA2E;IAC3E,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IACtD,IAAI,OAAO,GAAG,GAAS,EAAE,GAAE,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC1C,OAAO,GAAG,OAAO,CAAC;IACnB,CAAC,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACtB,MAAM,QAAQ,CAAC;IACf,IAAI,CAAC;QACJ,OAAO,MAAM,EAAE,EAAE,CAAC;IACnB,CAAC;YAAS,CAAC;QACV,OAAO,EAAE,CAAC;QACV,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI;YAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAClD,CAAC;AACF,CAAC","sourcesContent":["import type { Context } from \"@earendil-works/chord/context\";\nimport { type ExecutionEnv, getOrThrow } from \"../env/index.ts\";\n\n/** Tail of the mutation chain of each file, keyed by file system id and canonical path. */\nconst queues = new Map<string, Promise<void>>();\n\nasync function mutationKey(env: ExecutionEnv, path: string, context: Context): Promise<string> {\n\tconst absolutePath = getOrThrow(await env.absolutePath(path, context));\n\treturn `${env.id}\\0${await canonical(env, absolutePath, context)}`;\n}\n\n/**\n * The canonical path; for a file that does not exist yet, its canonical parent joined with its name, so a `write` that\n * creates a file and a later mutation of it share one key even under a symlinked directory.\n */\nasync function canonical(env: ExecutionEnv, absolutePath: string, context: Context): Promise<string> {\n\tconst result = await env.canonicalPath(absolutePath, context);\n\tif (result.ok) return result.value;\n\tif (result.error.code === \"not_supported\") return absolutePath;\n\tif (result.error.code !== \"not_found\") throw result.error;\n\t// The file system splits the path, so a name may contain characters that are separators elsewhere.\n\tconst parent = getOrThrow(await env.joinPath([absolutePath, \"..\"], context));\n\tif (parent === absolutePath || !absolutePath.startsWith(parent)) return absolutePath;\n\tconst name = absolutePath.slice(parent.length + (/[/\\\\]$/.test(parent) ? 0 : 1));\n\treturn getOrThrow(await env.joinPath([await canonical(env, parent, context), name], context));\n}\n\n/**\n * Serialize `edit` and `write` mutations of one file within this process: same file system id and canonical path,\n * whichever environment object the call got. Other files, and other file systems, never wait. Concurrent calls on one\n * file run in the order their keys resolve. Not a lock against `bash` or other processes.\n */\nexport async function withFileMutationQueue<T>(\n\tenv: ExecutionEnv,\n\tpath: string,\n\tfn: () => Promise<T>,\n\tcontext: Context,\n): Promise<T> {\n\tconst key = await mutationKey(env, path, context);\n\t// Take the slot without awaiting, so no other call can take it in between.\n\tconst previous = queues.get(key) ?? Promise.resolve();\n\tlet release = (): void => {};\n\tconst done = new Promise<void>((resolve) => {\n\t\trelease = resolve;\n\t});\n\tconst tail = previous.then(() => done);\n\tqueues.set(key, tail);\n\tawait previous;\n\ttry {\n\t\treturn await fn();\n\t} finally {\n\t\trelease();\n\t\tif (queues.get(key) === tail) queues.delete(key);\n\t}\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Context } from "@earendil-works/chord";
|
|
1
|
+
import type { Context } from "@earendil-works/chord/context";
|
|
2
2
|
import { type ExecutionEnv } from "../env/index.ts";
|
|
3
3
|
export declare function resolveToolPath(env: ExecutionEnv, path: string, context: Context): Promise<string>;
|
|
4
4
|
export declare function resolveReadToolPath(env: ExecutionEnv, path: string, context: Context): Promise<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../../src/tools/path-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../../src/tools/path-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,KAAK,YAAY,EAAc,MAAM,iBAAiB,CAAC;AAUhE,wBAAsB,eAAe,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAExG;AAED,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAc5G"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-utils.js","sourceRoot":"","sources":["../../src/tools/path-utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEhE,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,OAAgB;IACtF,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,YAAY,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,GAAiB,EAAE,IAAY,EAAE,OAAgB;IAC1F,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAC3D,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,OAAO,CAAC,CAAC;YAAE,OAAO,OAAO,CAAC;IACpE,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC","sourcesContent":["import type { Context } from \"@earendil-works/chord\";\nimport { type ExecutionEnv, getOrThrow } from \"../env/index.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, context: Context): Promise<string> {\n\treturn getOrThrow(await env.absolutePath(normalizeToolPath(path), context));\n}\n\nexport async function resolveReadToolPath(env: ExecutionEnv, path: string, context: Context): Promise<string> {\n\tconst resolved = await resolveToolPath(env, path, context);\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, context))) return variant;\n\t}\n\treturn resolved;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"path-utils.js","sourceRoot":"","sources":["../../src/tools/path-utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEhE,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,OAAgB;IACtF,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,YAAY,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,GAAiB,EAAE,IAAY,EAAE,OAAgB;IAC1F,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAC3D,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,OAAO,CAAC,CAAC;YAAE,OAAO,OAAO,CAAC;IACpE,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC","sourcesContent":["import type { Context } from \"@earendil-works/chord/context\";\nimport { type ExecutionEnv, getOrThrow } from \"../env/index.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, context: Context): Promise<string> {\n\treturn getOrThrow(await env.absolutePath(normalizeToolPath(path), context));\n}\n\nexport async function resolveReadToolPath(env: ExecutionEnv, path: string, context: Context): Promise<string> {\n\tconst resolved = await resolveToolPath(env, path, context);\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, context))) return variant;\n\t}\n\treturn resolved;\n}\n"]}
|
package/dist/tools/read.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAe,GAAG,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AACxF,OAAO,EAGN,UAAU,EAEV,YAAY,EACZ,aAAa,GACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,OAAO,EACN,iBAAiB,EACjB,iBAAiB,EACjB,UAAU,EAEV,cAAc,EACd,cAAc,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,8BAA8B,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EACN,YAAY,EACZ,oBAAoB,EAGpB,kBAAkB,EAClB,QAAQ,GACR,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD,MAAM,UAAU,GAAG,GAAG,CACrB;IACC,IAAI,EAAE,GAAG,CAAC,EAAE,WAAW,EAAE,iDAAiD,EAAE,CAAC;IAC7E,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC,CAAC;IAClF,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,iCAAiC,EAAE,CAAC,CAAC;CACnE,EACD,EAAE,IAAI,EAAE,IAAI,EAAE,CACd,CAAC;AAIF;;;GAGG;AACH,MAAM,0BAA0B,GAAG,KAAK,CAAC;IACxC,GAAG,EAAE;IACL,GAAG,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;CACzE,CAAC,CAAC;AAOH,MAAM,UAAU,GAAG,EAAE,GAAG,IAAI,CAAC;AAE7B,qGAAqG;AACrG,SAAS,UAAU,CAAC,KAAa;IAChC,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACpD,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,QAAQ,CACtB,MAAoB,EACpB,KAAa,EACb,GAAW,EACX,OAAgB,EAChB,OAAgB;IAEhB,MAAM,OAAO,GAAG,YAAY,EAAE,CAAC;IAC/B,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,IAAI,QAAQ,GAAG,OAAO,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,GAAG,GAAG,GAAI,CAAC;QAC1E,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,GAAG,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACrG,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM;QAC9B,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;QACzB,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,IAAI,IAAI,OAAO,CAAC;QAChB,KAAK,IAAI,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC;YAAE,QAAQ,EAAE,CAAC;QAC3G,IAAI,QAAQ,IAAI,iBAAiB,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,iBAAiB,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;IAChG,CAAC;IACD,OAAO,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;AAChC,CAAC;AAOD;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,OAAO,GAAoB,EAAE;IAC3D,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,0BAA0B,CAAC;IAClG,OAAO,UAAU,CAAC;QACjB,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,gEAAgE,OAAO,6EAA6E,iBAAiB,aAAa,iBAAiB,GAAG,IAAI,kIAAkI;QACzV,UAAU,EAAE,UAAU;QACtB,sBAAsB,EAAE,0BAA0B;QAClD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO;YAC/B,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;YACrC,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;YAC5B,MAAM,YAAY,GAAG,MAAM,mBAAmB,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;YACnE,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,gBAAgB,CAAC,YAAY,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;YACxF,IAAI,CAAC;gBACJ,2GAA2G;gBAC3G,6FAA6F;gBAC7F,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;oBACnC,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;oBACtD,MAAM,QAAQ,GAAG,MAAM,8BAA8B,CAAC;wBACrD,IAAI,EAAE,MAAM,CAAC,IAAI;wBACjB,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;qBAC1F,CAAC,CAAC;oBACH,MAAM,MAAM,GACX,QAAQ,KAAK,SAAS;wBACrB,CAAC,CAAC,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC;wBACtD,CAAC,CAAC,MAAM,SAAS,CACf,MAAM,EACN,MAAM,EACN,IAAI,EACJ,QAAQ,EACR,OAAO,CAAC,MAAM,EACd,MAAM,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,EAC9B,OAAO,CACP,CAAC;oBACL,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;oBACrD,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,KAAK,MAAM,CAAC,OAAO,CAAC;oBACjF,IAAI,SAAS,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;wBAAE,OAAO,MAAM,CAAC;oBACrF,IAAI,OAAO,KAAK,CAAC;wBAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,4BAA4B,CAAC,CAAC;gBACzE,CAAC;YACF,CAAC;oBAAS,CAAC;gBACV,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7B,CAAC;QACF,CAAC;KACD,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,SAAS,CACvB,MAAoB,EACpB,IAAc,EACd,IAAY,EACZ,QAAgB,EAChB,SAAqC,EACrC,EAAE,MAAM,EAAE,MAAM,EAAc,EAC9B,OAAgB;IAEhB,IAAI,KAA2D,CAAC;IAChE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACnE,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAClE,IAAI,QAAQ,KAAK,SAAS;YACzB,OAAO,UAAU,CAAC,GAAG,IAAI,iBAAiB,QAAQ,yCAAyC,CAAC,CAAC;QAC9F,KAAK,GAAG,QAAQ,CAAC;QACjB,IAAI,QAAQ,CAAC,aAAa,KAAK,SAAS;YACvC,KAAK,CAAC,IAAI,CAAC,kBAAkB,QAAQ,CAAC,aAAa,OAAO,QAAQ,CAAC,QAAQ,GAAG,CAAC,CAAC;QACjF,IAAI,QAAQ,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACpC,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC;YACtC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACjD,KAAK,CAAC,IAAI,CACT,gBAAgB,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,OAAO,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,MAAM,6BAA6B,KAAK,+BAA+B,CACtI,CAAC;QACH,CAAC;IACF,CAAC;SAAM,CAAC;QACP,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvC,OAAO,UAAU,CAChB,GAAG,IAAI,iBAAiB,QAAQ,+DAA+D,CAC/F,CAAC;QACH,CAAC;QACD,MAAM,YAAY,GAAG,mDAAmD,CAAC;QACzE,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC/C,OAAO,UAAU,CAChB,GAAG,IAAI,iBAAiB,QAAQ,QAAQ,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,wBAAwB,YAAY,EAAE,CACnG,CAAC;QACH,CAAC;QACD,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC9C,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,UAAU,CAAC,GAAG,IAAI,iBAAiB,QAAQ,6BAA6B,CAAC,CAAC;QACzG,IAAI,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;YACpE,OAAO,UAAU,CAChB,GAAG,IAAI,iBAAiB,QAAQ,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,iBAAiB,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,SAAS,KAAK,YAAY,EAAE,CACxI,CAAC;QACH,CAAC;QACD,KAAK,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC7C,CAAC;IACD,IAAI,CAAC,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,2EAA2E,CAAC,CAAC;IACrG,MAAM,WAAW,GAAqB;QACrC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,oBAAoB,KAAK,CAAC,QAAQ,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;KAC1G,CAAC;IACF,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,OAAgB,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;IACrF,OAAO,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;AAClE,CAAC;AAKD;;;GAGG;AACH,KAAK,UAAU,UAAU,CAAC,GAAqB,EAAE,OAAgB;IAChE,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7C,MAAM,KAAK,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7F,MAAM,MAAM,GAAG,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC;IAClD,OAAO;QACN,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI;QAC9C,MAAM,EAAE;YACP,QAAQ,EAAE,MAAM,EAAE,QAAQ,IAAI,oBAAoB,CAAC,QAAQ;YAC3D,SAAS,EAAE,MAAM,EAAE,SAAS,IAAI,oBAAoB,CAAC,SAAS;YAC9D,QAAQ,EAAE,MAAM,EAAE,QAAQ,IAAI,oBAAoB,CAAC,QAAQ;SAC3D;KACD,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,OAAe;IAClC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AAChH,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,QAAQ,CACtB,MAAoB,EACpB,IAAY,EACZ,MAA0B,EAC1B,KAAyB,EACzB,OAAgB;IAEhB,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,gBAAgB,GAAG,SAAS,GAAG,CAAC,CAAC;IACvC,oGAAoG;IACpG,MAAM,UAAU,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACzC,iHAAiH;IACjH,uFAAuF;IACvF,8GAA8G;IAC9G,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,YAAY,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,EAAE,UAAU,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC;IAC9G,MAAM,OAAO,GAAG,YAAY,KAAK,SAAS,IAAI,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5G,MAAM,MAAM,GAAG,KAAK,EAAE,OAA2B,EAAqB,EAAE,CACvE,UAAU,CACT,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,OAAO,CAAC,CACxG,CAAC;IACH,IAAI,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC;IACjC,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;IACzC,IAAI,SAAS,IAAI,cAAc,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,UAAU,MAAM,2BAA2B,cAAc,eAAe,CAAC,CAAC;IAC3F,CAAC;IAED,IAAI,gBAAoC,CAAC;IACzC,IAAI,iBAAiB,GAAG,cAAc,GAAG,UAAU,CAAC;IACpD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,KAAK,EAAE,cAAc,CAAC,CAAC;QAC5D,gBAAgB,GAAG,OAAO,GAAG,SAAS,CAAC;QACvC,+GAA+G;QAC/G,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,GAAG,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;QAC3F,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;QACvD,IAAI,iBAAiB,GAAG,CAAC,IAAI,WAAW,GAAG,CAAC;YAAE,IAAI,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC7E,CAAC;IACD,MAAM,KAAK,GAAG,iBAAiB,KAAK,CAAC,CAAC;IACtC,yFAAyF;IACzF,MAAM,eAAe,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC;IACrG,MAAM,MAAM,GAAG;QACd,KAAK,EAAE,KAAK,IAAI,IAAI,CAAC,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5F,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa;KACrC,CAAC;IACF,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;IAE3G,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,UAAU,EAAE,GAAG,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1E,MAAM,WAAW,GAAqB,EAAE,CAAC;IACzC,IAAI,UAAU,GAAG,QAAQ,CAAC;IAC1B,IAAI,OAAoC,CAAC;IACzC,IAAI,UAAU,CAAC,qBAAqB,EAAE,CAAC;QACtC,2GAA2G;QAC3G,uCAAuC;QACvC,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAC7C,MAAM,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACxF,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;QACpD,MAAM,GAAG,GAAG,YAAY,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;QACvD,UAAU,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAClE,WAAW,CAAC,IAAI,CAAC;YAChB,QAAQ,EAAE,MAAM;YAChB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,QAAQ,gBAAgB,OAAO,UAAU,CAAC,QAAQ,CAAC,iBAAiB,UAAU,CAAC,iBAAiB,CAAC,6BAA6B,UAAU,CAAC,GAAG,CAAC,uBAAuB,gBAAgB,MAAM,IAAI,eAAe,GAAG,GAAG,CAAC,EAAE;SAC/N,CAAC,CAAC;QACH,OAAO,GAAG,EAAE,UAAU,EAAE,EAAE,GAAG,UAAU,EAAE,WAAW,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;IAC/E,CAAC;SAAM,IAAI,UAAU,CAAC,SAAS,EAAE,CAAC;QACjC,MAAM,cAAc,GAAG,gBAAgB,GAAG,UAAU,CAAC,WAAW,GAAG,CAAC,CAAC;QACrE,MAAM,UAAU,GAAG,cAAc,GAAG,CAAC,CAAC;QACtC,MAAM,SAAS,GAAG,UAAU,CAAC,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,iBAAiB,CAAC,SAAS,CAAC;QACxG,WAAW,CAAC,IAAI,CAAC;YAChB,QAAQ,EAAE,MAAM;YAChB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,iBAAiB,gBAAgB,IAAI,cAAc,OAAO,cAAc,GAAG,SAAS,gBAAgB,UAAU,eAAe;SACtI,CAAC,CAAC;QACH,OAAO,GAAG,EAAE,UAAU,EAAE,CAAC;IAC1B,CAAC;SAAM,IAAI,gBAAgB,KAAK,SAAS,IAAI,SAAS,GAAG,gBAAgB,GAAG,cAAc,EAAE,CAAC;QAC5F,MAAM,SAAS,GAAG,cAAc,GAAG,CAAC,SAAS,GAAG,gBAAgB,CAAC,CAAC;QAClE,MAAM,UAAU,GAAG,SAAS,GAAG,gBAAgB,GAAG,CAAC,CAAC;QACpD,WAAW,CAAC,IAAI,CAAC;YAChB,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,GAAG,SAAS,mCAAmC,UAAU,eAAe;SACjF,CAAC,CAAC;IACJ,CAAC;IAED,OAAO;QACN,MAAM,EAAE,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAC9E,gBAAgB,EAAE,UAAU;QAC5B,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;QAC7C,WAAW;KACX,CAAC;AACH,CAAC","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
|
+
{"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/context\";\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"]}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { Context
|
|
2
|
-
import type { Op } from "@earendil-works/chord/delta";
|
|
1
|
+
import type { Context } from "@earendil-works/chord/context";
|
|
2
|
+
import type { Draft, Op } from "@earendil-works/chord/delta";
|
|
3
|
+
import type { JsonValue } from "@earendil-works/chord/json";
|
|
4
|
+
import type { WatchCall, WatchEnd, WatchHandle } from "@earendil-works/chord/watch";
|
|
3
5
|
import type { Message, Models } from "@earendil-works/pi-ai";
|
|
4
6
|
import type { TelemetryCarrier } from "@earendil-works/pi-telemetry";
|
|
5
7
|
import type { ExecutionEnv } from "./env/index.ts";
|
|
@@ -905,6 +907,8 @@ export interface Tx {
|
|
|
905
907
|
/** ID of the newest entry visible from the conversation; `undefined` when it has none. */
|
|
906
908
|
latestEntryId(conversationId: ConversationId): Promise<EntryId | undefined>;
|
|
907
909
|
scanTasks(query: TaskQuery, limit: number, cursor?: Cursor): Promise<Page<TaskRecord<JsonValue, JsonValue, JsonValue>, Cursor>>;
|
|
910
|
+
/** Committed submission record, such as one of `pi.live.run.inputs`; a placed input's `entry` is its transcript entry. */
|
|
911
|
+
submission(id: SubmissionId): Promise<SubmissionRecord | undefined>;
|
|
908
912
|
/** Committed submission with a conversation-scoped request ID. */
|
|
909
913
|
submissionByRequest(conversationId: ConversationId, requestId: string): Promise<SubmissionRecord | undefined>;
|
|
910
914
|
/** Create a conversation with explicitly selected ownership. */
|