@wrongstack/acp 1.0.17 → 1.0.24

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/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/agent/protocol-handler.ts
2
2
  import { randomUUID } from "node:crypto";
3
+ import { isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
3
4
 
4
5
  // src/types/acp-v1.ts
5
6
  var ACP_PROTOCOL_VERSION = 1;
@@ -180,7 +181,7 @@ function createRunTurnApi(sessionId, clientCapabilities, request, sendSessionUpd
180
181
  ...sendSessionUpdate ? { sendSessionUpdate } : {}
181
182
  };
182
183
  }
183
- function buildInitializeResult(agentName, modes, configOptions) {
184
+ function buildInitializeResult(agentName, modes, configOptions, clientCapabilities = {}) {
184
185
  return {
185
186
  protocolVersion: ACP_PROTOCOL_VERSION,
186
187
  agentCapabilities: {
@@ -207,9 +208,6 @@ function buildInitializeResult(agentName, modes, configOptions) {
207
208
  delete: {},
208
209
  resume: {},
209
210
  fork: {}
210
- },
211
- auth: {
212
- logout: {}
213
211
  }
214
212
  },
215
213
  agentInfo: {
@@ -217,7 +215,7 @@ function buildInitializeResult(agentName, modes, configOptions) {
217
215
  title: "WrongStack",
218
216
  version: WRONGSTACK_VERSION
219
217
  },
220
- authMethods: WRONGSTACK_AUTH_METHODS,
218
+ authMethods: clientCapabilities.auth?.terminal === true ? WRONGSTACK_AUTH_METHODS : [],
221
219
  modes,
222
220
  configOptions
223
221
  };
@@ -247,7 +245,8 @@ async function handleSessionNewOp(ctx, id, params) {
247
245
  id: sessionId,
248
246
  cwd,
249
247
  abort: new AbortController(),
250
- modeId: DEFAULT_MODE_ID,
248
+ modeId: ctx.modes[0]?.id ?? DEFAULT_MODE_ID,
249
+ configOptions: structuredClone([...ctx.configOptions]),
251
250
  createdAt: now,
252
251
  updatedAt: now,
253
252
  ...mcpServers.length > 0 ? { mcpServers } : {}
@@ -259,6 +258,7 @@ async function handleSessionNewOp(ctx, id, params) {
259
258
  sessionId,
260
259
  update: {
261
260
  sessionUpdate: "current_mode_update",
261
+ currentModeId: ctx.modes[0]?.id ?? DEFAULT_MODE_ID,
262
262
  modeId: ctx.modes[0]?.id ?? DEFAULT_MODE_ID
263
263
  }
264
264
  });
@@ -274,12 +274,12 @@ async function handleSessionNewOp(ctx, id, params) {
274
274
  await reportSkippedMcpServers(ctx, sessionId, skipped);
275
275
  await ctx.sendResult(id, {
276
276
  sessionId,
277
- modes: ctx.modes,
278
- configOptions: ctx.configOptions
277
+ modes: { currentModeId: state.modeId, availableModes: ctx.modes },
278
+ configOptions: state.configOptions
279
279
  });
280
280
  return false;
281
281
  }
282
- async function handleSessionLoadOp(ctx, id, params) {
282
+ async function handleSessionLoadOp(ctx, id, params, replayHistory = true) {
283
283
  const p = params ?? {};
284
284
  const sessionId = typeof p.sessionId === "string" ? p.sessionId : null;
285
285
  const loadCwd = typeof p.cwd === "string" ? p.cwd : void 0;
@@ -304,6 +304,7 @@ async function handleSessionLoadOp(ctx, id, params) {
304
304
  cwd: restoredCwd,
305
305
  abort: new AbortController(),
306
306
  modeId: persisted.modeId ?? DEFAULT_MODE_ID,
307
+ configOptions: structuredClone(persisted.configOptions ?? [...ctx.configOptions]),
307
308
  createdAt: persisted.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
308
309
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
309
310
  ...persisted.title !== void 0 ? { title: persisted.title } : {},
@@ -311,51 +312,69 @@ async function handleSessionLoadOp(ctx, id, params) {
311
312
  };
312
313
  ctx.sessions.set(sessionId, restored);
313
314
  ctx.seedFor?.(sessionId, persisted.history ?? []);
314
- for (const update of persisted.history ?? []) {
315
+ for (const update of replayHistory ? persisted.history ?? [] : []) {
315
316
  await ctx.sendNotification({ sessionId, update });
316
317
  }
317
- await ctx.sendNotification({
318
- sessionId,
319
- update: { sessionUpdate: "current_mode_update", modeId: restored.modeId }
320
- });
318
+ if (replayHistory)
319
+ await ctx.sendNotification({
320
+ sessionId,
321
+ update: {
322
+ sessionUpdate: "current_mode_update",
323
+ currentModeId: restored.modeId,
324
+ modeId: restored.modeId
325
+ }
326
+ });
321
327
  await reportSkippedMcpServers(ctx, sessionId, loadSkipped);
322
328
  await ctx.sendResult(id, {
323
- initialMode: { currentModeId: restored.modeId, availableModes: ctx.modes }
329
+ modes: { currentModeId: restored.modeId, availableModes: ctx.modes },
330
+ configOptions: restored.configOptions
324
331
  });
325
332
  return false;
326
333
  }
327
334
  }
328
335
  if (existing) {
336
+ if (existing.prompting) {
337
+ await ctx.sendError(id, -32e3, "cannot reload a session while a prompt is running");
338
+ return false;
339
+ }
329
340
  existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
330
- if (loadMcpServers.length > 0) {
341
+ const replay = ctx.replayFor?.(sessionId);
342
+ if (Array.isArray(p.mcpServers)) {
343
+ if (JSON.stringify(existing.mcpServers ?? []) !== JSON.stringify(loadMcpServers)) {
344
+ ctx.disposeFor?.(sessionId);
345
+ ctx.seedFor?.(sessionId, replay ?? []);
346
+ }
331
347
  existing.mcpServers = loadMcpServers;
332
348
  }
333
- const replay = ctx.replayFor?.(sessionId);
334
- if (replay) {
349
+ if (replayHistory && replay) {
335
350
  for (const update of replay) {
336
351
  await ctx.sendNotification({ sessionId, update });
337
352
  }
338
353
  }
339
- await ctx.sendNotification({
340
- sessionId,
341
- update: {
342
- sessionUpdate: "session_info_update",
343
- updatedAt: existing.updatedAt
344
- }
345
- });
346
- await ctx.sendNotification({
347
- sessionId,
348
- update: {
349
- sessionUpdate: "current_mode_update",
350
- modeId: existing.modeId
351
- }
352
- });
354
+ if (replayHistory)
355
+ await ctx.sendNotification({
356
+ sessionId,
357
+ update: {
358
+ sessionUpdate: "session_info_update",
359
+ updatedAt: existing.updatedAt
360
+ }
361
+ });
362
+ if (replayHistory)
363
+ await ctx.sendNotification({
364
+ sessionId,
365
+ update: {
366
+ sessionUpdate: "current_mode_update",
367
+ currentModeId: existing.modeId,
368
+ modeId: existing.modeId
369
+ }
370
+ });
353
371
  await reportSkippedMcpServers(ctx, sessionId, loadSkipped);
354
372
  await ctx.sendResult(id, {
355
- initialMode: {
373
+ modes: {
356
374
  currentModeId: existing.modeId,
357
375
  availableModes: ctx.modes
358
- }
376
+ },
377
+ configOptions: existing.configOptions ?? [...ctx.configOptions]
359
378
  });
360
379
  return false;
361
380
  }
@@ -393,6 +412,7 @@ async function handleSessionForkOp(ctx, id, params) {
393
412
  cwd: forkCwd,
394
413
  abort: new AbortController(),
395
414
  modeId: source.modeId,
415
+ configOptions: structuredClone(source.configOptions ?? [...ctx.configOptions]),
396
416
  createdAt: now,
397
417
  updatedAt: now,
398
418
  ...source.title !== void 0 ? { title: source.title } : {},
@@ -408,13 +428,17 @@ async function handleSessionForkOp(ctx, id, params) {
408
428
  await ctx.persist(forked, history);
409
429
  await ctx.sendNotification({
410
430
  sessionId,
411
- update: { sessionUpdate: "current_mode_update", modeId: forked.modeId }
431
+ update: {
432
+ sessionUpdate: "current_mode_update",
433
+ currentModeId: forked.modeId,
434
+ modeId: forked.modeId
435
+ }
412
436
  });
413
437
  await reportSkippedMcpServers(ctx, sessionId, forkSkipped);
414
438
  await ctx.sendResult(id, {
415
439
  sessionId,
416
- modes: ctx.modes,
417
- configOptions: ctx.configOptions
440
+ modes: { currentModeId: forked.modeId, availableModes: ctx.modes },
441
+ configOptions: forked.configOptions
418
442
  });
419
443
  return false;
420
444
  }
@@ -430,6 +454,11 @@ async function handleSessionPromptOp(ctx, id, params) {
430
454
  return false;
431
455
  }
432
456
  const session = ctx.sessions.get(sessionId);
457
+ if (session.prompting) {
458
+ await ctx.sendError(id, -32e3, "a prompt is already running for this session");
459
+ return false;
460
+ }
461
+ session.prompting = true;
433
462
  if (session.abort.signal.aborted) {
434
463
  session.abort = new AbortController();
435
464
  }
@@ -458,6 +487,8 @@ async function handleSessionPromptOp(ctx, id, params) {
458
487
  {
459
488
  sessionId,
460
489
  prompt: p.prompt,
490
+ modeId: session.modeId,
491
+ configOptions: session.configOptions,
461
492
  signal: turnSignal.signal,
462
493
  cwd: session.cwd,
463
494
  ...session.mcpServers ? { mcpServers: session.mcpServers } : {}
@@ -466,16 +497,25 @@ async function handleSessionPromptOp(ctx, id, params) {
466
497
  api
467
498
  );
468
499
  } catch (err) {
500
+ session.prompting = false;
469
501
  session.abort.signal.removeEventListener("abort", onCancel);
502
+ await Promise.all(pendingNotifications);
503
+ if (turnSignal.signal.aborted) {
504
+ await ctx.sendResult(id, { stopReason: "cancelled" });
505
+ return false;
506
+ }
470
507
  const { code, message, data } = errorToJsonRpc(err);
471
508
  await ctx.sendError(id, code, message, data);
472
509
  return false;
473
510
  }
474
511
  await Promise.all(pendingNotifications);
512
+ session.prompting = false;
475
513
  session.abort.signal.removeEventListener("abort", onCancel);
476
514
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
477
- await ctx.persist(session);
478
- await ctx.sendResult(id, { stopReason: result.stopReason });
515
+ if (ctx.sessions.get(sessionId) === session) await ctx.persist(session);
516
+ await ctx.sendResult(id, {
517
+ stopReason: turnSignal.signal.aborted ? "cancelled" : result.stopReason
518
+ });
479
519
  return false;
480
520
  }
481
521
  async function handleSetModeOp(ctx, id, params) {
@@ -491,7 +531,11 @@ async function handleSetModeOp(ctx, id, params) {
491
531
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
492
532
  await ctx.sendNotification({
493
533
  sessionId,
494
- update: { sessionUpdate: "current_mode_update", modeId }
534
+ update: {
535
+ sessionUpdate: "current_mode_update",
536
+ currentModeId: modeId,
537
+ modeId
538
+ }
495
539
  });
496
540
  await ctx.sendResult(id, {});
497
541
  return false;
@@ -502,21 +546,24 @@ async function handleSetConfigOptionOp(ctx, id, params) {
502
546
  const optionId = typeof p.configId === "string" ? p.configId : null;
503
547
  const value = typeof p.value === "string" ? p.value : null;
504
548
  const session = sessionId ? ctx.sessions.get(sessionId) : void 0;
505
- const option = optionId ? ctx.configOptions.find((o) => o.id === optionId) : void 0;
549
+ const options = session?.configOptions ?? structuredClone([...ctx.configOptions]);
550
+ const option = optionId ? options.find((o) => o.id === optionId) : void 0;
506
551
  if (!session || !option || value === null || !option.options.some((o) => o.value === value)) {
507
552
  await ctx.sendError(id, -32602, "invalid sessionId, configId, or value");
508
553
  return false;
509
554
  }
510
555
  option.currentValue = value;
556
+ session.configOptions = options;
511
557
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
512
558
  await ctx.sendNotification({
513
559
  sessionId,
514
560
  update: {
515
561
  sessionUpdate: "config_option_update",
516
- configOptions: [...ctx.configOptions]
562
+ configOptions: options
517
563
  }
518
564
  });
519
- await ctx.sendResult(id, { configOptions: [...ctx.configOptions] });
565
+ await ctx.persist(session);
566
+ await ctx.sendResult(id, { configOptions: options });
520
567
  return false;
521
568
  }
522
569
  async function reportSkippedMcpServers(ctx, sessionId, skipped) {
@@ -558,6 +605,8 @@ var ACPProtocolHandler = class {
558
605
  // session/request_permission). Keyed by our own `srv_N` ids.
559
606
  pendingOut = /* @__PURE__ */ new Map();
560
607
  nextOutId = 1;
608
+ promptRequests = /* @__PURE__ */ new Map();
609
+ persistenceWrites = /* @__PURE__ */ new Map();
561
610
  constructor(opts) {
562
611
  this.transport = opts.transport;
563
612
  this.defaultCwd = opts.defaultCwd;
@@ -583,20 +632,46 @@ var ACPProtocolHandler = class {
583
632
  */
584
633
  request(method, params, timeoutMs = 6e4) {
585
634
  const id = `srv_${this.nextOutId++}`;
586
- return new Promise((resolve4, reject) => {
587
- const timer = setTimeout(() => {
635
+ const sessionId = params?.sessionId;
636
+ const signal = sessionId ? this.sessions.get(sessionId)?.abort.signal : void 0;
637
+ return new Promise((resolve5, reject) => {
638
+ const cleanup = () => {
639
+ clearTimeout(timer);
640
+ signal?.removeEventListener("abort", abort);
588
641
  this.pendingOut.delete(id);
589
- reject(new Error(`${method} timed out after ${timeoutMs}ms`));
642
+ };
643
+ const fail = (error) => {
644
+ cleanup();
645
+ reject(error);
646
+ };
647
+ const abort = () => {
648
+ fail(new Error("client request cancelled"));
649
+ void this.transport.send(toWire({ jsonrpc: "2.0", method: "$/cancel_request", params: { requestId: id } })).catch(() => {
650
+ });
651
+ };
652
+ const timer = setTimeout(() => {
653
+ fail(new Error(`${method} timed out after ${timeoutMs}ms`));
590
654
  }, timeoutMs);
591
- this.pendingOut.set(id, { resolve: resolve4, reject, timer });
655
+ if (signal?.aborted) {
656
+ fail(new Error("client request cancelled"));
657
+ return;
658
+ }
659
+ signal?.addEventListener("abort", abort, { once: true });
660
+ this.pendingOut.set(id, {
661
+ resolve: (value) => {
662
+ cleanup();
663
+ resolve5(value);
664
+ },
665
+ reject: fail,
666
+ timer
667
+ });
592
668
  this.transport.send(toWire({ jsonrpc: "2.0", id, method, params })).catch((e) => {
593
- clearTimeout(timer);
594
- this.pendingOut.delete(id);
595
- reject(e instanceof Error ? e : new Error(String(e)));
669
+ fail(e instanceof Error ? e : new Error(String(e)));
596
670
  });
597
671
  });
598
672
  }
599
673
  maybeResolvePending(m) {
674
+ if (m.result === void 0 && m.error === void 0) return;
600
675
  const id = m.id;
601
676
  if (typeof id !== "string") return;
602
677
  const pending = this.pendingOut.get(id);
@@ -616,6 +691,7 @@ var ACPProtocolHandler = class {
616
691
  if (typeof msg !== "object" || msg === null) return false;
617
692
  const m = msg;
618
693
  if (m.id !== void 0 && (m.result !== void 0 || m.error !== void 0)) {
694
+ this.maybeResolvePending(msg);
619
695
  return false;
620
696
  }
621
697
  if (m.id !== void 0 && typeof m.method === "string") {
@@ -655,6 +731,7 @@ var ACPProtocolHandler = class {
655
731
  store: this.store,
656
732
  replayFor: this.replayFor,
657
733
  seedFor: this.seedFor,
734
+ disposeFor: this.disposeFor,
658
735
  onSessionNew: this.onSessionNew,
659
736
  allocId: () => this.allocId(),
660
737
  persist: (state, history) => this.persist(state, history),
@@ -692,14 +769,23 @@ var ACPProtocolHandler = class {
692
769
  return await this.handleSessionClose(id, params);
693
770
  case "session/delete":
694
771
  return await this.handleSessionDelete(id, params);
695
- case "session/prompt":
696
- return await handleSessionPromptOp(this.sessionContext(), id, params);
772
+ case "session/prompt": {
773
+ const sessionId = params?.sessionId;
774
+ if (typeof sessionId === "string" && !this.sessions.get(sessionId)?.prompting) {
775
+ this.promptRequests.set(id, sessionId);
776
+ }
777
+ try {
778
+ return await handleSessionPromptOp(this.sessionContext(), id, params);
779
+ } finally {
780
+ this.promptRequests.delete(id);
781
+ }
782
+ }
697
783
  case "session/set_mode":
698
784
  return await handleSetModeOp(this.sessionContext(), id, params);
699
785
  case "session/set_config_option":
700
786
  return await handleSetConfigOptionOp(this.sessionContext(), id, params);
701
787
  case "session/list":
702
- return await this.handleSessionList(id);
788
+ return await this.handleSessionList(id, params);
703
789
  case "session/fork":
704
790
  return await handleSessionForkOp(this.sessionContext(), id, params);
705
791
  case "providers/list":
@@ -728,7 +814,12 @@ var ACPProtocolHandler = class {
728
814
  this.initialized = true;
729
815
  await this.sendResult(
730
816
  id,
731
- buildInitializeResult(this.agentName, this.modes, this.configOptions)
817
+ buildInitializeResult(
818
+ this.agentName,
819
+ this.modes,
820
+ this.configOptions,
821
+ this.clientCapabilities
822
+ )
732
823
  );
733
824
  return false;
734
825
  }
@@ -737,29 +828,15 @@ var ACPProtocolHandler = class {
737
828
  return false;
738
829
  }
739
830
  async handleLogout(id, _params) {
740
- await this.sendResult(id, {});
831
+ await this.sendError(
832
+ id,
833
+ -32601,
834
+ "logout is not supported; manage provider credentials with wstack auth"
835
+ );
741
836
  return false;
742
837
  }
743
838
  async handleSessionResume(id, params) {
744
- const p = params ?? {};
745
- const sessionId = typeof p.sessionId === "string" ? p.sessionId : null;
746
- const existing = sessionId ? this.sessions.get(sessionId) : void 0;
747
- if (existing) {
748
- existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
749
- const resumeServers = parseMcpServers(p.mcpServers);
750
- if (resumeServers.length > 0) {
751
- existing.mcpServers = resumeServers;
752
- }
753
- await this.sendResult(id, {
754
- initialMode: {
755
- currentModeId: existing.modeId,
756
- availableModes: this.modes
757
- }
758
- });
759
- return false;
760
- }
761
- await this.sendError(id, -32e3, `session not found: ${sessionId}`);
762
- return false;
839
+ return handleSessionLoadOp(this.sessionContext(), id, params, false);
763
840
  }
764
841
  async handleSessionClose(id, params) {
765
842
  const p = params ?? {};
@@ -782,14 +859,12 @@ var ACPProtocolHandler = class {
782
859
  await this.sendError(id, -32e3, `session not found: ${sessionId}`);
783
860
  return false;
784
861
  }
785
- if (!this.sessions.has(sessionId)) {
786
- await this.sendResult(id, { configOptions: [...this.configOptions] });
787
- return false;
788
- }
789
862
  const session = this.sessions.get(sessionId);
790
- session.abort.abort();
863
+ session?.abort.abort();
791
864
  this.sessions.delete(sessionId);
792
865
  this.disposeSession(sessionId);
866
+ await this.persistenceWrites.get(sessionId);
867
+ await this.store?.delete?.(sessionId);
793
868
  await this.sendResult(id, {});
794
869
  return false;
795
870
  }
@@ -816,12 +891,23 @@ var ACPProtocolHandler = class {
816
891
  await this.sendError(id, -32e3, "MCP message routing not available through ACP");
817
892
  return false;
818
893
  }
819
- async handleSessionList(id) {
820
- const sessions = Array.from(this.sessions.values()).map((s) => {
894
+ async handleSessionList(id, params) {
895
+ const p = params ?? {};
896
+ if (p.cwd != null && (typeof p.cwd !== "string" || !isAbsolute2(p.cwd)) || p.cursor != null) {
897
+ await this.sendError(id, -32602, "invalid cwd or cursor");
898
+ return false;
899
+ }
900
+ const known = new Map(this.sessions);
901
+ for (const entry of await this.store?.list?.() ?? []) {
902
+ if (known.has(entry.id)) continue;
903
+ const saved = await this.store?.load(entry.id);
904
+ if (saved?.cwd) known.set(entry.id, { ...saved, id: entry.id });
905
+ }
906
+ const sessions = Array.from(known.values()).filter((s) => typeof p.cwd !== "string" || s.cwd && resolve2(s.cwd) === resolve2(p.cwd)).map((s) => {
821
907
  const out = {
822
908
  sessionId: s.id,
823
909
  cwd: s.cwd,
824
- updatedAt: s.updatedAt
910
+ updatedAt: s.updatedAt ?? ""
825
911
  };
826
912
  if (s.title !== void 0) out.title = s.title;
827
913
  return out;
@@ -844,6 +930,11 @@ var ACPProtocolHandler = class {
844
930
  return false;
845
931
  }
846
932
  case "$/cancel_request": {
933
+ const requestId = params?.requestId;
934
+ if (typeof requestId === "string" || typeof requestId === "number") {
935
+ const sessionId = this.promptRequests.get(requestId);
936
+ if (sessionId) this.sessions.get(sessionId)?.abort.abort();
937
+ }
847
938
  return false;
848
939
  }
849
940
  case "exit":
@@ -864,10 +955,18 @@ var ACPProtocolHandler = class {
864
955
  }
865
956
  async persist(state, history = void 0) {
866
957
  if (!this.store) return;
867
- try {
868
- await this.store.save(state, history ?? this.replayFor?.(state.id));
869
- } catch {
870
- }
958
+ const store = this.store;
959
+ const replay = history ?? this.replayFor?.(state.id);
960
+ const previous = this.persistenceWrites.get(state.id) ?? Promise.resolve();
961
+ const write = previous.then(() => store.save(state, replay)).then(
962
+ () => {
963
+ },
964
+ () => {
965
+ }
966
+ );
967
+ this.persistenceWrites.set(state.id, write);
968
+ await write;
969
+ if (this.persistenceWrites.get(state.id) === write) this.persistenceWrites.delete(state.id);
871
970
  }
872
971
  async sendError(id, code, message, data) {
873
972
  const error = { code, message };
@@ -927,9 +1026,9 @@ var StdioTransport = class {
927
1026
  }
928
1027
  send(msg) {
929
1028
  if (this.closed) return Promise.resolve();
930
- return new Promise((resolve4) => {
1029
+ return new Promise((resolve5) => {
931
1030
  const line = JSON.stringify(msg) + "\n";
932
- this.stdout.write(line, "utf8", () => resolve4());
1031
+ this.stdout.write(line, "utf8", () => resolve5());
933
1032
  });
934
1033
  }
935
1034
  sendRaw(chunk) {
@@ -942,8 +1041,8 @@ var StdioTransport = class {
942
1041
  return Promise.resolve(queued.message);
943
1042
  }
944
1043
  if (this.closed) return Promise.resolve(null);
945
- return new Promise((resolve4) => {
946
- this.resolveRead = resolve4;
1044
+ return new Promise((resolve5) => {
1045
+ this.resolveRead = resolve5;
947
1046
  });
948
1047
  }
949
1048
  onMessage(handler) {
@@ -1012,9 +1111,9 @@ var StdioTransport = class {
1012
1111
  }
1013
1112
  dispatch(msg, chars = JSON.stringify(msg).length) {
1014
1113
  if (this.resolveRead) {
1015
- const resolve4 = this.resolveRead;
1114
+ const resolve5 = this.resolveRead;
1016
1115
  this.resolveRead = null;
1017
- resolve4(msg);
1116
+ resolve5(msg);
1018
1117
  } else {
1019
1118
  let claimed = false;
1020
1119
  for (const handler of this.claimHandlers) {
@@ -1086,7 +1185,7 @@ var ClientTransport = class {
1086
1185
  import("@wrongstack/core/utils"),
1087
1186
  import("node:os")
1088
1187
  ]);
1089
- return new Promise((resolve4, reject) => {
1188
+ return new Promise((resolve5, reject) => {
1090
1189
  const timeout = setTimeout(() => {
1091
1190
  const child2 = this.child;
1092
1191
  this.child = null;
@@ -1139,7 +1238,7 @@ var ClientTransport = class {
1139
1238
  if (settled) return;
1140
1239
  settled = true;
1141
1240
  clearTimeout(timeout);
1142
- resolve4();
1241
+ resolve5();
1143
1242
  });
1144
1243
  return;
1145
1244
  }
@@ -1150,7 +1249,7 @@ var ClientTransport = class {
1150
1249
  child.stderr.on("data", (c) => this.onChildError(c));
1151
1250
  child.on("close", (code) => this.onChildClose(code));
1152
1251
  clearTimeout(timeout);
1153
- resolve4();
1252
+ resolve5();
1154
1253
  };
1155
1254
  const waitForMarker = (chunk) => {
1156
1255
  this.buffer += chunk;
@@ -1169,11 +1268,11 @@ var ClientTransport = class {
1169
1268
  }
1170
1269
  send(msg) {
1171
1270
  if (!this.child) return Promise.reject(new Error("ClientTransport not started"));
1172
- return new Promise((resolve4, reject) => {
1271
+ return new Promise((resolve5, reject) => {
1173
1272
  const line = JSON.stringify(msg) + "\n";
1174
1273
  this.child?.stdin.write(line, "utf8", (err) => {
1175
1274
  if (err) reject(err);
1176
- else resolve4();
1275
+ else resolve5();
1177
1276
  });
1178
1277
  });
1179
1278
  }
@@ -1184,8 +1283,8 @@ var ClientTransport = class {
1184
1283
  return Promise.resolve(queued.message);
1185
1284
  }
1186
1285
  if (this.closed) return Promise.resolve(null);
1187
- return new Promise((resolve4) => {
1188
- this.resolveRead = resolve4;
1286
+ return new Promise((resolve5) => {
1287
+ this.resolveRead = resolve5;
1189
1288
  });
1190
1289
  }
1191
1290
  onMessage(handler) {
@@ -1256,6 +1355,7 @@ var ClientTransport = class {
1256
1355
  this.messageQueue.length = 0;
1257
1356
  this.queuedChars = 0;
1258
1357
  this.handlers.clear();
1358
+ this.claimHandlers.clear();
1259
1359
  if (code !== 0 && code !== null) {
1260
1360
  writeErr(`[acp-child exited with code ${code}]
1261
1361
  `);
@@ -1263,20 +1363,31 @@ var ClientTransport = class {
1263
1363
  }
1264
1364
  dispatch(msg, chars = JSON.stringify(msg).length) {
1265
1365
  if (this.resolveRead) {
1266
- const resolve4 = this.resolveRead;
1366
+ const resolve5 = this.resolveRead;
1267
1367
  this.resolveRead = null;
1268
- resolve4(msg);
1269
- } else if (this.handlers.size === 0) {
1270
- if (this.messageQueue.length >= this.maxQueuedMessages || this.queuedChars + chars > this.maxQueuedChars) {
1271
- writeErr(
1272
- `[acp-child message queue exceeds ${this.maxQueuedMessages} entries or ${this.maxQueuedChars} characters]
1368
+ resolve5(msg);
1369
+ } else {
1370
+ let claimed = false;
1371
+ for (const handler of this.claimHandlers) {
1372
+ try {
1373
+ if (handler(msg)) claimed = true;
1374
+ } catch (err) {
1375
+ writeErr(`[acp-child handler error] ${err}
1376
+ `);
1377
+ }
1378
+ }
1379
+ if (!claimed && this.handlers.size === 0) {
1380
+ if (this.messageQueue.length >= this.maxQueuedMessages || this.queuedChars + chars > this.maxQueuedChars) {
1381
+ writeErr(
1382
+ `[acp-child message queue exceeds ${this.maxQueuedMessages} entries or ${this.maxQueuedChars} characters]
1273
1383
  `
1274
- );
1275
- this.stop();
1276
- return;
1384
+ );
1385
+ this.stop();
1386
+ return;
1387
+ }
1388
+ this.messageQueue.push({ message: msg, chars });
1389
+ this.queuedChars += chars;
1277
1390
  }
1278
- this.messageQueue.push({ message: msg, chars });
1279
- this.queuedChars += chars;
1280
1391
  }
1281
1392
  for (const handler of this.handlers) {
1282
1393
  try {
@@ -1410,7 +1521,7 @@ function toolToPriority(tool) {
1410
1521
  import { createServer } from "node:http";
1411
1522
  import { isIP } from "node:net";
1412
1523
  import { fileURLToPath } from "node:url";
1413
- import { expandIPv6, writeErr as writeErr2 } from "@wrongstack/core/utils";
1524
+ import { expandIPv6, isStandaloneBinary, writeErr as writeErr2 } from "@wrongstack/core/utils";
1414
1525
  import { timingSafeTokenEqual } from "@wrongstack/primitives";
1415
1526
  var LISTEN_RETRY_LIMIT = 5;
1416
1527
  var LISTEN_RETRY_BASE_MS = 25;
@@ -1456,15 +1567,30 @@ var WrongStackACPServer = class {
1456
1567
  this.transport.sendStartupMarker();
1457
1568
  }
1458
1569
  this.running = true;
1570
+ const prompts = /* @__PURE__ */ new Set();
1459
1571
  try {
1460
1572
  while (this.running) {
1461
1573
  const msg = await this.transport.read();
1462
1574
  if (!msg) break;
1575
+ if (msg.method === "session/prompt") {
1576
+ const task = this.handler.handleMessage(msg).then(
1577
+ () => {
1578
+ },
1579
+ (err) => {
1580
+ writeErr2(`[wstack-acp] prompt dispatch failed: ${String(err)}
1581
+ `);
1582
+ }
1583
+ );
1584
+ prompts.add(task);
1585
+ void task.finally(() => prompts.delete(task));
1586
+ continue;
1587
+ }
1463
1588
  const terminal = await this.handler.handleMessage(msg);
1464
1589
  if (terminal) break;
1465
1590
  }
1466
1591
  } finally {
1467
1592
  this.handler.close();
1593
+ await Promise.allSettled(prompts);
1468
1594
  this.transport.close();
1469
1595
  }
1470
1596
  }
@@ -1598,10 +1724,10 @@ var WrongStackACPServer = class {
1598
1724
  listenWithRetry(port, host, attempt = 0) {
1599
1725
  const server = this.httpServer;
1600
1726
  if (!server) return Promise.resolve();
1601
- return new Promise((resolve4, reject) => {
1727
+ return new Promise((resolve5, reject) => {
1602
1728
  const onListening = () => {
1603
1729
  server.removeListener("error", onError);
1604
- resolve4();
1730
+ resolve5();
1605
1731
  };
1606
1732
  const onError = (err) => {
1607
1733
  server.removeListener("listening", onListening);
@@ -1609,7 +1735,7 @@ var WrongStackACPServer = class {
1609
1735
  if (port === 0 && transient && attempt < LISTEN_RETRY_LIMIT) {
1610
1736
  const timer = setTimeout(
1611
1737
  () => {
1612
- this.listenWithRetry(port, host, attempt + 1).then(resolve4, reject);
1738
+ this.listenWithRetry(port, host, attempt + 1).then(resolve5, reject);
1613
1739
  },
1614
1740
  LISTEN_RETRY_BASE_MS * (attempt + 1)
1615
1741
  );
@@ -1637,8 +1763,8 @@ var WrongStackACPServer = class {
1637
1763
  if (!server) return;
1638
1764
  for (const socket of this.httpSockets) socket.destroy();
1639
1765
  this.httpSockets.clear();
1640
- await new Promise((resolve4) => {
1641
- server.close(() => resolve4());
1766
+ await new Promise((resolve5) => {
1767
+ server.close(() => resolve5());
1642
1768
  });
1643
1769
  }
1644
1770
  };
@@ -1669,7 +1795,7 @@ async function main() {
1669
1795
  const server = new WrongStackACPServer();
1670
1796
  await server.start();
1671
1797
  }
1672
- var isEntrypoint = process.argv[1] !== void 0 && fileURLToPath(import.meta.url) === process.argv[1];
1798
+ var isEntrypoint = process.argv[1] !== void 0 && !isStandaloneBinary() && fileURLToPath(import.meta.url) === process.argv[1];
1673
1799
  if (isEntrypoint) {
1674
1800
  main().catch((err) => {
1675
1801
  writeErr2(`[wstack-acp fatal] ${err}
@@ -1678,6 +1804,55 @@ if (isEntrypoint) {
1678
1804
  });
1679
1805
  }
1680
1806
 
1807
+ // src/client/acp-late-session.ts
1808
+ var LATE_CANCEL_SEND_TIMEOUT_MS = 1e4;
1809
+ function cancelLateAcpSession(transport, createPromise) {
1810
+ createPromise.then(async (lateId) => {
1811
+ try {
1812
+ await new Promise((resolve5, reject) => {
1813
+ const timer = setTimeout(() => {
1814
+ reject(new Error("late session/cancel send timed out"));
1815
+ }, LATE_CANCEL_SEND_TIMEOUT_MS);
1816
+ Promise.resolve(
1817
+ transport.send({
1818
+ jsonrpc: "2.0",
1819
+ method: "session/cancel",
1820
+ params: { sessionId: lateId }
1821
+ })
1822
+ ).then(
1823
+ () => {
1824
+ clearTimeout(timer);
1825
+ resolve5();
1826
+ },
1827
+ (sendErr) => {
1828
+ clearTimeout(timer);
1829
+ reject(sendErr instanceof Error ? sendErr : new Error(String(sendErr)));
1830
+ }
1831
+ );
1832
+ });
1833
+ } catch (err) {
1834
+ const message = err instanceof Error ? err.message : String(err);
1835
+ console.warn(
1836
+ JSON.stringify({
1837
+ level: "warn",
1838
+ event: "acp_session.late_cancel_failed",
1839
+ sessionId: lateId,
1840
+ message
1841
+ })
1842
+ );
1843
+ }
1844
+ }).catch((reason) => {
1845
+ const message = reason instanceof Error ? reason.message : String(reason);
1846
+ console.warn(
1847
+ JSON.stringify({
1848
+ level: "warn",
1849
+ event: "acp_session.late_cancel_failed",
1850
+ reason: message
1851
+ })
1852
+ );
1853
+ });
1854
+ }
1855
+
1681
1856
  // src/client/acp-message-routing.ts
1682
1857
  function isBestEffortAckMethod(method) {
1683
1858
  return method === "mcp/connect" || method === "mcp/message" || method === "mcp/disconnect" || method === "elicitation/create" || method === "elicitation/complete";
@@ -1725,6 +1900,13 @@ var FileServer = class {
1725
1900
  }
1726
1901
  /** Read a text file. Returns the content as a string. */
1727
1902
  async readTextFile(params) {
1903
+ if (params.line != null && (!Number.isSafeInteger(params.line) || params.line < 1) || params.limit != null && (!Number.isSafeInteger(params.limit) || params.limit < 0)) {
1904
+ throw new FsError(
1905
+ "INVALID_PATH",
1906
+ params.path,
1907
+ "line must be a positive integer and limit a non-negative integer"
1908
+ );
1909
+ }
1728
1910
  const safe = await this.resolveInside(params.path);
1729
1911
  const controller = new AbortController();
1730
1912
  const timer = setTimeout(() => controller.abort(), this.timeoutMs);
@@ -1743,7 +1925,12 @@ var FileServer = class {
1743
1925
  encoding: "utf8",
1744
1926
  signal: controller.signal
1745
1927
  });
1746
- return { content };
1928
+ if (params.line == null && params.limit == null) return { content };
1929
+ const lines = content.match(/[^\n]*\n|[^\n]+$/g) ?? [];
1930
+ const start = (params.line ?? 1) - 1;
1931
+ return {
1932
+ content: lines.slice(start, params.limit == null ? void 0 : start + params.limit).join("")
1933
+ };
1747
1934
  } catch (err) {
1748
1935
  if (err instanceof FsError) throw err;
1749
1936
  if (controller.signal.aborted) {
@@ -1848,7 +2035,8 @@ var FileServer = class {
1848
2035
  throw mapFsError(err, resolvedPath);
1849
2036
  }
1850
2037
  const normReal = isWindows ? real.toLowerCase() : real;
1851
- if (normReal === normRealRoot || normReal.startsWith(normRealRoot + path2.sep)) return;
2038
+ const realRootWithSep = normRealRoot.endsWith(path2.sep) ? normRealRoot : normRealRoot + path2.sep;
2039
+ if (normReal === normRealRoot || normReal.startsWith(realRootWithSep)) return;
1852
2040
  throw new FsError(
1853
2041
  "OUTSIDE_ROOT",
1854
2042
  resolvedPath,
@@ -1897,9 +2085,12 @@ async function handleAcpPermissionRequest(msg, permissionPolicy, sender, callbac
1897
2085
  );
1898
2086
  await sender.sendResult(id, { outcome });
1899
2087
  } catch (err) {
2088
+ if (isAbortLikeError(err)) {
2089
+ await sender.sendResult(id, { outcome: { outcome: "cancelled" } });
2090
+ return;
2091
+ }
1900
2092
  const message = err instanceof Error ? err.message : String(err);
1901
- const code = isAbortLikeError(err) ? -32800 : -32603;
1902
- await sender.sendErrorResponse(id, code, `permission policy failed: ${message}`);
2093
+ await sender.sendErrorResponse(id, -32603, `permission policy failed: ${message}`);
1903
2094
  }
1904
2095
  }
1905
2096
  async function handleAcpFsRequest(msg, fileServer, permissionPolicy, sender, callbackOptions = {}) {
@@ -1935,7 +2126,9 @@ async function handleAcpFsRequest(msg, fileServer, permissionPolicy, sender, cal
1935
2126
  if (msg.method === "fs/read_text_file") {
1936
2127
  const result = await fileServer.readTextFile({
1937
2128
  sessionId: params.sessionId ?? "",
1938
- path: params.path
2129
+ path: params.path,
2130
+ line: params.line,
2131
+ limit: params.limit
1939
2132
  });
1940
2133
  await sender.sendResult(id, result);
1941
2134
  } else {
@@ -2185,7 +2378,7 @@ async function executeLoadSession(ctx, sessionId, mcpServers, cwd) {
2185
2378
  "agent does not support session/load (loadSession capability not advertised)"
2186
2379
  );
2187
2380
  }
2188
- if (ctx.sessionId) {
2381
+ if (ctx.sessionId && ctx.sessionId !== sessionId) {
2189
2382
  await ctx.closeSession();
2190
2383
  }
2191
2384
  ctx.resetScratch();
@@ -2209,7 +2402,7 @@ async function executeResumeSession(ctx, sessionId, mcpServers, cwd) {
2209
2402
  "agent does not support session/resume (sessionCapabilities.resume not advertised)"
2210
2403
  );
2211
2404
  }
2212
- if (ctx.sessionId) {
2405
+ if (ctx.sessionId && ctx.sessionId !== sessionId) {
2213
2406
  await ctx.closeSession();
2214
2407
  }
2215
2408
  const servers = filterMcpServers(ctx.agentCapabilities, mcpServers ?? ctx.opts.mcpServers);
@@ -2387,6 +2580,7 @@ function handleAcpSessionUpdate(msg, scratch, emitProgress) {
2387
2580
  }
2388
2581
  return;
2389
2582
  }
2583
+ case "agent_thought_chunk":
2390
2584
  case "thought_chunk": {
2391
2585
  const text = extractText(u.content);
2392
2586
  if (text) {
@@ -2536,7 +2730,9 @@ var TerminalServer = class {
2536
2730
  this.instanceId = `term_srv_${randomBytes2(4).toString("hex")}`;
2537
2731
  this.maxTerminals = this.clampFiniteInt(opts.maxTerminals, 32);
2538
2732
  this.abortSignal = opts.signal;
2539
- if (opts.signal) {
2733
+ if (opts.signal?.aborted) {
2734
+ this.dispose();
2735
+ } else if (opts.signal) {
2540
2736
  opts.signal.addEventListener("abort", this.abortHandler, { once: true });
2541
2737
  }
2542
2738
  }
@@ -2582,7 +2778,7 @@ var TerminalServer = class {
2582
2778
  truncated: false,
2583
2779
  exitStatus: void 0,
2584
2780
  timeoutHandle: null,
2585
- exitPromise: new Promise((resolve4) => {
2781
+ exitPromise: new Promise((resolve5) => {
2586
2782
  proc.on("close", (code, signalName) => {
2587
2783
  if (state.timeoutHandle) {
2588
2784
  clearTimeout(state.timeoutHandle);
@@ -2593,7 +2789,7 @@ var TerminalServer = class {
2593
2789
  signal: typeof signalName === "string" ? signalName : null
2594
2790
  };
2595
2791
  state.exitStatus = exitStatus;
2596
- resolve4(exitStatus);
2792
+ resolve5(exitStatus);
2597
2793
  });
2598
2794
  proc.on("error", (err) => {
2599
2795
  if (state.timeoutHandle) {
@@ -2612,7 +2808,7 @@ var TerminalServer = class {
2612
2808
  }
2613
2809
  state.outputChunks.push(errorOutput);
2614
2810
  state.retainedBytes = errorOutput.length;
2615
- resolve4(exitStatus);
2811
+ resolve5(exitStatus);
2616
2812
  });
2617
2813
  })
2618
2814
  };
@@ -2919,7 +3115,7 @@ var WebSocketClientTransport = class {
2919
3115
  );
2920
3116
  }
2921
3117
  const timeoutMs = this.opts.handshakeTimeoutMs ?? 3e4;
2922
- return new Promise((resolve4, reject) => {
3118
+ return new Promise((resolve5, reject) => {
2923
3119
  const ws = new WS(this.opts.url, this.opts.protocols);
2924
3120
  this.ws = ws;
2925
3121
  const timer = setTimeout(() => {
@@ -2935,7 +3131,7 @@ var WebSocketClientTransport = class {
2935
3131
  }
2936
3132
  pending.reject(new Error(`WebSocket failed to open within ${timeoutMs}ms`));
2937
3133
  }, timeoutMs);
2938
- this.pendingStart = { resolve: resolve4, reject, timer };
3134
+ this.pendingStart = { resolve: resolve5, reject, timer };
2939
3135
  ws.addEventListener("open", () => {
2940
3136
  const pending = this.pendingStart;
2941
3137
  if (pending === null) return;
@@ -3050,7 +3246,6 @@ function finitePositiveLimit(value, fallback) {
3050
3246
  }
3051
3247
 
3052
3248
  // src/client/acp-session.ts
3053
- var LATE_CANCEL_SEND_TIMEOUT_MS = 1e4;
3054
3249
  var ACPSession = class _ACPSession {
3055
3250
  transport;
3056
3251
  fileServer;
@@ -3220,7 +3415,7 @@ var ACPSession = class _ACPSession {
3220
3415
  throw new ACPSessionError("protocol_error", "initialize returned no protocolVersion");
3221
3416
  }
3222
3417
  const r = result;
3223
- if (r.protocolVersion > ACP_PROTOCOL_VERSION) {
3418
+ if (r.protocolVersion !== ACP_PROTOCOL_VERSION) {
3224
3419
  throw new ACPSessionError(
3225
3420
  "unsupported_capability",
3226
3421
  `agent requires protocolVersion=${r.protocolVersion}, client supports up to ${ACP_PROTOCOL_VERSION}`
@@ -3256,6 +3451,12 @@ var ACPSession = class _ACPSession {
3256
3451
  `auth method "${methodId}" not in advertised methods: ${this.authMethods.map((m) => m.id).join(", ")}`
3257
3452
  );
3258
3453
  }
3454
+ if (this.authMethods.find((m) => m.id === methodId)?.type === "terminal") {
3455
+ throw new ACPSessionError(
3456
+ "auth_failed",
3457
+ "Terminal authentication requires interactive login followed by reconnect; it cannot use authenticate"
3458
+ );
3459
+ }
3259
3460
  const id = this.allocId();
3260
3461
  const result = await this.sendRequest(id, "authenticate", { methodId });
3261
3462
  if (isJsonRpcError(result)) {
@@ -3351,6 +3552,16 @@ var ACPSession = class _ACPSession {
3351
3552
  if (signal.aborted) {
3352
3553
  return emptyRunResult("cancelled");
3353
3554
  }
3555
+ const caps = this.agentCapabilities.promptCapabilities;
3556
+ for (const block of blocks) {
3557
+ const supported = block.type === "image" ? caps?.image : block.type === "audio" ? caps?.audio : block.type === "resource" ? caps?.embeddedContext : true;
3558
+ if (supported !== true) {
3559
+ throw new ACPSessionError(
3560
+ "unsupported_capability",
3561
+ `agent does not support ${block.type} prompt content`
3562
+ );
3563
+ }
3564
+ }
3354
3565
  let cancelled = false;
3355
3566
  this.promptCallbackAbort = new AbortController();
3356
3567
  const onAbort = () => {
@@ -3485,50 +3696,7 @@ var ACPSession = class _ACPSession {
3485
3696
  * being silently swallowed.
3486
3697
  */
3487
3698
  cancelLateSession(createPromise) {
3488
- createPromise.then(async (lateId) => {
3489
- try {
3490
- await new Promise((resolve4, reject) => {
3491
- const timer = setTimeout(() => {
3492
- reject(new Error("late session/cancel send timed out"));
3493
- }, LATE_CANCEL_SEND_TIMEOUT_MS);
3494
- Promise.resolve(
3495
- this.transport.send({
3496
- jsonrpc: "2.0",
3497
- method: "session/cancel",
3498
- params: { sessionId: lateId }
3499
- })
3500
- ).then(
3501
- () => {
3502
- clearTimeout(timer);
3503
- resolve4();
3504
- },
3505
- (sendErr) => {
3506
- clearTimeout(timer);
3507
- reject(sendErr instanceof Error ? sendErr : new Error(String(sendErr)));
3508
- }
3509
- );
3510
- });
3511
- } catch (err) {
3512
- const message = err instanceof Error ? err.message : String(err);
3513
- console.warn(
3514
- JSON.stringify({
3515
- level: "warn",
3516
- event: "acp_session.late_cancel_failed",
3517
- sessionId: lateId,
3518
- message
3519
- })
3520
- );
3521
- }
3522
- }).catch((reason) => {
3523
- const message = reason instanceof Error ? reason.message : String(reason);
3524
- console.warn(
3525
- JSON.stringify({
3526
- level: "warn",
3527
- event: "acp_session.late_cancel_failed",
3528
- reason: message
3529
- })
3530
- );
3531
- });
3699
+ cancelLateAcpSession(this.transport, createPromise);
3532
3700
  }
3533
3701
  async closeSession() {
3534
3702
  if (!this.sessionId) return;
@@ -3575,10 +3743,16 @@ var ACPSession = class _ACPSession {
3575
3743
  // Wire layer
3576
3744
  // ────────────────────────────────────────────────────────────────────
3577
3745
  allocId() {
3578
- return this.nextId++;
3746
+ let candidate = Number.isSafeInteger(this.nextId) && this.nextId >= 1 ? this.nextId : 1;
3747
+ for (let probe = 0; probe <= this.pending.size; probe++) {
3748
+ this.nextId = candidate >= Number.MAX_SAFE_INTEGER ? 1 : candidate + 1;
3749
+ if (!this.pending.has(candidate)) return candidate;
3750
+ candidate = this.nextId;
3751
+ }
3752
+ throw new ACPSessionError("protocol_error", "no JSON-RPC request id is available");
3579
3753
  }
3580
3754
  async sendRequest(id, method, params, timeoutMs) {
3581
- return new Promise((resolve4, reject) => {
3755
+ return new Promise((resolve5, reject) => {
3582
3756
  const effectiveTimeout = timeoutMs ?? this.timeoutMs;
3583
3757
  const handle = setTimeout(() => {
3584
3758
  this.pending.delete(id);
@@ -3588,7 +3762,7 @@ var ACPSession = class _ACPSession {
3588
3762
  }, effectiveTimeout);
3589
3763
  this.pending.set(id, {
3590
3764
  method,
3591
- resolve: resolve4,
3765
+ resolve: resolve5,
3592
3766
  reject,
3593
3767
  timeoutMs: effectiveTimeout,
3594
3768
  timeoutHandle: handle
@@ -3643,7 +3817,7 @@ var ACPSession = class _ACPSession {
3643
3817
  }
3644
3818
  const terminal = this.authMethods.find((m) => m.type === "terminal");
3645
3819
  const setupArgs = terminal?.args?.length ? terminal.args.join(" ") : void 0;
3646
- const setup = setupArgs !== void 0 ? `${this.opts.command} ${setupArgs}` : `${this.opts.command}${this.opts.args?.length ? ` ${this.opts.args.join(" ")}` : ""}`;
3820
+ const setup = setupArgs !== void 0 ? `${this.opts.command}${this.opts.args?.length ? ` ${this.opts.args.join(" ")}` : ""} ${setupArgs}` : `${this.opts.command}${this.opts.args?.length ? ` ${this.opts.args.join(" ")}` : ""}`;
3647
3821
  throw new ACPSessionError(
3648
3822
  "auth_failed",
3649
3823
  `This agent requires a terminal login before ACP can start. Run \`${setup}\` (or the CLI's /login), then retry.`
@@ -3725,7 +3899,7 @@ var ACPSession = class _ACPSession {
3725
3899
  }
3726
3900
  if (isBestEffortAckMethod(msg.method)) {
3727
3901
  if (msg.id !== void 0) {
3728
- this.sendResult(msg.id, {}).catch(() => {
3902
+ this.sendErrorResponse(msg.id, -32601, `Unsupported method: ${msg.method}`).catch(() => {
3729
3903
  });
3730
3904
  }
3731
3905
  return;
@@ -3734,6 +3908,13 @@ var ACPSession = class _ACPSession {
3734
3908
  return;
3735
3909
  }
3736
3910
  if (msg.method) {
3911
+ if (msg.id !== void 0) {
3912
+ void this.sendErrorResponse(msg.id, -32601, `Unsupported method: ${msg.method}`).catch(
3913
+ () => {
3914
+ }
3915
+ );
3916
+ return;
3917
+ }
3737
3918
  console.warn(
3738
3919
  JSON.stringify({
3739
3920
  level: "warn",
@@ -3807,12 +3988,12 @@ var ToolTranslator = class {
3807
3988
  id: callId,
3808
3989
  params: { name, arguments: args }
3809
3990
  });
3810
- return new Promise((resolve4, reject) => {
3991
+ return new Promise((resolve5, reject) => {
3811
3992
  const timeout = setTimeout(() => {
3812
3993
  this.pending.delete(callId);
3813
3994
  reject(new Error(`Tool call ${name} timed out after ${this.opts.totalTimeoutMs}ms`));
3814
3995
  }, this.opts.totalTimeoutMs);
3815
- this.pending.set(callId, { resolve: resolve4, reject, timeout });
3996
+ this.pending.set(callId, { resolve: resolve5, reject, timeout });
3816
3997
  });
3817
3998
  }
3818
3999
  cancelAll() {
@@ -4635,7 +4816,7 @@ async function probeWithBound(items, worker, limit) {
4635
4816
  }
4636
4817
  async function defaultProbe(desc, timeoutMs, spawnProcess = spawn2, platform = process.platform) {
4637
4818
  const start = Date.now();
4638
- return new Promise((resolve4) => {
4819
+ return new Promise((resolve5) => {
4639
4820
  let settled = false;
4640
4821
  let stdout = "";
4641
4822
  let stderr = "";
@@ -4648,7 +4829,7 @@ async function defaultProbe(desc, timeoutMs, spawnProcess = spawn2, platform = p
4648
4829
  }
4649
4830
  } catch {
4650
4831
  }
4651
- resolve4(result);
4832
+ resolve5(result);
4652
4833
  };
4653
4834
  let child;
4654
4835
  try {