@wrongstack/acp 1.0.19 → 1.0.26

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/agent.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;
@@ -177,7 +178,7 @@ function createRunTurnApi(sessionId, clientCapabilities, request, sendSessionUpd
177
178
  ...sendSessionUpdate ? { sendSessionUpdate } : {}
178
179
  };
179
180
  }
180
- function buildInitializeResult(agentName, modes, configOptions) {
181
+ function buildInitializeResult(agentName, modes, configOptions, clientCapabilities = {}) {
181
182
  return {
182
183
  protocolVersion: ACP_PROTOCOL_VERSION,
183
184
  agentCapabilities: {
@@ -204,9 +205,6 @@ function buildInitializeResult(agentName, modes, configOptions) {
204
205
  delete: {},
205
206
  resume: {},
206
207
  fork: {}
207
- },
208
- auth: {
209
- logout: {}
210
208
  }
211
209
  },
212
210
  agentInfo: {
@@ -214,7 +212,7 @@ function buildInitializeResult(agentName, modes, configOptions) {
214
212
  title: "WrongStack",
215
213
  version: WRONGSTACK_VERSION
216
214
  },
217
- authMethods: WRONGSTACK_AUTH_METHODS,
215
+ authMethods: clientCapabilities.auth?.terminal === true ? WRONGSTACK_AUTH_METHODS : [],
218
216
  modes,
219
217
  configOptions
220
218
  };
@@ -244,7 +242,8 @@ async function handleSessionNewOp(ctx, id, params) {
244
242
  id: sessionId,
245
243
  cwd,
246
244
  abort: new AbortController(),
247
- modeId: DEFAULT_MODE_ID,
245
+ modeId: ctx.modes[0]?.id ?? DEFAULT_MODE_ID,
246
+ configOptions: structuredClone([...ctx.configOptions]),
248
247
  createdAt: now,
249
248
  updatedAt: now,
250
249
  ...mcpServers.length > 0 ? { mcpServers } : {}
@@ -256,6 +255,7 @@ async function handleSessionNewOp(ctx, id, params) {
256
255
  sessionId,
257
256
  update: {
258
257
  sessionUpdate: "current_mode_update",
258
+ currentModeId: ctx.modes[0]?.id ?? DEFAULT_MODE_ID,
259
259
  modeId: ctx.modes[0]?.id ?? DEFAULT_MODE_ID
260
260
  }
261
261
  });
@@ -271,12 +271,12 @@ async function handleSessionNewOp(ctx, id, params) {
271
271
  await reportSkippedMcpServers(ctx, sessionId, skipped);
272
272
  await ctx.sendResult(id, {
273
273
  sessionId,
274
- modes: ctx.modes,
275
- configOptions: ctx.configOptions
274
+ modes: { currentModeId: state.modeId, availableModes: ctx.modes },
275
+ configOptions: state.configOptions
276
276
  });
277
277
  return false;
278
278
  }
279
- async function handleSessionLoadOp(ctx, id, params) {
279
+ async function handleSessionLoadOp(ctx, id, params, replayHistory = true) {
280
280
  const p = params ?? {};
281
281
  const sessionId = typeof p.sessionId === "string" ? p.sessionId : null;
282
282
  const loadCwd = typeof p.cwd === "string" ? p.cwd : void 0;
@@ -301,6 +301,7 @@ async function handleSessionLoadOp(ctx, id, params) {
301
301
  cwd: restoredCwd,
302
302
  abort: new AbortController(),
303
303
  modeId: persisted.modeId ?? DEFAULT_MODE_ID,
304
+ configOptions: structuredClone(persisted.configOptions ?? [...ctx.configOptions]),
304
305
  createdAt: persisted.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
305
306
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
306
307
  ...persisted.title !== void 0 ? { title: persisted.title } : {},
@@ -308,51 +309,69 @@ async function handleSessionLoadOp(ctx, id, params) {
308
309
  };
309
310
  ctx.sessions.set(sessionId, restored);
310
311
  ctx.seedFor?.(sessionId, persisted.history ?? []);
311
- for (const update of persisted.history ?? []) {
312
+ for (const update of replayHistory ? persisted.history ?? [] : []) {
312
313
  await ctx.sendNotification({ sessionId, update });
313
314
  }
314
- await ctx.sendNotification({
315
- sessionId,
316
- update: { sessionUpdate: "current_mode_update", modeId: restored.modeId }
317
- });
315
+ if (replayHistory)
316
+ await ctx.sendNotification({
317
+ sessionId,
318
+ update: {
319
+ sessionUpdate: "current_mode_update",
320
+ currentModeId: restored.modeId,
321
+ modeId: restored.modeId
322
+ }
323
+ });
318
324
  await reportSkippedMcpServers(ctx, sessionId, loadSkipped);
319
325
  await ctx.sendResult(id, {
320
- initialMode: { currentModeId: restored.modeId, availableModes: ctx.modes }
326
+ modes: { currentModeId: restored.modeId, availableModes: ctx.modes },
327
+ configOptions: restored.configOptions
321
328
  });
322
329
  return false;
323
330
  }
324
331
  }
325
332
  if (existing) {
333
+ if (existing.prompting) {
334
+ await ctx.sendError(id, -32e3, "cannot reload a session while a prompt is running");
335
+ return false;
336
+ }
326
337
  existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
327
- if (loadMcpServers.length > 0) {
338
+ const replay = ctx.replayFor?.(sessionId);
339
+ if (Array.isArray(p.mcpServers)) {
340
+ if (JSON.stringify(existing.mcpServers ?? []) !== JSON.stringify(loadMcpServers)) {
341
+ ctx.disposeFor?.(sessionId);
342
+ ctx.seedFor?.(sessionId, replay ?? []);
343
+ }
328
344
  existing.mcpServers = loadMcpServers;
329
345
  }
330
- const replay = ctx.replayFor?.(sessionId);
331
- if (replay) {
346
+ if (replayHistory && replay) {
332
347
  for (const update of replay) {
333
348
  await ctx.sendNotification({ sessionId, update });
334
349
  }
335
350
  }
336
- await ctx.sendNotification({
337
- sessionId,
338
- update: {
339
- sessionUpdate: "session_info_update",
340
- updatedAt: existing.updatedAt
341
- }
342
- });
343
- await ctx.sendNotification({
344
- sessionId,
345
- update: {
346
- sessionUpdate: "current_mode_update",
347
- modeId: existing.modeId
348
- }
349
- });
351
+ if (replayHistory)
352
+ await ctx.sendNotification({
353
+ sessionId,
354
+ update: {
355
+ sessionUpdate: "session_info_update",
356
+ updatedAt: existing.updatedAt
357
+ }
358
+ });
359
+ if (replayHistory)
360
+ await ctx.sendNotification({
361
+ sessionId,
362
+ update: {
363
+ sessionUpdate: "current_mode_update",
364
+ currentModeId: existing.modeId,
365
+ modeId: existing.modeId
366
+ }
367
+ });
350
368
  await reportSkippedMcpServers(ctx, sessionId, loadSkipped);
351
369
  await ctx.sendResult(id, {
352
- initialMode: {
370
+ modes: {
353
371
  currentModeId: existing.modeId,
354
372
  availableModes: ctx.modes
355
- }
373
+ },
374
+ configOptions: existing.configOptions ?? [...ctx.configOptions]
356
375
  });
357
376
  return false;
358
377
  }
@@ -390,6 +409,7 @@ async function handleSessionForkOp(ctx, id, params) {
390
409
  cwd: forkCwd,
391
410
  abort: new AbortController(),
392
411
  modeId: source.modeId,
412
+ configOptions: structuredClone(source.configOptions ?? [...ctx.configOptions]),
393
413
  createdAt: now,
394
414
  updatedAt: now,
395
415
  ...source.title !== void 0 ? { title: source.title } : {},
@@ -405,13 +425,17 @@ async function handleSessionForkOp(ctx, id, params) {
405
425
  await ctx.persist(forked, history);
406
426
  await ctx.sendNotification({
407
427
  sessionId,
408
- update: { sessionUpdate: "current_mode_update", modeId: forked.modeId }
428
+ update: {
429
+ sessionUpdate: "current_mode_update",
430
+ currentModeId: forked.modeId,
431
+ modeId: forked.modeId
432
+ }
409
433
  });
410
434
  await reportSkippedMcpServers(ctx, sessionId, forkSkipped);
411
435
  await ctx.sendResult(id, {
412
436
  sessionId,
413
- modes: ctx.modes,
414
- configOptions: ctx.configOptions
437
+ modes: { currentModeId: forked.modeId, availableModes: ctx.modes },
438
+ configOptions: forked.configOptions
415
439
  });
416
440
  return false;
417
441
  }
@@ -427,6 +451,11 @@ async function handleSessionPromptOp(ctx, id, params) {
427
451
  return false;
428
452
  }
429
453
  const session = ctx.sessions.get(sessionId);
454
+ if (session.prompting) {
455
+ await ctx.sendError(id, -32e3, "a prompt is already running for this session");
456
+ return false;
457
+ }
458
+ session.prompting = true;
430
459
  if (session.abort.signal.aborted) {
431
460
  session.abort = new AbortController();
432
461
  }
@@ -455,6 +484,8 @@ async function handleSessionPromptOp(ctx, id, params) {
455
484
  {
456
485
  sessionId,
457
486
  prompt: p.prompt,
487
+ modeId: session.modeId,
488
+ configOptions: session.configOptions,
458
489
  signal: turnSignal.signal,
459
490
  cwd: session.cwd,
460
491
  ...session.mcpServers ? { mcpServers: session.mcpServers } : {}
@@ -463,16 +494,25 @@ async function handleSessionPromptOp(ctx, id, params) {
463
494
  api
464
495
  );
465
496
  } catch (err) {
497
+ session.prompting = false;
466
498
  session.abort.signal.removeEventListener("abort", onCancel);
499
+ await Promise.all(pendingNotifications);
500
+ if (turnSignal.signal.aborted) {
501
+ await ctx.sendResult(id, { stopReason: "cancelled" });
502
+ return false;
503
+ }
467
504
  const { code, message, data } = errorToJsonRpc(err);
468
505
  await ctx.sendError(id, code, message, data);
469
506
  return false;
470
507
  }
471
508
  await Promise.all(pendingNotifications);
509
+ session.prompting = false;
472
510
  session.abort.signal.removeEventListener("abort", onCancel);
473
511
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
474
- await ctx.persist(session);
475
- await ctx.sendResult(id, { stopReason: result.stopReason });
512
+ if (ctx.sessions.get(sessionId) === session) await ctx.persist(session);
513
+ await ctx.sendResult(id, {
514
+ stopReason: turnSignal.signal.aborted ? "cancelled" : result.stopReason
515
+ });
476
516
  return false;
477
517
  }
478
518
  async function handleSetModeOp(ctx, id, params) {
@@ -488,7 +528,11 @@ async function handleSetModeOp(ctx, id, params) {
488
528
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
489
529
  await ctx.sendNotification({
490
530
  sessionId,
491
- update: { sessionUpdate: "current_mode_update", modeId }
531
+ update: {
532
+ sessionUpdate: "current_mode_update",
533
+ currentModeId: modeId,
534
+ modeId
535
+ }
492
536
  });
493
537
  await ctx.sendResult(id, {});
494
538
  return false;
@@ -499,21 +543,24 @@ async function handleSetConfigOptionOp(ctx, id, params) {
499
543
  const optionId = typeof p.configId === "string" ? p.configId : null;
500
544
  const value = typeof p.value === "string" ? p.value : null;
501
545
  const session = sessionId ? ctx.sessions.get(sessionId) : void 0;
502
- const option = optionId ? ctx.configOptions.find((o) => o.id === optionId) : void 0;
546
+ const options = session?.configOptions ?? structuredClone([...ctx.configOptions]);
547
+ const option = optionId ? options.find((o) => o.id === optionId) : void 0;
503
548
  if (!session || !option || value === null || !option.options.some((o) => o.value === value)) {
504
549
  await ctx.sendError(id, -32602, "invalid sessionId, configId, or value");
505
550
  return false;
506
551
  }
507
552
  option.currentValue = value;
553
+ session.configOptions = options;
508
554
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
509
555
  await ctx.sendNotification({
510
556
  sessionId,
511
557
  update: {
512
558
  sessionUpdate: "config_option_update",
513
- configOptions: [...ctx.configOptions]
559
+ configOptions: options
514
560
  }
515
561
  });
516
- await ctx.sendResult(id, { configOptions: [...ctx.configOptions] });
562
+ await ctx.persist(session);
563
+ await ctx.sendResult(id, { configOptions: options });
517
564
  return false;
518
565
  }
519
566
  async function reportSkippedMcpServers(ctx, sessionId, skipped) {
@@ -555,6 +602,8 @@ var ACPProtocolHandler = class {
555
602
  // session/request_permission). Keyed by our own `srv_N` ids.
556
603
  pendingOut = /* @__PURE__ */ new Map();
557
604
  nextOutId = 1;
605
+ promptRequests = /* @__PURE__ */ new Map();
606
+ persistenceWrites = /* @__PURE__ */ new Map();
558
607
  constructor(opts) {
559
608
  this.transport = opts.transport;
560
609
  this.defaultCwd = opts.defaultCwd;
@@ -580,20 +629,46 @@ var ACPProtocolHandler = class {
580
629
  */
581
630
  request(method, params, timeoutMs = 6e4) {
582
631
  const id = `srv_${this.nextOutId++}`;
583
- return new Promise((resolve2, reject) => {
584
- const timer = setTimeout(() => {
632
+ const sessionId = params?.sessionId;
633
+ const signal = sessionId ? this.sessions.get(sessionId)?.abort.signal : void 0;
634
+ return new Promise((resolve3, reject) => {
635
+ const cleanup = () => {
636
+ clearTimeout(timer);
637
+ signal?.removeEventListener("abort", abort);
585
638
  this.pendingOut.delete(id);
586
- reject(new Error(`${method} timed out after ${timeoutMs}ms`));
639
+ };
640
+ const fail = (error) => {
641
+ cleanup();
642
+ reject(error);
643
+ };
644
+ const abort = () => {
645
+ fail(new Error("client request cancelled"));
646
+ void this.transport.send(toWire({ jsonrpc: "2.0", method: "$/cancel_request", params: { requestId: id } })).catch(() => {
647
+ });
648
+ };
649
+ const timer = setTimeout(() => {
650
+ fail(new Error(`${method} timed out after ${timeoutMs}ms`));
587
651
  }, timeoutMs);
588
- this.pendingOut.set(id, { resolve: resolve2, reject, timer });
652
+ if (signal?.aborted) {
653
+ fail(new Error("client request cancelled"));
654
+ return;
655
+ }
656
+ signal?.addEventListener("abort", abort, { once: true });
657
+ this.pendingOut.set(id, {
658
+ resolve: (value) => {
659
+ cleanup();
660
+ resolve3(value);
661
+ },
662
+ reject: fail,
663
+ timer
664
+ });
589
665
  this.transport.send(toWire({ jsonrpc: "2.0", id, method, params })).catch((e) => {
590
- clearTimeout(timer);
591
- this.pendingOut.delete(id);
592
- reject(e instanceof Error ? e : new Error(String(e)));
666
+ fail(e instanceof Error ? e : new Error(String(e)));
593
667
  });
594
668
  });
595
669
  }
596
670
  maybeResolvePending(m) {
671
+ if (m.result === void 0 && m.error === void 0) return;
597
672
  const id = m.id;
598
673
  if (typeof id !== "string") return;
599
674
  const pending = this.pendingOut.get(id);
@@ -613,6 +688,7 @@ var ACPProtocolHandler = class {
613
688
  if (typeof msg !== "object" || msg === null) return false;
614
689
  const m = msg;
615
690
  if (m.id !== void 0 && (m.result !== void 0 || m.error !== void 0)) {
691
+ this.maybeResolvePending(msg);
616
692
  return false;
617
693
  }
618
694
  if (m.id !== void 0 && typeof m.method === "string") {
@@ -652,6 +728,7 @@ var ACPProtocolHandler = class {
652
728
  store: this.store,
653
729
  replayFor: this.replayFor,
654
730
  seedFor: this.seedFor,
731
+ disposeFor: this.disposeFor,
655
732
  onSessionNew: this.onSessionNew,
656
733
  allocId: () => this.allocId(),
657
734
  persist: (state, history) => this.persist(state, history),
@@ -689,14 +766,23 @@ var ACPProtocolHandler = class {
689
766
  return await this.handleSessionClose(id, params);
690
767
  case "session/delete":
691
768
  return await this.handleSessionDelete(id, params);
692
- case "session/prompt":
693
- return await handleSessionPromptOp(this.sessionContext(), id, params);
769
+ case "session/prompt": {
770
+ const sessionId = params?.sessionId;
771
+ if (typeof sessionId === "string" && !this.sessions.get(sessionId)?.prompting) {
772
+ this.promptRequests.set(id, sessionId);
773
+ }
774
+ try {
775
+ return await handleSessionPromptOp(this.sessionContext(), id, params);
776
+ } finally {
777
+ this.promptRequests.delete(id);
778
+ }
779
+ }
694
780
  case "session/set_mode":
695
781
  return await handleSetModeOp(this.sessionContext(), id, params);
696
782
  case "session/set_config_option":
697
783
  return await handleSetConfigOptionOp(this.sessionContext(), id, params);
698
784
  case "session/list":
699
- return await this.handleSessionList(id);
785
+ return await this.handleSessionList(id, params);
700
786
  case "session/fork":
701
787
  return await handleSessionForkOp(this.sessionContext(), id, params);
702
788
  case "providers/list":
@@ -725,7 +811,12 @@ var ACPProtocolHandler = class {
725
811
  this.initialized = true;
726
812
  await this.sendResult(
727
813
  id,
728
- buildInitializeResult(this.agentName, this.modes, this.configOptions)
814
+ buildInitializeResult(
815
+ this.agentName,
816
+ this.modes,
817
+ this.configOptions,
818
+ this.clientCapabilities
819
+ )
729
820
  );
730
821
  return false;
731
822
  }
@@ -734,29 +825,15 @@ var ACPProtocolHandler = class {
734
825
  return false;
735
826
  }
736
827
  async handleLogout(id, _params) {
737
- await this.sendResult(id, {});
828
+ await this.sendError(
829
+ id,
830
+ -32601,
831
+ "logout is not supported; manage provider credentials with wstack auth"
832
+ );
738
833
  return false;
739
834
  }
740
835
  async handleSessionResume(id, params) {
741
- const p = params ?? {};
742
- const sessionId = typeof p.sessionId === "string" ? p.sessionId : null;
743
- const existing = sessionId ? this.sessions.get(sessionId) : void 0;
744
- if (existing) {
745
- existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
746
- const resumeServers = parseMcpServers(p.mcpServers);
747
- if (resumeServers.length > 0) {
748
- existing.mcpServers = resumeServers;
749
- }
750
- await this.sendResult(id, {
751
- initialMode: {
752
- currentModeId: existing.modeId,
753
- availableModes: this.modes
754
- }
755
- });
756
- return false;
757
- }
758
- await this.sendError(id, -32e3, `session not found: ${sessionId}`);
759
- return false;
836
+ return handleSessionLoadOp(this.sessionContext(), id, params, false);
760
837
  }
761
838
  async handleSessionClose(id, params) {
762
839
  const p = params ?? {};
@@ -779,14 +856,12 @@ var ACPProtocolHandler = class {
779
856
  await this.sendError(id, -32e3, `session not found: ${sessionId}`);
780
857
  return false;
781
858
  }
782
- if (!this.sessions.has(sessionId)) {
783
- await this.sendResult(id, { configOptions: [...this.configOptions] });
784
- return false;
785
- }
786
859
  const session = this.sessions.get(sessionId);
787
- session.abort.abort();
860
+ session?.abort.abort();
788
861
  this.sessions.delete(sessionId);
789
862
  this.disposeSession(sessionId);
863
+ await this.persistenceWrites.get(sessionId);
864
+ await this.store?.delete?.(sessionId);
790
865
  await this.sendResult(id, {});
791
866
  return false;
792
867
  }
@@ -813,12 +888,23 @@ var ACPProtocolHandler = class {
813
888
  await this.sendError(id, -32e3, "MCP message routing not available through ACP");
814
889
  return false;
815
890
  }
816
- async handleSessionList(id) {
817
- const sessions = Array.from(this.sessions.values()).map((s) => {
891
+ async handleSessionList(id, params) {
892
+ const p = params ?? {};
893
+ if (p.cwd != null && (typeof p.cwd !== "string" || !isAbsolute2(p.cwd)) || p.cursor != null) {
894
+ await this.sendError(id, -32602, "invalid cwd or cursor");
895
+ return false;
896
+ }
897
+ const known = new Map(this.sessions);
898
+ for (const entry of await this.store?.list?.() ?? []) {
899
+ if (known.has(entry.id)) continue;
900
+ const saved = await this.store?.load(entry.id);
901
+ if (saved?.cwd) known.set(entry.id, { ...saved, id: entry.id });
902
+ }
903
+ const sessions = Array.from(known.values()).filter((s) => typeof p.cwd !== "string" || s.cwd && resolve2(s.cwd) === resolve2(p.cwd)).map((s) => {
818
904
  const out = {
819
905
  sessionId: s.id,
820
906
  cwd: s.cwd,
821
- updatedAt: s.updatedAt
907
+ updatedAt: s.updatedAt ?? ""
822
908
  };
823
909
  if (s.title !== void 0) out.title = s.title;
824
910
  return out;
@@ -841,6 +927,11 @@ var ACPProtocolHandler = class {
841
927
  return false;
842
928
  }
843
929
  case "$/cancel_request": {
930
+ const requestId = params?.requestId;
931
+ if (typeof requestId === "string" || typeof requestId === "number") {
932
+ const sessionId = this.promptRequests.get(requestId);
933
+ if (sessionId) this.sessions.get(sessionId)?.abort.abort();
934
+ }
844
935
  return false;
845
936
  }
846
937
  case "exit":
@@ -861,10 +952,18 @@ var ACPProtocolHandler = class {
861
952
  }
862
953
  async persist(state, history = void 0) {
863
954
  if (!this.store) return;
864
- try {
865
- await this.store.save(state, history ?? this.replayFor?.(state.id));
866
- } catch {
867
- }
955
+ const store = this.store;
956
+ const replay = history ?? this.replayFor?.(state.id);
957
+ const previous = this.persistenceWrites.get(state.id) ?? Promise.resolve();
958
+ const write = previous.then(() => store.save(state, replay)).then(
959
+ () => {
960
+ },
961
+ () => {
962
+ }
963
+ );
964
+ this.persistenceWrites.set(state.id, write);
965
+ await write;
966
+ if (this.persistenceWrites.get(state.id) === write) this.persistenceWrites.delete(state.id);
868
967
  }
869
968
  async sendError(id, code, message, data) {
870
969
  const error = { code, message };
@@ -1131,7 +1230,10 @@ function makeACPServerAgentTurn(opts) {
1131
1230
  }
1132
1231
  }
1133
1232
  }
1233
+ const agent = agents.get(sessionId);
1134
1234
  agents.delete(sessionId);
1235
+ void Promise.resolve().then(() => agent?.teardown?.()).catch(() => {
1236
+ });
1135
1237
  history.delete(sessionId);
1136
1238
  historyBytes.delete(sessionId);
1137
1239
  pendingSeed.delete(sessionId);
@@ -1254,7 +1356,10 @@ function promptToText(blocks) {
1254
1356
  } else if (b.type === "audio") {
1255
1357
  parts.push(`[audio: ${b.mimeType}]`);
1256
1358
  } else if (b.type === "resource") {
1257
- parts.push(`[embedded resource: ${b.resource.uri}]`);
1359
+ parts.push(
1360
+ "text" in b.resource && typeof b.resource.text === "string" ? `[embedded resource: ${b.resource.uri}]
1361
+ ${b.resource.text}` : `[embedded resource: ${b.resource.uri}]`
1362
+ );
1258
1363
  } else if (b.type === "resource_link") {
1259
1364
  parts.push(`[resource link: ${b.uri}]`);
1260
1365
  }
@@ -1398,6 +1503,7 @@ var ACPSessionStore = class {
1398
1503
  id: state.id,
1399
1504
  cwd: state.cwd,
1400
1505
  modeId: state.modeId,
1506
+ configOptions: state.configOptions,
1401
1507
  createdAt: state.createdAt,
1402
1508
  updatedAt: state.updatedAt,
1403
1509
  title: state.title,
@@ -1558,7 +1664,8 @@ var ACPSessionStore = class {
1558
1664
  if (target === null) return;
1559
1665
  try {
1560
1666
  await fsp2.unlink(target);
1561
- } catch {
1667
+ } catch (err) {
1668
+ if (err.code !== "ENOENT") throw err;
1562
1669
  }
1563
1670
  await this.withIndexLock(async () => {
1564
1671
  const entries = await this.readIndex();
@@ -1619,9 +1726,9 @@ var StdioTransport = class {
1619
1726
  }
1620
1727
  send(msg) {
1621
1728
  if (this.closed) return Promise.resolve();
1622
- return new Promise((resolve2) => {
1729
+ return new Promise((resolve3) => {
1623
1730
  const line = JSON.stringify(msg) + "\n";
1624
- this.stdout.write(line, "utf8", () => resolve2());
1731
+ this.stdout.write(line, "utf8", () => resolve3());
1625
1732
  });
1626
1733
  }
1627
1734
  sendRaw(chunk) {
@@ -1634,8 +1741,8 @@ var StdioTransport = class {
1634
1741
  return Promise.resolve(queued.message);
1635
1742
  }
1636
1743
  if (this.closed) return Promise.resolve(null);
1637
- return new Promise((resolve2) => {
1638
- this.resolveRead = resolve2;
1744
+ return new Promise((resolve3) => {
1745
+ this.resolveRead = resolve3;
1639
1746
  });
1640
1747
  }
1641
1748
  onMessage(handler) {
@@ -1704,9 +1811,9 @@ var StdioTransport = class {
1704
1811
  }
1705
1812
  dispatch(msg, chars = JSON.stringify(msg).length) {
1706
1813
  if (this.resolveRead) {
1707
- const resolve2 = this.resolveRead;
1814
+ const resolve3 = this.resolveRead;
1708
1815
  this.resolveRead = null;
1709
- resolve2(msg);
1816
+ resolve3(msg);
1710
1817
  } else {
1711
1818
  let claimed = false;
1712
1819
  for (const handler of this.claimHandlers) {
@@ -1866,7 +1973,7 @@ function toolToPriority(tool) {
1866
1973
  import { createServer } from "node:http";
1867
1974
  import { isIP } from "node:net";
1868
1975
  import { fileURLToPath } from "node:url";
1869
- import { expandIPv6, writeErr as writeErr2 } from "@wrongstack/core/utils";
1976
+ import { expandIPv6, isStandaloneBinary, writeErr as writeErr2 } from "@wrongstack/core/utils";
1870
1977
  import { timingSafeTokenEqual } from "@wrongstack/primitives";
1871
1978
  var LISTEN_RETRY_LIMIT = 5;
1872
1979
  var LISTEN_RETRY_BASE_MS = 25;
@@ -1912,15 +2019,30 @@ var WrongStackACPServer = class {
1912
2019
  this.transport.sendStartupMarker();
1913
2020
  }
1914
2021
  this.running = true;
2022
+ const prompts = /* @__PURE__ */ new Set();
1915
2023
  try {
1916
2024
  while (this.running) {
1917
2025
  const msg = await this.transport.read();
1918
2026
  if (!msg) break;
2027
+ if (msg.method === "session/prompt") {
2028
+ const task = this.handler.handleMessage(msg).then(
2029
+ () => {
2030
+ },
2031
+ (err) => {
2032
+ writeErr2(`[wstack-acp] prompt dispatch failed: ${String(err)}
2033
+ `);
2034
+ }
2035
+ );
2036
+ prompts.add(task);
2037
+ void task.finally(() => prompts.delete(task));
2038
+ continue;
2039
+ }
1919
2040
  const terminal = await this.handler.handleMessage(msg);
1920
2041
  if (terminal) break;
1921
2042
  }
1922
2043
  } finally {
1923
2044
  this.handler.close();
2045
+ await Promise.allSettled(prompts);
1924
2046
  this.transport.close();
1925
2047
  }
1926
2048
  }
@@ -2054,10 +2176,10 @@ var WrongStackACPServer = class {
2054
2176
  listenWithRetry(port, host, attempt = 0) {
2055
2177
  const server = this.httpServer;
2056
2178
  if (!server) return Promise.resolve();
2057
- return new Promise((resolve2, reject) => {
2179
+ return new Promise((resolve3, reject) => {
2058
2180
  const onListening = () => {
2059
2181
  server.removeListener("error", onError);
2060
- resolve2();
2182
+ resolve3();
2061
2183
  };
2062
2184
  const onError = (err) => {
2063
2185
  server.removeListener("listening", onListening);
@@ -2065,7 +2187,7 @@ var WrongStackACPServer = class {
2065
2187
  if (port === 0 && transient && attempt < LISTEN_RETRY_LIMIT) {
2066
2188
  const timer = setTimeout(
2067
2189
  () => {
2068
- this.listenWithRetry(port, host, attempt + 1).then(resolve2, reject);
2190
+ this.listenWithRetry(port, host, attempt + 1).then(resolve3, reject);
2069
2191
  },
2070
2192
  LISTEN_RETRY_BASE_MS * (attempt + 1)
2071
2193
  );
@@ -2093,8 +2215,8 @@ var WrongStackACPServer = class {
2093
2215
  if (!server) return;
2094
2216
  for (const socket of this.httpSockets) socket.destroy();
2095
2217
  this.httpSockets.clear();
2096
- await new Promise((resolve2) => {
2097
- server.close(() => resolve2());
2218
+ await new Promise((resolve3) => {
2219
+ server.close(() => resolve3());
2098
2220
  });
2099
2221
  }
2100
2222
  };
@@ -2125,7 +2247,7 @@ async function main() {
2125
2247
  const server = new WrongStackACPServer();
2126
2248
  await server.start();
2127
2249
  }
2128
- var isEntrypoint = process.argv[1] !== void 0 && fileURLToPath(import.meta.url) === process.argv[1];
2250
+ var isEntrypoint = process.argv[1] !== void 0 && !isStandaloneBinary() && fileURLToPath(import.meta.url) === process.argv[1];
2129
2251
  if (isEntrypoint) {
2130
2252
  main().catch((err) => {
2131
2253
  writeErr2(`[wstack-acp fatal] ${err}
@@ -0,0 +1,4 @@
1
+ import type { ACPClientTransport } from '../agent/stdio-transport.js';
2
+ import type { SessionId } from '../types/acp-v1.js';
3
+ export declare function cancelLateAcpSession(transport: ACPClientTransport, createPromise: Promise<SessionId>): void;
4
+ //# sourceMappingURL=acp-late-session.d.ts.map
@@ -0,0 +1,9 @@
1
+ export interface PendingRequest {
2
+ method: string;
3
+ resolve: (v: unknown) => void;
4
+ reject: (e: Error) => void;
5
+ timeoutMs: number;
6
+ timeoutHandle: ReturnType<typeof setTimeout>;
7
+ }
8
+ export type State = 'init' | 'ready' | 'authenticated' | 'sessioning' | 'prompting' | 'done' | 'closed';
9
+ //# sourceMappingURL=acp-request-state.d.ts.map