@wrongstack/acp 0.286.0 → 0.289.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/README.md +27 -23
- package/dist/agent/wrongstack-acp-agent.d.ts +7 -0
- package/dist/agent/wrongstack-acp-agent.d.ts.map +1 -1
- package/dist/agent.js +62 -18
- package/dist/agent.js.map +2 -2
- package/dist/client/acp-session.d.ts +17 -0
- package/dist/client/acp-session.d.ts.map +1 -1
- package/dist/client/file-server.d.ts +27 -5
- package/dist/client/file-server.d.ts.map +1 -1
- package/dist/client/terminal-server.d.ts +15 -0
- package/dist/client/terminal-server.d.ts.map +1 -1
- package/dist/client.js +201 -32
- package/dist/client.js.map +3 -3
- package/dist/index.js +291 -51
- package/dist/index.js.map +3 -3
- package/dist/integration/acp-subagent-runner.d.ts.map +1 -1
- package/dist/registry/acp-registry-fetch.d.ts.map +1 -1
- package/dist/registry/agents.catalog.d.ts.map +1 -1
- package/dist/registry/ensemble-registry.d.ts +1 -1
- package/dist/registry/ensemble-registry.d.ts.map +1 -1
- package/dist/sdk.js +290 -51
- package/dist/sdk.js.map +3 -3
- package/dist/wrongstack-acp-agent.js +62 -18
- package/dist/wrongstack-acp-agent.js.map +2 -2
- package/package.json +2 -2
|
@@ -326,6 +326,23 @@ export declare class ACPSession {
|
|
|
326
326
|
private scratch;
|
|
327
327
|
private resetScratch;
|
|
328
328
|
private handlePermissionRequest;
|
|
329
|
+
/**
|
|
330
|
+
* Enforce authorization at privileged callback sinks (fs/write,
|
|
331
|
+
* terminal/create). Unlike `handlePermissionRequest` which responds to
|
|
332
|
+
* agent-initiated `session/request_permission` messages, this method is
|
|
333
|
+
* called by the handler BEFORE dispatching to FileServer/TerminalServer,
|
|
334
|
+
* closing the gap where the agent simply skips the voluntary permission
|
|
335
|
+
* request and sends the privileged callback directly.
|
|
336
|
+
*
|
|
337
|
+
* Uses the session's permission policy. The default policy
|
|
338
|
+
* (`defaultPermissionPolicy`) auto-approves everything — this is correct
|
|
339
|
+
* for trusted local agents (CLI `acp spawn`, Director fan-out). For
|
|
340
|
+
* untrusted/remote agents, the host should inject
|
|
341
|
+
* `readOnlyPermissionPolicy` or an interactive policy.
|
|
342
|
+
*
|
|
343
|
+
* Returns true if the callback is authorized, false if denied.
|
|
344
|
+
*/
|
|
345
|
+
private authorizeCallback;
|
|
329
346
|
private handleFsRequest;
|
|
330
347
|
private handleTerminalRequest;
|
|
331
348
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acp-session.d.ts","sourceRoot":"","sources":["../../src/client/acp-session.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"acp-session.d.ts","sourceRoot":"","sources":["../../src/client/acp-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,17 @@ export interface FileServerOptions {
|
|
|
3
3
|
projectRoot: string;
|
|
4
4
|
/** Per-call timeout, default 30s. */
|
|
5
5
|
timeoutMs?: number;
|
|
6
|
+
/**
|
|
7
|
+
* Hard cap on the number of bytes that may be read in a single
|
|
8
|
+
* `readTextFile` call. Protects against a malicious agent requesting a
|
|
9
|
+
* gigantic file to exhaust host memory. Default 5 MiB.
|
|
10
|
+
*/
|
|
11
|
+
maxReadBytes?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Hard cap on the number of bytes that may be written in a single
|
|
14
|
+
* `writeTextFile` call. Default 5 MiB.
|
|
15
|
+
*/
|
|
16
|
+
maxWriteBytes?: number;
|
|
6
17
|
}
|
|
7
18
|
export interface ReadFileParams {
|
|
8
19
|
sessionId: string;
|
|
@@ -13,7 +24,7 @@ export interface WriteFileParams {
|
|
|
13
24
|
path: string;
|
|
14
25
|
content: string;
|
|
15
26
|
}
|
|
16
|
-
export type FsErrorCode = 'ENOENT' | 'EACCES' | 'OUTSIDE_ROOT' | 'TIMEOUT' | 'INVALID_PATH';
|
|
27
|
+
export type FsErrorCode = 'ENOENT' | 'EACCES' | 'OUTSIDE_ROOT' | 'TIMEOUT' | 'INVALID_PATH' | 'TOO_LARGE';
|
|
17
28
|
/**
|
|
18
29
|
* Thrown for protocol-level rejections (path outside root, etc.).
|
|
19
30
|
* The session converts these into JSON-RPC error responses.
|
|
@@ -25,7 +36,10 @@ export declare class FsError extends Error {
|
|
|
25
36
|
}
|
|
26
37
|
export declare class FileServer {
|
|
27
38
|
private readonly root;
|
|
39
|
+
private readonly realRoot;
|
|
28
40
|
private readonly timeoutMs;
|
|
41
|
+
private readonly maxReadBytes;
|
|
42
|
+
private readonly maxWriteBytes;
|
|
29
43
|
constructor(opts: FileServerOptions);
|
|
30
44
|
/** Read a text file. Returns the content as a string. */
|
|
31
45
|
readTextFile(params: ReadFileParams): Promise<{
|
|
@@ -34,11 +48,19 @@ export declare class FileServer {
|
|
|
34
48
|
/** Write a text file. Atomic via write-then-rename. */
|
|
35
49
|
writeTextFile(params: WriteFileParams): Promise<void>;
|
|
36
50
|
/**
|
|
37
|
-
* Resolve a path
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
51
|
+
* Resolve a path and verify it is inside the project root by realpath.
|
|
52
|
+
* Rejects with `FsError` if the textual path, the resolved path, or the
|
|
53
|
+
* real (symlink-resolved) path escapes the project root.
|
|
54
|
+
*
|
|
55
|
+
* For files that don't exist yet (e.g. a write to a new file), the
|
|
56
|
+
* nearest existing ancestor directory is realpath-checked instead.
|
|
41
57
|
*/
|
|
42
58
|
private resolveInside;
|
|
59
|
+
/**
|
|
60
|
+
* Resolve `resolvedPath` through `fs.realpath` and verify the result is
|
|
61
|
+
* inside `realRoot`. For non-existent paths (new files), walk up to the
|
|
62
|
+
* nearest existing ancestor and check that instead.
|
|
63
|
+
*/
|
|
64
|
+
private assertRealInside;
|
|
43
65
|
}
|
|
44
66
|
//# sourceMappingURL=file-server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-server.d.ts","sourceRoot":"","sources":["../../src/client/file-server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"file-server.d.ts","sourceRoot":"","sources":["../../src/client/file-server.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,WAAW,GACnB,QAAQ,GACR,QAAQ,GACR,cAAc,GACd,SAAS,GACT,cAAc,GACd,WAAW,CAAC;AAKhB;;;GAGG;AACH,qBAAa,OAAQ,SAAQ,KAAK;IAChC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,YAAY,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAK3D;CACF;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC,YAAY,IAAI,EAAE,iBAAiB,EAWlC;IAED,yDAAyD;IACnD,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CA8BvE;IAED,uDAAuD;IACjD,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA4C1D;IAED;;;;;;;OAOG;YACW,aAAa;IAmB3B;;;;OAIG;YACW,gBAAgB;CAwB/B"}
|
|
@@ -4,6 +4,13 @@ export interface TerminalServerOptions {
|
|
|
4
4
|
commandTimeoutMs?: number;
|
|
5
5
|
/** Bytes of output to retain per terminal. Default 1 MiB. */
|
|
6
6
|
outputByteLimit?: number;
|
|
7
|
+
/**
|
|
8
|
+
* Hard maximum cap on the per-call `outputByteLimit`. The agent can request
|
|
9
|
+
* a lower limit per terminal/create, but it can never raise it above this
|
|
10
|
+
* host-configured ceiling. Protects against memory exhaustion from a
|
|
11
|
+
* malicious agent requesting `outputByteLimit: Infinity`. Default 16 MiB.
|
|
12
|
+
*/
|
|
13
|
+
maxOutputByteLimit?: number;
|
|
7
14
|
/** Optional abort signal that kills ALL active terminals. */
|
|
8
15
|
signal?: AbortSignal;
|
|
9
16
|
}
|
|
@@ -12,6 +19,7 @@ export declare class TerminalServer {
|
|
|
12
19
|
private readonly projectRoot;
|
|
13
20
|
private readonly commandTimeoutMs;
|
|
14
21
|
private readonly outputByteLimit;
|
|
22
|
+
private readonly maxOutputByteLimit;
|
|
15
23
|
private nextId;
|
|
16
24
|
constructor(opts: TerminalServerOptions);
|
|
17
25
|
/** Spawn a new terminal. Returns the agent-facing id. */
|
|
@@ -50,5 +58,12 @@ export declare class TerminalServer {
|
|
|
50
58
|
releaseAll(): void;
|
|
51
59
|
private resolveCwd;
|
|
52
60
|
private buildEnv;
|
|
61
|
+
/**
|
|
62
|
+
* Clamp an agent-supplied numeric to a finite positive safe integer, falling
|
|
63
|
+
* back to `defaultValue` for undefined/NaN/non-finite values. Prevents
|
|
64
|
+
* negative, NaN, or Infinity values from disabling output caps or causing
|
|
65
|
+
* unbounded memory growth.
|
|
66
|
+
*/
|
|
67
|
+
private clampFiniteInt;
|
|
53
68
|
}
|
|
54
69
|
//# sourceMappingURL=terminal-server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-server.d.ts","sourceRoot":"","sources":["../../src/client/terminal-server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"terminal-server.d.ts","sourceRoot":"","sources":["../../src/client/terminal-server.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,6DAA6D;IAC7D,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAoBD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoC;IAC9D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,MAAM,CAAK;IAEnB,YAAY,IAAI,EAAE,qBAAqB,EAQtC;IAED,yDAAyD;IACzD,MAAM,CAAC,MAAM,EAAE;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,GAAG,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QACxC,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CAmGzB;IAED,iEAAiE;IACjE,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE;YAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;SAAE,CAAA;KAAE,CAQlI;IAED,oEAAoE;IAC9D,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAIjG;IAED,mFAAmF;IACnF,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAQ7B;IAED,uDAAuD;IACvD,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAahC;IAED,wDAAwD;IACxD,UAAU,IAAI,IAAI,CAIjB;IAED,OAAO,CAAC,UAAU;IAwBlB,OAAO,CAAC,QAAQ;IAwBhB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;CASvB"}
|
package/dist/client.js
CHANGED
|
@@ -42,7 +42,7 @@ var ClientTransport = class {
|
|
|
42
42
|
}
|
|
43
43
|
async start() {
|
|
44
44
|
if (this.child) return;
|
|
45
|
-
const [{ spawn: spawn2 }, { buildChildEnv }, os] = await Promise.all([
|
|
45
|
+
const [{ spawn: spawn2 }, { buildChildEnv: buildChildEnv2 }, os] = await Promise.all([
|
|
46
46
|
import("node:child_process"),
|
|
47
47
|
import("@wrongstack/core"),
|
|
48
48
|
import("node:os")
|
|
@@ -59,7 +59,7 @@ var ClientTransport = class {
|
|
|
59
59
|
const childArgs = this.opts.args ?? [];
|
|
60
60
|
const shim = process.platform === "win32" ? buildWin32CmdShimInvocation(this.opts.command, childArgs) : null;
|
|
61
61
|
this.child = spawn2(shim?.command ?? this.opts.command, shim?.args ?? childArgs, {
|
|
62
|
-
env: { ...
|
|
62
|
+
env: { ...buildChildEnv2(), ...this.opts.env },
|
|
63
63
|
cwd: spawnCwd,
|
|
64
64
|
stdio: ["pipe", "pipe", "pipe"],
|
|
65
65
|
windowsHide: true,
|
|
@@ -366,8 +366,12 @@ var ToolTranslator = class {
|
|
|
366
366
|
var ACP_PROTOCOL_VERSION = 1;
|
|
367
367
|
|
|
368
368
|
// src/client/file-server.ts
|
|
369
|
+
import { randomBytes } from "node:crypto";
|
|
370
|
+
import { realpathSync } from "node:fs";
|
|
369
371
|
import * as fsp from "node:fs/promises";
|
|
370
372
|
import * as path from "node:path";
|
|
373
|
+
var DEFAULT_MAX_READ_BYTES = 5 * 1024 * 1024;
|
|
374
|
+
var DEFAULT_MAX_WRITE_BYTES = 5 * 1024 * 1024;
|
|
371
375
|
var FsError = class extends Error {
|
|
372
376
|
code;
|
|
373
377
|
path;
|
|
@@ -380,23 +384,40 @@ var FsError = class extends Error {
|
|
|
380
384
|
};
|
|
381
385
|
var FileServer = class {
|
|
382
386
|
root;
|
|
387
|
+
realRoot;
|
|
383
388
|
timeoutMs;
|
|
389
|
+
maxReadBytes;
|
|
390
|
+
maxWriteBytes;
|
|
384
391
|
constructor(opts) {
|
|
385
392
|
this.root = path.resolve(opts.projectRoot);
|
|
393
|
+
this.realRoot = safeRealpathSync(this.root);
|
|
386
394
|
this.timeoutMs = opts.timeoutMs ?? 3e4;
|
|
395
|
+
this.maxReadBytes = opts.maxReadBytes ?? DEFAULT_MAX_READ_BYTES;
|
|
396
|
+
this.maxWriteBytes = opts.maxWriteBytes ?? DEFAULT_MAX_WRITE_BYTES;
|
|
387
397
|
}
|
|
388
398
|
/** Read a text file. Returns the content as a string. */
|
|
389
399
|
async readTextFile(params) {
|
|
390
|
-
const safe = this.resolveInside(params.path);
|
|
400
|
+
const safe = await this.resolveInside(params.path);
|
|
391
401
|
const controller = new AbortController();
|
|
392
402
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
393
403
|
try {
|
|
404
|
+
const stat2 = await fsp.stat(safe).catch((err) => {
|
|
405
|
+
throw mapFsError(err, safe);
|
|
406
|
+
});
|
|
407
|
+
if (stat2.size > this.maxReadBytes) {
|
|
408
|
+
throw new FsError(
|
|
409
|
+
"TOO_LARGE",
|
|
410
|
+
safe,
|
|
411
|
+
`file is ${stat2.size} bytes, max read is ${this.maxReadBytes} bytes`
|
|
412
|
+
);
|
|
413
|
+
}
|
|
394
414
|
const content = await fsp.readFile(safe, {
|
|
395
415
|
encoding: "utf8",
|
|
396
416
|
signal: controller.signal
|
|
397
417
|
});
|
|
398
418
|
return { content };
|
|
399
419
|
} catch (err) {
|
|
420
|
+
if (err instanceof FsError) throw err;
|
|
400
421
|
if (controller.signal.aborted) {
|
|
401
422
|
throw new FsError("TIMEOUT", safe, `readTextFile timed out after ${this.timeoutMs}ms`);
|
|
402
423
|
}
|
|
@@ -407,17 +428,31 @@ var FileServer = class {
|
|
|
407
428
|
}
|
|
408
429
|
/** Write a text file. Atomic via write-then-rename. */
|
|
409
430
|
async writeTextFile(params) {
|
|
410
|
-
const
|
|
431
|
+
const byteLength = Buffer.byteLength(params.content, "utf8");
|
|
432
|
+
if (byteLength > this.maxWriteBytes) {
|
|
433
|
+
throw new FsError(
|
|
434
|
+
"TOO_LARGE",
|
|
435
|
+
params.path,
|
|
436
|
+
`content is ${byteLength} bytes, max write is ${this.maxWriteBytes} bytes`
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
const safe = await this.resolveInside(params.path);
|
|
411
440
|
const controller = new AbortController();
|
|
412
441
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
413
|
-
const tmp = `${safe}.${
|
|
442
|
+
const tmp = `${safe}.${randomBytes(6).toString("hex")}.tmp`;
|
|
414
443
|
try {
|
|
415
444
|
await fsp.writeFile(tmp, params.content, {
|
|
416
445
|
encoding: "utf8",
|
|
417
446
|
signal: controller.signal
|
|
418
447
|
});
|
|
448
|
+
await this.assertRealInside(tmp);
|
|
449
|
+
await this.assertRealInside(path.dirname(safe));
|
|
419
450
|
await fsp.rename(tmp, safe);
|
|
420
451
|
} catch (err) {
|
|
452
|
+
if (err instanceof FsError) {
|
|
453
|
+
await fsp.unlink(tmp).catch(() => void 0);
|
|
454
|
+
throw err;
|
|
455
|
+
}
|
|
421
456
|
try {
|
|
422
457
|
await fsp.unlink(tmp);
|
|
423
458
|
} catch {
|
|
@@ -431,12 +466,14 @@ var FileServer = class {
|
|
|
431
466
|
}
|
|
432
467
|
}
|
|
433
468
|
/**
|
|
434
|
-
* Resolve a path
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
469
|
+
* Resolve a path and verify it is inside the project root by realpath.
|
|
470
|
+
* Rejects with `FsError` if the textual path, the resolved path, or the
|
|
471
|
+
* real (symlink-resolved) path escapes the project root.
|
|
472
|
+
*
|
|
473
|
+
* For files that don't exist yet (e.g. a write to a new file), the
|
|
474
|
+
* nearest existing ancestor directory is realpath-checked instead.
|
|
438
475
|
*/
|
|
439
|
-
resolveInside(p) {
|
|
476
|
+
async resolveInside(p) {
|
|
440
477
|
if (typeof p !== "string" || p.length === 0) {
|
|
441
478
|
throw new FsError("INVALID_PATH", p, "path is empty or not a string");
|
|
442
479
|
}
|
|
@@ -448,8 +485,38 @@ var FileServer = class {
|
|
|
448
485
|
if (resolved !== this.root && !resolved.startsWith(rootWithSep)) {
|
|
449
486
|
throw new FsError("OUTSIDE_ROOT", resolved, "path is outside the project root");
|
|
450
487
|
}
|
|
488
|
+
await this.assertRealInside(resolved);
|
|
451
489
|
return resolved;
|
|
452
490
|
}
|
|
491
|
+
/**
|
|
492
|
+
* Resolve `resolvedPath` through `fs.realpath` and verify the result is
|
|
493
|
+
* inside `realRoot`. For non-existent paths (new files), walk up to the
|
|
494
|
+
* nearest existing ancestor and check that instead.
|
|
495
|
+
*/
|
|
496
|
+
async assertRealInside(resolvedPath) {
|
|
497
|
+
let probe = resolvedPath;
|
|
498
|
+
for (; ; ) {
|
|
499
|
+
let real;
|
|
500
|
+
try {
|
|
501
|
+
real = await fsp.realpath(probe);
|
|
502
|
+
} catch (err) {
|
|
503
|
+
const code = err.code;
|
|
504
|
+
if (code === "ENOENT") {
|
|
505
|
+
const parent = path.dirname(probe);
|
|
506
|
+
if (parent === probe) return;
|
|
507
|
+
probe = parent;
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
throw mapFsError(err, resolvedPath);
|
|
511
|
+
}
|
|
512
|
+
if (real === this.realRoot || real.startsWith(this.realRoot + path.sep)) return;
|
|
513
|
+
throw new FsError(
|
|
514
|
+
"OUTSIDE_ROOT",
|
|
515
|
+
resolvedPath,
|
|
516
|
+
"path resolves through a symlink outside the project root"
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
453
520
|
};
|
|
454
521
|
function mapFsError(err, p) {
|
|
455
522
|
const code = err?.code;
|
|
@@ -460,12 +527,12 @@ function mapFsError(err, p) {
|
|
|
460
527
|
const msg = err instanceof Error ? err.message : String(err);
|
|
461
528
|
return new FsError("INVALID_PATH", p, msg);
|
|
462
529
|
}
|
|
463
|
-
function
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
530
|
+
function safeRealpathSync(p) {
|
|
531
|
+
try {
|
|
532
|
+
return realpathSync(p);
|
|
533
|
+
} catch {
|
|
534
|
+
return p;
|
|
467
535
|
}
|
|
468
|
-
return out;
|
|
469
536
|
}
|
|
470
537
|
|
|
471
538
|
// src/client/permission.ts
|
|
@@ -514,17 +581,21 @@ function makePermissionPolicy(decide) {
|
|
|
514
581
|
|
|
515
582
|
// src/client/terminal-server.ts
|
|
516
583
|
import { spawn } from "node:child_process";
|
|
584
|
+
import { realpathSync as realpathSync2 } from "node:fs";
|
|
517
585
|
import * as path2 from "node:path";
|
|
586
|
+
import { buildChildEnv } from "@wrongstack/core/utils";
|
|
518
587
|
var TerminalServer = class {
|
|
519
588
|
terminals = /* @__PURE__ */ new Map();
|
|
520
589
|
projectRoot;
|
|
521
590
|
commandTimeoutMs;
|
|
522
591
|
outputByteLimit;
|
|
592
|
+
maxOutputByteLimit;
|
|
523
593
|
nextId = 1;
|
|
524
594
|
constructor(opts) {
|
|
525
595
|
this.projectRoot = path2.resolve(opts.projectRoot);
|
|
526
596
|
this.commandTimeoutMs = opts.commandTimeoutMs ?? 5 * 6e4;
|
|
527
597
|
this.outputByteLimit = opts.outputByteLimit ?? 1024 * 1024;
|
|
598
|
+
this.maxOutputByteLimit = opts.maxOutputByteLimit ?? 16 * 1024 * 1024;
|
|
528
599
|
if (opts.signal) {
|
|
529
600
|
opts.signal.addEventListener("abort", () => this.releaseAll());
|
|
530
601
|
}
|
|
@@ -578,12 +649,15 @@ var TerminalServer = class {
|
|
|
578
649
|
state.exitStatus = exitStatus;
|
|
579
650
|
state.output += `[spawn error] ${err.message}
|
|
580
651
|
`;
|
|
581
|
-
state.retainedBytes
|
|
652
|
+
state.retainedBytes = Buffer.byteLength(state.output, "utf8");
|
|
582
653
|
resolve3(exitStatus);
|
|
583
654
|
});
|
|
584
655
|
})
|
|
585
656
|
};
|
|
586
|
-
const perCallByteLimit =
|
|
657
|
+
const perCallByteLimit = Math.min(
|
|
658
|
+
Math.max(1, this.clampFiniteInt(params.outputByteLimit, this.outputByteLimit)),
|
|
659
|
+
this.maxOutputByteLimit
|
|
660
|
+
);
|
|
587
661
|
proc.stdout?.setEncoding("utf8");
|
|
588
662
|
proc.stderr?.setEncoding("utf8");
|
|
589
663
|
const onData = (chunk) => {
|
|
@@ -663,24 +737,57 @@ var TerminalServer = class {
|
|
|
663
737
|
if (resolved !== this.projectRoot && !resolved.startsWith(rootWithSep)) {
|
|
664
738
|
return this.projectRoot;
|
|
665
739
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
if (env.PATHEXT !== void 0 && env.PATHEXT_CASE === void 0) {
|
|
673
|
-
env.PATHEXT_CASE = env.PATHEXT;
|
|
740
|
+
try {
|
|
741
|
+
const realRoot = realpathSync2(this.projectRoot);
|
|
742
|
+
const realCwd = realpathSync2(resolved);
|
|
743
|
+
const realRootWithSep = realRoot.endsWith(path2.sep) ? realRoot : realRoot + path2.sep;
|
|
744
|
+
if (realCwd !== realRoot && !realCwd.startsWith(realRootWithSep)) {
|
|
745
|
+
return realRoot;
|
|
674
746
|
}
|
|
747
|
+
return realCwd;
|
|
748
|
+
} catch {
|
|
749
|
+
return this.projectRoot;
|
|
675
750
|
}
|
|
751
|
+
}
|
|
752
|
+
buildEnv(agentEnv) {
|
|
753
|
+
const env = buildChildEnv();
|
|
676
754
|
if (agentEnv) {
|
|
677
755
|
for (const { name, value } of agentEnv) {
|
|
756
|
+
const upper = name.toUpperCase();
|
|
757
|
+
if (DENIED_AGENT_ENV_KEYS.has(upper)) continue;
|
|
678
758
|
env[name] = value;
|
|
679
759
|
}
|
|
680
760
|
}
|
|
681
761
|
return env;
|
|
682
762
|
}
|
|
763
|
+
/**
|
|
764
|
+
* Clamp an agent-supplied numeric to a finite positive safe integer, falling
|
|
765
|
+
* back to `defaultValue` for undefined/NaN/non-finite values. Prevents
|
|
766
|
+
* negative, NaN, or Infinity values from disabling output caps or causing
|
|
767
|
+
* unbounded memory growth.
|
|
768
|
+
*/
|
|
769
|
+
clampFiniteInt(value, defaultValue) {
|
|
770
|
+
if (value === void 0 || !Number.isFinite(value) || value < 1) {
|
|
771
|
+
return defaultValue;
|
|
772
|
+
}
|
|
773
|
+
return Math.trunc(value);
|
|
774
|
+
}
|
|
683
775
|
};
|
|
776
|
+
var DENIED_AGENT_ENV_KEYS = /* @__PURE__ */ new Set([
|
|
777
|
+
"NODE_OPTIONS",
|
|
778
|
+
"LD_PRELOAD",
|
|
779
|
+
"LD_LIBRARY_PATH",
|
|
780
|
+
"DYLD_INSERT_LIBRARIES",
|
|
781
|
+
"DYLD_LIBRARY_PATH",
|
|
782
|
+
"DYLD_FALLBACK_LIBRARY_PATH",
|
|
783
|
+
"PATH",
|
|
784
|
+
"PYTHONPATH",
|
|
785
|
+
"PYTHONSTARTUP",
|
|
786
|
+
"PERL5OPT",
|
|
787
|
+
"PERLLIB",
|
|
788
|
+
"RUBYOPT",
|
|
789
|
+
"RUBYLIB"
|
|
790
|
+
]);
|
|
684
791
|
|
|
685
792
|
// src/client/acp-session.ts
|
|
686
793
|
var ACPSessionError = class extends Error {
|
|
@@ -832,7 +939,7 @@ var ACPSession = class _ACPSession {
|
|
|
832
939
|
fs: { readTextFile: true, writeTextFile: true },
|
|
833
940
|
terminal: true
|
|
834
941
|
},
|
|
835
|
-
clientInfo: { name: "wrongstack", title: "WrongStack", version: "0.
|
|
942
|
+
clientInfo: { name: "wrongstack", title: "WrongStack", version: "0.287.0" }
|
|
836
943
|
});
|
|
837
944
|
if (isJsonRpcError(result)) {
|
|
838
945
|
throw new ACPSessionError("init_failed", `initialize failed: ${result.message}`, result);
|
|
@@ -1530,12 +1637,54 @@ var ACPSession = class _ACPSession {
|
|
|
1530
1637
|
return;
|
|
1531
1638
|
}
|
|
1532
1639
|
const policyAbort = new AbortController();
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1640
|
+
try {
|
|
1641
|
+
const outcome = await this.permissionPolicy({
|
|
1642
|
+
toolCall,
|
|
1643
|
+
options,
|
|
1644
|
+
signal: policyAbort.signal
|
|
1645
|
+
});
|
|
1646
|
+
await this.sendResult(id, { outcome });
|
|
1647
|
+
} catch (err) {
|
|
1648
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1649
|
+
await this.sendErrorResponse(id, -32603, `permission policy failed: ${message}`);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
/**
|
|
1653
|
+
* Enforce authorization at privileged callback sinks (fs/write,
|
|
1654
|
+
* terminal/create). Unlike `handlePermissionRequest` which responds to
|
|
1655
|
+
* agent-initiated `session/request_permission` messages, this method is
|
|
1656
|
+
* called by the handler BEFORE dispatching to FileServer/TerminalServer,
|
|
1657
|
+
* closing the gap where the agent simply skips the voluntary permission
|
|
1658
|
+
* request and sends the privileged callback directly.
|
|
1659
|
+
*
|
|
1660
|
+
* Uses the session's permission policy. The default policy
|
|
1661
|
+
* (`defaultPermissionPolicy`) auto-approves everything — this is correct
|
|
1662
|
+
* for trusted local agents (CLI `acp spawn`, Director fan-out). For
|
|
1663
|
+
* untrusted/remote agents, the host should inject
|
|
1664
|
+
* `readOnlyPermissionPolicy` or an interactive policy.
|
|
1665
|
+
*
|
|
1666
|
+
* Returns true if the callback is authorized, false if denied.
|
|
1667
|
+
*/
|
|
1668
|
+
async authorizeCallback(partial) {
|
|
1669
|
+
try {
|
|
1670
|
+
const outcome = await this.permissionPolicy({
|
|
1671
|
+
toolCall: {
|
|
1672
|
+
sessionUpdate: "tool_call_update",
|
|
1673
|
+
toolCallId: partial.toolCallId,
|
|
1674
|
+
title: partial.title,
|
|
1675
|
+
kind: partial.kind,
|
|
1676
|
+
status: "pending"
|
|
1677
|
+
},
|
|
1678
|
+
options: [
|
|
1679
|
+
{ optionId: "allow", name: "Allow", kind: "allow_once" },
|
|
1680
|
+
{ optionId: "reject", name: "Reject", kind: "reject_once" }
|
|
1681
|
+
],
|
|
1682
|
+
signal: new AbortController().signal
|
|
1683
|
+
});
|
|
1684
|
+
return outcome.outcome === "selected" && outcome.optionId !== "reject" && outcome.optionId !== "reject_once" && outcome.optionId !== "reject_always";
|
|
1685
|
+
} catch {
|
|
1686
|
+
return false;
|
|
1687
|
+
}
|
|
1539
1688
|
}
|
|
1540
1689
|
async handleFsRequest(msg) {
|
|
1541
1690
|
const id = msg.id;
|
|
@@ -1545,6 +1694,17 @@ var ACPSession = class _ACPSession {
|
|
|
1545
1694
|
await this.sendErrorResponse(id, -32602, "path is required");
|
|
1546
1695
|
return;
|
|
1547
1696
|
}
|
|
1697
|
+
if (msg.method === "fs/write_text_file") {
|
|
1698
|
+
const allowed = await this.authorizeCallback({
|
|
1699
|
+
toolCallId: `acp-fs-write-${id}`,
|
|
1700
|
+
title: `Write file: ${params.path}`,
|
|
1701
|
+
kind: "edit"
|
|
1702
|
+
});
|
|
1703
|
+
if (!allowed) {
|
|
1704
|
+
await this.sendErrorResponse(id, -32602, "filesystem write denied by permission policy");
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1548
1708
|
try {
|
|
1549
1709
|
if (msg.method === "fs/read_text_file") {
|
|
1550
1710
|
const result = await this.fileServer.readTextFile({
|
|
@@ -1573,6 +1733,15 @@ var ACPSession = class _ACPSession {
|
|
|
1573
1733
|
try {
|
|
1574
1734
|
switch (msg.method) {
|
|
1575
1735
|
case "terminal/create": {
|
|
1736
|
+
const allowed = await this.authorizeCallback({
|
|
1737
|
+
toolCallId: `acp-terminal-create-${id}`,
|
|
1738
|
+
title: `Run command: ${String(params.command ?? "")} ${(Array.isArray(params.args) ? params.args : []).join(" ")}`.trim(),
|
|
1739
|
+
kind: "execute"
|
|
1740
|
+
});
|
|
1741
|
+
if (!allowed) {
|
|
1742
|
+
await this.sendErrorResponse(id, -32602, "terminal create denied by permission policy");
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1576
1745
|
const createOpts = {
|
|
1577
1746
|
sessionId: String(params.sessionId ?? ""),
|
|
1578
1747
|
command: String(params.command ?? ""),
|