@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.js CHANGED
@@ -442,10 +442,10 @@ function readInjected(value) {
442
442
  }
443
443
  function getDaemonBuildInfo() {
444
444
  if (cached) return cached;
445
- const commit = readInjected(true ? "068ae7b54eceb85bb9843d2c7654c3d4687fce75" : void 0) ?? "unknown";
446
- const commitShort = readInjected(true ? "068ae7b5" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
447
- const version = readInjected(true ? "1.0.18-rc.7" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
448
- const builtAt = readInjected(true ? "2026-07-22T02:34:29.198Z" : void 0);
445
+ const commit = readInjected(true ? "ebf45e12869e32286272a11046f70914784f3f04" : void 0) ?? "unknown";
446
+ const commitShort = readInjected(true ? "ebf45e12" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
447
+ const version = readInjected(true ? "1.0.18-rc.8" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
448
+ const builtAt = readInjected(true ? "2026-07-22T04:22:40.678Z" : void 0);
449
449
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
450
450
  return cached;
451
451
  }
@@ -9869,40 +9869,113 @@ function isDurableCategory(category) {
9869
9869
  if (!category) return true;
9870
9870
  return !(category in OPERATING_NOTE_CATEGORY_TTL_DAYS);
9871
9871
  }
9872
- function selectOperatingNotesForPrompt(notes, now, cap = OPERATING_NOTES_PROMPT_CAP) {
9872
+ function deriveSubjectKey(note) {
9873
+ const explicit = typeof note.subjectKey === "string" ? note.subjectKey.trim().toLowerCase() : "";
9874
+ if (explicit) return explicit;
9875
+ const text = typeof note.text === "string" ? note.text.trimStart() : "";
9876
+ const m = /^\[([^\]]{1,80})\]/.exec(text);
9877
+ const tag = m ? m[1].trim().toLowerCase() : "";
9878
+ return tag || void 0;
9879
+ }
9880
+ function foldSameClassNotes(ranked) {
9881
+ const out = [];
9882
+ const survivorByKey = /* @__PURE__ */ new Map();
9883
+ for (const note of ranked) {
9884
+ const subject = deriveSubjectKey(note);
9885
+ const foldable = !note.pinned && !!note.category && !!subject;
9886
+ if (foldable) {
9887
+ const key2 = `${note.category}\0${subject}`;
9888
+ const existing = survivorByKey.get(key2);
9889
+ if (existing !== void 0) {
9890
+ const survivor = out[existing];
9891
+ survivor.subsumedCount += 1;
9892
+ if (typeof note.noteId === "string" && note.noteId) survivor.subsumedIds.push(note.noteId);
9893
+ continue;
9894
+ }
9895
+ survivorByKey.set(key2, out.length);
9896
+ }
9897
+ out.push({ note, subsumedIds: [], subsumedCount: 0 });
9898
+ }
9899
+ return out;
9900
+ }
9901
+ function renderedNoteBytes(folded) {
9902
+ return byteLength2(renderOperatingNoteLine(folded));
9903
+ }
9904
+ function selectOperatingNotesForPrompt(notes, now, cap = OPERATING_NOTES_PROMPT_CAP, byteBudget = OPERATING_NOTE_INJECTION_BYTE_BUDGET) {
9873
9905
  const valid = Array.isArray(notes) ? notes.filter((n) => n && typeof n.text === "string" && n.text.trim()) : [];
9874
- const kept = valid.map((note, index) => ({ note, index })).filter(({ note }) => note.pinned || !isNoteExpired(note, now));
9906
+ let kept = valid.map((note, index) => ({ note, index })).filter(({ note }) => note.pinned || !isNoteExpired(note, now));
9907
+ const supersededAtIndex = /* @__PURE__ */ new Map();
9908
+ for (const { note, index } of kept) {
9909
+ const target = typeof note.supersedes === "string" ? note.supersedes.trim().toLowerCase() : "";
9910
+ if (!target) continue;
9911
+ const prev = supersededAtIndex.get(target);
9912
+ if (prev === void 0 || index > prev) supersededAtIndex.set(target, index);
9913
+ }
9914
+ if (supersededAtIndex.size > 0) {
9915
+ kept = kept.filter(({ note, index }) => {
9916
+ if (note.pinned) return true;
9917
+ const byId = typeof note.noteId === "string" ? note.noteId.trim().toLowerCase() : "";
9918
+ const bySubject = deriveSubjectKey(note);
9919
+ const supIdx = Math.max(
9920
+ byId ? supersededAtIndex.get(byId) ?? -1 : -1,
9921
+ bySubject ? supersededAtIndex.get(bySubject) ?? -1 : -1
9922
+ );
9923
+ return !(supIdx > index);
9924
+ });
9925
+ }
9875
9926
  const rank = (n) => n.pinned ? 0 : isDurableCategory(n.category) ? 1 : 2;
9876
9927
  kept.sort((a, b) => {
9877
9928
  const r = rank(a.note) - rank(b.note);
9878
9929
  if (r !== 0) return r;
9879
9930
  return b.index - a.index;
9880
9931
  });
9881
- const omittedCount = Math.max(0, kept.length - cap);
9882
- const shown = (omittedCount > 0 ? kept.slice(0, cap) : kept).map((k) => k.note);
9932
+ const folded = foldSameClassNotes(kept.map((k) => k.note));
9933
+ const shown = [];
9934
+ let usedBytes = 0;
9935
+ let usedCount = 0;
9936
+ for (const entry of folded) {
9937
+ if (entry.note.pinned) {
9938
+ shown.push(entry);
9939
+ usedBytes += renderedNoteBytes(entry);
9940
+ usedCount += 1;
9941
+ continue;
9942
+ }
9943
+ if (usedCount >= cap) break;
9944
+ const bytes = renderedNoteBytes(entry);
9945
+ if (usedCount > 0 && usedBytes + bytes > byteBudget) break;
9946
+ shown.push(entry);
9947
+ usedBytes += bytes;
9948
+ usedCount += 1;
9949
+ }
9950
+ const omittedCount = Math.max(0, folded.length - shown.length);
9883
9951
  return { shown, omittedCount };
9884
9952
  }
9885
- function buildOperatingNotesSection(notes, now = Date.now()) {
9886
- const hasAny = Array.isArray(notes) ? notes.some((n) => n && typeof n.text === "string" && n.text.trim()) : false;
9887
- if (!hasAny) return "";
9953
+ function renderOperatingNoteLine(folded) {
9954
+ const n = folded.note;
9888
9955
  const categoryLabel = {
9889
9956
  provider_quirk: "provider quirk",
9890
9957
  pattern_to_avoid: "pattern to avoid",
9891
9958
  recovery_lesson: "recovery lesson"
9892
9959
  };
9960
+ const pin = n.pinned ? "\u{1F4CC} " : "";
9961
+ const cat = n.category && categoryLabel[n.category] ? `[${categoryLabel[n.category]}] ` : "";
9962
+ const fold = folded.subsumedCount > 0 ? ` _(+${folded.subsumedCount} earlier same-subject note${folded.subsumedCount === 1 ? "" : "s"} folded${folded.subsumedIds.length ? `: ${folded.subsumedIds.join(", ")}` : ""})_` : "";
9963
+ return `- ${pin}${cat}${truncateNote(n.text.trim())}${fold}`;
9964
+ }
9965
+ function buildOperatingNotesSection(notes, now = Date.now()) {
9966
+ const hasAny = Array.isArray(notes) ? notes.some((n) => n && typeof n.text === "string" && n.text.trim()) : false;
9967
+ if (!hasAny) return "";
9893
9968
  const { shown, omittedCount } = selectOperatingNotesForPrompt(notes ?? [], now);
9894
9969
  if (shown.length === 0) return "";
9895
9970
  const lines = ["## Operating Notes", ""];
9896
9971
  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.");
9897
9972
  lines.push("");
9898
- for (const n of shown) {
9899
- const pin = n.pinned ? "\u{1F4CC} " : "";
9900
- const cat = n.category && categoryLabel[n.category] ? `[${categoryLabel[n.category]}] ` : "";
9901
- lines.push(`- ${pin}${cat}${truncateNote(n.text.trim())}`);
9973
+ for (const entry of shown) {
9974
+ lines.push(renderOperatingNoteLine(entry));
9902
9975
  }
9903
9976
  if (omittedCount > 0) {
9904
9977
  lines.push("");
9905
- 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\`)._`);
9978
+ 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\`)._`);
9906
9979
  }
9907
9980
  return lines.join("\n");
9908
9981
  }
@@ -10026,7 +10099,7 @@ When you compose the task message you dispatch to a node, include these requirem
10026
10099
  - **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.
10027
10100
  - **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.`;
10028
10101
  }
10029
- var fs4, os3, path9, PROMPT_SOFT_CAP_BYTES, OPERATING_NOTES_PROMPT_CAP, OPERATING_NOTE_MAX_CHARS, TOOLS_SECTION, TOOL_EXPOSURE_PREFLIGHT_SECTION, WORKFLOW_SECTION, ONBOARDING_SECTION;
10102
+ var fs4, os3, path9, 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;
10030
10103
  var init_coordinator_prompt = __esm({
10031
10104
  "src/mesh/coordinator-prompt.ts"() {
10032
10105
  "use strict";
@@ -10041,6 +10114,7 @@ var init_coordinator_prompt = __esm({
10041
10114
  PROMPT_SOFT_CAP_BYTES = 60 * 1024;
10042
10115
  OPERATING_NOTES_PROMPT_CAP = 20;
10043
10116
  OPERATING_NOTE_MAX_CHARS = 300;
10117
+ OPERATING_NOTE_INJECTION_BYTE_BUDGET = 6 * 1024;
10044
10118
  TOOLS_SECTION = `## Available Tools
10045
10119
 
10046
10120
  | Tool | Purpose |
@@ -11810,9 +11884,13 @@ function normalizeOperatingNote(value) {
11810
11884
  ...typeof value.createdAt === "string" ? { createdAt: value.createdAt } : {},
11811
11885
  ...typeof value.sourceCoordinator === "string" ? { sourceCoordinator: value.sourceCoordinator } : {},
11812
11886
  // Operating-notes lifecycle: a repo-declared note may pin itself or set an
11813
- // explicit expiry, same as a runtime-recorded one.
11887
+ // explicit expiry, same as a runtime-recorded one. Phase 2 (b)/(c): it may
11888
+ // also declare supersedes/subjectKey to retire an earlier note or group
11889
+ // same-subject notes for folding.
11814
11890
  ...value.pinned === true ? { pinned: true } : {},
11815
- ...typeof value.expiresAt === "string" ? { expiresAt: value.expiresAt } : {}
11891
+ ...typeof value.expiresAt === "string" ? { expiresAt: value.expiresAt } : {},
11892
+ ...typeof value.supersedes === "string" ? { supersedes: value.supersedes } : {},
11893
+ ...typeof value.subjectKey === "string" ? { subjectKey: value.subjectKey } : {}
11816
11894
  };
11817
11895
  }
11818
11896
  function normalizeRepoMeshDeclarativeConfig(parsed) {
@@ -49024,6 +49102,16 @@ var CliProviderInstance = class _CliProviderInstance {
49024
49102
  generatingDebounceTimer = null;
49025
49103
  generatingDebouncePending = null;
49026
49104
  lastApprovalEventFingerprint = "";
49105
+ // INTERACTIVE-PROMPT-PUSH: edge-trigger key for the AskUserQuestion / waiting_choice
49106
+ // notification. An AskUserQuestion prompt is surfaced only as a display-only
49107
+ // `waiting_choice` overlay in getState(); the raw adapter status stays idle/generating,
49108
+ // so detectStatusTransition()'s status-keyed arms never fire and no push-worthy
49109
+ // agent:* event is emitted — the owner misses the "ACTION REQUIRED" prompt when the
49110
+ // app is backgrounded. We emit one agent:waiting_approval on ENTRY into the prompt
49111
+ // state (reusing the existing server push path, no cloud change) and dedupe on this
49112
+ // key so repeated status ticks with the same prompt do not re-fire. '' means no
49113
+ // prompt is currently active (cleared when the prompt is answered/gone).
49114
+ lastInteractivePromptEventKey = "";
49027
49115
  autoApproveBusy = false;
49028
49116
  autoApproveBusyTimer = null;
49029
49117
  lastAutoApprovalSignature = "";
@@ -51394,6 +51482,25 @@ ${buttons.join("\n")}`;
51394
51482
  }
51395
51483
  this.lastStatus = newStatus;
51396
51484
  }
51485
+ const interactivePrompt = adapterStatus.activeInteractivePrompt ?? null;
51486
+ if (interactivePrompt && newStatus !== "waiting_approval") {
51487
+ const firstQuestion = interactivePrompt.questions?.[0];
51488
+ const promptKey = `${interactivePrompt.promptId}::${firstQuestion?.question ?? ""}`;
51489
+ if (promptKey !== this.lastInteractivePromptEventKey) {
51490
+ this.lastInteractivePromptEventKey = promptKey;
51491
+ const modalMessage = firstQuestion ? firstQuestion.header ? `${firstQuestion.header}: ${firstQuestion.question}` : firstQuestion.question : void 0;
51492
+ const modalButtons = firstQuestion?.options?.map((option) => option.label);
51493
+ this.pushEvent({
51494
+ event: "agent:waiting_approval",
51495
+ chatTitle,
51496
+ timestamp: now,
51497
+ modalMessage,
51498
+ modalButtons
51499
+ });
51500
+ }
51501
+ } else if (!interactivePrompt && this.lastInteractivePromptEventKey) {
51502
+ this.lastInteractivePromptEventKey = "";
51503
+ }
51397
51504
  const firstTurnStartedAt = typeof this.adapter?.currentTurnStartedAt === "number" ? this.adapter.currentTurnStartedAt : 0;
51398
51505
  const collapsedAt = this.startupGraceCollapseAt;
51399
51506
  const turnStartedWithinCollapseWindow = collapsedAt !== null && firstTurnStartedAt > 0 && firstTurnStartedAt >= collapsedAt && firstTurnStartedAt - collapsedAt < STARTUP_GRACE_IDLE_COLLAPSE_WINDOW_MS;
@@ -60880,7 +60987,7 @@ var meshCoordinatorLaunchHandlers = {
60880
60987
  const buildOperatingNotesBestEffort = async (id) => {
60881
60988
  try {
60882
60989
  const { readOperatingNotes: readOperatingNotes2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
60883
- const noteEntries = readOperatingNotes2(id, { tail: 20 });
60990
+ const noteEntries = readOperatingNotes2(id, { tail: 100 });
60884
60991
  const notes = noteEntries.map((e) => {
60885
60992
  const p = e.payload || {};
60886
60993
  const text = typeof p.text === "string" ? p.text.trim() : "";
@@ -60895,7 +61002,13 @@ var meshCoordinatorLaunchHandlers = {
60895
61002
  // injection-side selection can honor them. Legacy notes lack
60896
61003
  // these → pinned defaults false, expiry governed by category TTL.
60897
61004
  pinned: p.pinned === true,
60898
- ...typeof p.expiresAt === "string" ? { expiresAt: p.expiresAt } : {}
61005
+ ...typeof p.expiresAt === "string" ? { expiresAt: p.expiresAt } : {},
61006
+ // Phase 2 (b)/(c): thread the ledger id + supersedes/subjectKey
61007
+ // so version-supersede targeting and same-class folding work.
61008
+ // Legacy notes lack them → no supersede, fold by leading [tag].
61009
+ noteId: e.id,
61010
+ ...typeof p.supersedes === "string" ? { supersedes: p.supersedes } : {},
61011
+ ...typeof p.subjectKey === "string" ? { subjectKey: p.subjectKey } : {}
60899
61012
  };
60900
61013
  }).filter((n) => n !== null);
60901
61014
  return notes.length ? notes : void 0;