@cortexkit/pi-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 (39) hide show
  1. package/dist/auto-search-worker.js +4 -4
  2. package/dist/chunk-embedding-write-worker.js +8 -9
  3. package/dist/{compartment-chunk-embedding-vt6afvdm.js → compartment-chunk-embedding-pkk5beqp.js} +3 -3
  4. package/dist/{embedding-backfill-jc5fqt5s.js → embedding-backfill-25fp3b8w.js} +7 -7
  5. package/dist/{index-0dr64gsy.js → index-0s14xaca.js} +2 -2
  6. package/dist/{index-n8e5e9fp.js → index-0x1c054v.js} +1 -1
  7. package/dist/{index-hxp6q5f8.js → index-48twbev2.js} +50 -36
  8. package/dist/{index-j3pz3a1b.js → index-4zemngt7.js} +1 -1
  9. package/dist/{index-r1hvaza0.js → index-5d506f7y.js} +719 -23
  10. package/dist/{index-z24d75tz.js → index-6981g1xv.js} +0 -2
  11. package/dist/{index-zpmz6x24.js → index-bkcwcghx.js} +1308 -104
  12. package/dist/{index-g0d51qzc.js → index-bvpar0bg.js} +2 -5
  13. package/dist/{index-w1avzkdt.js → index-g026ztby.js} +1 -1
  14. package/dist/{index-rjnc6kkf.js → index-kagezf2h.js} +3 -3
  15. package/dist/{index-dnadppqx.js → index-kmk3xpf9.js} +1 -1
  16. package/dist/{index-zz7dnyh6.js → index-sdsnb5b6.js} +8 -8
  17. package/dist/index-v0ht940m.js +488 -0
  18. package/dist/{index-kbswhkc8.js → index-vea2d9ka.js} +1 -1
  19. package/dist/{index-w5v0ntbw.js → index-wjbqw6r1.js} +1 -1
  20. package/dist/{index-z6k6jsmh.js → index-xa90f88m.js} +3 -3
  21. package/dist/{index-wpy2rzb2.js → index-xw070axm.js} +3 -3
  22. package/dist/index.js +499 -440
  23. package/dist/{message-fts-rowid-map-xe3ss2g3.js → message-fts-rowid-map-3mbvdgn9.js} +2 -4
  24. package/dist/{message-index-bounded-2zffszp9.js → message-index-bounded-avbaa7wa.js} +19 -22
  25. package/dist/migration-worker.js +7 -8
  26. package/dist/{project-embedding-registry-bn9ts1h0.js → project-embedding-registry-hc4ahjdt.js} +6 -6
  27. package/dist/raw-ordinal-warmup-worker.js +7 -30
  28. package/dist/{read-session-chunk-p0cexvvs.js → read-session-chunk-1rfprgc1.js} +4 -4
  29. package/dist/{read-session-raw-mq03ws9v.js → read-session-raw-g9b1zdn5.js} +1 -1
  30. package/dist/{search-archive-jw2wm4tw.js → search-archive-mpjeecwj.js} +5 -5
  31. package/dist/{search-vmcbbwm5.js → search-vkkfg27q.js} +13 -14
  32. package/dist/{storage-tags-r48vxthe.js → storage-tags-9qaasrky.js} +2 -2
  33. package/dist/subagent-entry.js +17 -18
  34. package/dist/tag-snapshot-worker-pi.js +9 -10
  35. package/package.json +1 -1
  36. package/dist/index-87xm7z44.js +0 -2100
  37. package/dist/index-n4wyxcgr.js +0 -712
  38. package/dist/index-wrr9rp9a.js +0 -16
  39. package/dist/message-index-jv1m3g8d.js +0 -58
package/dist/index.js CHANGED
@@ -4,7 +4,6 @@ import {
4
4
  harnessOwnsOpenCodeStore2
5
5
  } from "./index-0h2mkab2.js";
6
6
  import {
7
- getDataDir,
8
7
  ensureCortexKitArtifactGitignore,
9
8
  getProjectMagicContextHistorianDir,
10
9
  getMagicContextStorageDir,
@@ -46,11 +45,21 @@ import {
46
45
  cortexKitProjectConfigBasePath,
47
46
  migrateMagicContextConfigLocations,
48
47
  getProtectedTokensTierOverrides,
48
+ LkgRecoveryFenceError,
49
+ clearLkgFenceProcessState,
50
+ lkgFenceRequiresRebuild,
51
+ lkgRecoveryFenceUnclean,
52
+ ownsLkgDirtyFence,
53
+ invalidateCleanLkgRecoveryFence,
54
+ markLkgRecoveryDirty,
49
55
  rememberUnpublishedAutoSearchNoHint,
50
56
  getUnpublishedAutoSearchNoHint,
51
57
  forgetUnpublishedAutoSearchNoHint,
52
58
  registerAutoSearchWork,
53
59
  cancelAutoSearchWork,
60
+ readSessionActivity,
61
+ observeSessionActivity,
62
+ backfillSessionActivity,
54
63
  readSessionMeta,
55
64
  getOrCreateSessionMeta,
56
65
  updateSessionMeta,
@@ -251,7 +260,7 @@ import {
251
260
  registerTodoStateLifecycle,
252
261
  syncCtxMemoryToolEnabled,
253
262
  registerMagicContextTools
254
- } from "./index-zpmz6x24.js";
263
+ } from "./index-bkcwcghx.js";
255
264
  import"./index-4knyh1kt.js";
256
265
  import {
257
266
  canonicalModelIdentity,
@@ -277,6 +286,7 @@ import {
277
286
  isFailClosedBlockingError,
278
287
  shouldBypassFailClosedBlock,
279
288
  createFailClosedController,
289
+ piHarnessKindFromExecutable,
280
290
  getErrorMessage,
281
291
  describeError,
282
292
  startBootDeadline,
@@ -292,8 +302,9 @@ import {
292
302
  openCurrentDatabase,
293
303
  openDatabaseAsync,
294
304
  closeDatabase
295
- } from "./index-r1hvaza0.js";
305
+ } from "./index-5d506f7y.js";
296
306
  import {
307
+ sameProjectDirectory,
297
308
  pruneMissingProjectIdentities,
298
309
  setHomeProjectPermission,
299
310
  ProjectIdentityError,
@@ -304,7 +315,7 @@ import {
304
315
  resolveProjectIdentityForSession,
305
316
  beginBootQuietPeriod,
306
317
  scheduleAfterBootQuiet
307
- } from "./index-hxp6q5f8.js";
318
+ } from "./index-48twbev2.js";
308
319
  import {
309
320
  detectThinkingBindingMismatch,
310
321
  detectLatestTurnThinkingMismatch,
@@ -462,11 +473,12 @@ import {
462
473
  getCachedAbsoluteMessageCount2,
463
474
  primeInMemoryTailRawMessageCache2,
464
475
  readRawSessionMessageIdOrdinalsForRange2,
476
+ compareRawSessionMessageOrder2,
465
477
  getRawSessionMessageCount2,
466
478
  getRawSessionTagKeysThrough2,
467
479
  getLegacyProtectedTailStartOrdinal2,
468
480
  readSessionChunk2
469
- } from "./index-rjnc6kkf.js";
481
+ } from "./index-kagezf2h.js";
470
482
  import {
471
483
  UNKNOWN_FIT_RATIO,
472
484
  hasMeasuredDecisionCalibration,
@@ -553,7 +565,7 @@ import {
553
565
  getToolTagNumberByOwner2,
554
566
  getNullOwnerToolTag2,
555
567
  adoptNullOwnerToolTag2
556
- } from "./index-n8e5e9fp.js";
568
+ } from "./index-0x1c054v.js";
557
569
  import {
558
570
  textMentionsRecentCommit,
559
571
  hasMeaningfulUserText2,
@@ -587,36 +599,7 @@ import {
587
599
  observeTemporalDecisions,
588
600
  freezeTemporalDecisions,
589
601
  tagIdentityRevisionKey
590
- } from "./index-w1avzkdt.js";
591
- import {
592
- piHarnessKindFromExecutable
593
- } from "./index-n4wyxcgr.js";
594
- import {
595
- LkgRecoveryFenceError,
596
- clearLkgFenceProcessState,
597
- takeLkgRecoveryLockSweepPage,
598
- lkgFenceRequiresRebuild,
599
- lkgRecoveryFenceUnclean,
600
- ownsLkgDirtyFence,
601
- invalidateCleanLkgRecoveryFence,
602
- markLkgRecoveryDirty,
603
- gaDatabasePath,
604
- readSessionActivity,
605
- observeSessionActivity,
606
- backfillSessionActivity,
607
- clearCompressionDepth,
608
- clearCompressionDepthRange,
609
- cleanSearchText,
610
- matchedSearchSnippet,
611
- getMessageIndexSourceIdentity2,
612
- isMessageIndexSourceCurrent2,
613
- getLastIndexedOrdinal2,
614
- getMessageIndexReconciliationStartOrdinal2,
615
- isMessageIndexReconciledThrough2,
616
- indexSingleMessage2,
617
- indexMessagesAfterOrdinal2,
618
- sweepOrphanedOpenCodeMessageIndexesCooperatively2
619
- } from "./index-87xm7z44.js";
602
+ } from "./index-g026ztby.js";
620
603
  import {
621
604
  BoundedSessionMap,
622
605
  resolveOpenCodeDbPath,
@@ -633,11 +616,11 @@ import {
633
616
  import {
634
617
  runSessionWarmupWorker,
635
618
  RAW_SUMMARY_TEXT_MAX_CHARS2
636
- } from "./index-z24d75tz.js";
619
+ } from "./index-6981g1xv.js";
637
620
  import {
638
621
  formatSynapseLaneDescriptor
639
622
  } from "./index-gwxaz0z5.js";
640
- import"./index-g0d51qzc.js";
623
+ import"./index-bvpar0bg.js";
641
624
  import {
642
625
  buildCanonicalChunkTextFromFts2,
643
626
  buildCompartmentSummaryFallbackText2,
@@ -647,7 +630,7 @@ import {
647
630
  replaceCompartmentChunkEmbeddings2,
648
631
  deleteChunkEmbedBackoffForSession2,
649
632
  chunkWindowSourceKey2
650
- } from "./index-dnadppqx.js";
633
+ } from "./index-kmk3xpf9.js";
651
634
  import {
652
635
  recordSessionProjectIdentity,
653
636
  GIT_SWEEP_LEASE_RENEWAL_MS,
@@ -656,7 +639,7 @@ import {
656
639
  markGitSweepSuccessAndRelease,
657
640
  parkGitSweepNonIndexable,
658
641
  releaseGitSweepLease
659
- } from "./index-j3pz3a1b.js";
642
+ } from "./index-4zemngt7.js";
660
643
  import {
661
644
  embedPauseBySession,
662
645
  embedRunStateBySession,
@@ -682,7 +665,20 @@ import {
682
665
  embedSessionCompartmentChunks2,
683
666
  getEmbeddingCoverageStatus2,
684
667
  getAutoEmbeddingSessionCoverage2
685
- } from "./index-z6k6jsmh.js";
668
+ } from "./index-xa90f88m.js";
669
+ import {
670
+ clearCompressionDepth,
671
+ clearCompressionDepthRange,
672
+ cleanSearchText,
673
+ matchedSearchSnippet,
674
+ getMessageIndexSourceIdentity,
675
+ isMessageIndexSourceCurrent,
676
+ getLastIndexedOrdinal,
677
+ getMessageIndexReconciliationStartOrdinal,
678
+ isMessageIndexReconciledThrough,
679
+ indexSingleMessage,
680
+ indexMessagesAfterOrdinal
681
+ } from "./index-v0ht940m.js";
686
682
  import {
687
683
  rememberV1HiddenRun
688
684
  } from "./index-6x556c2q.js";
@@ -697,7 +693,7 @@ import {
697
693
  expandWorkspaceIdentitySetWithAliases,
698
694
  sourceNameForMemory,
699
695
  computeWorkspaceEpochFingerprint
700
- } from "./index-0dr64gsy.js";
696
+ } from "./index-0s14xaca.js";
701
697
  import {
702
698
  configureContextDatabasePragmas2
703
699
  } from "./index-fapenpch.js";
@@ -747,7 +743,7 @@ import {
747
743
  normalizeVerificationFiles,
748
744
  indexCommitsForProject,
749
745
  embedUnembeddedCommits
750
- } from "./index-zz7dnyh6.js";
746
+ } from "./index-sdsnb5b6.js";
751
747
  import {
752
748
  setEmbeddingSessionBusy2,
753
749
  isEmbeddingHostBusy2
@@ -783,7 +779,7 @@ import {
783
779
  } from "./index-wqpjhx1p.js";
784
780
  import {
785
781
  cosineSimilarity
786
- } from "./index-w5v0ntbw.js";
782
+ } from "./index-wjbqw6r1.js";
787
783
  import"./index-6wdwv3a6.js";
788
784
  import {
789
785
  DREAMER_PRIMER_INVESTIGATOR_ALLOWED_TOOLS,
@@ -811,10 +807,10 @@ import {
811
807
  drainPiTagSnapshotSession,
812
808
  assertPiTagSnapshotReady,
813
809
  ensurePiTagSnapshotReady
814
- } from "./index-wpy2rzb2.js";
810
+ } from "./index-xw070axm.js";
815
811
  import"./index-jn44h81p.js";
816
812
  import"./index-1cvyaw9a.js";
817
- import"./index-kbswhkc8.js";
813
+ import"./index-vea2d9ka.js";
818
814
  import {
819
815
  __toESM
820
816
  } from "./index-22jkk6wg.js";
@@ -824,9 +820,6 @@ import {
824
820
  import {
825
821
  normalizeProtectedToolName
826
822
  } from "./index-674f6qs2.js";
827
- import {
828
- sameProjectDirectory
829
- } from "./index-wrr9rp9a.js";
830
823
  import {
831
824
  logSlowWriteTransaction,
832
825
  WriteTransactionSteps
@@ -3556,7 +3549,7 @@ function startHostStallProfilerSwitch() {
3556
3549
  // package.json
3557
3550
  var package_default = {
3558
3551
  name: "@cortexkit/pi-magic-context",
3559
- version: "0.49.26",
3552
+ version: "0.49.28",
3560
3553
  type: "module",
3561
3554
  description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
3562
3555
  main: "dist/index.js",
@@ -14934,8 +14927,8 @@ async function mapMemories(args) {
14934
14927
  result.failureStage = outcome.failure.stage;
14935
14928
  }
14936
14929
  const committed = outcome.mapped + outcome.independent;
14930
+ const committedIds = new Set(outcome.committedIds);
14937
14931
  runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
14938
- const committedIds = new Set(outcome.committedIds);
14939
14932
  for (const memory of batch.inputs) {
14940
14933
  if (committedIds.has(memory.id)) {
14941
14934
  delete retries[memory.id];
@@ -14970,7 +14963,22 @@ async function mapMemories(args) {
14970
14963
  log2(`[dreamer] map-memories: targeted retry still omitted ${outcome.requeue.length} id(s); leaving them unmapped for the next run`);
14971
14964
  }
14972
14965
  }
14973
- if (outcome.failure?.class === "timeout" && committed === 0) {
14966
+ if (outcome.failure?.class === "timeout" && batch.inputs.length > 1) {
14967
+ const unmapped = batch.inputs.filter((memory) => !committedIds.has(memory.id));
14968
+ const splitSize = Math.floor(batch.inputs.length / 2);
14969
+ const splits = [];
14970
+ for (let offset = 0;offset < unmapped.length; offset += splitSize) {
14971
+ splits.push({
14972
+ inputs: unmapped.slice(offset, offset + splitSize),
14973
+ isOmissionRetry: batch.isOmissionRetry
14974
+ });
14975
+ }
14976
+ if (splits.length > 0) {
14977
+ batches.splice(i + 1, 0, ...splits);
14978
+ 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)`);
14979
+ }
14980
+ }
14981
+ if (outcome.failure?.class === "timeout" && batch.inputs.length === 1 && committed === 0) {
14974
14982
  consecutiveTimeouts += 1;
14975
14983
  timeoutStreakElapsedMs.push(outcome.failure.elapsedMs);
14976
14984
  if (consecutiveTimeouts >= CONSECUTIVE_TIMEOUT_LIMIT2) {
@@ -14978,7 +14986,7 @@ async function mapMemories(args) {
14978
14986
  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`);
14979
14987
  break;
14980
14988
  }
14981
- } else {
14989
+ } else if (committed > 0 || outcome.failure?.class !== "timeout") {
14982
14990
  consecutiveTimeouts = 0;
14983
14991
  timeoutStreakElapsedMs = [];
14984
14992
  }
@@ -16930,6 +16938,21 @@ function verifyAssistantText(messages) {
16930
16938
  const start = text.lastIndexOf("<verify>");
16931
16939
  return start >= 0 ? text.slice(start) : text;
16932
16940
  }
16941
+ function validateVerifyCompletion(messages, text, expectedIds, responseText = text) {
16942
+ let parsed;
16943
+ try {
16944
+ parsed = validateVerifyManifest(text, expectedIds);
16945
+ } catch (error) {
16946
+ throw providerOutputFailureFromInvalidManifest(messages, responseText) ?? error;
16947
+ }
16948
+ const ids = [...parsed.verified, ...parsed.updated, ...parsed.archived, ...parsed.skipped].map((entry) => entry.id);
16949
+ if (ids.length !== expectedIds.size || ids.some((id) => !expectedIds.has(id))) {
16950
+ const providerFailure = providerOutputFailureFromInvalidManifest(messages, responseText);
16951
+ if (providerFailure)
16952
+ throw providerFailure;
16953
+ }
16954
+ return parsed;
16955
+ }
16933
16956
  var IDENTICAL_PROVIDER_FAILURE_BATCH_LIMIT = 2;
16934
16957
  var VERIFY_BATCH_FLOOR_MS = 30000;
16935
16958
  function computeVerifyBatchSliceMs(remainingMs, _batchesRemaining) {
@@ -17088,6 +17111,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
17088
17111
  const timer = setTimeout(() => batchController.abort(new Error(`verify slice timed out after ${sliceMs}ms`)), sliceMs);
17089
17112
  const batchSignal = batchController.signal;
17090
17113
  const tokenBudget = verifyBatchTokenBudget(batch.length, args.tokenBudget);
17114
+ const expectedIds = new Set(batch.map((memory) => memory.id));
17091
17115
  let agentSessionId = null;
17092
17116
  let hiddenSessionId = null;
17093
17117
  let promptSettled = false;
@@ -17140,13 +17164,10 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
17140
17164
  onBudgetUpdate({ spent: 0, finalizeFired: false, sessionId });
17141
17165
  },
17142
17166
  parse: (text, completion) => {
17143
- const providerFailure = providerOutputFailureFromInvalidManifest(completion.messages ?? [], text);
17144
- if (providerFailure)
17145
- throw providerFailure;
17146
17167
  const manifest = verifyAssistantText(completion.messages ?? []) ?? (text.trim() ? text : null);
17147
17168
  if (!manifest)
17148
17169
  throw new Error("verify returned no output");
17149
- return validateVerifyManifest(manifest, new Set(batch.map((memory) => memory.id)));
17170
+ return validateVerifyCompletion(completion.messages ?? [], manifest, expectedIds, text);
17150
17171
  }
17151
17172
  });
17152
17173
  recordInvocation4(args, startedAt, {
@@ -17208,10 +17229,7 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
17208
17229
  const text = verifyAssistantText(messages);
17209
17230
  if (!text)
17210
17231
  throw new Error("verify returned no output");
17211
- const providerFailure = providerOutputFailureFromInvalidManifest(messages, text);
17212
- if (providerFailure)
17213
- throw providerFailure;
17214
- return validateVerifyManifest(text, new Set(batch.map((memory) => memory.id)));
17232
+ return validateVerifyCompletion(messages, text, expectedIds);
17215
17233
  }
17216
17234
  });
17217
17235
  promptSettled = true;
@@ -19481,390 +19499,70 @@ function clearDreamerTickFailure(db) {
19481
19499
  db.prepare("DELETE FROM schema_migrations_meta WHERE key = ?").run(DREAMER_TICK_FAILURE_KEY);
19482
19500
  }
19483
19501
 
19484
- // ../plugin/src/features/magic-context/message-index-async.ts
19485
- var indexCancellations = new Map;
19486
- var sessionEpochs = new Map;
19487
- var workerMeasurements = new WeakMap;
19488
- function isDatabaseLockedError(error) {
19489
- if (!error || typeof error !== "object")
19502
+ // ../plugin/src/plugin/dream-timer.ts
19503
+ var DREAM_TIMER_INTERVAL_MS = 15 * 60 * 1000;
19504
+ var GIT_COMMIT_BACKLOG_DRAIN_MAX_MS = 5 * 60 * 1000;
19505
+ var BOOT_PROJECT_JITTER_SLOT_MS = 1000;
19506
+ var activeTimer = null;
19507
+ var tickInFlight = false;
19508
+ var startupTickTimer = null;
19509
+ var startupTimers = new Map;
19510
+ var startupJitters = new Map;
19511
+ var nextStartupJitterSlot = 0;
19512
+ function directoryStillExists(directory) {
19513
+ try {
19514
+ return statSync3(directory).isDirectory();
19515
+ } catch {
19490
19516
  return false;
19491
- const e = error;
19492
- if (typeof e.code === "string") {
19493
- if (e.code === "SQLITE_BUSY" || e.code === "SQLITE_LOCKED")
19494
- return true;
19495
- }
19496
- if (typeof e.message === "string") {
19497
- if (/database is locked/i.test(e.message))
19498
- return true;
19499
- if (/sqlite_(busy|locked)/i.test(e.message))
19500
- return true;
19501
19517
  }
19502
- return false;
19503
- }
19504
- var INCREMENTAL_DEBOUNCE_MS = 100;
19505
- var RECONCILIATION_BATCH_SIZE = 100;
19506
-
19507
- class MagicContextMessageIndexHeapHolder {
19508
- reconciledSessions = new Set;
19509
- reconciledSnapshots = new Map;
19510
- stalledSessions = new Set;
19511
- reconciliationScheduledSessions = new Set;
19512
- clearAndReindexScheduledSessions = new Map;
19513
- sessionLocks = new Map;
19514
- incrementalTimers = new Map;
19515
- pendingIncrementalKeys = new Set;
19516
- completedIncrementalKeys = new Set;
19517
- activeReconcilerBuffers = new Map;
19518
- }
19519
- var heapHolder = new MagicContextMessageIndexHeapHolder;
19520
- var PREPARE_RETRY_MS = 60000;
19521
- async function sourceReady(source, sessionId) {
19522
- const prepare = source?.prepare;
19523
- return typeof prepare === "function" ? prepare(sessionId) : true;
19524
19518
  }
19525
- function defer(fn) {
19526
- const immediate = globalThis.setImmediate;
19527
- if (typeof immediate === "function") {
19528
- inRootAsyncContext2(() => immediate(fn));
19529
- return;
19519
+ function openTimerDatabaseOrNull(context) {
19520
+ let db;
19521
+ try {
19522
+ db = openCurrentDatabase();
19523
+ } catch (error) {
19524
+ log2(`[dreamer] storage fatal; skipping ${context}: ${getErrorMessage(error)}`);
19525
+ return null;
19530
19526
  }
19531
- inRootAsyncContext2(() => setTimeout(fn, 0));
19532
- }
19533
- function yieldToEventLoop() {
19534
- return new Promise((resolve) => defer(resolve));
19527
+ if (!db) {
19528
+ log2(`[dreamer] storage unavailable; skipping ${context} (the cache schema is newer than this binary supports — restart/upgrade OpenCode/Pi/Magic Context to recover)`);
19529
+ return null;
19530
+ }
19531
+ return db;
19535
19532
  }
19536
- function runWithSessionLock(sessionId, operation) {
19537
- const previous = heapHolder.sessionLocks.get(sessionId) ?? Promise.resolve();
19538
- const run = previous.catch(() => {
19539
- return;
19540
- }).then(async () => {
19541
- await operation();
19542
- });
19543
- heapHolder.sessionLocks.set(sessionId, run);
19544
- run.finally(() => {
19545
- if (heapHolder.sessionLocks.get(sessionId) === run) {
19546
- heapHolder.sessionLocks.delete(sessionId);
19547
- }
19548
- }).catch(() => {
19533
+ var refusedEmptyIdentityDirectories = new Set;
19534
+ function logEmptyIdentityRefusalOnce(directory) {
19535
+ if (refusedEmptyIdentityDirectories.has(directory))
19549
19536
  return;
19550
- });
19551
- return run;
19537
+ refusedEmptyIdentityDirectories.add(directory);
19538
+ log2(`[dreamer] not registering ${directory}: it has no project identity, so no project-scoped work runs for it`);
19552
19539
  }
19553
- function logIndexingError(sessionId, action, error) {
19554
- if (isDatabaseLockedError(error)) {
19555
- sessionLog2(sessionId, `message FTS async ${action} skipped (database busy; will retry on next reconciliation)`);
19540
+ var registeredProjects = new Map;
19541
+ function stopDreamScheduleTimerIfIdle() {
19542
+ if (registeredProjects.size !== 0)
19556
19543
  return;
19544
+ const stopped = activeTimer !== null || startupTickTimer !== null;
19545
+ if (activeTimer) {
19546
+ clearInterval(activeTimer);
19547
+ activeTimer = null;
19557
19548
  }
19558
- sessionLog2(sessionId, `message FTS async ${action} failed: ${error instanceof Error ? error.message : String(error)}`);
19559
- log2(`[message-index-async] ${action} failed for ${sessionId}:`, error);
19560
- }
19561
- function logReconciliationStall(sessionId, reason) {
19562
- if (heapHolder.stalledSessions.has(sessionId))
19563
- return;
19564
- heapHolder.stalledSessions.add(sessionId);
19565
- sessionLog2(sessionId, `message FTS reconciliation stalled: ${reason}; will retry`);
19566
- }
19567
- async function reconcileSessionIndex(db, sessionId, readMessages) {
19568
- if (!await sourceReady(readMessages, sessionId)) {
19569
- logReconciliationStall(sessionId, "raw message reader is not ready");
19570
- return false;
19549
+ if (startupTickTimer) {
19550
+ clearTimeout(startupTickTimer);
19551
+ startupTickTimer = null;
19571
19552
  }
19572
- await runWithSessionLock(sessionId, async () => {
19573
- if (!readMessages.getRevision && heapHolder.reconciledSessions.has(sessionId))
19574
- return;
19575
- let fallbackSnapshot = null;
19576
- try {
19577
- const fullReader = typeof readMessages === "function" ? readMessages : null;
19578
- const revision = readMessages.getRevision?.(sessionId);
19579
- const previous = heapHolder.reconciledSnapshots.get(sessionId);
19580
- 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;
19581
- let cursor = getMessageIndexReconciliationStartOrdinal2(db, sessionId);
19582
- let after = cursor === previous?.count ? previous.after : undefined;
19583
- while (cursor < finalWatermark) {
19584
- const pageEnd = Math.min(finalWatermark, cursor + RECONCILIATION_BATCH_SIZE);
19585
- const messages = readMessages.readPage ? readMessages.readPage(sessionId, cursor, RECONCILIATION_BATCH_SIZE, finalWatermark, after) : (fallbackSnapshot ?? []).filter((message) => message.ordinal > cursor && message.ordinal <= pageEnd);
19586
- const retainedMessages = fallbackSnapshot ?? messages;
19587
- heapHolder.activeReconcilerBuffers.set(sessionId, retainedMessages);
19588
- withSqliteBackgroundWriter2(() => indexMessagesAfterOrdinal2(db, sessionId, messages, cursor, pageEnd));
19589
- const nextCursor = getMessageIndexReconciliationStartOrdinal2(db, sessionId);
19590
- if (nextCursor <= cursor)
19591
- break;
19592
- cursor = nextCursor;
19593
- const last = messages.at(-1);
19594
- after = last?.ordinal === cursor && typeof last.createdAt === "number" ? { timeCreated: last.createdAt, id: last.id } : undefined;
19595
- if (cursor < finalWatermark) {
19596
- await yieldToEventLoop();
19597
- }
19598
- }
19599
- if (isMessageIndexReconciledThrough2(db, sessionId, finalWatermark)) {
19600
- heapHolder.reconciledSessions.add(sessionId);
19601
- if (revision !== undefined) {
19602
- if (!after && finalWatermark > 0 && readMessages.readPage) {
19603
- const last = readMessages.readPage(sessionId, finalWatermark - 1, 1, finalWatermark).at(-1);
19604
- if (last?.ordinal === finalWatermark && typeof last.createdAt === "number")
19605
- after = { timeCreated: last.createdAt, id: last.id };
19606
- }
19607
- heapHolder.reconciledSnapshots.set(sessionId, {
19608
- revision,
19609
- count: finalWatermark,
19610
- after
19611
- });
19612
- }
19613
- } else {
19614
- logReconciliationStall(sessionId, `gap after ordinal ${cursor} (store count ${finalWatermark})`);
19615
- }
19616
- } finally {
19617
- heapHolder.activeReconcilerBuffers.delete(sessionId);
19618
- }
19619
- });
19620
- return true;
19553
+ startupJitters.clear();
19554
+ nextStartupJitterSlot = 0;
19555
+ if (stopped)
19556
+ log2("[dreamer] stopped dream schedule timer (no projects left)");
19621
19557
  }
19622
- function scheduleReconciliation(db, sessionId, readMessages) {
19623
- if (!readMessages.getRevision && heapHolder.reconciledSessions.has(sessionId) || heapHolder.reconciliationScheduledSessions.has(sessionId)) {
19558
+ async function startDreamScheduleTimer(args) {
19559
+ if (!isUsableProjectIdentity(args.projectIdentity)) {
19560
+ logEmptyIdentityRefusalOnce(args.directory);
19624
19561
  return;
19625
19562
  }
19626
- heapHolder.reconciliationScheduledSessions.add(sessionId);
19627
- scheduleAfterBootQuiet(() => {
19628
- defer(() => {
19629
- let appendedDuringReconciliation = false;
19630
- const previousSnapshot = heapHolder.reconciledSnapshots.get(sessionId);
19631
- reconcileSessionIndex(db, sessionId, readMessages).then((ran) => {
19632
- if (ran) {
19633
- const snapshot = heapHolder.reconciledSnapshots.get(sessionId);
19634
- appendedDuringReconciliation = snapshot !== undefined && snapshot !== previousSnapshot && readMessages.getRevision !== undefined && readMessages.getRevision(sessionId) !== snapshot.revision;
19635
- return;
19636
- }
19637
- inRootAsyncContext2(() => setTimeout(() => {
19638
- heapHolder.reconciliationScheduledSessions.delete(sessionId);
19639
- scheduleReconciliation(db, sessionId, readMessages);
19640
- }, PREPARE_RETRY_MS)).unref?.();
19641
- }).catch((error) => {
19642
- logReconciliationStall(sessionId, error instanceof Error ? error.message : String(error));
19643
- if (isDatabaseLockedError(error))
19644
- inRootAsyncContext2(() => setTimeout(() => scheduleReconciliation(db, sessionId, readMessages), 1000)).unref();
19645
- }).finally(() => {
19646
- heapHolder.reconciliationScheduledSessions.delete(sessionId);
19647
- if (appendedDuringReconciliation)
19648
- scheduleReconciliation(db, sessionId, readMessages);
19649
- });
19650
- });
19651
- });
19652
- }
19653
- function scheduleIncrementalIndex(db, sessionId, messageId, messageSource) {
19654
- const schedulingKey = `${sessionId}\x00${messageId}`;
19655
- const epoch = sessionEpochs.get(sessionId) ?? 0;
19656
- if (heapHolder.pendingIncrementalKeys.has(schedulingKey))
19657
- return;
19658
- const activeTimer = heapHolder.incrementalTimers.get(schedulingKey);
19659
- if (activeTimer)
19660
- clearTimeout(activeTimer);
19661
- const timer = inRootAsyncContext2(() => setTimeout(() => {
19662
- heapHolder.incrementalTimers.delete(schedulingKey);
19663
- heapHolder.pendingIncrementalKeys.add(schedulingKey);
19664
- const indexMessage = async () => {
19665
- if ((sessionEpochs.get(sessionId) ?? 0) !== epoch || !heapHolder.pendingIncrementalKeys.has(schedulingKey))
19666
- return;
19667
- const message = typeof messageSource === "function" ? messageSource(sessionId, messageId) : messageSource;
19668
- if (!message)
19669
- return;
19670
- const revisionKey = `${schedulingKey}\x00${getMessageIndexSourceIdentity2(message)}`;
19671
- if (heapHolder.completedIncrementalKeys.has(revisionKey))
19672
- return;
19673
- const currentWatermark = getLastIndexedOrdinal2(db, sessionId);
19674
- if (message.ordinal <= currentWatermark && isMessageIndexSourceCurrent2(db, sessionId, message)) {
19675
- heapHolder.completedIncrementalKeys.add(revisionKey);
19676
- return;
19677
- }
19678
- const wasReconciled = heapHolder.reconciledSessions.delete(sessionId);
19679
- await indexSingleMessageOffThread(db, sessionId, message);
19680
- if ((sessionEpochs.get(sessionId) ?? 0) !== epoch || !heapHolder.pendingIncrementalKeys.has(schedulingKey))
19681
- return;
19682
- const finalWatermark = getLastIndexedOrdinal2(db, sessionId);
19683
- if (wasReconciled && isMessageIndexReconciledThrough2(db, sessionId, finalWatermark)) {
19684
- heapHolder.reconciledSessions.add(sessionId);
19685
- }
19686
- heapHolder.completedIncrementalKeys.add(revisionKey);
19687
- };
19688
- sourceReady(messageSource, sessionId).then((ready) => {
19689
- if ((sessionEpochs.get(sessionId) ?? 0) !== epoch)
19690
- return;
19691
- if (ready)
19692
- return runWithSessionLock(sessionId, indexMessage);
19693
- setTimeout(() => scheduleIncrementalIndex(db, sessionId, messageId, messageSource), PREPARE_RETRY_MS).unref?.();
19694
- }).catch((error) => {
19695
- if ((sessionEpochs.get(sessionId) ?? 0) !== epoch)
19696
- return;
19697
- heapHolder.reconciledSessions.delete(sessionId);
19698
- logIndexingError(sessionId, `incremental index for ${messageId}`, error);
19699
- if (isDatabaseLockedError(error))
19700
- setTimeout(() => scheduleIncrementalIndex(db, sessionId, messageId, messageSource), 1000).unref();
19701
- }).finally(() => {
19702
- if ((sessionEpochs.get(sessionId) ?? 0) === epoch)
19703
- heapHolder.pendingIncrementalKeys.delete(schedulingKey);
19704
- });
19705
- }, INCREMENTAL_DEBOUNCE_MS));
19706
- heapHolder.incrementalTimers.set(schedulingKey, timer);
19707
- }
19708
- async function indexSingleMessageOffThread(db, sessionId, message) {
19709
- const path = getSqliteDatabasePath2(db);
19710
- if (!path)
19711
- return withSqliteBackgroundWriter2(() => indexSingleMessage2(db, sessionId, message));
19712
- const cancellation = new Int32Array(new SharedArrayBuffer(4));
19713
- const active = indexCancellations.get(sessionId) ?? new Set;
19714
- active.add(cancellation);
19715
- indexCancellations.set(sessionId, active);
19716
- const result = await runSessionWarmupWorker({
19717
- path,
19718
- sessionId,
19719
- indexMessage: message,
19720
- harness: getHarness2(),
19721
- indexCancellation: cancellation.buffer,
19722
- ...indexCoordinationInput2()
19723
- }, {}).finally(() => {
19724
- active.delete(cancellation);
19725
- if (active.size === 0)
19726
- indexCancellations.delete(sessionId);
19727
- });
19728
- if (!("inserted" in result))
19729
- throw new Error("failure" in result ? result.detail : "unexpected indexing worker reply");
19730
- if (result.measurements) {
19731
- const sessions = workerMeasurements.get(db) ?? new Map;
19732
- sessions.set(sessionId, result.measurements);
19733
- workerMeasurements.set(db, sessions);
19734
- if (result.measurements.skipped)
19735
- log2(`[magic-context] incremental FTS skipped oversized source ${message.id} (${result.measurements.sourceBytes} bytes); identity and progress persisted`);
19736
- }
19737
- return result.inserted;
19738
- }
19739
- async function sweepOrphanedOpenCodeMessageIndexesOffThread(db, openSource, options = {}) {
19740
- if (getHarness2() === "opencode" || getHarness2() === "opencode2")
19741
- options = {
19742
- ...options,
19743
- fenceLockNames: options.fenceLockNames ?? takeLkgRecoveryLockSweepPage()
19744
- };
19745
- if (getHarness2() === "opencode2")
19746
- openSource = () => new Database2(gaDatabasePath(getDataDir(), process.env.OPENCODE_CHANNEL ?? "latest"), {
19747
- readonly: true,
19748
- fileMustExist: true
19749
- });
19750
- const path = getSqliteDatabasePath2(db);
19751
- if (!path || getHarness2() !== "opencode" && getHarness2() !== "opencode2")
19752
- return sweepOrphanedOpenCodeMessageIndexesCooperatively2(db, openSource, options);
19753
- let source = null;
19754
- let sourcePath = null;
19755
- try {
19756
- source = openSource();
19757
- if (source)
19758
- sourcePath = getSqliteDatabasePath2(source);
19759
- } catch {} finally {
19760
- if (sourcePath)
19761
- source?.close();
19762
- }
19763
- if (!sourcePath)
19764
- return sweepOrphanedOpenCodeMessageIndexesCooperatively2(db, () => source, options);
19765
- const reply = await runSessionWarmupWorker({
19766
- path,
19767
- sessionId: "",
19768
- harness: getHarness2(),
19769
- orphanSweep: { sourcePath, options }
19770
- }, {});
19771
- if (!("orphanSweep" in reply))
19772
- throw new Error("message history orphan sweep worker failed");
19773
- return reply.orphanSweep;
19774
- }
19775
- function clearSessionTracking(sessionId) {
19776
- sessionEpochs.set(sessionId, (sessionEpochs.get(sessionId) ?? 0) + 1);
19777
- for (const cancellation of indexCancellations.get(sessionId) ?? [])
19778
- Atomics.store(cancellation, 0, 1);
19779
- heapHolder.clearAndReindexScheduledSessions.delete(sessionId);
19780
- heapHolder.reconciledSessions.delete(sessionId);
19781
- heapHolder.reconciledSnapshots.delete(sessionId);
19782
- heapHolder.stalledSessions.delete(sessionId);
19783
- heapHolder.reconciliationScheduledSessions.delete(sessionId);
19784
- heapHolder.sessionLocks.delete(sessionId);
19785
- heapHolder.activeReconcilerBuffers.delete(sessionId);
19786
- const prefix = `${sessionId}\x00`;
19787
- for (const [key, timer] of heapHolder.incrementalTimers) {
19788
- if (key.startsWith(prefix)) {
19789
- clearTimeout(timer);
19790
- heapHolder.incrementalTimers.delete(key);
19791
- }
19792
- }
19793
- for (const key of heapHolder.pendingIncrementalKeys) {
19794
- if (key.startsWith(prefix)) {
19795
- heapHolder.pendingIncrementalKeys.delete(key);
19796
- }
19797
- }
19798
- for (const key of heapHolder.completedIncrementalKeys) {
19799
- if (key.startsWith(prefix))
19800
- heapHolder.completedIncrementalKeys.delete(key);
19801
- }
19802
- }
19803
-
19804
- // ../plugin/src/plugin/dream-timer.ts
19805
- var DREAM_TIMER_INTERVAL_MS = 15 * 60 * 1000;
19806
- var GIT_COMMIT_BACKLOG_DRAIN_MAX_MS = 5 * 60 * 1000;
19807
- var BOOT_PROJECT_JITTER_SLOT_MS = 1000;
19808
- var activeTimer = null;
19809
- var tickInFlight = false;
19810
- var startupTickTimer = null;
19811
- var startupTimers = new Map;
19812
- var startupJitters = new Map;
19813
- var nextStartupJitterSlot = 0;
19814
- function directoryStillExists(directory) {
19815
- try {
19816
- return statSync3(directory).isDirectory();
19817
- } catch {
19818
- return false;
19819
- }
19820
- }
19821
- function openTimerDatabaseOrNull(context) {
19822
- let db;
19823
- try {
19824
- db = openCurrentDatabase();
19825
- } catch (error) {
19826
- log2(`[dreamer] storage fatal; skipping ${context}: ${getErrorMessage(error)}`);
19827
- return null;
19828
- }
19829
- if (!db) {
19830
- log2(`[dreamer] storage unavailable; skipping ${context} (the cache schema is newer than this binary supports — restart/upgrade OpenCode/Pi/Magic Context to recover)`);
19831
- return null;
19832
- }
19833
- return db;
19834
- }
19835
- var refusedEmptyIdentityDirectories = new Set;
19836
- function logEmptyIdentityRefusalOnce(directory) {
19837
- if (refusedEmptyIdentityDirectories.has(directory))
19838
- return;
19839
- refusedEmptyIdentityDirectories.add(directory);
19840
- log2(`[dreamer] not registering ${directory}: it has no project identity, so no project-scoped work runs for it`);
19841
- }
19842
- var registeredProjects = new Map;
19843
- function stopDreamScheduleTimerIfIdle() {
19844
- if (registeredProjects.size !== 0)
19845
- return;
19846
- const stopped = activeTimer !== null || startupTickTimer !== null;
19847
- if (activeTimer) {
19848
- clearInterval(activeTimer);
19849
- activeTimer = null;
19850
- }
19851
- if (startupTickTimer) {
19852
- clearTimeout(startupTickTimer);
19853
- startupTickTimer = null;
19854
- }
19855
- startupJitters.clear();
19856
- nextStartupJitterSlot = 0;
19857
- if (stopped)
19858
- log2("[dreamer] stopped dream schedule timer (no projects left)");
19859
- }
19860
- async function startDreamScheduleTimer(args) {
19861
- if (!isUsableProjectIdentity(args.projectIdentity)) {
19862
- logEmptyIdentityRefusalOnce(args.directory);
19863
- return;
19864
- }
19865
- beginBootQuietPeriod();
19866
- const db = openTimerDatabaseOrNull("schedule timer registration");
19867
- if (!db)
19563
+ beginBootQuietPeriod();
19564
+ const db = openTimerDatabaseOrNull("schedule timer registration");
19565
+ if (!db)
19868
19566
  return;
19869
19567
  const dreamingEnabled = Boolean(args.dreamerConfig && args.dreamerConfig.disable !== true);
19870
19568
  const embeddingSweepEnabled = args.memoryEnabled === true;
@@ -19996,12 +19694,6 @@ async function runMessageHistoryMaintenance(db) {
19996
19694
  log2(`[message-index] pending Rust session cleanup: cleared=${rustCleanup.cleared} failed=${rustCleanup.failedSessionIds.length}`);
19997
19695
  }
19998
19696
  }
19999
- const sweep = await sweepOrphanedOpenCodeMessageIndexesOffThread(db, openOpenCodeDb);
20000
- if (sweep.reason)
20001
- log2(`[message-index] orphan sweep parked: ${sweep.reason}`);
20002
- if (sweep.deleted > 0) {
20003
- log2(`[message-index] orphan sweep: scanned=${sweep.scanned} deleted=${sweep.deleted} cursor=${sweep.cursor || "<complete>"}`);
20004
- }
20005
19697
  }
20006
19698
  function startupJitterMs(directory) {
20007
19699
  const existing = startupJitters.get(directory);
@@ -22953,6 +22645,8 @@ async function awaitInFlightDreamers(registrationOwner) {
22953
22645
  return;
22954
22646
  await Promise.allSettled(runs);
22955
22647
  }
22648
+ var RUNNER_TIMEOUT_HEADROOM_MINUTES = 5;
22649
+ var UNKNOWN_TASK_TIMEOUT_MINUTES = 30;
22956
22650
  function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRefreshNeeded, isRegistrationOwnerActive = () => true, getMaxOutputTokens = () => opts.config.maxTokens, getDreamerConfig = () => opts.config) {
22957
22651
  const runner = piSubagentRunnerFactory();
22958
22652
  const budgetStates = new Map;
@@ -23006,7 +22700,7 @@ function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRe
23006
22700
  userMessage,
23007
22701
  model: perTaskModel,
23008
22702
  fallbackModels: undefined,
23009
- timeoutMs: 30 * 60 * 1000,
22703
+ timeoutMs: ((taskConfig?.timeoutMinutes ?? UNKNOWN_TASK_TIMEOUT_MINUTES) + RUNNER_TIMEOUT_HEADROOM_MINUTES) * 60 * 1000,
23010
22704
  cwd: dreamSession.directory,
23011
22705
  signal: args.signal ? AbortSignal.any([args.signal, controller.signal]) : controller.signal,
23012
22706
  thinkingLevel: extractBodyVariant(args),
@@ -23115,7 +22809,11 @@ function accountingTaskFromTitle(title) {
23115
22809
  const task = title.slice("magic-context-dream-".length);
23116
22810
  if (!title.startsWith("magic-context-dream-") || task.length === 0)
23117
22811
  return null;
23118
- return task === "classify" ? "classify-memories" : task;
22812
+ if (task === "classify")
22813
+ return "classify-memories";
22814
+ if (task === "user-memories")
22815
+ return "review-user-memories";
22816
+ return task;
23119
22817
  }
23120
22818
  function readSessionTitle(args) {
23121
22819
  const body = args.body;
@@ -23934,6 +23632,290 @@ function commitPiCompactionModeRecord(db, sessionId, record) {
23934
23632
  // src/context-handler.ts
23935
23633
  import * as crypto3 from "node:crypto";
23936
23634
 
23635
+ // ../plugin/src/features/magic-context/message-index-async.ts
23636
+ var indexCancellations = new Map;
23637
+ var sessionEpochs = new Map;
23638
+ var workerMeasurements = new WeakMap;
23639
+ function isDatabaseLockedError(error) {
23640
+ if (!error || typeof error !== "object")
23641
+ return false;
23642
+ const e = error;
23643
+ if (typeof e.code === "string") {
23644
+ if (e.code === "SQLITE_BUSY" || e.code === "SQLITE_LOCKED")
23645
+ return true;
23646
+ }
23647
+ if (typeof e.message === "string") {
23648
+ if (/database is locked/i.test(e.message))
23649
+ return true;
23650
+ if (/sqlite_(busy|locked)/i.test(e.message))
23651
+ return true;
23652
+ }
23653
+ return false;
23654
+ }
23655
+ var INCREMENTAL_DEBOUNCE_MS = 100;
23656
+ var RECONCILIATION_BATCH_SIZE = 100;
23657
+
23658
+ class MagicContextMessageIndexHeapHolder {
23659
+ reconciledSessions = new Set;
23660
+ reconciledSnapshots = new Map;
23661
+ stalledSessions = new Set;
23662
+ reconciliationScheduledSessions = new Set;
23663
+ clearAndReindexScheduledSessions = new Map;
23664
+ sessionLocks = new Map;
23665
+ incrementalTimers = new Map;
23666
+ pendingIncrementalKeys = new Set;
23667
+ completedIncrementalKeys = new Set;
23668
+ activeReconcilerBuffers = new Map;
23669
+ }
23670
+ var heapHolder = new MagicContextMessageIndexHeapHolder;
23671
+ var PREPARE_RETRY_MS = 60000;
23672
+ async function sourceReady(source, sessionId) {
23673
+ const prepare = source?.prepare;
23674
+ return typeof prepare === "function" ? prepare(sessionId) : true;
23675
+ }
23676
+ function defer(fn) {
23677
+ const immediate = globalThis.setImmediate;
23678
+ if (typeof immediate === "function") {
23679
+ inRootAsyncContext2(() => immediate(fn));
23680
+ return;
23681
+ }
23682
+ inRootAsyncContext2(() => setTimeout(fn, 0));
23683
+ }
23684
+ function yieldToEventLoop() {
23685
+ return new Promise((resolve) => defer(resolve));
23686
+ }
23687
+ function runWithSessionLock(sessionId, operation) {
23688
+ const previous = heapHolder.sessionLocks.get(sessionId) ?? Promise.resolve();
23689
+ const run = previous.catch(() => {
23690
+ return;
23691
+ }).then(async () => {
23692
+ await operation();
23693
+ });
23694
+ heapHolder.sessionLocks.set(sessionId, run);
23695
+ run.finally(() => {
23696
+ if (heapHolder.sessionLocks.get(sessionId) === run) {
23697
+ heapHolder.sessionLocks.delete(sessionId);
23698
+ }
23699
+ }).catch(() => {
23700
+ return;
23701
+ });
23702
+ return run;
23703
+ }
23704
+ function logIndexingError(sessionId, action, error) {
23705
+ if (isDatabaseLockedError(error)) {
23706
+ sessionLog2(sessionId, `message FTS async ${action} skipped (database busy; will retry on next reconciliation)`);
23707
+ return;
23708
+ }
23709
+ sessionLog2(sessionId, `message FTS async ${action} failed: ${error instanceof Error ? error.message : String(error)}`);
23710
+ log2(`[message-index-async] ${action} failed for ${sessionId}:`, error);
23711
+ }
23712
+ function logReconciliationStall(sessionId, reason) {
23713
+ if (heapHolder.stalledSessions.has(sessionId))
23714
+ return;
23715
+ heapHolder.stalledSessions.add(sessionId);
23716
+ sessionLog2(sessionId, `message FTS reconciliation stalled: ${reason}; will retry`);
23717
+ }
23718
+ async function reconcileSessionIndex(db, sessionId, readMessages) {
23719
+ if (!await sourceReady(readMessages, sessionId)) {
23720
+ logReconciliationStall(sessionId, "raw message reader is not ready");
23721
+ return false;
23722
+ }
23723
+ await runWithSessionLock(sessionId, async () => {
23724
+ if (!readMessages.getRevision && heapHolder.reconciledSessions.has(sessionId))
23725
+ return;
23726
+ let fallbackSnapshot = null;
23727
+ try {
23728
+ const fullReader = typeof readMessages === "function" ? readMessages : null;
23729
+ const revision = readMessages.getRevision?.(sessionId);
23730
+ const previous = heapHolder.reconciledSnapshots.get(sessionId);
23731
+ 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;
23732
+ let cursor = getMessageIndexReconciliationStartOrdinal(db, sessionId);
23733
+ let after = cursor === previous?.count ? previous.after : undefined;
23734
+ while (cursor < finalWatermark) {
23735
+ const pageEnd = Math.min(finalWatermark, cursor + RECONCILIATION_BATCH_SIZE);
23736
+ const messages = readMessages.readPage ? readMessages.readPage(sessionId, cursor, RECONCILIATION_BATCH_SIZE, finalWatermark, after) : (fallbackSnapshot ?? []).filter((message) => message.ordinal > cursor && message.ordinal <= pageEnd);
23737
+ const retainedMessages = fallbackSnapshot ?? messages;
23738
+ heapHolder.activeReconcilerBuffers.set(sessionId, retainedMessages);
23739
+ withSqliteBackgroundWriter2(() => indexMessagesAfterOrdinal(db, sessionId, messages, cursor, pageEnd));
23740
+ const nextCursor = getMessageIndexReconciliationStartOrdinal(db, sessionId);
23741
+ if (nextCursor <= cursor)
23742
+ break;
23743
+ cursor = nextCursor;
23744
+ const last = messages.at(-1);
23745
+ after = last?.ordinal === cursor && typeof last.createdAt === "number" ? { timeCreated: last.createdAt, id: last.id } : undefined;
23746
+ if (cursor < finalWatermark) {
23747
+ await yieldToEventLoop();
23748
+ }
23749
+ }
23750
+ if (isMessageIndexReconciledThrough(db, sessionId, finalWatermark)) {
23751
+ heapHolder.reconciledSessions.add(sessionId);
23752
+ if (revision !== undefined) {
23753
+ if (!after && finalWatermark > 0 && readMessages.readPage) {
23754
+ const last = readMessages.readPage(sessionId, finalWatermark - 1, 1, finalWatermark).at(-1);
23755
+ if (last?.ordinal === finalWatermark && typeof last.createdAt === "number")
23756
+ after = { timeCreated: last.createdAt, id: last.id };
23757
+ }
23758
+ heapHolder.reconciledSnapshots.set(sessionId, {
23759
+ revision,
23760
+ count: finalWatermark,
23761
+ after
23762
+ });
23763
+ }
23764
+ } else {
23765
+ logReconciliationStall(sessionId, `gap after ordinal ${cursor} (store count ${finalWatermark})`);
23766
+ }
23767
+ } finally {
23768
+ heapHolder.activeReconcilerBuffers.delete(sessionId);
23769
+ }
23770
+ });
23771
+ return true;
23772
+ }
23773
+ function scheduleReconciliation(db, sessionId, readMessages) {
23774
+ if (!readMessages.getRevision && heapHolder.reconciledSessions.has(sessionId) || heapHolder.reconciliationScheduledSessions.has(sessionId)) {
23775
+ return;
23776
+ }
23777
+ heapHolder.reconciliationScheduledSessions.add(sessionId);
23778
+ scheduleAfterBootQuiet(() => {
23779
+ defer(() => {
23780
+ let appendedDuringReconciliation = false;
23781
+ const previousSnapshot = heapHolder.reconciledSnapshots.get(sessionId);
23782
+ reconcileSessionIndex(db, sessionId, readMessages).then((ran) => {
23783
+ if (ran) {
23784
+ const snapshot = heapHolder.reconciledSnapshots.get(sessionId);
23785
+ appendedDuringReconciliation = snapshot !== undefined && snapshot !== previousSnapshot && readMessages.getRevision !== undefined && readMessages.getRevision(sessionId) !== snapshot.revision;
23786
+ return;
23787
+ }
23788
+ inRootAsyncContext2(() => setTimeout(() => {
23789
+ heapHolder.reconciliationScheduledSessions.delete(sessionId);
23790
+ scheduleReconciliation(db, sessionId, readMessages);
23791
+ }, PREPARE_RETRY_MS)).unref?.();
23792
+ }).catch((error) => {
23793
+ logReconciliationStall(sessionId, error instanceof Error ? error.message : String(error));
23794
+ if (isDatabaseLockedError(error))
23795
+ inRootAsyncContext2(() => setTimeout(() => scheduleReconciliation(db, sessionId, readMessages), 1000)).unref();
23796
+ }).finally(() => {
23797
+ heapHolder.reconciliationScheduledSessions.delete(sessionId);
23798
+ if (appendedDuringReconciliation)
23799
+ scheduleReconciliation(db, sessionId, readMessages);
23800
+ });
23801
+ });
23802
+ });
23803
+ }
23804
+ function scheduleIncrementalIndex(db, sessionId, messageId, messageSource) {
23805
+ const schedulingKey = `${sessionId}\x00${messageId}`;
23806
+ const epoch = sessionEpochs.get(sessionId) ?? 0;
23807
+ if (heapHolder.pendingIncrementalKeys.has(schedulingKey))
23808
+ return;
23809
+ const activeTimer = heapHolder.incrementalTimers.get(schedulingKey);
23810
+ if (activeTimer)
23811
+ clearTimeout(activeTimer);
23812
+ const timer = inRootAsyncContext2(() => setTimeout(() => {
23813
+ heapHolder.incrementalTimers.delete(schedulingKey);
23814
+ heapHolder.pendingIncrementalKeys.add(schedulingKey);
23815
+ const indexMessage = async () => {
23816
+ if ((sessionEpochs.get(sessionId) ?? 0) !== epoch || !heapHolder.pendingIncrementalKeys.has(schedulingKey))
23817
+ return;
23818
+ const message = typeof messageSource === "function" ? messageSource(sessionId, messageId) : messageSource;
23819
+ if (!message)
23820
+ return;
23821
+ const revisionKey = `${schedulingKey}\x00${getMessageIndexSourceIdentity(message)}`;
23822
+ if (heapHolder.completedIncrementalKeys.has(revisionKey))
23823
+ return;
23824
+ const currentWatermark = getLastIndexedOrdinal(db, sessionId);
23825
+ if (message.ordinal <= currentWatermark && isMessageIndexSourceCurrent(db, sessionId, message)) {
23826
+ heapHolder.completedIncrementalKeys.add(revisionKey);
23827
+ return;
23828
+ }
23829
+ const wasReconciled = heapHolder.reconciledSessions.delete(sessionId);
23830
+ await indexSingleMessageOffThread(db, sessionId, message);
23831
+ if ((sessionEpochs.get(sessionId) ?? 0) !== epoch || !heapHolder.pendingIncrementalKeys.has(schedulingKey))
23832
+ return;
23833
+ const finalWatermark = getLastIndexedOrdinal(db, sessionId);
23834
+ if (wasReconciled && isMessageIndexReconciledThrough(db, sessionId, finalWatermark)) {
23835
+ heapHolder.reconciledSessions.add(sessionId);
23836
+ }
23837
+ heapHolder.completedIncrementalKeys.add(revisionKey);
23838
+ };
23839
+ sourceReady(messageSource, sessionId).then((ready) => {
23840
+ if ((sessionEpochs.get(sessionId) ?? 0) !== epoch)
23841
+ return;
23842
+ if (ready)
23843
+ return runWithSessionLock(sessionId, indexMessage);
23844
+ setTimeout(() => scheduleIncrementalIndex(db, sessionId, messageId, messageSource), PREPARE_RETRY_MS).unref?.();
23845
+ }).catch((error) => {
23846
+ if ((sessionEpochs.get(sessionId) ?? 0) !== epoch)
23847
+ return;
23848
+ heapHolder.reconciledSessions.delete(sessionId);
23849
+ logIndexingError(sessionId, `incremental index for ${messageId}`, error);
23850
+ if (isDatabaseLockedError(error))
23851
+ setTimeout(() => scheduleIncrementalIndex(db, sessionId, messageId, messageSource), 1000).unref();
23852
+ }).finally(() => {
23853
+ if ((sessionEpochs.get(sessionId) ?? 0) === epoch)
23854
+ heapHolder.pendingIncrementalKeys.delete(schedulingKey);
23855
+ });
23856
+ }, INCREMENTAL_DEBOUNCE_MS));
23857
+ heapHolder.incrementalTimers.set(schedulingKey, timer);
23858
+ }
23859
+ async function indexSingleMessageOffThread(db, sessionId, message) {
23860
+ const path = getSqliteDatabasePath2(db);
23861
+ if (!path)
23862
+ return withSqliteBackgroundWriter2(() => indexSingleMessage(db, sessionId, message));
23863
+ const cancellation = new Int32Array(new SharedArrayBuffer(4));
23864
+ const active = indexCancellations.get(sessionId) ?? new Set;
23865
+ active.add(cancellation);
23866
+ indexCancellations.set(sessionId, active);
23867
+ const result = await runSessionWarmupWorker({
23868
+ path,
23869
+ sessionId,
23870
+ indexMessage: message,
23871
+ harness: getHarness2(),
23872
+ indexCancellation: cancellation.buffer,
23873
+ ...indexCoordinationInput2()
23874
+ }, {}).finally(() => {
23875
+ active.delete(cancellation);
23876
+ if (active.size === 0)
23877
+ indexCancellations.delete(sessionId);
23878
+ });
23879
+ if (!("inserted" in result))
23880
+ throw new Error("failure" in result ? result.detail : "unexpected indexing worker reply");
23881
+ if (result.measurements) {
23882
+ const sessions = workerMeasurements.get(db) ?? new Map;
23883
+ sessions.set(sessionId, result.measurements);
23884
+ workerMeasurements.set(db, sessions);
23885
+ if (result.measurements.skipped)
23886
+ log2(`[magic-context] incremental FTS skipped oversized source ${message.id} (${result.measurements.sourceBytes} bytes); identity and progress persisted`);
23887
+ }
23888
+ return result.inserted;
23889
+ }
23890
+ function clearSessionTracking(sessionId) {
23891
+ sessionEpochs.set(sessionId, (sessionEpochs.get(sessionId) ?? 0) + 1);
23892
+ for (const cancellation of indexCancellations.get(sessionId) ?? [])
23893
+ Atomics.store(cancellation, 0, 1);
23894
+ heapHolder.clearAndReindexScheduledSessions.delete(sessionId);
23895
+ heapHolder.reconciledSessions.delete(sessionId);
23896
+ heapHolder.reconciledSnapshots.delete(sessionId);
23897
+ heapHolder.stalledSessions.delete(sessionId);
23898
+ heapHolder.reconciliationScheduledSessions.delete(sessionId);
23899
+ heapHolder.sessionLocks.delete(sessionId);
23900
+ heapHolder.activeReconcilerBuffers.delete(sessionId);
23901
+ const prefix = `${sessionId}\x00`;
23902
+ for (const [key, timer] of heapHolder.incrementalTimers) {
23903
+ if (key.startsWith(prefix)) {
23904
+ clearTimeout(timer);
23905
+ heapHolder.incrementalTimers.delete(key);
23906
+ }
23907
+ }
23908
+ for (const key of heapHolder.pendingIncrementalKeys) {
23909
+ if (key.startsWith(prefix)) {
23910
+ heapHolder.pendingIncrementalKeys.delete(key);
23911
+ }
23912
+ }
23913
+ for (const key of heapHolder.completedIncrementalKeys) {
23914
+ if (key.startsWith(prefix))
23915
+ heapHolder.completedIncrementalKeys.delete(key);
23916
+ }
23917
+ }
23918
+
23937
23919
  // ../plugin/src/hooks/magic-context/event-resolvers.ts
23938
23920
  var DEFAULT_CONTEXT_LIMIT = 200000;
23939
23921
  var clampWarnSeen = new Set;
@@ -26380,6 +26362,60 @@ function isHistorianDrainBudgetSpent(args) {
26380
26362
  return true;
26381
26363
  }
26382
26364
 
26365
+ // ../plugin/src/hooks/magic-context/historian-history-wait.ts
26366
+ var historyWaits = new Map;
26367
+ function newestMessageId(snapshot) {
26368
+ try {
26369
+ const count = getRawSessionMessageOrdinalCount2(snapshot.sessionId);
26370
+ const ids = readRawSessionMessageIdOrdinalsForRange2(snapshot.sessionId, count, count);
26371
+ const newest = ids.keys().next().value;
26372
+ if (newest)
26373
+ return newest;
26374
+ } catch {}
26375
+ return snapshot.rawLastMessageIdAtTrigger;
26376
+ }
26377
+ function recoveryWindow(snapshot, percentage) {
26378
+ return percentage >= escalationBands(snapshot.executeThresholdPercentage).forceMaterializationPercentage || snapshot.emergencyTailScale !== undefined;
26379
+ }
26380
+ function clearHistorianHistoryWait(sessionId) {
26381
+ historyWaits.delete(sessionId);
26382
+ }
26383
+ function recordHistorianNoProgress(snapshot, emergency = false) {
26384
+ const newest = newestMessageId(snapshot);
26385
+ if (!newest)
26386
+ return;
26387
+ const previous = historyWaits.get(snapshot.sessionId);
26388
+ const sameHistory = previous?.newestMessageId === newest;
26389
+ historyWaits.set(snapshot.sessionId, {
26390
+ newestMessageId: newest,
26391
+ recoveryAttempted: sameHistory && previous.recoveryAttempted || emergency || recoveryWindow(snapshot, snapshot.usagePercentage),
26392
+ floorLiftAttempted: sameHistory && previous.floorLiftAttempted || snapshot.liftLivePromptFloor === true
26393
+ });
26394
+ }
26395
+ function shouldWaitForNewHistorianHistory(snapshot, options = {}) {
26396
+ const wait = historyWaits.get(snapshot.sessionId);
26397
+ if (!wait)
26398
+ return false;
26399
+ const newest = newestMessageId(snapshot);
26400
+ if (newest && newest !== wait.newestMessageId) {
26401
+ try {
26402
+ const order = compareRawSessionMessageOrder2(snapshot.sessionId, newest, wait.newestMessageId);
26403
+ if (order === null || order > 0)
26404
+ return false;
26405
+ } catch {}
26406
+ }
26407
+ const recovery = options.emergency || recoveryWindow(snapshot, options.usagePercentage ?? snapshot.usagePercentage);
26408
+ const floorLift = snapshot.liftLivePromptFloor === true;
26409
+ if (recovery && !wait.recoveryAttempted || floorLift && !wait.floorLiftAttempted) {
26410
+ if (options.claim) {
26411
+ wait.recoveryAttempted ||= recovery;
26412
+ wait.floorLiftAttempted ||= floorLift;
26413
+ }
26414
+ return false;
26415
+ }
26416
+ return true;
26417
+ }
26418
+
26383
26419
  // ../plugin/src/hooks/magic-context/compartment-trigger.ts
26384
26420
  var PROACTIVE_TRIGGER_OFFSET_PERCENTAGE = 2;
26385
26421
  var POST_DROP_TARGET_RATIO = 0.75;
@@ -26638,6 +26674,13 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
26638
26674
  }
26639
26675
  return { shouldFire: false };
26640
26676
  }
26677
+ if (tailInfo.boundarySnapshot && shouldWaitForNewHistorianHistory(tailInfo.boundarySnapshot, {
26678
+ usagePercentage: usage.percentage,
26679
+ emergency: getOverflowState(db, sessionId).needsEmergencyRecovery
26680
+ })) {
26681
+ sessionLog2(sessionId, "compartment trigger: waiting for new history after no forward progress");
26682
+ return { shouldFire: false };
26683
+ }
26641
26684
  const canClearReasoning = reasoningProjection?.canClearReasoning ?? modelAcceptsEmptyContent(reasoningProjection?.providerID);
26642
26685
  const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage, preloadedActiveTags ?? getActiveTagsBySession2(db, sessionId), keepReasoningTokens, sessionMeta.clearedReasoningThroughTag, canClearReasoning, reasoningProjection?.budgetCutoff, eligiblePendingDropTagIds);
26643
26686
  const relativePostDropTarget = executeThresholdPercentage * POST_DROP_TARGET_RATIO;
@@ -30030,6 +30073,14 @@ async function runPiHistorian(deps) {
30030
30073
  return;
30031
30074
  }
30032
30075
  const protectedTailStart = boundarySnapshot.protectedTailStart;
30076
+ if (!forceDrainQuota && shouldWaitForNewHistorianHistory(boundarySnapshot, {
30077
+ emergency: getOverflowState(db, sessionId).needsEmergencyRecovery
30078
+ })) {
30079
+ telemetry.status = "noop";
30080
+ telemetry.failureReason = "waiting for new history after no forward progress";
30081
+ sessionLog2(sessionId, "historian no-op: waiting for new history after no forward progress");
30082
+ return;
30083
+ }
30033
30084
  const eligibleEndOrdinal = Math.min(boundarySnapshot.eligibleEndOrdinal, protectedTailStart);
30034
30085
  if (protectedTailStart <= offset || eligibleEndOrdinal <= offset) {
30035
30086
  telemetry.status = "noop";
@@ -30230,6 +30281,12 @@ ${chunkText}`,
30230
30281
  };
30231
30282
  retainDrainReservationForRetryThrottle = true;
30232
30283
  const historianEditorSystemPrompt = withContentLanguageDirective(HISTORIAN_EDITOR_SYSTEM_PROMPT, deps.language, { preserveUserQuotes: true });
30284
+ if (!forceDrainQuota) {
30285
+ shouldWaitForNewHistorianHistory(boundarySnapshot, {
30286
+ emergency: getOverflowState(db, sessionId).needsEmergencyRecovery,
30287
+ claim: true
30288
+ });
30289
+ }
30233
30290
  const firstResult = await runHistorianSubagentWithTransientRetries({
30234
30291
  runner,
30235
30292
  sessionId,
@@ -30393,6 +30450,7 @@ ${chunkText}`,
30393
30450
  const failCount = incrementHistorianFailure(db, sessionId, errorMsg);
30394
30451
  await notify(buildHistorianFailureNotice(failCount, errorMsg));
30395
30452
  }
30453
+ recordHistorianNoProgress(boundarySnapshot, inEmergency);
30396
30454
  rollbackDrainReservation();
30397
30455
  return;
30398
30456
  }
@@ -30488,6 +30546,7 @@ ${chunkText}`,
30488
30546
  }
30489
30547
  db.exec("COMMIT");
30490
30548
  published = true;
30549
+ clearHistorianHistoryWait(sessionId);
30491
30550
  logSlowWriteTransaction("pi_historian_publish", transactionStartedAt);
30492
30551
  } finally {
30493
30552
  if (!published) {