@warlock.js/ai-workspace 5.30.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.
Files changed (114) hide show
  1. package/CHANGELOG.md +46 -2
  2. package/README.md +34 -21
  3. package/cjs/index.cjs +3116 -309
  4. package/cjs/index.cjs.map +1 -1
  5. package/esm/backends/local.d.mts.map +1 -1
  6. package/esm/backends/local.mjs +165 -27
  7. package/esm/backends/local.mjs.map +1 -1
  8. package/esm/backends/mock.d.mts +11 -5
  9. package/esm/backends/mock.d.mts.map +1 -1
  10. package/esm/backends/mock.mjs +34 -0
  11. package/esm/backends/mock.mjs.map +1 -1
  12. package/esm/contracts/index.d.mts +3 -3
  13. package/esm/contracts/tool-io.type.d.mts +202 -4
  14. package/esm/contracts/tool-io.type.d.mts.map +1 -1
  15. package/esm/contracts/workspace-backend.contract.d.mts +61 -1
  16. package/esm/contracts/workspace-backend.contract.d.mts.map +1 -1
  17. package/esm/contracts/workspace-ops.contract.d.mts +32 -3
  18. package/esm/contracts/workspace-ops.contract.d.mts.map +1 -1
  19. package/esm/contracts/workspace-policy.type.d.mts +133 -12
  20. package/esm/contracts/workspace-policy.type.d.mts.map +1 -1
  21. package/esm/contracts/workspace.contract.d.mts +63 -9
  22. package/esm/contracts/workspace.contract.d.mts.map +1 -1
  23. package/esm/errors.d.mts +79 -5
  24. package/esm/errors.d.mts.map +1 -1
  25. package/esm/errors.mjs +22 -1
  26. package/esm/errors.mjs.map +1 -1
  27. package/esm/index.d.mts +13 -5
  28. package/esm/index.mjs +10 -2
  29. package/esm/ops.d.mts +21 -2
  30. package/esm/ops.d.mts.map +1 -1
  31. package/esm/ops.mjs +230 -64
  32. package/esm/ops.mjs.map +1 -1
  33. package/esm/policy/jail.mjs +73 -0
  34. package/esm/policy/jail.mjs.map +1 -0
  35. package/esm/policy/policy.d.mts +25 -15
  36. package/esm/policy/policy.d.mts.map +1 -1
  37. package/esm/policy/policy.mjs +61 -85
  38. package/esm/policy/policy.mjs.map +1 -1
  39. package/esm/policy/shell-check.d.mts +64 -0
  40. package/esm/policy/shell-check.d.mts.map +1 -0
  41. package/esm/policy/shell-check.mjs +627 -0
  42. package/esm/policy/shell-check.mjs.map +1 -0
  43. package/esm/policy/shell-parser.mjs +802 -0
  44. package/esm/policy/shell-parser.mjs.map +1 -0
  45. package/esm/policy/tokenize-command.d.mts +63 -0
  46. package/esm/policy/tokenize-command.d.mts.map +1 -0
  47. package/esm/policy/tokenize-command.mjs +75 -23
  48. package/esm/policy/tokenize-command.mjs.map +1 -1
  49. package/esm/shell-jobs.d.mts +45 -0
  50. package/esm/shell-jobs.d.mts.map +1 -0
  51. package/esm/shell-jobs.mjs +247 -0
  52. package/esm/shell-jobs.mjs.map +1 -0
  53. package/esm/todos.d.mts +22 -0
  54. package/esm/todos.d.mts.map +1 -0
  55. package/esm/todos.mjs +73 -0
  56. package/esm/todos.mjs.map +1 -0
  57. package/esm/tools/edit-file.d.mts.map +1 -1
  58. package/esm/tools/edit-file.mjs +2 -1
  59. package/esm/tools/edit-file.mjs.map +1 -1
  60. package/esm/tools/glob.d.mts.map +1 -1
  61. package/esm/tools/glob.mjs +2 -1
  62. package/esm/tools/glob.mjs.map +1 -1
  63. package/esm/tools/grep.d.mts.map +1 -1
  64. package/esm/tools/grep.mjs +2 -1
  65. package/esm/tools/grep.mjs.map +1 -1
  66. package/esm/tools/json-schemas.mjs +175 -0
  67. package/esm/tools/json-schemas.mjs.map +1 -0
  68. package/esm/tools/list-dir.d.mts +32 -0
  69. package/esm/tools/list-dir.d.mts.map +1 -0
  70. package/esm/tools/list-dir.mjs +50 -0
  71. package/esm/tools/list-dir.mjs.map +1 -0
  72. package/esm/tools/multi-edit.d.mts +38 -0
  73. package/esm/tools/multi-edit.d.mts.map +1 -0
  74. package/esm/tools/multi-edit.mjs +121 -0
  75. package/esm/tools/multi-edit.mjs.map +1 -0
  76. package/esm/tools/read-file.d.mts.map +1 -1
  77. package/esm/tools/read-file.mjs +2 -1
  78. package/esm/tools/read-file.mjs.map +1 -1
  79. package/esm/tools/run-shell.d.mts +34 -12
  80. package/esm/tools/run-shell.d.mts.map +1 -1
  81. package/esm/tools/run-shell.mjs +59 -53
  82. package/esm/tools/run-shell.mjs.map +1 -1
  83. package/esm/tools/run-tests.d.mts +8 -0
  84. package/esm/tools/run-tests.d.mts.map +1 -1
  85. package/esm/tools/run-tests.mjs +10 -3
  86. package/esm/tools/run-tests.mjs.map +1 -1
  87. package/esm/tools/schema.mjs +23 -18
  88. package/esm/tools/schema.mjs.map +1 -1
  89. package/esm/tools/shell-kill.d.mts +25 -0
  90. package/esm/tools/shell-kill.d.mts.map +1 -0
  91. package/esm/tools/shell-kill.mjs +36 -0
  92. package/esm/tools/shell-kill.mjs.map +1 -0
  93. package/esm/tools/shell-output.d.mts +30 -0
  94. package/esm/tools/shell-output.d.mts.map +1 -0
  95. package/esm/tools/shell-output.mjs +49 -0
  96. package/esm/tools/shell-output.mjs.map +1 -0
  97. package/esm/tools/todo.d.mts +43 -0
  98. package/esm/tools/todo.d.mts.map +1 -0
  99. package/esm/tools/todo.mjs +126 -0
  100. package/esm/tools/todo.mjs.map +1 -0
  101. package/esm/tools/write-file.d.mts.map +1 -1
  102. package/esm/tools/write-file.mjs +2 -1
  103. package/esm/tools/write-file.mjs.map +1 -1
  104. package/esm/workspace.d.mts +11 -8
  105. package/esm/workspace.d.mts.map +1 -1
  106. package/esm/workspace.mjs +108 -27
  107. package/esm/workspace.mjs.map +1 -1
  108. package/llms-full.txt +155 -34
  109. package/llms.txt +1 -1
  110. package/package.json +3 -3
  111. package/skills/README.md +2 -2
  112. package/skills/build-loop-agent/SKILL.md +7 -7
  113. package/skills/index.md +6 -1
  114. package/skills/use-a-workspace/SKILL.md +148 -27
@@ -1,4 +1,4 @@
1
- import { EditFileInput, EditFileResult, ExecOptions, GrepResult, ReadFileResult, 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
 
@@ -14,7 +14,11 @@ import { ToolContract } from "@warlock.js/ai";
14
14
  * multiple agents.
15
15
  */
16
16
  interface WorkspaceTools {
17
- /** All seven tools as `ToolContract[]`, ready for `ai.agent({ tools })`. */
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 `run_shell` tool (policy-gated command execution). */
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
- * 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.
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"] } });
@@ -93,12 +120,39 @@ interface Workspace {
93
120
  }>;
94
121
  /** Apply an exact-string edit under the read-before-edit stale guard. */
95
122
  editFile(input: EditFileInput): Promise<EditFileResult>;
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>;
96
127
  /**
97
128
  * Run a policy-gated shell command and capture its outcome. `signal`
98
129
  * cancels this one call: not started when already aborted, process tree
99
130
  * killed when it aborts mid-run, `cancelled: true` either way.
100
131
  */
101
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>;
102
156
  /** Search jailed file contents for a regex pattern. */
103
157
  grep(pattern: string, opts?: {
104
158
  glob?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.contract.d.mts","names":[],"sources":["../../../../../../../../packages/ai-workspace/src/contracts/workspace.contract.ts"],"mappings":";;;;;;;AAsBA;;;;;;;;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;EAsCZ;EAAA,SApCM,MAAA,EAAQ,eAAA;EA0CK;EAAA,SAvCb,KAAA,EAAO,cAAA;EA6CM;;;;;;EArCtB,QAAA,CACE,IAAA,UACA,IAAA;IAAS,MAAA;IAAiB,KAAA;EAAA,IACzB,OAAA,CAAQ,cAAA;EAFT;EAKF,SAAA,CACE,IAAA,UACA,OAAA,WACC,OAAA;IAAU,IAAA;IAAc,YAAA;EAAA;EANhB;EASX,QAAA,CAAS,KAAA,EAAO,aAAA,GAAgB,OAAA,CAAQ,cAAA;EALtC;;;;;EAYF,IAAA,CAAK,OAAA,UAAiB,IAAA,GAAO,WAAA,GAAc,OAAA,CAAQ,cAAA;EAPnC;EAUhB,IAAA,CACE,OAAA,UACA,IAAA;IAAS,IAAA;IAAe,UAAA;EAAA,IACvB,OAAA,CAAQ,UAAA;EANN;EASL,IAAA,CAAK,OAAA,WAAkB,OAAA;EATD;EAYtB,MAAA,CAAO,IAAA,WAAe,OAAA;EAZ6B;EAenD,KAAA,CAAM,IAAA,WAAe,OAAA;EAXnB;EAcF,MAAA,CAAO,IAAA,WAAe,OAAA;EAbI;;;;;EAoB1B,QAAA,IAAY,SAAA;EAhBW;;;;;EAuBvB,KAAA,CAAM,MAAA,WAAiB,SAAA;AAAA"}
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
@@ -6,13 +6,41 @@ import { AIError, AIErrorOptions } from "@warlock.js/ai";
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"` — a shell command's leading executable basename
10
- * was not in `shell.allow`, or was explicitly in `shell.deny`.
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
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.mts","names":[],"sources":["../../../../../../../packages/ai-workspace/src/errors.ts"],"mappings":";;;;;AAaA;;;;AAAoC;AAOpC;;;;KAPY,wBAAA;;;;;;KAOA,2BAAA,GAA8B,cAAA;EAQjC,sCANP,IAAA,EAAM,wBAAwB,EA0BE;EAxBhC,IAAA,WA0BsB;EAxBtB,OAAA,WAsBwC;EApBxC,OAAA;AAAA;;;;;;;;;;;;AA8BwE;AAoB1E;;;;AAAgC;cA9BnB,oBAAA,SAA6B,OAAA;EAqCL;EAAA,SAnCnB,IAAA,EAAM,wBAAA;EAqCI;EAAA,SAnCV,IAAA;EAmChB;EAAA,SAjCgB,OAAA;EAmChB;EAAA,SAjCgB,OAAA;cAEG,OAAA,UAAiB,OAAA,EAAS,2BAAA;AAAA;;AAqCnC;AAiBZ;;;;;;;KAlCY,oBAAA;;;;;;KAOA,yBAAA,GAA4B,cAAA;EAqCtB,iCAnChB,IAAA,EAAM,oBAAoB,EAqCP;EAnCnB,IAAA,UAmCoC;EAjCpC,OAAA,WAiCsE;EA/BtE,YAAA;EAEA,UAAA;AAAA;;;;;;;;;;;;;;;cAiBW,kBAAA,SAA2B,OAAA;;WAEtB,IAAA,EAAM,oBAAA;;WAEN,IAAA;;WAEA,OAAA;;WAEA,YAAA;;WAEA,UAAA;cAEG,OAAA,UAAiB,OAAA,EAAS,yBAAA;AAAA"}
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
@@ -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
@@ -1 +1 @@
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\"` — 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"}
package/esm/index.d.mts CHANGED
@@ -1,20 +1,28 @@
1
- import { EditFileInput, EditFileResult, ExecOptions, 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";
1
+ import { EditFileInput, EditFileResult, ExecOptions, GlobInput, GlobResult, GrepInput, GrepMatch, GrepResult, ListDirEntry, ListDirEntryType, ListDirInput, ListDirOptions, ListDirResult, MultiEditInput, MultiEditOperation, MultiEditResult, ReadFileInput, ReadFileResult, RunShellBackgroundResult, RunShellInput, RunShellResult, RunTestsInput, ShellJobInfo, ShellJobStatus, ShellKillInput, ShellOutputInput, ShellOutputOptions, ShellOutputResult, StartShellOptions, TodoListResult, TodoWriteInput, WorkspaceTodo, WorkspaceTodoInput, WorkspaceTodoStatus, WorkspaceToolName, WriteFileInput, WriteFileResult } from "./contracts/tool-io.type.mjs";
2
+ import { WorkspaceAllowCommand, WorkspaceBackendType, WorkspaceBackgroundPolicy, WorkspaceCommandCheckContext, WorkspaceCommandPattern, WorkspaceListPolicy, WorkspacePolicy, WorkspaceReadPolicy, WorkspaceShellInterpreter, WorkspaceShellPolicy } from "./contracts/workspace-policy.type.mjs";
3
+ import { WorkspaceBackend, WorkspaceBackendExecOptions, WorkspaceBackendExecResult, WorkspaceBackendProcess, WorkspaceBackendStartOptions, WorkspaceBackendStat, WorkspaceOutputStream } from "./contracts/workspace-backend.contract.mjs";
3
4
  import { WorkspaceOps } from "./contracts/workspace-ops.contract.mjs";
4
- import { WorkspaceBackendType, WorkspacePolicy, WorkspaceReadPolicy, WorkspaceShellPolicy } from "./contracts/workspace-policy.type.mjs";
5
5
  import { Workspace, WorkspaceTools } from "./contracts/workspace.contract.mjs";
6
- import { WorkspaceEditError, WorkspaceEditErrorOptions, WorkspaceEditFailure, WorkspacePolicyError, WorkspacePolicyErrorOptions, WorkspacePolicyViolation } from "./errors.mjs";
6
+ import { WorkspaceEditError, WorkspaceEditErrorOptions, WorkspaceEditFailure, WorkspaceJobError, WorkspaceJobErrorOptions, WorkspaceJobFailure, WorkspacePolicyError, WorkspacePolicyErrorOptions, WorkspacePolicyViolation, WorkspaceShellRefusalReason } from "./errors.mjs";
7
7
  import { ResolvedPath, buildEnv, isCommandAllowed, resolveInJail } from "./policy/policy.mjs";
8
+ import { WorkspaceShellCheck, WorkspaceShellPath, WorkspaceShellPathAccess, checkShellCommand } from "./policy/shell-check.mjs";
9
+ import { ArgvAnalysis, analyzeArgvCommand, tokenizeCommand } from "./policy/tokenize-command.mjs";
8
10
  import { createOps } from "./ops.mjs";
9
11
  import { createLocalBackend } from "./backends/local.mjs";
10
12
  import { MockBackendSeed, MockExecResult, createMockBackend } from "./backends/mock.mjs";
11
13
  import { makeEditFileTool } from "./tools/edit-file.mjs";
12
14
  import { MakeGlobToolOptions, makeGlobTool } from "./tools/glob.mjs";
13
15
  import { MakeGrepToolOptions, makeGrepTool } from "./tools/grep.mjs";
16
+ import { makeListDirTool } from "./tools/list-dir.mjs";
17
+ import { makeMultiEditTool } from "./tools/multi-edit.mjs";
14
18
  import { makeReadFileTool } from "./tools/read-file.mjs";
15
19
  import { MakeRunShellToolOptions, makeRunShellTool } from "./tools/run-shell.mjs";
16
20
  import { MakeRunTestsToolOptions, makeRunTestsTool } from "./tools/run-tests.mjs";
21
+ import { makeShellKillTool } from "./tools/shell-kill.mjs";
22
+ import { makeShellOutputTool } from "./tools/shell-output.mjs";
23
+ import { TodoStore } from "./todos.mjs";
24
+ import { makeTodoReadTool, makeTodoWriteTool } from "./tools/todo.mjs";
17
25
  import { makeWriteFileTool } from "./tools/write-file.mjs";
18
26
  import { workspace } from "./workspace.mjs";
19
- export { type EditFileInput, type EditFileResult, type ExecOptions, 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 };
27
+ export { type ArgvAnalysis, type EditFileInput, type EditFileResult, type ExecOptions, type GlobInput, type GlobResult, type GrepInput, type GrepMatch, type GrepResult, type ListDirEntry, type ListDirEntryType, type ListDirInput, type ListDirOptions, type ListDirResult, type MakeGlobToolOptions, type MakeGrepToolOptions, type MakeRunShellToolOptions, type MakeRunTestsToolOptions, type MockBackendSeed, type MockExecResult, type MultiEditInput, type MultiEditOperation, type MultiEditResult, type ReadFileInput, type ReadFileResult, type ResolvedPath, type RunShellBackgroundResult, type RunShellInput, type RunShellResult, type RunTestsInput, type ShellJobInfo, type ShellJobStatus, type ShellKillInput, type ShellOutputInput, type ShellOutputOptions, type ShellOutputResult, type StartShellOptions, type TodoListResult, TodoStore, type TodoWriteInput, type Workspace, type WorkspaceAllowCommand, type WorkspaceBackend, type WorkspaceBackendExecOptions, type WorkspaceBackendExecResult, type WorkspaceBackendProcess, type WorkspaceBackendStartOptions, type WorkspaceBackendStat, type WorkspaceBackendType, type WorkspaceBackgroundPolicy, type WorkspaceCommandCheckContext, type WorkspaceCommandPattern, WorkspaceEditError, type WorkspaceEditErrorOptions, type WorkspaceEditFailure, WorkspaceJobError, type WorkspaceJobErrorOptions, type WorkspaceJobFailure, type WorkspaceListPolicy, type WorkspaceOps, type WorkspaceOutputStream, type WorkspacePolicy, WorkspacePolicyError, type WorkspacePolicyErrorOptions, type WorkspacePolicyViolation, type WorkspaceReadPolicy, type WorkspaceShellCheck, type WorkspaceShellInterpreter, type WorkspaceShellPath, type WorkspaceShellPathAccess, type WorkspaceShellPolicy, type WorkspaceShellRefusalReason, type WorkspaceTodo, type WorkspaceTodoInput, type WorkspaceTodoStatus, type WorkspaceToolName, type WorkspaceTools, type WriteFileInput, type WriteFileResult, analyzeArgvCommand, buildEnv, checkShellCommand, createLocalBackend, createMockBackend, createOps, isCommandAllowed, makeEditFileTool, makeGlobTool, makeGrepTool, makeListDirTool, makeMultiEditTool, makeReadFileTool, makeRunShellTool, makeRunTestsTool, makeShellKillTool, makeShellOutputTool, makeTodoReadTool, makeTodoWriteTool, makeWriteFileTool, resolveInJail, tokenizeCommand, workspace };
20
28
  import "./workspace.mjs";
package/esm/index.mjs CHANGED
@@ -1,4 +1,6 @@
1
- import { WorkspaceEditError, WorkspacePolicyError } from "./errors.mjs";
1
+ import { WorkspaceEditError, WorkspaceJobError, WorkspacePolicyError } from "./errors.mjs";
2
+ import { analyzeArgvCommand, tokenizeCommand } from "./policy/tokenize-command.mjs";
3
+ import { checkShellCommand } from "./policy/shell-check.mjs";
2
4
  import { buildEnv, isCommandAllowed, resolveInJail } from "./policy/policy.mjs";
3
5
  import { createOps } from "./ops.mjs";
4
6
  import { createLocalBackend } from "./backends/local.mjs";
@@ -6,10 +8,16 @@ import { createMockBackend } from "./backends/mock.mjs";
6
8
  import { makeEditFileTool } from "./tools/edit-file.mjs";
7
9
  import { makeGlobTool } from "./tools/glob.mjs";
8
10
  import { makeGrepTool } from "./tools/grep.mjs";
11
+ import { makeListDirTool } from "./tools/list-dir.mjs";
12
+ import { makeMultiEditTool } from "./tools/multi-edit.mjs";
9
13
  import { makeReadFileTool } from "./tools/read-file.mjs";
10
14
  import { makeRunShellTool } from "./tools/run-shell.mjs";
11
15
  import { makeRunTestsTool } from "./tools/run-tests.mjs";
16
+ import { makeShellKillTool } from "./tools/shell-kill.mjs";
17
+ import { makeShellOutputTool } from "./tools/shell-output.mjs";
18
+ import { TodoStore } from "./todos.mjs";
19
+ import { makeTodoReadTool, makeTodoWriteTool } from "./tools/todo.mjs";
12
20
  import { makeWriteFileTool } from "./tools/write-file.mjs";
13
21
  import { workspace } from "./workspace.mjs";
14
22
 
15
- export { WorkspaceEditError, WorkspacePolicyError, buildEnv, createLocalBackend, createMockBackend, createOps, isCommandAllowed, makeEditFileTool, makeGlobTool, makeGrepTool, makeReadFileTool, makeRunShellTool, makeRunTestsTool, makeWriteFileTool, resolveInJail, workspace };
23
+ export { TodoStore, WorkspaceEditError, WorkspaceJobError, WorkspacePolicyError, analyzeArgvCommand, buildEnv, checkShellCommand, createLocalBackend, createMockBackend, createOps, isCommandAllowed, makeEditFileTool, makeGlobTool, makeGrepTool, makeListDirTool, makeMultiEditTool, makeReadFileTool, makeRunShellTool, makeRunTestsTool, makeShellKillTool, makeShellOutputTool, makeTodoReadTool, makeTodoWriteTool, makeWriteFileTool, resolveInJail, tokenizeCommand, workspace };
package/esm/ops.d.mts CHANGED
@@ -1,7 +1,24 @@
1
+ import { WorkspacePolicy } from "./contracts/workspace-policy.type.mjs";
1
2
  import { WorkspaceBackend } from "./contracts/workspace-backend.contract.mjs";
2
3
  import { WorkspaceOps } from "./contracts/workspace-ops.contract.mjs";
3
- import { WorkspacePolicy } from "./contracts/workspace-policy.type.mjs";
4
+ import { ShellJobs } from "./shell-jobs.mjs";
5
+
4
6
  //#region ../../packages/ai-workspace/src/ops.d.ts
7
+ /**
8
+ * The internal, single-instance implementation of {@link WorkspaceOps}.
9
+ * Holds the backend + policy and is the one place the jail, command
10
+ * gating, read caps, and the read-before-edit guard are enforced — both
11
+ * the agent-facing tools and the human-facing direct methods funnel
12
+ * through this object, so there is exactly one set of rules.
13
+ */
14
+ /**
15
+ * State an ops layer shares with the other projections of the same
16
+ * workspace (`readonly()`, `scope()`): the background commands it owns.
17
+ */
18
+ interface OpsRuntime {
19
+ /** The workspace's background commands. */
20
+ jobs: ShellJobs;
21
+ }
5
22
  /**
6
23
  * Create the policy-enforced operation layer over a backend.
7
24
  *
@@ -14,12 +31,14 @@ import { WorkspacePolicy } from "./contracts/workspace-policy.type.mjs";
14
31
  *
15
32
  * @param backend - The dumb IO executor (local disk or in-memory mock).
16
33
  * @param policy - The policy that bounds every operation.
34
+ * @param runtime - State shared with other views of the same workspace
35
+ * (its background commands); a fresh one when omitted.
17
36
  *
18
37
  * @example
19
38
  * const ops = createOps(localBackend, { cwd: "/srv/api", shell: { allow: ["npm"] } });
20
39
  * const { content, hash } = await ops.readFile("src/index.ts");
21
40
  */
22
- declare function createOps(backend: WorkspaceBackend, policy: WorkspacePolicy): WorkspaceOps;
41
+ declare function createOps(backend: WorkspaceBackend, policy: WorkspacePolicy, runtime?: OpsRuntime): WorkspaceOps;
23
42
  //#endregion
24
43
  export { createOps };
25
44
  //# sourceMappingURL=ops.d.mts.map
package/esm/ops.d.mts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ops.d.mts","names":[],"sources":["../../../../../../../packages/ai-workspace/src/ops.ts"],"mappings":";;;;;;;;;AAunBA;;;;;;;;;;;;iBAAgB,SAAA,CACd,OAAA,EAAS,gBAAA,EACT,MAAA,EAAQ,eAAA,GACP,YAAA"}
1
+ {"version":3,"file":"ops.d.mts","names":[],"sources":["../../../../../../../packages/ai-workspace/src/ops.ts"],"mappings":";;;;;;;;;AA8LA;;;;AAEiB;AAitBjB;;;AAjtBiB,UAFA,UAAA;EAqtBP;EAntBR,IAAA,EAAM,SAAS;AAAA;;;;;;;;;;;AAqtBF;;;;;;;;;iBAJC,SAAA,CACd,OAAA,EAAS,gBAAA,EACT,MAAA,EAAQ,eAAA,EACR,OAAA,GAAU,UAAA,GACT,YAAA"}