@wrongstack/acp 1.0.33 → 1.0.36

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/client.js CHANGED
@@ -691,39 +691,6 @@ async function ensureAuthenticated(host) {
691
691
  );
692
692
  }
693
693
 
694
- // src/client/acp-session-content.ts
695
- function textContent(text) {
696
- return { type: "text", text };
697
- }
698
- function imageContent(mimeType, data) {
699
- return { type: "image", mimeType, data };
700
- }
701
- function audioContent(mimeType, data) {
702
- return { type: "audio", mimeType, data };
703
- }
704
- function extractText(block) {
705
- if (typeof block !== "object" || block === null) return "";
706
- const b = block;
707
- if (b.type === "text" && typeof b.text === "string") return b.text;
708
- if (b.type === "resource" && b.resource && typeof b.resource === "object" && typeof b.resource.text === "string") {
709
- return b.resource.text;
710
- }
711
- return "";
712
- }
713
- function isRecord(v) {
714
- return typeof v === "object" && v !== null && !Array.isArray(v);
715
- }
716
- function emptyRunResult(stopReason) {
717
- return {
718
- text: "",
719
- stopReason,
720
- hasText: false,
721
- toolCalls: [],
722
- diffs: [],
723
- thoughts: ""
724
- };
725
- }
726
-
727
694
  // src/client/acp-message-routing.ts
728
695
  function isBestEffortAckMethod(method) {
729
696
  return method === "mcp/connect" || method === "mcp/message" || method === "mcp/disconnect" || method === "elicitation/create" || method === "elicitation/complete";
@@ -1191,10 +1158,45 @@ function isAbortLikeError(err) {
1191
1158
  }
1192
1159
  function resolvePermissionDeadline(value) {
1193
1160
  if (value === Number.POSITIVE_INFINITY) return null;
1194
- if (value !== void 0 && Number.isFinite(value) && value > 0) return Math.trunc(value);
1161
+ if (value !== void 0 && Number.isFinite(value) && value > 0) {
1162
+ return Math.min(Math.trunc(value), 2147483647);
1163
+ }
1195
1164
  return DEFAULT_PERMISSION_TIMEOUT_MS;
1196
1165
  }
1197
1166
 
1167
+ // src/client/acp-session-content.ts
1168
+ function textContent(text) {
1169
+ return { type: "text", text };
1170
+ }
1171
+ function imageContent(mimeType, data) {
1172
+ return { type: "image", mimeType, data };
1173
+ }
1174
+ function audioContent(mimeType, data) {
1175
+ return { type: "audio", mimeType, data };
1176
+ }
1177
+ function extractText(block) {
1178
+ if (typeof block !== "object" || block === null) return "";
1179
+ const b = block;
1180
+ if (b.type === "text" && typeof b.text === "string") return b.text;
1181
+ if (b.type === "resource" && b.resource && typeof b.resource === "object" && typeof b.resource.text === "string") {
1182
+ return b.resource.text;
1183
+ }
1184
+ return "";
1185
+ }
1186
+ function isRecord(v) {
1187
+ return typeof v === "object" && v !== null && !Array.isArray(v);
1188
+ }
1189
+ function emptyRunResult(stopReason) {
1190
+ return {
1191
+ text: "",
1192
+ stopReason,
1193
+ hasText: false,
1194
+ toolCalls: [],
1195
+ diffs: [],
1196
+ thoughts: ""
1197
+ };
1198
+ }
1199
+
1198
1200
  // src/client/acp-session-updates.ts
1199
1201
  function createSessionScratch() {
1200
1202
  return { text: "", thoughts: "", toolCalls: /* @__PURE__ */ new Map(), diffs: [] };
@@ -1390,6 +1392,146 @@ function emitProgress(event) {
1390
1392
  }
1391
1393
  }
1392
1394
 
1395
+ // src/client/acp-session-prompt.ts
1396
+ async function prompt(host, blocks, signal, onProgress) {
1397
+ if (host.closed) {
1398
+ throw new ACPSessionError("closed", "session is closed");
1399
+ }
1400
+ if (host.state !== "ready" && host.state !== "authenticated" && host.state !== "done") {
1401
+ throw new ACPSessionError("protocol_error", `prompt called in state=${host.state}`);
1402
+ }
1403
+ if (host.promptCallbackAbort) {
1404
+ throw new ACPSessionError("protocol_error", "prompt called while another prompt is running");
1405
+ }
1406
+ if (signal.aborted) {
1407
+ return emptyRunResult("cancelled");
1408
+ }
1409
+ const caps = host.agentCapabilities.promptCapabilities;
1410
+ for (const block of blocks) {
1411
+ const supported = block.type === "image" ? caps?.image : block.type === "audio" ? caps?.audio : block.type === "resource" ? caps?.embeddedContext : true;
1412
+ if (supported !== true) {
1413
+ throw new ACPSessionError(
1414
+ "unsupported_capability",
1415
+ `agent does not support ${block.type} prompt content`
1416
+ );
1417
+ }
1418
+ }
1419
+ let cancelled = false;
1420
+ host.promptCallbackAbort = new AbortController();
1421
+ const onAbort = () => {
1422
+ cancelled = true;
1423
+ host.promptCallbackAbort?.abort();
1424
+ if (host.sessionId) {
1425
+ host.transport.send({
1426
+ jsonrpc: "2.0",
1427
+ method: "session/cancel",
1428
+ params: { sessionId: host.sessionId }
1429
+ }).catch(() => {
1430
+ });
1431
+ }
1432
+ };
1433
+ signal.addEventListener("abort", onAbort, { once: true });
1434
+ let rejectCreate;
1435
+ const onCreateAbort = () => {
1436
+ signal.removeEventListener("abort", onAbort);
1437
+ rejectCreate?.(new ACPSessionError("aborted", "prompt was aborted by the parent"));
1438
+ };
1439
+ if (!host.sessionId) {
1440
+ let sessionId;
1441
+ const createPromise = host.createSessionWithAuth();
1442
+ try {
1443
+ sessionId = await Promise.race([
1444
+ createPromise,
1445
+ new Promise((_, reject) => {
1446
+ rejectCreate = reject;
1447
+ signal.addEventListener("abort", onCreateAbort, { once: true });
1448
+ })
1449
+ ]);
1450
+ } catch (err) {
1451
+ signal.removeEventListener("abort", onAbort);
1452
+ signal.removeEventListener("abort", onCreateAbort);
1453
+ rejectCreate = void 0;
1454
+ host.promptCallbackAbort?.abort();
1455
+ host.promptCallbackAbort = null;
1456
+ if (err instanceof ACPSessionError && err.kind === "aborted") {
1457
+ host.cancelLateSession(createPromise);
1458
+ return emptyRunResult("cancelled");
1459
+ }
1460
+ throw err;
1461
+ }
1462
+ signal.removeEventListener("abort", onCreateAbort);
1463
+ rejectCreate = void 0;
1464
+ host.sessionId = sessionId;
1465
+ }
1466
+ if (signal.aborted) {
1467
+ host.promptCallbackAbort = null;
1468
+ return emptyRunResult("cancelled");
1469
+ }
1470
+ host.resetScratch();
1471
+ host.progressHandler = onProgress ?? null;
1472
+ const promptId = host.allocId();
1473
+ let rejectTurn;
1474
+ const onTurnAbort = () => {
1475
+ rejectTurn?.(new ACPSessionError("aborted", "prompt was aborted by the parent"));
1476
+ };
1477
+ signal.addEventListener("abort", onTurnAbort, { once: true });
1478
+ const turnPromise = Promise.race([
1479
+ host.sendRequest(
1480
+ promptId,
1481
+ "session/prompt",
1482
+ {
1483
+ sessionId: host.sessionId,
1484
+ prompt: blocks
1485
+ },
1486
+ host.timeoutMs
1487
+ ),
1488
+ new Promise((_, reject) => {
1489
+ rejectTurn = reject;
1490
+ })
1491
+ ]);
1492
+ host.state = "prompting";
1493
+ let response;
1494
+ try {
1495
+ response = await turnPromise;
1496
+ } catch (err) {
1497
+ host.state = "done";
1498
+ const abortedKind = err instanceof ACPSessionError && err.kind === "aborted";
1499
+ if (cancelled || abortedKind) {
1500
+ return emptyRunResult("cancelled");
1501
+ }
1502
+ const msg = err instanceof Error ? err.message : String(err);
1503
+ if (signal.aborted) {
1504
+ throw new ACPSessionError("aborted", "prompt was aborted by the parent", err);
1505
+ }
1506
+ throw new ACPSessionError("prompt_failed", `session/prompt failed: ${msg}`, err);
1507
+ } finally {
1508
+ signal.removeEventListener("abort", onAbort);
1509
+ signal.removeEventListener("abort", onTurnAbort);
1510
+ signal.removeEventListener("abort", onCreateAbort);
1511
+ rejectTurn = void 0;
1512
+ rejectCreate = void 0;
1513
+ host.promptCallbackAbort?.abort();
1514
+ host.promptCallbackAbort = null;
1515
+ host.progressHandler = null;
1516
+ }
1517
+ host.state = "done";
1518
+ if (isJsonRpcError(response)) {
1519
+ throw new ACPSessionError("prompt_failed", `agent error: ${response.message}`, response);
1520
+ }
1521
+ const stopReason = response.stopReason ?? "end_turn";
1522
+ const finalText = host.scratch.text;
1523
+ return {
1524
+ text: finalText,
1525
+ stopReason,
1526
+ hasText: finalText.length > 0,
1527
+ usage: host.scratch.usage,
1528
+ plan: host.scratch.plan,
1529
+ toolCalls: [...host.scratch.toolCalls.values()],
1530
+ diffs: host.scratch.diffs,
1531
+ thoughts: host.scratch.thoughts
1532
+ };
1533
+ }
1534
+
1393
1535
  // src/client/permission.ts
1394
1536
  function pickAllow(options) {
1395
1537
  const ranked = [...options].sort((a, b) => {
@@ -1445,7 +1587,8 @@ function win32BatchTarget(command) {
1445
1587
  if (/\.(?:cmd|bat)$/i.test(command)) return command;
1446
1588
  if (/[\\/]/.test(command) || path2.extname(command)) return void 0;
1447
1589
  const exts = (process.env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD").toLowerCase().split(";");
1448
- for (const dir of (process.env["PATH"] ?? "").split(path2.delimiter)) {
1590
+ for (const rawDir of (process.env["PATH"] ?? "").split(path2.delimiter)) {
1591
+ const dir = rawDir.trim().replace(/^"|"$/g, "");
1449
1592
  if (!dir) continue;
1450
1593
  for (const ext of exts) {
1451
1594
  if (!ext) continue;
@@ -1709,15 +1852,13 @@ var TerminalServer = class {
1709
1852
  resolveCwd(cwd) {
1710
1853
  if (!cwd) return this.projectRoot;
1711
1854
  const resolved = path2.resolve(cwd);
1712
- const rootWithSep = this.projectRoot.endsWith(path2.sep) ? this.projectRoot : this.projectRoot + path2.sep;
1713
- if (resolved !== this.projectRoot && !resolved.startsWith(rootWithSep)) {
1855
+ if (!isInsideRoot(resolved, this.projectRoot)) {
1714
1856
  return this.projectRoot;
1715
1857
  }
1716
1858
  try {
1717
1859
  const realRoot = realpathSync2(this.projectRoot);
1718
1860
  const realCwd = realpathSync2(resolved);
1719
- const realRootWithSep = realRoot.endsWith(path2.sep) ? realRoot : realRoot + path2.sep;
1720
- if (realCwd !== realRoot && !realCwd.startsWith(realRootWithSep)) {
1861
+ if (!isInsideRoot(realCwd, realRoot)) {
1721
1862
  return realRoot;
1722
1863
  }
1723
1864
  return realCwd;
@@ -1749,6 +1890,12 @@ var TerminalServer = class {
1749
1890
  return Math.trunc(value);
1750
1891
  }
1751
1892
  };
1893
+ function isInsideRoot(child, root) {
1894
+ const fold = (p) => process.platform === "win32" ? p.toLowerCase() : p;
1895
+ const c = fold(child);
1896
+ const r = fold(root);
1897
+ return c === r || c.startsWith(r.endsWith(path2.sep) ? r : r + path2.sep);
1898
+ }
1752
1899
  var DENIED_AGENT_ENV_KEYS = /* @__PURE__ */ new Set([
1753
1900
  "NODE_OPTIONS",
1754
1901
  "LD_PRELOAD",
@@ -2012,6 +2159,7 @@ var WebSocketClientTransport = class {
2012
2159
  }
2013
2160
  dispatch(msg) {
2014
2161
  for (const handler of [...this.handlers]) {
2162
+ if (this.closed) break;
2015
2163
  try {
2016
2164
  handler(msg);
2017
2165
  } catch {
@@ -2288,142 +2436,7 @@ var ACPSession = class _ACPSession {
2288
2436
  // Prompt
2289
2437
  // ──────────────────────────────────────────────────────────────────────
2290
2438
  async prompt(blocks, signal, onProgress) {
2291
- if (this.closed) {
2292
- throw new ACPSessionError("closed", "session is closed");
2293
- }
2294
- if (this.state !== "ready" && this.state !== "authenticated" && this.state !== "done") {
2295
- throw new ACPSessionError("protocol_error", `prompt called in state=${this.state}`);
2296
- }
2297
- if (this.promptCallbackAbort) {
2298
- throw new ACPSessionError("protocol_error", "prompt called while another prompt is running");
2299
- }
2300
- if (signal.aborted) {
2301
- return emptyRunResult("cancelled");
2302
- }
2303
- const caps = this.agentCapabilities.promptCapabilities;
2304
- for (const block of blocks) {
2305
- const supported = block.type === "image" ? caps?.image : block.type === "audio" ? caps?.audio : block.type === "resource" ? caps?.embeddedContext : true;
2306
- if (supported !== true) {
2307
- throw new ACPSessionError(
2308
- "unsupported_capability",
2309
- `agent does not support ${block.type} prompt content`
2310
- );
2311
- }
2312
- }
2313
- let cancelled = false;
2314
- this.promptCallbackAbort = new AbortController();
2315
- const onAbort = () => {
2316
- cancelled = true;
2317
- this.promptCallbackAbort?.abort();
2318
- if (this.sessionId) {
2319
- this.transport.send({
2320
- jsonrpc: "2.0",
2321
- method: "session/cancel",
2322
- params: { sessionId: this.sessionId }
2323
- }).catch(() => {
2324
- });
2325
- }
2326
- };
2327
- signal.addEventListener("abort", onAbort, { once: true });
2328
- let rejectCreate;
2329
- const onCreateAbort = () => {
2330
- signal.removeEventListener("abort", onAbort);
2331
- rejectCreate?.(new ACPSessionError("aborted", "prompt was aborted by the parent"));
2332
- };
2333
- if (!this.sessionId) {
2334
- let sessionId;
2335
- const createPromise = this.createSessionWithAuth();
2336
- try {
2337
- sessionId = await Promise.race([
2338
- createPromise,
2339
- new Promise((_, reject) => {
2340
- rejectCreate = reject;
2341
- signal.addEventListener("abort", onCreateAbort, { once: true });
2342
- })
2343
- ]);
2344
- } catch (err) {
2345
- signal.removeEventListener("abort", onAbort);
2346
- signal.removeEventListener("abort", onCreateAbort);
2347
- rejectCreate = void 0;
2348
- this.promptCallbackAbort?.abort();
2349
- this.promptCallbackAbort = null;
2350
- if (err instanceof ACPSessionError && err.kind === "aborted") {
2351
- this.cancelLateSession(createPromise);
2352
- return emptyRunResult("cancelled");
2353
- }
2354
- throw err;
2355
- }
2356
- signal.removeEventListener("abort", onCreateAbort);
2357
- rejectCreate = void 0;
2358
- this.sessionId = sessionId;
2359
- }
2360
- if (signal.aborted) {
2361
- this.promptCallbackAbort = null;
2362
- return emptyRunResult("cancelled");
2363
- }
2364
- this.resetScratch();
2365
- this.progressHandler = onProgress ?? null;
2366
- const promptId = this.allocId();
2367
- let rejectTurn;
2368
- const onTurnAbort = () => {
2369
- rejectTurn?.(new ACPSessionError("aborted", "prompt was aborted by the parent"));
2370
- };
2371
- signal.addEventListener("abort", onTurnAbort, { once: true });
2372
- const turnPromise = Promise.race([
2373
- this.sendRequest(
2374
- promptId,
2375
- "session/prompt",
2376
- {
2377
- sessionId: this.sessionId,
2378
- prompt: blocks
2379
- },
2380
- this.timeoutMs
2381
- ),
2382
- new Promise((_, reject) => {
2383
- rejectTurn = reject;
2384
- })
2385
- ]);
2386
- this.state = "prompting";
2387
- let response;
2388
- try {
2389
- response = await turnPromise;
2390
- } catch (err) {
2391
- this.state = "done";
2392
- const abortedKind = err instanceof ACPSessionError && err.kind === "aborted";
2393
- if (cancelled || abortedKind) {
2394
- return emptyRunResult("cancelled");
2395
- }
2396
- const msg = err instanceof Error ? err.message : String(err);
2397
- if (signal.aborted) {
2398
- throw new ACPSessionError("aborted", "prompt was aborted by the parent", err);
2399
- }
2400
- throw new ACPSessionError("prompt_failed", `session/prompt failed: ${msg}`, err);
2401
- } finally {
2402
- signal.removeEventListener("abort", onAbort);
2403
- signal.removeEventListener("abort", onTurnAbort);
2404
- signal.removeEventListener("abort", onCreateAbort);
2405
- rejectTurn = void 0;
2406
- rejectCreate = void 0;
2407
- this.promptCallbackAbort?.abort();
2408
- this.promptCallbackAbort = null;
2409
- this.progressHandler = null;
2410
- }
2411
- this.state = "done";
2412
- if (isJsonRpcError(response)) {
2413
- throw new ACPSessionError("prompt_failed", `agent error: ${response.message}`, response);
2414
- }
2415
- const stopReason = response.stopReason ?? "end_turn";
2416
- const finalText = this.scratch.text;
2417
- return {
2418
- text: finalText,
2419
- stopReason,
2420
- hasText: finalText.length > 0,
2421
- usage: this.scratch.usage,
2422
- plan: this.scratch.plan,
2423
- toolCalls: [...this.scratch.toolCalls.values()],
2424
- diffs: this.scratch.diffs,
2425
- thoughts: this.scratch.thoughts
2426
- };
2439
+ return prompt(this.acpSessionPromptHost(), blocks, signal, onProgress);
2427
2440
  }
2428
2441
  /**
2429
2442
  * Best-effort cancel for a session the server confirmed after we already
@@ -2593,6 +2606,25 @@ var ACPSession = class _ACPSession {
2593
2606
  void this.progressHandler;
2594
2607
  return this;
2595
2608
  }
2609
+ acpSessionPromptHost() {
2610
+ void {
2611
+ closed: this.closed,
2612
+ state: this.state,
2613
+ promptCallbackAbort: this.promptCallbackAbort,
2614
+ agentCapabilities: this.agentCapabilities,
2615
+ sessionId: this.sessionId,
2616
+ transport: this.transport,
2617
+ createSessionWithAuth: this.createSessionWithAuth,
2618
+ cancelLateSession: this.cancelLateSession,
2619
+ resetScratch: this.resetScratch,
2620
+ progressHandler: this.progressHandler,
2621
+ allocId: this.allocId,
2622
+ sendRequest: this.sendRequest,
2623
+ timeoutMs: this.timeoutMs,
2624
+ scratch: this.scratch
2625
+ };
2626
+ return this;
2627
+ }
2596
2628
  };
2597
2629
 
2598
2630
  // src/integration/acp-subagent-runner.ts