@di-code/coding-agent 0.1.3 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/README.md +91 -30
  2. package/dist/cli.d.ts +15 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +96 -17
  5. package/dist/cli.js.map +1 -1
  6. package/dist/core/clipboard-image.d.ts +3 -3
  7. package/dist/core/clipboard-image.d.ts.map +1 -1
  8. package/dist/core/clipboard-image.js +26 -13
  9. package/dist/core/clipboard-image.js.map +1 -1
  10. package/dist/core/context-builder.d.ts.map +1 -1
  11. package/dist/core/context-builder.js +6 -2
  12. package/dist/core/context-builder.js.map +1 -1
  13. package/dist/core/image-input.d.ts +1 -1
  14. package/dist/core/image-input.d.ts.map +1 -1
  15. package/dist/core/image-input.js +55 -10
  16. package/dist/core/image-input.js.map +1 -1
  17. package/dist/core/resources/loader.d.ts.map +1 -1
  18. package/dist/core/resources/loader.js +15 -42
  19. package/dist/core/resources/loader.js.map +1 -1
  20. package/dist/core/resources/types.d.ts +6 -9
  21. package/dist/core/resources/types.d.ts.map +1 -1
  22. package/dist/core/resources/types.js.map +1 -1
  23. package/dist/core/session/session-manager.d.ts +12 -1
  24. package/dist/core/session/session-manager.d.ts.map +1 -1
  25. package/dist/core/session/session-manager.js +66 -3
  26. package/dist/core/session/session-manager.js.map +1 -1
  27. package/dist/core/session/session-storage.d.ts.map +1 -1
  28. package/dist/core/session/session-storage.js +30 -15
  29. package/dist/core/session/session-storage.js.map +1 -1
  30. package/dist/core/session/types.d.ts +6 -1
  31. package/dist/core/session/types.d.ts.map +1 -1
  32. package/dist/core/session/types.js +1 -1
  33. package/dist/core/session/types.js.map +1 -1
  34. package/dist/core/session.d.ts +38 -3
  35. package/dist/core/session.d.ts.map +1 -1
  36. package/dist/core/session.js +167 -9
  37. package/dist/core/session.js.map +1 -1
  38. package/dist/core/system-prompt.js +2 -2
  39. package/dist/core/system-prompt.js.map +1 -1
  40. package/dist/core/tools/bash.d.ts +2 -2
  41. package/dist/core/tools/bash.d.ts.map +1 -1
  42. package/dist/core/tools/bash.js.map +1 -1
  43. package/dist/core/tools/edit-diff.d.ts +25 -0
  44. package/dist/core/tools/edit-diff.d.ts.map +1 -0
  45. package/dist/core/tools/edit-diff.js +136 -0
  46. package/dist/core/tools/edit-diff.js.map +1 -0
  47. package/dist/core/tools/edit.d.ts +13 -4
  48. package/dist/core/tools/edit.d.ts.map +1 -1
  49. package/dist/core/tools/edit.js +64 -30
  50. package/dist/core/tools/edit.js.map +1 -1
  51. package/dist/core/tools/file-search.d.ts +14 -0
  52. package/dist/core/tools/file-search.d.ts.map +1 -0
  53. package/dist/core/tools/file-search.js +99 -0
  54. package/dist/core/tools/file-search.js.map +1 -0
  55. package/dist/core/tools/glob.d.ts +11 -0
  56. package/dist/core/tools/glob.d.ts.map +1 -0
  57. package/dist/core/tools/glob.js +44 -0
  58. package/dist/core/tools/glob.js.map +1 -0
  59. package/dist/core/tools/grep.d.ts +13 -0
  60. package/dist/core/tools/grep.d.ts.map +1 -0
  61. package/dist/core/tools/grep.js +67 -0
  62. package/dist/core/tools/grep.js.map +1 -0
  63. package/dist/core/tools/load-skill.d.ts +10 -0
  64. package/dist/core/tools/load-skill.d.ts.map +1 -0
  65. package/dist/core/tools/load-skill.js +22 -0
  66. package/dist/core/tools/load-skill.js.map +1 -0
  67. package/dist/core/tools/path-boundary.d.ts +2 -0
  68. package/dist/core/tools/path-boundary.d.ts.map +1 -1
  69. package/dist/core/tools/path-boundary.js +4 -0
  70. package/dist/core/tools/path-boundary.js.map +1 -1
  71. package/dist/core/tools/read.d.ts +2 -2
  72. package/dist/core/tools/read.d.ts.map +1 -1
  73. package/dist/core/tools/read.js.map +1 -1
  74. package/dist/core/tools/write.d.ts +2 -2
  75. package/dist/core/tools/write.d.ts.map +1 -1
  76. package/dist/core/tools/write.js.map +1 -1
  77. package/dist/core/user-data.d.ts +3 -0
  78. package/dist/core/user-data.d.ts.map +1 -0
  79. package/dist/core/user-data.js +8 -0
  80. package/dist/core/user-data.js.map +1 -0
  81. package/dist/entry.js +22 -2
  82. package/dist/entry.js.map +1 -1
  83. package/dist/extensions/runtime.js +2 -2
  84. package/dist/extensions/runtime.js.map +1 -1
  85. package/dist/i18n.d.ts +5 -0
  86. package/dist/i18n.d.ts.map +1 -0
  87. package/dist/i18n.js +223 -0
  88. package/dist/i18n.js.map +1 -0
  89. package/dist/index.d.ts +3 -0
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +3 -0
  92. package/dist/index.js.map +1 -1
  93. package/dist/main.d.ts +20 -1
  94. package/dist/main.d.ts.map +1 -1
  95. package/dist/main.js +272 -17
  96. package/dist/main.js.map +1 -1
  97. package/dist/mcp/config.d.ts +33 -0
  98. package/dist/mcp/config.d.ts.map +1 -0
  99. package/dist/mcp/config.js +211 -0
  100. package/dist/mcp/config.js.map +1 -0
  101. package/dist/mcp/loader.d.ts +21 -0
  102. package/dist/mcp/loader.d.ts.map +1 -0
  103. package/dist/mcp/loader.js +60 -0
  104. package/dist/mcp/loader.js.map +1 -0
  105. package/dist/mcp/tool-adapter.d.ts +5 -0
  106. package/dist/mcp/tool-adapter.d.ts.map +1 -0
  107. package/dist/mcp/tool-adapter.js +149 -0
  108. package/dist/mcp/tool-adapter.js.map +1 -0
  109. package/dist/modes/interactive-components.d.ts +2 -0
  110. package/dist/modes/interactive-components.d.ts.map +1 -1
  111. package/dist/modes/interactive-components.js +55 -31
  112. package/dist/modes/interactive-components.js.map +1 -1
  113. package/dist/modes/interactive-diff.d.ts +8 -0
  114. package/dist/modes/interactive-diff.d.ts.map +1 -0
  115. package/dist/modes/interactive-diff.js +78 -0
  116. package/dist/modes/interactive-diff.js.map +1 -0
  117. package/dist/modes/interactive-layout.d.ts +4 -0
  118. package/dist/modes/interactive-layout.d.ts.map +1 -1
  119. package/dist/modes/interactive-layout.js +4 -0
  120. package/dist/modes/interactive-layout.js.map +1 -1
  121. package/dist/modes/interactive-state.d.ts +5 -0
  122. package/dist/modes/interactive-state.d.ts.map +1 -1
  123. package/dist/modes/interactive-state.js +65 -11
  124. package/dist/modes/interactive-state.js.map +1 -1
  125. package/dist/modes/interactive.d.ts +19 -0
  126. package/dist/modes/interactive.d.ts.map +1 -1
  127. package/dist/modes/interactive.js +351 -52
  128. package/dist/modes/interactive.js.map +1 -1
  129. package/dist/modes/tree-selector.d.ts +30 -0
  130. package/dist/modes/tree-selector.d.ts.map +1 -0
  131. package/dist/modes/tree-selector.js +205 -0
  132. package/dist/modes/tree-selector.js.map +1 -0
  133. package/dist/plugins/loader.d.ts +19 -1
  134. package/dist/plugins/loader.d.ts.map +1 -1
  135. package/dist/plugins/loader.js +21 -2
  136. package/dist/plugins/loader.js.map +1 -1
  137. package/dist/provider-onboarding.d.ts +8 -1
  138. package/dist/provider-onboarding.d.ts.map +1 -1
  139. package/dist/provider-onboarding.js +160 -40
  140. package/dist/provider-onboarding.js.map +1 -1
  141. package/dist/rpc-entry.js +1 -1
  142. package/dist/rpc-entry.js.map +1 -1
  143. package/dist/startup.d.ts +34 -3
  144. package/dist/startup.d.ts.map +1 -1
  145. package/dist/startup.js +407 -87
  146. package/dist/startup.js.map +1 -1
  147. package/package.json +7 -4
@@ -0,0 +1,99 @@
1
+ import { readdir, readFile, realpath, stat } from "node:fs/promises";
2
+ import { basename, matchesGlob, relative, resolve, sep } from "node:path";
3
+ import { resolveAllowedExistingPath } from "./path-boundary.js";
4
+ export const DEFAULT_SEARCH_MAX_RESULTS = 200;
5
+ export const DEFAULT_SEARCH_MAX_OUTPUT_BYTES = 50 * 1024;
6
+ export const MAX_SEARCH_FILE_BYTES = 2 * 1024 * 1024;
7
+ export function assertSearchLimit(name, value) {
8
+ if (!Number.isInteger(value) || value < 1)
9
+ throw new Error(`${name} must be a positive integer`);
10
+ }
11
+ export function normalizeSearchPattern(pattern) {
12
+ const normalized = pattern.replaceAll("\\", "/").trim();
13
+ if (normalized.length === 0)
14
+ throw new Error("pattern must not be empty");
15
+ if (normalized.startsWith("/"))
16
+ throw new Error("pattern must be relative to the allowed root");
17
+ try {
18
+ matchesGlob("placeholder", normalized);
19
+ }
20
+ catch (cause) {
21
+ throw new Error(`Invalid glob pattern: ${cause instanceof Error ? cause.message : String(cause)}`);
22
+ }
23
+ return normalized;
24
+ }
25
+ export async function resolveSearchRoot(inputPath, allowedRoot) {
26
+ return resolveAllowedExistingPath(inputPath ?? ".", allowedRoot);
27
+ }
28
+ export async function* walkSearchFiles(rootPath, signal, displayRootPath = rootPath) {
29
+ const traversalRoot = await realpath(rootPath);
30
+ const displayRoot = await realpath(displayRootPath);
31
+ const rootMetadata = await stat(traversalRoot);
32
+ if (rootMetadata.isFile()) {
33
+ yield {
34
+ relativePath: relative(displayRoot, traversalRoot).split(sep).join("/"),
35
+ searchPath: basename(traversalRoot),
36
+ };
37
+ return;
38
+ }
39
+ if (!rootMetadata.isDirectory())
40
+ throw new Error("Search path must be a file or directory");
41
+ const initialEntries = await readDirectoryEntries(traversalRoot);
42
+ const pending = [{ directory: traversalRoot, entries: initialEntries, index: 0 }];
43
+ while (pending.length > 0) {
44
+ if (signal?.aborted)
45
+ throw new Error("Operation aborted");
46
+ const frame = pending[pending.length - 1];
47
+ if (!frame)
48
+ continue;
49
+ const entry = frame.entries[frame.index++];
50
+ if (!entry) {
51
+ pending.pop();
52
+ continue;
53
+ }
54
+ if (entry.isSymbolicLink())
55
+ continue;
56
+ const absolutePath = resolve(frame.directory, entry.name);
57
+ if (entry.isDirectory()) {
58
+ pending.push({ directory: absolutePath, entries: await readDirectoryEntries(absolutePath), index: 0 });
59
+ continue;
60
+ }
61
+ if (!entry.isFile())
62
+ continue;
63
+ const relativePath = relative(displayRoot, absolutePath).split(sep).join("/");
64
+ const searchPath = relative(traversalRoot, absolutePath).split(sep).join("/");
65
+ yield { relativePath, searchPath };
66
+ }
67
+ }
68
+ async function readDirectoryEntries(directory) {
69
+ const entries = await readdir(directory, { withFileTypes: true });
70
+ entries.sort((left, right) => left.name.localeCompare(right.name));
71
+ return entries;
72
+ }
73
+ export async function readSearchText(file, allowedRoot, signal) {
74
+ if (signal?.aborted)
75
+ throw new Error("Operation aborted");
76
+ const safePath = await resolveAllowedExistingPath(file.relativePath, allowedRoot);
77
+ const metadata = await stat(safePath);
78
+ if (!metadata.isFile() || metadata.size > MAX_SEARCH_FILE_BYTES)
79
+ return undefined;
80
+ const buffer = await readFile(safePath);
81
+ if (signal?.aborted)
82
+ throw new Error("Operation aborted");
83
+ const sampleLength = Math.min(buffer.length, 8 * 1024);
84
+ for (let index = 0; index < sampleLength; index++) {
85
+ if (buffer[index] === 0)
86
+ return undefined;
87
+ }
88
+ return buffer.toString("utf8");
89
+ }
90
+ export function appendBoundedLine(lines, line, maxOutputBytes) {
91
+ const separatorBytes = lines.length === 0 ? 0 : 1;
92
+ const nextBytes = Buffer.byteLength(line, "utf8") + separatorBytes;
93
+ const currentBytes = lines.reduce((total, item) => total + Buffer.byteLength(item, "utf8"), 0);
94
+ if (currentBytes + nextBytes > maxOutputBytes)
95
+ return false;
96
+ lines.push(line);
97
+ return true;
98
+ }
99
+ //# sourceMappingURL=file-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-search.js","sourceRoot":"","sources":["../../../src/core/tools/file-search.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdir, readFile, realpath, stat } from \"node:fs/promises\";\nimport { basename, matchesGlob, relative, resolve, sep } from \"node:path\";\nimport { resolveAllowedExistingPath } from \"./path-boundary.ts\";\n\nexport const DEFAULT_SEARCH_MAX_RESULTS = 200;\nexport const DEFAULT_SEARCH_MAX_OUTPUT_BYTES = 50 * 1024;\nexport const MAX_SEARCH_FILE_BYTES = 2 * 1024 * 1024;\n\nexport interface SearchFile {\n\treadonly relativePath: string;\n\treadonly searchPath: string;\n}\n\nexport function assertSearchLimit(name: string, value: number): void {\n\tif (!Number.isInteger(value) || value < 1) throw new Error(`${name} must be a positive integer`);\n}\n\nexport function normalizeSearchPattern(pattern: string): string {\n\tconst normalized = pattern.replaceAll(\"\\\\\", \"/\").trim();\n\tif (normalized.length === 0) throw new Error(\"pattern must not be empty\");\n\tif (normalized.startsWith(\"/\")) throw new Error(\"pattern must be relative to the allowed root\");\n\ttry {\n\t\tmatchesGlob(\"placeholder\", normalized);\n\t} catch (cause) {\n\t\tthrow new Error(`Invalid glob pattern: ${cause instanceof Error ? cause.message : String(cause)}`);\n\t}\n\treturn normalized;\n}\n\nexport async function resolveSearchRoot(inputPath: string | undefined, allowedRoot: string): Promise<string> {\n\treturn resolveAllowedExistingPath(inputPath ?? \".\", allowedRoot);\n}\n\nexport async function* walkSearchFiles(\n\trootPath: string,\n\tsignal?: AbortSignal,\n\tdisplayRootPath = rootPath,\n): AsyncGenerator<SearchFile, void, undefined> {\n\tconst traversalRoot = await realpath(rootPath);\n\tconst displayRoot = await realpath(displayRootPath);\n\tconst rootMetadata = await stat(traversalRoot);\n\tif (rootMetadata.isFile()) {\n\t\tyield {\n\t\t\trelativePath: relative(displayRoot, traversalRoot).split(sep).join(\"/\"),\n\t\t\tsearchPath: basename(traversalRoot),\n\t\t};\n\t\treturn;\n\t}\n\tif (!rootMetadata.isDirectory()) throw new Error(\"Search path must be a file or directory\");\n\tconst initialEntries = await readDirectoryEntries(traversalRoot);\n\tconst pending: Array<{\n\t\tdirectory: string;\n\t\tentries: Awaited<ReturnType<typeof readDirectoryEntries>>;\n\t\tindex: number;\n\t}> = [{ directory: traversalRoot, entries: initialEntries, index: 0 }];\n\n\twhile (pending.length > 0) {\n\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\tconst frame = pending[pending.length - 1];\n\t\tif (!frame) continue;\n\t\tconst entry = frame.entries[frame.index++];\n\t\tif (!entry) {\n\t\t\tpending.pop();\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.isSymbolicLink()) continue;\n\t\tconst absolutePath = resolve(frame.directory, entry.name);\n\t\tif (entry.isDirectory()) {\n\t\t\tpending.push({ directory: absolutePath, entries: await readDirectoryEntries(absolutePath), index: 0 });\n\t\t\tcontinue;\n\t\t}\n\t\tif (!entry.isFile()) continue;\n\t\tconst relativePath = relative(displayRoot, absolutePath).split(sep).join(\"/\");\n\t\tconst searchPath = relative(traversalRoot, absolutePath).split(sep).join(\"/\");\n\t\tyield { relativePath, searchPath };\n\t}\n}\n\nasync function readDirectoryEntries(directory: string) {\n\tconst entries = await readdir(directory, { withFileTypes: true });\n\tentries.sort((left, right) => left.name.localeCompare(right.name));\n\treturn entries;\n}\n\nexport async function readSearchText(\n\tfile: SearchFile,\n\tallowedRoot: string,\n\tsignal?: AbortSignal,\n): Promise<string | undefined> {\n\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\tconst safePath = await resolveAllowedExistingPath(file.relativePath, allowedRoot);\n\tconst metadata = await stat(safePath);\n\tif (!metadata.isFile() || metadata.size > MAX_SEARCH_FILE_BYTES) return undefined;\n\tconst buffer = await readFile(safePath);\n\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\tconst sampleLength = Math.min(buffer.length, 8 * 1024);\n\tfor (let index = 0; index < sampleLength; index++) {\n\t\tif (buffer[index] === 0) return undefined;\n\t}\n\treturn buffer.toString(\"utf8\");\n}\n\nexport function appendBoundedLine(lines: string[], line: string, maxOutputBytes: number): boolean {\n\tconst separatorBytes = lines.length === 0 ? 0 : 1;\n\tconst nextBytes = Buffer.byteLength(line, \"utf8\") + separatorBytes;\n\tconst currentBytes = lines.reduce((total, item) => total + Buffer.byteLength(item, \"utf8\"), 0);\n\tif (currentBytes + nextBytes > maxOutputBytes) return false;\n\tlines.push(line);\n\treturn true;\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { AgentTool } from "@di-code/agent";
2
+ import { type Static, type ToolResultContent, Type } from "@di-code/ai";
3
+ export declare const globParameters: Type.TObject<{
4
+ pattern: Type.TString;
5
+ path: Type.TOptional<Type.TString>;
6
+ maxResults: Type.TOptional<Type.TInteger>;
7
+ }>;
8
+ export type GlobParameters = Static<typeof globParameters>;
9
+ export type GlobTool = AgentTool<typeof globParameters, ToolResultContent[]>;
10
+ export declare function createGlobTool(allowedRoot: string): GlobTool;
11
+ //# sourceMappingURL=glob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../../../src/core/tools/glob.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,iBAAiB,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAWxE,eAAO,MAAM,cAAc;;;;EAIzB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AAC3D,MAAM,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,cAAc,EAAE,iBAAiB,EAAE,CAAC,CAAC;AAE7E,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,QAAQ,CA8B5D"}
@@ -0,0 +1,44 @@
1
+ import { matchesGlob } from "node:path";
2
+ import { Type } from "@di-code/ai";
3
+ import { appendBoundedLine, assertSearchLimit, DEFAULT_SEARCH_MAX_OUTPUT_BYTES, DEFAULT_SEARCH_MAX_RESULTS, normalizeSearchPattern, resolveSearchRoot, walkSearchFiles, } from "./file-search.js";
4
+ export const globParameters = Type.Object({
5
+ pattern: Type.String({ minLength: 1 }),
6
+ path: Type.Optional(Type.String({ minLength: 1 })),
7
+ maxResults: Type.Optional(Type.Integer({ minimum: 1 })),
8
+ });
9
+ export function createGlobTool(allowedRoot) {
10
+ return {
11
+ name: "glob",
12
+ description: "Find files inside the allowed root using a glob pattern such as **/*.ts. Returns sorted relative paths; symlinks are skipped.",
13
+ parameters: globParameters,
14
+ async execute(_toolCallId, parameters, signal) {
15
+ if (signal?.aborted)
16
+ throw new Error("Operation aborted");
17
+ if (parameters.path !== undefined && parameters.path.length === 0)
18
+ throw new Error("path must not be empty");
19
+ const pattern = normalizeSearchPattern(parameters.pattern);
20
+ const maxResults = parameters.maxResults ?? DEFAULT_SEARCH_MAX_RESULTS;
21
+ assertSearchLimit("maxResults", maxResults);
22
+ const root = await resolveSearchRoot(parameters.path, allowedRoot);
23
+ const displayRoot = await resolveSearchRoot(undefined, allowedRoot);
24
+ const lines = [];
25
+ let matched = 0;
26
+ let truncated = false;
27
+ for await (const file of walkSearchFiles(root, signal, displayRoot)) {
28
+ if (!matchesGlob(file.searchPath, pattern))
29
+ continue;
30
+ matched++;
31
+ if (matched > maxResults || !appendBoundedLine(lines, file.relativePath, DEFAULT_SEARCH_MAX_OUTPUT_BYTES)) {
32
+ truncated = true;
33
+ break;
34
+ }
35
+ }
36
+ if (lines.length === 0)
37
+ return [{ type: "text", text: "No files matched." }];
38
+ if (truncated)
39
+ lines.push(`[Results truncated at ${maxResults} matches or 50 KiB.]`);
40
+ return [{ type: "text", text: lines.join("\n") }];
41
+ },
42
+ };
43
+ }
44
+ //# sourceMappingURL=glob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob.js","sourceRoot":"","sources":["../../../src/core/tools/glob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,OAAO,EAAuC,IAAI,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EACN,iBAAiB,EACjB,iBAAiB,EACjB,+BAA+B,EAC/B,0BAA0B,EAC1B,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,GACf,MAAM,kBAAkB,CAAC;AAE1B,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC;IACzC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACtC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;IAClD,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;CACvD,CAAC,CAAC;AAKH,MAAM,UAAU,cAAc,CAAC,WAAmB;IACjD,OAAO;QACN,IAAI,EAAE,MAAM;QACZ,WAAW,EACV,+HAA+H;QAChI,UAAU,EAAE,cAAc;QAC1B,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM;YAC5C,IAAI,MAAM,EAAE,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;YAC1D,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;YAC7G,MAAM,OAAO,GAAG,sBAAsB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAC3D,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,IAAI,0BAA0B,CAAC;YACvE,iBAAiB,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;YAC5C,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YACnE,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YACpE,MAAM,KAAK,GAAa,EAAE,CAAC;YAC3B,IAAI,OAAO,GAAG,CAAC,CAAC;YAChB,IAAI,SAAS,GAAG,KAAK,CAAC;YACtB,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,CAAC;gBACrE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC;oBAAE,SAAS;gBACrD,OAAO,EAAE,CAAC;gBACV,IAAI,OAAO,GAAG,UAAU,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,+BAA+B,CAAC,EAAE,CAAC;oBAC3G,SAAS,GAAG,IAAI,CAAC;oBACjB,MAAM;gBACP,CAAC;YACF,CAAC;YACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC,CAAC;YAC7E,IAAI,SAAS;gBAAE,KAAK,CAAC,IAAI,CAAC,yBAAyB,UAAU,sBAAsB,CAAC,CAAC;YACrF,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnD,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["import { matchesGlob } from \"node:path\";\nimport type { AgentTool } from \"@di-code/agent\";\nimport { type Static, type ToolResultContent, Type } from \"@di-code/ai\";\nimport {\n\tappendBoundedLine,\n\tassertSearchLimit,\n\tDEFAULT_SEARCH_MAX_OUTPUT_BYTES,\n\tDEFAULT_SEARCH_MAX_RESULTS,\n\tnormalizeSearchPattern,\n\tresolveSearchRoot,\n\twalkSearchFiles,\n} from \"./file-search.ts\";\n\nexport const globParameters = Type.Object({\n\tpattern: Type.String({ minLength: 1 }),\n\tpath: Type.Optional(Type.String({ minLength: 1 })),\n\tmaxResults: Type.Optional(Type.Integer({ minimum: 1 })),\n});\n\nexport type GlobParameters = Static<typeof globParameters>;\nexport type GlobTool = AgentTool<typeof globParameters, ToolResultContent[]>;\n\nexport function createGlobTool(allowedRoot: string): GlobTool {\n\treturn {\n\t\tname: \"glob\",\n\t\tdescription:\n\t\t\t\"Find files inside the allowed root using a glob pattern such as **/*.ts. Returns sorted relative paths; symlinks are skipped.\",\n\t\tparameters: globParameters,\n\t\tasync execute(_toolCallId, parameters, signal): Promise<ToolResultContent[]> {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tif (parameters.path !== undefined && parameters.path.length === 0) throw new Error(\"path must not be empty\");\n\t\t\tconst pattern = normalizeSearchPattern(parameters.pattern);\n\t\t\tconst maxResults = parameters.maxResults ?? DEFAULT_SEARCH_MAX_RESULTS;\n\t\t\tassertSearchLimit(\"maxResults\", maxResults);\n\t\t\tconst root = await resolveSearchRoot(parameters.path, allowedRoot);\n\t\t\tconst displayRoot = await resolveSearchRoot(undefined, allowedRoot);\n\t\t\tconst lines: string[] = [];\n\t\t\tlet matched = 0;\n\t\t\tlet truncated = false;\n\t\t\tfor await (const file of walkSearchFiles(root, signal, displayRoot)) {\n\t\t\t\tif (!matchesGlob(file.searchPath, pattern)) continue;\n\t\t\t\tmatched++;\n\t\t\t\tif (matched > maxResults || !appendBoundedLine(lines, file.relativePath, DEFAULT_SEARCH_MAX_OUTPUT_BYTES)) {\n\t\t\t\t\ttruncated = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (lines.length === 0) return [{ type: \"text\", text: \"No files matched.\" }];\n\t\t\tif (truncated) lines.push(`[Results truncated at ${maxResults} matches or 50 KiB.]`);\n\t\t\treturn [{ type: \"text\", text: lines.join(\"\\n\") }];\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import type { AgentTool } from "@di-code/agent";
2
+ import { type Static, type ToolResultContent, Type } from "@di-code/ai";
3
+ export declare const grepParameters: Type.TObject<{
4
+ pattern: Type.TString;
5
+ path: Type.TOptional<Type.TString>;
6
+ include: Type.TOptional<Type.TString>;
7
+ caseSensitive: Type.TOptional<Type.TBoolean>;
8
+ maxResults: Type.TOptional<Type.TInteger>;
9
+ }>;
10
+ export type GrepParameters = Static<typeof grepParameters>;
11
+ export type GrepTool = AgentTool<typeof grepParameters, ToolResultContent[]>;
12
+ export declare function createGrepTool(allowedRoot: string): GrepTool;
13
+ //# sourceMappingURL=grep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../../src/core/tools/grep.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,iBAAiB,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAYxE,eAAO,MAAM,cAAc;;;;;;EAMzB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AAC3D,MAAM,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,cAAc,EAAE,iBAAiB,EAAE,CAAC,CAAC;AAE7E,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,QAAQ,CA6C5D"}
@@ -0,0 +1,67 @@
1
+ import { matchesGlob } from "node:path";
2
+ import { Type } from "@di-code/ai";
3
+ import { appendBoundedLine, assertSearchLimit, DEFAULT_SEARCH_MAX_OUTPUT_BYTES, DEFAULT_SEARCH_MAX_RESULTS, normalizeSearchPattern, readSearchText, resolveSearchRoot, walkSearchFiles, } from "./file-search.js";
4
+ export const grepParameters = Type.Object({
5
+ pattern: Type.String({ minLength: 1, maxLength: 1_000 }),
6
+ path: Type.Optional(Type.String({ minLength: 1 })),
7
+ include: Type.Optional(Type.String({ minLength: 1 })),
8
+ caseSensitive: Type.Optional(Type.Boolean()),
9
+ maxResults: Type.Optional(Type.Integer({ minimum: 1 })),
10
+ });
11
+ export function createGrepTool(allowedRoot) {
12
+ return {
13
+ name: "grep",
14
+ description: "Search UTF-8 text files inside the allowed root for a literal string. Returns sorted path:line matches; binary files and symlinks are skipped.",
15
+ parameters: grepParameters,
16
+ async execute(_toolCallId, parameters, signal) {
17
+ if (signal?.aborted)
18
+ throw new Error("Operation aborted");
19
+ if (parameters.pattern.length === 0)
20
+ throw new Error("pattern must not be empty");
21
+ if (parameters.pattern.length > 1_000)
22
+ throw new Error("pattern must not exceed 1000 characters");
23
+ if (parameters.path !== undefined && parameters.path.length === 0)
24
+ throw new Error("path must not be empty");
25
+ const include = parameters.include === undefined ? undefined : normalizeSearchPattern(parameters.include);
26
+ const maxResults = parameters.maxResults ?? DEFAULT_SEARCH_MAX_RESULTS;
27
+ assertSearchLimit("maxResults", maxResults);
28
+ const root = await resolveSearchRoot(parameters.path, allowedRoot);
29
+ const displayRoot = await resolveSearchRoot(undefined, allowedRoot);
30
+ const needle = parameters.caseSensitive === false ? parameters.pattern.toLocaleLowerCase() : parameters.pattern;
31
+ const lines = [];
32
+ let matched = 0;
33
+ let truncated = false;
34
+ for await (const file of walkSearchFiles(root, signal, displayRoot)) {
35
+ if (include !== undefined && !matchesInclude(file.searchPath, include))
36
+ continue;
37
+ const text = await readSearchText(file, allowedRoot, signal);
38
+ if (text === undefined)
39
+ continue;
40
+ const fileLines = text.split("\n");
41
+ for (let index = 0; index < fileLines.length; index++) {
42
+ const line = fileLines[index] ?? "";
43
+ const haystack = parameters.caseSensitive === false ? line.toLocaleLowerCase() : line;
44
+ if (!haystack.includes(needle))
45
+ continue;
46
+ matched++;
47
+ if (matched > maxResults ||
48
+ !appendBoundedLine(lines, `${file.relativePath}:${index + 1}: ${line}`, DEFAULT_SEARCH_MAX_OUTPUT_BYTES)) {
49
+ truncated = true;
50
+ break;
51
+ }
52
+ }
53
+ if (truncated)
54
+ break;
55
+ }
56
+ if (lines.length === 0)
57
+ return [{ type: "text", text: "No matches found." }];
58
+ if (truncated)
59
+ lines.push(`[Results truncated at ${maxResults} matches or 50 KiB.]`);
60
+ return [{ type: "text", text: lines.join("\n") }];
61
+ },
62
+ };
63
+ }
64
+ function matchesInclude(relativePath, pattern) {
65
+ return matchesGlob(relativePath, pattern);
66
+ }
67
+ //# sourceMappingURL=grep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grep.js","sourceRoot":"","sources":["../../../src/core/tools/grep.ts"],"names":[],"mappings":"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","sourcesContent":["import { matchesGlob } from \"node:path\";\nimport type { AgentTool } from \"@di-code/agent\";\nimport { type Static, type ToolResultContent, Type } from \"@di-code/ai\";\nimport {\n\tappendBoundedLine,\n\tassertSearchLimit,\n\tDEFAULT_SEARCH_MAX_OUTPUT_BYTES,\n\tDEFAULT_SEARCH_MAX_RESULTS,\n\tnormalizeSearchPattern,\n\treadSearchText,\n\tresolveSearchRoot,\n\twalkSearchFiles,\n} from \"./file-search.ts\";\n\nexport const grepParameters = Type.Object({\n\tpattern: Type.String({ minLength: 1, maxLength: 1_000 }),\n\tpath: Type.Optional(Type.String({ minLength: 1 })),\n\tinclude: Type.Optional(Type.String({ minLength: 1 })),\n\tcaseSensitive: Type.Optional(Type.Boolean()),\n\tmaxResults: Type.Optional(Type.Integer({ minimum: 1 })),\n});\n\nexport type GrepParameters = Static<typeof grepParameters>;\nexport type GrepTool = AgentTool<typeof grepParameters, ToolResultContent[]>;\n\nexport function createGrepTool(allowedRoot: string): GrepTool {\n\treturn {\n\t\tname: \"grep\",\n\t\tdescription:\n\t\t\t\"Search UTF-8 text files inside the allowed root for a literal string. Returns sorted path:line matches; binary files and symlinks are skipped.\",\n\t\tparameters: grepParameters,\n\t\tasync execute(_toolCallId, parameters, signal): Promise<ToolResultContent[]> {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tif (parameters.pattern.length === 0) throw new Error(\"pattern must not be empty\");\n\t\t\tif (parameters.pattern.length > 1_000) throw new Error(\"pattern must not exceed 1000 characters\");\n\t\t\tif (parameters.path !== undefined && parameters.path.length === 0) throw new Error(\"path must not be empty\");\n\t\t\tconst include = parameters.include === undefined ? undefined : normalizeSearchPattern(parameters.include);\n\t\t\tconst maxResults = parameters.maxResults ?? DEFAULT_SEARCH_MAX_RESULTS;\n\t\t\tassertSearchLimit(\"maxResults\", maxResults);\n\t\t\tconst root = await resolveSearchRoot(parameters.path, allowedRoot);\n\t\t\tconst displayRoot = await resolveSearchRoot(undefined, allowedRoot);\n\t\t\tconst needle = parameters.caseSensitive === false ? parameters.pattern.toLocaleLowerCase() : parameters.pattern;\n\t\t\tconst lines: string[] = [];\n\t\t\tlet matched = 0;\n\t\t\tlet truncated = false;\n\t\t\tfor await (const file of walkSearchFiles(root, signal, displayRoot)) {\n\t\t\t\tif (include !== undefined && !matchesInclude(file.searchPath, include)) continue;\n\t\t\t\tconst text = await readSearchText(file, allowedRoot, signal);\n\t\t\t\tif (text === undefined) continue;\n\t\t\t\tconst fileLines = text.split(\"\\n\");\n\t\t\t\tfor (let index = 0; index < fileLines.length; index++) {\n\t\t\t\t\tconst line = fileLines[index] ?? \"\";\n\t\t\t\t\tconst haystack = parameters.caseSensitive === false ? line.toLocaleLowerCase() : line;\n\t\t\t\t\tif (!haystack.includes(needle)) continue;\n\t\t\t\t\tmatched++;\n\t\t\t\t\tif (\n\t\t\t\t\t\tmatched > maxResults ||\n\t\t\t\t\t\t!appendBoundedLine(lines, `${file.relativePath}:${index + 1}: ${line}`, DEFAULT_SEARCH_MAX_OUTPUT_BYTES)\n\t\t\t\t\t) {\n\t\t\t\t\t\ttruncated = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (truncated) break;\n\t\t\t}\n\t\t\tif (lines.length === 0) return [{ type: \"text\", text: \"No matches found.\" }];\n\t\t\tif (truncated) lines.push(`[Results truncated at ${maxResults} matches or 50 KiB.]`);\n\t\t\treturn [{ type: \"text\", text: lines.join(\"\\n\") }];\n\t\t},\n\t};\n}\n\nfunction matchesInclude(relativePath: string, pattern: string): boolean {\n\treturn matchesGlob(relativePath, pattern);\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import type { AgentTool } from "@di-code/agent";
2
+ import { type Static, type ToolResultContent, Type } from "@di-code/ai";
3
+ import type { SkillCatalog } from "@di-code/skills";
4
+ export declare const loadSkillParameters: Type.TObject<{
5
+ name: Type.TString;
6
+ }>;
7
+ export type LoadSkillParameters = Static<typeof loadSkillParameters>;
8
+ export type LoadSkillTool = AgentTool<typeof loadSkillParameters, ToolResultContent[]>;
9
+ export declare function createLoadSkillTool(catalog: SkillCatalog): LoadSkillTool;
10
+ //# sourceMappingURL=load-skill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-skill.d.ts","sourceRoot":"","sources":["../../../src/core/tools/load-skill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,iBAAiB,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,eAAO,MAAM,mBAAmB;;EAE9B,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACrE,MAAM,MAAM,aAAa,GAAG,SAAS,CAAC,OAAO,mBAAmB,EAAE,iBAAiB,EAAE,CAAC,CAAC;AAEvF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa,CAaxE"}
@@ -0,0 +1,22 @@
1
+ import { Type } from "@di-code/ai";
2
+ import { readSkillContent } from "@di-code/skills";
3
+ export const loadSkillParameters = Type.Object({
4
+ name: Type.String({ minLength: 1 }),
5
+ });
6
+ export function createLoadSkillTool(catalog) {
7
+ return {
8
+ name: "load_skill",
9
+ description: "Load the untrusted instructions for a registered skill by name.",
10
+ parameters: loadSkillParameters,
11
+ async execute(_toolCallId, parameters, signal) {
12
+ if (signal?.aborted)
13
+ throw new Error("Operation aborted");
14
+ const skill = catalog.resolve(parameters.name);
15
+ if (!skill || skill.disableModelInvocation)
16
+ throw new Error(`Unknown or unavailable skill "${parameters.name}".`);
17
+ const content = await readSkillContent(skill, signal);
18
+ return [{ type: "text", text: `<skill name="${skill.name}" source="${skill.source}">\n${content}\n</skill>` }];
19
+ },
20
+ };
21
+ }
22
+ //# sourceMappingURL=load-skill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-skill.js","sourceRoot":"","sources":["../../../src/core/tools/load-skill.ts"],"names":[],"mappings":"AACA,OAAO,EAAuC,IAAI,EAAE,MAAM,aAAa,CAAC;AAExE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC,MAAM,CAAC;IAC9C,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;CACnC,CAAC,CAAC;AAKH,MAAM,UAAU,mBAAmB,CAAC,OAAqB;IACxD,OAAO;QACN,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,iEAAiE;QAC9E,UAAU,EAAE,mBAAmB;QAC/B,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM;YAC5C,IAAI,MAAM,EAAE,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;YAC1D,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAC/C,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,sBAAsB;gBAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,UAAU,CAAC,IAAI,IAAI,CAAC,CAAC;YAClH,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YACtD,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,KAAK,CAAC,IAAI,aAAa,KAAK,CAAC,MAAM,OAAO,OAAO,YAAY,EAAE,CAAC,CAAC;QAChH,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["import type { AgentTool } from \"@di-code/agent\";\nimport { type Static, type ToolResultContent, Type } from \"@di-code/ai\";\nimport type { SkillCatalog } from \"@di-code/skills\";\nimport { readSkillContent } from \"@di-code/skills\";\n\nexport const loadSkillParameters = Type.Object({\n\tname: Type.String({ minLength: 1 }),\n});\n\nexport type LoadSkillParameters = Static<typeof loadSkillParameters>;\nexport type LoadSkillTool = AgentTool<typeof loadSkillParameters, ToolResultContent[]>;\n\nexport function createLoadSkillTool(catalog: SkillCatalog): LoadSkillTool {\n\treturn {\n\t\tname: \"load_skill\",\n\t\tdescription: \"Load the untrusted instructions for a registered skill by name.\",\n\t\tparameters: loadSkillParameters,\n\t\tasync execute(_toolCallId, parameters, signal): Promise<ToolResultContent[]> {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tconst skill = catalog.resolve(parameters.name);\n\t\t\tif (!skill || skill.disableModelInvocation) throw new Error(`Unknown or unavailable skill \"${parameters.name}\".`);\n\t\t\tconst content = await readSkillContent(skill, signal);\n\t\t\treturn [{ type: \"text\", text: `<skill name=\"${skill.name}\" source=\"${skill.source}\">\\n${content}\\n</skill>` }];\n\t\t},\n\t};\n}\n"]}
@@ -1,3 +1,5 @@
1
1
  export declare function resolveAllowedMutationPath(inputPath: string, allowedRoot: string): Promise<string>;
2
2
  export declare function resolveAllowedFilePath(inputPath: string, allowedRoot: string): Promise<string>;
3
+ /** Resolve an existing file or directory while enforcing the realpath root boundary. */
4
+ export declare function resolveAllowedExistingPath(inputPath: string, allowedRoot: string): Promise<string>;
3
5
  //# sourceMappingURL=path-boundary.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"path-boundary.d.ts","sourceRoot":"","sources":["../../../src/core/tools/path-boundary.ts"],"names":[],"mappings":"AAoBA,wBAAsB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgCxG;AAED,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOpG"}
1
+ {"version":3,"file":"path-boundary.d.ts","sourceRoot":"","sources":["../../../src/core/tools/path-boundary.ts"],"names":[],"mappings":"AAoBA,wBAAsB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgCxG;AAED,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEpG;AAED,wFAAwF;AACxF,wBAAsB,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOxG"}
@@ -52,6 +52,10 @@ export async function resolveAllowedMutationPath(inputPath, allowedRoot) {
52
52
  }
53
53
  }
54
54
  export async function resolveAllowedFilePath(inputPath, allowedRoot) {
55
+ return resolveAllowedExistingPath(inputPath, allowedRoot);
56
+ }
57
+ /** Resolve an existing file or directory while enforcing the realpath root boundary. */
58
+ export async function resolveAllowedExistingPath(inputPath, allowedRoot) {
55
59
  const rootReal = await realpath(allowedRoot);
56
60
  const candidate = resolve(rootReal, inputPath);
57
61
  assertInsideRoot(rootReal, candidate);
@@ -1 +1 @@
1
- {"version":3,"file":"path-boundary.js","sourceRoot":"","sources":["../../../src/core/tools/path-boundary.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat, realpath } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, relative, resolve, sep } from \"node:path\";\n\nfunction assertInsideRoot(root: string, target: string): void {\n\tconst fromRoot = relative(root, target);\n\tif (fromRoot === \"\" || (fromRoot !== \"..\" && !fromRoot.startsWith(`..${sep}`) && !isAbsolute(fromRoot))) {\n\t\treturn;\n\t}\n\tthrow new Error(\"Path is outside the allowed root\");\n}\n\nfunction isMissingPathError(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"code\" in error &&\n\t\t(error.code === \"ENOENT\" || error.code === \"ENOTDIR\")\n\t);\n}\n\nexport async function resolveAllowedMutationPath(inputPath: string, allowedRoot: string): Promise<string> {\n\tconst rootReal = await realpath(allowedRoot);\n\tconst candidate = resolve(rootReal, inputPath);\n\tassertInsideRoot(rootReal, candidate);\n\n\tconst missingSegments: string[] = [];\n\tlet current = candidate;\n\n\twhile (true) {\n\t\ttry {\n\t\t\tconst currentReal = await realpath(current);\n\t\t\tassertInsideRoot(rootReal, currentReal);\n\t\t\tconst target = resolve(currentReal, ...missingSegments.reverse());\n\t\t\tassertInsideRoot(rootReal, target);\n\t\t\treturn target;\n\t\t} catch (error) {\n\t\t\tif (!isMissingPathError(error)) throw error;\n\t\t\tlet entryExists = false;\n\t\t\ttry {\n\t\t\t\tconst entry = await lstat(current);\n\t\t\t\tif (entry.isSymbolicLink()) throw new Error(\"Path is outside the allowed root\");\n\t\t\t\tentryExists = true;\n\t\t\t} catch (entryError) {\n\t\t\t\tif (!isMissingPathError(entryError)) throw entryError;\n\t\t\t}\n\t\t\tif (entryExists) continue;\n\t\t\tconst parent = dirname(current);\n\t\t\tif (parent === current) throw error;\n\t\t\tmissingSegments.push(basename(current));\n\t\t\tcurrent = parent;\n\t\t}\n\t}\n}\n\nexport async function resolveAllowedFilePath(inputPath: string, allowedRoot: string): Promise<string> {\n\tconst rootReal = await realpath(allowedRoot);\n\tconst candidate = resolve(rootReal, inputPath);\n\tassertInsideRoot(rootReal, candidate);\n\tconst targetReal = await realpath(candidate);\n\tassertInsideRoot(rootReal, targetReal);\n\treturn targetReal;\n}\n"]}
1
+ {"version":3,"file":"path-boundary.js","sourceRoot":"","sources":["../../../src/core/tools/path-boundary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAElF,SAAS,gBAAgB,CAAC,IAAY,EAAE,MAAc;IACrD,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,IAAI,QAAQ,KAAK,EAAE,IAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QACzG,OAAO;IACR,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAc;IACzC,OAAO,CACN,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACf,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CACrD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,SAAiB,EAAE,WAAmB;IACtF,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC/C,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAEtC,MAAM,eAAe,GAAa,EAAE,CAAC;IACrC,IAAI,OAAO,GAAG,SAAS,CAAC;IAExB,OAAO,IAAI,EAAE,CAAC;QACb,IAAI,CAAC;YACJ,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC5C,gBAAgB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;YACxC,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,GAAG,eAAe,CAAC,OAAO,EAAE,CAAC,CAAC;YAClE,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YACnC,OAAO,MAAM,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YAC5C,IAAI,WAAW,GAAG,KAAK,CAAC;YACxB,IAAI,CAAC;gBACJ,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC;gBACnC,IAAI,KAAK,CAAC,cAAc,EAAE;oBAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;gBAChF,WAAW,GAAG,IAAI,CAAC;YACpB,CAAC;YAAC,OAAO,UAAU,EAAE,CAAC;gBACrB,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC;oBAAE,MAAM,UAAU,CAAC;YACvD,CAAC;YACD,IAAI,WAAW;gBAAE,SAAS;YAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;YAChC,IAAI,MAAM,KAAK,OAAO;gBAAE,MAAM,KAAK,CAAC;YACpC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;YACxC,OAAO,GAAG,MAAM,CAAC;QAClB,CAAC;IACF,CAAC;AACF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,SAAiB,EAAE,WAAmB;IAClF,OAAO,0BAA0B,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;AAC3D,CAAC;AAED,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,SAAiB,EAAE,WAAmB;IACtF,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC/C,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACtC,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC7C,gBAAgB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACvC,OAAO,UAAU,CAAC;AACnB,CAAC","sourcesContent":["import { lstat, realpath } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, relative, resolve, sep } from \"node:path\";\n\nfunction assertInsideRoot(root: string, target: string): void {\n\tconst fromRoot = relative(root, target);\n\tif (fromRoot === \"\" || (fromRoot !== \"..\" && !fromRoot.startsWith(`..${sep}`) && !isAbsolute(fromRoot))) {\n\t\treturn;\n\t}\n\tthrow new Error(\"Path is outside the allowed root\");\n}\n\nfunction isMissingPathError(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"code\" in error &&\n\t\t(error.code === \"ENOENT\" || error.code === \"ENOTDIR\")\n\t);\n}\n\nexport async function resolveAllowedMutationPath(inputPath: string, allowedRoot: string): Promise<string> {\n\tconst rootReal = await realpath(allowedRoot);\n\tconst candidate = resolve(rootReal, inputPath);\n\tassertInsideRoot(rootReal, candidate);\n\n\tconst missingSegments: string[] = [];\n\tlet current = candidate;\n\n\twhile (true) {\n\t\ttry {\n\t\t\tconst currentReal = await realpath(current);\n\t\t\tassertInsideRoot(rootReal, currentReal);\n\t\t\tconst target = resolve(currentReal, ...missingSegments.reverse());\n\t\t\tassertInsideRoot(rootReal, target);\n\t\t\treturn target;\n\t\t} catch (error) {\n\t\t\tif (!isMissingPathError(error)) throw error;\n\t\t\tlet entryExists = false;\n\t\t\ttry {\n\t\t\t\tconst entry = await lstat(current);\n\t\t\t\tif (entry.isSymbolicLink()) throw new Error(\"Path is outside the allowed root\");\n\t\t\t\tentryExists = true;\n\t\t\t} catch (entryError) {\n\t\t\t\tif (!isMissingPathError(entryError)) throw entryError;\n\t\t\t}\n\t\t\tif (entryExists) continue;\n\t\t\tconst parent = dirname(current);\n\t\t\tif (parent === current) throw error;\n\t\t\tmissingSegments.push(basename(current));\n\t\t\tcurrent = parent;\n\t\t}\n\t}\n}\n\nexport async function resolveAllowedFilePath(inputPath: string, allowedRoot: string): Promise<string> {\n\treturn resolveAllowedExistingPath(inputPath, allowedRoot);\n}\n\n/** Resolve an existing file or directory while enforcing the realpath root boundary. */\nexport async function resolveAllowedExistingPath(inputPath: string, allowedRoot: string): Promise<string> {\n\tconst rootReal = await realpath(allowedRoot);\n\tconst candidate = resolve(rootReal, inputPath);\n\tassertInsideRoot(rootReal, candidate);\n\tconst targetReal = await realpath(candidate);\n\tassertInsideRoot(rootReal, targetReal);\n\treturn targetReal;\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import type { AgentTool } from "@di-code/agent";
2
- import { type Static, Type } from "@di-code/ai";
2
+ import { type Static, type ToolResultContent, Type } from "@di-code/ai";
3
3
  export declare const DEFAULT_READ_MAX_LINES = 2000;
4
4
  export declare const DEFAULT_READ_MAX_BYTES: number;
5
5
  export declare const readParameters: Type.TObject<{
@@ -12,6 +12,6 @@ export interface ReadToolOptions {
12
12
  readonly maxLines?: number;
13
13
  readonly maxBytes?: number;
14
14
  }
15
- export type ReadTool = AgentTool<typeof readParameters>;
15
+ export type ReadTool = AgentTool<typeof readParameters, ToolResultContent[]>;
16
16
  export declare function createReadTool(allowedRoot: string, options?: ReadToolOptions): ReadTool;
17
17
  //# sourceMappingURL=read.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,KAAK,MAAM,EAA0B,IAAI,EAAE,MAAM,aAAa,CAAC;AAGxE,eAAO,MAAM,sBAAsB,OAAQ,CAAC;AAC5C,eAAO,MAAM,sBAAsB,QAAY,CAAC;AAEhD,eAAO,MAAM,cAAc;;;;EAIzB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AAE3D,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,cAAc,CAAC,CAAC;AAuGxD,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,QAAQ,CA8B3F"}
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,iBAAiB,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGxE,eAAO,MAAM,sBAAsB,OAAQ,CAAC;AAC5C,eAAO,MAAM,sBAAsB,QAAY,CAAC;AAEhD,eAAO,MAAM,cAAc;;;;EAIzB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AAE3D,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,cAAc,EAAE,iBAAiB,EAAE,CAAC,CAAC;AAuG7E,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,QAAQ,CA8B3F"}
@@ -1 +1 @@
1
- {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport type { AgentTool } from \"@di-code/agent\";\nimport { type Static, type ToolResultContent, Type } from \"@di-code/ai\";\nimport { resolveAllowedFilePath } from \"./path-boundary.ts\";\n\nexport const DEFAULT_READ_MAX_LINES = 2_000;\nexport const DEFAULT_READ_MAX_BYTES = 50 * 1024;\n\nexport const readParameters = Type.Object({\n\tpath: Type.String({ minLength: 1 }),\n\toffset: Type.Optional(Type.Integer({ minimum: 1 })),\n\tlimit: Type.Optional(Type.Integer({ minimum: 1 })),\n});\n\nexport type ReadParameters = Static<typeof readParameters>;\n\nexport interface ReadToolOptions {\n\treadonly maxLines?: number;\n\treadonly maxBytes?: number;\n}\n\nexport type ReadTool = AgentTool<typeof readParameters>;\n\ninterface TextWindow {\n\treadonly content: string;\n\treadonly startLine: number;\n\treadonly endLine: number;\n\treadonly totalLines: number;\n\treadonly truncatedBy: \"limit\" | \"lines\" | \"bytes\" | null;\n}\n\nfunction assertPositiveInteger(name: string, value: number | undefined): void {\n\tif (value !== undefined && (!Number.isInteger(value) || value < 1)) {\n\t\tthrow new Error(`${name} must be a positive integer`);\n\t}\n}\n\nfunction splitLines(text: string): string[] {\n\tif (text.length === 0) return [];\n\tconst lines = text.split(\"\\n\");\n\tif (text.endsWith(\"\\n\")) lines.pop();\n\treturn lines;\n}\n\nfunction selectUserWindow(lines: readonly string[], offset: number, limit: number | undefined): TextWindow {\n\tif (lines.length === 0) {\n\t\tif (offset > 1) throw new Error(`Offset ${offset} is beyond end of file (0 lines total)`);\n\t\treturn { content: \"\", startLine: 1, endLine: 0, totalLines: 0, truncatedBy: null };\n\t}\n\n\tconst startIndex = offset - 1;\n\tif (startIndex >= lines.length) {\n\t\tthrow new Error(`Offset ${offset} is beyond end of file (${lines.length} lines total)`);\n\t}\n\n\tconst available = lines.slice(startIndex);\n\tconst selected = limit === undefined ? available : available.slice(0, limit);\n\tconst endLine = offset + selected.length - 1;\n\treturn {\n\t\tcontent: selected.join(\"\\n\"),\n\t\tstartLine: offset,\n\t\tendLine,\n\t\ttotalLines: lines.length,\n\t\ttruncatedBy: limit !== undefined && selected.length < available.length ? \"limit\" : null,\n\t};\n}\n\nfunction applyOutputLimits(window: TextWindow, maxLines: number, maxBytes: number): TextWindow {\n\tif (window.content === \"\") return window;\n\tconst lines = window.content.split(\"\\n\");\n\tconst selected: string[] = [];\n\tlet bytes = 0;\n\tlet truncatedBy: \"lines\" | \"bytes\" | null = null;\n\n\tfor (const line of lines) {\n\t\tif (selected.length >= maxLines) {\n\t\t\ttruncatedBy = \"lines\";\n\t\t\tbreak;\n\t\t}\n\t\tconst separatorBytes = selected.length === 0 ? 0 : 1;\n\t\tconst nextBytes = bytes + separatorBytes + Buffer.byteLength(line, \"utf8\");\n\t\tif (nextBytes > maxBytes) {\n\t\t\tif (selected.length === 0) throw new Error(\"A single line exceeds the read byte limit\");\n\t\t\ttruncatedBy = \"bytes\";\n\t\t\tbreak;\n\t\t}\n\t\tselected.push(line);\n\t\tbytes = nextBytes;\n\t}\n\n\tif (truncatedBy === null && selected.length < lines.length) truncatedBy = \"lines\";\n\tif (truncatedBy === null) return window;\n\treturn {\n\t\t...window,\n\t\tcontent: selected.join(\"\\n\"),\n\t\tendLine: window.startLine + selected.length - 1,\n\t\ttruncatedBy,\n\t};\n}\n\nfunction formatByteLimit(maxBytes: number): string {\n\tif (maxBytes % 1024 === 0) return `${maxBytes / 1024} KiB`;\n\treturn `${maxBytes} bytes`;\n}\n\nfunction appendContinuation(window: TextWindow, maxBytes: number): string {\n\tif (window.truncatedBy === null) return window.content;\n\tconst nextOffset = window.endLine + 1;\n\tif (window.truncatedBy === \"limit\") {\n\t\tconst remaining = window.totalLines - window.endLine;\n\t\treturn `${window.content}\\n\\n[${remaining} more lines in file. Use offset=${nextOffset} to continue.]`;\n\t}\n\tconst reason = window.truncatedBy === \"bytes\" ? ` (${formatByteLimit(maxBytes)} limit)` : \"\";\n\treturn `${window.content}\\n\\n[Showing lines ${window.startLine}-${window.endLine} of ${window.totalLines}${reason}. Use offset=${nextOffset} to continue.]`;\n}\n\nfunction containsNulByte(buffer: Buffer): boolean {\n\tconst sampleLength = Math.min(buffer.length, 8 * 1024);\n\tfor (let index = 0; index < sampleLength; index++) {\n\t\tif (buffer[index] === 0) return true;\n\t}\n\treturn false;\n}\n\nexport function createReadTool(allowedRoot: string, options: ReadToolOptions = {}): ReadTool {\n\tconst maxLines = options.maxLines ?? DEFAULT_READ_MAX_LINES;\n\tconst maxBytes = options.maxBytes ?? DEFAULT_READ_MAX_BYTES;\n\tassertPositiveInteger(\"maxLines\", maxLines);\n\tassertPositiveInteger(\"maxBytes\", maxBytes);\n\n\treturn {\n\t\tname: \"read\",\n\t\tdescription: \"Read a UTF-8 text file inside the allowed root. Use offset and limit for large files.\",\n\t\tparameters: readParameters,\n\t\tasync execute(_toolCallId, parameters, signal): Promise<ToolResultContent[]> {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tif (parameters.path.length === 0) throw new Error(\"path must not be empty\");\n\t\t\tassertPositiveInteger(\"offset\", parameters.offset);\n\t\t\tassertPositiveInteger(\"limit\", parameters.limit);\n\t\t\tconst absolutePath = await resolveAllowedFilePath(parameters.path, allowedRoot);\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tconst buffer = await readFile(absolutePath);\n\t\t\tif (containsNulByte(buffer)) {\n\t\t\t\tthrow new Error(\"Binary files are not supported by read\");\n\t\t\t}\n\t\t\tconst userWindow = selectUserWindow(\n\t\t\t\tsplitLines(buffer.toString(\"utf8\")),\n\t\t\t\tparameters.offset ?? 1,\n\t\t\t\tparameters.limit,\n\t\t\t);\n\t\t\tconst boundedWindow = applyOutputLimits(userWindow, maxLines, maxBytes);\n\t\t\treturn [{ type: \"text\", text: appendContinuation(boundedWindow, maxBytes) }];\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/core/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport type { AgentTool } from \"@di-code/agent\";\nimport { type Static, type ToolResultContent, Type } from \"@di-code/ai\";\nimport { resolveAllowedFilePath } from \"./path-boundary.ts\";\n\nexport const DEFAULT_READ_MAX_LINES = 2_000;\nexport const DEFAULT_READ_MAX_BYTES = 50 * 1024;\n\nexport const readParameters = Type.Object({\n\tpath: Type.String({ minLength: 1 }),\n\toffset: Type.Optional(Type.Integer({ minimum: 1 })),\n\tlimit: Type.Optional(Type.Integer({ minimum: 1 })),\n});\n\nexport type ReadParameters = Static<typeof readParameters>;\n\nexport interface ReadToolOptions {\n\treadonly maxLines?: number;\n\treadonly maxBytes?: number;\n}\n\nexport type ReadTool = AgentTool<typeof readParameters, ToolResultContent[]>;\n\ninterface TextWindow {\n\treadonly content: string;\n\treadonly startLine: number;\n\treadonly endLine: number;\n\treadonly totalLines: number;\n\treadonly truncatedBy: \"limit\" | \"lines\" | \"bytes\" | null;\n}\n\nfunction assertPositiveInteger(name: string, value: number | undefined): void {\n\tif (value !== undefined && (!Number.isInteger(value) || value < 1)) {\n\t\tthrow new Error(`${name} must be a positive integer`);\n\t}\n}\n\nfunction splitLines(text: string): string[] {\n\tif (text.length === 0) return [];\n\tconst lines = text.split(\"\\n\");\n\tif (text.endsWith(\"\\n\")) lines.pop();\n\treturn lines;\n}\n\nfunction selectUserWindow(lines: readonly string[], offset: number, limit: number | undefined): TextWindow {\n\tif (lines.length === 0) {\n\t\tif (offset > 1) throw new Error(`Offset ${offset} is beyond end of file (0 lines total)`);\n\t\treturn { content: \"\", startLine: 1, endLine: 0, totalLines: 0, truncatedBy: null };\n\t}\n\n\tconst startIndex = offset - 1;\n\tif (startIndex >= lines.length) {\n\t\tthrow new Error(`Offset ${offset} is beyond end of file (${lines.length} lines total)`);\n\t}\n\n\tconst available = lines.slice(startIndex);\n\tconst selected = limit === undefined ? available : available.slice(0, limit);\n\tconst endLine = offset + selected.length - 1;\n\treturn {\n\t\tcontent: selected.join(\"\\n\"),\n\t\tstartLine: offset,\n\t\tendLine,\n\t\ttotalLines: lines.length,\n\t\ttruncatedBy: limit !== undefined && selected.length < available.length ? \"limit\" : null,\n\t};\n}\n\nfunction applyOutputLimits(window: TextWindow, maxLines: number, maxBytes: number): TextWindow {\n\tif (window.content === \"\") return window;\n\tconst lines = window.content.split(\"\\n\");\n\tconst selected: string[] = [];\n\tlet bytes = 0;\n\tlet truncatedBy: \"lines\" | \"bytes\" | null = null;\n\n\tfor (const line of lines) {\n\t\tif (selected.length >= maxLines) {\n\t\t\ttruncatedBy = \"lines\";\n\t\t\tbreak;\n\t\t}\n\t\tconst separatorBytes = selected.length === 0 ? 0 : 1;\n\t\tconst nextBytes = bytes + separatorBytes + Buffer.byteLength(line, \"utf8\");\n\t\tif (nextBytes > maxBytes) {\n\t\t\tif (selected.length === 0) throw new Error(\"A single line exceeds the read byte limit\");\n\t\t\ttruncatedBy = \"bytes\";\n\t\t\tbreak;\n\t\t}\n\t\tselected.push(line);\n\t\tbytes = nextBytes;\n\t}\n\n\tif (truncatedBy === null && selected.length < lines.length) truncatedBy = \"lines\";\n\tif (truncatedBy === null) return window;\n\treturn {\n\t\t...window,\n\t\tcontent: selected.join(\"\\n\"),\n\t\tendLine: window.startLine + selected.length - 1,\n\t\ttruncatedBy,\n\t};\n}\n\nfunction formatByteLimit(maxBytes: number): string {\n\tif (maxBytes % 1024 === 0) return `${maxBytes / 1024} KiB`;\n\treturn `${maxBytes} bytes`;\n}\n\nfunction appendContinuation(window: TextWindow, maxBytes: number): string {\n\tif (window.truncatedBy === null) return window.content;\n\tconst nextOffset = window.endLine + 1;\n\tif (window.truncatedBy === \"limit\") {\n\t\tconst remaining = window.totalLines - window.endLine;\n\t\treturn `${window.content}\\n\\n[${remaining} more lines in file. Use offset=${nextOffset} to continue.]`;\n\t}\n\tconst reason = window.truncatedBy === \"bytes\" ? ` (${formatByteLimit(maxBytes)} limit)` : \"\";\n\treturn `${window.content}\\n\\n[Showing lines ${window.startLine}-${window.endLine} of ${window.totalLines}${reason}. Use offset=${nextOffset} to continue.]`;\n}\n\nfunction containsNulByte(buffer: Buffer): boolean {\n\tconst sampleLength = Math.min(buffer.length, 8 * 1024);\n\tfor (let index = 0; index < sampleLength; index++) {\n\t\tif (buffer[index] === 0) return true;\n\t}\n\treturn false;\n}\n\nexport function createReadTool(allowedRoot: string, options: ReadToolOptions = {}): ReadTool {\n\tconst maxLines = options.maxLines ?? DEFAULT_READ_MAX_LINES;\n\tconst maxBytes = options.maxBytes ?? DEFAULT_READ_MAX_BYTES;\n\tassertPositiveInteger(\"maxLines\", maxLines);\n\tassertPositiveInteger(\"maxBytes\", maxBytes);\n\n\treturn {\n\t\tname: \"read\",\n\t\tdescription: \"Read a UTF-8 text file inside the allowed root. Use offset and limit for large files.\",\n\t\tparameters: readParameters,\n\t\tasync execute(_toolCallId, parameters, signal): Promise<ToolResultContent[]> {\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tif (parameters.path.length === 0) throw new Error(\"path must not be empty\");\n\t\t\tassertPositiveInteger(\"offset\", parameters.offset);\n\t\t\tassertPositiveInteger(\"limit\", parameters.limit);\n\t\t\tconst absolutePath = await resolveAllowedFilePath(parameters.path, allowedRoot);\n\t\t\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n\t\t\tconst buffer = await readFile(absolutePath);\n\t\t\tif (containsNulByte(buffer)) {\n\t\t\t\tthrow new Error(\"Binary files are not supported by read\");\n\t\t\t}\n\t\t\tconst userWindow = selectUserWindow(\n\t\t\t\tsplitLines(buffer.toString(\"utf8\")),\n\t\t\t\tparameters.offset ?? 1,\n\t\t\t\tparameters.limit,\n\t\t\t);\n\t\t\tconst boundedWindow = applyOutputLimits(userWindow, maxLines, maxBytes);\n\t\t\treturn [{ type: \"text\", text: appendContinuation(boundedWindow, maxBytes) }];\n\t\t},\n\t};\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import type { AgentTool } from "@di-code/agent";
2
- import { type Static, Type } from "@di-code/ai";
2
+ import { type Static, type ToolResultContent, Type } from "@di-code/ai";
3
3
  export declare const writeParameters: Type.TObject<{
4
4
  path: Type.TString;
5
5
  content: Type.TString;
@@ -12,6 +12,6 @@ export interface WriteOperations {
12
12
  export interface WriteToolOptions {
13
13
  readonly operations?: WriteOperations;
14
14
  }
15
- export type WriteTool = AgentTool<typeof writeParameters>;
15
+ export type WriteTool = AgentTool<typeof writeParameters, ToolResultContent[]>;
16
16
  export declare function createWriteTool(allowedRoot: string, options?: WriteToolOptions): WriteTool;
17
17
  //# sourceMappingURL=write.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,KAAK,MAAM,EAA0B,IAAI,EAAE,MAAM,aAAa,CAAC;AAIxE,eAAO,MAAM,eAAe;;;EAG1B,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC/B,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;CACtC;AAED,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,eAAe,CAAC,CAAC;AAe1D,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS,CAwB9F"}
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,iBAAiB,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAIxE,eAAO,MAAM,eAAe;;;EAG1B,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC/B,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;CACtC;AAED,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,eAAe,EAAE,iBAAiB,EAAE,CAAC,CAAC;AAe/E,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS,CAwB9F"}
@@ -1 +1 @@
1
- {"version":3,"file":"write.js","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,OAAO,EAAE,SAAS,IAAI,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAuC,IAAI,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAEhE,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,CAAC;IAC1C,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACnC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE;CACtB,CAAC,CAAC;AAeH,MAAM,sBAAsB,GAAoB;IAC/C,KAAK,CAAC,KAAK,CAAC,SAAS;QACpB,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IACD,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO;QAChC,MAAM,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9C,CAAC;CACD,CAAC;AAEF,SAAS,cAAc,CAAC,MAA+B;IACtD,IAAI,MAAM,EAAE,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,WAAmB,EAAE,UAA4B,EAAE;IAClF,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,sBAAsB,CAAC;IAEhE,OAAO;QACN,IAAI,EAAE,OAAO;QACb,WAAW,EAAE,2EAA2E;QACxF,UAAU,EAAE,eAAe;QAC3B,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM;YAC5C,cAAc,CAAC,MAAM,CAAC,CAAC;YACvB,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;YAC5E,MAAM,YAAY,GAAG,MAAM,0BAA0B,CAAC,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YAEpF,OAAO,qBAAqB,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;gBACrD,cAAc,CAAC,MAAM,CAAC,CAAC;gBACvB,MAAM,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;gBAC9C,cAAc,CAAC,MAAM,CAAC,CAAC;gBACvB,MAAM,UAAU,CAAC,SAAS,CAAC,YAAY,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;gBAC7D,cAAc,CAAC,MAAM,CAAC,CAAC;gBAEvB,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC5D,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,KAAK,aAAa,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC5F,CAAC,CAAC,CAAC;QACJ,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["import { mkdir as fsMkdir, writeFile as fsWriteFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport type { AgentTool } from \"@di-code/agent\";\nimport { type Static, type ToolResultContent, Type } from \"@di-code/ai\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport { resolveAllowedMutationPath } from \"./path-boundary.ts\";\n\nexport const writeParameters = Type.Object({\n\tpath: Type.String({ minLength: 1 }),\n\tcontent: Type.String(),\n});\n\nexport type WriteParameters = Static<typeof writeParameters>;\n\nexport interface WriteOperations {\n\tmkdir(directory: string): Promise<void>;\n\twriteFile(filePath: string, content: string): Promise<void>;\n}\n\nexport interface WriteToolOptions {\n\treadonly operations?: WriteOperations;\n}\n\nexport type WriteTool = AgentTool<typeof writeParameters>;\n\nconst defaultWriteOperations: WriteOperations = {\n\tasync mkdir(directory) {\n\t\tawait fsMkdir(directory, { recursive: true });\n\t},\n\tasync writeFile(filePath, content) {\n\t\tawait fsWriteFile(filePath, content, \"utf8\");\n\t},\n};\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n}\n\nexport function createWriteTool(allowedRoot: string, options: WriteToolOptions = {}): WriteTool {\n\tconst operations = options.operations ?? defaultWriteOperations;\n\n\treturn {\n\t\tname: \"write\",\n\t\tdescription: \"Create or completely overwrite a UTF-8 text file inside the allowed root.\",\n\t\tparameters: writeParameters,\n\t\tasync execute(_toolCallId, parameters, signal): Promise<ToolResultContent[]> {\n\t\t\tthrowIfAborted(signal);\n\t\t\tif (parameters.path.length === 0) throw new Error(\"path must not be empty\");\n\t\t\tconst absolutePath = await resolveAllowedMutationPath(parameters.path, allowedRoot);\n\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tawait operations.mkdir(dirname(absolutePath));\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tawait operations.writeFile(absolutePath, parameters.content);\n\t\t\t\tthrowIfAborted(signal);\n\n\t\t\t\tconst bytes = Buffer.byteLength(parameters.content, \"utf8\");\n\t\t\t\treturn [{ type: \"text\", text: `Successfully wrote ${bytes} bytes to ${parameters.path}` }];\n\t\t\t});\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../../src/core/tools/write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,OAAO,EAAE,SAAS,IAAI,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAuC,IAAI,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAEhE,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,CAAC;IAC1C,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACnC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE;CACtB,CAAC,CAAC;AAeH,MAAM,sBAAsB,GAAoB;IAC/C,KAAK,CAAC,KAAK,CAAC,SAAS;QACpB,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IACD,KAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO;QAChC,MAAM,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9C,CAAC;CACD,CAAC;AAEF,SAAS,cAAc,CAAC,MAA+B;IACtD,IAAI,MAAM,EAAE,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,WAAmB,EAAE,UAA4B,EAAE;IAClF,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,sBAAsB,CAAC;IAEhE,OAAO;QACN,IAAI,EAAE,OAAO;QACb,WAAW,EAAE,2EAA2E;QACxF,UAAU,EAAE,eAAe;QAC3B,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM;YAC5C,cAAc,CAAC,MAAM,CAAC,CAAC;YACvB,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;YAC5E,MAAM,YAAY,GAAG,MAAM,0BAA0B,CAAC,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YAEpF,OAAO,qBAAqB,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;gBACrD,cAAc,CAAC,MAAM,CAAC,CAAC;gBACvB,MAAM,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;gBAC9C,cAAc,CAAC,MAAM,CAAC,CAAC;gBACvB,MAAM,UAAU,CAAC,SAAS,CAAC,YAAY,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;gBAC7D,cAAc,CAAC,MAAM,CAAC,CAAC;gBAEvB,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC5D,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,KAAK,aAAa,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC5F,CAAC,CAAC,CAAC;QACJ,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["import { mkdir as fsMkdir, writeFile as fsWriteFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport type { AgentTool } from \"@di-code/agent\";\nimport { type Static, type ToolResultContent, Type } from \"@di-code/ai\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport { resolveAllowedMutationPath } from \"./path-boundary.ts\";\n\nexport const writeParameters = Type.Object({\n\tpath: Type.String({ minLength: 1 }),\n\tcontent: Type.String(),\n});\n\nexport type WriteParameters = Static<typeof writeParameters>;\n\nexport interface WriteOperations {\n\tmkdir(directory: string): Promise<void>;\n\twriteFile(filePath: string, content: string): Promise<void>;\n}\n\nexport interface WriteToolOptions {\n\treadonly operations?: WriteOperations;\n}\n\nexport type WriteTool = AgentTool<typeof writeParameters, ToolResultContent[]>;\n\nconst defaultWriteOperations: WriteOperations = {\n\tasync mkdir(directory) {\n\t\tawait fsMkdir(directory, { recursive: true });\n\t},\n\tasync writeFile(filePath, content) {\n\t\tawait fsWriteFile(filePath, content, \"utf8\");\n\t},\n};\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n}\n\nexport function createWriteTool(allowedRoot: string, options: WriteToolOptions = {}): WriteTool {\n\tconst operations = options.operations ?? defaultWriteOperations;\n\n\treturn {\n\t\tname: \"write\",\n\t\tdescription: \"Create or completely overwrite a UTF-8 text file inside the allowed root.\",\n\t\tparameters: writeParameters,\n\t\tasync execute(_toolCallId, parameters, signal): Promise<ToolResultContent[]> {\n\t\t\tthrowIfAborted(signal);\n\t\t\tif (parameters.path.length === 0) throw new Error(\"path must not be empty\");\n\t\t\tconst absolutePath = await resolveAllowedMutationPath(parameters.path, allowedRoot);\n\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tawait operations.mkdir(dirname(absolutePath));\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tawait operations.writeFile(absolutePath, parameters.content);\n\t\t\t\tthrowIfAborted(signal);\n\n\t\t\t\tconst bytes = Buffer.byteLength(parameters.content, \"utf8\");\n\t\t\t\treturn [{ type: \"text\", text: `Successfully wrote ${bytes} bytes to ${parameters.path}` }];\n\t\t\t});\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,3 @@
1
+ /** Returns a stable, non-path-revealing directory name for workspace-scoped user data. */
2
+ export declare function workspaceStorageKey(cwd: string): string;
3
+ //# sourceMappingURL=user-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-data.d.ts","sourceRoot":"","sources":["../../src/core/user-data.ts"],"names":[],"mappings":"AAGA,0FAA0F;AAC1F,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGvD"}
@@ -0,0 +1,8 @@
1
+ import { createHash } from "node:crypto";
2
+ import { resolve } from "node:path";
3
+ /** Returns a stable, non-path-revealing directory name for workspace-scoped user data. */
4
+ export function workspaceStorageKey(cwd) {
5
+ const normalized = process.platform === "win32" ? resolve(cwd).toLowerCase() : resolve(cwd);
6
+ return createHash("sha256").update(normalized).digest("hex");
7
+ }
8
+ //# sourceMappingURL=user-data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-data.js","sourceRoot":"","sources":["../../src/core/user-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,0FAA0F;AAC1F,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC9C,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5F,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { resolve } from \"node:path\";\n\n/** Returns a stable, non-path-revealing directory name for workspace-scoped user data. */\nexport function workspaceStorageKey(cwd: string): string {\n\tconst normalized = process.platform === \"win32\" ? resolve(cwd).toLowerCase() : resolve(cwd);\n\treturn createHash(\"sha256\").update(normalized).digest(\"hex\");\n}\n"]}
package/dist/entry.js CHANGED
@@ -1,9 +1,22 @@
1
1
  #!/usr/bin/env node
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { createInterface } from "node:readline/promises";
2
5
  import { ProcessTerminal } from "@di-code/tui";
3
6
  import packageMetadata from "../package.json" with { type: "json" };
4
7
  import { runMain } from "./main.js";
5
8
  import { runProviderOnboarding, shouldStartProviderOnboarding } from "./provider-onboarding.js";
6
9
  import { loadStartupConfiguration, resolveStartupArgs, resolveStartupRuntime } from "./startup.js";
10
+ async function promptProjectTrust(cwd) {
11
+ const readline = createInterface({ input: process.stdin, output: process.stdout });
12
+ try {
13
+ const answer = await readline.question(`Trust project-local Skills, plugins, and extensions in ${cwd}? [y/N] `);
14
+ return /^(?:y|yes)$/i.test(answer.trim());
15
+ }
16
+ finally {
17
+ readline.close();
18
+ }
19
+ }
7
20
  try {
8
21
  const configuration = await loadStartupConfiguration(process.cwd());
9
22
  process.exitCode = await runMain(resolveStartupArgs(process.argv.slice(2)), {
@@ -11,10 +24,17 @@ try {
11
24
  createRuntime: (command) => {
12
25
  const isInteractiveTerminal = Boolean(process.stdin.isTTY && process.stdout.isTTY);
13
26
  if (shouldStartProviderOnboarding(command, isInteractiveTerminal, configuration)) {
14
- return runProviderOnboarding({ configuration, terminal: new ProcessTerminal() });
27
+ return runProviderOnboarding({
28
+ configuration,
29
+ terminal: new ProcessTerminal(),
30
+ agentDir: join(homedir(), ".di-code"),
31
+ });
15
32
  }
16
- return resolveStartupRuntime(configuration.environment, configuration.providers);
33
+ return resolveStartupRuntime(configuration.environment, configuration.providers, configuration.defaults);
17
34
  },
35
+ startupConfiguration: configuration,
36
+ isInteractiveTerminal: Boolean(process.stdin.isTTY && process.stdout.isTTY),
37
+ promptProjectTrust,
18
38
  stdout: (text) => process.stdout.write(text),
19
39
  stderr: (text) => process.stderr.write(text),
20
40
  });