@vraxis/agent-v 0.5.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/AGENTS.md +14 -2
  2. package/ARCHITECTURE.md +29 -0
  3. package/CHANGELOG.md +17 -0
  4. package/README.md +114 -16
  5. package/compatibility.json +37 -1
  6. package/dist/adapters/ai-sdk/index.d.ts.map +1 -1
  7. package/dist/adapters/ai-sdk/index.js +2 -1
  8. package/dist/adapters/ai-sdk/index.js.map +1 -1
  9. package/dist/adapters/providers/index.d.ts +100 -0
  10. package/dist/adapters/providers/index.d.ts.map +1 -0
  11. package/dist/adapters/providers/index.js +195 -0
  12. package/dist/adapters/providers/index.js.map +1 -0
  13. package/dist/core/agent-v.d.ts.map +1 -1
  14. package/dist/core/agent-v.js +6 -0
  15. package/dist/core/agent-v.js.map +1 -1
  16. package/dist/core/contracts.d.ts +11 -0
  17. package/dist/core/contracts.d.ts.map +1 -1
  18. package/dist/core/extensions.d.ts +4 -0
  19. package/dist/core/extensions.d.ts.map +1 -1
  20. package/dist/core/extensions.js +2 -0
  21. package/dist/core/extensions.js.map +1 -1
  22. package/dist/core/memory.d.ts +9 -1
  23. package/dist/core/memory.d.ts.map +1 -1
  24. package/dist/core/memory.js +17 -0
  25. package/dist/core/memory.js.map +1 -1
  26. package/dist/core/version.d.ts +1 -1
  27. package/dist/core/version.js +1 -1
  28. package/dist/node/credentials.d.ts +30 -0
  29. package/dist/node/credentials.d.ts.map +1 -0
  30. package/dist/node/credentials.js +104 -0
  31. package/dist/node/credentials.js.map +1 -0
  32. package/dist/node/doctor.js +6 -6
  33. package/dist/node/doctor.js.map +1 -1
  34. package/dist/node/index.d.ts +3 -0
  35. package/dist/node/index.d.ts.map +1 -1
  36. package/dist/node/index.js +3 -0
  37. package/dist/node/index.js.map +1 -1
  38. package/dist/node/skill-inventory.d.ts +63 -0
  39. package/dist/node/skill-inventory.d.ts.map +1 -0
  40. package/dist/node/skill-inventory.js +328 -0
  41. package/dist/node/skill-inventory.js.map +1 -0
  42. package/dist/node/skills.d.ts.map +1 -1
  43. package/dist/node/skills.js +10 -0
  44. package/dist/node/skills.js.map +1 -1
  45. package/dist/runtime/index.d.ts +55 -0
  46. package/dist/runtime/index.d.ts.map +1 -0
  47. package/dist/runtime/index.js +94 -0
  48. package/dist/runtime/index.js.map +1 -0
  49. package/dist/skills/index.d.ts +37 -0
  50. package/dist/skills/index.d.ts.map +1 -0
  51. package/dist/skills/index.js +128 -0
  52. package/dist/skills/index.js.map +1 -0
  53. package/dist/tools/approval.d.ts +26 -0
  54. package/dist/tools/approval.d.ts.map +1 -0
  55. package/dist/tools/approval.js +23 -0
  56. package/dist/tools/approval.js.map +1 -0
  57. package/dist/tools/browser.d.ts +25 -0
  58. package/dist/tools/browser.d.ts.map +1 -0
  59. package/dist/tools/browser.js +118 -0
  60. package/dist/tools/browser.js.map +1 -0
  61. package/dist/tools/http.d.ts +9 -0
  62. package/dist/tools/http.d.ts.map +1 -0
  63. package/dist/tools/http.js +123 -0
  64. package/dist/tools/http.js.map +1 -0
  65. package/dist/tools/index.d.ts +6 -0
  66. package/dist/tools/index.d.ts.map +1 -0
  67. package/dist/tools/index.js +6 -0
  68. package/dist/tools/index.js.map +1 -0
  69. package/dist/tools/names.d.ts +19 -0
  70. package/dist/tools/names.d.ts.map +1 -0
  71. package/dist/tools/names.js +19 -0
  72. package/dist/tools/names.js.map +1 -0
  73. package/dist/tools/node/index.d.ts +20 -0
  74. package/dist/tools/node/index.d.ts.map +1 -0
  75. package/dist/tools/node/index.js +406 -0
  76. package/dist/tools/node/index.js.map +1 -0
  77. package/dist/tools/pure.d.ts +13 -0
  78. package/dist/tools/pure.d.ts.map +1 -0
  79. package/dist/tools/pure.js +170 -0
  80. package/dist/tools/pure.js.map +1 -0
  81. package/examples/providers.ts +35 -0
  82. package/examples/runtime-kit.ts +25 -0
  83. package/examples/skill-inventory.ts +6 -0
  84. package/examples/smoke.ts +8 -0
  85. package/llms.txt +1 -1
  86. package/package.json +32 -15
  87. package/skills/agent-v/SKILL.md +8 -2
  88. package/skills/agent-v/references/integration-patterns.md +11 -0
@@ -0,0 +1,123 @@
1
+ import { defineOutput, defineTool } from "../core/index.js";
2
+ import { standardToolNames } from "./names.js";
3
+ async function readBoundedBody(response, limit) {
4
+ if (!response.body)
5
+ return { body: "", truncated: false };
6
+ const reader = response.body.getReader();
7
+ const chunks = [];
8
+ let total = 0;
9
+ let truncated = false;
10
+ try {
11
+ while (true) {
12
+ const next = await reader.read();
13
+ if (next.done)
14
+ break;
15
+ const remaining = limit - total;
16
+ if (remaining <= 0) {
17
+ truncated = true;
18
+ break;
19
+ }
20
+ const chunk = next.value.byteLength > remaining ? next.value.slice(0, remaining) : next.value;
21
+ chunks.push(chunk);
22
+ total += chunk.byteLength;
23
+ if (chunk.byteLength < next.value.byteLength) {
24
+ truncated = true;
25
+ break;
26
+ }
27
+ }
28
+ }
29
+ finally {
30
+ if (truncated)
31
+ await reader.cancel().catch(() => undefined);
32
+ reader.releaseLock();
33
+ }
34
+ const combined = new Uint8Array(total);
35
+ let offset = 0;
36
+ for (const chunk of chunks) {
37
+ combined.set(chunk, offset);
38
+ offset += chunk.byteLength;
39
+ }
40
+ return { body: new TextDecoder().decode(combined), truncated };
41
+ }
42
+ function allowedUrl(raw, allowedHosts) {
43
+ if (typeof raw !== "string" || !raw.trim())
44
+ throw new TypeError("url must be a non-empty string.");
45
+ let url;
46
+ try {
47
+ url = new URL(raw);
48
+ }
49
+ catch {
50
+ throw new TypeError("url must be a valid URL.");
51
+ }
52
+ if (url.username || url.password)
53
+ throw new TypeError("URLs containing credentials are not allowed.");
54
+ const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
55
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback))
56
+ throw new TypeError("Remote requests must use HTTPS.");
57
+ if (!allowedHosts.has(url.host.toLowerCase()) && !allowedHosts.has(url.hostname.toLowerCase()))
58
+ throw new TypeError(`Host ${url.host} is not allowed.`);
59
+ return url;
60
+ }
61
+ export function createHttpFetchTool(options) {
62
+ const allowedHosts = new Set(options.allowedHosts.map((host) => host.trim().toLowerCase()).filter(Boolean));
63
+ if (!allowedHosts.size)
64
+ throw new TypeError("http-fetch requires at least one allowed host.");
65
+ const performFetch = options.fetch ?? globalThis.fetch;
66
+ if (!performFetch)
67
+ throw new TypeError("No fetch implementation is available.");
68
+ const maxResponseBytes = options.maxResponseBytes ?? 256_000;
69
+ return defineTool({
70
+ name: standardToolNames.httpFetch,
71
+ version: "1.0.0",
72
+ description: "Fetch a bounded HTTPS or loopback resource from an explicit host allowlist.",
73
+ input: defineOutput({
74
+ name: "http-fetch-input",
75
+ jsonSchema: {
76
+ type: "object",
77
+ properties: { url: { type: "string" }, method: { enum: ["GET", "HEAD"] } },
78
+ required: ["url"],
79
+ additionalProperties: false,
80
+ },
81
+ parse(value) {
82
+ if (!value || typeof value !== "object" || Array.isArray(value))
83
+ throw new TypeError("HTTP input must be an object.");
84
+ const input = value;
85
+ const url = allowedUrl(input.url, allowedHosts);
86
+ const method = input.method === undefined ? "GET" : input.method;
87
+ if (method !== "GET" && method !== "HEAD")
88
+ throw new TypeError("method must be GET or HEAD.");
89
+ return { url: url.href, method };
90
+ },
91
+ }),
92
+ output: defineOutput({
93
+ name: "http-fetch-output",
94
+ jsonSchema: { type: "object" },
95
+ parse(value) {
96
+ if (!value || typeof value !== "object" || Array.isArray(value))
97
+ throw new TypeError("HTTP output must be an object.");
98
+ return value;
99
+ },
100
+ }),
101
+ risk: "external-side-effect",
102
+ sideEffect: "none",
103
+ requiredPermissions: ["network:fetch"],
104
+ requiresApproval: true,
105
+ approvalCategory: "network",
106
+ approvalReason: "Allow this agent to send a request to an approved network host.",
107
+ timeoutMs: options.timeoutMs ?? 15_000,
108
+ async execute(input, context) {
109
+ const response = await performFetch(input.url, { method: input.method, redirect: "manual", signal: context.abortSignal });
110
+ const bounded = input.method === "HEAD" ? { body: "", truncated: false } : await readBoundedBody(response, maxResponseBytes);
111
+ return {
112
+ url: input.url,
113
+ status: response.status,
114
+ ok: response.ok,
115
+ contentType: response.headers.get("content-type") ?? "",
116
+ body: bounded.body,
117
+ truncated: bounded.truncated,
118
+ redirectLocation: response.headers.get("location") ?? "",
119
+ };
120
+ },
121
+ });
122
+ }
123
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/tools/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export * from "./approval.js";
2
+ export * from "./browser.js";
3
+ export * from "./http.js";
4
+ export * from "./names.js";
5
+ export * from "./pure.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC"}
@@ -0,0 +1,6 @@
1
+ export * from "./approval.js";
2
+ export * from "./browser.js";
3
+ export * from "./http.js";
4
+ export * from "./names.js";
5
+ export * from "./pure.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC"}
@@ -0,0 +1,19 @@
1
+ export declare const standardToolNames: {
2
+ readonly calculate: "calculate";
3
+ readonly dateTime: "date-time";
4
+ readonly validateJson: "validate-json";
5
+ readonly listDirectory: "list-directory";
6
+ readonly readText: "read-text";
7
+ readonly searchText: "search-text";
8
+ readonly writeText: "write-text";
9
+ readonly applyTextEdits: "apply-text-edits";
10
+ readonly gitStatus: "git-status";
11
+ readonly gitDiff: "git-diff";
12
+ readonly runCommand: "run-command";
13
+ readonly httpFetch: "http-fetch";
14
+ readonly browserSnapshot: "browser-snapshot";
15
+ readonly browserNavigate: "browser-navigate";
16
+ readonly browserClick: "browser-click";
17
+ readonly browserType: "browser-type";
18
+ };
19
+ //# sourceMappingURL=names.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.d.ts","sourceRoot":"","sources":["../../src/tools/names.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;CAiBpB,CAAC"}
@@ -0,0 +1,19 @@
1
+ export const standardToolNames = {
2
+ calculate: "calculate",
3
+ dateTime: "date-time",
4
+ validateJson: "validate-json",
5
+ listDirectory: "list-directory",
6
+ readText: "read-text",
7
+ searchText: "search-text",
8
+ writeText: "write-text",
9
+ applyTextEdits: "apply-text-edits",
10
+ gitStatus: "git-status",
11
+ gitDiff: "git-diff",
12
+ runCommand: "run-command",
13
+ httpFetch: "http-fetch",
14
+ browserSnapshot: "browser-snapshot",
15
+ browserNavigate: "browser-navigate",
16
+ browserClick: "browser-click",
17
+ browserType: "browser-type",
18
+ };
19
+ //# sourceMappingURL=names.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.js","sourceRoot":"","sources":["../../src/tools/names.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,WAAW;IACrB,YAAY,EAAE,eAAe;IAC7B,aAAa,EAAE,gBAAgB;IAC/B,QAAQ,EAAE,WAAW;IACrB,UAAU,EAAE,aAAa;IACzB,SAAS,EAAE,YAAY;IACvB,cAAc,EAAE,kBAAkB;IAClC,SAAS,EAAE,YAAY;IACvB,OAAO,EAAE,UAAU;IACnB,UAAU,EAAE,aAAa;IACzB,SAAS,EAAE,YAAY;IACvB,eAAe,EAAE,kBAAkB;IACnC,eAAe,EAAE,kBAAkB;IACnC,YAAY,EAAE,eAAe;IAC7B,WAAW,EAAE,cAAc;CACnB,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type AgentTool } from "../../core/index.js";
2
+ export interface FilesystemToolOptions {
3
+ rootPath: string;
4
+ maxFileBytes?: number;
5
+ maxSearchFiles?: number;
6
+ maxSearchResults?: number;
7
+ ignoredDirectories?: readonly string[];
8
+ }
9
+ export declare function createFilesystemTools(options: FilesystemToolOptions): Promise<readonly AgentTool[]>;
10
+ export interface DevelopmentToolOptions {
11
+ rootPath: string;
12
+ allowedCommands: readonly string[];
13
+ inheritedEnvironment?: readonly string[];
14
+ environment?: Readonly<Record<string, string>>;
15
+ maxOutputBytes?: number;
16
+ timeoutMs?: number;
17
+ }
18
+ export declare function createDevelopmentTools(options: DevelopmentToolOptions): Promise<readonly AgentTool[]>;
19
+ export declare function createWorkspaceTools(options: FilesystemToolOptions & Partial<Omit<DevelopmentToolOptions, "rootPath">>): Promise<readonly AgentTool[]>;
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/node/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAA4B,KAAK,SAAS,EAAmB,MAAM,qBAAqB,CAAC;AA0DhG,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,SAAS,EAAE,CAAC,CAqMzG;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAYD,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,SAAS,SAAS,EAAE,CAAC,CAuF3G;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,sBAAsB,EAAE,UAAU,CAAC,CAAC,GAAG,OAAO,CAAC,SAAS,SAAS,EAAE,CAAC,CAW5J"}
@@ -0,0 +1,406 @@
1
+ import { execFile } from "node:child_process";
2
+ import { readdir, readFile, realpath, rename, stat, writeFile } from "node:fs/promises";
3
+ import { basename, dirname, isAbsolute, relative, resolve, sep } from "node:path";
4
+ import { promisify } from "node:util";
5
+ import { defineOutput, defineTool } from "../../core/index.js";
6
+ import { standardToolNames } from "../names.js";
7
+ const execFileAsync = promisify(execFile);
8
+ function record(value, label) {
9
+ if (!value || typeof value !== "object" || Array.isArray(value))
10
+ throw new TypeError(`${label} must be an object.`);
11
+ return value;
12
+ }
13
+ function stringField(value, field, options = {}) {
14
+ const selected = record(value, "Tool input")[field];
15
+ if (typeof selected !== "string" || (!options.allowEmpty && !selected.trim()))
16
+ throw new TypeError(`${field} must be ${options.allowEmpty ? "a string" : "a non-empty string"}.`);
17
+ if (selected.includes("\0"))
18
+ throw new TypeError(`${field} contains an invalid null byte.`);
19
+ return selected;
20
+ }
21
+ function within(root, candidate) {
22
+ const path = relative(root, candidate);
23
+ return path === "" || (!path.startsWith(`..${sep}`) && path !== ".." && !isAbsolute(path));
24
+ }
25
+ class BoundedWorkspace {
26
+ rootPath;
27
+ constructor(rootPath) {
28
+ this.rootPath = rootPath;
29
+ }
30
+ static async create(rootPath) {
31
+ const canonical = await realpath(rootPath);
32
+ if (!(await stat(canonical)).isDirectory())
33
+ throw new TypeError("Workspace root must be a directory.");
34
+ return new BoundedWorkspace(canonical);
35
+ }
36
+ lexical(relativePath) {
37
+ if (!relativePath || isAbsolute(relativePath))
38
+ throw new TypeError("Paths must be non-empty and relative to the workspace root.");
39
+ const candidate = resolve(this.rootPath, relativePath);
40
+ if (!within(this.rootPath, candidate))
41
+ throw new TypeError("Path escapes the approved workspace root.");
42
+ return candidate;
43
+ }
44
+ async existing(relativePath) {
45
+ const candidate = this.lexical(relativePath);
46
+ const canonical = await realpath(candidate);
47
+ if (!within(this.rootPath, canonical))
48
+ throw new TypeError("Path resolves outside the approved workspace root.");
49
+ return canonical;
50
+ }
51
+ async writable(relativePath) {
52
+ const candidate = this.lexical(relativePath);
53
+ const canonicalParent = await realpath(dirname(candidate));
54
+ if (!within(this.rootPath, canonicalParent))
55
+ throw new TypeError("Path parent resolves outside the approved workspace root.");
56
+ const existing = await realpath(candidate).catch((error) => {
57
+ if (error.code === "ENOENT")
58
+ return undefined;
59
+ throw error;
60
+ });
61
+ if (existing && !within(this.rootPath, existing))
62
+ throw new TypeError("Path resolves outside the approved workspace root.");
63
+ return candidate;
64
+ }
65
+ }
66
+ export async function createFilesystemTools(options) {
67
+ const workspace = await BoundedWorkspace.create(options.rootPath);
68
+ const maxFileBytes = options.maxFileBytes ?? 1_000_000;
69
+ const maxSearchFiles = options.maxSearchFiles ?? 5_000;
70
+ const maxSearchResults = options.maxSearchResults ?? 200;
71
+ const ignored = new Set(options.ignoredDirectories ?? [".git", "node_modules"]);
72
+ const objectOutput = defineOutput({
73
+ name: "workspace-tool-output",
74
+ jsonSchema: { type: "object" },
75
+ parse(value) { return record(value, "Workspace tool output"); },
76
+ });
77
+ const pathInput = (name) => defineOutput({
78
+ name,
79
+ jsonSchema: { type: "object", properties: { path: { type: "string" } }, required: ["path"], additionalProperties: false },
80
+ parse(value) { return { path: stringField(value, "path") }; },
81
+ });
82
+ const readBoundedText = async (path) => {
83
+ const info = await stat(path);
84
+ if (!info.isFile())
85
+ throw new TypeError("Path must identify a regular file.");
86
+ if (info.size > maxFileBytes)
87
+ throw new RangeError(`File exceeds the ${maxFileBytes} byte read limit.`);
88
+ const content = await readFile(path, "utf8");
89
+ if (content.includes("\0"))
90
+ throw new TypeError("Binary files are not supported.");
91
+ return content;
92
+ };
93
+ return [
94
+ defineTool({
95
+ name: standardToolNames.listDirectory,
96
+ version: "1.0.0",
97
+ description: "List one directory inside the approved workspace root.",
98
+ input: pathInput("list-directory-input"),
99
+ output: objectOutput,
100
+ risk: "read",
101
+ sideEffect: "none",
102
+ requiredPermissions: ["filesystem:read"],
103
+ requiresApproval: false,
104
+ timeoutMs: 5_000,
105
+ async execute({ path }) {
106
+ const directory = await workspace.existing(path);
107
+ const entries = await readdir(directory, { withFileTypes: true });
108
+ if (entries.length > 1_000)
109
+ throw new RangeError("Directory exceeds the 1000-entry listing limit.");
110
+ return {
111
+ path,
112
+ entries: entries.sort((left, right) => left.name.localeCompare(right.name)).map((entry) => ({
113
+ name: entry.name,
114
+ type: entry.isDirectory() ? "directory" : entry.isFile() ? "file" : entry.isSymbolicLink() ? "symlink" : "other",
115
+ })),
116
+ };
117
+ },
118
+ }),
119
+ defineTool({
120
+ name: standardToolNames.readText,
121
+ version: "1.0.0",
122
+ description: "Read one bounded UTF-8 text file inside the approved workspace root.",
123
+ input: pathInput("read-text-input"),
124
+ output: objectOutput,
125
+ risk: "read",
126
+ sideEffect: "none",
127
+ requiredPermissions: ["filesystem:read"],
128
+ requiresApproval: false,
129
+ timeoutMs: 5_000,
130
+ async execute({ path }) {
131
+ const file = await workspace.existing(path);
132
+ return { path, content: await readBoundedText(file) };
133
+ },
134
+ }),
135
+ defineTool({
136
+ name: standardToolNames.searchText,
137
+ version: "1.0.0",
138
+ description: "Search bounded workspace text files without following symlinks.",
139
+ input: defineOutput({
140
+ name: "search-text-input",
141
+ jsonSchema: { type: "object", properties: { query: { type: "string" }, path: { type: "string" } }, required: ["query"], additionalProperties: false },
142
+ parse(value) {
143
+ const input = record(value, "Search input");
144
+ return { query: stringField(value, "query"), path: input.path === undefined ? "." : stringField(value, "path") };
145
+ },
146
+ }),
147
+ output: objectOutput,
148
+ risk: "read",
149
+ sideEffect: "none",
150
+ requiredPermissions: ["filesystem:read"],
151
+ requiresApproval: false,
152
+ timeoutMs: 15_000,
153
+ async execute({ query, path }) {
154
+ const start = await workspace.existing(path);
155
+ const results = [];
156
+ let files = 0;
157
+ const walk = async (directory) => {
158
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
159
+ if (results.length >= maxSearchResults || files >= maxSearchFiles)
160
+ return;
161
+ if (entry.isSymbolicLink() || (entry.isDirectory() && ignored.has(entry.name)))
162
+ continue;
163
+ const child = resolve(directory, entry.name);
164
+ if (entry.isDirectory()) {
165
+ await walk(child);
166
+ continue;
167
+ }
168
+ if (!entry.isFile())
169
+ continue;
170
+ files += 1;
171
+ const info = await stat(child);
172
+ if (info.size > maxFileBytes)
173
+ continue;
174
+ const content = await readFile(child, "utf8").catch(() => "");
175
+ if (content.includes("\0"))
176
+ continue;
177
+ for (const [index, line] of content.split(/\r?\n/).entries()) {
178
+ if (line.includes(query))
179
+ results.push({ path: relative(workspace.rootPath, child), line: index + 1, text: line.slice(0, 500) });
180
+ if (results.length >= maxSearchResults)
181
+ break;
182
+ }
183
+ }
184
+ };
185
+ const info = await stat(start);
186
+ if (info.isDirectory())
187
+ await walk(start);
188
+ else {
189
+ files = 1;
190
+ const content = await readBoundedText(start);
191
+ for (const [index, line] of content.split(/\r?\n/).entries()) {
192
+ if (line.includes(query))
193
+ results.push({ path: relative(workspace.rootPath, start), line: index + 1, text: line.slice(0, 500) });
194
+ if (results.length >= maxSearchResults)
195
+ break;
196
+ }
197
+ }
198
+ return { query, path, filesSearched: files, results, truncated: files >= maxSearchFiles || results.length >= maxSearchResults };
199
+ },
200
+ }),
201
+ defineTool({
202
+ name: standardToolNames.writeText,
203
+ version: "1.0.0",
204
+ description: "Create or replace one UTF-8 text file inside the approved workspace root.",
205
+ input: defineOutput({
206
+ name: "write-text-input",
207
+ jsonSchema: { type: "object", properties: { path: { type: "string" }, content: { type: "string" } }, required: ["path", "content"], additionalProperties: false },
208
+ parse(value) { return { path: stringField(value, "path"), content: stringField(value, "content", { allowEmpty: true }) }; },
209
+ }),
210
+ output: objectOutput,
211
+ risk: "write",
212
+ sideEffect: "idempotent",
213
+ requiredPermissions: ["filesystem:write"],
214
+ requiresApproval: true,
215
+ approvalCategory: "write",
216
+ approvalReason: "Allow this agent to create or replace a file inside the approved workspace.",
217
+ timeoutMs: 5_000,
218
+ async execute({ path, content }) {
219
+ if (new TextEncoder().encode(content).byteLength > maxFileBytes)
220
+ throw new RangeError(`Content exceeds the ${maxFileBytes} byte write limit.`);
221
+ const file = await workspace.writable(path);
222
+ const temporary = resolve(dirname(file), `.${basename(file)}.${crypto.randomUUID()}.tmp`);
223
+ await writeFile(temporary, content, { encoding: "utf8", mode: 0o600 });
224
+ await rename(temporary, file);
225
+ return { path, bytes: new TextEncoder().encode(content).byteLength };
226
+ },
227
+ }),
228
+ defineTool({
229
+ name: standardToolNames.applyTextEdits,
230
+ version: "1.0.0",
231
+ description: "Apply exact, deterministic text replacements to one workspace file.",
232
+ input: defineOutput({
233
+ name: "apply-text-edits-input",
234
+ jsonSchema: {
235
+ type: "object",
236
+ properties: {
237
+ path: { type: "string" },
238
+ edits: { type: "array", minItems: 1, maxItems: 100, items: { type: "object", properties: { find: { type: "string" }, replace: { type: "string" }, all: { type: "boolean" } }, required: ["find", "replace"], additionalProperties: false } },
239
+ },
240
+ required: ["path", "edits"],
241
+ additionalProperties: false,
242
+ },
243
+ parse(value) {
244
+ const input = record(value, "Text edit input");
245
+ if (!Array.isArray(input.edits) || input.edits.length < 1 || input.edits.length > 100)
246
+ throw new TypeError("edits must contain between 1 and 100 entries.");
247
+ const edits = input.edits.map((item) => {
248
+ const edit = record(item, "Text edit");
249
+ if (typeof edit.find !== "string" || !edit.find)
250
+ throw new TypeError("Each edit find value must be non-empty.");
251
+ if (typeof edit.replace !== "string")
252
+ throw new TypeError("Each edit replace value must be a string.");
253
+ if (edit.all !== undefined && typeof edit.all !== "boolean")
254
+ throw new TypeError("Each edit all value must be a boolean.");
255
+ return { find: edit.find, replace: edit.replace, all: edit.all === true };
256
+ });
257
+ return { path: stringField(value, "path"), edits };
258
+ },
259
+ }),
260
+ output: objectOutput,
261
+ risk: "write",
262
+ sideEffect: "non-idempotent",
263
+ requiredPermissions: ["filesystem:write"],
264
+ requiresApproval: true,
265
+ approvalCategory: "write",
266
+ approvalReason: "Allow this agent to apply reviewed text edits inside the approved workspace.",
267
+ timeoutMs: 5_000,
268
+ async execute({ path, edits }) {
269
+ const file = await workspace.existing(path);
270
+ let content = await readBoundedText(file);
271
+ for (const edit of edits) {
272
+ const occurrences = content.split(edit.find).length - 1;
273
+ if (occurrences === 0)
274
+ throw new TypeError("An expected edit target was not found.");
275
+ if (!edit.all && occurrences !== 1)
276
+ throw new TypeError("An edit target is ambiguous; set all only when every occurrence should change.");
277
+ content = edit.all ? content.split(edit.find).join(edit.replace) : content.replace(edit.find, edit.replace);
278
+ }
279
+ if (new TextEncoder().encode(content).byteLength > maxFileBytes)
280
+ throw new RangeError(`Edited content exceeds the ${maxFileBytes} byte limit.`);
281
+ const temporary = resolve(dirname(file), `.${basename(file)}.${crypto.randomUUID()}.tmp`);
282
+ await writeFile(temporary, content, { encoding: "utf8", mode: 0o600 });
283
+ await rename(temporary, file);
284
+ return { path, editsApplied: edits.length, bytes: new TextEncoder().encode(content).byteLength };
285
+ },
286
+ }),
287
+ ];
288
+ }
289
+ async function execute(command, args, options) {
290
+ try {
291
+ const result = await execFileAsync(command, [...args], { cwd: options.cwd, signal: options.signal, timeout: options.timeout, env: options.env, maxBuffer: options.maxBuffer, encoding: "utf8" });
292
+ return { stdout: result.stdout, stderr: result.stderr, exitCode: 0 };
293
+ }
294
+ catch (error) {
295
+ const failure = error;
296
+ return { stdout: failure.stdout ?? "", stderr: failure.stderr ?? failure.message, exitCode: typeof failure.code === "number" ? failure.code : 1 };
297
+ }
298
+ }
299
+ export async function createDevelopmentTools(options) {
300
+ const workspace = await BoundedWorkspace.create(options.rootPath);
301
+ const allowedCommands = new Set(options.allowedCommands.map((item) => item.trim()).filter(Boolean));
302
+ if (!allowedCommands.size)
303
+ throw new TypeError("Development tools require an explicit command allowlist.");
304
+ const maxOutputBytes = options.maxOutputBytes ?? 512_000;
305
+ const timeoutMs = options.timeoutMs ?? 120_000;
306
+ const output = defineOutput({ name: "process-output", jsonSchema: { type: "object" }, parse(value) { return record(value, "Process output"); } });
307
+ const bounded = (value) => value.length > maxOutputBytes ? value.slice(0, maxOutputBytes) : value;
308
+ const environment = { ...options.environment };
309
+ for (const key of options.inheritedEnvironment ?? ["PATH", "TMPDIR", "LANG", "LC_ALL", "TERM"]) {
310
+ if (process.env[key] !== undefined)
311
+ environment[key] = process.env[key];
312
+ }
313
+ const git = async (args, signal) => {
314
+ const result = await execute("git", ["-C", workspace.rootPath, ...args], { cwd: workspace.rootPath, signal, timeout: timeoutMs, env: environment, maxBuffer: maxOutputBytes });
315
+ return { ...result, stdout: bounded(result.stdout), stderr: bounded(result.stderr), truncated: result.stdout.length > maxOutputBytes || result.stderr.length > maxOutputBytes };
316
+ };
317
+ return [
318
+ defineTool({
319
+ name: standardToolNames.gitStatus,
320
+ version: "1.0.0",
321
+ description: "Read concise Git working-tree status for the approved workspace.",
322
+ input: defineOutput({ name: "git-status-input", jsonSchema: { type: "object", additionalProperties: false }, parse: () => ({}) }),
323
+ output,
324
+ risk: "read",
325
+ sideEffect: "none",
326
+ requiredPermissions: ["git:read"],
327
+ requiresApproval: false,
328
+ timeoutMs,
329
+ execute(_, context) { return git(["status", "--short", "--branch"], context.abortSignal); },
330
+ }),
331
+ defineTool({
332
+ name: standardToolNames.gitDiff,
333
+ version: "1.0.0",
334
+ description: "Read a bounded Git diff for the approved workspace.",
335
+ input: defineOutput({
336
+ name: "git-diff-input",
337
+ jsonSchema: { type: "object", properties: { staged: { type: "boolean" } }, additionalProperties: false },
338
+ parse(value) {
339
+ const input = record(value, "Git diff input");
340
+ if (input.staged !== undefined && typeof input.staged !== "boolean")
341
+ throw new TypeError("staged must be a boolean.");
342
+ return { staged: input.staged === true };
343
+ },
344
+ }),
345
+ output,
346
+ risk: "read",
347
+ sideEffect: "none",
348
+ requiredPermissions: ["git:read"],
349
+ requiresApproval: false,
350
+ timeoutMs,
351
+ execute({ staged }, context) { return git(["diff", ...(staged ? ["--staged"] : []), "--no-ext-diff"], context.abortSignal); },
352
+ }),
353
+ defineTool({
354
+ name: standardToolNames.runCommand,
355
+ version: "1.0.0",
356
+ description: "Run one argument-array command from an explicit host allowlist with cwd constrained to the approved workspace.",
357
+ input: defineOutput({
358
+ name: "run-command-input",
359
+ jsonSchema: {
360
+ type: "object",
361
+ properties: { command: { type: "string" }, args: { type: "array", items: { type: "string" }, maxItems: 100 }, cwd: { type: "string" } },
362
+ required: ["command"],
363
+ additionalProperties: false,
364
+ },
365
+ parse(value) {
366
+ const input = record(value, "Command input");
367
+ const command = stringField(value, "command");
368
+ if (!allowedCommands.has(command))
369
+ throw new TypeError(`Command ${command} is not in the host allowlist.`);
370
+ if (input.args !== undefined && (!Array.isArray(input.args) || input.args.some((item) => typeof item !== "string") || input.args.length > 100))
371
+ throw new TypeError("args must contain at most 100 strings.");
372
+ return { command, args: input.args ?? [], cwd: input.cwd === undefined ? "." : stringField(value, "cwd") };
373
+ },
374
+ }),
375
+ output,
376
+ risk: "privileged",
377
+ sideEffect: "non-idempotent",
378
+ requiredPermissions: ["process:execute"],
379
+ requiresApproval: true,
380
+ approvalCategory: "command",
381
+ approvalReason: "Allow this agent to execute an allowlisted command inside the approved workspace.",
382
+ timeoutMs,
383
+ async execute({ command, args, cwd }, context) {
384
+ const workingDirectory = await workspace.existing(cwd);
385
+ if (!(await stat(workingDirectory)).isDirectory())
386
+ throw new TypeError("Command cwd must be a directory.");
387
+ const result = await execute(command, args, { cwd: workingDirectory, signal: context.abortSignal, timeout: timeoutMs, env: environment, maxBuffer: maxOutputBytes });
388
+ return { ...result, stdout: bounded(result.stdout), stderr: bounded(result.stderr), truncated: result.stdout.length > maxOutputBytes || result.stderr.length > maxOutputBytes };
389
+ },
390
+ }),
391
+ ];
392
+ }
393
+ export async function createWorkspaceTools(options) {
394
+ const filesystem = await createFilesystemTools(options);
395
+ if (!options.allowedCommands?.length)
396
+ return filesystem;
397
+ return [...filesystem, ...await createDevelopmentTools({
398
+ rootPath: options.rootPath,
399
+ allowedCommands: options.allowedCommands,
400
+ inheritedEnvironment: options.inheritedEnvironment,
401
+ environment: options.environment,
402
+ maxOutputBytes: options.maxOutputBytes,
403
+ timeoutMs: options.timeoutMs,
404
+ })];
405
+ }
406
+ //# sourceMappingURL=index.js.map