@pi-unipi/unipi 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/README.md +10 -134
  3. package/package.json +23 -23
  4. package/packages/app-bridge/package.json +2 -2
  5. package/packages/ask-user/package.json +2 -2
  6. package/packages/autocomplete/package.json +1 -1
  7. package/packages/background-tasks/package.json +2 -2
  8. package/packages/btw/package.json +2 -2
  9. package/packages/compactor/package.json +3 -3
  10. package/packages/core/README.md +4 -1
  11. package/packages/core/index.ts +1 -0
  12. package/packages/core/package.json +1 -1
  13. package/packages/core/src/settings/snapshot.ts +247 -0
  14. package/packages/dream/package.json +2 -2
  15. package/packages/footer/package.json +3 -3
  16. package/packages/fusion/package.json +4 -4
  17. package/packages/info-screen/package.json +2 -2
  18. package/packages/input-shortcuts/package.json +2 -2
  19. package/packages/kanboard/package.json +7 -7
  20. package/packages/long-horizon/README.md +1 -3
  21. package/packages/long-horizon/index.ts +131 -27
  22. package/packages/long-horizon/package.json +2 -2
  23. package/packages/long-horizon/src/owner.ts +33 -1
  24. package/packages/long-horizon/src/session-state.ts +366 -0
  25. package/packages/long-horizon/src/tools/graph.ts +10 -6
  26. package/packages/mcp/package.json +2 -2
  27. package/packages/memory/package.json +3 -3
  28. package/packages/notify/package.json +2 -2
  29. package/packages/skill-registry/package.json +2 -2
  30. package/packages/subagents/package.json +3 -3
  31. package/packages/subagents/src/index.ts +29 -5
  32. package/packages/unipi/bundled.js +950 -464
  33. package/packages/unipi/index.ts +18 -1
  34. package/packages/updater/package.json +2 -2
  35. package/packages/utility/package.json +2 -2
  36. package/packages/watchdog/package.json +3 -3
  37. package/packages/web-api/package.json +2 -2
  38. package/packages/workflow/package.json +2 -2
@@ -4250,8 +4250,183 @@ var init_schema = __esm({
4250
4250
  }
4251
4251
  });
4252
4252
 
4253
+ // packages/core/src/settings/snapshot.ts
4254
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync5, renameSync as renameSync3, writeFileSync as writeFileSync7 } from "node:fs";
4255
+ import { dirname as dirname4 } from "node:path";
4256
+ import { fileURLToPath } from "node:url";
4257
+ function coreVersion() {
4258
+ try {
4259
+ const pkg = new URL("../../package.json", import.meta.url);
4260
+ const parsed = JSON.parse(readFileSync5(fileURLToPath(pkg), "utf8"));
4261
+ return typeof parsed.version === "string" ? parsed.version : void 0;
4262
+ } catch {
4263
+ return void 0;
4264
+ }
4265
+ }
4266
+ function snapshotField(field) {
4267
+ const base = {
4268
+ key: field.key,
4269
+ label: field.label,
4270
+ ...field.description !== void 0 ? { description: field.description } : {},
4271
+ ...field.scopes !== void 0 ? { scopes: [...field.scopes] } : {},
4272
+ // validate stays in pi; the flag keeps the fact visible in the catalog.
4273
+ validated: "validate" in field && typeof field.validate === "function"
4274
+ };
4275
+ try {
4276
+ switch (field.type) {
4277
+ case "boolean":
4278
+ return { ...base, type: "boolean" };
4279
+ case "enum":
4280
+ return {
4281
+ ...base,
4282
+ type: "enum",
4283
+ options: field.options.map(enumOption),
4284
+ ...field.plainOptions !== void 0 ? { plainOptions: field.plainOptions } : {},
4285
+ ...field.allowCustom !== void 0 ? { allowCustom: field.allowCustom } : {},
4286
+ ...field.hint !== void 0 ? { hint: field.hint } : {},
4287
+ ...field.scope !== void 0 ? { scope: field.scope } : {},
4288
+ ...field.clearable !== void 0 ? { clearable: field.clearable } : {}
4289
+ };
4290
+ case "string":
4291
+ case "secret":
4292
+ return {
4293
+ ...base,
4294
+ type: field.type,
4295
+ ...field.emptyLabel !== void 0 ? { emptyLabel: field.emptyLabel } : {},
4296
+ ...field.hint !== void 0 ? { hint: field.hint } : {}
4297
+ };
4298
+ case "number":
4299
+ return {
4300
+ ...base,
4301
+ type: "number",
4302
+ ...field.min !== void 0 ? { min: field.min } : {},
4303
+ ...field.max !== void 0 ? { max: field.max } : {},
4304
+ ...field.unit !== void 0 ? { unit: field.unit } : {},
4305
+ ...field.zeroLabel !== void 0 ? { zeroLabel: field.zeroLabel } : {},
4306
+ ...field.hint !== void 0 ? { hint: field.hint } : {}
4307
+ };
4308
+ case "model":
4309
+ return {
4310
+ ...base,
4311
+ type: "model",
4312
+ ...field.provider !== void 0 ? { provider: field.provider } : {},
4313
+ ...field.capability !== void 0 ? { capability: field.capability } : {},
4314
+ ...field.emptyLabel !== void 0 ? { emptyLabel: field.emptyLabel } : {},
4315
+ ...field.presets !== void 0 ? { presets: [...field.presets] } : {},
4316
+ ...field.presetsByProvider !== void 0 ? { presetsByProvider: Object.fromEntries(Object.entries(field.presetsByProvider).map(([k, v]) => [k, [...v]])) } : {},
4317
+ ...field.emptyOption !== void 0 ? { emptyOption: field.emptyOption } : {},
4318
+ ...field.providerKey !== void 0 ? { providerKey: field.providerKey } : {},
4319
+ // The filter is a function; only the fact that the catalog is filtered survives.
4320
+ ...field.filter !== void 0 ? { filtered: true } : {}
4321
+ };
4322
+ case "multiselect":
4323
+ return {
4324
+ ...base,
4325
+ type: "multiselect",
4326
+ options: field.options.map(enumOption),
4327
+ ...field.plainOptions !== void 0 ? { plainOptions: field.plainOptions } : {},
4328
+ ...field.emptyLabel !== void 0 ? { emptyLabel: field.emptyLabel } : {}
4329
+ };
4330
+ case "order":
4331
+ return { ...base, type: "order", items: field.items().map((i) => ({ value: i.value, label: i.label })) };
4332
+ case "page":
4333
+ return { ...base, type: "page", sections: snapshotSections(typeof field.sections === "function" ? field.sections() : field.sections) };
4334
+ case "action":
4335
+ return { ...base, type: "action", command: field.command };
4336
+ default:
4337
+ return void 0;
4338
+ }
4339
+ } catch {
4340
+ return void 0;
4341
+ }
4342
+ }
4343
+ function snapshotSection(section3) {
4344
+ try {
4345
+ const fields = [];
4346
+ for (const field of section3.fields) {
4347
+ const snap = snapshotField(field);
4348
+ if (snap) fields.push(snap);
4349
+ }
4350
+ return {
4351
+ title: section3.title,
4352
+ ...section3.description !== void 0 ? { description: section3.description } : {},
4353
+ ...section3.advanced !== void 0 ? { advanced: section3.advanced } : {},
4354
+ fields
4355
+ };
4356
+ } catch {
4357
+ return void 0;
4358
+ }
4359
+ }
4360
+ function snapshotSections(sections) {
4361
+ const out = [];
4362
+ for (const section3 of sections) {
4363
+ const snap = snapshotSection(section3);
4364
+ if (snap) out.push(snap);
4365
+ }
4366
+ return out;
4367
+ }
4368
+ function snapshotNamespace(definition) {
4369
+ return {
4370
+ namespace: definition.namespace,
4371
+ label: definition.label,
4372
+ defaults: definition.defaults,
4373
+ projectOverrides: definition.projectOverrides !== false,
4374
+ mirrored: typeof definition.onSet === "function",
4375
+ sections: snapshotSections(definition.schema ?? [])
4376
+ };
4377
+ }
4378
+ function buildSettingsSnapshot() {
4379
+ const version = coreVersion();
4380
+ return {
4381
+ version: 1,
4382
+ writtenAt: (/* @__PURE__ */ new Date()).toISOString(),
4383
+ ...version !== void 0 ? { unipiVersion: version } : {},
4384
+ namespaces: listSettingsDefinitions().map(snapshotNamespace)
4385
+ };
4386
+ }
4387
+ function writeSettingsSnapshot() {
4388
+ try {
4389
+ const snapshot2 = buildSettingsSnapshot();
4390
+ const file = joinPath(unipiConfigRoot(), SNAPSHOT_FILE);
4391
+ mkdirSync8(dirname4(file), { recursive: true });
4392
+ const previous = readPrevious(file);
4393
+ if (previous && sameExceptWrittenAt(previous, snapshot2)) return;
4394
+ const tmp = `${file}.tmp`;
4395
+ writeFileSync7(tmp, `${JSON.stringify(snapshot2, null, 2)}
4396
+ `);
4397
+ renameSync3(tmp, file);
4398
+ } catch {
4399
+ }
4400
+ }
4401
+ function joinPath(root, file) {
4402
+ return root.endsWith("/") ? `${root}${file}` : `${root}/${file}`;
4403
+ }
4404
+ function readPrevious(file) {
4405
+ try {
4406
+ const parsed = JSON.parse(readFileSync5(file, "utf8"));
4407
+ return parsed && typeof parsed === "object" ? parsed : void 0;
4408
+ } catch {
4409
+ return void 0;
4410
+ }
4411
+ }
4412
+ function sameExceptWrittenAt(a, b) {
4413
+ const { writtenAt: _a, ...restA } = a;
4414
+ const { writtenAt: _b, ...restB } = b;
4415
+ return JSON.stringify(restA) === JSON.stringify(restB);
4416
+ }
4417
+ var SNAPSHOT_FILE;
4418
+ var init_snapshot = __esm({
4419
+ "packages/core/src/settings/snapshot.ts"() {
4420
+ "use strict";
4421
+ init_engine();
4422
+ init_paths2();
4423
+ init_schema();
4424
+ SNAPSHOT_FILE = "settings-schema.json";
4425
+ }
4426
+ });
4427
+
4253
4428
  // packages/core/src/settings/catalog.ts
4254
- import { readFileSync as readFileSync5 } from "node:fs";
4429
+ import { readFileSync as readFileSync6 } from "node:fs";
4255
4430
  import { homedir as homedir6 } from "node:os";
4256
4431
  import { join as join9 } from "node:path";
4257
4432
  function defaultModelCatalogPath() {
@@ -4276,7 +4451,7 @@ function loadModelCatalog(path31 = defaultModelCatalogPath()) {
4276
4451
  }
4277
4452
  function loadModelCatalogEntries(path31 = defaultModelCatalogPath()) {
4278
4453
  const isDefault = path31 === defaultModelCatalogPath();
4279
- const fromFile = parseModelCatalogEntries(() => readFileSync5(path31, "utf8"));
4454
+ const fromFile = parseModelCatalogEntries(() => readFileSync6(path31, "utf8"));
4280
4455
  if (!isDefault) return fromFile;
4281
4456
  const cached2 = readModelCache().map((m) => ({
4282
4457
  id: modelRef(m),
@@ -5405,7 +5580,7 @@ var init_open = __esm({
5405
5580
  });
5406
5581
 
5407
5582
  // packages/core/src/attach/detect.ts
5408
- import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync4 } from "node:fs";
5583
+ import { existsSync as existsSync6, readFileSync as readFileSync7, statSync as statSync4 } from "node:fs";
5409
5584
  import { homedir as homedir7 } from "node:os";
5410
5585
  import { basename, extname } from "node:path";
5411
5586
  function tokenFor(a) {
@@ -5474,7 +5649,7 @@ function tokenize(text, existing, size = (p) => statSync4(p).size, exists) {
5474
5649
  function stillReferenced(text, attachments) {
5475
5650
  return attachments.filter((a) => text.includes(tokenFor(a)));
5476
5651
  }
5477
- function expandForSubmit(text, attachments, read = (p) => readFileSync6(p)) {
5652
+ function expandForSubmit(text, attachments, read = (p) => readFileSync7(p)) {
5478
5653
  const used = stillReferenced(text, attachments).sort((a, b) => text.indexOf(tokenFor(a)) - text.indexOf(tokenFor(b)));
5479
5654
  const images = [];
5480
5655
  let out = text;
@@ -5753,7 +5928,7 @@ var init_move = __esm({
5753
5928
  });
5754
5929
 
5755
5930
  // packages/core/src/hints/store.ts
5756
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, unlinkSync as unlinkSync2 } from "node:fs";
5931
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync8, unlinkSync as unlinkSync2 } from "node:fs";
5757
5932
  function emptyStore() {
5758
5933
  return {
5759
5934
  counts: {},
@@ -5764,7 +5939,7 @@ function emptyStore() {
5764
5939
  function loadHintStore() {
5765
5940
  if (cache2) return cache2;
5766
5941
  try {
5767
- const raw = readFileSync7(statePath("hints", "hints.json", "global"), "utf-8");
5942
+ const raw = readFileSync8(statePath("hints", "hints.json", "global"), "utf-8");
5768
5943
  const parsed = JSON.parse(raw);
5769
5944
  if (typeof parsed === "object" && parsed !== null) {
5770
5945
  cache2 = {
@@ -5783,7 +5958,7 @@ function loadHintStore() {
5783
5958
  function saveStore() {
5784
5959
  if (!cache2) return;
5785
5960
  try {
5786
- writeFileSync7(statePath("hints", "hints.json", "global"), JSON.stringify(cache2, null, 2));
5961
+ writeFileSync8(statePath("hints", "hints.json", "global"), JSON.stringify(cache2, null, 2));
5787
5962
  } catch {
5788
5963
  }
5789
5964
  }
@@ -5818,7 +5993,7 @@ function resetHintHistory() {
5818
5993
  store.counts = {};
5819
5994
  store.learned = [];
5820
5995
  try {
5821
- writeFileSync7(statePath("hints", "hints.json", "global"), JSON.stringify(store, null, 2));
5996
+ writeFileSync8(statePath("hints", "hints.json", "global"), JSON.stringify(store, null, 2));
5822
5997
  } catch {
5823
5998
  }
5824
5999
  }
@@ -6948,7 +7123,7 @@ var init_lines = __esm({
6948
7123
  });
6949
7124
 
6950
7125
  // packages/core/src/hints/index.ts
6951
- import { fileURLToPath } from "node:url";
7126
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
6952
7127
  import {
6953
7128
  encodeKitty,
6954
7129
  deleteKittyImage,
@@ -7255,7 +7430,7 @@ function installHints(pi) {
7255
7430
  try {
7256
7431
  const v = getInstalledPackageVersion(cwd, "@pi-unipi/unipi");
7257
7432
  if (v !== "0.0.0") return v;
7258
- const thisDir = fileURLToPath(new URL(".", import.meta.url));
7433
+ const thisDir = fileURLToPath2(new URL(".", import.meta.url));
7259
7434
  return getInstalledPackageVersion(thisDir, "@pi-unipi/unipi");
7260
7435
  } catch {
7261
7436
  return "0.0.0";
@@ -7972,6 +8147,7 @@ __export(core_exports, {
7972
8147
  boundModelOutput: () => boundModelOutput,
7973
8148
  boxInnerWidth: () => boxInnerWidth,
7974
8149
  brailleArea: () => brailleArea,
8150
+ buildSettingsSnapshot: () => buildSettingsSnapshot,
7975
8151
  bus: () => bus,
7976
8152
  callCommandRunner: () => callCommandRunner,
7977
8153
  centerTo: () => centerTo,
@@ -8259,7 +8435,8 @@ __export(core_exports, {
8259
8435
  workspaceRoot: () => workspaceRoot,
8260
8436
  writeFile: () => writeFile,
8261
8437
  writeJson: () => writeJson,
8262
- writeModelCache: () => writeModelCache
8438
+ writeModelCache: () => writeModelCache,
8439
+ writeSettingsSnapshot: () => writeSettingsSnapshot
8263
8440
  });
8264
8441
  var init_core = __esm({
8265
8442
  "packages/core/index.ts"() {
@@ -8298,6 +8475,7 @@ var init_core = __esm({
8298
8475
  init_paths2();
8299
8476
  init_engine();
8300
8477
  init_schema();
8478
+ init_snapshot();
8301
8479
  init_hub();
8302
8480
  init_open();
8303
8481
  init_detect();
@@ -8994,7 +9172,7 @@ var init_schema2 = __esm({
8994
9172
 
8995
9173
  // packages/compactor/src/config/manager.ts
8996
9174
  import { homedir as homedir45 } from "node:os";
8997
- import { join as join74 } from "node:path";
9175
+ import { join as join75 } from "node:path";
8998
9176
  function translateLegacyConfig(raw) {
8999
9177
  const out = {};
9000
9178
  for (const key of Object.keys(DEFAULT_COMPACTOR_CONFIG)) {
@@ -9165,7 +9343,7 @@ var init_manager = __esm({
9165
9343
  }
9166
9344
  ]
9167
9345
  });
9168
- COMPACTOR_CONFIG_PATH = join74(homedir45(), ".unipi", "config", "compactor", "config.json");
9346
+ COMPACTOR_CONFIG_PATH = join75(homedir45(), ".unipi", "config", "compactor", "config.json");
9169
9347
  isRecord12 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
9170
9348
  }
9171
9349
  });
@@ -9395,12 +9573,12 @@ init_core();
9395
9573
 
9396
9574
  // packages/utility/src/index.ts
9397
9575
  init_core();
9398
- import { dirname as dirname6 } from "node:path";
9399
- import { fileURLToPath as fileURLToPath2 } from "node:url";
9576
+ import { dirname as dirname7 } from "node:path";
9577
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
9400
9578
  import { ToolExecutionComponent as ToolExecutionComponent2 } from "@earendil-works/pi-coding-agent";
9401
9579
 
9402
9580
  // packages/utility/src/browser/state.ts
9403
- import { existsSync as existsSync7, readFileSync as readFileSync8 } from "node:fs";
9581
+ import { existsSync as existsSync7, readFileSync as readFileSync9 } from "node:fs";
9404
9582
  import { homedir as homedir8 } from "node:os";
9405
9583
  import { join as join11 } from "node:path";
9406
9584
  import { execFile } from "node:child_process";
@@ -9410,7 +9588,7 @@ function hostDir(env = process.env) {
9410
9588
  function readHostConfig(path31) {
9411
9589
  if (!existsSync7(path31)) return { kind: "missing" };
9412
9590
  try {
9413
- const raw = readFileSync8(path31, "utf8");
9591
+ const raw = readFileSync9(path31, "utf8");
9414
9592
  const parsed = JSON.parse(raw);
9415
9593
  if (!parsed || typeof parsed !== "object") return { kind: "malformed" };
9416
9594
  return { kind: "ok", config: parsed };
@@ -9468,7 +9646,7 @@ function browserSectionText(running) {
9468
9646
  }
9469
9647
  function readAgentEndpoint(env = process.env) {
9470
9648
  try {
9471
- const raw = JSON.parse(readFileSync8(join11(hostDir(env), "browser-agent.json"), "utf8"));
9649
+ const raw = JSON.parse(readFileSync9(join11(hostDir(env), "browser-agent.json"), "utf8"));
9472
9650
  if (typeof raw.port !== "number" || raw.port <= 0 || raw.port > 65535) return void 0;
9473
9651
  return { port: raw.port, profile: typeof raw.profile === "string" ? raw.profile : "default", ...typeof raw.pid === "number" ? { pid: raw.pid } : {} };
9474
9652
  } catch {
@@ -10004,7 +10182,7 @@ Removed ${result2.removed} item(s), ${formatBytes2(result2.bytes)}.${failed}`);
10004
10182
 
10005
10183
  // packages/utility/src/rename/index.ts
10006
10184
  init_core();
10007
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync8 } from "node:fs";
10185
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync9 } from "node:fs";
10008
10186
  import { homedir as homedir11 } from "node:os";
10009
10187
  import { join as join14 } from "node:path";
10010
10188
 
@@ -10464,7 +10642,7 @@ function debugLog(line) {
10464
10642
  if (process.env.UNIPI_DEBUG_RENAME !== "1") return;
10465
10643
  try {
10466
10644
  const dir = join14(homedir11(), ".unipi", "logs");
10467
- mkdirSync8(dir, { recursive: true });
10645
+ mkdirSync9(dir, { recursive: true });
10468
10646
  appendFileSync2(join14(dir, "rename.log"), `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
10469
10647
  `);
10470
10648
  } catch {
@@ -10607,12 +10785,12 @@ function registerAutoRename(pi) {
10607
10785
  // packages/utility/src/move/index.ts
10608
10786
  init_core();
10609
10787
  import { execFileSync as execFileSync3 } from "node:child_process";
10610
- import { appendFileSync as appendFileSync3, copyFileSync as copyFileSync2, existsSync as existsSync13, mkdirSync as mkdirSync10, realpathSync as realpathSync3 } from "node:fs";
10788
+ import { appendFileSync as appendFileSync3, copyFileSync as copyFileSync2, existsSync as existsSync13, mkdirSync as mkdirSync11, realpathSync as realpathSync3 } from "node:fs";
10611
10789
  import { homedir as homedir13 } from "node:os";
10612
- import { basename as basename3, dirname as dirname4, join as join17, resolve as resolve6 } from "node:path";
10790
+ import { basename as basename3, dirname as dirname5, join as join17, resolve as resolve6 } from "node:path";
10613
10791
 
10614
10792
  // packages/utility/src/move/pi-state.ts
10615
- import { constants as constants2, copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync9, readdirSync as readdirSync4, readFileSync as readFileSync10, rmdirSync, statSync as statSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync9, openSync, readSync, closeSync, linkSync, renameSync as renameSync4 } from "node:fs";
10793
+ import { constants as constants2, copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync10, readdirSync as readdirSync4, readFileSync as readFileSync11, rmdirSync, statSync as statSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync10, openSync, readSync, closeSync, linkSync, renameSync as renameSync5 } from "node:fs";
10616
10794
  import { createReadStream, createWriteStream } from "node:fs";
10617
10795
  import { pipeline } from "node:stream/promises";
10618
10796
  import { homedir as homedir12 } from "node:os";
@@ -10691,14 +10869,14 @@ var piSessionsHandler = {
10691
10869
  return;
10692
10870
  }
10693
10871
  const original = header(from);
10694
- mkdirSync9(targetDir, { recursive: true });
10872
+ mkdirSync10(targetDir, { recursive: true });
10695
10873
  ctx.log({ area: "pi-sessions", action: "mkdir", from: "", to: targetDir, result: "ok" });
10696
10874
  const temporary = `${to}.move-${process.pid}-${Date.now()}`;
10697
10875
  try {
10698
10876
  if (original.cwd === ctx.oldRoot) {
10699
10877
  const parsed = JSON.parse(original.line.replace(/^\uFEFF/, ""));
10700
10878
  parsed.cwd = ctx.newRoot;
10701
- writeFileSync9(temporary, `${JSON.stringify(parsed)}
10879
+ writeFileSync10(temporary, `${JSON.stringify(parsed)}
10702
10880
  `, { flag: "wx", mode: statSync6(from).mode });
10703
10881
  ctx.log({ area: "pi-sessions", action: "rewrite-header", from, to: temporary, result: "ok", originalHeader: original.line });
10704
10882
  await pipeline(createReadStream(from, { start: original.offset }), createWriteStream(temporary, { flags: "a" }));
@@ -10732,7 +10910,7 @@ var piTrustHandler = {
10732
10910
  scan(ctx) {
10733
10911
  const file = join15(agentDir(), "trust.json");
10734
10912
  if (!existsSync11(file)) return [];
10735
- const data = JSON.parse(readFileSync10(file, "utf8"));
10913
+ const data = JSON.parse(readFileSync11(file, "utf8"));
10736
10914
  if (!Object.hasOwn(data, ctx.oldRoot)) return [];
10737
10915
  return [{
10738
10916
  area: "pi-trust",
@@ -10740,19 +10918,19 @@ var piTrustHandler = {
10740
10918
  apply() {
10741
10919
  if (ctx.dryRun) return;
10742
10920
  const lock = `${file}.lock`;
10743
- mkdirSync9(lock);
10921
+ mkdirSync10(lock);
10744
10922
  ctx.log({ area: "pi-trust", action: "lock", from: "", to: lock, result: "ok" });
10745
10923
  const temporary = `${file}.move-${process.pid}`;
10746
10924
  try {
10747
- const current = JSON.parse(readFileSync10(file, "utf8"));
10925
+ const current = JSON.parse(readFileSync11(file, "utf8"));
10748
10926
  if (!Object.hasOwn(current, ctx.oldRoot)) return;
10749
10927
  ctx.backup(file);
10750
10928
  current[ctx.newRoot] = current[ctx.oldRoot];
10751
10929
  delete current[ctx.oldRoot];
10752
- writeFileSync9(temporary, `${JSON.stringify(current, null, 2)}
10930
+ writeFileSync10(temporary, `${JSON.stringify(current, null, 2)}
10753
10931
  `, { flag: "wx", mode: statSync6(file).mode });
10754
10932
  ctx.log({ area: "pi-trust", action: "write", from: file, to: temporary, result: "ok" });
10755
- renameSync4(temporary, file);
10933
+ renameSync5(temporary, file);
10756
10934
  ctx.log({ area: "pi-trust", action: "replace-key", from: ctx.oldRoot, to: ctx.newRoot, result: "ok" });
10757
10935
  } finally {
10758
10936
  if (existsSync11(temporary)) {
@@ -10769,7 +10947,7 @@ var piTrustHandler = {
10769
10947
 
10770
10948
  // packages/utility/src/move/git.ts
10771
10949
  import { execFileSync as execFileSync2 } from "node:child_process";
10772
- import { existsSync as existsSync12, readFileSync as readFileSync11, statSync as statSync7 } from "node:fs";
10950
+ import { existsSync as existsSync12, readFileSync as readFileSync12, statSync as statSync7 } from "node:fs";
10773
10951
  import { join as join16 } from "node:path";
10774
10952
  var gitWorktreesHandler = {
10775
10953
  id: "git-worktrees",
@@ -10784,7 +10962,7 @@ var gitWorktreesHandler = {
10784
10962
  const entries2 = execFileSync2("git", ["-C", ctx.newRoot, "worktree", "list", "--porcelain"], { encoding: "utf8" }).matchAll(/^worktree (.+)$/gm);
10785
10963
  for (const [, root] of entries2) {
10786
10964
  const dotgit = join16(root, ".git");
10787
- if (existsSync12(dotgit) && statSync7(dotgit).isFile() && readFileSync11(dotgit, "utf8").includes(`${ctx.oldRoot}/`)) return true;
10965
+ if (existsSync12(dotgit) && statSync7(dotgit).isFile() && readFileSync12(dotgit, "utf8").includes(`${ctx.oldRoot}/`)) return true;
10788
10966
  }
10789
10967
  } catch {
10790
10968
  return false;
@@ -10838,7 +11016,7 @@ function createMoveContext(oldRoot, newRoot, dryRun) {
10838
11016
  const log = (entry) => {
10839
11017
  entries2.push(entry);
10840
11018
  if (dryRun) return;
10841
- mkdirSync10(dirname4(logFile), { recursive: true });
11019
+ mkdirSync11(dirname5(logFile), { recursive: true });
10842
11020
  appendFileSync3(logFile, `${JSON.stringify(entry)}
10843
11021
  `);
10844
11022
  };
@@ -10850,7 +11028,7 @@ function createMoveContext(oldRoot, newRoot, dryRun) {
10850
11028
  backup(file) {
10851
11029
  if (dryRun || !existsSync13(file) || backedUp.has(file)) return;
10852
11030
  const destination = join17(backupRoot, resolve6(file).replace(/^[/\\]/, ""));
10853
- mkdirSync10(dirname4(destination), { recursive: true });
11031
+ mkdirSync11(dirname5(destination), { recursive: true });
10854
11032
  copyFileSync2(file, destination);
10855
11033
  backedUp.add(file);
10856
11034
  log({ area: "backup", action: "copy", from: file, to: destination, result: "ok" });
@@ -11511,7 +11689,7 @@ function registerSummarizeCommand(pi) {
11511
11689
  }
11512
11690
 
11513
11691
  // packages/utility/src/attach/index.ts
11514
- import { readFileSync as readFileSync12 } from "node:fs";
11692
+ import { readFileSync as readFileSync13 } from "node:fs";
11515
11693
  import { getCapabilities as getCapabilities2, Image, Key as Key8, matchesKey as matchesKey7, truncateToWidth as truncateToWidth11 } from "@earendil-works/pi-tui";
11516
11694
  init_core();
11517
11695
  var ATTACHMENTS_ENTRY = "unipi:attachments";
@@ -11537,7 +11715,7 @@ function attachmentView(items, theme, preview, hint) {
11537
11715
  if (preview && canPreview()) {
11538
11716
  for (const a of items.filter((i) => i.kind === "image").slice(0, PREVIEW_MAX)) {
11539
11717
  try {
11540
- images.push(new Image(readFileSync12(a.path).toString("base64"), a.mimeType ?? "image/png", { fallbackColor: (s) => theme.fg("muted", s) }, { maxHeightCells: PREVIEW_ROWS, maxWidthCells: 32, filename: a.name }));
11718
+ images.push(new Image(readFileSync13(a.path).toString("base64"), a.mimeType ?? "image/png", { fallbackColor: (s) => theme.fg("muted", s) }, { maxHeightCells: PREVIEW_ROWS, maxWidthCells: 32, filename: a.name }));
11541
11719
  } catch {
11542
11720
  }
11543
11721
  }
@@ -13134,12 +13312,12 @@ import {
13134
13312
  } from "@earendil-works/pi-coding-agent";
13135
13313
 
13136
13314
  // packages/utility/src/render/pi-settings.ts
13137
- import { readFileSync as readFileSync15 } from "node:fs";
13315
+ import { readFileSync as readFileSync16 } from "node:fs";
13138
13316
  import { homedir as homedir15 } from "node:os";
13139
13317
  import { join as join22 } from "node:path";
13140
13318
  function readJson4(file) {
13141
13319
  try {
13142
- return JSON.parse(readFileSync15(file, "utf-8"));
13320
+ return JSON.parse(readFileSync16(file, "utf-8"));
13143
13321
  } catch {
13144
13322
  return {};
13145
13323
  }
@@ -14958,7 +15136,7 @@ registerSettings({
14958
15136
  });
14959
15137
 
14960
15138
  // packages/utility/src/index.ts
14961
- var VERSION = getPackageVersion(dirname6(fileURLToPath2(import.meta.url)));
15139
+ var VERSION = getPackageVersion(dirname7(fileURLToPath3(import.meta.url)));
14962
15140
  var ALL_COMMANDS = [
14963
15141
  UTILITY_COMMANDS.CONTINUE,
14964
15142
  UTILITY_COMMANDS.RETRY,
@@ -15037,12 +15215,12 @@ function src_default(pi) {
15037
15215
  // packages/workflow/index.ts
15038
15216
  init_core();
15039
15217
  import { tmpdir as tmpdir4 } from "node:os";
15040
- import { dirname as dirname8 } from "node:path";
15041
- import { fileURLToPath as fileURLToPath3 } from "node:url";
15218
+ import { dirname as dirname9 } from "node:path";
15219
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
15042
15220
 
15043
15221
  // packages/workflow/src/permission/index.ts
15044
15222
  init_core();
15045
- import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync13 } from "node:fs";
15223
+ import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync14 } from "node:fs";
15046
15224
  import { homedir as homedir18, tmpdir as tmpdir3 } from "node:os";
15047
15225
 
15048
15226
  // packages/workflow/src/status.ts
@@ -15643,7 +15821,7 @@ function debugLog2(line) {
15643
15821
  if (process.env.UNIPI_DEBUG_PERMISSION !== "1") return;
15644
15822
  try {
15645
15823
  const dir = `${homedir18()}/.unipi/logs`;
15646
- mkdirSync13(dir, { recursive: true });
15824
+ mkdirSync14(dir, { recursive: true });
15647
15825
  appendFileSync4(`${dir}/permission.log`, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
15648
15826
  `);
15649
15827
  } catch {
@@ -15790,8 +15968,8 @@ function registerPermissionModes(pi, controller) {
15790
15968
  import { join as join24, resolve as resolve10, sep as sep3 } from "node:path";
15791
15969
 
15792
15970
  // packages/workflow/src/plan/state.ts
15793
- import { mkdirSync as mkdirSync14 } from "node:fs";
15794
- import { dirname as dirname7, join as join23, resolve as resolve9 } from "node:path";
15971
+ import { mkdirSync as mkdirSync15 } from "node:fs";
15972
+ import { dirname as dirname8, join as join23, resolve as resolve9 } from "node:path";
15795
15973
  var PLAN_STATE_ENTRY = "unipi:plan-mode";
15796
15974
  var PLAN_MESSAGE_TYPE = "unipi:plan-mode-message";
15797
15975
  var sessionState = null;
@@ -15804,7 +15982,7 @@ function planFilePath(cwd, sessionId3) {
15804
15982
  return resolve9(cwd, "docs", "plans", planFileName(sessionId3));
15805
15983
  }
15806
15984
  function ensurePlanDir(planFile) {
15807
- mkdirSync14(dirname7(planFile), { recursive: true });
15985
+ mkdirSync15(dirname8(planFile), { recursive: true });
15808
15986
  }
15809
15987
  function restorePlanState(sessionId3, entries2, cwd) {
15810
15988
  sessionState = { sessionId: sessionId3, active: false, planFile: null, lastKeptHash: null };
@@ -15870,7 +16048,7 @@ function enforcePlanMode(input, state2, cwd, deps) {
15870
16048
  // packages/workflow/src/plan/index.ts
15871
16049
  init_core();
15872
16050
  import { createHash } from "node:crypto";
15873
- import { existsSync as existsSync15, readFileSync as readFileSync16 } from "node:fs";
16051
+ import { existsSync as existsSync15, readFileSync as readFileSync17 } from "node:fs";
15874
16052
  import { Type as Type3 } from "typebox";
15875
16053
 
15876
16054
  // packages/workflow/src/plan/review.ts
@@ -16082,7 +16260,7 @@ function sessionId2(ctx) {
16082
16260
  }
16083
16261
  function readPlanFile(planFile) {
16084
16262
  if (!planFile || !existsSync15(planFile)) return null;
16085
- const content = readFileSync16(planFile, "utf-8").trim();
16263
+ const content = readFileSync17(planFile, "utf-8").trim();
16086
16264
  return content.length > 0 ? content : null;
16087
16265
  }
16088
16266
  function planContentHash(content) {
@@ -16391,7 +16569,7 @@ Feedback: ${result2.feedback}` : ""}`
16391
16569
  }
16392
16570
 
16393
16571
  // packages/workflow/index.ts
16394
- var VERSION2 = getPackageVersion(dirname8(fileURLToPath3(import.meta.url)));
16572
+ var VERSION2 = getPackageVersion(dirname9(fileURLToPath4(import.meta.url)));
16395
16573
  function describeToolCall(event) {
16396
16574
  const input = event.input ?? {};
16397
16575
  const toolName = event.toolName;
@@ -16466,8 +16644,7 @@ function workflow_default(pi) {
16466
16644
 
16467
16645
  // packages/long-horizon/index.ts
16468
16646
  init_core();
16469
- import { existsSync as existsSync19, renameSync as renameSync6 } from "node:fs";
16470
- import { join as join27, resolve as resolve11 } from "node:path";
16647
+ import { join as join28, resolve as resolve11 } from "node:path";
16471
16648
  import { Text as Text2 } from "@earendil-works/pi-tui";
16472
16649
 
16473
16650
  // packages/long-horizon/src/compaction-brief.ts
@@ -16544,6 +16721,7 @@ var OwnerCoordinator = class {
16544
16721
  if (this.active) return void 0;
16545
16722
  const ownerId = randomUUID3();
16546
16723
  const now2 = new Date(this.deps.now?.() ?? Date.now()).toISOString();
16724
+ const session = this.deps.session?.();
16547
16725
  const owner = Object.freeze({
16548
16726
  ownerId,
16549
16727
  kind,
@@ -16551,7 +16729,10 @@ var OwnerCoordinator = class {
16551
16729
  status: "active",
16552
16730
  revision: 0,
16553
16731
  lease: Object.freeze({ ownerId, generation: 0 }),
16554
- updatedAt: now2
16732
+ updatedAt: now2,
16733
+ ...session?.id ? { sessionId: session.id } : {},
16734
+ ...session?.name ? { sessionName: session.name } : {},
16735
+ ...session?.pid ? { pid: session.pid } : {}
16555
16736
  });
16556
16737
  this.active = owner;
16557
16738
  return this.commit({ type: "activated", owner });
@@ -16635,6 +16816,19 @@ var OwnerCoordinator = class {
16635
16816
  } catch {
16636
16817
  }
16637
16818
  }
16819
+ const session = this.deps.session?.();
16820
+ const refresh = (owner) => {
16821
+ if (!owner) return owner;
16822
+ const stamp = owner.sessionId === void 0 && session?.id ? { sessionId: session.id } : {};
16823
+ const nameInfo = !owner.sessionName && session?.name ? { sessionName: session.name } : {};
16824
+ const pidInfo = session?.pid !== void 0 && owner.pid !== session.pid ? { pid: session.pid } : {};
16825
+ if (!("sessionId" in stamp) && !("sessionName" in nameInfo) && !("pid" in pidInfo)) return owner;
16826
+ const next = this.withOwner(owner, { ...stamp, ...nameInfo, ...pidInfo });
16827
+ this.persist();
16828
+ return next;
16829
+ };
16830
+ if (this.active) this.active = refresh(this.active);
16831
+ if (this.parked) this.parked = refresh(this.parked);
16638
16832
  const snapshot2 = this.snapshot();
16639
16833
  this.deps.onChange?.(snapshot2, { type: "restored", snapshot: snapshot2 });
16640
16834
  return snapshot2;
@@ -16981,8 +17175,8 @@ async function resolveMode(deps) {
16981
17175
 
16982
17176
  // packages/long-horizon/src/settings.ts
16983
17177
  init_core();
16984
- import { existsSync as existsSync16, mkdirSync as mkdirSync15, readFileSync as readFileSync17, writeFileSync as writeFileSync12 } from "node:fs";
16985
- import { dirname as dirname9, join as join25 } from "node:path";
17178
+ import { existsSync as existsSync16, mkdirSync as mkdirSync16, readFileSync as readFileSync18, writeFileSync as writeFileSync13 } from "node:fs";
17179
+ import { dirname as dirname10, join as join25 } from "node:path";
16986
17180
  import { homedir as homedir19 } from "node:os";
16987
17181
  var DEFAULT_SETTINGS3 = {
16988
17182
  judge: {
@@ -17104,8 +17298,8 @@ function withDecisionModel(settings, cwd) {
17104
17298
  import { Type as Type4 } from "typebox";
17105
17299
 
17106
17300
  // packages/long-horizon/src/graph/engine.ts
17107
- import { existsSync as existsSync17, mkdirSync as mkdirSync16, readFileSync as readFileSync18, renameSync as renameSync5, writeFileSync as writeFileSync13 } from "node:fs";
17108
- import { dirname as dirname10 } from "node:path";
17301
+ import { existsSync as existsSync17, mkdirSync as mkdirSync17, readFileSync as readFileSync19, renameSync as renameSync6, writeFileSync as writeFileSync14 } from "node:fs";
17302
+ import { dirname as dirname11 } from "node:path";
17109
17303
  var GRAPH_SCHEMA_VERSION = 1;
17110
17304
  var MAX_ADD_WORK = 32;
17111
17305
  var MAX_STOP = 20;
@@ -17142,7 +17336,7 @@ function emptyGraphFile() {
17142
17336
  function readGraphFile(path31) {
17143
17337
  try {
17144
17338
  if (!existsSync17(path31)) return emptyGraphFile();
17145
- const parsed = JSON.parse(readFileSync18(path31, "utf8"));
17339
+ const parsed = JSON.parse(readFileSync19(path31, "utf8"));
17146
17340
  if (parsed?.v !== GRAPH_SCHEMA_VERSION || !Array.isArray(parsed.history)) return emptyGraphFile();
17147
17341
  return { v: GRAPH_SCHEMA_VERSION, history: parsed.history, ...parsed.current ? { current: parsed.current } : {} };
17148
17342
  } catch {
@@ -17150,10 +17344,10 @@ function readGraphFile(path31) {
17150
17344
  }
17151
17345
  }
17152
17346
  function writeGraphFile(path31, file) {
17153
- mkdirSync16(dirname10(path31), { recursive: true });
17347
+ mkdirSync17(dirname11(path31), { recursive: true });
17154
17348
  const tmp = `${path31}.${process.pid}.tmp`;
17155
- writeFileSync13(tmp, JSON.stringify(file));
17156
- renameSync5(tmp, path31);
17349
+ writeFileSync14(tmp, JSON.stringify(file));
17350
+ renameSync6(tmp, path31);
17157
17351
  }
17158
17352
  var GraphEngine = class {
17159
17353
  constructor(deps) {
@@ -17782,6 +17976,7 @@ var GRAPH_ORCHESTRATION_PROMPT = `<orchestration_mode>
17782
17976
  Graph Mode is active for this session. You are the main-agent supervisor beside the data path.
17783
17977
  - Use update_agent_graph to schedule bounded work onto child subagents when a graph materially improves the task. The host runs the work; do not call run_subagent for graph work.
17784
17978
  - Each add_work item needs a self-contained instruction (the child does not see this conversation). Give items a short work_id so later work can name them.
17979
+ - Each item's profile is your choice: pick per item (a read-only investigation uses subagent_explore; write-capable work uses subagent_general or a custom profile). view_agent_graph lists the profiles available right now; a profile you name that does not exist is refused.
17785
17980
  - Dependencies are input_ids: work ids (or result ids r-<work_id>) of this graph whose committed results the item consumes. Work waits until its inputs complete; the host attaches bounded upstream handoffs, so do not restate upstream conclusions in the instruction. You may declare the whole DAG in one update.
17786
17981
  - Use view_agent_graph to observe work status, operators, waits, failures and committed result ids.
17787
17982
  - After scheduling, call yield_agent_graph instead of polling, sleeping, or writing a waiting message. Yield ends only the current supervisor turn; the host wakes you at the next durable checkpoint (failure, nothing runnable, or everything settled) and the graph stays open.
@@ -17801,7 +17996,7 @@ Swarm Mode is active for this session. Treat the durable asynchronous agent grap
17801
17996
  - Perform only the lightweight exploration needed to establish boundaries before dispatch.
17802
17997
  - Make every item bounded and self-contained with an explicit scope, expected output, and constraints.
17803
17998
  - Avoid overlapping writes. Prefer read-only investigation (profile subagent_explore) unless isolated workspaces are available.
17804
- - Schedule all independent items together with one update_agent_graph add_work (pick each item's profile). The host runs them; do not call run_subagent for swarm items.
17999
+ - Schedule all independent items together with one update_agent_graph add_work (pick each item's profile \u2014 view_agent_graph lists the profiles available right now; a read-only investigation uses subagent_explore, write-capable work uses subagent_general or a custom profile). The host runs them; do not call run_subagent for swarm items.
17805
18000
  - After scheduling, call yield_agent_graph. Do not poll, sleep, watch child logs, or wait synchronously; the host will wake you only when work needs attention or the whole swarm is settled.
17806
18001
  - On wake, call agent_swarm_status for compact statuses only. Read agent_output only for completed final results or narrow failed-item diagnosis.
17807
18002
  - Replace failed work with update_agent_graph using replaces=<failed work id> so the failed item no longer keeps the swarm in needs_attention. When all useful work is settled, finish the graph (operation=finish, result_ids, reason), deduplicate, verify, and semantically synthesize the results.
@@ -17829,6 +18024,7 @@ function graphView(engine, graph, options = {}) {
17829
18024
  revision: graph.revision,
17830
18025
  state: graph.finish ? "finished" : graph.abandoned ? "stopped" : "open",
17831
18026
  counts: countStatuses(graph, status2),
18027
+ ...options.profiles !== void 0 && options.profiles.length > 0 ? { available_profiles: [...options.profiles] } : {},
17832
18028
  work: shown.map((work) => ({
17833
18029
  work_id: work.workId,
17834
18030
  status: status2(work),
@@ -17884,7 +18080,7 @@ function swarmStatus(engine, graph) {
17884
18080
  var workSchema = Type4.Object({
17885
18081
  work_id: Type4.Optional(Type4.String({ description: "Short stable id for this work (e.g. scan-api). Later work names it in input_ids; its result id is r-<work_id>. Auto-assigned when omitted." })),
17886
18082
  instruction: Type4.String({ description: "Bounded, self-contained instruction: scope, expected output, constraints. The child does not see this conversation." }),
17887
- profile: Type4.Optional(Type4.String({ description: `Subagent profile for new work (default ${DEFAULT_PROFILE}; subagent_explore for read-only investigation).` })),
18083
+ profile: Type4.Optional(Type4.String({ description: `Subagent profile for this work (default ${DEFAULT_PROFILE}). Pick per work item: subagent_explore for read-only investigation, subagent_general for write-capable work on your model, or any custom profile listed in the Subagents section of your prompt. view_agent_graph reports the profiles available right now.` })),
17888
18084
  operator_id: Type4.Optional(Type4.String({ description: "Follow-up work on an EXISTING operator (from view_agent_graph): resumes that child's session. Omit for new work." })),
17889
18085
  input_ids: Type4.Optional(Type4.Array(Type4.String(), { description: `Work ids or result ids (r-<work_id>) of THIS graph whose committed results this work consumes (its dependencies). Max ${MAX_INPUT_IDS}.` })),
17890
18086
  selected_result_inputs: Type4.Optional(
@@ -17983,11 +18179,11 @@ function registerGraphTools(pi, deps) {
17983
18179
  const graph = p.graph_id ? engine.graph(p.graph_id) : engine.current();
17984
18180
  if (!graph) {
17985
18181
  if (p.history && engine.history().length > 0) {
17986
- return textResult(JSON.stringify({ current: null, history: engine.history().slice(0, 10).map((past) => ({ graph_id: past.graphId, task: past.task, state: past.finish ? "finished" : "stopped", selected_result_ids: past.finish?.resultIds ?? [] })) }, null, 2));
18182
+ return textResult(JSON.stringify({ current: null, profiles: deps.profiles(), history: engine.history().slice(0, 10).map((past) => ({ graph_id: past.graphId, task: past.task, state: past.finish ? "finished" : "stopped", selected_result_ids: past.finish?.resultIds ?? [] })) }, null, 2));
17987
18183
  }
17988
- return textResult(p.graph_id ? `No graph ${p.graph_id}.` : "No agent graph yet \u2014 schedule work with update_agent_graph operation=add_work.");
18184
+ return textResult(p.graph_id ? `No graph ${p.graph_id}. Available profiles: ${deps.profiles().join(", ") || "none"}.` : `No agent graph yet \u2014 schedule work with update_agent_graph operation=add_work. Available profiles: ${deps.profiles().join(", ") || "none"}.`);
17989
18185
  }
17990
- return textResult(JSON.stringify(graphView(engine, graph, { history: p.history === true }), null, 2));
18186
+ return textResult(JSON.stringify(graphView(engine, graph, { history: p.history === true, profiles: deps.profiles() }), null, 2));
17991
18187
  }
17992
18188
  });
17993
18189
  pi.registerTool({
@@ -19804,7 +20000,7 @@ function ownerEventClearsStash(event) {
19804
20000
 
19805
20001
  // packages/long-horizon/src/engine/ralph.ts
19806
20002
  init_core();
19807
- import { writeFileSync as writeFileSync14 } from "node:fs";
20003
+ import { writeFileSync as writeFileSync15 } from "node:fs";
19808
20004
  import { randomUUID as randomUUID5 } from "node:crypto";
19809
20005
  var RALPH_COMPLETE_MARKER2 = "<promise>COMPLETE</promise>";
19810
20006
  function parseChecklist(markdown) {
@@ -19954,7 +20150,7 @@ var RalphLoop = class {
19954
20150
  return { ok: false, reason: `unfinished goal exists (${goal.goal.status}) \u2014 clear or complete it first` };
19955
20151
  }
19956
20152
  ensureDir(this.taskPath(name));
19957
- writeFileSync14(this.taskPath(name), taskContent, "utf-8");
20153
+ writeFileSync15(this.taskPath(name), taskContent, "utf-8");
19958
20154
  const now2 = new Date(this.deps.now?.() ?? Date.now()).toISOString();
19959
20155
  const state2 = {
19960
20156
  version: 1,
@@ -20802,6 +20998,228 @@ function wireRuntime(pi, deps) {
20802
20998
  return { estimateGoal: (ctx) => estimateGoal(ctx) };
20803
20999
  }
20804
21000
 
21001
+ // packages/long-horizon/src/session-state.ts
21002
+ import { existsSync as existsSync18, mkdirSync as mkdirSync18, readFileSync as readFileSync20, readdirSync as readdirSync5, renameSync as renameSync7, rmSync as rmSync6, statSync as statSync9 } from "node:fs";
21003
+ import { join as join26 } from "node:path";
21004
+ function sessionStateDir(workspaceStateDir2, sessionId3) {
21005
+ return join26(workspaceStateDir2, "sessions", sessionId3);
21006
+ }
21007
+ function legacyWorkspaceDir(workspaceStateDir2) {
21008
+ return workspaceStateDir2;
21009
+ }
21010
+ function legacyInRepoDir(cwd) {
21011
+ return join26(cwd, ".unipi", "long-horizon");
21012
+ }
21013
+ function isContinuedSession(reason, priorEntryCount) {
21014
+ if (reason === "resume" || reason === "reload") return true;
21015
+ if (reason === "new" || reason === "fork") return false;
21016
+ return priorEntryCount > 0;
21017
+ }
21018
+ function legacyOwnershipEvidence(entriesJson, legacyDirs) {
21019
+ if (!entriesJson) return false;
21020
+ for (const dir of legacyDirs) {
21021
+ const state2 = readOwnerState(join26(dir, "state.json"));
21022
+ const owner = state2?.active ?? state2?.parked;
21023
+ const kind = owner?.kind;
21024
+ if (kind === "graph" || kind === "swarm") {
21025
+ try {
21026
+ const graph = JSON.parse(readJsonFile(join26(dir, "graph.json")));
21027
+ const graphId = graph?.current?.graphId;
21028
+ if (graphId && entriesJson.includes(graphId)) return true;
21029
+ } catch {
21030
+ }
21031
+ } else if (kind === "goal") {
21032
+ try {
21033
+ const goal = JSON.parse(readJsonFile(join26(dir, "goal.json")));
21034
+ if (goal?.goalId && entriesJson.includes(goal.goalId) || goal?.objective && entriesJson.includes(goal.objective)) return true;
21035
+ } catch {
21036
+ }
21037
+ } else if (kind === "ralph") {
21038
+ const loopName = owner?.label;
21039
+ if (loopName && entriesJson.includes(loopName)) return true;
21040
+ }
21041
+ }
21042
+ return false;
21043
+ }
21044
+ function adoptLegacyState(sessionDir2, sources, files) {
21045
+ const moved = [];
21046
+ if (existsSync18(join26(sessionDir2, "state.json"))) return moved;
21047
+ try {
21048
+ mkdirSync18(sessionDir2, { recursive: true });
21049
+ } catch {
21050
+ return moved;
21051
+ }
21052
+ for (const source of sources) {
21053
+ for (const file of files) {
21054
+ const from = join26(source, file);
21055
+ const to = join26(sessionDir2, file);
21056
+ if (!existsSync18(to) && existsSync18(from)) {
21057
+ try {
21058
+ renameSync7(from, to);
21059
+ moved.push(from);
21060
+ } catch {
21061
+ }
21062
+ }
21063
+ }
21064
+ const fromDir = join26(source, "ralph");
21065
+ const toDir = join26(sessionDir2, "ralph");
21066
+ if (!existsSync18(toDir) && existsSync18(fromDir)) {
21067
+ try {
21068
+ renameSync7(fromDir, toDir);
21069
+ moved.push(fromDir);
21070
+ } catch {
21071
+ }
21072
+ }
21073
+ }
21074
+ return moved;
21075
+ }
21076
+ function readOwnerState(path31) {
21077
+ if (!existsSync18(path31)) return void 0;
21078
+ try {
21079
+ const parsed = JSON.parse(readJsonFile(path31));
21080
+ return parsed && typeof parsed === "object" ? parsed : void 0;
21081
+ } catch {
21082
+ return void 0;
21083
+ }
21084
+ }
21085
+ function readJsonFile(path31) {
21086
+ return readFileSync20(path31, "utf8");
21087
+ }
21088
+ function scanSiblingGraphOwners(sessionsDir, ownSessionId, ...legacyDirs) {
21089
+ const out = [];
21090
+ const push = (sessionId3, state2, sessionName, ownDir = true) => {
21091
+ for (const slot of ["active", "parked"]) {
21092
+ const owner = state2?.[slot];
21093
+ if (!owner || owner.kind !== "graph" && owner.kind !== "swarm") continue;
21094
+ if (ownDir && owner.sessionId && owner.sessionId !== sessionId3) continue;
21095
+ out.push({
21096
+ sessionId: sessionId3,
21097
+ kind: owner.kind === "swarm" ? "swarm" : "graph",
21098
+ label: owner.label ?? "",
21099
+ ...sessionName !== void 0 ? { sessionName } : {},
21100
+ ...owner.pid !== void 0 ? { pid: owner.pid } : {},
21101
+ updatedAt: state2?.updatedAt ?? ""
21102
+ });
21103
+ break;
21104
+ }
21105
+ };
21106
+ try {
21107
+ for (const entry of readdirSync5(sessionsDir, { withFileTypes: true })) {
21108
+ if (!entry.isDirectory() || entry.name === ownSessionId) continue;
21109
+ const state2 = readOwnerState(join26(sessionsDir, entry.name, "state.json"));
21110
+ if (state2?.active?.sessionId === ownSessionId || state2?.parked?.sessionId === ownSessionId) continue;
21111
+ push(entry.name, state2);
21112
+ }
21113
+ } catch {
21114
+ }
21115
+ for (const legacyDir of legacyDirs) {
21116
+ if (!legacyDir) continue;
21117
+ const state2 = readOwnerState(join26(legacyDir, "state.json"));
21118
+ if (state2 && state2.active?.sessionId !== ownSessionId && state2.parked?.sessionId !== ownSessionId) {
21119
+ push("(legacy)", state2, void 0, false);
21120
+ }
21121
+ }
21122
+ return out.sort((a, b) => a.updatedAt < b.updatedAt ? 1 : -1);
21123
+ }
21124
+ function pidAlive(pid) {
21125
+ if (pid === void 0 || pid <= 0) return false;
21126
+ try {
21127
+ process.kill(pid, 0);
21128
+ return true;
21129
+ } catch {
21130
+ return false;
21131
+ }
21132
+ }
21133
+ function graphOwnerNote(owner) {
21134
+ if (!owner) return void 0;
21135
+ const name = owner.sessionName ?? (owner.sessionId !== "(workspace)" ? owner.sessionId.slice(0, 12) : "another session");
21136
+ if (pidAlive(owner.pid)) {
21137
+ return `Agent ${owner.kind} "${owner.label}" is running in another session (${name}) \u2014 /resume it to follow it.`;
21138
+ }
21139
+ return `Agent ${owner.kind} "${owner.label}" is paused in another session (${name}) \u2014 /resume it to continue.`;
21140
+ }
21141
+ function readGraphFile2(path31) {
21142
+ if (!existsSync18(path31)) return void 0;
21143
+ try {
21144
+ const parsed = JSON.parse(readJsonFile(path31));
21145
+ if (!parsed || !Array.isArray(parsed.history)) return void 0;
21146
+ return parsed;
21147
+ } catch {
21148
+ return void 0;
21149
+ }
21150
+ }
21151
+ function graphUpdatedAt(graph) {
21152
+ const at = graph.updatedAt ?? graph.createdAt;
21153
+ return typeof at === "number" ? at : 0;
21154
+ }
21155
+ function isClosed(graph) {
21156
+ return !!(graph.finish || graph.abandoned);
21157
+ }
21158
+ function aggregateGraphHistory(own, sessionsDir, ownSessionId) {
21159
+ const byId2 = /* @__PURE__ */ new Map();
21160
+ for (const graph of own) {
21161
+ const id = graph.graphId;
21162
+ if (typeof id === "string") byId2.set(id, graph);
21163
+ }
21164
+ try {
21165
+ for (const entry of readdirSync5(sessionsDir, { withFileTypes: true })) {
21166
+ if (!entry.isDirectory() || entry.name === ownSessionId) continue;
21167
+ const file = readGraphFile2(join26(sessionsDir, entry.name, "graph.json"));
21168
+ if (!file) continue;
21169
+ for (const graph of file.history) {
21170
+ if (typeof graph.graphId === "string" && !byId2.has(graph.graphId)) byId2.set(graph.graphId, graph);
21171
+ }
21172
+ if (file.current && isClosed(file.current) && typeof file.current.graphId === "string" && !byId2.has(file.current.graphId)) {
21173
+ byId2.set(file.current.graphId, file.current);
21174
+ }
21175
+ }
21176
+ } catch {
21177
+ }
21178
+ return [...byId2.values()].sort((a, b) => graphUpdatedAt(b) - graphUpdatedAt(a));
21179
+ }
21180
+ function findSiblingWork(sessionsDir, ownSessionId, graphId, workId) {
21181
+ try {
21182
+ for (const entry of readdirSync5(sessionsDir, { withFileTypes: true })) {
21183
+ if (!entry.isDirectory() || entry.name === ownSessionId) continue;
21184
+ const file = readGraphFile2(join26(sessionsDir, entry.name, "graph.json"));
21185
+ if (!file) continue;
21186
+ const candidates = [...file.history, ...file.current && isClosed(file.current) ? [file.current] : []];
21187
+ const graph = candidates.find((g) => g.graphId === graphId);
21188
+ if (!graph) continue;
21189
+ const work = graph["work"]?.find((w) => w.workId === workId);
21190
+ if (work) return { work, graph };
21191
+ }
21192
+ } catch {
21193
+ return void 0;
21194
+ }
21195
+ return void 0;
21196
+ }
21197
+ var PRUNE_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
21198
+ function pruneSiblingSessionDirs(sessionsDir, ownSessionId, now2) {
21199
+ const pruned = [];
21200
+ try {
21201
+ for (const entry of readdirSync5(sessionsDir, { withFileTypes: true })) {
21202
+ if (!entry.isDirectory() || entry.name === ownSessionId) continue;
21203
+ const dir = join26(sessionsDir, entry.name);
21204
+ try {
21205
+ if (now2 - statSync9(dir).mtimeMs <= PRUNE_AGE_MS) continue;
21206
+ const state2 = readOwnerState(join26(dir, "state.json"));
21207
+ if (state2?.active || state2?.parked) continue;
21208
+ const file = readGraphFile2(join26(dir, "graph.json"));
21209
+ if (file?.current && !isClosed(file.current)) continue;
21210
+ rmSync6(dir, { recursive: true, force: true });
21211
+ pruned.push(entry.name);
21212
+ } catch {
21213
+ }
21214
+ }
21215
+ } catch {
21216
+ }
21217
+ return pruned;
21218
+ }
21219
+ function sessionHasState(sessionDir2) {
21220
+ return existsSync18(join26(sessionDir2, "state.json")) || existsSync18(join26(sessionDir2, "goal.json")) || existsSync18(join26(sessionDir2, "graph.json"));
21221
+ }
21222
+
20805
21223
  // packages/long-horizon/src/progress-snapshot.ts
20806
21224
  var LABEL_MAX = 160;
20807
21225
  var SUMMARY_MAX = 300;
@@ -21499,9 +21917,9 @@ function renderProgressView(t, progress, width, height, now2, chart, workingMode
21499
21917
  }
21500
21918
 
21501
21919
  // packages/long-horizon/src/progress-chart.ts
21502
- import { existsSync as existsSync18, readFileSync as readFileSync19 } from "node:fs";
21920
+ import { existsSync as existsSync19, readFileSync as readFileSync21 } from "node:fs";
21503
21921
  import { createRequire } from "node:module";
21504
- import { join as join26 } from "node:path";
21922
+ import { join as join27 } from "node:path";
21505
21923
  import { allocateImageId, encodeKitty as encodeKitty2, getCapabilities as getCapabilities3, getPngDimensions, getCellDimensions } from "@earendil-works/pi-tui";
21506
21924
 
21507
21925
  // packages/long-horizon/src/progress-mermaid.ts
@@ -21661,7 +22079,7 @@ async function createDefaultRenderer() {
21661
22079
  if (!wasmReady) {
21662
22080
  const require2 = createRequire(import.meta.url);
21663
22081
  const wasmPath = require2.resolve("@resvg/resvg-wasm/index_bg.wasm");
21664
- wasmReady = resvg.initWasm(readFileSync19(wasmPath)).catch((err) => {
22082
+ wasmReady = resvg.initWasm(readFileSync21(wasmPath)).catch((err) => {
21665
22083
  if (/already initiali[sz]ed/i.test(err?.message ?? "")) return;
21666
22084
  wasmReady = null;
21667
22085
  throw err;
@@ -21670,12 +22088,12 @@ async function createDefaultRenderer() {
21670
22088
  await wasmReady;
21671
22089
  const fontFiles = FONT_CANDIDATES.filter((f) => {
21672
22090
  try {
21673
- return existsSync18(f);
22091
+ return existsSync19(f);
21674
22092
  } catch {
21675
22093
  return false;
21676
22094
  }
21677
22095
  });
21678
- const fontBuffers = fontFiles.map((f) => readFileSync19(f));
22096
+ const fontBuffers = fontFiles.map((f) => readFileSync21(f));
21679
22097
  if (!fontBuffers.length) throw new Error("no system font found for chart labels");
21680
22098
  return {
21681
22099
  async render(source) {
@@ -21922,19 +22340,12 @@ function createChartState(requestRender, supported = chartSupported()) {
21922
22340
  }
21923
22341
 
21924
22342
  // packages/long-horizon/index.ts
21925
- var LH_DIR = ".unipi/long-horizon";
22343
+ var currentSessionId;
22344
+ var workspaceStateDir = () => stateDir("long-horizon", "state");
22345
+ var lhSessionsDir = () => join28(workspaceStateDir(), "sessions");
21926
22346
  function lhStatePath(file) {
21927
- const dest = join27(stateDir("long-horizon", "state"), file);
21928
- if (!existsSync19(dest)) {
21929
- const legacy = join27(resolve11(process.cwd()), LH_DIR, file);
21930
- if (existsSync19(legacy)) {
21931
- try {
21932
- renameSync6(legacy, dest);
21933
- } catch {
21934
- }
21935
- }
21936
- }
21937
- return dest;
22347
+ const dir = currentSessionId ? sessionStateDir(workspaceStateDir(), currentSessionId) : workspaceStateDir();
22348
+ return join28(dir, file);
21938
22349
  }
21939
22350
  function longHorizon(pi) {
21940
22351
  const version = getPackageVersion("long-horizon");
@@ -21983,8 +22394,10 @@ function longHorizon(pi) {
21983
22394
  }
21984
22395
  }
21985
22396
  let graphOwnerChanged;
22397
+ const sessionInfo = {};
21986
22398
  const owner = new OwnerCoordinator({
21987
22399
  statePath: statePath2,
22400
+ session: () => sessionInfo,
21988
22401
  onChange: (_snapshot, event) => {
21989
22402
  gate.onOwnerChanged(event);
21990
22403
  if (ownerEventClearsStash(event)) {
@@ -22116,7 +22529,7 @@ function longHorizon(pi) {
22116
22529
  listActionableTasks: () => todoStore.get().filter((item) => item.status === "pending" || item.status === "in_progress").map((item) => `${item.status === "in_progress" ? "[in_progress] " : ""}${item.content}`)
22117
22530
  }
22118
22531
  });
22119
- const ralphDir = () => join27(stateDir("long-horizon", "state"), "ralph");
22532
+ const ralphDir = () => join28(stateDir("long-horizon", "state"), "ralph");
22120
22533
  const ralph = new RalphLoop({
22121
22534
  machine,
22122
22535
  owner,
@@ -22337,6 +22750,39 @@ function longHorizon(pi) {
22337
22750
  }, machine, toolset);
22338
22751
  pi.on("session_start", (event, ctx) => {
22339
22752
  const reason = event?.reason;
22753
+ const manager = ctx?.sessionManager;
22754
+ try {
22755
+ currentSessionId = manager?.getSessionId?.();
22756
+ } catch {
22757
+ }
22758
+ sessionInfo.id = currentSessionId;
22759
+ try {
22760
+ const name = pi.getSessionName?.();
22761
+ if (name) sessionInfo.name = name;
22762
+ } catch {
22763
+ }
22764
+ sessionInfo.pid = process.pid;
22765
+ const priorEntries = (() => {
22766
+ try {
22767
+ return manager?.getEntries?.().length ?? 0;
22768
+ } catch {
22769
+ return 0;
22770
+ }
22771
+ })();
22772
+ const continued = isContinuedSession(reason, priorEntries);
22773
+ const legacyDirs = [legacyWorkspaceDir(workspaceStateDir()), legacyInRepoDir(resolve11(process.cwd()))];
22774
+ const entriesJson = (() => {
22775
+ try {
22776
+ return JSON.stringify(manager?.getEntries?.() ?? []);
22777
+ } catch {
22778
+ return "";
22779
+ }
22780
+ })();
22781
+ const legacyEvidence = continued && !sessionHasState(sessionStateDir(workspaceStateDir(), currentSessionId ?? "")) ? legacyOwnershipEvidence(entriesJson, legacyDirs) : false;
22782
+ if (continued && legacyEvidence && !sessionHasState(sessionStateDir(workspaceStateDir(), currentSessionId ?? ""))) {
22783
+ adoptLegacyState(sessionStateDir(workspaceStateDir(), currentSessionId ?? ""), legacyDirs, ["state.json", "goal.json", "graph.json"]);
22784
+ }
22785
+ if (currentSessionId) pruneSiblingSessionDirs(lhSessionsDir(), currentSessionId, Date.now());
22340
22786
  if (reason === "new" || reason === "resume" || reason === "fork") gate.dropStickyWorkMode();
22341
22787
  gate.resetModeTools();
22342
22788
  gate.resetDisplay(loadSettings().defaultMode);
@@ -22354,7 +22800,7 @@ function longHorizon(pi) {
22354
22800
  ralph.restoreFromOwner();
22355
22801
  const goalStatus = machine.get()?.status;
22356
22802
  if (goalStatus === "active" || goalStatus === "waiting") {
22357
- const redeliver = !isChildProcess() && reason !== "new" && reason !== "fork" && ctx?.hasUI !== false;
22803
+ const redeliver = !isChildProcess() && ctx?.hasUI !== false;
22358
22804
  const recovery = redeliver ? continuation.recoverOnRestart() : (continuation.armRecovery(), void 0);
22359
22805
  if (recovery?.action === "redeliver") {
22360
22806
  setTimeout(() => {
@@ -22363,14 +22809,31 @@ function longHorizon(pi) {
22363
22809
  }, 0);
22364
22810
  }
22365
22811
  }
22812
+ if (!isChildProcess() && (reason === "new" || reason === "fork" || reason === "startup" || reason === void 0) && ctx?.hasUI) {
22813
+ try {
22814
+ const siblings = scanSiblingGraphOwners(lhSessionsDir(), currentSessionId ?? "", legacyWorkspaceDir(workspaceStateDir()), legacyInRepoDir(resolve11(process.cwd()))).filter((o) => o.sessionId !== currentSessionId);
22815
+ const note = graphOwnerNote(siblings[0]);
22816
+ if (note) {
22817
+ const ui = ctx.ui;
22818
+ ui?.notify?.(note, "info");
22819
+ }
22820
+ } catch {
22821
+ }
22822
+ }
22366
22823
  publishLh();
22367
22824
  });
22368
22825
  registerCommandRunner("unipi:lh-graph", async (_ctx, args) => {
22369
22826
  const a = args ?? {};
22370
22827
  const op = String(a.op ?? "");
22371
22828
  if (op === "output") {
22372
- const found = graphEngine.readResult(String(a.id ?? ""), typeof a.graphId === "string" ? a.graphId : void 0);
22373
- if (!found) return { ok: false, reason: "That step is gone." };
22829
+ const graphId = typeof a.graphId === "string" ? a.graphId : void 0;
22830
+ const found = graphEngine.readResult(String(a.id ?? ""), graphId);
22831
+ const sibling = !found && graphId && currentSessionId ? findSiblingWork(lhSessionsDir(), currentSessionId, graphId, String(a.id ?? "")) : void 0;
22832
+ if (!found) {
22833
+ if (!sibling) return { ok: false, reason: "That step is gone." };
22834
+ const w2 = sibling.work;
22835
+ return { ok: true, output: { graphId, id: String(w2.workId ?? a.id ?? ""), status: String(w2.status ?? ""), ...w2.resultId ? { resultId: w2.resultId } : {}, ...w2.operatorId ? { operatorId: w2.operatorId } : {}, instruction: String(w2.instruction ?? ""), ...w2.result ? { text: w2.result } : {}, ...w2.failure ? { failure: w2.failure } : {}, ...w2.resultTruncated ? { truncated: true } : {} } };
22836
+ }
22374
22837
  const w = found.work;
22375
22838
  return { ok: true, output: { graphId: found.graphId, id: w.workId, status: w.status, ...w.resultId ? { resultId: w.resultId } : {}, ...w.operatorId ? { operatorId: w.operatorId } : {}, instruction: w.instruction, ...w.result ? { text: w.result } : {}, ...w.failure ? { failure: w.failure } : {}, ...w.resultTruncated ? { truncated: true } : {} } };
22376
22839
  }
@@ -22382,14 +22845,18 @@ function longHorizon(pi) {
22382
22845
  return { ok: true };
22383
22846
  }
22384
22847
  if (op === "history") {
22385
- const runs = graphEngine.history().slice(0, 20).map(
22386
- (g) => lhProgressSnapshot({
22387
- finished: { kind: g.mode, label: g.task, terminalReason: g.finish ? "complete(finish)" : g.abandoned?.reason ?? "stopped", endedAt: new Date(g.finish?.at ?? g.abandoned?.at ?? g.updatedAt).toISOString() },
22388
- graph: g,
22848
+ const aggregate = currentSessionId ? aggregateGraphHistory(graphEngine.history().map((g) => g), lhSessionsDir(), currentSessionId) : graphEngine.history().map((g) => g);
22849
+ const runs = aggregate.slice(0, 20).map((g) => {
22850
+ const graph = g;
22851
+ const finish = g["finish"];
22852
+ const abandoned = g["abandoned"];
22853
+ return lhProgressSnapshot({
22854
+ finished: { kind: g["mode"], label: String(g["task"] ?? ""), terminalReason: finish ? "complete(finish)" : abandoned?.reason ?? "stopped", endedAt: new Date(finish?.at ?? abandoned?.at ?? graphUpdatedAt(g)).toISOString() },
22855
+ graph,
22389
22856
  graphEngine,
22390
22857
  now: Date.now()
22391
- }).last
22392
- ).filter((run4) => run4 !== void 0);
22858
+ }).last;
22859
+ }).filter((run4) => run4 !== void 0);
22393
22860
  return { ok: true, runs };
22394
22861
  }
22395
22862
  return { ok: false, reason: `unknown op "${op}"` };
@@ -22416,8 +22883,8 @@ function longHorizon(pi) {
22416
22883
  init_core();
22417
22884
  import * as fs15 from "node:fs";
22418
22885
  import * as os5 from "node:os";
22419
- import { dirname as dirname16, join as join37 } from "node:path";
22420
- import { fileURLToPath as fileURLToPath4 } from "node:url";
22886
+ import { dirname as dirname17, join as join38 } from "node:path";
22887
+ import { fileURLToPath as fileURLToPath5 } from "node:url";
22421
22888
 
22422
22889
  // packages/memory/session.ts
22423
22890
  import * as fs11 from "node:fs";
@@ -24004,7 +24471,7 @@ function writeConversionState(state2) {
24004
24471
  } catch {
24005
24472
  }
24006
24473
  }
24007
- function pidAlive(pid) {
24474
+ function pidAlive2(pid) {
24008
24475
  try {
24009
24476
  process.kill(pid, 0);
24010
24477
  return true;
@@ -24016,7 +24483,7 @@ function acquireConversionLock() {
24016
24483
  const p = LOCK_PATH();
24017
24484
  try {
24018
24485
  const cur = JSON.parse(fs12.readFileSync(p, "utf-8"));
24019
- if (typeof cur.pid === "number" && cur.pid !== process.pid && pidAlive(cur.pid)) {
24486
+ if (typeof cur.pid === "number" && cur.pid !== process.pid && pidAlive2(cur.pid)) {
24020
24487
  return false;
24021
24488
  }
24022
24489
  } catch {
@@ -25407,7 +25874,7 @@ var memoryMoveHandler = createMemoryMoveHandler();
25407
25874
  function getInfoRegistry() {
25408
25875
  return globalThis.__unipi_info_registry;
25409
25876
  }
25410
- var VERSION3 = getPackageVersion(dirname16(fileURLToPath4(import.meta.url)));
25877
+ var VERSION3 = getPackageVersion(dirname17(fileURLToPath5(import.meta.url)));
25411
25878
  var RECALL_CUSTOM_TYPE = "unipi-memory-recall-reminder";
25412
25879
  var RETRO_CUSTOM_TYPE = "unipi-memory-retro-reminder";
25413
25880
  var CARD_CUSTOM_TYPE = "unipi-memory-session-card";
@@ -25417,9 +25884,9 @@ function saveUsageLine(usage) {
25417
25884
  function memoryDebug(line) {
25418
25885
  if (process.env.UNIPI_DEBUG_MEMORY !== "1") return;
25419
25886
  try {
25420
- const dir = join37(os5.homedir(), ".unipi", "logs");
25887
+ const dir = join38(os5.homedir(), ".unipi", "logs");
25421
25888
  fs15.mkdirSync(dir, { recursive: true });
25422
- fs15.appendFileSync(join37(dir, "memory.log"), `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
25889
+ fs15.appendFileSync(join38(dir, "memory.log"), `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
25423
25890
  `);
25424
25891
  } catch {
25425
25892
  }
@@ -25497,7 +25964,7 @@ function memory_default(pi) {
25497
25964
  const walk2 = (d) => {
25498
25965
  for (const e of fs15.readdirSync(d, { withFileTypes: true })) {
25499
25966
  if (e.name.startsWith(".")) continue;
25500
- const p = join37(d, e.name);
25967
+ const p = join38(d, e.name);
25501
25968
  if (e.isDirectory()) walk2(p);
25502
25969
  else if (e.name.endsWith(".md")) n += 1;
25503
25970
  }
@@ -25627,7 +26094,7 @@ function memory_default(pi) {
25627
26094
  }, 12e4);
25628
26095
  pendingTimer.unref?.();
25629
26096
  if (migrating && ctx.hasUI) {
25630
- const askedFile = join37(memoryRoot(), ".v3-migrate-asked");
26097
+ const askedFile = join38(memoryRoot(), ".v3-migrate-asked");
25631
26098
  if (!fs15.existsSync(askedFile) && typeof ctx.ui.confirm === "function") {
25632
26099
  try {
25633
26100
  fs15.mkdirSync(memoryRoot(), { recursive: true });
@@ -26323,7 +26790,7 @@ function patchRunMeta(staging, patch) {
26323
26790
  if (!cur) return;
26324
26791
  writeRunMeta(staging, { ...cur, ...patch });
26325
26792
  }
26326
- function pidAlive2(pid) {
26793
+ function pidAlive3(pid) {
26327
26794
  if (!(pid > 0)) return false;
26328
26795
  try {
26329
26796
  process.kill(pid, 0);
@@ -26339,7 +26806,7 @@ function mtime(file) {
26339
26806
  return void 0;
26340
26807
  }
26341
26808
  }
26342
- function runFromStaging(staging, decisions = {}, now2 = Date.now(), alive2 = pidAlive2) {
26809
+ function runFromStaging(staging, decisions = {}, now2 = Date.now(), alive2 = pidAlive3) {
26343
26810
  const id = path20.basename(staging);
26344
26811
  const fromName = Number(id.replace(/^staging-/, ""));
26345
26812
  const meta2 = readRunMeta(staging);
@@ -27390,17 +27857,17 @@ Watch it in the work tray (\u2193 \u2192 Dream).`);
27390
27857
 
27391
27858
  // packages/info-screen/index.ts
27392
27859
  init_core();
27393
- import { dirname as dirname23 } from "node:path";
27394
- import { fileURLToPath as fileURLToPath5 } from "node:url";
27860
+ import { dirname as dirname24 } from "node:path";
27861
+ import { fileURLToPath as fileURLToPath6 } from "node:url";
27395
27862
 
27396
27863
  // packages/info-screen/registry.ts
27397
- import { existsSync as existsSync31, mkdirSync as mkdirSync26, readFileSync as readFileSync32, renameSync as renameSync10, writeFileSync as writeFileSync25 } from "node:fs";
27398
- import { dirname as dirname20, join as join46 } from "node:path";
27864
+ import { existsSync as existsSync31, mkdirSync as mkdirSync28, readFileSync as readFileSync34, renameSync as renameSync11, writeFileSync as writeFileSync26 } from "node:fs";
27865
+ import { dirname as dirname21, join as join47 } from "node:path";
27399
27866
  import { homedir as homedir26 } from "node:os";
27400
27867
 
27401
27868
  // packages/info-screen/config.ts
27402
- import { readFileSync as readFileSync31, writeFileSync as writeFileSync24, existsSync as existsSync30, mkdirSync as mkdirSync25 } from "node:fs";
27403
- import { join as join45, dirname as dirname19 } from "node:path";
27869
+ import { readFileSync as readFileSync33, writeFileSync as writeFileSync25, existsSync as existsSync30, mkdirSync as mkdirSync27 } from "node:fs";
27870
+ import { join as join46, dirname as dirname20 } from "node:path";
27404
27871
  import { homedir as homedir25 } from "node:os";
27405
27872
 
27406
27873
  // packages/info-screen/types.ts
@@ -27414,7 +27881,7 @@ var DEFAULT_SETTINGS4 = {
27414
27881
 
27415
27882
  // packages/info-screen/config.ts
27416
27883
  init_core();
27417
- var SETTINGS_PATH = join45(homedir25(), ".pi", "agent", "settings.json");
27884
+ var SETTINGS_PATH = join46(homedir25(), ".pi", "agent", "settings.json");
27418
27885
  var cachedSettings = null;
27419
27886
  function isRecord5(value) {
27420
27887
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -27975,17 +28442,17 @@ var SNAPSHOT_VERSION = 2;
27975
28442
  var scopeOf = (id) => PAGE_SCOPE[id] ?? "global";
27976
28443
  var VOLATILE = { has: (id) => scopeOf(id) === "session" };
27977
28444
  function cacheDir2() {
27978
- return join46(process.env.UNIPI_DIR || join46(homedir26(), ".unipi"), "cache");
28445
+ return join47(process.env.UNIPI_DIR || join47(homedir26(), ".unipi"), "cache");
27979
28446
  }
27980
28447
  function snapshotPath(bucket, cwd) {
27981
- if (bucket === "global") return join46(cacheDir2(), "info-screen.json");
28448
+ if (bucket === "global") return join47(cacheDir2(), "info-screen.json");
27982
28449
  let id = "default";
27983
28450
  try {
27984
28451
  id = workspaceId(cwd);
27985
28452
  } catch {
27986
28453
  id = "default";
27987
28454
  }
27988
- return join46(cacheDir2(), "info-screen", `${id.replace(/[^\w.-]/g, "_")}.json`);
28455
+ return join47(cacheDir2(), "info-screen", `${id.replace(/[^\w.-]/g, "_")}.json`);
27989
28456
  }
27990
28457
  var InfoRegistry = class {
27991
28458
  groups = /* @__PURE__ */ new Map();
@@ -28126,7 +28593,7 @@ var InfoRegistry = class {
28126
28593
  try {
28127
28594
  const path31 = snapshotPath(bucket, this.cwd);
28128
28595
  if (existsSync31(path31)) {
28129
- const parsed = JSON.parse(readFileSync32(path31, "utf-8"));
28596
+ const parsed = JSON.parse(readFileSync34(path31, "utf-8"));
28130
28597
  if (parsed?.version === SNAPSHOT_VERSION && parsed.pages && typeof parsed.pages === "object") {
28131
28598
  pages2 = Object.fromEntries(Object.entries(parsed.pages).filter(([id]) => scopeOf(id) === bucket));
28132
28599
  }
@@ -28161,10 +28628,10 @@ var InfoRegistry = class {
28161
28628
  if (!changed) continue;
28162
28629
  try {
28163
28630
  const path31 = snapshotPath(bucket, this.cwd);
28164
- mkdirSync26(dirname20(path31), { recursive: true });
28631
+ mkdirSync28(dirname21(path31), { recursive: true });
28165
28632
  const tmp = `${path31}.${process.pid}.tmp`;
28166
- writeFileSync25(tmp, JSON.stringify({ version: SNAPSHOT_VERSION, pages: pages2 }), "utf-8");
28167
- renameSync10(tmp, path31);
28633
+ writeFileSync26(tmp, JSON.stringify({ version: SNAPSHOT_VERSION, pages: pages2 }), "utf-8");
28634
+ renameSync11(tmp, path31);
28168
28635
  } catch {
28169
28636
  }
28170
28637
  }
@@ -28188,14 +28655,14 @@ init_core();
28188
28655
 
28189
28656
  // packages/info-screen/usage-parser.ts
28190
28657
  init_core();
28191
- import { readdirSync as readdirSync14, readFileSync as readFileSync33, statSync as statSync11, existsSync as existsSync32, mkdirSync as mkdirSync27, writeFileSync as writeFileSync26, renameSync as renameSync11, openSync as openSync4, readSync as readSync3, closeSync as closeSync3 } from "node:fs";
28192
- import { join as join47, basename as basename14, dirname as dirname21 } from "node:path";
28658
+ import { readdirSync as readdirSync15, readFileSync as readFileSync35, statSync as statSync12, existsSync as existsSync32, mkdirSync as mkdirSync29, writeFileSync as writeFileSync27, renameSync as renameSync12, openSync as openSync4, readSync as readSync3, closeSync as closeSync3 } from "node:fs";
28659
+ import { join as join48, basename as basename14, dirname as dirname22 } from "node:path";
28193
28660
  import { homedir as homedir27 } from "node:os";
28194
28661
  var DAILY_DAYS = 30;
28195
28662
  var CACHE_VERSION = 2;
28196
28663
  function getCachePath() {
28197
- const base = process.env.UNIPI_DIR || join47(homedir27(), ".unipi");
28198
- return join47(base, "cache", "usage-stats.json");
28664
+ const base = process.env.UNIPI_DIR || join48(homedir27(), ".unipi");
28665
+ return join48(base, "cache", "usage-stats.json");
28199
28666
  }
28200
28667
  var memCache = null;
28201
28668
  var writeTimer = null;
@@ -28235,7 +28702,7 @@ function readCache() {
28235
28702
  try {
28236
28703
  const path31 = getCachePath();
28237
28704
  if (!existsSync32(path31)) return empty2;
28238
- const parsed = JSON.parse(readFileSync33(path31, "utf-8"));
28705
+ const parsed = JSON.parse(readFileSync35(path31, "utf-8"));
28239
28706
  if (!parsed || parsed.version !== CACHE_VERSION) return empty2;
28240
28707
  if (!Array.isArray(parsed.models) || typeof parsed.files !== "object") return empty2;
28241
28708
  memCache = parsed;
@@ -28247,16 +28714,16 @@ function readCache() {
28247
28714
  function writeCache(cache6) {
28248
28715
  try {
28249
28716
  const path31 = getCachePath();
28250
- mkdirSync27(dirname21(path31), { recursive: true });
28717
+ mkdirSync29(dirname22(path31), { recursive: true });
28251
28718
  const tmp = `${path31}.${process.pid}.tmp`;
28252
- writeFileSync26(tmp, JSON.stringify(cache6), "utf-8");
28253
- renameSync11(tmp, path31);
28719
+ writeFileSync27(tmp, JSON.stringify(cache6), "utf-8");
28720
+ renameSync12(tmp, path31);
28254
28721
  } catch {
28255
28722
  }
28256
28723
  }
28257
28724
  function getSessionsDir() {
28258
- const agentDir2 = process.env.PI_CODING_AGENT_DIR || join47(homedir27(), ".pi", "agent");
28259
- return join47(agentDir2, "sessions");
28725
+ const agentDir2 = process.env.PI_CODING_AGENT_DIR || join48(homedir27(), ".pi", "agent");
28726
+ return join48(agentDir2, "sessions");
28260
28727
  }
28261
28728
  function getPeriodBounds() {
28262
28729
  const now2 = /* @__PURE__ */ new Date();
@@ -28371,9 +28838,9 @@ function extractRecords(filePath, modelIndex, models) {
28371
28838
  }
28372
28839
  function collectSessionFiles(dir, files) {
28373
28840
  try {
28374
- const entries2 = readdirSync14(dir, { withFileTypes: true });
28841
+ const entries2 = readdirSync15(dir, { withFileTypes: true });
28375
28842
  for (const entry of entries2) {
28376
- const entryPath = join47(dir, entry.name);
28843
+ const entryPath = join48(dir, entry.name);
28377
28844
  if (entry.isDirectory()) {
28378
28845
  collectSessionFiles(entryPath, files);
28379
28846
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
@@ -28421,7 +28888,7 @@ function collectStats() {
28421
28888
  let mtimeMs = 0;
28422
28889
  let size = 0;
28423
28890
  try {
28424
- const st = statSync11(filePath);
28891
+ const st = statSync12(filePath);
28425
28892
  mtimeMs = st.mtimeMs;
28426
28893
  size = st.size;
28427
28894
  } catch {
@@ -28514,7 +28981,7 @@ function collectStats() {
28514
28981
  const dirSavings = /* @__PURE__ */ new Map();
28515
28982
  const aggregateSavings = (path31, rows) => {
28516
28983
  if (!rows) return;
28517
- const dir = basename14(dirname21(path31));
28984
+ const dir = basename14(dirname22(path31));
28518
28985
  for (const [at, before, after, replies, paid, context2] of rows) {
28519
28986
  const key = `${at}:${before}`;
28520
28987
  if (seenCompactions.has(key)) continue;
@@ -28561,8 +29028,8 @@ function collectStats() {
28561
29028
 
28562
29029
  // packages/info-screen/pages/session.ts
28563
29030
  init_core();
28564
- import { existsSync as existsSync33, readFileSync as readFileSync34, statSync as statSync12 } from "node:fs";
28565
- import { dirname as dirname22, join as join48, resolve as resolve15 } from "node:path";
29031
+ import { existsSync as existsSync33, readFileSync as readFileSync36, statSync as statSync13 } from "node:fs";
29032
+ import { dirname as dirname23, join as join49, resolve as resolve15 } from "node:path";
28566
29033
  import { homedir as homedir28 } from "node:os";
28567
29034
 
28568
29035
  // packages/info-screen/pages/context.ts
@@ -28726,22 +29193,22 @@ function gitBranch(cwd) {
28726
29193
  let head2 = null;
28727
29194
  let dir = cwd;
28728
29195
  for (let i = 0; i < 40 && head2 === null; i++) {
28729
- const git = join48(dir, ".git");
29196
+ const git = join49(dir, ".git");
28730
29197
  if (existsSync33(git)) {
28731
29198
  try {
28732
- let headPath = join48(git, "HEAD");
28733
- if (statSync12(git).isFile()) {
28734
- const txt = readFileSync34(git, "utf-8").trim();
28735
- if (txt.startsWith("gitdir:")) headPath = join48(resolve15(dir, txt.slice(7).trim()), "HEAD");
29199
+ let headPath = join49(git, "HEAD");
29200
+ if (statSync13(git).isFile()) {
29201
+ const txt = readFileSync36(git, "utf-8").trim();
29202
+ if (txt.startsWith("gitdir:")) headPath = join49(resolve15(dir, txt.slice(7).trim()), "HEAD");
28736
29203
  }
28737
- const ref = readFileSync34(headPath, "utf-8").trim();
29204
+ const ref = readFileSync36(headPath, "utf-8").trim();
28738
29205
  head2 = ref.startsWith("ref: refs/heads/") ? ref.slice(16) : ref.slice(0, 7);
28739
29206
  } catch {
28740
29207
  head2 = null;
28741
29208
  }
28742
29209
  break;
28743
29210
  }
28744
- const up = dirname22(dir);
29211
+ const up = dirname23(dir);
28745
29212
  if (up === dir) break;
28746
29213
  dir = up;
28747
29214
  }
@@ -29999,7 +30466,7 @@ function armSelfDismiss(ms, hooks) {
29999
30466
 
30000
30467
  // packages/info-screen/index.ts
30001
30468
  init_core();
30002
- var VERSION4 = getPackageVersion(dirname23(fileURLToPath5(import.meta.url)));
30469
+ var VERSION4 = getPackageVersion(dirname24(fileURLToPath6(import.meta.url)));
30003
30470
  function info_screen_default(pi) {
30004
30471
  setPiApi(pi);
30005
30472
  registerCoreGroups();
@@ -30395,7 +30862,7 @@ init_core();
30395
30862
  // packages/core/src/child-agent/pi-spawn.ts
30396
30863
  import * as fs22 from "node:fs";
30397
30864
  import * as path24 from "node:path";
30398
- import { fileURLToPath as fileURLToPath6 } from "node:url";
30865
+ import { fileURLToPath as fileURLToPath7 } from "node:url";
30399
30866
  var PI_CODING_AGENT_PACKAGE = "@earendil-works/pi-coding-agent";
30400
30867
  var UNIPI_SUBAGENT_PI_BINARY_ENV = "UNIPI_SUBAGENT_PI_BINARY";
30401
30868
  function findPiPackageRootFromEntry(entryPoint) {
@@ -30411,7 +30878,7 @@ function findPiPackageRootFromEntry(entryPoint) {
30411
30878
  return void 0;
30412
30879
  }
30413
30880
  function resolveInstalledPiPackageRoot() {
30414
- return findPiPackageRootFromEntry(fileURLToPath6(import.meta.resolve(PI_CODING_AGENT_PACKAGE)));
30881
+ return findPiPackageRootFromEntry(fileURLToPath7(import.meta.resolve(PI_CODING_AGENT_PACKAGE)));
30415
30882
  }
30416
30883
  function resolvePiPackageRoot() {
30417
30884
  try {
@@ -30435,7 +30902,7 @@ function isStandalonePiExecutable(execPath) {
30435
30902
  function resolvePiCliScript(deps = {}) {
30436
30903
  const existsSync67 = deps.existsSync ?? fs22.existsSync;
30437
30904
  const realpathSync8 = deps.realpathSync ?? fs22.realpathSync;
30438
- const readFileSync70 = deps.readFileSync ?? ((filePath, encoding) => fs22.readFileSync(filePath, encoding));
30905
+ const readFileSync72 = deps.readFileSync ?? ((filePath, encoding) => fs22.readFileSync(filePath, encoding));
30439
30906
  const argv1 = deps.argv1 ?? process.argv[1];
30440
30907
  if (argv1) {
30441
30908
  const argvPath = normalizePath(argv1);
@@ -30460,7 +30927,7 @@ function resolvePiCliScript(deps = {}) {
30460
30927
  return path24.join(packageRoot, "package.json");
30461
30928
  });
30462
30929
  const packageJsonPath = resolvePackageJson();
30463
- const packageJson = JSON.parse(readFileSync70(packageJsonPath, "utf8"));
30930
+ const packageJson = JSON.parse(readFileSync72(packageJsonPath, "utf8"));
30464
30931
  const binField = packageJson.bin;
30465
30932
  const binPath = typeof binField === "string" ? binField : binField?.pi ?? Object.values(binField ?? {})[0];
30466
30933
  if (!binPath) return void 0;
@@ -30517,9 +30984,9 @@ function leadExtensionArgs(argv) {
30517
30984
 
30518
30985
  // packages/core/src/child-agent/runtime.ts
30519
30986
  import { spawn as defaultSpawn } from "node:child_process";
30520
- import { mkdirSync as mkdirSync28, unlinkSync as unlinkSync7, writeFileSync as writeFileSync27 } from "node:fs";
30987
+ import { mkdirSync as mkdirSync30, unlinkSync as unlinkSync7, writeFileSync as writeFileSync28 } from "node:fs";
30521
30988
  import { tmpdir as tmpdir5 } from "node:os";
30522
- import { dirname as dirname25, join as join50 } from "node:path";
30989
+ import { dirname as dirname26, join as join51 } from "node:path";
30523
30990
  import { randomUUID as randomUUID6 } from "node:crypto";
30524
30991
  function primaryArg(name, args) {
30525
30992
  if (!args) return "";
@@ -30654,9 +31121,9 @@ var ChildAgentRuntime = class {
30654
31121
  this.promptPath = void 0;
30655
31122
  }
30656
31123
  spawn() {
30657
- mkdirSync28(dirname25(this.cfg.sessionFile), { recursive: true });
30658
- this.promptPath = join50(tmpdir5(), `unipi-fusion-${randomUUID6()}.txt`);
30659
- writeFileSync27(this.promptPath, this.cfg.systemPrompt, "utf8");
31124
+ mkdirSync30(dirname26(this.cfg.sessionFile), { recursive: true });
31125
+ this.promptPath = join51(tmpdir5(), `unipi-fusion-${randomUUID6()}.txt`);
31126
+ writeFileSync28(this.promptPath, this.cfg.systemPrompt, "utf8");
30660
31127
  const command = this.cfg.command ?? getPiSpawnCommand([
30661
31128
  ...leadExtensionArgs(process.argv),
30662
31129
  "--mode",
@@ -31123,8 +31590,8 @@ function createCompletionDelivery(send) {
31123
31590
 
31124
31591
  // packages/subagents/src/manager.ts
31125
31592
  import { randomBytes as randomBytes2 } from "node:crypto";
31126
- import { existsSync as existsSync35, readFileSync as readFileSync36, writeFileSync as writeFileSync28, renameSync as renameSync12, mkdirSync as mkdirSync29 } from "node:fs";
31127
- import { join as join51 } from "node:path";
31593
+ import { existsSync as existsSync35, readFileSync as readFileSync38, writeFileSync as writeFileSync29, renameSync as renameSync13, mkdirSync as mkdirSync31 } from "node:fs";
31594
+ import { join as join52 } from "node:path";
31128
31595
  init_core();
31129
31596
  var MAX_CONCURRENT = 8;
31130
31597
  var DEPTH_ENV = "UNIPI_SUBAGENT_DEPTH";
@@ -31182,8 +31649,8 @@ var SubagentManager = class {
31182
31649
  this.runtimeFactory = runtimeFactory;
31183
31650
  }
31184
31651
  sessionDir(cwd, leadSessionId2) {
31185
- const dir = join51(stateDir("subagents", "state", cwd), "sessions", leadSessionId2);
31186
- mkdirSync29(dir, { recursive: true });
31652
+ const dir = join52(stateDir("subagents", "state", cwd), "sessions", leadSessionId2);
31653
+ mkdirSync31(dir, { recursive: true });
31187
31654
  this.activeDir = dir;
31188
31655
  return dir;
31189
31656
  }
@@ -31192,13 +31659,13 @@ var SubagentManager = class {
31192
31659
  records.clear();
31193
31660
  this.finishedEvents.clear();
31194
31661
  const dir = this.sessionDir(cwd, leadSessionId2);
31195
- const indexPath = join51(dir, "index.json");
31662
+ const indexPath = join52(dir, "index.json");
31196
31663
  if (!existsSync35(indexPath)) {
31197
31664
  publish();
31198
31665
  return;
31199
31666
  }
31200
31667
  try {
31201
- const list = JSON.parse(readFileSync36(indexPath, "utf8"));
31668
+ const list = JSON.parse(readFileSync38(indexPath, "utf8"));
31202
31669
  for (const rec of list) {
31203
31670
  records.set(rec.id, rec.status === "running" ? { ...rec, status: "failed", error: "interrupted \u2014 pi exited while it ran", endedAt: rec.lastActivity } : rec);
31204
31671
  }
@@ -31210,10 +31677,10 @@ var SubagentManager = class {
31210
31677
  persist() {
31211
31678
  if (this.activeDir === void 0) return;
31212
31679
  try {
31213
- const indexPath = join51(this.activeDir, "index.json");
31680
+ const indexPath = join52(this.activeDir, "index.json");
31214
31681
  const tmp = `${indexPath}.tmp-${String(process.pid)}`;
31215
- writeFileSync28(tmp, JSON.stringify([...records.values()], null, 2), "utf8");
31216
- renameSync12(tmp, indexPath);
31682
+ writeFileSync29(tmp, JSON.stringify([...records.values()], null, 2), "utf8");
31683
+ renameSync13(tmp, indexPath);
31217
31684
  } catch {
31218
31685
  }
31219
31686
  }
@@ -31327,7 +31794,7 @@ var SubagentManager = class {
31327
31794
  toolCalls: 0,
31328
31795
  lastActivity: Date.now(),
31329
31796
  task: opts.task.length > MAX_TASK_CHARS ? `${opts.task.slice(0, MAX_TASK_CHARS)}\u2026` : opts.task,
31330
- sessionFile: prev?.sessionFile ?? join51(this.sessionDir(opts.cwd, opts.leadSessionId), `${id}.jsonl`),
31797
+ sessionFile: prev?.sessionFile ?? join52(this.sessionDir(opts.cwd, opts.leadSessionId), `${id}.jsonl`),
31331
31798
  depth: prev?.depth ?? currentDepth() + 1
31332
31799
  };
31333
31800
  const runtime = this.spawnRuntime({
@@ -31451,8 +31918,8 @@ function setGraphRunPort(port) {
31451
31918
  }
31452
31919
 
31453
31920
  // packages/subagents/src/profiles.ts
31454
- import { existsSync as existsSync36, readdirSync as readdirSync15, readFileSync as readFileSync37 } from "node:fs";
31455
- import { basename as basename15, join as join52 } from "node:path";
31921
+ import { existsSync as existsSync36, readdirSync as readdirSync16, readFileSync as readFileSync39 } from "node:fs";
31922
+ import { basename as basename15, join as join53 } from "node:path";
31456
31923
  import { homedir as homedir29 } from "node:os";
31457
31924
  import { parseFrontmatter } from "@earendil-works/pi-coding-agent";
31458
31925
  var EXPLORE_TOOLS = ["read", "grep", "find", "ls", "web_search", "memory_search", "memory_list"];
@@ -31473,7 +31940,7 @@ function parseToolList(value) {
31473
31940
  function loadAgentFile(path31, source, pathName) {
31474
31941
  let raw;
31475
31942
  try {
31476
- raw = readFileSync37(path31, "utf8");
31943
+ raw = readFileSync39(path31, "utf8");
31477
31944
  } catch (e) {
31478
31945
  return { error: String(e) };
31479
31946
  }
@@ -31495,15 +31962,15 @@ function loadAgentFile(path31, source, pathName) {
31495
31962
  function agentsIn(dir, source, warnings) {
31496
31963
  const out = /* @__PURE__ */ new Map();
31497
31964
  if (!existsSync36(dir)) return out;
31498
- for (const entry of readdirSync15(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
31965
+ for (const entry of readdirSync16(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
31499
31966
  if (entry.isFile() && entry.name.endsWith(".md")) {
31500
31967
  const name = basename15(entry.name, ".md");
31501
- const r = loadAgentFile(join52(dir, entry.name), source, name);
31968
+ const r = loadAgentFile(join53(dir, entry.name), source, name);
31502
31969
  if ("error" in r) warnings.push(`${entry.name}: ${r.error}`);
31503
31970
  else out.set(r.profile.id, r.profile);
31504
31971
  } else if (entry.isDirectory()) {
31505
31972
  for (const cand of DIR_CANDIDATES) {
31506
- const file = join52(dir, entry.name, cand);
31973
+ const file = join53(dir, entry.name, cand);
31507
31974
  if (!existsSync36(file)) continue;
31508
31975
  const r = loadAgentFile(file, source, entry.name);
31509
31976
  if ("error" in r) warnings.push(`${entry.name}/${cand}: ${r.error}`);
@@ -31528,13 +31995,13 @@ function loadProfiles(cwd, home = homedir29()) {
31528
31995
  merged.set(id, profile);
31529
31996
  }
31530
31997
  };
31531
- load2(join52(home, ".unipi", "config", "agents"), "global");
31532
- load2(join52(cwd, ".unipi", "config", "agents"), "project");
31998
+ load2(join53(home, ".unipi", "config", "agents"), "global");
31999
+ load2(join53(cwd, ".unipi", "config", "agents"), "project");
31533
32000
  return { profiles: [...builtins, ...merged.values()], warnings };
31534
32001
  }
31535
32002
 
31536
32003
  // packages/subagents/src/transcript.ts
31537
- import { existsSync as existsSync37, readFileSync as readFileSync38, statSync as statSync13 } from "node:fs";
32004
+ import { existsSync as existsSync37, readFileSync as readFileSync40, statSync as statSync14 } from "node:fs";
31538
32005
  function firstLine(value) {
31539
32006
  return value.split("\n", 1)[0] ?? "";
31540
32007
  }
@@ -31569,10 +32036,10 @@ var fileCache = /* @__PURE__ */ new Map();
31569
32036
  function itemsFromSessionFile(path31) {
31570
32037
  if (!existsSync37(path31)) return [];
31571
32038
  try {
31572
- const st = statSync13(path31);
32039
+ const st = statSync14(path31);
31573
32040
  const hit = fileCache.get(path31);
31574
32041
  if (hit !== void 0 && hit.mtimeMs === st.mtimeMs && hit.size === st.size) return hit.items;
31575
- const items = parseSession(readFileSync38(path31, "utf8"));
32042
+ const items = parseSession(readFileSync40(path31, "utf8"));
31576
32043
  if (fileCache.size > 50) fileCache.clear();
31577
32044
  fileCache.set(path31, { mtimeMs: st.mtimeMs, size: st.size, items });
31578
32045
  return items;
@@ -31863,8 +32330,8 @@ var SubagentDock = class {
31863
32330
 
31864
32331
  // packages/subagents/src/agents.ts
31865
32332
  init_core();
31866
- import { existsSync as existsSync38, mkdirSync as mkdirSync30, readFileSync as readFileSync39, rmSync as rmSync6, writeFileSync as writeFileSync29 } from "node:fs";
31867
- import { dirname as dirname26, join as join53 } from "node:path";
32333
+ import { existsSync as existsSync38, mkdirSync as mkdirSync32, readFileSync as readFileSync41, rmSync as rmSync7, writeFileSync as writeFileSync30 } from "node:fs";
32334
+ import { dirname as dirname27, join as join54 } from "node:path";
31868
32335
  import { homedir as homedir30 } from "node:os";
31869
32336
  import { parseFrontmatter as parseFrontmatter2 } from "@earendil-works/pi-coding-agent";
31870
32337
 
@@ -31917,12 +32384,12 @@ var AGENTS_COMMAND = "unipi:agents";
31917
32384
  var NAME_RE = /^[a-z0-9][a-z0-9_-]{0,47}$/;
31918
32385
  var NEW_LABEL = "\uFF0B New agent\u2026";
31919
32386
  function agentsDir(scope, cwd, home = homedir30()) {
31920
- return join53(scope === "global" ? home : cwd, ".unipi", "config", "agents");
32387
+ return join54(scope === "global" ? home : cwd, ".unipi", "config", "agents");
31921
32388
  }
31922
32389
  function agentFile(profile, cwd, home = homedir30()) {
31923
32390
  if (profile.source === "builtin") return void 0;
31924
32391
  const dir = agentsDir(profile.source, cwd, home);
31925
- for (const candidate of [join53(dir, `${profile.id}.md`), ...["AGENT.md", "AGENTS.md", "agent.md", "agents.md"].map((f) => join53(dir, profile.id, f))]) {
32392
+ for (const candidate of [join54(dir, `${profile.id}.md`), ...["AGENT.md", "AGENTS.md", "agent.md", "agents.md"].map((f) => join54(dir, profile.id, f))]) {
31926
32393
  if (existsSync38(candidate)) return candidate;
31927
32394
  }
31928
32395
  return void 0;
@@ -32012,14 +32479,14 @@ async function createAgent(ctx, existing, onChange) {
32012
32479
  if (tools === null) return;
32013
32480
  const prompt = await ui.editor(`${name} \u2014 system prompt (enter saves)`, promptTemplate(name, description));
32014
32481
  if (prompt === void 0 || !prompt.trim()) return;
32015
- const file = join53(agentsDir(scope, ctx.cwd), `${name}.md`);
32016
- mkdirSync30(dirname26(file), { recursive: true });
32017
- writeFileSync29(file, agentMarkdown({ name, description, model, tools, prompt }), "utf8");
32482
+ const file = join54(agentsDir(scope, ctx.cwd), `${name}.md`);
32483
+ mkdirSync32(dirname27(file), { recursive: true });
32484
+ writeFileSync30(file, agentMarkdown({ name, description, model, tools, prompt }), "utf8");
32018
32485
  onChange();
32019
32486
  ui.notify(`Agent ${name} saved to ${file}. The agent can use it from the next turn.`, "info");
32020
32487
  }
32021
32488
  async function editAgent(ctx, file, onChange) {
32022
- const before = readFileSync39(file, "utf8");
32489
+ const before = readFileSync41(file, "utf8");
32023
32490
  const after = await ctx.ui.editor(`Edit ${file}`, before);
32024
32491
  if (after === void 0 || after === before) return;
32025
32492
  try {
@@ -32028,7 +32495,7 @@ async function editAgent(ctx, file, onChange) {
32028
32495
  ctx.ui.notify(`Not saved \u2014 frontmatter doesn't parse: ${e instanceof Error ? e.message : String(e)}`, "error");
32029
32496
  return;
32030
32497
  }
32031
- writeFileSync29(file, after, "utf8");
32498
+ writeFileSync30(file, after, "utf8");
32032
32499
  onChange();
32033
32500
  ctx.ui.notify(`Saved ${file}.`, "info");
32034
32501
  }
@@ -32045,16 +32512,16 @@ async function manageCustom(ctx, profile, onChange) {
32045
32512
  const choice = await ctx.ui.select(`${profile.id} (${profile.source}) \u2014 ${file}`, [EDIT, COPY, DELETE]);
32046
32513
  if (choice === EDIT) return editAgent(ctx, file, onChange);
32047
32514
  if (choice === COPY) {
32048
- const target = join53(agentsDir(other, ctx.cwd), `${profile.id}.md`);
32515
+ const target = join54(agentsDir(other, ctx.cwd), `${profile.id}.md`);
32049
32516
  if (existsSync38(target) && !await ctx.ui.confirm("Overwrite?", `${target} already exists.`)) return;
32050
- mkdirSync30(dirname26(target), { recursive: true });
32051
- writeFileSync29(target, readFileSync39(file, "utf8"), "utf8");
32517
+ mkdirSync32(dirname27(target), { recursive: true });
32518
+ writeFileSync30(target, readFileSync41(file, "utf8"), "utf8");
32052
32519
  onChange();
32053
32520
  ctx.ui.notify(`Copied to ${target}.`, "info");
32054
32521
  return;
32055
32522
  }
32056
32523
  if (choice === DELETE && await ctx.ui.confirm(`Delete ${profile.id}?`, file)) {
32057
- rmSync6(file);
32524
+ rmSync7(file);
32058
32525
  onChange();
32059
32526
  ctx.ui.notify(`Deleted ${file}.`, "info");
32060
32527
  }
@@ -32338,6 +32805,13 @@ ${body}
32338
32805
  profiles = loaded.profiles;
32339
32806
  return loaded.warnings;
32340
32807
  }
32808
+ function currentProfiles(cwd) {
32809
+ try {
32810
+ profiles = loadProfiles(cwd).profiles;
32811
+ } catch {
32812
+ }
32813
+ return profiles;
32814
+ }
32341
32815
  function refresh(cwd, ctx) {
32342
32816
  for (const w of reload(cwd)) {
32343
32817
  if (warned.has(w)) continue;
@@ -32542,7 +33016,7 @@ ${body}
32542
33016
  const cwd = ctx.cwd ?? process.cwd();
32543
33017
  const resumeId = params.resume?.trim() || void 0;
32544
33018
  const profileName = resumeId !== void 0 ? manager.record(resumeId)?.profile ?? params.profile : params.profile;
32545
- const profile = profiles.find((p) => p.id === profileName);
33019
+ const profile = currentProfiles(cwd).find((p) => p.id === profileName);
32546
33020
  if (!profile) {
32547
33021
  return textResult5(`Unknown profile "${params.profile}". Available: ${profiles.map((p) => p.id).join(", ")}`, { owner: "subagents", status: "failed", title: params.title }, true);
32548
33022
  }
@@ -32684,15 +33158,17 @@ ${body}
32684
33158
  });
32685
33159
  registerAgentsCommand(pi, { onChange: (cwd) => refresh(cwd) });
32686
33160
  const graphPort = {
32687
- profiles: () => enabled && canSpawn() ? profiles.map((p) => p.id) : [],
33161
+ // Fresh list: the graph validates a work item's profile against this, and
33162
+ // a profile created earlier in this turn must be accepted, not refused.
33163
+ profiles: () => enabled && canSpawn() ? currentProfiles(uiCtx2?.cwd ?? process.cwd()).map((p) => p.id) : [],
32688
33164
  start: (request) => {
32689
33165
  const ctx = uiCtx2;
32690
33166
  if (ctx === void 0) return { error: "subagents are not ready (no session)", retryable: true };
32691
33167
  if (!enabled || !canSpawn()) return { error: "subagents are disabled in this session" };
33168
+ const cwd = ctx.cwd ?? process.cwd();
32692
33169
  const resumeProfile = request.resume !== void 0 ? manager.record(request.resume)?.profile : void 0;
32693
- const profile = profiles.find((p) => p.id === (resumeProfile ?? request.profile));
33170
+ const profile = currentProfiles(cwd).find((p) => p.id === (resumeProfile ?? request.profile));
32694
33171
  if (!profile) return { error: `unknown subagent profile "${request.profile}" (available: ${profiles.map((p) => p.id).join(", ")})` };
32695
- const cwd = ctx.cwd ?? process.cwd();
32696
33172
  const start = manager.start({
32697
33173
  title: request.title,
32698
33174
  task: request.task,
@@ -32758,9 +33234,9 @@ init_core();
32758
33234
 
32759
33235
  // packages/background-tasks/src/config.ts
32760
33236
  init_core();
32761
- import { existsSync as existsSync39, mkdirSync as mkdirSync31, readFileSync as readFileSync40, renameSync as renameSync13, writeFileSync as writeFileSync30 } from "node:fs";
33237
+ import { existsSync as existsSync39, mkdirSync as mkdirSync33, readFileSync as readFileSync42, renameSync as renameSync14, writeFileSync as writeFileSync31 } from "node:fs";
32762
33238
  import { homedir as homedir31 } from "node:os";
32763
- import { join as join54 } from "node:path";
33239
+ import { join as join55 } from "node:path";
32764
33240
  var DEFAULT_CONFIG3 = {
32765
33241
  enabled: true,
32766
33242
  notifyOnCompletion: true,
@@ -32769,15 +33245,15 @@ var DEFAULT_CONFIG3 = {
32769
33245
  maxOutputBytes: 20 * 1024 * 1024
32770
33246
  };
32771
33247
  function getGlobalConfigPath() {
32772
- return join54(homedir31(), ".unipi", "config", "background-tasks.json");
33248
+ return join55(homedir31(), ".unipi", "config", "background-tasks.json");
32773
33249
  }
32774
33250
  function getWorkspaceConfigPath(cwd) {
32775
- return join54(cwd, ".unipi", "config", "background-tasks.json");
33251
+ return join55(cwd, ".unipi", "config", "background-tasks.json");
32776
33252
  }
32777
33253
  function loadConfigFromPath(filePath) {
32778
33254
  if (!existsSync39(filePath)) return null;
32779
33255
  try {
32780
- const parsed = JSON.parse(readFileSync40(filePath, "utf-8"));
33256
+ const parsed = JSON.parse(readFileSync42(filePath, "utf-8"));
32781
33257
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
32782
33258
  return parsed;
32783
33259
  } catch {
@@ -32869,12 +33345,12 @@ import { spawn as nodeSpawn2 } from "node:child_process";
32869
33345
  import { randomBytes as randomBytes4 } from "node:crypto";
32870
33346
  import { createWriteStream as createWriteStream2, existsSync as existsSync40 } from "node:fs";
32871
33347
  import { mkdir, writeFile as writeFile3 } from "node:fs/promises";
32872
- import { join as join58 } from "node:path";
33348
+ import { join as join59 } from "node:path";
32873
33349
  import { formatSize } from "@earendil-works/pi-coding-agent";
32874
33350
 
32875
33351
  // packages/background-tasks/src/types.ts
32876
- import { statSync as statSync14 } from "node:fs";
32877
- import { join as join55 } from "node:path";
33352
+ import { statSync as statSync15 } from "node:fs";
33353
+ import { join as join56 } from "node:path";
32878
33354
  import { open } from "node:fs/promises";
32879
33355
  import { extname as extname3, isAbsolute as isAbsolute7, win32 } from "node:path";
32880
33356
  function deriveCompletionDeliveryGuidance(notifyOnCompletion, triggerOnCompletion) {
@@ -33186,7 +33662,7 @@ function validateWindowsShellPath(path31, label) {
33186
33662
  }
33187
33663
  let stats;
33188
33664
  try {
33189
- stats = statSync14(path31);
33665
+ stats = statSync15(path31);
33190
33666
  } catch (error) {
33191
33667
  failShellInvocation(`${label} stat failed: ${shellErrorMessage(error)}`);
33192
33668
  }
@@ -33198,7 +33674,7 @@ function inspectWindowsShellCandidate(path31) {
33198
33674
  return { found: false, diagnostic: `${path31} is not a .exe or .com path` };
33199
33675
  }
33200
33676
  try {
33201
- const stats = statSync14(path31);
33677
+ const stats = statSync15(path31);
33202
33678
  if (stats.isFile()) return { found: true };
33203
33679
  return { found: false, diagnostic: `${path31} is not a regular file` };
33204
33680
  } catch (error) {
@@ -33213,7 +33689,7 @@ function resolveWindowsBash(env) {
33213
33689
  const diagnostics = [];
33214
33690
  for (const dir of pathValue.split(";").filter((entry) => entry.length > 0)) {
33215
33691
  for (const name of ["bash.exe", "bash.com"]) {
33216
- const candidate = join55(dir, name);
33692
+ const candidate = join56(dir, name);
33217
33693
  const result2 = inspectWindowsShellCandidate(candidate);
33218
33694
  if (result2.found) return candidate;
33219
33695
  diagnostics.push(result2.diagnostic);
@@ -33303,7 +33779,7 @@ function appendOutputTail(task, chunk) {
33303
33779
  task.outputTail = tail.slice(-OUTPUT_TAIL_LINES);
33304
33780
  }
33305
33781
  async function boundedRead(filePath, maxBytes, tail) {
33306
- const stats = statSync14(filePath);
33782
+ const stats = statSync15(filePath);
33307
33783
  const totalBytes = stats.size;
33308
33784
  const bytesToRead = Math.min(totalBytes, maxBytes);
33309
33785
  if (bytesToRead === 0) return { content: "", truncated: false, bytesRead: 0, totalBytes };
@@ -33348,7 +33824,7 @@ import { createHash as createHash7 } from "node:crypto";
33348
33824
  // packages/background-tasks/src/durable-fs.ts
33349
33825
  import { randomBytes as randomBytes3 } from "node:crypto";
33350
33826
  import { open as nodeOpen, rename as nodeRename, rm as nodeRm } from "node:fs/promises";
33351
- import { basename as basename16, dirname as dirname27, join as join56 } from "node:path";
33827
+ import { basename as basename16, dirname as dirname28, join as join57 } from "node:path";
33352
33828
  var DurableFileError = class extends Error {
33353
33829
  operation;
33354
33830
  path;
@@ -33486,7 +33962,7 @@ async function writeWithOperations(operations, path31, data) {
33486
33962
  }
33487
33963
  async function syncDirectoryAfterRename(operations, targetPath, temporaryPath) {
33488
33964
  if (operations.platform === "win32") return;
33489
- const directoryPath = dirname27(targetPath);
33965
+ const directoryPath = dirname28(targetPath);
33490
33966
  let handle;
33491
33967
  try {
33492
33968
  handle = await operations.openDirectory(directoryPath);
@@ -33545,7 +34021,7 @@ async function renameWithWindowsContention(operations, temporaryPath, targetPath
33545
34021
  return lastError;
33546
34022
  }
33547
34023
  function temporaryPathForTarget(target) {
33548
- return join56(dirname27(target), `.${basename16(target)}.${String(process.pid)}.${randomBytes3(6).toString("hex")}.tmp`);
34024
+ return join57(dirname28(target), `.${basename16(target)}.${String(process.pid)}.${randomBytes3(6).toString("hex")}.tmp`);
33549
34025
  }
33550
34026
  var nodeOperations = {
33551
34027
  platform: process.platform,
@@ -33610,10 +34086,10 @@ async function closeAndFsyncOutputStream(stream) {
33610
34086
  }
33611
34087
 
33612
34088
  // packages/background-tasks/src/pi-launch.ts
33613
- import { readFileSync as readFileSync41, realpathSync as realpathSync6, statSync as statSync15 } from "node:fs";
34089
+ import { readFileSync as readFileSync43, realpathSync as realpathSync6, statSync as statSync16 } from "node:fs";
33614
34090
  import { createRequire as createRequire3 } from "node:module";
33615
- import { dirname as dirname28, extname as extname4, isAbsolute as isAbsolute8, join as join57, relative as relative3, sep as sep5 } from "node:path";
33616
- import { fileURLToPath as fileURLToPath7 } from "node:url";
34091
+ import { dirname as dirname29, extname as extname4, isAbsolute as isAbsolute8, join as join58, relative as relative3, sep as sep5 } from "node:path";
34092
+ import { fileURLToPath as fileURLToPath8 } from "node:url";
33617
34093
  var PiLaunchResolutionError = class extends Error {
33618
34094
  code = "pi_executable_resolution_failed";
33619
34095
  constructor(message) {
@@ -33631,23 +34107,23 @@ function defaultResolvePackageJson(specifier) {
33631
34107
  if (specifier !== PI_PACKAGE_MANIFEST) throw manifestError;
33632
34108
  let packageEntry;
33633
34109
  try {
33634
- packageEntry = fileURLToPath7(import.meta.resolve(PI_PACKAGE_NAME));
34110
+ packageEntry = fileURLToPath8(import.meta.resolve(PI_PACKAGE_NAME));
33635
34111
  } catch (entryError) {
33636
34112
  throw new Error(
33637
34113
  `${errorMessage(manifestError)}; package entry resolve failed: ${errorMessage(entryError)}`
33638
34114
  );
33639
34115
  }
33640
34116
  const diagnostics = [];
33641
- let dir = dirname28(packageEntry);
34117
+ let dir = dirname29(packageEntry);
33642
34118
  for (; ; ) {
33643
- const candidate = join57(dir, "package.json");
34119
+ const candidate = join58(dir, "package.json");
33644
34120
  try {
33645
- if (statSync15(candidate).isFile()) return candidate;
34121
+ if (statSync16(candidate).isFile()) return candidate;
33646
34122
  diagnostics.push(`${candidate} is not a regular file`);
33647
34123
  } catch (statError) {
33648
34124
  diagnostics.push(`${candidate}: ${errorMessage(statError)}`);
33649
34125
  }
33650
- const parent = dirname28(dir);
34126
+ const parent = dirname29(dir);
33651
34127
  if (parent === dir) {
33652
34128
  throw new Error(
33653
34129
  `${errorMessage(manifestError)}; package entry search failed: ${diagnostics.join("; ")}`
@@ -33700,9 +34176,9 @@ function resolvePiLaunch(deps = {}) {
33700
34176
  const platform = deps.platform ?? process.platform;
33701
34177
  if (platform !== "win32") return { executable: "pi", argvPrefix: [], kind: "path" };
33702
34178
  const resolvePackageJson = deps.resolvePackageJson ?? defaultResolvePackageJson;
33703
- const readFile2 = deps.readFile ?? readFileSync41;
34179
+ const readFile2 = deps.readFile ?? readFileSync43;
33704
34180
  const realpath2 = deps.realpath ?? realpathSync6;
33705
- const stat = deps.stat ?? statSync15;
34181
+ const stat = deps.stat ?? statSync16;
33706
34182
  const execPath = deps.execPath ?? process.execPath;
33707
34183
  let manifestPath;
33708
34184
  try {
@@ -33710,14 +34186,14 @@ function resolvePiLaunch(deps = {}) {
33710
34186
  } catch (error) {
33711
34187
  failResolution(`package manifest resolve failed for ${PI_PACKAGE_MANIFEST}: ${errorMessage(error)}`);
33712
34188
  }
33713
- const packageRoot = dirname28(manifestPath);
34189
+ const packageRoot = dirname29(manifestPath);
33714
34190
  const packageRootReal = readPath("package root realpath", packageRoot, () => realpath2(packageRoot));
33715
34191
  const manifest = parseManifest(
33716
34192
  readPath("manifest read", manifestPath, () => readFile2(manifestPath)),
33717
34193
  manifestPath
33718
34194
  );
33719
34195
  const bin = readPiBin(manifest, manifestPath);
33720
- const targetCandidate = join57(packageRoot, bin);
34196
+ const targetCandidate = join58(packageRoot, bin);
33721
34197
  const targetReal = readPath("bin target realpath", targetCandidate, () => realpath2(targetCandidate));
33722
34198
  if (!pathInside(packageRootReal, targetReal)) {
33723
34199
  failResolution("Pi package bin target resolves outside the package root");
@@ -34405,8 +34881,8 @@ var BackgroundTaskRegistry = class _BackgroundTaskRegistry {
34405
34881
  const sessionId3 = sanitizePathSegment(ctx.sessionId ?? `session-${String(process.pid)}`);
34406
34882
  const runId = `${sessionId3}-${String(process.pid)}-${this.runtimeNonce}`;
34407
34883
  const tmpBase = process.env["UNIPI_BG_TMP_DIR"] ?? (process.env["TMPDIR"] ?? "/tmp");
34408
- const runtimeDirAbs = join58(tmpBase, "unipi-bg-tasks", runId);
34409
- const runtimeDirDisplay = join58(tmpBase, "unipi-bg-tasks", runId);
34884
+ const runtimeDirAbs = join59(tmpBase, "unipi-bg-tasks", runId);
34885
+ const runtimeDirDisplay = join59(tmpBase, "unipi-bg-tasks", runId);
34410
34886
  await mkdir(runtimeDirAbs, { recursive: true });
34411
34887
  this.runtimeDir = { abs: runtimeDirAbs, display: runtimeDirDisplay };
34412
34888
  return this.runtimeDir;
@@ -34422,9 +34898,9 @@ var BackgroundTaskRegistry = class _BackgroundTaskRegistry {
34422
34898
  const piTelemetryLaunch = piTelemetryRequested && baseInvocation.dialect === "posix" ? resolvePiLaunch({ platform: this.platform }) : void 0;
34423
34899
  const dir = await this.ensureRuntimeDir(ctx);
34424
34900
  const id = this.makeTaskIdFn();
34425
- const outputAbsPath = join58(dir.abs, `${id}.output`);
34426
- const metadataAbsPath = join58(dir.abs, `${id}.json`);
34427
- const outputPath = join58(dir.display, `${id}.output`);
34901
+ const outputAbsPath = join59(dir.abs, `${id}.output`);
34902
+ const metadataAbsPath = join59(dir.abs, `${id}.json`);
34903
+ const outputPath = join59(dir.display, `${id}.output`);
34428
34904
  const timeoutSeconds = typeof options.timeoutSeconds === "number" && Number.isFinite(options.timeoutSeconds) && options.timeoutSeconds > 0 ? Math.floor(options.timeoutSeconds) : void 0;
34429
34905
  const taskName = normalizeTaskName(options.name) ?? normalizeTaskName(options.description) ?? deriveTaskNameFromCommand(normalizedCommand);
34430
34906
  const trimmedDescription = options.description?.trim();
@@ -34477,7 +34953,7 @@ var BackgroundTaskRegistry = class _BackgroundTaskRegistry {
34477
34953
  if (baseInvocation.dialect === "posix") {
34478
34954
  if (piTelemetryLaunch === void 0)
34479
34955
  throw new Error("Pi telemetry launch spec was not resolved");
34480
- const wrapperAbsPath = join58(dir.abs, `${id}.pi-telemetry-wrapper.cjs`);
34956
+ const wrapperAbsPath = join59(dir.abs, `${id}.pi-telemetry-wrapper.cjs`);
34481
34957
  await writeFile3(
34482
34958
  wrapperAbsPath,
34483
34959
  createPiTelemetryWrapperSource(buildModelWindowIndex(ctx), piTelemetryLaunch),
@@ -34530,9 +35006,9 @@ ${normalizedCommand}`;
34530
35006
  throw new Error("Cannot adopt a background task while Pi is shutting down");
34531
35007
  const dir = await this.ensureRuntimeDir(ctx);
34532
35008
  const id = this.makeTaskIdFn();
34533
- const outputAbsPath = join58(dir.abs, `${id}.output`);
34534
- const metadataAbsPath = join58(dir.abs, `${id}.json`);
34535
- const outputPath = join58(dir.display, `${id}.output`);
35009
+ const outputAbsPath = join59(dir.abs, `${id}.output`);
35010
+ const metadataAbsPath = join59(dir.abs, `${id}.json`);
35011
+ const outputPath = join59(dir.display, `${id}.output`);
34536
35012
  const taskName = normalizeTaskName(options.name) ?? deriveTaskNameFromCommand(normalizedCommand);
34537
35013
  const task = {
34538
35014
  id,
@@ -37245,8 +37721,8 @@ function btwExtension(pi) {
37245
37721
 
37246
37722
  // packages/web-api/src/index.ts
37247
37723
  init_core();
37248
- import { dirname as dirname29 } from "node:path";
37249
- import { fileURLToPath as fileURLToPath8 } from "node:url";
37724
+ import { dirname as dirname30 } from "node:path";
37725
+ import { fileURLToPath as fileURLToPath9 } from "node:url";
37250
37726
 
37251
37727
  // packages/web-api/src/tools.ts
37252
37728
  init_registry();
@@ -39134,7 +39610,7 @@ var perplexityProvider = {
39134
39610
  registry4.register(perplexityProvider);
39135
39611
 
39136
39612
  // packages/web-api/src/index.ts
39137
- var VERSION5 = getPackageVersion(dirname29(fileURLToPath8(import.meta.url)));
39613
+ var VERSION5 = getPackageVersion(dirname30(fileURLToPath9(import.meta.url)));
39138
39614
  function getInfoRegistry2() {
39139
39615
  return globalThis.__unipi_info_registry;
39140
39616
  }
@@ -39235,8 +39711,8 @@ function src_default2(pi) {
39235
39711
  // packages/skill-registry/index.ts
39236
39712
  init_core();
39237
39713
  import { existsSync as existsSync46 } from "node:fs";
39238
- import { dirname as dirname31 } from "node:path";
39239
- import { fileURLToPath as fileURLToPath9 } from "node:url";
39714
+ import { dirname as dirname32 } from "node:path";
39715
+ import { fileURLToPath as fileURLToPath10 } from "node:url";
39240
39716
 
39241
39717
  // packages/skill-registry/src/registry.ts
39242
39718
  import { homedir as homedir34 } from "node:os";
@@ -39604,11 +40080,11 @@ var SkillEditor = class {
39604
40080
  };
39605
40081
 
39606
40082
  // packages/skill-registry/src/vault.ts
39607
- import { existsSync as existsSync45, readdirSync as readdirSync17, readFileSync as readFileSync45, statSync as statSync17 } from "node:fs";
40083
+ import { existsSync as existsSync45, readdirSync as readdirSync18, readFileSync as readFileSync47, statSync as statSync18 } from "node:fs";
39608
40084
  import { homedir as homedir36 } from "node:os";
39609
- import { join as join62 } from "node:path";
40085
+ import { join as join63 } from "node:path";
39610
40086
  function vaultDir() {
39611
- return process.env.UNIPI_SKILL_VAULT || join62(homedir36(), ".unipi", "skill-vault");
40087
+ return process.env.UNIPI_SKILL_VAULT || join63(homedir36(), ".unipi", "skill-vault");
39612
40088
  }
39613
40089
  function parseFrontmatter3(text) {
39614
40090
  const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---/);
@@ -39635,14 +40111,14 @@ function listVaultSkills(dir = vaultDir()) {
39635
40111
  if (depth > 3 || !existsSync45(d)) return;
39636
40112
  let names;
39637
40113
  try {
39638
- names = readdirSync17(d);
40114
+ names = readdirSync18(d);
39639
40115
  } catch {
39640
40116
  return;
39641
40117
  }
39642
40118
  if (names.includes("SKILL.md") && d !== dir) {
39643
- const filePath = join62(d, "SKILL.md");
40119
+ const filePath = join63(d, "SKILL.md");
39644
40120
  try {
39645
- const fm = parseFrontmatter3(readFileSync45(filePath, "utf-8"));
40121
+ const fm = parseFrontmatter3(readFileSync47(filePath, "utf-8"));
39646
40122
  const name = fm.name || d.split("/").pop();
39647
40123
  if (fm.description) out.push({ name, description: fm.description, filePath, baseDir: d });
39648
40124
  } catch {
@@ -39651,9 +40127,9 @@ function listVaultSkills(dir = vaultDir()) {
39651
40127
  }
39652
40128
  for (const n of names) {
39653
40129
  if (n.startsWith(".")) continue;
39654
- const p = join62(d, n);
40130
+ const p = join63(d, n);
39655
40131
  try {
39656
- if (statSync17(p).isDirectory()) walk2(p, depth + 1);
40132
+ if (statSync18(p).isDirectory()) walk2(p, depth + 1);
39657
40133
  } catch {
39658
40134
  }
39659
40135
  }
@@ -39664,7 +40140,7 @@ function listVaultSkills(dir = vaultDir()) {
39664
40140
 
39665
40141
  // packages/skill-registry/src/judge.ts
39666
40142
  init_core();
39667
- import { basename as basename17, dirname as dirname30 } from "node:path";
40143
+ import { basename as basename17, dirname as dirname31 } from "node:path";
39668
40144
  import { homedir as homedir37 } from "node:os";
39669
40145
  var SKILLS_JUDGED_ENTRY = "unipi:skills-judged";
39670
40146
  var SKILLS_REVEALED_ENTRY = "unipi:skills-revealed";
@@ -39783,7 +40259,7 @@ function hiddenIndex(hidden) {
39783
40259
  const odd = [];
39784
40260
  for (const e of [...hidden].sort((a, b) => a.name.localeCompare(b.name))) {
39785
40261
  if (e.location && basename17(e.location) === e.name) {
39786
- const parent = tilde(dirname30(e.location));
40262
+ const parent = tilde(dirname31(e.location));
39787
40263
  groups.set(parent, [...groups.get(parent) ?? [], e.name]);
39788
40264
  } else {
39789
40265
  odd.push(`${e.name} (${tilde(e.location)}/SKILL.md)`);
@@ -39872,7 +40348,7 @@ Read a skill's SKILL.md before using it.`;
39872
40348
  }
39873
40349
 
39874
40350
  // packages/skill-registry/index.ts
39875
- var VERSION6 = getPackageVersion(dirname31(fileURLToPath9(import.meta.url)));
40351
+ var VERSION6 = getPackageVersion(dirname32(fileURLToPath10(import.meta.url)));
39876
40352
  function skillRegistry(pi) {
39877
40353
  let state2 = null;
39878
40354
  let lastCatalog = [];
@@ -40051,8 +40527,8 @@ function skillRegistry(pi) {
40051
40527
 
40052
40528
  // packages/ask-user/index.ts
40053
40529
  init_core();
40054
- import { dirname as dirname32 } from "node:path";
40055
- import { fileURLToPath as fileURLToPath10 } from "node:url";
40530
+ import { dirname as dirname33 } from "node:path";
40531
+ import { fileURLToPath as fileURLToPath11 } from "node:url";
40056
40532
 
40057
40533
  // packages/ask-user/config.ts
40058
40534
  init_core();
@@ -40134,7 +40610,7 @@ function getAskUserSettings(cwd = process.cwd()) {
40134
40610
 
40135
40611
  // packages/ask-user/tools.ts
40136
40612
  init_core();
40137
- import { readFileSync as readFileSync46 } from "node:fs";
40613
+ import { readFileSync as readFileSync48 } from "node:fs";
40138
40614
  import { Type as Type13 } from "typebox";
40139
40615
  import { Text as Text7 } from "@earendil-works/pi-tui";
40140
40616
 
@@ -40145,9 +40621,9 @@ import { Input as Input4, Key as Key17, matchesKey as matchesKey17, truncateToWi
40145
40621
  // packages/ask-user/clipboard.ts
40146
40622
  import { spawnSync as spawnSync3 } from "node:child_process";
40147
40623
  import { randomUUID as randomUUID7 } from "node:crypto";
40148
- import { writeFileSync as writeFileSync33 } from "node:fs";
40624
+ import { writeFileSync as writeFileSync34 } from "node:fs";
40149
40625
  import { tmpdir as tmpdir6 } from "node:os";
40150
- import { join as join63 } from "node:path";
40626
+ import { join as join64 } from "node:path";
40151
40627
  function run2(cmd, args) {
40152
40628
  try {
40153
40629
  const r = spawnSync3(cmd, args, { timeout: 3e3, maxBuffer: 64 * 1024 * 1024 });
@@ -40172,9 +40648,9 @@ function readClipboardImageFile(env = process.env) {
40172
40648
  data = wayland ? run2("wl-paste", ["--type", found]) : run2("xclip", ["-selection", "clipboard", "-t", found, "-o"]);
40173
40649
  }
40174
40650
  if (!data) return void 0;
40175
- const file = join63(tmpdir6(), `unipi-ask-${randomUUID7()}${EXT2[type] ?? ".png"}`);
40651
+ const file = join64(tmpdir6(), `unipi-ask-${randomUUID7()}${EXT2[type] ?? ".png"}`);
40176
40652
  try {
40177
- writeFileSync33(file, data);
40653
+ writeFileSync34(file, data);
40178
40654
  return file;
40179
40655
  } catch {
40180
40656
  return void 0;
@@ -40776,7 +41252,7 @@ function imageParts(attachments) {
40776
41252
  for (const a of attachments) {
40777
41253
  if (a.kind !== "image") continue;
40778
41254
  try {
40779
- parts.push({ type: "image", data: readFileSync46(a.path).toString("base64"), mimeType: a.mimeType ?? "image/png" });
41255
+ parts.push({ type: "image", data: readFileSync48(a.path).toString("base64"), mimeType: a.mimeType ?? "image/png" });
40780
41256
  } catch {
40781
41257
  }
40782
41258
  }
@@ -40942,7 +41418,7 @@ function renderAskResult(details, theme) {
40942
41418
  }
40943
41419
 
40944
41420
  // packages/ask-user/index.ts
40945
- var VERSION7 = getPackageVersion(dirname32(fileURLToPath10(import.meta.url)));
41421
+ var VERSION7 = getPackageVersion(dirname33(fileURLToPath11(import.meta.url)));
40946
41422
  function ask_user_default(pi) {
40947
41423
  registerAskUserTools(pi);
40948
41424
  const sync = (cwd) => {
@@ -40968,8 +41444,8 @@ function ask_user_default(pi) {
40968
41444
 
40969
41445
  // packages/mcp/src/index.ts
40970
41446
  init_core();
40971
- import { dirname as dirname35 } from "node:path";
40972
- import { fileURLToPath as fileURLToPath11 } from "node:url";
41447
+ import { dirname as dirname36 } from "node:path";
41448
+ import { fileURLToPath as fileURLToPath12 } from "node:url";
40973
41449
 
40974
41450
  // packages/mcp/src/config/manager.ts
40975
41451
  import * as fs26 from "node:fs";
@@ -41052,7 +41528,7 @@ function ensureDir2(dir) {
41052
41528
  fs26.mkdirSync(dir, { recursive: true });
41053
41529
  }
41054
41530
  }
41055
- function readJsonFile(filePath) {
41531
+ function readJsonFile2(filePath) {
41056
41532
  try {
41057
41533
  const content = fs26.readFileSync(filePath, "utf-8");
41058
41534
  return JSON.parse(content);
@@ -41076,7 +41552,7 @@ function writeJsonFile(filePath, data, chmod) {
41076
41552
  }
41077
41553
  function loadMcpConfig(dir) {
41078
41554
  const filePath = path28.join(dir, "mcp-config.json");
41079
- const raw = readJsonFile(filePath);
41555
+ const raw = readJsonFile2(filePath);
41080
41556
  if (!raw) return { ...DEFAULT_MCP_CONFIG };
41081
41557
  const validation = validateMcpConfig(raw);
41082
41558
  if (!validation.valid) {
@@ -41089,7 +41565,7 @@ ${validation.errors.join("\n")}`
41089
41565
  }
41090
41566
  function loadMetadata(dir) {
41091
41567
  const filePath = path28.join(dir, "config.json");
41092
- const raw = readJsonFile(filePath);
41568
+ const raw = readJsonFile2(filePath);
41093
41569
  if (!raw) return { ...DEFAULT_METADATA, servers: {}, sync: { ...DEFAULT_METADATA.sync } };
41094
41570
  return {
41095
41571
  servers: raw.servers ?? {},
@@ -42854,7 +43330,7 @@ registerSettings({
42854
43330
  });
42855
43331
 
42856
43332
  // packages/mcp/src/index.ts
42857
- var VERSION8 = getPackageVersion(dirname35(fileURLToPath11(import.meta.url)));
43333
+ var VERSION8 = getPackageVersion(dirname36(fileURLToPath12(import.meta.url)));
42858
43334
  var registry5 = null;
42859
43335
  function getInfoRegistry3() {
42860
43336
  return globalThis.__unipi_info_registry;
@@ -43051,8 +43527,8 @@ Updated: ${catalog.lastUpdated}`,
43051
43527
 
43052
43528
  // packages/notify/index.ts
43053
43529
  init_core();
43054
- import { dirname as dirname39 } from "node:path";
43055
- import { fileURLToPath as fileURLToPath12 } from "node:url";
43530
+ import { dirname as dirname40 } from "node:path";
43531
+ import { fileURLToPath as fileURLToPath13 } from "node:url";
43056
43532
 
43057
43533
  // packages/notify/tools.ts
43058
43534
  init_core();
@@ -43060,7 +43536,7 @@ import { Type as Type14 } from "typebox";
43060
43536
 
43061
43537
  // packages/notify/settings.ts
43062
43538
  init_core();
43063
- import { join as join66 } from "path";
43539
+ import { join as join67 } from "path";
43064
43540
  import { homedir as homedir39 } from "os";
43065
43541
 
43066
43542
  // packages/notify/activity.ts
@@ -43380,8 +43856,8 @@ init_core();
43380
43856
 
43381
43857
  // packages/notify/ntfy-config.ts
43382
43858
  init_core();
43383
- import { readFileSync as readFileSync49, writeFileSync as writeFileSync36, mkdirSync as mkdirSync37, existsSync as existsSync49 } from "fs";
43384
- import { dirname as dirname37, join as join67 } from "path";
43859
+ import { readFileSync as readFileSync51, writeFileSync as writeFileSync37, mkdirSync as mkdirSync39, existsSync as existsSync49 } from "fs";
43860
+ import { dirname as dirname38, join as join68 } from "path";
43385
43861
  import { homedir as homedir40 } from "os";
43386
43862
  var DEFAULT_NTFY_CONFIG = {
43387
43863
  enabled: false,
@@ -43389,14 +43865,14 @@ var DEFAULT_NTFY_CONFIG = {
43389
43865
  priority: 3
43390
43866
  };
43391
43867
  function getGlobalNtfyPath() {
43392
- return join67(homedir40(), ".unipi", "config", "notify", "ntfy.json");
43868
+ return join68(homedir40(), ".unipi", "config", "notify", "ntfy.json");
43393
43869
  }
43394
43870
  function getProjectNtfyPath(cwd) {
43395
- return join67(cwd, ".unipi", "config", "notify", "ntfy.json");
43871
+ return join68(cwd, ".unipi", "config", "notify", "ntfy.json");
43396
43872
  }
43397
43873
  function readNtfyJson(filePath) {
43398
43874
  try {
43399
- const raw = readFileSync49(filePath, "utf-8");
43875
+ const raw = readFileSync51(filePath, "utf-8");
43400
43876
  const parsed = JSON.parse(raw);
43401
43877
  return { ...DEFAULT_NTFY_CONFIG, ...parsed };
43402
43878
  } catch (err) {
@@ -43446,7 +43922,7 @@ function migrateFromLegacyConfig() {
43446
43922
  if (existsSync49(globalNtfyPath)) {
43447
43923
  return;
43448
43924
  }
43449
- const legacyConfigPath = join67(
43925
+ const legacyConfigPath = join68(
43450
43926
  homedir40(),
43451
43927
  ".unipi",
43452
43928
  "config",
@@ -43455,7 +43931,7 @@ function migrateFromLegacyConfig() {
43455
43931
  );
43456
43932
  try {
43457
43933
  if (!existsSync49(legacyConfigPath)) return;
43458
- const raw = readFileSync49(legacyConfigPath, "utf-8");
43934
+ const raw = readFileSync51(legacyConfigPath, "utf-8");
43459
43935
  const parsed = JSON.parse(raw);
43460
43936
  const ntfySection = parsed.ntfy;
43461
43937
  if (!ntfySection) return;
@@ -43468,9 +43944,9 @@ function migrateFromLegacyConfig() {
43468
43944
  token: ntfySection.token,
43469
43945
  priority: ntfySection.priority ?? 3
43470
43946
  };
43471
- const dir = dirname37(globalNtfyPath);
43472
- mkdirSync37(dir, { recursive: true });
43473
- writeFileSync36(
43947
+ const dir = dirname38(globalNtfyPath);
43948
+ mkdirSync39(dir, { recursive: true });
43949
+ writeFileSync37(
43474
43950
  globalNtfyPath,
43475
43951
  JSON.stringify(migratedConfig, null, 2) + "\n",
43476
43952
  "utf-8"
@@ -43484,8 +43960,8 @@ import notifier from "node-notifier";
43484
43960
 
43485
43961
  // packages/notify/platforms/focus-win.ts
43486
43962
  import { execFile as execFile4 } from "child_process";
43487
- import { writeFileSync as writeFileSync37, rmSync as rmSync7, mkdtempSync } from "fs";
43488
- import { join as join68 } from "path";
43963
+ import { writeFileSync as writeFileSync38, rmSync as rmSync8, mkdtempSync } from "fs";
43964
+ import { join as join69 } from "path";
43489
43965
  import { tmpdir as tmpdir7 } from "os";
43490
43966
  var POWERCHECK_SCRIPT = `
43491
43967
  param($targetPid)
@@ -43521,9 +43997,9 @@ async function isWindowFocusedOnWindows() {
43521
43997
  }
43522
43998
  let tmpDir = null;
43523
43999
  try {
43524
- tmpDir = mkdtempSync(join68(tmpdir7(), "pi-focus-"));
43525
- const scriptPath = join68(tmpDir, "check.ps1");
43526
- writeFileSync37(scriptPath, POWERCHECK_SCRIPT, "utf-8");
44000
+ tmpDir = mkdtempSync(join69(tmpdir7(), "pi-focus-"));
44001
+ const scriptPath = join69(tmpDir, "check.ps1");
44002
+ writeFileSync38(scriptPath, POWERCHECK_SCRIPT, "utf-8");
43527
44003
  const stdout = await new Promise((resolve18, reject) => {
43528
44004
  execFile4(
43529
44005
  "powershell.exe",
@@ -43551,7 +44027,7 @@ async function isWindowFocusedOnWindows() {
43551
44027
  } finally {
43552
44028
  if (tmpDir) {
43553
44029
  try {
43554
- rmSync7(tmpDir, { recursive: true });
44030
+ rmSync8(tmpDir, { recursive: true });
43555
44031
  } catch {
43556
44032
  }
43557
44033
  }
@@ -43759,15 +44235,15 @@ async function publishToEndpoint(endpoint, title, message, priority, options) {
43759
44235
  }
43760
44236
 
43761
44237
  // packages/notify/app-endpoints.ts
43762
- import { mkdirSync as mkdirSync38, readFileSync as readFileSync50, renameSync as renameSync15, writeFileSync as writeFileSync38 } from "fs";
43763
- import { dirname as dirname38, join as join69 } from "path";
44238
+ import { mkdirSync as mkdirSync40, readFileSync as readFileSync52, renameSync as renameSync16, writeFileSync as writeFileSync39 } from "fs";
44239
+ import { dirname as dirname39, join as join70 } from "path";
43764
44240
  import { homedir as homedir41 } from "os";
43765
44241
  function path30() {
43766
- return join69(homedir41(), ".unipi", "config", "notify", "app-endpoints.json");
44242
+ return join70(homedir41(), ".unipi", "config", "notify", "app-endpoints.json");
43767
44243
  }
43768
44244
  function loadAppEndpoints() {
43769
44245
  try {
43770
- const raw = readFileSync50(path30(), "utf-8");
44246
+ const raw = readFileSync52(path30(), "utf-8");
43771
44247
  const parsed = JSON.parse(raw);
43772
44248
  if (!Array.isArray(parsed.endpoints)) return [];
43773
44249
  return parsed.endpoints.filter((e) => !!e && typeof e.url === "string");
@@ -43777,17 +44253,17 @@ function loadAppEndpoints() {
43777
44253
  }
43778
44254
 
43779
44255
  // packages/notify/notify-key.ts
43780
- import { readFileSync as readFileSync51 } from "fs";
43781
- import { join as join70 } from "path";
44256
+ import { readFileSync as readFileSync53 } from "fs";
44257
+ import { join as join71 } from "path";
43782
44258
  import { homedir as homedir42 } from "os";
43783
44259
  function notifyKeyPath() {
43784
44260
  const dir = process.env.UNIPI_HOST_DIR;
43785
- if (dir && dir.trim() !== "") return join70(dir, "notify-key");
43786
- return join70(homedir42(), ".unipi", "app-host", "notify-key");
44261
+ if (dir && dir.trim() !== "") return join71(dir, "notify-key");
44262
+ return join71(homedir42(), ".unipi", "app-host", "notify-key");
43787
44263
  }
43788
44264
  function loadNotifyKey() {
43789
44265
  try {
43790
- const bytes = readFileSync51(notifyKeyPath());
44266
+ const bytes = readFileSync53(notifyKeyPath());
43791
44267
  if (bytes.length !== 32) return void 0;
43792
44268
  return bytes;
43793
44269
  } catch {
@@ -45939,7 +46415,7 @@ ${results.join("\n")}`, "info");
45939
46415
  }
45940
46416
 
45941
46417
  // packages/notify/index.ts
45942
- var VERSION9 = getPackageVersion(dirname39(fileURLToPath12(import.meta.url)));
46418
+ var VERSION9 = getPackageVersion(dirname40(fileURLToPath13(import.meta.url)));
45943
46419
  var unsubTerminalInput;
45944
46420
  function notify_default(pi) {
45945
46421
  registerNotifyTools(pi);
@@ -45982,15 +46458,15 @@ function notify_default(pi) {
45982
46458
 
45983
46459
  // packages/kanboard/index.ts
45984
46460
  init_core();
45985
- import { dirname as dirname41 } from "node:path";
45986
- import { fileURLToPath as fileURLToPath14 } from "node:url";
46461
+ import { dirname as dirname42 } from "node:path";
46462
+ import { fileURLToPath as fileURLToPath15 } from "node:url";
45987
46463
 
45988
46464
  // packages/kanboard/src/bin.ts
45989
46465
  import { execFile as execFile5 } from "node:child_process";
45990
46466
  import { existsSync as existsSync50 } from "node:fs";
45991
46467
  import { createRequire as createRequire4 } from "node:module";
45992
- import { dirname as dirname40, join as join71, resolve as resolve17 } from "node:path";
45993
- import { fileURLToPath as fileURLToPath13 } from "node:url";
46468
+ import { dirname as dirname41, join as join72, resolve as resolve17 } from "node:path";
46469
+ import { fileURLToPath as fileURLToPath14 } from "node:url";
45994
46470
  function platformKey(platform = process.platform, arch = process.arch) {
45995
46471
  return `${platform}-${arch}`;
45996
46472
  }
@@ -45998,7 +46474,7 @@ function exeSuffix(platform = process.platform) {
45998
46474
  return platform === "win32" ? ".exe" : "";
45999
46475
  }
46000
46476
  function repoRoot() {
46001
- return resolve17(dirname40(fileURLToPath13(import.meta.url)), "..", "..", "..");
46477
+ return resolve17(dirname41(fileURLToPath14(import.meta.url)), "..", "..", "..");
46002
46478
  }
46003
46479
  function isRunnable(path31) {
46004
46480
  try {
@@ -46012,7 +46488,7 @@ function platformPackagePath(from) {
46012
46488
  try {
46013
46489
  const require2 = createRequire4(from ?? import.meta.url);
46014
46490
  const pkg = require2.resolve(`${name}/package.json`);
46015
- return join71(dirname40(pkg), "bin", `unipi-kanboard${exeSuffix()}`);
46491
+ return join72(dirname41(pkg), "bin", `unipi-kanboard${exeSuffix()}`);
46016
46492
  } catch {
46017
46493
  return null;
46018
46494
  }
@@ -46022,7 +46498,7 @@ function devBuildPath() {
46022
46498
  const names = [`unipi-kanboard${exeSuffix()}`];
46023
46499
  for (const profile of ["release", "debug"]) {
46024
46500
  for (const name of names) {
46025
- const candidate = join71(root, "crates", "kanboard", "target", profile, name);
46501
+ const candidate = join72(root, "crates", "kanboard", "target", profile, name);
46026
46502
  if (isRunnable(candidate)) return candidate;
46027
46503
  }
46028
46504
  }
@@ -46161,9 +46637,9 @@ function boardProgressData(tasks, slug) {
46161
46637
  // packages/kanboard/src/commands.ts
46162
46638
  init_core();
46163
46639
  import { execFile as execFile6, spawn as spawn6 } from "node:child_process";
46164
- import { existsSync as existsSync52, mkdtempSync as mkdtempSync2, readFileSync as readFileSync53, rmSync as rmSync8, writeFileSync as writeFileSync39 } from "node:fs";
46640
+ import { existsSync as existsSync52, mkdtempSync as mkdtempSync2, readFileSync as readFileSync55, rmSync as rmSync9, writeFileSync as writeFileSync40 } from "node:fs";
46165
46641
  import { homedir as homedir43, hostname as osHostname, networkInterfaces, tmpdir as tmpdir8 } from "node:os";
46166
- import { join as join72 } from "node:path";
46642
+ import { join as join73 } from "node:path";
46167
46643
  import { promisify } from "node:util";
46168
46644
  import { truncateToWidth as truncateToWidth28, visibleWidth as visibleWidth27 } from "@earendil-works/pi-tui";
46169
46645
 
@@ -46246,7 +46722,7 @@ function asStopResult(value) {
46246
46722
 
46247
46723
  // packages/kanboard/src/settings.ts
46248
46724
  init_core();
46249
- import { existsSync as existsSync51, readFileSync as readFileSync52 } from "node:fs";
46725
+ import { existsSync as existsSync51, readFileSync as readFileSync54 } from "node:fs";
46250
46726
  var DEFAULT_SETTINGS6 = {
46251
46727
  chainGate: "in_review",
46252
46728
  idleMin: 10,
@@ -46364,7 +46840,7 @@ function storedDoWritesSet(cwd) {
46364
46840
  for (const file of [projectSettingsPath(cwd, KANBOARD_NAMESPACE), globalSettingsPath(KANBOARD_NAMESPACE)]) {
46365
46841
  try {
46366
46842
  if (!existsSync51(file)) continue;
46367
- const parsed = JSON.parse(readFileSync52(file, "utf-8"));
46843
+ const parsed = JSON.parse(readFileSync54(file, "utf-8"));
46368
46844
  if (typeof parsed.doWrites === "number") return true;
46369
46845
  } catch {
46370
46846
  }
@@ -46375,7 +46851,7 @@ function storedLegacyDoCredits(cwd) {
46375
46851
  for (const file of [projectSettingsPath(cwd, KANBOARD_NAMESPACE), globalSettingsPath(KANBOARD_NAMESPACE)]) {
46376
46852
  try {
46377
46853
  if (!existsSync51(file)) continue;
46378
- const parsed = JSON.parse(readFileSync52(file, "utf-8"));
46854
+ const parsed = JSON.parse(readFileSync54(file, "utf-8"));
46379
46855
  const value = parsed.doCredits;
46380
46856
  if (typeof value === "number" && value >= 0) return value;
46381
46857
  } catch {
@@ -46504,7 +46980,7 @@ function detectFilePaths(text) {
46504
46980
  const wrapped = token.match(/^!?\[[^\]]*\]\((.+)\)[.,;:]?$/) ?? token.match(/^\((.+)\)[.,;:]?$/);
46505
46981
  if (wrapped) token = wrapped[1];
46506
46982
  token = token.replace(/[.,;:]+$/, "");
46507
- const expanded = token.startsWith("~/") ? join72(homedir43(), token.slice(2)) : token;
46983
+ const expanded = token.startsWith("~/") ? join73(homedir43(), token.slice(2)) : token;
46508
46984
  if (!(token.startsWith("/") || token.startsWith("~/"))) continue;
46509
46985
  if (!existsSync52(expanded)) continue;
46510
46986
  if (!found.includes(expanded)) found.push(expanded);
@@ -46591,11 +47067,11 @@ function formatBoardUrls(options) {
46591
47067
  };
46592
47068
  }
46593
47069
  function kanboardHome() {
46594
- return process.env.UNIPI_KANBOARD_HOME?.trim() || join72(homedir43(), ".unipi", "kanboard");
47070
+ return process.env.UNIPI_KANBOARD_HOME?.trim() || join73(homedir43(), ".unipi", "kanboard");
46595
47071
  }
46596
47072
  function readDaemonInfo() {
46597
47073
  try {
46598
- const raw = readFileSync53(join72(kanboardHome(), "daemon.json"), "utf-8");
47074
+ const raw = readFileSync55(join73(kanboardHome(), "daemon.json"), "utf-8");
46599
47075
  const parsed = JSON.parse(raw);
46600
47076
  return typeof parsed?.port === "number" ? parsed : null;
46601
47077
  } catch {
@@ -46805,8 +47281,8 @@ async function runAdd(deps, ctx, text) {
46805
47281
  try {
46806
47282
  const argv = ["add", ...parsed.title ? [parsed.title] : [], ...parsed.flags];
46807
47283
  if (parsed.description) {
46808
- bodyFile = join72(mkdtempSync2(join72(tmpdir8(), "kb-add-")), "body.md");
46809
- writeFileSync39(bodyFile, parsed.description);
47284
+ bodyFile = join73(mkdtempSync2(join73(tmpdir8(), "kb-add-")), "body.md");
47285
+ writeFileSync40(bodyFile, parsed.description);
46810
47286
  argv.push("--body-file", bodyFile);
46811
47287
  }
46812
47288
  for (const file of parsed.attaches) argv.push("--attach", file);
@@ -46821,7 +47297,7 @@ async function runAdd(deps, ctx, text) {
46821
47297
  "error"
46822
47298
  );
46823
47299
  } finally {
46824
- if (bodyFile) rmSync8(join72(bodyFile, ".."), { recursive: true, force: true });
47300
+ if (bodyFile) rmSync9(join73(bodyFile, ".."), { recursive: true, force: true });
46825
47301
  }
46826
47302
  }
46827
47303
  async function captureToBacklog(deps, opts) {
@@ -46842,8 +47318,8 @@ async function captureToBacklog(deps, opts) {
46842
47318
  const attaches = detectFilePaths(text);
46843
47319
  let bodyFile = null;
46844
47320
  try {
46845
- bodyFile = join72(mkdtempSync2(join72(tmpdir8(), "kb-capture-")), "body.md");
46846
- writeFileSync39(bodyFile, text);
47321
+ bodyFile = join73(mkdtempSync2(join73(tmpdir8(), "kb-capture-")), "body.md");
47322
+ writeFileSync40(bodyFile, text);
46847
47323
  const argv = ["--project", slug, "add", "--status", "backlog", "--body-file", bodyFile];
46848
47324
  for (const file of attaches) argv.push("--attach", file);
46849
47325
  const task = asTask("add", await client.run(argv, { cwd: opts.cwd }));
@@ -46855,7 +47331,7 @@ async function captureToBacklog(deps, opts) {
46855
47331
  reason: error instanceof KanboardCliError ? error.message : String(error)
46856
47332
  };
46857
47333
  } finally {
46858
- if (bodyFile) rmSync8(join72(bodyFile, ".."), { recursive: true, force: true });
47334
+ if (bodyFile) rmSync9(join73(bodyFile, ".."), { recursive: true, force: true });
46859
47335
  }
46860
47336
  }
46861
47337
  async function runStatus(deps, ctx) {
@@ -46927,8 +47403,8 @@ async function runDoctor(deps, ctx, pi) {
46927
47403
  }
46928
47404
  }
46929
47405
  try {
46930
- const settingsPath = join72(kanboardHome(), "settings.json");
46931
- const raw = JSON.parse(readFileSync53(settingsPath, "utf-8"));
47406
+ const settingsPath = join73(kanboardHome(), "settings.json");
47407
+ const raw = JSON.parse(readFileSync55(settingsPath, "utf-8"));
46932
47408
  const argv = Array.isArray(raw.piCommand) ? raw.piCommand : [];
46933
47409
  if (argv.length === 0) {
46934
47410
  lines4.push(check(null, "summarize via pi: not configured (open the board from pi once)"));
@@ -47697,20 +48173,20 @@ function createWriteGuard(options = {}) {
47697
48173
 
47698
48174
  // packages/kanboard/src/move.ts
47699
48175
  init_core();
47700
- import { existsSync as existsSync53, readdirSync as readdirSync18, readFileSync as readFileSync54 } from "node:fs";
48176
+ import { existsSync as existsSync53, readdirSync as readdirSync19, readFileSync as readFileSync56 } from "node:fs";
47701
48177
  import { homedir as homedir44 } from "node:os";
47702
- import { join as join73 } from "node:path";
48178
+ import { join as join74 } from "node:path";
47703
48179
  function kanboardHome2(env = process.env) {
47704
- return env.UNIPI_KANBOARD_HOME?.trim() || join73(homedir44(), ".unipi", "kanboard");
48180
+ return env.UNIPI_KANBOARD_HOME?.trim() || join74(homedir44(), ".unipi", "kanboard");
47705
48181
  }
47706
48182
  function projects() {
47707
- const root = join73(kanboardHome2(), "projects");
48183
+ const root = join74(kanboardHome2(), "projects");
47708
48184
  if (!existsSync53(root)) return [];
47709
48185
  const result2 = [];
47710
- for (const entry of readdirSync18(root, { withFileTypes: true })) {
48186
+ for (const entry of readdirSync19(root, { withFileTypes: true })) {
47711
48187
  if (!entry.isDirectory()) continue;
47712
48188
  try {
47713
- const parsed = JSON.parse(readFileSync54(join73(root, entry.name, "project.json"), "utf8"));
48189
+ const parsed = JSON.parse(readFileSync56(join74(root, entry.name, "project.json"), "utf8"));
47714
48190
  if (typeof parsed.slug === "string" && typeof parsed.root === "string") result2.push(parsed);
47715
48191
  } catch {
47716
48192
  }
@@ -47739,7 +48215,7 @@ var kanboardMoveHandler = {
47739
48215
  async apply() {
47740
48216
  if (ctx.dryRun) return;
47741
48217
  if (!binary) throw new Error("kanboard binary unavailable \u2014 update kanboard binary or build the dev binary");
47742
- ctx.backup(join73(kanboardHome2(), "projects", match.slug, "project.json"));
48218
+ ctx.backup(join74(kanboardHome2(), "projects", match.slug, "project.json"));
47743
48219
  const client = createCli(binary, { ...process.env, UNIPI_KANBOARD_HOME: kanboardHome2(), UNIPI_KANBOARD_ACTOR: "user" });
47744
48220
  try {
47745
48221
  await client.run(["project", "rebind", match.slug, "--root", ctx.newRoot], { cwd: ctx.newRoot });
@@ -48262,10 +48738,10 @@ function createDebugLog(env = process.env) {
48262
48738
  if (env.UNIPI_DEBUG_KANBOARD !== "1") return () => void 0;
48263
48739
  return (line) => {
48264
48740
  try {
48265
- const { appendFileSync: appendFileSync7, mkdirSync: mkdirSync47 } = __require("node:fs");
48741
+ const { appendFileSync: appendFileSync7, mkdirSync: mkdirSync49 } = __require("node:fs");
48266
48742
  const { homedir: homedir58 } = __require("node:os");
48267
48743
  const dir = `${homedir58()}/.unipi/logs`;
48268
- mkdirSync47(dir, { recursive: true });
48744
+ mkdirSync49(dir, { recursive: true });
48269
48745
  appendFileSync7(`${dir}/kanboard.log`, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
48270
48746
  `);
48271
48747
  } catch {
@@ -48274,7 +48750,7 @@ function createDebugLog(env = process.env) {
48274
48750
  }
48275
48751
 
48276
48752
  // packages/kanboard/index.ts
48277
- var VERSION10 = getPackageVersion(dirname41(fileURLToPath14(import.meta.url)));
48753
+ var VERSION10 = getPackageVersion(dirname42(fileURLToPath15(import.meta.url)));
48278
48754
  var KANBOARD_SKILL = "kanboard";
48279
48755
  var lastCliPrefix = null;
48280
48756
  function kanboard_default(pi) {
@@ -50901,7 +51377,7 @@ function buildCardData(opts) {
50901
51377
  }
50902
51378
  var dbg = (debug, data) => {
50903
51379
  if (!debug) return;
50904
- import("node:fs").then(({ writeFileSync: writeFileSync48 }) => writeFileSync48("/tmp/compactor-debug.json", JSON.stringify(data, null, 2))).catch(() => {
51380
+ import("node:fs").then(({ writeFileSync: writeFileSync49 }) => writeFileSync49("/tmp/compactor-debug.json", JSON.stringify(data, null, 2))).catch(() => {
50905
51381
  });
50906
51382
  };
50907
51383
  var readReason = (event) => {
@@ -52012,13 +52488,13 @@ function vccRecall(blocks, input) {
52012
52488
  }
52013
52489
 
52014
52490
  // packages/compactor/src/tools/ctx-doctor.ts
52015
- import { existsSync as existsSync54, readFileSync as readFileSync55, readdirSync as readdirSync19, statSync as statSync18 } from "node:fs";
52491
+ import { existsSync as existsSync54, readFileSync as readFileSync57, readdirSync as readdirSync20, statSync as statSync19 } from "node:fs";
52016
52492
  import { homedir as homedir46 } from "node:os";
52017
- import { join as join75 } from "node:path";
52018
- var LEGACY_DB_DIR = join75(homedir46(), ".unipi", "db", "compactor");
52493
+ import { join as join76 } from "node:path";
52494
+ var LEGACY_DB_DIR = join76(homedir46(), ".unipi", "db", "compactor");
52019
52495
  function piCompactionEnabled() {
52020
52496
  try {
52021
- const raw = JSON.parse(readFileSync55(join75(homedir46(), ".pi", "agent", "settings.json"), "utf-8"));
52497
+ const raw = JSON.parse(readFileSync57(join76(homedir46(), ".pi", "agent", "settings.json"), "utf-8"));
52022
52498
  const enabled = raw?.compaction?.enabled;
52023
52499
  return typeof enabled === "boolean" ? enabled : true;
52024
52500
  } catch {
@@ -52027,7 +52503,7 @@ function piCompactionEnabled() {
52027
52503
  }
52028
52504
  function dirSizeMb(dir) {
52029
52505
  try {
52030
- return readdirSync19(dir).reduce((sum, name) => sum + statSync18(join75(dir, name)).size, 0) / (1024 * 1024);
52506
+ return readdirSync20(dir).reduce((sum, name) => sum + statSync19(join76(dir, name)).size, 0) / (1024 * 1024);
52031
52507
  } catch {
52032
52508
  return 0;
52033
52509
  }
@@ -52339,8 +52815,8 @@ function compactorExtension(pi) {
52339
52815
 
52340
52816
  // packages/footer/src/index.ts
52341
52817
  init_core();
52342
- import { dirname as dirname42 } from "node:path";
52343
- import { fileURLToPath as fileURLToPath15 } from "node:url";
52818
+ import { dirname as dirname43 } from "node:path";
52819
+ import { fileURLToPath as fileURLToPath16 } from "node:url";
52344
52820
 
52345
52821
  // packages/footer/src/registry/index.ts
52346
52822
  var FooterRegistry = class {
@@ -52403,7 +52879,7 @@ function getFooterRegistry() {
52403
52879
 
52404
52880
  // packages/footer/src/config.ts
52405
52881
  init_core();
52406
- import { statSync as statSync19 } from "node:fs";
52882
+ import { statSync as statSync20 } from "node:fs";
52407
52883
 
52408
52884
  // packages/footer/src/rendering/icons.ts
52409
52885
  function detectNerdFontSupport() {
@@ -52744,7 +53220,7 @@ function configFileStamp(cwd) {
52744
53220
  let stamp = "";
52745
53221
  for (const file of [globalSettingsPath("footer"), projectSettingsPath(cwd, "footer")]) {
52746
53222
  try {
52747
- stamp += `${statSync19(file).mtimeMs};`;
53223
+ stamp += `${statSync20(file).mtimeMs};`;
52748
53224
  } catch {
52749
53225
  stamp += "-1;";
52750
53226
  }
@@ -53986,7 +54462,7 @@ function trayHasItems() {
53986
54462
  }
53987
54463
  publishSharedTps();
53988
54464
  var VERSION11 = getPackageVersion(
53989
- findPackageRoot(dirname42(fileURLToPath15(import.meta.url)), "@pi-unipi/footer") ?? dirname42(fileURLToPath15(import.meta.url))
54465
+ findPackageRoot(dirname43(fileURLToPath16(import.meta.url)), "@pi-unipi/footer") ?? dirname43(fileURLToPath16(import.meta.url))
53990
54466
  );
53991
54467
  function footerExtension(pi) {
53992
54468
  const state2 = {
@@ -54357,21 +54833,21 @@ function wireTpsStreamingEvents(pi) {
54357
54833
 
54358
54834
  // packages/updater/src/index.ts
54359
54835
  init_core();
54360
- import { fileURLToPath as fileURLToPath21 } from "node:url";
54836
+ import { fileURLToPath as fileURLToPath22 } from "node:url";
54361
54837
 
54362
54838
  // packages/updater/src/commands.ts
54363
54839
  init_core();
54364
54840
 
54365
54841
  // packages/updater/src/tui/readme-overlay.ts
54366
- import { readFileSync as readFileSync57 } from "fs";
54842
+ import { readFileSync as readFileSync59 } from "fs";
54367
54843
 
54368
54844
  // packages/updater/src/readme.ts
54369
54845
  init_core();
54370
54846
  init_core();
54371
- import { dirname as dirname43 } from "node:path";
54372
- import { fileURLToPath as fileURLToPath16 } from "node:url";
54373
- import { existsSync as existsSync55, readFileSync as readFileSync56 } from "fs";
54374
- import { join as join76 } from "path";
54847
+ import { dirname as dirname44 } from "node:path";
54848
+ import { fileURLToPath as fileURLToPath17 } from "node:url";
54849
+ import { existsSync as existsSync55, readFileSync as readFileSync58 } from "fs";
54850
+ import { join as join77 } from "path";
54375
54851
  var PACKAGE_MAP = {
54376
54852
  core: MODULES.CORE,
54377
54853
  workflow: MODULES.WORKFLOW,
@@ -54394,15 +54870,15 @@ var PACKAGE_MAP = {
54394
54870
  updater: MODULES.UPDATER
54395
54871
  };
54396
54872
  function resolveUnipiRoot() {
54397
- const dir = dirname43(fileURLToPath16(import.meta.url));
54873
+ const dir = dirname44(fileURLToPath17(import.meta.url));
54398
54874
  const root = findPackageRoot(dir, "@pi-unipi/unipi");
54399
- return root ?? join76(dir, "..");
54875
+ return root ?? join77(dir, "..");
54400
54876
  }
54401
54877
  function getPackageVersion2(dir) {
54402
54878
  try {
54403
- const pkgPath = join76(dir, "package.json");
54879
+ const pkgPath = join77(dir, "package.json");
54404
54880
  if (existsSync55(pkgPath)) {
54405
- const raw = readFileSync56(pkgPath, "utf-8");
54881
+ const raw = readFileSync58(pkgPath, "utf-8");
54406
54882
  const pkg = JSON.parse(raw);
54407
54883
  return pkg.version ?? "0.0.0";
54408
54884
  }
@@ -54413,7 +54889,7 @@ function getPackageVersion2(dir) {
54413
54889
  function discoverReadmes() {
54414
54890
  const root = resolveUnipiRoot();
54415
54891
  const entries2 = [];
54416
- const rootReadme = join76(root, "README.md");
54892
+ const rootReadme = join77(root, "README.md");
54417
54893
  if (existsSync55(rootReadme)) {
54418
54894
  entries2.push({
54419
54895
  name: "unipi",
@@ -54423,16 +54899,16 @@ function discoverReadmes() {
54423
54899
  });
54424
54900
  }
54425
54901
  for (const [shortName2, fullName] of Object.entries(PACKAGE_MAP)) {
54426
- const workspacePath = join76(root, "packages", shortName2, "README.md");
54427
- const nodeModulesPath = join76(root, "node_modules", fullName, "README.md");
54902
+ const workspacePath = join77(root, "packages", shortName2, "README.md");
54903
+ const nodeModulesPath = join77(root, "node_modules", fullName, "README.md");
54428
54904
  let readmePath = null;
54429
54905
  let versionDir = null;
54430
54906
  if (existsSync55(workspacePath)) {
54431
54907
  readmePath = workspacePath;
54432
- versionDir = join76(root, "packages", shortName2);
54908
+ versionDir = join77(root, "packages", shortName2);
54433
54909
  } else if (existsSync55(nodeModulesPath)) {
54434
54910
  readmePath = nodeModulesPath;
54435
- versionDir = join76(root, "node_modules", fullName);
54911
+ versionDir = join77(root, "node_modules", fullName);
54436
54912
  }
54437
54913
  if (readmePath && versionDir) {
54438
54914
  entries2.push({
@@ -54448,14 +54924,14 @@ function discoverReadmes() {
54448
54924
  function resolveReadmePath(packageName) {
54449
54925
  const root = resolveUnipiRoot();
54450
54926
  if (!packageName || packageName === "unipi") {
54451
- const rootReadme = join76(root, "README.md");
54927
+ const rootReadme = join77(root, "README.md");
54452
54928
  return existsSync55(rootReadme) ? rootReadme : null;
54453
54929
  }
54454
54930
  const fullName = PACKAGE_MAP[packageName];
54455
54931
  if (!fullName) return null;
54456
- const workspacePath = join76(root, "packages", packageName, "README.md");
54932
+ const workspacePath = join77(root, "packages", packageName, "README.md");
54457
54933
  if (existsSync55(workspacePath)) return workspacePath;
54458
- const nodeModulesPath = join76(root, "node_modules", fullName, "README.md");
54934
+ const nodeModulesPath = join77(root, "node_modules", fullName, "README.md");
54459
54935
  if (existsSync55(nodeModulesPath)) return nodeModulesPath;
54460
54936
  return null;
54461
54937
  }
@@ -54651,7 +55127,7 @@ function renderReadmeOverlay(params) {
54651
55127
  const idx = entries2.findIndex((e) => e.path === readmePath);
54652
55128
  if (idx >= 0) {
54653
55129
  openDirectIndex = idx;
54654
- const content = readFileSync57(readmePath, "utf-8");
55130
+ const content = readFileSync59(readmePath, "utf-8");
54655
55131
  const lines4 = renderMarkdown(content, (process.stdout.columns ?? 80) - 4, void 0);
54656
55132
  contentCache.set(readmePath, lines4);
54657
55133
  }
@@ -54673,7 +55149,7 @@ function renderReadmeOverlay(params) {
54673
55149
  return contentCache.get(entry.path);
54674
55150
  }
54675
55151
  try {
54676
- const content = readFileSync57(entry.path, "utf-8");
55152
+ const content = readFileSync59(entry.path, "utf-8");
54677
55153
  const lines4 = renderMarkdown(content, (process.stdout.columns ?? 80) - 4, theme);
54678
55154
  contentCache.set(entry.path, lines4);
54679
55155
  return lines4;
@@ -54686,13 +55162,13 @@ function renderReadmeOverlay(params) {
54686
55162
 
54687
55163
  // packages/updater/src/tui/changelog-overlay.ts
54688
55164
  import { existsSync as existsSync57 } from "fs";
54689
- import { fileURLToPath as fileURLToPath18 } from "node:url";
55165
+ import { fileURLToPath as fileURLToPath19 } from "node:url";
54690
55166
 
54691
55167
  // packages/updater/src/changelog.ts
54692
55168
  init_core();
54693
- import { existsSync as existsSync56, readFileSync as readFileSync58 } from "fs";
54694
- import { dirname as dirname44, join as join77 } from "node:path";
54695
- import { fileURLToPath as fileURLToPath17 } from "node:url";
55169
+ import { existsSync as existsSync56, readFileSync as readFileSync60 } from "fs";
55170
+ import { dirname as dirname45, join as join78 } from "node:path";
55171
+ import { fileURLToPath as fileURLToPath18 } from "node:url";
54696
55172
 
54697
55173
  // packages/updater/src/version.ts
54698
55174
  init_core();
@@ -54700,21 +55176,21 @@ init_core();
54700
55176
  // packages/updater/src/changelog.ts
54701
55177
  function resolveChangelogPath() {
54702
55178
  try {
54703
- const dir = dirname44(fileURLToPath17(import.meta.url));
55179
+ const dir = dirname45(fileURLToPath18(import.meta.url));
54704
55180
  const root = findPackageRoot(dir, "@pi-unipi/unipi");
54705
55181
  if (root) {
54706
- const packaged = join77(root, "CHANGELOG.md");
55182
+ const packaged = join78(root, "CHANGELOG.md");
54707
55183
  if (existsSync56(packaged)) return packaged;
54708
55184
  }
54709
55185
  } catch {
54710
55186
  }
54711
- return join77(process.cwd(), "CHANGELOG.md");
55187
+ return join78(process.cwd(), "CHANGELOG.md");
54712
55188
  }
54713
55189
  var VERSION_HEADER_RE = /^## \[(.+?)\](?:\s*[-—–]\s*(.+))?$/;
54714
55190
  var SECTION_HEADER_RE = /^### (.+)$/;
54715
55191
  function parseChangelog(filePath) {
54716
55192
  if (!existsSync56(filePath)) return [];
54717
- return parseChangelogContent(readFileSync58(filePath, "utf-8"));
55193
+ return parseChangelogContent(readFileSync60(filePath, "utf-8"));
54718
55194
  }
54719
55195
  function parseChangelogContent(content) {
54720
55196
  const trimmed = content.trim();
@@ -54802,7 +55278,7 @@ function getNewerVersions(entries2, installedVersion) {
54802
55278
  init_core();
54803
55279
  function renderChangelogOverlay() {
54804
55280
  const installedVersion = getInstalledPackageVersion(
54805
- fileURLToPath18(new URL("..", import.meta.url)),
55281
+ fileURLToPath19(new URL("..", import.meta.url)),
54806
55282
  "@pi-unipi/unipi"
54807
55283
  );
54808
55284
  return createListDetailOverlay({
@@ -54882,7 +55358,7 @@ function registerCommands3(pi) {
54882
55358
 
54883
55359
  // packages/updater/src/settings.ts
54884
55360
  init_core();
54885
- import { join as join78 } from "path";
55361
+ import { join as join79 } from "path";
54886
55362
  import { homedir as homedir47 } from "os";
54887
55363
  var DEFAULT_CONFIG7 = {
54888
55364
  checkIntervalMs: 36e5,
@@ -54942,23 +55418,23 @@ function mergeWithDefaults2(loaded) {
54942
55418
 
54943
55419
  // packages/updater/src/checker.ts
54944
55420
  init_core();
54945
- import { dirname as dirname47 } from "node:path";
54946
- import { fileURLToPath as fileURLToPath19 } from "node:url";
55421
+ import { dirname as dirname48 } from "node:path";
55422
+ import { fileURLToPath as fileURLToPath20 } from "node:url";
54947
55423
 
54948
55424
  // packages/updater/src/cache.ts
54949
55425
  init_core();
54950
- import { readFileSync as readFileSync59, writeFileSync as writeFileSync40, mkdirSync as mkdirSync39, existsSync as existsSync58 } from "fs";
54951
- import { dirname as dirname46, join as join79 } from "path";
55426
+ import { readFileSync as readFileSync61, writeFileSync as writeFileSync41, mkdirSync as mkdirSync41, existsSync as existsSync58 } from "fs";
55427
+ import { dirname as dirname47, join as join80 } from "path";
54952
55428
  import { homedir as homedir48 } from "os";
54953
55429
  function resolveCachePath() {
54954
55430
  const base = UPDATER_DIRS.CACHE.replace("~", homedir48());
54955
- return join79(base, "last-check.json");
55431
+ return join80(base, "last-check.json");
54956
55432
  }
54957
55433
  function readLastCheck() {
54958
55434
  const cachePath = resolveCachePath();
54959
55435
  try {
54960
55436
  if (existsSync58(cachePath)) {
54961
- const raw = readFileSync59(cachePath, "utf-8");
55437
+ const raw = readFileSync61(cachePath, "utf-8");
54962
55438
  const parsed = JSON.parse(raw);
54963
55439
  if (parsed.lastCheck && parsed.latestVersion) {
54964
55440
  return parsed;
@@ -54970,9 +55446,9 @@ function readLastCheck() {
54970
55446
  }
54971
55447
  function writeLastCheck(cache6) {
54972
55448
  const cachePath = resolveCachePath();
54973
- const dir = dirname46(cachePath);
54974
- mkdirSync39(dir, { recursive: true });
54975
- writeFileSync40(cachePath, JSON.stringify(cache6, null, 2) + "\n", "utf-8");
55449
+ const dir = dirname47(cachePath);
55450
+ mkdirSync41(dir, { recursive: true });
55451
+ writeFileSync41(cachePath, JSON.stringify(cache6, null, 2) + "\n", "utf-8");
54976
55452
  }
54977
55453
  function isCheckDue(intervalMs) {
54978
55454
  const cache6 = readLastCheck();
@@ -54994,9 +55470,9 @@ function isVersionSkipped(version) {
54994
55470
 
54995
55471
  // packages/updater/src/channel.ts
54996
55472
  init_core();
54997
- import { existsSync as existsSync59, readFileSync as readFileSync60 } from "node:fs";
55473
+ import { existsSync as existsSync59, readFileSync as readFileSync62 } from "node:fs";
54998
55474
  import { homedir as homedir49 } from "node:os";
54999
- import { join as join80 } from "node:path";
55475
+ import { join as join81 } from "node:path";
55000
55476
  var UNIPI_PACKAGE = "@pi-unipi/unipi";
55001
55477
  function prereleaseChannel(version) {
55002
55478
  const m = /^v?\d+\.\d+\.\d+-([0-9A-Za-z-]+)/.exec(version.trim());
@@ -55014,15 +55490,15 @@ function channelFromSpec(spec) {
55014
55490
  }
55015
55491
  function piAgentDir(env = process.env) {
55016
55492
  const dir = env.PI_CODING_AGENT_DIR;
55017
- if (dir) return dir.startsWith("~") ? join80(homedir49(), dir.slice(1)) : dir;
55018
- return join80(homedir49(), ".pi", "agent");
55493
+ if (dir) return dir.startsWith("~") ? join81(homedir49(), dir.slice(1)) : dir;
55494
+ return join81(homedir49(), ".pi", "agent");
55019
55495
  }
55020
55496
  function readUnipiPackageSpec(cwd = process.cwd(), env = process.env) {
55021
- const files = [join80(cwd, ".pi", "settings.json"), join80(piAgentDir(env), "settings.json")];
55497
+ const files = [join81(cwd, ".pi", "settings.json"), join81(piAgentDir(env), "settings.json")];
55022
55498
  for (const file of files) {
55023
55499
  try {
55024
55500
  if (!existsSync59(file)) continue;
55025
- const settings = JSON.parse(readFileSync60(file, "utf8"));
55501
+ const settings = JSON.parse(readFileSync62(file, "utf8"));
55026
55502
  for (const pkg of settings.packages ?? []) {
55027
55503
  const source = typeof pkg === "string" ? pkg : pkg?.source;
55028
55504
  if (typeof source !== "string") continue;
@@ -55059,7 +55535,7 @@ function registryUrl(env = process.env) {
55059
55535
  return `${base}/-/package/${UNIPI_PACKAGE}/dist-tags`;
55060
55536
  }
55061
55537
  function getInstalledVersion() {
55062
- const dir = dirname47(dirname47(fileURLToPath19(import.meta.url)));
55538
+ const dir = dirname48(dirname48(fileURLToPath20(import.meta.url)));
55063
55539
  return getInstalledPackageVersion(dir, UNIPI_PACKAGE);
55064
55540
  }
55065
55541
  function currentChannel(currentVersion = getInstalledVersion()) {
@@ -55129,12 +55605,12 @@ import { Key as Key22, matchesKey as matchesKey24, truncateToWidth as truncateTo
55129
55605
  init_core();
55130
55606
  import { exec } from "child_process";
55131
55607
  import { promisify as promisify2 } from "util";
55132
- import { dirname as dirname48 } from "node:path";
55133
- import { fileURLToPath as fileURLToPath20 } from "node:url";
55608
+ import { dirname as dirname49 } from "node:path";
55609
+ import { fileURLToPath as fileURLToPath21 } from "node:url";
55134
55610
  var execAsync = promisify2(exec);
55135
55611
  var INSTALL_TIMEOUT_MS = 6e4;
55136
55612
  async function installUpdate(channel = "latest") {
55137
- const thisDir = dirname48(dirname48(fileURLToPath20(import.meta.url)));
55613
+ const thisDir = dirname49(dirname49(fileURLToPath21(import.meta.url)));
55138
55614
  try {
55139
55615
  await execAsync(
55140
55616
  `pi install ${installSpec(channel)}`,
@@ -55331,9 +55807,9 @@ function renderUpdateOverlay(checkResult, providedNewerVersions) {
55331
55807
 
55332
55808
  // packages/updater/src/remote-changelog.ts
55333
55809
  init_core();
55334
- import { existsSync as existsSync60, mkdirSync as mkdirSync40, readFileSync as readFileSync61, writeFileSync as writeFileSync41 } from "node:fs";
55810
+ import { existsSync as existsSync60, mkdirSync as mkdirSync42, readFileSync as readFileSync63, writeFileSync as writeFileSync42 } from "node:fs";
55335
55811
  import { homedir as homedir50 } from "node:os";
55336
- import { join as join81 } from "node:path";
55812
+ import { join as join82 } from "node:path";
55337
55813
  var CHANGELOG_RAW_BASE = "https://raw.githubusercontent.com/Neuron-Mr-White/unipi";
55338
55814
  function cacheDirectory(opts) {
55339
55815
  return (opts?.cacheDir ?? UPDATER_DIRS.CACHE).replace("~", homedir50());
@@ -55344,9 +55820,9 @@ function fallbackRefs(version) {
55344
55820
  }
55345
55821
  async function fetchRemoteChangelog(version, opts = {}) {
55346
55822
  const cacheDir3 = cacheDirectory(opts);
55347
- const cachePath = join81(cacheDir3, `changelog-${version}.md`);
55823
+ const cachePath = join82(cacheDir3, `changelog-${version}.md`);
55348
55824
  try {
55349
- if (existsSync60(cachePath)) return readFileSync61(cachePath, "utf8");
55825
+ if (existsSync60(cachePath)) return readFileSync63(cachePath, "utf8");
55350
55826
  } catch {
55351
55827
  }
55352
55828
  const fetchImpl = opts.fetchImpl ?? fetch;
@@ -55361,8 +55837,8 @@ async function fetchRemoteChangelog(version, opts = {}) {
55361
55837
  if (!response.ok) return null;
55362
55838
  const content = await response.text();
55363
55839
  if (cacheable) {
55364
- mkdirSync40(cacheDir3, { recursive: true });
55365
- writeFileSync41(cachePath, content, "utf8");
55840
+ mkdirSync42(cacheDir3, { recursive: true });
55841
+ writeFileSync42(cachePath, content, "utf8");
55366
55842
  }
55367
55843
  return content;
55368
55844
  } catch {
@@ -55379,7 +55855,7 @@ async function loadUpdateChangelog(currentVersion, latestVersion, opts) {
55379
55855
  }
55380
55856
 
55381
55857
  // packages/updater/src/index.ts
55382
- var VERSION12 = getPackageVersion(fileURLToPath21(new URL("..", import.meta.url)));
55858
+ var VERSION12 = getPackageVersion(fileURLToPath22(new URL("..", import.meta.url)));
55383
55859
  function updaterExtension(pi) {
55384
55860
  registerCommands3(pi);
55385
55861
  pi.on("session_start", async (_event, ctx) => {
@@ -55501,8 +55977,8 @@ import { copyToClipboard } from "@earendil-works/pi-coding-agent";
55501
55977
  import { Key as Key24 } from "@earendil-works/pi-tui";
55502
55978
 
55503
55979
  // packages/input-shortcuts/src/registers.ts
55504
- import { existsSync as existsSync61, mkdirSync as mkdirSync41, readFileSync as readFileSync62, renameSync as renameSync16, writeFileSync as writeFileSync42 } from "node:fs";
55505
- import { dirname as dirname49, join as join82 } from "node:path";
55980
+ import { existsSync as existsSync61, mkdirSync as mkdirSync43, readFileSync as readFileSync64, renameSync as renameSync17, writeFileSync as writeFileSync43 } from "node:fs";
55981
+ import { dirname as dirname50, join as join83 } from "node:path";
55506
55982
 
55507
55983
  // packages/input-shortcuts/src/types.ts
55508
55984
  var DEFAULT_CONFIG8 = {
@@ -55518,7 +55994,7 @@ var RegisterStore = class {
55518
55994
  filePath;
55519
55995
  loaded = false;
55520
55996
  constructor(baseDir) {
55521
- this.filePath = baseDir ? join82(baseDir, REGISTERS_FILE) : REGISTERS_FILE;
55997
+ this.filePath = baseDir ? join83(baseDir, REGISTERS_FILE) : REGISTERS_FILE;
55522
55998
  }
55523
55999
  /** Get the stash register contents. */
55524
56000
  getStash() {
@@ -55537,7 +56013,7 @@ var RegisterStore = class {
55537
56013
  this.loaded = true;
55538
56014
  try {
55539
56015
  if (existsSync61(this.filePath)) {
55540
- const raw = readFileSync62(this.filePath, "utf-8");
56016
+ const raw = readFileSync64(this.filePath, "utf-8");
55541
56017
  const parsed = JSON.parse(raw);
55542
56018
  this.data = { stash: typeof parsed.stash === "string" ? parsed.stash : "" };
55543
56019
  } else {
@@ -55550,13 +56026,13 @@ var RegisterStore = class {
55550
56026
  /** Atomic write: write to .tmp then rename. */
55551
56027
  save() {
55552
56028
  try {
55553
- const dir = dirname49(this.filePath);
56029
+ const dir = dirname50(this.filePath);
55554
56030
  if (!existsSync61(dir)) {
55555
- mkdirSync41(dir, { recursive: true });
56031
+ mkdirSync43(dir, { recursive: true });
55556
56032
  }
55557
56033
  const tmpPath = this.filePath + ".tmp";
55558
- writeFileSync42(tmpPath, JSON.stringify(this.data, null, 2), "utf-8");
55559
- renameSync16(tmpPath, this.filePath);
56034
+ writeFileSync43(tmpPath, JSON.stringify(this.data, null, 2), "utf-8");
56035
+ renameSync17(tmpPath, this.filePath);
55560
56036
  } catch {
55561
56037
  }
55562
56038
  }
@@ -55818,8 +56294,8 @@ var ChordOverlay = class extends Container {
55818
56294
  };
55819
56295
 
55820
56296
  // packages/input-shortcuts/src/settings.ts
55821
- import { existsSync as existsSync62, mkdirSync as mkdirSync42, readFileSync as readFileSync63, renameSync as renameSync17, writeFileSync as writeFileSync43 } from "node:fs";
55822
- import { dirname as dirname50, join as join83 } from "node:path";
56297
+ import { existsSync as existsSync62, mkdirSync as mkdirSync44, readFileSync as readFileSync65, renameSync as renameSync18, writeFileSync as writeFileSync44 } from "node:fs";
56298
+ import { dirname as dirname51, join as join84 } from "node:path";
55823
56299
  init_core();
55824
56300
  var ALT_KEY_OPTIONS = [
55825
56301
  "alt+a",
@@ -55962,17 +56438,17 @@ function importLegacyInputShortcuts() {
55962
56438
  const layers = settingsLayers("input-shortcuts", process.cwd());
55963
56439
  if (layers.global || layers.project) return;
55964
56440
  try {
55965
- const raw = readFileSync63(CONFIG_FILE, "utf-8");
56441
+ const raw = readFileSync65(CONFIG_FILE, "utf-8");
55966
56442
  setSettings("input-shortcuts", JSON.parse(raw), "project", process.cwd());
55967
56443
  } catch {
55968
56444
  }
55969
56445
  }
55970
56446
  function loadConfig8(baseDir) {
55971
56447
  if (baseDir) {
55972
- const filePath = join83(baseDir, CONFIG_FILE);
56448
+ const filePath = join84(baseDir, CONFIG_FILE);
55973
56449
  try {
55974
56450
  if (existsSync62(filePath)) {
55975
- const raw = readFileSync63(filePath, "utf-8");
56451
+ const raw = readFileSync65(filePath, "utf-8");
55976
56452
  const parsed = JSON.parse(raw);
55977
56453
  return {
55978
56454
  chordKey: typeof parsed.chordKey === "string" ? parsed.chordKey : DEFAULT_CONFIG8.chordKey,
@@ -56286,21 +56762,21 @@ function inputShortcutsExtension(pi) {
56286
56762
 
56287
56763
  // packages/fusion/src/index.ts
56288
56764
  init_core();
56289
- import { join as join86 } from "node:path";
56765
+ import { join as join87 } from "node:path";
56290
56766
 
56291
56767
  // packages/fusion/src/preset.ts
56292
56768
  init_core();
56293
- import { existsSync as existsSync63, mkdirSync as mkdirSync43, readFileSync as readFileSync65, writeFileSync as writeFileSync45, renameSync as renameSync19 } from "node:fs";
56769
+ import { existsSync as existsSync63, mkdirSync as mkdirSync45, readFileSync as readFileSync67, writeFileSync as writeFileSync46, renameSync as renameSync20 } from "node:fs";
56294
56770
  import { homedir as homedir52 } from "node:os";
56295
- import { dirname as dirname51, join as join85 } from "node:path";
56771
+ import { dirname as dirname52, join as join86 } from "node:path";
56296
56772
 
56297
56773
  // packages/fusion/src/pi-settings.ts
56298
- import { readFileSync as readFileSync64, renameSync as renameSync18, writeFileSync as writeFileSync44 } from "node:fs";
56774
+ import { readFileSync as readFileSync66, renameSync as renameSync19, writeFileSync as writeFileSync45 } from "node:fs";
56299
56775
  import { homedir as homedir51 } from "node:os";
56300
- import { join as join84 } from "node:path";
56776
+ import { join as join85 } from "node:path";
56301
56777
  function piSettingsPath2(env = process.env, home = homedir51()) {
56302
56778
  const envDir = env.PI_CODING_AGENT_DIR?.trim();
56303
- return join84(envDir !== void 0 && envDir.length > 0 ? envDir : join84(home, ".pi", "agent"), "settings.json");
56779
+ return join85(envDir !== void 0 && envDir.length > 0 ? envDir : join85(home, ".pi", "agent"), "settings.json");
56304
56780
  }
56305
56781
  function mergeDefaultModel(raw, patch) {
56306
56782
  const settings = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? { ...raw } : {};
@@ -56312,7 +56788,7 @@ function mergeDefaultModel(raw, patch) {
56312
56788
  function readDefaultModel(deps = {}) {
56313
56789
  try {
56314
56790
  const path31 = piSettingsPath2(deps.env);
56315
- const raw = JSON.parse(deps.readFile !== void 0 ? deps.readFile(path31) : readFileSync64(path31, "utf8"));
56791
+ const raw = JSON.parse(deps.readFile !== void 0 ? deps.readFile(path31) : readFileSync66(path31, "utf8"));
56316
56792
  const key = typeof raw.defaultProvider === "string" && typeof raw.defaultModel === "string" ? `${raw.defaultProvider}/${raw.defaultModel}` : void 0;
56317
56793
  return { key, thinking: typeof raw.defaultThinkingLevel === "string" ? raw.defaultThinkingLevel : void 0 };
56318
56794
  } catch {
@@ -56323,15 +56799,15 @@ function persistDefaultModel(patch, deps = {}) {
56323
56799
  const path31 = piSettingsPath2(deps.env);
56324
56800
  let current = {};
56325
56801
  try {
56326
- current = JSON.parse(deps.readFile !== void 0 ? deps.readFile(path31) : readFileSync64(path31, "utf8"));
56802
+ current = JSON.parse(deps.readFile !== void 0 ? deps.readFile(path31) : readFileSync66(path31, "utf8"));
56327
56803
  } catch {
56328
56804
  current = {};
56329
56805
  }
56330
56806
  const next = mergeDefaultModel(current, patch);
56331
56807
  const tmp = `${path31}.tmp-${String(process.pid)}`;
56332
- writeFileSync44(tmp, `${JSON.stringify(next, null, 2)}
56808
+ writeFileSync45(tmp, `${JSON.stringify(next, null, 2)}
56333
56809
  `, "utf8");
56334
- renameSync18(tmp, path31);
56810
+ renameSync19(tmp, path31);
56335
56811
  return path31;
56336
56812
  }
56337
56813
 
@@ -56352,10 +56828,10 @@ function emptyPreset() {
56352
56828
  };
56353
56829
  }
56354
56830
  function globalPresetPath(home = homedir52()) {
56355
- return join85(home, ".unipi", "config", "fusion", "preset.json");
56831
+ return join86(home, ".unipi", "config", "fusion", "preset.json");
56356
56832
  }
56357
56833
  function projectPresetPath(cwd) {
56358
- return join85(cwd, ".unipi", "fusion-preset.json");
56834
+ return join86(cwd, ".unipi", "fusion-preset.json");
56359
56835
  }
56360
56836
  function modelKey(model) {
56361
56837
  return `${model.provider}/${model.id}`;
@@ -56450,7 +56926,7 @@ function mergePresets(base, over) {
56450
56926
  function readJson6(path31) {
56451
56927
  try {
56452
56928
  if (!existsSync63(path31)) return void 0;
56453
- return JSON.parse(readFileSync65(path31, "utf8"));
56929
+ return JSON.parse(readFileSync67(path31, "utf8"));
56454
56930
  } catch {
56455
56931
  return void 0;
56456
56932
  }
@@ -56531,11 +57007,11 @@ function loadPreset(cwd, home = homedir52()) {
56531
57007
  return { preset, globalPath, projectPath, hasProjectLayer };
56532
57008
  }
56533
57009
  function writeJsonAtomic2(path31, value) {
56534
- mkdirSync43(dirname51(path31), { recursive: true });
57010
+ mkdirSync45(dirname52(path31), { recursive: true });
56535
57011
  const tmp = `${path31}.${String(process.pid)}.tmp`;
56536
- writeFileSync45(tmp, `${JSON.stringify(value, null, 2)}
57012
+ writeFileSync46(tmp, `${JSON.stringify(value, null, 2)}
56537
57013
  `, "utf8");
56538
- renameSync19(tmp, path31);
57014
+ renameSync20(tmp, path31);
56539
57015
  }
56540
57016
  function saveCuration(path31, curation) {
56541
57017
  const existing = parsePreset(readJson6(path31));
@@ -57787,7 +58263,7 @@ ${progressText(runtime, id)}`, { progress: runtime.progress(id), id, owner: "fus
57787
58263
  }
57788
58264
 
57789
58265
  // packages/fusion/src/index.ts
57790
- import { readFileSync as readFileSync66 } from "node:fs";
58266
+ import { readFileSync as readFileSync68 } from "node:fs";
57791
58267
 
57792
58268
  // packages/fusion/src/api.ts
57793
58269
  var FUSION_API_KEY = /* @__PURE__ */ Symbol.for("unipi.fusion.api");
@@ -57798,7 +58274,7 @@ function publishFusionApi(api) {
57798
58274
  // packages/fusion/src/index.ts
57799
58275
  var MODEL_COMMAND = `${UNIPI_PREFIX}model`;
57800
58276
  function sidekickSessionPath(leadSessionId2) {
57801
- return join86(stateDir("fusion", "session"), "sidekick", `${leadSessionId2 ?? "default"}.jsonl`);
58277
+ return join87(stateDir("fusion", "session"), "sidekick", `${leadSessionId2 ?? "default"}.jsonl`);
57802
58278
  }
57803
58279
  var SIDEKICK_WAKE_WIDGET_KEY = "fusion-sidekick-wake";
57804
58280
  function isLeadIdle(ctx) {
@@ -58165,7 +58641,7 @@ Handoffs: ${String(runtime.reports.size)} \xB7 runtime alive: ${String(runtime.i
58165
58641
  const currentKey = ctx.model ? modelKey(ctx.model) : void 0;
58166
58642
  let defaultKey;
58167
58643
  try {
58168
- const raw = JSON.parse(readFileSync66(piSettingsPath2(), "utf8"));
58644
+ const raw = JSON.parse(readFileSync68(piSettingsPath2(), "utf8"));
58169
58645
  if (typeof raw.defaultProvider === "string" && typeof raw.defaultModel === "string") {
58170
58646
  defaultKey = `${raw.defaultProvider}/${raw.defaultModel}`;
58171
58647
  }
@@ -58337,7 +58813,7 @@ ${String(result2.curation.lead.length)} lead \xB7 ${String(result2.curation.side
58337
58813
 
58338
58814
  // packages/watchdog/index.ts
58339
58815
  init_core();
58340
- import { appendFileSync as appendFileSync6, mkdirSync as mkdirSync44 } from "node:fs";
58816
+ import { appendFileSync as appendFileSync6, mkdirSync as mkdirSync46 } from "node:fs";
58341
58817
  import * as os10 from "node:os";
58342
58818
 
58343
58819
  // packages/watchdog/src/config.ts
@@ -58643,7 +59119,7 @@ ${tail || unfinished ? tail + unfinished : "(no output yet)"}`;
58643
59119
  }
58644
59120
 
58645
59121
  // packages/watchdog/src/bash-kill.ts
58646
- import { existsSync as existsSync64, readdirSync as readdirSync20, readFileSync as readFileSync67 } from "node:fs";
59122
+ import { existsSync as existsSync64, readdirSync as readdirSync21, readFileSync as readFileSync69 } from "node:fs";
58647
59123
  import { execFileSync as execFileSync5 } from "node:child_process";
58648
59124
  import { homedir as homedir53 } from "node:os";
58649
59125
  function commandNeedles(command, env = process.env, home = homedir53()) {
@@ -58668,13 +59144,13 @@ function fromLinux(parentPid, command) {
58668
59144
  const pgids = [];
58669
59145
  const needles = commandNeedles(command);
58670
59146
  try {
58671
- for (const entry of existsSync64("/proc") ? readdirSync20("/proc") : []) {
59147
+ for (const entry of existsSync64("/proc") ? readdirSync21("/proc") : []) {
58672
59148
  if (!/^\d+$/.test(entry)) continue;
58673
59149
  const pid = Number(entry);
58674
59150
  if (pid === parentPid) continue;
58675
59151
  let stat;
58676
59152
  try {
58677
- stat = readFileSync67(`/proc/${entry}/stat`, "utf-8");
59153
+ stat = readFileSync69(`/proc/${entry}/stat`, "utf-8");
58678
59154
  } catch {
58679
59155
  continue;
58680
59156
  }
@@ -58685,7 +59161,7 @@ function fromLinux(parentPid, command) {
58685
59161
  if (ppid !== parentPid) continue;
58686
59162
  let cmdline = "";
58687
59163
  try {
58688
- cmdline = readFileSync67(`/proc/${entry}/cmdline`, "utf-8").replace(/\0/g, " ");
59164
+ cmdline = readFileSync69(`/proc/${entry}/cmdline`, "utf-8").replace(/\0/g, " ");
58689
59165
  } catch {
58690
59166
  continue;
58691
59167
  }
@@ -58795,7 +59271,7 @@ function debugLog3(line) {
58795
59271
  if (process.env.UNIPI_DEBUG_WATCHDOG !== "1") return;
58796
59272
  try {
58797
59273
  const dir = `${os10.homedir()}/.unipi/logs`;
58798
- mkdirSync44(dir, { recursive: true });
59274
+ mkdirSync46(dir, { recursive: true });
58799
59275
  appendFileSync6(`${dir}/watchdog.log`, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
58800
59276
  `);
58801
59277
  } catch {
@@ -59222,9 +59698,9 @@ function queueWarn(item, decision, reason) {
59222
59698
  import { AgentSession, SessionManager as SessionManager4 } from "@earendil-works/pi-coding-agent";
59223
59699
  import { createRequire as createRequire5 } from "node:module";
59224
59700
  import { createServer as createServer2 } from "node:net";
59225
- import { chmodSync as chmodSync3, existsSync as existsSync66, mkdirSync as mkdirSync46, readdirSync as readdirSync23, readFileSync as readFileSync69, renameSync as renameSync21, rmSync as rmSync9, writeFileSync as writeFileSync47 } from "node:fs";
59701
+ import { chmodSync as chmodSync3, existsSync as existsSync66, mkdirSync as mkdirSync48, readdirSync as readdirSync24, readFileSync as readFileSync71, renameSync as renameSync22, rmSync as rmSync10, writeFileSync as writeFileSync48 } from "node:fs";
59226
59702
  import { homedir as homedir57 } from "node:os";
59227
- import { dirname as dirname52, join as join90, relative as relative7, resolve as resolvePath3 } from "node:path";
59703
+ import { dirname as dirname53, join as join91, relative as relative7, resolve as resolvePath3 } from "node:path";
59228
59704
 
59229
59705
  // packages/app-bridge/src/dialogs.ts
59230
59706
  var DialogHub = class {
@@ -59356,24 +59832,24 @@ function wrapUi(ui, hub, onNotify) {
59356
59832
  }
59357
59833
 
59358
59834
  // packages/app-bridge/src/media.ts
59359
- import { mkdirSync as mkdirSync45, renameSync as renameSync20, writeFileSync as writeFileSync46 } from "node:fs";
59835
+ import { mkdirSync as mkdirSync47, renameSync as renameSync21, writeFileSync as writeFileSync47 } from "node:fs";
59360
59836
  import { homedir as homedir55 } from "node:os";
59361
- import { join as join87 } from "node:path";
59837
+ import { join as join88 } from "node:path";
59362
59838
  var MAX_ENTRIES = 500;
59363
59839
  var seq = 0;
59364
59840
  var entries = /* @__PURE__ */ new Map();
59365
59841
  var registeredPaths = /* @__PURE__ */ new Set();
59366
59842
  function bridgeDir() {
59367
- return process.env.UNIPI_BRIDGE_DIR || join87(homedir55(), ".unipi", "bridge");
59843
+ return process.env.UNIPI_BRIDGE_DIR || join88(homedir55(), ".unipi", "bridge");
59368
59844
  }
59369
59845
  function writeAllowList() {
59370
59846
  try {
59371
59847
  const dir = bridgeDir();
59372
- mkdirSync45(dir, { recursive: true, mode: 448 });
59373
- const file = join87(dir, `${process.pid}.files.json`);
59848
+ mkdirSync47(dir, { recursive: true, mode: 448 });
59849
+ const file = join88(dir, `${process.pid}.files.json`);
59374
59850
  const tmp = `${file}.tmp`;
59375
- writeFileSync46(tmp, JSON.stringify([...registeredPaths]), { mode: 384 });
59376
- renameSync20(tmp, file);
59851
+ writeFileSync47(tmp, JSON.stringify([...registeredPaths]), { mode: 384 });
59852
+ renameSync21(tmp, file);
59377
59853
  } catch {
59378
59854
  }
59379
59855
  }
@@ -60052,8 +60528,8 @@ function setRemoteDialogs(hub) {
60052
60528
  import { CombinedAutocompleteProvider } from "@earendil-works/pi-tui";
60053
60529
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
60054
60530
  import { spawnSync as spawnSync4 } from "node:child_process";
60055
- import { existsSync as existsSync65, readdirSync as readdirSync21 } from "node:fs";
60056
- import { join as join88, relative as relative5, sep as sep6 } from "node:path";
60531
+ import { existsSync as existsSync65, readdirSync as readdirSync22 } from "node:fs";
60532
+ import { join as join89, relative as relative5, sep as sep6 } from "node:path";
60057
60533
  var MAX_ITEMS = 30;
60058
60534
  var TIMEOUT_MS2 = 4e3;
60059
60535
  var fdCache;
@@ -60061,7 +60537,7 @@ function findFd() {
60061
60537
  if (fdCache !== void 0) return fdCache;
60062
60538
  const exe = process.platform === "win32" ? ".exe" : "";
60063
60539
  try {
60064
- const local = join88(getAgentDir(), "bin", `fd${exe}`);
60540
+ const local = join89(getAgentDir(), "bin", `fd${exe}`);
60065
60541
  if (existsSync65(local)) return fdCache = local;
60066
60542
  } catch {
60067
60543
  }
@@ -60083,14 +60559,14 @@ function walk(cwd, query) {
60083
60559
  const { dir, depth } = queue.shift();
60084
60560
  let names;
60085
60561
  try {
60086
- names = readdirSync21(dir, { withFileTypes: true });
60562
+ names = readdirSync22(dir, { withFileTypes: true });
60087
60563
  } catch {
60088
60564
  continue;
60089
60565
  }
60090
60566
  for (const d of names) {
60091
60567
  seen++;
60092
60568
  if (SKIP.has(d.name)) continue;
60093
- const full = join88(dir, d.name);
60569
+ const full = join89(dir, d.name);
60094
60570
  const rel = relative5(cwd, full).split(sep6).join("/");
60095
60571
  const isDir = d.isDirectory();
60096
60572
  if (isDir && depth < 5) queue.push({ dir: full, depth: depth + 1 });
@@ -60129,9 +60605,9 @@ async function fileSuggestions(cwd, query) {
60129
60605
 
60130
60606
  // packages/app-bridge/src/paths.ts
60131
60607
  import { spawn as spawn7 } from "node:child_process";
60132
- import { readFileSync as readFileSync68, readdirSync as readdirSync22, realpathSync as realpathSync7, statSync as statSync20 } from "node:fs";
60608
+ import { readFileSync as readFileSync70, readdirSync as readdirSync23, realpathSync as realpathSync7, statSync as statSync21 } from "node:fs";
60133
60609
  import { homedir as homedir56, userInfo } from "node:os";
60134
- import { join as join89, relative as relative6, resolve as resolvePath2, sep as sep7 } from "node:path";
60610
+ import { join as join90, relative as relative6, resolve as resolvePath2, sep as sep7 } from "node:path";
60135
60611
  var PATHS_STAT_MAX = 200;
60136
60612
  var PATH_MAX_CHARS = 4096;
60137
60613
  function inside(target, root) {
@@ -60140,12 +60616,12 @@ function inside(target, root) {
60140
60616
  }
60141
60617
  function resolveAgainst(path31, cwd, home) {
60142
60618
  if (path31 === "~") return home;
60143
- if (path31.startsWith("~/")) return join89(home, path31.slice(2));
60619
+ if (path31.startsWith("~/")) return join90(home, path31.slice(2));
60144
60620
  return resolvePath2(cwd, path31);
60145
60621
  }
60146
60622
  var kindOf2 = (abs) => {
60147
60623
  try {
60148
- const st = statSync20(abs);
60624
+ const st = statSync21(abs);
60149
60625
  return st.isDirectory() ? "dir" : st.isFile() ? "file" : "missing";
60150
60626
  } catch {
60151
60627
  return "missing";
@@ -60183,13 +60659,13 @@ function resolveSafeRoots(config, home, user) {
60183
60659
  }
60184
60660
  return out;
60185
60661
  }
60186
- var hostConfigPath = () => join89(process.env.UNIPI_HOST_DIR || join89(homedir56(), ".unipi", "app-host"), "config.json");
60662
+ var hostConfigPath = () => join90(process.env.UNIPI_HOST_DIR || join90(homedir56(), ".unipi", "app-host"), "config.json");
60187
60663
  var rootsCache;
60188
60664
  function hostSafeRoots(now2 = Date.now(), home = homedir56()) {
60189
60665
  if (rootsCache && now2 - rootsCache.at < 5e3) return rootsCache;
60190
60666
  let fs28 = {};
60191
60667
  try {
60192
- const parsed = JSON.parse(readFileSync68(hostConfigPath(), "utf8"));
60668
+ const parsed = JSON.parse(readFileSync70(hostConfigPath(), "utf8"));
60193
60669
  if (parsed && typeof parsed.fs === "object" && parsed.fs) fs28 = parsed.fs;
60194
60670
  } catch {
60195
60671
  }
@@ -60281,14 +60757,14 @@ function walkTree(cwd, files, dirs) {
60281
60757
  const { dir, depth } = queue.shift();
60282
60758
  let names;
60283
60759
  try {
60284
- names = readdirSync22(dir, { withFileTypes: true });
60760
+ names = readdirSync23(dir, { withFileTypes: true });
60285
60761
  } catch {
60286
60762
  continue;
60287
60763
  }
60288
60764
  for (const d of names) {
60289
60765
  if (SKIP2.has(d.name)) continue;
60290
60766
  seen++;
60291
- const full = join89(dir, d.name);
60767
+ const full = join90(dir, d.name);
60292
60768
  const rel = relative6(cwd, full).split(sep7).join("/");
60293
60769
  if (d.isDirectory()) {
60294
60770
  dirs.push(rel);
@@ -60335,7 +60811,7 @@ async function statPaths(paths, cwd, home = homedir56(), extra = hostSafeRoots(D
60335
60811
  idx ??= await treeIndex(cwd);
60336
60812
  const m = suffixMatch(idx, path31);
60337
60813
  if (m) {
60338
- at = join89(cwd, m.rel);
60814
+ at = join90(cwd, m.rel);
60339
60815
  kind = kindOf2(at);
60340
60816
  }
60341
60817
  } catch {
@@ -60407,11 +60883,11 @@ function entryPreview(entry, max = 120) {
60407
60883
  return text.length > max ? `${text.slice(0, max - 1)}\u2026` : text;
60408
60884
  }
60409
60885
  function bridgeDir2() {
60410
- return process.env.UNIPI_BRIDGE_DIR || join90(homedir57(), ".unipi", "bridge");
60886
+ return process.env.UNIPI_BRIDGE_DIR || join91(homedir57(), ".unipi", "bridge");
60411
60887
  }
60412
60888
  function bridgeSocketPath(dir, pid, platform = process.platform) {
60413
60889
  if (platform === "win32") return "\\\\.\\pipe\\unipi-bridge-" + pid;
60414
- return join90(dir, `${pid}.sock`);
60890
+ return join91(dir, `${pid}.sock`);
60415
60891
  }
60416
60892
  function isBridgePipe(path31) {
60417
60893
  return path31.startsWith("\\\\.\\pipe\\");
@@ -60427,7 +60903,7 @@ function alive(pid) {
60427
60903
  function sweepDead(dir) {
60428
60904
  let names;
60429
60905
  try {
60430
- names = readdirSync23(dir);
60906
+ names = readdirSync24(dir);
60431
60907
  } catch {
60432
60908
  return;
60433
60909
  }
@@ -60435,7 +60911,7 @@ function sweepDead(dir) {
60435
60911
  const m = /^(\d+)\.(json|sock)$/.exec(name);
60436
60912
  if (!m) continue;
60437
60913
  const pid = Number(m[1]);
60438
- if (pid !== process.pid && !alive(pid)) rmSync9(join90(dir, name), { force: true });
60914
+ if (pid !== process.pid && !alive(pid)) rmSync10(join91(dir, name), { force: true });
60439
60915
  }
60440
60916
  }
60441
60917
  function isInsideCwd(path31, cwd) {
@@ -60467,11 +60943,11 @@ var patchAgentSessionPrototype = (Ctor) => {
60467
60943
  };
60468
60944
  var patchAllRuntimeCopies = () => {
60469
60945
  patchAgentSessionPrototype(AgentSession);
60470
- const pkgRoot = join90(dirname52(dirname52(process.execPath)), "lib", "node_modules", "@earendil-works", "pi-coding-agent");
60946
+ const pkgRoot = join91(dirname53(dirname53(process.execPath)), "lib", "node_modules", "@earendil-works", "pi-coding-agent");
60471
60947
  const candidates = [
60472
- join90(pkgRoot, "dist", "bundle", "index.js"),
60473
- join90(pkgRoot, "dist", "index.js"),
60474
- process.argv[1] ? resolvePath3(dirname52(process.argv[1]), "..", "lib", "node_modules", "@earendil-works", "pi-coding-agent", "dist", "bundle", "index.js") : ""
60948
+ join91(pkgRoot, "dist", "bundle", "index.js"),
60949
+ join91(pkgRoot, "dist", "index.js"),
60950
+ process.argv[1] ? resolvePath3(dirname53(process.argv[1]), "..", "lib", "node_modules", "@earendil-works", "pi-coding-agent", "dist", "bundle", "index.js") : ""
60475
60951
  ];
60476
60952
  for (const index of candidates) {
60477
60953
  if (!index) continue;
@@ -60906,10 +61382,10 @@ function createBridge(pi, deps = defaultDeps2) {
60906
61382
  ...recordLhMode(lhMode())
60907
61383
  };
60908
61384
  try {
60909
- const file = join90(dir, `${process.pid}.json`);
61385
+ const file = join91(dir, `${process.pid}.json`);
60910
61386
  const tmp = `${file}.tmp`;
60911
- writeFileSync47(tmp, JSON.stringify(record), { mode: 384 });
60912
- renameSync21(tmp, file);
61387
+ writeFileSync48(tmp, JSON.stringify(record), { mode: 384 });
61388
+ renameSync22(tmp, file);
60913
61389
  } catch {
60914
61390
  }
60915
61391
  };
@@ -61082,7 +61558,7 @@ function createBridge(pi, deps = defaultDeps2) {
61082
61558
  const imageContent = (i) => {
61083
61559
  if ("data" in i) return { type: "image", mimeType: i.mime, data: i.data };
61084
61560
  try {
61085
- return { type: "image", mimeType: i.mime, data: readFileSync69(i.path).toString("base64") };
61561
+ return { type: "image", mimeType: i.mime, data: readFileSync71(i.path).toString("base64") };
61086
61562
  } catch {
61087
61563
  return { type: "text", text: `[image unavailable: ${i.path}]` };
61088
61564
  }
@@ -61575,7 +62051,7 @@ function createBridge(pi, deps = defaultDeps2) {
61575
62051
  const entry = resolveMedia(msg.mediaRef);
61576
62052
  if (!entry) return write(sock, { t: "media_error", mediaRef: msg.mediaRef, message: "That image is no longer available. Re-open the message." });
61577
62053
  try {
61578
- const data = entry.kind === "base64" ? entry.data : readFileSync69(entry.path).toString("base64");
62054
+ const data = entry.kind === "base64" ? entry.data : readFileSync71(entry.path).toString("base64");
61579
62055
  for (let i = 0; i < data.length || i === 0; i += MEDIA_CHUNK_CHARS) {
61580
62056
  const piece = data.slice(i, i + MEDIA_CHUNK_CHARS);
61581
62057
  const done = i + MEDIA_CHUNK_CHARS >= data.length;
@@ -61726,14 +62202,14 @@ function createBridge(pi, deps = defaultDeps2) {
61726
62202
  if (server || !ctx) return;
61727
62203
  const dir = bridgeDir2();
61728
62204
  try {
61729
- mkdirSync46(dir, { recursive: true, mode: 448 });
62205
+ mkdirSync48(dir, { recursive: true, mode: 448 });
61730
62206
  chmodSync3(dir, 448);
61731
62207
  } catch {
61732
62208
  return;
61733
62209
  }
61734
62210
  sweepDead(dir);
61735
62211
  socketPath = bridgeSocketPath(dir, process.pid);
61736
- if (!isBridgePipe(socketPath)) rmSync9(socketPath, { force: true });
62212
+ if (!isBridgePipe(socketPath)) rmSync10(socketPath, { force: true });
61737
62213
  const srv = createServer2((sock) => {
61738
62214
  clients.add(sock);
61739
62215
  if (idleWaitingSince !== void 0) {
@@ -61802,8 +62278,8 @@ function createBridge(pi, deps = defaultDeps2) {
61802
62278
  server?.close();
61803
62279
  server = void 0;
61804
62280
  const dir = bridgeDir2();
61805
- rmSync9(join90(dir, `${process.pid}.json`), { force: true });
61806
- if (socketPath && !isBridgePipe(socketPath)) rmSync9(socketPath, { force: true });
62281
+ rmSync10(join91(dir, `${process.pid}.json`), { force: true });
62282
+ if (socketPath && !isBridgePipe(socketPath)) rmSync10(socketPath, { force: true });
61807
62283
  socketPath = void 0;
61808
62284
  };
61809
62285
  const safe = (fn) => (...args) => {
@@ -62221,6 +62697,16 @@ function index_default(pi) {
62221
62697
  }
62222
62698
  }
62223
62699
  }
62700
+ try {
62701
+ writeSettingsSnapshot();
62702
+ } catch {
62703
+ }
62704
+ api.on("session_start", () => {
62705
+ try {
62706
+ writeSettingsSnapshot();
62707
+ } catch {
62708
+ }
62709
+ });
62224
62710
  }
62225
62711
  export {
62226
62712
  index_default as default