@wrongstack/tools 0.296.4 → 0.298.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/dist/_spawn-stream.d.ts +2 -0
- package/dist/_util.d.ts +29 -1
- package/dist/audit.js +128 -65
- package/dist/bash.js +20 -4
- package/dist/batch-tool-use.js +70 -7
- package/dist/browser/index.js +31 -1
- package/dist/builtin.js +883 -351
- package/dist/codebase-index/index.js +95 -16
- package/dist/codebase-index/project-server-protocol.d.ts +26 -0
- package/dist/codebase-index/project-server.js +76 -16
- package/dist/codebase-index/worker.js +36 -13
- package/dist/codebase-index/writer-search-helpers.d.ts +16 -0
- package/dist/codebase-index/writer.d.ts +13 -1
- package/dist/design.d.ts +1 -1
- package/dist/design.js +4 -3
- package/dist/diff.js +11 -1
- package/dist/e2e.js +9 -6
- package/dist/edit.js +9 -6
- package/dist/exec.js +562 -516
- package/dist/format.js +131 -65
- package/dist/git.js +22 -2
- package/dist/glob.js +11 -5
- package/dist/grep.js +160 -104
- package/dist/index.js +834 -290
- package/dist/install.js +135 -68
- package/dist/json.js +64 -18
- package/dist/kanban-board-inputs.d.ts +21 -4
- package/dist/kanban.js +20 -5
- package/dist/languages/index.js +143 -71
- package/dist/lint.js +128 -65
- package/dist/next-steps.d.ts +23 -0
- package/dist/next-steps.js +4 -0
- package/dist/outdated.js +101 -26
- package/dist/pack.js +883 -351
- package/dist/patch.js +11 -1
- package/dist/plan.js +32 -10
- package/dist/process-registry.d.ts +9 -0
- package/dist/process-registry.js +15 -0
- package/dist/ps-slash.js +15 -0
- package/dist/read.js +104 -23
- package/dist/replace.js +11 -3
- package/dist/scaffold.js +9 -6
- package/dist/session-kanban.js +21 -5
- package/dist/task.js +3 -1
- package/dist/test.js +128 -65
- package/dist/todo.js +3 -1
- package/dist/tool-tier.js +883 -351
- package/dist/tool-use.d.ts +1 -1
- package/dist/tool-use.js +28 -4
- package/dist/tree.d.ts +1 -0
- package/dist/tree.js +44 -8
- package/dist/typecheck.js +128 -65
- package/dist/write.js +9 -6
- package/package.json +5 -4
- package/dist/_concurrency.d.ts.map +0 -1
- package/dist/_danger-detect.d.ts.map +0 -1
- package/dist/_edit-match.d.ts.map +0 -1
- package/dist/_env.d.ts.map +0 -1
- package/dist/_fetch-guard.d.ts.map +0 -1
- package/dist/_output-spool.d.ts.map +0 -1
- package/dist/_redact-command.d.ts.map +0 -1
- package/dist/_regex.d.ts.map +0 -1
- package/dist/_session-shell.d.ts.map +0 -1
- package/dist/_shell-pick.d.ts.map +0 -1
- package/dist/_spawn-stream.d.ts.map +0 -1
- package/dist/_syntax-check.d.ts.map +0 -1
- package/dist/_util.d.ts.map +0 -1
- package/dist/_win32-resolve.d.ts.map +0 -1
- package/dist/audit.d.ts.map +0 -1
- package/dist/audit.js.map +0 -7
- package/dist/auto-proceed-loop-guard.d.ts.map +0 -1
- package/dist/auto-proceed-loop-guard.js.map +0 -7
- package/dist/bash-kill-guard.d.ts.map +0 -1
- package/dist/bash.d.ts.map +0 -1
- package/dist/bash.js.map +0 -7
- package/dist/batch-tool-use.d.ts.map +0 -1
- package/dist/batch-tool-use.js.map +0 -7
- package/dist/browser/artifacts.d.ts.map +0 -1
- package/dist/browser/index.d.ts.map +0 -1
- package/dist/browser/index.js.map +0 -7
- package/dist/browser/manager.d.ts.map +0 -1
- package/dist/browser/network-guard-proxy.d.ts.map +0 -1
- package/dist/browser/security.d.ts.map +0 -1
- package/dist/browser/tools.d.ts.map +0 -1
- package/dist/browser/types.d.ts.map +0 -1
- package/dist/builtin.d.ts.map +0 -1
- package/dist/builtin.js.map +0 -7
- package/dist/circuit-breaker.d.ts.map +0 -1
- package/dist/circuit-breaker.js.map +0 -7
- package/dist/codebase-index/background-indexer.d.ts.map +0 -1
- package/dist/codebase-index/bm25.d.ts.map +0 -1
- package/dist/codebase-index/circuit-breaker.d.ts.map +0 -1
- package/dist/codebase-index/codebase-index-tool.d.ts.map +0 -1
- package/dist/codebase-index/codebase-search-tool.d.ts.map +0 -1
- package/dist/codebase-index/codebase-stats-tool.d.ts.map +0 -1
- package/dist/codebase-index/dead-code-scan.d.ts.map +0 -1
- package/dist/codebase-index/generic-parser.d.ts.map +0 -1
- package/dist/codebase-index/gitignore.d.ts.map +0 -1
- package/dist/codebase-index/go-parser.d.ts.map +0 -1
- package/dist/codebase-index/index-service.d.ts.map +0 -1
- package/dist/codebase-index/index.d.ts.map +0 -1
- package/dist/codebase-index/index.js.map +0 -7
- package/dist/codebase-index/indexer.d.ts.map +0 -1
- package/dist/codebase-index/json-parser.d.ts.map +0 -1
- package/dist/codebase-index/languages.d.ts.map +0 -1
- package/dist/codebase-index/lsp-kind.d.ts.map +0 -1
- package/dist/codebase-index/parser-dispatch.d.ts.map +0 -1
- package/dist/codebase-index/project-server-cache.d.ts.map +0 -1
- package/dist/codebase-index/project-server-client.d.ts.map +0 -1
- package/dist/codebase-index/project-server-endpoint.d.ts.map +0 -1
- package/dist/codebase-index/project-server-protocol.d.ts.map +0 -1
- package/dist/codebase-index/project-server.d.ts.map +0 -1
- package/dist/codebase-index/project-server.js.map +0 -7
- package/dist/codebase-index/py-parser.d.ts.map +0 -1
- package/dist/codebase-index/refs-extractor.d.ts.map +0 -1
- package/dist/codebase-index/rs-parser.d.ts.map +0 -1
- package/dist/codebase-index/schema.d.ts.map +0 -1
- package/dist/codebase-index/spawn-gate.d.ts.map +0 -1
- package/dist/codebase-index/sqlite-runtime.d.ts.map +0 -1
- package/dist/codebase-index/ts-parser.d.ts.map +0 -1
- package/dist/codebase-index/worker-protocol.d.ts.map +0 -1
- package/dist/codebase-index/worker.d.ts.map +0 -1
- package/dist/codebase-index/worker.js.map +0 -7
- package/dist/codebase-index/writer-admin.d.ts.map +0 -1
- package/dist/codebase-index/writer-bulk-insert.d.ts.map +0 -1
- package/dist/codebase-index/writer-graph-helpers.d.ts.map +0 -1
- package/dist/codebase-index/writer-graph-reader.d.ts.map +0 -1
- package/dist/codebase-index/writer-helpers.d.ts.map +0 -1
- package/dist/codebase-index/writer-pragmas.d.ts.map +0 -1
- package/dist/codebase-index/writer-ref-mapper.d.ts.map +0 -1
- package/dist/codebase-index/writer-schema.d.ts.map +0 -1
- package/dist/codebase-index/writer-search-helpers.d.ts.map +0 -1
- package/dist/codebase-index/writer-store-pool.d.ts.map +0 -1
- package/dist/codebase-index/writer.d.ts.map +0 -1
- package/dist/codebase-index/yaml-parser.d.ts.map +0 -1
- package/dist/design.d.ts.map +0 -1
- package/dist/design.js.map +0 -7
- package/dist/diff.d.ts.map +0 -1
- package/dist/diff.js.map +0 -7
- package/dist/document.d.ts.map +0 -1
- package/dist/document.js.map +0 -7
- package/dist/e2e.d.ts.map +0 -1
- package/dist/e2e.js.map +0 -7
- package/dist/edit.d.ts.map +0 -1
- package/dist/edit.js.map +0 -7
- package/dist/exec-kill-guard.d.ts.map +0 -1
- package/dist/exec.d.ts.map +0 -1
- package/dist/exec.js.map +0 -7
- package/dist/fetch.d.ts.map +0 -1
- package/dist/fetch.js.map +0 -7
- package/dist/format.d.ts.map +0 -1
- package/dist/format.js.map +0 -7
- package/dist/git.d.ts.map +0 -1
- package/dist/git.js.map +0 -7
- package/dist/glob.d.ts.map +0 -1
- package/dist/glob.js.map +0 -7
- package/dist/grep.d.ts.map +0 -1
- package/dist/grep.js.map +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -7
- package/dist/install.d.ts.map +0 -1
- package/dist/install.js.map +0 -7
- package/dist/json.d.ts.map +0 -1
- package/dist/json.js.map +0 -7
- package/dist/kanban-board-inputs.d.ts.map +0 -1
- package/dist/kanban-decomposition-actions.d.ts.map +0 -1
- package/dist/kanban-detail-actions.d.ts.map +0 -1
- package/dist/kanban-evidence-bridge.d.ts.map +0 -1
- package/dist/kanban-presence.d.ts.map +0 -1
- package/dist/kanban-split-task-handler.d.ts.map +0 -1
- package/dist/kanban-task-inputs.d.ts.map +0 -1
- package/dist/kanban-tool-results.d.ts.map +0 -1
- package/dist/kanban-tool-schema.d.ts.map +0 -1
- package/dist/kanban-tool-types.d.ts.map +0 -1
- package/dist/kanban.d.ts.map +0 -1
- package/dist/kanban.js.map +0 -7
- package/dist/languages/detect.d.ts.map +0 -1
- package/dist/languages/diagnostics.d.ts.map +0 -1
- package/dist/languages/execute-tool.d.ts.map +0 -1
- package/dist/languages/execute.d.ts.map +0 -1
- package/dist/languages/index.d.ts.map +0 -1
- package/dist/languages/index.js.map +0 -7
- package/dist/languages/legacy-bridge.d.ts.map +0 -1
- package/dist/languages/package-tool.d.ts.map +0 -1
- package/dist/languages/plan.d.ts.map +0 -1
- package/dist/languages/profile-helpers.d.ts.map +0 -1
- package/dist/languages/profiles/additional.d.ts.map +0 -1
- package/dist/languages/profiles/primary.d.ts.map +0 -1
- package/dist/languages/registry.d.ts.map +0 -1
- package/dist/languages/tool.d.ts.map +0 -1
- package/dist/languages/types.d.ts.map +0 -1
- package/dist/lint.d.ts.map +0 -1
- package/dist/lint.js.map +0 -7
- package/dist/logs.d.ts.map +0 -1
- package/dist/logs.js.map +0 -7
- package/dist/memory.d.ts.map +0 -1
- package/dist/memory.js.map +0 -7
- package/dist/mode.d.ts.map +0 -1
- package/dist/mode.js.map +0 -7
- package/dist/next-steps.d.ts.map +0 -1
- package/dist/next-steps.js.map +0 -7
- package/dist/outdated.d.ts.map +0 -1
- package/dist/outdated.js.map +0 -7
- package/dist/pack.d.ts.map +0 -1
- package/dist/pack.js.map +0 -7
- package/dist/patch.d.ts.map +0 -1
- package/dist/patch.js.map +0 -7
- package/dist/plan.d.ts.map +0 -1
- package/dist/plan.js.map +0 -7
- package/dist/process-guardian.d.ts.map +0 -1
- package/dist/process-registry-persistent.d.ts.map +0 -1
- package/dist/process-registry.d.ts.map +0 -1
- package/dist/process-registry.js.map +0 -7
- package/dist/ps-slash.d.ts.map +0 -1
- package/dist/ps-slash.js.map +0 -7
- package/dist/read.d.ts.map +0 -1
- package/dist/read.js.map +0 -7
- package/dist/replace.d.ts.map +0 -1
- package/dist/replace.js.map +0 -7
- package/dist/scaffold.d.ts.map +0 -1
- package/dist/scaffold.js.map +0 -7
- package/dist/search.d.ts.map +0 -1
- package/dist/search.js.map +0 -7
- package/dist/session-kanban.d.ts.map +0 -1
- package/dist/session-kanban.js.map +0 -7
- package/dist/set-working-dir.d.ts.map +0 -1
- package/dist/skill.d.ts.map +0 -1
- package/dist/skill.js.map +0 -7
- package/dist/spawn-background.d.ts.map +0 -1
- package/dist/task.d.ts.map +0 -1
- package/dist/task.js.map +0 -7
- package/dist/test.d.ts.map +0 -1
- package/dist/test.js.map +0 -7
- package/dist/todo.d.ts.map +0 -1
- package/dist/todo.js.map +0 -7
- package/dist/tool-diff.d.ts.map +0 -1
- package/dist/tool-diff.js.map +0 -7
- package/dist/tool-help.d.ts.map +0 -1
- package/dist/tool-help.js.map +0 -7
- package/dist/tool-icon-map.d.ts.map +0 -1
- package/dist/tool-icons.d.ts.map +0 -1
- package/dist/tool-icons.js.map +0 -7
- package/dist/tool-search.d.ts.map +0 -1
- package/dist/tool-search.js.map +0 -7
- package/dist/tool-summary.d.ts.map +0 -1
- package/dist/tool-summary.js.map +0 -7
- package/dist/tool-tier.d.ts.map +0 -1
- package/dist/tool-tier.js.map +0 -7
- package/dist/tool-use.d.ts.map +0 -1
- package/dist/tool-use.js.map +0 -7
- package/dist/tree.d.ts.map +0 -1
- package/dist/tree.js.map +0 -7
- package/dist/typecheck.d.ts.map +0 -1
- package/dist/typecheck.js.map +0 -7
- package/dist/win32.d.ts.map +0 -1
- package/dist/win32.js.map +0 -7
- package/dist/write.d.ts.map +0 -1
- package/dist/write.js.map +0 -7
package/dist/install.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/install.ts
|
|
2
|
-
import { join as
|
|
2
|
+
import { join as join8 } from "node:path";
|
|
3
3
|
import {
|
|
4
4
|
detectEcosystem as detectPackageEcosystem,
|
|
5
5
|
recordPackageAction
|
|
@@ -85,11 +85,12 @@ function createOutputSpool(opts) {
|
|
|
85
85
|
return {
|
|
86
86
|
write(text) {
|
|
87
87
|
if (finalized || !text) return;
|
|
88
|
-
|
|
88
|
+
const textBytes = Buffer.byteLength(text, "utf8");
|
|
89
|
+
totalBytes += textBytes;
|
|
89
90
|
if (!stream && !failed) {
|
|
90
|
-
if (headBytes +
|
|
91
|
+
if (headBytes + textBytes <= threshold) {
|
|
91
92
|
head += text;
|
|
92
|
-
headBytes +=
|
|
93
|
+
headBytes += textBytes;
|
|
93
94
|
return;
|
|
94
95
|
}
|
|
95
96
|
head += text;
|
|
@@ -99,7 +100,7 @@ function createOutputSpool(opts) {
|
|
|
99
100
|
}
|
|
100
101
|
if (stream) {
|
|
101
102
|
if (stream.writableLength > SPOOL_WRITE_HWM_BYTES) {
|
|
102
|
-
droppedBytes +=
|
|
103
|
+
droppedBytes += textBytes;
|
|
103
104
|
return;
|
|
104
105
|
}
|
|
105
106
|
stream.write(text);
|
|
@@ -121,6 +122,52 @@ function createOutputSpool(opts) {
|
|
|
121
122
|
};
|
|
122
123
|
}
|
|
123
124
|
|
|
125
|
+
// src/_win32-resolve.ts
|
|
126
|
+
import * as fs from "node:fs";
|
|
127
|
+
import * as path2 from "node:path";
|
|
128
|
+
function resolveWin32Command(cmd) {
|
|
129
|
+
if (process.platform !== "win32") return cmd;
|
|
130
|
+
if (cmd.includes("/") || cmd.includes("\\") || path2.extname(cmd.replace(/\//g, "\\"))) {
|
|
131
|
+
return cmd;
|
|
132
|
+
}
|
|
133
|
+
const pathext = (process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC").toLowerCase().split(";");
|
|
134
|
+
const pathDirs = (process.env["PATH"] ?? "").split(path2.delimiter);
|
|
135
|
+
for (const dir of pathDirs) {
|
|
136
|
+
const base = path2.join(dir, cmd);
|
|
137
|
+
for (const ext of pathext) {
|
|
138
|
+
const full = `${base}${ext}`;
|
|
139
|
+
try {
|
|
140
|
+
fs.accessSync(full, fs.constants.X_OK);
|
|
141
|
+
return full;
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return cmd;
|
|
147
|
+
}
|
|
148
|
+
var WIN32_SHELL_META = /[&|<>"\r\n\0]/;
|
|
149
|
+
function assertSafeWin32ShellArgs(args) {
|
|
150
|
+
for (const arg of args) {
|
|
151
|
+
if (typeof arg === "string" && WIN32_SHELL_META.test(arg)) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
'win32 cmd shim spawn: argument contains a shell metacharacter (one of & | < > ", or a newline) that could enable command injection through the .cmd/.bat wrapper - refusing to run. Offending argument: ' + JSON.stringify(arg)
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function buildWin32CmdShimInvocation(command, args = []) {
|
|
159
|
+
assertSafeWin32ShellArgs([command, ...args]);
|
|
160
|
+
const line = ["call", quoteWin32CmdArg(command), ...args.map(quoteWin32CmdArg)].join(" ");
|
|
161
|
+
return {
|
|
162
|
+
command: process.env["COMSPEC"] ?? "cmd.exe",
|
|
163
|
+
args: ["/d", "/c", line],
|
|
164
|
+
windowsVerbatimArguments: true
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function quoteWin32CmdArg(arg) {
|
|
168
|
+
return `"${arg}"`;
|
|
169
|
+
}
|
|
170
|
+
|
|
124
171
|
// src/process-registry.ts
|
|
125
172
|
import { spawn } from "node:child_process";
|
|
126
173
|
import * as os from "node:os";
|
|
@@ -452,6 +499,7 @@ var ProcessRegistryImpl = class {
|
|
|
452
499
|
}
|
|
453
500
|
/** Get all tracked processes. */
|
|
454
501
|
list() {
|
|
502
|
+
this._pruneAllStale();
|
|
455
503
|
return Array.from(this.processes.values());
|
|
456
504
|
}
|
|
457
505
|
/** Get processes filtered by name (e.g. 'bash', 'exec'). */
|
|
@@ -482,6 +530,7 @@ var ProcessRegistryImpl = class {
|
|
|
482
530
|
* Combined stats for observability — used by /ps and the TUI status bar.
|
|
483
531
|
*/
|
|
484
532
|
stats() {
|
|
533
|
+
this._pruneAllStale();
|
|
485
534
|
return {
|
|
486
535
|
activeCount: this.activeCount,
|
|
487
536
|
backgroundCount: this.activeBackgroundCount,
|
|
@@ -721,6 +770,19 @@ var ProcessRegistryImpl = class {
|
|
|
721
770
|
this.processes.delete(pid);
|
|
722
771
|
}
|
|
723
772
|
}
|
|
773
|
+
/**
|
|
774
|
+
* Remove every stale entry, not just one PID. `list()`/`stats()` — the
|
|
775
|
+
* surfaces the TUI status bar and `/ps` poll — must prune too: a child
|
|
776
|
+
* whose 'close' event never fires (e.g. Windows grandchildren holding stdio
|
|
777
|
+
* open) would otherwise linger in the registry until someone looks up its
|
|
778
|
+
* exact PID, and PID reuse meanwhile makes `get()`/`kill()` target the
|
|
779
|
+
* wrong process. RAM-leak audit 2026-07-31, MEDIUM.
|
|
780
|
+
*/
|
|
781
|
+
_pruneAllStale() {
|
|
782
|
+
for (const [pid, entry] of this.processes) {
|
|
783
|
+
if (this._isStaleEntry(entry)) this.processes.delete(pid);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
724
786
|
};
|
|
725
787
|
var _registry;
|
|
726
788
|
function getProcessRegistry() {
|
|
@@ -730,60 +792,17 @@ function getProcessRegistry() {
|
|
|
730
792
|
return _registry;
|
|
731
793
|
}
|
|
732
794
|
|
|
733
|
-
// src/_win32-resolve.ts
|
|
734
|
-
import * as fs from "node:fs";
|
|
735
|
-
import * as path2 from "node:path";
|
|
736
|
-
function resolveWin32Command(cmd) {
|
|
737
|
-
if (process.platform !== "win32") return cmd;
|
|
738
|
-
if (cmd.includes("/") || cmd.includes("\\") || path2.extname(cmd.replace(/\//g, "\\"))) {
|
|
739
|
-
return cmd;
|
|
740
|
-
}
|
|
741
|
-
const pathext = (process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC").toLowerCase().split(";");
|
|
742
|
-
const pathDirs = (process.env["PATH"] ?? "").split(path2.delimiter);
|
|
743
|
-
for (const dir of pathDirs) {
|
|
744
|
-
const base = path2.join(dir, cmd);
|
|
745
|
-
for (const ext of pathext) {
|
|
746
|
-
const full = `${base}${ext}`;
|
|
747
|
-
try {
|
|
748
|
-
fs.accessSync(full, fs.constants.X_OK);
|
|
749
|
-
return full;
|
|
750
|
-
} catch {
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
return cmd;
|
|
755
|
-
}
|
|
756
|
-
var WIN32_SHELL_META = /[&|<>"\r\n\0]/;
|
|
757
|
-
function assertSafeWin32ShellArgs(args) {
|
|
758
|
-
for (const arg of args) {
|
|
759
|
-
if (typeof arg === "string" && WIN32_SHELL_META.test(arg)) {
|
|
760
|
-
throw new Error(
|
|
761
|
-
'win32 cmd shim spawn: argument contains a shell metacharacter (one of & | < > ", or a newline) that could enable command injection through the .cmd/.bat wrapper - refusing to run. Offending argument: ' + JSON.stringify(arg)
|
|
762
|
-
);
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
function buildWin32CmdShimInvocation(command, args = []) {
|
|
767
|
-
assertSafeWin32ShellArgs([command, ...args]);
|
|
768
|
-
const line = ["call", quoteWin32CmdArg(command), ...args.map(quoteWin32CmdArg)].join(" ");
|
|
769
|
-
return {
|
|
770
|
-
command: process.env["COMSPEC"] ?? "cmd.exe",
|
|
771
|
-
args: ["/d", "/c", line],
|
|
772
|
-
windowsVerbatimArguments: true
|
|
773
|
-
};
|
|
774
|
-
}
|
|
775
|
-
function quoteWin32CmdArg(arg) {
|
|
776
|
-
return `"${arg}"`;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
795
|
// src/_spawn-stream.ts
|
|
780
796
|
var isWin = process.platform === "win32";
|
|
781
797
|
async function* spawnStream(opts) {
|
|
782
798
|
const max = opts.maxBytes ?? 2e5;
|
|
783
799
|
const flushAt = opts.flushBytes ?? 4 * 1024;
|
|
784
800
|
const maxQueue = opts.maxQueueSize ?? 500;
|
|
801
|
+
const maxQueueBytes = opts.maxQueueBytes ?? 1024 * 1024;
|
|
785
802
|
let stdout = "";
|
|
786
803
|
let stderr = "";
|
|
804
|
+
let stdoutRetainedBytes = 0;
|
|
805
|
+
let stderrRetainedBytes = 0;
|
|
787
806
|
let pending = "";
|
|
788
807
|
let error;
|
|
789
808
|
const spool = createOutputSpool({ tool: opts.cmd, thresholdBytes: max });
|
|
@@ -825,6 +844,7 @@ async function* spawnStream(opts) {
|
|
|
825
844
|
});
|
|
826
845
|
}
|
|
827
846
|
const queue = [];
|
|
847
|
+
let queuedBytes = 0;
|
|
828
848
|
let waiter;
|
|
829
849
|
let paused = false;
|
|
830
850
|
const wake = () => {
|
|
@@ -835,7 +855,7 @@ async function* spawnStream(opts) {
|
|
|
835
855
|
}
|
|
836
856
|
};
|
|
837
857
|
const resume = () => {
|
|
838
|
-
if (paused && queue.length
|
|
858
|
+
if (paused && queue.length <= Math.floor(maxQueue / 2) && queuedBytes <= Math.floor(maxQueueBytes / 2)) {
|
|
839
859
|
paused = false;
|
|
840
860
|
child.stdout?.resume();
|
|
841
861
|
child.stderr?.resume();
|
|
@@ -845,11 +865,16 @@ async function* spawnStream(opts) {
|
|
|
845
865
|
const s = c.toString();
|
|
846
866
|
stdoutBytes += c.byteLength;
|
|
847
867
|
emitProcessOutput({ pid, stream: "stdout", chunk: c });
|
|
848
|
-
if (
|
|
868
|
+
if (stdoutRetainedBytes < max) {
|
|
869
|
+
const retained = utf8Prefix(s, max - stdoutRetainedBytes);
|
|
870
|
+
stdout += retained;
|
|
871
|
+
stdoutRetainedBytes += Buffer.byteLength(retained, "utf8");
|
|
872
|
+
}
|
|
849
873
|
spool.write(s);
|
|
850
|
-
queue.push({ kind: "out", data: s });
|
|
874
|
+
queue.push({ kind: "out", data: s, bytes: c.byteLength });
|
|
875
|
+
queuedBytes += c.byteLength;
|
|
851
876
|
wake();
|
|
852
|
-
if (!paused && queue.length >= maxQueue) {
|
|
877
|
+
if (!paused && (queue.length >= maxQueue || queuedBytes >= maxQueueBytes)) {
|
|
853
878
|
paused = true;
|
|
854
879
|
child.stdout?.pause();
|
|
855
880
|
child.stderr?.pause();
|
|
@@ -859,11 +884,16 @@ async function* spawnStream(opts) {
|
|
|
859
884
|
const s = c.toString();
|
|
860
885
|
stderrBytes += c.byteLength;
|
|
861
886
|
emitProcessOutput({ pid, stream: "stderr", chunk: c });
|
|
862
|
-
if (
|
|
887
|
+
if (stderrRetainedBytes < max) {
|
|
888
|
+
const retained = utf8Prefix(s, max - stderrRetainedBytes);
|
|
889
|
+
stderr += retained;
|
|
890
|
+
stderrRetainedBytes += Buffer.byteLength(retained, "utf8");
|
|
891
|
+
}
|
|
863
892
|
spool.write(s);
|
|
864
|
-
queue.push({ kind: "err", data: s });
|
|
893
|
+
queue.push({ kind: "err", data: s, bytes: c.byteLength });
|
|
894
|
+
queuedBytes += c.byteLength;
|
|
865
895
|
wake();
|
|
866
|
-
if (!paused && queue.length >= maxQueue) {
|
|
896
|
+
if (!paused && (queue.length >= maxQueue || queuedBytes >= maxQueueBytes)) {
|
|
867
897
|
paused = true;
|
|
868
898
|
child.stdout?.pause();
|
|
869
899
|
child.stderr?.pause();
|
|
@@ -873,7 +903,9 @@ async function* spawnStream(opts) {
|
|
|
873
903
|
child.stderr?.on("data", onErr);
|
|
874
904
|
child.on("error", (e) => {
|
|
875
905
|
error = e.message;
|
|
876
|
-
|
|
906
|
+
const bytes = Buffer.byteLength(e.message, "utf8");
|
|
907
|
+
queue.push({ kind: "error", data: e.message, bytes });
|
|
908
|
+
queuedBytes += bytes;
|
|
877
909
|
wake();
|
|
878
910
|
});
|
|
879
911
|
const completeTelemetry = (code, signal, timedOut = false) => {
|
|
@@ -894,7 +926,13 @@ async function* spawnStream(opts) {
|
|
|
894
926
|
if (typeof pid === "number") registry.unregister(pid);
|
|
895
927
|
const exitCode2 = code ?? (signal ? 1 : 0);
|
|
896
928
|
completeTelemetry(exitCode2, signal ?? void 0);
|
|
897
|
-
queue.push({
|
|
929
|
+
queue.push({
|
|
930
|
+
kind: "close",
|
|
931
|
+
data: "",
|
|
932
|
+
bytes: 0,
|
|
933
|
+
code: exitCode2,
|
|
934
|
+
...signal ? { signal } : {}
|
|
935
|
+
});
|
|
898
936
|
wake();
|
|
899
937
|
});
|
|
900
938
|
const onAbort = () => {
|
|
@@ -906,7 +944,7 @@ async function* spawnStream(opts) {
|
|
|
906
944
|
} catch {
|
|
907
945
|
}
|
|
908
946
|
}
|
|
909
|
-
queue.push({ kind: "close", data: "", code: 124 });
|
|
947
|
+
queue.push({ kind: "close", data: "", bytes: 0, code: 124 });
|
|
910
948
|
completeTelemetry(124, "SIGKILL", true);
|
|
911
949
|
wake();
|
|
912
950
|
};
|
|
@@ -924,6 +962,7 @@ async function* spawnStream(opts) {
|
|
|
924
962
|
});
|
|
925
963
|
}
|
|
926
964
|
const chunk = queue.shift();
|
|
965
|
+
queuedBytes -= chunk.bytes;
|
|
927
966
|
resume();
|
|
928
967
|
if (chunk.kind === "close") {
|
|
929
968
|
if (!spawnFailed) exitCode = chunk.code ?? 0;
|
|
@@ -950,7 +989,7 @@ async function* spawnStream(opts) {
|
|
|
950
989
|
stdout: spooled ? stdout + spoolNote(spooled) : stdout,
|
|
951
990
|
stderr,
|
|
952
991
|
exitCode,
|
|
953
|
-
truncated:
|
|
992
|
+
truncated: stdoutBytes > stdoutRetainedBytes || stderrBytes > stderrRetainedBytes,
|
|
954
993
|
error,
|
|
955
994
|
spoolPath: spooled?.path,
|
|
956
995
|
spoolBytes: spooled?.bytes
|
|
@@ -974,6 +1013,20 @@ async function* spawnStream(opts) {
|
|
|
974
1013
|
}
|
|
975
1014
|
}
|
|
976
1015
|
}
|
|
1016
|
+
function utf8Prefix(text, maxBytes) {
|
|
1017
|
+
if (maxBytes <= 0) return "";
|
|
1018
|
+
if (Buffer.byteLength(text, "utf8") <= maxBytes) return text;
|
|
1019
|
+
let low = 0;
|
|
1020
|
+
let high = text.length;
|
|
1021
|
+
while (low < high) {
|
|
1022
|
+
const mid = Math.ceil((low + high) / 2);
|
|
1023
|
+
if (Buffer.byteLength(text.slice(0, mid), "utf8") <= maxBytes) low = mid;
|
|
1024
|
+
else high = mid - 1;
|
|
1025
|
+
}
|
|
1026
|
+
let end = low;
|
|
1027
|
+
if (end > 0 && /[\uD800-\uDBFF]/.test(text[end - 1])) end--;
|
|
1028
|
+
return text.slice(0, end);
|
|
1029
|
+
}
|
|
977
1030
|
|
|
978
1031
|
// src/_util.ts
|
|
979
1032
|
import * as path3 from "node:path";
|
|
@@ -2555,8 +2608,8 @@ async function scanDirectory(directory, depth, profiles, limits, state, extraIgn
|
|
|
2555
2608
|
collectFileEvidence(directory, fullPath, entry.name, profiles, state);
|
|
2556
2609
|
}
|
|
2557
2610
|
}
|
|
2558
|
-
function collectFileEvidence(directory, fullPath,
|
|
2559
|
-
const lower =
|
|
2611
|
+
function collectFileEvidence(directory, fullPath, basename5, profiles, state) {
|
|
2612
|
+
const lower = basename5.toLowerCase();
|
|
2560
2613
|
const extension = path4.extname(lower);
|
|
2561
2614
|
for (const profile of profiles) {
|
|
2562
2615
|
const detector = profile.detectors.find(
|
|
@@ -2567,7 +2620,7 @@ function collectFileEvidence(directory, fullPath, basename4, profiles, state) {
|
|
|
2567
2620
|
candidate.evidence.push({
|
|
2568
2621
|
kind: detector.kind,
|
|
2569
2622
|
path: fullPath,
|
|
2570
|
-
value:
|
|
2623
|
+
value: basename5,
|
|
2571
2624
|
weight: detector.weight
|
|
2572
2625
|
});
|
|
2573
2626
|
if (detector.kind === "manifest" || detector.kind === "config") {
|
|
@@ -3186,10 +3239,11 @@ var MAX_ARGUMENTS = 128;
|
|
|
3186
3239
|
var MAX_ARGUMENT_LENGTH = 4096;
|
|
3187
3240
|
var PACKAGE_NAME_RE = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+(?:@[a-z0-9*+._~^<>=|-]+)?$/i;
|
|
3188
3241
|
var COMPOSER_PACKAGE_RE = /^[a-z0-9_.-]+\/[a-z0-9_.-]+(?::[a-z0-9*+._~^<>=|-]+)?$/i;
|
|
3189
|
-
var GO_MODULE_RE = /^(?:[a-z0-9
|
|
3242
|
+
var GO_MODULE_RE = /^(?:[a-z0-9-]+\.)+[a-z0-9-]+\/[a-z0-9._~+/@-]+$/i;
|
|
3190
3243
|
var PYTHON_PACKAGE_RE = /^[a-z0-9._-]+(?:\[[a-z0-9._,-]+\])?==[a-z0-9*+._!~-]+$/i;
|
|
3191
3244
|
async function planLanguageOperation(options) {
|
|
3192
3245
|
validateOperationOptions(options.operation, options.operationOptions?.packages);
|
|
3246
|
+
validateFilterOption(options.operationOptions?.filter);
|
|
3193
3247
|
const detection = await detectLanguageWorkspaces(options);
|
|
3194
3248
|
const candidates = detection.workspaces.filter(
|
|
3195
3249
|
(workspace) => workspace.capabilities.includes(options.operation)
|
|
@@ -3362,6 +3416,15 @@ function workspaceDepth(workspaceRoot, projectRoot) {
|
|
|
3362
3416
|
const relative5 = path6.relative(path6.resolve(projectRoot), path6.resolve(workspaceRoot));
|
|
3363
3417
|
return relative5 === "" ? 0 : relative5.split(path6.sep).length;
|
|
3364
3418
|
}
|
|
3419
|
+
function validateFilterOption(filter) {
|
|
3420
|
+
if (filter === void 0) return;
|
|
3421
|
+
if (filter.startsWith("-") || filter.includes(" --")) {
|
|
3422
|
+
throw new Error(`Invalid test filter (parsed as a runner option): "${filter}"`);
|
|
3423
|
+
}
|
|
3424
|
+
if (filter.length > MAX_ARGUMENT_LENGTH || /[\r\n\0]/.test(filter)) {
|
|
3425
|
+
throw new Error("Invalid test filter (oversized or contains a control character)");
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3365
3428
|
function validateOperationOptions(operation, packages) {
|
|
3366
3429
|
if (!operation.startsWith("package-") || !packages) return;
|
|
3367
3430
|
for (const value of packages) {
|
|
@@ -3744,6 +3807,10 @@ var installTool = {
|
|
|
3744
3807
|
description: "Install, update or manage packages using the detected package manager (pnpm/npm/yarn). Strongly preferred over raw shell commands for dependency management because it is structured and safer.",
|
|
3745
3808
|
usageHint: "ALWAYS USE THIS INSTEAD OF BASH FOR PACKAGE WORK:\n\n- Empty `packages` \u2192 normal `install` (respects lockfile).\n- Provide names \u2192 adds/updates specific packages.\n- `dry_run: true` for safe preview.\n- Set `save` appropriately.\nThis tool has proper capability declaration and is heavily recommended in the security posture of the project.",
|
|
3746
3809
|
permission: "confirm",
|
|
3810
|
+
// WS-046: gives permission decisions something to key on.
|
|
3811
|
+
// What gets installed is the subject — installing a package runs its
|
|
3812
|
+
// lifecycle scripts, so `express` and `evil-pkg` must not be interchangeable.
|
|
3813
|
+
subjectKey: "packages",
|
|
3747
3814
|
mutating: true,
|
|
3748
3815
|
riskTier: "standard",
|
|
3749
3816
|
icon: "package",
|
|
@@ -3913,10 +3980,10 @@ function resolveManifestPath(cwd, pkgManager) {
|
|
|
3913
3980
|
case "pnpm":
|
|
3914
3981
|
case "yarn":
|
|
3915
3982
|
case "npm":
|
|
3916
|
-
return
|
|
3983
|
+
return join8(cwd, "package.json");
|
|
3917
3984
|
/* v8 ignore next 2 -- pkgManager is always pnpm/yarn/npm; the default is defensive. */
|
|
3918
3985
|
default:
|
|
3919
|
-
return
|
|
3986
|
+
return join8(cwd, "package.json");
|
|
3920
3987
|
}
|
|
3921
3988
|
}
|
|
3922
3989
|
export {
|
package/dist/json.js
CHANGED
|
@@ -27,12 +27,13 @@ function ensureInsideRoot(absPath, ctx) {
|
|
|
27
27
|
function safeResolve(input, ctx) {
|
|
28
28
|
return ensureInsideRoot(resolvePath(input, ctx), ctx);
|
|
29
29
|
}
|
|
30
|
-
async function
|
|
31
|
-
if (ctx.allowOutsideProjectRoot) return;
|
|
30
|
+
async function resolveRealInsideRoot(absPath, ctx) {
|
|
31
|
+
if (ctx.allowOutsideProjectRoot) return absPath;
|
|
32
32
|
const realRoots = await Promise.all(
|
|
33
33
|
allowedRoots(ctx).map((r) => fsp.realpath(r).catch(() => path.resolve(r)))
|
|
34
34
|
);
|
|
35
35
|
let probe = absPath;
|
|
36
|
+
const pendingTail = [];
|
|
36
37
|
for (; ; ) {
|
|
37
38
|
let real;
|
|
38
39
|
try {
|
|
@@ -40,13 +41,16 @@ async function assertRealInsideRoot(absPath, ctx) {
|
|
|
40
41
|
} catch (err) {
|
|
41
42
|
if (err.code === "ENOENT") {
|
|
42
43
|
const parent = path.dirname(probe);
|
|
43
|
-
if (parent === probe) return;
|
|
44
|
+
if (parent === probe) return absPath;
|
|
45
|
+
pendingTail.unshift(path.basename(probe));
|
|
44
46
|
probe = parent;
|
|
45
47
|
continue;
|
|
46
48
|
}
|
|
47
49
|
throw err;
|
|
48
50
|
}
|
|
49
|
-
if (isInsideAny(real, realRoots))
|
|
51
|
+
if (isInsideAny(real, realRoots)) {
|
|
52
|
+
return pendingTail.length > 0 ? path.join(real, ...pendingTail) : real;
|
|
53
|
+
}
|
|
50
54
|
throw new Error(
|
|
51
55
|
`Path "${absPath}" resolves through a symlink outside project root "${realRoots[0]}"`
|
|
52
56
|
);
|
|
@@ -54,11 +58,28 @@ async function assertRealInsideRoot(absPath, ctx) {
|
|
|
54
58
|
}
|
|
55
59
|
async function safeResolveReal(input, ctx) {
|
|
56
60
|
const abs = safeResolve(input, ctx);
|
|
57
|
-
await
|
|
58
|
-
return abs;
|
|
61
|
+
return await resolveRealInsideRoot(abs, ctx);
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
// src/json.ts
|
|
65
|
+
var MAX_JSON_FILE_BYTES = 16 * 1024 * 1024;
|
|
66
|
+
var MAX_JSON_FILE_BYTES_HUMAN = "16 MiB";
|
|
67
|
+
var JsonFileTooLargeError = class extends Error {
|
|
68
|
+
constructor(filePath, size) {
|
|
69
|
+
super(
|
|
70
|
+
`json: "${filePath}" is ${size} bytes \u2014 exceeds the ${MAX_JSON_FILE_BYTES_HUMAN} file-size limit. Extract the relevant portion into a smaller file first, or use a shell tool to query it directly.`
|
|
71
|
+
);
|
|
72
|
+
this.name = "JsonFileTooLargeError";
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
async function readJsonFileBounded(filePath, ctx) {
|
|
76
|
+
const resolved = await safeResolveReal(filePath, ctx);
|
|
77
|
+
const stat2 = await fs.stat(resolved);
|
|
78
|
+
if (stat2.size > MAX_JSON_FILE_BYTES) {
|
|
79
|
+
throw new JsonFileTooLargeError(filePath, stat2.size);
|
|
80
|
+
}
|
|
81
|
+
return fs.readFile(resolved, "utf8");
|
|
82
|
+
}
|
|
62
83
|
var jsonTool = {
|
|
63
84
|
name: "json",
|
|
64
85
|
category: "Data",
|
|
@@ -66,6 +87,7 @@ var jsonTool = {
|
|
|
66
87
|
usageHint: 'VERY USEFUL FOR DATA INSPECTION:\n\n- `action: "parse"` (default): read/pretty-print/convert JSON, JSON5, or YAML from `file` or `data`.\n- `action: "query"`: JMESPath-like query (`a.b[0].c`, `items[*].name`, filters, functions).\n- `action: "validate"`: validate data against a JSON Schema (`schema` param).\n- `action: "transform"`: chain multiple JMESPath transforms (`transforms` param).\n- `action: "merge"`: deep merge `base` and `patch` objects (`conflictResolution` param).\nPrefer this over raw `read` + manual parsing when dealing with configuration or data files.',
|
|
67
88
|
permission: "auto",
|
|
68
89
|
mutating: false,
|
|
90
|
+
maxOutputBytes: 262144,
|
|
69
91
|
timeoutMs: 5e3,
|
|
70
92
|
capabilities: ["fs.read"],
|
|
71
93
|
icon: "json",
|
|
@@ -132,9 +154,15 @@ async function executeParse(input, ctx) {
|
|
|
132
154
|
let raw;
|
|
133
155
|
if (input.file) {
|
|
134
156
|
try {
|
|
135
|
-
raw = await
|
|
136
|
-
} catch {
|
|
137
|
-
return {
|
|
157
|
+
raw = await readJsonFileBounded(input.file, ctx);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
return {
|
|
160
|
+
data: null,
|
|
161
|
+
formatted: "",
|
|
162
|
+
type: "unknown",
|
|
163
|
+
action: "parse",
|
|
164
|
+
error: error instanceof JsonFileTooLargeError ? error.message : "Could not read file"
|
|
165
|
+
};
|
|
138
166
|
}
|
|
139
167
|
} else if (input.data) {
|
|
140
168
|
raw = input.data;
|
|
@@ -190,10 +218,16 @@ async function executeQuery(input, ctx) {
|
|
|
190
218
|
let parsed;
|
|
191
219
|
if (input.file) {
|
|
192
220
|
try {
|
|
193
|
-
const raw = await
|
|
221
|
+
const raw = await readJsonFileBounded(input.file, ctx);
|
|
194
222
|
parsed = JSON.parse(raw);
|
|
195
|
-
} catch {
|
|
196
|
-
return {
|
|
223
|
+
} catch (error) {
|
|
224
|
+
return {
|
|
225
|
+
data: null,
|
|
226
|
+
formatted: "",
|
|
227
|
+
type: "unknown",
|
|
228
|
+
action: "query",
|
|
229
|
+
error: error instanceof JsonFileTooLargeError ? error.message : "Could not read/parse file"
|
|
230
|
+
};
|
|
197
231
|
}
|
|
198
232
|
} else if (input.data) {
|
|
199
233
|
try {
|
|
@@ -232,10 +266,16 @@ async function executeValidate(input, ctx) {
|
|
|
232
266
|
let parsed;
|
|
233
267
|
if (input.file) {
|
|
234
268
|
try {
|
|
235
|
-
const raw = await
|
|
269
|
+
const raw = await readJsonFileBounded(input.file, ctx);
|
|
236
270
|
parsed = JSON.parse(raw);
|
|
237
|
-
} catch {
|
|
238
|
-
return {
|
|
271
|
+
} catch (error) {
|
|
272
|
+
return {
|
|
273
|
+
data: null,
|
|
274
|
+
formatted: "",
|
|
275
|
+
type: "unknown",
|
|
276
|
+
action: "validate",
|
|
277
|
+
error: error instanceof JsonFileTooLargeError ? error.message : "Could not read/parse file"
|
|
278
|
+
};
|
|
239
279
|
}
|
|
240
280
|
} else if (input.data) {
|
|
241
281
|
try {
|
|
@@ -274,10 +314,16 @@ async function executeTransform(input, ctx) {
|
|
|
274
314
|
let parsed;
|
|
275
315
|
if (input.file) {
|
|
276
316
|
try {
|
|
277
|
-
const raw = await
|
|
317
|
+
const raw = await readJsonFileBounded(input.file, ctx);
|
|
278
318
|
parsed = JSON.parse(raw);
|
|
279
|
-
} catch {
|
|
280
|
-
return {
|
|
319
|
+
} catch (error) {
|
|
320
|
+
return {
|
|
321
|
+
data: null,
|
|
322
|
+
formatted: "",
|
|
323
|
+
type: "unknown",
|
|
324
|
+
action: "transform",
|
|
325
|
+
error: error instanceof JsonFileTooLargeError ? error.message : "Could not read/parse file"
|
|
326
|
+
};
|
|
281
327
|
}
|
|
282
328
|
} else if (input.data) {
|
|
283
329
|
try {
|
|
@@ -8,8 +8,17 @@ export declare function boardCreateInput(input: KanbanToolInput, title: string):
|
|
|
8
8
|
mode: "assess" | "enforce" | "off";
|
|
9
9
|
decomposition: "auto" | "propose";
|
|
10
10
|
};
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
} | {
|
|
12
|
+
completionGate: {
|
|
13
|
+
enforcement: 'strict' | 'soft';
|
|
14
|
+
};
|
|
15
|
+
title: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
tags?: string[];
|
|
18
|
+
generatedBy?: string;
|
|
19
|
+
atomicity?: {
|
|
20
|
+
mode: "assess" | "enforce" | "off";
|
|
21
|
+
decomposition: "auto" | "propose";
|
|
13
22
|
};
|
|
14
23
|
};
|
|
15
24
|
export declare function boardUpdatePatch(input: KanbanToolInput): {
|
|
@@ -20,8 +29,16 @@ export declare function boardUpdatePatch(input: KanbanToolInput): {
|
|
|
20
29
|
mode: "assess" | "enforce" | "off";
|
|
21
30
|
decomposition: "auto" | "propose";
|
|
22
31
|
};
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
} | {
|
|
33
|
+
completionGate: {
|
|
34
|
+
enforcement: 'strict' | 'soft';
|
|
35
|
+
};
|
|
36
|
+
title?: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
tags?: string[];
|
|
39
|
+
atomicity?: {
|
|
40
|
+
mode: "assess" | "enforce" | "off";
|
|
41
|
+
decomposition: "auto" | "propose";
|
|
25
42
|
};
|
|
26
43
|
};
|
|
27
44
|
export declare function duplicateBoardOptions(input: KanbanToolInput): {
|
package/dist/kanban.js
CHANGED
|
@@ -345,6 +345,10 @@ async function handleKanbanDetailAction(projectRoot, input) {
|
|
|
345
345
|
}
|
|
346
346
|
|
|
347
347
|
// src/kanban-board-inputs.ts
|
|
348
|
+
function agentSettableGate(enforcement) {
|
|
349
|
+
if (enforcement === void 0 || enforcement === "off") return {};
|
|
350
|
+
return { completionGate: { enforcement } };
|
|
351
|
+
}
|
|
348
352
|
function boardCreateInput(input, title) {
|
|
349
353
|
return {
|
|
350
354
|
title,
|
|
@@ -357,7 +361,7 @@ function boardCreateInput(input, title) {
|
|
|
357
361
|
decomposition: input.atomicityDecomposition ?? "propose"
|
|
358
362
|
}
|
|
359
363
|
} : {},
|
|
360
|
-
...input.gateEnforcement
|
|
364
|
+
...agentSettableGate(input.gateEnforcement)
|
|
361
365
|
};
|
|
362
366
|
}
|
|
363
367
|
function boardUpdatePatch(input) {
|
|
@@ -371,7 +375,7 @@ function boardUpdatePatch(input) {
|
|
|
371
375
|
decomposition: input.atomicityDecomposition ?? "propose"
|
|
372
376
|
}
|
|
373
377
|
} : {},
|
|
374
|
-
...input.gateEnforcement
|
|
378
|
+
...agentSettableGate(input.gateEnforcement)
|
|
375
379
|
};
|
|
376
380
|
}
|
|
377
381
|
function duplicateBoardOptions(input) {
|
|
@@ -386,6 +390,7 @@ function duplicateBoardOptions(input) {
|
|
|
386
390
|
|
|
387
391
|
// src/kanban-task-inputs.ts
|
|
388
392
|
import { randomUUID } from "node:crypto";
|
|
393
|
+
import { clampSubagentCapabilities } from "@wrongstack/core/security";
|
|
389
394
|
function taskInput(input) {
|
|
390
395
|
const assignment = hasAssignmentInput(input) ? assignmentForTaskCreate(input) : void 0;
|
|
391
396
|
return {
|
|
@@ -483,6 +488,10 @@ function taskPatch(input) {
|
|
|
483
488
|
...input.actualHours !== void 0 ? { actualHours: input.actualHours } : {}
|
|
484
489
|
};
|
|
485
490
|
}
|
|
491
|
+
function clampRequestedCapabilities(requested) {
|
|
492
|
+
if (requested === void 0) return void 0;
|
|
493
|
+
return clampSubagentCapabilities(requested).granted;
|
|
494
|
+
}
|
|
486
495
|
function assignmentInput(input) {
|
|
487
496
|
return {
|
|
488
497
|
agentId: input.agentId,
|
|
@@ -493,7 +502,7 @@ function assignmentInput(input) {
|
|
|
493
502
|
fallbackProfile: input.fallbackProfile,
|
|
494
503
|
fallbackModels: input.fallbackModels,
|
|
495
504
|
tools: input.tools,
|
|
496
|
-
allowedCapabilities: input.allowedCapabilities,
|
|
505
|
+
allowedCapabilities: clampRequestedCapabilities(input.allowedCapabilities),
|
|
497
506
|
assignee: input.assignee,
|
|
498
507
|
leaseId: input.leaseId,
|
|
499
508
|
claimedAt: input.claimedAt,
|
|
@@ -520,7 +529,7 @@ function assignmentForTaskCreate(input) {
|
|
|
520
529
|
...input.fallbackProfile !== void 0 ? { fallbackProfile: input.fallbackProfile } : {},
|
|
521
530
|
...input.fallbackModels !== void 0 ? { fallbackModels: input.fallbackModels } : {},
|
|
522
531
|
...input.tools !== void 0 ? { tools: input.tools } : {},
|
|
523
|
-
...input.allowedCapabilities !== void 0 ? { allowedCapabilities: input.allowedCapabilities } : {},
|
|
532
|
+
...input.allowedCapabilities !== void 0 ? { allowedCapabilities: clampRequestedCapabilities(input.allowedCapabilities) } : {},
|
|
524
533
|
...input.leaseId !== void 0 ? { leaseId: input.leaseId } : {},
|
|
525
534
|
...input.claimedAt !== void 0 ? { claimedAt: input.claimedAt } : {},
|
|
526
535
|
...input.heartbeatAt !== void 0 ? { heartbeatAt: input.heartbeatAt } : {},
|
|
@@ -731,7 +740,13 @@ var KANBAN_INPUT_SCHEMA = {
|
|
|
731
740
|
moveTasksToColumnId: { type: "string" },
|
|
732
741
|
atomicityMode: { type: "string", enum: ["off", "assess", "enforce"] },
|
|
733
742
|
atomicityDecomposition: { type: "string", enum: ["auto", "propose"] },
|
|
734
|
-
gateEnforcement: {
|
|
743
|
+
gateEnforcement: {
|
|
744
|
+
type: "string",
|
|
745
|
+
// WS-023: `'off'` is deliberately absent. The agent whose work this gate
|
|
746
|
+
// checks must not be able to switch it off; it may only tighten. Turning
|
|
747
|
+
// a gate off stays a human decision, made through board config.
|
|
748
|
+
enum: ["strict", "soft"]
|
|
749
|
+
},
|
|
735
750
|
subtasks: {
|
|
736
751
|
type: "array",
|
|
737
752
|
minItems: 2,
|