acpx 0.17.1 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/{agent-registry-evUoJINz.js → agent-registry-Ct2yWPW7.js} +18 -18
  2. package/dist/agent-registry-Ct2yWPW7.js.map +1 -0
  3. package/dist/agent-registry.d.ts.map +1 -1
  4. package/dist/agent-registry.js +1 -1
  5. package/dist/{cli-B2ycKzaO.js → cli-Bmki9cj5.js} +5 -5
  6. package/dist/{cli-B2ycKzaO.js.map → cli-Bmki9cj5.js.map} +1 -1
  7. package/dist/cli.js +167 -131
  8. package/dist/cli.js.map +1 -1
  9. package/dist/{client-CkbBoSGd.d.ts → client-DzaUKazo.d.ts} +23 -15
  10. package/dist/client-DzaUKazo.d.ts.map +1 -0
  11. package/dist/{flows-xGyhmoHO.js → flows--Ewxn_fQ.js} +665 -460
  12. package/dist/flows--Ewxn_fQ.js.map +1 -0
  13. package/dist/flows.d.ts +19 -7
  14. package/dist/flows.d.ts.map +1 -1
  15. package/dist/flows.js +1 -1
  16. package/dist/{invocation-options-eZEBh4vd.js → invocation-options-DRzxHZmr.js} +34 -12
  17. package/dist/invocation-options-DRzxHZmr.js.map +1 -0
  18. package/dist/{ipc-D2rstexT.js → ipc-fMgj5vrO.js} +818 -302
  19. package/dist/ipc-fMgj5vrO.js.map +1 -0
  20. package/dist/{output-D2xL6wdp.js → output-D6kDs8BN.js} +30 -14
  21. package/dist/output-D6kDs8BN.js.map +1 -0
  22. package/dist/{queue-owner-runtime-Dgv3e2Tp.js → queue-owner-runtime-CLwjMYNL.js} +1448 -717
  23. package/dist/queue-owner-runtime-CLwjMYNL.js.map +1 -0
  24. package/dist/runtime.d.ts +57 -3
  25. package/dist/runtime.d.ts.map +1 -1
  26. package/dist/runtime.js +303 -166
  27. package/dist/runtime.js.map +1 -1
  28. package/dist/session-options-Dp6mzNSy.d.ts.map +1 -1
  29. package/dist/{session-watch-CTe9Bnbc.js → session-watch-BfdIo-n4.js} +5 -5
  30. package/dist/session-watch-BfdIo-n4.js.map +1 -0
  31. package/dist/{watch-DxuXEc8n.js → watch-DH-zXQH2.js} +2 -2
  32. package/dist/{watch-DxuXEc8n.js.map → watch-DH-zXQH2.js.map} +1 -1
  33. package/package.json +1 -1
  34. package/skills/acpx/SKILL.md +69 -30
  35. package/dist/agent-registry-evUoJINz.js.map +0 -1
  36. package/dist/client-CkbBoSGd.d.ts.map +0 -1
  37. package/dist/flows-xGyhmoHO.js.map +0 -1
  38. package/dist/invocation-options-eZEBh4vd.js.map +0 -1
  39. package/dist/ipc-D2rstexT.js.map +0 -1
  40. package/dist/output-D2xL6wdp.js.map +0 -1
  41. package/dist/queue-owner-runtime-Dgv3e2Tp.js.map +0 -1
  42. package/dist/session-watch-CTe9Bnbc.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { n as AGENT_REGISTRY, r as BUILT_IN_AGENT_PACKAGES, t as AGENT_ARGV_REGISTRY } from "./agent-registry-evUoJINz.js";
1
+ import { M as runTimedExecFile, n as AGENT_REGISTRY, r as BUILT_IN_AGENT_PACKAGES, t as AGENT_ARGV_REGISTRY } from "./agent-registry-Ct2yWPW7.js";
2
2
  import { statSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import fs$1 from "node:fs/promises";
@@ -9,6 +9,7 @@ import { root } from "@openclaw/fs-safe";
9
9
  import { z } from "zod";
10
10
  import { createHash, randomInt, randomUUID } from "node:crypto";
11
11
  import { isHardlinkFallbackError } from "@openclaw/fs-safe/durability";
12
+ import { acquireFileLock } from "@openclaw/fs-safe/file-lock";
12
13
  import net from "node:net";
13
14
  //#region src/errors.ts
14
15
  var AcpxOperationalError = class extends Error {
@@ -216,6 +217,7 @@ const OUTPUT_ERROR_CODES = [
216
217
  "TIMEOUT",
217
218
  "PERMISSION_DENIED",
218
219
  "PERMISSION_PROMPT_UNAVAILABLE",
220
+ "EXEC_DISABLED",
219
221
  "RUNTIME",
220
222
  "USAGE"
221
223
  ];
@@ -229,12 +231,12 @@ const SESSION_RECORD_SCHEMA = "acpx.session.v1";
229
231
  //#endregion
230
232
  //#region src/acp/error-shapes.ts
231
233
  const RESOURCE_NOT_FOUND_ACP_CODES = /* @__PURE__ */ new Set([-32001, -32002]);
232
- function asRecord$5(value) {
234
+ function asRecord$4(value) {
233
235
  if (!value || typeof value !== "object" || Array.isArray(value)) return;
234
236
  return value;
235
237
  }
236
238
  function toAcpErrorPayload(value) {
237
- const record = asRecord$5(value);
239
+ const record = asRecord$4(value);
238
240
  if (!record) return;
239
241
  if (typeof record.code !== "number" || !Number.isFinite(record.code)) return;
240
242
  if (typeof record.message !== "string" || record.message.length === 0) return;
@@ -248,7 +250,7 @@ function extractAcpErrorInternal(value, depth) {
248
250
  if (depth > 5) return;
249
251
  const direct = toAcpErrorPayload(value);
250
252
  if (direct) return direct;
251
- const record = asRecord$5(value);
253
+ const record = asRecord$4(value);
252
254
  if (!record) return;
253
255
  return extractNestedAcpError(record, depth);
254
256
  }
@@ -283,7 +285,7 @@ function hasSessionNotFoundHint(value, depth = 0) {
283
285
  if (depth > 4) return false;
284
286
  if (isSessionNotFoundText(value)) return true;
285
287
  if (Array.isArray(value)) return value.some((entry) => hasSessionNotFoundHint(entry, depth + 1));
286
- const record = asRecord$5(value);
288
+ const record = asRecord$4(value);
287
289
  if (!record) return false;
288
290
  return Object.values(record).some((entry) => hasSessionNotFoundHint(entry, depth + 1));
289
291
  }
@@ -310,6 +312,15 @@ function isProcessAlive(pid) {
310
312
  return false;
311
313
  }
312
314
  }
315
+ function isProcessDefinitelyDead(pid) {
316
+ if (!pid || !Number.isSafeInteger(pid) || pid <= 0 || pid === process.pid) return false;
317
+ try {
318
+ process.kill(pid, 0);
319
+ return false;
320
+ } catch (error) {
321
+ return error.code === "ESRCH";
322
+ }
323
+ }
313
324
  //#endregion
314
325
  //#region \0@oxc-project+runtime@0.149.0/helpers/esm/usingCtx.js
315
326
  function _usingCtx() {
@@ -430,18 +441,18 @@ function defaultSessionEventLog(sessionId) {
430
441
  }
431
442
  //#endregion
432
443
  //#region src/acp/jsonrpc.ts
433
- function asRecord$4(value) {
444
+ function asRecord$3(value) {
434
445
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
435
446
  return value;
436
447
  }
437
448
  function isAcpMessageObject(value) {
438
- return asRecord$4(value) !== null;
449
+ return asRecord$3(value) !== null;
439
450
  }
440
451
  function hasValidId(value) {
441
452
  return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
442
453
  }
443
454
  function isErrorObject(value) {
444
- const record = asRecord$4(value);
455
+ const record = asRecord$3(value);
445
456
  return !!record && typeof record.code === "number" && Number.isFinite(record.code) && typeof record.message === "string";
446
457
  }
447
458
  function hasResultOrError(value) {
@@ -466,7 +477,7 @@ function isJsonRpcResponse(value) {
466
477
  return hasResultOrError(value);
467
478
  }
468
479
  function isAcpJsonRpcMessage(value) {
469
- const record = asRecord$4(value);
480
+ const record = asRecord$3(value);
470
481
  if (!record || record.jsonrpc !== "2.0") return false;
471
482
  return isJsonRpcNotificationRecord(record) || isJsonRpcRequest(record) || isJsonRpcResponse(record);
472
483
  }
@@ -476,14 +487,29 @@ function isJsonRpcNotification(message) {
476
487
  function isSessionUpdateNotification(message) {
477
488
  return isJsonRpcNotification(message) && message.method === "session/update";
478
489
  }
490
+ function sessionUpdateHasRequiredFields(update) {
491
+ const kind = update.sessionUpdate;
492
+ if (typeof kind !== "string") return false;
493
+ if (kind === "agent_message_chunk" || kind === "agent_thought_chunk" || kind === "user_message_chunk") return isChunkContent(update.content);
494
+ if (kind === "plan") return Array.isArray(update.entries) && update.entries.every(isPlanEntry);
495
+ return true;
496
+ }
497
+ function isChunkContent(value) {
498
+ const content = asRecord$3(value);
499
+ return content !== null && typeof content.type === "string" && (content.type !== "text" || typeof content.text === "string");
500
+ }
501
+ function isPlanEntry(value) {
502
+ const entry = asRecord$3(value);
503
+ return entry !== null && typeof entry.status === "string" && typeof entry.content === "string";
504
+ }
479
505
  function extractSessionUpdateNotification(message) {
480
506
  if (!isSessionUpdateNotification(message)) return;
481
- const params = asRecord$4(message.params);
507
+ const params = asRecord$3(message.params);
482
508
  if (!params) return;
483
509
  const sessionId = typeof params.sessionId === "string" ? params.sessionId : null;
484
510
  if (!sessionId) return;
485
- const update = asRecord$4(params.update);
486
- if (!update || typeof update.sessionUpdate !== "string") return;
511
+ const update = asRecord$3(params.update);
512
+ if (!update || !sessionUpdateHasRequiredFields(update)) return;
487
513
  return {
488
514
  sessionId,
489
515
  update
@@ -491,18 +517,18 @@ function extractSessionUpdateNotification(message) {
491
517
  }
492
518
  function parsePromptStopReason(message) {
493
519
  if (!Object.hasOwn(message, "id") || !Object.hasOwn(message, "result")) return;
494
- const record = asRecord$4(message.result);
520
+ const record = asRecord$3(message.result);
495
521
  if (!record) return;
496
522
  return typeof record.stopReason === "string" ? record.stopReason : void 0;
497
523
  }
498
524
  function parsePermissionNotice(message) {
499
525
  if (!Object.hasOwn(message, "id") || !Object.hasOwn(message, "result")) return;
500
- const notice = asRecord$4(asRecord$4(asRecord$4(message.result)?._meta)?.acpx)?.permissionNotice;
526
+ const notice = asRecord$3(asRecord$3(asRecord$3(message.result)?._meta)?.acpx)?.permissionNotice;
501
527
  return typeof notice === "string" ? notice : void 0;
502
528
  }
503
529
  function parseJsonRpcErrorMessage(message) {
504
530
  if (!Object.hasOwn(message, "error")) return;
505
- const errorRecord = asRecord$4(message.error);
531
+ const errorRecord = asRecord$3(message.error);
506
532
  if (!errorRecord || typeof errorRecord.message !== "string") return;
507
533
  return errorRecord.message;
508
534
  }
@@ -1098,7 +1124,7 @@ function resolveAgentArgvForCommand(agentCommand) {
1098
1124
  }
1099
1125
  //#endregion
1100
1126
  //#region src/session/persistence/parse.ts
1101
- function asRecord$3(value) {
1127
+ function asRecord$2(value) {
1102
1128
  if (!value || typeof value !== "object" || Array.isArray(value)) return;
1103
1129
  return value;
1104
1130
  }
@@ -1117,12 +1143,12 @@ function parsePersistedAgentArgv(record) {
1117
1143
  function hasModelConfigOption(options) {
1118
1144
  if (!Array.isArray(options)) return false;
1119
1145
  return options.some((entry) => {
1120
- const option = asRecord$3(entry);
1146
+ const option = asRecord$2(entry);
1121
1147
  return option?.category === "model" || option?.id === "model";
1122
1148
  });
1123
1149
  }
1124
1150
  function parseConfigOptions(raw) {
1125
- if (!Array.isArray(raw) || !raw.every((entry) => asRecord$3(entry) !== void 0)) return;
1151
+ if (!Array.isArray(raw) || !raw.every((entry) => asRecord$2(entry) !== void 0)) return;
1126
1152
  return raw;
1127
1153
  }
1128
1154
  function parseAvailableCommand(raw) {
@@ -1130,7 +1156,7 @@ function parseAvailableCommand(raw) {
1130
1156
  const name = raw.trim();
1131
1157
  return name ? { name } : void 0;
1132
1158
  }
1133
- const record = asRecord$3(raw);
1159
+ const record = asRecord$2(raw);
1134
1160
  if (!record) return;
1135
1161
  const name = parseNonEmptyString$1(record.name);
1136
1162
  if (!name) return;
@@ -1153,7 +1179,7 @@ function parseNonEmptyString$1(value) {
1153
1179
  }
1154
1180
  function parseTokenUsage(raw) {
1155
1181
  if (raw === void 0 || raw === null) return;
1156
- const record = asRecord$3(raw);
1182
+ const record = asRecord$2(raw);
1157
1183
  if (!record) return null;
1158
1184
  const usage = {};
1159
1185
  for (const field of [
@@ -1176,7 +1202,7 @@ function isNonNegativeFiniteNumber(value) {
1176
1202
  }
1177
1203
  function parseUsageCost(raw) {
1178
1204
  if (raw === void 0 || raw === null) return;
1179
- const record = asRecord$3(raw);
1205
+ const record = asRecord$2(raw);
1180
1206
  if (!record) return null;
1181
1207
  return parseUsageCostRecord(record);
1182
1208
  }
@@ -1202,7 +1228,7 @@ function parseCostCurrency(value) {
1202
1228
  }
1203
1229
  function parseRequestTokenUsage(raw) {
1204
1230
  if (raw === void 0 || raw === null) return;
1205
- const record = asRecord$3(raw);
1231
+ const record = asRecord$2(raw);
1206
1232
  if (!record) return null;
1207
1233
  const usage = {};
1208
1234
  for (const [key, value] of Object.entries(record)) {
@@ -1213,25 +1239,25 @@ function parseRequestTokenUsage(raw) {
1213
1239
  return usage;
1214
1240
  }
1215
1241
  function isSessionMessageImage(raw) {
1216
- const record = asRecord$3(raw);
1242
+ const record = asRecord$2(raw);
1217
1243
  if (!record || typeof record.source !== "string") return false;
1218
1244
  if (record.size === void 0 || record.size === null) return true;
1219
- const size = asRecord$3(record.size);
1245
+ const size = asRecord$2(record.size);
1220
1246
  return !!size && isFiniteNumber(size.width) && isFiniteNumber(size.height);
1221
1247
  }
1222
1248
  function isSessionMessageAudio(raw) {
1223
- const record = asRecord$3(raw);
1249
+ const record = asRecord$2(raw);
1224
1250
  return !!record && typeof record.source === "string" && typeof record.mime_type === "string";
1225
1251
  }
1226
1252
  function isFiniteNumber(value) {
1227
1253
  return typeof value === "number" && Number.isFinite(value);
1228
1254
  }
1229
1255
  function isUserContent(raw) {
1230
- const record = asRecord$3(raw);
1256
+ const record = asRecord$2(raw);
1231
1257
  if (!record) return false;
1232
1258
  if (typeof record.Text === "string") return true;
1233
1259
  if (record.Mention !== void 0) {
1234
- const mention = asRecord$3(record.Mention);
1260
+ const mention = asRecord$2(record.Mention);
1235
1261
  return !!mention && typeof mention.uri === "string" && typeof mention.content === "string";
1236
1262
  }
1237
1263
  if (record.Image !== void 0) return isSessionMessageImage(record.Image);
@@ -1239,7 +1265,7 @@ function isUserContent(raw) {
1239
1265
  return false;
1240
1266
  }
1241
1267
  function isToolUse(raw) {
1242
- const record = asRecord$3(raw);
1268
+ const record = asRecord$2(raw);
1243
1269
  return !!record && hasStringFields$1(record, [
1244
1270
  "id",
1245
1271
  "name",
@@ -1253,18 +1279,18 @@ function isOptionalString(value) {
1253
1279
  return value === void 0 || value === null || typeof value === "string";
1254
1280
  }
1255
1281
  function isToolResultContent(raw) {
1256
- const record = asRecord$3(raw);
1282
+ const record = asRecord$2(raw);
1257
1283
  if (!record) return false;
1258
1284
  if (typeof record.Text === "string") return true;
1259
1285
  if (record.Image !== void 0) return isSessionMessageImage(record.Image);
1260
1286
  return false;
1261
1287
  }
1262
1288
  function isToolResult(raw) {
1263
- const record = asRecord$3(raw);
1289
+ const record = asRecord$2(raw);
1264
1290
  return !!record && typeof record.tool_use_id === "string" && typeof record.tool_name === "string" && typeof record.is_error === "boolean" && isToolResultContent(record.content);
1265
1291
  }
1266
1292
  function isAgentContent(raw) {
1267
- const record = asRecord$3(raw);
1293
+ const record = asRecord$2(raw);
1268
1294
  if (!record) return false;
1269
1295
  if (typeof record.Text === "string") return true;
1270
1296
  if (record.Thinking !== void 0) return isThinkingContent(record.Thinking);
@@ -1273,21 +1299,21 @@ function isAgentContent(raw) {
1273
1299
  return false;
1274
1300
  }
1275
1301
  function isThinkingContent(raw) {
1276
- const thinking = asRecord$3(raw);
1302
+ const thinking = asRecord$2(raw);
1277
1303
  return !!thinking && typeof thinking.text === "string" && isOptionalString(thinking.signature);
1278
1304
  }
1279
1305
  function isUserMessage(raw) {
1280
- const record = asRecord$3(raw);
1306
+ const record = asRecord$2(raw);
1281
1307
  if (!record || record.User === void 0) return false;
1282
- const user = asRecord$3(record.User);
1308
+ const user = asRecord$2(record.User);
1283
1309
  return !!user && typeof user.id === "string" && Array.isArray(user.content) && user.content.every((entry) => isUserContent(entry));
1284
1310
  }
1285
1311
  function isAgentMessage(raw) {
1286
- const record = asRecord$3(raw);
1312
+ const record = asRecord$2(raw);
1287
1313
  if (!record || record.Agent === void 0) return false;
1288
- const agent = asRecord$3(record.Agent);
1314
+ const agent = asRecord$2(record.Agent);
1289
1315
  if (!agent || !Array.isArray(agent.content) || !agent.content.every(isAgentContent)) return false;
1290
- const toolResults = asRecord$3(agent.tool_results);
1316
+ const toolResults = asRecord$2(agent.tool_results);
1291
1317
  if (!toolResults) return false;
1292
1318
  return Object.values(toolResults).every(isToolResult);
1293
1319
  }
@@ -1320,7 +1346,7 @@ function hasValidConversationCore(record) {
1320
1346
  return Array.isArray(record.messages) && record.messages.every(isConversationMessage) && typeof record.updated_at === "string";
1321
1347
  }
1322
1348
  function parseAcpxState(raw) {
1323
- const record = asRecord$3(raw);
1349
+ const record = asRecord$2(raw);
1324
1350
  if (!record) return;
1325
1351
  const state = {};
1326
1352
  if (record.reset_on_next_ensure === true) state.reset_on_next_ensure = true;
@@ -1348,13 +1374,13 @@ function assignStringState(state, key, value) {
1348
1374
  if (typeof value === "string") state[key] = value;
1349
1375
  }
1350
1376
  function parseStringMap(raw, preserveEmpty = false) {
1351
- const record = asRecord$3(raw);
1377
+ const record = asRecord$2(raw);
1352
1378
  if (!record) return;
1353
1379
  const parsed = Object.fromEntries(Object.entries(record).filter((entry) => typeof entry[1] === "string"));
1354
1380
  return Object.keys(parsed).length > 0 || preserveEmpty ? parsed : void 0;
1355
1381
  }
1356
1382
  function assignParsedSessionOptions(state, raw) {
1357
- const sessionOptions = asRecord$3(raw);
1383
+ const sessionOptions = asRecord$2(raw);
1358
1384
  if (!sessionOptions) return;
1359
1385
  const parsedSessionOptions = {};
1360
1386
  if (typeof sessionOptions.model === "string") parsedSessionOptions.model = sessionOptions.model;
@@ -1370,11 +1396,11 @@ function assignSessionOptionSystemPrompt(options, value) {
1370
1396
  options.system_prompt = value;
1371
1397
  return;
1372
1398
  }
1373
- const appendRecord = asRecord$3(value);
1399
+ const appendRecord = asRecord$2(value);
1374
1400
  if (appendRecord && typeof appendRecord.append === "string" && appendRecord.append.length > 0) options.system_prompt = { append: appendRecord.append };
1375
1401
  }
1376
1402
  function parseEventLog(raw, sessionId) {
1377
- const record = asRecord$3(raw);
1403
+ const record = asRecord$2(raw);
1378
1404
  if (!record || !hasValidEventLogCore(record)) return defaultSessionEventLog(sessionId);
1379
1405
  return {
1380
1406
  active_path: record.active_path,
@@ -1393,7 +1419,7 @@ function isPositiveInteger$1(value) {
1393
1419
  }
1394
1420
  function parseImportedFrom(raw) {
1395
1421
  if (raw == null) return;
1396
- const record = asRecord$3(raw);
1422
+ const record = asRecord$2(raw);
1397
1423
  if (!record || typeof record.record_id !== "string" || typeof record.cwd_original !== "string" || typeof record.exported_by !== "string" || typeof record.exported_at !== "string") return null;
1398
1424
  return {
1399
1425
  recordId: record.record_id,
@@ -1443,7 +1469,7 @@ function normalizeOptionalSignal(value) {
1443
1469
  return Symbol("invalid");
1444
1470
  }
1445
1471
  function parseSessionRecord(raw) {
1446
- const record = asRecord$3(raw);
1472
+ const record = asRecord$2(raw);
1447
1473
  if (!record) return null;
1448
1474
  if (record.schema !== "acpx.session.v1") return null;
1449
1475
  const optionals = validSessionOptionals({
@@ -1488,7 +1514,7 @@ function parseSessionRecord(raw) {
1488
1514
  lastAgentExitAt: optionals.lastAgentExitAt,
1489
1515
  lastAgentDisconnectReason: optionals.lastAgentDisconnectReason,
1490
1516
  protocolVersion: typeof record.protocol_version === "number" ? record.protocol_version : void 0,
1491
- agentCapabilities: asRecord$3(record.agent_capabilities),
1517
+ agentCapabilities: asRecord$2(record.agent_capabilities),
1492
1518
  title: conversation.title,
1493
1519
  messages: conversation.messages,
1494
1520
  updated_at: conversation.updated_at,
@@ -1601,111 +1627,28 @@ function assertPersistedKeyPolicy(value) {
1601
1627
  throw new Error(`Persisted key policy violation (expected snake_case keys): ${violations.join(", ")}`);
1602
1628
  }
1603
1629
  //#endregion
1604
- //#region src/session/persistence/index.ts
1605
- const SESSION_INDEX_SCHEMA = "acpx.session-index.v1";
1606
- function asRecord$2(value) {
1607
- if (!value || typeof value !== "object" || Array.isArray(value)) return;
1608
- return value;
1609
- }
1610
- function parseIndexEntry(raw) {
1611
- const record = asRecord$2(raw);
1612
- if (!record) return;
1613
- if (!hasRequiredIndexEntryFields(record)) return;
1614
- if (record.name !== void 0 && typeof record.name !== "string") return;
1615
- return {
1616
- file: record.file,
1617
- acpxRecordId: record.acpxRecordId,
1618
- acpSessionId: record.acpSessionId,
1619
- agentCommand: record.agentCommand,
1620
- cwd: record.cwd,
1621
- name: record.name,
1622
- closed: record.closed,
1623
- lastUsedAt: record.lastUsedAt
1624
- };
1625
- }
1626
- function hasRequiredIndexEntryFields(record) {
1627
- return [
1628
- "file",
1629
- "acpxRecordId",
1630
- "acpSessionId",
1631
- "agentCommand",
1632
- "cwd",
1633
- "lastUsedAt"
1634
- ].every((key) => typeof record[key] === "string") && typeof record.closed === "boolean";
1635
- }
1636
- function sessionIndexPath(sessionDir) {
1637
- return path.join(sessionDir, "index.json");
1638
- }
1639
- function toSessionIndexEntry(record, fileName) {
1640
- return {
1641
- file: fileName,
1642
- acpxRecordId: record.acpxRecordId,
1643
- acpSessionId: record.acpSessionId,
1644
- agentCommand: record.agentCommand,
1645
- cwd: record.cwd,
1646
- name: record.name,
1647
- closed: record.closed === true,
1648
- lastUsedAt: record.lastUsedAt
1649
- };
1630
+ //#region src/session/persistence/discovery.ts
1631
+ const DISCOVERY_BATCH_SIZE = 4;
1632
+ /** Select and return the same canonical snapshot; cached metadata cannot exclude records. */
1633
+ async function* scanSessionRecords(sessionDir) {
1634
+ const files = (await fs$1.readdir(sessionDir, { withFileTypes: true }).catch((error) => {
1635
+ if (error.code === "ENOENT") return [];
1636
+ throw error;
1637
+ })).filter((entry) => entry.isFile() && entry.name.endsWith(".json") && entry.name !== "index.json").map((entry) => entry.name).toSorted();
1638
+ for (let offset = 0; offset < files.length; offset += DISCOVERY_BATCH_SIZE) {
1639
+ const records = await Promise.all(files.slice(offset, offset + DISCOVERY_BATCH_SIZE).map((file) => readRecord(path.join(sessionDir, file))));
1640
+ for (const record of records) if (record) yield record;
1641
+ }
1650
1642
  }
1651
- async function readSessionIndex(sessionDir) {
1652
- const filePath = sessionIndexPath(sessionDir);
1643
+ async function readRecord(file) {
1653
1644
  try {
1654
- const payload = await fs$1.readFile(filePath, "utf8");
1655
- const record = asRecord$2(JSON.parse(payload));
1656
- if (!record || record.schema !== SESSION_INDEX_SCHEMA || !Array.isArray(record.files)) return;
1657
- const files = record.files.filter((entry) => typeof entry === "string");
1658
- if (files.length !== record.files.length || !Array.isArray(record.entries)) return;
1659
- const entries = record.entries.map((entry) => parseIndexEntry(entry)).filter((entry) => Boolean(entry));
1660
- if (entries.length !== record.entries.length) return;
1661
- return {
1662
- schema: SESSION_INDEX_SCHEMA,
1663
- files,
1664
- entries
1665
- };
1645
+ const payload = await fs$1.readFile(file, "utf8");
1646
+ const record = parseSessionRecord(JSON.parse(payload));
1647
+ return record && path.basename(file) === `${safeSessionId(record.acpxRecordId)}.json` ? record : void 0;
1666
1648
  } catch {
1667
1649
  return;
1668
1650
  }
1669
1651
  }
1670
- async function writeSessionIndex(sessionDir, index) {
1671
- await writePrivateJsonFile(sessionIndexPath(sessionDir), {
1672
- schema: SESSION_INDEX_SCHEMA,
1673
- files: index.files.toSorted(),
1674
- entries: index.entries.toSorted((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt))
1675
- });
1676
- }
1677
- async function rebuildSessionIndex(sessionDir) {
1678
- const index = await scanSessionIndex(sessionDir);
1679
- await writeSessionIndex(sessionDir, index);
1680
- return index;
1681
- }
1682
- async function scanSessionIndex(sessionDir) {
1683
- const files = await listSessionFiles(sessionDir).catch((error) => {
1684
- if (error.code === "ENOENT") return [];
1685
- throw error;
1686
- });
1687
- const indexEntries = [];
1688
- for (const file of files) try {
1689
- const payload = await fs$1.readFile(path.join(sessionDir, file), "utf8");
1690
- const parsed = parseSessionRecord(JSON.parse(payload));
1691
- if (!parsed) continue;
1692
- indexEntries.push(toSessionIndexEntry(parsed, file));
1693
- } catch {}
1694
- return {
1695
- schema: SESSION_INDEX_SCHEMA,
1696
- files,
1697
- entries: indexEntries.toSorted((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt))
1698
- };
1699
- }
1700
- async function loadOrRebuildSessionIndex(sessionDir) {
1701
- const files = await listSessionFiles(sessionDir);
1702
- const existing = await readSessionIndex(sessionDir);
1703
- if (existing && existing.files.length === files.length && existing.files.every((file, index) => file === files[index])) return existing;
1704
- return await rebuildSessionIndex(sessionDir);
1705
- }
1706
- async function listSessionFiles(sessionDir) {
1707
- return (await fs$1.readdir(sessionDir, { withFileTypes: true })).filter((entry) => entry.isFile() && entry.name.endsWith(".json") && entry.name !== "index.json").map((entry) => entry.name).toSorted();
1708
- }
1709
1652
  //#endregion
1710
1653
  //#region src/session/persistence/repository.ts
1711
1654
  const DEFAULT_HISTORY_LIMIT = 20;
@@ -1719,62 +1662,56 @@ async function ensureSessionDir() {
1719
1662
  mode: 448
1720
1663
  });
1721
1664
  }
1722
- async function loadRecordFromIndexEntry(entry) {
1723
- try {
1724
- const payload = await fs$1.readFile(path.join(sessionBaseDir(), entry.file), "utf8");
1725
- return parseSessionRecord(JSON.parse(payload)) ?? void 0;
1726
- } catch {
1727
- return;
1728
- }
1729
- }
1730
- async function loadSessionIndexEntries(readOnly = false) {
1665
+ async function* sessionRecords(readOnly = false) {
1731
1666
  if (!readOnly) await ensureSessionDir();
1732
- return (await measurePerf("session.index_load", async () => {
1733
- return await (readOnly ? scanSessionIndex : loadOrRebuildSessionIndex)(sessionBaseDir());
1734
- })).entries;
1667
+ yield* scanSessionRecords(sessionBaseDir());
1735
1668
  }
1736
- function matchesSessionEntry(session, normalizedCwd, normalizedName, includeClosed = false) {
1737
- if (session.cwd !== normalizedCwd) return false;
1669
+ function matchesSession(session, agentCommand, normalizedName, includeClosed = false) {
1670
+ if (session.agentCommand !== agentCommand) return false;
1738
1671
  if (!includeClosed && session.closed) return false;
1739
1672
  if (normalizedName == null) return session.name == null;
1740
1673
  return session.name === normalizedName;
1741
1674
  }
1675
+ function isNewer(record, previous) {
1676
+ return !previous || record.lastUsedAt.localeCompare(previous.lastUsedAt) > 0;
1677
+ }
1742
1678
  async function writeSessionRecord(record) {
1743
1679
  await measurePerf("session.write_record", async () => {
1744
1680
  const persisted = serializeSessionRecordForDisk(record);
1745
1681
  assertPersistedKeyPolicy(persisted);
1746
- const file = sessionFilePath(record.acpxRecordId);
1747
- await writePrivateJsonFile(file, persisted);
1748
- const sessionDir = sessionBaseDir();
1749
- const index = await loadOrRebuildSessionIndex(sessionDir);
1750
- const fileName = path.basename(file);
1751
- const entries = index.entries.filter((entry) => entry.file !== fileName);
1752
- entries.push(toSessionIndexEntry(record, fileName));
1753
- await writeSessionIndex(sessionDir, {
1754
- files: [.../* @__PURE__ */ new Set([...index.files.filter((entry) => entry !== fileName), fileName])],
1755
- entries
1756
- });
1682
+ await writePrivateJsonFile(sessionFilePath(record.acpxRecordId), persisted);
1757
1683
  });
1758
1684
  }
1759
1685
  async function resolveSessionRecord(sessionId) {
1760
1686
  await ensureSessionDir();
1761
1687
  try {
1762
1688
  const directRecord = await measurePerf("session.resolve_direct", async () => await readSessionRecord(sessionId));
1763
- if (directRecord) return directRecord;
1689
+ if (directRecord?.acpxRecordId === sessionId) return directRecord;
1764
1690
  } catch {}
1765
- const entries = await loadSessionIndexEntries();
1766
- const exactEntries = entries.filter((entry) => entry.acpxRecordId === sessionId || entry.acpSessionId === sessionId);
1767
- const exactRecords = (await Promise.all(exactEntries.map((entry) => loadRecordFromIndexEntry(entry)))).filter((entry) => Boolean(entry));
1691
+ const { exactRecords, suffixRecords } = await findSessionIdMatches(sessionId);
1768
1692
  if (exactRecords.length === 1) return exactRecords[0];
1769
1693
  if (exactRecords.length > 1) throw new SessionResolutionError(`Multiple sessions match id: ${sessionId}`);
1770
- const suffixEntries = entries.filter((entry) => entry.acpxRecordId.endsWith(sessionId) || entry.acpSessionId.endsWith(sessionId));
1771
- const suffixRecords = (await Promise.all(suffixEntries.map((entry) => loadRecordFromIndexEntry(entry)))).filter((entry) => Boolean(entry));
1772
1694
  if (suffixRecords.length === 1) return suffixRecords[0];
1773
1695
  if (suffixRecords.length > 1) throw new SessionResolutionError(`Session id is ambiguous: ${sessionId}`);
1774
1696
  incrementPerfCounter("session.resolve_miss");
1775
1697
  throw new SessionNotFoundError(sessionId);
1776
1698
  }
1777
- /** Reads a canonical record without creating directories or rebuilding the lookup index. */
1699
+ async function findSessionIdMatches(sessionId) {
1700
+ const exactRecords = [];
1701
+ const suffixRecords = [];
1702
+ for await (const record of sessionRecords()) {
1703
+ if (record.acpxRecordId === sessionId || record.acpSessionId === sessionId) retainMatch(exactRecords, record);
1704
+ if (record.acpxRecordId.endsWith(sessionId) || record.acpSessionId.endsWith(sessionId)) retainMatch(suffixRecords, record);
1705
+ }
1706
+ return {
1707
+ exactRecords,
1708
+ suffixRecords
1709
+ };
1710
+ }
1711
+ function retainMatch(matches, record) {
1712
+ if (matches.length < 2) matches.push(record);
1713
+ }
1714
+ /** Reads one canonical record without creating directories or scanning other sessions. */
1778
1715
  async function readSessionRecord(sessionId) {
1779
1716
  let payload;
1780
1717
  try {
@@ -1784,7 +1721,8 @@ async function readSessionRecord(sessionId) {
1784
1721
  throw error;
1785
1722
  }
1786
1723
  try {
1787
- return parseSessionRecord(JSON.parse(payload)) ?? void 0;
1724
+ const record = parseSessionRecord(JSON.parse(payload));
1725
+ return record?.acpxRecordId === sessionId ? record : void 0;
1788
1726
  } catch {
1789
1727
  return;
1790
1728
  }
@@ -1824,42 +1762,49 @@ function isoNow() {
1824
1762
  return (/* @__PURE__ */ new Date()).toISOString();
1825
1763
  }
1826
1764
  async function listSessions() {
1827
- await ensureSessionDir();
1828
- const entries = await loadSessionIndexEntries();
1829
- const records = [];
1830
- for (const entry of entries) {
1831
- const parsed = await loadRecordFromIndexEntry(entry);
1832
- if (parsed) records.push(parsed);
1833
- }
1834
- records.sort((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt));
1835
- return records;
1765
+ return await collectSessionRecords();
1836
1766
  }
1837
1767
  async function listSessionsForAgent(agentCommand) {
1838
- const entries = (await loadSessionIndexEntries()).filter((session) => session.agentCommand === agentCommand);
1839
- return (await Promise.all(entries.map((entry) => loadRecordFromIndexEntry(entry)))).filter((entry) => Boolean(entry)).toSorted((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt));
1768
+ return await collectSessionRecords(agentCommand);
1769
+ }
1770
+ async function collectSessionRecords(agentCommand) {
1771
+ const records = [];
1772
+ for await (const record of sessionRecords()) if (agentCommand === void 0 || record.agentCommand === agentCommand) records.push(record);
1773
+ return records.toSorted((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt));
1840
1774
  }
1841
1775
  async function findSession(options) {
1842
1776
  const normalizedCwd = absolutePath(options.cwd);
1843
1777
  const normalizedName = normalizeName(options.name);
1844
- const match = (await loadSessionIndexEntries(options.readOnly)).find((session) => session.agentCommand === options.agentCommand && matchesSessionEntry(session, normalizedCwd, normalizedName, options.includeClosed));
1845
- if (!match) return;
1846
- return await loadRecordFromIndexEntry(match);
1778
+ let match;
1779
+ for await (const record of sessionRecords(options.readOnly)) if (record.cwd === normalizedCwd && matchesSession(record, options.agentCommand, normalizedName, options.includeClosed) && isNewer(record, match)) match = record;
1780
+ return match;
1847
1781
  }
1848
1782
  async function findSessionByDirectoryWalk(options) {
1849
1783
  const normalizedName = normalizeName(options.name);
1784
+ const directories = walkDirectories(options);
1785
+ let match;
1786
+ let distance = Infinity;
1787
+ for await (const record of sessionRecords()) {
1788
+ const candidateDistance = directories.get(record.cwd);
1789
+ if (candidateDistance !== void 0 && matchesSession(record, options.agentCommand, normalizedName) && (candidateDistance < distance || candidateDistance === distance && isNewer(record, match))) {
1790
+ match = record;
1791
+ distance = candidateDistance;
1792
+ }
1793
+ }
1794
+ return match;
1795
+ }
1796
+ function walkDirectories(options) {
1850
1797
  const normalizedStart = absolutePath(options.cwd);
1851
1798
  const normalizedBoundary = absolutePath(options.boundary ?? normalizedStart);
1852
1799
  const walkBoundary = isWithinBoundary(normalizedBoundary, normalizedStart) ? normalizedBoundary : normalizedStart;
1853
- const sessions = (await loadSessionIndexEntries()).filter((session) => session.agentCommand === options.agentCommand);
1800
+ const directories = /* @__PURE__ */ new Map();
1801
+ const walkRoot = path.parse(normalizedStart).root;
1854
1802
  let current = normalizedStart;
1855
- const walkRoot = path.parse(current).root;
1856
- for (;;) {
1857
- const match = sessions.find((session) => matchesSessionEntry(session, current, normalizedName));
1858
- if (match) return await loadRecordFromIndexEntry(match);
1859
- const parent = nextWalkParent(current, walkBoundary, walkRoot);
1860
- if (!parent) return;
1861
- current = parent;
1803
+ while (current) {
1804
+ directories.set(current, directories.size);
1805
+ current = nextWalkParent(current, walkBoundary, walkRoot);
1862
1806
  }
1807
+ return directories;
1863
1808
  }
1864
1809
  function nextWalkParent(current, walkBoundary, walkRoot) {
1865
1810
  if (current === walkBoundary || current === walkRoot) return;
@@ -1871,11 +1816,11 @@ function closedAtOrLastUsedAt(record) {
1871
1816
  return record.closedAt ?? record.lastUsedAt;
1872
1817
  }
1873
1818
  function isSessionStreamFile(fileName, safeId) {
1874
- return fileName === `${safeId}.stream.ndjson` || fileName === `${safeId}.stream.lock` || fileName.startsWith(`${safeId}.stream.`);
1819
+ const segmentPrefix = `${safeId}.stream.`;
1820
+ return fileName === `${safeId}.stream.ndjson` || fileName === `${safeId}.stream.lock` || fileName.startsWith(segmentPrefix) && /^\d+\.ndjson$/.test(fileName.slice(segmentPrefix.length));
1875
1821
  }
1876
1822
  async function pruneSessions(options = {}) {
1877
- await ensureSessionDir();
1878
- const records = await loadPrunableRecords(filterPruneCandidates(await loadSessionIndexEntries(), options.agentCommand), options.before ?? (options.olderThanMs != null ? new Date(Date.now() - options.olderThanMs) : void 0));
1823
+ const records = await loadPrunableRecords(options.before ?? (options.olderThanMs != null ? new Date(Date.now() - options.olderThanMs) : void 0), options.agentCommand);
1879
1824
  if (options.dryRun) return {
1880
1825
  pruned: records,
1881
1826
  bytesFreed: 0,
@@ -1888,24 +1833,20 @@ async function pruneSessions(options = {}) {
1888
1833
  dirEntries = await fs$1.readdir(sessionDir);
1889
1834
  } catch {}
1890
1835
  for (const record of records) bytesFreed += await pruneSessionFiles(record, sessionDir, dirEntries, options.includeHistory === true);
1891
- await rebuildSessionIndex(sessionDir).catch(() => {});
1892
1836
  return {
1893
1837
  pruned: records,
1894
1838
  bytesFreed,
1895
1839
  dryRun: false
1896
1840
  };
1897
1841
  }
1898
- function filterPruneCandidates(entries, agentCommand) {
1899
- return entries.filter((entry) => entry.closed && (!agentCommand || entry.agentCommand === agentCommand));
1842
+ function isPruneCandidate(record, agentCommand) {
1843
+ return record.closed === true && (!agentCommand || record.agentCommand === agentCommand);
1900
1844
  }
1901
- async function loadPrunableRecords(entries, cutoff) {
1845
+ async function loadPrunableRecords(cutoff, agentCommand) {
1902
1846
  const records = [];
1903
1847
  const cutoffIso = cutoff?.toISOString();
1904
- for (const entry of entries) {
1905
- const record = await loadRecordFromIndexEntry(entry);
1906
- if (record && isBeforeCutoff(record, cutoffIso)) records.push(record);
1907
- }
1908
- return records;
1848
+ for await (const record of sessionRecords()) if (isPruneCandidate(record, agentCommand) && isBeforeCutoff(record, cutoffIso)) records.push(record);
1849
+ return records.toSorted((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt));
1909
1850
  }
1910
1851
  function isBeforeCutoff(record, cutoffIso) {
1911
1852
  return !cutoffIso || closedAtOrLastUsedAt(record) < cutoffIso;
@@ -1963,12 +1904,12 @@ function isAudioBlock(value) {
1963
1904
  }
1964
1905
  function isResourceLinkBlock(value) {
1965
1906
  const record = asRecord$1(value);
1966
- return record?.type === "resource_link" && isNonEmptyString(record.uri) && (record.title === void 0 || typeof record.title === "string") && (record.name === void 0 || typeof record.name === "string");
1907
+ return record?.type === "resource_link" && isNonEmptyString(record.uri) && (record.title == null || typeof record.title === "string") && typeof record.name === "string";
1967
1908
  }
1968
1909
  function isResourcePayload(value) {
1969
1910
  const record = asRecord$1(value);
1970
1911
  if (!record || !isNonEmptyString(record.uri)) return false;
1971
- return record.text === void 0 || typeof record.text === "string";
1912
+ return typeof record.text === "string" || typeof record.blob === "string";
1972
1913
  }
1973
1914
  function isResourceBlock(value) {
1974
1915
  const record = asRecord$1(value);
@@ -2011,12 +1952,12 @@ function validateAudioContentBlock(record, index) {
2011
1952
  }
2012
1953
  function validateResourceLinkContentBlock(record, index) {
2013
1954
  if (!isNonEmptyString(record.uri)) return `prompt[${index}] resource_link block must include a non-empty uri`;
2014
- if (record.title !== void 0 && typeof record.title !== "string") return `prompt[${index}] resource_link block title must be a string when present`;
2015
- if (record.name !== void 0 && typeof record.name !== "string") return `prompt[${index}] resource_link block name must be a string when present`;
1955
+ if (record.title != null && typeof record.title !== "string") return `prompt[${index}] resource_link block title must be a string or null when present`;
1956
+ if (typeof record.name !== "string") return `prompt[${index}] resource_link block must include a string name`;
2016
1957
  }
2017
1958
  function validateResourceContentBlock(record, index) {
2018
1959
  if (!asRecord$1(record.resource)) return `prompt[${index}] resource block must include a resource object`;
2019
- return isResourcePayload(record.resource) ? void 0 : `prompt[${index}] resource block resource must include a non-empty uri and optional text`;
1960
+ return isResourcePayload(record.resource) ? void 0 : `prompt[${index}] resource block resource must include a non-empty uri and a string text or blob field`;
2020
1961
  }
2021
1962
  function getContentBlockValidationError(value, index) {
2022
1963
  const record = asRecord$1(value);
@@ -2101,6 +2042,318 @@ function resourceBlockDisplayText(block) {
2101
2042
  return "text" in block.resource && typeof block.resource.text === "string" ? block.resource.text : block.resource.uri;
2102
2043
  }
2103
2044
  //#endregion
2045
+ //#region src/process-identity.ts
2046
+ const IDENTITY_QUERY_TIMEOUT_MS$1 = 2e3;
2047
+ const LINUX_PROCESS_QUERY = `
2048
+ const fs = require('node:fs/promises');
2049
+ const [targetPid, observerPid] = process.argv.slice(1);
2050
+ if (process.ppid !== Number(observerPid)) process.exit(1);
2051
+ const timeNamespace = (pid) => fs.readlink('/proc/' + pid + '/ns/time').catch((error) => {
2052
+ if (error.code === 'ENOENT') return 'unsupported';
2053
+ throw error;
2054
+ });
2055
+ Promise.all([
2056
+ fs.readFile('/proc/sys/kernel/random/boot_id', 'utf8'),
2057
+ fs.readlink('/proc/self/ns/pid'),
2058
+ timeNamespace('self'),
2059
+ fs.readlink('/proc/' + observerPid + '/ns/pid'),
2060
+ timeNamespace(observerPid),
2061
+ fs.readFile('/proc/self/stat', 'utf8'),
2062
+ fs.readFile('/proc/' + targetPid + '/stat', 'utf8').catch(() => null),
2063
+ ]).then(([bootId, pidNamespace, timeNamespace, observerPidNamespace,
2064
+ observerTimeNamespace, selfStat, targetStat]) => {
2065
+ process.stdout.write(JSON.stringify({bootId: bootId.trim(), pidNamespace,
2066
+ timeNamespace, observerPidNamespace, observerTimeNamespace,
2067
+ helperPid: process.pid, selfStat, targetStat}));
2068
+ }).catch(() => { process.exitCode = 1; });
2069
+ `;
2070
+ const POSIX_MONTHS = [
2071
+ "Jan",
2072
+ "Feb",
2073
+ "Mar",
2074
+ "Apr",
2075
+ "May",
2076
+ "Jun",
2077
+ "Jul",
2078
+ "Aug",
2079
+ "Sep",
2080
+ "Oct",
2081
+ "Nov",
2082
+ "Dec"
2083
+ ];
2084
+ let ownIdentity;
2085
+ let ownIdentityPending;
2086
+ function parseProcessBirthIdentity(raw) {
2087
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
2088
+ const identity = raw;
2089
+ if (identity.kind === "linux-proc") return parseLinuxIdentity(identity);
2090
+ if (identity.kind !== "posix-lstart" && identity.kind !== "windows-creation") return;
2091
+ if (!isCanonicalBirth(identity.value, identity.kind)) return;
2092
+ return {
2093
+ kind: identity.kind,
2094
+ value: identity.value
2095
+ };
2096
+ }
2097
+ function isCanonicalBirth(value, kind) {
2098
+ if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3,7}Z$/.test(value) || !Number.isFinite(Date.parse(value))) return false;
2099
+ const canonical = new Date(value).toISOString();
2100
+ return canonical.slice(0, 19) === value.slice(0, 19) && (kind === "posix-lstart" ? value === canonical && value.endsWith(".000Z") : /\.\d{7}Z$/.test(value));
2101
+ }
2102
+ function parseLinuxScope(raw) {
2103
+ const { bootId, pidNamespace, timeNamespace } = raw;
2104
+ if (typeof bootId !== "string" || !/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/.test(bootId) || typeof pidNamespace !== "string" || !/^pid:\[[1-9]\d*\]$/.test(pidNamespace) || !isTimeNamespace(timeNamespace)) return;
2105
+ return {
2106
+ bootId,
2107
+ pidNamespace,
2108
+ timeNamespace
2109
+ };
2110
+ }
2111
+ function isTimeNamespace(value) {
2112
+ return typeof value === "string" && (value === "unsupported" || /^time:\[[1-9]\d*\]$/.test(value));
2113
+ }
2114
+ function isStartTicks(value) {
2115
+ return typeof value === "string" && /^(?:0|[1-9]\d*)$/.test(value);
2116
+ }
2117
+ function parseLinuxIdentity(raw) {
2118
+ const scope = parseLinuxScope(raw);
2119
+ if (!scope || !isStartTicks(raw.startTicks)) return;
2120
+ return {
2121
+ kind: "linux-proc",
2122
+ ...scope,
2123
+ startTicks: raw.startTicks
2124
+ };
2125
+ }
2126
+ function compareLinuxScope(expected, observed) {
2127
+ if (expected.bootId !== observed.bootId) return "different";
2128
+ if (expected.pidNamespace !== observed.pidNamespace || expected.timeNamespace !== observed.timeNamespace) return "unknown";
2129
+ return "matching";
2130
+ }
2131
+ function compareLinuxIdentity(expected, observed) {
2132
+ const scope = compareLinuxScope(expected, observed);
2133
+ return scope === "matching" ? expected.startTicks === observed.startTicks ? "matching" : "different" : scope;
2134
+ }
2135
+ function compareProcessBirthIdentity(expected, observed) {
2136
+ if (!expected || expected.kind !== observed.kind) return "unknown";
2137
+ if (expected.kind === "linux-proc" && observed.kind === "linux-proc") return compareLinuxIdentity(expected, observed);
2138
+ if ("value" in expected && "value" in observed) return expected.value === observed.value ? "matching" : "different";
2139
+ return "unknown";
2140
+ }
2141
+ function parseLinuxStat(raw) {
2142
+ if (typeof raw !== "string") return;
2143
+ const prefix = /^(\d+) \(/.exec(raw);
2144
+ const close = raw.lastIndexOf(")");
2145
+ if (!prefix || close < 0) return;
2146
+ const pid = Number(prefix[1]);
2147
+ const fields = raw.slice(close + 2).trim().split(/\s+/u);
2148
+ const startTicks = fields[19];
2149
+ if (!isPositivePid(pid) || !isStartTicks(startTicks) || !/^[RSDTtKWPI]$/.test(fields[0] ?? "")) return;
2150
+ return {
2151
+ pid,
2152
+ startTicks
2153
+ };
2154
+ }
2155
+ async function readLinuxObservation(pid, timeoutMs) {
2156
+ const output = await runTimedExecFile(process.execPath, [
2157
+ "--input-type=commonjs",
2158
+ "-e",
2159
+ LINUX_PROCESS_QUERY,
2160
+ String(pid),
2161
+ String(process.pid)
2162
+ ], {
2163
+ timeoutMs,
2164
+ maxBufferBytes: 8192,
2165
+ env: {
2166
+ ...process.env,
2167
+ NODE_OPTIONS: ""
2168
+ }
2169
+ });
2170
+ const raw = JSON.parse(output);
2171
+ const scope = parseLinuxScope(raw);
2172
+ const self = parseLinuxStat(raw.selfStat);
2173
+ if (!scope || !self || self.pid !== raw.helperPid || scope.pidNamespace !== raw.observerPidNamespace || scope.timeNamespace !== raw.observerTimeNamespace) return;
2174
+ const target = parseLinuxStat(raw.targetStat);
2175
+ return {
2176
+ scope,
2177
+ ...target?.pid === pid ? { identity: {
2178
+ kind: "linux-proc",
2179
+ ...scope,
2180
+ startTicks: target.startTicks
2181
+ } } : {}
2182
+ };
2183
+ }
2184
+ function isPositivePid(pid) {
2185
+ return Number.isSafeInteger(pid) && pid > 0;
2186
+ }
2187
+ function windowsPowerShellPath() {
2188
+ const systemRoot = process.env.SystemRoot;
2189
+ if (!systemRoot || !path.win32.isAbsolute(systemRoot)) throw new Error("Windows system directory is unavailable");
2190
+ return path.win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
2191
+ }
2192
+ function windowsProcessQuery(pid) {
2193
+ return [
2194
+ "$ErrorActionPreference = 'Stop'",
2195
+ `Get-CimInstance -ClassName Win32_Process -Property ProcessId,ParentProcessId,CreationDate${pid === void 0 ? "" : ` -Filter 'ProcessId = ${pid}'`} | ForEach-Object {`,
2196
+ "if ($null -ne $_.CreationDate) {",
2197
+ "'{0} {1} 0 S {2}' -f $_.ProcessId,$_.ParentProcessId,$_.CreationDate.ToUniversalTime().ToString('o')",
2198
+ "}",
2199
+ "}"
2200
+ ].join("\n");
2201
+ }
2202
+ function posixBirth(value) {
2203
+ const match = /^\w{3}\s+(\w{3})\s+(\d{1,2})\s+(\d{2}):(\d{2}):(\d{2})\s+(\d{4})$/.exec(value);
2204
+ if (!match) return;
2205
+ const month = POSIX_MONTHS.indexOf(match[1]);
2206
+ if (month < 0) return;
2207
+ const valueUtc = `${match[6]}-${String(month + 1).padStart(2, "0")}-${match[2].padStart(2, "0")}T${match[3]}:${match[4]}:${match[5]}.000Z`;
2208
+ return isCanonicalBirth(valueUtc, "posix-lstart") ? valueUtc : void 0;
2209
+ }
2210
+ function parseProcessTable(output) {
2211
+ const table = /* @__PURE__ */ new Map();
2212
+ for (const line of output.split("\n")) {
2213
+ const match = /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(.+?)\s*$/.exec(line);
2214
+ if (!match || match[4].startsWith("Z")) continue;
2215
+ const pid = Number(match[1]);
2216
+ const birth = processTableBirth(match[5]);
2217
+ if (Number.isSafeInteger(pid) && pid > 0 && birth) table.set(pid, {
2218
+ pid,
2219
+ parentPid: Number(match[2]),
2220
+ groupPid: Number(match[3]),
2221
+ birth
2222
+ });
2223
+ }
2224
+ return table;
2225
+ }
2226
+ function processTableBirth(value) {
2227
+ if (process.platform !== "win32") return posixBirth(value);
2228
+ return isCanonicalBirth(value, "windows-creation") ? value : void 0;
2229
+ }
2230
+ async function readProcessTable(timeoutMs, pid) {
2231
+ if (pid !== void 0 && (!Number.isSafeInteger(pid) || pid <= 0)) throw new Error("Process identity requires a positive PID");
2232
+ if (process.platform === "win32") return parseProcessTable(await runTimedExecFile(windowsPowerShellPath(), [
2233
+ "-NoLogo",
2234
+ "-NoProfile",
2235
+ "-NonInteractive",
2236
+ "-Command",
2237
+ windowsProcessQuery(pid)
2238
+ ], {
2239
+ timeoutMs,
2240
+ windowsHide: true
2241
+ }));
2242
+ return parseProcessTable(await runTimedExecFile("ps", [
2243
+ ...pid === void 0 ? ["-e"] : ["-p", String(pid)],
2244
+ "-o",
2245
+ "pid=,ppid=,pgid=,stat=,lstart="
2246
+ ], {
2247
+ timeoutMs,
2248
+ env: {
2249
+ ...process.env,
2250
+ LC_ALL: "C",
2251
+ TZ: "UTC"
2252
+ }
2253
+ }));
2254
+ }
2255
+ async function probeProcessIdentity(pid, timeoutMs = IDENTITY_QUERY_TIMEOUT_MS$1) {
2256
+ if (!isPositivePid(pid)) return { state: "unknown" };
2257
+ if (isProcessDefinitelyDead(pid)) return { state: "dead" };
2258
+ try {
2259
+ const identity = await readNativeIdentity(pid, timeoutMs);
2260
+ if (identity) return {
2261
+ state: "alive",
2262
+ identity
2263
+ };
2264
+ } catch {}
2265
+ return { state: isProcessDefinitelyDead(pid) ? "dead" : "unknown" };
2266
+ }
2267
+ async function readNativeIdentity(pid, timeoutMs) {
2268
+ if (process.platform === "linux") return (await readLinuxObservation(pid, timeoutMs))?.identity;
2269
+ const entry = (await readProcessTable(timeoutMs, pid)).get(pid);
2270
+ return entry ? processBirthIdentity(entry.birth) : void 0;
2271
+ }
2272
+ function processBirthIdentity(value) {
2273
+ return {
2274
+ kind: process.platform === "win32" ? "windows-creation" : "posix-lstart",
2275
+ value
2276
+ };
2277
+ }
2278
+ function nativeIdentityKind() {
2279
+ if (process.platform === "linux") return "linux-proc";
2280
+ return process.platform === "win32" ? "windows-creation" : "posix-lstart";
2281
+ }
2282
+ function comparisonToIncarnation(comparison) {
2283
+ return comparison === "different" ? "gone" : comparison;
2284
+ }
2285
+ async function observeLinuxIncarnation(pid, expected, timeoutMs) {
2286
+ const observed = await readLinuxObservation(pid, timeoutMs).catch(() => void 0);
2287
+ if (!observed) return "unknown";
2288
+ const scope = compareLinuxScope(expected, observed.scope);
2289
+ if (scope !== "matching") return comparisonToIncarnation(scope);
2290
+ if (isProcessDefinitelyDead(pid)) return "gone";
2291
+ return observed.identity ? comparisonToIncarnation(compareProcessBirthIdentity(expected, observed.identity)) : "unknown";
2292
+ }
2293
+ async function observeProcessIncarnation(pid, expected, timeoutMs = IDENTITY_QUERY_TIMEOUT_MS$1) {
2294
+ if (!isPositivePid(pid)) return "unknown";
2295
+ if (expected?.kind === "linux-proc") return process.platform === "linux" ? await observeLinuxIncarnation(pid, expected, timeoutMs) : "unknown";
2296
+ if (expected && expected.kind !== nativeIdentityKind()) return "unknown";
2297
+ return await observeTimestampIncarnation(pid, expected, timeoutMs);
2298
+ }
2299
+ async function observeTimestampIncarnation(pid, expected, timeoutMs) {
2300
+ if (isProcessDefinitelyDead(pid)) return "gone";
2301
+ if (!expected) return "unknown";
2302
+ const observed = await probeProcessIdentity(pid, timeoutMs);
2303
+ if (observed.state === "dead") return "gone";
2304
+ return observed.state === "alive" ? comparisonToIncarnation(compareProcessBirthIdentity(expected, observed.identity)) : "unknown";
2305
+ }
2306
+ async function getOwnProcessIdentity() {
2307
+ if (ownIdentity) return ownIdentity;
2308
+ ownIdentityPending ??= probeProcessIdentity(process.pid).then((probe) => {
2309
+ if (probe.state === "alive") ownIdentity = probe.identity;
2310
+ return ownIdentity;
2311
+ }).finally(() => {
2312
+ ownIdentityPending = void 0;
2313
+ });
2314
+ return await ownIdentityPending;
2315
+ }
2316
+ //#endregion
2317
+ //#region src/session/lock-owner.ts
2318
+ const OBSERVATION_REUSE_MS = 1e3;
2319
+ const IDENTITY_QUERY_TIMEOUT_MS = 1e3;
2320
+ function lockOwnerPid(value) {
2321
+ if (!value || typeof value !== "object" || !("pid" in value)) return;
2322
+ const pid = value.pid;
2323
+ return typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? pid : void 0;
2324
+ }
2325
+ function canReuseObservation(preserved, pid, identity, fresh) {
2326
+ return !fresh && preserved?.pid === pid && compareProcessBirthIdentity(preserved.identity, identity) === "matching" && performance.now() - preserved.checkedAt < OBSERVATION_REUSE_MS;
2327
+ }
2328
+ async function createLockOwner(options = {}) {
2329
+ const { signal, capturedIdentity } = options;
2330
+ signal?.throwIfAborted();
2331
+ const processIdentity = capturedIdentity === void 0 ? await getOwnProcessIdentity() : capturedIdentity.processIdentity;
2332
+ signal?.throwIfAborted();
2333
+ let preserved;
2334
+ return {
2335
+ payload: {
2336
+ pid: process.pid,
2337
+ processIdentity
2338
+ },
2339
+ async hasExited(payload, fresh, signal) {
2340
+ signal?.throwIfAborted();
2341
+ const pid = lockOwnerPid(payload);
2342
+ if (!pid) return false;
2343
+ const identity = parseProcessBirthIdentity(payload.processIdentity);
2344
+ if (identity && canReuseObservation(preserved, pid, identity, fresh)) return false;
2345
+ const incarnation = await observeProcessIncarnation(pid, identity, IDENTITY_QUERY_TIMEOUT_MS);
2346
+ signal?.throwIfAborted();
2347
+ if (identity && incarnation !== "gone") preserved = {
2348
+ pid,
2349
+ identity,
2350
+ checkedAt: performance.now()
2351
+ };
2352
+ return incarnation === "gone";
2353
+ }
2354
+ };
2355
+ }
2356
+ //#endregion
2104
2357
  //#region src/session/queue/paths.ts
2105
2358
  function shortHash(value, length) {
2106
2359
  return createHash("sha256").update(value).digest("hex").slice(0, length);
@@ -2124,6 +2377,156 @@ function queueSocketPath(sessionId, homeDir = os.homedir()) {
2124
2377
  return path.join(queueSocketBaseDir(homeDir) ?? "/tmp", `${key}.sock`);
2125
2378
  }
2126
2379
  //#endregion
2380
+ //#region src/session/queue/lease-mutation.ts
2381
+ const GUARD_WAIT_MS = 2e3;
2382
+ const localMutations = /* @__PURE__ */ new Map();
2383
+ var QueueLeaseGuardSettlementError = class extends QueueConnectionError {
2384
+ constructor(errors) {
2385
+ super("Queue lease guard settlement failed; cleanup remains retryable", {
2386
+ detailCode: "QUEUE_LEASE_GUARD_SETTLEMENT_FAILED",
2387
+ origin: "queue",
2388
+ retryable: false,
2389
+ cause: new AggregateError(errors, "Queue lease mutation and settlement errors")
2390
+ });
2391
+ }
2392
+ };
2393
+ async function canonicalLeasePath(sessionId) {
2394
+ const requested = queueLockFilePath(sessionId);
2395
+ try {
2396
+ return path.join(await fs$1.realpath(path.dirname(requested)), path.basename(requested));
2397
+ } catch (error) {
2398
+ if (error.code === "ENOENT") return path.resolve(requested);
2399
+ throw error;
2400
+ }
2401
+ }
2402
+ async function acquireGuard(lockPath, owner) {
2403
+ return await acquireFileLock(lockPath, {
2404
+ managerKey: "acpx.queue-lease-mutation",
2405
+ lockPath: `${lockPath}.guard`,
2406
+ staleMs: Infinity,
2407
+ timeoutMs: GUARD_WAIT_MS,
2408
+ retry: {
2409
+ minTimeout: 5,
2410
+ maxTimeout: 15,
2411
+ factor: 1,
2412
+ randomize: false
2413
+ },
2414
+ staleRecovery: "remove-if-unchanged",
2415
+ payload: () => owner.payload,
2416
+ shouldReclaim: ({ payload }) => owner.hasExited(payload),
2417
+ shouldRemoveStaleLock: ({ payload }) => owner.hasExited(payload, true)
2418
+ });
2419
+ }
2420
+ function matchesReservation(value, expected) {
2421
+ if (!value || typeof value !== "object") return false;
2422
+ const record = value;
2423
+ return record.pid === expected.pid && record.sessionId === expected.sessionId && record.ownerGeneration === expected.ownerGeneration;
2424
+ }
2425
+ async function rollbackReservation(lockPath, expected) {
2426
+ try {
2427
+ if (!(await fs$1.lstat(lockPath)).isFile()) return;
2428
+ const raw = await fs$1.readFile(lockPath, "utf8");
2429
+ let value;
2430
+ try {
2431
+ value = JSON.parse(raw);
2432
+ } catch {
2433
+ return;
2434
+ }
2435
+ if (matchesReservation(value, expected)) await fs$1.unlink(lockPath);
2436
+ } catch (error) {
2437
+ if (error.code !== "ENOENT") throw error;
2438
+ }
2439
+ }
2440
+ async function settleReceipt(lockPath, receipt) {
2441
+ if (receipt.reservation) {
2442
+ if (!await receipt.guard.verifyStillHeld()) {
2443
+ await receipt.guard.release();
2444
+ receipt.guard = await acquireGuard(lockPath, receipt.owner);
2445
+ }
2446
+ await rollbackReservation(lockPath, receipt.reservation);
2447
+ receipt.reservation = void 0;
2448
+ }
2449
+ await receipt.guard.release();
2450
+ }
2451
+ async function settleFailedGuard(lockPath, state) {
2452
+ const failed = state.failed;
2453
+ if (!failed) return;
2454
+ try {
2455
+ await settleReceipt(lockPath, failed);
2456
+ if (state.failed === failed) state.failed = void 0;
2457
+ } catch (error) {
2458
+ throw new QueueLeaseGuardSettlementError([error]);
2459
+ }
2460
+ }
2461
+ async function locallySerialized(lockPath, run) {
2462
+ let state = localMutations.get(lockPath);
2463
+ if (!state) {
2464
+ state = { tail: Promise.resolve() };
2465
+ localMutations.set(lockPath, state);
2466
+ }
2467
+ const current = state;
2468
+ const operation = current.tail.then(async () => await run(current));
2469
+ const tail = operation.then(() => {}, () => {});
2470
+ current.tail = tail;
2471
+ try {
2472
+ return await operation;
2473
+ } finally {
2474
+ if (current.tail === tail && !current.failed && localMutations.get(lockPath) === current) localMutations.delete(lockPath);
2475
+ }
2476
+ }
2477
+ async function settlePendingQueueLeaseGuard(sessionId) {
2478
+ if (localMutations.size === 0) return;
2479
+ const lockPath = await canonicalLeasePath(sessionId);
2480
+ if (!localMutations.has(lockPath)) return;
2481
+ await locallySerialized(lockPath, async (state) => await settleFailedGuard(lockPath, state));
2482
+ }
2483
+ async function withQueueLeaseMutation(sessionId, mutate, options = {}) {
2484
+ const { reservation, capturedIdentity } = options;
2485
+ await fs$1.mkdir(path.dirname(queueLockFilePath(sessionId)), {
2486
+ recursive: true,
2487
+ mode: 448
2488
+ });
2489
+ const lockPath = await canonicalLeasePath(sessionId);
2490
+ const expected = reservation ? {
2491
+ pid: reservation.pid,
2492
+ sessionId: reservation.sessionId,
2493
+ ownerGeneration: reservation.ownerGeneration
2494
+ } : void 0;
2495
+ const publishedReservation = () => reservation?.published ? expected : void 0;
2496
+ return await locallySerialized(lockPath, async (state) => {
2497
+ await settleFailedGuard(lockPath, state);
2498
+ const owner = await createLockOwner({ capturedIdentity });
2499
+ const guard = await acquireGuard(lockPath, owner);
2500
+ let outcome;
2501
+ try {
2502
+ outcome = {
2503
+ ok: true,
2504
+ value: await mutate()
2505
+ };
2506
+ } catch (error) {
2507
+ outcome = {
2508
+ ok: false,
2509
+ error
2510
+ };
2511
+ }
2512
+ const published = publishedReservation();
2513
+ const receipt = {
2514
+ guard,
2515
+ owner,
2516
+ reservation: outcome.ok ? void 0 : published
2517
+ };
2518
+ try {
2519
+ await settleReceipt(lockPath, receipt);
2520
+ } catch (error) {
2521
+ if (outcome.ok) receipt.reservation = published;
2522
+ state.failed = receipt;
2523
+ throw new QueueLeaseGuardSettlementError(outcome.ok ? [error] : [outcome.error, error]);
2524
+ }
2525
+ if (!outcome.ok) throw outcome.error;
2526
+ return outcome.value;
2527
+ });
2528
+ }
2529
+ //#endregion
2127
2530
  //#region src/session/queue/lease-store.ts
2128
2531
  const PROCESS_SIGTERM_GRACE_MS = process.platform === "win32" ? 4e3 : 12e3;
2129
2532
  const PROCESS_SIGKILL_GRACE_MS = 1500;
@@ -2133,6 +2536,7 @@ function parseQueueOwnerRecord(raw) {
2133
2536
  if (!raw || typeof raw !== "object") return null;
2134
2537
  const record = raw;
2135
2538
  if (!hasValidQueueOwnerRecordFields(record)) return null;
2539
+ const processIdentity = parseProcessBirthIdentity(record.processIdentity);
2136
2540
  return {
2137
2541
  pid: record.pid,
2138
2542
  sessionId: record.sessionId,
@@ -2141,12 +2545,19 @@ function parseQueueOwnerRecord(raw) {
2141
2545
  heartbeatAt: record.heartbeatAt,
2142
2546
  ownerGeneration: record.ownerGeneration,
2143
2547
  queueDepth: record.queueDepth,
2144
- ...record.sharedRuntime === true ? { sharedRuntime: true } : {},
2145
- ...record.sessionWatch === true ? { sessionWatch: true } : {},
2548
+ ...processIdentity ? { processIdentity } : {},
2549
+ ...parseQueueOwnerCapabilities(record),
2146
2550
  ...typeof record.mcpConfigPath === "string" ? { mcpConfigPath: record.mcpConfigPath } : {},
2147
2551
  ...typeof record.mcpConfigFingerprint === "string" ? { mcpConfigFingerprint: record.mcpConfigFingerprint } : {}
2148
2552
  };
2149
2553
  }
2554
+ function parseQueueOwnerCapabilities(record) {
2555
+ return {
2556
+ ...record.sharedRuntime === true ? { sharedRuntime: true } : {},
2557
+ ...record.sessionWatch === true ? { sessionWatch: true } : {},
2558
+ ...record.persistsControlState === true ? { persistsControlState: true } : {}
2559
+ };
2560
+ }
2150
2561
  function hasValidQueueOwnerRecordFields(record) {
2151
2562
  return !Array.isArray(record) && isPositiveInteger(record.pid) && typeof record.sessionId === "string" && typeof record.socketPath === "string" && typeof record.createdAt === "string" && typeof record.heartbeatAt === "string" && isPositiveInteger(record.ownerGeneration) && isNonNegativeInteger(record.queueDepth);
2152
2563
  }
@@ -2199,15 +2610,20 @@ async function removeSocketFile(socketPath) {
2199
2610
  if (error.code !== "ENOENT") throw error;
2200
2611
  }
2201
2612
  }
2202
- async function waitForProcessExit(pid, timeoutMs) {
2613
+ async function waitForProcessExit(pid, timeoutMs, hasExited) {
2203
2614
  const deadline = Date.now() + Math.max(0, timeoutMs);
2204
2615
  while (Date.now() <= deadline) {
2205
- if (!isProcessAlive(pid)) return true;
2616
+ if (hasExited(pid)) return true;
2206
2617
  await waitMs(PROCESS_POLL_MS);
2207
2618
  }
2208
- return !isProcessAlive(pid);
2619
+ return hasExited(pid);
2209
2620
  }
2210
- async function cleanupQueueOwnerFiles(sessionId, socketPath, isCurrent) {
2621
+ async function cleanupQueueOwnerFiles(sessionId, socketPath, isCurrent, capturedIdentity) {
2622
+ await withQueueLeaseMutation(sessionId, async () => {
2623
+ await cleanupGuardedQueueOwnerFiles(sessionId, socketPath, isCurrent);
2624
+ }, { capturedIdentity });
2625
+ }
2626
+ async function cleanupGuardedQueueOwnerFiles(sessionId, socketPath, isCurrent) {
2211
2627
  if (!await isCurrent()) return;
2212
2628
  await removeSocketFile(socketPath).catch(() => {});
2213
2629
  if (!await isCurrent()) return;
@@ -2215,9 +2631,11 @@ async function cleanupQueueOwnerFiles(sessionId, socketPath, isCurrent) {
2215
2631
  if (error.code !== "ENOENT") throw error;
2216
2632
  });
2217
2633
  }
2218
- async function ownsQueueLease(owner, requireStale = false) {
2219
- const current = await readQueueOwnerRecord(owner.sessionId);
2220
- return current?.pid === owner.pid && current.ownerGeneration === owner.ownerGeneration && current.sessionId === owner.sessionId && (!requireStale || !ownerIsAlive(current) || isQueueOwnerHeartbeatStale(current));
2634
+ async function ownsQueueLease(owner) {
2635
+ return matchesQueueOwner(await readQueueOwnerRecord(owner.sessionId), owner);
2636
+ }
2637
+ function matchesQueueOwner(current, expected) {
2638
+ return current?.sessionId === expected.sessionId && current.pid === expected.pid && current.ownerGeneration === expected.ownerGeneration;
2221
2639
  }
2222
2640
  function ownerIsAlive(owner) {
2223
2641
  return owner.pid === process.pid || isProcessAlive(owner.pid);
@@ -2234,41 +2652,55 @@ async function readQueueOwnerRecord(sessionId) {
2234
2652
  }
2235
2653
  async function terminateProcess(pid, beforeSignal) {
2236
2654
  if (!isProcessAlive(pid)) return false;
2237
- for (const [signal, graceMs] of [["SIGTERM", PROCESS_SIGTERM_GRACE_MS], ["SIGKILL", PROCESS_SIGKILL_GRACE_MS]]) {
2655
+ return await terminateWithDispatch(pid, async (signal) => {
2238
2656
  if (beforeSignal && !await beforeSignal(signal)) return false;
2239
- try {
2240
- process.kill(pid, signal);
2241
- } catch {
2242
- return false;
2243
- }
2244
- if (await waitForProcessExit(pid, graceMs)) return true;
2657
+ return dispatchSignal(pid, signal);
2658
+ }, (target) => !isProcessAlive(target));
2659
+ }
2660
+ function dispatchSignal(pid, signal) {
2661
+ try {
2662
+ process.kill(pid, signal);
2663
+ return true;
2664
+ } catch {
2665
+ return false;
2666
+ }
2667
+ }
2668
+ async function dispatchQueueOwnerSignal(pid, signal) {
2669
+ if (process.platform !== "win32") return dispatchSignal(pid, signal);
2670
+ const systemRoot = process.env.SystemRoot;
2671
+ if (!systemRoot || !path.win32.isAbsolute(systemRoot)) throw new Error("Windows queue-owner cleanup requires an absolute SystemRoot directory");
2672
+ const taskkill = path.win32.join(systemRoot, "System32", "taskkill.exe");
2673
+ await runTimedExecFile(taskkill, [
2674
+ "/pid",
2675
+ String(pid),
2676
+ "/T",
2677
+ "/F"
2678
+ ], {
2679
+ timeoutMs: PROCESS_SIGTERM_GRACE_MS,
2680
+ windowsHide: true
2681
+ });
2682
+ return true;
2683
+ }
2684
+ async function terminateWithDispatch(pid, dispatch, hasExited) {
2685
+ for (const [signal, graceMs] of [["SIGTERM", PROCESS_SIGTERM_GRACE_MS], ["SIGKILL", PROCESS_SIGKILL_GRACE_MS]]) {
2686
+ if (!await dispatch(signal)) return false;
2687
+ if (await waitForProcessExit(pid, graceMs, hasExited)) return true;
2245
2688
  }
2246
2689
  return false;
2247
2690
  }
2248
2691
  async function resolveUsableQueueOwner(sessionId, owner) {
2249
- if (ownerIsAlive(owner) && !isQueueOwnerHeartbeatStale(owner)) return owner;
2250
- await terminateQueueOwnerForSession(sessionId, owner, true);
2251
- const current = await readQueueOwnerRecord(sessionId);
2252
- return current?.pid === owner.pid && current.ownerGeneration === owner.ownerGeneration && ownerIsAlive(current) && !isQueueOwnerHeartbeatStale(current) ? current : void 0;
2253
- }
2254
- async function readQueueOwnerStatus(sessionId) {
2692
+ await settlePendingQueueLeaseGuard(sessionId);
2255
2693
  const observed = await readQueueOwnerRecord(sessionId);
2256
- if (!observed) return;
2257
- const owner = await resolveUsableQueueOwner(sessionId, observed);
2258
- if (!owner) return;
2259
- return {
2260
- pid: owner.pid,
2261
- socketPath: owner.socketPath,
2262
- heartbeatAt: owner.heartbeatAt,
2263
- ownerGeneration: owner.ownerGeneration,
2264
- queueDepth: owner.queueDepth,
2265
- alive: true,
2266
- stale: isQueueOwnerHeartbeatStale(owner)
2267
- };
2694
+ if (!matchesQueueOwner(observed, owner)) return;
2695
+ if (ownerIsAlive(observed) && !isQueueOwnerHeartbeatStale(observed)) return observed;
2696
+ await terminateQueueOwnerForSession(sessionId, observed, true);
2697
+ const current = await readQueueOwnerRecord(sessionId);
2698
+ return matchesQueueOwner(current, observed) && ownerIsAlive(current) && !isQueueOwnerHeartbeatStale(current) ? current : void 0;
2268
2699
  }
2269
2700
  async function tryAcquireQueueOwnerLease(sessionId, mcpConfigOrNowIsoFactory, nowIsoFactory = nowIso) {
2270
2701
  const { mcpConfigPath, clock } = resolveLeaseArguments(mcpConfigOrNowIsoFactory, nowIsoFactory);
2271
2702
  const mcpConfigMetadata = createMcpConfigMetadata(mcpConfigPath, readMcpConfigFingerprint(mcpConfigOrNowIsoFactory));
2703
+ const processIdentity = await getOwnProcessIdentity();
2272
2704
  await ensureQueueDir();
2273
2705
  const lockPath = queueLockFilePath(sessionId);
2274
2706
  const socketPath = queueSocketPath(sessionId);
@@ -2281,25 +2713,38 @@ async function tryAcquireQueueOwnerLease(sessionId, mcpConfigOrNowIsoFactory, no
2281
2713
  socketPath,
2282
2714
  createdAt,
2283
2715
  ownerGeneration,
2716
+ ...processIdentity ? { processIdentity } : {},
2284
2717
  ...mcpConfigMetadata,
2285
2718
  updates: Promise.resolve(),
2286
2719
  released: false
2287
2720
  };
2721
+ const reservation = {
2722
+ pid: lease.pid,
2723
+ sessionId,
2724
+ ownerGeneration,
2725
+ published: false
2726
+ };
2288
2727
  try {
2289
- await stageQueueOwnerRecord(lease, 0, () => createdAt, async (tempPath, payload) => {
2290
- try {
2291
- await fs$1.link(tempPath, lockPath);
2292
- } catch (error) {
2293
- if (!isHardlinkFallbackError(error)) throw error;
2294
- await fs$1.writeFile(lockPath, payload, {
2295
- encoding: "utf8",
2296
- flag: "wx",
2297
- mode: 384
2298
- });
2299
- }
2728
+ return await withQueueLeaseMutation(sessionId, async () => {
2729
+ await stageQueueOwnerRecord(lease, 0, clock, async (tempPath, payload) => {
2730
+ try {
2731
+ await fs$1.link(tempPath, lockPath);
2732
+ } catch (error) {
2733
+ if (!isHardlinkFallbackError(error)) throw error;
2734
+ await fs$1.writeFile(lockPath, payload, {
2735
+ encoding: "utf8",
2736
+ flag: "wx",
2737
+ mode: 384
2738
+ });
2739
+ }
2740
+ reservation.published = true;
2741
+ });
2742
+ await removeSocketFile(socketPath).catch(() => {});
2743
+ return lease;
2744
+ }, {
2745
+ reservation,
2746
+ capturedIdentity: lease
2300
2747
  });
2301
- await removeSocketFile(socketPath).catch(() => {});
2302
- return lease;
2303
2748
  } catch (error) {
2304
2749
  return await handleLeaseCollision(sessionId, error);
2305
2750
  }
@@ -2324,18 +2769,20 @@ async function handleLeaseCollision(sessionId, error) {
2324
2769
  }
2325
2770
  async function cleanupAbandonedReservation(sessionId) {
2326
2771
  const lockPath = queueLockFilePath(sessionId);
2327
- try {
2328
- const observed = await fs$1.lstat(lockPath, { bigint: true });
2329
- if (!observed.isFile() || Date.now() - Number(observed.mtimeMs) <= QUEUE_OWNER_STALE_HEARTBEAT_MS) return;
2330
- const raw = await fs$1.readFile(lockPath, "utf8");
2331
- if (await readQueueOwnerRecord(sessionId)) return;
2332
- await cleanupQueueOwnerFiles(sessionId, queueSocketPath(sessionId), async () => {
2333
- const current = await fs$1.lstat(lockPath, { bigint: true });
2334
- return current.isFile() && current.dev === observed.dev && current.ino === observed.ino && current.mtimeNs === observed.mtimeNs && await fs$1.readFile(lockPath, "utf8") === raw;
2335
- });
2336
- } catch (error) {
2337
- if (error.code !== "ENOENT") throw error;
2338
- }
2772
+ await withQueueLeaseMutation(sessionId, async () => {
2773
+ try {
2774
+ const observed = await fs$1.lstat(lockPath, { bigint: true });
2775
+ if (!observed.isFile() || Date.now() - Number(observed.mtimeMs) <= QUEUE_OWNER_STALE_HEARTBEAT_MS) return;
2776
+ const raw = await fs$1.readFile(lockPath, "utf8");
2777
+ if (await readQueueOwnerRecord(sessionId)) return;
2778
+ await cleanupGuardedQueueOwnerFiles(sessionId, queueSocketPath(sessionId), async () => {
2779
+ const current = await fs$1.lstat(lockPath, { bigint: true });
2780
+ return current.isFile() && current.dev === observed.dev && current.ino === observed.ino && current.mtimeNs === observed.mtimeNs && await fs$1.readFile(lockPath, "utf8") === raw;
2781
+ });
2782
+ } catch (error) {
2783
+ if (error.code !== "ENOENT") throw error;
2784
+ }
2785
+ });
2339
2786
  }
2340
2787
  function resolveLeaseArguments(mcpConfigOrNowIsoFactory, nowIsoFactory) {
2341
2788
  if (typeof mcpConfigOrNowIsoFactory === "string") return {
@@ -2358,9 +2805,12 @@ function resolveLeaseArguments(mcpConfigOrNowIsoFactory, nowIsoFactory) {
2358
2805
  function refreshQueueOwnerLease(lease, options, nowIsoFactory = nowIso) {
2359
2806
  if (lease.released) return Promise.resolve();
2360
2807
  const update = lease.updates.then(async () => {
2361
- await stageQueueOwnerRecord(lease, options.queueDepth, nowIsoFactory, async (tempPath) => {
2362
- if (await ownsQueueLease(lease)) await fs$1.rename(tempPath, lease.lockPath);
2363
- });
2808
+ await withQueueLeaseMutation(lease.sessionId, async () => {
2809
+ if (!await ownsQueueLease(lease)) return;
2810
+ await stageQueueOwnerRecord(lease, options.queueDepth, nowIsoFactory, async (tempPath) => {
2811
+ await fs$1.rename(tempPath, lease.lockPath);
2812
+ });
2813
+ }, { capturedIdentity: lease });
2364
2814
  });
2365
2815
  lease.updates = update.catch(() => {});
2366
2816
  return update;
@@ -2373,9 +2823,11 @@ async function stageQueueOwnerRecord(lease, queueDepth, clock, publish) {
2373
2823
  createdAt: lease.createdAt,
2374
2824
  heartbeatAt: clock(),
2375
2825
  ownerGeneration: lease.ownerGeneration,
2826
+ ...lease.processIdentity ? { processIdentity: lease.processIdentity } : {},
2376
2827
  queueDepth: Math.max(0, Math.round(queueDepth)),
2377
2828
  sharedRuntime: true,
2378
2829
  sessionWatch: true,
2830
+ persistsControlState: true,
2379
2831
  ...lease.mcpConfigPath ? { mcpConfigPath: lease.mcpConfigPath } : {},
2380
2832
  ...lease.mcpConfigFingerprint ? { mcpConfigFingerprint: lease.mcpConfigFingerprint } : {}
2381
2833
  }, null, 2);
@@ -2395,13 +2847,48 @@ async function stageQueueOwnerRecord(lease, queueDepth, clock, publish) {
2395
2847
  async function releaseQueueOwnerLease(lease) {
2396
2848
  lease.released = true;
2397
2849
  await lease.updates;
2398
- await cleanupQueueOwnerFiles(lease.sessionId, lease.socketPath, () => ownsQueueLease(lease));
2850
+ await cleanupQueueOwnerFiles(lease.sessionId, lease.socketPath, () => ownsQueueLease(lease), lease);
2851
+ }
2852
+ function unverifiedQueueOwnerError(owner) {
2853
+ return new QueueConnectionError(`Cannot safely retire queue owner pid ${owner.pid}: its process birth identity is unverified. Its lease was retained. Retry from the same local process namespace with process-query access, or let the owner finish normal shutdown or idle expiry.`, {
2854
+ detailCode: "QUEUE_OWNER_IDENTITY_UNVERIFIED",
2855
+ origin: "queue",
2856
+ retryable: true
2857
+ });
2858
+ }
2859
+ async function dispatchVerifiedQueueOwnerSignal(owner, signal, requireStale, retirement) {
2860
+ return await withQueueLeaseMutation(owner.sessionId, async () => {
2861
+ const current = await readQueueOwnerRecord(owner.sessionId);
2862
+ retirement.leaseReleased = current?.pid !== owner.pid;
2863
+ if (!matchesQueueOwner(current, owner) || requireStale && signal === "SIGTERM" && !isQueueOwnerHeartbeatStale(current)) return false;
2864
+ const identity = await observeProcessIncarnation(current.pid, current.processIdentity);
2865
+ if (identity !== "matching") {
2866
+ retirement.gone = identity === "gone";
2867
+ retirement.unverified = identity === "unknown";
2868
+ return false;
2869
+ }
2870
+ return await dispatchQueueOwnerSignal(owner.pid, signal);
2871
+ });
2872
+ }
2873
+ async function retireQueueOwner(owner, requireStale) {
2874
+ const deadline = performance.now() + PROCESS_SIGTERM_GRACE_MS + PROCESS_SIGKILL_GRACE_MS;
2875
+ const retirement = {
2876
+ gone: false,
2877
+ unverified: false,
2878
+ leaseReleased: false
2879
+ };
2880
+ await terminateWithDispatch(owner.pid, (signal) => dispatchVerifiedQueueOwnerSignal(owner, signal, requireStale, retirement), isProcessDefinitelyDead);
2881
+ if (retirement.unverified && requireStale) throw unverifiedQueueOwnerError(owner);
2882
+ if (retirement.leaseReleased || retirement.unverified) await waitForProcessExit(owner.pid, Math.max(0, deadline - performance.now()), isProcessDefinitelyDead);
2883
+ const gone = retirement.gone || await observeProcessIncarnation(owner.pid, owner.processIdentity) === "gone";
2884
+ if (retirement.unverified && !gone) throw unverifiedQueueOwnerError(owner);
2885
+ return gone;
2399
2886
  }
2400
2887
  async function terminateQueueOwnerForSession(sessionId, expectedOwner, requireStale = false) {
2888
+ await settlePendingQueueLeaseGuard(sessionId);
2401
2889
  const owner = expectedOwner ?? await readQueueOwnerRecord(sessionId);
2402
- if (!owner || owner.sessionId !== sessionId) return;
2403
- if (ownerIsAlive(owner)) await terminateProcess(owner.pid, (signal) => ownsQueueLease(owner, requireStale && signal === "SIGTERM"));
2404
- if (ownerIsAlive(owner)) return;
2890
+ if (!owner || owner.sessionId !== sessionId || owner.pid === process.pid) return;
2891
+ if (!await retireQueueOwner(owner, requireStale)) return;
2405
2892
  await cleanupQueueOwnerFiles(sessionId, owner.socketPath, () => ownsQueueLease(owner));
2406
2893
  }
2407
2894
  async function waitMs(ms) {
@@ -2456,7 +2943,12 @@ async function connectToQueueOwner(owner, maxAttempts = QUEUE_CONNECT_ATTEMPTS)
2456
2943
  }
2457
2944
  //#endregion
2458
2945
  //#region src/session/queue/ipc-health.ts
2946
+ async function isStillCurrent(owner) {
2947
+ const current = await readQueueOwnerRecord(owner.sessionId);
2948
+ return current?.pid === owner.pid && current.ownerGeneration === owner.ownerGeneration;
2949
+ }
2459
2950
  async function probeQueueOwnerHealth(sessionId) {
2951
+ await settlePendingQueueLeaseGuard(sessionId);
2460
2952
  const ownerRecord = await readQueueOwnerRecord(sessionId);
2461
2953
  if (!ownerRecord) return {
2462
2954
  sessionId,
@@ -2465,7 +2957,7 @@ async function probeQueueOwnerHealth(sessionId) {
2465
2957
  socketReachable: false,
2466
2958
  pidAlive: false
2467
2959
  };
2468
- const owner = await readQueueOwnerStatus(sessionId);
2960
+ const owner = await resolveUsableQueueOwner(sessionId, ownerRecord);
2469
2961
  if (!owner) return {
2470
2962
  sessionId,
2471
2963
  hasLease: false,
@@ -2473,10 +2965,9 @@ async function probeQueueOwnerHealth(sessionId) {
2473
2965
  socketReachable: false,
2474
2966
  pidAlive: false
2475
2967
  };
2476
- const pidAlive = owner.alive;
2477
2968
  let socketReachable = false;
2478
2969
  try {
2479
- const socket = await connectToQueueOwner(ownerRecord, 2);
2970
+ const socket = await connectToQueueOwner(owner, 2);
2480
2971
  if (socket) {
2481
2972
  socketReachable = true;
2482
2973
  if (!socket.destroyed) socket.end();
@@ -2484,12 +2975,19 @@ async function probeQueueOwnerHealth(sessionId) {
2484
2975
  } catch {
2485
2976
  socketReachable = false;
2486
2977
  }
2978
+ if (!await isStillCurrent(owner)) return {
2979
+ sessionId,
2980
+ hasLease: false,
2981
+ healthy: false,
2982
+ socketReachable: false,
2983
+ pidAlive: false
2984
+ };
2487
2985
  return {
2488
2986
  sessionId,
2489
2987
  hasLease: true,
2490
2988
  healthy: socketReachable,
2491
2989
  socketReachable,
2492
- pidAlive,
2990
+ pidAlive: true,
2493
2991
  pid: owner.pid,
2494
2992
  socketPath: owner.socketPath,
2495
2993
  ownerGeneration: owner.ownerGeneration,
@@ -3051,6 +3549,12 @@ async function runQueueOwnerRequest(options) {
3051
3549
  onAbort();
3052
3550
  return;
3053
3551
  }
3552
+ try {
3553
+ options.assertDispatch?.();
3554
+ } catch (error) {
3555
+ finishReject(error);
3556
+ return;
3557
+ }
3054
3558
  try {
3055
3559
  socket.write(`${requestLine}\n`);
3056
3560
  } catch (error) {
@@ -3164,10 +3668,11 @@ async function submitToQueueOwner(owner, options) {
3164
3668
  }
3165
3669
  });
3166
3670
  }
3167
- async function submitControlToQueueOwner(owner, request, isExpectedResponse) {
3671
+ async function submitControlToQueueOwner(owner, request, isExpectedResponse, assertDispatch) {
3168
3672
  return await runQueueOwnerRequest({
3169
3673
  owner,
3170
3674
  request,
3675
+ assertDispatch,
3171
3676
  onMessage: (message, { state, resolve, reject }) => {
3172
3677
  if (message.type === "error") {
3173
3678
  reject(queueConnectionErrorFromOwner(message, false));
@@ -3209,25 +3714,25 @@ async function submitCancelToQueueOwner(owner, targetRequestId) {
3209
3714
  ...targetRequestId !== void 0 ? { targetRequestId } : {}
3210
3715
  }, (message) => message.type === "cancel_result"))?.cancelled;
3211
3716
  }
3212
- async function submitSetModeToQueueOwner(owner, modeId, timeoutMs) {
3717
+ async function submitSetModeToQueueOwner(owner, modeId, timeoutMs, assertDispatch) {
3213
3718
  return await submitControlToQueueOwner(owner, {
3214
3719
  type: "set_mode",
3215
3720
  requestId: randomUUID(),
3216
3721
  ownerGeneration: owner.ownerGeneration,
3217
3722
  modeId,
3218
3723
  timeoutMs
3219
- }, (message) => message.type === "set_mode_result") ? true : void 0;
3724
+ }, (message) => message.type === "set_mode_result", assertDispatch) ? true : void 0;
3220
3725
  }
3221
- async function submitSetModelToQueueOwner(owner, modelId, timeoutMs) {
3726
+ async function submitSetModelToQueueOwner(owner, modelId, timeoutMs, assertDispatch) {
3222
3727
  return await submitControlToQueueOwner(owner, {
3223
3728
  type: "set_model",
3224
3729
  requestId: randomUUID(),
3225
3730
  ownerGeneration: owner.ownerGeneration,
3226
3731
  modelId,
3227
3732
  timeoutMs
3228
- }, (message) => message.type === "set_model_result");
3733
+ }, (message) => message.type === "set_model_result", assertDispatch);
3229
3734
  }
3230
- async function submitSetConfigOptionToQueueOwner(owner, configId, value, timeoutMs) {
3735
+ async function submitSetConfigOptionToQueueOwner(owner, configId, value, timeoutMs, assertDispatch) {
3231
3736
  return (await submitControlToQueueOwner(owner, {
3232
3737
  type: "set_config_option",
3233
3738
  requestId: randomUUID(),
@@ -3235,7 +3740,7 @@ async function submitSetConfigOptionToQueueOwner(owner, configId, value, timeout
3235
3740
  configId,
3236
3741
  value,
3237
3742
  timeoutMs
3238
- }, (message) => message.type === "set_config_option_result"))?.response;
3743
+ }, (message) => message.type === "set_config_option_result", assertDispatch))?.response;
3239
3744
  }
3240
3745
  async function submitCloseSessionToQueueOwner(owner, timeoutMs) {
3241
3746
  return (await submitControlToQueueOwner(owner, {
@@ -3305,7 +3810,10 @@ async function tryCloseSessionOnRunningOwner(options) {
3305
3810
  verbose: options.verbose,
3306
3811
  requestName: "close_session",
3307
3812
  logPrefix: "[acpx] requested session/close on active owner pid",
3308
- submit: (owner) => submitCloseSessionToQueueOwner(owner, options.timeoutMs)
3813
+ submit: (owner) => {
3814
+ options.onOwnerSelected?.(owner);
3815
+ return submitCloseSessionToQueueOwner(owner, options.timeoutMs);
3816
+ }
3309
3817
  });
3310
3818
  }
3311
3819
  async function tryCancelOnRunningOwner(options) {
@@ -3317,34 +3825,42 @@ async function tryCancelOnRunningOwner(options) {
3317
3825
  submit: (owner) => submitCancelToQueueOwner(owner, options.targetRequestId)
3318
3826
  });
3319
3827
  }
3320
- async function trySetModeOnRunningOwner(sessionId, modeId, timeoutMs, verbose) {
3828
+ async function withOwnerControlPersistence(owner, operation) {
3829
+ const persistsControlState = owner.persistsControlState === true;
3830
+ const value = await operation;
3831
+ return value === void 0 ? void 0 : {
3832
+ value,
3833
+ persistsControlState
3834
+ };
3835
+ }
3836
+ async function trySetModeOnRunningOwner(sessionId, modeId, timeoutMs, verbose, assertDispatch) {
3321
3837
  return await tryControlOnRunningOwner({
3322
3838
  sessionId,
3323
3839
  verbose,
3324
3840
  requestName: "set_mode",
3325
3841
  logPrefix: "[acpx] requested session/set_mode on owner pid",
3326
- submit: (owner) => submitSetModeToQueueOwner(owner, modeId, timeoutMs)
3842
+ submit: (owner) => withOwnerControlPersistence(owner, submitSetModeToQueueOwner(owner, modeId, timeoutMs, assertDispatch))
3327
3843
  });
3328
3844
  }
3329
- async function trySetModelOnRunningOwner(sessionId, modelId, timeoutMs, verbose) {
3845
+ async function trySetModelOnRunningOwner(sessionId, modelId, timeoutMs, verbose, assertDispatch) {
3330
3846
  return await tryControlOnRunningOwner({
3331
3847
  sessionId,
3332
3848
  verbose,
3333
3849
  requestName: "set_model",
3334
3850
  logPrefix: "[acpx] requested a model config update on owner pid",
3335
- submit: (owner) => submitSetModelToQueueOwner(owner, modelId, timeoutMs)
3851
+ submit: (owner) => withOwnerControlPersistence(owner, submitSetModelToQueueOwner(owner, modelId, timeoutMs, assertDispatch))
3336
3852
  });
3337
3853
  }
3338
- async function trySetConfigOptionOnRunningOwner(sessionId, configId, value, timeoutMs, verbose) {
3854
+ async function trySetConfigOptionOnRunningOwner(sessionId, configId, value, timeoutMs, verbose, assertDispatch) {
3339
3855
  return await tryControlOnRunningOwner({
3340
3856
  sessionId,
3341
3857
  verbose,
3342
3858
  requestName: "set_config_option",
3343
3859
  logPrefix: "[acpx] requested session/set_config_option on owner pid",
3344
- submit: (owner) => submitSetConfigOptionToQueueOwner(owner, configId, value, timeoutMs)
3860
+ submit: (owner) => withOwnerControlPersistence(owner, submitSetConfigOptionToQueueOwner(owner, configId, value, timeoutMs, assertDispatch))
3345
3861
  });
3346
3862
  }
3347
3863
  //#endregion
3348
- export { startPerfTimer as $, listSessions as A, AgentSpawnError as At, extractAgentSessionId as B, SessionModeReplayError as Bt, textPrompt as C, OUTPUT_ERROR_ORIGINS as Ct, findSession as D, SESSION_RECORD_SCHEMA as Dt, findGitRepositoryRoot as E, PERMISSION_POLICY_ACTIONS as Et, resolveSessionRecord as F, GeminiAcpStartupTimeoutError as Ft, watchSession as G, SESSION_JOURNAL_SCHEMA as H, SessionNotFoundError as Ht, writeSessionRecord as I, PermissionDeniedError as It, incrementPerfCounter as J, formatPerfMetric as K, assertPersistedKeyPolicy as L, PermissionPromptUnavailableError as Lt, normalizeName as M, AuthPolicyError as Mt, pruneSessions as N, ClaudeAcpSessionCreateTimeoutError as Nt, findSessionByDirectoryWalk as O, AcpxOperationalError as Ot, readSessionRecord as P, CopilotAcpUnsupportedError as Pt, setPerfGauge as Q, parseSessionRecord as R, QueueConnectionError as Rt, promptToDisplayText as S, OUTPUT_ERROR_CODES as St, absolutePath as T, PERMISSION_MODES as Tt, SessionJournalReader as U, SessionResumeRequiredError as Ut, normalizeAgentSessionId as V, SessionModelReplayError as Vt, SessionWatchError as W, UnsupportedPromptContentError as Wt, recordPerfDuration as X, measurePerf as Y, resetPerfMetrics as Z, waitMs as _, formatUnknownErrorMessage as _t, trySetModelOnRunningOwner as a, parsePermissionNotice as at, mergePromptSourceWithText as b, EXIT_CODES as bt, queueRequestExceedsLimit as c, defaultSessionEventLog as ct, readQueueOwnerRecord as d, sessionEventLockPath as dt, extractSessionUpdateNotification as et, refreshQueueOwnerLease as f, sessionEventSegmentPath as ft, tryAcquireQueueOwnerLease as g, extractAcpError as gt, terminateQueueOwnerForSession as h, isProcessAlive as ht, trySetModeOnRunningOwner as i, parseJsonRpcErrorMessage as it, listSessionsForAgent as j, AgentStartupError as jt, isoNow as k, AgentDisconnectedError as kt, parseQueueRequest as l, sessionBaseDir as lt, terminateProcess as m, writePrivateJsonFile as mt, tryCloseSessionOnRunningOwner as n, isAcpMessageObject as nt, trySubmitToRunningOwner as o, parsePromptStopReason as ot, releaseQueueOwnerLease as p, writePrivateFile as pt, getPerfMetricsSnapshot as q, trySetConfigOptionOnRunningOwner as r, isSessionUpdateNotification as rt, queueRequestByteLimit as s, DEFAULT_EVENT_SEGMENT_MAX_BYTES as st, tryCancelOnRunningOwner as t, isAcpJsonRpcMessage as tt, probeQueueOwnerHealth as u, sessionEventActivePath as ut, PromptInputValidationError as v, isAcpResourceNotFoundError as vt, DEFAULT_HISTORY_LIMIT as w, OUTPUT_FORMATS as wt, parsePromptSource as x, NON_INTERACTIVE_PERMISSION_POLICIES as xt, getUnsupportedPromptContentMessage as y, AUTH_POLICIES as yt, serializeSessionRecordForDisk as z, SessionConfigOptionReplayError as zt };
3864
+ export { measurePerf as $, findGitRepositoryRoot as A, PERMISSION_POLICY_ACTIONS as At, writeSessionRecord as B, PermissionDeniedError as Bt, getUnsupportedPromptContentMessage as C, AUTH_POLICIES as Ct, textPrompt as D, OUTPUT_ERROR_ORIGINS as Dt, promptToDisplayText as E, OUTPUT_ERROR_CODES as Et, listSessionsForAgent as F, AgentStartupError as Ft, normalizeAgentSessionId as G, SessionModelReplayError as Gt, parseSessionRecord as H, QueueConnectionError as Ht, normalizeName as I, AuthPolicyError as It, SessionWatchError as J, UnsupportedPromptContentError as Jt, SESSION_JOURNAL_SCHEMA as K, SessionNotFoundError as Kt, pruneSessions as L, ClaudeAcpSessionCreateTimeoutError as Lt, findSessionByDirectoryWalk as M, AcpxOperationalError as Mt, isoNow as N, AgentDisconnectedError as Nt, DEFAULT_HISTORY_LIMIT as O, OUTPUT_FORMATS as Ot, listSessions as P, AgentSpawnError as Pt, incrementPerfCounter as Q, readSessionRecord as R, CopilotAcpUnsupportedError as Rt, PromptInputValidationError as S, isAcpResourceNotFoundError as St, parsePromptSource as T, NON_INTERACTIVE_PERMISSION_POLICIES as Tt, serializeSessionRecordForDisk as U, SessionConfigOptionReplayError as Ut, assertPersistedKeyPolicy as V, PermissionPromptUnavailableError as Vt, extractAgentSessionId as W, SessionModeReplayError as Wt, formatPerfMetric as X, watchSession as Y, getPerfMetricsSnapshot as Z, waitMs as _, writePrivateFile as _t, trySetModelOnRunningOwner as a, isAcpJsonRpcMessage as at, lockOwnerPid as b, extractAcpError as bt, queueRequestExceedsLimit as c, parseJsonRpcErrorMessage as ct, readQueueOwnerRecord as d, DEFAULT_EVENT_SEGMENT_MAX_BYTES as dt, recordPerfDuration as et, refreshQueueOwnerLease as f, defaultSessionEventLog as ft, tryAcquireQueueOwnerLease as g, sessionEventSegmentPath as gt, terminateQueueOwnerForSession as h, sessionEventLockPath as ht, trySetModeOnRunningOwner as i, extractSessionUpdateNotification as it, findSession as j, SESSION_RECORD_SCHEMA as jt, absolutePath as k, PERMISSION_MODES as kt, parseQueueRequest as l, parsePermissionNotice as lt, terminateProcess as m, sessionEventActivePath as mt, tryCloseSessionOnRunningOwner as n, setPerfGauge as nt, trySubmitToRunningOwner as o, isAcpMessageObject as ot, releaseQueueOwnerLease as p, sessionBaseDir as pt, SessionJournalReader as q, SessionResumeRequiredError as qt, trySetConfigOptionOnRunningOwner as r, startPerfTimer as rt, queueRequestByteLimit as s, isSessionUpdateNotification as st, tryCancelOnRunningOwner as t, resetPerfMetrics as tt, probeQueueOwnerHealth as u, parsePromptStopReason as ut, QueueLeaseGuardSettlementError as v, writePrivateJsonFile as vt, mergePromptSourceWithText as w, EXIT_CODES as wt, readProcessTable as x, formatUnknownErrorMessage as xt, createLockOwner as y, isProcessAlive as yt, resolveSessionRecord as z, GeminiAcpStartupTimeoutError as zt };
3349
3865
 
3350
- //# sourceMappingURL=ipc-D2rstexT.js.map
3866
+ //# sourceMappingURL=ipc-fMgj5vrO.js.map