@adhdev/daemon-standalone 1.0.18-rc.7 → 1.0.18-rc.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -30265,10 +30265,10 @@ var require_dist3 = __commonJS({
30265
30265
  }
30266
30266
  function getDaemonBuildInfo() {
30267
30267
  if (cached2) return cached2;
30268
- const commit = readInjected(true ? "068ae7b54eceb85bb9843d2c7654c3d4687fce75" : void 0) ?? "unknown";
30269
- const commitShort = readInjected(true ? "068ae7b5" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30270
- const version2 = readInjected(true ? "1.0.18-rc.7" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30271
- const builtAt = readInjected(true ? "2026-07-22T02:35:12.533Z" : void 0);
30268
+ const commit = readInjected(true ? "3a1a0d16c0dd7ce3df15591fbe30b7e8638ab876" : void 0) ?? "unknown";
30269
+ const commitShort = readInjected(true ? "3a1a0d16" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
30270
+ const version2 = readInjected(true ? "1.0.18-rc.9" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
30271
+ const builtAt = readInjected(true ? "2026-07-22T05:27:46.385Z" : void 0);
30272
30272
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
30273
30273
  return cached2;
30274
30274
  }
@@ -39779,40 +39779,113 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
39779
39779
  if (!category) return true;
39780
39780
  return !(category in OPERATING_NOTE_CATEGORY_TTL_DAYS);
39781
39781
  }
39782
- function selectOperatingNotesForPrompt(notes, now, cap = OPERATING_NOTES_PROMPT_CAP) {
39782
+ function deriveSubjectKey(note) {
39783
+ const explicit = typeof note.subjectKey === "string" ? note.subjectKey.trim().toLowerCase() : "";
39784
+ if (explicit) return explicit;
39785
+ const text = typeof note.text === "string" ? note.text.trimStart() : "";
39786
+ const m = /^\[([^\]]{1,80})\]/.exec(text);
39787
+ const tag = m ? m[1].trim().toLowerCase() : "";
39788
+ return tag || void 0;
39789
+ }
39790
+ function foldSameClassNotes(ranked) {
39791
+ const out = [];
39792
+ const survivorByKey = /* @__PURE__ */ new Map();
39793
+ for (const note of ranked) {
39794
+ const subject = deriveSubjectKey(note);
39795
+ const foldable = !note.pinned && !!note.category && !!subject;
39796
+ if (foldable) {
39797
+ const key2 = `${note.category}\0${subject}`;
39798
+ const existing = survivorByKey.get(key2);
39799
+ if (existing !== void 0) {
39800
+ const survivor = out[existing];
39801
+ survivor.subsumedCount += 1;
39802
+ if (typeof note.noteId === "string" && note.noteId) survivor.subsumedIds.push(note.noteId);
39803
+ continue;
39804
+ }
39805
+ survivorByKey.set(key2, out.length);
39806
+ }
39807
+ out.push({ note, subsumedIds: [], subsumedCount: 0 });
39808
+ }
39809
+ return out;
39810
+ }
39811
+ function renderedNoteBytes(folded) {
39812
+ return byteLength2(renderOperatingNoteLine(folded));
39813
+ }
39814
+ function selectOperatingNotesForPrompt(notes, now, cap = OPERATING_NOTES_PROMPT_CAP, byteBudget = OPERATING_NOTE_INJECTION_BYTE_BUDGET) {
39783
39815
  const valid = Array.isArray(notes) ? notes.filter((n) => n && typeof n.text === "string" && n.text.trim()) : [];
39784
- const kept = valid.map((note, index) => ({ note, index })).filter(({ note }) => note.pinned || !isNoteExpired(note, now));
39816
+ let kept = valid.map((note, index) => ({ note, index })).filter(({ note }) => note.pinned || !isNoteExpired(note, now));
39817
+ const supersededAtIndex = /* @__PURE__ */ new Map();
39818
+ for (const { note, index } of kept) {
39819
+ const target = typeof note.supersedes === "string" ? note.supersedes.trim().toLowerCase() : "";
39820
+ if (!target) continue;
39821
+ const prev = supersededAtIndex.get(target);
39822
+ if (prev === void 0 || index > prev) supersededAtIndex.set(target, index);
39823
+ }
39824
+ if (supersededAtIndex.size > 0) {
39825
+ kept = kept.filter(({ note, index }) => {
39826
+ if (note.pinned) return true;
39827
+ const byId = typeof note.noteId === "string" ? note.noteId.trim().toLowerCase() : "";
39828
+ const bySubject = deriveSubjectKey(note);
39829
+ const supIdx = Math.max(
39830
+ byId ? supersededAtIndex.get(byId) ?? -1 : -1,
39831
+ bySubject ? supersededAtIndex.get(bySubject) ?? -1 : -1
39832
+ );
39833
+ return !(supIdx > index);
39834
+ });
39835
+ }
39785
39836
  const rank = (n) => n.pinned ? 0 : isDurableCategory(n.category) ? 1 : 2;
39786
39837
  kept.sort((a, b) => {
39787
39838
  const r = rank(a.note) - rank(b.note);
39788
39839
  if (r !== 0) return r;
39789
39840
  return b.index - a.index;
39790
39841
  });
39791
- const omittedCount = Math.max(0, kept.length - cap);
39792
- const shown = (omittedCount > 0 ? kept.slice(0, cap) : kept).map((k) => k.note);
39842
+ const folded = foldSameClassNotes(kept.map((k) => k.note));
39843
+ const shown = [];
39844
+ let usedBytes = 0;
39845
+ let usedCount = 0;
39846
+ for (const entry of folded) {
39847
+ if (entry.note.pinned) {
39848
+ shown.push(entry);
39849
+ usedBytes += renderedNoteBytes(entry);
39850
+ usedCount += 1;
39851
+ continue;
39852
+ }
39853
+ if (usedCount >= cap) break;
39854
+ const bytes = renderedNoteBytes(entry);
39855
+ if (usedCount > 0 && usedBytes + bytes > byteBudget) break;
39856
+ shown.push(entry);
39857
+ usedBytes += bytes;
39858
+ usedCount += 1;
39859
+ }
39860
+ const omittedCount = Math.max(0, folded.length - shown.length);
39793
39861
  return { shown, omittedCount };
39794
39862
  }
39795
- function buildOperatingNotesSection(notes, now = Date.now()) {
39796
- const hasAny = Array.isArray(notes) ? notes.some((n) => n && typeof n.text === "string" && n.text.trim()) : false;
39797
- if (!hasAny) return "";
39863
+ function renderOperatingNoteLine(folded) {
39864
+ const n = folded.note;
39798
39865
  const categoryLabel = {
39799
39866
  provider_quirk: "provider quirk",
39800
39867
  pattern_to_avoid: "pattern to avoid",
39801
39868
  recovery_lesson: "recovery lesson"
39802
39869
  };
39870
+ const pin = n.pinned ? "\u{1F4CC} " : "";
39871
+ const cat = n.category && categoryLabel[n.category] ? `[${categoryLabel[n.category]}] ` : "";
39872
+ const fold = folded.subsumedCount > 0 ? ` _(+${folded.subsumedCount} earlier same-subject note${folded.subsumedCount === 1 ? "" : "s"} folded${folded.subsumedIds.length ? `: ${folded.subsumedIds.join(", ")}` : ""})_` : "";
39873
+ return `- ${pin}${cat}${truncateNote(n.text.trim())}${fold}`;
39874
+ }
39875
+ function buildOperatingNotesSection(notes, now = Date.now()) {
39876
+ const hasAny = Array.isArray(notes) ? notes.some((n) => n && typeof n.text === "string" && n.text.trim()) : false;
39877
+ if (!hasAny) return "";
39803
39878
  const { shown, omittedCount } = selectOperatingNotesForPrompt(notes ?? [], now);
39804
39879
  if (shown.length === 0) return "";
39805
39880
  const lines = ["## Operating Notes", ""];
39806
39881
  lines.push("Lessons earlier coordinators on this mesh recorded via `mesh_record_note`. Treat them as accumulated operating knowledge \u2014 apply them. When you learn a durable lesson (a provider quirk, a pattern to avoid, a recovery lesson), record it with `mesh_record_note` so future coordinators inherit it.");
39807
39882
  lines.push("");
39808
- for (const n of shown) {
39809
- const pin = n.pinned ? "\u{1F4CC} " : "";
39810
- const cat = n.category && categoryLabel[n.category] ? `[${categoryLabel[n.category]}] ` : "";
39811
- lines.push(`- ${pin}${cat}${truncateNote(n.text.trim())}`);
39883
+ for (const entry of shown) {
39884
+ lines.push(renderOperatingNoteLine(entry));
39812
39885
  }
39813
39886
  if (omittedCount > 0) {
39814
39887
  lines.push("");
39815
- lines.push(`_${omittedCount} lower-priority note${omittedCount === 1 ? "" : "s"} omitted (kept in ledger; expired-and-unpinned notes are also hidden from this list but retained for audit; prune with \`mesh_forget_note\`)._`);
39888
+ lines.push(`_${omittedCount} lower-priority note${omittedCount === 1 ? "" : "s"} omitted to fit the injection cap/byte-budget (kept in ledger; expired, superseded, and same-subject-folded notes are also hidden from this list but retained for audit; prune with \`mesh_forget_note\`)._`);
39816
39889
  }
39817
39890
  return lines.join("\n");
39818
39891
  }
@@ -39942,6 +40015,7 @@ When you compose the task message you dispatch to a node, include these requirem
39942
40015
  var PROMPT_SOFT_CAP_BYTES;
39943
40016
  var OPERATING_NOTES_PROMPT_CAP;
39944
40017
  var OPERATING_NOTE_MAX_CHARS;
40018
+ var OPERATING_NOTE_INJECTION_BYTE_BUDGET;
39945
40019
  var TOOLS_SECTION;
39946
40020
  var TOOL_EXPOSURE_PREFLIGHT_SECTION;
39947
40021
  var WORKFLOW_SECTION;
@@ -39960,6 +40034,7 @@ When you compose the task message you dispatch to a node, include these requirem
39960
40034
  PROMPT_SOFT_CAP_BYTES = 60 * 1024;
39961
40035
  OPERATING_NOTES_PROMPT_CAP = 20;
39962
40036
  OPERATING_NOTE_MAX_CHARS = 300;
40037
+ OPERATING_NOTE_INJECTION_BYTE_BUDGET = 6 * 1024;
39963
40038
  TOOLS_SECTION = `## Available Tools
39964
40039
 
39965
40040
  | Tool | Purpose |
@@ -41751,9 +41826,13 @@ ${rendered}`, "utf-8");
41751
41826
  ...typeof value.createdAt === "string" ? { createdAt: value.createdAt } : {},
41752
41827
  ...typeof value.sourceCoordinator === "string" ? { sourceCoordinator: value.sourceCoordinator } : {},
41753
41828
  // Operating-notes lifecycle: a repo-declared note may pin itself or set an
41754
- // explicit expiry, same as a runtime-recorded one.
41829
+ // explicit expiry, same as a runtime-recorded one. Phase 2 (b)/(c): it may
41830
+ // also declare supersedes/subjectKey to retire an earlier note or group
41831
+ // same-subject notes for folding.
41755
41832
  ...value.pinned === true ? { pinned: true } : {},
41756
- ...typeof value.expiresAt === "string" ? { expiresAt: value.expiresAt } : {}
41833
+ ...typeof value.expiresAt === "string" ? { expiresAt: value.expiresAt } : {},
41834
+ ...typeof value.supersedes === "string" ? { supersedes: value.supersedes } : {},
41835
+ ...typeof value.subjectKey === "string" ? { subjectKey: value.subjectKey } : {}
41757
41836
  };
41758
41837
  }
41759
41838
  function normalizeRepoMeshDeclarativeConfig(parsed) {
@@ -46516,6 +46595,33 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
46516
46595
  }
46517
46596
  return isWithinCloneBootstrapGrace(targetNodeId);
46518
46597
  }
46598
+ function resolveDeadTargetVerdict(components, meshId, mesh, task) {
46599
+ const NOT_DEAD = { dead: false, nodeDead: false, reason: "" };
46600
+ const targetSessionId = readNonEmptyString(task.targetSessionId);
46601
+ const targetNodeId = readNonEmptyString(task.targetNodeId);
46602
+ if (!targetSessionId && !targetNodeId) return NOT_DEAD;
46603
+ const lastUpdateMs = Date.parse(task.updatedAt || task.createdAt || "");
46604
+ if (Number.isFinite(lastUpdateMs) && Date.now() - lastUpdateMs < DEAD_TARGET_GRACE_MS) return NOT_DEAD;
46605
+ const nodes = Array.isArray(mesh?.nodes) ? mesh.nodes : [];
46606
+ if (targetNodeId) {
46607
+ const nodePresent = nodes.some((n) => meshNodeIdMatches(n, targetNodeId));
46608
+ if (!nodePresent) {
46609
+ if (isTargetNodeTransientlyUnresolved(mesh, task)) return NOT_DEAD;
46610
+ return { dead: true, nodeDead: true, reason: "dead_target_node_absent" };
46611
+ }
46612
+ }
46613
+ if (targetSessionId) {
46614
+ const node = targetNodeId ? nodes.find((n) => meshNodeIdMatches(n, targetNodeId)) : void 0;
46615
+ const nodeIsLocal = node ? isLocalAutoLaunchNode(node) : true;
46616
+ if (nodeIsLocal) {
46617
+ const verdict = resolveSessionBusyVerdict(components, targetSessionId);
46618
+ if (verdict === "UNKNOWN") {
46619
+ return { dead: true, nodeDead: false, reason: "dead_target_session_absent" };
46620
+ }
46621
+ }
46622
+ }
46623
+ return NOT_DEAD;
46624
+ }
46519
46625
  function retractActionableSkipIfPreviouslyNotified(meshId, taskId) {
46520
46626
  const dedupKey = `${meshId}:${taskId}`;
46521
46627
  if (!lastActionableSkipNotified.delete(dedupKey)) return;
@@ -47033,6 +47139,21 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
47033
47139
  continue;
47034
47140
  }
47035
47141
  if (task.targetSessionId) {
47142
+ const deadTarget = resolveDeadTargetVerdict(components, meshId, mesh, task);
47143
+ if (deadTarget.dead) {
47144
+ const requeued = requeueTask(meshId, task.id, {
47145
+ reason: deadTarget.reason,
47146
+ clearTargetSession: true,
47147
+ // Keep the node pin if only the SESSION died on a still-live node; clear it
47148
+ // when the NODE itself is absent (nothing to pin to).
47149
+ clearTargetNode: deadTarget.nodeDead
47150
+ });
47151
+ if (requeued) {
47152
+ LOG2.warn("MeshQueue", `DEAD-TARGET-SELFHEAL: task ${task.id} (mesh ${meshId}) was pinned to a dead target (${deadTarget.reason}); requeued${deadTarget.nodeDead ? " and unpinned node" : ""} (requeueCount=${requeued.requeueCount ?? "?"}, status=${requeued.status}).`);
47153
+ }
47154
+ markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_dead_requeued" });
47155
+ continue;
47156
+ }
47036
47157
  markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_constraint" });
47037
47158
  continue;
47038
47159
  }
@@ -47628,6 +47749,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
47628
47749
  var ACTIONABLE_SKIP_REASON_PREFIXES;
47629
47750
  var TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON;
47630
47751
  var lastActionableSkipNotified;
47752
+ var DEAD_TARGET_GRACE_MS;
47631
47753
  var init_mesh_queue_assignment = __esm2({
47632
47754
  "src/mesh/mesh-queue-assignment.ts"() {
47633
47755
  "use strict";
@@ -47689,6 +47811,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
47689
47811
  ];
47690
47812
  TRANSIENT_TARGET_NODE_BOOTSTRAP_PENDING_REASON = "target_node_bootstrap_pending";
47691
47813
  lastActionableSkipNotified = /* @__PURE__ */ new Map();
47814
+ DEAD_TARGET_GRACE_MS = 6e4;
47692
47815
  }
47693
47816
  });
47694
47817
  function readSettings(state) {
@@ -73169,6 +73292,40 @@ ${formatManifestValidationIssues2(validation.issues)}`,
73169
73292
  var POINTER_NAME = ".adhdev-current";
73170
73293
  var STABLE_FILES = [POINTER_NAME, "adhdev.cmd", "adhdev.ps1", "adhdev"];
73171
73294
  var DEFAULT_HEALTH_PORT = 19222;
73295
+ function fetchLocalJson(port, pathname) {
73296
+ return new Promise((resolve27) => {
73297
+ const req = http2.get(`http://127.0.0.1:${port}${pathname}`, { timeout: 1500 }, (res) => {
73298
+ let body = "";
73299
+ res.setEncoding("utf8");
73300
+ res.on("data", (chunk) => {
73301
+ body += chunk;
73302
+ });
73303
+ res.on("end", () => resolve27({ ok: res.statusCode === 200, body }));
73304
+ });
73305
+ req.on("timeout", () => req.destroy());
73306
+ req.on("error", () => resolve27({ ok: false, body: "" }));
73307
+ });
73308
+ }
73309
+ async function fetchLocalHealth(port) {
73310
+ const { ok, body } = await fetchLocalJson(port, "/health");
73311
+ if (!ok) return { ok: false };
73312
+ try {
73313
+ const pid = Number(JSON.parse(body)?.pid);
73314
+ return { ok: true, pid: Number.isFinite(pid) ? pid : void 0 };
73315
+ } catch {
73316
+ return { ok: false };
73317
+ }
73318
+ }
73319
+ async function fetchLocalStatusVersion(port) {
73320
+ const { ok, body } = await fetchLocalJson(port, "/api/v1/status");
73321
+ if (!ok) return void 0;
73322
+ try {
73323
+ const version2 = JSON.parse(body)?.status?.version;
73324
+ return typeof version2 === "string" ? version2 : void 0;
73325
+ } catch {
73326
+ return void 0;
73327
+ }
73328
+ }
73172
73329
  var ADHDEV_OWNED_MARKERS = [
73173
73330
  "session-host-daemon",
73174
73331
  "node_modules/adhdev",
@@ -73446,6 +73603,8 @@ exec "${portableNode}" "${cliEntry}" "$@"
73446
73603
  }
73447
73604
  }
73448
73605
  function createDefaultWindowsAtomicHooks(options) {
73606
+ const healthPort = options.healthPort ?? DEFAULT_HEALTH_PORT;
73607
+ const healthTimeoutMs = options.healthTimeoutMs ?? 3e4;
73449
73608
  return {
73450
73609
  install: (stagedPrefix, portableNode) => {
73451
73610
  const env2 = {
@@ -73456,7 +73615,7 @@ exec "${portableNode}" "${cliEntry}" "$@"
73456
73615
  };
73457
73616
  const pathKey = Object.keys(env2).find((key2) => key2.toLowerCase() === "path") || "Path";
73458
73617
  env2[pathKey] = `${path20.dirname(portableNode)};${env2[pathKey] || ""}`;
73459
- (0, import_child_process6.execFileSync)(portableNode, [
73618
+ const installOutput = String((0, import_child_process6.execFileSync)(portableNode, [
73460
73619
  options.npmCliPath,
73461
73620
  "install",
73462
73621
  "-g",
@@ -73471,7 +73630,8 @@ exec "${portableNode}" "${cliEntry}" "$@"
73471
73630
  maxBuffer: 20 * 1024 * 1024,
73472
73631
  windowsHide: true,
73473
73632
  env: env2
73474
- });
73633
+ }));
73634
+ if (installOutput.trim()) options.log(installOutput.trim());
73475
73635
  },
73476
73636
  restart: (portableNode, stagedCliEntry) => {
73477
73637
  const restartArgv = options.restartArgv.map((arg, index) => index === 0 ? stagedCliEntry : arg);
@@ -73498,28 +73658,12 @@ exec "${portableNode}" "${cliEntry}" "$@"
73498
73658
  child.unref();
73499
73659
  },
73500
73660
  waitForHealth: async (pid, targetVersion) => {
73501
- const deadline = Date.now() + 3e4;
73661
+ const deadline = Date.now() + healthTimeoutMs;
73502
73662
  while (Date.now() < deadline) {
73503
- const result = await new Promise((resolve27) => {
73504
- const req = http2.get(`http://127.0.0.1:${DEFAULT_HEALTH_PORT}/health`, { timeout: 1500 }, (res) => {
73505
- let body = "";
73506
- res.setEncoding("utf8");
73507
- res.on("data", (chunk) => {
73508
- body += chunk;
73509
- });
73510
- res.on("end", () => {
73511
- let responsePid;
73512
- try {
73513
- responsePid = Number(JSON.parse(body)?.pid);
73514
- } catch {
73515
- }
73516
- resolve27({ ok: res.statusCode === 200, pid: responsePid, body });
73517
- });
73518
- });
73519
- req.on("timeout", () => req.destroy());
73520
- req.on("error", () => resolve27({ ok: false }));
73521
- });
73522
- if (result.ok && result.pid === pid && result.body?.includes(targetVersion)) return true;
73663
+ const liveness = await fetchLocalHealth(healthPort);
73664
+ const alive = liveness.ok && liveness.pid === pid;
73665
+ const version2 = alive ? await fetchLocalStatusVersion(healthPort) : void 0;
73666
+ if (alive && version2 === targetVersion) return true;
73523
73667
  await new Promise((resolve27) => setTimeout(resolve27, 500));
73524
73668
  }
73525
73669
  return false;
@@ -73946,22 +74090,31 @@ ${body}
73946
74090
  `Cannot upgrade: owned processes still running under current prefix: ${preStop.survivors.map((s2) => s2.pid).join(", ")}`
73947
74091
  );
73948
74092
  }
73949
- await performWindowsAtomicUpgrade({
73950
- layout: windowsInstallerLayout,
73951
- packageName: payload.packageName,
73952
- targetVersion: payload.targetVersion,
73953
- portableNode,
73954
- excludePids: upgradePids,
73955
- hooks: createDefaultWindowsAtomicHooks({
74093
+ try {
74094
+ await performWindowsAtomicUpgrade({
74095
+ layout: windowsInstallerLayout,
73956
74096
  packageName: payload.packageName,
73957
74097
  targetVersion: payload.targetVersion,
73958
- npmCliPath,
73959
- restartArgv,
73960
- cwd: payload.cwd || process.cwd(),
73961
- env: Object.fromEntries(Object.entries(process.env).filter(([key2]) => key2 !== UPGRADE_HELPER_ENV)),
73962
- log: appendUpgradeLog
73963
- })
73964
- });
74098
+ portableNode,
74099
+ excludePids: upgradePids,
74100
+ hooks: createDefaultWindowsAtomicHooks({
74101
+ packageName: payload.packageName,
74102
+ targetVersion: payload.targetVersion,
74103
+ npmCliPath,
74104
+ restartArgv,
74105
+ cwd: payload.cwd || process.cwd(),
74106
+ env: Object.fromEntries(Object.entries(process.env).filter(([key2]) => key2 !== UPGRADE_HELPER_ENV)),
74107
+ log: appendUpgradeLog
74108
+ })
74109
+ });
74110
+ } catch (error48) {
74111
+ emitUpgradeFailureNotice([
74112
+ `adhdev ${payload.packageName}@${payload.targetVersion} upgrade failed and was rolled back: ${error48?.message || String(error48)}`,
74113
+ `Previous version preserved (active prefix: ${windowsInstallerLayout.activePrefix}).`,
74114
+ "See daemon-upgrade.log for the full install/health trace. The next daemon start will retry."
74115
+ ]);
74116
+ throw error48;
74117
+ }
73965
74118
  try {
73966
74119
  fs15.unlinkSync(getUpgradeFailureNoticePath());
73967
74120
  } catch {
@@ -78806,6 +78959,16 @@ ${body}
78806
78959
  generatingDebounceTimer = null;
78807
78960
  generatingDebouncePending = null;
78808
78961
  lastApprovalEventFingerprint = "";
78962
+ // INTERACTIVE-PROMPT-PUSH: edge-trigger key for the AskUserQuestion / waiting_choice
78963
+ // notification. An AskUserQuestion prompt is surfaced only as a display-only
78964
+ // `waiting_choice` overlay in getState(); the raw adapter status stays idle/generating,
78965
+ // so detectStatusTransition()'s status-keyed arms never fire and no push-worthy
78966
+ // agent:* event is emitted — the owner misses the "ACTION REQUIRED" prompt when the
78967
+ // app is backgrounded. We emit one agent:waiting_approval on ENTRY into the prompt
78968
+ // state (reusing the existing server push path, no cloud change) and dedupe on this
78969
+ // key so repeated status ticks with the same prompt do not re-fire. '' means no
78970
+ // prompt is currently active (cleared when the prompt is answered/gone).
78971
+ lastInteractivePromptEventKey = "";
78809
78972
  autoApproveBusy = false;
78810
78973
  autoApproveBusyTimer = null;
78811
78974
  lastAutoApprovalSignature = "";
@@ -81176,6 +81339,25 @@ ${buttons.join("\n")}`;
81176
81339
  }
81177
81340
  this.lastStatus = newStatus;
81178
81341
  }
81342
+ const interactivePrompt = adapterStatus.activeInteractivePrompt ?? null;
81343
+ if (interactivePrompt && newStatus !== "waiting_approval") {
81344
+ const firstQuestion = interactivePrompt.questions?.[0];
81345
+ const promptKey = `${interactivePrompt.promptId}::${firstQuestion?.question ?? ""}`;
81346
+ if (promptKey !== this.lastInteractivePromptEventKey) {
81347
+ this.lastInteractivePromptEventKey = promptKey;
81348
+ const modalMessage = firstQuestion ? firstQuestion.header ? `${firstQuestion.header}: ${firstQuestion.question}` : firstQuestion.question : void 0;
81349
+ const modalButtons = firstQuestion?.options?.map((option) => option.label);
81350
+ this.pushEvent({
81351
+ event: "agent:waiting_approval",
81352
+ chatTitle,
81353
+ timestamp: now,
81354
+ modalMessage,
81355
+ modalButtons
81356
+ });
81357
+ }
81358
+ } else if (!interactivePrompt && this.lastInteractivePromptEventKey) {
81359
+ this.lastInteractivePromptEventKey = "";
81360
+ }
81179
81361
  const firstTurnStartedAt = typeof this.adapter?.currentTurnStartedAt === "number" ? this.adapter.currentTurnStartedAt : 0;
81180
81362
  const collapsedAt = this.startupGraceCollapseAt;
81181
81363
  const turnStartedWithinCollapseWindow = collapsedAt !== null && firstTurnStartedAt > 0 && firstTurnStartedAt >= collapsedAt && firstTurnStartedAt - collapsedAt < STARTUP_GRACE_IDLE_COLLAPSE_WINDOW_MS;
@@ -90604,7 +90786,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
90604
90786
  const buildOperatingNotesBestEffort = async (id) => {
90605
90787
  try {
90606
90788
  const { readOperatingNotes: readOperatingNotes2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
90607
- const noteEntries = readOperatingNotes2(id, { tail: 20 });
90789
+ const noteEntries = readOperatingNotes2(id, { tail: 100 });
90608
90790
  const notes = noteEntries.map((e) => {
90609
90791
  const p = e.payload || {};
90610
90792
  const text = typeof p.text === "string" ? p.text.trim() : "";
@@ -90619,7 +90801,13 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
90619
90801
  // injection-side selection can honor them. Legacy notes lack
90620
90802
  // these → pinned defaults false, expiry governed by category TTL.
90621
90803
  pinned: p.pinned === true,
90622
- ...typeof p.expiresAt === "string" ? { expiresAt: p.expiresAt } : {}
90804
+ ...typeof p.expiresAt === "string" ? { expiresAt: p.expiresAt } : {},
90805
+ // Phase 2 (b)/(c): thread the ledger id + supersedes/subjectKey
90806
+ // so version-supersede targeting and same-class folding work.
90807
+ // Legacy notes lack them → no supersede, fold by leading [tag].
90808
+ noteId: e.id,
90809
+ ...typeof p.supersedes === "string" ? { supersedes: p.supersedes } : {},
90810
+ ...typeof p.subjectKey === "string" ? { subjectKey: p.subjectKey } : {}
90623
90811
  };
90624
90812
  }).filter((n) => n !== null);
90625
90813
  return notes.length ? notes : void 0;