@adhdev/daemon-core 1.0.18-rc.7 → 1.0.18-rc.8

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.mjs CHANGED
@@ -437,10 +437,10 @@ function readInjected(value) {
437
437
  }
438
438
  function getDaemonBuildInfo() {
439
439
  if (cached) return cached;
440
- const commit = readInjected(true ? "068ae7b54eceb85bb9843d2c7654c3d4687fce75" : void 0) ?? "unknown";
441
- const commitShort = readInjected(true ? "068ae7b5" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
442
- const version = readInjected(true ? "1.0.18-rc.7" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
443
- const builtAt = readInjected(true ? "2026-07-22T02:34:29.198Z" : void 0);
440
+ const commit = readInjected(true ? "ebf45e12869e32286272a11046f70914784f3f04" : void 0) ?? "unknown";
441
+ const commitShort = readInjected(true ? "ebf45e12" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
442
+ const version = readInjected(true ? "1.0.18-rc.8" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
443
+ const builtAt = readInjected(true ? "2026-07-22T04:22:40.678Z" : void 0);
444
444
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
445
445
  return cached;
446
446
  }
@@ -9865,40 +9865,113 @@ function isDurableCategory(category) {
9865
9865
  if (!category) return true;
9866
9866
  return !(category in OPERATING_NOTE_CATEGORY_TTL_DAYS);
9867
9867
  }
9868
- function selectOperatingNotesForPrompt(notes, now, cap = OPERATING_NOTES_PROMPT_CAP) {
9868
+ function deriveSubjectKey(note) {
9869
+ const explicit = typeof note.subjectKey === "string" ? note.subjectKey.trim().toLowerCase() : "";
9870
+ if (explicit) return explicit;
9871
+ const text = typeof note.text === "string" ? note.text.trimStart() : "";
9872
+ const m = /^\[([^\]]{1,80})\]/.exec(text);
9873
+ const tag = m ? m[1].trim().toLowerCase() : "";
9874
+ return tag || void 0;
9875
+ }
9876
+ function foldSameClassNotes(ranked) {
9877
+ const out = [];
9878
+ const survivorByKey = /* @__PURE__ */ new Map();
9879
+ for (const note of ranked) {
9880
+ const subject = deriveSubjectKey(note);
9881
+ const foldable = !note.pinned && !!note.category && !!subject;
9882
+ if (foldable) {
9883
+ const key2 = `${note.category}\0${subject}`;
9884
+ const existing = survivorByKey.get(key2);
9885
+ if (existing !== void 0) {
9886
+ const survivor = out[existing];
9887
+ survivor.subsumedCount += 1;
9888
+ if (typeof note.noteId === "string" && note.noteId) survivor.subsumedIds.push(note.noteId);
9889
+ continue;
9890
+ }
9891
+ survivorByKey.set(key2, out.length);
9892
+ }
9893
+ out.push({ note, subsumedIds: [], subsumedCount: 0 });
9894
+ }
9895
+ return out;
9896
+ }
9897
+ function renderedNoteBytes(folded) {
9898
+ return byteLength2(renderOperatingNoteLine(folded));
9899
+ }
9900
+ function selectOperatingNotesForPrompt(notes, now, cap = OPERATING_NOTES_PROMPT_CAP, byteBudget = OPERATING_NOTE_INJECTION_BYTE_BUDGET) {
9869
9901
  const valid = Array.isArray(notes) ? notes.filter((n) => n && typeof n.text === "string" && n.text.trim()) : [];
9870
- const kept = valid.map((note, index) => ({ note, index })).filter(({ note }) => note.pinned || !isNoteExpired(note, now));
9902
+ let kept = valid.map((note, index) => ({ note, index })).filter(({ note }) => note.pinned || !isNoteExpired(note, now));
9903
+ const supersededAtIndex = /* @__PURE__ */ new Map();
9904
+ for (const { note, index } of kept) {
9905
+ const target = typeof note.supersedes === "string" ? note.supersedes.trim().toLowerCase() : "";
9906
+ if (!target) continue;
9907
+ const prev = supersededAtIndex.get(target);
9908
+ if (prev === void 0 || index > prev) supersededAtIndex.set(target, index);
9909
+ }
9910
+ if (supersededAtIndex.size > 0) {
9911
+ kept = kept.filter(({ note, index }) => {
9912
+ if (note.pinned) return true;
9913
+ const byId = typeof note.noteId === "string" ? note.noteId.trim().toLowerCase() : "";
9914
+ const bySubject = deriveSubjectKey(note);
9915
+ const supIdx = Math.max(
9916
+ byId ? supersededAtIndex.get(byId) ?? -1 : -1,
9917
+ bySubject ? supersededAtIndex.get(bySubject) ?? -1 : -1
9918
+ );
9919
+ return !(supIdx > index);
9920
+ });
9921
+ }
9871
9922
  const rank = (n) => n.pinned ? 0 : isDurableCategory(n.category) ? 1 : 2;
9872
9923
  kept.sort((a, b) => {
9873
9924
  const r = rank(a.note) - rank(b.note);
9874
9925
  if (r !== 0) return r;
9875
9926
  return b.index - a.index;
9876
9927
  });
9877
- const omittedCount = Math.max(0, kept.length - cap);
9878
- const shown = (omittedCount > 0 ? kept.slice(0, cap) : kept).map((k) => k.note);
9928
+ const folded = foldSameClassNotes(kept.map((k) => k.note));
9929
+ const shown = [];
9930
+ let usedBytes = 0;
9931
+ let usedCount = 0;
9932
+ for (const entry of folded) {
9933
+ if (entry.note.pinned) {
9934
+ shown.push(entry);
9935
+ usedBytes += renderedNoteBytes(entry);
9936
+ usedCount += 1;
9937
+ continue;
9938
+ }
9939
+ if (usedCount >= cap) break;
9940
+ const bytes = renderedNoteBytes(entry);
9941
+ if (usedCount > 0 && usedBytes + bytes > byteBudget) break;
9942
+ shown.push(entry);
9943
+ usedBytes += bytes;
9944
+ usedCount += 1;
9945
+ }
9946
+ const omittedCount = Math.max(0, folded.length - shown.length);
9879
9947
  return { shown, omittedCount };
9880
9948
  }
9881
- function buildOperatingNotesSection(notes, now = Date.now()) {
9882
- const hasAny = Array.isArray(notes) ? notes.some((n) => n && typeof n.text === "string" && n.text.trim()) : false;
9883
- if (!hasAny) return "";
9949
+ function renderOperatingNoteLine(folded) {
9950
+ const n = folded.note;
9884
9951
  const categoryLabel = {
9885
9952
  provider_quirk: "provider quirk",
9886
9953
  pattern_to_avoid: "pattern to avoid",
9887
9954
  recovery_lesson: "recovery lesson"
9888
9955
  };
9956
+ const pin = n.pinned ? "\u{1F4CC} " : "";
9957
+ const cat = n.category && categoryLabel[n.category] ? `[${categoryLabel[n.category]}] ` : "";
9958
+ const fold = folded.subsumedCount > 0 ? ` _(+${folded.subsumedCount} earlier same-subject note${folded.subsumedCount === 1 ? "" : "s"} folded${folded.subsumedIds.length ? `: ${folded.subsumedIds.join(", ")}` : ""})_` : "";
9959
+ return `- ${pin}${cat}${truncateNote(n.text.trim())}${fold}`;
9960
+ }
9961
+ function buildOperatingNotesSection(notes, now = Date.now()) {
9962
+ const hasAny = Array.isArray(notes) ? notes.some((n) => n && typeof n.text === "string" && n.text.trim()) : false;
9963
+ if (!hasAny) return "";
9889
9964
  const { shown, omittedCount } = selectOperatingNotesForPrompt(notes ?? [], now);
9890
9965
  if (shown.length === 0) return "";
9891
9966
  const lines = ["## Operating Notes", ""];
9892
9967
  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.");
9893
9968
  lines.push("");
9894
- for (const n of shown) {
9895
- const pin = n.pinned ? "\u{1F4CC} " : "";
9896
- const cat = n.category && categoryLabel[n.category] ? `[${categoryLabel[n.category]}] ` : "";
9897
- lines.push(`- ${pin}${cat}${truncateNote(n.text.trim())}`);
9969
+ for (const entry of shown) {
9970
+ lines.push(renderOperatingNoteLine(entry));
9898
9971
  }
9899
9972
  if (omittedCount > 0) {
9900
9973
  lines.push("");
9901
- 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\`)._`);
9974
+ 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\`)._`);
9902
9975
  }
9903
9976
  return lines.join("\n");
9904
9977
  }
@@ -10022,7 +10095,7 @@ When you compose the task message you dispatch to a node, include these requirem
10022
10095
  - **Scoped test runs.** For a validation or code-change task, instruct the worker to run only the tests covering the changed files (\`vitest run <path>\` or \`-t <name>\`), not the whole suite. Run the full suite only when the task is explicitly a full-suite gate \u2014 a broad daemon-core run is minutes of wall-clock and the biggest source of worker slowness.
10023
10096
  - **Branch convergence state.** For a worktree task, require the completion report to classify the touched branch into exactly one final state: \`merged_to_main\`, \`pushed_feature_branch_needs_merge\`, \`blocked_review\`, \`cleanup_candidate\`, or \`not_mergeable\`. A task that ends on a non-main branch is not complete unless the report names that state and the next step.`;
10024
10097
  }
10025
- var PROMPT_SOFT_CAP_BYTES, OPERATING_NOTES_PROMPT_CAP, OPERATING_NOTE_MAX_CHARS, TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION, ONBOARDING_SECTION;
10098
+ var PROMPT_SOFT_CAP_BYTES, OPERATING_NOTES_PROMPT_CAP, OPERATING_NOTE_MAX_CHARS, OPERATING_NOTE_INJECTION_BYTE_BUDGET, TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION, ONBOARDING_SECTION;
10026
10099
  var init_coordinator_prompt = __esm({
10027
10100
  "src/mesh/coordinator-prompt.ts"() {
10028
10101
  "use strict";
@@ -10034,6 +10107,7 @@ var init_coordinator_prompt = __esm({
10034
10107
  PROMPT_SOFT_CAP_BYTES = 60 * 1024;
10035
10108
  OPERATING_NOTES_PROMPT_CAP = 20;
10036
10109
  OPERATING_NOTE_MAX_CHARS = 300;
10110
+ OPERATING_NOTE_INJECTION_BYTE_BUDGET = 6 * 1024;
10037
10111
  TOOLS_SECTION = `## Available Tools
10038
10112
 
10039
10113
  | Tool | Purpose |
@@ -11806,9 +11880,13 @@ function normalizeOperatingNote(value) {
11806
11880
  ...typeof value.createdAt === "string" ? { createdAt: value.createdAt } : {},
11807
11881
  ...typeof value.sourceCoordinator === "string" ? { sourceCoordinator: value.sourceCoordinator } : {},
11808
11882
  // Operating-notes lifecycle: a repo-declared note may pin itself or set an
11809
- // explicit expiry, same as a runtime-recorded one.
11883
+ // explicit expiry, same as a runtime-recorded one. Phase 2 (b)/(c): it may
11884
+ // also declare supersedes/subjectKey to retire an earlier note or group
11885
+ // same-subject notes for folding.
11810
11886
  ...value.pinned === true ? { pinned: true } : {},
11811
- ...typeof value.expiresAt === "string" ? { expiresAt: value.expiresAt } : {}
11887
+ ...typeof value.expiresAt === "string" ? { expiresAt: value.expiresAt } : {},
11888
+ ...typeof value.supersedes === "string" ? { supersedes: value.supersedes } : {},
11889
+ ...typeof value.subjectKey === "string" ? { subjectKey: value.subjectKey } : {}
11812
11890
  };
11813
11891
  }
11814
11892
  function normalizeRepoMeshDeclarativeConfig(parsed) {
@@ -48597,6 +48675,16 @@ var CliProviderInstance = class _CliProviderInstance {
48597
48675
  generatingDebounceTimer = null;
48598
48676
  generatingDebouncePending = null;
48599
48677
  lastApprovalEventFingerprint = "";
48678
+ // INTERACTIVE-PROMPT-PUSH: edge-trigger key for the AskUserQuestion / waiting_choice
48679
+ // notification. An AskUserQuestion prompt is surfaced only as a display-only
48680
+ // `waiting_choice` overlay in getState(); the raw adapter status stays idle/generating,
48681
+ // so detectStatusTransition()'s status-keyed arms never fire and no push-worthy
48682
+ // agent:* event is emitted — the owner misses the "ACTION REQUIRED" prompt when the
48683
+ // app is backgrounded. We emit one agent:waiting_approval on ENTRY into the prompt
48684
+ // state (reusing the existing server push path, no cloud change) and dedupe on this
48685
+ // key so repeated status ticks with the same prompt do not re-fire. '' means no
48686
+ // prompt is currently active (cleared when the prompt is answered/gone).
48687
+ lastInteractivePromptEventKey = "";
48600
48688
  autoApproveBusy = false;
48601
48689
  autoApproveBusyTimer = null;
48602
48690
  lastAutoApprovalSignature = "";
@@ -50967,6 +51055,25 @@ ${buttons.join("\n")}`;
50967
51055
  }
50968
51056
  this.lastStatus = newStatus;
50969
51057
  }
51058
+ const interactivePrompt = adapterStatus.activeInteractivePrompt ?? null;
51059
+ if (interactivePrompt && newStatus !== "waiting_approval") {
51060
+ const firstQuestion = interactivePrompt.questions?.[0];
51061
+ const promptKey = `${interactivePrompt.promptId}::${firstQuestion?.question ?? ""}`;
51062
+ if (promptKey !== this.lastInteractivePromptEventKey) {
51063
+ this.lastInteractivePromptEventKey = promptKey;
51064
+ const modalMessage = firstQuestion ? firstQuestion.header ? `${firstQuestion.header}: ${firstQuestion.question}` : firstQuestion.question : void 0;
51065
+ const modalButtons = firstQuestion?.options?.map((option) => option.label);
51066
+ this.pushEvent({
51067
+ event: "agent:waiting_approval",
51068
+ chatTitle,
51069
+ timestamp: now,
51070
+ modalMessage,
51071
+ modalButtons
51072
+ });
51073
+ }
51074
+ } else if (!interactivePrompt && this.lastInteractivePromptEventKey) {
51075
+ this.lastInteractivePromptEventKey = "";
51076
+ }
50970
51077
  const firstTurnStartedAt = typeof this.adapter?.currentTurnStartedAt === "number" ? this.adapter.currentTurnStartedAt : 0;
50971
51078
  const collapsedAt = this.startupGraceCollapseAt;
50972
51079
  const turnStartedWithinCollapseWindow = collapsedAt !== null && firstTurnStartedAt > 0 && firstTurnStartedAt >= collapsedAt && firstTurnStartedAt - collapsedAt < STARTUP_GRACE_IDLE_COLLAPSE_WINDOW_MS;
@@ -60458,7 +60565,7 @@ var meshCoordinatorLaunchHandlers = {
60458
60565
  const buildOperatingNotesBestEffort = async (id) => {
60459
60566
  try {
60460
60567
  const { readOperatingNotes: readOperatingNotes2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
60461
- const noteEntries = readOperatingNotes2(id, { tail: 20 });
60568
+ const noteEntries = readOperatingNotes2(id, { tail: 100 });
60462
60569
  const notes = noteEntries.map((e) => {
60463
60570
  const p = e.payload || {};
60464
60571
  const text = typeof p.text === "string" ? p.text.trim() : "";
@@ -60473,7 +60580,13 @@ var meshCoordinatorLaunchHandlers = {
60473
60580
  // injection-side selection can honor them. Legacy notes lack
60474
60581
  // these → pinned defaults false, expiry governed by category TTL.
60475
60582
  pinned: p.pinned === true,
60476
- ...typeof p.expiresAt === "string" ? { expiresAt: p.expiresAt } : {}
60583
+ ...typeof p.expiresAt === "string" ? { expiresAt: p.expiresAt } : {},
60584
+ // Phase 2 (b)/(c): thread the ledger id + supersedes/subjectKey
60585
+ // so version-supersede targeting and same-class folding work.
60586
+ // Legacy notes lack them → no supersede, fold by leading [tag].
60587
+ noteId: e.id,
60588
+ ...typeof p.supersedes === "string" ? { supersedes: p.supersedes } : {},
60589
+ ...typeof p.subjectKey === "string" ? { subjectKey: p.subjectKey } : {}
60477
60590
  };
60478
60591
  }).filter((n) => n !== null);
60479
60592
  return notes.length ? notes : void 0;