@cortexkit/opencode-magic-context 0.49.26 → 0.49.28

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 (110) hide show
  1. package/dist/auto-search-worker.js +4 -4
  2. package/dist/chunk-embedding-write-worker.js +7 -7
  3. package/dist/{compartment-chunk-embedding-96ec8a7h.js → compartment-chunk-embedding-qgp0tbf7.js} +3 -3
  4. package/dist/config/schema/magic-context.d.ts +393 -393
  5. package/dist/{conflict-warning-hook-dv8fdc2w.js → conflict-warning-hook-tgnw019g.js} +4 -4
  6. package/dist/{context-pjqa24nd.js → context-dxfztzsd.js} +116 -116
  7. package/dist/{embedding-backfill-a91rvdf9.js → embedding-backfill-cwnyr4fj.js} +5 -5
  8. package/dist/features/magic-context/dreamer/map-memories.d.ts +2 -2
  9. package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
  10. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  11. package/dist/features/magic-context/message-index-async.d.ts +1 -4
  12. package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
  13. package/dist/features/magic-context/message-index.d.ts +1 -28
  14. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  15. package/dist/features/magic-context/session-retention.fixture.d.ts +4 -0
  16. package/dist/features/magic-context/session-retention.fixture.d.ts.map +1 -0
  17. package/dist/features/magic-context/storage-session-tables.d.ts +5 -12
  18. package/dist/features/magic-context/storage-session-tables.d.ts.map +1 -1
  19. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  20. package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
  21. package/dist/hooks/magic-context/historian-history-wait.d.ts +10 -0
  22. package/dist/hooks/magic-context/historian-history-wait.d.ts.map +1 -0
  23. package/dist/hooks/magic-context/lkg-recovery-fence.d.ts +0 -5
  24. package/dist/hooks/magic-context/lkg-recovery-fence.d.ts.map +1 -1
  25. package/dist/hooks/magic-context/raw-ordinal-warmup.d.ts +0 -10
  26. package/dist/hooks/magic-context/raw-ordinal-warmup.d.ts.map +1 -1
  27. package/dist/hooks/magic-context/todo-view.d.ts +2 -2
  28. package/dist/{index-w55zp7kf.js → index-077ht5cq.js} +1 -1
  29. package/dist/{index-xyx2ge2y.js → index-31y1mznz.js} +4 -4
  30. package/dist/{index-6g1zq1sk.js → index-4cpc59jv.js} +5 -5
  31. package/dist/{index-e5b61ty6.js → index-4ypqzvhf.js} +1 -1
  32. package/dist/index-7gbcg8xv.js +559 -0
  33. package/dist/{index-9q1g3c5q.js → index-7kkphb6c.js} +1 -1
  34. package/dist/{index-zn2n24yr.js → index-89k1w6x2.js} +3 -3
  35. package/dist/{index-sgchhbvy.js → index-a2fz9hpa.js} +1 -1
  36. package/dist/{index-hjnrp6vy.js → index-dcqk0y27.js} +1508 -85
  37. package/dist/{index-r3bteyev.js → index-dzfevacx.js} +9 -9
  38. package/dist/{index-rp78nqvr.js → index-fq2p158w.js} +10 -10
  39. package/dist/{index-56tfs78r.js → index-k9jx7ma9.js} +598 -610
  40. package/dist/{index-6tmf87w0.js → index-mm93m7y6.js} +1 -1
  41. package/dist/{index-va2g2q57.js → index-n8qmmd7f.js} +3 -3
  42. package/dist/{index-whcgcfrv.js → index-np3rgfw4.js} +1 -1
  43. package/dist/{index-m8va3mma.js → index-rverk2vz.js} +0 -2
  44. package/dist/{index-cfaet9cg.js → index-s1n8dd2n.js} +1 -1
  45. package/dist/{index-mphkpyap.js → index-t3rbb8wr.js} +2 -2
  46. package/dist/{index-064v90e8.js → index-tvzvjggy.js} +1 -1
  47. package/dist/{index-j43m40zd.js → index-v0dhhxsq.js} +1 -1
  48. package/dist/{index-xbbgcwd2.js → index-v15ne3a3.js} +2 -5
  49. package/dist/{index-x9k3mqrf.js → index-xrnsygfq.js} +1 -1
  50. package/dist/{index-dn2ke3b9.js → index-yzjhx10y.js} +5 -5
  51. package/dist/index.js +38 -38
  52. package/dist/{message-fts-rowid-map-4ksatb6f.js → message-fts-rowid-map-va36knh4.js} +2 -4
  53. package/dist/{message-index-bounded-whh5wv4a.js → message-index-bounded-7ayzb2ah.js} +22 -25
  54. package/dist/migration-worker.js +6 -6
  55. package/dist/plugin/dream-timer.d.ts.map +1 -1
  56. package/dist/{project-embedding-registry-t9fx6jwh.js → project-embedding-registry-94nncs5g.js} +4 -4
  57. package/dist/raw-ordinal-warmup-worker.js +7 -30
  58. package/dist/{read-session-chunk-mqmkgdx0.js → read-session-chunk-jzb6w9w5.js} +6 -6
  59. package/dist/{read-session-raw-dy2xxafq.js → read-session-raw-b2sd67v4.js} +1 -1
  60. package/dist/{recomp-orchestrator-qwcz86r1.js → recomp-orchestrator-02sa2ngr.js} +17 -17
  61. package/dist/{search-archive-8md3twv2.js → search-archive-q1vw7zg4.js} +7 -7
  62. package/dist/{search-y2t97tbb.js → search-spq6nfyf.js} +14 -15
  63. package/dist/{send-session-notification-kzazs7fy.js → send-session-notification-afzzqy5b.js} +3 -3
  64. package/dist/{storage-tags-a77q4v77.js → storage-tags-6hvkk01c.js} +2 -2
  65. package/dist/{transform-postprocess-phase-ppm1tfa7.js → transform-postprocess-phase-6menyyxc.js} +17 -17
  66. package/dist/v2/auto-search-worker.js +4 -4
  67. package/dist/v2/chunk-embedding-write-worker.js +7 -7
  68. package/dist/v2/{compartment-chunk-embedding-5rmrn09z.js → compartment-chunk-embedding-63dzs31c.js} +3 -3
  69. package/dist/v2/{context-10pj3bwd.js → context-0qhz1nnj.js} +199 -209
  70. package/dist/v2/{embedding-backfill-r9mzt2eg.js → embedding-backfill-rh5dgsva.js} +5 -5
  71. package/dist/v2/hooks/context.d.ts.map +1 -1
  72. package/dist/v2/{message-fts-rowid-map-2s3ta05x.js → message-fts-rowid-map-78awh03v.js} +2 -4
  73. package/dist/v2/{message-index-bounded-f4269d3a.js → message-index-bounded-n09b74vr.js} +21 -24
  74. package/dist/v2/migration-worker.js +6 -6
  75. package/dist/v2/{project-embedding-registry-tgtmart7.js → project-embedding-registry-6pak822h.js} +4 -4
  76. package/dist/v2/raw-ordinal-warmup-worker.js +7 -30
  77. package/dist/v2/{read-session-chunk-ym5zvexn.js → read-session-chunk-vpt69tw9.js} +5 -5
  78. package/dist/v2/{read-session-raw-484g8kf2.js → read-session-raw-5x088amc.js} +1 -1
  79. package/dist/v2/{recomp-orchestrator-b44s2snm.js → recomp-orchestrator-txsmbnvw.js} +14 -14
  80. package/dist/v2/{search-archive-pmf2c1kb.js → search-archive-t9bcz4ht.js} +6 -6
  81. package/dist/v2/{search-75tf953m.js → search-dcy9b6gd.js} +13 -14
  82. package/dist/v2/{send-session-notification-gd21f5rq.js → send-session-notification-vdsgnf4c.js} +1 -1
  83. package/dist/v2/{server-7dpnt0ys.js → server-4sn3at8p.js} +1 -1
  84. package/dist/v2/{server-wj8h2q21.js → server-6avtp9jp.js} +1 -1
  85. package/dist/v2/{server-wwaw5npa.js → server-6g98c227.js} +1 -1
  86. package/dist/v2/{server-erk2bvdf.js → server-6vd2n183.js} +0 -2
  87. package/dist/v2/{server-4frtg2z1.js → server-awr8qa4c.js} +2 -5
  88. package/dist/v2/{server-7d6m1p2b.js → server-cqjvxyrp.js} +1 -1
  89. package/dist/v2/{server-317f3x9y.js → server-e3hc93wk.js} +5 -5
  90. package/dist/v2/{server-9zh78t8y.js → server-jj9qye8y.js} +1468 -73
  91. package/dist/v2/{server-fa7vtp40.js → server-nxas8v1d.js} +1 -1
  92. package/dist/v2/{server-vjer2jdn.js → server-txbc3czn.js} +9 -9
  93. package/dist/v2/{server-rqebawx1.js → server-v77xa5wj.js} +1 -1
  94. package/dist/v2/{server-jcsw05aa.js → server-xkvxss5g.js} +4 -4
  95. package/dist/v2/{server-hnmm8yzq.js → server-yqek3s4k.js} +1 -1
  96. package/dist/v2/server-z2n2xaeh.js +538 -0
  97. package/dist/v2/{server-nmwhe524.js → server-z3mk9jdc.js} +2 -2
  98. package/dist/v2/{server-w7x9wgm0.js → server-zmcrvpm3.js} +2 -2
  99. package/dist/v2/server.js +2 -2
  100. package/dist/v2/{storage-tags-q71rnr9f.js → storage-tags-18nbaanf.js} +2 -2
  101. package/dist/v2/store-reader.d.ts +0 -2
  102. package/dist/v2/store-reader.d.ts.map +1 -1
  103. package/dist/v2/{wrapup-orchestrator-gwww44st.js → wrapup-orchestrator-gjkszser.js} +14 -14
  104. package/dist/{wrapup-orchestrator-a9r8r4sk.js → wrapup-orchestrator-n5h5sr1x.js} +18 -18
  105. package/package.json +1 -1
  106. package/src/shared/write-transaction-attribution.test.ts +1 -6
  107. package/dist/index-40ma8m0c.js +0 -2251
  108. package/dist/message-index-ywx53y30.js +0 -61
  109. package/dist/v2/message-index-v6g8mgn7.js +0 -60
  110. package/dist/v2/server-k46ec388.js +0 -2220
@@ -144,11 +144,11 @@ import {
144
144
  getDroppedTagsByNumbers2,
145
145
  getMaxDroppedTagNumber2,
146
146
  getTagById2
147
- } from "./index-whcgcfrv.js";
147
+ } from "./index-np3rgfw4.js";
148
148
  import {
149
149
  observeTemporalDecisions,
150
150
  stableStringify
151
- } from "./index-x9k3mqrf.js";
151
+ } from "./index-xrnsygfq.js";
152
152
  import {
153
153
  withContentLanguageDirective,
154
154
  parseCron,
@@ -169,7 +169,7 @@ import {
169
169
  buildInMemoryTailRawMessages2,
170
170
  RawOrdinalsNotReady2,
171
171
  runSessionWarmupWorker
172
- } from "./index-m8va3mma.js";
172
+ } from "./index-rverk2vz.js";
173
173
  import {
174
174
  openCodeDbExists,
175
175
  withReadOnlySessionDb,
@@ -177,7 +177,7 @@ import {
177
177
  latestPersistedMessageForRecovery,
178
178
  latestRealUserTurnIdFromOpenCodeDb,
179
179
  findLastAssistantModelFromOpenCodeDb
180
- } from "./index-j43m40zd.js";
180
+ } from "./index-v0dhhxsq.js";
181
181
  import {
182
182
  expandToolPart,
183
183
  askAnswerLines,
@@ -302,11 +302,11 @@ import {
302
302
  readRawSessionMessageById2,
303
303
  readSessionChunk2,
304
304
  readRawSessionSeedTail2
305
- } from "./index-dn2ke3b9.js";
305
+ } from "./index-yzjhx10y.js";
306
306
  import {
307
307
  BoundedSessionMap,
308
308
  getMeasuredToolDefinitionTokens
309
- } from "./index-cfaet9cg.js";
309
+ } from "./index-s1n8dd2n.js";
310
310
  import {
311
311
  isFailClosedBlockingError,
312
312
  shouldBypassFailClosedBlock,
@@ -320,7 +320,7 @@ import {
320
320
  runSqliteOptimize,
321
321
  openDatabase,
322
322
  openCurrentDatabase
323
- } from "./index-6g1zq1sk.js";
323
+ } from "./index-4cpc59jv.js";
324
324
  import {
325
325
  registerOwnRpcServerInstance,
326
326
  rpcPortDir,
@@ -339,7 +339,7 @@ import {
339
339
  resolveStoredPathWorkspaceIdentity,
340
340
  storedPathBelongsToWorkspace,
341
341
  computeWorkspaceEpochFingerprint
342
- } from "./index-9q1g3c5q.js";
342
+ } from "./index-7kkphb6c.js";
343
343
  import {
344
344
  V2_MEMORY_CATEGORIES,
345
345
  CATEGORY_PRIORITY
@@ -368,6 +368,15 @@ import {
368
368
  getLastCompartmentEndMessage,
369
369
  getLastCompartmentEndMessageId,
370
370
  providerResponseFailed,
371
+ LkgRecoveryFenceError,
372
+ lkgServedArrayDigest,
373
+ hasCleanLkgRecoveryFence,
374
+ dirtyLkgRecoveryRevision,
375
+ ownsLkgDirtyFence,
376
+ invalidateCleanLkgRecoveryFence,
377
+ prepareLkgCaptureFence,
378
+ certifyCommittedLkgCapture,
379
+ markLkgRecoveryDirty,
371
380
  getOrCreateSessionMeta,
372
381
  updateSessionMeta,
373
382
  retryPendingSessionCleanups,
@@ -495,6 +504,7 @@ import {
495
504
  readCoordinateRebaseNotice,
496
505
  formatCoordinateRebaseNotice,
497
506
  rebaseSessionCoordinatesAsync,
507
+ shouldWaitForNewHistorianHistory,
498
508
  EMPTY_READ_REPLY,
499
509
  noteTouchedAt,
500
510
  renderGlance,
@@ -515,7 +525,7 @@ import {
515
525
  snapRangeToCompartments,
516
526
  getActiveCompartmentRun,
517
527
  startCompartmentAgent
518
- } from "./index-hjnrp6vy.js";
528
+ } from "./index-dcqk0y27.js";
519
529
  import {
520
530
  embedPauseBySession,
521
531
  embedRunStateBySession,
@@ -543,7 +553,7 @@ import {
543
553
  embedSessionCompartmentChunks2,
544
554
  getEmbeddingCoverageStatus2,
545
555
  getAutoEmbeddingSessionCoverage2
546
- } from "./index-sgchhbvy.js";
556
+ } from "./index-a2fz9hpa.js";
547
557
  import {
548
558
  SubcClient,
549
559
  connectionFileExists,
@@ -553,27 +563,15 @@ import {
553
563
  SynapseEmbeddingProvider
554
564
  } from "./index-wfybj1dw.js";
555
565
  import {
556
- LkgRecoveryFenceError,
557
- lkgServedArrayDigest,
558
- takeLkgRecoveryLockSweepPage,
559
- hasCleanLkgRecoveryFence,
560
- dirtyLkgRecoveryRevision,
561
- ownsLkgDirtyFence,
562
- invalidateCleanLkgRecoveryFence,
563
- prepareLkgCaptureFence,
564
- certifyCommittedLkgCapture,
565
- markLkgRecoveryDirty,
566
- gaDatabasePath,
567
- getMessageIndexSourceIdentity2,
568
- isMessageIndexSourceCurrent2,
569
- getLastIndexedOrdinal2,
570
- getMessageIndexReconciliationStartOrdinal2,
571
- isMessageIndexReconciledThrough2,
572
- clearIndexedMessages2,
573
- indexSingleMessage2,
574
- indexMessagesAfterOrdinal2,
575
- sweepOrphanedOpenCodeMessageIndexesCooperatively2
576
- } from "./index-40ma8m0c.js";
566
+ getMessageIndexSourceIdentity,
567
+ isMessageIndexSourceCurrent,
568
+ getLastIndexedOrdinal,
569
+ getMessageIndexReconciliationStartOrdinal,
570
+ isMessageIndexReconciledThrough,
571
+ clearIndexedMessages,
572
+ indexSingleMessage,
573
+ indexMessagesAfterOrdinal
574
+ } from "./index-7gbcg8xv.js";
577
575
  import {
578
576
  renderUserFacingFailure,
579
577
  userFacingFailureCode,
@@ -617,7 +615,7 @@ import {
617
615
  readGitFileChangeTimesSince,
618
616
  verificationFileExists,
619
617
  normalizeVerificationFiles
620
- } from "./index-mphkpyap.js";
618
+ } from "./index-t3rbb8wr.js";
621
619
  import {
622
620
  registerNotificationSink2,
623
621
  pushNotification2,
@@ -637,7 +635,7 @@ import {
637
635
  } from "./index-4dhv7mqz.js";
638
636
  import {
639
637
  cosineSimilarity
640
- } from "./index-e5b61ty6.js";
638
+ } from "./index-4ypqzvhf.js";
641
639
  import {
642
640
  computeCueContentHash,
643
641
  hasMuralCueColumns,
@@ -683,17 +681,17 @@ import {
683
681
  resolveMemoriesByIdsForSearch2,
684
682
  unifiedSearch2,
685
683
  formatSearchResults2
686
- } from "./index-r3bteyev.js";
684
+ } from "./index-dzfevacx.js";
687
685
  import {
688
686
  flushIgnoredMessages2,
689
687
  clearIgnoredMessages2,
690
688
  observeIgnoredNotificationEvent2,
691
689
  sendStatusNotification2
692
- } from "./index-w55zp7kf.js";
690
+ } from "./index-077ht5cq.js";
693
691
  import {
694
692
  setRecompStarting2,
695
693
  setRecompTerminal2
696
- } from "./index-va2g2q57.js";
694
+ } from "./index-n8qmmd7f.js";
697
695
  import {
698
696
  makeToolCompositeKey,
699
697
  searchMessageHistoryOffThread,
@@ -755,7 +753,7 @@ import {
755
753
  abortSessionFailClosed2,
756
754
  evaluateEmergencyFailClosed2,
757
755
  runPostTransformPhase2
758
- } from "./index-rp78nqvr.js";
756
+ } from "./index-fq2p158w.js";
759
757
  import {
760
758
  ANNOUNCEMENT_VERSION2,
761
759
  ANNOUNCEMENT_FEATURES2,
@@ -768,7 +766,7 @@ import {
768
766
  } from "./index-fmsr7v69.js";
769
767
  import {
770
768
  renderSearchArchiveOffThread2
771
- } from "./index-zn2n24yr.js";
769
+ } from "./index-89k1w6x2.js";
772
770
  import {
773
771
  COMPACTION_ENABLED_PATH,
774
772
  isDreamerRunnable,
@@ -7186,8 +7184,8 @@ async function mapMemories(args) {
7186
7184
  result.failureStage = outcome.failure.stage;
7187
7185
  }
7188
7186
  const committed = outcome.mapped + outcome.independent;
7187
+ const committedIds = new Set(outcome.committedIds);
7189
7188
  runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
7190
- const committedIds = new Set(outcome.committedIds);
7191
7189
  for (const memory of batch.inputs) {
7192
7190
  if (committedIds.has(memory.id)) {
7193
7191
  delete retries[memory.id];
@@ -7222,7 +7220,22 @@ async function mapMemories(args) {
7222
7220
  log2(`[dreamer] map-memories: targeted retry still omitted ${outcome.requeue.length} id(s); leaving them unmapped for the next run`);
7223
7221
  }
7224
7222
  }
7225
- if (outcome.failure?.class === "timeout" && committed === 0) {
7223
+ if (outcome.failure?.class === "timeout" && batch.inputs.length > 1) {
7224
+ const unmapped = batch.inputs.filter((memory) => !committedIds.has(memory.id));
7225
+ const splitSize = Math.floor(batch.inputs.length / 2);
7226
+ const splits = [];
7227
+ for (let offset = 0;offset < unmapped.length; offset += splitSize) {
7228
+ splits.push({
7229
+ inputs: unmapped.slice(offset, offset + splitSize),
7230
+ isOmissionRetry: batch.isOmissionRetry
7231
+ });
7232
+ }
7233
+ if (splits.length > 0) {
7234
+ batches.splice(i + 1, 0, ...splits);
7235
+ log2(`[dreamer] map-memories: splitting batch [${batch.inputs.map((memory) => memory.id).join(",")}] into ${splits.map((split) => split.inputs.length).join("+")} after timeout; retrying ${unmapped.length} uncommitted mapping(s)`);
7236
+ }
7237
+ }
7238
+ if (outcome.failure?.class === "timeout" && batch.inputs.length === 1 && committed === 0) {
7226
7239
  consecutiveTimeouts += 1;
7227
7240
  timeoutStreakElapsedMs.push(outcome.failure.elapsedMs);
7228
7241
  if (consecutiveTimeouts >= CONSECUTIVE_TIMEOUT_LIMIT2) {
@@ -7230,7 +7243,7 @@ async function mapMemories(args) {
7230
7243
  log2(`[dreamer] map-memories starvation: circuit breaker tripped after ${consecutiveTimeouts} consecutive batch timeouts with no committed mappings (investigation made no durable progress); per-batch elapsed [${timeoutStreakElapsedMs.join("ms, ")}ms] vs ${sliceMs}ms slice; stopping with ${result.remaining} mapping(s) remaining`);
7231
7244
  break;
7232
7245
  }
7233
- } else {
7246
+ } else if (committed > 0 || outcome.failure?.class !== "timeout") {
7234
7247
  consecutiveTimeouts = 0;
7235
7248
  timeoutStreakElapsedMs = [];
7236
7249
  }
@@ -9065,6 +9078,21 @@ function verifyAssistantText(messages) {
9065
9078
  const start = text.lastIndexOf("<verify>");
9066
9079
  return start >= 0 ? text.slice(start) : text;
9067
9080
  }
9081
+ function validateVerifyCompletion(messages, text, expectedIds, responseText = text) {
9082
+ let parsed;
9083
+ try {
9084
+ parsed = validateVerifyManifest(text, expectedIds);
9085
+ } catch (error) {
9086
+ throw providerOutputFailureFromInvalidManifest(messages, responseText) ?? error;
9087
+ }
9088
+ const ids = [...parsed.verified, ...parsed.updated, ...parsed.archived, ...parsed.skipped].map((entry) => entry.id);
9089
+ if (ids.length !== expectedIds.size || ids.some((id) => !expectedIds.has(id))) {
9090
+ const providerFailure = providerOutputFailureFromInvalidManifest(messages, responseText);
9091
+ if (providerFailure)
9092
+ throw providerFailure;
9093
+ }
9094
+ return parsed;
9095
+ }
9068
9096
  var IDENTICAL_PROVIDER_FAILURE_BATCH_LIMIT = 2;
9069
9097
  var VERIFY_BATCH_FLOOR_MS = 30000;
9070
9098
  function computeVerifyBatchSliceMs(remainingMs, _batchesRemaining) {
@@ -9223,6 +9251,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
9223
9251
  const timer = setTimeout(() => batchController.abort(new Error(`verify slice timed out after ${sliceMs}ms`)), sliceMs);
9224
9252
  const batchSignal = batchController.signal;
9225
9253
  const tokenBudget = verifyBatchTokenBudget(batch.length, args.tokenBudget);
9254
+ const expectedIds = new Set(batch.map((memory) => memory.id));
9226
9255
  let agentSessionId = null;
9227
9256
  let hiddenSessionId = null;
9228
9257
  let promptSettled = false;
@@ -9275,13 +9304,10 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
9275
9304
  onBudgetUpdate({ spent: 0, finalizeFired: false, sessionId });
9276
9305
  },
9277
9306
  parse: (text, completion) => {
9278
- const providerFailure = providerOutputFailureFromInvalidManifest(completion.messages ?? [], text);
9279
- if (providerFailure)
9280
- throw providerFailure;
9281
9307
  const manifest = verifyAssistantText(completion.messages ?? []) ?? (text.trim() ? text : null);
9282
9308
  if (!manifest)
9283
9309
  throw new Error("verify returned no output");
9284
- return validateVerifyManifest(manifest, new Set(batch.map((memory) => memory.id)));
9310
+ return validateVerifyCompletion(completion.messages ?? [], manifest, expectedIds, text);
9285
9311
  }
9286
9312
  });
9287
9313
  recordInvocation4(args, startedAt, {
@@ -9343,10 +9369,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
9343
9369
  const text = verifyAssistantText(messages);
9344
9370
  if (!text)
9345
9371
  throw new Error("verify returned no output");
9346
- const providerFailure = providerOutputFailureFromInvalidManifest(messages, text);
9347
- if (providerFailure)
9348
- throw providerFailure;
9349
- return validateVerifyManifest(text, new Set(batch.map((memory) => memory.id)));
9372
+ return validateVerifyCompletion(messages, text, expectedIds);
9350
9373
  }
9351
9374
  });
9352
9375
  promptSettled = true;
@@ -11469,494 +11492,115 @@ function clearDreamerTickFailure(db) {
11469
11492
  db.prepare("DELETE FROM schema_migrations_meta WHERE key = ?").run(DREAMER_TICK_FAILURE_KEY);
11470
11493
  }
11471
11494
 
11472
- // src/features/magic-context/message-index-async.ts
11473
- var indexCancellations = new Map;
11474
- var sessionEpochs = new Map;
11475
- var workerMeasurements = new WeakMap;
11476
- function isDatabaseLockedError(error) {
11477
- if (!error || typeof error !== "object")
11495
+ // src/plugin/dream-timer.ts
11496
+ var DREAM_TIMER_INTERVAL_MS = 15 * 60 * 1000;
11497
+ var GIT_COMMIT_BACKLOG_DRAIN_MAX_MS = 5 * 60 * 1000;
11498
+ var BOOT_PROJECT_JITTER_SLOT_MS = 1000;
11499
+ var activeTimer = null;
11500
+ var tickInFlight = false;
11501
+ var startupTickTimer = null;
11502
+ var startupTimers = new Map;
11503
+ var startupJitters = new Map;
11504
+ var nextStartupJitterSlot = 0;
11505
+ function directoryStillExists(directory) {
11506
+ try {
11507
+ return statSync2(directory).isDirectory();
11508
+ } catch {
11478
11509
  return false;
11479
- const e = error;
11480
- if (typeof e.code === "string") {
11481
- if (e.code === "SQLITE_BUSY" || e.code === "SQLITE_LOCKED")
11482
- return true;
11483
- }
11484
- if (typeof e.message === "string") {
11485
- if (/database is locked/i.test(e.message))
11486
- return true;
11487
- if (/sqlite_(busy|locked)/i.test(e.message))
11488
- return true;
11489
11510
  }
11490
- return false;
11491
- }
11492
- var INCREMENTAL_DEBOUNCE_MS = 100;
11493
- var RECONCILIATION_BATCH_SIZE = 100;
11494
-
11495
- class MagicContextMessageIndexHeapHolder {
11496
- reconciledSessions = new Set;
11497
- reconciledSnapshots = new Map;
11498
- stalledSessions = new Set;
11499
- reconciliationScheduledSessions = new Set;
11500
- clearAndReindexScheduledSessions = new Map;
11501
- sessionLocks = new Map;
11502
- incrementalTimers = new Map;
11503
- pendingIncrementalKeys = new Set;
11504
- completedIncrementalKeys = new Set;
11505
- activeReconcilerBuffers = new Map;
11506
11511
  }
11507
- var heapHolder = new MagicContextMessageIndexHeapHolder;
11508
- function clearCompletedIncrementalKeys(sessionId) {
11509
- const prefix = `${sessionId}\x00`;
11510
- for (const key of heapHolder.completedIncrementalKeys) {
11511
- if (key.startsWith(prefix))
11512
- heapHolder.completedIncrementalKeys.delete(key);
11512
+ function openTimerDatabaseOrNull(context) {
11513
+ let db;
11514
+ try {
11515
+ db = openCurrentDatabase();
11516
+ } catch (error) {
11517
+ log2(`[dreamer] storage fatal; skipping ${context}: ${getErrorMessage(error)}`);
11518
+ return null;
11513
11519
  }
11514
- }
11515
- var PREPARE_RETRY_MS = 60000;
11516
- async function sourceReady(source, sessionId) {
11517
- const prepare = source?.prepare;
11518
- return typeof prepare === "function" ? prepare(sessionId) : true;
11519
- }
11520
- function defer(fn) {
11521
- const immediate = globalThis.setImmediate;
11522
- if (typeof immediate === "function") {
11523
- inRootAsyncContext2(() => immediate(fn));
11524
- return;
11520
+ if (!db) {
11521
+ log2(`[dreamer] storage unavailable; skipping ${context} (the cache schema is newer than this binary supports — restart/upgrade OpenCode/Pi/Magic Context to recover)`);
11522
+ return null;
11525
11523
  }
11526
- inRootAsyncContext2(() => setTimeout(fn, 0));
11527
- }
11528
- function yieldToEventLoop() {
11529
- return new Promise((resolve) => defer(resolve));
11524
+ return db;
11530
11525
  }
11531
- function runWithSessionLock(sessionId, operation) {
11532
- const previous = heapHolder.sessionLocks.get(sessionId) ?? Promise.resolve();
11533
- const run = previous.catch(() => {
11534
- return;
11535
- }).then(async () => {
11536
- await operation();
11537
- });
11538
- heapHolder.sessionLocks.set(sessionId, run);
11539
- run.finally(() => {
11540
- if (heapHolder.sessionLocks.get(sessionId) === run) {
11541
- heapHolder.sessionLocks.delete(sessionId);
11542
- }
11543
- }).catch(() => {
11526
+ var refusedEmptyIdentityDirectories = new Set;
11527
+ function logEmptyIdentityRefusalOnce(directory) {
11528
+ if (refusedEmptyIdentityDirectories.has(directory))
11544
11529
  return;
11545
- });
11546
- return run;
11530
+ refusedEmptyIdentityDirectories.add(directory);
11531
+ log2(`[dreamer] not registering ${directory}: it has no project identity, so no project-scoped work runs for it`);
11547
11532
  }
11548
- function logIndexingError(sessionId, action, error) {
11549
- if (isDatabaseLockedError(error)) {
11550
- sessionLog2(sessionId, `message FTS async ${action} skipped (database busy; will retry on next reconciliation)`);
11533
+ var registeredProjects = new Map;
11534
+ function stopDreamScheduleTimerIfIdle() {
11535
+ if (registeredProjects.size !== 0)
11551
11536
  return;
11537
+ const stopped = activeTimer !== null || startupTickTimer !== null;
11538
+ if (activeTimer) {
11539
+ clearInterval(activeTimer);
11540
+ activeTimer = null;
11552
11541
  }
11553
- sessionLog2(sessionId, `message FTS async ${action} failed: ${error instanceof Error ? error.message : String(error)}`);
11554
- log2(`[message-index-async] ${action} failed for ${sessionId}:`, error);
11555
- }
11556
- function serializedMessageBytes(messages) {
11557
- try {
11558
- return Buffer.byteLength(JSON.stringify(messages));
11559
- } catch {
11560
- return 0;
11542
+ if (startupTickTimer) {
11543
+ clearTimeout(startupTickTimer);
11544
+ startupTickTimer = null;
11561
11545
  }
11546
+ startupJitters.clear();
11547
+ nextStartupJitterSlot = 0;
11548
+ if (stopped)
11549
+ log2("[dreamer] stopped dream schedule timer (no projects left)");
11562
11550
  }
11563
- function logReconciliationStall(sessionId, reason) {
11564
- if (heapHolder.stalledSessions.has(sessionId))
11551
+ async function startDreamScheduleTimer(args) {
11552
+ if (!isUsableProjectIdentity(args.projectIdentity)) {
11553
+ logEmptyIdentityRefusalOnce(args.directory);
11565
11554
  return;
11566
- heapHolder.stalledSessions.add(sessionId);
11567
- sessionLog2(sessionId, `message FTS reconciliation stalled: ${reason}; will retry`);
11568
- }
11569
- async function reconcileSessionIndex(db, sessionId, readMessages) {
11570
- if (!await sourceReady(readMessages, sessionId)) {
11571
- logReconciliationStall(sessionId, "raw message reader is not ready");
11572
- return false;
11573
11555
  }
11574
- await runWithSessionLock(sessionId, async () => {
11575
- if (!readMessages.getRevision && heapHolder.reconciledSessions.has(sessionId))
11556
+ beginBootQuietPeriod();
11557
+ const db = openTimerDatabaseOrNull("schedule timer registration");
11558
+ if (!db)
11559
+ return;
11560
+ const dreamingEnabled = Boolean(args.dreamerConfig && args.dreamerConfig.disable !== true);
11561
+ const embeddingSweepEnabled = args.memoryEnabled === true;
11562
+ const commitIndexingEnabled = args.gitCommitIndexing?.enabled === true;
11563
+ const previousRegistration = registeredProjects.get(args.directory);
11564
+ const isNewRegistration = previousRegistration === undefined;
11565
+ if (previousRegistration && previousRegistration !== args) {
11566
+ const pendingStartup = startupTimers.get(args.directory);
11567
+ if (pendingStartup) {
11568
+ clearTimeout(pendingStartup);
11569
+ startupTimers.delete(args.directory);
11570
+ }
11571
+ }
11572
+ registeredProjects.set(args.directory, args);
11573
+ if (isNewRegistration) {
11574
+ log2(`[dreamer] registered project ${args.projectIdentity} (dreaming=${dreamingEnabled} embeddings=${embeddingSweepEnabled} commits=${commitIndexingEnabled}; total=${registeredProjects.size})`);
11575
+ }
11576
+ if (!activeTimer) {
11577
+ log2(`[dreamer] started independent schedule timer (every ${DREAM_TIMER_INTERVAL_MS / 60000}m)`);
11578
+ startupTickTimer = scheduleAfterBootQuiet(() => {
11579
+ startupTickTimer = null;
11580
+ runTick("startup");
11581
+ });
11582
+ const timer = inRootAsyncContext2(() => setInterval(() => runTick("interval"), DREAM_TIMER_INTERVAL_MS));
11583
+ if (typeof timer === "object" && "unref" in timer) {
11584
+ timer.unref();
11585
+ }
11586
+ activeTimer = timer;
11587
+ } else if (isNewRegistration || previousRegistration !== args) {
11588
+ scheduleInitialProjectRun(args, db);
11589
+ }
11590
+ return () => {
11591
+ if (registeredProjects.get(args.directory) !== args) {
11592
+ stopDreamScheduleTimerIfIdle();
11576
11593
  return;
11577
- let fallbackSnapshot = null;
11578
- try {
11579
- const fullReader = typeof readMessages === "function" ? readMessages : null;
11580
- const revision = readMessages.getRevision?.(sessionId);
11581
- const previous = heapHolder.reconciledSnapshots.get(sessionId);
11582
- const finalWatermark = previous && revision === previous.revision ? previous.count : revision !== undefined && readMessages.getCountAfterRevision ? previous && revision > previous.revision ? previous.count + readMessages.getCountAfterRevision(sessionId, previous.revision, revision) : readMessages.getCountAfterRevision(sessionId, -1, revision) : readMessages.getCount ? readMessages.getCount(sessionId) : (fallbackSnapshot = fullReader?.(sessionId) ?? []).length;
11583
- let cursor = getMessageIndexReconciliationStartOrdinal2(db, sessionId);
11584
- let after = cursor === previous?.count ? previous.after : undefined;
11585
- while (cursor < finalWatermark) {
11586
- const pageEnd = Math.min(finalWatermark, cursor + RECONCILIATION_BATCH_SIZE);
11587
- const messages = readMessages.readPage ? readMessages.readPage(sessionId, cursor, RECONCILIATION_BATCH_SIZE, finalWatermark, after) : (fallbackSnapshot ?? []).filter((message) => message.ordinal > cursor && message.ordinal <= pageEnd);
11588
- const retainedMessages = fallbackSnapshot ?? messages;
11589
- heapHolder.activeReconcilerBuffers.set(sessionId, retainedMessages);
11590
- withSqliteBackgroundWriter2(() => indexMessagesAfterOrdinal2(db, sessionId, messages, cursor, pageEnd));
11591
- const nextCursor = getMessageIndexReconciliationStartOrdinal2(db, sessionId);
11592
- if (nextCursor <= cursor)
11593
- break;
11594
- cursor = nextCursor;
11595
- const last = messages.at(-1);
11596
- after = last?.ordinal === cursor && typeof last.createdAt === "number" ? { timeCreated: last.createdAt, id: last.id } : undefined;
11597
- if (cursor < finalWatermark) {
11598
- await yieldToEventLoop();
11599
- }
11600
- }
11601
- if (isMessageIndexReconciledThrough2(db, sessionId, finalWatermark)) {
11602
- heapHolder.reconciledSessions.add(sessionId);
11603
- if (revision !== undefined) {
11604
- if (!after && finalWatermark > 0 && readMessages.readPage) {
11605
- const last = readMessages.readPage(sessionId, finalWatermark - 1, 1, finalWatermark).at(-1);
11606
- if (last?.ordinal === finalWatermark && typeof last.createdAt === "number")
11607
- after = { timeCreated: last.createdAt, id: last.id };
11608
- }
11609
- heapHolder.reconciledSnapshots.set(sessionId, {
11610
- revision,
11611
- count: finalWatermark,
11612
- after
11613
- });
11614
- }
11615
- } else {
11616
- logReconciliationStall(sessionId, `gap after ordinal ${cursor} (store count ${finalWatermark})`);
11617
- }
11618
- } finally {
11619
- heapHolder.activeReconcilerBuffers.delete(sessionId);
11620
11594
  }
11621
- });
11622
- return true;
11623
- }
11624
- function scheduleReconciliation(db, sessionId, readMessages) {
11625
- if (!readMessages.getRevision && heapHolder.reconciledSessions.has(sessionId) || heapHolder.reconciliationScheduledSessions.has(sessionId)) {
11626
- return;
11627
- }
11628
- heapHolder.reconciliationScheduledSessions.add(sessionId);
11629
- scheduleAfterBootQuiet(() => {
11630
- defer(() => {
11631
- let appendedDuringReconciliation = false;
11632
- const previousSnapshot = heapHolder.reconciledSnapshots.get(sessionId);
11633
- reconcileSessionIndex(db, sessionId, readMessages).then((ran) => {
11634
- if (ran) {
11635
- const snapshot = heapHolder.reconciledSnapshots.get(sessionId);
11636
- appendedDuringReconciliation = snapshot !== undefined && snapshot !== previousSnapshot && readMessages.getRevision !== undefined && readMessages.getRevision(sessionId) !== snapshot.revision;
11637
- return;
11638
- }
11639
- inRootAsyncContext2(() => setTimeout(() => {
11640
- heapHolder.reconciliationScheduledSessions.delete(sessionId);
11641
- scheduleReconciliation(db, sessionId, readMessages);
11642
- }, PREPARE_RETRY_MS)).unref?.();
11643
- }).catch((error) => {
11644
- logReconciliationStall(sessionId, error instanceof Error ? error.message : String(error));
11645
- if (isDatabaseLockedError(error))
11646
- inRootAsyncContext2(() => setTimeout(() => scheduleReconciliation(db, sessionId, readMessages), 1000)).unref();
11647
- }).finally(() => {
11648
- heapHolder.reconciliationScheduledSessions.delete(sessionId);
11649
- if (appendedDuringReconciliation)
11650
- scheduleReconciliation(db, sessionId, readMessages);
11651
- });
11652
- });
11653
- });
11654
- }
11655
- function scheduleIncrementalIndex(db, sessionId, messageId, messageSource) {
11656
- const schedulingKey = `${sessionId}\x00${messageId}`;
11657
- const epoch = sessionEpochs.get(sessionId) ?? 0;
11658
- if (heapHolder.pendingIncrementalKeys.has(schedulingKey))
11659
- return;
11660
- const activeTimer = heapHolder.incrementalTimers.get(schedulingKey);
11661
- if (activeTimer)
11662
- clearTimeout(activeTimer);
11663
- const timer = inRootAsyncContext2(() => setTimeout(() => {
11664
- heapHolder.incrementalTimers.delete(schedulingKey);
11665
- heapHolder.pendingIncrementalKeys.add(schedulingKey);
11666
- const indexMessage = async () => {
11667
- if ((sessionEpochs.get(sessionId) ?? 0) !== epoch || !heapHolder.pendingIncrementalKeys.has(schedulingKey))
11668
- return;
11669
- const message = typeof messageSource === "function" ? messageSource(sessionId, messageId) : messageSource;
11670
- if (!message)
11671
- return;
11672
- const revisionKey = `${schedulingKey}\x00${getMessageIndexSourceIdentity2(message)}`;
11673
- if (heapHolder.completedIncrementalKeys.has(revisionKey))
11674
- return;
11675
- const currentWatermark = getLastIndexedOrdinal2(db, sessionId);
11676
- if (message.ordinal <= currentWatermark && isMessageIndexSourceCurrent2(db, sessionId, message)) {
11677
- heapHolder.completedIncrementalKeys.add(revisionKey);
11678
- return;
11679
- }
11680
- const wasReconciled = heapHolder.reconciledSessions.delete(sessionId);
11681
- await indexSingleMessageOffThread(db, sessionId, message);
11682
- if ((sessionEpochs.get(sessionId) ?? 0) !== epoch || !heapHolder.pendingIncrementalKeys.has(schedulingKey))
11683
- return;
11684
- const finalWatermark = getLastIndexedOrdinal2(db, sessionId);
11685
- if (wasReconciled && isMessageIndexReconciledThrough2(db, sessionId, finalWatermark)) {
11686
- heapHolder.reconciledSessions.add(sessionId);
11687
- }
11688
- heapHolder.completedIncrementalKeys.add(revisionKey);
11689
- };
11690
- sourceReady(messageSource, sessionId).then((ready) => {
11691
- if ((sessionEpochs.get(sessionId) ?? 0) !== epoch)
11692
- return;
11693
- if (ready)
11694
- return runWithSessionLock(sessionId, indexMessage);
11695
- setTimeout(() => scheduleIncrementalIndex(db, sessionId, messageId, messageSource), PREPARE_RETRY_MS).unref?.();
11696
- }).catch((error) => {
11697
- if ((sessionEpochs.get(sessionId) ?? 0) !== epoch)
11698
- return;
11699
- heapHolder.reconciledSessions.delete(sessionId);
11700
- logIndexingError(sessionId, `incremental index for ${messageId}`, error);
11701
- if (isDatabaseLockedError(error))
11702
- setTimeout(() => scheduleIncrementalIndex(db, sessionId, messageId, messageSource), 1000).unref();
11703
- }).finally(() => {
11704
- if ((sessionEpochs.get(sessionId) ?? 0) === epoch)
11705
- heapHolder.pendingIncrementalKeys.delete(schedulingKey);
11706
- });
11707
- }, INCREMENTAL_DEBOUNCE_MS));
11708
- heapHolder.incrementalTimers.set(schedulingKey, timer);
11709
- }
11710
- async function indexSingleMessageOffThread(db, sessionId, message) {
11711
- const path = getSqliteDatabasePath2(db);
11712
- if (!path)
11713
- return withSqliteBackgroundWriter2(() => indexSingleMessage2(db, sessionId, message));
11714
- const cancellation = new Int32Array(new SharedArrayBuffer(4));
11715
- const active = indexCancellations.get(sessionId) ?? new Set;
11716
- active.add(cancellation);
11717
- indexCancellations.set(sessionId, active);
11718
- const result = await runSessionWarmupWorker({
11719
- path,
11720
- sessionId,
11721
- indexMessage: message,
11722
- harness: getHarness2(),
11723
- indexCancellation: cancellation.buffer,
11724
- ...indexCoordinationInput2()
11725
- }, {}).finally(() => {
11726
- active.delete(cancellation);
11727
- if (active.size === 0)
11728
- indexCancellations.delete(sessionId);
11729
- });
11730
- if (!("inserted" in result))
11731
- throw new Error("failure" in result ? result.detail : "unexpected indexing worker reply");
11732
- if (result.measurements) {
11733
- const sessions = workerMeasurements.get(db) ?? new Map;
11734
- sessions.set(sessionId, result.measurements);
11735
- workerMeasurements.set(db, sessions);
11736
- if (result.measurements.skipped)
11737
- log2(`[magic-context] incremental FTS skipped oversized source ${message.id} (${result.measurements.sourceBytes} bytes); identity and progress persisted`);
11738
- }
11739
- return result.inserted;
11740
- }
11741
- async function sweepOrphanedOpenCodeMessageIndexesOffThread(db, openSource, options = {}) {
11742
- if (getHarness2() === "opencode" || getHarness2() === "opencode2")
11743
- options = {
11744
- ...options,
11745
- fenceLockNames: options.fenceLockNames ?? takeLkgRecoveryLockSweepPage()
11746
- };
11747
- if (getHarness2() === "opencode2")
11748
- openSource = () => new Database2(gaDatabasePath(getDataDir(), process.env.OPENCODE_CHANNEL ?? "latest"), {
11749
- readonly: true,
11750
- fileMustExist: true
11751
- });
11752
- const path = getSqliteDatabasePath2(db);
11753
- if (!path || getHarness2() !== "opencode" && getHarness2() !== "opencode2")
11754
- return sweepOrphanedOpenCodeMessageIndexesCooperatively2(db, openSource, options);
11755
- let source = null;
11756
- let sourcePath = null;
11757
- try {
11758
- source = openSource();
11759
- if (source)
11760
- sourcePath = getSqliteDatabasePath2(source);
11761
- } catch {} finally {
11762
- if (sourcePath)
11763
- source?.close();
11764
- }
11765
- if (!sourcePath)
11766
- return sweepOrphanedOpenCodeMessageIndexesCooperatively2(db, () => source, options);
11767
- const reply = await runSessionWarmupWorker({
11768
- path,
11769
- sessionId: "",
11770
- harness: getHarness2(),
11771
- orphanSweep: { sourcePath, options }
11772
- }, {});
11773
- if (!("orphanSweep" in reply))
11774
- throw new Error("message history orphan sweep worker failed");
11775
- return reply.orphanSweep;
11776
- }
11777
- function scheduleClearAndReindex(db, sessionId, readMessages) {
11778
- heapHolder.reconciledSessions.delete(sessionId);
11779
- heapHolder.reconciledSnapshots.delete(sessionId);
11780
- heapHolder.reconciliationScheduledSessions.delete(sessionId);
11781
- clearCompletedIncrementalKeys(sessionId);
11782
- const existing = heapHolder.clearAndReindexScheduledSessions.get(sessionId);
11783
- if (existing) {
11784
- existing.source = readMessages;
11785
- return;
11786
- }
11787
- const pending = { source: readMessages };
11788
- heapHolder.clearAndReindexScheduledSessions.set(sessionId, pending);
11789
- scheduleAfterBootQuiet(() => {
11790
- defer(() => {
11791
- let cleared = false;
11792
- runWithSessionLock(sessionId, () => {
11793
- if (heapHolder.clearAndReindexScheduledSessions.get(sessionId) !== pending)
11794
- return;
11795
- heapHolder.clearAndReindexScheduledSessions.delete(sessionId);
11796
- heapHolder.reconciledSessions.delete(sessionId);
11797
- heapHolder.reconciledSnapshots.delete(sessionId);
11798
- clearCompletedIncrementalKeys(sessionId);
11799
- clearIndexedMessages2(db, sessionId);
11800
- cleared = true;
11801
- }).then(() => cleared ? reconcileSessionIndex(db, sessionId, pending.source) : undefined).catch((error) => {
11802
- heapHolder.reconciledSessions.delete(sessionId);
11803
- if (heapHolder.clearAndReindexScheduledSessions.get(sessionId) === pending)
11804
- heapHolder.clearAndReindexScheduledSessions.delete(sessionId);
11805
- logIndexingError(sessionId, "clear and reindex", error);
11806
- });
11807
- });
11808
- });
11809
- }
11810
- function getMessageIndexQueueHeapStats() {
11811
- return {
11812
- queueLength: heapHolder.reconciliationScheduledSessions.size + heapHolder.incrementalTimers.size + heapHolder.pendingIncrementalKeys.size,
11813
- reconciliationScheduled: heapHolder.reconciliationScheduledSessions.size,
11814
- incrementalTimers: heapHolder.incrementalTimers.size,
11815
- pendingIncremental: heapHolder.pendingIncrementalKeys.size,
11816
- activeSessionLocks: heapHolder.sessionLocks.size,
11817
- completedIncrementalKeys: heapHolder.completedIncrementalKeys.size,
11818
- activeBufferMessages: [...heapHolder.activeReconcilerBuffers.values()].reduce((sum, messages) => sum + messages.length, 0),
11819
- activeBufferBytes: [...heapHolder.activeReconcilerBuffers.values()].reduce((sum, messages) => sum + serializedMessageBytes(messages), 0)
11820
- };
11821
- }
11822
- function clearSessionTracking(sessionId) {
11823
- sessionEpochs.set(sessionId, (sessionEpochs.get(sessionId) ?? 0) + 1);
11824
- for (const cancellation of indexCancellations.get(sessionId) ?? [])
11825
- Atomics.store(cancellation, 0, 1);
11826
- heapHolder.clearAndReindexScheduledSessions.delete(sessionId);
11827
- heapHolder.reconciledSessions.delete(sessionId);
11828
- heapHolder.reconciledSnapshots.delete(sessionId);
11829
- heapHolder.stalledSessions.delete(sessionId);
11830
- heapHolder.reconciliationScheduledSessions.delete(sessionId);
11831
- heapHolder.sessionLocks.delete(sessionId);
11832
- heapHolder.activeReconcilerBuffers.delete(sessionId);
11833
- const prefix = `${sessionId}\x00`;
11834
- for (const [key, timer] of heapHolder.incrementalTimers) {
11835
- if (key.startsWith(prefix)) {
11836
- clearTimeout(timer);
11837
- heapHolder.incrementalTimers.delete(key);
11838
- }
11839
- }
11840
- for (const key of heapHolder.pendingIncrementalKeys) {
11841
- if (key.startsWith(prefix)) {
11842
- heapHolder.pendingIncrementalKeys.delete(key);
11843
- }
11844
- }
11845
- for (const key of heapHolder.completedIncrementalKeys) {
11846
- if (key.startsWith(prefix))
11847
- heapHolder.completedIncrementalKeys.delete(key);
11848
- }
11849
- }
11850
-
11851
- // src/plugin/dream-timer.ts
11852
- var DREAM_TIMER_INTERVAL_MS = 15 * 60 * 1000;
11853
- var GIT_COMMIT_BACKLOG_DRAIN_MAX_MS = 5 * 60 * 1000;
11854
- var BOOT_PROJECT_JITTER_SLOT_MS = 1000;
11855
- var activeTimer = null;
11856
- var tickInFlight = false;
11857
- var startupTickTimer = null;
11858
- var startupTimers = new Map;
11859
- var startupJitters = new Map;
11860
- var nextStartupJitterSlot = 0;
11861
- function directoryStillExists(directory) {
11862
- try {
11863
- return statSync2(directory).isDirectory();
11864
- } catch {
11865
- return false;
11866
- }
11867
- }
11868
- function openTimerDatabaseOrNull(context) {
11869
- let db;
11870
- try {
11871
- db = openCurrentDatabase();
11872
- } catch (error) {
11873
- log2(`[dreamer] storage fatal; skipping ${context}: ${getErrorMessage(error)}`);
11874
- return null;
11875
- }
11876
- if (!db) {
11877
- log2(`[dreamer] storage unavailable; skipping ${context} (the cache schema is newer than this binary supports — restart/upgrade OpenCode/Pi/Magic Context to recover)`);
11878
- return null;
11879
- }
11880
- return db;
11881
- }
11882
- var refusedEmptyIdentityDirectories = new Set;
11883
- function logEmptyIdentityRefusalOnce(directory) {
11884
- if (refusedEmptyIdentityDirectories.has(directory))
11885
- return;
11886
- refusedEmptyIdentityDirectories.add(directory);
11887
- log2(`[dreamer] not registering ${directory}: it has no project identity, so no project-scoped work runs for it`);
11888
- }
11889
- var registeredProjects = new Map;
11890
- function stopDreamScheduleTimerIfIdle() {
11891
- if (registeredProjects.size !== 0)
11892
- return;
11893
- const stopped = activeTimer !== null || startupTickTimer !== null;
11894
- if (activeTimer) {
11895
- clearInterval(activeTimer);
11896
- activeTimer = null;
11897
- }
11898
- if (startupTickTimer) {
11899
- clearTimeout(startupTickTimer);
11900
- startupTickTimer = null;
11901
- }
11902
- startupJitters.clear();
11903
- nextStartupJitterSlot = 0;
11904
- if (stopped)
11905
- log2("[dreamer] stopped dream schedule timer (no projects left)");
11906
- }
11907
- async function startDreamScheduleTimer(args) {
11908
- if (!isUsableProjectIdentity(args.projectIdentity)) {
11909
- logEmptyIdentityRefusalOnce(args.directory);
11910
- return;
11911
- }
11912
- beginBootQuietPeriod();
11913
- const db = openTimerDatabaseOrNull("schedule timer registration");
11914
- if (!db)
11915
- return;
11916
- const dreamingEnabled = Boolean(args.dreamerConfig && args.dreamerConfig.disable !== true);
11917
- const embeddingSweepEnabled = args.memoryEnabled === true;
11918
- const commitIndexingEnabled = args.gitCommitIndexing?.enabled === true;
11919
- const previousRegistration = registeredProjects.get(args.directory);
11920
- const isNewRegistration = previousRegistration === undefined;
11921
- if (previousRegistration && previousRegistration !== args) {
11922
- const pendingStartup = startupTimers.get(args.directory);
11923
- if (pendingStartup) {
11924
- clearTimeout(pendingStartup);
11925
- startupTimers.delete(args.directory);
11926
- }
11927
- }
11928
- registeredProjects.set(args.directory, args);
11929
- if (isNewRegistration) {
11930
- log2(`[dreamer] registered project ${args.projectIdentity} (dreaming=${dreamingEnabled} embeddings=${embeddingSweepEnabled} commits=${commitIndexingEnabled}; total=${registeredProjects.size})`);
11931
- }
11932
- if (!activeTimer) {
11933
- log2(`[dreamer] started independent schedule timer (every ${DREAM_TIMER_INTERVAL_MS / 60000}m)`);
11934
- startupTickTimer = scheduleAfterBootQuiet(() => {
11935
- startupTickTimer = null;
11936
- runTick("startup");
11937
- });
11938
- const timer = inRootAsyncContext2(() => setInterval(() => runTick("interval"), DREAM_TIMER_INTERVAL_MS));
11939
- if (typeof timer === "object" && "unref" in timer) {
11940
- timer.unref();
11941
- }
11942
- activeTimer = timer;
11943
- } else if (isNewRegistration || previousRegistration !== args) {
11944
- scheduleInitialProjectRun(args, db);
11945
- }
11946
- return () => {
11947
- if (registeredProjects.get(args.directory) !== args) {
11948
- stopDreamScheduleTimerIfIdle();
11949
- return;
11950
- }
11951
- registeredProjects.delete(args.directory);
11952
- const startupTimer = startupTimers.get(args.directory);
11953
- if (startupTimer) {
11954
- clearTimeout(startupTimer);
11955
- startupTimers.delete(args.directory);
11956
- }
11957
- log2(`[dreamer] unregistered project ${args.projectIdentity} (remaining=${registeredProjects.size})`);
11958
- stopDreamScheduleTimerIfIdle();
11959
- };
11595
+ registeredProjects.delete(args.directory);
11596
+ const startupTimer = startupTimers.get(args.directory);
11597
+ if (startupTimer) {
11598
+ clearTimeout(startupTimer);
11599
+ startupTimers.delete(args.directory);
11600
+ }
11601
+ log2(`[dreamer] unregistered project ${args.projectIdentity} (remaining=${registeredProjects.size})`);
11602
+ stopDreamScheduleTimerIfIdle();
11603
+ };
11960
11604
  }
11961
11605
  var tickStages = {
11962
11606
  runMessageHistoryMaintenance,
@@ -12043,12 +11687,6 @@ async function runMessageHistoryMaintenance(db) {
12043
11687
  log2(`[message-index] pending Rust session cleanup: cleared=${rustCleanup.cleared} failed=${rustCleanup.failedSessionIds.length}`);
12044
11688
  }
12045
11689
  }
12046
- const sweep = await sweepOrphanedOpenCodeMessageIndexesOffThread(db, openOpenCodeDb);
12047
- if (sweep.reason)
12048
- log2(`[message-index] orphan sweep parked: ${sweep.reason}`);
12049
- if (sweep.deleted > 0) {
12050
- log2(`[message-index] orphan sweep: scanned=${sweep.scanned} deleted=${sweep.deleted} cursor=${sweep.cursor || "<complete>"}`);
12051
- }
12052
11690
  }
12053
11691
  function startupJitterMs(directory) {
12054
11692
  const existing = startupJitters.get(directory);
@@ -13049,96 +12687,439 @@ function createScheduler(config) {
13049
12687
  }
13050
12688
  return "defer";
13051
12689
  }
13052
- };
12690
+ };
12691
+ }
12692
+ function computeHardCacheExpired(cacheTtl, lastResponseTime, now, onInvalid) {
12693
+ let ttlMs;
12694
+ try {
12695
+ ttlMs = parseCacheTtl(cacheTtl);
12696
+ } catch (error) {
12697
+ onInvalid?.(error);
12698
+ ttlMs = 5 * 60 * 1000;
12699
+ }
12700
+ return lastResponseTime > 0 && now - lastResponseTime > ttlMs;
12701
+ }
12702
+
12703
+ // src/features/magic-context/storage-unavailable-reason.ts
12704
+ function describeStorageUnavailability(fallbackCause) {
12705
+ const migration = getMigrationOnOpenRefusal();
12706
+ const blockingProcesses = migration?.blockingProcesses ?? migration?.serverPids.map((pid) => ({ kind: "process", pid })) ?? [];
12707
+ if (migration && (blockingProcesses.length > 0 || migration.unreadableFile)) {
12708
+ return {
12709
+ kind: "migration_guard",
12710
+ persistedVersion: migration.persistedVersion,
12711
+ supportedVersion: migration.supportedVersion,
12712
+ blockingProcesses,
12713
+ ...migration.unreadableFile ? { unreadableFile: migration.unreadableFile } : {},
12714
+ ...migration.unreadableArm ? { unreadableArm: migration.unreadableArm } : {}
12715
+ };
12716
+ }
12717
+ const fence = getSchemaFenceRejection();
12718
+ if (fence) {
12719
+ return {
12720
+ kind: "schema_fence",
12721
+ persistedVersion: fence.persistedVersion,
12722
+ supportedVersion: fence.supportedVersion
12723
+ };
12724
+ }
12725
+ return { kind: "storage_failure", cause: fallbackCause };
12726
+ }
12727
+
12728
+ // src/plugin/rpc-handlers.ts
12729
+ import { randomUUID as randomUUID3 } from "node:crypto";
12730
+ import { once } from "node:events";
12731
+ import { rmSync as rmSync2 } from "node:fs";
12732
+ import { join as join5 } from "node:path";
12733
+
12734
+ // src/features/magic-context/memory/memory-diagnostics.ts
12735
+ function emptyMemoryImportanceHistogram() {
12736
+ return {
12737
+ total: 0,
12738
+ unclassified: 0,
12739
+ bands: {
12740
+ "0-19": 0,
12741
+ "20-39": 0,
12742
+ "40-59": 0,
12743
+ "60-79": 0,
12744
+ "80-100": 0
12745
+ }
12746
+ };
12747
+ }
12748
+ function readHistogramRow(db, projectPath, classifiedColumn) {
12749
+ const unclassified = classifiedColumn ? "SUM(CASE WHEN classified_at IS NULL THEN 1 ELSE 0 END)" : "COUNT(*)";
12750
+ return db.prepare(`SELECT COUNT(*) AS total,
12751
+ ${unclassified} AS unclassified,
12752
+ SUM(CASE WHEN COALESCE(importance, 50) BETWEEN 0 AND 19 THEN 1 ELSE 0 END) AS band_0_19,
12753
+ SUM(CASE WHEN COALESCE(importance, 50) BETWEEN 20 AND 39 THEN 1 ELSE 0 END) AS band_20_39,
12754
+ SUM(CASE WHEN COALESCE(importance, 50) BETWEEN 40 AND 59 THEN 1 ELSE 0 END) AS band_40_59,
12755
+ SUM(CASE WHEN COALESCE(importance, 50) BETWEEN 60 AND 79 THEN 1 ELSE 0 END) AS band_60_79,
12756
+ SUM(CASE WHEN COALESCE(importance, 50) BETWEEN 80 AND 100 THEN 1 ELSE 0 END) AS band_80_100
12757
+ FROM memories
12758
+ WHERE project_path = ? AND status = 'active'`).get(projectPath);
12759
+ }
12760
+ function getActiveMemoryImportanceHistogram(db, projectPath) {
12761
+ let row;
12762
+ try {
12763
+ row = readHistogramRow(db, projectPath, true);
12764
+ } catch (error) {
12765
+ if (!String(error).includes("no such column: classified_at"))
12766
+ throw error;
12767
+ row = readHistogramRow(db, projectPath, false);
12768
+ }
12769
+ return {
12770
+ total: Number(row.total ?? 0),
12771
+ unclassified: Number(row.unclassified ?? 0),
12772
+ bands: {
12773
+ "0-19": Number(row.band_0_19 ?? 0),
12774
+ "20-39": Number(row.band_20_39 ?? 0),
12775
+ "40-59": Number(row.band_40_59 ?? 0),
12776
+ "60-79": Number(row.band_60_79 ?? 0),
12777
+ "80-100": Number(row.band_80_100 ?? 0)
12778
+ }
12779
+ };
12780
+ }
12781
+
12782
+ // src/features/magic-context/message-index-async.ts
12783
+ var indexCancellations = new Map;
12784
+ var sessionEpochs = new Map;
12785
+ var workerMeasurements = new WeakMap;
12786
+ function isDatabaseLockedError(error) {
12787
+ if (!error || typeof error !== "object")
12788
+ return false;
12789
+ const e = error;
12790
+ if (typeof e.code === "string") {
12791
+ if (e.code === "SQLITE_BUSY" || e.code === "SQLITE_LOCKED")
12792
+ return true;
12793
+ }
12794
+ if (typeof e.message === "string") {
12795
+ if (/database is locked/i.test(e.message))
12796
+ return true;
12797
+ if (/sqlite_(busy|locked)/i.test(e.message))
12798
+ return true;
12799
+ }
12800
+ return false;
12801
+ }
12802
+ var INCREMENTAL_DEBOUNCE_MS = 100;
12803
+ var RECONCILIATION_BATCH_SIZE = 100;
12804
+
12805
+ class MagicContextMessageIndexHeapHolder {
12806
+ reconciledSessions = new Set;
12807
+ reconciledSnapshots = new Map;
12808
+ stalledSessions = new Set;
12809
+ reconciliationScheduledSessions = new Set;
12810
+ clearAndReindexScheduledSessions = new Map;
12811
+ sessionLocks = new Map;
12812
+ incrementalTimers = new Map;
12813
+ pendingIncrementalKeys = new Set;
12814
+ completedIncrementalKeys = new Set;
12815
+ activeReconcilerBuffers = new Map;
12816
+ }
12817
+ var heapHolder = new MagicContextMessageIndexHeapHolder;
12818
+ function clearCompletedIncrementalKeys(sessionId) {
12819
+ const prefix = `${sessionId}\x00`;
12820
+ for (const key of heapHolder.completedIncrementalKeys) {
12821
+ if (key.startsWith(prefix))
12822
+ heapHolder.completedIncrementalKeys.delete(key);
12823
+ }
12824
+ }
12825
+ var PREPARE_RETRY_MS = 60000;
12826
+ async function sourceReady(source, sessionId) {
12827
+ const prepare = source?.prepare;
12828
+ return typeof prepare === "function" ? prepare(sessionId) : true;
12829
+ }
12830
+ function defer(fn) {
12831
+ const immediate = globalThis.setImmediate;
12832
+ if (typeof immediate === "function") {
12833
+ inRootAsyncContext2(() => immediate(fn));
12834
+ return;
12835
+ }
12836
+ inRootAsyncContext2(() => setTimeout(fn, 0));
12837
+ }
12838
+ function yieldToEventLoop() {
12839
+ return new Promise((resolve) => defer(resolve));
12840
+ }
12841
+ function runWithSessionLock(sessionId, operation) {
12842
+ const previous = heapHolder.sessionLocks.get(sessionId) ?? Promise.resolve();
12843
+ const run = previous.catch(() => {
12844
+ return;
12845
+ }).then(async () => {
12846
+ await operation();
12847
+ });
12848
+ heapHolder.sessionLocks.set(sessionId, run);
12849
+ run.finally(() => {
12850
+ if (heapHolder.sessionLocks.get(sessionId) === run) {
12851
+ heapHolder.sessionLocks.delete(sessionId);
12852
+ }
12853
+ }).catch(() => {
12854
+ return;
12855
+ });
12856
+ return run;
12857
+ }
12858
+ function logIndexingError(sessionId, action, error) {
12859
+ if (isDatabaseLockedError(error)) {
12860
+ sessionLog2(sessionId, `message FTS async ${action} skipped (database busy; will retry on next reconciliation)`);
12861
+ return;
12862
+ }
12863
+ sessionLog2(sessionId, `message FTS async ${action} failed: ${error instanceof Error ? error.message : String(error)}`);
12864
+ log2(`[message-index-async] ${action} failed for ${sessionId}:`, error);
12865
+ }
12866
+ function serializedMessageBytes(messages) {
12867
+ try {
12868
+ return Buffer.byteLength(JSON.stringify(messages));
12869
+ } catch {
12870
+ return 0;
12871
+ }
12872
+ }
12873
+ function logReconciliationStall(sessionId, reason) {
12874
+ if (heapHolder.stalledSessions.has(sessionId))
12875
+ return;
12876
+ heapHolder.stalledSessions.add(sessionId);
12877
+ sessionLog2(sessionId, `message FTS reconciliation stalled: ${reason}; will retry`);
12878
+ }
12879
+ async function reconcileSessionIndex(db, sessionId, readMessages) {
12880
+ if (!await sourceReady(readMessages, sessionId)) {
12881
+ logReconciliationStall(sessionId, "raw message reader is not ready");
12882
+ return false;
12883
+ }
12884
+ await runWithSessionLock(sessionId, async () => {
12885
+ if (!readMessages.getRevision && heapHolder.reconciledSessions.has(sessionId))
12886
+ return;
12887
+ let fallbackSnapshot = null;
12888
+ try {
12889
+ const fullReader = typeof readMessages === "function" ? readMessages : null;
12890
+ const revision = readMessages.getRevision?.(sessionId);
12891
+ const previous = heapHolder.reconciledSnapshots.get(sessionId);
12892
+ const finalWatermark = previous && revision === previous.revision ? previous.count : revision !== undefined && readMessages.getCountAfterRevision ? previous && revision > previous.revision ? previous.count + readMessages.getCountAfterRevision(sessionId, previous.revision, revision) : readMessages.getCountAfterRevision(sessionId, -1, revision) : readMessages.getCount ? readMessages.getCount(sessionId) : (fallbackSnapshot = fullReader?.(sessionId) ?? []).length;
12893
+ let cursor = getMessageIndexReconciliationStartOrdinal(db, sessionId);
12894
+ let after = cursor === previous?.count ? previous.after : undefined;
12895
+ while (cursor < finalWatermark) {
12896
+ const pageEnd = Math.min(finalWatermark, cursor + RECONCILIATION_BATCH_SIZE);
12897
+ const messages = readMessages.readPage ? readMessages.readPage(sessionId, cursor, RECONCILIATION_BATCH_SIZE, finalWatermark, after) : (fallbackSnapshot ?? []).filter((message) => message.ordinal > cursor && message.ordinal <= pageEnd);
12898
+ const retainedMessages = fallbackSnapshot ?? messages;
12899
+ heapHolder.activeReconcilerBuffers.set(sessionId, retainedMessages);
12900
+ withSqliteBackgroundWriter2(() => indexMessagesAfterOrdinal(db, sessionId, messages, cursor, pageEnd));
12901
+ const nextCursor = getMessageIndexReconciliationStartOrdinal(db, sessionId);
12902
+ if (nextCursor <= cursor)
12903
+ break;
12904
+ cursor = nextCursor;
12905
+ const last = messages.at(-1);
12906
+ after = last?.ordinal === cursor && typeof last.createdAt === "number" ? { timeCreated: last.createdAt, id: last.id } : undefined;
12907
+ if (cursor < finalWatermark) {
12908
+ await yieldToEventLoop();
12909
+ }
12910
+ }
12911
+ if (isMessageIndexReconciledThrough(db, sessionId, finalWatermark)) {
12912
+ heapHolder.reconciledSessions.add(sessionId);
12913
+ if (revision !== undefined) {
12914
+ if (!after && finalWatermark > 0 && readMessages.readPage) {
12915
+ const last = readMessages.readPage(sessionId, finalWatermark - 1, 1, finalWatermark).at(-1);
12916
+ if (last?.ordinal === finalWatermark && typeof last.createdAt === "number")
12917
+ after = { timeCreated: last.createdAt, id: last.id };
12918
+ }
12919
+ heapHolder.reconciledSnapshots.set(sessionId, {
12920
+ revision,
12921
+ count: finalWatermark,
12922
+ after
12923
+ });
12924
+ }
12925
+ } else {
12926
+ logReconciliationStall(sessionId, `gap after ordinal ${cursor} (store count ${finalWatermark})`);
12927
+ }
12928
+ } finally {
12929
+ heapHolder.activeReconcilerBuffers.delete(sessionId);
12930
+ }
12931
+ });
12932
+ return true;
13053
12933
  }
13054
- function computeHardCacheExpired(cacheTtl, lastResponseTime, now, onInvalid) {
13055
- let ttlMs;
13056
- try {
13057
- ttlMs = parseCacheTtl(cacheTtl);
13058
- } catch (error) {
13059
- onInvalid?.(error);
13060
- ttlMs = 5 * 60 * 1000;
12934
+ function scheduleReconciliation(db, sessionId, readMessages) {
12935
+ if (!readMessages.getRevision && heapHolder.reconciledSessions.has(sessionId) || heapHolder.reconciliationScheduledSessions.has(sessionId)) {
12936
+ return;
13061
12937
  }
13062
- return lastResponseTime > 0 && now - lastResponseTime > ttlMs;
12938
+ heapHolder.reconciliationScheduledSessions.add(sessionId);
12939
+ scheduleAfterBootQuiet(() => {
12940
+ defer(() => {
12941
+ let appendedDuringReconciliation = false;
12942
+ const previousSnapshot = heapHolder.reconciledSnapshots.get(sessionId);
12943
+ reconcileSessionIndex(db, sessionId, readMessages).then((ran) => {
12944
+ if (ran) {
12945
+ const snapshot = heapHolder.reconciledSnapshots.get(sessionId);
12946
+ appendedDuringReconciliation = snapshot !== undefined && snapshot !== previousSnapshot && readMessages.getRevision !== undefined && readMessages.getRevision(sessionId) !== snapshot.revision;
12947
+ return;
12948
+ }
12949
+ inRootAsyncContext2(() => setTimeout(() => {
12950
+ heapHolder.reconciliationScheduledSessions.delete(sessionId);
12951
+ scheduleReconciliation(db, sessionId, readMessages);
12952
+ }, PREPARE_RETRY_MS)).unref?.();
12953
+ }).catch((error) => {
12954
+ logReconciliationStall(sessionId, error instanceof Error ? error.message : String(error));
12955
+ if (isDatabaseLockedError(error))
12956
+ inRootAsyncContext2(() => setTimeout(() => scheduleReconciliation(db, sessionId, readMessages), 1000)).unref();
12957
+ }).finally(() => {
12958
+ heapHolder.reconciliationScheduledSessions.delete(sessionId);
12959
+ if (appendedDuringReconciliation)
12960
+ scheduleReconciliation(db, sessionId, readMessages);
12961
+ });
12962
+ });
12963
+ });
13063
12964
  }
13064
-
13065
- // src/features/magic-context/storage-unavailable-reason.ts
13066
- function describeStorageUnavailability(fallbackCause) {
13067
- const migration = getMigrationOnOpenRefusal();
13068
- const blockingProcesses = migration?.blockingProcesses ?? migration?.serverPids.map((pid) => ({ kind: "process", pid })) ?? [];
13069
- if (migration && (blockingProcesses.length > 0 || migration.unreadableFile)) {
13070
- return {
13071
- kind: "migration_guard",
13072
- persistedVersion: migration.persistedVersion,
13073
- supportedVersion: migration.supportedVersion,
13074
- blockingProcesses,
13075
- ...migration.unreadableFile ? { unreadableFile: migration.unreadableFile } : {},
13076
- ...migration.unreadableArm ? { unreadableArm: migration.unreadableArm } : {}
12965
+ function scheduleIncrementalIndex(db, sessionId, messageId, messageSource) {
12966
+ const schedulingKey = `${sessionId}\x00${messageId}`;
12967
+ const epoch = sessionEpochs.get(sessionId) ?? 0;
12968
+ if (heapHolder.pendingIncrementalKeys.has(schedulingKey))
12969
+ return;
12970
+ const activeTimer = heapHolder.incrementalTimers.get(schedulingKey);
12971
+ if (activeTimer)
12972
+ clearTimeout(activeTimer);
12973
+ const timer = inRootAsyncContext2(() => setTimeout(() => {
12974
+ heapHolder.incrementalTimers.delete(schedulingKey);
12975
+ heapHolder.pendingIncrementalKeys.add(schedulingKey);
12976
+ const indexMessage = async () => {
12977
+ if ((sessionEpochs.get(sessionId) ?? 0) !== epoch || !heapHolder.pendingIncrementalKeys.has(schedulingKey))
12978
+ return;
12979
+ const message = typeof messageSource === "function" ? messageSource(sessionId, messageId) : messageSource;
12980
+ if (!message)
12981
+ return;
12982
+ const revisionKey = `${schedulingKey}\x00${getMessageIndexSourceIdentity(message)}`;
12983
+ if (heapHolder.completedIncrementalKeys.has(revisionKey))
12984
+ return;
12985
+ const currentWatermark = getLastIndexedOrdinal(db, sessionId);
12986
+ if (message.ordinal <= currentWatermark && isMessageIndexSourceCurrent(db, sessionId, message)) {
12987
+ heapHolder.completedIncrementalKeys.add(revisionKey);
12988
+ return;
12989
+ }
12990
+ const wasReconciled = heapHolder.reconciledSessions.delete(sessionId);
12991
+ await indexSingleMessageOffThread(db, sessionId, message);
12992
+ if ((sessionEpochs.get(sessionId) ?? 0) !== epoch || !heapHolder.pendingIncrementalKeys.has(schedulingKey))
12993
+ return;
12994
+ const finalWatermark = getLastIndexedOrdinal(db, sessionId);
12995
+ if (wasReconciled && isMessageIndexReconciledThrough(db, sessionId, finalWatermark)) {
12996
+ heapHolder.reconciledSessions.add(sessionId);
12997
+ }
12998
+ heapHolder.completedIncrementalKeys.add(revisionKey);
13077
12999
  };
13000
+ sourceReady(messageSource, sessionId).then((ready) => {
13001
+ if ((sessionEpochs.get(sessionId) ?? 0) !== epoch)
13002
+ return;
13003
+ if (ready)
13004
+ return runWithSessionLock(sessionId, indexMessage);
13005
+ setTimeout(() => scheduleIncrementalIndex(db, sessionId, messageId, messageSource), PREPARE_RETRY_MS).unref?.();
13006
+ }).catch((error) => {
13007
+ if ((sessionEpochs.get(sessionId) ?? 0) !== epoch)
13008
+ return;
13009
+ heapHolder.reconciledSessions.delete(sessionId);
13010
+ logIndexingError(sessionId, `incremental index for ${messageId}`, error);
13011
+ if (isDatabaseLockedError(error))
13012
+ setTimeout(() => scheduleIncrementalIndex(db, sessionId, messageId, messageSource), 1000).unref();
13013
+ }).finally(() => {
13014
+ if ((sessionEpochs.get(sessionId) ?? 0) === epoch)
13015
+ heapHolder.pendingIncrementalKeys.delete(schedulingKey);
13016
+ });
13017
+ }, INCREMENTAL_DEBOUNCE_MS));
13018
+ heapHolder.incrementalTimers.set(schedulingKey, timer);
13019
+ }
13020
+ async function indexSingleMessageOffThread(db, sessionId, message) {
13021
+ const path = getSqliteDatabasePath2(db);
13022
+ if (!path)
13023
+ return withSqliteBackgroundWriter2(() => indexSingleMessage(db, sessionId, message));
13024
+ const cancellation = new Int32Array(new SharedArrayBuffer(4));
13025
+ const active = indexCancellations.get(sessionId) ?? new Set;
13026
+ active.add(cancellation);
13027
+ indexCancellations.set(sessionId, active);
13028
+ const result = await runSessionWarmupWorker({
13029
+ path,
13030
+ sessionId,
13031
+ indexMessage: message,
13032
+ harness: getHarness2(),
13033
+ indexCancellation: cancellation.buffer,
13034
+ ...indexCoordinationInput2()
13035
+ }, {}).finally(() => {
13036
+ active.delete(cancellation);
13037
+ if (active.size === 0)
13038
+ indexCancellations.delete(sessionId);
13039
+ });
13040
+ if (!("inserted" in result))
13041
+ throw new Error("failure" in result ? result.detail : "unexpected indexing worker reply");
13042
+ if (result.measurements) {
13043
+ const sessions = workerMeasurements.get(db) ?? new Map;
13044
+ sessions.set(sessionId, result.measurements);
13045
+ workerMeasurements.set(db, sessions);
13046
+ if (result.measurements.skipped)
13047
+ log2(`[magic-context] incremental FTS skipped oversized source ${message.id} (${result.measurements.sourceBytes} bytes); identity and progress persisted`);
13078
13048
  }
13079
- const fence = getSchemaFenceRejection();
13080
- if (fence) {
13081
- return {
13082
- kind: "schema_fence",
13083
- persistedVersion: fence.persistedVersion,
13084
- supportedVersion: fence.supportedVersion
13085
- };
13049
+ return result.inserted;
13050
+ }
13051
+ function scheduleClearAndReindex(db, sessionId, readMessages) {
13052
+ heapHolder.reconciledSessions.delete(sessionId);
13053
+ heapHolder.reconciledSnapshots.delete(sessionId);
13054
+ heapHolder.reconciliationScheduledSessions.delete(sessionId);
13055
+ clearCompletedIncrementalKeys(sessionId);
13056
+ const existing = heapHolder.clearAndReindexScheduledSessions.get(sessionId);
13057
+ if (existing) {
13058
+ existing.source = readMessages;
13059
+ return;
13086
13060
  }
13087
- return { kind: "storage_failure", cause: fallbackCause };
13061
+ const pending = { source: readMessages };
13062
+ heapHolder.clearAndReindexScheduledSessions.set(sessionId, pending);
13063
+ scheduleAfterBootQuiet(() => {
13064
+ defer(() => {
13065
+ let cleared = false;
13066
+ runWithSessionLock(sessionId, () => {
13067
+ if (heapHolder.clearAndReindexScheduledSessions.get(sessionId) !== pending)
13068
+ return;
13069
+ heapHolder.clearAndReindexScheduledSessions.delete(sessionId);
13070
+ heapHolder.reconciledSessions.delete(sessionId);
13071
+ heapHolder.reconciledSnapshots.delete(sessionId);
13072
+ clearCompletedIncrementalKeys(sessionId);
13073
+ clearIndexedMessages(db, sessionId);
13074
+ cleared = true;
13075
+ }).then(() => cleared ? reconcileSessionIndex(db, sessionId, pending.source) : undefined).catch((error) => {
13076
+ heapHolder.reconciledSessions.delete(sessionId);
13077
+ if (heapHolder.clearAndReindexScheduledSessions.get(sessionId) === pending)
13078
+ heapHolder.clearAndReindexScheduledSessions.delete(sessionId);
13079
+ logIndexingError(sessionId, "clear and reindex", error);
13080
+ });
13081
+ });
13082
+ });
13088
13083
  }
13089
-
13090
- // src/plugin/rpc-handlers.ts
13091
- import { randomUUID as randomUUID3 } from "node:crypto";
13092
- import { once } from "node:events";
13093
- import { rmSync as rmSync2 } from "node:fs";
13094
- import { join as join5 } from "node:path";
13095
-
13096
- // src/features/magic-context/memory/memory-diagnostics.ts
13097
- function emptyMemoryImportanceHistogram() {
13084
+ function getMessageIndexQueueHeapStats() {
13098
13085
  return {
13099
- total: 0,
13100
- unclassified: 0,
13101
- bands: {
13102
- "0-19": 0,
13103
- "20-39": 0,
13104
- "40-59": 0,
13105
- "60-79": 0,
13106
- "80-100": 0
13107
- }
13086
+ queueLength: heapHolder.reconciliationScheduledSessions.size + heapHolder.incrementalTimers.size + heapHolder.pendingIncrementalKeys.size,
13087
+ reconciliationScheduled: heapHolder.reconciliationScheduledSessions.size,
13088
+ incrementalTimers: heapHolder.incrementalTimers.size,
13089
+ pendingIncremental: heapHolder.pendingIncrementalKeys.size,
13090
+ activeSessionLocks: heapHolder.sessionLocks.size,
13091
+ completedIncrementalKeys: heapHolder.completedIncrementalKeys.size,
13092
+ activeBufferMessages: [...heapHolder.activeReconcilerBuffers.values()].reduce((sum, messages) => sum + messages.length, 0),
13093
+ activeBufferBytes: [...heapHolder.activeReconcilerBuffers.values()].reduce((sum, messages) => sum + serializedMessageBytes(messages), 0)
13108
13094
  };
13109
13095
  }
13110
- function readHistogramRow(db, projectPath, classifiedColumn) {
13111
- const unclassified = classifiedColumn ? "SUM(CASE WHEN classified_at IS NULL THEN 1 ELSE 0 END)" : "COUNT(*)";
13112
- return db.prepare(`SELECT COUNT(*) AS total,
13113
- ${unclassified} AS unclassified,
13114
- SUM(CASE WHEN COALESCE(importance, 50) BETWEEN 0 AND 19 THEN 1 ELSE 0 END) AS band_0_19,
13115
- SUM(CASE WHEN COALESCE(importance, 50) BETWEEN 20 AND 39 THEN 1 ELSE 0 END) AS band_20_39,
13116
- SUM(CASE WHEN COALESCE(importance, 50) BETWEEN 40 AND 59 THEN 1 ELSE 0 END) AS band_40_59,
13117
- SUM(CASE WHEN COALESCE(importance, 50) BETWEEN 60 AND 79 THEN 1 ELSE 0 END) AS band_60_79,
13118
- SUM(CASE WHEN COALESCE(importance, 50) BETWEEN 80 AND 100 THEN 1 ELSE 0 END) AS band_80_100
13119
- FROM memories
13120
- WHERE project_path = ? AND status = 'active'`).get(projectPath);
13121
- }
13122
- function getActiveMemoryImportanceHistogram(db, projectPath) {
13123
- let row;
13124
- try {
13125
- row = readHistogramRow(db, projectPath, true);
13126
- } catch (error) {
13127
- if (!String(error).includes("no such column: classified_at"))
13128
- throw error;
13129
- row = readHistogramRow(db, projectPath, false);
13096
+ function clearSessionTracking(sessionId) {
13097
+ sessionEpochs.set(sessionId, (sessionEpochs.get(sessionId) ?? 0) + 1);
13098
+ for (const cancellation of indexCancellations.get(sessionId) ?? [])
13099
+ Atomics.store(cancellation, 0, 1);
13100
+ heapHolder.clearAndReindexScheduledSessions.delete(sessionId);
13101
+ heapHolder.reconciledSessions.delete(sessionId);
13102
+ heapHolder.reconciledSnapshots.delete(sessionId);
13103
+ heapHolder.stalledSessions.delete(sessionId);
13104
+ heapHolder.reconciliationScheduledSessions.delete(sessionId);
13105
+ heapHolder.sessionLocks.delete(sessionId);
13106
+ heapHolder.activeReconcilerBuffers.delete(sessionId);
13107
+ const prefix = `${sessionId}\x00`;
13108
+ for (const [key, timer] of heapHolder.incrementalTimers) {
13109
+ if (key.startsWith(prefix)) {
13110
+ clearTimeout(timer);
13111
+ heapHolder.incrementalTimers.delete(key);
13112
+ }
13130
13113
  }
13131
- return {
13132
- total: Number(row.total ?? 0),
13133
- unclassified: Number(row.unclassified ?? 0),
13134
- bands: {
13135
- "0-19": Number(row.band_0_19 ?? 0),
13136
- "20-39": Number(row.band_20_39 ?? 0),
13137
- "40-59": Number(row.band_40_59 ?? 0),
13138
- "60-79": Number(row.band_60_79 ?? 0),
13139
- "80-100": Number(row.band_80_100 ?? 0)
13114
+ for (const key of heapHolder.pendingIncrementalKeys) {
13115
+ if (key.startsWith(prefix)) {
13116
+ heapHolder.pendingIncrementalKeys.delete(key);
13140
13117
  }
13141
- };
13118
+ }
13119
+ for (const key of heapHolder.completedIncrementalKeys) {
13120
+ if (key.startsWith(prefix))
13121
+ heapHolder.completedIncrementalKeys.delete(key);
13122
+ }
13142
13123
  }
13143
13124
 
13144
13125
  // src/features/magic-context/session-cache-ttl.ts
@@ -14238,6 +14219,13 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
14238
14219
  }
14239
14220
  return { shouldFire: false };
14240
14221
  }
14222
+ if (tailInfo.boundarySnapshot && shouldWaitForNewHistorianHistory(tailInfo.boundarySnapshot, {
14223
+ usagePercentage: usage.percentage,
14224
+ emergency: getOverflowState(db, sessionId).needsEmergencyRecovery
14225
+ })) {
14226
+ sessionLog2(sessionId, "compartment trigger: waiting for new history after no forward progress");
14227
+ return { shouldFire: false };
14228
+ }
14241
14229
  const canClearReasoning = reasoningProjection?.canClearReasoning ?? modelAcceptsEmptyContent(reasoningProjection?.providerID);
14242
14230
  const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage, preloadedActiveTags ?? getActiveTagsBySession2(db, sessionId), keepReasoningTokens, sessionMeta.clearedReasoningThroughTag, canClearReasoning, reasoningProjection?.budgetCutoff, eligiblePendingDropTagIds);
14243
14231
  const relativePostDropTarget = executeThresholdPercentage * POST_DROP_TARGET_RATIO;
@@ -16481,8 +16469,8 @@ function registerRpcHandlers(rpcServer, args) {
16481
16469
  const db = readDatabase();
16482
16470
  if (!db)
16483
16471
  return { ok: false, error: "db unavailable" };
16484
- const { runManagedRecomp } = await importPluginModule(() => import("./recomp-orchestrator-qwcz86r1.js"));
16485
- const { sendIgnoredMessage } = await importPluginModule(() => import("./send-session-notification-kzazs7fy.js"));
16472
+ const { runManagedRecomp } = await importPluginModule(() => import("./recomp-orchestrator-02sa2ngr.js"));
16473
+ const { sendIgnoredMessage } = await importPluginModule(() => import("./send-session-notification-afzzqy5b.js"));
16486
16474
  log2(`[rpc] recomp requested for session ${sessionId}`);
16487
16475
  if (!args.client && !args.hiddenCompletionExecutor) {
16488
16476
  return {
@@ -16566,7 +16554,7 @@ function registerRpcHandlers(rpcServer, args) {
16566
16554
  const db = readDatabase();
16567
16555
  if (!db)
16568
16556
  return { ok: false, error: "db unavailable" };
16569
- const { runManagedWrapup } = await importPluginModule(() => import("./wrapup-orchestrator-a9r8r4sk.js"));
16557
+ const { runManagedWrapup } = await importPluginModule(() => import("./wrapup-orchestrator-n5h5sr1x.js"));
16570
16558
  const model = liveSessionState.liveModelBySession.get(sessionId);
16571
16559
  const contextLimit = model ? resolveContextLimit(model.providerID, model.modelID, { db, sessionID: sessionId }) : 128000;
16572
16560
  const ctx = {
@@ -28614,7 +28602,7 @@ function singleLine(value) {
28614
28602
  // src/tools/ctx-note/tools.ts
28615
28603
  function captureAnchorOrdinal(db, sessionId) {
28616
28604
  try {
28617
- const ordinal = getLastIndexedOrdinal2(db, sessionId);
28605
+ const ordinal = getLastIndexedOrdinal(db, sessionId);
28618
28606
  return ordinal > 0 ? ordinal : null;
28619
28607
  } catch {
28620
28608
  return null;
@@ -29798,4 +29786,4 @@ function applyHiddenSubagentTemperature(input, output, log) {
29798
29786
  output.temperature = temperature;
29799
29787
  }
29800
29788
 
29801
- export { logConfigDependencyNotesOnce, openOpenCodeDb, DREAMER_SYSTEM_PROMPT, runSessionProjectBackfill, SMART_NOTE_COMPILER_SYSTEM_PROMPT, createLiveSessionState, RETROSPECTIVE_MAX_USER_MESSAGE_CHARS, RETROSPECTIVE_MESSAGE_TRUNCATION_MARKER, OpenCodeRetrospectiveRawProvider, createDreamTaskExecutor, runManualDream, runDueTasksForProject, scheduleClearAndReindex, startDreamScheduleTimer, createDreamTimerModuleClient, ensureProjectRegisteredFromOpenCodeDirectory, recordOpenCodeProvenInputFloor, resolveOpenCodeProvenInputFloor, resolveContextWindowGeometry, resolveContextLimit, resolveExecuteThresholdDetail, resolveModelKey, resolveSessionId, createScheduler, describeStorageUnavailability, resolveSessionCacheTtl, runEmbedHistoryDrain, pauseEmbedHistoryDrain, formatEmbedStatusText, getSessionProperties, getSessionCreatedInfo, getMessageUpdatedAssistantInfo, getMessageUpdatedInfo, getSessionErrorInfo, getMessageRemovedInfo, summarizeManualDream, createMagicContextCommandHandler, getLiveNotificationParams, createChatMessageHook, createEventHook, createCommandExecuteBeforeHook, createToolExecuteAfterHook, recordHiddenVariantWarning, buildStatusDetail, isDebugRpcEnabled, registerRpcHandlers, maybeAutoEmbedSessionBackfill, clearTransformDecisionSession, scheduleOpenCodeTransformDecisionWrite, stageOpenCodeV2TransformDecision, bindOpenCodeV2TransformDecision, maybeDeliverChannel2, armLatestThinkingRecovery, armBindingRecoverySafely, armLatestThinkingRecoveryFromError, createM0RenderOptions, lkgReplayLimit, measureLkgReplay, STORAGE_BUSY_MESSAGE, StorageBusyRefusalError, bindSystemDateRequestIdentity, bindSystemDateOutput, registerSystemDateRefresh, rustWrapupHistorianFields, UnmanagedOverWindowError, UnresolvedHistoryBoundaryError, clearMessageTokensCache, createTransform, createSubcModuleClient, createModuleToolBackends, createRustRefusalRecovery, createSystemPromptHashHandler, tryMessagesTransformLkgReplay, createMessagesTransformHandler, primaryOnlyToolIds, subagentToolRefusal, guardSubagentTools, hideSubagentTools, removePrimaryOnlyTools, storedSubagentMode, createCtxExpandTools, CTX_MEMORY_LIST_TOOL_NAME, CTX_MEMORY_ACTIONS, createCtxMemoryTools, createCtxMemoryListTools, CTX_NOTE_DESCRIPTION, withoutSmartNoteText, createCtxNoteTools, createCtxReduceTools, createCtxSearchTools, parameterDescriptionsFor, applyJsonSchemaParameterDescriptions, MagicContextRpcServer, subagentTemperature, applyHiddenSubagentTemperature };
29789
+ export { logConfigDependencyNotesOnce, openOpenCodeDb, DREAMER_SYSTEM_PROMPT, runSessionProjectBackfill, SMART_NOTE_COMPILER_SYSTEM_PROMPT, createLiveSessionState, RETROSPECTIVE_MAX_USER_MESSAGE_CHARS, RETROSPECTIVE_MESSAGE_TRUNCATION_MARKER, OpenCodeRetrospectiveRawProvider, createDreamTaskExecutor, runManualDream, runDueTasksForProject, startDreamScheduleTimer, createDreamTimerModuleClient, ensureProjectRegisteredFromOpenCodeDirectory, recordOpenCodeProvenInputFloor, resolveOpenCodeProvenInputFloor, resolveContextWindowGeometry, resolveContextLimit, resolveExecuteThresholdDetail, resolveModelKey, resolveSessionId, createScheduler, describeStorageUnavailability, scheduleClearAndReindex, resolveSessionCacheTtl, runEmbedHistoryDrain, pauseEmbedHistoryDrain, formatEmbedStatusText, getSessionProperties, getSessionCreatedInfo, getMessageUpdatedAssistantInfo, getMessageUpdatedInfo, getSessionErrorInfo, getMessageRemovedInfo, summarizeManualDream, createMagicContextCommandHandler, getLiveNotificationParams, createChatMessageHook, createEventHook, createCommandExecuteBeforeHook, createToolExecuteAfterHook, recordHiddenVariantWarning, buildStatusDetail, isDebugRpcEnabled, registerRpcHandlers, maybeAutoEmbedSessionBackfill, clearTransformDecisionSession, scheduleOpenCodeTransformDecisionWrite, stageOpenCodeV2TransformDecision, bindOpenCodeV2TransformDecision, maybeDeliverChannel2, armLatestThinkingRecovery, armBindingRecoverySafely, armLatestThinkingRecoveryFromError, createM0RenderOptions, lkgReplayLimit, measureLkgReplay, STORAGE_BUSY_MESSAGE, StorageBusyRefusalError, bindSystemDateRequestIdentity, bindSystemDateOutput, registerSystemDateRefresh, rustWrapupHistorianFields, UnmanagedOverWindowError, UnresolvedHistoryBoundaryError, clearMessageTokensCache, createTransform, createSubcModuleClient, createModuleToolBackends, createRustRefusalRecovery, createSystemPromptHashHandler, tryMessagesTransformLkgReplay, createMessagesTransformHandler, primaryOnlyToolIds, subagentToolRefusal, guardSubagentTools, hideSubagentTools, removePrimaryOnlyTools, storedSubagentMode, createCtxExpandTools, CTX_MEMORY_LIST_TOOL_NAME, CTX_MEMORY_ACTIONS, createCtxMemoryTools, createCtxMemoryListTools, CTX_NOTE_DESCRIPTION, withoutSmartNoteText, createCtxNoteTools, createCtxReduceTools, createCtxSearchTools, parameterDescriptionsFor, applyJsonSchemaParameterDescriptions, MagicContextRpcServer, subagentTemperature, applyHiddenSubagentTemperature };