@wrongstack/acp 1.0.28 → 1.0.32

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
@@ -236,6 +236,10 @@ async function handleSessionNewOp(ctx, id, params) {
236
236
  }
237
237
  const skipped = [];
238
238
  const mcpServers = parseMcpServers(p.mcpServers, (reason) => skipped.push(reason));
239
+ if (ctx.sessions.size >= ctx.maxSessions) {
240
+ await ctx.sendError(id, -32e3, `active session limit reached (${ctx.maxSessions})`);
241
+ return false;
242
+ }
239
243
  const sessionId = `sess_${ctx.allocId()}`;
240
244
  const now = (/* @__PURE__ */ new Date()).toISOString();
241
245
  const state = {
@@ -286,6 +290,9 @@ async function handleSessionLoadOp(ctx, id, params, replayHistory = true) {
286
290
  if (!existing && sessionId && ctx.store) {
287
291
  const persisted = await ctx.store.load(sessionId);
288
292
  if (persisted) {
293
+ if (ctx.sessions.has(sessionId)) {
294
+ return handleSessionLoadOp(ctx, id, params, replayHistory);
295
+ }
289
296
  if (ctx.sessions.size >= ctx.maxSessions) {
290
297
  await ctx.sendError(id, -32e3, `active session limit reached (${ctx.maxSessions})`);
291
298
  return false;
@@ -294,8 +301,15 @@ async function handleSessionLoadOp(ctx, id, params, replayHistory = true) {
294
301
  await ctx.sendError(id, -32602, `cwd must be an absolute path to an existing directory`);
295
302
  return false;
296
303
  }
297
- const candidateCwd = persisted.cwd ?? loadCwd ?? ctx.defaultCwd;
304
+ const candidateCwd = loadCwd ?? persisted.cwd ?? ctx.defaultCwd;
298
305
  const restoredCwd = await resolveSessionCwd(candidateCwd) ?? ctx.defaultCwd;
306
+ if (ctx.sessions.has(sessionId)) {
307
+ return handleSessionLoadOp(ctx, id, params, replayHistory);
308
+ }
309
+ if (ctx.sessions.size >= ctx.maxSessions) {
310
+ await ctx.sendError(id, -32e3, `active session limit reached (${ctx.maxSessions})`);
311
+ return false;
312
+ }
299
313
  const restored = {
300
314
  id: sessionId,
301
315
  cwd: restoredCwd,
@@ -399,9 +413,17 @@ async function handleSessionForkOp(ctx, id, params) {
399
413
  }
400
414
  forkCwd = resolved;
401
415
  }
416
+ if (ctx.sessions.get(sourceId) !== source) {
417
+ await ctx.sendError(id, -32e3, `session not found: ${sourceId}`);
418
+ return false;
419
+ }
402
420
  const forkSkipped = [];
403
421
  const forkRequested = parseMcpServers(p.mcpServers, (reason) => forkSkipped.push(reason));
404
422
  const forkMcpServers = forkRequested.length > 0 ? forkRequested : source.mcpServers;
423
+ if (ctx.sessions.size >= ctx.maxSessions) {
424
+ await ctx.sendError(id, -32e3, `active session limit reached (${ctx.maxSessions})`);
425
+ return false;
426
+ }
405
427
  const now = (/* @__PURE__ */ new Date()).toISOString();
406
428
  const sessionId = `sess_${ctx.allocId()}`;
407
429
  const forked = {
@@ -475,6 +497,7 @@ async function handleSessionPromptOp(ctx, id, params) {
475
497
  let result;
476
498
  const pendingNotifications = [];
477
499
  const emit = (update) => {
500
+ if (ctx.sessions.get(sessionId) !== session) return;
478
501
  const notifPromise = ctx.sendNotification({ sessionId, update });
479
502
  pendingNotifications.push(notifPromise.catch(() => {
480
503
  }));
@@ -1510,20 +1533,24 @@ var ACPSessionStore = class {
1510
1533
  const tmp = `${target}.${process.pid}.${Date.now()}.${++this.writeSeq}.tmp`;
1511
1534
  let renamed = false;
1512
1535
  try {
1513
- await fsp2.writeFile(
1514
- tmp,
1515
- JSON.stringify({
1516
- id: state.id,
1517
- cwd: state.cwd,
1518
- modeId: state.modeId,
1519
- configOptions: state.configOptions,
1520
- createdAt: state.createdAt,
1521
- updatedAt: state.updatedAt,
1522
- title: state.title,
1523
- ...history && history.length > 0 ? { history } : {}
1524
- }),
1525
- "utf8"
1526
- );
1536
+ const data = JSON.stringify({
1537
+ id: state.id,
1538
+ cwd: state.cwd,
1539
+ modeId: state.modeId,
1540
+ configOptions: state.configOptions,
1541
+ createdAt: state.createdAt,
1542
+ updatedAt: state.updatedAt,
1543
+ title: state.title,
1544
+ ...history && history.length > 0 ? { history } : {}
1545
+ });
1546
+ try {
1547
+ await fsp2.writeFile(tmp, data, "utf8");
1548
+ } catch (err) {
1549
+ if (err.code !== "ENOENT") throw err;
1550
+ this.initialized = false;
1551
+ await this.init();
1552
+ await fsp2.writeFile(tmp, data, "utf8");
1553
+ }
1527
1554
  await fsp2.rename(tmp, target);
1528
1555
  renamed = true;
1529
1556
  } finally {
@@ -1547,13 +1574,15 @@ var ACPSessionStore = class {
1547
1574
  }
1548
1575
  /** List all persisted sessions. */
1549
1576
  async list() {
1550
- const indexEntries = await this.readIndex();
1551
- if (indexEntries !== null) {
1552
- return indexEntries.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
1553
- }
1554
- const sessions = await this.scanForSessions();
1555
- void this.writeIndex(sessions).catch(() => void 0);
1556
- return sessions;
1577
+ return this.withIndexLock(async () => {
1578
+ const indexEntries = await this.readIndex();
1579
+ if (indexEntries !== null) {
1580
+ return indexEntries.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
1581
+ }
1582
+ const sessions = await this.scanForSessions();
1583
+ await this.writeIndex(sessions).catch(() => void 0);
1584
+ return sessions;
1585
+ });
1557
1586
  }
1558
1587
  /**
1559
1588
  * Walk `<dir>` and parse every session file into `{id, updatedAt}`
@@ -1578,7 +1607,7 @@ var ACPSessionStore = class {
1578
1607
  try {
1579
1608
  const data = await fsp2.readFile(path2.join(this.dir, file), "utf8");
1580
1609
  const parsed = JSON.parse(data);
1581
- if (parsed.id) {
1610
+ if (parsed && typeof parsed.id === "string" && parsed.id !== "" && (parsed.updatedAt === void 0 || typeof parsed.updatedAt === "string")) {
1582
1611
  sessions.push({ id: parsed.id, updatedAt: parsed.updatedAt ?? "" });
1583
1612
  }
1584
1613
  } catch {
@@ -1962,7 +1991,7 @@ function toACPInputSchema(src) {
1962
1991
  if (typeof s.maximum === "number") out.maximum = s.maximum;
1963
1992
  if (s.items) out.items = toACPInputSchema(s.items);
1964
1993
  if (s.properties && typeof s.properties === "object") {
1965
- const props = {};
1994
+ const props = /* @__PURE__ */ Object.create(null);
1966
1995
  for (const [k, v] of Object.entries(s.properties)) {
1967
1996
  props[k] = toACPInputSchema(v);
1968
1997
  }
@@ -2318,6 +2347,7 @@ var WsBridgeTransport = class {
2318
2347
  receive(msg) {
2319
2348
  if (this.closed) return;
2320
2349
  for (const handler of [...this.handlers]) {
2350
+ if (this.closed) break;
2321
2351
  try {
2322
2352
  handler(msg);
2323
2353
  } catch {
@@ -0,0 +1,24 @@
1
+ import type { ACPMessage } from '../types/acp-messages.js';
2
+ import { type ACPCallbackOptions, type ACPResponseSender } from './acp-session-callbacks.js';
3
+ import type { ACPProgressEvent } from './acp-session-types.js';
4
+ export interface AcpSessionMessagesHost {
5
+ sendResult: (id: string | number, result: unknown) => Promise<void>;
6
+ sendErrorResponse: (id: string | number, code: number, message: string) => Promise<void>;
7
+ promptCallbackAbort: AbortController | null;
8
+ callbackAbort: AbortController;
9
+ pending: Map<string | number, import('./acp-request-state.js').PendingRequest>;
10
+ sessionId: import('../types/acp-v1.js').SessionId | null;
11
+ scratch: import('./acp-session-updates.js').ACPSessionScratch;
12
+ emitProgress: (event: import('./acp-session-types.js').ACPProgressEvent) => void;
13
+ permissionPolicy: import('./permission.js').PermissionPolicy;
14
+ responseSender: () => import('./acp-session-callbacks.js').ACPResponseSender;
15
+ callbackOptions: () => import('./acp-session-callbacks.js').ACPCallbackOptions;
16
+ fileServer: import('./file-server.js').FileServer;
17
+ terminalServer: import('./terminal-server.js').TerminalServer;
18
+ progressHandler: import('./acp-session-types.js').ACPProgressHandler | null;
19
+ }
20
+ export declare function responseSender(this: AcpSessionMessagesHost): ACPResponseSender;
21
+ export declare function callbackOptions(this: AcpSessionMessagesHost): ACPCallbackOptions;
22
+ export declare function handleMessage(this: AcpSessionMessagesHost, msg: ACPMessage): void;
23
+ export declare function emitProgress(this: AcpSessionMessagesHost, event: ACPProgressEvent): void;
24
+ //# sourceMappingURL=acp-session-messages.d.ts.map
@@ -139,5 +139,6 @@ export declare class ACPSession {
139
139
  private scratch;
140
140
  private resetScratch;
141
141
  private acpSessionAuthHost;
142
+ private acpSessionMessagesHost;
142
143
  }
143
144
  //# sourceMappingURL=acp-session.d.ts.map