@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,131 @@
|
|
|
1
|
+
import { EditFileInput, EditFileResult, GrepResult, RunShellResult, WorkspaceToolName } from "./tool-io.type.mjs";
|
|
2
|
+
import { WorkspacePolicy } from "./workspace-policy.type.mjs";
|
|
3
|
+
import { ToolContract } from "@warlock.js/ai";
|
|
4
|
+
|
|
5
|
+
//#region ../@warlock.js/ai-workspace/src/contracts/workspace.contract.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* The agent-facing tool surface of a workspace. Each factory returns a
|
|
8
|
+
* {@link ToolContract} ready to hand to `ai.agent({ tools })`. `all()`
|
|
9
|
+
* vends every tool; `pick(...)` selects a least-privilege subset (e.g.
|
|
10
|
+
* `pick("readFile", "grep", "glob")` for a read-only reviewer).
|
|
11
|
+
*
|
|
12
|
+
* Every factory takes an optional `{ name }` so the same workspace can
|
|
13
|
+
* expose the same capability under a custom tool name when wiring
|
|
14
|
+
* multiple agents.
|
|
15
|
+
*/
|
|
16
|
+
interface WorkspaceTools {
|
|
17
|
+
/** All seven tools as `ToolContract[]`, ready for `ai.agent({ tools })`. */
|
|
18
|
+
all(): ToolContract[];
|
|
19
|
+
/** A named subset, e.g. `pick("readFile", "grep")` for least privilege. */
|
|
20
|
+
pick(...names: WorkspaceToolName[]): ToolContract[];
|
|
21
|
+
/** The `read_file` tool. */
|
|
22
|
+
readFile(opts?: {
|
|
23
|
+
name?: string;
|
|
24
|
+
}): ToolContract;
|
|
25
|
+
/** The `edit_file` tool (exact-string replace + stale-hash guard). */
|
|
26
|
+
editFile(opts?: {
|
|
27
|
+
name?: string;
|
|
28
|
+
}): ToolContract;
|
|
29
|
+
/** The `write_file` tool (atomic full-content write). */
|
|
30
|
+
writeFile(opts?: {
|
|
31
|
+
name?: string;
|
|
32
|
+
}): ToolContract;
|
|
33
|
+
/** The `run_shell` tool (policy-gated command execution). */
|
|
34
|
+
runShell(opts?: {
|
|
35
|
+
name?: string;
|
|
36
|
+
}): ToolContract;
|
|
37
|
+
/** The `run_tests` tool (runs the configured test command). */
|
|
38
|
+
runTests(opts?: {
|
|
39
|
+
name?: string;
|
|
40
|
+
command?: string;
|
|
41
|
+
}): ToolContract;
|
|
42
|
+
/** The `grep` tool (regex content search). */
|
|
43
|
+
grep(opts?: {
|
|
44
|
+
name?: string;
|
|
45
|
+
}): ToolContract;
|
|
46
|
+
/** The `glob` tool (path pattern match). */
|
|
47
|
+
glob(opts?: {
|
|
48
|
+
name?: string;
|
|
49
|
+
}): ToolContract;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The bounded place an agent reads, writes, and runs commands — a
|
|
53
|
+
* policy-enforced handle to a filesystem + shell. One policy, one shared
|
|
54
|
+
* ops layer, two ways to touch it: the agent uses `.tools.*`; you use the
|
|
55
|
+
* direct methods.
|
|
56
|
+
*
|
|
57
|
+
* This is the **W1 surface only**: the policy jail, the seven tools, the
|
|
58
|
+
* direct methods, `tools.all()` / `tools.pick()`, plus the two
|
|
59
|
+
* composition projections `readonly()` and `scope()`. The transactional
|
|
60
|
+
* lifecycle (`snapshot` / `restore` / `diff` / `changes`), hooks
|
|
61
|
+
* (`on`), `dispose`, `usage`, and the worktree/container backends are
|
|
62
|
+
* deferred to later phases and intentionally absent here.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* const ws = ai.workspace({ cwd: "/srv/acme-api", shell: { allow: ["npm"], inheritEnv: ["PATH"] } });
|
|
66
|
+
* const dev = ai.agent({ model, tools: ws.tools.all() });
|
|
67
|
+
* await dev.execute("Make the failing cart-total suite green.");
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* // Drive it directly from code.
|
|
71
|
+
* await ws.writeFile("src/routes.ts", BOILERPLATE);
|
|
72
|
+
* const files = await ws.glob("src/models/**\/*.ts");
|
|
73
|
+
*/
|
|
74
|
+
interface Workspace {
|
|
75
|
+
/** The immutable policy bounding this workspace. */
|
|
76
|
+
readonly policy: WorkspacePolicy;
|
|
77
|
+
/** Agent-facing tool factories. */
|
|
78
|
+
readonly tools: WorkspaceTools;
|
|
79
|
+
/**
|
|
80
|
+
* Read a jailed file. `offset`/`limit` select a 1-based line window;
|
|
81
|
+
* returns the content, its SHA-256 `hash`, and the file's total line
|
|
82
|
+
* count (for read-before-edit).
|
|
83
|
+
*/
|
|
84
|
+
readFile(path: string, opts?: {
|
|
85
|
+
offset?: number;
|
|
86
|
+
limit?: number;
|
|
87
|
+
}): Promise<{
|
|
88
|
+
content: string;
|
|
89
|
+
hash: string;
|
|
90
|
+
totalLines: number;
|
|
91
|
+
}>;
|
|
92
|
+
/** Atomically write full content to a jailed path. */
|
|
93
|
+
writeFile(path: string, content: string): Promise<{
|
|
94
|
+
hash: string;
|
|
95
|
+
bytesWritten: number;
|
|
96
|
+
}>;
|
|
97
|
+
/** Apply an exact-string edit under the read-before-edit stale guard. */
|
|
98
|
+
editFile(input: EditFileInput): Promise<EditFileResult>;
|
|
99
|
+
/** Run a policy-gated shell command and capture its outcome. */
|
|
100
|
+
exec(command: string, opts?: {
|
|
101
|
+
timeoutMs?: number;
|
|
102
|
+
}): Promise<RunShellResult>;
|
|
103
|
+
/** Search jailed file contents for a regex pattern. */
|
|
104
|
+
grep(pattern: string, opts?: {
|
|
105
|
+
glob?: string;
|
|
106
|
+
ignoreCase?: boolean;
|
|
107
|
+
}): Promise<GrepResult>;
|
|
108
|
+
/** Resolve a glob to matching workspace-relative paths within the jail. */
|
|
109
|
+
glob(pattern: string): Promise<string[]>;
|
|
110
|
+
/** Whether a jailed path exists (file or directory). */
|
|
111
|
+
exists(path: string): Promise<boolean>;
|
|
112
|
+
/** Create a directory (and parents) at a jailed path; idempotent. */
|
|
113
|
+
mkdir(path: string): Promise<void>;
|
|
114
|
+
/** Remove a file or directory tree at a jailed path. */
|
|
115
|
+
remove(path: string): Promise<void>;
|
|
116
|
+
/**
|
|
117
|
+
* A read-only projection of this workspace — the same jail and state,
|
|
118
|
+
* but the write/edit/shell tools are omitted from `tools.*` and the
|
|
119
|
+
* mutating direct methods reject. For least-privilege reviewers.
|
|
120
|
+
*/
|
|
121
|
+
readonly(): Workspace;
|
|
122
|
+
/**
|
|
123
|
+
* A sub-jailed view rooted at `subdir` (relative to this workspace's
|
|
124
|
+
* `cwd`), sharing the same backend. Narrows the blast radius — e.g.
|
|
125
|
+
* `ws.scope("packages/api")` jails an agent to that package.
|
|
126
|
+
*/
|
|
127
|
+
scope(subdir: string): Workspace;
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
export { Workspace, WorkspaceTools };
|
|
131
|
+
//# sourceMappingURL=workspace.contract.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.contract.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-workspace/src/contracts/workspace.contract.ts"],"mappings":";;;;;;;AAoBA;;;;;;;;UAAiB,cAAA;EAYqB;EAVpC,GAAA,IAAO,YAAA;EAcyB;EAZhC,IAAA,IAAQ,KAAA,EAAO,iBAAA,KAAsB,YAAA;EAcO;EAZ5C,QAAA,CAAS,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAFpC;EAIA,QAAA,CAAS,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAFpC;EAIA,SAAA,CAAU,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAFrC;EAIA,QAAA,CAAS,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAFpC;EAIA,QAAA,CAAS,IAAA;IAAS,IAAA;IAAe,OAAA;EAAA,IAAqB,YAAA;EAFpC;EAIlB,IAAA,CAAK,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAFd;EAIlB,IAAA,CAAK,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;AAAA;;;;;;;;;AAAY;AA0B9C;;;;;;;;;;;;;;UAAiB,SAAA;EAuCO;EAAA,SArCb,MAAA,EAAQ,eAAA;EA2CK;EAAA,SAxCb,KAAA,EAAO,cAAA;EAsDO;;;;;EA/CvB,QAAA,CACE,IAAA,UACA,IAAA;IAAS,MAAA;IAAiB,KAAA;EAAA,IACzB,OAAA;IAAU,OAAA;IAAiB,IAAA;IAAc,UAAA;EAAA;EAA/B;EAGb,SAAA,CACE,IAAA,UACA,OAAA,WACC,OAAA;IAAU,IAAA;IAAc,YAAA;EAAA;EADzB;EAIF,QAAA,CAAS,KAAA,EAAO,aAAA,GAAgB,OAAA,CAAQ,cAAA;EAH3B;EAMb,IAAA,CAAK,OAAA,UAAiB,IAAA;IAAS,SAAA;EAAA,IAAuB,OAAA,CAAQ,cAAA;EAHrD;EAMT,IAAA,CACE,OAAA,UACA,IAAA;IAAS,IAAA;IAAe,UAAA;EAAA,IACvB,OAAA,CAAQ,UAAA;EANoB;EAS/B,IAAA,CAAK,OAAA,WAAkB,OAAA;EAT+B;EAYtD,MAAA,CAAO,IAAA,WAAe,OAAA;EATtB;EAYA,KAAA,CAAM,IAAA,WAAe,OAAA;EAVV;EAaX,MAAA,CAAO,IAAA,WAAe,OAAA;EAbpB;;;;;EAoBF,QAAA,IAAY,SAAA;EAbZ;;;;;EAoBA,KAAA,CAAM,MAAA,WAAiB,SAAA;AAAA"}
|
package/esm/errors.d.mts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { AIError, AIErrorOptions } from "@warlock.js/ai";
|
|
2
|
+
|
|
3
|
+
//#region ../@warlock.js/ai-workspace/src/errors.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Why a workspace policy check rejected an operation.
|
|
6
|
+
*
|
|
7
|
+
* - `"path-escape"` — a resolved path fell outside the `cwd` jail (or an
|
|
8
|
+
* `allowPaths` root), or matched a `denyPaths` glob.
|
|
9
|
+
* - `"denied-command"` — a shell command's leading executable basename
|
|
10
|
+
* was not in `shell.allow`, or was explicitly in `shell.deny`.
|
|
11
|
+
*/
|
|
12
|
+
type WorkspacePolicyViolation = "path-escape" | "denied-command";
|
|
13
|
+
/**
|
|
14
|
+
* Options for {@link WorkspacePolicyError} — the structured `type`
|
|
15
|
+
* discriminator plus, where relevant, the offending path or command for
|
|
16
|
+
* branchable diagnostics without parsing the message.
|
|
17
|
+
*/
|
|
18
|
+
type WorkspacePolicyErrorOptions = AIErrorOptions & {
|
|
19
|
+
/** Which policy rule was violated. */type: WorkspacePolicyViolation; /** The offending workspace-relative path (for `"path-escape"`). */
|
|
20
|
+
path?: string; /** The offending command line (for `"denied-command"`). */
|
|
21
|
+
command?: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* The workspace policy engine refused an operation — a path escaped the
|
|
25
|
+
* jail (or hit a deny glob), or a shell command's executable was not
|
|
26
|
+
* allowed.
|
|
27
|
+
*
|
|
28
|
+
* **Surface.** This is returned to the agent as tool-error *data*, never
|
|
29
|
+
* a thrown run-killer — the agent reads the failure and self-corrects.
|
|
30
|
+
* Extends the framework `AIError` (category `"tool"`, code
|
|
31
|
+
* `TOOL_EXEC_FAILED`) so it flows through the same typed error contract
|
|
32
|
+
* as every other AI error; branch on `error.type` for the specific
|
|
33
|
+
* violation.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* if (error instanceof WorkspacePolicyError && error.type === "denied-command") {
|
|
37
|
+
* console.warn(`Blocked command: ${error.command}`);
|
|
38
|
+
* }
|
|
39
|
+
*/
|
|
40
|
+
declare class WorkspacePolicyError extends AIError {
|
|
41
|
+
/** Which policy rule was violated. */
|
|
42
|
+
readonly type: WorkspacePolicyViolation;
|
|
43
|
+
/** The offending path, when the violation was a path escape. */
|
|
44
|
+
readonly path?: string;
|
|
45
|
+
/** The offending command, when the violation was a denied command. */
|
|
46
|
+
readonly command?: string;
|
|
47
|
+
constructor(message: string, options: WorkspacePolicyErrorOptions);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Why an edit was rejected.
|
|
51
|
+
*
|
|
52
|
+
* - `"not-found"` — the `oldString` did not appear in the file.
|
|
53
|
+
* - `"not-unique"` — `oldString` matched more than once and `replaceAll`
|
|
54
|
+
* was not set, so the edit is ambiguous.
|
|
55
|
+
* - `"stale-hash"` — the file's current hash did not match the supplied
|
|
56
|
+
* `expectHash`; the file changed since it was read.
|
|
57
|
+
*/
|
|
58
|
+
type WorkspaceEditFailure = "not-found" | "not-unique" | "stale-hash";
|
|
59
|
+
/**
|
|
60
|
+
* Options for {@link WorkspaceEditError} — the structured `type`
|
|
61
|
+
* discriminator plus optional match-count / hash context for the
|
|
62
|
+
* `"not-unique"` and `"stale-hash"` cases.
|
|
63
|
+
*/
|
|
64
|
+
type WorkspaceEditErrorOptions = AIErrorOptions & {
|
|
65
|
+
/** Why the edit was rejected. */type: WorkspaceEditFailure; /** The edited file's workspace-relative path. */
|
|
66
|
+
path: string; /** How many times `oldString` matched (for `"not-unique"`). */
|
|
67
|
+
matches?: number; /** The hash the caller expected (for `"stale-hash"`). */
|
|
68
|
+
expectedHash?: string; /** The file's actual current hash (for `"stale-hash"`). */
|
|
69
|
+
actualHash?: string;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* An `editFile` operation was rejected by the read-before-edit guard:
|
|
73
|
+
* the `oldString` was absent, matched non-uniquely without `replaceAll`,
|
|
74
|
+
* or the file's hash no longer matched the supplied `expectHash`.
|
|
75
|
+
*
|
|
76
|
+
* **Surface.** Like {@link WorkspacePolicyError}, returned to the agent
|
|
77
|
+
* as tool-error *data* so it can re-read and retry. Extends `AIError`
|
|
78
|
+
* (category `"tool"`, code `TOOL_EXEC_FAILED`); branch on `error.type`.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* if (error instanceof WorkspaceEditError && error.type === "stale-hash") {
|
|
82
|
+
* // re-read the file and retry the edit with the fresh hash
|
|
83
|
+
* }
|
|
84
|
+
*/
|
|
85
|
+
declare class WorkspaceEditError extends AIError {
|
|
86
|
+
/** Why the edit was rejected. */
|
|
87
|
+
readonly type: WorkspaceEditFailure;
|
|
88
|
+
/** The edited file's workspace-relative path. */
|
|
89
|
+
readonly path: string;
|
|
90
|
+
/** How many times `oldString` matched, for the `"not-unique"` case. */
|
|
91
|
+
readonly matches?: number;
|
|
92
|
+
/** The hash the caller expected, for the `"stale-hash"` case. */
|
|
93
|
+
readonly expectedHash?: string;
|
|
94
|
+
/** The file's actual current hash, for the `"stale-hash"` case. */
|
|
95
|
+
readonly actualHash?: string;
|
|
96
|
+
constructor(message: string, options: WorkspaceEditErrorOptions);
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
export { WorkspaceEditError, WorkspaceEditErrorOptions, WorkspaceEditFailure, WorkspacePolicyError, WorkspacePolicyErrorOptions, WorkspacePolicyViolation };
|
|
100
|
+
//# sourceMappingURL=errors.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.mts","names":[],"sources":["../../../../../../@warlock.js/ai-workspace/src/errors.ts"],"mappings":";;;;;AAUA;;;;AAAoC;AAOpC;KAPY,wBAAA;;;;;;KAOA,2BAAA,GAA8B,cAAA;EAMxC,sCAJA,IAAA,EAAM,wBAAwB,EAIvB;EAFP,IAAA,WAsBgC;EApBhC,OAAA;AAAA;;;;;;;;;;;;;;;AA4BwE;AAmB1E;;cA3Ba,oBAAA,SAA6B,OAAA;EA2BV;EAAA,SAzBd,IAAA,EAAM,wBAAA;EAgCZ;EAAA,SA9BM,IAAA;;WAEA,OAAA;cAEG,OAAA,UAAiB,OAAA,EAAS,2BAAA;AAAA;;;;;;;AAoCnC;AAiBZ;;KAlCY,oBAAA;;;;;;KAOA,yBAAA,GAA4B,cAAA;EA6BtB,iCA3BhB,IAAA,EAAM,oBAAoB,EA6BV;EA3BhB,IAAA,UA+BgB;EA7BhB,OAAA;EAEA,YAAA,WA+B6C;EA7B7C,UAAA;AAAA;AA6BsE;;;;;;;;;;;;;;AAAA,cAZ3D,kBAAA,SAA2B,OAAA;;WAEtB,IAAA,EAAM,oBAAA;;WAEN,IAAA;;WAEA,OAAA;;WAEA,YAAA;;WAEA,UAAA;cAEG,OAAA,UAAiB,OAAA,EAAS,yBAAA;AAAA"}
|
package/esm/errors.mjs
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { AIError } from "@warlock.js/ai";
|
|
2
|
+
|
|
3
|
+
//#region ../@warlock.js/ai-workspace/src/errors.ts
|
|
4
|
+
/**
|
|
5
|
+
* The workspace policy engine refused an operation — a path escaped the
|
|
6
|
+
* jail (or hit a deny glob), or a shell command's executable was not
|
|
7
|
+
* allowed.
|
|
8
|
+
*
|
|
9
|
+
* **Surface.** This is returned to the agent as tool-error *data*, never
|
|
10
|
+
* a thrown run-killer — the agent reads the failure and self-corrects.
|
|
11
|
+
* Extends the framework `AIError` (category `"tool"`, code
|
|
12
|
+
* `TOOL_EXEC_FAILED`) so it flows through the same typed error contract
|
|
13
|
+
* as every other AI error; branch on `error.type` for the specific
|
|
14
|
+
* violation.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* if (error instanceof WorkspacePolicyError && error.type === "denied-command") {
|
|
18
|
+
* console.warn(`Blocked command: ${error.command}`);
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
var WorkspacePolicyError = class extends AIError {
|
|
22
|
+
constructor(message, options) {
|
|
23
|
+
super("TOOL_EXEC_FAILED", message, options);
|
|
24
|
+
this.name = "WorkspacePolicyError";
|
|
25
|
+
this.type = options.type;
|
|
26
|
+
this.path = options.path;
|
|
27
|
+
this.command = options.command;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* An `editFile` operation was rejected by the read-before-edit guard:
|
|
32
|
+
* the `oldString` was absent, matched non-uniquely without `replaceAll`,
|
|
33
|
+
* or the file's hash no longer matched the supplied `expectHash`.
|
|
34
|
+
*
|
|
35
|
+
* **Surface.** Like {@link WorkspacePolicyError}, returned to the agent
|
|
36
|
+
* as tool-error *data* so it can re-read and retry. Extends `AIError`
|
|
37
|
+
* (category `"tool"`, code `TOOL_EXEC_FAILED`); branch on `error.type`.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* if (error instanceof WorkspaceEditError && error.type === "stale-hash") {
|
|
41
|
+
* // re-read the file and retry the edit with the fresh hash
|
|
42
|
+
* }
|
|
43
|
+
*/
|
|
44
|
+
var WorkspaceEditError = class extends AIError {
|
|
45
|
+
constructor(message, options) {
|
|
46
|
+
super("TOOL_EXEC_FAILED", message, options);
|
|
47
|
+
this.name = "WorkspaceEditError";
|
|
48
|
+
this.type = options.type;
|
|
49
|
+
this.path = options.path;
|
|
50
|
+
this.matches = options.matches;
|
|
51
|
+
this.expectedHash = options.expectedHash;
|
|
52
|
+
this.actualHash = options.actualHash;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
export { WorkspaceEditError, WorkspacePolicyError };
|
|
58
|
+
//# sourceMappingURL=errors.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../../../../../../@warlock.js/ai-workspace/src/errors.ts"],"sourcesContent":["import { AIError, type AIErrorOptions } from \"@warlock.js/ai\";\n\n/**\n * Why a workspace policy check rejected an operation.\n *\n * - `\"path-escape\"` — a resolved path fell outside the `cwd` jail (or an\n * `allowPaths` root), or matched a `denyPaths` glob.\n * - `\"denied-command\"` — a shell command's leading executable basename\n * was not in `shell.allow`, or was explicitly in `shell.deny`.\n */\nexport type WorkspacePolicyViolation = \"path-escape\" | \"denied-command\";\n\n/**\n * Options for {@link WorkspacePolicyError} — the structured `type`\n * discriminator plus, where relevant, the offending path or command for\n * branchable diagnostics without parsing the message.\n */\nexport type WorkspacePolicyErrorOptions = AIErrorOptions & {\n /** Which policy rule was violated. */\n type: WorkspacePolicyViolation;\n /** The offending workspace-relative path (for `\"path-escape\"`). */\n path?: string;\n /** The offending command line (for `\"denied-command\"`). */\n command?: string;\n};\n\n/**\n * The workspace policy engine refused an operation — a path escaped the\n * jail (or hit a deny glob), or a shell command's executable was not\n * allowed.\n *\n * **Surface.** This is returned to the agent as tool-error *data*, never\n * a thrown run-killer — the agent reads the failure and self-corrects.\n * Extends the framework `AIError` (category `\"tool\"`, code\n * `TOOL_EXEC_FAILED`) so it flows through the same typed error contract\n * as every other AI error; branch on `error.type` for the specific\n * violation.\n *\n * @example\n * if (error instanceof WorkspacePolicyError && error.type === \"denied-command\") {\n * console.warn(`Blocked command: ${error.command}`);\n * }\n */\nexport class WorkspacePolicyError extends AIError {\n /** Which policy rule was violated. */\n public readonly type: WorkspacePolicyViolation;\n /** The offending path, when the violation was a path escape. */\n public readonly path?: string;\n /** The offending command, when the violation was a denied command. */\n public readonly command?: string;\n\n public constructor(message: string, options: WorkspacePolicyErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"WorkspacePolicyError\";\n this.type = options.type;\n this.path = options.path;\n this.command = options.command;\n }\n}\n\n/**\n * Why an edit was rejected.\n *\n * - `\"not-found\"` — the `oldString` did not appear in the file.\n * - `\"not-unique\"` — `oldString` matched more than once and `replaceAll`\n * was not set, so the edit is ambiguous.\n * - `\"stale-hash\"` — the file's current hash did not match the supplied\n * `expectHash`; the file changed since it was read.\n */\nexport type WorkspaceEditFailure = \"not-found\" | \"not-unique\" | \"stale-hash\";\n\n/**\n * Options for {@link WorkspaceEditError} — the structured `type`\n * discriminator plus optional match-count / hash context for the\n * `\"not-unique\"` and `\"stale-hash\"` cases.\n */\nexport type WorkspaceEditErrorOptions = AIErrorOptions & {\n /** Why the edit was rejected. */\n type: WorkspaceEditFailure;\n /** The edited file's workspace-relative path. */\n path: string;\n /** How many times `oldString` matched (for `\"not-unique\"`). */\n matches?: number;\n /** The hash the caller expected (for `\"stale-hash\"`). */\n expectedHash?: string;\n /** The file's actual current hash (for `\"stale-hash\"`). */\n actualHash?: string;\n};\n\n/**\n * An `editFile` operation was rejected by the read-before-edit guard:\n * the `oldString` was absent, matched non-uniquely without `replaceAll`,\n * or the file's hash no longer matched the supplied `expectHash`.\n *\n * **Surface.** Like {@link WorkspacePolicyError}, returned to the agent\n * as tool-error *data* so it can re-read and retry. Extends `AIError`\n * (category `\"tool\"`, code `TOOL_EXEC_FAILED`); branch on `error.type`.\n *\n * @example\n * if (error instanceof WorkspaceEditError && error.type === \"stale-hash\") {\n * // re-read the file and retry the edit with the fresh hash\n * }\n */\nexport class WorkspaceEditError extends AIError {\n /** Why the edit was rejected. */\n public readonly type: WorkspaceEditFailure;\n /** The edited file's workspace-relative path. */\n public readonly path: string;\n /** How many times `oldString` matched, for the `\"not-unique\"` case. */\n public readonly matches?: number;\n /** The hash the caller expected, for the `\"stale-hash\"` case. */\n public readonly expectedHash?: string;\n /** The file's actual current hash, for the `\"stale-hash\"` case. */\n public readonly actualHash?: string;\n\n public constructor(message: string, options: WorkspaceEditErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"WorkspaceEditError\";\n this.type = options.type;\n this.path = options.path;\n this.matches = options.matches;\n this.expectedHash = options.expectedHash;\n this.actualHash = options.actualHash;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2CA,IAAa,uBAAb,cAA0C,QAAQ;CAQhD,AAAO,YAAY,SAAiB,SAAsC;EACxE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;EACpB,KAAK,OAAO,QAAQ;EACpB,KAAK,UAAU,QAAQ;CACzB;AACF;;;;;;;;;;;;;;;AA6CA,IAAa,qBAAb,cAAwC,QAAQ;CAY9C,AAAO,YAAY,SAAiB,SAAoC;EACtE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;EACpB,KAAK,OAAO,QAAQ;EACpB,KAAK,UAAU,QAAQ;EACvB,KAAK,eAAe,QAAQ;EAC5B,KAAK,aAAa,QAAQ;CAC5B;AACF"}
|
package/esm/index.d.mts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { EditFileInput, EditFileResult, GlobInput, GlobResult, GrepInput, GrepMatch, GrepResult, ReadFileInput, ReadFileResult, RunShellInput, RunShellResult, RunTestsInput, WorkspaceToolName, WriteFileInput, WriteFileResult } from "./contracts/tool-io.type.mjs";
|
|
2
|
+
import { WorkspaceBackend, WorkspaceBackendExecOptions, WorkspaceBackendExecResult } from "./contracts/workspace-backend.contract.mjs";
|
|
3
|
+
import { WorkspaceOps } from "./contracts/workspace-ops.contract.mjs";
|
|
4
|
+
import { WorkspaceBackendType, WorkspacePolicy, WorkspaceReadPolicy, WorkspaceShellPolicy } from "./contracts/workspace-policy.type.mjs";
|
|
5
|
+
import { Workspace, WorkspaceTools } from "./contracts/workspace.contract.mjs";
|
|
6
|
+
import { WorkspaceEditError, WorkspaceEditErrorOptions, WorkspaceEditFailure, WorkspacePolicyError, WorkspacePolicyErrorOptions, WorkspacePolicyViolation } from "./errors.mjs";
|
|
7
|
+
import { ResolvedPath, buildEnv, isCommandAllowed, resolveInJail } from "./policy/policy.mjs";
|
|
8
|
+
import { createOps } from "./ops.mjs";
|
|
9
|
+
import { createLocalBackend } from "./backends/local.mjs";
|
|
10
|
+
import { MockBackendSeed, MockExecResult, createMockBackend } from "./backends/mock.mjs";
|
|
11
|
+
import { makeEditFileTool } from "./tools/edit-file.mjs";
|
|
12
|
+
import { MakeGlobToolOptions, makeGlobTool } from "./tools/glob.mjs";
|
|
13
|
+
import { MakeGrepToolOptions, makeGrepTool } from "./tools/grep.mjs";
|
|
14
|
+
import { makeReadFileTool } from "./tools/read-file.mjs";
|
|
15
|
+
import { MakeRunShellToolOptions, makeRunShellTool } from "./tools/run-shell.mjs";
|
|
16
|
+
import { MakeRunTestsToolOptions, makeRunTestsTool } from "./tools/run-tests.mjs";
|
|
17
|
+
import { makeWriteFileTool } from "./tools/write-file.mjs";
|
|
18
|
+
import { workspace } from "./workspace.mjs";
|
|
19
|
+
export { type EditFileInput, type EditFileResult, type GlobInput, type GlobResult, type GrepInput, type GrepMatch, type GrepResult, type MakeGlobToolOptions, type MakeGrepToolOptions, type MakeRunShellToolOptions, type MakeRunTestsToolOptions, type MockBackendSeed, type MockExecResult, type ReadFileInput, type ReadFileResult, type ResolvedPath, type RunShellInput, type RunShellResult, type RunTestsInput, type Workspace, type WorkspaceBackend, type WorkspaceBackendExecOptions, type WorkspaceBackendExecResult, type WorkspaceBackendType, WorkspaceEditError, type WorkspaceEditErrorOptions, type WorkspaceEditFailure, type WorkspaceOps, type WorkspacePolicy, WorkspacePolicyError, type WorkspacePolicyErrorOptions, type WorkspacePolicyViolation, type WorkspaceReadPolicy, type WorkspaceShellPolicy, type WorkspaceToolName, type WorkspaceTools, type WriteFileInput, type WriteFileResult, buildEnv, createLocalBackend, createMockBackend, createOps, isCommandAllowed, makeEditFileTool, makeGlobTool, makeGrepTool, makeReadFileTool, makeRunShellTool, makeRunTestsTool, makeWriteFileTool, resolveInJail, workspace };
|
|
20
|
+
import "./workspace.mjs";
|
package/esm/index.mjs
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { WorkspaceEditError, WorkspacePolicyError } from "./errors.mjs";
|
|
2
|
+
import { buildEnv, isCommandAllowed, resolveInJail } from "./policy/policy.mjs";
|
|
3
|
+
import { createOps } from "./ops.mjs";
|
|
4
|
+
import { createLocalBackend } from "./backends/local.mjs";
|
|
5
|
+
import { createMockBackend } from "./backends/mock.mjs";
|
|
6
|
+
import { makeEditFileTool } from "./tools/edit-file.mjs";
|
|
7
|
+
import { makeGlobTool } from "./tools/glob.mjs";
|
|
8
|
+
import { makeGrepTool } from "./tools/grep.mjs";
|
|
9
|
+
import { makeReadFileTool } from "./tools/read-file.mjs";
|
|
10
|
+
import { makeRunShellTool } from "./tools/run-shell.mjs";
|
|
11
|
+
import { makeRunTestsTool } from "./tools/run-tests.mjs";
|
|
12
|
+
import { makeWriteFileTool } from "./tools/write-file.mjs";
|
|
13
|
+
import { workspace } from "./workspace.mjs";
|
|
14
|
+
|
|
15
|
+
export { WorkspaceEditError, WorkspacePolicyError, buildEnv, createLocalBackend, createMockBackend, createOps, isCommandAllowed, makeEditFileTool, makeGlobTool, makeGrepTool, makeReadFileTool, makeRunShellTool, makeRunTestsTool, makeWriteFileTool, resolveInJail, workspace };
|
package/esm/ops.d.mts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { WorkspaceBackend } from "./contracts/workspace-backend.contract.mjs";
|
|
2
|
+
import { WorkspaceOps } from "./contracts/workspace-ops.contract.mjs";
|
|
3
|
+
import { WorkspacePolicy } from "./contracts/workspace-policy.type.mjs";
|
|
4
|
+
//#region ../@warlock.js/ai-workspace/src/ops.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Create the policy-enforced operation layer over a backend.
|
|
7
|
+
*
|
|
8
|
+
* The returned {@link WorkspaceOps} is the single seam both the
|
|
9
|
+
* agent-facing `.tools.*` factories and the human-facing direct methods
|
|
10
|
+
* delegate to — one jail, one command-gate, one read-before-edit guard,
|
|
11
|
+
* regardless of caller. Path inputs are workspace-relative and resolved
|
|
12
|
+
* against `policy.cwd`; escapes and denied commands surface as typed
|
|
13
|
+
* {@link WorkspacePolicyError} / {@link WorkspaceEditError}.
|
|
14
|
+
*
|
|
15
|
+
* @param backend - The dumb IO executor (local disk or in-memory mock).
|
|
16
|
+
* @param policy - The policy that bounds every operation.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* const ops = createOps(localBackend, { cwd: "/srv/api", shell: { allow: ["npm"] } });
|
|
20
|
+
* const { content, hash } = await ops.readFile("src/index.ts");
|
|
21
|
+
*/
|
|
22
|
+
declare function createOps(backend: WorkspaceBackend, policy: WorkspacePolicy): WorkspaceOps;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { createOps };
|
|
25
|
+
//# sourceMappingURL=ops.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ops.d.mts","names":[],"sources":["../../../../../../@warlock.js/ai-workspace/src/ops.ts"],"mappings":";;;;;;;;;AAqbA;;;;;;;;;;;;iBAAgB,SAAA,CACd,OAAA,EAAS,gBAAA,EACT,MAAA,EAAQ,eAAA,GACP,YAAA"}
|