@warlock.js/ai-workspace 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/LICENSE +21 -0
- package/README.md +149 -0
- package/cjs/index.cjs +1609 -0
- package/cjs/index.cjs.map +1 -0
- package/esm/backends/local.d.mts +22 -0
- package/esm/backends/local.d.mts.map +1 -0
- package/esm/backends/local.mjs +208 -0
- package/esm/backends/local.mjs.map +1 -0
- package/esm/backends/mock.d.mts +62 -0
- package/esm/backends/mock.d.mts.map +1 -0
- package/esm/backends/mock.mjs +167 -0
- package/esm/backends/mock.mjs.map +1 -0
- package/esm/contracts/index.d.mts +5 -0
- package/esm/contracts/tool-io.type.d.mts +149 -0
- package/esm/contracts/tool-io.type.d.mts.map +1 -0
- package/esm/contracts/workspace-backend.contract.d.mts +69 -0
- package/esm/contracts/workspace-backend.contract.d.mts.map +1 -0
- package/esm/contracts/workspace-ops.contract.d.mts +72 -0
- package/esm/contracts/workspace-ops.contract.d.mts.map +1 -0
- package/esm/contracts/workspace-policy.type.d.mts +86 -0
- package/esm/contracts/workspace-policy.type.d.mts.map +1 -0
- package/esm/contracts/workspace.contract.d.mts +131 -0
- package/esm/contracts/workspace.contract.d.mts.map +1 -0
- package/esm/errors.d.mts +100 -0
- package/esm/errors.d.mts.map +1 -0
- package/esm/errors.mjs +58 -0
- package/esm/errors.mjs.map +1 -0
- package/esm/index.d.mts +20 -0
- package/esm/index.mjs +15 -0
- package/esm/ops.d.mts +25 -0
- package/esm/ops.d.mts.map +1 -0
- package/esm/ops.mjs +294 -0
- package/esm/ops.mjs.map +1 -0
- package/esm/policy/policy.d.mts +71 -0
- package/esm/policy/policy.d.mts.map +1 -0
- package/esm/policy/policy.mjs +184 -0
- package/esm/policy/policy.mjs.map +1 -0
- package/esm/tools/edit-file.d.mts +40 -0
- package/esm/tools/edit-file.d.mts.map +1 -0
- package/esm/tools/edit-file.mjs +57 -0
- package/esm/tools/edit-file.mjs.map +1 -0
- package/esm/tools/glob.d.mts +37 -0
- package/esm/tools/glob.d.mts.map +1 -0
- package/esm/tools/glob.mjs +45 -0
- package/esm/tools/glob.mjs.map +1 -0
- package/esm/tools/grep.d.mts +36 -0
- package/esm/tools/grep.d.mts.map +1 -0
- package/esm/tools/grep.mjs +51 -0
- package/esm/tools/grep.mjs.map +1 -0
- package/esm/tools/read-file.d.mts +35 -0
- package/esm/tools/read-file.d.mts.map +1 -0
- package/esm/tools/read-file.mjs +64 -0
- package/esm/tools/read-file.mjs.map +1 -0
- package/esm/tools/run-shell.d.mts +35 -0
- package/esm/tools/run-shell.d.mts.map +1 -0
- package/esm/tools/run-shell.mjs +65 -0
- package/esm/tools/run-shell.mjs.map +1 -0
- package/esm/tools/run-tests.d.mts +40 -0
- package/esm/tools/run-tests.d.mts.map +1 -0
- package/esm/tools/run-tests.mjs +67 -0
- package/esm/tools/run-tests.mjs.map +1 -0
- package/esm/tools/schema.mjs +111 -0
- package/esm/tools/schema.mjs.map +1 -0
- package/esm/tools/write-file.d.mts +33 -0
- package/esm/tools/write-file.d.mts.map +1 -0
- package/esm/tools/write-file.mjs +52 -0
- package/esm/tools/write-file.mjs.map +1 -0
- package/esm/workspace.d.mts +54 -0
- package/esm/workspace.d.mts.map +1 -0
- package/esm/workspace.mjs +210 -0
- package/esm/workspace.mjs.map +1 -0
- package/llms-full.txt +231 -0
- package/llms.txt +10 -0
- package/package.json +42 -0
- package/skills/README.md +13 -0
- package/skills/build-loop-agent/SKILL.md +100 -0
- package/skills/use-a-workspace/SKILL.md +117 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-file.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/edit-file.ts"],"mappings":";;;;;;;;;AAkDA;;;;;;;;;;;;;;;;;AAG6C;;;;;;;;iBAH7B,gBAAA,CACd,GAAA,EAAK,YAAA,EACL,OAAA;EAAY,IAAA;AAAA,IACX,YAAA,CAAa,aAAA,EAAe,cAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { objectSchema, optionalBooleanField, optionalStringField, stringField } from "./schema.mjs";
|
|
2
|
+
import { tool } from "@warlock.js/ai";
|
|
3
|
+
|
|
4
|
+
//#region ../@warlock.js/ai-workspace/src/tools/edit-file.ts
|
|
5
|
+
/** Default tool name exposed to the LLM. */
|
|
6
|
+
const DEFAULT_NAME = "edit_file";
|
|
7
|
+
/** Input schema for the `edit_file` tool. */
|
|
8
|
+
const inputSchema = objectSchema({
|
|
9
|
+
path: stringField(),
|
|
10
|
+
oldString: stringField(),
|
|
11
|
+
newString: stringField(),
|
|
12
|
+
replaceAll: optionalBooleanField(),
|
|
13
|
+
expectHash: optionalStringField()
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* Build the agent-facing `edit_file` tool over a workspace's policy-
|
|
17
|
+
* enforced {@link WorkspaceOps}.
|
|
18
|
+
*
|
|
19
|
+
* The tool validates `{ path, oldString, newString, replaceAll?,
|
|
20
|
+
* expectHash? }` against a Standard Schema, then delegates to
|
|
21
|
+
* `ops.editFile`, which applies the exact-string replacement under the
|
|
22
|
+
* read-before-edit guard and returns the replacement count plus the
|
|
23
|
+
* post-edit `hash`.
|
|
24
|
+
*
|
|
25
|
+
* **Errors flow as data.** A non-unique `oldString` (without
|
|
26
|
+
* `replaceAll`), a missing `oldString`, or a stale `expectHash` cause
|
|
27
|
+
* `ops` to throw a `WorkspaceEditError`; the `tool()` wrapper catches it
|
|
28
|
+
* and surfaces it in the returned `{ error }` field — `invoke()` never
|
|
29
|
+
* throws — so the agent can re-read and retry.
|
|
30
|
+
*
|
|
31
|
+
* @param ops - The shared, policy-enforced operation layer.
|
|
32
|
+
* @param options - Optional overrides; `name` renames the LLM-visible tool.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* const editTool = makeEditFileTool(ops);
|
|
36
|
+
* const { data, error } = await editTool.invoke({
|
|
37
|
+
* path: "src/index.ts",
|
|
38
|
+
* oldString: "const a = 1;",
|
|
39
|
+
* newString: "const a = 2;",
|
|
40
|
+
* expectHash,
|
|
41
|
+
* });
|
|
42
|
+
* if (error) console.warn(error.message); // e.g. stale-hash → re-read
|
|
43
|
+
*/
|
|
44
|
+
function makeEditFileTool(ops, options) {
|
|
45
|
+
return tool({
|
|
46
|
+
name: options?.name ?? DEFAULT_NAME,
|
|
47
|
+
description: "Replace an exact substring in a workspace file. oldString must match uniquely unless replaceAll is set. Pass expectHash (from read_file) to reject the edit if the file changed since you read it. Returns the number of replacements and the new content hash.",
|
|
48
|
+
input: inputSchema,
|
|
49
|
+
async execute(input) {
|
|
50
|
+
return ops.editFile(input);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
export { makeEditFileTool };
|
|
57
|
+
//# sourceMappingURL=edit-file.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-file.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/edit-file.ts"],"sourcesContent":["import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport {\n objectSchema,\n optionalBooleanField,\n optionalStringField,\n stringField,\n} from \"./schema\";\nimport type { EditFileInput, EditFileResult, WorkspaceOps } from \"../contracts\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"edit_file\";\n\n/** Input schema for the `edit_file` tool. */\nconst inputSchema = objectSchema<EditFileInput>({\n path: stringField(),\n oldString: stringField(),\n newString: stringField(),\n replaceAll: optionalBooleanField(),\n expectHash: optionalStringField(),\n});\n\n/**\n * Build the agent-facing `edit_file` tool over a workspace's policy-\n * enforced {@link WorkspaceOps}.\n *\n * The tool validates `{ path, oldString, newString, replaceAll?,\n * expectHash? }` against a Standard Schema, then delegates to\n * `ops.editFile`, which applies the exact-string replacement under the\n * read-before-edit guard and returns the replacement count plus the\n * post-edit `hash`.\n *\n * **Errors flow as data.** A non-unique `oldString` (without\n * `replaceAll`), a missing `oldString`, or a stale `expectHash` cause\n * `ops` to throw a `WorkspaceEditError`; the `tool()` wrapper catches it\n * and surfaces it in the returned `{ error }` field — `invoke()` never\n * throws — so the agent can re-read and retry.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const editTool = makeEditFileTool(ops);\n * const { data, error } = await editTool.invoke({\n * path: \"src/index.ts\",\n * oldString: \"const a = 1;\",\n * newString: \"const a = 2;\",\n * expectHash,\n * });\n * if (error) console.warn(error.message); // e.g. stale-hash → re-read\n */\nexport function makeEditFileTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<EditFileInput, EditFileResult> {\n return tool<EditFileInput, EditFileResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Replace an exact substring in a workspace file. oldString must match \" +\n \"uniquely unless replaceAll is set. Pass expectHash (from read_file) to \" +\n \"reject the edit if the file changed since you read it. Returns the \" +\n \"number of replacements and the new content hash.\",\n input: inputSchema,\n async execute(input) {\n return ops.editFile(input);\n },\n });\n}\n"],"mappings":";;;;;AAUA,MAAM,eAAe;;AAGrB,MAAM,cAAc,aAA4B;CAC9C,MAAM,YAAY;CAClB,WAAW,YAAY;CACvB,WAAW,YAAY;CACvB,YAAY,qBAAqB;CACjC,YAAY,oBAAoB;AAClC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BD,SAAgB,iBACd,KACA,SAC6C;CAC7C,OAAO,KAAoC;EACzC,MAAM,SAAS,QAAQ;EACvB,aACE;EAIF,OAAO;EACP,MAAM,QAAQ,OAAO;GACnB,OAAO,IAAI,SAAS,KAAK;EAC3B;CACF,CAAC;AACH"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { GlobInput, GlobResult } from "../contracts/tool-io.type.mjs";
|
|
2
|
+
import { WorkspaceOps } from "../contracts/workspace-ops.contract.mjs";
|
|
3
|
+
import { ToolContract } from "@warlock.js/ai";
|
|
4
|
+
|
|
5
|
+
//#region ../@warlock.js/ai-workspace/src/tools/glob.d.ts
|
|
6
|
+
/** Options accepted by {@link makeGlobTool} to customize the vended tool. */
|
|
7
|
+
interface MakeGlobToolOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Override the tool name the LLM sees. Defaults to `"glob"`. Use a
|
|
10
|
+
* custom name when wiring several workspaces into one agent so each
|
|
11
|
+
* path-match surface is addressable.
|
|
12
|
+
*/
|
|
13
|
+
name?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Build the agent-facing `glob` tool — resolve a glob pattern to the
|
|
17
|
+
* matching workspace-relative paths within the jail. The returned
|
|
18
|
+
* {@link ToolContract} validates the LLM's arguments, delegates to
|
|
19
|
+
* {@link WorkspaceOps.glob} (which returns a bare sorted `string[]`), and
|
|
20
|
+
* wraps the result in a {@link GlobResult} so the agent always reads a
|
|
21
|
+
* stable `{ paths }` envelope. The jail and `denyPaths` filtering are
|
|
22
|
+
* enforced in the shared ops layer; a policy violation surfaces as typed
|
|
23
|
+
* tool-error *data* via the runtime's `invoke()` wrapper.
|
|
24
|
+
*
|
|
25
|
+
* @param ops - The policy-enforced operation layer to delegate to.
|
|
26
|
+
* @param options - Optional `{ name }` override for the vended tool name.
|
|
27
|
+
* @returns A {@link ToolContract} the agent can call as `glob`.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const glob = makeGlobTool(ops);
|
|
31
|
+
* const { data } = await glob.invoke({ pattern: "src/models/**\/*.ts" });
|
|
32
|
+
* console.log(data?.paths);
|
|
33
|
+
*/
|
|
34
|
+
declare function makeGlobTool(ops: WorkspaceOps, options?: MakeGlobToolOptions): ToolContract<GlobInput, GlobResult>;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { MakeGlobToolOptions, makeGlobTool };
|
|
37
|
+
//# sourceMappingURL=glob.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/glob.ts"],"mappings":";;;;;;UAKiB,mBAAA;;;AAAjB;;;EAME,IAAI;AAAA;AAgCN;;;;;;;;;;;;;;;;;;AAGqC;AAHrC,iBAAgB,YAAA,CACd,GAAA,EAAK,YAAA,EACL,OAAA,GAAU,mBAAA,GACT,YAAA,CAAa,SAAA,EAAW,UAAA"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { objectSchema, stringField } from "./schema.mjs";
|
|
2
|
+
import { tool } from "@warlock.js/ai";
|
|
3
|
+
|
|
4
|
+
//#region ../@warlock.js/ai-workspace/src/tools/glob.ts
|
|
5
|
+
/**
|
|
6
|
+
* Standard Schema for {@link GlobInput} — a single required `pattern`
|
|
7
|
+
* string. Built on the package's shared, dependency-free schema builders
|
|
8
|
+
* (no schema library, matching the validator idiom the `@warlock.js/ai`
|
|
9
|
+
* tool runtime expects).
|
|
10
|
+
*/
|
|
11
|
+
const globInputSchema = objectSchema({ pattern: stringField() });
|
|
12
|
+
/**
|
|
13
|
+
* Build the agent-facing `glob` tool — resolve a glob pattern to the
|
|
14
|
+
* matching workspace-relative paths within the jail. The returned
|
|
15
|
+
* {@link ToolContract} validates the LLM's arguments, delegates to
|
|
16
|
+
* {@link WorkspaceOps.glob} (which returns a bare sorted `string[]`), and
|
|
17
|
+
* wraps the result in a {@link GlobResult} so the agent always reads a
|
|
18
|
+
* stable `{ paths }` envelope. The jail and `denyPaths` filtering are
|
|
19
|
+
* enforced in the shared ops layer; a policy violation surfaces as typed
|
|
20
|
+
* tool-error *data* via the runtime's `invoke()` wrapper.
|
|
21
|
+
*
|
|
22
|
+
* @param ops - The policy-enforced operation layer to delegate to.
|
|
23
|
+
* @param options - Optional `{ name }` override for the vended tool name.
|
|
24
|
+
* @returns A {@link ToolContract} the agent can call as `glob`.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const glob = makeGlobTool(ops);
|
|
28
|
+
* const { data } = await glob.invoke({ pattern: "src/models/**\/*.ts" });
|
|
29
|
+
* console.log(data?.paths);
|
|
30
|
+
*/
|
|
31
|
+
function makeGlobTool(ops, options) {
|
|
32
|
+
return tool({
|
|
33
|
+
name: options?.name ?? "glob",
|
|
34
|
+
description: "Find files in the workspace whose path matches a glob pattern (supports `*`, `**`, and `?`). Returns the matching workspace-relative paths, sorted.",
|
|
35
|
+
action: (input) => `Finding files matching ${input.pattern}`,
|
|
36
|
+
input: globInputSchema,
|
|
37
|
+
async execute(input) {
|
|
38
|
+
return { paths: await ops.glob(input.pattern) };
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { makeGlobTool };
|
|
45
|
+
//# sourceMappingURL=glob.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/glob.ts"],"sourcesContent":["import { type ToolContract, tool } from \"@warlock.js/ai\";\nimport { objectSchema, stringField } from \"./schema\";\nimport type { GlobInput, GlobResult, WorkspaceOps } from \"../contracts\";\n\n/** Options accepted by {@link makeGlobTool} to customize the vended tool. */\nexport interface MakeGlobToolOptions {\n /**\n * Override the tool name the LLM sees. Defaults to `\"glob\"`. Use a\n * custom name when wiring several workspaces into one agent so each\n * path-match surface is addressable.\n */\n name?: string;\n}\n\n/**\n * Standard Schema for {@link GlobInput} — a single required `pattern`\n * string. Built on the package's shared, dependency-free schema builders\n * (no schema library, matching the validator idiom the `@warlock.js/ai`\n * tool runtime expects).\n */\nconst globInputSchema = objectSchema<GlobInput>({\n pattern: stringField(),\n});\n\n/**\n * Build the agent-facing `glob` tool — resolve a glob pattern to the\n * matching workspace-relative paths within the jail. The returned\n * {@link ToolContract} validates the LLM's arguments, delegates to\n * {@link WorkspaceOps.glob} (which returns a bare sorted `string[]`), and\n * wraps the result in a {@link GlobResult} so the agent always reads a\n * stable `{ paths }` envelope. The jail and `denyPaths` filtering are\n * enforced in the shared ops layer; a policy violation surfaces as typed\n * tool-error *data* via the runtime's `invoke()` wrapper.\n *\n * @param ops - The policy-enforced operation layer to delegate to.\n * @param options - Optional `{ name }` override for the vended tool name.\n * @returns A {@link ToolContract} the agent can call as `glob`.\n *\n * @example\n * const glob = makeGlobTool(ops);\n * const { data } = await glob.invoke({ pattern: \"src/models/**\\/*.ts\" });\n * console.log(data?.paths);\n */\nexport function makeGlobTool(\n ops: WorkspaceOps,\n options?: MakeGlobToolOptions,\n): ToolContract<GlobInput, GlobResult> {\n return tool<GlobInput, GlobResult>({\n name: options?.name ?? \"glob\",\n description:\n \"Find files in the workspace whose path matches a glob pattern \" +\n \"(supports `*`, `**`, and `?`). Returns the matching \" +\n \"workspace-relative paths, sorted.\",\n action: (input) => `Finding files matching ${input.pattern}`,\n input: globInputSchema,\n async execute(input) {\n const paths = await ops.glob(input.pattern);\n\n return { paths };\n },\n });\n}\n"],"mappings":";;;;;;;;;;AAoBA,MAAM,kBAAkB,aAAwB,EAC9C,SAAS,YAAY,EACvB,CAAC;;;;;;;;;;;;;;;;;;;;AAqBD,SAAgB,aACd,KACA,SACqC;CACrC,OAAO,KAA4B;EACjC,MAAM,SAAS,QAAQ;EACvB,aACE;EAGF,SAAS,UAAU,0BAA0B,MAAM;EACnD,OAAO;EACP,MAAM,QAAQ,OAAO;GAGnB,OAAO,EAAE,aAFW,IAAI,KAAK,MAAM,OAAO,EAE3B;EACjB;CACF,CAAC;AACH"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { GrepInput, GrepResult } from "../contracts/tool-io.type.mjs";
|
|
2
|
+
import { WorkspaceOps } from "../contracts/workspace-ops.contract.mjs";
|
|
3
|
+
import { ToolContract } from "@warlock.js/ai";
|
|
4
|
+
|
|
5
|
+
//#region ../@warlock.js/ai-workspace/src/tools/grep.d.ts
|
|
6
|
+
/** Options accepted by {@link makeGrepTool} to customize the vended tool. */
|
|
7
|
+
interface MakeGrepToolOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Override the tool name the LLM sees. Defaults to `"grep"`. Use a
|
|
10
|
+
* custom name when wiring several workspaces into one agent so each
|
|
11
|
+
* search surface is addressable.
|
|
12
|
+
*/
|
|
13
|
+
name?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Build the agent-facing `grep` tool — a regex content search across the
|
|
17
|
+
* jailed file set. The returned {@link ToolContract} validates the LLM's
|
|
18
|
+
* arguments, then delegates verbatim to {@link WorkspaceOps.grep}, so the
|
|
19
|
+
* policy jail, `denyPaths` filtering, and match cap are enforced in the
|
|
20
|
+
* single shared ops layer rather than duplicated here. A policy violation
|
|
21
|
+
* (e.g. a jail-resolution failure) surfaces as typed tool-error *data*
|
|
22
|
+
* via the runtime's `invoke()` wrapper, never as a thrown run-killer.
|
|
23
|
+
*
|
|
24
|
+
* @param ops - The policy-enforced operation layer to delegate to.
|
|
25
|
+
* @param options - Optional `{ name }` override for the vended tool name.
|
|
26
|
+
* @returns A {@link ToolContract} the agent can call as `grep`.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* const grep = makeGrepTool(ops);
|
|
30
|
+
* const { data } = await grep.invoke({ pattern: "TODO", glob: "src/*.ts" });
|
|
31
|
+
* console.log(data?.total, data?.matches);
|
|
32
|
+
*/
|
|
33
|
+
declare function makeGrepTool(ops: WorkspaceOps, options?: MakeGrepToolOptions): ToolContract<GrepInput, GrepResult>;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { MakeGrepToolOptions, makeGrepTool };
|
|
36
|
+
//# sourceMappingURL=grep.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grep.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/grep.ts"],"mappings":";;;;;;UAUiB,mBAAA;;;AAAjB;;;EAME,IAAI;AAAA;AAiCN;;;;;;;;;;;;;;;;;;AAAA,iBAAgB,YAAA,CACd,GAAA,EAAK,YAAA,EACL,OAAA,GAAU,mBAAA,GACT,YAAA,CAAa,SAAA,EAAW,UAAA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { objectSchema, optionalBooleanField, optionalStringField, stringField } from "./schema.mjs";
|
|
2
|
+
import { tool } from "@warlock.js/ai";
|
|
3
|
+
|
|
4
|
+
//#region ../@warlock.js/ai-workspace/src/tools/grep.ts
|
|
5
|
+
/**
|
|
6
|
+
* Standard Schema for {@link GrepInput} — `pattern` is a required string;
|
|
7
|
+
* `glob` and `ignoreCase` are optional. Built on the package's shared,
|
|
8
|
+
* dependency-free schema builders (no schema library, matching the
|
|
9
|
+
* validator idiom the `@warlock.js/ai` tool runtime expects).
|
|
10
|
+
*/
|
|
11
|
+
const grepInputSchema = objectSchema({
|
|
12
|
+
pattern: stringField(),
|
|
13
|
+
glob: optionalStringField(),
|
|
14
|
+
ignoreCase: optionalBooleanField()
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Build the agent-facing `grep` tool — a regex content search across the
|
|
18
|
+
* jailed file set. The returned {@link ToolContract} validates the LLM's
|
|
19
|
+
* arguments, then delegates verbatim to {@link WorkspaceOps.grep}, so the
|
|
20
|
+
* policy jail, `denyPaths` filtering, and match cap are enforced in the
|
|
21
|
+
* single shared ops layer rather than duplicated here. A policy violation
|
|
22
|
+
* (e.g. a jail-resolution failure) surfaces as typed tool-error *data*
|
|
23
|
+
* via the runtime's `invoke()` wrapper, never as a thrown run-killer.
|
|
24
|
+
*
|
|
25
|
+
* @param ops - The policy-enforced operation layer to delegate to.
|
|
26
|
+
* @param options - Optional `{ name }` override for the vended tool name.
|
|
27
|
+
* @returns A {@link ToolContract} the agent can call as `grep`.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const grep = makeGrepTool(ops);
|
|
31
|
+
* const { data } = await grep.invoke({ pattern: "TODO", glob: "src/*.ts" });
|
|
32
|
+
* console.log(data?.total, data?.matches);
|
|
33
|
+
*/
|
|
34
|
+
function makeGrepTool(ops, options) {
|
|
35
|
+
return tool({
|
|
36
|
+
name: options?.name ?? "grep",
|
|
37
|
+
description: "Search file contents across the workspace for a regular-expression pattern. Optionally narrow the scanned files with a glob and match case-insensitively. Returns every matching line with its file path and 1-based line number.",
|
|
38
|
+
action: (input) => `Searching for /${input.pattern}/`,
|
|
39
|
+
input: grepInputSchema,
|
|
40
|
+
async execute(input) {
|
|
41
|
+
return ops.grep(input.pattern, {
|
|
42
|
+
glob: input.glob,
|
|
43
|
+
ignoreCase: input.ignoreCase
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
//#endregion
|
|
50
|
+
export { makeGrepTool };
|
|
51
|
+
//# sourceMappingURL=grep.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grep.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/grep.ts"],"sourcesContent":["import { type ToolContract, tool } from \"@warlock.js/ai\";\nimport {\n objectSchema,\n optionalBooleanField,\n optionalStringField,\n stringField,\n} from \"./schema\";\nimport type { GrepInput, GrepResult, WorkspaceOps } from \"../contracts\";\n\n/** Options accepted by {@link makeGrepTool} to customize the vended tool. */\nexport interface MakeGrepToolOptions {\n /**\n * Override the tool name the LLM sees. Defaults to `\"grep\"`. Use a\n * custom name when wiring several workspaces into one agent so each\n * search surface is addressable.\n */\n name?: string;\n}\n\n/**\n * Standard Schema for {@link GrepInput} — `pattern` is a required string;\n * `glob` and `ignoreCase` are optional. Built on the package's shared,\n * dependency-free schema builders (no schema library, matching the\n * validator idiom the `@warlock.js/ai` tool runtime expects).\n */\nconst grepInputSchema = objectSchema<GrepInput>({\n pattern: stringField(),\n glob: optionalStringField(),\n ignoreCase: optionalBooleanField(),\n});\n\n/**\n * Build the agent-facing `grep` tool — a regex content search across the\n * jailed file set. The returned {@link ToolContract} validates the LLM's\n * arguments, then delegates verbatim to {@link WorkspaceOps.grep}, so the\n * policy jail, `denyPaths` filtering, and match cap are enforced in the\n * single shared ops layer rather than duplicated here. A policy violation\n * (e.g. a jail-resolution failure) surfaces as typed tool-error *data*\n * via the runtime's `invoke()` wrapper, never as a thrown run-killer.\n *\n * @param ops - The policy-enforced operation layer to delegate to.\n * @param options - Optional `{ name }` override for the vended tool name.\n * @returns A {@link ToolContract} the agent can call as `grep`.\n *\n * @example\n * const grep = makeGrepTool(ops);\n * const { data } = await grep.invoke({ pattern: \"TODO\", glob: \"src/*.ts\" });\n * console.log(data?.total, data?.matches);\n */\nexport function makeGrepTool(\n ops: WorkspaceOps,\n options?: MakeGrepToolOptions,\n): ToolContract<GrepInput, GrepResult> {\n return tool<GrepInput, GrepResult>({\n name: options?.name ?? \"grep\",\n description:\n \"Search file contents across the workspace for a regular-expression \" +\n \"pattern. Optionally narrow the scanned files with a glob and match \" +\n \"case-insensitively. Returns every matching line with its file path \" +\n \"and 1-based line number.\",\n action: (input) => `Searching for /${input.pattern}/`,\n input: grepInputSchema,\n async execute(input) {\n return ops.grep(input.pattern, {\n glob: input.glob,\n ignoreCase: input.ignoreCase,\n });\n },\n });\n}\n"],"mappings":";;;;;;;;;;AAyBA,MAAM,kBAAkB,aAAwB;CAC9C,SAAS,YAAY;CACrB,MAAM,oBAAoB;CAC1B,YAAY,qBAAqB;AACnC,CAAC;;;;;;;;;;;;;;;;;;;AAoBD,SAAgB,aACd,KACA,SACqC;CACrC,OAAO,KAA4B;EACjC,MAAM,SAAS,QAAQ;EACvB,aACE;EAIF,SAAS,UAAU,kBAAkB,MAAM,QAAQ;EACnD,OAAO;EACP,MAAM,QAAQ,OAAO;GACnB,OAAO,IAAI,KAAK,MAAM,SAAS;IAC7B,MAAM,MAAM;IACZ,YAAY,MAAM;GACpB,CAAC;EACH;CACF,CAAC;AACH"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ReadFileInput, ReadFileResult } from "../contracts/tool-io.type.mjs";
|
|
2
|
+
import { WorkspaceOps } from "../contracts/workspace-ops.contract.mjs";
|
|
3
|
+
import { ToolContract } from "@warlock.js/ai";
|
|
4
|
+
|
|
5
|
+
//#region ../@warlock.js/ai-workspace/src/tools/read-file.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Build the agent-facing `read_file` tool over a workspace's policy-
|
|
8
|
+
* enforced {@link WorkspaceOps}.
|
|
9
|
+
*
|
|
10
|
+
* The tool validates `{ path, startLine?, limit? }` against a Standard
|
|
11
|
+
* Schema, then delegates to `ops.readFile`, mapping the result into the
|
|
12
|
+
* agent wire shape {@link ReadFileResult} — the `hash` an agent must
|
|
13
|
+
* carry into a later `edit_file` (read-before-edit), plus the `startLine`
|
|
14
|
+
* / `endLine` / `truncated` window metadata derived from the requested
|
|
15
|
+
* range and the file's `totalLines`.
|
|
16
|
+
*
|
|
17
|
+
* **Errors flow as data.** Policy violations (a jail escape) are thrown
|
|
18
|
+
* by `ops`; the `tool()` wrapper catches them and surfaces them in the
|
|
19
|
+
* returned `{ error }` field — `invoke()` never throws — so the agent can
|
|
20
|
+
* read the failure and self-correct.
|
|
21
|
+
*
|
|
22
|
+
* @param ops - The shared, policy-enforced operation layer.
|
|
23
|
+
* @param options - Optional overrides; `name` renames the LLM-visible tool.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* const readTool = makeReadFileTool(ops);
|
|
27
|
+
* const { data, error } = await readTool.invoke({ path: "src/index.ts" });
|
|
28
|
+
* if (!error) console.log(data.hash); // feed into edit_file's expectHash
|
|
29
|
+
*/
|
|
30
|
+
declare function makeReadFileTool(ops: WorkspaceOps, options?: {
|
|
31
|
+
name?: string;
|
|
32
|
+
}): ToolContract<ReadFileInput, ReadFileResult>;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { makeReadFileTool };
|
|
35
|
+
//# sourceMappingURL=read-file.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-file.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/read-file.ts"],"mappings":";;;;;;;;;AAsCA;;;;;;;;;;;;;;;;;AAG6C;;;iBAH7B,gBAAA,CACd,GAAA,EAAK,YAAA,EACL,OAAA;EAAY,IAAA;AAAA,IACX,YAAA,CAAa,aAAA,EAAe,cAAA"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { objectSchema, optionalNumberField, stringField } from "./schema.mjs";
|
|
2
|
+
import { tool } from "@warlock.js/ai";
|
|
3
|
+
|
|
4
|
+
//#region ../@warlock.js/ai-workspace/src/tools/read-file.ts
|
|
5
|
+
/** Default tool name exposed to the LLM. */
|
|
6
|
+
const DEFAULT_NAME = "read_file";
|
|
7
|
+
/** Input schema for the `read_file` tool. */
|
|
8
|
+
const inputSchema = objectSchema({
|
|
9
|
+
path: stringField(),
|
|
10
|
+
startLine: optionalNumberField(),
|
|
11
|
+
limit: optionalNumberField()
|
|
12
|
+
});
|
|
13
|
+
/**
|
|
14
|
+
* Build the agent-facing `read_file` tool over a workspace's policy-
|
|
15
|
+
* enforced {@link WorkspaceOps}.
|
|
16
|
+
*
|
|
17
|
+
* The tool validates `{ path, startLine?, limit? }` against a Standard
|
|
18
|
+
* Schema, then delegates to `ops.readFile`, mapping the result into the
|
|
19
|
+
* agent wire shape {@link ReadFileResult} — the `hash` an agent must
|
|
20
|
+
* carry into a later `edit_file` (read-before-edit), plus the `startLine`
|
|
21
|
+
* / `endLine` / `truncated` window metadata derived from the requested
|
|
22
|
+
* range and the file's `totalLines`.
|
|
23
|
+
*
|
|
24
|
+
* **Errors flow as data.** Policy violations (a jail escape) are thrown
|
|
25
|
+
* by `ops`; the `tool()` wrapper catches them and surfaces them in the
|
|
26
|
+
* returned `{ error }` field — `invoke()` never throws — so the agent can
|
|
27
|
+
* read the failure and self-correct.
|
|
28
|
+
*
|
|
29
|
+
* @param ops - The shared, policy-enforced operation layer.
|
|
30
|
+
* @param options - Optional overrides; `name` renames the LLM-visible tool.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* const readTool = makeReadFileTool(ops);
|
|
34
|
+
* const { data, error } = await readTool.invoke({ path: "src/index.ts" });
|
|
35
|
+
* if (!error) console.log(data.hash); // feed into edit_file's expectHash
|
|
36
|
+
*/
|
|
37
|
+
function makeReadFileTool(ops, options) {
|
|
38
|
+
return tool({
|
|
39
|
+
name: options?.name ?? DEFAULT_NAME,
|
|
40
|
+
description: "Read a file from the workspace, returning a numbered line window plus the file's content hash. Pass the hash to edit_file's expectHash to guard against editing a stale version. Use startLine/limit to page through large files.",
|
|
41
|
+
input: inputSchema,
|
|
42
|
+
async execute(input) {
|
|
43
|
+
const startLine = input.startLine !== void 0 ? Math.max(1, input.startLine) : 1;
|
|
44
|
+
const { content, hash, totalLines } = await ops.readFile(input.path, {
|
|
45
|
+
offset: startLine,
|
|
46
|
+
limit: input.limit
|
|
47
|
+
});
|
|
48
|
+
const returnedLines = content.length === 0 ? 0 : content.split("\n").length;
|
|
49
|
+
const endLine = Math.min(totalLines, startLine + Math.max(returnedLines, 1) - 1);
|
|
50
|
+
return {
|
|
51
|
+
content,
|
|
52
|
+
startLine,
|
|
53
|
+
endLine,
|
|
54
|
+
totalLines,
|
|
55
|
+
truncated: endLine < totalLines,
|
|
56
|
+
hash
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
//#endregion
|
|
63
|
+
export { makeReadFileTool };
|
|
64
|
+
//# sourceMappingURL=read-file.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-file.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/read-file.ts"],"sourcesContent":["import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport { objectSchema, optionalNumberField, stringField } from \"./schema\";\nimport type { ReadFileInput, ReadFileResult, WorkspaceOps } from \"../contracts\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"read_file\";\n\n/** Input schema for the `read_file` tool. */\nconst inputSchema = objectSchema<ReadFileInput>({\n path: stringField(),\n startLine: optionalNumberField(),\n limit: optionalNumberField(),\n});\n\n/**\n * Build the agent-facing `read_file` tool over a workspace's policy-\n * enforced {@link WorkspaceOps}.\n *\n * The tool validates `{ path, startLine?, limit? }` against a Standard\n * Schema, then delegates to `ops.readFile`, mapping the result into the\n * agent wire shape {@link ReadFileResult} — the `hash` an agent must\n * carry into a later `edit_file` (read-before-edit), plus the `startLine`\n * / `endLine` / `truncated` window metadata derived from the requested\n * range and the file's `totalLines`.\n *\n * **Errors flow as data.** Policy violations (a jail escape) are thrown\n * by `ops`; the `tool()` wrapper catches them and surfaces them in the\n * returned `{ error }` field — `invoke()` never throws — so the agent can\n * read the failure and self-correct.\n *\n * @param ops - The shared, policy-enforced operation layer.\n * @param options - Optional overrides; `name` renames the LLM-visible tool.\n *\n * @example\n * const readTool = makeReadFileTool(ops);\n * const { data, error } = await readTool.invoke({ path: \"src/index.ts\" });\n * if (!error) console.log(data.hash); // feed into edit_file's expectHash\n */\nexport function makeReadFileTool(\n ops: WorkspaceOps,\n options?: { name?: string },\n): ToolContract<ReadFileInput, ReadFileResult> {\n return tool<ReadFileInput, ReadFileResult>({\n name: options?.name ?? DEFAULT_NAME,\n description:\n \"Read a file from the workspace, returning a numbered line window plus \" +\n \"the file's content hash. Pass the hash to edit_file's expectHash to \" +\n \"guard against editing a stale version. Use startLine/limit to page \" +\n \"through large files.\",\n input: inputSchema,\n async execute(input) {\n const startLine = input.startLine !== undefined ? Math.max(1, input.startLine) : 1;\n const { content, hash, totalLines } = await ops.readFile(input.path, {\n offset: startLine,\n limit: input.limit,\n });\n\n // The window's last line is the start plus however many lines the\n // ops layer actually returned (it caps at `limit` / the policy\n // default), bounded by the file's end.\n const returnedLines = content.length === 0 ? 0 : content.split(\"\\n\").length;\n const endLine = Math.min(totalLines, startLine + Math.max(returnedLines, 1) - 1);\n const truncated = endLine < totalLines;\n\n return { content, startLine, endLine, totalLines, truncated, hash };\n },\n });\n}\n"],"mappings":";;;;;AAKA,MAAM,eAAe;;AAGrB,MAAM,cAAc,aAA4B;CAC9C,MAAM,YAAY;CAClB,WAAW,oBAAoB;CAC/B,OAAO,oBAAoB;AAC7B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AA0BD,SAAgB,iBACd,KACA,SAC6C;CAC7C,OAAO,KAAoC;EACzC,MAAM,SAAS,QAAQ;EACvB,aACE;EAIF,OAAO;EACP,MAAM,QAAQ,OAAO;GACnB,MAAM,YAAY,MAAM,cAAc,SAAY,KAAK,IAAI,GAAG,MAAM,SAAS,IAAI;GACjF,MAAM,EAAE,SAAS,MAAM,eAAe,MAAM,IAAI,SAAS,MAAM,MAAM;IACnE,QAAQ;IACR,OAAO,MAAM;GACf,CAAC;GAKD,MAAM,gBAAgB,QAAQ,WAAW,IAAI,IAAI,QAAQ,MAAM,IAAI,CAAC,CAAC;GACrE,MAAM,UAAU,KAAK,IAAI,YAAY,YAAY,KAAK,IAAI,eAAe,CAAC,IAAI,CAAC;GAG/E,OAAO;IAAE;IAAS;IAAW;IAAS;IAAY,WAFhC,UAAU;IAEiC;GAAK;EACpE;CACF,CAAC;AACH"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { RunShellInput, RunShellResult } from "../contracts/tool-io.type.mjs";
|
|
2
|
+
import { WorkspaceOps } from "../contracts/workspace-ops.contract.mjs";
|
|
3
|
+
import { ToolContract } from "@warlock.js/ai";
|
|
4
|
+
|
|
5
|
+
//#region ../@warlock.js/ai-workspace/src/tools/run-shell.d.ts
|
|
6
|
+
/** Options for {@link makeRunShellTool}. */
|
|
7
|
+
interface MakeRunShellToolOptions {
|
|
8
|
+
/** Override the tool name exposed to the LLM (default `"run_shell"`). */
|
|
9
|
+
name?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Build the `run_shell` tool — a {@link ToolContract} that runs a single
|
|
13
|
+
* shell command through the policy-enforced {@link WorkspaceOps} layer.
|
|
14
|
+
*
|
|
15
|
+
* The command's leading executable basename is gated against the shell
|
|
16
|
+
* allow/deny policy by `ops.exec`; a blocked command throws a
|
|
17
|
+
* `WorkspacePolicyError` which the `tool()` runtime catches and surfaces
|
|
18
|
+
* in the result's `error` field (never a thrown run-killer), so the agent
|
|
19
|
+
* reads the refusal as tool data and self-corrects. A command that runs
|
|
20
|
+
* but exits non-zero is *not* an error — its `exitCode`/`stderr` come back
|
|
21
|
+
* in `data` for the agent to inspect.
|
|
22
|
+
*
|
|
23
|
+
* @param ops - The policy-enforced operation layer to delegate `exec` to.
|
|
24
|
+
* @param options - Optional tool-name override.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const runShell = makeRunShellTool(ops);
|
|
28
|
+
* const { data, error } = await runShell.invoke({ command: "npm run build" });
|
|
29
|
+
* if (error) handleDenied(error);
|
|
30
|
+
* else console.log(data.exitCode, data.stdout);
|
|
31
|
+
*/
|
|
32
|
+
declare function makeRunShellTool(ops: WorkspaceOps, options?: MakeRunShellToolOptions): ToolContract<RunShellInput, RunShellResult>;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { MakeRunShellToolOptions, makeRunShellTool };
|
|
35
|
+
//# sourceMappingURL=run-shell.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-shell.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/run-shell.ts"],"mappings":";;;;;;UAsDiB,uBAAA;;EAEf,IAAI;AAAA;;;;AAAA;AAwBN;;;;;;;;;;;;;;;;;iBAAgB,gBAAA,CACd,GAAA,EAAK,YAAA,EACL,OAAA,GAAU,uBAAA,GACT,YAAA,CAAa,aAAA,EAAe,cAAA"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { tool } from "@warlock.js/ai";
|
|
2
|
+
|
|
3
|
+
//#region ../@warlock.js/ai-workspace/src/tools/run-shell.ts
|
|
4
|
+
/** The default tool name `run_shell` is exposed to the LLM under. */
|
|
5
|
+
const DEFAULT_RUN_SHELL_TOOL_NAME = "run_shell";
|
|
6
|
+
/**
|
|
7
|
+
* Hand-rolled Standard Schema for {@link RunShellInput}. We validate the
|
|
8
|
+
* model's arguments without a runtime schema dependency: `command` must be
|
|
9
|
+
* a non-empty string, and `timeoutMs` (when present) a positive number.
|
|
10
|
+
* Invalid args surface as a `SchemaValidationError` in the tool result's
|
|
11
|
+
* `error` field rather than reaching `ops.exec`.
|
|
12
|
+
*/
|
|
13
|
+
const runShellInputSchema = { "~standard": {
|
|
14
|
+
version: 1,
|
|
15
|
+
vendor: "@warlock.js/ai-workspace",
|
|
16
|
+
validate: (value) => {
|
|
17
|
+
if (typeof value !== "object" || value === null) return { issues: [{ message: "expected an object" }] };
|
|
18
|
+
const candidate = value;
|
|
19
|
+
if (typeof candidate.command !== "string" || candidate.command.length === 0) return { issues: [{
|
|
20
|
+
message: "command must be a non-empty string",
|
|
21
|
+
path: ["command"]
|
|
22
|
+
}] };
|
|
23
|
+
if (candidate.timeoutMs !== void 0 && (typeof candidate.timeoutMs !== "number" || candidate.timeoutMs <= 0)) return { issues: [{
|
|
24
|
+
message: "timeoutMs must be a positive number",
|
|
25
|
+
path: ["timeoutMs"]
|
|
26
|
+
}] };
|
|
27
|
+
const result = { command: candidate.command };
|
|
28
|
+
if (candidate.timeoutMs !== void 0) result.timeoutMs = candidate.timeoutMs;
|
|
29
|
+
return { value: result };
|
|
30
|
+
}
|
|
31
|
+
} };
|
|
32
|
+
/**
|
|
33
|
+
* Build the `run_shell` tool — a {@link ToolContract} that runs a single
|
|
34
|
+
* shell command through the policy-enforced {@link WorkspaceOps} layer.
|
|
35
|
+
*
|
|
36
|
+
* The command's leading executable basename is gated against the shell
|
|
37
|
+
* allow/deny policy by `ops.exec`; a blocked command throws a
|
|
38
|
+
* `WorkspacePolicyError` which the `tool()` runtime catches and surfaces
|
|
39
|
+
* in the result's `error` field (never a thrown run-killer), so the agent
|
|
40
|
+
* reads the refusal as tool data and self-corrects. A command that runs
|
|
41
|
+
* but exits non-zero is *not* an error — its `exitCode`/`stderr` come back
|
|
42
|
+
* in `data` for the agent to inspect.
|
|
43
|
+
*
|
|
44
|
+
* @param ops - The policy-enforced operation layer to delegate `exec` to.
|
|
45
|
+
* @param options - Optional tool-name override.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* const runShell = makeRunShellTool(ops);
|
|
49
|
+
* const { data, error } = await runShell.invoke({ command: "npm run build" });
|
|
50
|
+
* if (error) handleDenied(error);
|
|
51
|
+
* else console.log(data.exitCode, data.stdout);
|
|
52
|
+
*/
|
|
53
|
+
function makeRunShellTool(ops, options) {
|
|
54
|
+
return tool({
|
|
55
|
+
name: options?.name ?? DEFAULT_RUN_SHELL_TOOL_NAME,
|
|
56
|
+
description: "Run a single shell command inside the workspace. The command's executable must be permitted by the shell policy; output is byte-capped and the run is time-limited. A non-zero exit code is returned as data, not an error.",
|
|
57
|
+
action: (input) => `Running \`${input.command}\``,
|
|
58
|
+
input: runShellInputSchema,
|
|
59
|
+
execute: (input) => ops.exec(input.command, { timeoutMs: input.timeoutMs })
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
export { makeRunShellTool };
|
|
65
|
+
//# sourceMappingURL=run-shell.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-shell.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/run-shell.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n RunShellInput,\n RunShellResult,\n WorkspaceOps,\n} from \"../contracts\";\n\n/** The default tool name `run_shell` is exposed to the LLM under. */\nconst DEFAULT_RUN_SHELL_TOOL_NAME = \"run_shell\";\n\n/**\n * Hand-rolled Standard Schema for {@link RunShellInput}. We validate the\n * model's arguments without a runtime schema dependency: `command` must be\n * a non-empty string, and `timeoutMs` (when present) a positive number.\n * Invalid args surface as a `SchemaValidationError` in the tool result's\n * `error` field rather than reaching `ops.exec`.\n */\nconst runShellInputSchema: StandardSchemaV1<RunShellInput> = {\n \"~standard\": {\n version: 1,\n vendor: \"@warlock.js/ai-workspace\",\n validate: (value) => {\n if (typeof value !== \"object\" || value === null) {\n return { issues: [{ message: \"expected an object\" }] };\n }\n\n const candidate = value as Record<string, unknown>;\n\n if (typeof candidate.command !== \"string\" || candidate.command.length === 0) {\n return { issues: [{ message: \"command must be a non-empty string\", path: [\"command\"] }] };\n }\n\n if (\n candidate.timeoutMs !== undefined &&\n (typeof candidate.timeoutMs !== \"number\" || candidate.timeoutMs <= 0)\n ) {\n return {\n issues: [{ message: \"timeoutMs must be a positive number\", path: [\"timeoutMs\"] }],\n };\n }\n\n const result: RunShellInput = { command: candidate.command };\n\n if (candidate.timeoutMs !== undefined) {\n result.timeoutMs = candidate.timeoutMs as number;\n }\n\n return { value: result };\n },\n },\n};\n\n/** Options for {@link makeRunShellTool}. */\nexport interface MakeRunShellToolOptions {\n /** Override the tool name exposed to the LLM (default `\"run_shell\"`). */\n name?: string;\n}\n\n/**\n * Build the `run_shell` tool — a {@link ToolContract} that runs a single\n * shell command through the policy-enforced {@link WorkspaceOps} layer.\n *\n * The command's leading executable basename is gated against the shell\n * allow/deny policy by `ops.exec`; a blocked command throws a\n * `WorkspacePolicyError` which the `tool()` runtime catches and surfaces\n * in the result's `error` field (never a thrown run-killer), so the agent\n * reads the refusal as tool data and self-corrects. A command that runs\n * but exits non-zero is *not* an error — its `exitCode`/`stderr` come back\n * in `data` for the agent to inspect.\n *\n * @param ops - The policy-enforced operation layer to delegate `exec` to.\n * @param options - Optional tool-name override.\n *\n * @example\n * const runShell = makeRunShellTool(ops);\n * const { data, error } = await runShell.invoke({ command: \"npm run build\" });\n * if (error) handleDenied(error);\n * else console.log(data.exitCode, data.stdout);\n */\nexport function makeRunShellTool(\n ops: WorkspaceOps,\n options?: MakeRunShellToolOptions,\n): ToolContract<RunShellInput, RunShellResult> {\n return tool<RunShellInput, RunShellResult>({\n name: options?.name ?? DEFAULT_RUN_SHELL_TOOL_NAME,\n description:\n \"Run a single shell command inside the workspace. The command's \" +\n \"executable must be permitted by the shell policy; output is \" +\n \"byte-capped and the run is time-limited. A non-zero exit code is \" +\n \"returned as data, not an error.\",\n action: (input) => `Running \\`${input.command}\\``,\n input: runShellInputSchema,\n execute: (input) => ops.exec(input.command, { timeoutMs: input.timeoutMs }),\n });\n}\n"],"mappings":";;;;AASA,MAAM,8BAA8B;;;;;;;;AASpC,MAAM,sBAAuD,EAC3D,aAAa;CACX,SAAS;CACT,QAAQ;CACR,WAAW,UAAU;EACnB,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,qBAAqB,CAAC,EAAE;EAGvD,MAAM,YAAY;EAElB,IAAI,OAAO,UAAU,YAAY,YAAY,UAAU,QAAQ,WAAW,GACxE,OAAO,EAAE,QAAQ,CAAC;GAAE,SAAS;GAAsC,MAAM,CAAC,SAAS;EAAE,CAAC,EAAE;EAG1F,IACE,UAAU,cAAc,WACvB,OAAO,UAAU,cAAc,YAAY,UAAU,aAAa,IAEnE,OAAO,EACL,QAAQ,CAAC;GAAE,SAAS;GAAuC,MAAM,CAAC,WAAW;EAAE,CAAC,EAClF;EAGF,MAAM,SAAwB,EAAE,SAAS,UAAU,QAAQ;EAE3D,IAAI,UAAU,cAAc,QAC1B,OAAO,YAAY,UAAU;EAG/B,OAAO,EAAE,OAAO,OAAO;CACzB;AACF,EACF;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,iBACd,KACA,SAC6C;CAC7C,OAAO,KAAoC;EACzC,MAAM,SAAS,QAAQ;EACvB,aACE;EAIF,SAAS,UAAU,aAAa,MAAM,QAAQ;EAC9C,OAAO;EACP,UAAU,UAAU,IAAI,KAAK,MAAM,SAAS,EAAE,WAAW,MAAM,UAAU,CAAC;CAC5E,CAAC;AACH"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { RunShellResult, RunTestsInput } from "../contracts/tool-io.type.mjs";
|
|
2
|
+
import { WorkspaceOps } from "../contracts/workspace-ops.contract.mjs";
|
|
3
|
+
import { ToolContract } from "@warlock.js/ai";
|
|
4
|
+
|
|
5
|
+
//#region ../@warlock.js/ai-workspace/src/tools/run-tests.d.ts
|
|
6
|
+
/** Options for {@link makeRunTestsTool}. */
|
|
7
|
+
interface MakeRunTestsToolOptions {
|
|
8
|
+
/** Override the tool name exposed to the LLM (default `"run_tests"`). */
|
|
9
|
+
name?: string;
|
|
10
|
+
/**
|
|
11
|
+
* The base test command to run (default `"npm test"`). When the model
|
|
12
|
+
* supplies a `pattern`, it is appended to this command.
|
|
13
|
+
*/
|
|
14
|
+
command?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Build the `run_tests` tool — a {@link ToolContract} convenience over
|
|
18
|
+
* `run_shell` that runs the workspace's configured test command through
|
|
19
|
+
* the policy-enforced {@link WorkspaceOps} layer.
|
|
20
|
+
*
|
|
21
|
+
* The base command defaults to `"npm test"` and can be overridden via
|
|
22
|
+
* `options.command`. When the model passes a `pattern`, it is appended to
|
|
23
|
+
* the command as a path/suite filter forwarded to the runner (e.g.
|
|
24
|
+
* `"npm test src/cart"`). Like `run_shell`, the resolved command's
|
|
25
|
+
* executable is gated by the shell policy — a denial surfaces in the
|
|
26
|
+
* result's `error` field — and a non-zero exit (failing tests) comes back
|
|
27
|
+
* as `data` for the agent to read and fix.
|
|
28
|
+
*
|
|
29
|
+
* @param ops - The policy-enforced operation layer to delegate `exec` to.
|
|
30
|
+
* @param options - Optional tool-name and base-command overrides.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* const runTests = makeRunTestsTool(ops, { command: "pnpm test" });
|
|
34
|
+
* const { data } = await runTests.invoke({ pattern: "cart-total" });
|
|
35
|
+
* if (data.exitCode !== 0) inspect(data.stderr);
|
|
36
|
+
*/
|
|
37
|
+
declare function makeRunTestsTool(ops: WorkspaceOps, options?: MakeRunTestsToolOptions): ToolContract<RunTestsInput, RunShellResult>;
|
|
38
|
+
//#endregion
|
|
39
|
+
export { MakeRunTestsToolOptions, makeRunTestsTool };
|
|
40
|
+
//# sourceMappingURL=run-tests.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-tests.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/run-tests.ts"],"mappings":";;;;;;UAqDiB,uBAAA;;EAEf,IAAA;EAFe;;;;EAOf,OAAO;AAAA;;;;;;;;;;;;;;;;;;AA2BoC;;;;iBAH7B,gBAAA,CACd,GAAA,EAAK,YAAA,EACL,OAAA,GAAU,uBAAA,GACT,YAAA,CAAa,aAAA,EAAe,cAAA"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { tool } from "@warlock.js/ai";
|
|
2
|
+
|
|
3
|
+
//#region ../@warlock.js/ai-workspace/src/tools/run-tests.ts
|
|
4
|
+
/** The default tool name `run_tests` is exposed to the LLM under. */
|
|
5
|
+
const DEFAULT_RUN_TESTS_TOOL_NAME = "run_tests";
|
|
6
|
+
/** The default command run when no `command` override is configured. */
|
|
7
|
+
const DEFAULT_TEST_COMMAND = "npm test";
|
|
8
|
+
/**
|
|
9
|
+
* Hand-rolled Standard Schema for {@link RunTestsInput}. `pattern` is the
|
|
10
|
+
* only field and is optional; when present it must be a string. Validation
|
|
11
|
+
* happens without a runtime schema dependency, mirroring the wider tool
|
|
12
|
+
* layer.
|
|
13
|
+
*/
|
|
14
|
+
const runTestsInputSchema = { "~standard": {
|
|
15
|
+
version: 1,
|
|
16
|
+
vendor: "@warlock.js/ai-workspace",
|
|
17
|
+
validate: (value) => {
|
|
18
|
+
if (value === void 0 || value === null) return { value: {} };
|
|
19
|
+
if (typeof value !== "object") return { issues: [{ message: "expected an object" }] };
|
|
20
|
+
const candidate = value;
|
|
21
|
+
if (candidate.pattern !== void 0 && typeof candidate.pattern !== "string") return { issues: [{
|
|
22
|
+
message: "pattern must be a string",
|
|
23
|
+
path: ["pattern"]
|
|
24
|
+
}] };
|
|
25
|
+
const result = {};
|
|
26
|
+
if (candidate.pattern !== void 0) result.pattern = candidate.pattern;
|
|
27
|
+
return { value: result };
|
|
28
|
+
}
|
|
29
|
+
} };
|
|
30
|
+
/**
|
|
31
|
+
* Build the `run_tests` tool — a {@link ToolContract} convenience over
|
|
32
|
+
* `run_shell` that runs the workspace's configured test command through
|
|
33
|
+
* the policy-enforced {@link WorkspaceOps} layer.
|
|
34
|
+
*
|
|
35
|
+
* The base command defaults to `"npm test"` and can be overridden via
|
|
36
|
+
* `options.command`. When the model passes a `pattern`, it is appended to
|
|
37
|
+
* the command as a path/suite filter forwarded to the runner (e.g.
|
|
38
|
+
* `"npm test src/cart"`). Like `run_shell`, the resolved command's
|
|
39
|
+
* executable is gated by the shell policy — a denial surfaces in the
|
|
40
|
+
* result's `error` field — and a non-zero exit (failing tests) comes back
|
|
41
|
+
* as `data` for the agent to read and fix.
|
|
42
|
+
*
|
|
43
|
+
* @param ops - The policy-enforced operation layer to delegate `exec` to.
|
|
44
|
+
* @param options - Optional tool-name and base-command overrides.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* const runTests = makeRunTestsTool(ops, { command: "pnpm test" });
|
|
48
|
+
* const { data } = await runTests.invoke({ pattern: "cart-total" });
|
|
49
|
+
* if (data.exitCode !== 0) inspect(data.stderr);
|
|
50
|
+
*/
|
|
51
|
+
function makeRunTestsTool(ops, options) {
|
|
52
|
+
const baseCommand = options?.command ?? DEFAULT_TEST_COMMAND;
|
|
53
|
+
return tool({
|
|
54
|
+
name: options?.name ?? DEFAULT_RUN_TESTS_TOOL_NAME,
|
|
55
|
+
description: "Run the workspace's test suite, optionally narrowed to a path or name pattern forwarded to the test runner. Failing tests return a non-zero exit code as data, not an error.",
|
|
56
|
+
action: (input) => input.pattern ? `Running tests matching "${input.pattern}"` : "Running tests",
|
|
57
|
+
input: runTestsInputSchema,
|
|
58
|
+
execute: (input) => {
|
|
59
|
+
const command = input.pattern ? `${baseCommand} ${input.pattern}` : baseCommand;
|
|
60
|
+
return ops.exec(command);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
export { makeRunTestsTool };
|
|
67
|
+
//# sourceMappingURL=run-tests.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-tests.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/tools/run-tests.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n RunShellResult,\n RunTestsInput,\n WorkspaceOps,\n} from \"../contracts\";\n\n/** The default tool name `run_tests` is exposed to the LLM under. */\nconst DEFAULT_RUN_TESTS_TOOL_NAME = \"run_tests\";\n\n/** The default command run when no `command` override is configured. */\nconst DEFAULT_TEST_COMMAND = \"npm test\";\n\n/**\n * Hand-rolled Standard Schema for {@link RunTestsInput}. `pattern` is the\n * only field and is optional; when present it must be a string. Validation\n * happens without a runtime schema dependency, mirroring the wider tool\n * layer.\n */\nconst runTestsInputSchema: StandardSchemaV1<RunTestsInput> = {\n \"~standard\": {\n version: 1,\n vendor: \"@warlock.js/ai-workspace\",\n validate: (value) => {\n // A no-argument call (the common case) is valid and runs the bare\n // test command.\n if (value === undefined || value === null) {\n return { value: {} };\n }\n\n if (typeof value !== \"object\") {\n return { issues: [{ message: \"expected an object\" }] };\n }\n\n const candidate = value as Record<string, unknown>;\n\n if (candidate.pattern !== undefined && typeof candidate.pattern !== \"string\") {\n return { issues: [{ message: \"pattern must be a string\", path: [\"pattern\"] }] };\n }\n\n const result: RunTestsInput = {};\n\n if (candidate.pattern !== undefined) {\n result.pattern = candidate.pattern as string;\n }\n\n return { value: result };\n },\n },\n};\n\n/** Options for {@link makeRunTestsTool}. */\nexport interface MakeRunTestsToolOptions {\n /** Override the tool name exposed to the LLM (default `\"run_tests\"`). */\n name?: string;\n /**\n * The base test command to run (default `\"npm test\"`). When the model\n * supplies a `pattern`, it is appended to this command.\n */\n command?: string;\n}\n\n/**\n * Build the `run_tests` tool — a {@link ToolContract} convenience over\n * `run_shell` that runs the workspace's configured test command through\n * the policy-enforced {@link WorkspaceOps} layer.\n *\n * The base command defaults to `\"npm test\"` and can be overridden via\n * `options.command`. When the model passes a `pattern`, it is appended to\n * the command as a path/suite filter forwarded to the runner (e.g.\n * `\"npm test src/cart\"`). Like `run_shell`, the resolved command's\n * executable is gated by the shell policy — a denial surfaces in the\n * result's `error` field — and a non-zero exit (failing tests) comes back\n * as `data` for the agent to read and fix.\n *\n * @param ops - The policy-enforced operation layer to delegate `exec` to.\n * @param options - Optional tool-name and base-command overrides.\n *\n * @example\n * const runTests = makeRunTestsTool(ops, { command: \"pnpm test\" });\n * const { data } = await runTests.invoke({ pattern: \"cart-total\" });\n * if (data.exitCode !== 0) inspect(data.stderr);\n */\nexport function makeRunTestsTool(\n ops: WorkspaceOps,\n options?: MakeRunTestsToolOptions,\n): ToolContract<RunTestsInput, RunShellResult> {\n const baseCommand = options?.command ?? DEFAULT_TEST_COMMAND;\n\n return tool<RunTestsInput, RunShellResult>({\n name: options?.name ?? DEFAULT_RUN_TESTS_TOOL_NAME,\n description:\n \"Run the workspace's test suite, optionally narrowed to a path or \" +\n \"name pattern forwarded to the test runner. Failing tests return a \" +\n \"non-zero exit code as data, not an error.\",\n action: (input) =>\n input.pattern ? `Running tests matching \"${input.pattern}\"` : \"Running tests\",\n input: runTestsInputSchema,\n execute: (input) => {\n const command = input.pattern ? `${baseCommand} ${input.pattern}` : baseCommand;\n\n return ops.exec(command);\n },\n });\n}\n"],"mappings":";;;;AASA,MAAM,8BAA8B;;AAGpC,MAAM,uBAAuB;;;;;;;AAQ7B,MAAM,sBAAuD,EAC3D,aAAa;CACX,SAAS;CACT,QAAQ;CACR,WAAW,UAAU;EAGnB,IAAI,UAAU,UAAa,UAAU,MACnC,OAAO,EAAE,OAAO,CAAC,EAAE;EAGrB,IAAI,OAAO,UAAU,UACnB,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,qBAAqB,CAAC,EAAE;EAGvD,MAAM,YAAY;EAElB,IAAI,UAAU,YAAY,UAAa,OAAO,UAAU,YAAY,UAClE,OAAO,EAAE,QAAQ,CAAC;GAAE,SAAS;GAA4B,MAAM,CAAC,SAAS;EAAE,CAAC,EAAE;EAGhF,MAAM,SAAwB,CAAC;EAE/B,IAAI,UAAU,YAAY,QACxB,OAAO,UAAU,UAAU;EAG7B,OAAO,EAAE,OAAO,OAAO;CACzB;AACF,EACF;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,iBACd,KACA,SAC6C;CAC7C,MAAM,cAAc,SAAS,WAAW;CAExC,OAAO,KAAoC;EACzC,MAAM,SAAS,QAAQ;EACvB,aACE;EAGF,SAAS,UACP,MAAM,UAAU,2BAA2B,MAAM,QAAQ,KAAK;EAChE,OAAO;EACP,UAAU,UAAU;GAClB,MAAM,UAAU,MAAM,UAAU,GAAG,YAAY,GAAG,MAAM,YAAY;GAEpE,OAAO,IAAI,KAAK,OAAO;EACzB;CACF,CAAC;AACH"}
|