@centralu/darwin-arm64 0.1.0-beta.39 → 0.1.0-beta.41

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.
@@ -1470,7 +1470,7 @@ var require_sender = __commonJS({
1470
1470
  */
1471
1471
  static frame(data, options) {
1472
1472
  let mask;
1473
- let merge2 = false;
1473
+ let merge3 = false;
1474
1474
  let offset = 2;
1475
1475
  let skipMasking = false;
1476
1476
  if (options.mask) {
@@ -1503,7 +1503,7 @@ var require_sender = __commonJS({
1503
1503
  }
1504
1504
  } else {
1505
1505
  dataLength = data.length;
1506
- merge2 = options.mask && options.readOnly && !skipMasking;
1506
+ merge3 = options.mask && options.readOnly && !skipMasking;
1507
1507
  }
1508
1508
  let payloadLength = dataLength;
1509
1509
  if (dataLength >= 65536) {
@@ -1513,7 +1513,7 @@ var require_sender = __commonJS({
1513
1513
  offset += 2;
1514
1514
  payloadLength = 126;
1515
1515
  }
1516
- const target = Buffer.allocUnsafe(merge2 ? dataLength + offset : offset);
1516
+ const target = Buffer.allocUnsafe(merge3 ? dataLength + offset : offset);
1517
1517
  target[0] = options.fin ? options.opcode | 128 : options.opcode;
1518
1518
  if (options.rsv1) target[0] |= 64;
1519
1519
  target[1] = payloadLength;
@@ -1530,7 +1530,7 @@ var require_sender = __commonJS({
1530
1530
  target[offset - 2] = mask[2];
1531
1531
  target[offset - 1] = mask[3];
1532
1532
  if (skipMasking) return [target, data];
1533
- if (merge2) {
1533
+ if (merge3) {
1534
1534
  applyMask(data, mask, target, offset, dataLength);
1535
1535
  return [target];
1536
1536
  }
@@ -2286,7 +2286,7 @@ var require_websocket = __commonJS({
2286
2286
  var http = __require("http");
2287
2287
  var net2 = __require("net");
2288
2288
  var tls = __require("tls");
2289
- var { randomBytes: randomBytes12, createHash: createHash11 } = __require("crypto");
2289
+ var { randomBytes: randomBytes13, createHash: createHash11 } = __require("crypto");
2290
2290
  var { Duplex, Readable } = __require("stream");
2291
2291
  var { URL: URL2 } = __require("url");
2292
2292
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -2824,7 +2824,7 @@ var require_websocket = __commonJS({
2824
2824
  }
2825
2825
  }
2826
2826
  const defaultPort = isSecure ? 443 : 80;
2827
- const key = randomBytes12(16).toString("base64");
2827
+ const key = randomBytes13(16).toString("base64");
2828
2828
  const request = isSecure ? https.request : http.request;
2829
2829
  const protocolSet = /* @__PURE__ */ new Set();
2830
2830
  let perMessageDeflate;
@@ -43048,13 +43048,13 @@ function refuse(message, io2 = realIo) {
43048
43048
 
43049
43049
  // packages/agent-host/src/main.ts
43050
43050
  import { parseArgs } from "node:util";
43051
- import { randomBytes as randomBytes11 } from "node:crypto";
43051
+ import { randomBytes as randomBytes12 } from "node:crypto";
43052
43052
  import { hostname as hostname5 } from "node:os";
43053
- import { dirname as dirname23, join as join52 } from "node:path";
43054
- import { mkdirSync as mkdirSync19, writeSync as writeSync3 } from "node:fs";
43053
+ import { dirname as dirname26, join as join57 } from "node:path";
43054
+ import { mkdirSync as mkdirSync20, writeSync as writeSync3 } from "node:fs";
43055
43055
 
43056
43056
  // packages/protocol/src/version.generated.ts
43057
- var APP_VERSION = "0.1.0-beta.39";
43057
+ var APP_VERSION = "0.1.0-beta.41";
43058
43058
 
43059
43059
  // packages/protocol/src/brand.ts
43060
43060
  var APP_NAME = "Centralu";
@@ -43998,14 +43998,14 @@ function promiseAllObject(promisesObj) {
43998
43998
  }
43999
43999
  function randomString(length = 10) {
44000
44000
  const chars = "abcdefghijklmnopqrstuvwxyz";
44001
- let str4 = "";
44001
+ let str5 = "";
44002
44002
  for (let i = 0; i < length; i++) {
44003
- str4 += chars[Math.floor(Math.random() * chars.length)];
44003
+ str5 += chars[Math.floor(Math.random() * chars.length)];
44004
44004
  }
44005
- return str4;
44005
+ return str5;
44006
44006
  }
44007
- function esc(str4) {
44008
- return JSON.stringify(str4);
44007
+ function esc(str5) {
44008
+ return JSON.stringify(str5);
44009
44009
  }
44010
44010
  function slugify(input) {
44011
44011
  return input.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
@@ -44119,8 +44119,8 @@ var primitiveTypes = /* @__PURE__ */ new Set([
44119
44119
  "symbol",
44120
44120
  "undefined"
44121
44121
  ]);
44122
- function escapeRegex(str4) {
44123
- return str4.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
44122
+ function escapeRegex(str5) {
44123
+ return str5.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
44124
44124
  }
44125
44125
  function clone(inst, def, params) {
44126
44126
  const cl2 = new inst._zod.constr(def ?? inst._zod.def);
@@ -57740,6 +57740,7 @@ function isProjectId(value) {
57740
57740
  }
57741
57741
  var ToolName = external_exports.string().min(1);
57742
57742
  var AppId = external_exports.string();
57743
+ var PermissionPreset = external_exports.enum(["safe", "normal", "edits", "auto"]);
57743
57744
 
57744
57745
  // packages/protocol/src/entities/sessions.ts
57745
57746
  var SessionState = external_exports.enum(["idle", "working", "waiting_approval", "waiting_input", "limited", "error"]);
@@ -57885,7 +57886,9 @@ var MachineOperation = external_exports.object({
57885
57886
  step: external_exports.string(),
57886
57887
  /** The version it brings the machine to, when there is one */
57887
57888
  target: external_exports.string().nullable().default(null),
57888
- at: external_exports.number()
57889
+ at: external_exports.number(),
57890
+ /** What `wait_idle` (sessions in a turn) or `wait_open` (open terminals and project commands) waits for: how many. Additive (#1579) */
57891
+ count: external_exports.number().optional()
57889
57892
  });
57890
57893
  var MachineAutostart = external_exports.object({
57891
57894
  on: external_exports.boolean().catch(false),
@@ -58502,8 +58505,40 @@ var AdapterCapabilities = external_exports.object({
58502
58505
  * process reads the prompt cache as a live one does (~100% at 2, 6 and 10 minutes on the 1 h lifetime), for +0.7 s
58503
58506
  * (p50) before the first token (#561). Absent is false: a tool whose resume is slow or untested keeps its process.
58504
58507
  */
58505
- sleep: external_exports.boolean().optional()
58508
+ sleep: external_exports.boolean().optional(),
58509
+ /**
58510
+ * The permission presets this tool can keep (docs/plans/acp-adapter.md §5.3). Absent: every preset. Cursor's ACP
58511
+ * server has no switch that makes it ask before every change, so it does not list `safe`, and a session asked to run
58512
+ * it under `safe` is refused (`launchRefusal`) rather than run under a promise it cannot keep.
58513
+ */
58514
+ presets: external_exports.array(PermissionPreset).optional(),
58515
+ /**
58516
+ * Whether the tool can be kept from reading the repository's own files (`.cursor/rules`, `AGENTS.md`, its config)
58517
+ * when the project is not trusted (#92). Absent: true. False: it runs only in trusted projects.
58518
+ */
58519
+ keepsRepoFilesOut: external_exports.boolean().optional(),
58520
+ /**
58521
+ * Whether the tool takes Centralu's role text (`systemPromptAppend`). Absent: true. False: it cannot be a session that
58522
+ * is given one (the orchestrator, a coordinator, an app's agent), nor one that must read no setting files.
58523
+ */
58524
+ roleText: external_exports.boolean().optional()
58506
58525
  });
58526
+ var ORCHESTRATOR_DEMANDS = { permissionPreset: "normal", projectTrusted: false, noSettingFiles: true, roleText: true };
58527
+ function launchRefusal(caps, label, demands) {
58528
+ if (caps.roleText === false && (demands.roleText || demands.noSettingFiles)) {
58529
+ return `${label} cannot take Centralu's role instructions yet, so it cannot run the orchestrator, a coordinator or an app's agent.`;
58530
+ }
58531
+ if (caps.keepsRepoFilesOut === false && !demands.projectTrusted) {
58532
+ return `${label} cannot be kept from reading a project's own files (such as AGENTS.md and its rules), so it runs only in trusted projects.`;
58533
+ }
58534
+ if (caps.presets && !caps.presets.includes(demands.permissionPreset)) {
58535
+ const kept = caps.presets.map((p) => PRESET_LABEL[p]).join(" or ");
58536
+ const asked = PRESET_LABEL[demands.permissionPreset];
58537
+ return `${label} cannot be made to ask before a change as ${asked} does, so it cannot run under ${asked}${kept ? ` (only under ${kept})` : ""}.`;
58538
+ }
58539
+ return null;
58540
+ }
58541
+ var PRESET_LABEL = { safe: "Safe", normal: "Normal", edits: "Edits", auto: "Auto" };
58507
58542
  var ModelOption = external_exports.object({
58508
58543
  id: external_exports.string(),
58509
58544
  label: external_exports.string(),
@@ -58522,7 +58557,6 @@ var ModelOption = external_exports.object({
58522
58557
  });
58523
58558
 
58524
58559
  // packages/protocol/src/entities/approvals.ts
58525
- var PermissionPreset = external_exports.enum(["safe", "normal", "edits", "auto"]);
58526
58560
  var ApprovalDecision = external_exports.enum(["allow", "deny", "always"]);
58527
58561
  var ProjectConsentKind = external_exports.enum(["delegate", "apps", "agents"]);
58528
58562
  var ApprovalScope = external_exports.enum(["session", "project"]);
@@ -59440,8 +59474,8 @@ function alwaysAllowKey(detail) {
59440
59474
  if (detail.kind === "file_edit" && detail.path.trim() !== "" && detail.path !== NO_PATH) return detail.path;
59441
59475
  return "";
59442
59476
  }
59443
- function matchesAllowRule(key, matcher) {
59444
- return key === matcher;
59477
+ function matchesAllowRule(key, matcher2) {
59478
+ return key === matcher2;
59445
59479
  }
59446
59480
 
59447
59481
  // packages/protocol/src/events.ts
@@ -64520,6 +64554,9 @@ function commandInterpreter(env = process.env) {
64520
64554
  function clonesFiles(platform = HOST_PLATFORM) {
64521
64555
  return platform === "darwin";
64522
64556
  }
64557
+ function installsCliAsVersionFolders(platform = HOST_PLATFORM) {
64558
+ return platform === "win32";
64559
+ }
64523
64560
  function locksRunningPrograms(platform = HOST_PLATFORM) {
64524
64561
  return platform === "win32";
64525
64562
  }
@@ -64529,6 +64566,13 @@ function usesShippedConpty(platform = HOST_PLATFORM) {
64529
64566
  function locksWatchedFolders(platform = HOST_PLATFORM) {
64530
64567
  return platform === "win32";
64531
64568
  }
64569
+ function systemSettingsFolder(app2, env = process.env, platform = HOST_PLATFORM) {
64570
+ if (platform === "win32") return win32.join(env.ProgramData || "C:\\ProgramData", app2);
64571
+ return platform === "darwin" ? `/Library/Application Support/${app2}` : `/etc/${app2}`;
64572
+ }
64573
+ function openCodeMatchesAnyCase(platform = HOST_PLATFORM) {
64574
+ return platform === "win32";
64575
+ }
64532
64576
  function programFile(name, platform = HOST_PLATFORM) {
64533
64577
  return platform === "win32" ? `${name}.exe` : name;
64534
64578
  }
@@ -66281,15 +66325,15 @@ function buildViewCsp(csp) {
66281
66325
  const frame = sanitizeDomains(csp?.frameDomains);
66282
66326
  const base2 = sanitizeDomains(csp?.baseUriDomains);
66283
66327
  const r = resource.kept.join(" ");
66284
- const join53 = (...parts) => parts.filter(Boolean).join(" ");
66328
+ const join58 = (...parts) => parts.filter(Boolean).join(" ");
66285
66329
  const policy = [
66286
66330
  "default-src 'none'",
66287
- `script-src ${join53("'unsafe-inline'", r)}`,
66288
- `style-src ${join53("'unsafe-inline'", r)}`,
66289
- `img-src ${join53("data: blob:", r)}`,
66290
- `font-src ${join53("data:", r)}`,
66291
- `media-src ${join53("data: blob:", r)}`,
66292
- `worker-src ${join53("blob:", r)}`,
66331
+ `script-src ${join58("'unsafe-inline'", r)}`,
66332
+ `style-src ${join58("'unsafe-inline'", r)}`,
66333
+ `img-src ${join58("data: blob:", r)}`,
66334
+ `font-src ${join58("data:", r)}`,
66335
+ `media-src ${join58("data: blob:", r)}`,
66336
+ `worker-src ${join58("blob:", r)}`,
66293
66337
  `connect-src ${connect2.kept.length ? connect2.kept.join(" ") : "'none'"}`,
66294
66338
  `frame-src ${frame.kept.length ? frame.kept.join(" ") : "'none'"}`,
66295
66339
  "form-action 'none'",
@@ -67050,8 +67094,8 @@ function launchFor(path2, deps = realDeps(), depth = 0) {
67050
67094
  return plain3;
67051
67095
  }
67052
67096
  const dir = win325.dirname(path2);
67053
- const relative9 = shimTarget(text2);
67054
- const target = relative9 ? win325.resolve(dir, relative9) : nodeShimEntry(text2, dir, deps);
67097
+ const relative10 = shimTarget(text2);
67098
+ const target = relative10 ? win325.resolve(dir, relative10) : nodeShimEntry(text2, dir, deps);
67055
67099
  if (!target || !deps.exists(target)) return plain3;
67056
67100
  if (/\.(c|m)?js$/i.test(target)) return { command: deps.node, args: [target] };
67057
67101
  if (/\.exe$/i.test(target)) return { command: target, args: [] };
@@ -67073,9 +67117,9 @@ function nodeShimEntry(text2, dir, deps) {
67073
67117
  const started = /^\s*"%NODE_EXE%"\s+"%(\w+)%"\s+%\*\s*$/m.exec(text2)?.[1];
67074
67118
  if (!started) return null;
67075
67119
  const set3 = (name) => new RegExp(`^\\s*SET\\s+"${name}=%~dp0\\\\?([^"%]+)"`, "im").exec(text2)?.[1]?.trim() ?? null;
67076
- const relative9 = set3(started);
67077
- if (!relative9) return null;
67078
- const base2 = win325.resolve(dir, relative9);
67120
+ const relative10 = set3(started);
67121
+ if (!relative10) return null;
67122
+ const base2 = win325.resolve(dir, relative10);
67079
67123
  const prefixJs = set3("NPM_PREFIX_JS");
67080
67124
  if (!prefixJs || !deps.runJs) return base2;
67081
67125
  const prefix = deps.runJs(win325.resolve(dir, prefixJs))?.trim().split(/\r?\n/).pop()?.trim();
@@ -67251,6 +67295,24 @@ function shoot(handle, signal, os2 = REAL_OS) {
67251
67295
  const tree = descendants(rows3, [pid]);
67252
67296
  return rows3.filter((r) => tree.has(r.pid));
67253
67297
  }
67298
+ function notedGroups(rows3, root3, known, self2) {
67299
+ if (rows3.length === 0) return new Set(known);
67300
+ const live2 = new Set(rows3.map((r) => r.pgid));
67301
+ const kept = new Set([...known].filter((g5) => live2.has(g5)));
67302
+ if (rows3.some((r) => r.pid === root3)) {
67303
+ for (const g5 of killTargets([...rows3], root3, self2)) if (g5 !== root3) kept.add(g5);
67304
+ }
67305
+ return kept;
67306
+ }
67307
+ async function noteGroups(root3, known, platform = process.platform) {
67308
+ if (platform === "win32") return /* @__PURE__ */ new Set();
67309
+ let rows3 = [];
67310
+ try {
67311
+ rows3 = parsePs((await runFile("ps", ["-A", "-o", "pid=,ppid=,pgid="], { encoding: "utf8", timeout: 2e3, windowsHide: true })).stdout);
67312
+ } catch {
67313
+ }
67314
+ return notedGroups(rows3, root3, known, process.pid);
67315
+ }
67254
67316
  function killTree(handle, signal, os2 = REAL_OS) {
67255
67317
  shoot(handle, signal, os2);
67256
67318
  }
@@ -68233,7 +68295,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
68233
68295
  }
68234
68296
  exports._ = _;
68235
68297
  const plus = new _Code("+");
68236
- function str4(strs, ...args) {
68298
+ function str5(strs, ...args) {
68237
68299
  const expr = [safeStringify(strs[0])];
68238
68300
  let i = 0;
68239
68301
  while (i < args.length) {
@@ -68244,7 +68306,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
68244
68306
  optimize(expr);
68245
68307
  return new _Code(expr);
68246
68308
  }
68247
- exports.str = str4;
68309
+ exports.str = str5;
68248
68310
  function addCodeArg(code2, arg) {
68249
68311
  if (arg instanceof _Code) code2.push(...arg._items);
68250
68312
  else if (arg instanceof Name) code2.push(arg);
@@ -68277,7 +68339,7 @@ var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
68277
68339
  if (typeof b2 == "string" && b2[0] === '"' && !(a instanceof Name)) return `"${a}${b2.slice(1)}`;
68278
68340
  }
68279
68341
  function strConcat(c1, c2) {
68280
- return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str4`${c1}${c2}`;
68342
+ return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str5`${c1}${c2}`;
68281
68343
  }
68282
68344
  exports.strConcat = strConcat;
68283
68345
  function interpolate(x) {
@@ -69156,21 +69218,21 @@ var require_util = /* @__PURE__ */ __commonJSMin(((exports) => {
69156
69218
  return (0, codegen_1._)`${topSchemaRef}${schemaPath}${(0, codegen_1.getProperty)(keyword)}`;
69157
69219
  }
69158
69220
  exports.schemaRefOrVal = schemaRefOrVal;
69159
- function unescapeFragment(str4) {
69160
- return unescapeJsonPointer(decodeURIComponent(str4));
69221
+ function unescapeFragment(str5) {
69222
+ return unescapeJsonPointer(decodeURIComponent(str5));
69161
69223
  }
69162
69224
  exports.unescapeFragment = unescapeFragment;
69163
- function escapeFragment(str4) {
69164
- return encodeURIComponent(escapeJsonPointer(str4));
69225
+ function escapeFragment(str5) {
69226
+ return encodeURIComponent(escapeJsonPointer(str5));
69165
69227
  }
69166
69228
  exports.escapeFragment = escapeFragment;
69167
- function escapeJsonPointer(str4) {
69168
- if (typeof str4 == "number") return `${str4}`;
69169
- return str4.replace(/~/g, "~0").replace(/\//g, "~1");
69229
+ function escapeJsonPointer(str5) {
69230
+ if (typeof str5 == "number") return `${str5}`;
69231
+ return str5.replace(/~/g, "~0").replace(/\//g, "~1");
69170
69232
  }
69171
69233
  exports.escapeJsonPointer = escapeJsonPointer;
69172
- function unescapeJsonPointer(str4) {
69173
- return str4.replace(/~1/g, "/").replace(/~0/g, "~");
69234
+ function unescapeJsonPointer(str5) {
69235
+ return str5.replace(/~1/g, "/").replace(/~0/g, "~");
69174
69236
  }
69175
69237
  exports.unescapeJsonPointer = unescapeJsonPointer;
69176
69238
  function eachItem(xs2, f) {
@@ -70056,8 +70118,8 @@ var require_json_schema_traverse = /* @__PURE__ */ __commonJSMin(((exports, modu
70056
70118
  post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
70057
70119
  }
70058
70120
  }
70059
- function escapeJsonPtr(str4) {
70060
- return str4.replace(/~/g, "~0").replace(/\//g, "~1");
70121
+ function escapeJsonPtr(str5) {
70122
+ return str5.replace(/~/g, "~0").replace(/\//g, "~1");
70061
70123
  }
70062
70124
  }));
70063
70125
  var require_resolve = /* @__PURE__ */ __commonJSMin(((exports) => {
@@ -70949,9 +71011,9 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
70949
71011
  isIPV6: false
70950
71012
  };
70951
71013
  }
70952
- function findToken(str4, token2) {
71014
+ function findToken(str5, token2) {
70953
71015
  let ind = 0;
70954
- for (let i = 0; i < str4.length; i++) if (str4[i] === token2) ind++;
71016
+ for (let i = 0; i < str5.length; i++) if (str5[i] === token2) ind++;
70955
71017
  return ind;
70956
71018
  }
70957
71019
  function removeDotSegments(path2) {
@@ -71225,41 +71287,41 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
71225
71287
  schemelessOptions.skipEscape = true;
71226
71288
  return serialize(resolved, schemelessOptions);
71227
71289
  }
71228
- function resolveComponent(base2, relative9, options, skipNormalization) {
71290
+ function resolveComponent(base2, relative10, options, skipNormalization) {
71229
71291
  const target = {};
71230
71292
  if (!skipNormalization) {
71231
71293
  base2 = parse6(serialize(base2, options), options);
71232
- relative9 = parse6(serialize(relative9, options), options);
71294
+ relative10 = parse6(serialize(relative10, options), options);
71233
71295
  }
71234
71296
  options = options || {};
71235
- if (!options.tolerant && relative9.scheme) {
71236
- target.scheme = relative9.scheme;
71237
- target.userinfo = relative9.userinfo;
71238
- target.host = relative9.host;
71239
- target.port = relative9.port;
71240
- target.path = removeDotSegments(relative9.path || "");
71241
- target.query = relative9.query;
71297
+ if (!options.tolerant && relative10.scheme) {
71298
+ target.scheme = relative10.scheme;
71299
+ target.userinfo = relative10.userinfo;
71300
+ target.host = relative10.host;
71301
+ target.port = relative10.port;
71302
+ target.path = removeDotSegments(relative10.path || "");
71303
+ target.query = relative10.query;
71242
71304
  } else {
71243
- if (relative9.userinfo !== void 0 || relative9.host !== void 0 || relative9.port !== void 0) {
71244
- target.userinfo = relative9.userinfo;
71245
- target.host = relative9.host;
71246
- target.port = relative9.port;
71247
- target.path = removeDotSegments(relative9.path || "");
71248
- target.query = relative9.query;
71305
+ if (relative10.userinfo !== void 0 || relative10.host !== void 0 || relative10.port !== void 0) {
71306
+ target.userinfo = relative10.userinfo;
71307
+ target.host = relative10.host;
71308
+ target.port = relative10.port;
71309
+ target.path = removeDotSegments(relative10.path || "");
71310
+ target.query = relative10.query;
71249
71311
  } else {
71250
- if (!relative9.path) {
71312
+ if (!relative10.path) {
71251
71313
  target.path = base2.path;
71252
- if (relative9.query !== void 0) target.query = relative9.query;
71314
+ if (relative10.query !== void 0) target.query = relative10.query;
71253
71315
  else target.query = base2.query;
71254
71316
  } else {
71255
- if (relative9.path[0] === "/") target.path = removeDotSegments(relative9.path);
71317
+ if (relative10.path[0] === "/") target.path = removeDotSegments(relative10.path);
71256
71318
  else {
71257
- if ((base2.userinfo !== void 0 || base2.host !== void 0 || base2.port !== void 0) && !base2.path) target.path = "/" + relative9.path;
71258
- else if (!base2.path) target.path = relative9.path;
71259
- else target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative9.path;
71319
+ if ((base2.userinfo !== void 0 || base2.host !== void 0 || base2.port !== void 0) && !base2.path) target.path = "/" + relative10.path;
71320
+ else if (!base2.path) target.path = relative10.path;
71321
+ else target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative10.path;
71260
71322
  target.path = removeDotSegments(target.path);
71261
71323
  }
71262
- target.query = relative9.query;
71324
+ target.query = relative10.query;
71263
71325
  }
71264
71326
  target.userinfo = base2.userinfo;
71265
71327
  target.host = base2.host;
@@ -71267,7 +71329,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
71267
71329
  }
71268
71330
  target.scheme = base2.scheme;
71269
71331
  }
71270
- target.fragment = relative9.fragment;
71332
+ target.fragment = relative10.fragment;
71271
71333
  return target;
71272
71334
  }
71273
71335
  function equal(uriA, uriB, options) {
@@ -71466,7 +71528,7 @@ var require_core$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
71466
71528
  const util_1 = require_util();
71467
71529
  const $dataRefSchema = require_data();
71468
71530
  const uri_1 = require_uri();
71469
- const defaultRegExp = (str4, flags) => new RegExp(str4, flags);
71531
+ const defaultRegExp = (str5, flags) => new RegExp(str5, flags);
71470
71532
  defaultRegExp.code = "new RegExp";
71471
71533
  const META_IGNORE_OPTIONS = [
71472
71534
  "removeAdditional",
@@ -72172,16 +72234,16 @@ var require_multipleOf = /* @__PURE__ */ __commonJSMin(((exports) => {
72172
72234
  }));
72173
72235
  var require_ucs2length = /* @__PURE__ */ __commonJSMin(((exports) => {
72174
72236
  Object.defineProperty(exports, "__esModule", { value: true });
72175
- function ucs2length(str4) {
72176
- const len = str4.length;
72237
+ function ucs2length(str5) {
72238
+ const len = str5.length;
72177
72239
  let length = 0;
72178
72240
  let pos = 0;
72179
72241
  let value;
72180
72242
  while (pos < len) {
72181
72243
  length++;
72182
- value = str4.charCodeAt(pos++);
72244
+ value = str5.charCodeAt(pos++);
72183
72245
  if (value >= 55296 && value <= 56319 && pos < len) {
72184
- value = str4.charCodeAt(pos);
72246
+ value = str5.charCodeAt(pos);
72185
72247
  if ((value & 64512) === 56320) pos++;
72186
72248
  }
72187
72249
  }
@@ -74892,8 +74954,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
74892
74954
  30,
74893
74955
  31
74894
74956
  ];
74895
- function date5(str4) {
74896
- const matches2 = DATE.exec(str4);
74957
+ function date5(str5) {
74958
+ const matches2 = DATE.exec(str5);
74897
74959
  if (!matches2) return false;
74898
74960
  const year = +matches2[1];
74899
74961
  const month = +matches2[2];
@@ -74908,8 +74970,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
74908
74970
  }
74909
74971
  const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
74910
74972
  function getTime(strictTimeZone) {
74911
- return function time3(str4) {
74912
- const matches2 = TIME.exec(str4);
74973
+ return function time3(str5) {
74974
+ const matches2 = TIME.exec(str5);
74913
74975
  if (!matches2) return false;
74914
74976
  const hr2 = +matches2[1];
74915
74977
  const min = +matches2[2];
@@ -74946,8 +75008,8 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
74946
75008
  const DATE_TIME_SEPARATOR = /t|\s/i;
74947
75009
  function getDateTime(strictTimeZone) {
74948
75010
  const time3 = getTime(strictTimeZone);
74949
- return function date_time(str4) {
74950
- const dateTime = str4.split(DATE_TIME_SEPARATOR);
75011
+ return function date_time(str5) {
75012
+ const dateTime = str5.split(DATE_TIME_SEPARATOR);
74951
75013
  return dateTime.length === 2 && date5(dateTime[0]) && time3(dateTime[1]);
74952
75014
  };
74953
75015
  }
@@ -74968,13 +75030,13 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
74968
75030
  }
74969
75031
  const NOT_URI_FRAGMENT = /\/|:/;
74970
75032
  const URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
74971
- function uri(str4) {
74972
- return NOT_URI_FRAGMENT.test(str4) && URI.test(str4);
75033
+ function uri(str5) {
75034
+ return NOT_URI_FRAGMENT.test(str5) && URI.test(str5);
74973
75035
  }
74974
75036
  const BYTE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gm;
74975
- function byte(str4) {
75037
+ function byte(str5) {
74976
75038
  BYTE.lastIndex = 0;
74977
- return BYTE.test(str4);
75039
+ return BYTE.test(str5);
74978
75040
  }
74979
75041
  const MIN_INT32 = -(2 ** 31);
74980
75042
  const MAX_INT32 = 2 ** 31 - 1;
@@ -74988,10 +75050,10 @@ var require_formats = /* @__PURE__ */ __commonJSMin(((exports) => {
74988
75050
  return true;
74989
75051
  }
74990
75052
  const Z_ANCHOR = /[^\\]\\Z/;
74991
- function regex(str4) {
74992
- if (Z_ANCHOR.test(str4)) return false;
75053
+ function regex(str5) {
75054
+ if (Z_ANCHOR.test(str5)) return false;
74993
75055
  try {
74994
- new RegExp(str4);
75056
+ new RegExp(str5);
74995
75057
  return true;
74996
75058
  } catch (e) {
74997
75059
  return false;
@@ -81172,8 +81234,8 @@ var Server = class extends Protocol {
81172
81234
  _registerLoggingHandler() {
81173
81235
  this.setRequestHandler("logging/setLevel", async (request, ctx) => {
81174
81236
  const transportSessionId = ctx.sessionId || ctx.http?.req?.headers.get("mcp-session-id") || void 0;
81175
- const { level } = request.params;
81176
- const parseResult = parseSchema(LoggingLevelSchema, level);
81237
+ const { level: level2 } = request.params;
81238
+ const parseResult = parseSchema(LoggingLevelSchema, level2);
81177
81239
  if (parseResult.success) this._loggingLevels.set(transportSessionId, parseResult.data);
81178
81240
  return {};
81179
81241
  });
@@ -81184,18 +81246,18 @@ var Server = class extends Protocol {
81184
81246
  ...ctx,
81185
81247
  mcpReq: {
81186
81248
  ...ctx.mcpReq,
81187
- log: (level, data, logger) => {
81249
+ log: (level2, data, logger) => {
81188
81250
  if (!this._capabilities.logging) return Promise.resolve();
81189
81251
  let threshold;
81190
81252
  if (this._servedModernEra()) {
81191
81253
  threshold = ctx.mcpReq.envelope?.[LOG_LEVEL_META_KEY];
81192
81254
  if (threshold === void 0) return Promise.resolve();
81193
81255
  } else threshold = this._loggingLevels.get(ctx.sessionId) ?? this._loggingLevels.get(void 0);
81194
- if (threshold !== void 0 && this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(threshold)) return Promise.resolve();
81256
+ if (threshold !== void 0 && this.LOG_LEVEL_SEVERITY.get(level2) < this.LOG_LEVEL_SEVERITY.get(threshold)) return Promise.resolve();
81195
81257
  return ctx.mcpReq.notify({
81196
81258
  method: "notifications/message",
81197
81259
  params: {
81198
- level,
81260
+ level: level2,
81199
81261
  data,
81200
81262
  logger
81201
81263
  }
@@ -81213,10 +81275,10 @@ var Server = class extends Protocol {
81213
81275
  };
81214
81276
  }
81215
81277
  _loggingLevels = /* @__PURE__ */ new Map();
81216
- LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level, index) => [level, index]));
81217
- isMessageIgnored = (level, sessionId) => {
81278
+ LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level2, index) => [level2, index]));
81279
+ isMessageIgnored = (level2, sessionId) => {
81218
81280
  const currentLevel = this._loggingLevels.get(sessionId);
81219
- return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false;
81281
+ return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level2) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false;
81220
81282
  };
81221
81283
  /**
81222
81284
  * Registers new capabilities. This can only be called before connecting to a transport.
@@ -89604,7 +89666,7 @@ var require_code$12 = /* @__PURE__ */ __commonJSMin2(((exports) => {
89604
89666
  }
89605
89667
  exports._ = _;
89606
89668
  const plus = new _Code("+");
89607
- function str4(strs, ...args) {
89669
+ function str5(strs, ...args) {
89608
89670
  const expr = [safeStringify(strs[0])];
89609
89671
  let i = 0;
89610
89672
  while (i < args.length) {
@@ -89615,7 +89677,7 @@ var require_code$12 = /* @__PURE__ */ __commonJSMin2(((exports) => {
89615
89677
  optimize(expr);
89616
89678
  return new _Code(expr);
89617
89679
  }
89618
- exports.str = str4;
89680
+ exports.str = str5;
89619
89681
  function addCodeArg(code2, arg) {
89620
89682
  if (arg instanceof _Code) code2.push(...arg._items);
89621
89683
  else if (arg instanceof Name) code2.push(arg);
@@ -89648,7 +89710,7 @@ var require_code$12 = /* @__PURE__ */ __commonJSMin2(((exports) => {
89648
89710
  if (typeof b2 == "string" && b2[0] === '"' && !(a instanceof Name)) return `"${a}${b2.slice(1)}`;
89649
89711
  }
89650
89712
  function strConcat(c1, c2) {
89651
- return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str4`${c1}${c2}`;
89713
+ return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str5`${c1}${c2}`;
89652
89714
  }
89653
89715
  exports.strConcat = strConcat;
89654
89716
  function interpolate(x) {
@@ -90527,21 +90589,21 @@ var require_util2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
90527
90589
  return (0, codegen_1._)`${topSchemaRef}${schemaPath}${(0, codegen_1.getProperty)(keyword)}`;
90528
90590
  }
90529
90591
  exports.schemaRefOrVal = schemaRefOrVal;
90530
- function unescapeFragment(str4) {
90531
- return unescapeJsonPointer(decodeURIComponent(str4));
90592
+ function unescapeFragment(str5) {
90593
+ return unescapeJsonPointer(decodeURIComponent(str5));
90532
90594
  }
90533
90595
  exports.unescapeFragment = unescapeFragment;
90534
- function escapeFragment(str4) {
90535
- return encodeURIComponent(escapeJsonPointer(str4));
90596
+ function escapeFragment(str5) {
90597
+ return encodeURIComponent(escapeJsonPointer(str5));
90536
90598
  }
90537
90599
  exports.escapeFragment = escapeFragment;
90538
- function escapeJsonPointer(str4) {
90539
- if (typeof str4 == "number") return `${str4}`;
90540
- return str4.replace(/~/g, "~0").replace(/\//g, "~1");
90600
+ function escapeJsonPointer(str5) {
90601
+ if (typeof str5 == "number") return `${str5}`;
90602
+ return str5.replace(/~/g, "~0").replace(/\//g, "~1");
90541
90603
  }
90542
90604
  exports.escapeJsonPointer = escapeJsonPointer;
90543
- function unescapeJsonPointer(str4) {
90544
- return str4.replace(/~1/g, "/").replace(/~0/g, "~");
90605
+ function unescapeJsonPointer(str5) {
90606
+ return str5.replace(/~1/g, "/").replace(/~0/g, "~");
90545
90607
  }
90546
90608
  exports.unescapeJsonPointer = unescapeJsonPointer;
90547
90609
  function eachItem(xs2, f) {
@@ -91427,8 +91489,8 @@ var require_json_schema_traverse2 = /* @__PURE__ */ __commonJSMin2(((exports, mo
91427
91489
  post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
91428
91490
  }
91429
91491
  }
91430
- function escapeJsonPtr(str4) {
91431
- return str4.replace(/~/g, "~0").replace(/\//g, "~1");
91492
+ function escapeJsonPtr(str5) {
91493
+ return str5.replace(/~/g, "~0").replace(/\//g, "~1");
91432
91494
  }
91433
91495
  }));
91434
91496
  var require_resolve2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
@@ -92320,9 +92382,9 @@ var require_utils2 = /* @__PURE__ */ __commonJSMin2(((exports, module) => {
92320
92382
  isIPV6: false
92321
92383
  };
92322
92384
  }
92323
- function findToken(str4, token2) {
92385
+ function findToken(str5, token2) {
92324
92386
  let ind = 0;
92325
- for (let i = 0; i < str4.length; i++) if (str4[i] === token2) ind++;
92387
+ for (let i = 0; i < str5.length; i++) if (str5[i] === token2) ind++;
92326
92388
  return ind;
92327
92389
  }
92328
92390
  function removeDotSegments(path2) {
@@ -92596,41 +92658,41 @@ var require_fast_uri2 = /* @__PURE__ */ __commonJSMin2(((exports, module) => {
92596
92658
  schemelessOptions.skipEscape = true;
92597
92659
  return serialize(resolved, schemelessOptions);
92598
92660
  }
92599
- function resolveComponent(base2, relative9, options, skipNormalization) {
92661
+ function resolveComponent(base2, relative10, options, skipNormalization) {
92600
92662
  const target = {};
92601
92663
  if (!skipNormalization) {
92602
92664
  base2 = parse6(serialize(base2, options), options);
92603
- relative9 = parse6(serialize(relative9, options), options);
92665
+ relative10 = parse6(serialize(relative10, options), options);
92604
92666
  }
92605
92667
  options = options || {};
92606
- if (!options.tolerant && relative9.scheme) {
92607
- target.scheme = relative9.scheme;
92608
- target.userinfo = relative9.userinfo;
92609
- target.host = relative9.host;
92610
- target.port = relative9.port;
92611
- target.path = removeDotSegments(relative9.path || "");
92612
- target.query = relative9.query;
92668
+ if (!options.tolerant && relative10.scheme) {
92669
+ target.scheme = relative10.scheme;
92670
+ target.userinfo = relative10.userinfo;
92671
+ target.host = relative10.host;
92672
+ target.port = relative10.port;
92673
+ target.path = removeDotSegments(relative10.path || "");
92674
+ target.query = relative10.query;
92613
92675
  } else {
92614
- if (relative9.userinfo !== void 0 || relative9.host !== void 0 || relative9.port !== void 0) {
92615
- target.userinfo = relative9.userinfo;
92616
- target.host = relative9.host;
92617
- target.port = relative9.port;
92618
- target.path = removeDotSegments(relative9.path || "");
92619
- target.query = relative9.query;
92676
+ if (relative10.userinfo !== void 0 || relative10.host !== void 0 || relative10.port !== void 0) {
92677
+ target.userinfo = relative10.userinfo;
92678
+ target.host = relative10.host;
92679
+ target.port = relative10.port;
92680
+ target.path = removeDotSegments(relative10.path || "");
92681
+ target.query = relative10.query;
92620
92682
  } else {
92621
- if (!relative9.path) {
92683
+ if (!relative10.path) {
92622
92684
  target.path = base2.path;
92623
- if (relative9.query !== void 0) target.query = relative9.query;
92685
+ if (relative10.query !== void 0) target.query = relative10.query;
92624
92686
  else target.query = base2.query;
92625
92687
  } else {
92626
- if (relative9.path[0] === "/") target.path = removeDotSegments(relative9.path);
92688
+ if (relative10.path[0] === "/") target.path = removeDotSegments(relative10.path);
92627
92689
  else {
92628
- if ((base2.userinfo !== void 0 || base2.host !== void 0 || base2.port !== void 0) && !base2.path) target.path = "/" + relative9.path;
92629
- else if (!base2.path) target.path = relative9.path;
92630
- else target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative9.path;
92690
+ if ((base2.userinfo !== void 0 || base2.host !== void 0 || base2.port !== void 0) && !base2.path) target.path = "/" + relative10.path;
92691
+ else if (!base2.path) target.path = relative10.path;
92692
+ else target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative10.path;
92631
92693
  target.path = removeDotSegments(target.path);
92632
92694
  }
92633
- target.query = relative9.query;
92695
+ target.query = relative10.query;
92634
92696
  }
92635
92697
  target.userinfo = base2.userinfo;
92636
92698
  target.host = base2.host;
@@ -92638,7 +92700,7 @@ var require_fast_uri2 = /* @__PURE__ */ __commonJSMin2(((exports, module) => {
92638
92700
  }
92639
92701
  target.scheme = base2.scheme;
92640
92702
  }
92641
- target.fragment = relative9.fragment;
92703
+ target.fragment = relative10.fragment;
92642
92704
  return target;
92643
92705
  }
92644
92706
  function equal(uriA, uriB, options) {
@@ -92837,7 +92899,7 @@ var require_core$32 = /* @__PURE__ */ __commonJSMin2(((exports) => {
92837
92899
  const util_1 = require_util2();
92838
92900
  const $dataRefSchema = require_data2();
92839
92901
  const uri_1 = require_uri2();
92840
- const defaultRegExp = (str4, flags) => new RegExp(str4, flags);
92902
+ const defaultRegExp = (str5, flags) => new RegExp(str5, flags);
92841
92903
  defaultRegExp.code = "new RegExp";
92842
92904
  const META_IGNORE_OPTIONS = [
92843
92905
  "removeAdditional",
@@ -93543,16 +93605,16 @@ var require_multipleOf2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
93543
93605
  }));
93544
93606
  var require_ucs2length2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
93545
93607
  Object.defineProperty(exports, "__esModule", { value: true });
93546
- function ucs2length(str4) {
93547
- const len = str4.length;
93608
+ function ucs2length(str5) {
93609
+ const len = str5.length;
93548
93610
  let length = 0;
93549
93611
  let pos = 0;
93550
93612
  let value;
93551
93613
  while (pos < len) {
93552
93614
  length++;
93553
- value = str4.charCodeAt(pos++);
93615
+ value = str5.charCodeAt(pos++);
93554
93616
  if (value >= 55296 && value <= 56319 && pos < len) {
93555
- value = str4.charCodeAt(pos);
93617
+ value = str5.charCodeAt(pos);
93556
93618
  if ((value & 64512) === 56320) pos++;
93557
93619
  }
93558
93620
  }
@@ -96263,8 +96325,8 @@ var require_formats2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
96263
96325
  30,
96264
96326
  31
96265
96327
  ];
96266
- function date5(str4) {
96267
- const matches2 = DATE.exec(str4);
96328
+ function date5(str5) {
96329
+ const matches2 = DATE.exec(str5);
96268
96330
  if (!matches2) return false;
96269
96331
  const year = +matches2[1];
96270
96332
  const month = +matches2[2];
@@ -96279,8 +96341,8 @@ var require_formats2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
96279
96341
  }
96280
96342
  const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
96281
96343
  function getTime(strictTimeZone) {
96282
- return function time3(str4) {
96283
- const matches2 = TIME.exec(str4);
96344
+ return function time3(str5) {
96345
+ const matches2 = TIME.exec(str5);
96284
96346
  if (!matches2) return false;
96285
96347
  const hr2 = +matches2[1];
96286
96348
  const min = +matches2[2];
@@ -96317,8 +96379,8 @@ var require_formats2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
96317
96379
  const DATE_TIME_SEPARATOR = /t|\s/i;
96318
96380
  function getDateTime(strictTimeZone) {
96319
96381
  const time3 = getTime(strictTimeZone);
96320
- return function date_time(str4) {
96321
- const dateTime = str4.split(DATE_TIME_SEPARATOR);
96382
+ return function date_time(str5) {
96383
+ const dateTime = str5.split(DATE_TIME_SEPARATOR);
96322
96384
  return dateTime.length === 2 && date5(dateTime[0]) && time3(dateTime[1]);
96323
96385
  };
96324
96386
  }
@@ -96339,13 +96401,13 @@ var require_formats2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
96339
96401
  }
96340
96402
  const NOT_URI_FRAGMENT = /\/|:/;
96341
96403
  const URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;
96342
- function uri(str4) {
96343
- return NOT_URI_FRAGMENT.test(str4) && URI.test(str4);
96404
+ function uri(str5) {
96405
+ return NOT_URI_FRAGMENT.test(str5) && URI.test(str5);
96344
96406
  }
96345
96407
  const BYTE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gm;
96346
- function byte(str4) {
96408
+ function byte(str5) {
96347
96409
  BYTE.lastIndex = 0;
96348
- return BYTE.test(str4);
96410
+ return BYTE.test(str5);
96349
96411
  }
96350
96412
  const MIN_INT32 = -(2 ** 31);
96351
96413
  const MAX_INT32 = 2 ** 31 - 1;
@@ -96359,10 +96421,10 @@ var require_formats2 = /* @__PURE__ */ __commonJSMin2(((exports) => {
96359
96421
  return true;
96360
96422
  }
96361
96423
  const Z_ANCHOR = /[^\\]\\Z/;
96362
- function regex(str4) {
96363
- if (Z_ANCHOR.test(str4)) return false;
96424
+ function regex(str5) {
96425
+ if (Z_ANCHOR.test(str5)) return false;
96364
96426
  try {
96365
- new RegExp(str4);
96427
+ new RegExp(str5);
96366
96428
  return true;
96367
96429
  } catch (e) {
96368
96430
  return false;
@@ -98240,10 +98302,10 @@ var Client = class extends Protocol2 {
98240
98302
  * Remains functional during the deprecation window (at least twelve months).
98241
98303
  * Migrate to stderr logging (STDIO servers) or OpenTelemetry.
98242
98304
  */
98243
- async setLoggingLevel(level, options) {
98305
+ async setLoggingLevel(level2, options) {
98244
98306
  return this.request({
98245
98307
  method: "logging/setLevel",
98246
- params: { level }
98308
+ params: { level: level2 }
98247
98309
  }, options);
98248
98310
  }
98249
98311
  /** Retrieves a prompt by name from the server, passing the given arguments for template substitution. */
@@ -107449,13 +107511,13 @@ One session = one agent process (Claude Code or Codex).
107449
107511
  handoff by default (it can be restored from Settings \u2192 Archive); a checkbox keeps it instead. The orchestrator can hand a session off too
107450
107512
  (its handoff_session command), after you approve its card unless the session is at rest and runs under a preset the orchestrator has itself; the original stays. A worktree session cannot be
107451
107513
  handed off yet.
107452
- - Archiving: session's \u22EF \u2192 Archive session. The session leaves the sidebar and goes to the session
107514
+ - Archiving: session's \u22EF \u2192 Archive session\u2026. The session leaves the sidebar and goes to the session
107453
107515
  archive (Settings \u2192 Archive), where the person can read it, restore it, or delete it for good.
107454
- An idle session is archived at once and the toast offers Undo; one that is working, waiting on the
107455
- person or running a background job asks first, in a dialog that also asks whether deleting it for
107456
- good should delete the conversation file on the tool's side (on by default). Nothing is deleted on
107457
- its own; turning that off leaves the conversation on the tool, so it can be loaded again from the
107458
- past-conversation list in the New session window.
107516
+ A dialog asks first, for every session, and also asks whether deleting it for good should delete the
107517
+ conversation file on the tool's side (on by default); with the worktree kept it lists anything the archive drops that Undo does
107518
+ not bring back, and the toast after it offers Undo. Nothing is
107519
+ deleted on its own; turning that off leaves the conversation on the tool, so it can be loaded again
107520
+ from the past-conversation list in the New session window.
107459
107521
  - There is no menu to change a session's agent (claude \u2194 codex). A new tool does not know the old
107460
107522
  conversation, so use a handoff to continue with a different tool. The orchestrator is the one
107461
107523
  exception (Settings \u2192 Orchestrator).`,
@@ -108706,14 +108768,43 @@ var RoutedCards = class {
108706
108768
  }
108707
108769
  };
108708
108770
 
108771
+ // packages/agent-host/src/mail/address.ts
108772
+ var SEGMENT = "[A-Za-z0-9][A-Za-z0-9._~-]{0,63}";
108773
+ var SEGMENT_RE = new RegExp(`^${SEGMENT}$`);
108774
+ var SESSION_RE = new RegExp(`^session:(${SEGMENT})/(${SEGMENT})$`);
108775
+ var APP_RE = new RegExp(`^app:(${SEGMENT})/(${SEGMENT})/(${SEGMENT})$`);
108776
+ var PERSON = "person";
108777
+ var CENTRALU = "centralu";
108778
+ function sessionAddress(host, sessionId) {
108779
+ return checked(`session:${host}/${sessionId}`);
108780
+ }
108781
+ function parseAddress(address) {
108782
+ if (address === PERSON) return { kind: "person" };
108783
+ if (address === CENTRALU) return { kind: "centralu" };
108784
+ const [, host, sessionId] = SESSION_RE.exec(address) ?? [];
108785
+ if (host !== void 0 && sessionId !== void 0) return { kind: "session", host, sessionId };
108786
+ const [, appHost, projectId, appId] = APP_RE.exec(address) ?? [];
108787
+ if (appHost !== void 0 && projectId !== void 0 && appId !== void 0) return { kind: "app", host: appHost, projectId, appId };
108788
+ return null;
108789
+ }
108790
+ function checked(address) {
108791
+ if (!parseAddress(address)) throw new Error(`Not a mail address: ${JSON.stringify(address)}`);
108792
+ return address;
108793
+ }
108794
+
108709
108795
  // packages/agent-host/src/sessions/handoff-children.ts
108710
108796
  var SETTLED_CHILD = "finished; waiting for more work";
108711
108797
  function movedChildrenNote(moved, commands = "") {
108712
108798
  return [
108713
108799
  "## Work handed over with this note (added by Centralu)",
108714
108800
  "",
108715
- "The previous session started or asked for these. They now answer to you as each line says (a finished one waits",
108716
- "for more work); use the handle with the tool it names.",
108801
+ ...moved.some((c) => c.thread) ? [
108802
+ "The previous session started or asked for these, or held these mail threads. They are yours now as each line says",
108803
+ "(a finished session waits for more work); use the handle with the tool it names."
108804
+ ] : [
108805
+ "The previous session started or asked for these. They now answer to you as each line says (a finished one waits",
108806
+ "for more work); use the handle with the tool it names."
108807
+ ],
108717
108808
  ...commands ? [commands] : [],
108718
108809
  "",
108719
108810
  ...moved.map((c) => `- ${movedLabel(c)} \u2014 ${c.state} \u2014 handle: ${c.handle}. ${c.can}`),
@@ -108725,6 +108816,7 @@ function movedChildrenNote(moved, commands = "") {
108725
108816
  }
108726
108817
  function movedLabel(c) {
108727
108818
  if (c.request) return "start_agent request";
108819
+ if (c.thread) return `mail thread "${oneLineText(c.name, 120)}"`;
108728
108820
  return `"${oneLineText(c.name, 120)}"${c.project ? ` (project ${c.project})` : ""}`;
108729
108821
  }
108730
108822
  var HandoffChildren = class {
@@ -108827,18 +108919,30 @@ var HandoffChildren = class {
108827
108919
  can: "The card stands in your session; if the person allows it, the session it starts is yours and its report comes to you. agent_session (status, wait, stop) works on the handle meanwhile."
108828
108920
  });
108829
108921
  }
108830
- if (!moved.length) return [];
108922
+ const mailWhich = scope === "live" ? sessions.length ? { assignees: sessions } : null : "all";
108923
+ if (!moved.length && mailWhich === null) return [];
108924
+ let threads = [];
108831
108925
  try {
108832
- this.host.store.children.moveChildren(from, to, {
108833
- sessions,
108834
- startRequests: starts.map((r) => ({ requestId: r.req.requestId, request: { ...r.req, callerId: to } })),
108835
- armReports,
108836
- queued: !removed
108837
- });
108926
+ this.host.store.children.moveChildren(
108927
+ from,
108928
+ to,
108929
+ {
108930
+ sessions,
108931
+ startRequests: starts.map((r) => ({ requestId: r.req.requestId, request: { ...r.req, callerId: to } })),
108932
+ armReports,
108933
+ // With no child moving, the memory side does not run, so the queue's rows do not move either
108934
+ queued: !removed && moved.length > 0
108935
+ },
108936
+ mailWhich === null ? void 0 : () => void (threads = this.host.mailHandOver(from, to, mailWhich))
108937
+ );
108838
108938
  } catch (e) {
108839
- console.error(`[agent-host] handoff left the children with ${from.slice(0, 8)}: ${e.message}`);
108939
+ console.error(`[agent-host] handoff left the children and the mail with ${from.slice(0, 8)}: ${e.message}`);
108840
108940
  return [];
108841
108941
  }
108942
+ const childCount = moved.length;
108943
+ moved.push(...threads.map((t) => movedThread(t)));
108944
+ if (!moved.length) return [];
108945
+ if (removed && threads.some((t) => t.unread > 0)) this.host.mailPoke(to);
108842
108946
  for (const id3 of sessions) {
108843
108947
  const s = this.host.meta.get(id3);
108844
108948
  if (s.askedBy !== from && s.parentSessionId !== from) continue;
@@ -108846,7 +108950,7 @@ var HandoffChildren = class {
108846
108950
  if (s.parentSessionId === from) s.parentSessionId = to;
108847
108951
  this.host.emit({ type: "session_owner", sessionId: id3, askedBy: s.askedBy ?? null, parentSessionId: s.parentSessionId ?? null });
108848
108952
  }
108849
- this.host.children.moveOwner(from, to, { sessions, armReports, starts, queued: !removed }, () => {
108953
+ if (childCount) this.host.children.moveOwner(from, to, { sessions, armReports, starts, queued: !removed }, () => {
108850
108954
  for (const r of starts) {
108851
108955
  r.req.callerId = to;
108852
108956
  const ask = this.host.hostCards.hostAsks.get(r.req.requestId);
@@ -108874,10 +108978,22 @@ var HandoffChildren = class {
108874
108978
  text: `Handed over to \u201C${heir.name}\u201D [${to}], which follows them from now on: ${items.join("; ")}. This session no longer does.`
108875
108979
  });
108876
108980
  }
108877
- console.error(`[agent-host] handoff ${from.slice(0, 8)} -> ${to.slice(0, 8)} (${scope}): moved ${sessions.length} session(s), ${starts.length} start request(s)`);
108981
+ console.error(`[agent-host] handoff ${from.slice(0, 8)} -> ${to.slice(0, 8)} (${scope}): moved ${sessions.length} session(s), ${starts.length} start request(s), ${threads.length} mail thread(s)`);
108878
108982
  return moved;
108879
108983
  }
108880
108984
  };
108985
+ function movedThread(t) {
108986
+ const unread = t.unread ? `, ${t.unread} unread message${t.unread === 1 ? "" : "s"} now waiting for you` : "";
108987
+ const other = (address) => {
108988
+ const p = address === null ? null : parseAddress(address);
108989
+ if (!p) return "nobody";
108990
+ if (p.kind === "session") return `session ${p.sessionId}`;
108991
+ return p.kind === "person" ? "the person" : p.kind === "app" ? `app ${p.appId}` : "Centralu";
108992
+ };
108993
+ const can = t.kind === "closed" ? "It is closed; what the previous session had not read of it waits for you. Read it with the mail tool." : t.kind === "left" ? t.role === "owner" ? "You are its assignee and the previous session was its owner, so it did not move: once that session is archived, the person owns it." : "You own it and the previous session had its work, so it did not move: once that session is archived, it is open with nobody on it; reassign or close it." : t.role === "owner" ? `You own it now (assignee: ${other(t.thread.assignee)}); its reports come to you in it. Read and answer it with the mail tool.` : t.role === "assignee" ? `It is assigned to you now (owner: ${other(t.thread.owner)}): the work is yours to do, and your report goes to its owner. Read it with the mail tool.` : "You are in it in the previous session's place. Read it with the mail tool.";
108994
+ const state = t.kind === "closed" ? `done${t.thread.outcome ? ` (${t.thread.outcome})` : ""}` : t.thread.state;
108995
+ return { handle: t.thread.id, name: t.thread.subject, state: `${state}${unread}`, can, thread: true };
108996
+ }
108881
108997
 
108882
108998
  // packages/agent-host/src/sessions/child-ownership.ts
108883
108999
  import { randomUUID as randomUUID12 } from "node:crypto";
@@ -108932,6 +109048,8 @@ var ChildOwnership = class {
108932
109048
  * work's, as every turn was.
108933
109049
  */
108934
109050
  starters = /* @__PURE__ */ new Map();
109051
+ /** Turns the tool started by itself, no message behind them (`turnStarted`, #1576); memory only, a swap forgets it */
109052
+ cliTurns = /* @__PURE__ */ new Set();
108935
109053
  lastBy = /* @__PURE__ */ new Map();
108936
109054
  /** Messages that went into the running turn and may not have been taken into it (#1377), by session */
108937
109055
  pending = /* @__PURE__ */ new Map();
@@ -109278,6 +109396,8 @@ var ChildOwnership = class {
109278
109396
  const next = found && Date.now() - found.at < NEXT_STARTER_MS ? found : void 0;
109279
109397
  const { at: _at, ...starter } = next ?? { by: "work", at: 0 };
109280
109398
  this.starters.set(sessionId, starter);
109399
+ if (next) this.cliTurns.delete(sessionId);
109400
+ else this.cliTurns.add(sessionId);
109281
109401
  this.persistTurn(sessionId);
109282
109402
  const source = next ? this.nextSource.get(sessionId) ?? "caller" : "caller";
109283
109403
  this.nextSource.delete(sessionId);
@@ -109298,6 +109418,7 @@ var ChildOwnership = class {
109298
109418
  turnEnded(sessionId, taken) {
109299
109419
  const starter = { ...this.starters.get(sessionId) ?? { by: "work" } };
109300
109420
  this.starters.delete(sessionId);
109421
+ this.cliTurns.delete(sessionId);
109301
109422
  const pending = this.pending.get(sessionId) ?? [];
109302
109423
  this.pending.delete(sessionId);
109303
109424
  for (const p of pending) {
@@ -109312,6 +109433,24 @@ var ChildOwnership = class {
109312
109433
  this.store.settings.deleteAppSetting(reportTurnKey(sessionId));
109313
109434
  return starter;
109314
109435
  }
109436
+ /**
109437
+ * Whether the turn running is the paused work's continuing (#1576): the caller's report is held over the session's own
109438
+ * background work, and the turn the tool started when that work ended is the work's (`turnStarted`, no message began
109439
+ * it) with no person's message in it. That turn's end is the report the caller is owed, so it is the caller's too.
109440
+ * The one place that says so, as `reportBackIfAwaited` reads the same turn for its report
109441
+ */
109442
+ continuesPause(sessionId, callerId) {
109443
+ if (this.reports.get(sessionId) !== callerId || !this.paused.has(sessionId)) return false;
109444
+ const owner = this.turns.get(sessionId)?.callerId;
109445
+ if (owner && owner !== callerId || this.mailTurns.get(sessionId) && this.mailTurns.get(sessionId)?.ownerId !== callerId) return false;
109446
+ const starter = this.starters.get(sessionId);
109447
+ return this.cliTurns.has(sessionId) && starter?.by === "work" && !this.personInTurn(sessionId);
109448
+ }
109449
+ /** Whether a person started the running turn or wrote into it (#1576): a refusal says "the person may be using it" for no other */
109450
+ personInTurn(sessionId) {
109451
+ const starter = this.starters.get(sessionId);
109452
+ return starter?.by !== "work" || !!starter.wrote || !!starter.added || (this.pending.get(sessionId) ?? []).some((p) => p.by === "person");
109453
+ }
109315
109454
  /** Whose the session's last turn was (#1377): its last response is the person's answer when it was theirs */
109316
109455
  lastTurnBy(sessionId) {
109317
109456
  return this.lastBy.get(sessionId);
@@ -112244,7 +112383,8 @@ var AgentSessionActions = class {
112244
112383
  };
112245
112384
  }
112246
112385
  const from = { sessionId: callerId, name: labelOf(this.host.store.messages, caller) };
112247
- if (d2 && !d2.settled && observing || (!d2 || d2.settled) && this.host.busyForCaller(callerId, child.id)) {
112386
+ const continuing = (!d2 || d2.settled) && inTurn(child.state) && this.host.children.continuesPause(child.id, callerId);
112387
+ if (d2 && !d2.settled && observing || (!d2 || d2.settled) && !continuing && this.host.busyForCaller(callerId, child.id)) {
112248
112388
  if (!opts.ifBusy) return { ok: true, tool: child.tool, ...this.host.busyAnswer(callerId, child, "agent_session") };
112249
112389
  if (opts.ifBusy === "queue") return { ok: true, tool: child.tool, ...this.host.enqueue(callerId, child, "agent_session", opts.text ?? "") };
112250
112390
  if (opts.ifBusy === "fork" || opts.ifBusy === "fork_read") {
@@ -112258,16 +112398,18 @@ var AgentSessionActions = class {
112258
112398
  if (!note2.available || !d2 || d2.settled) return { ok: false, error: `No note to "${child.name}": ${note2.why ?? "nothing is running there to add it to"}. Queue it or fork it instead.` };
112259
112399
  this.host.autoBoundaryOpen(callerId, "a note into a turn that is not its own");
112260
112400
  }
112261
- if (d2 && !d2.settled) {
112401
+ if (d2 && !d2.settled || continuing) {
112262
112402
  const text2 = agentNoteFrame(from.name, opts.text ?? "");
112263
112403
  if (!this.host.adapters.get(child.tool)?.capabilities.midTurnInput || !this.host.handles.get(child.id)?.sendTracked) {
112264
112404
  return { ok: false, error: `"${child.name}" cannot take messages while working. Wait for it (action "wait") or stop it first.` };
112265
112405
  }
112266
- const own = observing ? this.flow.observeTurn(child, true, true) : void 0;
112406
+ const own = observing || continuing ? this.flow.observeTurn(child, true, true) : void 0;
112267
112407
  if (own) void own.run.wait.done.then(own.release, own.release);
112268
- const record2 = own ? { run: own.run, waiters: 0, told: false } : void 0;
112408
+ const record2 = own && !continuing ? { run: own.run, waiters: 0, told: false } : void 0;
112269
112409
  if (record2) this.host.children.setObserverNote(key, record2);
112270
- const wait = own?.run.wait ?? d2.wait;
112410
+ const run4 = own?.run ?? d2;
112411
+ if (!run4) return { ok: false, error: `No note to "${child.name}": nothing is running there to add it to.` };
112412
+ const wait = run4.wait;
112271
112413
  const releaseHold = wait.holdInput();
112272
112414
  const release = () => {
112273
112415
  releaseHold();
@@ -112278,7 +112420,9 @@ var AgentSessionActions = class {
112278
112420
  const fail4 = (e) => {
112279
112421
  if (!record2) {
112280
112422
  wait.fail(e);
112281
- return release();
112423
+ release();
112424
+ if (continuing) void this.flow.tellNoteRefused(callerId, child.id, e.message);
112425
+ return;
112282
112426
  }
112283
112427
  wait.fail(new Error(`your note was not delivered: ${e.message}`));
112284
112428
  release();
@@ -112298,11 +112442,13 @@ var AgentSessionActions = class {
112298
112442
  return { ok: false, error: `Could not send it: ${e.message}` };
112299
112443
  }
112300
112444
  if (!observing) this.host.children.delegated(callerId, child.id, "agent_session", opts.text ?? "");
112301
- const working = this.flow.agentWorking(d2, false, observing);
112445
+ const working = this.flow.agentWorking(run4, false, observing || continuing);
112446
+ if (continuing && working.ok && working.state === "working") return { ...working, reported: this.host.children.awaitingReport.get(child.id) === callerId, via: "note" };
112302
112447
  return working.ok && observing ? { ...working, via: "note" } : working;
112303
112448
  }
112304
112449
  if (inTurn(child.state) || this.host.agentRuns.has(child.id) && !this.host.agentRuns.get(child.id).finished) {
112305
- return { ok: false, error: `"${child.name}" is busy with something else (the person may be using it). Try again later.` };
112450
+ const person = this.host.children.personInTurn(child.id);
112451
+ return { ok: false, error: `"${child.name}" is busy with ${person ? "something else (the person may be using it)" : "a turn you did not give it (another session, or its own tool, started it)"}. Try again later.` };
112306
112452
  }
112307
112453
  if (d2) this.host.children.dropDelegation(key);
112308
112454
  const full = this.flow.starts.agentCeilingError(callerId, child.projectId ?? "");
@@ -112430,6 +112576,10 @@ var RemoteAgents = class {
112430
112576
  toldCards = /* @__PURE__ */ new Set();
112431
112577
  /** Whose each child's turn is, as settled in this host (`Whose`); one not here is read from its record (`whoseOf`) */
112432
112578
  whose = /* @__PURE__ */ new Map();
112579
+ /** What made a turn another's, when it is told (#1576): a message from another session of this hub, else it is read as the person's */
112580
+ otherBy = /* @__PURE__ */ new Map();
112581
+ /** Notes sent into a running turn whose record has not come back yet (memory only): two quick notes both are the caller's own */
112582
+ notesSent = /* @__PURE__ */ new Map();
112433
112583
  /** Children an instruction is on its way to: its own send's answer says what came of it, and the machine is not read meanwhile */
112434
112584
  pending = /* @__PURE__ */ new Set();
112435
112585
  /** Children whose turn's end the linked reports held back while whose the turn was could not be told (`reportGate`) */
@@ -112760,14 +112910,19 @@ var RemoteAgents = class {
112760
112910
  if (now.running) {
112761
112911
  const g5 = await this.gate(rec, reader);
112762
112912
  if (!g5.live) {
112763
- return { ok: false, error: `"${rec.name}" is busy on ${rec.machine} with a turn you did not give it (the person may be using it there). A queue or a fork does not reach another machine: try again once it is at rest.` };
112913
+ return { ok: false, error: `"${rec.name}" is busy on ${rec.machine} with ${this.otherTurn(rec, g5)}. A queue or a fork does not reach another machine: try again once it is at rest.` };
112764
112914
  }
112765
112915
  const refused3 = this.gateError(callerId);
112766
112916
  if (refused3) return { ok: false, error: refused3 };
112767
112917
  const framed3 = agentNoteFrame(label, text2);
112768
112918
  this.sentNow(rec.sessionId, framed3);
112919
+ this.notesSent.set(rec.sessionId, [...this.notesSent.get(rec.sessionId) ?? [], framed3]);
112769
112920
  const sent2 = await reader.send(rec.sessionId, framed3, label, { depth: depthOf(this.host, callerId) + 1 });
112770
- if (!sent2.ok) return { ok: false, error: `Could not send to "${rec.name}" on ${rec.machine}: ${bare(sent2.error)}.` };
112921
+ if (!sent2.ok) {
112922
+ const at2 = (this.notesSent.get(rec.sessionId) ?? []).lastIndexOf(framed3);
112923
+ if (at2 >= 0) this.notesSent.set(rec.sessionId, (this.notesSent.get(rec.sessionId) ?? []).filter((_, i) => i !== at2));
112924
+ return { ok: false, error: `Could not send to "${rec.name}" on ${rec.machine}: ${bare(sent2.error)}.` };
112925
+ }
112771
112926
  return this.gateNow(rec).live ? this.working(rec, this.reportArmed(rec) || this.waits.has(rec.sessionId)) : this.wentElsewhere(rec);
112772
112927
  }
112773
112928
  const refused2 = this.gateError(callerId);
@@ -112776,6 +112931,7 @@ var RemoteAgents = class {
112776
112931
  if (full) return { ok: false, error: full };
112777
112932
  const framed2 = agentNoteFrame(label, text2);
112778
112933
  this.save(this.records().map((r) => r.sessionId === rec.sessionId ? { ...r, turn: randomUUID17(), sent: framed2 } : r));
112934
+ this.notesSent.delete(rec.sessionId);
112779
112935
  this.whose.set(rec.sessionId, "unknown");
112780
112936
  this.pending.add(rec.sessionId);
112781
112937
  this.host.children.setLinkedReport(rec.sessionId, { orchestratorId: callerId, busy: false });
@@ -112891,7 +113047,9 @@ var RemoteAgents = class {
112891
113047
  const whose = this.whoseOf(rec);
112892
113048
  const owned = whose === "caller";
112893
113049
  const report = this.host.children.linkedReports.get(rec.sessionId);
112894
- const running2 = this.waits.has(rec.sessionId) || report?.orchestratorId === rec.callerId && !report.paused;
113050
+ const mine = report?.orchestratorId === rec.callerId;
113051
+ const continuesPause = mine && !!report.paused && report.busy && report.turn?.by === "work";
113052
+ const running2 = this.waits.has(rec.sessionId) || mine && (!report.paused || continuesPause);
112895
113053
  return { owned, live: owned && running2, whose };
112896
113054
  }
112897
113055
  /** Whose the child's turn is (`Whose`): as settled here, else as its record kept it; `caller` is re-read after a restart */
@@ -112905,6 +113063,7 @@ var RemoteAgents = class {
112905
113063
  */
112906
113064
  setWhose(rec, whose, why) {
112907
113065
  const was = this.whoseOf(rec);
113066
+ if (was !== "other" || whose !== "other") this.otherBy.delete(rec.sessionId);
112908
113067
  this.whose.set(rec.sessionId, whose);
112909
113068
  const kept = whose === "unknown" ? void 0 : whose;
112910
113069
  const stored = this.of(rec.sessionId);
@@ -112956,12 +113115,26 @@ var RemoteAgents = class {
112956
113115
  if (whose === "unknown") this.setWhose(rec, "other");
112957
113116
  return this.wentElsewhere(rec);
112958
113117
  }
113118
+ /**
113119
+ * What the running turn is, said for a caller it is not live for (#1576): named by what started it, so only a message
113120
+ * of a person's on that machine is "the person may be using it". A turn the hub has no sight of the start of (a swap,
113121
+ * a machine that lists no background work) is said to be the child's own, not a person's
113122
+ */
113123
+ otherTurn(rec, g5) {
113124
+ if (g5.whose === "unknown") return "a turn the hub cannot tell the owner of (the link dropped, or Centralu restarted, and the machine cannot be read now)";
113125
+ if (g5.whose === "other") {
113126
+ return this.otherBy.get(rec.sessionId) === "session" ? "a turn another session asked of it (it is not the one you gave it)" : "a turn you did not give it (the person may be using it there)";
113127
+ }
113128
+ const report = this.host.children.linkedReports.get(rec.sessionId);
113129
+ if (report && report.orchestratorId !== rec.callerId) return "a turn another session is owed the end of (it is not the one you gave it)";
113130
+ return "a turn it started by itself that this hub cannot tie to the work you gave it";
113131
+ }
112959
113132
  /**
112960
113133
  * What the caller gets of a child whose turn the gate does not let through: the fact and the state word. Nothing of
112961
113134
  * the turn: no card, no words, and nothing owed or armed for it
112962
113135
  */
112963
113136
  notYours(rec, g5, running2, state) {
112964
- const why = g5.whose === "unknown" ? `The hub could not tell whose its last turn on ${rec.machine} was (the link dropped, or Centralu restarted, and the machine cannot be read now)` : running2 ? `It is in a turn on ${rec.machine} that you did not give it (the person is using it there)` : `Its last turn on ${rec.machine} was not one you gave it (the person's)`;
113137
+ const why = g5.whose === "unknown" ? `The hub could not tell whose its last turn on ${rec.machine} was (the link dropped, or Centralu restarted, and the machine cannot be read now)` : running2 ? `It is in ${this.otherTurn(rec, g5)} on ${rec.machine}` : `Its last turn on ${rec.machine} was not one you gave it (the person's)`;
112965
113138
  const notice = `${why}: nothing of that turn is yours to read, and none of it is reported to you.${running2 ? " Send to it once it is at rest." : ""}`;
112966
113139
  if (running2) return { ...this.working(rec, false), notice };
112967
113140
  return { ok: true, state: "at_rest", sessionId: rec.sessionId, sessionName: rec.name, tool: rec.tool, answer: "(not yours to read: a turn you did not give it)", sessionState: state, notice };
@@ -112981,13 +113154,23 @@ var RemoteAgents = class {
112981
113154
  if (e.seq <= (this.lastSeq.get(sessionId) ?? -1)) return;
112982
113155
  this.lastSeq.set(sessionId, e.seq);
112983
113156
  }
113157
+ const own = this.notesSent.get(sessionId) ?? [];
113158
+ const earlier = typeof e.text === "string" && e.text !== rec.sent ? own.indexOf(e.text) : -1;
113159
+ if (earlier >= 0 && this.fromHubHere(rec, e.from)) {
113160
+ this.notesSent.set(sessionId, own.filter((_, i) => i !== earlier));
113161
+ if (this.whoseOf(rec) === "caller" && this.gateNow(rec).live) return;
113162
+ }
112984
113163
  if (this.fromHubHere(rec, e.from) && e.text === rec.sent) {
113164
+ const at2 = own.indexOf(rec.sent ?? "");
113165
+ if (at2 >= 0) this.notesSent.set(sessionId, own.filter((_, i) => i !== at2));
112985
113166
  if (this.whoseOf(rec) === "caller" && this.gateNow(rec).live) return;
112986
113167
  if (this.busy.has(sessionId)) return this.setWhose(rec, "other", "took what you sent as a message into a turn already running there that you did not give it (the person's, most likely)");
112987
113168
  this.tookInstruction(rec);
112988
113169
  return;
112989
113170
  }
112990
113171
  this.setWhose(rec, "other", "took a message there that you did not send (the person on that machine, most likely)");
113172
+ if (this.fromHubHere(rec, e.from)) this.otherBy.set(sessionId, "session");
113173
+ else this.otherBy.delete(sessionId);
112991
113174
  }
112992
113175
  /**
112993
113176
  * The machine recorded the instruction this hub last sent the child, with no other turn running there: the turn that
@@ -113278,6 +113461,8 @@ var RemoteAgents = class {
113278
113461
  if (this.records().some((r) => r.sessionId === sessionId)) this.save(this.records().filter((r) => r.sessionId !== sessionId));
113279
113462
  this.toldDown.delete(sessionId);
113280
113463
  this.whose.delete(sessionId);
113464
+ this.otherBy.delete(sessionId);
113465
+ this.notesSent.delete(sessionId);
113281
113466
  this.pending.delete(sessionId);
113282
113467
  this.deferred.delete(sessionId);
113283
113468
  this.busy.delete(sessionId);
@@ -114137,6 +114322,20 @@ var LaunchRecords = class {
114137
114322
  rememberBackgroundTasks(m) {
114138
114323
  this.host.store.settings.setAppSetting(this.backgroundTasksKey(m.id), JSON.stringify(m.backgroundTasks.filter((t) => t.status === "running")));
114139
114324
  }
114325
+ /**
114326
+ * The running cost total of the conversation a session's tool reports (`CreateSessionOpts.runningCost`), as its last process
114327
+ * read it: kept so the next process, after a restart, a wake or a host swap, takes a turn's cost as what it added. Read only
114328
+ * for a resumed conversation; anything but a number at or above 0 is none.
114329
+ */
114330
+ runningCostFor(sessionId, resumed) {
114331
+ const key = `running_cost:${sessionId}`;
114332
+ const raw = resumed ? this.host.store.settings.appSetting(key) : null;
114333
+ const last = raw === null ? null : Number(raw);
114334
+ return {
114335
+ last: last !== null && Number.isFinite(last) && last >= 0 ? last : null,
114336
+ keep: (total) => this.host.store.settings.setAppSetting(key, String(total))
114337
+ };
114338
+ }
114140
114339
  /** The persisted prefix a process was started with. It is separate from SessionInfo: a screen never needs it. */
114141
114340
  toolPrefixKey(sessionId) {
114142
114341
  return `tool_prefix:${sessionId}`;
@@ -118056,30 +118255,6 @@ ${note}` : note;
118056
118255
  }
118057
118256
  };
118058
118257
 
118059
- // packages/agent-host/src/mail/address.ts
118060
- var SEGMENT = "[A-Za-z0-9][A-Za-z0-9._~-]{0,63}";
118061
- var SEGMENT_RE = new RegExp(`^${SEGMENT}$`);
118062
- var SESSION_RE = new RegExp(`^session:(${SEGMENT})/(${SEGMENT})$`);
118063
- var APP_RE = new RegExp(`^app:(${SEGMENT})/(${SEGMENT})/(${SEGMENT})$`);
118064
- var PERSON = "person";
118065
- var CENTRALU = "centralu";
118066
- function sessionAddress(host, sessionId) {
118067
- return checked(`session:${host}/${sessionId}`);
118068
- }
118069
- function parseAddress(address) {
118070
- if (address === PERSON) return { kind: "person" };
118071
- if (address === CENTRALU) return { kind: "centralu" };
118072
- const [, host, sessionId] = SESSION_RE.exec(address) ?? [];
118073
- if (host !== void 0 && sessionId !== void 0) return { kind: "session", host, sessionId };
118074
- const [, appHost, projectId, appId] = APP_RE.exec(address) ?? [];
118075
- if (appHost !== void 0 && projectId !== void 0 && appId !== void 0) return { kind: "app", host: appHost, projectId, appId };
118076
- return null;
118077
- }
118078
- function checked(address) {
118079
- if (!parseAddress(address)) throw new Error(`Not a mail address: ${JSON.stringify(address)}`);
118080
- return address;
118081
- }
118082
-
118083
118258
  // packages/agent-host/src/mail/ids.ts
118084
118259
  import { randomBytes as randomBytes5 } from "node:crypto";
118085
118260
  var CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
@@ -118169,10 +118344,10 @@ function sweepMail(db, now, limits = {}) {
118169
118344
  result.messages += db.prepare(`DELETE FROM mail_messages WHERE id IN (SELECT value FROM json_each(?))`).run(json2).changes;
118170
118345
  };
118171
118346
  const dropThreads = (where2, cut2) => {
118172
- const pick3 = db.prepare(`SELECT t.id FROM mail_threads t WHERE ${where2} ORDER BY t.created_at LIMIT @batch`);
118347
+ const pick4 = db.prepare(`SELECT t.id FROM mail_threads t WHERE ${where2} ORDER BY t.created_at LIMIT @batch`);
118173
118348
  for (; ; ) {
118174
118349
  const done2 = db.transaction(() => {
118175
- const ids = pick3.all({ cut: cut2, batch: l.batch }).map((r) => r.id);
118350
+ const ids = pick4.all({ cut: cut2, batch: l.batch }).map((r) => r.id);
118176
118351
  if (ids.length === 0) return true;
118177
118352
  const json2 = JSON.stringify(ids);
118178
118353
  db.prepare(`DELETE FROM mail_receipts WHERE message_id IN (SELECT id FROM mail_messages WHERE thread_id IN (SELECT value FROM json_each(?)))`).run(json2);
@@ -118415,6 +118590,64 @@ var MailRecords = class {
118415
118590
  const rows3 = this.db.prepare(`SELECT * FROM mail_threads WHERE ${column} = ? ORDER BY created_at, id`).all(address);
118416
118591
  return rows3.map(toThread).filter((t) => !states || states.includes(t.state));
118417
118592
  }
118593
+ /** The threads that are not done in which a party is still a participant, whatever its role: what a handoff moves of it (§4.1) */
118594
+ openThreadsWith(address) {
118595
+ const rows3 = this.db.prepare(
118596
+ `SELECT t.* FROM mail_threads t JOIN mail_participants p ON p.thread_id = t.id AND p.address = ? AND p.left_at IS NULL
118597
+ WHERE t.state <> 'done' ORDER BY t.created_at, t.id`
118598
+ ).all(address);
118599
+ return rows3.map(toThread);
118600
+ }
118601
+ /** The done threads in which `address` has mail it has neither read nor handled: what a handoff's move of its receipts looks at too (§4.1) */
118602
+ doneThreadsWithUnread(address) {
118603
+ const rows3 = this.db.prepare(
118604
+ `SELECT t.* FROM mail_threads t WHERE t.state = 'done' AND EXISTS (
118605
+ SELECT 1 FROM mail_receipts r JOIN mail_messages m ON m.id = r.message_id
118606
+ WHERE m.thread_id = t.id AND r.recipient = ? AND r.read_at IS NULL AND r.handled_at IS NULL)
118607
+ ORDER BY t.created_at, t.id`
118608
+ ).all(address);
118609
+ return rows3.map(toThread);
118610
+ }
118611
+ /**
118612
+ * A handoff's move of what `from` has not read in a thread to `to` (§4.1): each receipt neither read nor handled is `to`'s, and
118613
+ * waits to be delivered to it, delivered to `from` or not (what went into the original's conversation never reached its
118614
+ * successor). A hold about the original itself (its delivery `uncertain` or `failed`, its `loop`, `budget` or `policy`, each
118615
+ * judged for the original's wakes) is let go, for the delivery to judge again for the successor; one the person settled
118616
+ * (`released`, `dismissed`) stays. Not moved, and handled for `from` instead: a receipt for a message `to` sent itself (nobody
118617
+ * is sent their own words), and one for a message `to` already has a receipt for (it keeps its own). So nothing of the
118618
+ * original's stays waiting (`handleUnread`), and a restored original is not woken for mail of a thread it has left. Returns how many moved
118619
+ */
118620
+ moveUnreadReceipts(threadId, from, to, now) {
118621
+ const moved = this.db.prepare(
118622
+ `UPDATE OR IGNORE mail_receipts
118623
+ SET recipient = @to, delivered_at = NULL, attempt_at = NULL, attempts = 0, last_error = NULL,
118624
+ held = CASE WHEN held IN ('uncertain', 'failed', 'loop', 'budget', 'policy') THEN NULL ELSE held END
118625
+ WHERE recipient = @from AND read_at IS NULL AND handled_at IS NULL
118626
+ AND message_id IN (SELECT id FROM mail_messages WHERE thread_id = @threadId AND sender <> @to)`
118627
+ ).run({ threadId, from, to }).changes;
118628
+ this.handleUnread(threadId, from, now);
118629
+ return moved;
118630
+ }
118631
+ /** How many of `from`'s unread receipts in the thread `moveUnreadReceipts` would give `to`: not those of `to`'s own messages, nor those it has already */
118632
+ movableUnread(threadId, from, to) {
118633
+ const row = this.db.prepare(
118634
+ `SELECT COUNT(*) AS n FROM mail_receipts r JOIN mail_messages m ON m.id = r.message_id
118635
+ WHERE r.recipient = @from AND r.read_at IS NULL AND r.handled_at IS NULL AND m.thread_id = @threadId AND m.sender <> @to
118636
+ AND NOT EXISTS (SELECT 1 FROM mail_receipts o WHERE o.message_id = r.message_id AND o.recipient = @to)`
118637
+ ).get({ threadId, from, to });
118638
+ return row?.n ?? 0;
118639
+ }
118640
+ /**
118641
+ * `from`'s receipts in a thread that are neither read nor handled are handled: a handoff's original left the thread, or is about to
118642
+ * when it goes (§4.1), and nothing of it waits there, so a restored original is not woken for a thread it no longer holds
118643
+ */
118644
+ handleUnread(threadId, from, now) {
118645
+ this.db.prepare(
118646
+ `UPDATE mail_receipts SET handled_at = @now
118647
+ WHERE recipient = @from AND read_at IS NULL AND handled_at IS NULL
118648
+ AND message_id IN (SELECT id FROM mail_messages WHERE thread_id = @threadId)`
118649
+ ).run({ threadId, from, now });
118650
+ }
118418
118651
  /**
118419
118652
  * What a party's mailbox holds (§9.3, §10.1): the threads it is in that are not done, and the done ones it has mail unread
118420
118653
  * in, with how much it has not read. Unread first, then the latest change. A mail held back for the person (a loop, a spent
@@ -118945,6 +119178,19 @@ function scriptMessageFrame(credential, text2) {
118945
119178
  ` + quoted2(text2);
118946
119179
  }
118947
119180
 
119181
+ // packages/agent-host/src/sessions/tool-fit.ts
119182
+ function sessionDemands(m, files, preset = m.permissionPreset) {
119183
+ return {
119184
+ permissionPreset: preset,
119185
+ ...files.projectTrusted === void 0 ? {} : { projectTrusted: files.projectTrusted },
119186
+ ...files.noSettingFiles ? { noSettingFiles: true } : {},
119187
+ roleText: m.kind === "orchestrator" || !!m.roleAppend
119188
+ };
119189
+ }
119190
+ function unfitError(reason) {
119191
+ return rpcError(reason, "refused", { retryable: false, data: { reason: "tool_unfit" } });
119192
+ }
119193
+
118948
119194
  // packages/agent-host/src/sessions/session-start.ts
118949
119195
  import { randomUUID as randomUUID19 } from "node:crypto";
118950
119196
 
@@ -119367,7 +119613,8 @@ var SessionStarts = class {
119367
119613
  ...params.lsp !== void 0 ? { lsp: params.lsp } : {}
119368
119614
  });
119369
119615
  const from = this.host.handleSink();
119370
- handle = await adapter.createSession(
119616
+ handle = await startFit(
119617
+ adapter,
119371
119618
  this.launchOptions(info, {
119372
119619
  tool: params.tool,
119373
119620
  cwd,
@@ -119535,6 +119782,7 @@ var SessionStarts = class {
119535
119782
  ...this.autoCompactFor(m, !!o.adopt),
119536
119783
  permissionPreset: o.permissionPreset,
119537
119784
  resumeExternalId: o.resumeExternalId,
119785
+ runningCost: this.host.launches.runningCostFor(m.id, !!o.resumeExternalId),
119538
119786
  ...o.knownGoal ? { knownGoal: o.knownGoal } : {},
119539
119787
  // Which settings files reach this session (decision 3, #92, #152) — what the session is,
119540
119788
  // and the trust at this exact moment of launch. If this is a builder session, the
@@ -119678,7 +119926,8 @@ var SessionStarts = class {
119678
119926
  if (!adopt) m.agentVersion = this.host.versionHint?.(tool) ?? null;
119679
119927
  const kept = adopt ? this.host.launches.keptBackgroundTasks(sessionId) : null;
119680
119928
  try {
119681
- const creating = adapter.createSession(
119929
+ const creating = startFit(
119930
+ adapter,
119682
119931
  this.launchOptions(m, {
119683
119932
  tool,
119684
119933
  cwd,
@@ -119776,6 +120025,15 @@ var SessionStarts = class {
119776
120025
  }
119777
120026
  }
119778
120027
  };
120028
+ function startFit(adapter, opts, sink) {
120029
+ const reason = launchRefusal(adapter.capabilities, adapter.descriptor.label, {
120030
+ permissionPreset: opts.permissionPreset,
120031
+ projectTrusted: opts.projectTrusted,
120032
+ noSettingFiles: opts.noSettingFiles,
120033
+ roleText: !!opts.systemPromptAppend
120034
+ });
120035
+ return reason ? Promise.reject(unfitError(reason)) : adapter.createSession(opts, sink);
120036
+ }
119779
120037
 
119780
120038
  // packages/agent-host/src/sessions/session-events.ts
119781
120039
  import { randomUUID as randomUUID20 } from "node:crypto";
@@ -120834,7 +121092,7 @@ ${adapterText}` : adapterText;
120834
121092
  if (!handle) throw rpcError("The session is not running");
120835
121093
  this.host.record.closeStream(sessionId);
120836
121094
  this.host.cardRests.turnAsked(sessionId);
120837
- handle.send(text2);
121095
+ handle.send(text2, { compact: true });
120838
121096
  }
120839
121097
  /**
120840
121098
  * Turns a touched file into a path relative to the project (#185).
@@ -122628,6 +122886,11 @@ var InputReceipts = class {
122628
122886
  // packages/agent-host/src/adapters/local-process.ts
122629
122887
  import { EventEmitter as EventEmitter3 } from "node:events";
122630
122888
  import { PassThrough as PassThrough2 } from "node:stream";
122889
+ var HOST_WATCH = '{ g=; while read -r l; do case $l in +*) g="$g ${l#+}";; -*) n=; for p in $g; do [ "$p" = "${l#-}" ] || n="$n $p"; done; g=$n;; esac; done; for p in $g $(' + watchTreeGroups() + '); do kill -9 -"$p" 2>/dev/null; done; kill -9 0 2>/dev/null; } <&3 >/dev/null 2>&1 & exec "$@" 3<&-';
122890
+ function watchTreeGroups() {
122891
+ return `ps -A -o pid= -o ppid= -o pgid= 2>/dev/null | awk -v r="$$" '{ p[$1] = $2; g[$1] = $3 } END { d[r] = 1; do { c = 0; for (k in p) if (!(k in d) && (p[k] in d)) { d[k] = 1; c = 1 } } while (c); for (k in d) if (g[k] != g[r] && g[k] > 1) print g[k] }'`;
122892
+ }
122893
+ var GROUP_CHECK_MS = 2e3;
122631
122894
  var STDERR_TAIL2 = 8 * 1024;
122632
122895
  var LocalAgentProcess = class _LocalAgentProcess extends EventEmitter3 {
122633
122896
  constructor(child, spawnedAt, os2) {
@@ -122641,6 +122904,8 @@ var LocalAgentProcess = class _LocalAgentProcess extends EventEmitter3 {
122641
122904
  child.stderr?.on("data", (d2) => this.tail.push(d2));
122642
122905
  child.stdin?.on("error", () => {
122643
122906
  });
122907
+ child.stdio[3]?.on("error", () => {
122908
+ });
122644
122909
  child.on("error", (e) => {
122645
122910
  this.emit("error", e);
122646
122911
  if (child.pid === void 0) this.onExit();
@@ -122655,12 +122920,18 @@ var LocalAgentProcess = class _LocalAgentProcess extends EventEmitter3 {
122655
122920
  stderr;
122656
122921
  killed = false;
122657
122922
  tail = new Tail(STDERR_TAIL2);
122923
+ /** The helper groups its tree had when last asked (`noteTree`) and that still have a member, swept once it has gone */
122924
+ groups = /* @__PURE__ */ new Set();
122925
+ /** While groups are held: they are read again, so one that has emptied is dropped before its number is reused */
122926
+ groupClock = null;
122927
+ noting = null;
122658
122928
  /** Reported once the output has all arrived, so a reader never sees the exit before the last line */
122659
122929
  exitReport = new ExitAfterDrain(() => this.emit("exit", this.child.exitCode, this.child.signalCode));
122660
122930
  static spawn(spec, os2) {
122661
122931
  let started;
122662
122932
  try {
122663
- started = spawnGrouped(spec.command, spec.args, { cwd: spec.cwd, env: spec.env, stdio: ["pipe", "pipe", "pipe"] }, os2?.platform);
122933
+ const watched = !!spec.endsWithHost && hasProcessGroups(os2?.platform);
122934
+ started = watched ? spawnGrouped("/bin/sh", ["-c", HOST_WATCH, "centralu-agent", spec.command, ...spec.args], { cwd: spec.cwd, env: spec.env, stdio: ["pipe", "pipe", "pipe", "pipe"] }, os2?.platform) : spawnGrouped(spec.command, spec.args, { cwd: spec.cwd, env: spec.env, stdio: ["pipe", "pipe", "pipe"] }, os2?.platform);
122664
122935
  } catch (err) {
122665
122936
  started = { child: notStarted(err), spawnedAt: Date.now() };
122666
122937
  }
@@ -122680,6 +122951,38 @@ var LocalAgentProcess = class _LocalAgentProcess extends EventEmitter3 {
122680
122951
  get signalCode() {
122681
122952
  return this.child.signalCode;
122682
122953
  }
122954
+ noteTree() {
122955
+ const pid = this.child.pid;
122956
+ if (pid === void 0 || this.ended || !this.grouped) return Promise.resolve();
122957
+ this.noting ??= noteGroups(pid, this.groups, this.os?.platform).then((groups) => {
122958
+ if (this.ended) return;
122959
+ this.tellWatch(this.groups, groups);
122960
+ this.groups = groups;
122961
+ if (groups.size === 0) this.stopGroupClock();
122962
+ else if (!this.groupClock) {
122963
+ this.groupClock = setInterval(() => void this.noteTree(), GROUP_CHECK_MS);
122964
+ this.groupClock.unref?.();
122965
+ }
122966
+ }).finally(() => {
122967
+ this.noting = null;
122968
+ });
122969
+ return this.noting;
122970
+ }
122971
+ /**
122972
+ * The host watch (`HOST_WATCH`) is told each helper group as it is noted and as it is dropped, so a host killed outright
122973
+ * still ends them: they are in groups of their own, which the watch's own `kill -9 0` does not reach (#1562 Mac re-check:
122974
+ * Cursor's `zsh -c` and its `sleep 300` outlived a SIGKILLed host)
122975
+ */
122976
+ tellWatch(before, after) {
122977
+ const watch3 = this.child.stdio[3];
122978
+ if (!watch3 || !("write" in watch3) || watch3.destroyed) return;
122979
+ const lines = [...[...after].filter((g5) => !before.has(g5)).map((g5) => `+${g5}`), ...[...before].filter((g5) => !after.has(g5)).map((g5) => `-${g5}`)];
122980
+ if (lines.length) watch3.write(lines.join("\n") + "\n");
122981
+ }
122982
+ stopGroupClock() {
122983
+ if (this.groupClock) clearInterval(this.groupClock);
122984
+ this.groupClock = null;
122985
+ }
122683
122986
  /** The end of what the CLI wrote to stderr */
122684
122987
  stderrTail() {
122685
122988
  return this.tail.text;
@@ -122710,6 +123013,8 @@ var LocalAgentProcess = class _LocalAgentProcess extends EventEmitter3 {
122710
123013
  return true;
122711
123014
  }
122712
123015
  onExit() {
123016
+ const watch3 = this.child.stdio[3];
123017
+ if (watch3 && "destroy" in watch3) watch3.destroy();
122713
123018
  this.sweep();
122714
123019
  if (!this.stdout.readableEnded) this.stdout.once("end", () => this.exitReport.drained());
122715
123020
  this.exitReport.exited(this.stdout.readableEnded);
@@ -122718,6 +123023,8 @@ var LocalAgentProcess = class _LocalAgentProcess extends EventEmitter3 {
122718
123023
  sweep() {
122719
123024
  const pid = this.child.pid;
122720
123025
  if (pid === void 0) return;
123026
+ this.stopGroupClock();
123027
+ for (const g5 of this.groups) stopGroup(g5, KILL_GRACE_MS);
122721
123028
  const root3 = { pid, spawnedAt: this.spawnedAt, exitedAt: Date.now() };
122722
123029
  void sweepExited(root3, this.os?.platform, this.os?.listProcesses ? { listProcesses: this.os.listProcesses, taskkill: this.os.taskkill } : void 0).then((ended) => {
122723
123030
  if (ended.length > 0) console.error(`[agent] ended ${ended.length} process(es) pid ${pid} left running: ${ended.join(", ")}`);
@@ -122728,7 +123035,8 @@ function notStarted(err) {
122728
123035
  const stand = new EventEmitter3();
122729
123036
  const stdout = new PassThrough2();
122730
123037
  stdout.end();
122731
- Object.assign(stand, { stdin: new PassThrough2(), stdout, stderr: null, pid: void 0, exitCode: null, signalCode: null, kill: () => false });
123038
+ const stdin = new PassThrough2();
123039
+ Object.assign(stand, { stdin, stdout, stderr: null, stdio: [stdin, stdout, null], pid: void 0, exitCode: null, signalCode: null, kill: () => false });
122732
123040
  process.nextTick(() => stand.emit("error", err));
122733
123041
  return stand;
122734
123042
  }
@@ -123167,13 +123475,13 @@ function systemEvents(m, sessionId) {
123167
123475
  function informationalEvents(m, sessionId) {
123168
123476
  const out = [];
123169
123477
  const text2 = str(m.content).trim();
123170
- const level = str(m.level);
123171
- if (text2 && level !== "info") {
123478
+ const level2 = str(m.level);
123479
+ if (text2 && level2 !== "info") {
123172
123480
  const hook = /\bblocked by hook\b/.test(text2);
123173
123481
  out.push({
123174
123482
  type: "notice",
123175
123483
  sessionId,
123176
- level: level === "warning" ? "warning" : "info",
123484
+ level: level2 === "warning" ? "warning" : "info",
123177
123485
  text: text2,
123178
123486
  from: "Claude Code",
123179
123487
  label: hook ? "hook" : "notice",
@@ -123188,8 +123496,8 @@ function notificationEvents(m, sessionId) {
123188
123496
  if (!text2 || str(m.key) === "error-compacting-conversation") return out;
123189
123497
  const color = str(m.color);
123190
123498
  const priority = str(m.priority);
123191
- const level = color === "error" ? "error" : color === "warning" || priority === "high" || priority === "immediate" ? "warning" : "info";
123192
- out.push({ type: "notice", sessionId, level, text: text2, from: "Claude Code", label: "notice" });
123499
+ const level2 = color === "error" ? "error" : color === "warning" || priority === "high" || priority === "immediate" ? "warning" : "info";
123500
+ out.push({ type: "notice", sessionId, level: level2, text: text2, from: "Claude Code", label: "notice" });
123193
123501
  return out;
123194
123502
  }
123195
123503
  function apiRetryEvents(m, sessionId) {
@@ -123898,8 +124206,8 @@ import { resolve as resolve18 } from "node:path";
123898
124206
  // packages/agent-host/src/adapters/always-allow.ts
123899
124207
  var AlwaysAllowRules = class {
123900
124208
  matchers = /* @__PURE__ */ new Set();
123901
- add(matcher) {
123902
- this.matchers.add(matcher);
124209
+ add(matcher2) {
124210
+ this.matchers.add(matcher2);
123903
124211
  }
123904
124212
  addAll(matchers) {
123905
124213
  for (const m of matchers) this.matchers.add(m);
@@ -124002,13 +124310,13 @@ var ClaudeCards = class {
124002
124310
  this.releaseOnCancel(requestId, options?.signal);
124003
124311
  });
124004
124312
  }
124005
- respondApproval(requestId, decision, scope, matcher) {
124313
+ respondApproval(requestId, decision, scope, matcher2) {
124006
124314
  const p = this.pending.take(requestId);
124007
124315
  if (!p) return false;
124008
124316
  if (decision === "deny") {
124009
124317
  p.resolve({ behavior: "deny", message: "Denied by user" });
124010
124318
  } else {
124011
- if (decision === "always" && matcher) this.alwaysAllow.add(matcher);
124319
+ if (decision === "always" && matcher2) this.alwaysAllow.add(matcher2);
124012
124320
  p.resolve({ behavior: "allow", updatedInput: p.input });
124013
124321
  }
124014
124322
  this.host.emit({ type: "approval_resolved", sessionId: this.host.sessionId, requestId, decision });
@@ -125315,8 +125623,8 @@ var ClaudeSession = class {
125315
125623
  this.notify = null;
125316
125624
  if (starting) this.emit({ type: "state_change", sessionId: this.sessionId, state: "working" });
125317
125625
  }
125318
- respondApproval(requestId, decision, scope, matcher) {
125319
- return this.cards.respondApproval(requestId, decision, scope, matcher);
125626
+ respondApproval(requestId, decision, scope, matcher2) {
125627
+ return this.cards.respondApproval(requestId, decision, scope, matcher2);
125320
125628
  }
125321
125629
  answerQuestion(requestId, answers) {
125322
125630
  return this.cards.answerQuestion(requestId, answers);
@@ -127283,7 +127591,7 @@ function reconcileOpenedThread(sessionId, answered, recorded) {
127283
127591
  // packages/agent-host/src/adapters/codex/sandbox.ts
127284
127592
  import { realpath as realpath8 } from "node:fs/promises";
127285
127593
  import { resolve as resolve19 } from "node:path";
127286
- async function sandboxConfig(cwd, preset, readConfig) {
127594
+ async function sandboxConfig(cwd, preset, readConfig2) {
127287
127595
  const roots = [];
127288
127596
  try {
127289
127597
  const stdout = await runGit(cwd, ["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"], { timeout: 5e3 });
@@ -127296,7 +127604,7 @@ async function sandboxConfig(cwd, preset, readConfig) {
127296
127604
  } catch {
127297
127605
  }
127298
127606
  if (roots.length) {
127299
- const config2 = await readConfig();
127607
+ const config2 = await readConfig2();
127300
127608
  const workspace = config2.sandbox_workspace_write;
127301
127609
  if (Array.isArray(workspace?.writable_roots)) {
127302
127610
  roots.push(...workspace.writable_roots.filter((p) => typeof p === "string"));
@@ -127476,7 +127784,7 @@ var CodexRequests = class {
127476
127784
  applyRules(matchers) {
127477
127785
  this.alwaysAllow.addAll(matchers);
127478
127786
  }
127479
- respondApproval(requestId, decision, _scope, matcher) {
127787
+ respondApproval(requestId, decision, _scope, matcher2) {
127480
127788
  const open6 = this.approvals.take(requestId);
127481
127789
  if (open6 === void 0) return false;
127482
127790
  const serverId = open6.serverId;
@@ -127489,7 +127797,7 @@ var CodexRequests = class {
127489
127797
  this.host.emit({ type: "approval_resolved", sessionId: this.host.sessionId, requestId, decision });
127490
127798
  return true;
127491
127799
  }
127492
- if (decision === "always" && matcher) this.alwaysAllow.add(matcher);
127800
+ if (decision === "always" && matcher2) this.alwaysAllow.add(matcher2);
127493
127801
  this.host.respond(serverId, { decision: toCodexDecision(decision) });
127494
127802
  this.host.emit({ type: "approval_resolved", sessionId: this.host.sessionId, requestId, decision });
127495
127803
  return true;
@@ -127519,12 +127827,12 @@ function requestOrigin(r) {
127519
127827
  };
127520
127828
  }
127521
127829
 
127522
- // packages/agent-host/src/adapters/codex/bridge-path.ts
127830
+ // packages/agent-host/src/adapters/bridge-path.ts
127523
127831
  import { existsSync as existsSync16 } from "node:fs";
127524
127832
  import { fileURLToPath as fileURLToPath3 } from "node:url";
127525
127833
  function bridgePath() {
127526
127834
  const candidates = [
127527
- new URL("./orchestrator-bridge.mjs", import.meta.url),
127835
+ new URL("./codex/orchestrator-bridge.mjs", import.meta.url),
127528
127836
  // the source tree
127529
127837
  new URL("./codex-orchestrator-bridge.mjs", import.meta.url)
127530
127838
  // the bundled build-output layout
@@ -128290,7 +128598,7 @@ var CodexSession = class {
128290
128598
  turns = new CodexTurns();
128291
128599
  /**
128292
128600
  * Messages that arrived while a compact/review turn was running (measured while dogfooding,
128293
- * 2026-09-02, MGH session).
128601
+ * 2026-09-02, a dogfooding session).
128294
128602
  *
128295
128603
  * In codex 0.147.0, turn/start **answers success while dropping the input** while a compact
128296
128604
  * turn is running — the rollout kept only the settings application (thread_settings_applied)
@@ -128658,8 +128966,8 @@ var CodexSession = class {
128658
128966
  outputSchemaParam() {
128659
128967
  return this.opts.outputSchema ? { outputSchema: this.opts.outputSchema } : {};
128660
128968
  }
128661
- respondApproval(requestId, decision, _scope, matcher) {
128662
- return this.serverRequests.respondApproval(requestId, decision, _scope, matcher);
128969
+ respondApproval(requestId, decision, _scope, matcher2) {
128970
+ return this.serverRequests.respondApproval(requestId, decision, _scope, matcher2);
128663
128971
  }
128664
128972
  applyRules(matchers) {
128665
128973
  this.serverRequests.applyRules(matchers);
@@ -129258,9 +129566,1776 @@ var CodexAdapter = class {
129258
129566
  }
129259
129567
  };
129260
129568
 
129569
+ // packages/agent-host/src/adapters/cursor-agent/index.ts
129570
+ import { execFile as execFile9 } from "node:child_process";
129571
+ import { promisify as promisify8 } from "node:util";
129572
+
129573
+ // packages/agent-host/src/adapters/frames/recorder.ts
129574
+ import { appendFileSync as appendFileSync2, existsSync as existsSync18, mkdirSync as mkdirSync19 } from "node:fs";
129575
+ import { join as join44 } from "node:path";
129576
+ var processes = /* @__PURE__ */ new Map();
129577
+ function frameRecorder(sessionId, env = process.env) {
129578
+ const dir = env.CC_RECORD_FRAMES;
129579
+ if (!dir) return void 0;
129580
+ mkdirSync19(dir, { recursive: true });
129581
+ let n = (processes.get(sessionId) ?? 0) + 1;
129582
+ while (existsSync18(join44(dir, `${sessionId}.${n}.jsonl`))) n++;
129583
+ processes.set(sessionId, n);
129584
+ const file2 = join44(dir, `${sessionId}.${n}.jsonl`);
129585
+ const start2 = Date.now();
129586
+ const write = (entry) => {
129587
+ try {
129588
+ appendFileSync2(file2, `${JSON.stringify({ t: Date.now() - start2, ...entry })}
129589
+ `);
129590
+ } catch (e) {
129591
+ console.error("[acp] recorder could not write", file2, errorMessage(e));
129592
+ }
129593
+ };
129594
+ return {
129595
+ frame: (direction, line) => write({ dir: direction, line }),
129596
+ exit: (code2, signal) => write({ dir: "exit", msg: { code: code2, sig: signal } })
129597
+ };
129598
+ }
129599
+
129600
+ // packages/agent-host/src/adapters/acp/session.ts
129601
+ import { isAbsolute as isAbsolute10, join as join45 } from "node:path";
129602
+
129603
+ // packages/agent-host/src/adapters/acp/connection.ts
129604
+ import { randomBytes as randomBytes8 } from "node:crypto";
129605
+ var RpcError = class extends Error {
129606
+ constructor(code2, message, data) {
129607
+ super(message);
129608
+ this.code = code2;
129609
+ this.data = data;
129610
+ }
129611
+ code;
129612
+ data;
129613
+ };
129614
+ var METHOD_NOT_FOUND2 = -32601;
129615
+ var AUTH_REQUIRED = -32e3;
129616
+ var FRAME = external_exports.looseObject({
129617
+ id: external_exports.union([external_exports.string(), external_exports.number(), external_exports.null()]).optional(),
129618
+ method: external_exports.string().optional(),
129619
+ params: external_exports.unknown().optional(),
129620
+ result: external_exports.unknown().optional(),
129621
+ error: external_exports.looseObject({ code: external_exports.number(), message: external_exports.string(), data: external_exports.unknown().optional() }).optional()
129622
+ });
129623
+ var NO_TIMEOUT_MS = 2 ** 31 - 1;
129624
+ var AcpConnection = class {
129625
+ constructor(proc, handlers, label) {
129626
+ this.proc = proc;
129627
+ this.handlers = handlers;
129628
+ this.label = label;
129629
+ proc.on("error", (err) => this.finish(null, new Error(`${label} failed to start: ${err.message}`)));
129630
+ proc.stdin.on("error", () => {
129631
+ });
129632
+ const lines = lineSplitter((line) => this.onLine(line));
129633
+ proc.stdout.on("data", (chunk) => lines.push(chunk));
129634
+ proc.stderr?.on("data", (d2) => {
129635
+ const s = String(d2).trim();
129636
+ if (s) console.error(`[${label}]`, s.slice(0, 500));
129637
+ });
129638
+ proc.on("exit", (code2) => this.finish(code2, null));
129639
+ }
129640
+ proc;
129641
+ handlers;
129642
+ label;
129643
+ /**
129644
+ * String ids with a per-connection prefix: a host that adopts a process the previous host was talking to can
129645
+ * receive answers to that host's requests (§4.4), and plain counters on both would have collided.
129646
+ */
129647
+ idPrefix = `c${randomBytes8(3).toString("hex")}-`;
129648
+ nextId = 1;
129649
+ pending = new PendingRequests();
129650
+ closed = false;
129651
+ finished = false;
129652
+ finish(code2, startError) {
129653
+ if (this.finished) return;
129654
+ this.finished = true;
129655
+ const expected = this.closed && !startError;
129656
+ this.closed = true;
129657
+ this.pending.rejectAll(startError ?? new Error(expected ? `request cancelled while closing ${this.label}` : `${this.label} exited`));
129658
+ this.handlers.onExit(code2, expected);
129659
+ }
129660
+ onLine(line) {
129661
+ if (!line.trim()) return;
129662
+ this.handlers.onFrame?.("in", line);
129663
+ let raw;
129664
+ try {
129665
+ raw = JSON.parse(line);
129666
+ } catch {
129667
+ console.error(`[${this.label}] non-JSON output:`, line.slice(0, 200));
129668
+ return;
129669
+ }
129670
+ const parsed = FRAME.safeParse(raw);
129671
+ if (!parsed.success) {
129672
+ console.error(`[${this.label}] a frame that is not a JSON-RPC message:`, line.slice(0, 200));
129673
+ return;
129674
+ }
129675
+ const msg = parsed.data;
129676
+ const id3 = msg.id ?? void 0;
129677
+ if (id3 !== void 0 && msg.method === void 0) {
129678
+ const error51 = msg.error ? new RpcError(msg.error.code, msg.error.message, msg.error.data) : null;
129679
+ const settled = error51 ? this.pending.reject(id3, error51) : this.pending.resolve(id3, msg.result);
129680
+ if (!settled) this.handlers.onStrayResponse?.(id3, msg.result, error51);
129681
+ return;
129682
+ }
129683
+ if (id3 !== void 0 && msg.method !== void 0) {
129684
+ void this.answer(id3, msg.method, msg.params);
129685
+ return;
129686
+ }
129687
+ if (msg.method !== void 0) this.handlers.onNotification(msg.method, msg.params);
129688
+ }
129689
+ async answer(id3, method, params) {
129690
+ try {
129691
+ const result = await this.handlers.onRequest(id3, method, params);
129692
+ this.write({ jsonrpc: "2.0", id: id3, result: result ?? null });
129693
+ } catch (err) {
129694
+ const e = err instanceof RpcError ? err : new RpcError(-32603, errorMessage(err));
129695
+ this.write({ jsonrpc: "2.0", id: id3, error: { code: e.code, message: e.message, ...e.data === void 0 ? {} : { data: e.data } } });
129696
+ }
129697
+ }
129698
+ /** The answer, unread: each caller parses it with the schema of what it asked (`wire.ts`) */
129699
+ request(method, params, timeoutMs = 12e4) {
129700
+ if (this.closed) return Promise.reject(new Error(`${this.label} has already exited`));
129701
+ const id3 = `${this.idPrefix}${this.nextId++}`;
129702
+ const answer = this.pending.wait(id3, timeoutMs, `No response for ${method}`);
129703
+ this.write({ jsonrpc: "2.0", id: id3, method, params });
129704
+ return answer;
129705
+ }
129706
+ notify(method, params) {
129707
+ this.write({ jsonrpc: "2.0", method, params });
129708
+ }
129709
+ get isClosed() {
129710
+ return this.closed;
129711
+ }
129712
+ stderrTail() {
129713
+ return this.proc.stderrTail?.() ?? "";
129714
+ }
129715
+ write(obj) {
129716
+ if (this.closed) return;
129717
+ const line = JSON.stringify(obj);
129718
+ this.handlers.onFrame?.("out", line);
129719
+ this.proc.stdin.write(line + "\n");
129720
+ }
129721
+ /** stdin EOF first, so the agent can end its own children; the whole tree is killed after `graceMs` */
129722
+ async dispose(graceMs = 2e3) {
129723
+ if (this.closed) return;
129724
+ this.closed = true;
129725
+ this.proc.stdin.end();
129726
+ const exited = this.finished || await waitForExit(this.proc, graceMs);
129727
+ if (!exited) this.proc.kill("SIGKILL");
129728
+ }
129729
+ /** Lets go of the process without closing it (a host swap under the keeper): no EOF, no signal */
129730
+ async detach() {
129731
+ if (this.closed) return;
129732
+ await this.proc.detach?.();
129733
+ this.closed = true;
129734
+ this.finished = true;
129735
+ this.pending.rejectAll(new Error(`request abandoned: this host let go of ${this.label}`));
129736
+ }
129737
+ };
129738
+
129739
+ // packages/agent-host/src/adapters/acp/known-updates.ts
129740
+ var KNOWN_UPDATES = /* @__PURE__ */ new Set([
129741
+ "agent_message_chunk",
129742
+ "agent_thought_chunk",
129743
+ "user_message_chunk",
129744
+ "tool_call",
129745
+ "tool_call_update",
129746
+ "plan",
129747
+ "available_commands_update",
129748
+ "current_mode_update",
129749
+ "config_option_update",
129750
+ "session_info_update",
129751
+ "usage_update",
129752
+ "compaction_update",
129753
+ "compaction_summary_chunk",
129754
+ "notice",
129755
+ "session/update",
129756
+ "session/request_permission"
129757
+ ]);
129758
+
129759
+ // packages/agent-host/src/adapters/acp/wire.ts
129760
+ var nullish3 = (t) => t.nullish().transform((v) => v ?? void 0);
129761
+ var InitializeResult = external_exports.looseObject({
129762
+ protocolVersion: external_exports.number(),
129763
+ agentCapabilities: nullish3(
129764
+ external_exports.looseObject({
129765
+ loadSession: nullish3(external_exports.boolean()),
129766
+ promptCapabilities: nullish3(external_exports.looseObject({ image: nullish3(external_exports.boolean()) })),
129767
+ sessionCapabilities: nullish3(
129768
+ external_exports.looseObject({ resume: external_exports.unknown().optional(), list: external_exports.unknown().optional(), close: external_exports.unknown().optional() })
129769
+ )
129770
+ })
129771
+ ),
129772
+ agentInfo: nullish3(external_exports.looseObject({ name: nullish3(external_exports.string()), version: nullish3(external_exports.string()) }))
129773
+ });
129774
+ var SelectOption = external_exports.looseObject({ value: external_exports.string(), name: nullish3(external_exports.string()) });
129775
+ var ConfigOption = external_exports.looseObject({
129776
+ id: external_exports.string(),
129777
+ category: nullish3(external_exports.string()),
129778
+ type: nullish3(external_exports.string()),
129779
+ currentValue: external_exports.unknown().optional(),
129780
+ options: nullish3(external_exports.array(external_exports.union([SelectOption, external_exports.looseObject({ options: external_exports.array(SelectOption) })])))
129781
+ });
129782
+ var OpenedSession = external_exports.looseObject({
129783
+ sessionId: nullish3(external_exports.string()),
129784
+ configOptions: nullish3(external_exports.array(ConfigOption))
129785
+ });
129786
+ var StopReason = external_exports.enum(["end_turn", "max_tokens", "max_turn_requests", "refusal", "cancelled"]);
129787
+ var PromptResult = external_exports.looseObject({
129788
+ stopReason: external_exports.union([StopReason, external_exports.string()]),
129789
+ usage: nullish3(
129790
+ external_exports.looseObject({
129791
+ inputTokens: nullish3(external_exports.number()),
129792
+ outputTokens: nullish3(external_exports.number()),
129793
+ cachedReadTokens: nullish3(external_exports.number()),
129794
+ cachedWriteTokens: nullish3(external_exports.number()),
129795
+ thoughtTokens: nullish3(external_exports.number())
129796
+ })
129797
+ )
129798
+ });
129799
+ var ContentBlock = external_exports.looseObject({
129800
+ type: external_exports.string(),
129801
+ text: nullish3(external_exports.string()),
129802
+ data: nullish3(external_exports.string()),
129803
+ mimeType: nullish3(external_exports.string())
129804
+ });
129805
+ var ToolCallContent = external_exports.looseObject({
129806
+ type: external_exports.string(),
129807
+ content: nullish3(ContentBlock),
129808
+ path: nullish3(external_exports.string()),
129809
+ oldText: nullish3(external_exports.string()),
129810
+ newText: nullish3(external_exports.string())
129811
+ });
129812
+ var CallStatus = external_exports.enum(["pending", "in_progress", "completed", "failed"]);
129813
+ var ToolCallFields = external_exports.looseObject({
129814
+ toolCallId: external_exports.string(),
129815
+ title: nullish3(external_exports.string()),
129816
+ kind: nullish3(external_exports.string()),
129817
+ status: nullish3(external_exports.union([CallStatus, external_exports.string()])),
129818
+ rawInput: external_exports.unknown().optional(),
129819
+ rawOutput: external_exports.unknown().optional(),
129820
+ content: nullish3(external_exports.array(ToolCallContent)),
129821
+ locations: nullish3(external_exports.array(external_exports.looseObject({ path: external_exports.string() })))
129822
+ });
129823
+ var Chunk = external_exports.looseObject({ content: ContentBlock, messageId: nullish3(external_exports.string()) });
129824
+ var SessionUpdate = external_exports.discriminatedUnion("sessionUpdate", [
129825
+ Chunk.extend({ sessionUpdate: external_exports.literal("agent_message_chunk") }),
129826
+ Chunk.extend({ sessionUpdate: external_exports.literal("agent_thought_chunk") }),
129827
+ Chunk.extend({ sessionUpdate: external_exports.literal("user_message_chunk") }),
129828
+ ToolCallFields.extend({ sessionUpdate: external_exports.literal("tool_call") }),
129829
+ ToolCallFields.extend({ sessionUpdate: external_exports.literal("tool_call_update") }),
129830
+ external_exports.looseObject({
129831
+ sessionUpdate: external_exports.literal("plan"),
129832
+ entries: external_exports.array(external_exports.looseObject({ content: external_exports.string(), status: external_exports.string(), priority: nullish3(external_exports.string()) }))
129833
+ }),
129834
+ external_exports.looseObject({
129835
+ sessionUpdate: external_exports.literal("available_commands_update"),
129836
+ availableCommands: external_exports.array(
129837
+ external_exports.looseObject({ name: external_exports.string(), description: nullish3(external_exports.string()), input: nullish3(external_exports.looseObject({ hint: nullish3(external_exports.string()) })) })
129838
+ )
129839
+ }),
129840
+ external_exports.looseObject({ sessionUpdate: external_exports.literal("session_info_update"), title: nullish3(external_exports.string()) }),
129841
+ external_exports.looseObject({
129842
+ sessionUpdate: external_exports.literal("usage_update"),
129843
+ used: external_exports.number(),
129844
+ size: external_exports.number(),
129845
+ cost: external_exports.looseObject({ amount: external_exports.number(), currency: external_exports.string() }).nullish()
129846
+ }),
129847
+ external_exports.looseObject({ sessionUpdate: external_exports.literal("current_mode_update"), currentModeId: external_exports.string() }),
129848
+ external_exports.looseObject({ sessionUpdate: external_exports.literal("config_option_update"), configOptions: external_exports.array(ConfigOption) }),
129849
+ external_exports.looseObject({
129850
+ sessionUpdate: external_exports.literal("compaction_update"),
129851
+ compactionId: external_exports.string(),
129852
+ status: external_exports.string(),
129853
+ summary: nullish3(external_exports.array(ContentBlock)),
129854
+ error: nullish3(external_exports.string())
129855
+ }),
129856
+ external_exports.looseObject({ sessionUpdate: external_exports.literal("compaction_summary_chunk"), compactionId: external_exports.string(), content: ContentBlock }),
129857
+ external_exports.looseObject({ sessionUpdate: external_exports.literal("notice"), severity: external_exports.string(), title: external_exports.string(), description: nullish3(external_exports.string()) })
129858
+ ]);
129859
+ var SessionNotification = external_exports.looseObject({ sessionId: external_exports.string(), update: external_exports.looseObject({ sessionUpdate: external_exports.string() }) });
129860
+ function readUpdate(params) {
129861
+ const n = SessionNotification.safeParse(params);
129862
+ if (!n.success) return null;
129863
+ const u = SessionUpdate.safeParse(n.data.update);
129864
+ return u.success ? { sessionId: n.data.sessionId, update: u.data } : { sessionId: n.data.sessionId, other: n.data.update.sessionUpdate };
129865
+ }
129866
+ var PermissionOptionKind = external_exports.enum(["allow_once", "allow_always", "reject_once", "reject_always"]);
129867
+ var PermissionOption = external_exports.looseObject({ optionId: external_exports.string(), kind: external_exports.union([PermissionOptionKind, external_exports.string()]), name: nullish3(external_exports.string()) });
129868
+ var RequestPermission = external_exports.looseObject({
129869
+ sessionId: external_exports.string(),
129870
+ toolCall: ToolCallFields,
129871
+ options: external_exports.array(PermissionOption)
129872
+ });
129873
+ function blockText(block) {
129874
+ return block?.type === "text" ? block.text ?? "" : "";
129875
+ }
129876
+
129877
+ // packages/agent-host/src/adapters/acp/normalize.ts
129878
+ var KIND_TOOL = {
129879
+ execute: "Bash",
129880
+ edit: "Edit",
129881
+ delete: "Edit",
129882
+ move: "Edit",
129883
+ read: "Read",
129884
+ search: "Grep",
129885
+ fetch: "WebFetch",
129886
+ think: "Think"
129887
+ };
129888
+ var OPEN_TAG = "<think>";
129889
+ var CLOSE_TAG = "</think>";
129890
+ function heldTail(text2) {
129891
+ for (let n = Math.min(CLOSE_TAG.length - 1, text2.length); n > 0; n--) if (CLOSE_TAG.startsWith(text2.slice(text2.length - n))) return n;
129892
+ return 0;
129893
+ }
129894
+ var AcpNormalizer = class {
129895
+ constructor(sessionId, label = "The tool") {
129896
+ this.sessionId = sessionId;
129897
+ this.label = label;
129898
+ }
129899
+ sessionId;
129900
+ label;
129901
+ /** Calls announced as `tool_call` and not yet ended, by `toolCallId` */
129902
+ open = /* @__PURE__ */ new Map();
129903
+ /** Every field said of each call this turn, merged: a permission request may carry less than the call did (measured, Cursor) */
129904
+ known = /* @__PURE__ */ new Map();
129905
+ compactions = /* @__PURE__ */ new Map();
129906
+ segment = 0;
129907
+ lastChunk = "other";
129908
+ /** The events one update says. `available_commands_update` and the like are read by the session, not here */
129909
+ update(u) {
129910
+ const sessionId = this.sessionId;
129911
+ const held3 = u.sessionUpdate === "agent_message_chunk" ? [] : this.releaseHeld();
129912
+ return [...held3, ...this.translate(u, sessionId)];
129913
+ }
129914
+ translate(u, sessionId) {
129915
+ switch (u.sessionUpdate) {
129916
+ case "agent_message_chunk": {
129917
+ const text2 = blockText(u.content);
129918
+ if (u.content.type === "image" && u.content.data && u.content.mimeType) {
129919
+ return [{ type: "message_image", sessionId, mime: u.content.mimeType, data: u.content.data }];
129920
+ }
129921
+ if (this.lastChunk !== "message") this.segment++;
129922
+ this.lastChunk = "message";
129923
+ return this.messageText(u.messageId ?? `acp-${this.segment}`, text2);
129924
+ }
129925
+ case "agent_thought_chunk": {
129926
+ this.lastChunk = "thought";
129927
+ const text2 = blockText(u.content);
129928
+ return text2 ? [{ type: "reasoning_delta", sessionId, text: text2 }] : [];
129929
+ }
129930
+ case "tool_call":
129931
+ case "tool_call_update":
129932
+ this.lastChunk = "other";
129933
+ return this.toolCall(u);
129934
+ case "plan":
129935
+ return [planUpdate(sessionId, u.entries, "content")];
129936
+ case "session_info_update":
129937
+ return u.title ? [{ type: "session_title", sessionId, title: u.title, auto: true }] : [];
129938
+ case "compaction_summary_chunk":
129939
+ this.compactions.set(u.compactionId, (this.compactions.get(u.compactionId) ?? "") + blockText(u.content));
129940
+ return [];
129941
+ case "compaction_update":
129942
+ return this.compaction(u);
129943
+ case "notice":
129944
+ return [{ type: "notice", sessionId, level: u.severity === "error" ? "error" : u.severity === "warning" ? "warning" : "info", text: [u.title, u.description].filter(Boolean).join("\n") }];
129945
+ default:
129946
+ return [];
129947
+ }
129948
+ }
129949
+ /**
129950
+ * A message's text, without the `</think>` a model leaves where it put its reasoning in the reply (OpenCode's free
129951
+ * model, measured in its own store: a step's text is the reasoning, then `</think>`, then the reply, with no `<think>`
129952
+ * before it). The first such tag of a message is dropped with the space after it; a tag split over two chunks is held
129953
+ * until the next one says whether it is one. The reasoning before it stays reply text: it was shown as it came.
129954
+ */
129955
+ messageText(messageId, text2) {
129956
+ const before = this.lastMessage === messageId ? [] : this.releaseHeld();
129957
+ const m = this.messages.get(messageId) ?? { held: "", settled: false, trim: false };
129958
+ this.messages.set(messageId, m);
129959
+ let out = m.held + text2;
129960
+ m.held = "";
129961
+ if (m.trim) {
129962
+ out = out.replace(/^\s+/, "");
129963
+ m.trim = out === "";
129964
+ }
129965
+ if (!m.settled) {
129966
+ const open6 = out.indexOf(OPEN_TAG);
129967
+ const close = out.indexOf(CLOSE_TAG);
129968
+ if (close !== -1 && (open6 === -1 || open6 > close)) {
129969
+ const after = out.slice(close + CLOSE_TAG.length).replace(/^\s+/, "");
129970
+ m.trim = after === "";
129971
+ out = out.slice(0, close) + after;
129972
+ m.settled = true;
129973
+ } else if (open6 !== -1) m.settled = true;
129974
+ else {
129975
+ const keep = heldTail(out);
129976
+ m.held = out.slice(out.length - keep);
129977
+ out = out.slice(0, out.length - keep);
129978
+ }
129979
+ }
129980
+ this.lastMessage = messageId;
129981
+ return [...before, ...out ? [{ type: "message_delta", sessionId: this.sessionId, role: "assistant", text: out, messageId }] : []];
129982
+ }
129983
+ /** What a message held back, once something else comes: it was no tag */
129984
+ releaseHeld() {
129985
+ const m = this.lastMessage === null ? void 0 : this.messages.get(this.lastMessage);
129986
+ if (!m?.held || this.lastMessage === null) return [];
129987
+ const text2 = m.held;
129988
+ m.held = "";
129989
+ return [{ type: "message_delta", sessionId: this.sessionId, role: "assistant", text: text2, messageId: this.lastMessage }];
129990
+ }
129991
+ messages = /* @__PURE__ */ new Map();
129992
+ lastMessage = null;
129993
+ /** Calls the person (or a Stop) refused this turn: whatever the agent says of them after, they did not run as asked */
129994
+ refused = /* @__PURE__ */ new Set();
129995
+ /**
129996
+ * What this turn has heard of a call, merged with `u`. `asked`: `u` is a permission request's, which may carry less than
129997
+ * the call did (no input, measured on Cursor) and a looser kind: Cursor asks about a `delete` call as `kind: edit`
129998
+ * (measured), so the call's own kind wins over the request's label
129999
+ */
130000
+ call(u, asked = false) {
130001
+ const before = this.known.get(u.toolCallId);
130002
+ const merged = { ...before, ...definedFields(u) };
130003
+ return ToolCallFields.parse(asked && before?.kind ? { ...merged, kind: before.kind } : merged);
130004
+ }
130005
+ /** The call was answered no (a reject, or cancelled by a Stop): its end is said as not ok */
130006
+ refuse(callId) {
130007
+ this.refused.add(callId);
130008
+ }
130009
+ toolCall(u) {
130010
+ const id3 = u.toolCallId;
130011
+ const seen = this.call(u);
130012
+ this.known.set(id3, seen);
130013
+ const events2 = [];
130014
+ let call2 = this.open.get(id3);
130015
+ if (!call2) {
130016
+ const ready = hasInput(seen.rawInput) || seen.status !== void 0 && seen.status !== "pending";
130017
+ if (!ready) return [];
130018
+ call2 = { output: "" };
130019
+ this.open.set(id3, call2);
130020
+ events2.push({ type: "tool_call", sessionId: this.sessionId, callId: id3, summary: summaryOf2(seen), ...seen.rawInput === void 0 ? {} : { input: seen.rawInput } });
130021
+ if (seen.kind === "edit" || seen.kind === "delete" || seen.kind === "move") {
130022
+ const paths = pathsOf2(seen);
130023
+ if (paths.length) events2.push({ type: "files_touched", sessionId: this.sessionId, paths });
130024
+ }
130025
+ }
130026
+ const output = outputOf(u);
130027
+ if (output !== null && output.startsWith(call2.output) && output.length > call2.output.length && u.status !== "completed" && u.status !== "failed") {
130028
+ events2.push({ type: "tool_output_delta", sessionId: this.sessionId, callId: id3, text: output.slice(call2.output.length) });
130029
+ call2.output = output;
130030
+ }
130031
+ if (u.status === "completed" || u.status === "failed") {
130032
+ this.open.delete(id3);
130033
+ const refused2 = this.refused.delete(id3);
130034
+ const ok = u.status === "completed" && !refused2 && rawOutputOk(seen.rawOutput);
130035
+ const text2 = output ?? rawOutputText(seen.rawOutput) ?? call2.output;
130036
+ events2.push(toolResultEvent(this.sessionId, id3, ok, text2 || (refused2 ? "Not run: the request was refused." : ""), `${this.label} did not return the output`));
130037
+ }
130038
+ return events2;
130039
+ }
130040
+ /** Calls still open when the turn ended: closed as failed, so no card spins forever (`why`: what ended them) */
130041
+ endTurn(why = "the turn ended before this call finished") {
130042
+ const events2 = [...this.releaseHeld(), ...[...this.open.keys()].map((id3) => toolResultEvent(this.sessionId, id3, false, "", why))];
130043
+ this.open.clear();
130044
+ this.known.clear();
130045
+ this.refused.clear();
130046
+ this.messages.clear();
130047
+ this.lastChunk = "other";
130048
+ return events2;
130049
+ }
130050
+ compaction(u) {
130051
+ if (u.status === "in_progress") return [{ type: "activity", sessionId: this.sessionId, activity: "compacting" }];
130052
+ const summary = u.summary?.map(blockText).join("") || this.compactions.get(u.compactionId) || "";
130053
+ this.compactions.delete(u.compactionId);
130054
+ const done2 = { type: "activity", sessionId: this.sessionId, activity: null };
130055
+ if (u.status === "completed") return [done2, { type: "compaction", sessionId: this.sessionId, failed: false, ...summary ? { reason: firstLine2(summary, 300) } : {} }];
130056
+ if (u.status === "failed") return [done2, { type: "compaction", sessionId: this.sessionId, failed: true, ...u.error ? { reason: u.error } : {} }];
130057
+ return [done2];
130058
+ }
130059
+ };
130060
+ function tokenUsage(result) {
130061
+ const u = result.usage;
130062
+ if (!u || u.inputTokens === void 0 && u.outputTokens === void 0) return null;
130063
+ return {
130064
+ inputTokens: u.inputTokens ?? 0,
130065
+ outputTokens: (u.outputTokens ?? 0) + (u.thoughtTokens ?? 0),
130066
+ cacheReadTokens: u.cachedReadTokens ?? 0,
130067
+ cacheCreationTokens: u.cachedWriteTokens ?? 0
130068
+ };
130069
+ }
130070
+ function definedFields(u) {
130071
+ return Object.fromEntries(Object.entries(u).filter(([k2, v]) => v !== void 0 && k2 !== "sessionUpdate"));
130072
+ }
130073
+ function hasInput(v) {
130074
+ return typeof v === "object" && v !== null && Object.keys(v).some((k2) => k2 !== "cwd");
130075
+ }
130076
+ function pathsOf2(u) {
130077
+ const fromDiffs = (u.content ?? []).flatMap((c) => c.type === "diff" && c.path ? [c.path] : []);
130078
+ return [.../* @__PURE__ */ new Set([...(u.locations ?? []).map((l) => l.path), ...fromDiffs])];
130079
+ }
130080
+ function summaryOf2(u) {
130081
+ const tool = u.kind && KIND_TOOL[u.kind] || u.title || "tool";
130082
+ const command = u.kind === "execute" ? commandOf(u) : "";
130083
+ return { tool, title: firstLine2(command || u.title || tool), paths: pathsOf2(u) };
130084
+ }
130085
+ function outputOf(u) {
130086
+ if (!u.content?.length) return null;
130087
+ return u.content.map((c) => c.type === "content" ? blockText(c.content) : c.type === "diff" ? `${c.path ?? ""}
130088
+ ${c.newText ?? ""}` : "").join("");
130089
+ }
130090
+ function rawOutputText(raw) {
130091
+ if (typeof raw === "string") return raw;
130092
+ const o = asObj(raw);
130093
+ if (typeof o.output === "string") return o.output;
130094
+ const streams = [o.stdout, o.stderr].filter((s) => typeof s === "string" && s.length > 0);
130095
+ if (streams.length) return streams.join(streams[0]?.endsWith("\n") ? "" : "\n");
130096
+ if (typeof o.error === "string") return o.error;
130097
+ return null;
130098
+ }
130099
+ function rawOutputOk(raw) {
130100
+ const o = asObj(raw);
130101
+ if (typeof o.exitCode === "number" && o.exitCode !== 0) return false;
130102
+ return o.success !== false;
130103
+ }
130104
+ function commandOf(u) {
130105
+ const command = asObj(u.rawInput).command;
130106
+ return typeof command === "string" ? command : "";
130107
+ }
130108
+
130109
+ // packages/agent-host/src/adapters/acp/session.ts
130110
+ var TREE_NOTE_MS = 2e3;
130111
+ var CANCELLED = { outcome: { outcome: "cancelled" } };
130112
+ var AcpSession = class {
130113
+ constructor(opts, emit, deps) {
130114
+ this.opts = opts;
130115
+ this.emit = emit;
130116
+ this.deps = deps;
130117
+ this.preset = opts.permissionPreset;
130118
+ this.normalizer = new AcpNormalizer(opts.sessionId, deps.label);
130119
+ this.unmapped = new UnmappedTypes(deps.label, opts.sessionId, KNOWN_UPDATES);
130120
+ this.conn = new AcpConnection(
130121
+ deps.process,
130122
+ {
130123
+ onNotification: (method, params) => this.onNotification(method, params),
130124
+ onRequest: (id3, method, params) => this.onRequest(id3, method, params),
130125
+ onExit: (code2, expected) => this.onExit(code2, expected),
130126
+ ...deps.onFrame ? { onFrame: deps.onFrame } : {}
130127
+ },
130128
+ deps.label
130129
+ );
130130
+ this.ready = this.open();
130131
+ this.ready.catch(() => {
130132
+ });
130133
+ }
130134
+ opts;
130135
+ emit;
130136
+ deps;
130137
+ externalId = null;
130138
+ /**
130139
+ * A process a previous host left is ended and the conversation opened again in a new one (`AcpAdapter`): a turn it was
130140
+ * running is not carried on, so the host ends it as one the restart stopped (`SessionManager.adoptKept`)
130141
+ */
130142
+ takesOverTurn = false;
130143
+ ready;
130144
+ phase = "opening";
130145
+ conn;
130146
+ normalizer;
130147
+ unmapped;
130148
+ cards = new PendingCards();
130149
+ cardIds = new CardIds();
130150
+ rules = new AlwaysAllowRules();
130151
+ queue = [];
130152
+ turn = null;
130153
+ /** While `session/load` is answered, every update is the replay of the conversation, not this turn (§4.3) */
130154
+ replaying = false;
130155
+ canClose = false;
130156
+ preset;
130157
+ commands = [];
130158
+ lastTurnInputs = [];
130159
+ /** Commands running now, by call id, and the clock that notes the tree while any runs */
130160
+ commandsRunning = /* @__PURE__ */ new Set();
130161
+ treeClock = null;
130162
+ /** The last context reading taken, for the compact turn's comparison */
130163
+ lastUsed = null;
130164
+ /** The session's running cost as last read at a turn's end: a turn's own cost is what it adds (OpenCode sends the total) */
130165
+ costTotal = null;
130166
+ get sessionId() {
130167
+ return this.opts.sessionId;
130168
+ }
130169
+ async open() {
130170
+ try {
130171
+ const init = InitializeResult.parse(
130172
+ await this.conn.request(
130173
+ "initialize",
130174
+ {
130175
+ protocolVersion: 1,
130176
+ clientInfo: { name: "centralu", version: "1" },
130177
+ // No fs and no terminal: the agent uses its own tools, whose approvals come as `session/request_permission`
130178
+ clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false, session: { compaction: {} } }
130179
+ },
130180
+ 6e4
130181
+ )
130182
+ );
130183
+ const version2 = init.agentInfo?.version ?? await this.deps.version();
130184
+ if (version2) this.emit({ type: "agent_version", sessionId: this.sessionId, version: version2 });
130185
+ this.canClose = init.agentCapabilities?.sessionCapabilities?.close !== void 0;
130186
+ const base2 = { cwd: this.opts.cwd, mcpServers: this.mcpServers() };
130187
+ const resume = this.opts.resumeExternalId;
130188
+ if (resume) {
130189
+ const caps = init.agentCapabilities;
130190
+ const method = caps?.loadSession ? "session/load" : caps?.sessionCapabilities?.resume !== void 0 ? "session/resume" : null;
130191
+ if (!method) throw rpcError(`${this.deps.label} cannot reopen a conversation`, "session_not_found");
130192
+ this.replaying = method === "session/load";
130193
+ try {
130194
+ OpenedSession.parse(await this.conn.request(method, { ...base2, sessionId: resume }, 15e4) ?? {});
130195
+ } finally {
130196
+ this.replaying = false;
130197
+ }
130198
+ this.externalId = resume;
130199
+ this.costTotal = this.opts.runningCost?.last ?? null;
130200
+ } else {
130201
+ const opened = OpenedSession.parse(await this.conn.request("session/new", base2, 12e4));
130202
+ if (!opened.sessionId) throw new Error(`${this.deps.label} opened a session without an id`);
130203
+ this.externalId = opened.sessionId;
130204
+ this.costTotal = 0;
130205
+ this.opts.runningCost?.keep(0);
130206
+ }
130207
+ this.phase = "ready";
130208
+ this.next();
130209
+ } catch (err) {
130210
+ throw this.openError(err);
130211
+ }
130212
+ }
130213
+ openError(err) {
130214
+ if (err instanceof RpcError && err.code === AUTH_REQUIRED) {
130215
+ return rpcError(`${this.deps.label} is not logged in. Log in from a terminal, then try again.`, "not_logged_in");
130216
+ }
130217
+ if (err instanceof RpcError) return rpcError(`${this.deps.label}: ${err.message}`, "internal");
130218
+ return err instanceof Error ? err : new Error(String(err));
130219
+ }
130220
+ /** Centralu's own tools through the same stdio bridge Codex starts (§6). Apps are not attached to an ACP agent yet */
130221
+ mcpServers() {
130222
+ const bridge = this.opts.orchestratorBridge;
130223
+ if (!this.opts.orchestratorTools || !bridge) return [];
130224
+ return [
130225
+ {
130226
+ name: ORCHESTRATOR_MCP_NAME,
130227
+ command: process.execPath,
130228
+ args: [bridgePath()],
130229
+ env: [
130230
+ { name: "CC_HOST_URL", value: bridge.url },
130231
+ { name: "CC_BRIDGE_TOKEN", value: bridge.token },
130232
+ { name: "CC_SESSION_ID", value: this.opts.sessionId }
130233
+ ]
130234
+ }
130235
+ ];
130236
+ }
130237
+ appAttachment() {
130238
+ return "failed";
130239
+ }
130240
+ send(text2, opts) {
130241
+ this.enqueue({ text: text2, ...opts?.id ? { id: opts.id } : {}, ...opts?.compact ? { compact: true } : {} });
130242
+ }
130243
+ sendTracked(text2, observer) {
130244
+ return new Promise((resolve21, reject) => this.enqueue({ text: text2, ...observer ? { observer } : {}, done: { resolve: resolve21, reject } }));
130245
+ }
130246
+ enqueue(input) {
130247
+ if (this.phase === "closed") {
130248
+ const e = new Error(`${this.deps.label} session is closed`);
130249
+ input.observer?.fail(e);
130250
+ input.done?.reject(e);
130251
+ return;
130252
+ }
130253
+ this.queue.push(input);
130254
+ this.next();
130255
+ }
130256
+ /** ACP runs one prompt at a time; a message sent during a turn waits for it (§5.4) */
130257
+ next() {
130258
+ if (this.phase !== "ready" || this.turn) return;
130259
+ const input = this.queue.shift();
130260
+ if (input) void this.run(input);
130261
+ }
130262
+ turnInputs() {
130263
+ return this.lastTurnInputs;
130264
+ }
130265
+ async run(input) {
130266
+ const turn = { input, stopAsked: false, cancelResent: false, usedBefore: this.lastUsed, used: null, compacted: false, costTotal: null };
130267
+ this.turn = turn;
130268
+ this.emit({ type: "state_change", sessionId: this.sessionId, state: "working" });
130269
+ input.observer?.accepted?.();
130270
+ let result = null;
130271
+ let failure3 = null;
130272
+ try {
130273
+ result = PromptResult.parse(
130274
+ await this.conn.request("session/prompt", { sessionId: this.externalId, prompt: [{ type: "text", text: input.text }] }, NO_TIMEOUT_MS)
130275
+ );
130276
+ } catch (err) {
130277
+ failure3 = err instanceof Error ? err : new Error(String(err));
130278
+ }
130279
+ if (turn.stopTimer) clearTimeout(turn.stopTimer);
130280
+ if (this.phase === "closed") return;
130281
+ for (const e of this.normalizer.endTurn()) this.emit(e);
130282
+ this.stopTreeClock();
130283
+ this.closeCards();
130284
+ this.lastTurnInputs = input.id ? [input.id] : [];
130285
+ this.turn = null;
130286
+ if (failure3) {
130287
+ this.emit({ type: "error", sessionId: this.sessionId, error: { code: "internal", message: `${this.deps.label}: ${failure3.message}`, retryable: true } });
130288
+ input.observer?.fail(failure3);
130289
+ input.done?.reject(failure3);
130290
+ } else if (result) {
130291
+ this.endTurn(result, turn);
130292
+ input.observer?.complete();
130293
+ input.done?.resolve();
130294
+ }
130295
+ this.next();
130296
+ }
130297
+ endTurn(result, turn) {
130298
+ const sessionId = this.sessionId;
130299
+ const tokens = tokenUsage(result);
130300
+ const cost = tokens ? this.turnCost(turn) : null;
130301
+ if (tokens) this.emit({ type: "usage_update", sessionId, tokens: cost === null ? tokens : { ...tokens, costUsd: cost } });
130302
+ if (result.stopReason === "end_turn" && this.inferredCompaction(turn)) {
130303
+ this.emit({ type: "compaction", sessionId, failed: false, trigger: "manual" });
130304
+ }
130305
+ switch (result.stopReason) {
130306
+ case "cancelled":
130307
+ this.emit({ type: "state_change", sessionId, state: "waiting_input", reason: turn.stopAsked ? "interrupted" : INTERRUPTED_BY_TOOL });
130308
+ return;
130309
+ case "refusal":
130310
+ this.emit({ type: "error", sessionId, error: { code: "internal", message: `${this.deps.label} refused to go on with this turn.`, retryable: false } });
130311
+ return;
130312
+ case "max_tokens":
130313
+ case "max_turn_requests":
130314
+ this.emit({
130315
+ type: "notice",
130316
+ sessionId,
130317
+ level: "warning",
130318
+ from: this.deps.label,
130319
+ text: result.stopReason === "max_tokens" ? `${this.deps.label} stopped: the reply reached its length limit.` : `${this.deps.label} stopped: the turn reached its limit of steps.`
130320
+ });
130321
+ this.emit({ type: "turn_complete", sessionId });
130322
+ return;
130323
+ default:
130324
+ this.emit({ type: "turn_complete", sessionId });
130325
+ }
130326
+ }
130327
+ /**
130328
+ * A command the tool runs may start helpers in a group of their own (Cursor's shell, measured on macOS), which outlive
130329
+ * the tool if it is killed: its tree is noted while a command runs, so they are ended with it (`noteTree`)
130330
+ */
130331
+ watchCommands(e) {
130332
+ const proc = this.deps.process;
130333
+ if (!proc.noteTree) return;
130334
+ if (e.type === "tool_call" && e.summary.tool === "Bash") {
130335
+ this.commandsRunning.add(e.callId);
130336
+ void proc.noteTree();
130337
+ if (!this.treeClock) {
130338
+ this.treeClock = setInterval(() => void proc.noteTree?.(), TREE_NOTE_MS);
130339
+ this.treeClock.unref?.();
130340
+ }
130341
+ } else if (e.type === "tool_result") {
130342
+ this.commandsRunning.delete(e.callId);
130343
+ if (this.commandsRunning.size === 0) this.stopTreeClock();
130344
+ }
130345
+ }
130346
+ stopTreeClock() {
130347
+ this.commandsRunning.clear();
130348
+ if (this.treeClock) clearInterval(this.treeClock);
130349
+ this.treeClock = null;
130350
+ }
130351
+ /** What this turn added to the conversation's running cost, once a total from before it is known (`CreateSessionOpts.runningCost`); null otherwise */
130352
+ turnCost(turn) {
130353
+ if (turn.costTotal === null) return null;
130354
+ const before = this.costTotal;
130355
+ this.costTotal = turn.costTotal;
130356
+ this.opts.runningCost?.keep(turn.costTotal);
130357
+ return before === null || turn.costTotal < before ? null : turn.costTotal - before;
130358
+ }
130359
+ /** Centralu's own compact turn, with no protocol frame for it, whose reading went down (§5.6) */
130360
+ inferredCompaction(turn) {
130361
+ if (!this.deps.infersCompaction || !turn.input.compact || turn.compacted) return false;
130362
+ return turn.usedBefore !== null && turn.used !== null && turn.used < turn.usedBefore;
130363
+ }
130364
+ interrupt() {
130365
+ const turn = this.turn;
130366
+ if (!turn || this.phase !== "ready") return;
130367
+ turn.stopAsked = true;
130368
+ this.conn.notify("session/cancel", { sessionId: this.externalId });
130369
+ this.closeCards();
130370
+ if (!turn.stopTimer) {
130371
+ turn.stopTimer = setTimeout(() => {
130372
+ if (this.turn === turn) this.emit(stopNotConfirmed(this.sessionId, `${this.deps.label} has not confirmed the stop; the turn may still be running.`));
130373
+ }, this.deps.stopConfirmMs);
130374
+ turn.stopTimer.unref?.();
130375
+ }
130376
+ }
130377
+ onNotification(method, params) {
130378
+ if (method !== "session/update") {
130379
+ this.unmapped.note(method);
130380
+ return;
130381
+ }
130382
+ const read = readUpdate(params);
130383
+ if (!read || read.sessionId !== this.externalId || this.replaying) return;
130384
+ const turn = this.turn;
130385
+ this.resendLostCancel();
130386
+ if ("other" in read) {
130387
+ this.unmapped.note(read.other);
130388
+ return;
130389
+ }
130390
+ const u = read.update;
130391
+ switch (u.sessionUpdate) {
130392
+ case "available_commands_update":
130393
+ this.commands = u.availableCommands.map((c) => ({ name: c.name, ...c.description ? { description: c.description } : {}, ...c.input?.hint ? { argumentHint: c.input.hint } : {} }));
130394
+ return;
130395
+ case "usage_update":
130396
+ if (u.used > 0 && u.size > 0) {
130397
+ this.emit({ type: "context_update", sessionId: this.sessionId, used: u.used, window: u.size, exactness: "exact" });
130398
+ this.lastUsed = u.used;
130399
+ if (turn) turn.used = u.used;
130400
+ }
130401
+ if (turn && u.cost?.currency === "USD") turn.costTotal = u.cost.amount;
130402
+ return;
130403
+ default:
130404
+ for (const e of this.normalizer.update(u)) {
130405
+ this.watchCommands(e);
130406
+ if (e.type !== "compaction") {
130407
+ this.emit(e);
130408
+ continue;
130409
+ }
130410
+ if (turn) turn.compacted = true;
130411
+ this.emit({ ...e, trigger: turn?.input.compact ? "manual" : "auto" });
130412
+ }
130413
+ }
130414
+ }
130415
+ /**
130416
+ * A cancel that reached the agent before its turn had begun is lost (ACP's cancel is a notification, answered by nothing
130417
+ * but the turn's end): the turn going on after it, by an update or by a question, gets the cancel once more
130418
+ */
130419
+ resendLostCancel() {
130420
+ const turn = this.turn;
130421
+ if (!turn?.stopAsked || turn.cancelResent) return;
130422
+ turn.cancelResent = true;
130423
+ this.conn.notify("session/cancel", { sessionId: this.externalId });
130424
+ }
130425
+ /** Answers a question cancelled, and remembers the call did not run as asked */
130426
+ refused(callId) {
130427
+ this.normalizer.refuse(callId);
130428
+ return CANCELLED;
130429
+ }
130430
+ /** Whether a question asked now belongs to a turn that still runs: none after Stop or after the turn's end */
130431
+ askable(turn) {
130432
+ return this.phase !== "closed" && turn !== null && this.turn === turn && !turn.stopAsked;
130433
+ }
130434
+ async onRequest(_id, method, params) {
130435
+ if (method !== "session/request_permission") {
130436
+ this.unmapped.note(method);
130437
+ throw new RpcError(METHOD_NOT_FOUND2, `Method not found: ${method}`);
130438
+ }
130439
+ const req = RequestPermission.parse(params);
130440
+ const turn = this.turn;
130441
+ this.resendLostCancel();
130442
+ if (!this.askable(turn)) return this.refused(req.toolCall.toolCallId);
130443
+ const call2 = this.normalizer.call(req.toolCall, true);
130444
+ const detail = detailOf(call2, this.opts.cwd);
130445
+ const once = pick3(req.options, "allow_once");
130446
+ const granted = once !== void 0 && await this.grantedWithoutCard(detail, call2);
130447
+ if (!this.askable(turn)) return this.refused(req.toolCall.toolCallId);
130448
+ if (granted && once) return selected(once);
130449
+ const requestId = this.cardIds.next("acp-req");
130450
+ return new Promise((resolve21) => {
130451
+ this.cards.set(requestId, { callId: req.toolCall.toolCallId, options: req.options, answer: resolve21 });
130452
+ this.emit({ type: "approval_request", sessionId: this.sessionId, requestId, detail });
130453
+ });
130454
+ }
130455
+ /** `auto`, Centralu's own tool, an `edits` grant inside the folder, or a rule the person saved: answered `allow_once` without a card */
130456
+ async grantedWithoutCard(detail, call2) {
130457
+ if (this.preset === "auto" || this.deps.centraluToolOf?.(call2)) return true;
130458
+ if (this.preset === "edits" && call2.kind === "edit" && detail.kind === "file_edit") {
130459
+ const paths = editPaths(call2).map((p) => isAbsolute10(p) ? p : join45(this.opts.cwd, p));
130460
+ if (await insideFolder(this.opts.cwd, paths)) return true;
130461
+ }
130462
+ const key = alwaysAllowKey(detail);
130463
+ return key !== null && this.rules.allows(key);
130464
+ }
130465
+ respondApproval(requestId, decision, _scope, matcher2) {
130466
+ const card = this.cards.take(requestId);
130467
+ if (!card) return false;
130468
+ if (decision === "always" && matcher2) this.rules.add(matcher2);
130469
+ const option = pick3(card.options, decision === "deny" ? "reject_once" : "allow_once");
130470
+ if (decision === "deny" || !option) this.normalizer.refuse(card.callId);
130471
+ card.answer(option ? selected(option) : CANCELLED);
130472
+ this.emit({ type: "approval_resolved", sessionId: this.sessionId, requestId, decision });
130473
+ return true;
130474
+ }
130475
+ applyRules(matchers) {
130476
+ this.rules.addAll(matchers);
130477
+ }
130478
+ setPermissionPreset(preset) {
130479
+ this.preset = preset;
130480
+ }
130481
+ async listCommands() {
130482
+ return this.commands;
130483
+ }
130484
+ closeCards() {
130485
+ this.cards.release((requestId, card) => {
130486
+ this.normalizer.refuse(card.callId);
130487
+ card.answer(CANCELLED);
130488
+ this.emit({ type: "approval_resolved", sessionId: this.sessionId, requestId, decision: "deny" });
130489
+ });
130490
+ }
130491
+ onExit(code2, expected) {
130492
+ if (expected || this.phase === "closed") return;
130493
+ const wasOpen = this.phase === "ready";
130494
+ this.phase = "closed";
130495
+ this.closeCards();
130496
+ const turn = this.turn;
130497
+ this.turn = null;
130498
+ if (turn?.stopTimer) clearTimeout(turn.stopTimer);
130499
+ this.dropQueue("were still queued when the agent exited");
130500
+ const failure3 = new Error(`${this.deps.label} exited${code2 === null ? "" : ` (code ${code2})`}`);
130501
+ if (turn) {
130502
+ for (const e of this.normalizer.endTurn()) this.emit(e);
130503
+ this.stopTreeClock();
130504
+ turn.input.observer?.fail(failure3);
130505
+ turn.input.done?.reject(failure3);
130506
+ }
130507
+ if (turn || !wasOpen) {
130508
+ const tail = this.conn.stderrTail().trim().split("\n").slice(-5).join("\n");
130509
+ this.emit({ type: "error", sessionId: this.sessionId, error: { code: "adapter_crashed", message: tail ? `${failure3.message}: ${tail}` : failure3.message, retryable: true } });
130510
+ } else {
130511
+ this.emit({ type: "session_live", sessionId: this.sessionId, live: false, reason: "asleep" });
130512
+ }
130513
+ }
130514
+ dropQueue(when) {
130515
+ const left = this.queue.splice(0);
130516
+ if (left.length === 0) return;
130517
+ const e = new Error(`${left.length} message(s) ${when}`);
130518
+ for (const i of left) {
130519
+ i.observer?.fail(e);
130520
+ i.done?.reject(e);
130521
+ }
130522
+ this.emit(undeliveredError(this.sessionId, left.length, when));
130523
+ }
130524
+ async dispose() {
130525
+ if (this.phase === "closed") return;
130526
+ const turn = this.turn;
130527
+ this.phase = "closed";
130528
+ this.closeCards();
130529
+ this.dropQueue("were still queued when the session closed");
130530
+ if (turn) {
130531
+ for (const e of this.normalizer.endTurn("Centralu stopped this session while the call ran")) this.emit(e);
130532
+ this.stopTreeClock();
130533
+ }
130534
+ if (turn && this.externalId) {
130535
+ if (turn.stopTimer) clearTimeout(turn.stopTimer);
130536
+ this.conn.notify("session/cancel", { sessionId: this.externalId });
130537
+ turn.input.observer?.fail(new Error("the session closed"));
130538
+ turn.input.done?.reject(new Error("the session closed"));
130539
+ }
130540
+ if (this.canClose && this.externalId && !this.conn.isClosed) {
130541
+ await this.conn.request("session/close", { sessionId: this.externalId }, 2e3).catch(() => {
130542
+ });
130543
+ }
130544
+ await this.conn.dispose();
130545
+ }
130546
+ };
130547
+ function pick3(options, kind) {
130548
+ return options.find((o) => o.kind === kind);
130549
+ }
130550
+ function selected(option) {
130551
+ return { outcome: { outcome: "selected", optionId: option.optionId } };
130552
+ }
130553
+ var str2 = (v) => typeof v === "string" ? v : "";
130554
+ function editPaths(call2) {
130555
+ const input = asObj(call2.rawInput);
130556
+ const fromDiffs = (call2.content ?? []).flatMap((c) => c.type === "diff" && c.path ? [c.path] : []);
130557
+ const named = [str2(input.filePath), str2(input.path), str2(input.file_path)].filter(Boolean);
130558
+ return [.../* @__PURE__ */ new Set([...fromDiffs, ...(call2.locations ?? []).map((l) => l.path), ...named])];
130559
+ }
130560
+ function detailOf(call2, cwd) {
130561
+ const input = asObj(call2.rawInput);
130562
+ if (call2.kind === "execute") return { kind: "command", command: str2(input.command) || call2.title || "", cwd: str2(input.cwd) || cwd };
130563
+ if (call2.kind === "edit" || call2.kind === "delete" || call2.kind === "move") {
130564
+ const diffs = (call2.content ?? []).flatMap((c) => c.type === "diff" && c.path ? [{ path: c.path, diff: c.newText ?? "" }] : []);
130565
+ const changes = diffs.length ? diffs : editPaths(call2).map((path2) => ({ path: path2, diff: str2(input.content) || str2(input.newString) || str2(input.new_string) }));
130566
+ return fileEditDetail(call2.kind === "delete" ? changes.map((c) => ({ ...c, change: "delete" })) : changes);
130567
+ }
130568
+ return { kind: "other", raw: JSON.stringify({ title: call2.title, kind: call2.kind, input: call2.rawInput }).slice(0, 2e3) };
130569
+ }
130570
+
130571
+ // packages/agent-host/src/adapters/acp/adapter.ts
130572
+ var STOP_CONFIRM_MS2 = 1e4;
130573
+ var AcpAdapter = class {
130574
+ constructor(profile, spawn6 = spawnLocalAgent, stopConfirmMs = STOP_CONFIRM_MS2) {
130575
+ this.profile = profile;
130576
+ this.spawn = spawn6;
130577
+ this.stopConfirmMs = stopConfirmMs;
130578
+ }
130579
+ profile;
130580
+ spawn;
130581
+ stopConfirmMs;
130582
+ get descriptor() {
130583
+ return this.profile.descriptor;
130584
+ }
130585
+ get capabilities() {
130586
+ return this.profile.capabilities;
130587
+ }
130588
+ get surface() {
130589
+ return this.profile.surface;
130590
+ }
130591
+ detect() {
130592
+ return this.profile.detect();
130593
+ }
130594
+ installedVersion() {
130595
+ return this.profile.installedVersion();
130596
+ }
130597
+ async createSession(opts, emit) {
130598
+ const label = this.profile.descriptor.label;
130599
+ const launch2 = await this.profile.launch(opts);
130600
+ if (!launch2) throw rpcError(`${label} is not installed. ${this.profile.descriptor.install}`, "tool_not_installed");
130601
+ const recorder = frameRecorder(opts.sessionId);
130602
+ const spec = { command: launch2.command, args: launch2.args, cwd: opts.cwd, env: { ...process.env, ...launch2.env } };
130603
+ const source = opts.processSource;
130604
+ if (source?.adopt) await closeAdopted(source.adopt, opts.sessionId, emit);
130605
+ const proc = source ? source.spawn(spec) : this.spawn({ ...spec, endsWithHost: true });
130606
+ if (recorder) proc.once("exit", (code2, signal) => recorder.exit(code2, signal));
130607
+ const session2 = new AcpSession(opts, emit, {
130608
+ label,
130609
+ process: proc,
130610
+ version: () => this.profile.installedVersion(),
130611
+ stopConfirmMs: this.stopConfirmMs,
130612
+ ...this.profile.centraluToolOf ? { centraluToolOf: (call2) => this.profile.centraluToolOf?.(call2) ?? null } : {},
130613
+ ...this.profile.infersCompaction ? { infersCompaction: true } : {},
130614
+ ...recorder ? { onFrame: recorder.frame } : {}
130615
+ });
130616
+ try {
130617
+ await session2.ready;
130618
+ } catch (err) {
130619
+ await session2.dispose().catch(() => {
130620
+ });
130621
+ throw err;
130622
+ }
130623
+ return session2;
130624
+ }
130625
+ };
130626
+ var ADOPTED_EXIT_MS = 5e3;
130627
+ async function closeAdopted(adopt, sessionId, emit) {
130628
+ adopt.process.kill("SIGKILL");
130629
+ await waitForExit(adopt.process, ADOPTED_EXIT_MS);
130630
+ for (const call2 of adopt.openCalls) emit(toolResultEvent(sessionId, call2.callId, false, "", "Centralu restarted while this call ran; it was stopped"));
130631
+ }
130632
+
130633
+ // packages/agent-host/src/adapters/cursor-agent/install.ts
130634
+ import { existsSync as existsSync19, readdirSync as readdirSync11, realpathSync as realpathSync10 } from "node:fs";
130635
+ import { dirname as dirname22, join as join46, win32 as win327 } from "node:path";
130636
+ var realFs = {
130637
+ exists: existsSync19,
130638
+ list: (dir) => {
130639
+ try {
130640
+ return readdirSync11(dir);
130641
+ } catch {
130642
+ return [];
130643
+ }
130644
+ },
130645
+ real: (p) => {
130646
+ try {
130647
+ return realpathSync10(p);
130648
+ } catch {
130649
+ return p;
130650
+ }
130651
+ }
130652
+ };
130653
+ var VERSION = /^(\d{4})\.(\d{1,2})\.(\d{1,2})(?:-(\d{2})-(\d{2})-(\d{2}))?-[a-f0-9]+$/;
130654
+ function newestVersion(names) {
130655
+ const key = (n) => {
130656
+ const m = VERSION.exec(n);
130657
+ if (!m) return null;
130658
+ const pad = (s) => (s ?? "00").padStart(2, "0");
130659
+ return `${m[1]}${pad(m[2])}${pad(m[3])}${pad(m[4])}${pad(m[5])}${pad(m[6])}`;
130660
+ };
130661
+ const dated = names.flatMap((n) => {
130662
+ const k2 = key(n);
130663
+ return k2 ? [{ n, k: k2 }] : [];
130664
+ });
130665
+ dated.sort((a, b2) => a.k < b2.k ? 1 : a.k > b2.k ? -1 : 0);
130666
+ return dated[0]?.n ?? null;
130667
+ }
130668
+ function findCursor(env = process.env, platform, fs2 = realFs) {
130669
+ if (installsCliAsVersionFolders(platform)) {
130670
+ const roots = [whichTool("agent", env, platform), whichTool("cursor-agent", env, platform)].flatMap((p) => p ? [win327.dirname(p)] : []).concat(env.LOCALAPPDATA ? [win327.join(env.LOCALAPPDATA, "cursor-agent")] : []);
130671
+ for (const root3 of roots) {
130672
+ const version2 = newestVersion(fs2.list(win327.join(root3, "versions")));
130673
+ if (!version2) continue;
130674
+ const dir = win327.join(root3, "versions", version2);
130675
+ const node = win327.join(dir, "node.exe");
130676
+ const entry = win327.join(dir, "index.js");
130677
+ if (fs2.exists(node) && fs2.exists(entry)) return { version: version2, command: node, args: [entry], env: { CURSOR_INVOKED_AS: "agent.cmd" } };
130678
+ }
130679
+ return null;
130680
+ }
130681
+ for (const name of ["cursor-agent", "agent"]) {
130682
+ const found = whichTool(name, env, platform);
130683
+ if (!found) continue;
130684
+ const real3 = fs2.real(found);
130685
+ const dir = dirname22(real3);
130686
+ const version2 = dir.split(/[\\/]/).pop() ?? "";
130687
+ if (VERSION.test(version2) && fs2.exists(join46(dir, "index.js"))) return { version: version2, command: real3, args: [], env: {} };
130688
+ }
130689
+ return null;
130690
+ }
130691
+
130692
+ // packages/agent-host/src/adapters/cursor-agent/surface.ts
130693
+ import { execFile as execFile8 } from "node:child_process";
130694
+ import { readdirSync as readdirSync12, readFileSync as readFileSync16 } from "node:fs";
130695
+ import { dirname as dirname23, join as join47 } from "node:path";
130696
+ import { promisify as promisify7 } from "node:util";
130697
+
130698
+ // packages/agent-host/src/adapters/cursor-agent/surface-baseline.ts
130699
+ var SURFACE_BASELINE3 = {
130700
+ recorded: "2026-10-26",
130701
+ version: "agent 2026.10.22-27dcf5f",
130702
+ offered: {
130703
+ command: [
130704
+ "about",
130705
+ "agent",
130706
+ "bedrock",
130707
+ "create-chat",
130708
+ "cursor/ask_question",
130709
+ "cursor/create_plan",
130710
+ "cursor/generate_image",
130711
+ "cursor/list_available_models",
130712
+ "cursor/task",
130713
+ "cursor/update_todos",
130714
+ "help",
130715
+ "login",
130716
+ "logout",
130717
+ "ls",
130718
+ "mcp",
130719
+ "models",
130720
+ "persist",
130721
+ "plugin",
130722
+ "prompt",
130723
+ "resume",
130724
+ "team",
130725
+ "update",
130726
+ "worker"
130727
+ ],
130728
+ message: [
130729
+ "agent_message_chunk",
130730
+ "agent_thought_chunk",
130731
+ "available_commands_update",
130732
+ "current_mode_update",
130733
+ "plan",
130734
+ "session_info_update",
130735
+ "subagent_spawned",
130736
+ "subagent_state_update",
130737
+ "tool_call",
130738
+ "tool_call_update",
130739
+ "user_message_chunk"
130740
+ ],
130741
+ capability: [
130742
+ "authMethods.cursor_login",
130743
+ "loadSession",
130744
+ "mcpCapabilities.http",
130745
+ "mcpCapabilities.sse",
130746
+ "promptCapabilities.audio",
130747
+ "promptCapabilities.embeddedContext",
130748
+ "promptCapabilities.image",
130749
+ "sessionCapabilities.list"
130750
+ ]
130751
+ }
130752
+ };
130753
+
130754
+ // packages/agent-host/src/adapters/cursor-agent/surface.ts
130755
+ var exec6 = promisify7(execFile8);
130756
+ var Initialized = external_exports.looseObject({ agentCapabilities: external_exports.unknown().optional(), authMethods: external_exports.array(external_exports.looseObject({ id: external_exports.string() })).optional() });
130757
+ async function gather() {
130758
+ const install = findCursor();
130759
+ if (!install) throw new Error("Cursor CLI is not installed on this machine (https://cursor.com/install)");
130760
+ const env = { ...process.env, ...install.env };
130761
+ const { stdout: help } = await exec6(install.command, [...install.args, "--help"], { timeout: 3e4, windowsHide: true, env });
130762
+ const proc = spawnLocalAgent({ command: install.command, args: [...install.args, "acp"], cwd: process.cwd(), env });
130763
+ const conn = new AcpConnection(proc, { onNotification: () => {
130764
+ }, onRequest: async () => null, onExit: () => {
130765
+ } }, "cursor-surface");
130766
+ let init;
130767
+ try {
130768
+ init = await conn.request("initialize", { protocolVersion: 1, clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false }, clientInfo: { name: "centralu-surface", version: "1" } }, 6e4);
130769
+ } finally {
130770
+ await conn.dispose();
130771
+ }
130772
+ const dir = dirname23(install.args[0] ?? install.command);
130773
+ const names = /* @__PURE__ */ new Set();
130774
+ for (const f of readdirSync12(dir).filter((n) => n.endsWith(".js"))) {
130775
+ const text2 = readFileSync16(join47(dir, f), "utf8");
130776
+ if (!text2.includes("session/request_permission") && !text2.includes("cursor_login")) continue;
130777
+ for (const m of text2.matchAll(/sessionUpdate:"([a-z_]+)"|"(cursor\/[a-z_]+)"/g)) {
130778
+ const name = m[1] ?? m[2];
130779
+ if (name) names.add(name);
130780
+ }
130781
+ }
130782
+ return { version: install.version, help, initialize: JSON.stringify(init), bundle: JSON.stringify([...names].sort()) };
130783
+ }
130784
+ function capabilityNames(value, prefix = "") {
130785
+ if (!value || typeof value !== "object" || Array.isArray(value)) return prefix ? [prefix] : [];
130786
+ const entries = Object.entries(asObj(value));
130787
+ if (entries.length === 0) return prefix ? [prefix] : [];
130788
+ return entries.flatMap(([k2, v]) => typeof v === "object" && v !== null ? capabilityNames(v, prefix ? `${prefix}.${k2}` : k2) : [prefix ? `${prefix}.${k2}` : k2]);
130789
+ }
130790
+ function readCursorSurface(inputs) {
130791
+ const init = Initialized.parse(JSON.parse(inputs.initialize ?? "{}"));
130792
+ const commands = [...(inputs.help ?? "").matchAll(/^ {2}([a-z][\w-]*)(?: |$)/gm)].flatMap((m) => m[1] ? [m[1]] : []);
130793
+ const bundle = external_exports.array(external_exports.string()).parse(JSON.parse(inputs.bundle ?? "[]"));
130794
+ return {
130795
+ version: `agent ${inputs.version ?? "unknown"}`,
130796
+ offered: {
130797
+ command: [.../* @__PURE__ */ new Set([...commands, ...bundle.filter((n) => n.startsWith("cursor/"))])].sort(),
130798
+ message: bundle.filter((n) => !n.startsWith("cursor/")).sort(),
130799
+ capability: [...capabilityNames(init.agentCapabilities), ...(init.authMethods ?? []).map((a) => `authMethods.${a.id}`)].sort()
130800
+ }
130801
+ };
130802
+ }
130803
+ var BUNDLE = "read in the 2026.10.22-27dcf5f bundle (3635.index.js), 2026-10-26";
130804
+ var CURSOR_SURFACE = {
130805
+ gather,
130806
+ read: readCursorSurface,
130807
+ limitations: [
130808
+ {
130809
+ kind: "capability",
130810
+ name: "authMethods.cursor_login",
130811
+ treatment: "blocked",
130812
+ measured: "agent 2026.10.22-27dcf5f, not logged in: authenticate {methodId: cursor_login} started the browser login flow and did not answer in 8 s",
130813
+ offeredWhenMeasured: true,
130814
+ note: "Never called: a background session must not open a browser. Logging in stays `agent login` in a terminal."
130815
+ },
130816
+ {
130817
+ kind: "message",
130818
+ name: "current_mode_update",
130819
+ treatment: "dropped",
130820
+ measured: BUNDLE,
130821
+ offeredWhenMeasured: true,
130822
+ note: "Modes (agent, plan, ask) are not offered until the settings menu maps them (PR 2)."
130823
+ },
130824
+ {
130825
+ kind: "message",
130826
+ name: "subagent_spawned",
130827
+ treatment: "dropped",
130828
+ measured: `${BUNDLE}: sent only when the client advertises sub-agent support, which Centralu does not yet`,
130829
+ offeredWhenMeasured: true,
130830
+ note: "Sub-agents run as ordinary tool calls until they are mapped onto subagent_event (PR 2)."
130831
+ },
130832
+ {
130833
+ kind: "message",
130834
+ name: "subagent_state_update",
130835
+ treatment: "dropped",
130836
+ measured: `${BUNDLE}: as subagent_spawned`,
130837
+ offeredWhenMeasured: true,
130838
+ note: "As subagent_spawned."
130839
+ },
130840
+ ...["cursor/ask_question", "cursor/create_plan", "cursor/update_todos", "cursor/task", "cursor/generate_image"].map((name) => ({
130841
+ kind: "command",
130842
+ name,
130843
+ treatment: "blocked",
130844
+ measured: `${BUNDLE}: Cursor's own request to the client`,
130845
+ offeredWhenMeasured: true,
130846
+ note: name === "cursor/ask_question" ? "Answered method-not-found, so Cursor asks each question as a session/request_permission whose options are the answers (its own fallback). Question cards come in PR 2." : "Answered method-not-found; mapped in PR 2."
130847
+ })),
130848
+ {
130849
+ kind: "command",
130850
+ name: "cursor/list_available_models",
130851
+ treatment: "dropped",
130852
+ measured: "agent 2026.10.22-27dcf5f, logged in: answers every model with its options (effort as category thought_level, context and fast as model_config)",
130853
+ offeredWhenMeasured: true,
130854
+ note: "The model list comes in PR 2. Setting a model through session/set_config_option also writes it to ~/.cursor/acp-config.json (measured), which would change the default of every Cursor session the person runs, so PR 1 runs Cursor\u2019s default model."
130855
+ }
130856
+ ],
130857
+ baseline: SURFACE_BASELINE3
130858
+ };
130859
+
130860
+ // packages/agent-host/src/adapters/cursor-agent/index.ts
130861
+ var exec7 = promisify8(execFile9);
130862
+ var CAPABILITIES = {
130863
+ approvals: true,
130864
+ contextUsage: "none",
130865
+ resume: true,
130866
+ autoTitle: true,
130867
+ attachments: [],
130868
+ verbosities: [],
130869
+ exclusiveWriter: false,
130870
+ backgroundTasks: false,
130871
+ midTurnInput: false,
130872
+ gatesSteps: false,
130873
+ toolDeferral: false,
130874
+ instructionChannel: "server",
130875
+ processModel: "per-session",
130876
+ builtInSubagents: false,
130877
+ peerMessaging: false,
130878
+ sleep: false,
130879
+ presets: ["auto"],
130880
+ keepsRepoFilesOut: false,
130881
+ roleText: false
130882
+ };
130883
+ var DESCRIPTOR = {
130884
+ name: "cursor-agent",
130885
+ label: "Cursor",
130886
+ mark: "\u25B2",
130887
+ install: "curl https://cursor.com/install -fsS | bash (Windows: irm 'https://cursor.com/install?win32=true' | iex)",
130888
+ login: "agent login"
130889
+ };
130890
+ function centraluToolOfCall(call2) {
130891
+ if (call2.kind !== "other") return null;
130892
+ const m = /^([\w-]+): ([\w-]+)$/.exec(call2.title ?? "");
130893
+ return m?.[2] && m[1] === `${ORCHESTRATOR_MCP_NAME}-${m[2]}` ? m[2] : null;
130894
+ }
130895
+ var STATUS_FOR_MS = 6e4;
130896
+ function cursorProfile() {
130897
+ let status = null;
130898
+ return {
130899
+ descriptor: DESCRIPTOR,
130900
+ capabilities: CAPABILITIES,
130901
+ surface: CURSOR_SURFACE,
130902
+ async launch() {
130903
+ const install = findCursor();
130904
+ return install ? { command: install.command, args: [...install.args, "acp"], env: install.env } : null;
130905
+ },
130906
+ async installedVersion() {
130907
+ return findCursor()?.version ?? null;
130908
+ },
130909
+ centraluToolOf: centraluToolOfCall,
130910
+ async detect() {
130911
+ if (status && Date.now() - status.at < STATUS_FOR_MS) return status.result;
130912
+ const install = findCursor();
130913
+ let result;
130914
+ if (!install) {
130915
+ result = { tool: DESCRIPTOR.name, installed: false, loggedIn: false, detail: "Cursor CLI not found (install it from https://cursor.com/cli)" };
130916
+ } else {
130917
+ const out = await exec7(install.command, [...install.args, "status"], { timeout: 2e4, windowsHide: true, env: { ...process.env, ...install.env } }).then((r) => r.stdout, (e) => e.stdout ?? "");
130918
+ const loggedIn = /\bLogged in\b/.test(out) && !/Not logged in/i.test(out);
130919
+ result = { tool: DESCRIPTOR.name, installed: true, loggedIn, detail: loggedIn ? `agent ${install.version}` : `agent ${install.version} \xB7 login required` };
130920
+ }
130921
+ status = { at: Date.now(), result };
130922
+ return result;
130923
+ }
130924
+ };
130925
+ }
130926
+ var CursorAgentAdapter = class extends AcpAdapter {
130927
+ tool = "cursor-agent";
130928
+ constructor() {
130929
+ super(cursorProfile());
130930
+ }
130931
+ };
130932
+
130933
+ // packages/agent-host/src/adapters/opencode/index.ts
130934
+ import { execFile as execFile11 } from "node:child_process";
130935
+ import { promisify as promisify10 } from "node:util";
130936
+
130937
+ // packages/agent-host/src/adapters/opencode/config-files.ts
130938
+ import { existsSync as existsSync20, readdirSync as readdirSync13, readFileSync as readFileSync17 } from "node:fs";
130939
+ import { homedir as homedir13 } from "node:os";
130940
+ import { dirname as dirname24, extname as extname3, join as join48, relative as relative7 } from "node:path";
130941
+ function readOpenCodeConfigs(cwd, env, home = homedir13()) {
130942
+ const global2 = join48(env.XDG_CONFIG_HOME || join48(home, ".config"), "opencode");
130943
+ const project2 = !env.OPENCODE_DISABLE_PROJECT_CONFIG;
130944
+ const upward = project2 ? foldersUp(cwd) : [];
130945
+ const dotFolders = [...upward.map((d2) => join48(d2, ".opencode")), join48(home, ".opencode"), ...env.OPENCODE_CONFIG_DIR ? [env.OPENCODE_CONFIG_DIR] : []];
130946
+ const files = [
130947
+ ...["config.json", "opencode.json", "opencode.jsonc"].map((f) => join48(global2, f)),
130948
+ ...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : [],
130949
+ ...upward.flatMap((d2) => CONFIG_NAMES.map((f) => join48(d2, f))),
130950
+ ...dotFolders.flatMap((d2) => CONFIG_NAMES.map((f) => join48(d2, f))),
130951
+ ...CONFIG_NAMES.map((f) => join48(managedFolder(env), f))
130952
+ ];
130953
+ return {
130954
+ layers: files.map(readConfig).filter((c) => c !== null),
130955
+ agentFiles: [...new Set([global2, ...dotFolders].flatMap(agentNames))]
130956
+ };
130957
+ }
130958
+ var CONFIG_NAMES = ["opencode.json", "opencode.jsonc"];
130959
+ function foldersUp(cwd) {
130960
+ const out = [];
130961
+ for (let d2 = cwd; ; d2 = dirname24(d2)) {
130962
+ out.push(d2);
130963
+ if (existsSync20(join48(d2, ".git")) || dirname24(d2) === d2) return out;
130964
+ }
130965
+ }
130966
+ function managedFolder(env) {
130967
+ if (env.OPENCODE_TEST_MANAGED_CONFIG_DIR) return env.OPENCODE_TEST_MANAGED_CONFIG_DIR;
130968
+ return systemSettingsFolder("opencode", env);
130969
+ }
130970
+ function readConfig(path2) {
130971
+ try {
130972
+ const v = JSON.parse(withoutComments(readFileSync17(path2, "utf8")));
130973
+ return isObj(v) ? v : null;
130974
+ } catch {
130975
+ return null;
130976
+ }
130977
+ }
130978
+ function withoutComments(text2) {
130979
+ let out = "";
130980
+ for (let i = 0; i < text2.length; i++) {
130981
+ const c = text2[i];
130982
+ if (c === '"') {
130983
+ const end = stringEnd(text2, i);
130984
+ out += text2.slice(i, end);
130985
+ i = end - 1;
130986
+ } else if (c === "/" && text2[i + 1] === "/") {
130987
+ while (i < text2.length && text2[i] !== "\n") i++;
130988
+ out += "\n";
130989
+ } else if (c === "/" && text2[i + 1] === "*") {
130990
+ i = text2.indexOf("*/", i + 2);
130991
+ if (i === -1) break;
130992
+ i++;
130993
+ } else out += c;
130994
+ }
130995
+ return out.replace(/,(\s*[}\]])/g, "$1");
130996
+ }
130997
+ function stringEnd(text2, start2) {
130998
+ for (let i = start2 + 1; i < text2.length; i++) {
130999
+ if (text2[i] === "\\") i++;
131000
+ else if (text2[i] === '"') return i + 1;
131001
+ }
131002
+ return text2.length;
131003
+ }
131004
+ function agentNames(folder) {
131005
+ const named = (sub, deep) => markdown(join48(folder, sub), deep).map((f) => relative7(join48(folder, sub), f).replaceAll("\\", "/").slice(0, -extname3(f).length));
131006
+ return [...named("agent", true), ...named("agents", true), ...named("mode", false), ...named("modes", false)];
131007
+ }
131008
+ function markdown(folder, deep) {
131009
+ try {
131010
+ return readdirSync13(folder, { withFileTypes: true }).flatMap((e) => {
131011
+ const path2 = join48(folder, e.name);
131012
+ if (e.isDirectory()) return deep ? markdown(path2, deep) : [];
131013
+ return e.name.endsWith(".md") ? [path2] : [];
131014
+ });
131015
+ } catch {
131016
+ return [];
131017
+ }
131018
+ }
131019
+
131020
+ // packages/agent-host/src/adapters/opencode/config.ts
131021
+ function presetEnv(preset, env, configs, platform = HOST_PLATFORM) {
131022
+ if (preset === "auto") return {};
131023
+ const theirs = jsonObject(env.OPENCODE_CONFIG_CONTENT ?? "");
131024
+ const all = [...configs.layers, theirs, ...env.OPENCODE_PERMISSION === void 0 ? [] : [{ permission: jsonObject(env.OPENCODE_PERMISSION) }]];
131025
+ return holdEnv(preset === "safe" ? "safe" : "normal", theirs, all, configs.agentFiles, env.OPENCODE_PERMISSION, matcher(platform));
131026
+ }
131027
+ var NORMAL_ASKS = ["edit", "bash"];
131028
+ var SAFE_ASKS = ["edit", "bash", "webfetch", "websearch"];
131029
+ var NATIVE_AGENTS = ["build", "plan", "general", "explore"];
131030
+ var DEFAULT_ASKS = {
131031
+ read: [["*", "allow"], ["*.env", "ask"], ["*.env.*", "ask"], ["*.env.example", "allow"]],
131032
+ doom_loop: [["*", "ask"]]
131033
+ };
131034
+ function holdEnv(hold, theirs, all, agentFiles, permissionEnv, matches2) {
131035
+ const named = (group) => all.flatMap((c) => Object.keys(asObj(c[group])));
131036
+ const modes = new Set(named("mode"));
131037
+ const configured = [.../* @__PURE__ */ new Set([...named("agent"), ...modes])];
131038
+ const held3 = [.../* @__PURE__ */ new Set([...NATIVE_AGENTS, ...configured, ...agentFiles])];
131039
+ const primary = hold === "safe" ? "build" : [...all].reverse().map((c) => c.default_agent).find((a) => typeof a === "string") ?? "build";
131040
+ const entered = hold === "safe" ? held3 : [.../* @__PURE__ */ new Set([primary, ...NATIVE_AGENTS, ...configured])];
131041
+ const globalLevel = level(all);
131042
+ const levelOf = (name) => level([...all.map((c) => asObj(asObj(c.agent)[name])), ...all.map((c) => asObj(asObj(c.mode)[name]))]);
131043
+ const rulesFor = (levels, asks) => ({ ...ownRules(hold, levels, asks, matches2), ...taskRules(hold, levels, held3, primary, asks, matches2) });
131044
+ const globalRules = rulesFor([globalLevel], false);
131045
+ const entries = (group, names) => Object.fromEntries(
131046
+ names.map((name) => {
131047
+ const entry = asObj(asObj(theirs[group])[name]);
131048
+ return [name, { ...entry, permission: withRules(entry.permission, rulesFor([globalLevel, levelOf(name)], name === primary)) }];
131049
+ })
131050
+ );
131051
+ const config2 = {
131052
+ ...theirs,
131053
+ permission: withRules(theirs.permission, globalRules),
131054
+ agent: { ...asObj(theirs.agent), ...entries("agent", entered.filter((n) => !modes.has(n))) },
131055
+ ...entered.some((n) => modes.has(n)) ? { mode: { ...asObj(theirs.mode), ...entries("mode", entered.filter((n) => modes.has(n))) } } : {},
131056
+ ...hold === "safe" ? { default_agent: "build" } : {}
131057
+ };
131058
+ const out = { OPENCODE_CONFIG_CONTENT: JSON.stringify(config2) };
131059
+ if (permissionEnv !== void 0 && hold === "safe") out.OPENCODE_PERMISSION = JSON.stringify(withRules(jsonObject(permissionEnv), globalRules));
131060
+ return out;
131061
+ }
131062
+ function ownRules(hold, levels, asks, matches2) {
131063
+ const presetTools = hold === "safe" ? SAFE_ASKS : NORMAL_ASKS;
131064
+ const askedHere = levels.flatMap((l) => Object.entries(l).filter(([key, v]) => !/[*?]/.test(key) && key !== "task" && rulesOfValue(v).some(([, a]) => a === "ask")).map(([key]) => key));
131065
+ const tools = [.../* @__PURE__ */ new Set([...Object.keys(DEFAULT_ASKS), ...askedHere, ...presetTools])];
131066
+ return Object.fromEntries(
131067
+ tools.flatMap((tool) => {
131068
+ const theirs = levels.flatMap((l) => toolRules(l, tool, matches2));
131069
+ const rules = hold === "safe" && SAFE_ASKS.includes(tool) ? [["*", "ask"], ...theirs.filter(([, a]) => a === "deny")] : [...DEFAULT_ASKS[tool] ?? (presetTools.includes(tool) ? [["*", "ask"]] : []), ...theirs];
131070
+ const ordered = Object.entries(inOrder(rules));
131071
+ const first = ordered.findIndex(([, a]) => a === "ask");
131072
+ if (first < 0) return [];
131073
+ const fromAsk = ordered.slice(first);
131074
+ return [[ownKey(tool), Object.fromEntries(asks ? fromAsk : fromAsk.map(([p, a]) => [p, a === "ask" ? "deny" : a]))]];
131075
+ })
131076
+ );
131077
+ }
131078
+ function taskRules(hold, levels, held3, primary, asks, matches2) {
131079
+ const theirs = levels.flatMap((l) => toolRules(l, "task", matches2));
131080
+ const denies = theirs.filter(([, a]) => a === "deny");
131081
+ const base2 = hold === "safe" ? [["*", "ask"], ...denies.some(([p]) => p === "*") ? [] : held3.map((n) => [n, "allow"])] : theirs;
131082
+ const rules = [...base2, [primary, "deny"], ...denies];
131083
+ return { [ownKey("task")]: inOrder(asks ? rules : rules.map(([p, a]) => [p, a === "ask" ? "deny" : a])) };
131084
+ }
131085
+ var ownKey = (tool) => `${tool}*`;
131086
+ var withRules = (p, rules) => ({ ...Object.fromEntries(Object.entries(permissionOf(p)).filter(([k2]) => !(k2 in rules))), ...rules });
131087
+ var permissionOf = (p) => typeof p === "string" ? { "*": p } : asObj(p);
131088
+ function merge2(a, b2) {
131089
+ if (!isObj(a) || !isObj(b2)) return b2;
131090
+ const out = { ...a, ...b2 };
131091
+ for (const k2 of Object.keys(b2)) if (k2 in a && isObj(a[k2]) && isObj(b2[k2])) out[k2] = merge2(a[k2], b2[k2]);
131092
+ return out;
131093
+ }
131094
+ function level(cs2) {
131095
+ const tools = cs2.map((c) => c.tools).filter(isObj).reduce(merge2, {});
131096
+ const fromTools = Object.fromEntries(Object.entries(asObj(tools)).map(([t, on2]) => [["write", "patch"].includes(t) ? "edit" : t, on2 ? "allow" : "deny"]));
131097
+ const permission = cs2.flatMap((c) => c.permission === void 0 ? [] : [permissionOf(c.permission)]).reduce(merge2, {});
131098
+ return asObj(merge2(fromTools, permission));
131099
+ }
131100
+ var rulesOfValue = (v) => typeof v === "string" ? [["*", v]] : Object.entries(asObj(v)).flatMap(([p, a]) => typeof a === "string" ? [[p, a]] : []);
131101
+ var toolRules = (l, tool, matches2) => Object.entries(l).filter(([key]) => matches2(tool, key)).flatMap(([, v]) => rulesOfValue(v));
131102
+ function inOrder(rules) {
131103
+ const out = /* @__PURE__ */ new Map();
131104
+ for (const [pattern, action] of rules) {
131105
+ if (pattern === "*") out.clear();
131106
+ out.delete(pattern);
131107
+ out.set(pattern, action);
131108
+ }
131109
+ return Object.fromEntries(out);
131110
+ }
131111
+ function matcher(platform) {
131112
+ const flags = openCodeMatchesAnyCase(platform) ? "si" : "s";
131113
+ return (value, pattern) => {
131114
+ const re2 = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
131115
+ return new RegExp(`^${re2.endsWith(" .*") ? `${re2.slice(0, -3)}( .*)?` : re2}$`, flags).test(value);
131116
+ };
131117
+ }
131118
+
131119
+ // packages/agent-host/src/adapters/opencode/install.ts
131120
+ function findOpenCode(env = process.env) {
131121
+ const path2 = whichTool("opencode", env);
131122
+ return path2 ? launchFor(path2) : null;
131123
+ }
131124
+
131125
+ // packages/agent-host/src/adapters/opencode/surface.ts
131126
+ import { execFile as execFile10 } from "node:child_process";
131127
+ import { readFileSync as readFileSync18 } from "node:fs";
131128
+ import { promisify as promisify9 } from "node:util";
131129
+
131130
+ // packages/agent-host/src/adapters/opencode/surface-baseline.ts
131131
+ var SURFACE_BASELINE4 = {
131132
+ recorded: "2026-10-25",
131133
+ version: "opencode 1.19.4",
131134
+ offered: {
131135
+ command: [
131136
+ "acp",
131137
+ "agent",
131138
+ "attach",
131139
+ "completion",
131140
+ "db",
131141
+ "debug",
131142
+ "export",
131143
+ "github",
131144
+ "import",
131145
+ "mcp",
131146
+ "models",
131147
+ "plugin",
131148
+ "pr",
131149
+ "providers",
131150
+ "run",
131151
+ "serve",
131152
+ "session",
131153
+ "stats",
131154
+ "uninstall",
131155
+ "upgrade",
131156
+ "web"
131157
+ ],
131158
+ message: [
131159
+ "agent_message_chunk",
131160
+ "agent_thought_chunk",
131161
+ "available_commands_update",
131162
+ "config_option_update",
131163
+ "tool_call",
131164
+ "tool_call_update",
131165
+ "usage_update"
131166
+ ],
131167
+ capability: [
131168
+ "authMethods.opencode-login",
131169
+ "loadSession",
131170
+ "mcpCapabilities.http",
131171
+ "mcpCapabilities.sse",
131172
+ "promptCapabilities.embeddedContext",
131173
+ "promptCapabilities.image",
131174
+ "sessionCapabilities.close",
131175
+ "sessionCapabilities.fork",
131176
+ "sessionCapabilities.list",
131177
+ "sessionCapabilities.resume"
131178
+ ]
131179
+ }
131180
+ };
131181
+
131182
+ // packages/agent-host/src/adapters/opencode/surface.ts
131183
+ var exec8 = promisify9(execFile10);
131184
+ var Initialized2 = external_exports.looseObject({ agentCapabilities: external_exports.unknown().optional(), authMethods: external_exports.array(external_exports.looseObject({ id: external_exports.string() })).optional() });
131185
+ async function gather2() {
131186
+ const install = findOpenCode();
131187
+ if (!install) throw new Error("OpenCode is not installed on this machine (npm i -g opencode-ai)");
131188
+ const [{ stdout: version2 }, shown3] = await Promise.all([
131189
+ exec8(install.command, [...install.args, "--version"], { timeout: 3e4, windowsHide: true }),
131190
+ exec8(install.command, [...install.args, "--help"], { timeout: 3e4, windowsHide: true })
131191
+ ]);
131192
+ const help = shown3.stdout + shown3.stderr;
131193
+ const proc = spawnLocalAgent({ command: install.command, args: [...install.args, "acp"], cwd: process.cwd(), env: process.env });
131194
+ const conn = new AcpConnection(proc, { onNotification: () => {
131195
+ }, onRequest: async () => null, onExit: () => {
131196
+ } }, "opencode-surface");
131197
+ let init;
131198
+ try {
131199
+ init = await conn.request("initialize", { protocolVersion: 1, clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false }, clientInfo: { name: "centralu-surface", version: "1" } }, 6e4);
131200
+ } finally {
131201
+ await conn.dispose();
131202
+ }
131203
+ const names = new Set([...readFileSync18(install.command).toString("latin1").matchAll(/sessionUpdate:"([a-z_]+)"/g)].flatMap((m) => m[1] ? [m[1]] : []));
131204
+ return { version: version2.trim(), help, initialize: JSON.stringify(init), bundle: JSON.stringify([...names].sort()) };
131205
+ }
131206
+ function capabilityNames2(value, prefix = "") {
131207
+ if (!value || typeof value !== "object" || Array.isArray(value)) return prefix ? [prefix] : [];
131208
+ const entries = Object.entries(asObj(value));
131209
+ if (entries.length === 0) return prefix ? [prefix] : [];
131210
+ return entries.flatMap(([k2, v]) => typeof v === "object" && v !== null ? capabilityNames2(v, prefix ? `${prefix}.${k2}` : k2) : [prefix ? `${prefix}.${k2}` : k2]);
131211
+ }
131212
+ function readOpenCodeSurface(inputs) {
131213
+ const init = Initialized2.parse(JSON.parse(inputs.initialize ?? "{}"));
131214
+ const commands = [...(inputs.help ?? "").matchAll(/^ {2}opencode ([a-z][\w-]*)/gm)].flatMap((m) => m[1] ? [m[1]] : []);
131215
+ return {
131216
+ version: `opencode ${inputs.version ?? "unknown"}`,
131217
+ offered: {
131218
+ command: [...new Set(commands)].sort(),
131219
+ message: external_exports.array(external_exports.string()).parse(JSON.parse(inputs.bundle ?? "[]")).sort(),
131220
+ capability: [...capabilityNames2(init.agentCapabilities), ...(init.authMethods ?? []).map((a) => `authMethods.${a.id}`)].sort()
131221
+ }
131222
+ };
131223
+ }
131224
+ var MEASURED = "opencode 1.19.4, 2026-10-26";
131225
+ var OPENCODE_SURFACE = {
131226
+ gather: gather2,
131227
+ read: readOpenCodeSurface,
131228
+ limitations: [
131229
+ {
131230
+ kind: "message",
131231
+ name: "compaction_update",
131232
+ treatment: "worked-around",
131233
+ measured: `${MEASURED}: /compact ran with the client advertising session.compaction and sent no compaction frame, only text and a smaller usage_update`,
131234
+ offeredWhenMeasured: false,
131235
+ note: "Its compaction is read from the compact turn Centralu started (infersCompaction, \xA75.6): ended end_turn with a smaller reading. When this kind appears, the frames can replace the inference."
131236
+ },
131237
+ {
131238
+ kind: "capability",
131239
+ name: "authMethods.opencode-login",
131240
+ treatment: "blocked",
131241
+ measured: `${MEASURED}: session/new works without authenticate (its own free models), and a provider's key is the person's OpenCode settings`,
131242
+ offeredWhenMeasured: true,
131243
+ note: "Never called: logging in stays `opencode auth login` in a terminal."
131244
+ },
131245
+ {
131246
+ kind: "capability",
131247
+ name: "sessionCapabilities.resume",
131248
+ treatment: "dropped",
131249
+ measured: `${MEASURED}: session/load answered in about 1 s, session/resume in about 45 s`,
131250
+ offeredWhenMeasured: true,
131251
+ note: "A conversation is reopened with session/load, whose replay is dropped."
131252
+ },
131253
+ {
131254
+ kind: "capability",
131255
+ name: "sessionCapabilities.fork",
131256
+ treatment: "dropped",
131257
+ measured: MEASURED,
131258
+ offeredWhenMeasured: true,
131259
+ note: "Forks start a new conversation with the handoff, as for any tool, until a fork maps onto session/fork."
131260
+ },
131261
+ {
131262
+ kind: "message",
131263
+ name: "config_option_update",
131264
+ treatment: "dropped",
131265
+ measured: MEASURED,
131266
+ offeredWhenMeasured: true,
131267
+ note: "The model and mode options are not offered in the settings menu yet; the person\u2019s OpenCode default model runs."
131268
+ }
131269
+ ],
131270
+ baseline: SURFACE_BASELINE4
131271
+ };
131272
+
131273
+ // packages/agent-host/src/adapters/opencode/index.ts
131274
+ var exec9 = promisify10(execFile11);
131275
+ var CAPABILITIES2 = {
131276
+ approvals: true,
131277
+ contextUsage: "exact",
131278
+ resume: true,
131279
+ autoTitle: true,
131280
+ attachments: [],
131281
+ verbosities: [],
131282
+ exclusiveWriter: false,
131283
+ backgroundTasks: false,
131284
+ midTurnInput: false,
131285
+ gatesSteps: false,
131286
+ toolDeferral: false,
131287
+ instructionChannel: "server",
131288
+ processModel: "per-session",
131289
+ builtInSubagents: false,
131290
+ peerMessaging: false,
131291
+ sleep: false,
131292
+ keepsRepoFilesOut: false,
131293
+ roleText: false
131294
+ };
131295
+ var DESCRIPTOR2 = {
131296
+ name: "opencode",
131297
+ label: "OpenCode",
131298
+ mark: "O",
131299
+ install: "npm i -g opencode-ai (or: curl -fsSL https://opencode.ai/install | bash)",
131300
+ login: "opencode auth login"
131301
+ };
131302
+ var VERSION_FOR_MS = 6e4;
131303
+ function openCodeProfile() {
131304
+ let known = null;
131305
+ const version2 = async () => {
131306
+ if (known && Date.now() - known.at < VERSION_FOR_MS) return known.version;
131307
+ const l = findOpenCode();
131308
+ const v = l ? await exec9(l.command, [...l.args, "--version"], { timeout: 2e4, windowsHide: true }).then((r) => r.stdout.trim() || null, () => null) : null;
131309
+ known = { at: Date.now(), version: v };
131310
+ return v;
131311
+ };
131312
+ return {
131313
+ descriptor: DESCRIPTOR2,
131314
+ capabilities: CAPABILITIES2,
131315
+ surface: OPENCODE_SURFACE,
131316
+ infersCompaction: true,
131317
+ async launch(opts) {
131318
+ const l = findOpenCode();
131319
+ return l ? { command: l.command, args: [...l.args, "acp"], env: presetEnv(opts.permissionPreset, process.env, readOpenCodeConfigs(opts.cwd, process.env)) } : null;
131320
+ },
131321
+ installedVersion: version2,
131322
+ async detect() {
131323
+ if (!findOpenCode()) return { tool: DESCRIPTOR2.name, installed: false, loggedIn: false, detail: "OpenCode not found (npm i -g opencode-ai)" };
131324
+ const v = await version2();
131325
+ return { tool: DESCRIPTOR2.name, installed: true, loggedIn: true, detail: v ? `opencode ${v}` : "opencode" };
131326
+ }
131327
+ };
131328
+ }
131329
+ var OpenCodeAdapter = class extends AcpAdapter {
131330
+ tool = "opencode";
131331
+ constructor() {
131332
+ super(openCodeProfile());
131333
+ }
131334
+ };
131335
+
129261
131336
  // packages/agent-host/src/adapters/registry.ts
129262
131337
  function createAdapters() {
129263
- const all = [new ClaudeAdapter(), new CodexAdapter()];
131338
+ const all = [new ClaudeAdapter(), new CodexAdapter(), new CursorAgentAdapter(), new OpenCodeAdapter()];
129264
131339
  return new Map(all.map((a) => [a.tool, a]));
129265
131340
  }
129266
131341
  function registeredTool(adapters2, preferred) {
@@ -129306,6 +131381,14 @@ var ToolRegistry = class {
129306
131381
  }
129307
131382
  return this.firstTool(this.host.store.settings.appSetting("orchestrator_tool"));
129308
131383
  }
131384
+ /**
131385
+ * Why `tool` cannot run a session that demands this (`launchRefusal`), or null: the one rule, for every path that puts
131386
+ * a tool on a session (`tool-fit.ts`). A tool this host does not run is `toolProblem`'s to say
131387
+ */
131388
+ refusal(tool, demands) {
131389
+ const adapter = this.host.adapters.get(tool);
131390
+ return adapter ? launchRefusal(adapter.capabilities, adapter.descriptor.label, demands) : null;
131391
+ }
129309
131392
  /** Why `tool` cannot start a session here now (not registered, not installed, not logged in), or null when it can */
129310
131393
  async toolProblem(tool) {
129311
131394
  const adapter = this.host.adapters.get(tool);
@@ -129351,13 +131434,13 @@ var ApprovalRules = class {
129351
131434
  * restart (C-2). `matcher` is the card's own key, which the caller has checked (`respondApproval`).
129352
131435
  * A session with no project (#994) has no project to remember for: its project answer is kept for the session.
129353
131436
  */
129354
- rememberAlways(sessionId, sessionProjectId, scope, matcher) {
131437
+ rememberAlways(sessionId, sessionProjectId, scope, matcher2) {
129355
131438
  const projectId = scope === "project" ? sessionProjectId : null;
129356
131439
  this.host.store.permissions.addApprovalRule({
129357
131440
  scope: projectId !== null ? "project" : "session",
129358
131441
  sessionId: projectId !== null ? void 0 : sessionId,
129359
131442
  projectId: projectId ?? void 0,
129360
- matcher,
131443
+ matcher: matcher2,
129361
131444
  decision: "allow"
129362
131445
  });
129363
131446
  }
@@ -130331,6 +132414,8 @@ var OrchestratorSlot = class {
130331
132414
  * session's own settings takes over — this value is never read again after that.
130332
132415
  */
130333
132416
  configureOrchestrator(tool) {
132417
+ const refused2 = this.host.refusal(tool, ORCHESTRATOR_DEMANDS);
132418
+ if (refused2) throw unfitError(refused2);
130334
132419
  this.host.store.settings.setAppSetting("orchestrator_tool", tool);
130335
132420
  }
130336
132421
  /** The orchestrator being created (#839): a second ask while it is made waits on this one, never makes another */
@@ -130367,7 +132452,7 @@ var OrchestratorSlot = class {
130367
132452
  kind: "orchestrator",
130368
132453
  cwd: orchestratorHome(),
130369
132454
  tool: this.host.firstTool(configured),
130370
- permissionPreset: "normal"
132455
+ permissionPreset: ORCHESTRATOR_DEMANDS.permissionPreset
130371
132456
  });
130372
132457
  this.host.rename(info.id, "Orchestrator");
130373
132458
  return this.host.meta.get(info.id);
@@ -130487,6 +132572,8 @@ var SessionSettings = class {
130487
132572
  host;
130488
132573
  async updateSettings(sessionId, s, by = "person") {
130489
132574
  const m = this.host.openSession(sessionId);
132575
+ const refused2 = s.permissionPreset ? this.host.toolRefusal(m, m.tool, s.permissionPreset) : null;
132576
+ if (refused2) throw unfitError(refused2);
130490
132577
  if (s.model !== void 0 && s.model !== m.model) this.host.cacheLedger.note(sessionId, "model_change");
130491
132578
  if (s.model !== void 0) m.model = s.model;
130492
132579
  if (s.effort !== void 0) m.effort = s.effort;
@@ -130998,8 +133085,8 @@ var UsageRecorder = class {
130998
133085
  };
130999
133086
 
131000
133087
  // packages/agent-host/src/sessions/manager.ts
131001
- import { homedir as homedir14 } from "node:os";
131002
- import { join as join46 } from "node:path";
133088
+ import { homedir as homedir15 } from "node:os";
133089
+ import { join as join51 } from "node:path";
131003
133090
 
131004
133091
  // packages/agent-host/src/dev-services/store.ts
131005
133092
  import Database2 from "better-sqlite3";
@@ -131016,7 +133103,7 @@ function pragmaNumber(db, name) {
131016
133103
  }
131017
133104
 
131018
133105
  // packages/agent-host/src/dev-services/store.ts
131019
- import { existsSync as existsSync19, readFileSync as readFileSync16 } from "node:fs";
133106
+ import { existsSync as existsSync22, readFileSync as readFileSync19 } from "node:fs";
131020
133107
  import { fileURLToPath as fileURLToPath4 } from "node:url";
131021
133108
 
131022
133109
  // packages/agent-host/src/dev-services/store/app-runs.ts
@@ -131626,8 +133713,10 @@ var ChildOwnershipRecords = class {
131626
133713
  * stored request names its caller too). A crash before the commit leaves every row on the original; after it, every
131627
133714
  * row on the new session, so the next host (a swap or a restart) reads one owner either way. `armReports`: children
131628
133715
  * whose report the original was waiting for in a call (its wait had taken the mark down), now owed to the new session.
133716
+ * `then`: the rest of the handoff's move that is stored elsewhere (the mail, `MailService.handOver`), run last in the same
133717
+ * transaction, so a refusal there leaves every row of both on the original.
131629
133718
  */
131630
- moveChildren(from, to, move) {
133719
+ moveChildren(from, to, move, then) {
131631
133720
  this.db.transaction(() => {
131632
133721
  const owner = this.db.prepare(
131633
133722
  `UPDATE sessions SET
@@ -131650,6 +133739,7 @@ var ChildOwnershipRecords = class {
131650
133739
  ON CONFLICT(session_id) DO UPDATE SET caller_session_id = excluded.caller_session_id, marked_at = excluded.marked_at`
131651
133740
  );
131652
133741
  for (const id3 of move.armReports ?? []) arm.run(id3, to, Date.now());
133742
+ then?.();
131653
133743
  })();
131654
133744
  }
131655
133745
  };
@@ -132180,7 +134270,7 @@ var UserAppAdoptionRecords = class {
132180
134270
 
132181
134271
  // packages/agent-host/src/dev-services/store/migrate-runner.ts
132182
134272
  import Database from "better-sqlite3";
132183
- import { existsSync as existsSync18 } from "node:fs";
134273
+ import { existsSync as existsSync21 } from "node:fs";
132184
134274
 
132185
134275
  // packages/agent-host/src/dev-services/store/store-keys.ts
132186
134276
  var APP_SETTINGS_DDL = `
@@ -132524,7 +134614,7 @@ var MigrationRunner = class extends ReaderGate {
132524
134614
  const probe = bare2;
132525
134615
  const steps = probe.migrationSteps();
132526
134616
  const known = steps.at(-1)?.to ?? 0;
132527
- if (dbPath2 === ":memory:" || !existsSync18(dbPath2)) return empty(known);
134617
+ if (dbPath2 === ":memory:" || !existsSync21(dbPath2)) return empty(known);
132528
134618
  const db = new Database(dbPath2, { readonly: true, fileMustExist: true });
132529
134619
  try {
132530
134620
  probe.db = db;
@@ -133415,11 +135505,11 @@ var StepsV28ToV40 = class extends MigrationRange {
133415
135505
  );
133416
135506
  `);
133417
135507
  const insert = this.db.prepare(`INSERT INTO messages_fts (rowid, body, session_id, seq) VALUES (?, ?, ?, ?)`);
133418
- const pick3 = this.db.prepare(
135508
+ const pick4 = this.db.prepare(
133419
135509
  `SELECT rowid, session_id, seq, kind, payload FROM messages
133420
135510
  WHERE kind IN ${kinds} AND session_id NOT IN (SELECT id FROM sessions WHERE deleted_at IS NOT NULL)`
133421
135511
  );
133422
- for (const r of pick3.all()) {
135512
+ for (const r of pick4.all()) {
133423
135513
  const body = indexedText(r.kind, r.payload);
133424
135514
  if (!body) continue;
133425
135515
  insert.run(r.rowid, body, r.session_id, r.seq);
@@ -134935,7 +137025,7 @@ var TrashRecords = class {
134935
137025
  */
134936
137026
  async purgeSession(sessionId, chunk = DELETE_CHUNK) {
134937
137027
  if (!this.isTrashed(sessionId)) return false;
134938
- const pick3 = this.db.prepare(`SELECT rowid FROM messages WHERE session_id = ? LIMIT ?`);
137028
+ const pick4 = this.db.prepare(`SELECT rowid FROM messages WHERE session_id = ? LIMIT ?`);
134939
137029
  const dropFts = this.db.prepare(`DELETE FROM messages_fts WHERE rowid = ?`);
134940
137030
  const dropMsg = this.db.prepare(`DELETE FROM messages WHERE rowid = ?`);
134941
137031
  const pickSub = this.db.prepare(`SELECT rowid FROM subagent_messages WHERE session_id = ? LIMIT ?`);
@@ -134944,7 +137034,7 @@ var TrashRecords = class {
134944
137034
  const subs = pickSub.all(sessionId, chunk);
134945
137035
  for (const { rowid } of subs) dropSub.run(rowid);
134946
137036
  if (subs.length === chunk) return false;
134947
- const rows3 = pick3.all(sessionId, chunk);
137037
+ const rows3 = pick4.all(sessionId, chunk);
134948
137038
  for (const { rowid } of rows3) {
134949
137039
  dropFts.run(rowid);
134950
137040
  dropMsg.run(rowid);
@@ -134975,12 +137065,12 @@ var TrashRecords = class {
134975
137065
  }
134976
137066
  /** One session's messages in seq order, one transaction per chunk, letting the event loop go between chunks (#179) */
134977
137067
  async walkMessages(sessionId, chunk, withPayload, each2) {
134978
- const pick3 = this.db.prepare(
137068
+ const pick4 = this.db.prepare(
134979
137069
  `SELECT rowid, seq${withPayload ? ", kind, payload" : ""} FROM messages WHERE session_id = ? AND seq > ? ORDER BY seq LIMIT ?`
134980
137070
  );
134981
137071
  let after = Number.MIN_SAFE_INTEGER;
134982
137072
  const step = this.db.transaction(() => {
134983
- const rows3 = pick3.all(sessionId, after, chunk);
137073
+ const rows3 = pick4.all(sessionId, after, chunk);
134984
137074
  each2(rows3);
134985
137075
  const last = rows3.at(-1);
134986
137076
  if (last) after = last.seq;
@@ -135156,14 +137246,14 @@ var SubagentStepRecords = class {
135156
137246
  this.db.prepare(`SELECT MAX(trashed_at) as prev FROM subagent_messages WHERE session_id = ? AND trashed_at IS NOT NULL`).get(sessionId)
135157
137247
  );
135158
137248
  const at2 = prev !== null && prev >= now ? prev + 1 : now;
135159
- const pick3 = this.db.prepare(
137249
+ const pick4 = this.db.prepare(
135160
137250
  `SELECT rowid, LENGTH(CAST(payload AS BLOB)) as bytes FROM subagent_messages
135161
137251
  WHERE session_id = ? AND trashed_at IS NULL AND rowid <= ? LIMIT ?`
135162
137252
  );
135163
137253
  const mark = this.db.prepare(`UPDATE subagent_messages SET trashed_at = ? WHERE rowid = ?`);
135164
137254
  const moved = { steps: 0, bytes: 0 };
135165
137255
  await this.inChunks(() => {
135166
- const rows3 = pick3.all(sessionId, last, chunk);
137256
+ const rows3 = pick4.all(sessionId, last, chunk);
135167
137257
  for (const r of rows3) {
135168
137258
  mark.run(at2, r.rowid);
135169
137259
  moved.steps++;
@@ -135176,10 +137266,10 @@ var SubagentStepRecords = class {
135176
137266
  /** Puts a live session's trashed steps back under their cards. False when it had none in the trash */
135177
137267
  async restore(sessionId, chunk = CHUNK) {
135178
137268
  if (!this.hasTrashed(sessionId)) return false;
135179
- const pick3 = this.db.prepare(`SELECT rowid FROM subagent_messages WHERE session_id = ? AND trashed_at IS NOT NULL LIMIT ?`);
137269
+ const pick4 = this.db.prepare(`SELECT rowid FROM subagent_messages WHERE session_id = ? AND trashed_at IS NOT NULL LIMIT ?`);
135180
137270
  const unmark = this.db.prepare(`UPDATE subagent_messages SET trashed_at = NULL WHERE rowid = ?`);
135181
137271
  await this.inChunks(() => {
135182
- const rows3 = pick3.all(sessionId, chunk);
137272
+ const rows3 = pick4.all(sessionId, chunk);
135183
137273
  for (const r of rows3) unmark.run(r.rowid);
135184
137274
  return rows3.length < chunk;
135185
137275
  });
@@ -135193,12 +137283,12 @@ var SubagentStepRecords = class {
135193
137283
  */
135194
137284
  async purge(sessionId, upTo, chunk = CHUNK) {
135195
137285
  if (!this.hasTrashed(sessionId, upTo)) return false;
135196
- const pick3 = this.db.prepare(
137286
+ const pick4 = this.db.prepare(
135197
137287
  `SELECT rowid FROM subagent_messages WHERE session_id = ? AND trashed_at IS NOT NULL AND trashed_at <= ? LIMIT ?`
135198
137288
  );
135199
137289
  const drop = this.db.prepare(`DELETE FROM subagent_messages WHERE rowid = ?`);
135200
137290
  await this.inChunks(() => {
135201
- const rows3 = pick3.all(sessionId, upTo, chunk);
137291
+ const rows3 = pick4.all(sessionId, upTo, chunk);
135202
137292
  for (const r of rows3) drop.run(r.rowid);
135203
137293
  return rows3.length < chunk;
135204
137294
  });
@@ -135236,7 +137326,7 @@ function resolveSchemaPath() {
135236
137326
  new URL("../../../protocol/src/schema/schema.sql", import.meta.url)
135237
137327
  // source tree
135238
137328
  ].map((u) => fileURLToPath4(u));
135239
- const found = candidates.find((p) => existsSync19(p));
137329
+ const found = candidates.find((p) => existsSync22(p));
135240
137330
  if (!found) throw new Error(`schema.sql not found: ${candidates.join(", ")}`);
135241
137331
  return found;
135242
137332
  }
@@ -135271,7 +137361,7 @@ var Store = class extends MigrationRunner {
135271
137361
  this.db.pragma("synchronous = FULL");
135272
137362
  this.db.pragma("busy_timeout = 5000");
135273
137363
  this.db.pragma(`journal_size_limit = ${WAL_SIZE_LIMIT}`);
135274
- this.db.exec(readFileSync16(SCHEMA_PATH, "utf8"));
137364
+ this.db.exec(readFileSync19(SCHEMA_PATH, "utf8"));
135275
137365
  this.migrate();
135276
137366
  } catch (e) {
135277
137367
  this.db.close();
@@ -135513,7 +137603,7 @@ async function gitBranchPr(projectCwd, branch, signal, timeoutMs = OK2.timeout)
135513
137603
 
135514
137604
  // packages/agent-host/src/dev-services/file-search.ts
135515
137605
  import { readdir as readdir11 } from "node:fs/promises";
135516
- import { join as join44 } from "node:path";
137606
+ import { join as join49 } from "node:path";
135517
137607
  var MAX_FILES2 = 2e4;
135518
137608
  var TTL_MS2 = 15e3;
135519
137609
  var WALK_DEPTH = 6;
@@ -135551,7 +137641,7 @@ async function walk(root3) {
135551
137641
  if (e.name.startsWith(".") && e.name !== ".env") continue;
135552
137642
  if (e.isDirectory()) {
135553
137643
  if (SKIP.has(e.name) || depth >= WALK_DEPTH) continue;
135554
- queue.push({ dir: join44(dir, e.name), rel: wireJoin(rel, e.name), depth: depth + 1 });
137644
+ queue.push({ dir: join49(dir, e.name), rel: wireJoin(rel, e.name), depth: depth + 1 });
135555
137645
  } else {
135556
137646
  out.push(wireJoin(rel, e.name).normalize("NFC"));
135557
137647
  if (out.length >= MAX_FILES2) break;
@@ -136639,8 +138729,8 @@ var HostCards = class {
136639
138729
  // packages/agent-host/src/dev-services/message-image.ts
136640
138730
  import { constants as constants8 } from "node:fs";
136641
138731
  import { open as open5, realpath as realpath9 } from "node:fs/promises";
136642
- import { homedir as homedir13 } from "node:os";
136643
- import { posix as posix3, sep as sep10, win32 as win327 } from "node:path";
138732
+ import { homedir as homedir14 } from "node:os";
138733
+ import { posix as posix3, sep as sep10, win32 as win328 } from "node:path";
136644
138734
  import { fileURLToPath as fileURLToPath5 } from "node:url";
136645
138735
  var OPEN_FLAGS = constants8.O_RDONLY | constants8.O_NONBLOCK | (constants8.O_NOFOLLOW ?? 0);
136646
138736
  var MESSAGE_IMAGE_KINDS = "PNG, JPEG, GIF or WebP";
@@ -136675,7 +138765,7 @@ function mentions(text2, written) {
136675
138765
  }
136676
138766
  return false;
136677
138767
  }
136678
- function resolveWrittenPath(written, cwd, home = homedir13(), p = sep10 === "\\" ? win327 : posix3) {
138768
+ function resolveWrittenPath(written, cwd, home = homedir14(), p = sep10 === "\\" ? win328 : posix3) {
136679
138769
  const windows2 = p.sep === "\\";
136680
138770
  let abs;
136681
138771
  if (/^file:/i.test(written)) {
@@ -136742,13 +138832,13 @@ async function readMessageImage(abs, max = IMAGE_PREVIEW_MAX_BYTES) {
136742
138832
 
136743
138833
  // packages/agent-host/src/dev-services/attachments.ts
136744
138834
  import { lstat as lstat9, mkdir as mkdir6, readdir as readdir12, rm as rm8, writeFile as writeFile4 } from "node:fs/promises";
136745
- import { extname as extname3, join as join45 } from "node:path";
136746
- var root2 = () => join45(dataRoot(), "attachments");
138835
+ import { extname as extname4, join as join50 } from "node:path";
138836
+ var root2 = () => join50(dataRoot(), "attachments");
136747
138837
  function sessionDir(sessionId) {
136748
138838
  if (!isSessionId(sessionId)) {
136749
138839
  throw rpcError(`Not a session id: ${sessionId}`);
136750
138840
  }
136751
- return join45(root2(), sessionId);
138841
+ return join50(root2(), sessionId);
136752
138842
  }
136753
138843
  var EXT = {
136754
138844
  "image/png": ".png",
@@ -136759,8 +138849,8 @@ var EXT = {
136759
138849
  async function saveAttachment(sessionId, name, mime, dataBase64) {
136760
138850
  const dir = sessionDir(sessionId);
136761
138851
  await mkdir6(dir, { recursive: true });
136762
- const ext2 = extname3(name) || EXT[mime] || "";
136763
- const file2 = join45(dir, `${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext2}`);
138852
+ const ext2 = extname4(name) || EXT[mime] || "";
138853
+ const file2 = join50(dir, `${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext2}`);
136764
138854
  const buf = Buffer.from(dataBase64, "base64");
136765
138855
  await writeFile4(file2, buf);
136766
138856
  return { kind: mime.startsWith("image/") ? "image" : "file", path: file2, name, mime, bytes: buf.length };
@@ -136781,10 +138871,10 @@ async function sweepAttachments(maxBytes = ATTACHMENTS_MAX_BYTES) {
136781
138871
  return 0;
136782
138872
  }
136783
138873
  for (const d2 of dirs) {
136784
- const dir = join45(root2(), d2);
138874
+ const dir = join50(root2(), d2);
136785
138875
  const names = await readdir12(dir).catch(() => []);
136786
138876
  for (const name of names) {
136787
- const p = join45(dir, name);
138877
+ const p = join50(dir, name);
136788
138878
  const s = await lstat9(p).catch(() => null);
136789
138879
  if (s?.isFile()) files.push({ path: p, size: s.size, mtime: s.mtimeMs });
136790
138880
  }
@@ -137230,10 +139320,10 @@ var ToolQueries = class {
137230
139320
  import { rmSync as rmSync6 } from "node:fs";
137231
139321
 
137232
139322
  // packages/agent-host/src/sessions/app-builder.ts
137233
- import { relative as relative7 } from "node:path";
139323
+ import { relative as relative8 } from "node:path";
137234
139324
  function builderRole(app2, cwd) {
137235
139325
  const name = JSON.stringify(frameField(app2.name ?? app2.appId));
137236
- const rel = relative7(cwd, app2.dir) || ".";
139326
+ const rel = relative8(cwd, app2.dir) || ".";
137237
139327
  const where2 = app2.projectId === null ? `This is a user-folder app (used across several projects). Your working folder is the app folder: ${app2.dir}` : app2.root ? `This project is the app: its manifest stands at the project's root, so your working folder is the app folder: ${app2.dir}` : `This is this project's app (committed to the repository and shared with the team). App folder: ${rel}/ (${app2.dir})`;
137238
139328
  const idRule = app2.root ? "whose id names the app" : "whose id matches the folder name";
137239
139329
  return `You are the session that builds the Centralu app ${name} (id ${app2.appId}). ${where2}
@@ -137687,7 +139777,7 @@ async function gitStatusForApp(summary, files) {
137687
139777
  // packages/agent-host/src/sessions/session-trash.ts
137688
139778
  import { basename as basename12 } from "node:path";
137689
139779
  import { rm as rm9 } from "node:fs/promises";
137690
- import { existsSync as existsSync20, statSync as statSync14 } from "node:fs";
139780
+ import { existsSync as existsSync23, statSync as statSync14 } from "node:fs";
137691
139781
  var SessionTrash = class {
137692
139782
  constructor(host) {
137693
139783
  this.host = host;
@@ -137815,8 +139905,35 @@ var SessionTrash = class {
137815
139905
  if (this.host.readGrants.get(sessionId)?.size || this.host.delegationFlow.starts.agentConsents.has(sessionId)) {
137816
139906
  out.push("What the person allowed it (reading other sessions, starting agents) is dropped; a restored session asks again.");
137817
139907
  }
137818
- const mail2 = this.host.mailWork(sessionId);
137819
- if (!mail2) out.push("Its mailbox could not be read, so what it holds there is not known.");
139908
+ const heir = this.host.handoffHeir(sessionId);
139909
+ const plan = heir ? this.host.mailHandOverPreview(sessionId, heir) : null;
139910
+ const mail2 = heir && plan ? null : this.host.mailWork(sessionId);
139911
+ const listed2 = (list) => list.slice(0, 3).map((x) => plainQuoted(x.subject, 60)).join(", ") + (list.length > 3 ? ", \u2026" : "");
139912
+ const threads = (n, what = "unfinished mail thread") => n === 1 ? `${/^[aeiou]/.test(what) ? "an" : "a"} ${what}` : `${n} ${what}s`;
139913
+ if (heir && plan) {
139914
+ const goes = plan.moved;
139915
+ if (goes.length) {
139916
+ const of2 = (role) => goes.filter((g5) => g5.role === role).length;
139917
+ const parts = [
139918
+ ...of2("assignee") ? [`is the assignee of ${threads(of2("assignee"))}`] : [],
139919
+ ...of2("owner") ? [`owns ${threads(of2("owner"))}`] : [],
139920
+ ...of2("member") ? [`is in ${threads(of2("member"))} as a member`] : []
139921
+ ];
139922
+ const joined = parts.length > 1 ? `${parts.slice(0, -1).join(", ")} and ${parts.at(-1)}` : parts[0];
139923
+ out.push(`It ${joined} (${listed2(goes)}); ${goes.length === 1 ? "it goes" : "they go"} to ${name(heir)}, which took over from it, and ${goes.length === 1 ? "stays" : "stay"} there if it is restored.`);
139924
+ }
139925
+ if (plan.closed.length) {
139926
+ out.push(`It has unread mail in ${threads(plan.closed.length, "closed mail thread")} (${listed2(plan.closed)}); that mail goes to ${name(heir)}.`);
139927
+ }
139928
+ const ownedLeft = plan.left.filter((l) => l.role === "owner");
139929
+ if (ownedLeft.length) {
139930
+ out.push(`It owns ${threads(ownedLeft.length)} whose assignee is ${name(heir)} (${listed2(ownedLeft)}); ${ownedLeft.length === 1 ? "it becomes" : "they become"} the person's.`);
139931
+ }
139932
+ const assignedLeft = plan.left.filter((l) => l.role === "assignee");
139933
+ if (assignedLeft.length) {
139934
+ out.push(`It is the assignee of ${threads(assignedLeft.length)} ${name(heir)} owns (${listed2(assignedLeft)}); ${assignedLeft.length === 1 ? "it goes" : "they go"} back to open with nobody on ${assignedLeft.length === 1 ? "it" : "them"}.`);
139935
+ }
139936
+ } else if (!mail2) out.push("Its mailbox could not be read, so what it holds there is not known.");
137820
139937
  else {
137821
139938
  const subjects = (list) => list.slice(0, 3).map((x) => plainQuoted(x, 60)).join(", ") + (list.length > 3 ? ", \u2026" : "");
137822
139939
  if (mail2.assigned.length) {
@@ -137965,7 +140082,7 @@ var SessionTrash = class {
137965
140082
  bytes: r.bytes + files,
137966
140083
  conversationFile: !r.hasConversationFile ? "none" : record2.removeExternal ? "remove" : "keep",
137967
140084
  // Only a worktree still on disk is listed — the merged-worktree cleanup removes the folder before the session archive is emptied
137968
- worktree: r.worktree && existsSync20(r.worktree.path) ? { ...r.worktree, remove: record2.removeWorktree } : null
140085
+ worktree: r.worktree && existsSync23(r.worktree.path) ? { ...r.worktree, remove: record2.removeWorktree } : null
137969
140086
  };
137970
140087
  })
137971
140088
  );
@@ -138037,7 +140154,7 @@ var SessionTrash = class {
138037
140154
  const home = projects.find((p) => p.id === record2.projectId) ?? (record2.projectPath ? projects.find((p) => p.path === record2.projectPath) : void 0);
138038
140155
  if (home) {
138039
140156
  projectId = home.id;
138040
- } else if (record2.projectPath && existsSync20(record2.projectPath) && statSync14(record2.projectPath).isDirectory()) {
140157
+ } else if (record2.projectPath && existsSync23(record2.projectPath) && statSync14(record2.projectPath).isDirectory()) {
138041
140158
  this.host.store.projects.addProject({
138042
140159
  id: record2.projectId,
138043
140160
  path: record2.projectPath,
@@ -138106,8 +140223,8 @@ var SessionTrash = class {
138106
140223
  const path2 = s.worktree.path;
138107
140224
  const repo = record2.projectPath;
138108
140225
  await gitWorktreeRemove(repo, path2, true).catch((e) => {
138109
- if (!existsSync20(path2)) return;
138110
- if (!existsSync20(repo)) {
140226
+ if (!existsSync23(path2)) return;
140227
+ if (!existsSync23(repo)) {
138111
140228
  throw rpcError(`The project folder ${repo} is not there any more (moved or deleted), so git cannot remove its worktree at ${path2}. It stays in the session archive: delete that folder yourself, then delete this entry again.`);
138112
140229
  }
138113
140230
  throw rpcError(`Could not remove the worktree at ${path2}: ${e.message.trim()}. It stays in the session archive; remove the folder or fix the cause, then delete it again.`);
@@ -138417,7 +140534,7 @@ ${list}` : `${label} has no ordinary session. The person has to open one there;
138417
140534
 
138418
140535
  // packages/agent-host/src/sessions/manager.ts
138419
140536
  var SessionManager = class {
138420
- constructor(store2, adapters2, emit, endpoint, worktreeRoot = join46(homedir14(), DATA_DIR, "worktrees"), keeperOpts = {}) {
140537
+ constructor(store2, adapters2, emit, endpoint, worktreeRoot = join51(homedir15(), DATA_DIR, "worktrees"), keeperOpts = {}) {
138421
140538
  this.store = store2;
138422
140539
  this.adapters = adapters2;
138423
140540
  this.emit = emit;
@@ -139200,6 +141317,23 @@ var SessionManager = class {
139200
141317
  const text2 = this.projectRegistry.projectTrusted(m.projectId) ? "Restarted the agent to apply this project's new trust: it now reads the project's own settings and instruction files (CLAUDE.md, AGENTS.md)." : "Restarted the agent to apply this project's removed trust: it now reads only your own settings, not the project's files.";
139201
141318
  this.onEvent({ type: "notice", sessionId, level: "info", from: "Centralu", label: "restart", text: text2 });
139202
141319
  }
141320
+ /**
141321
+ * The turn a kept process was running is over after a swap (#698): the session goes idle with the quit mark and its line,
141322
+ * and the report it owed goes now, rather than naming a later turn
141323
+ */
141324
+ turnLostInSwap(m) {
141325
+ const change = { type: "state_change", sessionId: m.id, state: "idle", reason: "reattach_failed" };
141326
+ this.events.applyStateHint(change, m);
141327
+ this.interruptions.markStoppedByQuit(m, true);
141328
+ this.store.sessions.upsertSession(m);
141329
+ this.emit(change);
141330
+ const turnBy = this.children.turnEnded(m.id, null);
141331
+ if (this.children.awaitingReport.has(m.id)) void this.reports.reportBackIfAwaited(m.id, { type: "state_change", sessionId: m.id, state: "idle" }, void 0, void 0, turnBy);
141332
+ else {
141333
+ if (turnBy.by !== "person") void this.reports.tellAskerOfInterruption(m.id, this.children.turnOwners.get(m.id)?.callerId);
141334
+ void this.reports.reportToPersonIfOwed(m.id, { type: "state_change", sessionId: m.id, state: "idle" }, void 0, turnBy);
141335
+ }
141336
+ }
139203
141337
  /**
139204
141338
  * Re-attaches the tool processes a previous host left running in the keeper (#280 step 2), so
139205
141339
  * those sessions come back live — mid-turn, with a waiting approval re-delivered by the tool —
@@ -139220,21 +141354,12 @@ var SessionManager = class {
139220
141354
  const r = await this.resumeSession(k2.sessionId, k2.process).catch((err) => ({ resumed: false, reason: err.message }));
139221
141355
  const adopted = r.resumed ? this.handles.get(k2.sessionId) : void 0;
139222
141356
  if (adopted) this.adoptedProcesses.add(adopted);
141357
+ if (adopted?.takesOverTurn === false && inTurn(m.state)) this.turnLostInSwap(m);
139223
141358
  if (this.children.queued.has(k2.sessionId)) this.queue.scheduleDrain(k2.sessionId);
139224
141359
  if (!r.resumed) {
139225
141360
  console.error(`[agent-host] could not re-attach ${k2.sessionId.slice(0, 8)}: ${r.reason ?? "unknown"}`);
139226
141361
  if (inTurn(m.state)) {
139227
- const change = { type: "state_change", sessionId: m.id, state: "idle", reason: "reattach_failed" };
139228
- this.events.applyStateHint(change, m);
139229
- this.interruptions.markStoppedByQuit(m, true);
139230
- this.store.sessions.upsertSession(m);
139231
- this.emit(change);
139232
- const turnBy = this.children.turnEnded(m.id, null);
139233
- if (this.children.awaitingReport.has(m.id)) void this.reports.reportBackIfAwaited(m.id, { type: "state_change", sessionId: m.id, state: "idle" }, void 0, void 0, turnBy);
139234
- else {
139235
- if (turnBy.by !== "person") void this.reports.tellAskerOfInterruption(m.id, this.children.turnOwners.get(m.id)?.callerId);
139236
- void this.reports.reportToPersonIfOwed(m.id, { type: "state_change", sessionId: m.id, state: "idle" }, void 0, turnBy);
139237
- }
141362
+ this.turnLostInSwap(m);
139238
141363
  } else if (this.children.pausedReports.has(m.id)) {
139239
141364
  void this.reports.reportBackIfAwaited(m.id, { type: "state_change", sessionId: m.id, state: "idle" });
139240
141365
  }
@@ -139468,6 +141593,10 @@ var SessionManager = class {
139468
141593
  * A session is a slot, and the agent is a tool — the slot (name, order, history, grid panel) is left
139469
141594
  * untouched, and only the tool underneath it is swapped.
139470
141595
  */
141596
+ /** Why `tool` cannot run this session as its next launch would, under `preset` (its own by default), or null (`tool-fit.ts`) */
141597
+ toolRefusal(m, tool = m.tool, preset = m.permissionPreset) {
141598
+ return this.toolRegistry.refusal(tool, sessionDemands(m, this.launchInputs.settingFilesFor(m), preset));
141599
+ }
139471
141600
  async switchTool(sessionId, tool) {
139472
141601
  this.notBeingRemoved(sessionId);
139473
141602
  this.refuseWhileLettingGo();
@@ -139475,6 +141604,8 @@ var SessionManager = class {
139475
141604
  if (m.tool === tool) return { ...m, live: this.handles.has(sessionId) };
139476
141605
  const adapter = this.adapters.get(tool);
139477
141606
  if (!adapter) throw rpcError(`Unknown tool: ${tool}`, "tool_not_installed");
141607
+ const refused2 = this.toolRefusal(m, tool);
141608
+ if (refused2) throw unfitError(refused2);
139478
141609
  const d2 = await adapter.detect();
139479
141610
  if (!d2.installed || !d2.loggedIn) {
139480
141611
  throw rpcError(`Cannot use ${tool}: ${d2.detail}`, "tool_not_installed");
@@ -139506,7 +141637,7 @@ var SessionManager = class {
139506
141637
  this.events.settleState(m, { type: "state_change", sessionId, state: "idle", reason: "tool_changed" });
139507
141638
  return { ...m, live: false };
139508
141639
  }
139509
- respondApproval(sessionId, requestId, decision, scope, matcher) {
141640
+ respondApproval(sessionId, requestId, decision, scope, matcher2) {
139510
141641
  const m = this.meta.get(sessionId);
139511
141642
  const ask = this.hostCards.hostAsks.get(requestId);
139512
141643
  if (ask && ask.sessionId === sessionId) {
@@ -139521,7 +141652,7 @@ var SessionManager = class {
139521
141652
  }
139522
141653
  const pending = m?.pendingApproval;
139523
141654
  const key = pending?.requestId === requestId ? alwaysAllowKey(pending.detail) : "";
139524
- const remembered = decision === "always" && matcher !== void 0 && key !== "" && matcher === key ? matcher : void 0;
141655
+ const remembered = decision === "always" && matcher2 !== void 0 && key !== "" && matcher2 === key ? matcher2 : void 0;
139525
141656
  const landed = this.requireHandle(sessionId).respondApproval(requestId, decision, scope, remembered);
139526
141657
  if (!landed && this.hostCards.answeredApprovals.get(requestId) === sessionId) return;
139527
141658
  if (landed) {
@@ -140067,6 +142198,15 @@ var SessionManager = class {
140067
142198
  return null;
140068
142199
  }
140069
142200
  },
142201
+ mailHandOver: (...a) => self2.mailHooks?.handOver?.(...a) ?? [],
142202
+ mailHandOverPreview: (from, to) => {
142203
+ try {
142204
+ return self2.mailHooks?.handOverPreview?.(from, to) ?? { moved: [], closed: [], left: [] };
142205
+ } catch (e) {
142206
+ console.error(`[mail] ${from.slice(0, 8)}: what a handoff would move could not be read (${errorMessage(e)})`);
142207
+ return null;
142208
+ }
142209
+ },
140070
142210
  mailGone: (id3) => {
140071
142211
  try {
140072
142212
  self2.mailHooks?.sessionGone(id3);
@@ -140233,6 +142373,8 @@ var SessionManager = class {
140233
142373
  },
140234
142374
  nameOf: (...a) => self2.reports.nameOf(...a),
140235
142375
  toolProblem: (...a) => self2.toolRegistry.toolProblem(...a),
142376
+ toolRefusal: (...a) => self2.toolRefusal(...a),
142377
+ refusal: (...a) => self2.toolRegistry.refusal(...a),
140236
142378
  trashSession: (...a) => self2.trashSession(...a),
140237
142379
  purgeSession: (...a) => self2.purgeSession(...a),
140238
142380
  trashIf: (...a) => self2.trash.trashIf(...a),
@@ -141394,6 +143536,91 @@ id: ${sessionIdOf(assignee)}` : "";
141394
143536
  return moved;
141395
143537
  });
141396
143538
  }
143539
+ /**
143540
+ * A handoff moves the original session's mail to the session that took over (§4.1, #693, #738, #1574): in each thread that moves,
143541
+ * the successor takes the original's place, as owner, as assignee or as a member, and what the original has not read waits for the
143542
+ * successor instead (`moveUnreadReceipts`). Each thread keeps its state and leaves an `event`; nobody is woken inside the move
143543
+ * (it runs before the successor's first message); the unread mail waits for the successor's next rest or the caller's poke. The
143544
+ * host's own write, like `partyGone`'s: no policy or rate check, since the handoff's own checks came first (`HandoffChildren.moveChildren` trusts its caller the same way).
143545
+ *
143546
+ * `which`: `all` for an original that goes (every thread not done it is in), or the sessions whose work moved to the successor
143547
+ * while the original stays (the threads the original owns with one of them as assignee). A thread the successor already holds
143548
+ * the other side of (it owns what the original is assigned, or the reverse) stays where it is: the two sides of a thread are two
143549
+ * parties, and the original's departure moves it on as before (`partyGone`); it is returned as `left`, so the note says so. A
143550
+ * done thread the original has unread mail in (a closing report it never read) stays done and keeps its parties; the successor
143551
+ * joins it and the unread mail waits for it (`closed`). Called inside the transaction of the children's move
143552
+ * (`ChildOwnershipRecords.moveChildren`), so the mail and the children move together or not at all. Returns every thread it
143553
+ * looked at by what became of it, for the handoff note (`handOverPreview` reads the same plan without writing)
143554
+ */
143555
+ handOver(fromSessionId, toSessionId, which) {
143556
+ if (fromSessionId === toSessionId || !this.records.hasThreads()) return [];
143557
+ const from = this.sessionAddress(fromSessionId);
143558
+ const to = this.sessionAddress(toSessionId);
143559
+ return this.run(() => {
143560
+ const now = this.now();
143561
+ const done2 = this.handOverPlan(from, to, which).map((p) => {
143562
+ const { thread: t, role, kind } = p;
143563
+ if (kind === "left") {
143564
+ this.records.handleUnread(t.id, from, now);
143565
+ return { ...p, unread: 0 };
143566
+ }
143567
+ if (kind === "closed") {
143568
+ this.records.putParticipantIfAbsent(t.id, to, "member", now);
143569
+ return { ...p, unread: this.records.moveUnreadReceipts(t.id, from, to, now) };
143570
+ }
143571
+ this.records.leaveParticipant(t.id, from, now);
143572
+ if (role === "member") this.records.putParticipantIfAbsent(t.id, to, "member", now);
143573
+ else this.records.putParticipant(t.id, to, role, now);
143574
+ const facts = { change: "handed_over", party: from, to, role };
143575
+ const after = role === "member" ? (this.insertEvent(t.id, "The session in this thread handed over: the session that took over is in it instead.", facts, now), t) : this.moveState(
143576
+ t,
143577
+ role === "owner" ? { owner: to } : { assignee: to },
143578
+ now,
143579
+ facts,
143580
+ role === "owner" ? "The owner handed over: the session that took over owns the thread." : "The assignee handed over: the session that took over has the work."
143581
+ );
143582
+ return { thread: after, role, kind, unread: this.records.moveUnreadReceipts(t.id, from, to, now) };
143583
+ });
143584
+ if (which === "all") for (const t of this.records.doneThreadsWithUnread(from)) this.records.handleUnread(t.id, from, now);
143585
+ return done2;
143586
+ });
143587
+ }
143588
+ /**
143589
+ * What `handOver` would do with the original's mail, read only: the archive preview of an original with an heir says it thread by
143590
+ * thread (#1574), from the same plan the move follows
143591
+ */
143592
+ handOverPreview(fromSessionId, toSessionId, which = "all") {
143593
+ const preview = { moved: [], closed: [], left: [] };
143594
+ if (fromSessionId === toSessionId || !this.records.hasThreads()) return preview;
143595
+ for (const p of this.handOverPlan(this.sessionAddress(fromSessionId), this.sessionAddress(toSessionId), which)) preview[p.kind].push({ subject: p.thread.subject, role: p.role });
143596
+ return preview;
143597
+ }
143598
+ /**
143599
+ * The threads a handoff looks at, and what becomes of each (`HandedOverThread.kind`): those not done the original owns, is assigned or
143600
+ * is in, and the done ones it has unread mail in; with `assignees`, only those it owns whose assignee is one of them. A thread in a
143601
+ * state a newer build wrote is not touched
143602
+ */
143603
+ handOverPlan(from, to, which) {
143604
+ const assignees = which === "all" ? null : new Set(which.assignees.map((id3) => this.sessionAddress(id3)));
143605
+ const open6 = ["open", "running", "review", "blocked"];
143606
+ const threads = /* @__PURE__ */ new Map();
143607
+ for (const t of [
143608
+ ...this.records.threadsOfParty(from, "owner", open6),
143609
+ ...this.records.threadsOfParty(from, "assignee", open6),
143610
+ ...this.records.openThreadsWith(from),
143611
+ ...this.records.doneThreadsWithUnread(from)
143612
+ ]) threads.set(t.id, t);
143613
+ const plan = [];
143614
+ for (const t of threads.values()) {
143615
+ if (t.state === "unknown") continue;
143616
+ const role = t.owner === from ? "owner" : t.assignee === from ? "assignee" : "member";
143617
+ if (assignees && !(role === "owner" && t.assignee !== null && assignees.has(t.assignee))) continue;
143618
+ if (t.state === "done" && (assignees || this.records.movableUnread(t.id, from, to) === 0)) continue;
143619
+ const kind = t.state === "done" ? "closed" : role === "owner" && t.assignee === to || role === "assignee" && t.owner === to ? "left" : "moved";
143620
+ plan.push({ thread: t, role, kind });
143621
+ }
143622
+ return plan;
143623
+ }
141397
143624
  /**
141398
143625
  * A turn of delegated work ended (§6): Centralu's `report` in the thread, for its owner, and the thread's state moves with it: `review`
141399
143626
  * when the work was reported, `running` when it paused on the session's own background work, and `done` (`dropped`, taken over) when the
@@ -142966,13 +145193,13 @@ function mailRelations(host) {
142966
145193
  }
142967
145194
 
142968
145195
  // packages/agent-host/src/rpc.ts
142969
- import { relative as relative8 } from "node:path";
145196
+ import { relative as relative9 } from "node:path";
142970
145197
 
142971
145198
  // packages/agent-host/src/themes.ts
142972
- import { randomBytes as randomBytes8 } from "node:crypto";
145199
+ import { randomBytes as randomBytes9 } from "node:crypto";
142973
145200
  import { watch as watch2 } from "node:fs";
142974
145201
  import { mkdir as mkdir7, readdir as readdir13, readFile as readFile7, rename as rename5, stat as stat9, unlink as unlink2, writeFile as writeFile5 } from "node:fs/promises";
142975
- import { basename as basename13, join as join47 } from "node:path";
145202
+ import { basename as basename13, join as join52 } from "node:path";
142976
145203
  var MAX_FILE_BYTES = 256 * 1024;
142977
145204
  var SETTLE_MS = 100;
142978
145205
  var ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,59}$/;
@@ -143017,7 +145244,7 @@ var ThemeFiles = class {
143017
145244
  * a token updates it, and an unchanged one does not touch the folder (or wake the watcher).
143018
145245
  */
143019
145246
  async writeSchema() {
143020
- const path2 = join47(this.dir, THEME_SCHEMA_FILE);
145247
+ const path2 = join52(this.dir, THEME_SCHEMA_FILE);
143021
145248
  const text2 = `${JSON.stringify(themeFileJsonSchema(), null, 2)}
143022
145249
  `;
143023
145250
  const current = await readFile7(path2, "utf8").catch(() => null);
@@ -143066,7 +145293,7 @@ var ThemeFiles = class {
143066
145293
  /** The absolute path for an id, for the shell to trash or reveal. Refuses anything that is not an id. */
143067
145294
  pathOf(id3) {
143068
145295
  if (!ID_PATTERN.test(id3)) throw rpcError(`Not a theme id: ${id3}`);
143069
- return join47(this.dir, `${id3}.json`);
145296
+ return join52(this.dir, `${id3}.json`);
143070
145297
  }
143071
145298
  async freeId(name) {
143072
145299
  const base2 = name.toLowerCase().normalize("NFKD").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "theme";
@@ -143078,11 +145305,11 @@ var ThemeFiles = class {
143078
145305
  );
143079
145306
  if (!taken) return id3;
143080
145307
  }
143081
- return `${base2}-${randomBytes8(4).toString("hex")}`;
145308
+ return `${base2}-${randomBytes9(4).toString("hex")}`;
143082
145309
  }
143083
145310
  /** Temp file and rename: a reader (the watcher, an editor, an agent) never sees half a file */
143084
145311
  async atomicWrite(path2, text2) {
143085
- const tmp = `${path2}.${randomBytes8(6).toString("hex")}.tmp`;
145312
+ const tmp = `${path2}.${randomBytes9(6).toString("hex")}.tmp`;
143086
145313
  try {
143087
145314
  await writeFile5(tmp, text2, "utf8");
143088
145315
  await rename5(tmp, path2);
@@ -143095,11 +145322,11 @@ var ThemeFiles = class {
143095
145322
  };
143096
145323
 
143097
145324
  // packages/agent-host/src/dev-services/strays.ts
143098
- import { execFile as execFile8 } from "node:child_process";
143099
- import { realpathSync as realpathSync10 } from "node:fs";
143100
- import { promisify as promisify7 } from "node:util";
145325
+ import { execFile as execFile12 } from "node:child_process";
145326
+ import { realpathSync as realpathSync11 } from "node:fs";
145327
+ import { promisify as promisify11 } from "node:util";
143101
145328
  import { userInfo } from "node:os";
143102
- var exec6 = promisify7(execFile8);
145329
+ var exec10 = promisify11(execFile12);
143103
145330
  function parsePsRows(out) {
143104
145331
  const rows3 = [];
143105
145332
  for (const line of out.split("\n")) {
@@ -143165,7 +145392,7 @@ function pickStrays(rows3, cwdOf, roots, selfPid, held3 = []) {
143165
145392
  }
143166
145393
  async function psRows() {
143167
145394
  try {
143168
- const { stdout } = await exec6("ps", ["-U", userInfo().username, "-o", "pid=,ppid=,tty=,command="], {
145395
+ const { stdout } = await exec10("ps", ["-U", userInfo().username, "-o", "pid=,ppid=,tty=,command="], {
143169
145396
  maxBuffer: 8 * 1024 * 1024,
143170
145397
  timeout: 5e3,
143171
145398
  windowsHide: true
@@ -143178,7 +145405,7 @@ async function psRows() {
143178
145405
  async function cwdsOf(pids) {
143179
145406
  if (pids.length === 0) return /* @__PURE__ */ new Map();
143180
145407
  try {
143181
- const { stdout } = await exec6("lsof", ["-a", "-d", "cwd", "-p", pids.join(","), "-Fpn"], {
145408
+ const { stdout } = await exec10("lsof", ["-a", "-d", "cwd", "-p", pids.join(","), "-Fpn"], {
143182
145409
  maxBuffer: 8 * 1024 * 1024,
143183
145410
  timeout: 5e3,
143184
145411
  windowsHide: true
@@ -143201,7 +145428,7 @@ function resolveRoots(roots) {
143201
145428
  for (const r of roots) {
143202
145429
  out.add(r);
143203
145430
  try {
143204
- out.add(realpathSync10(r));
145431
+ out.add(realpathSync11(r));
143205
145432
  } catch {
143206
145433
  }
143207
145434
  }
@@ -143383,8 +145610,8 @@ function createRpcHandler(mgr2, adapters2, { settleDeletedProjects, deleteOutloo
143383
145610
  },
143384
145611
  "agents.reportOwed": async (p) => ({ owed: mgr2.reportOwed(RpcMethods["agents.reportOwed"].params.parse(p).sessionId) }),
143385
145612
  "agents.respondApproval": async (p) => {
143386
- const { sessionId, requestId, decision, scope, matcher } = RpcMethods["agents.respondApproval"].params.parse(p);
143387
- mgr2.respondApproval(sessionId, requestId, decision, scope, matcher);
145613
+ const { sessionId, requestId, decision, scope, matcher: matcher2 } = RpcMethods["agents.respondApproval"].params.parse(p);
145614
+ mgr2.respondApproval(sessionId, requestId, decision, scope, matcher2);
143388
145615
  return { ok: true };
143389
145616
  },
143390
145617
  "agents.answerQuestion": async (p) => {
@@ -143735,7 +145962,7 @@ function createRpcHandler(mgr2, adapters2, { settleDeletedProjects, deleteOutloo
143735
145962
  const project2 = (await mgr2.listProjects()).find((x) => x.id === projectId);
143736
145963
  const app2 = apps.list().find((a) => a.appId === appId && a.projectId === projectId);
143737
145964
  if (!project2 || !app2) throw rpcError(noSuchAppText({ projectId, appId }));
143738
- const { repo, commits } = await gitLogPath(project2.path, relative8(project2.path, app2.dir), 20);
145965
+ const { repo, commits } = await gitLogPath(project2.path, relative9(project2.path, app2.dir), 20);
143739
145966
  return { kind: "git", repo, commits };
143740
145967
  },
143741
145968
  "apps.restoreVersion": async (p) => {
@@ -144460,7 +146687,7 @@ var sameCause = (a, b2) => a !== null && b2 !== null && JSON.stringify(a) === JS
144460
146687
 
144461
146688
  // packages/agent-host/src/dev-services/terminal.ts
144462
146689
  import { createRequire } from "node:module";
144463
- import { existsSync as existsSync21 } from "node:fs";
146690
+ import { existsSync as existsSync24 } from "node:fs";
144464
146691
 
144465
146692
  // packages/agent-host/src/dev-services/pty-slot.ts
144466
146693
  function isPtyModule(m) {
@@ -144792,9 +147019,9 @@ function commandShell(command, platform = process.platform, env = process.env) {
144792
147019
  }
144793
147020
  function shellPath() {
144794
147021
  const fromEnv = process.env.SHELL;
144795
- if (fromEnv && existsSync21(fromEnv)) return fromEnv;
147022
+ if (fromEnv && existsSync24(fromEnv)) return fromEnv;
144796
147023
  for (const candidate of ["/bin/zsh", "/bin/bash", "/bin/sh"]) {
144797
- if (existsSync21(candidate)) return candidate;
147024
+ if (existsSync24(candidate)) return candidate;
144798
147025
  }
144799
147026
  return process.platform === "win32" ? "powershell.exe" : "/bin/sh";
144800
147027
  }
@@ -144941,9 +147168,9 @@ var CommandRunner = class {
144941
147168
  };
144942
147169
 
144943
147170
  // packages/agent-host/src/updates.ts
144944
- import { execFile as execFile9, execFileSync as execFileSync3 } from "node:child_process";
144945
- import { existsSync as existsSync22, readFileSync as readFileSync17 } from "node:fs";
144946
- import { join as join48 } from "node:path";
147171
+ import { execFile as execFile13, execFileSync as execFileSync3 } from "node:child_process";
147172
+ import { existsSync as existsSync25, readFileSync as readFileSync20 } from "node:fs";
147173
+ import { join as join53 } from "node:path";
144947
147174
  var REGISTRY_URL = `https://registry.npmjs.org/${APP_SLUG}/latest`;
144948
147175
  var CHECK_INTERVAL_MS = 6 * 60 * 60 * 1e3;
144949
147176
  var SCHEDULED_TIMEOUT_MS = 3e3;
@@ -144963,13 +147190,13 @@ function hubUpdatesMachines(f) {
144963
147190
  }
144964
147191
  function existingInstalledCopy() {
144965
147192
  const path2 = installedCopyPath();
144966
- return existsSync22(path2) ? path2 : null;
147193
+ return existsSync25(path2) ? path2 : null;
144967
147194
  }
144968
147195
  function readInstalledCopyVersion(copy, platform = HOST_PLATFORM) {
144969
147196
  const source = installedCopyVersionSource(copy, platform);
144970
147197
  if (!source) return null;
144971
147198
  try {
144972
- const text2 = source.kind === "command" ? execFileSync3(source.file, source.args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5e3, windowsHide: true }) : readFileSync17(join48(copy, source.name), "utf8");
147199
+ const text2 = source.kind === "command" ? execFileSync3(source.file, source.args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5e3, windowsHide: true }) : readFileSync20(join53(copy, source.name), "utf8");
144973
147200
  return text2.trim() || null;
144974
147201
  } catch {
144975
147202
  return null;
@@ -144998,7 +147225,7 @@ function failureDetail(file2, stderr) {
144998
147225
  function runCommand(file2, args, resolve21) {
144999
147226
  const cmd = commandFor(file2, args, resolve21);
145000
147227
  return new Promise((done2, reject) => {
145001
- execFile9(cmd.file, cmd.args, { timeout: INSTALL_TIMEOUT_MS, windowsHide: true }, (err, _stdout, stderr) => {
147228
+ execFile13(cmd.file, cmd.args, { timeout: INSTALL_TIMEOUT_MS, windowsHide: true }, (err, _stdout, stderr) => {
145002
147229
  if (!err) return done2();
145003
147230
  if (err.code === "ENOENT") {
145004
147231
  return reject(new Error(`${file2} was not found on PATH (${err.message})`));
@@ -145729,8 +147956,8 @@ var SessionSleeper = class {
145729
147956
 
145730
147957
  // packages/agent-host/src/dev-services/instance-lock.ts
145731
147958
  import { execFileSync as execFileSync4 } from "node:child_process";
145732
- import { readFileSync as readFileSync18, unlinkSync as unlinkSync3, writeFileSync as writeFileSync10 } from "node:fs";
145733
- import { dirname as dirname22, join as join49 } from "node:path";
147959
+ import { readFileSync as readFileSync21, unlinkSync as unlinkSync3, writeFileSync as writeFileSync10 } from "node:fs";
147960
+ import { dirname as dirname25, join as join54 } from "node:path";
145734
147961
  import Database3 from "better-sqlite3";
145735
147962
  var OWNERSHIP_FILE = "host-ownership.sqlite";
145736
147963
  var held = /* @__PURE__ */ new Set();
@@ -145776,15 +148003,15 @@ function parseHolder(raw) {
145776
148003
  function acquireInstanceLock(dbPath2, startOf = processStartTime, describe4 = {}, now = Date.now) {
145777
148004
  if (dbPath2 === ":memory:") return { ok: true, release: () => {
145778
148005
  } };
145779
- const lockPath = join49(dirname22(dbPath2), "host.lock");
148006
+ const lockPath = join54(dirname25(dbPath2), "host.lock");
145780
148007
  const readHolder = () => {
145781
148008
  try {
145782
- return parseHolder(readFileSync18(lockPath, "utf8"));
148009
+ return parseHolder(readFileSync21(lockPath, "utf8"));
145783
148010
  } catch {
145784
148011
  return null;
145785
148012
  }
145786
148013
  };
145787
- const owner = takeOwnership(join49(dirname22(dbPath2), OWNERSHIP_FILE));
148014
+ const owner = takeOwnership(join54(dirname25(dbPath2), OWNERSHIP_FILE));
145788
148015
  if (!owner) {
145789
148016
  const holder = readHolder();
145790
148017
  const pid = holder && Number.isInteger(holder.pid) && holder.pid > 0 ? holder.pid : null;
@@ -145801,7 +148028,7 @@ function acquireInstanceLock(dbPath2, startOf = processStartTime, describe4 = {}
145801
148028
  if (released) return;
145802
148029
  released = true;
145803
148030
  try {
145804
- if (parseHolder(readFileSync18(lockPath, "utf8")).pid === process.pid) unlinkSync3(lockPath);
148031
+ if (parseHolder(readFileSync21(lockPath, "utf8")).pid === process.pid) unlinkSync3(lockPath);
145805
148032
  } catch {
145806
148033
  }
145807
148034
  giveUp(owner);
@@ -145880,8 +148107,8 @@ function methodParams(method) {
145880
148107
  var EFFECTS = {
145881
148108
  "agents.deleteSession": (p) => "The session moves to the session archive" + (p.deleteWorktree === true ? ", and its worktree is marked to be deleted when the session is deleted from the archive for good" : "") + (p.deleteExternal === true ? ", and the tool's own conversation file with it" : "") + ".",
145882
148109
  "agents.archiveIfAtRest": () => "The session moves to the session archive if that loses nothing it holds.",
145883
- "agents.deleteAgentChild": (p) => `The session moves to the session archive, and the worktree on the branch ${plainQuoted(str2(p.worktreeBranch), 120)} is deleted when nothing in it would be lost.`,
145884
- "agents.deleteOrchestratorSkill": (p) => `The orchestrator skill ${plainQuoted(str2(p.name), 120)} is deleted.`,
148110
+ "agents.deleteAgentChild": (p) => `The session moves to the session archive, and the worktree on the branch ${plainQuoted(str3(p.worktreeBranch), 120)} is deleted when nothing in it would be lost.`,
148111
+ "agents.deleteOrchestratorSkill": (p) => `The orchestrator skill ${plainQuoted(str3(p.name), 120)} is deleted.`,
145885
148112
  "projects.delete": () => "Centralu's records of the project are erased: its sessions, their conversations, its search index and approval rules. Its folder stays.",
145886
148113
  "apps.remove": () => "The app's folder moves to app-trash in the data folder, and it is taken off every session it was attached to.",
145887
148114
  "machines.remove": () => "The machine is unlinked: its sessions and projects leave this computer's lists.",
@@ -145942,7 +148169,7 @@ var quoted3 = (name) => plainQuoted(name, 120);
145942
148169
  function fields2(v) {
145943
148170
  return v !== null && typeof v === "object" && !Array.isArray(v) ? { ...v } : {};
145944
148171
  }
145945
- var str2 = (v) => typeof v === "string" ? v : "";
148172
+ var str3 = (v) => typeof v === "string" ? v : "";
145946
148173
 
145947
148174
  // packages/agent-host/src/host-requests.ts
145948
148175
  var HOST_REQUEST_LIMITS = { maxOpen: 8, lifetimeMs: 15 * 6e4, windowGoneMs: 3e4 };
@@ -145976,9 +148203,9 @@ var HostRequests = class {
145976
148203
  const p = fields3(params);
145977
148204
  switch (method) {
145978
148205
  case "trash.purge":
145979
- return this.viaTrash(method, from, signal, { action: "delete", sessionId: str3(p.sessionId), what: "session" }, () => ({ ok: true }));
148206
+ return this.viaTrash(method, from, signal, { action: "delete", sessionId: str4(p.sessionId), what: "session" }, () => ({ ok: true }));
145980
148207
  case "trash.purgeSubagentSteps":
145981
- return this.viaTrash(method, from, signal, { action: "delete", sessionId: str3(p.sessionId), what: "steps" }, () => ({ ok: true }));
148208
+ return this.viaTrash(method, from, signal, { action: "delete", sessionId: str4(p.sessionId), what: "steps" }, () => ({ ok: true }));
145982
148209
  case "trash.empty":
145983
148210
  return this.viaTrash(method, from, signal, { action: "empty" }, (purged, text2, failed3) => ({ purged, failed: failed3, text: text2 }));
145984
148211
  default:
@@ -146073,7 +148300,7 @@ var HostRequests = class {
146073
148300
  return r;
146074
148301
  } catch (e) {
146075
148302
  if (m === "trash.purge") {
146076
- const sessionId = str3(fields3(p).sessionId);
148303
+ const sessionId = str4(fields3(p).sessionId);
146077
148304
  failed3.push({ sessionId, name: names.get(sessionId) ?? sessionId, error: e instanceof Error ? e.message : String(e) });
146078
148305
  }
146079
148306
  throw e;
@@ -146121,7 +148348,7 @@ var HostRequests = class {
146121
148348
  function fields3(params) {
146122
148349
  return params !== null && typeof params === "object" && !Array.isArray(params) ? { ...params } : {};
146123
148350
  }
146124
- var str3 = (v) => typeof v === "string" ? v : "";
148351
+ var str4 = (v) => typeof v === "string" ? v : "";
146125
148352
  function refusalOf(outcome) {
146126
148353
  if (outcome === "too_many_requests" || outcome === "no_window") return outcome;
146127
148354
  return outcome === "denied" ? "denied" : outcome === "expired" ? "expired" : "withdrawn";
@@ -146331,7 +148558,7 @@ var Shutdown = class {
146331
148558
  var SHUTDOWN_BOUND_MS = 1e4;
146332
148559
 
146333
148560
  // packages/agent-host/src/lifecycle/endings.ts
146334
- import { appendFileSync as appendFileSync2 } from "node:fs";
148561
+ import { appendFileSync as appendFileSync3 } from "node:fs";
146335
148562
  var SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
146336
148563
  var REPEAT_AFTER_MS = 1e3;
146337
148564
  function deliberateRepeat(first, now) {
@@ -146380,7 +148607,7 @@ function crashRecorder(path2, log, maxBytes = MAX_LOG_BYTES) {
146380
148607
  log(`[agent-host] ${kind} ${detail}`);
146381
148608
  try {
146382
148609
  rotateIfLarge(path2, maxBytes);
146383
- appendFileSync2(path2, `[${(/* @__PURE__ */ new Date()).toISOString()}] ${kind}: ${detail}
148610
+ appendFileSync3(path2, `[${(/* @__PURE__ */ new Date()).toISOString()}] ${kind}: ${detail}
146384
148611
  `);
146385
148612
  } catch {
146386
148613
  }
@@ -146415,17 +148642,17 @@ function wireEndings(o) {
146415
148642
 
146416
148643
  // packages/agent-host/src/lifecycle/stopped-mark.ts
146417
148644
  import { rmSync as rmSync7, writeFileSync as writeFileSync11 } from "node:fs";
146418
- import { join as join50 } from "node:path";
148645
+ import { join as join55 } from "node:path";
146419
148646
  var STOPPED_MARK_FILE = "stopped.json";
146420
148647
  function clearStoppedMark(dataDir) {
146421
148648
  try {
146422
- rmSync7(join50(dataDir, STOPPED_MARK_FILE), { force: true });
148649
+ rmSync7(join55(dataDir, STOPPED_MARK_FILE), { force: true });
146423
148650
  } catch {
146424
148651
  }
146425
148652
  }
146426
148653
  function markQuit(dataDir, now = Date.now()) {
146427
148654
  try {
146428
- writeFileSync11(join50(dataDir, STOPPED_MARK_FILE), `${JSON.stringify({ v: 1, by: "quit", at: now })}
148655
+ writeFileSync11(join55(dataDir, STOPPED_MARK_FILE), `${JSON.stringify({ v: 1, by: "quit", at: now })}
146429
148656
  `);
146430
148657
  } catch {
146431
148658
  }
@@ -146566,7 +148793,7 @@ async function runDrain(bound, deps) {
146566
148793
  }
146567
148794
 
146568
148795
  // packages/agent-host/src/links/install.ts
146569
- import { existsSync as existsSync23, readFileSync as readFileSync19 } from "node:fs";
148796
+ import { existsSync as existsSync26, readFileSync as readFileSync22 } from "node:fs";
146570
148797
  import { hostname as hostname4 } from "node:os";
146571
148798
  import { fileURLToPath as fileURLToPath6 } from "node:url";
146572
148799
  import { gzipSync } from "node:zlib";
@@ -146575,7 +148802,7 @@ import { gzipSync } from "node:zlib";
146575
148802
  import { createPublicKey, verify } from "node:crypto";
146576
148803
  var NPM_REGISTRY = "https://registry.npmjs.org";
146577
148804
  var PACKAGE_NAME = /^(@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
146578
- var VERSION = /^[0-9A-Za-z][0-9A-Za-z.+-]{0,63}$/;
148805
+ var VERSION2 = /^[0-9A-Za-z][0-9A-Za-z.+-]{0,63}$/;
146579
148806
  var NotPublished = class extends Error {
146580
148807
  constructor(id3) {
146581
148808
  super(`${id3} is not on the npm registry`);
@@ -146596,7 +148823,7 @@ async function getJson(url2, f, id3) {
146596
148823
  return res.json();
146597
148824
  }
146598
148825
  async function verifiedPackage(name, version2, opts = {}) {
146599
- if (!PACKAGE_NAME.test(name) || !VERSION.test(version2)) throw new Error(`Not a package to install: ${name}@${version2}`);
148826
+ if (!PACKAGE_NAME.test(name) || !VERSION2.test(version2)) throw new Error(`Not a package to install: ${name}@${version2}`);
146600
148827
  const registry2 = (opts.registry ?? NPM_REGISTRY).replace(/\/+$/, "");
146601
148828
  const f = opts.fetch ?? globalThis.fetch;
146602
148829
  const now = opts.now?.() ?? Date.now();
@@ -146694,7 +148921,7 @@ function updateNodeSentence(o) {
146694
148921
  // packages/agent-host/src/links/tunnel.ts
146695
148922
  import { execFileSync as execFileSync5, spawn as spawn5 } from "node:child_process";
146696
148923
  import { createConnection as createConnection2, createServer as createServer3 } from "node:net";
146697
- import { win32 as win328 } from "node:path";
148924
+ import { win32 as win329 } from "node:path";
146698
148925
  import { hostname as hostname3 } from "node:os";
146699
148926
 
146700
148927
  // packages/agent-host/src/links/ssh-run.ts
@@ -147315,8 +149542,8 @@ function ansiText(bytes2) {
147315
149542
  ansiDecoder = null;
147316
149543
  if (process.platform !== "win32") return null;
147317
149544
  try {
147318
- const root3 = process.env.SystemRoot && win328.isAbsolute(process.env.SystemRoot) ? process.env.SystemRoot : "C:\\Windows";
147319
- const out = execFileSync5(win328.join(root3, "System32", "reg.exe"), ["query", "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage", "/v", "ACP"], { encoding: "utf8", timeout: 5e3, windowsHide: true });
149545
+ const root3 = process.env.SystemRoot && win329.isAbsolute(process.env.SystemRoot) ? process.env.SystemRoot : "C:\\Windows";
149546
+ const out = execFileSync5(win329.join(root3, "System32", "reg.exe"), ["query", "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage", "/v", "ACP"], { encoding: "utf8", timeout: 5e3, windowsHide: true });
147320
149547
  const page = /\bACP\s+REG_SZ\s+(\d+)/.exec(out)?.[1];
147321
149548
  if (page) ansiDecoder = new TextDecoder(CODE_PAGE_NAMES[page] ?? `windows-${page}`, { fatal: true });
147322
149549
  } catch {
@@ -147606,7 +149833,7 @@ async function installRemote(o) {
147606
149833
  function bundled(name, source) {
147607
149834
  for (const url2 of [new URL(`./${name}`, import.meta.url), new URL(source, import.meta.url)]) {
147608
149835
  const file2 = fileURLToPath6(url2);
147609
- if (existsSync23(file2)) return readFileSync19(file2, "utf8");
149836
+ if (existsSync26(file2)) return readFileSync22(file2, "utf8");
147610
149837
  }
147611
149838
  throw new Error(`${name} is missing from this host`);
147612
149839
  }
@@ -147677,7 +149904,7 @@ async function removeAutostart(c) {
147677
149904
  }
147678
149905
 
147679
149906
  // packages/agent-host/src/links/machine-ids.ts
147680
- import { randomBytes as randomBytes9 } from "node:crypto";
149907
+ import { randomBytes as randomBytes10 } from "node:crypto";
147681
149908
  function isMachineId(value) {
147682
149909
  return MACHINE_ID_RE.test(value);
147683
149910
  }
@@ -147705,10 +149932,10 @@ function decodeNumber(value) {
147705
149932
  }
147706
149933
  function newMachineId(name, taken) {
147707
149934
  const slug = name.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^[^a-z]+/, "").replace(/-+$/, "").slice(0, 24);
147708
- const base2 = slug && MACHINE_ID_RE.test(slug) ? slug : `m-${randomBytes9(3).toString("hex")}`;
149935
+ const base2 = slug && MACHINE_ID_RE.test(slug) ? slug : `m-${randomBytes10(3).toString("hex")}`;
147709
149936
  if (!taken.has(base2)) return base2;
147710
149937
  for (let i = 2; i < 1e3; i++) if (!taken.has(`${base2}-${i}`)) return `${base2}-${i}`;
147711
- return `m-${randomBytes9(4).toString("hex")}`;
149938
+ return `m-${randomBytes10(4).toString("hex")}`;
147712
149939
  }
147713
149940
 
147714
149941
  // packages/agent-host/src/links/qualifier.ts
@@ -149438,14 +151665,14 @@ var LinkedMachine = class extends LinkedMachineConnection {
149438
151665
  * again at once, and finds the managed launcher first
149439
151666
  */
149440
151667
  async install() {
149441
- const pick3 = await this.versionToInstall();
149442
- return this.operate("install", pick3.version, async (c, installer) => {
149443
- if (pick3.note) this.deps.log?.(`[links] ${this.name}: ${pick3.note}`);
151668
+ const pick4 = await this.versionToInstall();
151669
+ return this.operate("install", pick4.version, async (c, installer) => {
151670
+ if (pick4.note) this.deps.log?.(`[links] ${this.name}: ${pick4.note}`);
149444
151671
  const r = await installRemote({
149445
151672
  exec: c.exec,
149446
151673
  spec: c.spec,
149447
151674
  target: c.target,
149448
- version: pick3.version,
151675
+ version: pick4.version,
149449
151676
  runtime: installer.runtime(),
149450
151677
  script: c.script,
149451
151678
  registry: installer.registry,
@@ -149454,7 +151681,7 @@ var LinkedMachine = class extends LinkedMachineConnection {
149454
151681
  });
149455
151682
  this.removedByPerson = false;
149456
151683
  this.deps.log?.(`[links] ${this.name}: installed Centralu ${r.current.version} on Node ${r.current.node}${r.previous ? `; ${r.previous.version} kept to roll back to` : ""}`);
149457
- return { ...this.installedNow(r), ...pick3.note ? { note: pick3.note } : {} };
151684
+ return { ...this.installedNow(r), ...pick4.note ? { note: pick4.note } : {} };
149458
151685
  });
149459
151686
  }
149460
151687
  /**
@@ -149468,22 +151695,22 @@ var LinkedMachine = class extends LinkedMachineConnection {
149468
151695
  }
149469
151696
  async updateNow(opts) {
149470
151697
  if (opts.unasked === true) await this.probeActivity();
149471
- const pick3 = await this.versionToInstall();
151698
+ const pick4 = await this.versionToInstall();
149472
151699
  const there = this.versions?.remote.version;
149473
- if (there && releaseOf(there) !== pick3.version && isNewerVersion(there, pick3.version)) {
149474
- throw linkError2(`${this.name} runs Centralu ${there}, newer than ${pick3.version}, the version this computer would install; updating would downgrade it, so nothing was changed`);
151700
+ if (there && releaseOf(there) !== pick4.version && isNewerVersion(there, pick4.version)) {
151701
+ throw linkError2(`${this.name} runs Centralu ${there}, newer than ${pick4.version}, the version this computer would install; updating would downgrade it, so nothing was changed`);
149475
151702
  }
149476
- return this.operate("update", pick3.version, async (c, installer) => {
149477
- if (pick3.note) this.deps.log?.(`[links] ${this.name}: ${pick3.note}`);
151703
+ return this.operate("update", pick4.version, async (c, installer) => {
151704
+ if (pick4.note) this.deps.log?.(`[links] ${this.name}: ${pick4.note}`);
149478
151705
  c.beforeStop = () => this.beforeStop(c, opts.when ?? "idle", opts.unasked === true);
149479
151706
  const r = await updateRemote(c, {
149480
- version: pick3.version,
151707
+ version: pick4.version,
149481
151708
  runtime: installer.runtime(),
149482
151709
  registry: installer.registry,
149483
151710
  nodeDist: installer.nodeDist
149484
151711
  });
149485
151712
  this.deps.log?.(`[links] ${this.name}: updated to Centralu ${r.current.version}${r.previous ? `; ${r.previous.version} kept to roll back to` : ""}`);
149486
- const note = [pick3.note, r.note].filter(Boolean).join(" ");
151713
+ const note = [pick4.note, r.note].filter(Boolean).join(" ");
149487
151714
  return { ...this.installedNow(r), ...note ? { note } : {} };
149488
151715
  });
149489
151716
  }
@@ -149546,42 +151773,48 @@ var LinkedMachine = class extends LinkedMachineConnection {
149546
151773
  let shown3 = null;
149547
151774
  let seen = false;
149548
151775
  let blind = 0;
149549
- const show = (step, why) => {
149550
- if (shown3 === step) return;
149551
- shown3 = step;
149552
- c.step(step);
151776
+ const show = (step, why, count) => {
151777
+ const key = `${step}:${count ?? ""}`;
151778
+ if (shown3 === key) return;
151779
+ shown3 = key;
151780
+ c.step(step, count);
149553
151781
  this.deps.log?.(`[links] ${this.name}: ${why}`);
149554
151782
  };
149555
151783
  const unread = () => show("wait_unread", "what runs there cannot be read now; the update keeps waiting, since the sessions there may be in a turn (Cancel ends it)");
149556
- const busy = (n) => show("wait_idle", `the update waits for ${n} ${n === 1 ? "session" : "sessions"} in a turn`);
151784
+ const busy = (a) => {
151785
+ const turns = turnsRunning(a);
151786
+ const n = open6(a);
151787
+ if (turns > 0) show("wait_idle", `the update waits for ${turns} ${turns === 1 ? "session" : "sessions"} in a turn`, turns);
151788
+ else show("wait_open", `the update waits for ${n} open ${n === 1 ? "terminal or project command" : "terminals and project commands"}, which an update this computer starts by itself closes none of`, n);
151789
+ };
149557
151790
  const open6 = (a) => unasked ? a.terminals + a.commandRuns : 0;
149558
151791
  const holding = (a) => turnsRunning(a) + open6(a);
149559
151792
  let openSince = null;
151793
+ const now = this.deps.now ?? Date.now;
149560
151794
  const openLimit = this.deps.openWorkWaitMs ?? OPEN_WORK_WAIT_MS;
149561
151795
  for (; ; ) {
149562
151796
  const r = await read();
149563
151797
  if (r.activity) {
149564
151798
  seen = true;
149565
151799
  blind = 0;
149566
- if (turnsRunning(r.activity) === 0 && open6(r.activity) > 0) {
149567
- openSince ??= Date.now();
149568
- if (Date.now() - openSince >= openLimit) {
151800
+ if (open6(r.activity) > 0) {
151801
+ openSince ??= now();
151802
+ if (now() - openSince >= openLimit) {
149569
151803
  throw new Error(
149570
151804
  `Terminals or project commands stayed open on ${this.name}, and an update this computer starts by itself closes none of them; nothing was switched`
149571
151805
  );
149572
151806
  }
149573
151807
  } else openSince = null;
149574
- const running2 = holding(r.activity);
149575
- if (running2 === 0) {
151808
+ if (holding(r.activity) === 0) {
149576
151809
  await pause(settle, wait.signal);
149577
151810
  const again = await read();
149578
151811
  if (again.activity && holding(again.activity) === 0) {
149579
151812
  if (shown3) c.step("stop");
149580
151813
  return null;
149581
151814
  }
149582
- if (again.activity) busy(holding(again.activity));
151815
+ if (again.activity) busy(again.activity);
149583
151816
  else unread();
149584
- } else busy(running2);
151817
+ } else busy(r.activity);
149585
151818
  } else if (seen) {
149586
151819
  unread();
149587
151820
  } else if (r.cannot || ++blind > ACTIVITY_UNREAD_RETRIES) {
@@ -149754,13 +151987,13 @@ var LinkedMachine = class extends LinkedMachineConnection {
149754
151987
  * may be removing
149755
151988
  */
149756
151989
  async autostart(on2) {
149757
- const exec7 = this.tunnel.exec?.bind(this.tunnel);
149758
- if (!exec7) throw linkError2(`This computer has no shell on ${this.name} to change how it starts`);
151990
+ const exec11 = this.tunnel.exec?.bind(this.tunnel);
151991
+ if (!exec11) throw linkError2(`This computer has no shell on ${this.name} to change how it starts`);
149759
151992
  const action = on2 === void 0 ? "status" : on2 ? "on" : "off";
149760
151993
  const done2 = this.begin("autostart", action, null);
149761
151994
  let state;
149762
151995
  try {
149763
- const a = await autostartRemote({ exec: exec7, spec: this.record.remote, target: this.record.sshTarget }, action);
151996
+ const a = await autostartRemote({ exec: exec11, spec: this.record.remote, target: this.record.sshTarget }, action);
149764
151997
  state = { on: a.on, how: a.how, linger: a.linger };
149765
151998
  this.bootStart = state;
149766
151999
  if (on2 !== void 0) {
@@ -149882,9 +152115,9 @@ var LinkedMachine = class extends LinkedMachineConnection {
149882
152115
  const tunnelExec = this.tunnel.exec.bind(this.tunnel);
149883
152116
  const done2 = this.begin(kind, null, target);
149884
152117
  const at2 = Date.now();
149885
- const step = (s) => {
152118
+ const step = (s, count) => {
149886
152119
  this.deps.log?.(`[links] ${this.name}: ${kind}${target ? ` ${target}` : ""}: ${s}`);
149887
- this.operation = { kind, step: s, target, at: at2 };
152120
+ this.operation = { kind, step: s, target, at: at2, ...count === void 0 ? {} : { count } };
149888
152121
  this.deps.broadcast({ type: "machine_status", machine: this.info() });
149889
152122
  };
149890
152123
  let result;
@@ -149903,7 +152136,7 @@ var LinkedMachine = class extends LinkedMachineConnection {
149903
152136
  return result && typeof result === "object" && "machine" in result ? { ...result, machine: this.info() } : result;
149904
152137
  }
149905
152138
  /** What update.ts runs things there with */
149906
- control(exec7, script, step) {
152139
+ control(exec11, script, step) {
149907
152140
  const hold = async () => {
149908
152141
  this.held = true;
149909
152142
  this.clearRetry();
@@ -149914,7 +152147,7 @@ var LinkedMachine = class extends LinkedMachineConnection {
149914
152147
  this.setStatus("updating", null);
149915
152148
  };
149916
152149
  return {
149917
- exec: exec7,
152150
+ exec: exec11,
149918
152151
  spec: this.record.remote,
149919
152152
  target: this.record.sshTarget,
149920
152153
  script,
@@ -150037,9 +152270,9 @@ function probeTarget(target, opts = {}) {
150037
152270
  }
150038
152271
 
150039
152272
  // packages/agent-host/src/links/ssh-config.ts
150040
- import { readdirSync as readdirSync11, readFileSync as readFileSync20, statSync as statSync15 } from "node:fs";
150041
- import { homedir as homedir15 } from "node:os";
150042
- import { isAbsolute as isAbsolute10, join as join51, normalize, parse as parse5, sep as sep11 } from "node:path";
152273
+ import { readdirSync as readdirSync14, readFileSync as readFileSync23, statSync as statSync15 } from "node:fs";
152274
+ import { homedir as homedir16 } from "node:os";
152275
+ import { isAbsolute as isAbsolute11, join as join56, normalize, parse as parse5, sep as sep11 } from "node:path";
150043
152276
  var MAX_DEPTH = 16;
150044
152277
  function splitConfigLine(raw) {
150045
152278
  const m = /^\s*([^\s=#]+)\s*(?:=\s*)?([\s\S]*)$/.exec(raw);
@@ -150105,19 +152338,19 @@ function expandGlob(path2) {
150105
152338
  const next = [];
150106
152339
  for (const base2 of found) {
150107
152340
  if (!MAGIC.test(seg)) {
150108
- next.push(join51(base2, seg));
152341
+ next.push(join56(base2, seg));
150109
152342
  continue;
150110
152343
  }
150111
152344
  let names;
150112
152345
  try {
150113
- names = readdirSync11(base2);
152346
+ names = readdirSync14(base2);
150114
152347
  } catch {
150115
152348
  continue;
150116
152349
  }
150117
152350
  const re2 = segmentRe(seg);
150118
152351
  for (const name of names.sort()) {
150119
152352
  if (name.startsWith(".") && !seg.startsWith(".")) continue;
150120
- if (re2.test(name)) next.push(join51(base2, name));
152353
+ if (re2.test(name)) next.push(join56(base2, name));
150121
152354
  }
150122
152355
  }
150123
152356
  found = next;
@@ -150147,7 +152380,7 @@ function readSshConfig(opts) {
150147
152380
  }
150148
152381
  let text2;
150149
152382
  try {
150150
- text2 = readFileSync20(file2, "utf8");
152383
+ text2 = readFileSync23(file2, "utf8");
150151
152384
  } catch (err) {
150152
152385
  if (depth > 0 && err.code !== "ENOENT") notes.push(`${tildePath(file2, opts.home)} could not be read`);
150153
152386
  return;
@@ -150172,11 +152405,11 @@ function readSshConfig(opts) {
150172
152405
  }
150173
152406
  for (const arg of split2.args) {
150174
152407
  let path2 = arg;
150175
- if (path2.startsWith("~/") || process.platform === "win32" && path2.startsWith("~\\")) path2 = join51(opts.home, path2.slice(2));
152408
+ if (path2.startsWith("~/") || process.platform === "win32" && path2.startsWith("~\\")) path2 = join56(opts.home, path2.slice(2));
150176
152409
  else if (path2.startsWith("~")) {
150177
152410
  notes.push(`An Include of another user's folder (${where2(file2, n + 1)}) was not read`);
150178
152411
  continue;
150179
- } else if (!isAbsolute10(path2)) path2 = join51(opts.home, ".ssh", path2);
152412
+ } else if (!isAbsolute11(path2)) path2 = join56(opts.home, ".ssh", path2);
150180
152413
  for (const f of expandGlob(path2)) read(f, depth + 1);
150181
152414
  }
150182
152415
  }
@@ -150222,8 +152455,8 @@ function resolveAlias(alias, opts = {}) {
150222
152455
  var MAX_HOSTS = 100;
150223
152456
  var RESOLVE_AT_ONCE = 8;
150224
152457
  async function listSshHosts(opts = {}) {
150225
- const home = opts.home ?? homedir15();
150226
- const file2 = opts.configFile ?? join51(home, ".ssh", "config");
152458
+ const home = opts.home ?? homedir16();
152459
+ const file2 = opts.configFile ?? join56(home, ".ssh", "config");
150227
152460
  const read = readSshConfig({ file: file2, home });
150228
152461
  const notes = [...read.notes];
150229
152462
  const listed2 = read.hosts.slice(0, MAX_HOSTS);
@@ -151759,7 +153992,7 @@ function keepUnseen(ordered, kept) {
151759
153992
  }
151760
153993
 
151761
153994
  // packages/agent-host/src/links/stored.ts
151762
- import { randomBytes as randomBytes10 } from "node:crypto";
153995
+ import { randomBytes as randomBytes11 } from "node:crypto";
151763
153996
  function storeRegistry(store2) {
151764
153997
  return {
151765
153998
  list: () => store2.machines.listLinkedMachines(),
@@ -151775,7 +154008,7 @@ var HUB_SELF_ID = /^hub-[0-9a-f]{8}$/;
151775
154008
  function hubSelfId(store2) {
151776
154009
  const kept = store2.settings.appSetting(HUB_SELF_KEY);
151777
154010
  if (kept && HUB_SELF_ID.test(kept)) return kept;
151778
- const made = `hub-${randomBytes10(4).toString("hex")}`;
154011
+ const made = `hub-${randomBytes11(4).toString("hex")}`;
151779
154012
  store2.settings.updateAppSetting(HUB_SELF_KEY, (current) => current !== null && HUB_SELF_ID.test(current) ? current : made);
151780
154013
  return store2.settings.appSetting(HUB_SELF_KEY) ?? made;
151781
154014
  }
@@ -151896,8 +154129,8 @@ function reverseCalls(o) {
151896
154129
  }
151897
154130
 
151898
154131
  // packages/agent-host/src/lifecycle/work-job.ts
151899
- import { execFile as execFile10 } from "node:child_process";
151900
- import { promisify as promisify8 } from "node:util";
154132
+ import { execFile as execFile14 } from "node:child_process";
154133
+ import { promisify as promisify12 } from "node:util";
151901
154134
  var WORK_JOB_LIMITS = 8192;
151902
154135
  var JOIN_SCRIPT = (pid, limits) => `
151903
154136
  $ErrorActionPreference = 'Stop'
@@ -151937,7 +154170,7 @@ public static class CentraluWorkJob {
151937
154170
  '@
151938
154171
  [CentraluWorkJob]::Join(${pid}, ${limits})
151939
154172
  `;
151940
- var run3 = promisify8(execFile10);
154173
+ var run3 = promisify12(execFile14);
151941
154174
  var REAL_OS2 = {
151942
154175
  needed: endsWorkWithJob(),
151943
154176
  powershell: async (script) => {
@@ -151969,7 +154202,7 @@ function workJobLine(job) {
151969
154202
 
151970
154203
  // packages/agent-host/src/dev-services/broker-pty.ts
151971
154204
  import { spawn as startProcess } from "node:child_process";
151972
- import { existsSync as existsSync24 } from "node:fs";
154205
+ import { existsSync as existsSync27 } from "node:fs";
151973
154206
  import { createRequire as createRequire3 } from "node:module";
151974
154207
  import { createInterface as createInterface2 } from "node:readline";
151975
154208
  import { fileURLToPath as fileURLToPath7 } from "node:url";
@@ -151995,7 +154228,7 @@ function parseBrokerEvent(line) {
151995
154228
  }
151996
154229
  function brokerPath() {
151997
154230
  const path2 = fileURLToPath7(new URL("./pty-broker.mjs", import.meta.url));
151998
- if (!existsSync24(path2)) throw new Error(`terminal broker not found: ${path2}`);
154231
+ if (!existsSync27(path2)) throw new Error(`terminal broker not found: ${path2}`);
151999
154232
  return path2;
152000
154233
  }
152001
154234
  var BrokerPty = class {
@@ -152151,7 +154384,7 @@ function brokerPtyModule(broker, fallback = loadNodePty) {
152151
154384
  }
152152
154385
 
152153
154386
  // packages/agent-host/src/elevation.ts
152154
- import { execFile as execFile11 } from "node:child_process";
154387
+ import { execFile as execFile15 } from "node:child_process";
152155
154388
  function detectToken(o = {}) {
152156
154389
  const source = elevationSource(o.platform ?? HOST_PLATFORM, o.env ?? process.env);
152157
154390
  if (source.kind === "uid") {
@@ -152159,7 +154392,7 @@ function detectToken(o = {}) {
152159
154392
  return Promise.resolve({ elevated: getuid ? getuid() === 0 : null, logon: null });
152160
154393
  }
152161
154394
  const run4 = o.run ?? ((file2, args) => new Promise(
152162
- (resolve21, reject) => execFile11(file2, args, { windowsHide: true, timeout: 1e4, encoding: "utf8" }, (err, stdout) => err ? reject(err) : resolve21(stdout))
154395
+ (resolve21, reject) => execFile15(file2, args, { windowsHide: true, timeout: 1e4, encoding: "utf8" }, (err, stdout) => err ? reject(err) : resolve21(stdout))
152163
154396
  ));
152164
154397
  return run4(source.file, source.args).then(
152165
154398
  (out) => ({ elevated: elevatedFromGroups(out), logon: logonFromGroups(out) }),
@@ -152184,7 +154417,7 @@ var handedToken = launch.tokenOnStdin ? await readHandedToken(process.stdin).cat
152184
154417
  console.error(`[agent-host] the launcher did not hand over the token: ${e.message}`);
152185
154418
  process.exit(1);
152186
154419
  }) : launch.token;
152187
- var BUILD = true ? "bd398a1" : "dev";
154420
+ var BUILD = true ? "0a894cb" : "dev";
152188
154421
  var { values } = parseArgs({
152189
154422
  options: {
152190
154423
  port: { type: "string", default: "5175" },
@@ -152198,24 +154431,24 @@ var { values } = parseArgs({
152198
154431
  }
152199
154432
  });
152200
154433
  var control = launch.underKeeper ? new ControlChannel(process.stdin) : null;
152201
- var token = values.token || handedToken || randomBytes11(16).toString("hex");
154434
+ var token = values.token || handedToken || randomBytes12(16).toString("hex");
152202
154435
  var keeperToken = launch.underKeeper && !values.token && handedToken ? token : null;
152203
- var httpSecret = keeperToken ? deriveHttpSecret(keeperToken) : randomBytes11(32).toString("base64url");
154436
+ var httpSecret = keeperToken ? deriveHttpSecret(keeperToken) : randomBytes12(32).toString("base64url");
152204
154437
  start.at("data folder");
152205
154438
  var movedNote = null;
152206
154439
  function defaultDbPath() {
152207
154440
  const { dir, legacy } = chooseDataDir();
152208
154441
  movedNote = legacy && migrateLegacyDataDir(legacy, dir) ? `[agent-host] data folder moved: ${legacy} -> ${dir}` : null;
152209
- mkdirSync19(dir, { recursive: true });
154442
+ mkdirSync20(dir, { recursive: true });
152210
154443
  process.env.CC_DATA_DIR = dir;
152211
- return join52(dir, "store.db");
154444
+ return join57(dir, "store.db");
152212
154445
  }
152213
154446
  var dbPath = values.memory ? ":memory:" : values.db ?? defaultDbPath();
152214
154447
  var privateFolder = privateDataFolderFor(dbPath);
152215
154448
  var madePrivate = privateFolder ? keepDataFolderPrivate(privateFolder) : [];
152216
154449
  start.at("log");
152217
154450
  var stopLog = dbPath === ":memory:" ? () => {
152218
- } : teeStderrToFile(hostLogPath(dirname23(dbPath)));
154451
+ } : teeStderrToFile(hostLogPath(dirname26(dbPath)));
152219
154452
  if (dbPath !== ":memory:") console.error(startupBanner({ build: BUILD, db: dbPath, pid: process.pid }));
152220
154453
  if (movedNote) console.error(movedNote);
152221
154454
  if (madePrivate.length > 0) console.error(`[agent-host] data folder made private: ${madePrivate.join(", ")}`);
@@ -152262,7 +154495,7 @@ if (!lock.ok) {
152262
154495
  }
152263
154496
  var ownership = lock;
152264
154497
  process.on("exit", ownership.release);
152265
- if (dbPath !== ":memory:") clearStoppedMark(dirname23(dbPath));
154498
+ if (dbPath !== ":memory:") clearStoppedMark(dirname26(dbPath));
152266
154499
  start.at("hold signals");
152267
154500
  var signals = holdSignals(process, (line) => console.error(line));
152268
154501
  start.at("store");
@@ -152279,7 +154512,7 @@ start.at("work job");
152279
154512
  var workJobSaid = workJobLine(await workJob);
152280
154513
  if (workJobSaid) console.error(workJobSaid);
152281
154514
  start.at("keeper children");
152282
- var held2 = launch.underKeeper ? await connectHeldChildren(dirname23(dbPath)) : null;
154515
+ var held2 = launch.underKeeper ? await connectHeldChildren(dirname26(dbPath)) : null;
152283
154516
  start.at("services");
152284
154517
  var screens = new Gate();
152285
154518
  var events = new Gate();
@@ -152296,7 +154529,7 @@ var mgr = new SessionManager(
152296
154529
  },
152297
154530
  (sessionId) => bridgeAddress(launch.frontDoor, port, token, sessionId),
152298
154531
  // Worktrees are created next to the data folder — dev and the packaged app never touch each other's worktrees
152299
- join52(dirname23(dbPath), "worktrees"),
154532
+ join57(dirname26(dbPath), "worktrees"),
152300
154533
  held2 ? { processes: held2.processes, keptSessions: held2.kept.sessionIds } : {}
152301
154534
  );
152302
154535
  mgr.useProjectsHome(() => projectsHome());
@@ -152390,6 +154623,8 @@ mail.onChange((c) => events.push({ type: "mail_changed", threadId: c.threadId, t
152390
154623
  mgr.useMail({
152391
154624
  askedBy: (id3) => askedBy(askedByHost, mailTurns, id3),
152392
154625
  sessionGone: (id3) => void mail.sessionGone(id3),
154626
+ handOver: (from, to, which) => mail.handOver(from, to, which),
154627
+ handOverPreview: (from, to) => mail.handOverPreview(from, to),
152393
154628
  openWork: (id3) => mail.openWorkOf(id3),
152394
154629
  sessionChanged: (id3) => mailDelivery.poke(id3),
152395
154630
  sessionEvent: (e, ended) => mailTurns.observe(e, ended),
@@ -152444,7 +154679,7 @@ var installByDefault = installsByDefault({
152444
154679
  env: process.env
152445
154680
  });
152446
154681
  var { auto: autoUpdateCheck, autoApply: autoApplyUpdates } = updateSwitches(store.settings, { installByDefault });
152447
- var themes = new ThemeFiles(join52(dataRoot(), "themes"), () => events.push({ type: "themes_changed" }));
154682
+ var themes = new ThemeFiles(join57(dataRoot(), "themes"), () => events.push({ type: "themes_changed" }));
152448
154683
  await themes.start().catch((e) => console.error(`[themes] the themes folder is unavailable: ${e.message}`));
152449
154684
  var updates = new UpdateService((status) => events.push({ type: "update_status", status }), {
152450
154685
  // A host from source never installs by itself, whatever is stored (#1501 review, `installsByItself`)
@@ -152758,13 +154993,13 @@ var endings = wireEndings({
152758
154993
  heldChildren: held2 !== null,
152759
154994
  leave: (mode, handOver) => void shutdown.leave(mode, handOver),
152760
154995
  force: (why) => shutdown.force(why),
152761
- record: crashRecorder(join52(dirname23(dbPath), "host-errors.log"), (line) => console.error(line)),
154996
+ record: crashRecorder(join57(dirname26(dbPath), "host-errors.log"), (line) => console.error(line)),
152762
154997
  stdin: process.stdin,
152763
154998
  watchParent: values["watch-parent"] === true,
152764
154999
  log: (line) => console.error(line),
152765
155000
  // The person quit Centralu here: a linked hub must not start a host in this folder by itself (#1143)
152766
155001
  quit: () => {
152767
- if (dbPath !== ":memory:") markQuit(dirname23(dbPath));
155002
+ if (dbPath !== ":memory:") markQuit(dirname26(dbPath));
152768
155003
  }
152769
155004
  });
152770
155005
  stopNow = endings.stopAsked;