@warlock.js/ai-workspace 5.29.0 → 5.31.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 +68 -0
- package/README.md +40 -21
- package/cjs/index.cjs +3295 -360
- package/cjs/index.cjs.map +1 -1
- package/esm/backends/local.d.mts +1 -1
- package/esm/backends/local.d.mts.map +1 -1
- package/esm/backends/local.mjs +199 -31
- package/esm/backends/local.mjs.map +1 -1
- package/esm/backends/mock.d.mts +12 -6
- package/esm/backends/mock.d.mts.map +1 -1
- package/esm/backends/mock.mjs +45 -3
- package/esm/backends/mock.mjs.map +1 -1
- package/esm/contracts/index.d.mts +3 -3
- package/esm/contracts/tool-io.type.d.mts +248 -11
- package/esm/contracts/tool-io.type.d.mts.map +1 -1
- package/esm/contracts/workspace-backend.contract.d.mts +78 -2
- package/esm/contracts/workspace-backend.contract.d.mts.map +1 -1
- package/esm/contracts/workspace-ops.contract.d.mts +41 -16
- package/esm/contracts/workspace-ops.contract.d.mts.map +1 -1
- package/esm/contracts/workspace-policy.type.d.mts +142 -14
- package/esm/contracts/workspace-policy.type.d.mts.map +1 -1
- package/esm/contracts/workspace.contract.d.mts +75 -22
- package/esm/contracts/workspace.contract.d.mts.map +1 -1
- package/esm/errors.d.mts +80 -6
- package/esm/errors.d.mts.map +1 -1
- package/esm/errors.mjs +23 -2
- package/esm/errors.mjs.map +1 -1
- package/esm/index.d.mts +13 -5
- package/esm/index.mjs +10 -2
- package/esm/ops.d.mts +22 -3
- package/esm/ops.d.mts.map +1 -1
- package/esm/ops.mjs +341 -82
- package/esm/ops.mjs.map +1 -1
- package/esm/policy/jail.mjs +73 -0
- package/esm/policy/jail.mjs.map +1 -0
- package/esm/policy/policy.d.mts +26 -16
- package/esm/policy/policy.d.mts.map +1 -1
- package/esm/policy/policy.mjs +62 -86
- package/esm/policy/policy.mjs.map +1 -1
- package/esm/policy/shell-check.d.mts +64 -0
- package/esm/policy/shell-check.d.mts.map +1 -0
- package/esm/policy/shell-check.mjs +627 -0
- package/esm/policy/shell-check.mjs.map +1 -0
- package/esm/policy/shell-parser.mjs +802 -0
- package/esm/policy/shell-parser.mjs.map +1 -0
- package/esm/policy/tokenize-command.d.mts +63 -0
- package/esm/policy/tokenize-command.d.mts.map +1 -0
- package/esm/policy/tokenize-command.mjs +76 -24
- package/esm/policy/tokenize-command.mjs.map +1 -1
- package/esm/shell-jobs.d.mts +45 -0
- package/esm/shell-jobs.d.mts.map +1 -0
- package/esm/shell-jobs.mjs +247 -0
- package/esm/shell-jobs.mjs.map +1 -0
- package/esm/todos.d.mts +22 -0
- package/esm/todos.d.mts.map +1 -0
- package/esm/todos.mjs +73 -0
- package/esm/todos.mjs.map +1 -0
- package/esm/tools/edit-file.d.mts +1 -1
- package/esm/tools/edit-file.d.mts.map +1 -1
- package/esm/tools/edit-file.mjs +3 -2
- package/esm/tools/edit-file.mjs.map +1 -1
- package/esm/tools/glob.d.mts +1 -1
- package/esm/tools/glob.d.mts.map +1 -1
- package/esm/tools/glob.mjs +3 -2
- package/esm/tools/glob.mjs.map +1 -1
- package/esm/tools/grep.d.mts +1 -1
- package/esm/tools/grep.d.mts.map +1 -1
- package/esm/tools/grep.mjs +3 -2
- package/esm/tools/grep.mjs.map +1 -1
- package/esm/tools/json-schemas.mjs +175 -0
- package/esm/tools/json-schemas.mjs.map +1 -0
- package/esm/tools/list-dir.d.mts +32 -0
- package/esm/tools/list-dir.d.mts.map +1 -0
- package/esm/tools/list-dir.mjs +50 -0
- package/esm/tools/list-dir.mjs.map +1 -0
- package/esm/tools/multi-edit.d.mts +38 -0
- package/esm/tools/multi-edit.d.mts.map +1 -0
- package/esm/tools/multi-edit.mjs +121 -0
- package/esm/tools/multi-edit.mjs.map +1 -0
- package/esm/tools/read-file.d.mts +5 -5
- package/esm/tools/read-file.d.mts.map +1 -1
- package/esm/tools/read-file.mjs +10 -20
- package/esm/tools/read-file.mjs.map +1 -1
- package/esm/tools/run-shell.d.mts +39 -13
- package/esm/tools/run-shell.d.mts.map +1 -1
- package/esm/tools/run-shell.mjs +60 -47
- package/esm/tools/run-shell.mjs.map +1 -1
- package/esm/tools/run-tests.d.mts +11 -2
- package/esm/tools/run-tests.d.mts.map +1 -1
- package/esm/tools/run-tests.mjs +15 -7
- package/esm/tools/run-tests.mjs.map +1 -1
- package/esm/tools/schema.mjs +24 -19
- package/esm/tools/schema.mjs.map +1 -1
- package/esm/tools/shell-kill.d.mts +25 -0
- package/esm/tools/shell-kill.d.mts.map +1 -0
- package/esm/tools/shell-kill.mjs +36 -0
- package/esm/tools/shell-kill.mjs.map +1 -0
- package/esm/tools/shell-output.d.mts +30 -0
- package/esm/tools/shell-output.d.mts.map +1 -0
- package/esm/tools/shell-output.mjs +49 -0
- package/esm/tools/shell-output.mjs.map +1 -0
- package/esm/tools/todo.d.mts +43 -0
- package/esm/tools/todo.d.mts.map +1 -0
- package/esm/tools/todo.mjs +126 -0
- package/esm/tools/todo.mjs.map +1 -0
- package/esm/tools/write-file.d.mts +1 -1
- package/esm/tools/write-file.d.mts.map +1 -1
- package/esm/tools/write-file.mjs +3 -2
- package/esm/tools/write-file.mjs.map +1 -1
- package/esm/workspace.d.mts +12 -9
- package/esm/workspace.d.mts.map +1 -1
- package/esm/workspace.mjs +109 -28
- package/esm/workspace.mjs.map +1 -1
- package/llms-full.txt +192 -35
- package/llms.txt +1 -1
- package/package.json +5 -4
- package/skills/README.md +2 -2
- package/skills/build-loop-agent/SKILL.md +7 -7
- package/skills/index.md +6 -1
- package/skills/use-a-workspace/SKILL.md +185 -28
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region
|
|
1
|
+
//#region ../../packages/ai-workspace/src/contracts/workspace-policy.type.d.ts
|
|
2
2
|
/**
|
|
3
3
|
* The executor a workspace runs on. `"local"` uses `@warlock.js/fs` +
|
|
4
4
|
* `node:child_process` against the real filesystem under `cwd`;
|
|
@@ -8,33 +8,130 @@
|
|
|
8
8
|
* past the W1 surface and intentionally absent from this union.
|
|
9
9
|
*/
|
|
10
10
|
type WorkspaceBackendType = "local" | "mock";
|
|
11
|
+
/**
|
|
12
|
+
* How `run_shell` / `exec()` interpret a command line.
|
|
13
|
+
*
|
|
14
|
+
* - `"none"` (default) — **argv mode**: the line is tokenized into one argv
|
|
15
|
+
* (quotes respected, no shell semantics) and spawned without a shell. Any
|
|
16
|
+
* unquoted shell operator is refused with a message that says how to split
|
|
17
|
+
* the line.
|
|
18
|
+
* - `"sh"` — **shell mode** (explicit opt-in): the line runs through
|
|
19
|
+
* `/bin/sh -c` (`cmd.exe /d /v:off /s /c` on Windows). Before it runs, the
|
|
20
|
+
* line is parsed and every simple command in it — inside `;`, `&&`, `||`,
|
|
21
|
+
* pipes, subshells, `$( )` and backticks — is checked against the same
|
|
22
|
+
* `deny` / `allow` / `denyPatterns` / `allowCommand` rules, and every
|
|
23
|
+
* redirection target and `cd` directory against the path jail. Syntax the
|
|
24
|
+
* parser cannot read with confidence is refused, never guessed at.
|
|
25
|
+
*/
|
|
26
|
+
type WorkspaceShellInterpreter = "none" | "sh";
|
|
27
|
+
/**
|
|
28
|
+
* A command pattern in `shell.denyPatterns`.
|
|
29
|
+
*
|
|
30
|
+
* - A **string** is tokenized like a command and matches a command whose
|
|
31
|
+
* argv **starts with** those tokens — the executable compared by basename,
|
|
32
|
+
* case-insensitively. `"git push --force"` matches
|
|
33
|
+
* `git push --force origin main` but not `git push origin --force`.
|
|
34
|
+
* - A **RegExp** is tested against the command's argv joined with single
|
|
35
|
+
* spaces, the executable reduced to its basename (`/usr/bin/git push`
|
|
36
|
+
* is tested as `"git push"`). Use it for option orders and spellings:
|
|
37
|
+
* `/^git push\b.*(--force|-f\b)/`. A `g`/`y` flag is ignored.
|
|
38
|
+
*/
|
|
39
|
+
type WorkspaceCommandPattern = string | RegExp;
|
|
40
|
+
/** What {@link WorkspaceAllowCommand} is told about the command it checks. */
|
|
41
|
+
interface WorkspaceCommandCheckContext {
|
|
42
|
+
/** The full command line the checked command is part of. */
|
|
43
|
+
command: string;
|
|
44
|
+
/** The interpreter the line runs under. */
|
|
45
|
+
interpreter: WorkspaceShellInterpreter;
|
|
46
|
+
/**
|
|
47
|
+
* True when the argv is a command run through a wrapper the policy
|
|
48
|
+
* unwrapped (`time npm test` is checked as `["time", "npm", "test"]` and
|
|
49
|
+
* then as the nested `["npm", "test"]`) or by `find -exec`.
|
|
50
|
+
*/
|
|
51
|
+
nested: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A host predicate over every simple command's argv. Return `true` to let
|
|
55
|
+
* the command through, `false` to refuse it, or a string to refuse it with
|
|
56
|
+
* that explanation (shown to the model). A throw refuses the command too —
|
|
57
|
+
* the hook fails closed. It can only narrow what `allow` / `deny` permit,
|
|
58
|
+
* never widen it, and it must be synchronous.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* allowCommand: (argv) =>
|
|
62
|
+
* argv[0] === "git" && argv[1] === "push" ? "Pushing is done by the release job." : true
|
|
63
|
+
*/
|
|
64
|
+
type WorkspaceAllowCommand = (argv: readonly string[], context: WorkspaceCommandCheckContext) => boolean | string;
|
|
65
|
+
/**
|
|
66
|
+
* Background-command sub-policy. Present ⇒ `run_shell({ background: true })`
|
|
67
|
+
* and `startShell()` may start long-running commands (dev servers,
|
|
68
|
+
* watchers) that belong to the workspace; absent ⇒ background commands are
|
|
69
|
+
* refused. Every background command is killed — its whole process tree —
|
|
70
|
+
* by `workspace.dispose()`, and when the Node process exits.
|
|
71
|
+
*/
|
|
72
|
+
interface WorkspaceBackgroundPolicy {
|
|
73
|
+
/** Most background commands running at once. Default `4`. */
|
|
74
|
+
maxJobs?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Wall-clock cap for one background command, after which its process tree
|
|
77
|
+
* is killed (`status: "timed-out"`). Default: none — it runs until killed
|
|
78
|
+
* or disposed. A model-supplied `timeoutMs` can only lower it.
|
|
79
|
+
*/
|
|
80
|
+
timeoutMs?: number;
|
|
81
|
+
}
|
|
11
82
|
/**
|
|
12
83
|
* Shell-execution sub-policy for `run_shell` / `run_tests` and the
|
|
13
84
|
* `exec()` direct method.
|
|
14
85
|
*
|
|
15
|
-
* **Command gating.**
|
|
16
|
-
* respected, NO shell semantics) and executed
|
|
17
|
-
* containing unquoted shell metacharacters (`;`,
|
|
18
|
-
* `$`, redirection, parentheses) is
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
86
|
+
* **Command gating.** By default (`interpreter: "none"`) each command is
|
|
87
|
+
* tokenized into an argv (quotes respected, NO shell semantics) and executed
|
|
88
|
+
* without a shell; a command containing unquoted shell metacharacters (`;`,
|
|
89
|
+
* `&`, `|`, backticks, `$`, redirection, parentheses) is refused with a
|
|
90
|
+
* message that names the operator and suggests the split. With
|
|
91
|
+
* `interpreter: "sh"` the line runs through a shell, and every simple
|
|
92
|
+
* command in it is checked. Each command's executable basename is matched
|
|
93
|
+
* against `allow` / `deny`; **deny wins** (and matches case-insensitively).
|
|
94
|
+
* A non-empty `allow` is exhaustive. `denyPatterns` and `allowCommand`
|
|
95
|
+
* then refine the decision per argv. A command that would run another
|
|
96
|
+
* command out of the policy's sight (`bash -c`, `eval`, `xargs`, `sudo`, …)
|
|
97
|
+
* is refused when the policy has deny rules, unless it is named in `allow`.
|
|
24
98
|
*
|
|
25
99
|
* **Environment.** The spawned process does NOT inherit `process.env`.
|
|
26
100
|
* The effective environment is `{ ...pick(process.env, inheritEnv), ...env }`
|
|
27
101
|
* — so `run_shell` cannot find `node`/`npm` unless `inheritEnv` includes
|
|
28
|
-
* `"PATH"`. This is deliberate isolation, documented loudly.
|
|
102
|
+
* `"PATH"`. This is deliberate isolation, documented loudly. Shell mode also
|
|
103
|
+
* drops the variables that change how a shell parses or starts (`ENV`,
|
|
104
|
+
* `BASH_ENV`, `SHELLOPTS`, `BASHOPTS`, `CDPATH`, `IFS`, `PS4`, `BASH_FUNC_*`).
|
|
29
105
|
*/
|
|
30
106
|
interface WorkspaceShellPolicy {
|
|
31
107
|
/** Allowed executable basenames (e.g. `["npm", "node"]`). */
|
|
32
108
|
allow?: string[];
|
|
33
109
|
/** Denied executable basenames — wins over `allow`. */
|
|
34
110
|
deny?: string[];
|
|
111
|
+
/**
|
|
112
|
+
* Argument-level denials checked against every simple command (see
|
|
113
|
+
* {@link WorkspaceCommandPattern}), e.g. `["rm -rf /", "git push --force"]`.
|
|
114
|
+
*/
|
|
115
|
+
denyPatterns?: WorkspaceCommandPattern[];
|
|
116
|
+
/**
|
|
117
|
+
* Host predicate over every simple command's argv (see
|
|
118
|
+
* {@link WorkspaceAllowCommand}).
|
|
119
|
+
*/
|
|
120
|
+
allowCommand?: WorkspaceAllowCommand;
|
|
121
|
+
/**
|
|
122
|
+
* How the command line is interpreted. Default `"none"` (argv mode, no
|
|
123
|
+
* shell). `"sh"` is an explicit opt-in to shell syntax — pipes, `&&`,
|
|
124
|
+
* redirections — still checked per simple command (see
|
|
125
|
+
* {@link WorkspaceShellInterpreter}).
|
|
126
|
+
*/
|
|
127
|
+
interpreter?: WorkspaceShellInterpreter;
|
|
35
128
|
/** Per-command wall-clock cap; on expiry the process is SIGKILLed. */
|
|
36
129
|
timeoutMs?: number;
|
|
37
|
-
/**
|
|
130
|
+
/**
|
|
131
|
+
* Max bytes captured from stdout/stderr before output is truncated. For a
|
|
132
|
+
* background command it is the most output kept per command; older output
|
|
133
|
+
* is dropped first.
|
|
134
|
+
*/
|
|
38
135
|
maxOutputBytes?: number;
|
|
39
136
|
/** Explicit environment variables injected into every spawned process. */
|
|
40
137
|
env?: Record<string, string>;
|
|
@@ -43,16 +140,45 @@ interface WorkspaceShellPolicy {
|
|
|
43
140
|
* Nothing from `process.env` leaks in unless listed here.
|
|
44
141
|
*/
|
|
45
142
|
inheritEnv?: string[];
|
|
143
|
+
/**
|
|
144
|
+
* Enables background commands (see {@link WorkspaceBackgroundPolicy}).
|
|
145
|
+
* Absent ⇒ `run_shell({ background: true })` is refused.
|
|
146
|
+
*/
|
|
147
|
+
background?: WorkspaceBackgroundPolicy;
|
|
46
148
|
}
|
|
47
149
|
/**
|
|
48
150
|
* Read sub-policy controlling how much of a file a single read returns.
|
|
151
|
+
* Applies equally to the `read_file` tool and the `readFile()` direct method.
|
|
49
152
|
*/
|
|
50
153
|
interface WorkspaceReadPolicy {
|
|
51
|
-
/**
|
|
154
|
+
/**
|
|
155
|
+
* Hard cap on the UTF-8 bytes of the `content` a single read returns,
|
|
156
|
+
* line-number prefixes and newlines included. A window that would exceed
|
|
157
|
+
* it is cut — mid-line if needed, always on a character boundary — and
|
|
158
|
+
* the result reports `bytesCapped: true`. Absent ⇒ no byte cap. The
|
|
159
|
+
* whole file is still read and hashed; this bounds what is returned.
|
|
160
|
+
*/
|
|
52
161
|
maxBytes?: number;
|
|
53
162
|
/** Default number of lines a read returns when no explicit limit is given. */
|
|
54
163
|
defaultLines?: number;
|
|
55
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Directory-listing sub-policy for the `list_dir` tool and `listDir()`.
|
|
167
|
+
* `denyPaths` are always hidden from listings; `ignore` hides more without
|
|
168
|
+
* blocking access (an ignored file can still be read).
|
|
169
|
+
*/
|
|
170
|
+
interface WorkspaceListPolicy {
|
|
171
|
+
/**
|
|
172
|
+
* Globs (`*`, `**`, `?`; a bare name also hides its contents) left out of
|
|
173
|
+
* listings. Default `["node_modules", ".git"]`; setting it replaces the
|
|
174
|
+
* default.
|
|
175
|
+
*/
|
|
176
|
+
ignore?: string[];
|
|
177
|
+
/** Deepest `depth` a listing may ask for. Default `5`. */
|
|
178
|
+
maxDepth?: number;
|
|
179
|
+
/** Most entries one listing returns; the rest set `truncated`. Default `500`. */
|
|
180
|
+
maxEntries?: number;
|
|
181
|
+
}
|
|
56
182
|
/**
|
|
57
183
|
* The full policy that bounds a workspace — the single source of truth
|
|
58
184
|
* the policy engine enforces for both `.tools.*` and the direct methods.
|
|
@@ -83,9 +209,11 @@ interface WorkspacePolicy {
|
|
|
83
209
|
shell?: WorkspaceShellPolicy;
|
|
84
210
|
/** Read sub-policy (byte cap + default line window). */
|
|
85
211
|
read?: WorkspaceReadPolicy;
|
|
212
|
+
/** Directory-listing sub-policy (ignore globs + bounds). */
|
|
213
|
+
list?: WorkspaceListPolicy;
|
|
86
214
|
/** Executor the workspace runs on. Defaults to `"local"`. */
|
|
87
215
|
backend?: WorkspaceBackendType;
|
|
88
216
|
}
|
|
89
217
|
//#endregion
|
|
90
|
-
export { WorkspaceBackendType, WorkspacePolicy, WorkspaceReadPolicy, WorkspaceShellPolicy };
|
|
218
|
+
export { WorkspaceAllowCommand, WorkspaceBackendType, WorkspaceBackgroundPolicy, WorkspaceCommandCheckContext, WorkspaceCommandPattern, WorkspaceListPolicy, WorkspacePolicy, WorkspaceReadPolicy, WorkspaceShellInterpreter, WorkspaceShellPolicy };
|
|
91
219
|
//# sourceMappingURL=workspace-policy.type.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-policy.type.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"workspace-policy.type.d.mts","names":[],"sources":["../../../../../../../../packages/ai-workspace/src/contracts/workspace-policy.type.ts"],"mappings":";;AAQA;;;;AAAgC;AAiBhC;;KAjBY,oBAAA;;AAiByB;AAcrC;;;;AAAqD;AAGrD;;;;;;;;KAjBY,yBAAA;AA2BJ;AAcR;;;;;;;;AAEuC;AAUvC;;AA1BQ,KAbI,uBAAA,YAAmC,MAAM;;UAGpC,4BAAA;EAuEA;EArEf,OAAA;;EAEA,WAAA,EAAa,yBAAyB;EAiFvB;;;;;EA3Ef,MAAA;AAAA;;;;;;;;;;;;KAcU,qBAAA,IACV,IAAA,qBACA,OAAA,EAAS,4BAA4B;;;;AAsFC;AAOxC;;;UAnFiB,yBAAA;EA6FH;EA3FZ,OAAA;EAmGkC;;;;;EA7FlC,SAAS;AAAA;AAuGC;AAsBZ;;;;;;;;;;;;;;;;;;;;;AAcgC;;AApCpB,UA5EK,oBAAA;;EAEf,KAAA;;EAEA,IAAA;;;;;EAKA,YAAA,GAAe,uBAAA;;;;;EAKf,YAAA,GAAe,qBAAA;;;;;;;EAOf,WAAA,GAAc,yBAAA;;EAEd,SAAA;;;;;;EAMA,cAAA;;EAEA,GAAA,GAAM,MAAA;;;;;EAKN,UAAA;;;;;EAKA,UAAA,GAAa,yBAAA;AAAA;;;;;UAOE,mBAAA;;;;;;;;EAQf,QAAA;;EAEA,YAAY;AAAA;;;;;;UAQG,mBAAA;;;;;;EAMf,MAAA;;EAEA,QAAA;;EAEA,UAAA;AAAA;;;;;;;;;;;;;;;;;;;;UAsBe,eAAA;;EAEf,GAAA;;EAEA,UAAA;;EAEA,SAAA;;EAEA,KAAA,GAAQ,oBAAA;;EAER,IAAA,GAAO,mBAAA;;EAEP,IAAA,GAAO,mBAAA;;EAEP,OAAA,GAAU,oBAAA;AAAA"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { EditFileInput, EditFileResult, GrepResult, RunShellResult, WorkspaceToolName } from "./tool-io.type.mjs";
|
|
1
|
+
import { EditFileInput, EditFileResult, ExecOptions, GrepResult, ListDirOptions, ListDirResult, MultiEditInput, MultiEditResult, ReadFileResult, RunShellResult, ShellJobInfo, ShellOutputOptions, ShellOutputResult, StartShellOptions, TodoListResult, WorkspaceTodoInput, WorkspaceToolName } from "./tool-io.type.mjs";
|
|
2
2
|
import { WorkspacePolicy } from "./workspace-policy.type.mjs";
|
|
3
3
|
import { ToolContract } from "@warlock.js/ai";
|
|
4
4
|
|
|
5
|
-
//#region
|
|
5
|
+
//#region ../../packages/ai-workspace/src/contracts/workspace.contract.d.ts
|
|
6
6
|
/**
|
|
7
7
|
* The agent-facing tool surface of a workspace. Each factory returns a
|
|
8
8
|
* {@link ToolContract} ready to hand to `ai.agent({ tools })`. `all()`
|
|
@@ -14,7 +14,11 @@ import { ToolContract } from "@warlock.js/ai";
|
|
|
14
14
|
* multiple agents.
|
|
15
15
|
*/
|
|
16
16
|
interface WorkspaceTools {
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Every tool this workspace vends, as `ToolContract[]`, ready for
|
|
19
|
+
* `ai.agent({ tools })`. `shell_output` / `shell_kill` are included only
|
|
20
|
+
* when the policy enables background commands (`shell.background`).
|
|
21
|
+
*/
|
|
18
22
|
all(): ToolContract[];
|
|
19
23
|
/** A named subset, e.g. `pick("readFile", "grep")` for least privilege. */
|
|
20
24
|
pick(...names: WorkspaceToolName[]): ToolContract[];
|
|
@@ -26,14 +30,30 @@ interface WorkspaceTools {
|
|
|
26
30
|
editFile(opts?: {
|
|
27
31
|
name?: string;
|
|
28
32
|
}): ToolContract;
|
|
33
|
+
/** The `multi_edit` tool (several replacements in one file, all or none). */
|
|
34
|
+
multiEdit(opts?: {
|
|
35
|
+
name?: string;
|
|
36
|
+
}): ToolContract;
|
|
29
37
|
/** The `write_file` tool (atomic full-content write). */
|
|
30
38
|
writeFile(opts?: {
|
|
31
39
|
name?: string;
|
|
32
40
|
}): ToolContract;
|
|
33
|
-
/** The `
|
|
41
|
+
/** The `list_dir` tool (bounded, jailed directory listing). */
|
|
42
|
+
listDir(opts?: {
|
|
43
|
+
name?: string;
|
|
44
|
+
}): ToolContract;
|
|
45
|
+
/** The `run_shell` tool (policy-gated command execution, optionally in the background). */
|
|
34
46
|
runShell(opts?: {
|
|
35
47
|
name?: string;
|
|
36
48
|
}): ToolContract;
|
|
49
|
+
/** The `shell_output` tool (read a background command's new output). */
|
|
50
|
+
shellOutput(opts?: {
|
|
51
|
+
name?: string;
|
|
52
|
+
}): ToolContract;
|
|
53
|
+
/** The `shell_kill` tool (stop a background command). */
|
|
54
|
+
shellKill(opts?: {
|
|
55
|
+
name?: string;
|
|
56
|
+
}): ToolContract;
|
|
37
57
|
/** The `run_tests` tool (runs the configured test command). */
|
|
38
58
|
runTests(opts?: {
|
|
39
59
|
name?: string;
|
|
@@ -47,6 +67,14 @@ interface WorkspaceTools {
|
|
|
47
67
|
glob(opts?: {
|
|
48
68
|
name?: string;
|
|
49
69
|
}): ToolContract;
|
|
70
|
+
/** The `todo_write` tool (replace the workspace's todo list). */
|
|
71
|
+
todoWrite(opts?: {
|
|
72
|
+
name?: string;
|
|
73
|
+
}): ToolContract;
|
|
74
|
+
/** The `todo_read` tool (read the workspace's todo list). */
|
|
75
|
+
todoRead(opts?: {
|
|
76
|
+
name?: string;
|
|
77
|
+
}): ToolContract;
|
|
50
78
|
}
|
|
51
79
|
/**
|
|
52
80
|
* The bounded place an agent reads, writes, and runs commands — a
|
|
@@ -54,12 +82,11 @@ interface WorkspaceTools {
|
|
|
54
82
|
* ops layer, two ways to touch it: the agent uses `.tools.*`; you use the
|
|
55
83
|
* direct methods.
|
|
56
84
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* deferred to later phases and intentionally absent here.
|
|
85
|
+
* The surface: the policy jail, the tools, the direct methods,
|
|
86
|
+
* `tools.all()` / `tools.pick()`, the two composition projections
|
|
87
|
+
* `readonly()` and `scope()`, background commands, a todo list, and
|
|
88
|
+
* `dispose()`. Projections share their parent's background commands and
|
|
89
|
+
* todo list; disposing any of them disposes the whole workspace.
|
|
63
90
|
*
|
|
64
91
|
* @example
|
|
65
92
|
* const ws = ai.workspace({ cwd: "/srv/acme-api", shell: { allow: ["npm"], inheritEnv: ["PATH"] } });
|
|
@@ -77,18 +104,15 @@ interface Workspace {
|
|
|
77
104
|
/** Agent-facing tool factories. */
|
|
78
105
|
readonly tools: WorkspaceTools;
|
|
79
106
|
/**
|
|
80
|
-
* Read a jailed file. `offset`/`limit` select a 1-based line window
|
|
81
|
-
*
|
|
82
|
-
*
|
|
107
|
+
* Read a jailed file. `offset`/`limit` select a 1-based line window and
|
|
108
|
+
* `read.maxBytes` bounds the returned bytes; returns the numbered content,
|
|
109
|
+
* the full file's SHA-256 `hash` (for read-before-edit), and the window
|
|
110
|
+
* metadata — the same shape the `read_file` tool returns.
|
|
83
111
|
*/
|
|
84
112
|
readFile(path: string, opts?: {
|
|
85
113
|
offset?: number;
|
|
86
114
|
limit?: number;
|
|
87
|
-
}): Promise<
|
|
88
|
-
content: string;
|
|
89
|
-
hash: string;
|
|
90
|
-
totalLines: number;
|
|
91
|
-
}>;
|
|
115
|
+
}): Promise<ReadFileResult>;
|
|
92
116
|
/** Atomically write full content to a jailed path. */
|
|
93
117
|
writeFile(path: string, content: string): Promise<{
|
|
94
118
|
hash: string;
|
|
@@ -96,10 +120,39 @@ interface Workspace {
|
|
|
96
120
|
}>;
|
|
97
121
|
/** Apply an exact-string edit under the read-before-edit stale guard. */
|
|
98
122
|
editFile(input: EditFileInput): Promise<EditFileResult>;
|
|
99
|
-
/**
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
123
|
+
/** Apply several exact-string edits to one file — all of them or none. */
|
|
124
|
+
multiEdit(input: MultiEditInput): Promise<MultiEditResult>;
|
|
125
|
+
/** List a jailed directory (bounded, ignores applied, symlinks not followed). */
|
|
126
|
+
listDir(path?: string, opts?: ListDirOptions): Promise<ListDirResult>;
|
|
127
|
+
/**
|
|
128
|
+
* Run a policy-gated shell command and capture its outcome. `signal`
|
|
129
|
+
* cancels this one call: not started when already aborted, process tree
|
|
130
|
+
* killed when it aborts mid-run, `cancelled: true` either way.
|
|
131
|
+
*/
|
|
132
|
+
exec(command: string, opts?: ExecOptions): Promise<RunShellResult>;
|
|
133
|
+
/**
|
|
134
|
+
* Start a policy-checked background command owned by this workspace
|
|
135
|
+
* (needs `shell.background`). It runs until it exits, `shellKill(id)`,
|
|
136
|
+
* its timeout, or `dispose()`.
|
|
137
|
+
*/
|
|
138
|
+
startShell(command: string, opts?: StartShellOptions): Promise<ShellJobInfo>;
|
|
139
|
+
/** New output of a background command since `since` (default: the last read). */
|
|
140
|
+
shellOutput(id: string, opts?: ShellOutputOptions): Promise<ShellOutputResult>;
|
|
141
|
+
/** Kill a background command's whole process tree. */
|
|
142
|
+
shellKill(id: string): Promise<ShellJobInfo>;
|
|
143
|
+
/** Every background command this workspace knows, oldest first. */
|
|
144
|
+
listShells(): ShellJobInfo[];
|
|
145
|
+
/** The workspace's todo list (what `todo_read` returns). */
|
|
146
|
+
readTodos(): TodoListResult;
|
|
147
|
+
/** Replace the workspace's todo list (what `todo_write` does). */
|
|
148
|
+
writeTodos(todos: WorkspaceTodoInput[]): TodoListResult;
|
|
149
|
+
/**
|
|
150
|
+
* Close the workspace: kill every background command's process tree and
|
|
151
|
+
* refuse new commands (a foreground command already running finishes on
|
|
152
|
+
* its own, or by its timeout / signal). Idempotent; resolves once the
|
|
153
|
+
* background processes are gone.
|
|
154
|
+
*/
|
|
155
|
+
dispose(): Promise<void>;
|
|
103
156
|
/** Search jailed file contents for a regex pattern. */
|
|
104
157
|
grep(pattern: string, opts?: {
|
|
105
158
|
glob?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.contract.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"workspace.contract.d.mts","names":[],"sources":["../../../../../../../../packages/ai-workspace/src/contracts/workspace.contract.ts"],"mappings":";;;;;;;AAgCA;;;;;;;;UAAiB,cAAA;EAgBsB;;;;;EAVrC,GAAA,IAAO,YAAA;EAsByB;EApBhC,IAAA,IAAQ,KAAA,EAAO,iBAAA,KAAsB,YAAA;EAwBA;EAtBrC,QAAA,CAAS,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAJpC;EAMA,QAAA,CAAS,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAJrB;EAMf,SAAA,CAAU,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAJ5B;EAMT,SAAA,CAAU,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAJ5B;EAMT,OAAA,CAAQ,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAJzB;EAMV,QAAA,CAAS,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAJ1B;EAMV,WAAA,CAAY,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAJ/B;EAMR,SAAA,CAAU,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAJ5B;EAMT,QAAA,CAAS,IAAA;IAAS,IAAA;IAAe,OAAA;EAAA,IAAqB,YAAA;EAJf;EAMvC,IAAA,CAAK,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAJK;EAMrC,IAAA,CAAK,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAJvB;EAMT,SAAA,CAAU,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;EAJhC;EAML,QAAA,CAAS,IAAA;IAAS,IAAA;EAAA,IAAkB,YAAA;AAAA;;;;;;;;;;;AAAY;AAyBlD;;;;;;;;;;;UAAiB,SAAA;EAsB2B;EAAA,SApBjC,MAAA,EAAQ,eAAA;EAuBa;EAAA,SApBrB,KAAA,EAAO,cAAA;EAoB+B;;;;;;EAZ/C,QAAA,CAAS,IAAA,UAAc,IAAA;IAAS,MAAA;IAAiB,KAAA;EAAA,IAAmB,OAAA,CAAQ,cAAA;EAgC7C;EA7B/B,SAAA,CAAU,IAAA,UAAc,OAAA,WAAkB,OAAA;IAAU,IAAA;IAAc,YAAA;EAAA;EAsCzB;EAnCzC,QAAA,CAAS,KAAA,EAAO,aAAA,GAAgB,OAAA,CAAQ,cAAA;EA8CuC;EA3C/E,SAAA,CAAU,KAAA,EAAO,cAAA,GAAiB,OAAA,CAAQ,eAAA;EA8CnB;EA3CvB,OAAA,CAAQ,IAAA,WAAe,IAAA,GAAO,cAAA,GAAiB,OAAA,CAAQ,aAAA;EAiDlC;;;;;EA1CrB,IAAA,CAAK,OAAA,UAAiB,IAAA,GAAO,WAAA,GAAc,OAAA,CAAQ,cAAA;EA9B1C;;;;;EAqCT,UAAA,CAAW,OAAA,UAAiB,IAAA,GAAO,iBAAA,GAAoB,OAAA,CAAQ,YAAA;EA1B/B;EA6BhC,WAAA,CAAY,EAAA,UAAY,IAAA,GAAO,kBAAA,GAAqB,OAAA,CAAQ,iBAAA;EA7BrC;EAgCvB,SAAA,CAAU,EAAA,WAAa,OAAA,CAAQ,YAAA;EAhC6C;EAmC5E,UAAA,IAAc,YAAA;EAhCJ;EAmCV,SAAA,IAAa,cAAA;EAnC6B;EAsC1C,UAAA,CAAW,KAAA,EAAO,kBAAA,KAAuB,cAAA;EAtCyB;;;;;;EA8ClE,OAAA,IAAW,OAAA;EAxCM;EA2CjB,IAAA,CAAK,OAAA,UAAiB,IAAA;IAAS,IAAA;IAAe,UAAA;EAAA,IAAyB,OAAA,CAAQ,UAAA;EAxCvE;EA2CR,IAAA,CAAK,OAAA,WAAkB,OAAA;EA3CA;EA8CvB,MAAA,CAAO,IAAA,WAAe,OAAA;EA9CiC;EAiDvD,KAAA,CAAM,IAAA,WAAe,OAAA;EA1ChB;EA6CL,MAAA,CAAO,IAAA,WAAe,OAAA;EA7CA;;;;;EAoDtB,QAAA,IAAY,SAAA;EA7CgB;;;;;EAoD5B,KAAA,CAAM,MAAA,WAAiB,SAAA;AAAA"}
|
package/esm/errors.d.mts
CHANGED
|
@@ -1,18 +1,46 @@
|
|
|
1
1
|
import { AIError, AIErrorOptions } from "@warlock.js/ai";
|
|
2
2
|
|
|
3
|
-
//#region
|
|
3
|
+
//#region ../../packages/ai-workspace/src/errors.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Why a workspace policy check rejected an operation.
|
|
6
6
|
*
|
|
7
7
|
* - `"path-escape"` — a resolved path fell outside the `cwd` jail (or an
|
|
8
8
|
* `allowPaths` root), or matched a `denyPaths` glob.
|
|
9
|
-
* - `"denied-command"` —
|
|
10
|
-
*
|
|
9
|
+
* - `"denied-command"` — the shell policy refused a command line (`reason`
|
|
10
|
+
* says why: an executable outside `shell.allow` or inside `shell.deny`, a
|
|
11
|
+
* metacharacter in argv mode, a redirection outside the jail in shell
|
|
12
|
+
* mode, …), or a mutating method was called on a read-only projection.
|
|
11
13
|
* - `"unsafe-pattern"` — a `grep` pattern was too long or matched a
|
|
12
14
|
* catastrophic-backtracking shape (nested quantifiers) that could hang
|
|
13
15
|
* the process (ReDoS).
|
|
14
16
|
*/
|
|
15
17
|
type WorkspacePolicyViolation = "path-escape" | "denied-command" | "unsafe-pattern";
|
|
18
|
+
/**
|
|
19
|
+
* Why the shell policy refused a command. Set on every
|
|
20
|
+
* {@link WorkspacePolicyError} of type `"denied-command"` raised for a
|
|
21
|
+
* command line, so a host can phrase the refusal itself instead of parsing
|
|
22
|
+
* the message.
|
|
23
|
+
*
|
|
24
|
+
* - `"metacharacter"` — argv mode (the default `interpreter: "none"`) met an
|
|
25
|
+
* unquoted shell operator (`&&`, `;`, `|`, `>`, `$`, backticks, …). The
|
|
26
|
+
* message suggests how to split the line into separate commands.
|
|
27
|
+
* - `"unbalanced-quotes"` — a quote is never closed.
|
|
28
|
+
* - `"denied-executable"` — a command's executable is listed in `shell.deny`.
|
|
29
|
+
* - `"not-allowed"` — a command's executable is not in `shell.allow`, the
|
|
30
|
+
* `shell.allowCommand` hook refused it, the workspace has no shell policy,
|
|
31
|
+
* the line is empty, or background commands are not enabled.
|
|
32
|
+
* - `"denied-pattern"` — a command matched one of `shell.denyPatterns`.
|
|
33
|
+
* - `"nested-command"` — a command runs another command the policy cannot
|
|
34
|
+
* inspect (`bash -c …`, `eval`, `xargs`, `sudo`, a wrapper with options the
|
|
35
|
+
* policy cannot read), while the policy has deny rules it would bypass.
|
|
36
|
+
* - `"unsupported-syntax"` — shell mode (`interpreter: "sh"`) met syntax the
|
|
37
|
+
* policy does not parse with confidence (compound commands, arithmetic,
|
|
38
|
+
* brace expansion, background `&`, a command name decided at run time, …),
|
|
39
|
+
* so the line is refused rather than guessed at.
|
|
40
|
+
* - `"path-escape"` — shell mode: a redirection target or `cd` directory
|
|
41
|
+
* resolves outside the workspace jail or hits `denyPaths`.
|
|
42
|
+
*/
|
|
43
|
+
type WorkspaceShellRefusalReason = "metacharacter" | "unbalanced-quotes" | "denied-executable" | "not-allowed" | "denied-pattern" | "nested-command" | "unsupported-syntax" | "path-escape";
|
|
16
44
|
/**
|
|
17
45
|
* Options for {@link WorkspacePolicyError} — the structured `type`
|
|
18
46
|
* discriminator plus, where relevant, the offending path or command for
|
|
@@ -22,7 +50,9 @@ type WorkspacePolicyErrorOptions = AIErrorOptions & {
|
|
|
22
50
|
/** Which policy rule was violated. */type: WorkspacePolicyViolation; /** The offending workspace-relative path (for `"path-escape"`). */
|
|
23
51
|
path?: string; /** The offending command line (for `"denied-command"`). */
|
|
24
52
|
command?: string; /** The offending regex pattern (for `"unsafe-pattern"`). */
|
|
25
|
-
pattern?: string;
|
|
53
|
+
pattern?: string; /** Why the shell policy refused the command (for `"denied-command"`). */
|
|
54
|
+
reason?: WorkspaceShellRefusalReason; /** The executable the refusal is about, when there is one. */
|
|
55
|
+
executable?: string;
|
|
26
56
|
};
|
|
27
57
|
/**
|
|
28
58
|
* The workspace policy engine refused an operation — a path escaped the
|
|
@@ -50,6 +80,14 @@ declare class WorkspacePolicyError extends AIError {
|
|
|
50
80
|
readonly command?: string;
|
|
51
81
|
/** The offending regex pattern, when the violation was `"unsafe-pattern"`. */
|
|
52
82
|
readonly pattern?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Why the shell policy refused a command line — set on every
|
|
85
|
+
* `"denied-command"` refusal of a command (see
|
|
86
|
+
* {@link WorkspaceShellRefusalReason}).
|
|
87
|
+
*/
|
|
88
|
+
readonly reason?: WorkspaceShellRefusalReason;
|
|
89
|
+
/** The executable the shell refusal is about, when there is one. */
|
|
90
|
+
readonly executable?: string;
|
|
53
91
|
constructor(message: string, options: WorkspacePolicyErrorOptions);
|
|
54
92
|
}
|
|
55
93
|
/**
|
|
@@ -72,7 +110,8 @@ type WorkspaceEditErrorOptions = AIErrorOptions & {
|
|
|
72
110
|
path: string; /** How many times `oldString` matched (for `"not-unique"`). */
|
|
73
111
|
matches?: number; /** The hash the caller expected (for `"stale-hash"`). */
|
|
74
112
|
expectedHash?: string; /** The file's actual current hash (for `"stale-hash"`). */
|
|
75
|
-
actualHash?: string;
|
|
113
|
+
actualHash?: string; /** 0-based index of the failing edit, for a `multiEdit` rejection. */
|
|
114
|
+
editIndex?: number;
|
|
76
115
|
};
|
|
77
116
|
/**
|
|
78
117
|
* An `editFile` operation was rejected by the read-before-edit guard:
|
|
@@ -99,8 +138,43 @@ declare class WorkspaceEditError extends AIError {
|
|
|
99
138
|
readonly expectedHash?: string;
|
|
100
139
|
/** The file's actual current hash, for the `"stale-hash"` case. */
|
|
101
140
|
readonly actualHash?: string;
|
|
141
|
+
/**
|
|
142
|
+
* For a `multiEdit` rejection, the 0-based index of the edit that failed.
|
|
143
|
+
* Nothing was written: a multi-edit applies all of its edits or none.
|
|
144
|
+
*/
|
|
145
|
+
readonly editIndex?: number;
|
|
102
146
|
constructor(message: string, options: WorkspaceEditErrorOptions);
|
|
103
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Why a background-command operation failed.
|
|
150
|
+
*
|
|
151
|
+
* - `"not-found"` — no background command with that id exists in this
|
|
152
|
+
* workspace (it never existed, or it finished long enough ago to be
|
|
153
|
+
* pruned).
|
|
154
|
+
*/
|
|
155
|
+
type WorkspaceJobFailure = "not-found";
|
|
156
|
+
/** Options for {@link WorkspaceJobError}. */
|
|
157
|
+
type WorkspaceJobErrorOptions = AIErrorOptions & {
|
|
158
|
+
/** Why the operation failed. */type: WorkspaceJobFailure; /** The background command id the caller asked for. */
|
|
159
|
+
jobId: string;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* A `shell_output` / `shell_kill` call (or `shellOutput()` / `shellKill()`)
|
|
163
|
+
* named a background command this workspace does not know. Returned to the
|
|
164
|
+
* agent as tool-error data like the other workspace errors.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* if (error instanceof WorkspaceJobError && error.type === "not-found") {
|
|
168
|
+
* console.warn(`No background command ${error.jobId}`);
|
|
169
|
+
* }
|
|
170
|
+
*/
|
|
171
|
+
declare class WorkspaceJobError extends AIError {
|
|
172
|
+
/** Why the operation failed. */
|
|
173
|
+
readonly type: WorkspaceJobFailure;
|
|
174
|
+
/** The background command id the caller asked for. */
|
|
175
|
+
readonly jobId: string;
|
|
176
|
+
constructor(message: string, options: WorkspaceJobErrorOptions);
|
|
177
|
+
}
|
|
104
178
|
//#endregion
|
|
105
|
-
export { WorkspaceEditError, WorkspaceEditErrorOptions, WorkspaceEditFailure, WorkspacePolicyError, WorkspacePolicyErrorOptions, WorkspacePolicyViolation };
|
|
179
|
+
export { WorkspaceEditError, WorkspaceEditErrorOptions, WorkspaceEditFailure, WorkspaceJobError, WorkspaceJobErrorOptions, WorkspaceJobFailure, WorkspacePolicyError, WorkspacePolicyErrorOptions, WorkspacePolicyViolation, WorkspaceShellRefusalReason };
|
|
106
180
|
//# sourceMappingURL=errors.d.mts.map
|
package/esm/errors.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.mts","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"errors.d.mts","names":[],"sources":["../../../../../../../packages/ai-workspace/src/errors.ts"],"mappings":";;;;;AAeA;;;;AAAoC;AA2BpC;;;;AAAuC;AAevC;KA1CY,wBAAA;;;;;;;;;;;;;;;;;AAsDA;AAoBZ;;;;;;;;KA/CY,2BAAA;;;;;;KAeA,2BAAA,GAA8B,cAAA;EA8CxB,sCA5ChB,IAAA,EAAM,wBAAA,EA8CU;EA5ChB,IAAA,WA8CmB;EA5CnB,OAAA,WA4CoC;EA1CpC,OAAA,WA0CwE;EAxCxE,MAAA,GAAS,2BAAA,EA8DqB;EA5D9B,UAAA;AAAA;AA4D8B;AAOhC;;;;;;;;;;;;;AAYW;AAiBX;;AApCgC,cAxCnB,oBAAA,SAA6B,OAAA;EA8ElB;EAAA,SA5EN,IAAA,EAAM,wBAAA;EA0EgB;EAAA,SAxEtB,IAAA;EAwE6B;EAAA,SAtE7B,OAAA;EAwEA;EAAA,SAtEA,OAAA;EAwEA;;;;;EAAA,SAlEA,MAAA,GAAS,2BAAA;EA+EN;EAAA,SA7EH,UAAA;cAEG,OAAA,UAAiB,OAAA,EAAS,2BAAA;AAAA;AA2EyB;AAoBxE;;;;AAA+B;AAG/B;;;AAvBwE,KArD5D,oBAAA;;;;;;KAOA,yBAAA,GAA4B,cAAA;EAsF3B,iCApFX,IAAA,EAAM,oBAAoB;EAE1B,IAAA,UAwF6C;EAtF7C,OAAA,WAgF4C;EA9E5C,YAAA,WA8EqC;EA5ErC,UAAA,WA8EsB;EA5EtB,SAAA;AAAA;;;;;AAgFqE;;;;;;;;;;cA/D1D,kBAAA,SAA2B,OAAA;;WAEtB,IAAA,EAAM,oBAAA;;WAEN,IAAA;;WAEA,OAAA;;WAEA,YAAA;;WAEA,UAAA;;;;;WAKA,SAAA;cAEG,OAAA,UAAiB,OAAA,EAAS,yBAAA;AAAA;;;;;;;;KAoBnC,mBAAA;;KAGA,wBAAA,GAA2B,cAAA;kCAErC,IAAA,EAAM,mBAAmB;EAEzB,KAAA;AAAA;;;;;;;;;;;cAaW,iBAAA,SAA0B,OAAA;;WAErB,IAAA,EAAM,mBAAA;;WAEN,KAAA;cAEG,OAAA,UAAiB,OAAA,EAAS,wBAAA;AAAA"}
|
package/esm/errors.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AIError } from "@warlock.js/ai";
|
|
2
2
|
|
|
3
|
-
//#region
|
|
3
|
+
//#region ../../packages/ai-workspace/src/errors.ts
|
|
4
4
|
/**
|
|
5
5
|
* The workspace policy engine refused an operation — a path escaped the
|
|
6
6
|
* jail (or hit a deny glob), or a shell command's executable was not
|
|
@@ -26,6 +26,8 @@ var WorkspacePolicyError = class extends AIError {
|
|
|
26
26
|
this.path = options.path;
|
|
27
27
|
this.command = options.command;
|
|
28
28
|
this.pattern = options.pattern;
|
|
29
|
+
this.reason = options.reason;
|
|
30
|
+
this.executable = options.executable;
|
|
29
31
|
}
|
|
30
32
|
};
|
|
31
33
|
/**
|
|
@@ -51,9 +53,28 @@ var WorkspaceEditError = class extends AIError {
|
|
|
51
53
|
this.matches = options.matches;
|
|
52
54
|
this.expectedHash = options.expectedHash;
|
|
53
55
|
this.actualHash = options.actualHash;
|
|
56
|
+
this.editIndex = options.editIndex;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* A `shell_output` / `shell_kill` call (or `shellOutput()` / `shellKill()`)
|
|
61
|
+
* named a background command this workspace does not know. Returned to the
|
|
62
|
+
* agent as tool-error data like the other workspace errors.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* if (error instanceof WorkspaceJobError && error.type === "not-found") {
|
|
66
|
+
* console.warn(`No background command ${error.jobId}`);
|
|
67
|
+
* }
|
|
68
|
+
*/
|
|
69
|
+
var WorkspaceJobError = class extends AIError {
|
|
70
|
+
constructor(message, options) {
|
|
71
|
+
super("TOOL_EXEC_FAILED", message, options);
|
|
72
|
+
this.name = "WorkspaceJobError";
|
|
73
|
+
this.type = options.type;
|
|
74
|
+
this.jobId = options.jobId;
|
|
54
75
|
}
|
|
55
76
|
};
|
|
56
77
|
|
|
57
78
|
//#endregion
|
|
58
|
-
export { WorkspaceEditError, WorkspacePolicyError };
|
|
79
|
+
export { WorkspaceEditError, WorkspaceJobError, WorkspacePolicyError };
|
|
59
80
|
//# sourceMappingURL=errors.mjs.map
|
package/esm/errors.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.mjs","names":[],"sources":["../../../../../../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 * - `\"unsafe-pattern\"` — a `grep` pattern was too long or matched a\n * catastrophic-backtracking shape (nested quantifiers) that could hang\n * the process (ReDoS).\n */\nexport type WorkspacePolicyViolation = \"path-escape\" | \"denied-command\" | \"unsafe-pattern\";\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 /** The offending regex pattern (for `\"unsafe-pattern\"`). */\n pattern?: 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 /** The offending regex pattern, when the violation was `\"unsafe-pattern\"`. */\n public readonly pattern?: 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 this.pattern = options.pattern;\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":";;;;;;;;;;;;;;;;;;;;AAgDA,IAAa,uBAAb,cAA0C,QAAQ;CAUhD,AAAO,YAAY,SAAiB,SAAsC;EACxE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;EACpB,KAAK,OAAO,QAAQ;EACpB,KAAK,UAAU,QAAQ;EACvB,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"}
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../../../../../../../packages/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\"` — the shell policy refused a command line (`reason`\n * says why: an executable outside `shell.allow` or inside `shell.deny`, a\n * metacharacter in argv mode, a redirection outside the jail in shell\n * mode, …), or a mutating method was called on a read-only projection.\n * - `\"unsafe-pattern\"` — a `grep` pattern was too long or matched a\n * catastrophic-backtracking shape (nested quantifiers) that could hang\n * the process (ReDoS).\n */\nexport type WorkspacePolicyViolation = \"path-escape\" | \"denied-command\" | \"unsafe-pattern\";\n\n/**\n * Why the shell policy refused a command. Set on every\n * {@link WorkspacePolicyError} of type `\"denied-command\"` raised for a\n * command line, so a host can phrase the refusal itself instead of parsing\n * the message.\n *\n * - `\"metacharacter\"` — argv mode (the default `interpreter: \"none\"`) met an\n * unquoted shell operator (`&&`, `;`, `|`, `>`, `$`, backticks, …). The\n * message suggests how to split the line into separate commands.\n * - `\"unbalanced-quotes\"` — a quote is never closed.\n * - `\"denied-executable\"` — a command's executable is listed in `shell.deny`.\n * - `\"not-allowed\"` — a command's executable is not in `shell.allow`, the\n * `shell.allowCommand` hook refused it, the workspace has no shell policy,\n * the line is empty, or background commands are not enabled.\n * - `\"denied-pattern\"` — a command matched one of `shell.denyPatterns`.\n * - `\"nested-command\"` — a command runs another command the policy cannot\n * inspect (`bash -c …`, `eval`, `xargs`, `sudo`, a wrapper with options the\n * policy cannot read), while the policy has deny rules it would bypass.\n * - `\"unsupported-syntax\"` — shell mode (`interpreter: \"sh\"`) met syntax the\n * policy does not parse with confidence (compound commands, arithmetic,\n * brace expansion, background `&`, a command name decided at run time, …),\n * so the line is refused rather than guessed at.\n * - `\"path-escape\"` — shell mode: a redirection target or `cd` directory\n * resolves outside the workspace jail or hits `denyPaths`.\n */\nexport type WorkspaceShellRefusalReason =\n | \"metacharacter\"\n | \"unbalanced-quotes\"\n | \"denied-executable\"\n | \"not-allowed\"\n | \"denied-pattern\"\n | \"nested-command\"\n | \"unsupported-syntax\"\n | \"path-escape\";\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 /** The offending regex pattern (for `\"unsafe-pattern\"`). */\n pattern?: string;\n /** Why the shell policy refused the command (for `\"denied-command\"`). */\n reason?: WorkspaceShellRefusalReason;\n /** The executable the refusal is about, when there is one. */\n executable?: 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 /** The offending regex pattern, when the violation was `\"unsafe-pattern\"`. */\n public readonly pattern?: string;\n /**\n * Why the shell policy refused a command line — set on every\n * `\"denied-command\"` refusal of a command (see\n * {@link WorkspaceShellRefusalReason}).\n */\n public readonly reason?: WorkspaceShellRefusalReason;\n /** The executable the shell refusal is about, when there is one. */\n public readonly executable?: 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 this.pattern = options.pattern;\n this.reason = options.reason;\n this.executable = options.executable;\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 /** 0-based index of the failing edit, for a `multiEdit` rejection. */\n editIndex?: number;\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 * For a `multiEdit` rejection, the 0-based index of the edit that failed.\n * Nothing was written: a multi-edit applies all of its edits or none.\n */\n public readonly editIndex?: number;\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 this.editIndex = options.editIndex;\n }\n}\n\n/**\n * Why a background-command operation failed.\n *\n * - `\"not-found\"` — no background command with that id exists in this\n * workspace (it never existed, or it finished long enough ago to be\n * pruned).\n */\nexport type WorkspaceJobFailure = \"not-found\";\n\n/** Options for {@link WorkspaceJobError}. */\nexport type WorkspaceJobErrorOptions = AIErrorOptions & {\n /** Why the operation failed. */\n type: WorkspaceJobFailure;\n /** The background command id the caller asked for. */\n jobId: string;\n};\n\n/**\n * A `shell_output` / `shell_kill` call (or `shellOutput()` / `shellKill()`)\n * named a background command this workspace does not know. Returned to the\n * agent as tool-error data like the other workspace errors.\n *\n * @example\n * if (error instanceof WorkspaceJobError && error.type === \"not-found\") {\n * console.warn(`No background command ${error.jobId}`);\n * }\n */\nexport class WorkspaceJobError extends AIError {\n /** Why the operation failed. */\n public readonly type: WorkspaceJobFailure;\n /** The background command id the caller asked for. */\n public readonly jobId: string;\n\n public constructor(message: string, options: WorkspaceJobErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"WorkspaceJobError\";\n this.type = options.type;\n this.jobId = options.jobId;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAyFA,IAAa,uBAAb,cAA0C,QAAQ;CAkBhD,AAAO,YAAY,SAAiB,SAAsC;EACxE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;EACpB,KAAK,OAAO,QAAQ;EACpB,KAAK,UAAU,QAAQ;EACvB,KAAK,UAAU,QAAQ;EACvB,KAAK,SAAS,QAAQ;EACtB,KAAK,aAAa,QAAQ;CAC5B;AACF;;;;;;;;;;;;;;;AA+CA,IAAa,qBAAb,cAAwC,QAAQ;CAiB9C,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;EAC1B,KAAK,YAAY,QAAQ;CAC3B;AACF;;;;;;;;;;;AA6BA,IAAa,oBAAb,cAAuC,QAAQ;CAM7C,AAAO,YAAY,SAAiB,SAAmC;EACrE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;EACpB,KAAK,QAAQ,QAAQ;CACvB;AACF"}
|