@cortexkit/opencode-magic-context 0.34.0 → 0.34.2

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 (97) hide show
  1. package/README.md +20 -3
  2. package/dist/config/migrate-experimental.d.ts +2 -1
  3. package/dist/config/migrate-experimental.d.ts.map +1 -1
  4. package/dist/config/project-security.d.ts +2 -0
  5. package/dist/config/project-security.d.ts.map +1 -1
  6. package/dist/config/schema/magic-context.d.ts +8 -15
  7. package/dist/config/schema/magic-context.d.ts.map +1 -1
  8. package/dist/features/magic-context/dreamer/classify.d.ts +1 -0
  9. package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
  10. package/dist/features/magic-context/dreamer/map-memories.d.ts +1 -0
  11. package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
  12. package/dist/features/magic-context/dreamer/refresh-primers.d.ts +1 -0
  13. package/dist/features/magic-context/dreamer/refresh-primers.d.ts.map +1 -1
  14. package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts +2 -0
  15. package/dist/features/magic-context/dreamer/storage-dream-runs.d.ts.map +1 -1
  16. package/dist/features/magic-context/dreamer/storage-task-schedule.d.ts +9 -5
  17. package/dist/features/magic-context/dreamer/storage-task-schedule.d.ts.map +1 -1
  18. package/dist/features/magic-context/dreamer/task-executor.d.ts +4 -1
  19. package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
  20. package/dist/features/magic-context/dreamer/task-gates.d.ts +19 -1
  21. package/dist/features/magic-context/dreamer/task-gates.d.ts.map +1 -1
  22. package/dist/features/magic-context/dreamer/task-registry.d.ts +28 -0
  23. package/dist/features/magic-context/dreamer/task-registry.d.ts.map +1 -1
  24. package/dist/features/magic-context/dreamer/task-scheduler.d.ts +7 -1
  25. package/dist/features/magic-context/dreamer/task-scheduler.d.ts.map +1 -1
  26. package/dist/features/magic-context/dreamer/verify-gate.d.ts +8 -2
  27. package/dist/features/magic-context/dreamer/verify-gate.d.ts.map +1 -1
  28. package/dist/features/magic-context/dreamer/verify.d.ts +3 -0
  29. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  30. package/dist/features/magic-context/fail-closed-block.d.ts +2 -0
  31. package/dist/features/magic-context/fail-closed-block.d.ts.map +1 -1
  32. package/dist/features/magic-context/memory/storage-memory.d.ts.map +1 -1
  33. package/dist/features/magic-context/migrations.d.ts +23 -1
  34. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  35. package/dist/features/magic-context/mural/compress-cues.d.ts +8 -3
  36. package/dist/features/magic-context/mural/compress-cues.d.ts.map +1 -1
  37. package/dist/features/magic-context/mural/cue-validation.d.ts +7 -7
  38. package/dist/features/magic-context/mural/cue-validation.d.ts.map +1 -1
  39. package/dist/features/magic-context/mural/storage-mural-cues.d.ts +13 -3
  40. package/dist/features/magic-context/mural/storage-mural-cues.d.ts.map +1 -1
  41. package/dist/features/magic-context/storage-db.d.ts +24 -5
  42. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  43. package/dist/features/magic-context/storage-meta-persisted.d.ts +1 -0
  44. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  45. package/dist/features/magic-context/storage.d.ts +1 -1
  46. package/dist/features/magic-context/storage.d.ts.map +1 -1
  47. package/dist/hooks/magic-context/command-handler.d.ts +1 -0
  48. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  49. package/dist/hooks/magic-context/event-handler.d.ts +1 -0
  50. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  51. package/dist/hooks/magic-context/execute-status.d.ts +5 -1
  52. package/dist/hooks/magic-context/execute-status.d.ts.map +1 -1
  53. package/dist/hooks/magic-context/hook.d.ts +3 -5
  54. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  55. package/dist/hooks/magic-context/inject-compartments.d.ts +1 -1
  56. package/dist/hooks/magic-context/live-session-state.d.ts +3 -0
  57. package/dist/hooks/magic-context/live-session-state.d.ts.map +1 -1
  58. package/dist/hooks/magic-context/rust-mode-transform.d.ts +16 -0
  59. package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
  60. package/dist/hooks/magic-context/transform-context-state.d.ts +1 -0
  61. package/dist/hooks/magic-context/transform-context-state.d.ts.map +1 -1
  62. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +1 -1
  63. package/dist/hooks/magic-context/transform.d.ts +3 -2
  64. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  65. package/dist/{index-56yjaeb2.js → index-2y0ybv5x.js} +11 -1
  66. package/dist/{index-s0mdn690.js → index-z70pxrq1.js} +232 -49
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +599 -131
  69. package/dist/plugin/dream-timer.d.ts +4 -2
  70. package/dist/plugin/dream-timer.d.ts.map +1 -1
  71. package/dist/plugin/hooks/create-session-hooks.d.ts +1 -1
  72. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  73. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  74. package/dist/{recomp-orchestrator-8e82vmvb.js → recomp-orchestrator-6cd3nrt7.js} +1 -1
  75. package/dist/shared/rpc-types.d.ts +5 -0
  76. package/dist/shared/rpc-types.d.ts.map +1 -1
  77. package/dist/shared/rpc-utils.d.ts +19 -0
  78. package/dist/shared/rpc-utils.d.ts.map +1 -1
  79. package/dist/shared/tui-runtime-specifiers.d.ts +20 -0
  80. package/dist/shared/tui-runtime-specifiers.d.ts.map +1 -0
  81. package/dist/{task-config-5nx3qfz8.js → task-config-gbp7r23g.js} +1 -1
  82. package/package.json +2 -2
  83. package/src/config/index.test.ts +32 -0
  84. package/src/config/migrate-experimental.test.ts +41 -0
  85. package/src/config/migrate-experimental.ts +36 -10
  86. package/src/config/project-security.test.ts +22 -0
  87. package/src/config/project-security.ts +23 -0
  88. package/src/config/schema/magic-context.test.ts +2 -1
  89. package/src/config/schema/magic-context.ts +12 -23
  90. package/src/features/magic-context/dreamer/task-registry.ts +47 -0
  91. package/src/shared/rpc-types.ts +9 -0
  92. package/src/shared/rpc-utils.test.ts +157 -0
  93. package/src/shared/rpc-utils.ts +137 -0
  94. package/src/shared/tui-runtime-specifiers.ts +32 -0
  95. package/src/tui/slots/sidebar-content.tsx +50 -8
  96. package/src/tui/tui-compiled-runtime-imports.test.ts +151 -0
  97. package/src/tui-compiled/slots/sidebar-content.tsx +234 -145
package/dist/index.js CHANGED
@@ -2,11 +2,13 @@ import {
2
2
  CANONICAL_DREAM_TASKS,
3
3
  buildDreamTaskRuntimeConfigs,
4
4
  compareTaskOrder,
5
+ formatDreamTaskBacklogs,
5
6
  isCanonicalDreamTask,
6
7
  leaseKeyFor,
7
8
  leaseKindFor,
9
+ processedDreamTaskItems,
8
10
  userMemoryCollectionEnabled
9
- } from "./index-56yjaeb2.js";
11
+ } from "./index-2y0ybv5x.js";
10
12
  import {
11
13
  AGE_RECLAIM_MIN_TOKENS,
12
14
  AdmissionClass,
@@ -136,6 +138,7 @@ import {
136
138
  getDroppedTagsBySession,
137
139
  getEmbeddingCoverageStatus,
138
140
  getEmergencyInputSample,
141
+ getEmergencyRecoveryArmedAt,
139
142
  getHistorianFailureState,
140
143
  getLastCompartmentEndMessage,
141
144
  getLastCompartmentEndMessageId,
@@ -206,6 +209,7 @@ import {
206
209
  hasLengthCappedOutput,
207
210
  hasMemoryClassifiedAtColumn,
208
211
  hasMemoryShareableColumn,
212
+ hasMuralCueColumns,
209
213
  hasRunnableCompartmentWindow,
210
214
  injectM0M1,
211
215
  injectTemporalMarkers,
@@ -276,6 +280,7 @@ import {
276
280
  recordHighPressureNoEligibleHead,
277
281
  recordMemoryMapping,
278
282
  recordMemoryVerifications,
283
+ recordMuralCueRejection,
279
284
  recordOverflowDetected,
280
285
  recordSessionProjectIdentity,
281
286
  recordToolDefinition,
@@ -369,7 +374,7 @@ import {
369
374
  upsertCommits,
370
375
  verificationFileExists,
371
376
  withContentLanguageDirective
372
- } from "./index-s0mdn690.js";
377
+ } from "./index-z70pxrq1.js";
373
378
  import {
374
379
  deriveHistorianChunkTokens,
375
380
  deriveTriggerBudget,
@@ -9165,9 +9170,10 @@ function migrateLegacyExperimental(rawConfig, warnings) {
9165
9170
  const exp = experimental;
9166
9171
  const hasUM = "user_memories" in exp;
9167
9172
  const hasPKF = "pin_key_files" in exp;
9173
+ const hasMural = "mural" in exp;
9168
9174
  const TOP_LEVEL_GRADUATED = ["temporal_awareness", "caveman_text_compression"];
9169
9175
  const MEMORY_GRADUATED = ["auto_search", "git_commit_indexing"];
9170
- const hasGraduated = TOP_LEVEL_GRADUATED.some((k) => (k in exp)) || MEMORY_GRADUATED.some((k) => (k in exp));
9176
+ const hasGraduated = TOP_LEVEL_GRADUATED.some((k) => (k in exp)) || MEMORY_GRADUATED.some((k) => (k in exp)) || hasMural;
9171
9177
  if (!hasUM && !hasPKF && !hasGraduated) {
9172
9178
  return rawConfig;
9173
9179
  }
@@ -9175,6 +9181,15 @@ function migrateLegacyExperimental(rawConfig, warnings) {
9175
9181
  const dreamer = typeof patched.dreamer === "object" && patched.dreamer !== null ? { ...patched.dreamer } : {};
9176
9182
  const memory = typeof patched.memory === "object" && patched.memory !== null ? { ...patched.memory } : {};
9177
9183
  const newExperimental = { ...exp };
9184
+ const coerceToObject = (value) => {
9185
+ if (typeof value === "boolean") {
9186
+ return { enabled: value };
9187
+ }
9188
+ if (typeof value === "object" && value !== null) {
9189
+ return { ...value };
9190
+ }
9191
+ return;
9192
+ };
9178
9193
  const relocate = (key, dest, destLabel) => {
9179
9194
  if (!(key in exp))
9180
9195
  return;
@@ -9195,15 +9210,20 @@ function migrateLegacyExperimental(rawConfig, warnings) {
9195
9210
  relocate(key, patched, "");
9196
9211
  for (const key of MEMORY_GRADUATED)
9197
9212
  relocate(key, memory, "memory.");
9198
- const coerceToObject = (value) => {
9199
- if (typeof value === "boolean") {
9200
- return { enabled: value };
9201
- }
9202
- if (typeof value === "object" && value !== null) {
9203
- return { ...value };
9213
+ if (hasMural) {
9214
+ const oldMural = coerceToObject(exp.mural);
9215
+ const existingMural = patched.mural;
9216
+ if (existingMural === undefined) {
9217
+ patched.mural = oldMural ?? exp.mural;
9218
+ warnings.push('Deprecated "experimental.mural"; use top-level "mural" instead (migrated in memory; run `doctor` to persist).');
9219
+ } else if (oldMural !== undefined && typeof existingMural === "object" && existingMural !== null && !Array.isArray(existingMural)) {
9220
+ patched.mural = {
9221
+ ...oldMural,
9222
+ ...existingMural
9223
+ };
9204
9224
  }
9205
- return;
9206
- };
9225
+ delete newExperimental.mural;
9226
+ }
9207
9227
  if (hasUM) {
9208
9228
  const oldUM = coerceToObject(exp.user_memories);
9209
9229
  if (oldUM !== undefined) {
@@ -9419,6 +9439,17 @@ function stripUnsafeProjectConfigFields(projectRaw) {
9419
9439
  warnings.push(`Ignoring historian.${removed.join("/")} from project config ` + "(security: historian model selection is user-level only; a repository cannot force extra compaction cost).");
9420
9440
  }
9421
9441
  }
9442
+ const mural = projectRaw.mural;
9443
+ if (isPlainObject(mural) && "model" in mural) {
9444
+ delete mural.model;
9445
+ warnings.push("Ignoring mural.model from project config (security: the mural cue-compressor model is a user-level setting; a repository cannot choose where project memory is sent).");
9446
+ }
9447
+ const experimental = projectRaw.experimental;
9448
+ const legacyMural = isPlainObject(experimental) ? experimental.mural : undefined;
9449
+ if (isPlainObject(legacyMural) && "model" in legacyMural) {
9450
+ delete legacyMural.model;
9451
+ warnings.push("Ignoring experimental.mural.model from project config (security: the mural cue-compressor model is a user-level setting; use user-level mural.model).");
9452
+ }
9422
9453
  for (const agentKey of HIDDEN_AGENT_KEYS) {
9423
9454
  const block = projectRaw[agentKey];
9424
9455
  if (!isPlainObject(block))
@@ -10440,6 +10471,15 @@ function formatFailClosedBlockingProcesses(processes) {
10440
10471
  }
10441
10472
  function formatFailClosedBlockingMessage(reason) {
10442
10473
  if (reason.kind === "migration_guard") {
10474
+ if (reason.unreadableFile) {
10475
+ const arm = reason.unreadableArm ?? "io";
10476
+ const recovery = arm === "io" ? `If no OpenCode server is running, it is safe to delete ${reason.unreadableFile} and retry.` : `The file may be a recent incomplete write; retry after the file is older than the ten-minute grace window, or stop OpenCode before deleting it.`;
10477
+ return [
10478
+ `Magic Context cannot migrate the shared database because RPC discovery file ${reason.unreadableFile} is uncertain (${arm} arm), so the absence of a live OpenCode server cannot be proven.`,
10479
+ recovery,
10480
+ `Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
10481
+ ].join(" ");
10482
+ }
10443
10483
  return [
10444
10484
  `Magic Context cannot migrate the shared database because ${formatFailClosedBlockingProcesses(reason.blockingProcesses)} may be running an older Magic Context build that would fail against the migrated database.`,
10445
10485
  "Restart the blocking process (it will pick up the new build and migrate on start), or shut it down and retry.",
@@ -10448,7 +10488,7 @@ function formatFailClosedBlockingMessage(reason) {
10448
10488
  }
10449
10489
  if (reason.kind === "schema_fence") {
10450
10490
  return [
10451
- `Magic Context cannot operate: this Magic Context build is older than the database; upgrade/restart this harness (database schema v${reason.persistedVersion}, build supports through v${reason.supportedVersion}).`,
10491
+ `Magic Context cannot operate: this Magic Context build is older than the database; upgrade/restart this harness (upstream migration lane v${reason.persistedVersion}, build supports through v${reason.supportedVersion}).`,
10452
10492
  `Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
10453
10493
  ].join(" ");
10454
10494
  }
@@ -11786,6 +11826,7 @@ function createLiveSessionState() {
11786
11826
  deferredMaterializationSessions: new Set,
11787
11827
  sessionDirectoryBySession: new Map,
11788
11828
  recompProgressBySession: new Map,
11829
+ dreamerProgressByProject: new Map,
11789
11830
  internalChildSessions: new Set
11790
11831
  };
11791
11832
  }
@@ -12817,6 +12858,7 @@ function seedTaskScheduleState(db, projectPath, task, nextDueAt, lastRunAt, sche
12817
12858
  db.prepare("INSERT INTO task_schedule_state (project_path, task, last_run_at, next_due_at, schedule, last_status, last_error, retry_count) VALUES (?, ?, ?, ?, ?, NULL, NULL, 0) ON CONFLICT(project_path, task) DO NOTHING").run(projectPath, task, lastRunAt, nextDueAt, schedule);
12818
12859
  }
12819
12860
  function writeTaskScheduleState(db, row) {
12861
+ const broadCycleUpdate = row.lastBroadRunAt === undefined ? "last_broad_run_at = task_schedule_state.last_broad_run_at" : "last_broad_run_at = excluded.last_broad_run_at";
12820
12862
  db.prepare(`INSERT INTO task_schedule_state
12821
12863
  (project_path, task, last_run_at, next_due_at, schedule, last_status, last_error, retry_count, last_checked_commit, last_broad_run_at, retrospective_watermark_ms)
12822
12864
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
@@ -12828,7 +12870,7 @@ function writeTaskScheduleState(db, row) {
12828
12870
  last_error = excluded.last_error,
12829
12871
  retry_count = excluded.retry_count,
12830
12872
  last_checked_commit = COALESCE(excluded.last_checked_commit, task_schedule_state.last_checked_commit),
12831
- last_broad_run_at = COALESCE(excluded.last_broad_run_at, task_schedule_state.last_broad_run_at),
12873
+ ${broadCycleUpdate},
12832
12874
  retrospective_watermark_ms = COALESCE(excluded.retrospective_watermark_ms, task_schedule_state.retrospective_watermark_ms)`).run(row.projectPath, row.task, row.lastRunAt, row.nextDueAt, row.schedule, row.lastStatus, row.lastError, row.retryCount, row.lastCheckedCommit ?? null, row.lastBroadRunAt ?? null, row.retrospectiveWatermarkMs ?? null);
12833
12875
  }
12834
12876
  function isRetrospectiveWindowProcessed(db, projectPath, windowKey) {
@@ -12956,7 +12998,7 @@ function hasBalancedParentheses(cue) {
12956
12998
  }
12957
12999
  return depth === 0;
12958
13000
  }
12959
- function validateCue(cue, importance) {
13001
+ function validateCue(cue, importance, ownId) {
12960
13002
  const trimmed = cue.trim();
12961
13003
  if (trimmed.length === 0)
12962
13004
  return { reason: "empty" };
@@ -12964,8 +13006,9 @@ function validateCue(cue, importance) {
12964
13006
  const length = [...trimmed].length;
12965
13007
  if (length > budget)
12966
13008
  return { reason: `over-budget ${length}>${budget}` };
12967
- if (/#\d+/.test(trimmed))
13009
+ if (ownId !== undefined && new RegExp(`#${ownId}\\b`).test(trimmed)) {
12968
13010
  return { reason: "leaked-id" };
13011
+ }
12969
13012
  if (!hasBalancedParentheses(trimmed))
12970
13013
  return { reason: "unbalanced-parens" };
12971
13014
  const markers = trimmed.split("⊘").length - 1;
@@ -12981,6 +13024,7 @@ function validateCue(cue, importance) {
12981
13024
  // src/features/magic-context/mural/compress-cues.ts
12982
13025
  var COMPRESS_CUES_CHUNK_SIZE = 40;
12983
13026
  var CHUNK_TIMEOUT_FLOOR_MS = 240000;
13027
+ var CUE_REJECTION_LATCH_THRESHOLD = 3;
12984
13028
  var CONSECUTIVE_TIMEOUT_LIMIT = 2;
12985
13029
  function toPromptMemory(candidate) {
12986
13030
  const memory = candidate.memory;
@@ -12991,6 +13035,38 @@ function toPromptMemory(candidate) {
12991
13035
  content: memory.content
12992
13036
  };
12993
13037
  }
13038
+ function stripOwnIdToken(value, ownId) {
13039
+ return value.replace(new RegExp(`#${ownId}\\b`, "g"), "");
13040
+ }
13041
+ function truncateCue(value, budget) {
13042
+ const trimmed = value.trim();
13043
+ const codepoints = [...trimmed];
13044
+ if (codepoints.length <= budget)
13045
+ return trimmed;
13046
+ const prefix = codepoints.slice(0, budget).join("");
13047
+ const boundary = prefix.search(/\s+\S*$/);
13048
+ return (boundary > 0 ? prefix.slice(0, boundary) : prefix).trim();
13049
+ }
13050
+ function sanitizeCue(value, candidate) {
13051
+ return truncateCue(stripOwnIdToken(value, candidate.memory.id), cueBudgetFor(candidate.memory.importance ?? 50));
13052
+ }
13053
+ function deterministicFallbackCue(candidate, lastCandidate) {
13054
+ const importance = candidate.memory.importance ?? 50;
13055
+ const budget = cueBudgetFor(importance);
13056
+ const sanitizedCandidate = sanitizeCue(lastCandidate, candidate);
13057
+ if (validateCue(sanitizedCandidate, importance, candidate.memory.id) === null) {
13058
+ return sanitizedCandidate;
13059
+ }
13060
+ const sourceSlice = sanitizeCue(candidate.memory.content, candidate);
13061
+ if (validateCue(sourceSlice, importance, candidate.memory.id) === null) {
13062
+ return sourceSlice;
13063
+ }
13064
+ const grammarSafe = truncateCue(sourceSlice.replaceAll("⊘", "").replace(/[()]/g, "").replace(/\b(?:must not|never|without|instead of|exclude|excludes)\b/gi, "").replace(/\s+/g, " "), budget);
13065
+ if (validateCue(grammarSafe, importance, candidate.memory.id) === null) {
13066
+ return grammarSafe;
13067
+ }
13068
+ return "memory";
13069
+ }
12994
13070
  function selectCandidates(db, projectIdentity) {
12995
13071
  const memories = getMemoriesByProject(db, projectIdentity, ["active", "permanent"]);
12996
13072
  const cueState = getMuralCueState(db, memories.map((memory) => memory.id));
@@ -13044,6 +13120,7 @@ async function runCompressCues(args) {
13044
13120
  result.skipped += outcome.skipped;
13045
13121
  result.remaining -= outcome.compressed;
13046
13122
  result.chunks += 1;
13123
+ args.onProgress?.(result.compressed + result.skipped);
13047
13124
  if (outcome.failure?.class === "timeout") {
13048
13125
  consecutiveTimeouts += 1;
13049
13126
  timeoutStreakElapsedMs.push(outcome.failure.elapsedMs);
@@ -13158,10 +13235,19 @@ function applyCues(args, chunk, manifestText) {
13158
13235
  const candidate = byId.get(entry.id);
13159
13236
  if (!candidate)
13160
13237
  throw new Error(`cues manifest contains unknown id ${entry.id}`);
13161
- const failure = validateCue(entry.cue, candidate.memory.importance ?? 50);
13238
+ const importance = candidate.memory.importance ?? 50;
13239
+ const failure = validateCue(entry.cue, importance, candidate.memory.id);
13162
13240
  if (failure) {
13241
+ const rejectionCount = recordMuralCueRejection(args.db, args.projectIdentity, entry.id, candidate.contentHash);
13242
+ if (rejectionCount >= CUE_REJECTION_LATCH_THRESHOLD) {
13243
+ const fallback = deterministicFallbackCue(candidate, entry.cue);
13244
+ setMuralCue(args.db, args.projectIdentity, entry.id, fallback, candidate.contentHash);
13245
+ compressed += 1;
13246
+ log(`[dreamer] compress-cues: fallback cue for memory ${entry.id} (${failure.reason}; ${rejectionCount} rejections; fallback)`);
13247
+ continue;
13248
+ }
13163
13249
  skipped += 1;
13164
- log(`[dreamer] compress-cues: skipped cue for memory ${entry.id} (${failure.reason})`);
13250
+ log(`[dreamer] compress-cues: skipped cue for memory ${entry.id} (${failure.reason}; rejection ${rejectionCount}/${CUE_REJECTION_LATCH_THRESHOLD})`);
13165
13251
  continue;
13166
13252
  }
13167
13253
  setMuralCue(args.db, args.projectIdentity, entry.id, entry.cue.trim(), candidate.contentHash);
@@ -13852,6 +13938,7 @@ async function runClassify(args) {
13852
13938
  result.changed += counts.changed;
13853
13939
  result.remaining -= counts.classified;
13854
13940
  result.chunks += 1;
13941
+ args.onProgress?.(result.classified);
13855
13942
  }
13856
13943
  result.complete = result.remaining === 0;
13857
13944
  log(`[dreamer] classify: stage=${stage} classified=${result.classified} changed=${result.changed} chunks=${result.chunks} remaining=${result.remaining} complete=${result.complete}`);
@@ -16005,6 +16092,7 @@ async function mapMemories(args) {
16005
16092
  result.independent += counts.independent;
16006
16093
  result.remaining -= counts.mapped + counts.independent;
16007
16094
  result.batches += 1;
16095
+ args.onProgress?.(result.mapped + result.independent);
16008
16096
  }
16009
16097
  result.complete = result.remaining === 0;
16010
16098
  log(`[dreamer] map-memories: mapped=${result.mapped} independent=${result.independent} batches=${result.batches} remaining=${result.remaining} complete=${result.complete}`);
@@ -16606,6 +16694,7 @@ async function refreshPrimers(args) {
16606
16694
  result.refreshed += 1;
16607
16695
  else
16608
16696
  result.skipped += 1;
16697
+ args.onProgress?.(result.refreshed + result.skipped);
16609
16698
  }
16610
16699
  log(`[dreamer] refresh-primers: refreshed=${result.refreshed} skipped=${result.skipped}`);
16611
16700
  return result;
@@ -16875,6 +16964,206 @@ function insertDreamRun(db, run) {
16875
16964
  getInsertDreamRunStatement(db).run(run.projectPath, run.startedAt, run.finishedAt, run.holderId, JSON.stringify(run.tasks), run.tasksSucceeded, run.tasksFailed, run.smartNotesSurfaced, run.smartNotesPending, run.memoryChanges ? JSON.stringify(run.memoryChanges) : null, run.parentSessionId ?? null);
16876
16965
  }
16877
16966
 
16967
+ // src/features/magic-context/dreamer/task-gates.ts
16968
+ function countActiveMemories(db, projectPath) {
16969
+ const row = db.prepare("SELECT COUNT(*) AS cnt FROM memories WHERE project_path = ? AND status IN ('active','permanent')").get(projectPath);
16970
+ return row?.cnt ?? 0;
16971
+ }
16972
+ function countUnmappedActiveMemories(db, projectPath) {
16973
+ const row = db.prepare(`SELECT COUNT(*) AS cnt
16974
+ FROM memories m
16975
+ WHERE m.project_path = ?
16976
+ AND m.status IN ('active','permanent')
16977
+ AND NOT EXISTS (
16978
+ SELECT 1 FROM memory_verifications v WHERE v.memory_id = m.id
16979
+ )`).get(projectPath);
16980
+ return row?.cnt ?? 0;
16981
+ }
16982
+ function countCompartmentsSince(db, projectPath, since) {
16983
+ const row = db.prepare(`SELECT COUNT(*) AS cnt
16984
+ FROM compartments c
16985
+ JOIN session_projects sp ON sp.session_id = c.session_id
16986
+ WHERE sp.project_path = ? AND c.created_at > ?`).get(projectPath, since);
16987
+ return row?.cnt ?? 0;
16988
+ }
16989
+ function countProjectSessionsSince(db, projectPath, since) {
16990
+ const row = since === null ? db.prepare("SELECT COUNT(*) AS cnt FROM session_projects WHERE project_path = ?").get(projectPath) : db.prepare("SELECT COUNT(*) AS cnt FROM session_projects WHERE project_path = ? AND updated_at > ?").get(projectPath, since);
16991
+ return row?.cnt ?? 0;
16992
+ }
16993
+ function countMappedMemories(db, projectPath) {
16994
+ const row = db.prepare(`SELECT COUNT(DISTINCT m.id) AS cnt
16995
+ FROM memories m
16996
+ JOIN memory_verifications v ON v.memory_id = m.id
16997
+ WHERE m.project_path = ?
16998
+ AND m.status IN ('active','permanent')
16999
+ AND v.file_path <> ''`).get(projectPath);
17000
+ return row?.cnt ?? 0;
17001
+ }
17002
+ function countUnverifiedMappedMemories(db, projectPath) {
17003
+ const row = db.prepare(`SELECT COUNT(DISTINCT m.id) AS cnt
17004
+ FROM memories m
17005
+ JOIN memory_verifications v ON v.memory_id = m.id
17006
+ WHERE m.project_path = ?
17007
+ AND m.status IN ('active','permanent')
17008
+ AND v.file_path <> ''
17009
+ AND v.verified_at = 0`).get(projectPath);
17010
+ return row?.cnt ?? 0;
17011
+ }
17012
+ function countBroadCycleCandidates(db, projectPath, cycleStartAt) {
17013
+ const row = db.prepare(`SELECT COUNT(*) AS cnt
17014
+ FROM memories m
17015
+ WHERE m.project_path = ?
17016
+ AND m.status IN ('active','permanent')
17017
+ AND (
17018
+ SELECT MAX(v.verified_at)
17019
+ FROM memory_verifications v
17020
+ WHERE v.memory_id = m.id
17021
+ AND v.file_path <> ''
17022
+ ) < ?`).get(projectPath, cycleStartAt);
17023
+ return row?.cnt ?? 0;
17024
+ }
17025
+ function countCueCandidates(db, projectPath) {
17026
+ if (!hasMuralCueColumns(db))
17027
+ return countActiveMemories(db, projectPath);
17028
+ const row = db.prepare(`SELECT COUNT(*) AS cnt
17029
+ FROM memories
17030
+ WHERE project_path = ?
17031
+ AND status IN ('active','permanent')
17032
+ AND (mural_cue IS NULL OR mural_cue_hash IS NULL OR updated_at > mural_cue_at)`).get(projectPath);
17033
+ return row?.cnt ?? 0;
17034
+ }
17035
+ function countStalePrimers(db, projectPath) {
17036
+ const row = db.prepare(`SELECT COUNT(*) AS cnt
17037
+ FROM primers
17038
+ WHERE project_path = ?
17039
+ AND status = 'active'
17040
+ AND (answer IS NULL OR TRIM(answer) = '' OR answer_refreshed_at IS NULL
17041
+ OR last_observed_at > answer_refreshed_at)`).get(projectPath);
17042
+ return row?.cnt ?? 0;
17043
+ }
17044
+ function countUnclassifiedActiveMemories(db, projectPath) {
17045
+ if (!hasMemoryClassifiedAtColumn(db))
17046
+ return countActiveMemories(db, projectPath);
17047
+ const row = db.prepare(`SELECT COUNT(*) AS cnt
17048
+ FROM memories
17049
+ WHERE project_path = ?
17050
+ AND status IN ('active','permanent')
17051
+ AND classified_at IS NULL`).get(projectPath);
17052
+ return row?.cnt ?? 0;
17053
+ }
17054
+ function countPendingSmartNotes(db, projectPath) {
17055
+ const row = db.prepare("SELECT COUNT(*) AS cnt FROM notes WHERE project_path = ? AND type = 'smart' AND status = 'pending'").get(projectPath);
17056
+ return row?.cnt ?? 0;
17057
+ }
17058
+ function countUserMemoryCandidates(db) {
17059
+ const row = db.prepare("SELECT COUNT(*) AS cnt FROM user_memory_candidates").get();
17060
+ return row?.cnt ?? 0;
17061
+ }
17062
+ function countActivePrimers(db, projectPath) {
17063
+ const row = db.prepare("SELECT COUNT(*) AS cnt FROM primers WHERE project_path = ? AND status = 'active'").get(projectPath);
17064
+ return row?.cnt ?? 0;
17065
+ }
17066
+ function getDreamTaskBacklog(db, projectPath, task, options = {}) {
17067
+ switch (task) {
17068
+ case "map-memories": {
17069
+ const total = countActiveMemories(db, projectPath);
17070
+ return { pending: countUnmappedActiveMemories(db, projectPath), total };
17071
+ }
17072
+ case "verify": {
17073
+ return {
17074
+ pending: countUnverifiedMappedMemories(db, projectPath),
17075
+ total: countMappedMemories(db, projectPath)
17076
+ };
17077
+ }
17078
+ case "verify-broad": {
17079
+ const total = countMappedMemories(db, projectPath);
17080
+ const cycleStartAt = getTaskScheduleState(db, projectPath, "verify-broad")?.lastBroadRunAt;
17081
+ const pending = cycleStartAt == null ? total : countBroadCycleCandidates(db, projectPath, cycleStartAt);
17082
+ return { pending, total };
17083
+ }
17084
+ case "curate": {
17085
+ const total = countActiveMemories(db, projectPath);
17086
+ return { pending: total, total };
17087
+ }
17088
+ case "compress-cues": {
17089
+ const total = countActiveMemories(db, projectPath);
17090
+ return { pending: countCueCandidates(db, projectPath), total };
17091
+ }
17092
+ case "classify-memories": {
17093
+ const total = countActiveMemories(db, projectPath);
17094
+ return { pending: countUnclassifiedActiveMemories(db, projectPath), total };
17095
+ }
17096
+ case "retrospective": {
17097
+ const pending = countProjectSessionsSince(db, projectPath, options.retrospectiveWatermarkMs ?? null);
17098
+ return { pending, total: pending };
17099
+ }
17100
+ case "maintain-docs": {
17101
+ const total = countCompartmentsSince(db, projectPath, 0);
17102
+ const pending = countCompartmentsSince(db, projectPath, options.lastRunAt ?? 0);
17103
+ return { pending, total };
17104
+ }
17105
+ case "evaluate-smart-notes": {
17106
+ const pending = countPendingSmartNotes(db, projectPath);
17107
+ return { pending, total: pending };
17108
+ }
17109
+ case "review-user-memories": {
17110
+ const pending = countUserMemoryCandidates(db);
17111
+ return { pending, total: pending };
17112
+ }
17113
+ case "promote-primers": {
17114
+ const pending = countPrimerCandidatesForProject(db, projectPath);
17115
+ return { pending, total: pending };
17116
+ }
17117
+ case "refresh-primers": {
17118
+ const total = countActivePrimers(db, projectPath);
17119
+ return { pending: countStalePrimers(db, projectPath), total };
17120
+ }
17121
+ default: {
17122
+ const _exhaustive = task;
17123
+ return _exhaustive;
17124
+ }
17125
+ }
17126
+ }
17127
+ function getDreamTaskBacklogs(db, projectPath, tasks = CANONICAL_DREAM_TASKS, options = {}) {
17128
+ const result = {};
17129
+ for (const task of tasks)
17130
+ result[task] = getDreamTaskBacklog(db, projectPath, task, options);
17131
+ return result;
17132
+ }
17133
+ function evaluateTaskGate(task, ctx) {
17134
+ const { db, projectIdentity: project, lastRunAt } = ctx;
17135
+ switch (task) {
17136
+ case "map-memories":
17137
+ return countUnmappedActiveMemories(db, project) > 0;
17138
+ case "verify":
17139
+ return countActiveMemories(db, project) > 0;
17140
+ case "verify-broad":
17141
+ return getTaskScheduleState(db, project, "verify-broad")?.lastBroadRunAt != null || countActiveMemories(db, project) > 0;
17142
+ case "curate":
17143
+ return countActiveMemories(db, project) > 0;
17144
+ case "compress-cues":
17145
+ return countActiveMemories(db, project) > 0;
17146
+ case "classify-memories":
17147
+ return countActiveMemories(db, project) > 0;
17148
+ case "retrospective":
17149
+ return countProjectSessionsSince(db, project, ctx.retrospectiveWatermarkMs ?? null) > 0;
17150
+ case "maintain-docs":
17151
+ return countCompartmentsSince(db, project, lastRunAt ?? 0) > 0;
17152
+ case "evaluate-smart-notes":
17153
+ return getSmartNotesNeedingCompilation(db, project, Date.now(), 1).length > 0 || getStaleCompiledSmartNotes(db, project, Date.now(), 1).length > 0 || getPendingSmartNotes(db, project).some((note) => note.checkStatus === "fallback");
17154
+ case "review-user-memories":
17155
+ return getUserMemoryCandidates(db).length >= ctx.promotionThreshold;
17156
+ case "promote-primers":
17157
+ return countPrimerCandidatesForProject(db, project) >= (ctx.promotionThreshold ?? 2);
17158
+ case "refresh-primers":
17159
+ return getActivePrimers(db, project).some((primer) => !primer.answer.trim() || primer.answerRefreshedAt == null || (primer.lastObservedAt ?? 0) > primer.answerRefreshedAt);
17160
+ default: {
17161
+ const _exhaustive = task;
17162
+ return Boolean(_exhaustive);
17163
+ }
17164
+ }
17165
+ }
17166
+
16878
17167
  // src/features/magic-context/dreamer/verify.ts
16879
17168
  import { createHash as createHash5 } from "node:crypto";
16880
17169
 
@@ -16883,6 +17172,30 @@ import path4 from "node:path";
16883
17172
  function minOf(values) {
16884
17173
  return values.reduce((acc, v) => v < acc ? v : acc, Number.POSITIVE_INFINITY);
16885
17174
  }
17175
+ function ensureBroadCycleStart(args) {
17176
+ const current = getTaskScheduleState(args.db, args.projectIdentity, "verify-broad");
17177
+ if (current?.lastBroadRunAt != null && current.lastBroadRunAt > 0) {
17178
+ return current.lastBroadRunAt;
17179
+ }
17180
+ if (!current)
17181
+ return args.runStartedAt;
17182
+ if (!args.holderId || !args.leaseKey) {
17183
+ throw new Error("verify-broad cycle opening requires the task lease");
17184
+ }
17185
+ return runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
17186
+ const latest = getTaskScheduleState(args.db, args.projectIdentity, "verify-broad");
17187
+ if (!latest)
17188
+ return args.runStartedAt;
17189
+ if (latest.lastBroadRunAt != null && latest.lastBroadRunAt > 0) {
17190
+ return latest.lastBroadRunAt;
17191
+ }
17192
+ writeTaskScheduleState(args.db, {
17193
+ ...latest,
17194
+ lastBroadRunAt: args.runStartedAt
17195
+ });
17196
+ return args.runStartedAt;
17197
+ });
17198
+ }
16886
17199
  async function partitionVerifyScope(args) {
16887
17200
  const runStartedAt = args.now ?? Date.now();
16888
17201
  const active = getMemoriesByProject(args.db, args.projectIdentity);
@@ -16894,6 +17207,29 @@ async function partitionVerifyScope(args) {
16894
17207
  content: m.content,
16895
17208
  mappedFiles: verById.get(m.id)?.files ?? []
16896
17209
  });
17210
+ if (args.forceBroad) {
17211
+ const broadCycleStartAt = ensureBroadCycleStart({
17212
+ db: args.db,
17213
+ projectIdentity: args.projectIdentity,
17214
+ holderId: args.holderId,
17215
+ leaseKey: args.leaseKey,
17216
+ runStartedAt
17217
+ });
17218
+ const broadCandidates = candidates.filter((m) => (verById.get(m.id)?.verifiedAt ?? 0) < broadCycleStartAt).sort((a, b) => {
17219
+ const verifiedAtA = verById.get(a.id)?.verifiedAt ?? 0;
17220
+ const verifiedAtB = verById.get(b.id)?.verifiedAt ?? 0;
17221
+ return verifiedAtA - verifiedAtB || a.id - b.id;
17222
+ });
17223
+ return {
17224
+ runStartedAt,
17225
+ mode: "broad",
17226
+ inScope: broadCandidates.map(toPrompt),
17227
+ inScopeIds: broadCandidates.map((m) => m.id),
17228
+ skippedIds: candidates.filter((m) => !broadCandidates.some((candidate) => candidate.id === m.id)).map((m) => m.id),
17229
+ broadCycleStartAt,
17230
+ reason: `broad cycle (${broadCandidates.length} remain; started ${broadCycleStartAt})`
17231
+ };
17232
+ }
16897
17233
  if (candidates.length === 0) {
16898
17234
  return {
16899
17235
  runStartedAt,
@@ -16904,16 +17240,6 @@ async function partitionVerifyScope(args) {
16904
17240
  reason: "no file-mapped memories in scope"
16905
17241
  };
16906
17242
  }
16907
- if (args.forceBroad) {
16908
- return {
16909
- runStartedAt,
16910
- mode: "broad",
16911
- inScope: candidates.map(toPrompt),
16912
- inScopeIds: candidates.map((m) => m.id),
16913
- skippedIds: [],
16914
- reason: "broad full-pool verification of file-mapped memories"
16915
- };
16916
- }
16917
17243
  const allInScope = (mode, reason) => ({
16918
17244
  runStartedAt,
16919
17245
  mode,
@@ -17032,7 +17358,25 @@ function parseVerifyManifest(text) {
17032
17358
 
17033
17359
  // src/features/magic-context/dreamer/verify.ts
17034
17360
  var VERIFY_BATCH_SIZE = 50;
17361
+ function closeBroadCycle(args, cycleStartAt) {
17362
+ if (!args.forceBroad || cycleStartAt === undefined)
17363
+ return;
17364
+ if (!getTaskScheduleState(args.db, args.projectIdentity, "verify-broad"))
17365
+ return;
17366
+ runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
17367
+ const current = getTaskScheduleState(args.db, args.projectIdentity, "verify-broad");
17368
+ if (current?.lastBroadRunAt !== cycleStartAt)
17369
+ return;
17370
+ if (!current)
17371
+ return;
17372
+ writeTaskScheduleState(args.db, {
17373
+ ...current,
17374
+ lastBroadRunAt: null
17375
+ });
17376
+ });
17377
+ }
17035
17378
  async function runVerify(args) {
17379
+ const runStartedAt = Date.now();
17036
17380
  const result = {
17037
17381
  verified: 0,
17038
17382
  updated: 0,
@@ -17047,14 +17391,20 @@ async function runVerify(args) {
17047
17391
  db: args.db,
17048
17392
  projectIdentity: args.projectIdentity,
17049
17393
  projectDirectory: args.sessionDirectory,
17050
- forceBroad: args.forceBroad
17394
+ forceBroad: args.forceBroad,
17395
+ now: runStartedAt,
17396
+ holderId: args.holderId,
17397
+ leaseKey: args.leaseKey
17051
17398
  });
17052
17399
  result.mode = gate.mode;
17400
+ result.broadCycleStartAt = gate.broadCycleStartAt;
17053
17401
  result.inScope = gate.inScope.length;
17054
17402
  result.remaining = gate.inScope.length;
17055
17403
  log(`[dreamer] ${args.forceBroad ? "verify-broad" : "verify"} gate: mode=${gate.mode} in_scope=${gate.inScope.length} skipped=${gate.skippedIds.length} reason=${gate.reason}`);
17056
- if (gate.inScope.length === 0)
17404
+ if (gate.inScope.length === 0) {
17405
+ closeBroadCycle(args, gate.broadCycleStartAt);
17057
17406
  return result;
17407
+ }
17058
17408
  const batches = [];
17059
17409
  for (let i = 0;i < gate.inScope.length; i += VERIFY_BATCH_SIZE) {
17060
17410
  batches.push(gate.inScope.slice(i, i + VERIFY_BATCH_SIZE));
@@ -17074,8 +17424,11 @@ async function runVerify(args) {
17074
17424
  result.archived += counts.archived;
17075
17425
  result.remaining -= counts.verified + counts.updated + counts.archived;
17076
17426
  result.batches += 1;
17427
+ args.onProgress?.(result.verified + result.updated + result.archived);
17077
17428
  }
17078
17429
  result.complete = result.remaining === 0;
17430
+ if (result.complete)
17431
+ closeBroadCycle(args, gate.broadCycleStartAt);
17079
17432
  log(`[dreamer] ${args.forceBroad ? "verify-broad" : "verify"}: verified=${result.verified} updated=${result.updated} archived=${result.archived} batches=${result.batches} remaining=${result.remaining} complete=${result.complete}`);
17080
17433
  return result;
17081
17434
  } finally {
@@ -17365,6 +17718,20 @@ function createDreamTaskExecutor(deps) {
17365
17718
  const { db, projectIdentity, holderId, leaseKey } = ctx;
17366
17719
  const startedAt = Date.now();
17367
17720
  const deadline = startedAt + config.timeoutMinutes * 60 * 1000;
17721
+ const backlogAtStart = getDreamTaskBacklog(db, projectIdentity, config.task);
17722
+ const reportProgress = (processed) => {
17723
+ deps.onProgress?.({
17724
+ task: config.task,
17725
+ processed: Math.max(0, processed),
17726
+ total: backlogAtStart.pending,
17727
+ startedAt
17728
+ });
17729
+ };
17730
+ reportProgress(0);
17731
+ const incompleteMessage = (remaining) => {
17732
+ const processed = processedDreamTaskItems(backlogAtStart.pending, remaining);
17733
+ return `${config.task} incomplete: ${remaining} remain (was ${backlogAtStart.pending} at run start; processed ${processed} this run)`;
17734
+ };
17368
17735
  const parent = await resolveParentSessionId();
17369
17736
  let moduleRoute;
17370
17737
  if (config.task === "map-memories" || config.task === "compress-cues" || config.task === "verify" || config.task === "verify-broad" || config.task === "retrospective") {
@@ -17393,7 +17760,19 @@ function createDreamTaskExecutor(deps) {
17393
17760
  name: config.task,
17394
17761
  durationMs: Date.now() - startedAt,
17395
17762
  resultChars: 0,
17396
- ...error ? { error } : {}
17763
+ ...error ? { error } : {},
17764
+ backlog: (() => {
17765
+ const end = extra?.backlogAfter ?? getDreamTaskBacklog(db, projectIdentity, config.task);
17766
+ const processed = processedDreamTaskItems(backlogAtStart.pending, end.pending);
17767
+ const value = {
17768
+ pendingAtStart: backlogAtStart.pending,
17769
+ totalAtStart: backlogAtStart.total,
17770
+ pendingAtEnd: end.pending,
17771
+ totalAtEnd: end.total,
17772
+ processed
17773
+ };
17774
+ return value;
17775
+ })()
17397
17776
  }
17398
17777
  ],
17399
17778
  tasksSucceeded: status === "completed" ? 1 : 0,
@@ -17427,8 +17806,8 @@ function createDreamTaskExecutor(deps) {
17427
17806
  }
17428
17807
  try {
17429
17808
  if (config.task === "compress-cues") {
17430
- if (deps.experimentalMural?.enabled !== true) {
17431
- log("[dreamer] compress-cues: skipped (experimental.mural is not enabled)");
17809
+ if (deps.mural?.enabled !== true) {
17810
+ log("[dreamer] compress-cues: skipped (mural is not enabled)");
17432
17811
  recordRun("completed", null);
17433
17812
  return { status: "completed" };
17434
17813
  }
@@ -17447,12 +17826,13 @@ function createDreamTaskExecutor(deps) {
17447
17826
  holderId,
17448
17827
  leaseKey,
17449
17828
  deadline,
17450
- model: config.model ?? deps.experimentalMural.model ?? deps.dreamerModel,
17451
- fallbackModels: config.fallbackModels
17829
+ model: config.model ?? deps.mural.model ?? deps.dreamerModel,
17830
+ fallbackModels: config.fallbackModels,
17831
+ onProgress: (processed) => reportProgress(processed)
17452
17832
  });
17453
17833
  log(`[dreamer] compress-cues: compressed=${result.compressed} skipped=${result.skipped} chunks=${result.chunks} remaining=${result.remaining}`);
17454
17834
  if (!result.complete) {
17455
- const error = `compress-cues incomplete: ${result.remaining} selected memories remain`;
17835
+ const error = incompleteMessage(result.remaining);
17456
17836
  recordRun("failed", error);
17457
17837
  return { status: "failed", transient: true, error };
17458
17838
  }
@@ -17489,11 +17869,12 @@ function createDreamTaskExecutor(deps) {
17489
17869
  deadline,
17490
17870
  model: config.model,
17491
17871
  fallbackModels: config.fallbackModels,
17492
- moduleRoute
17872
+ moduleRoute,
17873
+ onProgress: (processed) => reportProgress(processed)
17493
17874
  });
17494
17875
  log(`[dreamer] map-memories: mapped=${result.mapped} independent=${result.independent} batches=${result.batches} remaining=${result.remaining}`);
17495
17876
  if (!result.complete) {
17496
- const error = `map-memories incomplete: ${result.remaining} selected memories remain`;
17877
+ const error = incompleteMessage(result.remaining);
17497
17878
  recordRun("failed", error);
17498
17879
  return { status: "failed", transient: true, error };
17499
17880
  }
@@ -17515,19 +17896,32 @@ function createDreamTaskExecutor(deps) {
17515
17896
  model: config.model,
17516
17897
  fallbackModels: config.fallbackModels,
17517
17898
  language: config.language ?? deps.language,
17518
- moduleRoute
17899
+ moduleRoute,
17900
+ onProgress: (processed2) => reportProgress(processed2)
17519
17901
  });
17902
+ const processed = result.verified + result.updated + result.archived;
17903
+ const broadProgress = config.task === "verify-broad" ? `verify-broad cycle ${result.broadCycleStartAt ?? "open"}: verified ${processed}, ${result.remaining} remain` : null;
17904
+ const backlogAfter = config.task === "verify-broad" ? { pending: result.remaining, total: backlogAtStart.total } : undefined;
17520
17905
  if (!result.complete) {
17521
- const error = `${config.task} incomplete: ${result.remaining} selected memories remain`;
17906
+ if (broadProgress && processed > 0) {
17907
+ recordRun("completed", broadProgress, {
17908
+ memoryChanges: computeMemoryDelta(memoryBefore),
17909
+ backlogAfter
17910
+ });
17911
+ return { status: "completed", error: broadProgress };
17912
+ }
17913
+ const error = incompleteMessage(result.remaining);
17522
17914
  recordRun("failed", error, {
17523
- memoryChanges: computeMemoryDelta(memoryBefore)
17915
+ memoryChanges: computeMemoryDelta(memoryBefore),
17916
+ backlogAfter
17524
17917
  });
17525
17918
  return { status: "failed", transient: true, error };
17526
17919
  }
17527
- recordRun("completed", null, {
17528
- memoryChanges: computeMemoryDelta(memoryBefore)
17920
+ recordRun("completed", broadProgress, {
17921
+ memoryChanges: computeMemoryDelta(memoryBefore),
17922
+ backlogAfter
17529
17923
  });
17530
- return { status: "completed" };
17924
+ return broadProgress ? { status: "completed", error: broadProgress } : { status: "completed" };
17531
17925
  }
17532
17926
  if (config.task === "classify-memories") {
17533
17927
  let moduleArgs;
@@ -17577,11 +17971,12 @@ function createDreamTaskExecutor(deps) {
17577
17971
  deadline,
17578
17972
  model: config.model,
17579
17973
  fallbackModels: config.fallbackModels,
17580
- ...moduleArgs
17974
+ ...moduleArgs,
17975
+ onProgress: (processed) => reportProgress(processed)
17581
17976
  });
17582
17977
  log(`[dreamer] classify-memories: stage=${result.stage} classified=${result.classified} changed=${result.changed} chunks=${result.chunks} remaining=${result.remaining}`);
17583
17978
  if (!result.complete) {
17584
- const error = `classify-memories incomplete: ${result.remaining} selected memories remain`;
17979
+ const error = incompleteMessage(result.remaining);
17585
17980
  recordRun("failed", error);
17586
17981
  return { status: "failed", transient: true, error };
17587
17982
  }
@@ -17617,7 +18012,8 @@ function createDreamTaskExecutor(deps) {
17617
18012
  model: config.model,
17618
18013
  fallbackModels: config.fallbackModels,
17619
18014
  language: config.language ?? deps.language,
17620
- rawProviderFactory: deps.primerRawProviderFactory
18015
+ rawProviderFactory: deps.primerRawProviderFactory,
18016
+ onProgress: (processed) => reportProgress(processed)
17621
18017
  });
17622
18018
  recordRun("completed", null);
17623
18019
  log(`[dreamer] refresh-primers: refreshed=${result.refreshed} skipped=${result.skipped}`);
@@ -17671,6 +18067,8 @@ function createDreamTaskExecutor(deps) {
17671
18067
  recordRun("failed", brief);
17672
18068
  log(`[dreamer] task ${config.task} failed (transient=${transient}): ${brief}`);
17673
18069
  return { status: "failed", transient, error: brief };
18070
+ } finally {
18071
+ deps.onProgress?.(null, config.task);
17674
18072
  }
17675
18073
  };
17676
18074
  }
@@ -18046,66 +18444,6 @@ async function runAgenticTask(config, ctx, helpers) {
18046
18444
  }
18047
18445
  }
18048
18446
 
18049
- // src/features/magic-context/dreamer/task-gates.ts
18050
- function countActiveMemories(db, projectPath) {
18051
- const row = db.prepare("SELECT COUNT(*) AS cnt FROM memories WHERE project_path = ? AND status IN ('active','permanent')").get(projectPath);
18052
- return row?.cnt ?? 0;
18053
- }
18054
- function countUnmappedActiveMemories(db, projectPath) {
18055
- const row = db.prepare(`SELECT COUNT(*) AS cnt
18056
- FROM memories m
18057
- WHERE m.project_path = ?
18058
- AND m.status IN ('active','permanent')
18059
- AND NOT EXISTS (
18060
- SELECT 1 FROM memory_verifications v WHERE v.memory_id = m.id
18061
- )`).get(projectPath);
18062
- return row?.cnt ?? 0;
18063
- }
18064
- function countCompartmentsSince(db, projectPath, since) {
18065
- const row = db.prepare(`SELECT COUNT(*) AS cnt
18066
- FROM compartments c
18067
- JOIN session_projects sp ON sp.session_id = c.session_id
18068
- WHERE sp.project_path = ? AND c.created_at > ?`).get(projectPath, since);
18069
- return row?.cnt ?? 0;
18070
- }
18071
- function countProjectSessionsSince(db, projectPath, since) {
18072
- const row = since === null ? db.prepare("SELECT COUNT(*) AS cnt FROM session_projects WHERE project_path = ?").get(projectPath) : db.prepare("SELECT COUNT(*) AS cnt FROM session_projects WHERE project_path = ? AND updated_at > ?").get(projectPath, since);
18073
- return row?.cnt ?? 0;
18074
- }
18075
- function evaluateTaskGate(task, ctx) {
18076
- const { db, projectIdentity: project, lastRunAt } = ctx;
18077
- switch (task) {
18078
- case "map-memories":
18079
- return countUnmappedActiveMemories(db, project) > 0;
18080
- case "verify":
18081
- return countActiveMemories(db, project) > 0;
18082
- case "verify-broad":
18083
- return countActiveMemories(db, project) > 0;
18084
- case "curate":
18085
- return countActiveMemories(db, project) > 0;
18086
- case "compress-cues":
18087
- return countActiveMemories(db, project) > 0;
18088
- case "classify-memories":
18089
- return countActiveMemories(db, project) > 0;
18090
- case "retrospective":
18091
- return countProjectSessionsSince(db, project, ctx.retrospectiveWatermarkMs ?? null) > 0;
18092
- case "maintain-docs":
18093
- return countCompartmentsSince(db, project, lastRunAt ?? 0) > 0;
18094
- case "evaluate-smart-notes":
18095
- return getSmartNotesNeedingCompilation(db, project, Date.now(), 1).length > 0 || getStaleCompiledSmartNotes(db, project, Date.now(), 1).length > 0 || getPendingSmartNotes(db, project).some((note) => note.checkStatus === "fallback");
18096
- case "review-user-memories":
18097
- return getUserMemoryCandidates(db).length >= ctx.promotionThreshold;
18098
- case "promote-primers":
18099
- return countPrimerCandidatesForProject(db, project) >= (ctx.promotionThreshold ?? 2);
18100
- case "refresh-primers":
18101
- return getActivePrimers(db, project).some((primer) => !primer.answer.trim() || primer.answerRefreshedAt == null || (primer.lastObservedAt ?? 0) > primer.answerRefreshedAt);
18102
- default: {
18103
- const _exhaustive = task;
18104
- return Boolean(_exhaustive);
18105
- }
18106
- }
18107
- }
18108
-
18109
18447
  // src/features/magic-context/dreamer/task-scheduler.ts
18110
18448
  var MAX_TASK_RETRIES = 3;
18111
18449
  function ensureSeeded(db, projectIdentity, config, now) {
@@ -18253,10 +18591,10 @@ async function runDomainGroup(deps, group, cb) {
18253
18591
  cb?.onRan?.(due.config.task);
18254
18592
  } else if (outcome.transient) {
18255
18593
  recordTransientFailure(db, projectIdentity, due, finishedAt, outcome.error ?? null);
18256
- cb?.onFailed?.(due.config.task);
18594
+ cb?.onFailed?.(due.config.task, outcome.error);
18257
18595
  } else {
18258
18596
  advanceAfterRun(db, projectIdentity, due, finishedAt, "failed", outcome.error ?? null);
18259
- cb?.onFailed?.(due.config.task);
18597
+ cb?.onFailed?.(due.config.task, outcome.error);
18260
18598
  }
18261
18599
  }
18262
18600
  } finally {
@@ -18265,7 +18603,15 @@ async function runDomainGroup(deps, group, cb) {
18265
18603
  }
18266
18604
  async function runManualDream(deps) {
18267
18605
  const now = Date.now();
18268
- const result = { ran: [], skippedNoWork: [], deferredBusy: [], failed: [] };
18606
+ const result = {
18607
+ ran: [],
18608
+ skippedNoWork: [],
18609
+ deferredBusy: [],
18610
+ failed: [],
18611
+ failureDetails: [],
18612
+ backlogBefore: {},
18613
+ backlogAfter: {}
18614
+ };
18269
18615
  let selected;
18270
18616
  let forceGate = false;
18271
18617
  if (deps.task) {
@@ -18279,6 +18625,9 @@ async function runManualDream(deps) {
18279
18625
  }
18280
18626
  if (selected.length === 0)
18281
18627
  return result;
18628
+ const selectedTaskNames = selected.map((config) => config.task);
18629
+ result.backlogBefore = getDreamTaskBacklogs(deps.db, deps.projectIdentity, selectedTaskNames);
18630
+ result.backlogAfter = { ...result.backlogBefore };
18282
18631
  for (const cfg of selected)
18283
18632
  ensureSeeded(deps.db, deps.projectIdentity, cfg, now);
18284
18633
  const dueAll = selected.map((config) => ({ config, scheduledAt: now }));
@@ -18300,8 +18649,10 @@ async function runManualDream(deps) {
18300
18649
  else
18301
18650
  result.skippedNoWork.push(d.config.task);
18302
18651
  }
18303
- if (gated.length === 0)
18652
+ if (gated.length === 0) {
18653
+ result.backlogAfter = getDreamTaskBacklogs(deps.db, deps.projectIdentity, selectedTaskNames);
18304
18654
  return result;
18655
+ }
18305
18656
  const groups = new Map;
18306
18657
  for (const d of gated) {
18307
18658
  const kind = leaseKindFor(d.config.task);
@@ -18313,9 +18664,14 @@ async function runManualDream(deps) {
18313
18664
  forceGate,
18314
18665
  leaseWaitMs: MANUAL_RUN_LEASE_WAIT_MS,
18315
18666
  onRan: (t) => result.ran.push(t),
18316
- onFailed: (t) => result.failed.push(t),
18667
+ onFailed: (task, error) => {
18668
+ result.failed.push(task);
18669
+ if (error)
18670
+ result.failureDetails?.push(`${task}: ${error}`);
18671
+ },
18317
18672
  onBusy: (t) => result.deferredBusy.push(t)
18318
18673
  })));
18674
+ result.backlogAfter = getDreamTaskBacklogs(deps.db, deps.projectIdentity, selectedTaskNames);
18319
18675
  return result;
18320
18676
  }
18321
18677
  async function runDueTasksForProject(deps) {
@@ -18888,10 +19244,11 @@ async function sweepProject(reg, origin, db, gitCommitEnabled) {
18888
19244
  ensureProjectRegistered: reg.ensureRegistered,
18889
19245
  language: reg.language,
18890
19246
  dreamerModel: dreamerConfig.model,
18891
- experimentalMural: reg.experimentalMural,
19247
+ mural: reg.mural,
18892
19248
  memoryInjectionBudgetTokens: reg.memoryInjectionBudgetTokens,
18893
19249
  transformMode: reg.transformMode,
18894
- moduleClient: reg.moduleClient
19250
+ moduleClient: reg.moduleClient,
19251
+ onProgress: (progress, completedTask) => reg.onDreamerProgress?.(progress, completedTask)
18895
19252
  });
18896
19253
  const ran = await runDueTasksForProject({
18897
19254
  db,
@@ -20347,7 +20704,7 @@ function formatExecuteThreshold(detail, contextLimit) {
20347
20704
  }
20348
20705
  return `${percentage}%${clampNote}`;
20349
20706
  }
20350
- function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit) {
20707
+ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit, dreamer) {
20351
20708
  const thresholdDetail = resolveExecuteThresholdDetail(executeThresholdPercentageConfig, liveModelKey, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, {
20352
20709
  tokensConfig: executeThresholdTokens,
20353
20710
  contextLimit,
@@ -20402,6 +20759,11 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
20402
20759
  `**Protected tags:** ${protectedTags}`,
20403
20760
  `**Subagent session:** ${meta.isSubagent}`
20404
20761
  ];
20762
+ if (dreamer?.backlog && Object.keys(dreamer.backlog).length > 0) {
20763
+ lines.push("", "### Dreamer", ...dreamer.progress ? [
20764
+ `- Running: ${dreamer.progress.task} — ${dreamer.progress.processed}/${dreamer.progress.total} processed`
20765
+ ] : [], ...formatDreamTaskBacklogs(dreamer.backlog).split("\\n"));
20766
+ }
20405
20767
  if (meta.lastContextPercentage > 0 || meta.lastInputTokens > 0) {
20406
20768
  lines.push("", "### Context Usage", `- Last percentage: ${meta.lastContextPercentage.toFixed(1)}%`, `- Last input tokens: ${meta.lastInputTokens.toLocaleString()}`, `- Resolved context limit: ${displayContextLimit > 0 ? displayContextLimit.toLocaleString() : "unknown"}`, `- Proactive compartment evaluation: ${proactiveCompartmentTrigger}%`, `- Post-drop target for historian: ${(executeThresholdPercentage * POST_DROP_TARGET_RATIO).toFixed(0)}% (${executeThresholdPercentage}% * ${POST_DROP_TARGET_RATIO})`, `- Commit cluster trigger: ${commitClusterTrigger?.enabled !== false ? `enabled (min ${commitClusterTrigger?.min_clusters ?? 3} clusters)` : "disabled"}, tail-size trigger: > 3x compartment budget`);
20407
20769
  }
@@ -20424,6 +20786,12 @@ ${c.content}
20424
20786
  lines.push(`- §${op.tagId}§ → ${op.operation}`);
20425
20787
  }
20426
20788
  }
20789
+ if (dreamer?.backlog && Object.keys(dreamer.backlog).length > 0) {
20790
+ lines.push("", "### Dreamer Backlog", formatDreamTaskBacklogs(dreamer.backlog));
20791
+ }
20792
+ if (dreamer?.progress) {
20793
+ lines.push("", "### Dreamer Progress", `- ${dreamer.progress.task}: ${dreamer.progress.processed}/${dreamer.progress.total} processed this run`);
20794
+ }
20427
20795
  return lines.join(`
20428
20796
  `);
20429
20797
  } catch (error) {
@@ -20659,16 +21027,32 @@ ${sidekickResult}
20659
21027
  await sendUserPrompt(deps.sidekick.client, sessionId, augmentedPrompt);
20660
21028
  throwSentinel("CTX-AUG");
20661
21029
  }
21030
+ function readDreamTaskBacklogsSafely(db, projectPath, tasks) {
21031
+ try {
21032
+ return getDreamTaskBacklogs(db, projectPath, tasks);
21033
+ } catch {
21034
+ return {};
21035
+ }
21036
+ }
20662
21037
  function summarizeManualDream(s) {
20663
21038
  const lines = ["## /ctx-dream", ""];
20664
21039
  if (s.ran.length > 0)
20665
21040
  lines.push(`Ran: ${s.ran.join(", ")}`);
20666
21041
  if (s.failed.length > 0)
20667
21042
  lines.push(`Failed: ${s.failed.join(", ")}`);
21043
+ if ((s.failureDetails?.length ?? 0) > 0) {
21044
+ lines.push("Failure details:", ...(s.failureDetails ?? []).map((detail) => `- ${detail}`));
21045
+ }
20668
21046
  if (s.skippedNoWork.length > 0)
20669
21047
  lines.push(`Skipped (no work): ${s.skippedNoWork.join(", ")}`);
20670
21048
  if (s.deferredBusy.length > 0)
20671
21049
  lines.push(`Busy: ${s.deferredBusy.join(", ")} — another dream task holds this domain's lease; retry in a minute`);
21050
+ if (Object.keys(s.backlogBefore ?? {}).length > 0) {
21051
+ lines.push("", "Backlog at run start:", formatDreamTaskBacklogs(s.backlogBefore ?? {}));
21052
+ }
21053
+ if (Object.keys(s.backlogAfter ?? {}).length > 0) {
21054
+ lines.push("", "Backlog at run end:", formatDreamTaskBacklogs(s.backlogAfter ?? {}));
21055
+ }
20672
21056
  if (s.ran.length === 0 && s.failed.length === 0 && s.skippedNoWork.length === 0 && s.deferredBusy.length === 0) {
20673
21057
  lines.push("No enabled dream tasks to run.");
20674
21058
  }
@@ -20696,7 +21080,17 @@ Unknown task "${requested}". Valid tasks: ${CANONICAL_DREAM_TASKS.join(", ")}.`,
20696
21080
  }
20697
21081
  task = requested;
20698
21082
  }
20699
- await deps.sendNotification(sessionId, task ? `Running dream task "${task}"...` : "Starting dream run...", dreamNotificationParams);
21083
+ const backlogTasks = task ? [task] : CANONICAL_DREAM_TASKS;
21084
+ const backlogBefore = readDreamTaskBacklogsSafely(deps.db, deps.dreamer.projectPath, backlogTasks);
21085
+ await deps.sendNotification(sessionId, [
21086
+ "## /ctx-dream",
21087
+ "",
21088
+ task ? `Running dream task "${task}"...` : "Starting dream run...",
21089
+ "",
21090
+ "Backlog before starting:",
21091
+ formatDreamTaskBacklogs(backlogBefore, backlogTasks)
21092
+ ].join(`
21093
+ `), dreamNotificationParams);
20700
21094
  const summary = await deps.dreamer.runManual(task);
20701
21095
  await deps.sendNotification(sessionId, summarizeManualDream(summary), dreamNotificationParams);
20702
21096
  throwSentinel("CTX-DREAM");
@@ -20834,7 +21228,10 @@ Embedding status is unavailable.`;
20834
21228
  }
20835
21229
  const liveModelKey = deps.getLiveModelKey?.(sessionId);
20836
21230
  const liveContextLimit = deps.getContextLimit?.(sessionId);
20837
- const statusOutput = executeStatus(deps.db, sessionId, deps.protectedTags, deps.executeThresholdPercentage, liveModelKey, deps.historyBudgetPercentage, deps.commitClusterTrigger, deps.executeThresholdTokens, liveContextLimit);
21231
+ const statusOutput = executeStatus(deps.db, sessionId, deps.protectedTags, deps.executeThresholdPercentage, liveModelKey, deps.historyBudgetPercentage, deps.commitClusterTrigger, deps.executeThresholdTokens, liveContextLimit, deps.dreamer ? {
21232
+ backlog: readDreamTaskBacklogsSafely(deps.db, deps.dreamer.projectPath, CANONICAL_DREAM_TASKS),
21233
+ progress: deps.getDreamerProgress?.() ?? null
21234
+ } : undefined);
20838
21235
  const moduleStatus = rustStatus ? `
20839
21236
 
20840
21237
  ${formatRustStatusText(rustStatus)}` : "";
@@ -24858,7 +25255,8 @@ function loadContextUsage(contextUsageMap, db, sessionId) {
24858
25255
  if (persisted) {
24859
25256
  contextUsageMap.set(sessionId, {
24860
25257
  ...persisted,
24861
- lastResponseTime: persistedLastResponseTime ?? persisted.updatedAt
25258
+ lastResponseTime: persistedLastResponseTime ?? persisted.updatedAt,
25259
+ hasUsageTokens: false
24862
25260
  });
24863
25261
  return persisted.usage;
24864
25262
  }
@@ -29347,6 +29745,19 @@ function passUsage(usage, limit) {
29347
29745
  context_limit_tokens: limit
29348
29746
  };
29349
29747
  }
29748
+ function shouldDisarmRustEmergencyRecovery(input) {
29749
+ if (input.finalWireEstimate?.trusted === true && input.providerProvenLimitTokens > 0 && input.finalWireEstimate.tokens < input.providerProvenLimitTokens * 0.8) {
29750
+ return "trusted-final-wire";
29751
+ }
29752
+ if (!input.materialized || input.usagePercentage >= 80)
29753
+ return null;
29754
+ if (input.recoveryOrigin !== "provider_overflow")
29755
+ return "fresh-usage";
29756
+ if (input.usageEntry?.hasUsageTokens === true && (input.recoveryArmedAt === null || input.usageEntry.updatedAt > input.recoveryArmedAt)) {
29757
+ return "fresh-usage";
29758
+ }
29759
+ return null;
29760
+ }
29350
29761
  function directiveTextOf(response) {
29351
29762
  const directives = isRecord3(response.host_directives) ? response.host_directives : undefined;
29352
29763
  const channel2 = isRecord3(directives?.channel2_nudge) ? directives.channel2_nudge : undefined;
@@ -29826,6 +30237,7 @@ function createRustModeTransform(deps, options) {
29826
30237
  }
29827
30238
  }
29828
30239
  const reduceAvailability = resolveCtxReduceAvailability(sessionId);
30240
+ resolveTodowriteAvailabilityFromMessages(sessionId, messages);
29829
30241
  const toolPresent = reduceAvailability.frozen && reduceAvailability.callable;
29830
30242
  try {
29831
30243
  if (preflightError)
@@ -30085,6 +30497,7 @@ function createRustModeTransform(deps, options) {
30085
30497
  const pages = buildPagedModuleTransformPayloads(body);
30086
30498
  logStage(sessionId, "wireBuild", wireBuildStartedAt, timings, wireDelta ? `mode=tail_delta input=${encodedInput.length}` : "mode=full");
30087
30499
  let response;
30500
+ let servedFinalWireEstimate;
30088
30501
  for (const [index, page] of pages.entries()) {
30089
30502
  timings.transportBytes += Buffer.byteLength(JSON.stringify(page));
30090
30503
  const transportStartedAt = performance.now();
@@ -30232,6 +30645,15 @@ function createRustModeTransform(deps, options) {
30232
30645
  if (typeof boundaryId === "string" && boundaryId.length > 0) {
30233
30646
  assertNativeBoundary(appliedMessages, sessionId, boundaryId);
30234
30647
  }
30648
+ if (passInputs.emergency_recovery_armed === true) {
30649
+ servedFinalWireEstimate = estimateFinalWireInputTokens({
30650
+ messages: appliedMessages,
30651
+ systemPromptTokens: sessionMeta.systemPromptTokens,
30652
+ providerID: model?.providerID,
30653
+ modelID: model?.modelID,
30654
+ agentName: deps.getNotificationParams?.(sessionId)?.agent
30655
+ });
30656
+ }
30235
30657
  logStage(sessionId, "apply", applyStartedAt, timings);
30236
30658
  const lkgSnapshotStartedAt = performance.now();
30237
30659
  const explicitDecision = typeof response.decision === "string" || typeof response.action === "string" || typeof response.cache_bust === "boolean";
@@ -30320,10 +30742,20 @@ function createRustModeTransform(deps, options) {
30320
30742
  sessionLog(sessionId, "rust compartment mirror-back failed (ignored):", error);
30321
30743
  }
30322
30744
  }
30323
- if (materializeReason !== "none" && passUsageSnapshot.percentage < 80 && getOverflowState(deps.db, sessionId).needsEmergencyRecovery) {
30745
+ const currentOverflowState = getOverflowState(deps.db, sessionId, modelKey);
30746
+ const disarmEvidence = currentOverflowState.needsEmergencyRecovery ? shouldDisarmRustEmergencyRecovery({
30747
+ materialized: materializeReason !== "none",
30748
+ usagePercentage: passUsageSnapshot.percentage,
30749
+ recoveryOrigin: currentOverflowState.emergencyRecoveryOrigin,
30750
+ recoveryArmedAt: getEmergencyRecoveryArmedAt(sessionId),
30751
+ usageEntry: deps.contextUsageMap.get(sessionId),
30752
+ finalWireEstimate: servedFinalWireEstimate,
30753
+ providerProvenLimitTokens: currentOverflowState.detectedContextLimit
30754
+ }) : null;
30755
+ if (disarmEvidence) {
30324
30756
  try {
30325
30757
  clearEmergencyRecovery(deps.db, sessionId);
30326
- sessionLog(sessionId, `rust pass disarmed emergency recovery after ${materializeReason} at ${passUsageSnapshot.percentage.toFixed(1)}% usage`);
30758
+ sessionLog(sessionId, `rust pass disarmed emergency recovery via ${disarmEvidence} after ${materializeReason} at ${passUsageSnapshot.percentage.toFixed(1)}% usage`);
30327
30759
  } catch {}
30328
30760
  }
30329
30761
  wireCaches.set(sessionId, pendingWireCache);
@@ -31411,7 +31843,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
31411
31843
  historyBudgetTokens,
31412
31844
  temporalAwareness: deps.experimentalTemporalAwareness,
31413
31845
  hardSignals: m0HardSignals,
31414
- muralEnabled: deps.experimentalMuralEnabled
31846
+ muralEnabled: deps.muralEnabled
31415
31847
  }
31416
31848
  });
31417
31849
  passOutcome.markFinalized();
@@ -31891,7 +32323,8 @@ function createEventHandler2(deps) {
31891
32323
  inputTokens: totalInputTokens
31892
32324
  },
31893
32325
  updatedAt: now,
31894
- lastResponseTime: now
32326
+ lastResponseTime: now,
32327
+ hasUsageTokens: true
31895
32328
  });
31896
32329
  updates.lastContextPercentage = percentage;
31897
32330
  updates.lastInputTokens = totalInputTokens;
@@ -33083,11 +33516,13 @@ function createMagicContextHook(deps) {
33083
33516
  const fence = getSchemaFenceRejection();
33084
33517
  recordHookInitFailure({
33085
33518
  type: "storage",
33086
- reason: migration && blockingProcesses.length > 0 ? {
33519
+ reason: migration && (blockingProcesses.length > 0 || migration.unreadableFile) ? {
33087
33520
  kind: "migration_guard",
33088
33521
  persistedVersion: migration.persistedVersion,
33089
33522
  supportedVersion: migration.supportedVersion,
33090
- blockingProcesses
33523
+ blockingProcesses,
33524
+ ...migration.unreadableFile ? { unreadableFile: migration.unreadableFile } : {},
33525
+ ...migration.unreadableArm ? { unreadableArm: migration.unreadableArm } : {}
33091
33526
  } : fence ? {
33092
33527
  kind: "schema_fence",
33093
33528
  persistedVersion: fence.persistedVersion,
@@ -33152,6 +33587,7 @@ function createMagicContextHook(deps) {
33152
33587
  const sessionDirectoryBySession = deps.liveSessionState?.sessionDirectoryBySession ?? new Map;
33153
33588
  const internalChildSessions = deps.liveSessionState?.internalChildSessions ?? new Set;
33154
33589
  const recompProgressBySession = deps.liveSessionState?.recompProgressBySession ?? new Map;
33590
+ const dreamerProgressByProject = deps.liveSessionState?.dreamerProgressByProject ?? new Map;
33155
33591
  const channel1StateBySession = new Map;
33156
33592
  const channel2DirectiveTextBySession = new Map;
33157
33593
  const resolveLiveModel = (sessionId) => {
@@ -33192,6 +33628,7 @@ function createMagicContextHook(deps) {
33192
33628
  deferredMaterializationSessions,
33193
33629
  sessionDirectoryBySession,
33194
33630
  recompProgressBySession,
33631
+ dreamerProgressByProject,
33195
33632
  internalChildSessions
33196
33633
  },
33197
33634
  directory: deps.directory,
@@ -33602,7 +34039,7 @@ function createMagicContextHook(deps) {
33602
34039
  compactionOff,
33603
34040
  experimentalUserMemories: userMemoryCollectionEnabled(dreamerConfig),
33604
34041
  experimentalTemporalAwareness: deps.config.temporal_awareness === true,
33605
- experimentalMuralEnabled: deps.config.experimental?.mural?.enabled === true,
34042
+ muralEnabled: deps.config.mural?.enabled === true,
33606
34043
  historianTwoPass: deps.config.historian?.two_pass === true,
33607
34044
  liveModelBySession,
33608
34045
  sessionDirectoryBySession,
@@ -33696,7 +34133,14 @@ function createMagicContextHook(deps) {
33696
34133
  userMemoryCollectionEnabled: userMemoryCollectionEnabled(dreaming),
33697
34134
  language: deps.config.language,
33698
34135
  transformMode: deps.config.transform_mode,
33699
- moduleClient: rustModeModuleClient
34136
+ moduleClient: rustModeModuleClient,
34137
+ onProgress: (progress, completedTask) => {
34138
+ if (progress) {
34139
+ dreamerProgressByProject.set(projectPath, progress);
34140
+ } else if (dreamerProgressByProject.get(projectPath)?.task === completedTask) {
34141
+ dreamerProgressByProject.delete(projectPath);
34142
+ }
34143
+ }
33700
34144
  });
33701
34145
  runDueTasksForProject({
33702
34146
  db,
@@ -33723,6 +34167,7 @@ function createMagicContextHook(deps) {
33723
34167
  const model = resolveLiveModel(sessionId);
33724
34168
  return model ? `${model.providerID}/${model.modelID}` : undefined;
33725
34169
  },
34170
+ getDreamerProgress: () => dreamerProgressByProject.get(projectPath) ?? null,
33726
34171
  getContextLimit: (sessionId) => {
33727
34172
  const model = resolveLiveModel(sessionId);
33728
34173
  if (!model)
@@ -33770,10 +34215,17 @@ function createMagicContextHook(deps) {
33770
34215
  }),
33771
34216
  userMemoryCollectionEnabled: userMemoryCollectionEnabled(dreamerConfig),
33772
34217
  language: deps.config.language,
33773
- experimentalMural: deps.config.experimental?.mural,
34218
+ mural: deps.config.mural,
33774
34219
  memoryInjectionBudgetTokens: deps.config.memory?.injection_budget_tokens,
33775
34220
  transformMode: deps.config.transform_mode,
33776
- moduleClient: rustModeModuleClient
34221
+ moduleClient: rustModeModuleClient,
34222
+ onProgress: (progress, completedTask) => {
34223
+ if (progress) {
34224
+ dreamerProgressByProject.set(projectPath, progress);
34225
+ } else if (dreamerProgressByProject.get(projectPath)?.task === completedTask) {
34226
+ dreamerProgressByProject.delete(projectPath);
34227
+ }
34228
+ }
33777
34229
  }),
33778
34230
  task
33779
34231
  })
@@ -34385,6 +34837,13 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
34385
34837
  const docsTokens = m0Blocks.docsTokens;
34386
34838
  const profileTokens = m0Blocks.profileTokens;
34387
34839
  let lastDreamerRunAt = null;
34840
+ let dreamerBacklog;
34841
+ const dreamerProgress = projectIdentity ? liveSessionState?.dreamerProgressByProject?.get(projectIdentity) ?? null : null;
34842
+ if (projectIdentity) {
34843
+ try {
34844
+ dreamerBacklog = getDreamTaskBacklogs(db, projectIdentity, CANONICAL_DREAM_TASKS);
34845
+ } catch {}
34846
+ }
34388
34847
  if (projectIdentity) {
34389
34848
  try {
34390
34849
  lastDreamerRunAt = getMostRecentTaskRunAt(db, projectIdentity);
@@ -34480,6 +34939,8 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
34480
34939
  lastTransformError: meta?.last_transform_error ? String(meta.last_transform_error) : null,
34481
34940
  lastDreamerRunAt,
34482
34941
  projectIdentity,
34942
+ dreamerBacklog,
34943
+ dreamerProgress,
34483
34944
  compartmentTokens: calibrated.compartmentTokens,
34484
34945
  factTokens: calibrated.factTokens,
34485
34946
  memoryTokens: calibrated.memoryTokens,
@@ -34721,7 +35182,7 @@ function registerRpcHandlers(rpcServer, args) {
34721
35182
  const buildManagedCtx = async (db) => {
34722
35183
  const { deriveHistorianChunkTokens: deriveHistorianChunkTokens2, resolveHistorianContextLimit: resolveHistorianContextLimit2 } = await import("./derive-budgets-9crp0cvd.js");
34723
35184
  const { resolveFallbackChain: resolveFallbackChain2 } = await import("./resolve-fallbacks-5n5tvge1.js");
34724
- const { userMemoryCollectionEnabled: userMemoryCollectionEnabled2 } = await import("./task-config-5nx3qfz8.js");
35185
+ const { userMemoryCollectionEnabled: userMemoryCollectionEnabled2 } = await import("./task-config-gbp7r23g.js");
34725
35186
  const DEFAULT_HISTORIAN_TIMEOUT_MS2 = 600000;
34726
35187
  return {
34727
35188
  client: args.client,
@@ -34746,7 +35207,7 @@ function registerRpcHandlers(rpcServer, args) {
34746
35207
  const db = getDb();
34747
35208
  if (!db)
34748
35209
  return { ok: false, error: "db unavailable" };
34749
- const { runManagedRecomp: runManagedRecomp2 } = await import("./recomp-orchestrator-8e82vmvb.js");
35210
+ const { runManagedRecomp: runManagedRecomp2 } = await import("./recomp-orchestrator-6cd3nrt7.js");
34750
35211
  const { sendIgnoredMessage: sendIgnoredMessage2 } = await import("./send-session-notification-6njqewzk.js");
34751
35212
  log(`[rpc] recomp requested for session ${sessionId}`);
34752
35213
  const ctx = await buildManagedCtx(db);
@@ -34762,7 +35223,7 @@ function registerRpcHandlers(rpcServer, args) {
34762
35223
  const db = getDb();
34763
35224
  if (!db)
34764
35225
  return { ok: false, error: "db unavailable" };
34765
- const { runManagedUpgrade: runManagedUpgrade2 } = await import("./recomp-orchestrator-8e82vmvb.js");
35226
+ const { runManagedUpgrade: runManagedUpgrade2 } = await import("./recomp-orchestrator-6cd3nrt7.js");
34766
35227
  const { sendIgnoredMessage: sendIgnoredMessage2 } = await import("./send-session-notification-6njqewzk.js");
34767
35228
  log(`[rpc] session-upgrade requested for session ${sessionId}`);
34768
35229
  const ctx = await buildManagedCtx(db);
@@ -37014,13 +37475,20 @@ var server = async (ctx) => {
37014
37475
  embeddingConfig: pluginConfig.embedding,
37015
37476
  memoryEnabled: pluginConfig.memory?.enabled === true,
37016
37477
  memoryInjectionBudgetTokens: pluginConfig.memory?.injection_budget_tokens,
37017
- experimentalMural: pluginConfig.experimental?.mural,
37478
+ mural: pluginConfig.mural,
37018
37479
  gitCommitIndexing: pluginConfig.memory.git_commit_indexing?.enabled ? {
37019
37480
  enabled: true,
37020
37481
  since_days: pluginConfig.memory.git_commit_indexing.since_days,
37021
37482
  max_commits: pluginConfig.memory.git_commit_indexing.max_commits
37022
37483
  } : undefined,
37023
37484
  ensureRegistered: ensureProjectRegisteredFromOpenCodeDirectory,
37485
+ onDreamerProgress: (progress, completedTask) => {
37486
+ if (progress) {
37487
+ liveSessionState.dreamerProgressByProject.set(timerProjectIdentity, progress);
37488
+ } else if (liveSessionState.dreamerProgressByProject.get(timerProjectIdentity)?.task === completedTask) {
37489
+ liveSessionState.dreamerProgressByProject.delete(timerProjectIdentity);
37490
+ }
37491
+ },
37024
37492
  moduleClient: classifyModuleClient
37025
37493
  };
37026
37494
  try {