@cortexkit/pi-magic-context 0.49.6 → 0.49.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/auto-search-worker.js +10 -7
  2. package/dist/{compaction-marker-candidates-9w5g66at.js → compaction-marker-candidates-n4dphyz4.js} +3 -3
  3. package/dist/compaction-marker-candidates-worker.js +2 -2
  4. package/dist/embedding-backfill-chva00z3.js +20 -0
  5. package/dist/embedding-worker-process.js +6 -2
  6. package/dist/embedding-worker.js +6 -5
  7. package/dist/historian-calibration-extension.js +1 -1
  8. package/dist/{index-j7pa3t4s.js → index-18qe4gs1.js} +113 -27
  9. package/dist/{index-0j0zqb4t.js → index-3veejgp8.js} +1 -1
  10. package/dist/{index-0vrkp1jq.js → index-43zqh10z.js} +1 -1
  11. package/dist/{index-r56xgha8.js → index-4pxydwvg.js} +1 -1
  12. package/dist/{index-5rc9kvrk.js → index-4vj37yw9.js} +4 -3
  13. package/dist/{index-fcacf11g.js → index-54r5vs9w.js} +4 -4
  14. package/dist/{index-cd6bjrj4.js → index-6e3yv1jt.js} +4 -4
  15. package/dist/{index-swab4319.js → index-729kkhmf.js} +4 -3
  16. package/dist/{index-gc2749rx.js → index-8yjhda46.js} +9 -6
  17. package/dist/{index-8vbh4tzw.js → index-asrxgdt4.js} +63 -19
  18. package/dist/{index-wf7mwdnf.js → index-fdxzpnbj.js} +2 -2
  19. package/dist/{index-kamhcj15.js → index-gqxsds3q.js} +6 -3
  20. package/dist/{index-x3dcnxh0.js → index-gycf3sp7.js} +5 -1
  21. package/dist/{index-kxxdyjfs.js → index-hy44m6jc.js} +1 -1
  22. package/dist/{index-0p9gemc6.js → index-jsyjxgfy.js} +172 -36
  23. package/dist/{index-290tzwp1.js → index-kh3jt7t9.js} +1 -1
  24. package/dist/{index-hfnjfk4d.js → index-pdhbj18g.js} +71 -70
  25. package/dist/{index-fx0r5485.js → index-qc8e13wc.js} +22 -22
  26. package/dist/{index-9mn34fnx.js → index-r8cy8hmq.js} +5 -5
  27. package/dist/{index-57mvvbbh.js → index-rggtvnbg.js} +40 -29
  28. package/dist/{index-21v1kfs7.js → index-szh001qb.js} +6 -3
  29. package/dist/{index-cyrnv16r.js → index-ta0ed2nc.js} +2 -0
  30. package/dist/{index-5z1h0qsa.js → index-tdhppxwd.js} +9 -9
  31. package/dist/{index-p4matwj0.js → index-v26j81w6.js} +5 -5
  32. package/dist/{index-mcgehvgw.js → index-zzn7qbps.js} +1 -1
  33. package/dist/index.js +1071 -390
  34. package/dist/{logger-j6tecd1h.js → logger-qg51hmp7.js} +1 -1
  35. package/dist/{message-index-m6snq0fh.js → message-index-1wta074c.js} +14 -14
  36. package/dist/{message-index-bounded-8a94k72f.js → message-index-bounded-ss3ctdwb.js} +14 -14
  37. package/dist/{message-time-backfill-xqwg3hg1.js → message-time-backfill-q9s88zdv.js} +3 -3
  38. package/dist/migration-worker.js +10 -10
  39. package/dist/{project-embedding-registry-3x6nmszv.js → project-embedding-registry-bxawck58.js} +11 -11
  40. package/dist/raw-ordinal-warmup-worker.js +17 -15
  41. package/dist/{read-session-chunk-49e1ayzs.js → read-session-chunk-1jagnq05.js} +11 -9
  42. package/dist/{read-session-raw-545mgtjz.js → read-session-raw-hm7ptnrh.js} +3 -3
  43. package/dist/{safe-notification-target-vn9kpmv4.js → safe-notification-target-62ty6rvp.js} +2 -2
  44. package/dist/{search-q76dp7w9.js → search-x7cachq0.js} +21 -21
  45. package/dist/{sqlite-wp7wddw3.js → sqlite-adjpy399.js} +3 -1
  46. package/dist/{storage-memory-7417pbm9.js → storage-memory-axnsk5t1.js} +2 -2
  47. package/dist/{storage-tags-cvjsrbqh.js → storage-tags-6j5s2q49.js} +5 -5
  48. package/dist/subagent-entry.js +24 -24
  49. package/package.json +1 -1
  50. package/dist/embedding-backfill-809pgwvb.js +0 -20
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  log2,
16
16
  sessionLog2,
17
17
  flushLogger2
18
- } from "./index-x3dcnxh0.js";
18
+ } from "./index-gycf3sp7.js";
19
19
  import {
20
20
  COMPACTION_ENABLED_PATH,
21
21
  isDreamerRunnable,
@@ -168,6 +168,7 @@ import {
168
168
  readPiSessionMessagePage,
169
169
  findRestartModelSeedFromBranch,
170
170
  iterateEntriesToRawMessageRange,
171
+ iterateAsyncEntriesToRawMessageRange,
171
172
  convertEntriesToRawMessages,
172
173
  convertPiAssistantEntryById,
173
174
  convertEntriesToRawMessagePage,
@@ -249,7 +250,7 @@ import {
249
250
  registerTodoStateLifecycle,
250
251
  syncCtxMemoryToolEnabled,
251
252
  registerMagicContextTools
252
- } from "./index-0p9gemc6.js";
253
+ } from "./index-jsyjxgfy.js";
253
254
  import {
254
255
  canonicalModelIdentity,
255
256
  piModelRefToCanonical,
@@ -266,7 +267,7 @@ import {
266
267
  DEFAULT_HISTORIAN_TIMEOUT_MS,
267
268
  DREAM_TASK_PROMOTION_DEFAULTS,
268
269
  LIVE_RELOAD_CONFIG_PATHS
269
- } from "./index-cyrnv16r.js";
270
+ } from "./index-ta0ed2nc.js";
270
271
  import {
271
272
  FAIL_CLOSED_DOCTOR_COMMAND,
272
273
  formatFailClosedBlockingMessage,
@@ -289,7 +290,7 @@ import {
289
290
  openCurrentDatabase,
290
291
  openDatabaseAsync,
291
292
  closeDatabase
292
- } from "./index-57mvvbbh.js";
293
+ } from "./index-rggtvnbg.js";
293
294
  import {
294
295
  setHomeProjectPermission,
295
296
  ProjectIdentityError,
@@ -300,7 +301,7 @@ import {
300
301
  resolveProjectIdentityForSession,
301
302
  beginBootQuietPeriod,
302
303
  scheduleAfterBootQuiet
303
- } from "./index-kamhcj15.js";
304
+ } from "./index-gqxsds3q.js";
304
305
  import {
305
306
  detectThinkingBindingMismatch,
306
307
  detectLatestTurnThinkingMismatch,
@@ -398,6 +399,7 @@ import {
398
399
  getPendingPiCompactionMarkerState,
399
400
  setPendingPiCompactionMarkerState,
400
401
  clearPendingPiCompactionMarkerStateIf,
402
+ logPiCompactionMarkerClear,
401
403
  getSessionsWithPendingPiMarker,
402
404
  setSessionWorkMetrics,
403
405
  getSessionWorkMetrics,
@@ -438,6 +440,7 @@ import {
438
440
  opaqueThinkingCost,
439
441
  SKELETON_REAL_INPUT_MAX_BYTES,
440
442
  toolInputStringBytes,
443
+ hasRawMessageProvider2,
441
444
  setRawMessageProvider2,
442
445
  withRawMessageProvider2,
443
446
  cleanUserText2,
@@ -445,6 +448,7 @@ import {
445
448
  readRawSessionMessages2,
446
449
  getRawSessionMessageOrdinalCount2,
447
450
  visitRawSessionMessages2,
451
+ visitRawSessionMessagesAsync2,
448
452
  readRawSessionMessageRange2,
449
453
  prepareRawSessionOrdinals2,
450
454
  primeTailRawMessageCache2,
@@ -455,7 +459,7 @@ import {
455
459
  getRawSessionTagKeysThrough2,
456
460
  getLegacyProtectedTailStartOrdinal2,
457
461
  readSessionChunk2
458
- } from "./index-j7pa3t4s.js";
462
+ } from "./index-18qe4gs1.js";
459
463
  import {
460
464
  UNKNOWN_FIT_RATIO,
461
465
  hasMeasuredDecisionCalibration,
@@ -538,7 +542,7 @@ import {
538
542
  getToolTagNumberByOwner2,
539
543
  getNullOwnerToolTag2,
540
544
  adoptNullOwnerToolTag2
541
- } from "./index-fcacf11g.js";
545
+ } from "./index-54r5vs9w.js";
542
546
  import {
543
547
  textMentionsRecentCommit,
544
548
  hasMeaningfulUserText2,
@@ -550,8 +554,9 @@ import {
550
554
  estimateTokens,
551
555
  estimateFixedPromptTokens,
552
556
  normalizeText
553
- } from "./index-r56xgha8.js";
557
+ } from "./index-4pxydwvg.js";
554
558
  import {
559
+ inRootAsyncContext2,
555
560
  Database2,
556
561
  getSqliteDatabasePath2,
557
562
  withSqliteBackgroundWriter2,
@@ -562,7 +567,7 @@ import {
562
567
  isTransientSqliteError2,
563
568
  withAsyncPrivilegedWriter2,
564
569
  indexCoordinationInput2
565
- } from "./index-8vbh4tzw.js";
570
+ } from "./index-asrxgdt4.js";
566
571
  import {
567
572
  stableStringify,
568
573
  closeQuietly,
@@ -574,7 +579,7 @@ import {
574
579
  tagRevisionKey,
575
580
  tagIdentityRevisionKey,
576
581
  TAG_INSERT_CONFLICT_ROWS
577
- } from "./index-mcgehvgw.js";
582
+ } from "./index-zzn7qbps.js";
578
583
  import {
579
584
  resolveOpenCodeDbPath,
580
585
  assertOpenCodeStoreGeneration,
@@ -587,10 +592,10 @@ import"./index-399p688g.js";
587
592
  import {
588
593
  runSessionWarmupWorker,
589
594
  RAW_SUMMARY_TEXT_MAX_CHARS2
590
- } from "./index-5rc9kvrk.js";
595
+ } from "./index-4vj37yw9.js";
591
596
  import {
592
597
  piHarnessKindFromExecutable
593
- } from "./index-0j0zqb4t.js";
598
+ } from "./index-3veejgp8.js";
594
599
  import {
595
600
  BoundedSessionMap,
596
601
  LkgRecoveryFenceError,
@@ -613,13 +618,13 @@ import {
613
618
  indexSingleMessage2,
614
619
  indexMessagesAfterOrdinal2,
615
620
  sweepOrphanedOpenCodeMessageIndexes2
616
- } from "./index-5z1h0qsa.js";
621
+ } from "./index-tdhppxwd.js";
617
622
  import {
618
623
  formatSynapseLaneDescriptor
619
- } from "./index-21v1kfs7.js";
624
+ } from "./index-szh001qb.js";
620
625
  import {
621
626
  sameProjectDirectory
622
- } from "./index-wf7mwdnf.js";
627
+ } from "./index-fdxzpnbj.js";
623
628
  import {
624
629
  buildCanonicalChunkTextFromFts,
625
630
  buildCompartmentSummaryFallbackText,
@@ -629,7 +634,7 @@ import {
629
634
  replaceCompartmentChunkEmbeddings,
630
635
  deleteChunkEmbedBackoffForSession,
631
636
  chunkWindowSourceKey
632
- } from "./index-cd6bjrj4.js";
637
+ } from "./index-6e3yv1jt.js";
633
638
  import {
634
639
  recordSessionProjectIdentity,
635
640
  GIT_SWEEP_LEASE_RENEWAL_MS,
@@ -654,7 +659,7 @@ import {
654
659
  embedSessionCompartmentChunks2,
655
660
  getEmbeddingCoverageStatus2,
656
661
  getAutoEmbeddingSessionCoverage2
657
- } from "./index-fx0r5485.js";
662
+ } from "./index-qc8e13wc.js";
658
663
  import {
659
664
  V2_MEMORY_CATEGORIES
660
665
  } from "./index-135e8hvg.js";
@@ -666,7 +671,7 @@ import {
666
671
  expandWorkspaceIdentitySetWithAliases,
667
672
  sourceNameForMemory,
668
673
  computeWorkspaceEpochFingerprint
669
- } from "./index-9mn34fnx.js";
674
+ } from "./index-r8cy8hmq.js";
670
675
  import {
671
676
  configureContextDatabasePragmas2
672
677
  } from "./index-fapenpch.js";
@@ -716,7 +721,7 @@ import {
716
721
  normalizeVerificationFiles,
717
722
  indexCommitsForProject,
718
723
  embedUnembeddedCommits
719
- } from "./index-hfnjfk4d.js";
724
+ } from "./index-pdhbj18g.js";
720
725
  import {
721
726
  setEmbeddingSessionBusy2,
722
727
  isEmbeddingHostBusy2
@@ -740,18 +745,18 @@ import {
740
745
  setMemoryClassification2,
741
746
  archiveMemory2,
742
747
  getMemoryCountsByStatus2
743
- } from "./index-kxxdyjfs.js";
748
+ } from "./index-hy44m6jc.js";
744
749
  import {
745
750
  invalidateMemory2
746
751
  } from "./index-yhzhdp3m.js";
747
752
  import {
748
753
  pushNotification2
749
754
  } from "./index-q1yc6bs0.js";
750
- import"./index-gc2749rx.js";
755
+ import"./index-8yjhda46.js";
751
756
  import {
752
757
  cosineSimilarity
753
- } from "./index-p4matwj0.js";
754
- import"./index-swab4319.js";
758
+ } from "./index-v26j81w6.js";
759
+ import"./index-729kkhmf.js";
755
760
  import {
756
761
  buildHiddenAgentRegistrations,
757
762
  logTemperatureDecision,
@@ -759,7 +764,7 @@ import {
759
764
  SubagentTelemetry,
760
765
  readHostSystemPrompt
761
766
  } from "./index-zh1jw8qp.js";
762
- import"./index-290tzwp1.js";
767
+ import"./index-kh3jt7t9.js";
763
768
  import {
764
769
  __toESM
765
770
  } from "./index-22jkk6wg.js";
@@ -772,7 +777,7 @@ import {
772
777
  import {
773
778
  logSlowWriteTransaction,
774
779
  WriteTransactionSteps
775
- } from "./index-0vrkp1jq.js";
780
+ } from "./index-43zqh10z.js";
776
781
  import {
777
782
  setStoragePrivatePermissionEnforcement,
778
783
  ensureStorageDirectorySync,
@@ -2367,7 +2372,7 @@ function observeNoteNudgeServe(args) {
2367
2372
  sessions.set(args.sessionId, seen);
2368
2373
  return {
2369
2374
  isFirstServe: warm && args.isLiveTail && firstServe,
2370
- isCacheBustingPass: args.anchorMessageId !== null && args.isCacheBustingPass
2375
+ prefixRewriteCoversAnchor: args.anchorMessageId !== null && args.prefixRewriteCoversAnchor
2371
2376
  };
2372
2377
  }
2373
2378
  var NOTE_NUDGE_COOLDOWN_MS = 15 * 60 * 1000;
@@ -2406,7 +2411,7 @@ function peekNoteNudgeText(db, sessionId, currentUserMessageId, projectIdentity,
2406
2411
  const state = getPersistedNoteNudge(db, sessionId);
2407
2412
  if (!state.triggerPending)
2408
2413
  return null;
2409
- if (eligibility && !eligibility.isFirstServe && !eligibility.isCacheBustingPass) {
2414
+ if (eligibility && !eligibility.isFirstServe && !eligibility.prefixRewriteCoversAnchor) {
2410
2415
  sessionLog2(sessionId, "note-nudge: deferring — user already served or first serve unproven");
2411
2416
  return null;
2412
2417
  }
@@ -3327,7 +3332,7 @@ function createNodeHostStallProfiler(deps) {
3327
3332
  if (stopped || !existsSync5(enablePath))
3328
3333
  return;
3329
3334
  session = api;
3330
- api.connect();
3335
+ inRootAsyncContext2(() => api.connect());
3331
3336
  await post(api, "Profiler.enable");
3332
3337
  await post(api, "Profiler.setSamplingInterval", {
3333
3338
  interval: Math.round(options.sampleIntervalMs * 1000)
@@ -3377,7 +3382,6 @@ function createNodeHostStallProfiler(deps) {
3377
3382
  isUnavailable: () => unavailable
3378
3383
  };
3379
3384
  }
3380
-
3381
3385
  // ../plugin/src/shared/host-stall-profiler.ts
3382
3386
  function hostStallProfilerDir() {
3383
3387
  return path7.join(getMagicContextStorageDir(), HOST_PROFILER_DIRNAME);
@@ -3394,7 +3398,7 @@ function startHostStallProfilerSwitch() {
3394
3398
  now: () => performance.now(),
3395
3399
  wallNow: () => Date.now(),
3396
3400
  setInterval: (callback, ms) => {
3397
- const handle = setInterval(callback, ms);
3401
+ const handle = inRootAsyncContext2(() => setInterval(callback, ms));
3398
3402
  handle.unref?.();
3399
3403
  return handle;
3400
3404
  },
@@ -3421,7 +3425,7 @@ function startHostStallProfilerSwitch() {
3421
3425
  // package.json
3422
3426
  var package_default = {
3423
3427
  name: "@cortexkit/pi-magic-context",
3424
- version: "0.49.6",
3428
+ version: "0.49.8",
3425
3429
  type: "module",
3426
3430
  description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
3427
3431
  main: "dist/index.js",
@@ -9694,7 +9698,7 @@ function startLeaseHeartbeat(db, holderId, leaseKey, onLost, intervalOrAcquisiti
9694
9698
  }
9695
9699
  };
9696
9700
  beat();
9697
- const timer = lost ? undefined : setInterval(() => withSqliteBackgroundWriter2(beat), intervalMs);
9701
+ const timer = lost ? undefined : inRootAsyncContext2(() => setInterval(() => withSqliteBackgroundWriter2(beat), intervalMs));
9698
9702
  return {
9699
9703
  stop: () => {
9700
9704
  if (timer)
@@ -10320,6 +10324,25 @@ async function runHiddenSingleShotPrompt(args) {
10320
10324
  });
10321
10325
  promptSettled = true;
10322
10326
  return { validated: run.validated, completion: run.output, childSessionId: handle.id };
10327
+ } catch (error) {
10328
+ if (args.onFailureOutput) {
10329
+ let timer;
10330
+ try {
10331
+ const completion = await Promise.race([
10332
+ args.executor.collect(handle, 200),
10333
+ new Promise((resolve) => {
10334
+ timer = setTimeout(() => resolve(null), 5000);
10335
+ })
10336
+ ]);
10337
+ if (completion)
10338
+ args.onFailureOutput(completion);
10339
+ } catch (readError) {
10340
+ log2(`${args.callContext}: failed to collect interrupted output: ${String(readError)}`);
10341
+ } finally {
10342
+ clearTimeout(timer);
10343
+ }
10344
+ }
10345
+ throw error;
10323
10346
  } finally {
10324
10347
  await args.executor.close(handle, {
10325
10348
  promptSettled,
@@ -13906,31 +13929,33 @@ function snapshotMaintainDocsFiles(docsDir) {
13906
13929
  import { createHash as createHash8 } from "node:crypto";
13907
13930
 
13908
13931
  // ../plugin/src/features/magic-context/dreamer/map-memories-prompt.ts
13932
+ import { execFile as execFile2 } from "node:child_process";
13909
13933
  import { existsSync as existsSync8, statSync as statSync2 } from "node:fs";
13910
13934
  import path10 from "node:path";
13935
+ import { promisify as promisify2 } from "node:util";
13911
13936
  var MAP_MEMORIES_SYSTEM_PROMPT = `You are a memory mapper for the magic-context system. You map project memories to the repository files that back them.
13912
13937
 
13913
13938
  A memory's BACKING FILES are the file(s) whose code the memory makes a claim about — the files you would open to check whether the memory is accurate. You do NOT judge accuracy, rewrite, or remove anything. You only LOCATE backing files.
13914
13939
 
13915
- Tools (read-only): read, grep, glob. Request line ranges rather than full files; use offset/limit for longer files. Each memory may come with "Likely files" already named in it and confirmed to exist — confirm those FIRST (cheap) instead of searching. Use search/grep to FIND code only when no likely files are given. Do not guess — confirm a file exists and genuinely backs the memory before listing it. Keep reads minimal: you do not need to read a whole file to confirm it backs a one-line claim.
13940
+ Tools (read-only): read, grep, glob. Use at most 3 tool calls per memory, including searches and reads. Never read the same file twice in a batch; reuse what you already learned. Never repeat an identical search or use broad searches such as "fn " or ".*". Request one useful line range, not repeated slices. Each memory may come with "Likely files" from paths it names or a literal identifier search, confirmed to exist — confirm those FIRST (cheap) instead of searching. Use search/grep to FIND code only when no likely files are given. Do not guess — confirm a file exists and genuinely backs the memory before listing it. You are locating code, not exhaustively verifying the claim. If the call budget runs out before you can decide, leave that id unmapped and move on; uncertainty is not evidence of file-independence.
13916
13941
 
13917
13942
  For each memory decide ONE of:
13918
- - Backing files found → the COMPLETE set of repo-relative paths whose code the memory is about.
13943
+ - Backing files found → the specific repo-relative paths whose code the memory is about; do not chase every caller or related implementation.
13919
13944
  - File-independent → the memory describes EXTERNAL behavior (a provider / API / platform / protocol limit, e.g. "Anthropic returns 400 on empty content"), or a pure process / workflow / philosophy rule, with NO specific local file that backs it. A BEHAVIORAL claim (when to act, how to work, who decides, or tool-usage discipline) is file-independent even when it cites file paths or commands as examples: a named file is not the file backing the rule.
13920
13945
 
13921
- Output ONE XML manifest at the very end and NOTHING else — no narration, no per-memory commentary, no reasoning:
13946
+ Output ONE XML manifest and NOTHING else — no narration, no per-memory commentary, no reasoning. Open <mappings> before investigating. Emit each <memory> element as soon as you decide its mapping, before investigating the next memory, so an interruption cannot lose completed work. Close </mappings> when finished:
13922
13947
  <mappings>
13923
13948
  <memory id="N" files="path/a.ts,path/b.ts"/>
13924
13949
  <memory id="M" independent="true"/>
13925
13950
  </mappings>
13926
13951
 
13927
13952
  Rules:
13928
- - Every input memory id MUST appear exactly once.
13953
+ - Every decided input memory id MUST appear exactly once. Omit uncertain ids rather than inventing backing files or declaring them independent.
13929
13954
  - files: repo-relative, comma-separated, no spaces inside a path. Only files that actually exist and genuinely back the memory.
13930
13955
  - A BACKING FILE is CODE that implements or handles the claim — not a file that merely mentions it. A path named inside a process directive is an action target or example, not evidence that the file backs the directive. A markdown doc (.md), a PARITY/notes file, or a test that only DESCRIBES an external fact is NOT a backing file. If the only place a memory's fact appears is prose/docs/a test (no code implements or handles it), mark it independent="true".
13931
13956
  - Many CONSTRAINTS are HYBRID: "external system does X, and OUR code handles it here." Map those to the HANDLING code (you can verify the handling, even though you can't verify the external behavior). Only mark independent when there is NO local code that implements or handles the fact.
13932
13957
  - Prefer the most specific file(s); do not pad with tangential files. Most memories map to one file; some to a few.
13933
- - When you genuinely cannot find any local backing and it is not clearly external, still emit the memory with independent="true" (do not drop it).`;
13958
+ - Mark independent="true" only for the file-independent cases above; an investigation that runs out of calls stays unmapped.`;
13934
13959
  var MAX_SEED_PATHS_PER_MEMORY = 3;
13935
13960
  var PATH_PATTERN = "`?((?:[\\w.-]+\\/)+[\\w.-]+\\.(?:ts|tsx|js|jsx|mjs|cjs|rs|go|py|json|jsonc|sql|toml|sh))`?";
13936
13961
  function extractMemoryCandidatePaths(content, repoDir) {
@@ -13952,10 +13977,65 @@ function extractMemoryCandidatePaths(content, repoDir) {
13952
13977
  }
13953
13978
  return [...found];
13954
13979
  }
13980
+ var execFileAsync2 = promisify2(execFile2);
13981
+ async function seedIdentifierCandidates(memories, repoDir) {
13982
+ const identifiers = new Map(memories.filter((memory) => memory.candidates.length === 0).map((memory) => [
13983
+ memory.id,
13984
+ [
13985
+ ...new Set(memory.content.match(/\b(?:[A-Z][A-Za-z0-9_]{3,}|[a-z][a-z0-9]*[A-Z][A-Za-z0-9_]*|[a-z][a-z0-9]*_[A-Za-z0-9_]{2,})\b/g) ?? [])
13986
+ ].slice(0, 3)
13987
+ ]));
13988
+ const terms = [...new Set([...identifiers.values()].flat())];
13989
+ if (terms.length === 0)
13990
+ return memories;
13991
+ let stdout;
13992
+ try {
13993
+ const result = await execFileAsync2("git", [
13994
+ "-c",
13995
+ "core.quotePath=false",
13996
+ "grep",
13997
+ "--no-color",
13998
+ "-n",
13999
+ "-I",
14000
+ "-F",
14001
+ ...terms.flatMap((term) => ["-e", term]),
14002
+ "--",
14003
+ ...["ts", "tsx", "js", "jsx", "mjs", "cjs", "rs", "go", "py", "sql", "sh"].map((extension) => `*.${extension}`)
14004
+ ], {
14005
+ cwd: repoDir,
14006
+ encoding: "utf8",
14007
+ timeout: 2000,
14008
+ maxBuffer: 256 * 1024,
14009
+ windowsHide: true,
14010
+ killSignal: "SIGKILL"
14011
+ });
14012
+ stdout = result.stdout;
14013
+ } catch {
14014
+ return memories;
14015
+ }
14016
+ const matches = stdout.split(`
14017
+ `).flatMap((line) => {
14018
+ const match = line.match(/^([^:]+):\d+:(.*)$/);
14019
+ return match ? [{ file: match[1], text: match[2] }] : [];
14020
+ });
14021
+ return memories.map((memory) => {
14022
+ const names = identifiers.get(memory.id);
14023
+ if (!names?.length)
14024
+ return memory;
14025
+ const files = new Map;
14026
+ for (const match of matches) {
14027
+ const score = names.filter((name) => match.text.includes(name)).length;
14028
+ if (score)
14029
+ files.set(match.file, Math.max(files.get(match.file) ?? 0, score));
14030
+ }
14031
+ const candidates = [...files].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_SEED_PATHS_PER_MEMORY).map(([file]) => file);
14032
+ return candidates.length ? { ...memory, candidates } : memory;
14033
+ });
14034
+ }
13955
14035
  function buildMapMemoriesPrompt(projectPath, memories) {
13956
14036
  const list = memories.map((m) => {
13957
14037
  const seed = m.candidates.length ? `
13958
- Likely files (named in the memory, confirmed to exist): ${m.candidates.join(", ")}` : "";
14038
+ Likely files (candidate paths, confirmed to exist): ${m.candidates.join(", ")}` : "";
13959
14039
  return `[${m.id}] ${m.category}
13960
14040
  ${m.content}${seed}`;
13961
14041
  }).join(`
@@ -13965,13 +14045,13 @@ ${m.content}${seed}`;
13965
14045
 
13966
14046
  Project: ${projectPath}
13967
14047
 
13968
- For each memory below, find the repo file(s) it makes a claim about, or mark it file-independent. Behavioral process/workflow directives stay file-independent even when they name files. When "Likely files" are listed, those paths are named in the memory and confirmed to exist — START there only for code claims: confirm each actually backs the claim (a quick read/outline), drop any that don't, add others only if genuinely needed. Search from scratch only when no likely files are given. Then output ONE <mappings> manifest covering every id.
14048
+ For each memory below, find the repo file(s) it makes a claim about, or mark it file-independent. Behavioral process/workflow directives stay file-independent even when they name files. When "Likely files" are listed, those paths either occur in the memory or match its identifiers in a bounded literal source search — START there only for code claims: confirm each actually backs the claim (a quick read/outline), drop any that don't, add others only if genuinely needed. Search from scratch only when no likely files are given. Use at most 3 tool calls per memory, reuse prior reads, and emit each decided mapping immediately in ONE <mappings> manifest. Leave uncertain ids unmapped and move on.
13969
14049
 
13970
14050
  <memories>
13971
14051
  ${list}
13972
14052
  </memories>`;
13973
14053
  }
13974
- var MEMORY_ELEMENT_PATTERN = "<memory\\b([^>]*)(?:\\/>|>([\\s\\S]*?)<\\/memory>)";
14054
+ var MEMORY_ELEMENT_PATTERN = "<memory\\b([^<>]*)(?:\\/>|>([\\s\\S]*?)<\\/memory>)";
13975
14055
  var NESTED_FILE_PATTERN = "<file\\b([^>]*)\\/?>";
13976
14056
  function extractNestedFilePaths(inner) {
13977
14057
  const files = [];
@@ -14029,12 +14109,78 @@ function validateMapMemoriesManifest(text, expectedIds) {
14029
14109
  assertNoDuplicateManifestIds(parsed.filter((entry) => expectedIds.has(entry.id)).map((entry) => entry.id), "mappings");
14030
14110
  return parsed;
14031
14111
  }
14112
+ function parsePartialMapMemoriesManifest(text, expectedIds) {
14113
+ const start = text.indexOf("<mappings>");
14114
+ if (start < 0)
14115
+ return [];
14116
+ const entries = new Map;
14117
+ const conflicts = new Set;
14118
+ for (const match of text.slice(start).matchAll(new RegExp(MEMORY_ELEMENT_PATTERN, "gi"))) {
14119
+ try {
14120
+ const entry = parseMapMemoriesManifest(`<mappings>${match[0]}</mappings>`)[0];
14121
+ if (!entry || !expectedIds.has(entry.id) || conflicts.has(entry.id))
14122
+ continue;
14123
+ const previous = entries.get(entry.id);
14124
+ if (previous && JSON.stringify(previous) !== JSON.stringify(entry)) {
14125
+ entries.delete(entry.id);
14126
+ conflicts.add(entry.id);
14127
+ } else {
14128
+ entries.set(entry.id, entry);
14129
+ }
14130
+ } catch {}
14131
+ }
14132
+ return [...entries.values()];
14133
+ }
14032
14134
 
14033
14135
  // ../plugin/src/features/magic-context/dreamer/map-memories.ts
14034
- var MAP_BATCH_SIZE = 20;
14136
+ var MAP_BATCH_SIZE = 5;
14035
14137
  var MAP_BATCH_FLOOR_MS = 240000;
14036
14138
  var CONSECUTIVE_TIMEOUT_LIMIT2 = 2;
14037
14139
  var MAX_INDEPENDENT_REQUEUE_PER_RUN = MAP_BATCH_SIZE;
14140
+ function retryKey(project) {
14141
+ return `map-memories:retry:${project}`;
14142
+ }
14143
+ function readRetries(db, project) {
14144
+ try {
14145
+ const parsed = JSON.parse(getDreamState(db, retryKey(project)) ?? "{}");
14146
+ if (!isRecord(parsed))
14147
+ return {};
14148
+ const retries = {};
14149
+ for (const [id, value] of Object.entries(parsed)) {
14150
+ if (isRecord(value) && typeof value.content === "string" && typeof value.failures === "number" && typeof value.deferUntil === "number") {
14151
+ retries[id] = {
14152
+ content: value.content,
14153
+ failures: value.failures,
14154
+ deferUntil: value.deferUntil
14155
+ };
14156
+ }
14157
+ }
14158
+ return retries;
14159
+ } catch {
14160
+ return {};
14161
+ }
14162
+ }
14163
+ function contentKey(memory) {
14164
+ return createHash8("sha256").update(`${memory.category}
14165
+ ${memory.content}`).digest("hex");
14166
+ }
14167
+ function isDeferred(memory, retries) {
14168
+ const retry = retries[memory.id];
14169
+ return !!retry && retry.content === contentKey(memory) && retry.deferUntil > Date.now();
14170
+ }
14171
+ function assistantText(messages) {
14172
+ return messages.filter((message) => isRecord(message) && (message.role === "assistant" || isRecord(message.info) && message.info.role === "assistant")).map((message) => extractLatestAssistantText([message]) ?? (isRecord(message) && Array.isArray(message.content) ? message.content.filter(isRecord).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join(`
14173
+ `) : "")).join(`
14174
+ `);
14175
+ }
14176
+ function currentManifest(text) {
14177
+ const start = text.lastIndexOf("<mappings>");
14178
+ return start < 0 ? text : text.slice(start);
14179
+ }
14180
+ function logBatchUsage(messages, committed, startedAt) {
14181
+ const steps = messages.filter((message) => isRecord(message) && (message.role === "assistant" || isRecord(message.info) && message.info.role === "assistant")).length;
14182
+ log2(`[dreamer] map-memories batch: steps=${steps} committed=${committed} steps_per_memory=${committed ? (steps / committed).toFixed(2) : "n/a"} elapsed_ms=${Date.now() - startedAt}`);
14183
+ }
14038
14184
  function computeMapBatchSliceMs(remainingMs, batchesRemaining) {
14039
14185
  return Math.min(remainingMs, Math.max(MAP_BATCH_FLOOR_MS, Math.floor(remainingMs / batchesRemaining)));
14040
14186
  }
@@ -14056,7 +14202,7 @@ function toMapInput(memory, repoDir) {
14056
14202
  candidates: extractMemoryCandidatePaths(memory.content, repoDir)
14057
14203
  };
14058
14204
  }
14059
- function selectMapMemoryInputs(db, projectIdentity, repoDir) {
14205
+ function selectMapMemoryInputs(db, projectIdentity, repoDir, includeDeferred = false) {
14060
14206
  const active = getMemoriesByProject2(db, projectIdentity);
14061
14207
  const activeIds = active.map((m) => m.id);
14062
14208
  const unmapped = new Set(getUnmappedMemoryIds(db, activeIds));
@@ -14075,7 +14221,8 @@ function selectMapMemoryInputs(db, projectIdentity, repoDir) {
14075
14221
  if (requeue.length >= MAX_INDEPENDENT_REQUEUE_PER_RUN)
14076
14222
  break;
14077
14223
  }
14078
- return [...unmappedInputs, ...requeue];
14224
+ const retries = readRetries(db, projectIdentity);
14225
+ return [...unmappedInputs, ...requeue].filter((memory) => includeDeferred || !isDeferred(memory, retries));
14079
14226
  }
14080
14227
  async function mapMemories(args) {
14081
14228
  const result = {
@@ -14085,14 +14232,21 @@ async function mapMemories(args) {
14085
14232
  remaining: 0,
14086
14233
  complete: true
14087
14234
  };
14088
- const inputs = selectMapMemoryInputs(args.db, args.projectIdentity, args.sessionDirectory);
14235
+ const allInputs = selectMapMemoryInputs(args.db, args.projectIdentity, args.sessionDirectory, true);
14236
+ const retries = readRetries(args.db, args.projectIdentity);
14237
+ const activeIds = new Set(allInputs.map((memory) => String(memory.id)));
14238
+ for (const id of Object.keys(retries))
14239
+ if (!activeIds.has(id))
14240
+ delete retries[id];
14241
+ const inputs = allInputs.filter((memory) => !isDeferred(memory, retries));
14242
+ result.remaining = allInputs.length;
14243
+ result.complete = result.remaining === 0;
14089
14244
  if (inputs.length === 0)
14090
14245
  return result;
14091
14246
  const batches = [];
14092
14247
  for (let i = 0;i < inputs.length; i += MAP_BATCH_SIZE) {
14093
14248
  batches.push({ inputs: inputs.slice(i, i + MAP_BATCH_SIZE), isOmissionRetry: false });
14094
14249
  }
14095
- result.remaining = inputs.length;
14096
14250
  const abortController = new AbortController;
14097
14251
  const heartbeat = startLeaseHeartbeat(args.db, args.holderId, args.leaseKey, (reason) => abortController.abort(new Error(reason)), args.leaseAcquisition);
14098
14252
  try {
@@ -14115,6 +14269,24 @@ async function mapMemories(args) {
14115
14269
  break;
14116
14270
  const outcome = await mapOneBatch(args, batch.inputs, sliceMs, abortController.signal);
14117
14271
  const committed = outcome.mapped + outcome.independent;
14272
+ runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
14273
+ const committedIds = new Set(outcome.committedIds);
14274
+ for (const memory of batch.inputs) {
14275
+ if (committedIds.has(memory.id)) {
14276
+ delete retries[memory.id];
14277
+ continue;
14278
+ }
14279
+ const content = contentKey(memory);
14280
+ const prior = retries[memory.id];
14281
+ const failures = (prior?.content === content ? prior.failures : 0) + 1;
14282
+ retries[memory.id] = {
14283
+ content,
14284
+ failures,
14285
+ deferUntil: failures < 2 ? 0 : Date.now() + Math.min(24, 2 ** (failures - 2)) * 3600000
14286
+ };
14287
+ }
14288
+ setDreamState(args.db, retryKey(args.projectIdentity), JSON.stringify(retries));
14289
+ });
14118
14290
  result.mapped += outcome.mapped;
14119
14291
  result.independent += outcome.independent;
14120
14292
  result.remaining -= committed;
@@ -14124,8 +14296,8 @@ async function mapMemories(args) {
14124
14296
  }
14125
14297
  if (outcome.requeue?.length) {
14126
14298
  if (!batch.isOmissionRetry) {
14127
- batches.splice(i + 1, 0, {
14128
- inputs: outcome.requeue,
14299
+ batches.push({
14300
+ inputs: outcome.requeue.filter((memory) => !isDeferred(memory, retries)),
14129
14301
  isOmissionRetry: true
14130
14302
  });
14131
14303
  log2(`[dreamer] map-memories: committed ${committed}/${batch.inputs.length} mapping(s); requeueing ${outcome.requeue.length} omitted id(s) in a targeted retry`);
@@ -14133,12 +14305,12 @@ async function mapMemories(args) {
14133
14305
  log2(`[dreamer] map-memories: targeted retry still omitted ${outcome.requeue.length} id(s); leaving them unmapped for the next run`);
14134
14306
  }
14135
14307
  }
14136
- if (outcome.failure?.class === "timeout") {
14308
+ if (outcome.failure?.class === "timeout" && committed === 0) {
14137
14309
  consecutiveTimeouts += 1;
14138
14310
  timeoutStreakElapsedMs.push(outcome.failure.elapsedMs);
14139
14311
  if (consecutiveTimeouts >= CONSECUTIVE_TIMEOUT_LIMIT2) {
14140
14312
  result.stopReason = "timeout-circuit-breaker";
14141
- log2(`[dreamer] map-memories starvation: circuit breaker tripped after ${consecutiveTimeouts} consecutive batch timeouts (model too slow for its time slice); per-batch elapsed [${timeoutStreakElapsedMs.join("ms, ")}ms] vs ${sliceMs}ms slice; stopping with ${result.remaining} mapping(s) remaining`);
14313
+ 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`);
14142
14314
  break;
14143
14315
  }
14144
14316
  } else {
@@ -14158,12 +14330,13 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
14158
14330
  let promptSettled = false;
14159
14331
  const startedAt = Date.now();
14160
14332
  let budgetFinalized = false;
14333
+ let failedCompletion;
14161
14334
  const onBudgetUpdate = (state) => {
14162
14335
  budgetFinalized ||= state.finalizeFired;
14163
14336
  args.onBudgetUpdate?.(state);
14164
14337
  };
14165
14338
  try {
14166
- const prompt = buildMapMemoriesPrompt(args.projectIdentity, batch);
14339
+ const prompt = buildMapMemoriesPrompt(args.projectIdentity, await seedIdentifierCandidates(batch, args.sessionDirectory));
14167
14340
  if (args.hiddenCompletionExecutor) {
14168
14341
  const run = await runHiddenSingleShotPrompt({
14169
14342
  executor: args.hiddenCompletionExecutor,
@@ -14179,14 +14352,18 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
14179
14352
  fallbackModels: args.fallbackModels,
14180
14353
  timeoutMs: sliceMs,
14181
14354
  signal,
14182
- parse: (text) => validateMapMemoriesManifest(text, new Set(batch.map((memory) => memory.id)))
14355
+ onFailureOutput: (completion) => {
14356
+ failedCompletion = completion;
14357
+ },
14358
+ parse: (text, completion) => validateMapMemoriesManifest(currentManifest(assistantText(completion.messages ?? []) || text), new Set(batch.map((memory) => memory.id)))
14183
14359
  });
14184
14360
  recordInvocation2(args, startedAt, {
14185
14361
  status: "completed",
14186
14362
  messages: run.completion.messages ?? []
14187
14363
  });
14188
- const outcome = await applyParsedBatchMappings(args, batch, run.validated, budgetFinalized);
14189
- const returnedIds = new Set(run.validated.map((entry) => entry.id));
14364
+ const outcome = await applyParsedBatchMappings(args, batch, run.validated, budgetFinalized, true);
14365
+ logBatchUsage(run.completion.messages ?? [], outcome.mapped + outcome.independent, startedAt);
14366
+ const returnedIds = new Set(outcome.committedIds);
14190
14367
  return {
14191
14368
  ...outcome,
14192
14369
  requeue: budgetFinalized ? [] : batch.filter((memory) => !returnedIds.has(memory.id))
@@ -14229,7 +14406,7 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
14229
14406
  fetchOutput: async () => {
14230
14407
  const messagesResponse = await client.session.messages({
14231
14408
  path: { id: agentSessionId },
14232
- query: { directory: args.sessionDirectory, limit: 100 }
14409
+ query: { directory: args.sessionDirectory, limit: 200 }
14233
14410
  });
14234
14411
  return normalizeSDKResponse(messagesResponse, [], {
14235
14412
  preferResponseOnMissingData: true
@@ -14239,7 +14416,7 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
14239
14416
  if (hasLengthCappedOutput(messages)) {
14240
14417
  throw new Error("map-memories returned length-capped output");
14241
14418
  }
14242
- const text = extractLatestAssistantText(messages);
14419
+ const text = currentManifest(assistantText(messages));
14243
14420
  if (!text)
14244
14421
  throw new Error("map-memories returned no output");
14245
14422
  return validateMapMemoriesManifest(text, new Set(batch.map((memory) => memory.id)));
@@ -14247,8 +14424,9 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
14247
14424
  });
14248
14425
  promptSettled = true;
14249
14426
  recordInvocation2(args, startedAt, { status: "completed", messages: run.output });
14250
- const outcome = await applyParsedBatchMappings(args, batch, run.validated, budgetFinalized);
14251
- const returnedIds = new Set(run.validated.filter((entry) => batch.some((memory) => memory.id === entry.id)).map((entry) => entry.id));
14427
+ const outcome = await applyParsedBatchMappings(args, batch, run.validated, budgetFinalized, true);
14428
+ logBatchUsage(run.output, outcome.mapped + outcome.independent, startedAt);
14429
+ const returnedIds = new Set(outcome.committedIds);
14252
14430
  return {
14253
14431
  ...outcome,
14254
14432
  requeue: budgetFinalized ? [] : batch.filter((memory) => !returnedIds.has(memory.id))
@@ -14256,18 +14434,23 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
14256
14434
  } catch (error) {
14257
14435
  const desc = describeError(error);
14258
14436
  log2(`[dreamer] map-memories batch failed: ${desc.brief}`, desc.stackHead ? { stackHead: desc.stackHead } : undefined);
14437
+ const messages = failedCompletion?.messages ?? await readFailedChildMessages(args.client, agentSessionId, args.sessionDirectory) ?? [];
14259
14438
  recordInvocation2(args, startedAt, {
14260
14439
  status: failedInvocationStatus(error),
14261
14440
  error,
14262
- messages: await readFailedChildMessages(args.client, agentSessionId, args.sessionDirectory)
14441
+ messages
14263
14442
  });
14264
- if (error instanceof DreamerModuleFailureError || error instanceof DreamTokenBudgetExceeded)
14443
+ if (error instanceof DreamerModuleFailureError)
14265
14444
  throw error;
14266
14445
  if (signal.aborted)
14267
14446
  throw error;
14447
+ const recovered = parsePartialMapMemoriesManifest(assistantText(messages) || failedCompletion?.text || "", new Set(batch.map((memory) => memory.id)));
14448
+ const outcome = await applyParsedBatchMappings(args, batch, recovered, budgetFinalized, true);
14449
+ logBatchUsage(messages, outcome.mapped + outcome.independent, startedAt);
14450
+ if (error instanceof DreamTokenBudgetExceeded)
14451
+ throw error;
14268
14452
  return {
14269
- mapped: 0,
14270
- independent: 0,
14453
+ ...outcome,
14271
14454
  failure: {
14272
14455
  class: isTimeoutClassError2(error) ? "timeout" : "other",
14273
14456
  elapsedMs: Date.now() - startedAt
@@ -14286,7 +14469,7 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
14286
14469
  });
14287
14470
  }
14288
14471
  }
14289
- async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = false) {
14472
+ async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = false, allowPartial = false) {
14290
14473
  const batchIds = new Set(batch.map((memory) => memory.id));
14291
14474
  const valid = parsed.filter((entry) => batchIds.has(entry.id));
14292
14475
  const unknown = parsed.filter((entry) => !batchIds.has(entry.id));
@@ -14294,14 +14477,14 @@ async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = f
14294
14477
  log2(`[dreamer] map-memories warning: dropping ${unknown.length} unknown mapping entr${unknown.length === 1 ? "y" : "ies"} outside the current batch (${unknown.map((entry) => entry.id).join(", ")})`);
14295
14478
  }
14296
14479
  assertNoDuplicateManifestIds(valid.map((entry) => entry.id), "mappings");
14297
- if (!budgetFinalized && valid.length * 2 < batch.length) {
14480
+ if (!allowPartial && !budgetFinalized && valid.length * 2 < batch.length) {
14298
14481
  throw new Error(`mappings manifest covers ${valid.length}/${batch.length} batch ids after filtering unknown entries; rejecting mostly-wrong manifest`);
14299
14482
  }
14300
14483
  if (budgetFinalized && valid.length < batch.length) {
14301
14484
  log2(`[dreamer] map-memories: accepted partial manifest after token budget: ${valid.length}/${batch.length}`);
14302
14485
  }
14303
14486
  if (valid.length === 0)
14304
- return { mapped: 0, independent: 0 };
14487
+ return { mapped: 0, independent: 0, committedIds: [] };
14305
14488
  const planned = [];
14306
14489
  const batchById = new Map(batch.map((memory) => [memory.id, memory]));
14307
14490
  for (const p of valid) {
@@ -14350,9 +14533,10 @@ async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = f
14350
14533
  });
14351
14534
  }
14352
14535
  if (planned.length === 0)
14353
- return { mapped: 0, independent: 0 };
14536
+ return { mapped: 0, independent: 0, committedIds: [] };
14354
14537
  let mapped = 0;
14355
14538
  let independent = 0;
14539
+ const committedIds = [];
14356
14540
  if (args.moduleRoute) {
14357
14541
  const identities = getModuleMemoryIdentities(args.db, args.projectIdentity, planned.map((item) => item.id));
14358
14542
  const rows = planned.map((item) => {
@@ -14390,19 +14574,22 @@ async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = f
14390
14574
  const accepted = new Set(result.accepted.filter((id) => typeof id === "number"));
14391
14575
  for (const item of planned) {
14392
14576
  const identity = identities.get(item.id);
14393
- if (identity && accepted.has(identity.moduleId))
14577
+ if (identity && accepted.has(identity.moduleId)) {
14394
14578
  item.independent ? independent += 1 : mapped += 1;
14579
+ committedIds.push(item.id);
14580
+ }
14395
14581
  }
14396
- return { mapped, independent };
14582
+ return { mapped, independent, committedIds };
14397
14583
  }
14398
14584
  const now = Date.now();
14399
14585
  runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
14400
14586
  for (const item of planned) {
14401
14587
  recordMemoryMapping(args.db, item.id, item.files, now, item.mappingOrigin);
14402
14588
  item.independent ? independent += 1 : mapped += 1;
14589
+ committedIds.push(item.id);
14403
14590
  }
14404
14591
  });
14405
- return { mapped, independent };
14592
+ return { mapped, independent, committedIds };
14406
14593
  }
14407
14594
  function recordInvocation2(args, startedAt, params) {
14408
14595
  if (!args.parentSessionId)
@@ -14670,32 +14857,41 @@ async function promotePrimers(args) {
14670
14857
  // ../plugin/src/features/magic-context/dreamer/primer-seed.ts
14671
14858
  var PRIMER_SEED_CAP_TOKENS = 4000;
14672
14859
  function renderUserAndToolOrientation(sessionId, startOrdinal, endOrdinal, capTokens) {
14860
+ const renderer = orientationRenderer(capTokens);
14861
+ visitRawSessionMessages2(sessionId, startOrdinal, endOrdinal, renderer.visit, {
14862
+ summary: true
14863
+ });
14864
+ return renderer.result();
14865
+ }
14866
+ function orientationRenderer(capTokens) {
14673
14867
  const lines = [];
14674
14868
  let tokens = 0;
14675
14869
  let sawMessage = false;
14676
- visitRawSessionMessages2(sessionId, startOrdinal, endOrdinal, (msg) => {
14677
- sawMessage = true;
14678
- const out = [];
14679
- if (msg.role === "user" && hasMeaningfulUserText2(msg.parts)) {
14680
- const text = extractTexts2(msg.parts).map((t) => cleanUserText2(t)).map(normalizeText).filter((t) => t.length > 0).join(" / ");
14681
- if (text)
14682
- out.push(`U: ${text}`);
14683
- }
14684
- for (const tc of extractToolCallSummaries(msg.parts))
14685
- out.push(tc);
14686
- for (const line of out) {
14687
- const lineTokens = estimateTokens(line);
14688
- if (tokens + lineTokens > capTokens && lines.length > 0) {
14689
- lines.push("… (orientation truncated; investigate the current source directly)");
14690
- return false;
14870
+ return {
14871
+ visit: (msg) => {
14872
+ sawMessage = true;
14873
+ const out = [];
14874
+ if (msg.role === "user" && hasMeaningfulUserText2(msg.parts)) {
14875
+ const text = extractTexts2(msg.parts).map((t) => cleanUserText2(t)).map(normalizeText).filter((t) => t.length > 0).join(" / ");
14876
+ if (text)
14877
+ out.push(`U: ${text}`);
14878
+ }
14879
+ for (const tc of extractToolCallSummaries(msg.parts))
14880
+ out.push(tc);
14881
+ for (const line of out) {
14882
+ const lineTokens = estimateTokens(line);
14883
+ if (tokens + lineTokens > capTokens && lines.length > 0) {
14884
+ lines.push("… (orientation truncated; investigate the current source directly)");
14885
+ return false;
14886
+ }
14887
+ lines.push(line);
14888
+ tokens += lineTokens;
14691
14889
  }
14692
- lines.push(line);
14693
- tokens += lineTokens;
14694
- }
14695
- return true;
14696
- }, { summary: true });
14697
- return sawMessage ? lines.join(`
14698
- `) : null;
14890
+ return true;
14891
+ },
14892
+ result: () => sawMessage ? lines.join(`
14893
+ `) : null
14894
+ };
14699
14895
  }
14700
14896
  function selectPrimerOriginCandidate(db, primer) {
14701
14897
  return getPrimerCandidatesByIds(db, primer.sourceCandidateIds).filter((candidate) => candidate.projectPath === primer.projectPath).sort((a, b) => b.sourceMessageTime - a.sourceMessageTime || b.id - a.id)[0];
@@ -14729,16 +14925,37 @@ function closedBookOriginP1(db, sessionId, originStartMessage) {
14729
14925
  return { orientation, sessionId };
14730
14926
  }
14731
14927
  function buildPrimerSeed(db, primer) {
14928
+ return buildPrimerSeedUsing(db, selectPrimerOriginCandidate(db, primer), renderUserAndToolOrientation);
14929
+ }
14930
+ async function buildPrimerSeedAsync(db, primer) {
14732
14931
  const mostRecent = selectPrimerOriginCandidate(db, primer);
14932
+ let orientation = null;
14933
+ if (mostRecent && typeof mostRecent.sourceCompartmentStart === "number" && typeof mostRecent.sourceCompartmentEnd === "number") {
14934
+ try {
14935
+ const renderer = orientationRenderer(PRIMER_SEED_CAP_TOKENS);
14936
+ await visitRawSessionMessagesAsync2(mostRecent.sessionId, mostRecent.sourceCompartmentStart, mostRecent.sourceCompartmentEnd, renderer.visit, { summary: true });
14937
+ orientation = renderer.result();
14938
+ } catch {
14939
+ orientation = null;
14940
+ }
14941
+ }
14942
+ return buildPrimerSeedUsing(db, mostRecent, () => orientation);
14943
+ }
14944
+ function buildPrimerSeedUsing(db, mostRecent, render) {
14733
14945
  if (!mostRecent || typeof mostRecent.sourceCompartmentStart !== "number" || typeof mostRecent.sourceCompartmentEnd !== "number") {
14734
- return { kind: "closed-book", orientation: "", prePost: "", sessionId: null };
14946
+ return {
14947
+ kind: "closed-book",
14948
+ orientation: "",
14949
+ prePost: "",
14950
+ sessionId: null
14951
+ };
14735
14952
  }
14736
14953
  const sessionId = mostRecent.sessionId;
14737
14954
  const start = mostRecent.sourceCompartmentStart;
14738
14955
  const end = mostRecent.sourceCompartmentEnd;
14739
14956
  let orientation = null;
14740
14957
  try {
14741
- orientation = renderUserAndToolOrientation(sessionId, start, end, PRIMER_SEED_CAP_TOKENS);
14958
+ orientation = render(sessionId, start, end, PRIMER_SEED_CAP_TOKENS);
14742
14959
  } catch {
14743
14960
  orientation = null;
14744
14961
  }
@@ -14869,14 +15086,14 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
14869
15086
  provider = null;
14870
15087
  }
14871
15088
  }
14872
- const seed = withRawSessionMessageCache2(() => {
15089
+ const seed = await (async () => {
14873
15090
  const unregister = provider && originSessionId ? setRawMessageProvider2(originSessionId, provider) : null;
14874
15091
  try {
14875
- return buildPrimerSeed(args.db, primer);
15092
+ return provider?.iterateMessageRangeAsync ? await buildPrimerSeedAsync(args.db, primer) : withRawSessionMessageCache2(() => buildPrimerSeed(args.db, primer));
14876
15093
  } finally {
14877
15094
  unregister?.();
14878
15095
  }
14879
- });
15096
+ })();
14880
15097
  let agentSessionId = null;
14881
15098
  let promptSettled = false;
14882
15099
  const startedAt = Date.now();
@@ -14892,7 +15109,10 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
14892
15109
  prompt,
14893
15110
  title: "magic-context-dream-refresh-primers",
14894
15111
  callContext: "dreamer:refresh-primers",
14895
- metadata: { tokenBudget: args.tokenBudget, onBudgetUpdate: args.onBudgetUpdate },
15112
+ metadata: {
15113
+ tokenBudget: args.tokenBudget,
15114
+ onBudgetUpdate: args.onBudgetUpdate
15115
+ },
14896
15116
  model: args.model,
14897
15117
  fallbackModels: args.fallbackModels,
14898
15118
  language: args.language,
@@ -14958,7 +15178,10 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
14958
15178
  validateOutput: (messages) => parseAnswer(messages, primer.answer)
14959
15179
  });
14960
15180
  promptSettled = true;
14961
- recordInvocation3(args, startedAt, { status: "completed", messages: run.output });
15181
+ recordInvocation3(args, startedAt, {
15182
+ status: "completed",
15183
+ messages: run.output
15184
+ });
14962
15185
  const answer = run.validated.trim();
14963
15186
  if (!answer)
14964
15187
  return false;
@@ -14973,7 +15196,10 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
14973
15196
  } catch (error) {
14974
15197
  const desc = describeError(error);
14975
15198
  log2(`[dreamer] refresh-primers failed (primer #${primer.id}): ${desc.brief}`, desc.stackHead ? { stackHead: desc.stackHead } : undefined);
14976
- recordInvocation3(args, startedAt, { status: failedInvocationStatus(error), error });
15199
+ recordInvocation3(args, startedAt, {
15200
+ status: failedInvocationStatus(error),
15201
+ error
15202
+ });
14977
15203
  throw error;
14978
15204
  } finally {
14979
15205
  if (agentSessionId && args.client)
@@ -15587,9 +15813,9 @@ function insertDreamRun(db, run) {
15587
15813
  import { createHash as createHash9 } from "node:crypto";
15588
15814
 
15589
15815
  // ../plugin/src/features/magic-context/dreamer/verify-diff.ts
15590
- import { execFile as execFile2 } from "node:child_process";
15591
- import { promisify as promisify2 } from "node:util";
15592
- var exec = promisify2(execFile2);
15816
+ import { execFile as execFile3 } from "node:child_process";
15817
+ import { promisify as promisify3 } from "node:util";
15818
+ var exec = promisify3(execFile3);
15593
15819
  var VERIFY_DIFF_BUDGET_TOKENS = 12000;
15594
15820
  var MAX_DIFF_BYTES = 4 * 1024 * 1024;
15595
15821
  async function git2(root, args) {
@@ -18401,10 +18627,10 @@ async function sourceReady(source, sessionId) {
18401
18627
  function defer(fn) {
18402
18628
  const immediate = globalThis.setImmediate;
18403
18629
  if (typeof immediate === "function") {
18404
- immediate(fn);
18630
+ inRootAsyncContext2(() => immediate(fn));
18405
18631
  return;
18406
18632
  }
18407
- setTimeout(fn, 0);
18633
+ inRootAsyncContext2(() => setTimeout(fn, 0));
18408
18634
  }
18409
18635
  function yieldToEventLoop() {
18410
18636
  return new Promise((resolve) => defer(resolve));
@@ -18481,14 +18707,14 @@ function scheduleReconciliation(db, sessionId, readMessages) {
18481
18707
  reconcileSessionIndex(db, sessionId, readMessages).then((ran) => {
18482
18708
  if (ran)
18483
18709
  return;
18484
- setTimeout(() => {
18710
+ inRootAsyncContext2(() => setTimeout(() => {
18485
18711
  heapHolder.reconciliationScheduledSessions.delete(sessionId);
18486
18712
  scheduleReconciliation(db, sessionId, readMessages);
18487
- }, PREPARE_RETRY_MS).unref?.();
18713
+ }, PREPARE_RETRY_MS)).unref?.();
18488
18714
  }).catch((error) => {
18489
18715
  logIndexingError(sessionId, "reconciliation", error);
18490
18716
  if (isDatabaseLockedError(error))
18491
- setTimeout(() => scheduleReconciliation(db, sessionId, readMessages), 1000).unref();
18717
+ inRootAsyncContext2(() => setTimeout(() => scheduleReconciliation(db, sessionId, readMessages), 1000)).unref();
18492
18718
  }).finally(() => {
18493
18719
  heapHolder.reconciliationScheduledSessions.delete(sessionId);
18494
18720
  });
@@ -18503,7 +18729,7 @@ function scheduleIncrementalIndex(db, sessionId, messageId, messageSource) {
18503
18729
  const activeTimer = heapHolder.incrementalTimers.get(schedulingKey);
18504
18730
  if (activeTimer)
18505
18731
  clearTimeout(activeTimer);
18506
- const timer = setTimeout(() => {
18732
+ const timer = inRootAsyncContext2(() => setTimeout(() => {
18507
18733
  heapHolder.incrementalTimers.delete(schedulingKey);
18508
18734
  heapHolder.pendingIncrementalKeys.add(schedulingKey);
18509
18735
  const indexMessage = async () => {
@@ -18547,7 +18773,7 @@ function scheduleIncrementalIndex(db, sessionId, messageId, messageSource) {
18547
18773
  if ((sessionEpochs.get(sessionId) ?? 0) === epoch)
18548
18774
  heapHolder.pendingIncrementalKeys.delete(schedulingKey);
18549
18775
  });
18550
- }, INCREMENTAL_DEBOUNCE_MS);
18776
+ }, INCREMENTAL_DEBOUNCE_MS));
18551
18777
  heapHolder.incrementalTimers.set(schedulingKey, timer);
18552
18778
  }
18553
18779
  async function indexSingleMessageOffThread(db, sessionId, message) {
@@ -18721,7 +18947,7 @@ async function startDreamScheduleTimer(args) {
18721
18947
  startupTickTimer = null;
18722
18948
  runTick("startup");
18723
18949
  });
18724
- const timer = setInterval(() => runTick("interval"), DREAM_TIMER_INTERVAL_MS);
18950
+ const timer = inRootAsyncContext2(() => setInterval(() => runTick("interval"), DREAM_TIMER_INTERVAL_MS));
18725
18951
  if (typeof timer === "object" && "unref" in timer) {
18726
18952
  timer.unref();
18727
18953
  }
@@ -19017,7 +19243,7 @@ async function runCompiledSmartNoteSweep(reg, db) {
19017
19243
  }
19018
19244
  }
19019
19245
  function startGitSweepLeaseRenewal(db, projectIdentity, holderId) {
19020
- const timer = setInterval(() => {
19246
+ const timer = inRootAsyncContext2(() => setInterval(() => {
19021
19247
  try {
19022
19248
  if (!renewGitSweepLease(db, projectIdentity, holderId)) {
19023
19249
  log2(`[git-commits] sweep lease renewal failed for ${projectIdentity}`);
@@ -19025,7 +19251,7 @@ function startGitSweepLeaseRenewal(db, projectIdentity, holderId) {
19025
19251
  } catch (error) {
19026
19252
  log2(`[git-commits] sweep lease renewal errored for ${projectIdentity}: ${error instanceof Error ? error.message : String(error)}`);
19027
19253
  }
19028
- }, GIT_SWEEP_LEASE_RENEWAL_MS);
19254
+ }, GIT_SWEEP_LEASE_RENEWAL_MS));
19029
19255
  timer.unref?.();
19030
19256
  return () => clearInterval(timer);
19031
19257
  }
@@ -19906,11 +20132,19 @@ class PiSubagentRunner {
19906
20132
  });
19907
20133
  return new Promise((resolve) => {
19908
20134
  let accountingMessages = [];
20135
+ let mappingMessages = [];
20136
+ const isMemoryMapper = options.agent.replace(/^magic-context-/, "") === "dreamer-memory-mapper";
19909
20137
  let settled = false;
19910
20138
  const settle = (result) => {
19911
20139
  if (settled)
19912
20140
  return;
19913
20141
  settled = true;
20142
+ if (isMemoryMapper) {
20143
+ result = {
20144
+ ...result,
20145
+ meta: { ...result.meta, mappingMessages: [...mappingMessages] }
20146
+ };
20147
+ }
19914
20148
  if (accountingMessages.length > 0 && /historian|dreamer|classify/i.test(options.agent)) {
19915
20149
  const last = [...accountingMessages].reverse().find((message) => message !== null && typeof message === "object" && message.role === "assistant");
19916
20150
  const responseChars = result.ok ? result.assistantText.length : last?.content?.filter((part) => typeof part === "object" && part !== null && part.type === "text" && typeof part.text === "string").reduce((sum, part) => sum + part.text.length, 0) ?? 0;
@@ -19943,7 +20177,7 @@ class PiSubagentRunner {
19943
20177
  const parentEnv = { ...process.env };
19944
20178
  delete parentEnv.MAGIC_CONTEXT_HISTORIAN_TEMPERATURE;
19945
20179
  try {
19946
- child = this.spawnImpl(this.invocation.command, [...this.invocation.prefixArgs, ...this.extraArgs, ...args], {
20180
+ child = inRootAsyncContext2(() => this.spawnImpl(this.invocation.command, [...this.invocation.prefixArgs, ...this.extraArgs, ...args], {
19947
20181
  windowsHide: true,
19948
20182
  cwd: options.cwd,
19949
20183
  env: {
@@ -19964,7 +20198,7 @@ class PiSubagentRunner {
19964
20198
  "pipe",
19965
20199
  "pipe"
19966
20200
  ]
19967
- });
20201
+ }));
19968
20202
  } catch (error) {
19969
20203
  cleanupSystemPromptFile();
19970
20204
  settle({
@@ -20103,6 +20337,12 @@ class PiSubagentRunner {
20103
20337
  if (typeof event !== "object" || event === null)
20104
20338
  return;
20105
20339
  const e = event;
20340
+ if (isMemoryMapper) {
20341
+ if (e.type === "message_end" && e.message)
20342
+ mappingMessages.push(e.message);
20343
+ if (e.type === "agent_end" && Array.isArray(e.messages))
20344
+ mappingMessages = e.messages;
20345
+ }
20106
20346
  if (rpcBudget && e.type === "response" && event.success === false) {
20107
20347
  const reply = event;
20108
20348
  if (reply.command === "prompt" || reply.command === "steer" && !sawAgentEnd) {
@@ -20722,7 +20962,9 @@ import { join as join13 } from "node:path";
20722
20962
 
20723
20963
  // src/dreamer/bounded-session-reader.ts
20724
20964
  import { closeSync, opendirSync, openSync, readSync } from "node:fs";
20965
+ import { open as open4, opendir } from "node:fs/promises";
20725
20966
  import { join as join11 } from "node:path";
20967
+ import { setImmediate as yieldToHost } from "node:timers/promises";
20726
20968
  var MAX_ENTRY_BYTES = 1024 * 1024;
20727
20969
  var OVERSIZED_HEAD_BYTES = 64 * 1024;
20728
20970
  var OVERSIZED_TAIL_BYTES = 4 * 1024;
@@ -20793,86 +21035,107 @@ function lastMatch(text, pattern) {
20793
21035
  last = match[1];
20794
21036
  return last;
20795
21037
  }
21038
+
21039
+ class LineDecoder {
21040
+ pending = Buffer.alloc(0);
21041
+ oversized = null;
21042
+ *append(buffer) {
21043
+ const bytes = buffer.length;
21044
+ let start = 0;
21045
+ for (let i = 0;i < bytes; i++) {
21046
+ if (buffer[i] !== 10)
21047
+ continue;
21048
+ const piece = buffer.subarray(start, i);
21049
+ if (this.oversized) {
21050
+ yield this.oversized.append(piece).stub();
21051
+ this.oversized = null;
21052
+ } else if (this.pending.length + piece.length > MAX_ENTRY_BYTES) {
21053
+ yield new OversizedLine().append(this.pending).append(piece).stub();
21054
+ } else {
21055
+ yield Buffer.concat([this.pending, piece]).toString("utf8");
21056
+ }
21057
+ this.pending = Buffer.alloc(0);
21058
+ start = i + 1;
21059
+ }
21060
+ const rest = buffer.subarray(start, bytes);
21061
+ if (this.oversized) {
21062
+ this.oversized.append(rest);
21063
+ } else if (this.pending.length + rest.length > MAX_ENTRY_BYTES) {
21064
+ this.oversized = new OversizedLine().append(this.pending).append(rest);
21065
+ this.pending = Buffer.alloc(0);
21066
+ } else {
21067
+ this.pending = Buffer.concat([this.pending, rest]);
21068
+ }
21069
+ }
21070
+ *finish() {
21071
+ if (this.oversized)
21072
+ yield this.oversized.stub();
21073
+ else if (this.pending.length)
21074
+ yield this.pending.toString("utf8");
21075
+ }
21076
+ }
20796
21077
  function* lines(path) {
20797
21078
  const fd = openSync(path, "r");
20798
21079
  const buffer = Buffer.alloc(64 * 1024);
20799
- let pending = Buffer.alloc(0);
20800
- let oversized = null;
21080
+ const decoder = new LineDecoder;
20801
21081
  try {
20802
21082
  while (true) {
20803
21083
  const bytes = readSync(fd, buffer, 0, buffer.length, null);
20804
21084
  if (bytes === 0)
20805
21085
  break;
20806
- let start = 0;
20807
- for (let i = 0;i < bytes; i++) {
20808
- if (buffer[i] !== 10)
20809
- continue;
20810
- const piece = buffer.subarray(start, i);
20811
- if (oversized) {
20812
- yield oversized.append(piece).stub();
20813
- oversized = null;
20814
- } else if (pending.length + piece.length > MAX_ENTRY_BYTES) {
20815
- yield new OversizedLine().append(pending).append(piece).stub();
20816
- } else {
20817
- yield Buffer.concat([pending, piece]).toString("utf8");
20818
- }
20819
- pending = Buffer.alloc(0);
20820
- start = i + 1;
20821
- }
20822
- const rest = buffer.subarray(start, bytes);
20823
- if (oversized) {
20824
- oversized.append(rest);
20825
- } else if (pending.length + rest.length > MAX_ENTRY_BYTES) {
20826
- oversized = new OversizedLine().append(pending).append(rest);
20827
- pending = Buffer.alloc(0);
20828
- } else {
20829
- pending = Buffer.concat([pending, rest]);
20830
- }
21086
+ yield* decoder.append(buffer.subarray(0, bytes));
20831
21087
  }
20832
- if (oversized)
20833
- yield oversized.stub();
20834
- else if (pending.length)
20835
- yield pending.toString("utf8");
21088
+ yield* decoder.finish();
20836
21089
  } finally {
20837
21090
  closeSync(fd);
20838
21091
  }
20839
21092
  }
21093
+ async function* asyncLines(path) {
21094
+ const file = await open4(path, "r");
21095
+ const buffer = Buffer.alloc(64 * 1024);
21096
+ const decoder = new LineDecoder;
21097
+ try {
21098
+ while (true) {
21099
+ const { bytesRead } = await file.read(buffer, 0, buffer.length, null);
21100
+ if (bytesRead === 0)
21101
+ break;
21102
+ yield* decoder.append(buffer.subarray(0, bytesRead));
21103
+ await yieldToHost();
21104
+ }
21105
+ yield* decoder.finish();
21106
+ } finally {
21107
+ await file.close();
21108
+ }
21109
+ }
20840
21110
  function record(value) {
20841
21111
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
20842
21112
  }
20843
- function* sessionFiles(directory, nested) {
21113
+ async function* asyncSessionEntries(path) {
21114
+ for await (const line of asyncLines(path)) {
21115
+ try {
21116
+ yield JSON.parse(line);
21117
+ } catch {}
21118
+ }
21119
+ }
21120
+ async function* asyncSessionFiles(directory, nested) {
20844
21121
  let dir;
20845
21122
  try {
20846
- dir = opendirSync(directory);
21123
+ dir = await opendir(directory);
20847
21124
  } catch {
20848
21125
  return;
20849
21126
  }
20850
- try {
20851
- while (true) {
20852
- const entry = dir.readSync();
20853
- if (!entry)
20854
- break;
20855
- const path = join11(directory, entry.name);
20856
- if (entry.isFile() && entry.name.endsWith(".jsonl"))
20857
- yield path;
20858
- else if (nested && entry.isDirectory())
20859
- yield* sessionFiles(path, false);
20860
- }
20861
- } finally {
20862
- dir.closeSync();
20863
- }
20864
- }
20865
- function findSession(directory, nested, sessionId) {
20866
- for (const header of sessionHeaders(directory, nested)) {
20867
- if (header.id === sessionId)
20868
- return header.path;
21127
+ for await (const entry of dir) {
21128
+ const path = join11(directory, entry.name);
21129
+ if (entry.isFile() && entry.name.endsWith(".jsonl"))
21130
+ yield path;
21131
+ else if (nested && entry.isDirectory())
21132
+ yield* asyncSessionFiles(path, false);
20869
21133
  }
20870
- return null;
20871
21134
  }
20872
- function* sessionHeaders(directory, nested) {
20873
- for (const path of sessionFiles(directory, nested)) {
21135
+ async function* asyncSessionHeaders(directory, nested) {
21136
+ for await (const path of asyncSessionFiles(directory, nested)) {
20874
21137
  try {
20875
- for (const line of lines(path)) {
21138
+ for await (const line of asyncLines(path)) {
20876
21139
  const header = record(JSON.parse(line));
20877
21140
  if (header.type === "session")
20878
21141
  yield { ...header, path };
@@ -20881,13 +21144,6 @@ function* sessionHeaders(directory, nested) {
20881
21144
  } catch {}
20882
21145
  }
20883
21146
  }
20884
- function* sessionEntries(path) {
20885
- for (const line of lines(path)) {
20886
- try {
20887
- yield JSON.parse(line);
20888
- } catch {}
20889
- }
20890
- }
20891
21147
 
20892
21148
  // src/dreamer/pi-session-api.ts
20893
21149
  import { existsSync as existsSync11, readFileSync as readFileSync10, realpathSync as realpathSync4, statSync as statSync5 } from "node:fs";
@@ -21154,15 +21410,9 @@ function projectEntry(value) {
21154
21410
  }
21155
21411
  };
21156
21412
  }
21157
- function* summaryEntries(path) {
21413
+ function summaryProjector() {
21158
21414
  let pendingResults = 0;
21159
- for (const line of lines(path)) {
21160
- let parsed;
21161
- try {
21162
- parsed = JSON.parse(line);
21163
- } catch {
21164
- continue;
21165
- }
21415
+ return (parsed) => {
21166
21416
  const entry = projectEntry(parsed);
21167
21417
  const e = record2(entry);
21168
21418
  if (e.type === "message") {
@@ -21173,7 +21423,30 @@ function* summaryEntries(path) {
21173
21423
  } else if (m.role === "user" || m.role === "assistant")
21174
21424
  pendingResults = 0;
21175
21425
  }
21176
- yield entry;
21426
+ return entry;
21427
+ };
21428
+ }
21429
+ function* summaryEntries(path) {
21430
+ const project = summaryProjector();
21431
+ for (const line of lines(path)) {
21432
+ let entry;
21433
+ try {
21434
+ entry = JSON.parse(line);
21435
+ } catch {}
21436
+ if (entry !== undefined)
21437
+ yield project(entry);
21438
+ }
21439
+ }
21440
+ async function* asyncSummaryEntries(path) {
21441
+ const project = summaryProjector();
21442
+ for await (const line of asyncLines(path)) {
21443
+ let entry;
21444
+ try {
21445
+ entry = JSON.parse(line);
21446
+ } catch {
21447
+ continue;
21448
+ }
21449
+ yield project(entry);
21177
21450
  }
21178
21451
  }
21179
21452
  function createPiPrimerRawProviderFactory(deps = {}) {
@@ -21186,13 +21459,23 @@ function createPiPrimerRawProviderFactory(deps = {}) {
21186
21459
  return null;
21187
21460
  directory = join13(mod.getAgentDir(), "sessions");
21188
21461
  }
21189
- const path = findSession(directory, !deps.sessionDir, sessionId);
21462
+ let path = null;
21463
+ for await (const header of asyncSessionHeaders(directory, !deps.sessionDir)) {
21464
+ if (header.id === sessionId) {
21465
+ path = header.path;
21466
+ break;
21467
+ }
21468
+ }
21190
21469
  if (!path)
21191
21470
  return null;
21471
+ const sessionPath = path;
21192
21472
  return {
21193
21473
  readMessages() {
21194
21474
  throw new Error("Pi primer history requires bounded pages");
21195
21475
  },
21476
+ iterateMessageRangeAsync(from, to) {
21477
+ return iterateAsyncEntriesToRawMessageRange(asyncSummaryEntries(sessionPath), from - 1, Number.MAX_SAFE_INTEGER, to);
21478
+ },
21196
21479
  iterateMessageRange(from, to) {
21197
21480
  return iterateEntriesToRawMessageRange(summaryEntries(path), from - 1, Number.MAX_SAFE_INTEGER, to);
21198
21481
  },
@@ -21207,6 +21490,7 @@ function createPiPrimerRawProviderFactory(deps = {}) {
21207
21490
  }
21208
21491
 
21209
21492
  // src/dreamer/retrospective-raw-provider-pi.ts
21493
+ import { stat as stat2 } from "node:fs/promises";
21210
21494
  import { join as join14 } from "node:path";
21211
21495
  class PiRetrospectiveRawProvider {
21212
21496
  deps;
@@ -21222,11 +21506,11 @@ class PiRetrospectiveRawProvider {
21222
21506
  return [];
21223
21507
  directory = join14(mod.getAgentDir(), "sessions");
21224
21508
  }
21225
- const sessions = this.deps.listSessions ? await this.deps.listSessions(directory) : directory ? sessionHeaders(directory, !this.deps.sessionDir) : [];
21509
+ const sessions = this.deps.listSessions ? await this.deps.listSessions(directory) : directory ? asyncSessionHeaders(directory, !this.deps.sessionDir) : [];
21226
21510
  const projectCwd = this.deps.projectCwd;
21227
21511
  const result = [];
21228
21512
  this.sessionPathById.clear();
21229
- for (const raw of sessions) {
21513
+ for await (const raw of sessions) {
21230
21514
  const info = raw;
21231
21515
  if (!info || typeof info !== "object")
21232
21516
  continue;
@@ -21248,7 +21532,7 @@ class PiRetrospectiveRawProvider {
21248
21532
  }
21249
21533
  async readUserMessagesSince(sessionId, sinceMs, capPerSession) {
21250
21534
  const limit = Math.max(1, Math.floor(capPerSession));
21251
- const messages = await this.selectUserEntries(sessionId, (ts) => ts > sinceMs, limit + 1, false);
21535
+ const messages = await this.selectUserEntries(sessionId, (ts) => ts > sinceMs, limit + 1, false, sinceMs);
21252
21536
  return {
21253
21537
  messages: messages.slice(0, limit),
21254
21538
  truncated: messages.length > limit
@@ -21257,7 +21541,7 @@ class PiRetrospectiveRawProvider {
21257
21541
  async readOldestMessageTimesSince(sessionIds, sinceMs) {
21258
21542
  const out = new Map;
21259
21543
  for (const sessionId of sessionIds) {
21260
- const [oldest] = await this.selectUserEntries(sessionId, (ts) => ts > sinceMs, 1, false);
21544
+ const [oldest] = await this.selectUserEntries(sessionId, (ts) => ts > sinceMs, 1, false, sinceMs);
21261
21545
  if (oldest)
21262
21546
  out.set(sessionId, oldest.ts);
21263
21547
  }
@@ -21266,15 +21550,19 @@ class PiRetrospectiveRawProvider {
21266
21550
  async readUserMessagesBefore(sessionId, beforeMs, count) {
21267
21551
  return this.selectUserEntries(sessionId, (ts) => ts <= beforeMs, Math.max(1, Math.floor(count)), true);
21268
21552
  }
21269
- async selectUserEntries(sessionId, eligible, limit, newest) {
21553
+ async selectUserEntries(sessionId, eligible, limit, newest, sinceMs) {
21270
21554
  const filePath = this.sessionPathById.get(sessionId);
21271
21555
  if (!filePath)
21272
21556
  return [];
21273
21557
  const kept = [];
21274
21558
  try {
21275
- const entries = this.deps.loadEntriesFromFile ? await this.deps.loadEntriesFromFile(filePath) : sessionEntries(filePath);
21559
+ if (sinceMs !== undefined && !this.deps.loadEntriesFromFile) {
21560
+ if ((await stat2(filePath)).mtimeMs < sinceMs)
21561
+ return [];
21562
+ }
21563
+ const entries = this.deps.loadEntriesFromFile ? await this.deps.loadEntriesFromFile(filePath) : asyncSessionEntries(filePath);
21276
21564
  let ordinal = 0;
21277
- for (const entry of entries) {
21565
+ for await (const entry of entries) {
21278
21566
  const row = normalizePiUserEntry(entry, sessionId, ++ordinal);
21279
21567
  if (!row || !eligible(row.ts))
21280
21568
  continue;
@@ -21622,6 +21910,10 @@ function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRe
21622
21910
  });
21623
21911
  }
21624
21912
  assertRegistrationOwnerActive();
21913
+ const mappingMessages = result.meta?.mappingMessages;
21914
+ if (budgetTask === "map-memories" && Array.isArray(mappingMessages)) {
21915
+ dreamSession.messages = mappingMessages.filter(isRecord).filter((message) => message.role === "assistant").map((message) => makeMessage("assistant", Array.isArray(message.content) ? message.content.filter(isRecord).flatMap((part) => part.type === "text" && typeof part.text === "string" ? [{ type: "text", text: part.text }] : part.type === "toolCall" ? syntheticToolParts(1) : []) : []));
21916
+ }
21625
21917
  if (!result.ok) {
21626
21918
  if (result.reason === "token_budget") {
21627
21919
  throw new DreamTokenBudgetExceeded(dreamSession.id, Number(result.meta?.tokenBudget?.spent ?? 0));
@@ -21635,13 +21927,14 @@ function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRe
21635
21927
  }
21636
21928
  throw error;
21637
21929
  }
21638
- dreamSession.messages = [
21639
- makeMessage("user", [{ type: "text", text: userMessage }]),
21640
- makeMessage("assistant", [
21641
- ...syntheticToolParts(result.toolCallCount ?? 0, result.completedToolCalls),
21642
- ...result.assistantText ? [{ type: "text", text: result.assistantText }] : []
21643
- ])
21644
- ];
21930
+ if (!(budgetTask === "map-memories" && Array.isArray(mappingMessages)))
21931
+ dreamSession.messages = [
21932
+ makeMessage("user", [{ type: "text", text: userMessage }]),
21933
+ makeMessage("assistant", [
21934
+ ...syntheticToolParts(result.toolCallCount ?? 0, result.completedToolCalls),
21935
+ ...result.assistantText ? [{ type: "text", text: result.assistantText }] : []
21936
+ ])
21937
+ ];
21645
21938
  onAdjunctsRefreshNeeded?.(opts.projectIdentity);
21646
21939
  } finally {
21647
21940
  inFlightDreams.delete(runPromise);
@@ -22389,7 +22682,7 @@ function reconcilePiCompactionMode(args) {
22389
22682
  return NO_TRANSITION;
22390
22683
  let clearedSomething = false;
22391
22684
  if (getPendingPiCompactionMarkerState(args.db, args.sessionId) !== null) {
22392
- setPendingPiCompactionMarkerState(args.db, args.sessionId, null);
22685
+ setPendingPiCompactionMarkerState(args.db, args.sessionId, null, "compaction-disabled");
22393
22686
  clearedSomething = true;
22394
22687
  }
22395
22688
  if (getOverflowState(args.db, args.sessionId).needsEmergencyRecovery) {
@@ -23110,7 +23403,7 @@ function schedulePiTransformDecisionResolve(args) {
23110
23403
  return false;
23111
23404
  pendingPiDecisionBySession.delete(args.sessionId);
23112
23405
  const token = addScheduledWriteToken(args.sessionId);
23113
- setTimeout(() => {
23406
+ inRootAsyncContext2(() => setTimeout(() => {
23114
23407
  try {
23115
23408
  if (!hasScheduledWriteToken(args.sessionId, token))
23116
23409
  return;
@@ -23130,7 +23423,7 @@ function schedulePiTransformDecisionResolve(args) {
23130
23423
  } finally {
23131
23424
  deleteScheduledWriteToken(args.sessionId, token);
23132
23425
  }
23133
- }, 0);
23426
+ }, 0));
23134
23427
  return true;
23135
23428
  }
23136
23429
  function addScheduledWriteToken(sessionId) {
@@ -23286,21 +23579,40 @@ function buildReplacementContent(tagId) {
23286
23579
  return `[dropped §${tagId}§]`;
23287
23580
  }
23288
23581
  function applyNewToolDrop(target, options) {
23289
- if (!target)
23582
+ if (options.persist && (target?.prepareDrop?.() ?? target?.canDrop?.()) === false) {
23583
+ return { result: "incomplete", mode: "full" };
23584
+ }
23585
+ if (!target) {
23586
+ options.persist?.("full");
23290
23587
  return { result: "absent", mode: "full" };
23291
- if (options.inWindow && hasSmallToolInput(target) || options.keepSkeleton === true || target.cannotRemove?.() === true) {
23588
+ }
23589
+ if (options.inWindow && hasSmallToolInput(target) || options.keepSkeleton === true || target.cannotRemove?.() === true || target.wouldStrandConversationEnd?.() === true) {
23292
23590
  const strip = target.hasAttachments?.() === true;
23591
+ options.persist?.(strip ? "skeleton_stripped" : "skeleton_real");
23293
23592
  return {
23294
23593
  result: (strip ? target.skeletonStripped?.() : target.skeletonReal?.()) ?? "absent",
23295
23594
  mode: strip ? "skeleton_stripped" : "skeleton_real"
23296
23595
  };
23297
23596
  }
23597
+ options.persist?.("full");
23298
23598
  const result = target.drop?.() ?? "absent";
23299
23599
  if (result === "truncated" && target.hasAttachments?.() === true) {
23300
23600
  return { result: target.skeletonStripped?.() ?? "absent", mode: "skeleton_stripped" };
23301
23601
  }
23302
23602
  return { result, mode: result === "truncated" ? "skeleton_real" : "full" };
23303
23603
  }
23604
+ function applyPersistedToolDrop(db, sessionId, tagNumber, target, options, afterWrite) {
23605
+ return applyNewToolDrop(target, {
23606
+ ...options,
23607
+ persist: (mode) => {
23608
+ db.transaction(() => {
23609
+ updateTagDropMode2(db, sessionId, tagNumber, mode);
23610
+ updateTagStatus2(db, sessionId, tagNumber, "dropped");
23611
+ afterWrite?.();
23612
+ }).immediate();
23613
+ }
23614
+ });
23615
+ }
23304
23616
  function hasSmallToolInput(target) {
23305
23617
  const bytes = target?.inputStringBytes?.() ?? null;
23306
23618
  return bytes !== null && bytes <= SKELETON_REAL_INPUT_MAX_BYTES;
@@ -23375,11 +23687,11 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23375
23687
  diagnostics.reasons[reason] = (diagnostics.reasons[reason] ?? 0) + 1;
23376
23688
  };
23377
23689
  let admitted = false;
23378
- let startedAt = 0;
23379
23690
  try {
23380
23691
  db.transaction(() => {
23381
23692
  admitted = true;
23382
- startedAt = performance.now();
23693
+ }).immediate();
23694
+ {
23383
23695
  const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
23384
23696
  const tags = preloadedTags ?? getTagsByNumbers2(db, sessionId, [
23385
23697
  ...new Set([
@@ -23431,10 +23743,31 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23431
23743
  continue;
23432
23744
  }
23433
23745
  }
23746
+ const persistDrop = (mode) => {
23747
+ let startedAt = 0;
23748
+ try {
23749
+ db.transaction(() => {
23750
+ startedAt = performance.now();
23751
+ if (mode)
23752
+ updateTagDropMode2(db, sessionId, pendingOp.tagId, mode);
23753
+ updateTagStatus2(db, sessionId, pendingOp.tagId, "dropped");
23754
+ if (!synthetic)
23755
+ removePendingOp(db, sessionId, pendingOp.tagId);
23756
+ }).immediate();
23757
+ } finally {
23758
+ if (startedAt)
23759
+ logSlowWriteTransaction("apply_pending_operation", startedAt);
23760
+ }
23761
+ };
23434
23762
  let shouldPersistDrop = false;
23435
23763
  if (isToolTag) {
23436
23764
  if (editMarkerTagIds.has(pendingOp.tagId)) {
23437
23765
  const strip = target?.hasAttachments?.() === true;
23766
+ if (target?.canDrop?.() === false || target?.prepareDrop?.() === false || !target) {
23767
+ reject("edit_marker_incomplete");
23768
+ continue;
23769
+ }
23770
+ persistDrop(strip ? "edit_marker_stripped" : "edit_marker");
23438
23771
  const markResult = (strip ? target?.editMarkerStripped?.() : target?.editMarker?.()) ?? "absent";
23439
23772
  if (markResult === "incomplete" || markResult === "absent") {
23440
23773
  reject(`edit_marker_${markResult}`);
@@ -23443,11 +23776,11 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23443
23776
  didMutateMessage = true;
23444
23777
  operationMutated = true;
23445
23778
  onTagReduced?.({ tagNumber: pendingOp.tagId, mode: "edit_marker" });
23446
- updateTagDropMode2(db, sessionId, pendingOp.tagId, strip ? "edit_marker_stripped" : "edit_marker");
23447
23779
  shouldPersistDrop = true;
23448
23780
  } else {
23449
23781
  const { result: dropResult, mode: appliedMode } = applyNewToolDrop(target, {
23450
- inWindow: skeletonWindow.has(pendingOp.tagId)
23782
+ inWindow: skeletonWindow.has(pendingOp.tagId),
23783
+ persist: persistDrop
23451
23784
  });
23452
23785
  if (dropResult === "incomplete" || synthetic && dropResult !== "removed" && dropResult !== "truncated") {
23453
23786
  reject(`drop_${dropResult}`);
@@ -23463,12 +23796,12 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23463
23796
  } else {
23464
23797
  reject(`drop_${dropResult}`);
23465
23798
  }
23466
- updateTagDropMode2(db, sessionId, pendingOp.tagId, appliedMode);
23467
23799
  shouldPersistDrop = true;
23468
23800
  }
23469
23801
  } else if (target) {
23470
23802
  const replacement = buildReplacementContent(pendingOp.tagId);
23471
23803
  const priorContent = target.getContent?.();
23804
+ persistDrop();
23472
23805
  const changed = target.setContent(replacement);
23473
23806
  if (changed) {
23474
23807
  didMutateMessage = true;
@@ -23485,30 +23818,25 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
23485
23818
  shouldPersistDrop = true;
23486
23819
  } else if (!synthetic) {
23487
23820
  reject("target_absent");
23821
+ persistDrop();
23488
23822
  shouldPersistDrop = true;
23489
23823
  }
23490
23824
  if (!shouldPersistDrop) {
23491
23825
  reject("not_persisted");
23492
23826
  continue;
23493
23827
  }
23494
- updateTagStatus2(db, sessionId, pendingOp.tagId, "dropped");
23495
- if (!synthetic)
23496
- removePendingOp(db, sessionId, pendingOp.tagId);
23497
23828
  if (operationMutated)
23498
23829
  diagnostics.mutated += 1;
23499
23830
  else
23500
23831
  diagnostics.persistedWithoutMutation += 1;
23501
23832
  }
23502
- }).immediate();
23833
+ }
23503
23834
  } catch (error) {
23504
23835
  if (!admitted && /database (?:table )?is locked|sqlite_(busy|locked)/i.test(String(error))) {
23505
23836
  sessionLog2(sessionId, "pending operations write admission busy; retaining prior bytes for retry");
23506
23837
  return false;
23507
23838
  }
23508
23839
  throw error;
23509
- } finally {
23510
- if (admitted)
23511
- logSlowWriteTransaction("apply_pending_operations", startedAt);
23512
23840
  }
23513
23841
  if (diagnostics) {
23514
23842
  onBatchComplete?.(diagnostics);
@@ -24880,6 +25208,24 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
24880
25208
  };
24881
25209
  }
24882
25210
 
25211
+ // ../plugin/src/hooks/magic-context/degraded-pass-refusal.ts
25212
+ class DegradedPassRefusalError extends Error {
25213
+ site;
25214
+ code = "DEGRADED_PASS_REFUSAL";
25215
+ recoverable = true;
25216
+ estimatedTokens;
25217
+ contextLimitTokens;
25218
+ constructor(site, options) {
25219
+ super(renderUserFacingFailure("transform_pass_degraded", "plain"), {
25220
+ cause: options?.cause
25221
+ });
25222
+ this.site = site;
25223
+ this.name = "DegradedPassRefusalError";
25224
+ this.estimatedTokens = options?.estimatedTokens;
25225
+ this.contextLimitTokens = options?.contextLimitTokens;
25226
+ }
25227
+ }
25228
+
24883
25229
  // ../plugin/src/hooks/magic-context/dropped-token-estimate.ts
24884
25230
  var TAG_QUERY_CHUNK_SIZE = 900;
24885
25231
  var ESTIMATED_CHARACTERS_PER_TOKEN = 3.5;
@@ -25500,8 +25846,110 @@ function advanceToolReclaimWatermarkToCurrentMax(db, sessionId) {
25500
25846
  return maxTagNumber;
25501
25847
  }
25502
25848
 
25849
+ // ../plugin/src/shared/recovery-reasoning-prefix.ts
25850
+ function recoveryReasoningPrefixes(messages, partsKey) {
25851
+ const prefixes = [];
25852
+ for (let index = 0;index < messages.length; index++) {
25853
+ const message = messages[index];
25854
+ const parts = message?.[partsKey];
25855
+ if (!Array.isArray(parts))
25856
+ continue;
25857
+ for (let p = 0;p < parts.length; p++) {
25858
+ const type = parts[p]?.type;
25859
+ if (type !== "thinking" && type !== "reasoning" && type !== "redactedThinking" && type !== "redacted_thinking")
25860
+ continue;
25861
+ prefixes.push(JSON.stringify([
25862
+ ...messages.slice(0, index),
25863
+ { ...message, [partsKey]: parts.slice(0, p + 1) }
25864
+ ]));
25865
+ }
25866
+ }
25867
+ return prefixes;
25868
+ }
25869
+
25503
25870
  // ../plugin/src/shared/tag-transcript.ts
25504
25871
  import { createHash as createHash11 } from "node:crypto";
25872
+ function buildPersistedDropTargets(transcript, tags, options = {}) {
25873
+ const targets = new Map;
25874
+ const tools = new Map(tags.filter((tag) => tag.type === "tool").map((tag) => [makeToolCompositeKey(tag.toolOwnerMessageId ?? "", tag.messageId), tag]));
25875
+ const textTags = new Map(tags.filter((tag) => tag.type !== "tool").map((tag) => [tag.messageId, tag]));
25876
+ const byNumber = new Map(tags.map((tag) => [tag.tagNumber, tag]));
25877
+ const aggregates = new Map;
25878
+ const pending = new Map;
25879
+ const resolved = new Map;
25880
+ const ambiguousCalls = new Set;
25881
+ const end = new ConversationEndGuard(transcript.messages);
25882
+ for (const message of transcript.messages) {
25883
+ let ordinal = 0;
25884
+ let resultRun;
25885
+ const parts = message.parts;
25886
+ const reasoning = message.requiresToolArcSkeleton ?? (message.info.role === "assistant" && parts.some((part) => part.kind === "thinking"));
25887
+ for (const part of parts) {
25888
+ if (part.kind !== "tool_result")
25889
+ resultRun = undefined;
25890
+ if (part.kind === "text") {
25891
+ const prefix = /^§(\d+)§\s/.exec(part.getText() ?? "");
25892
+ const tag = (prefix ? byNumber.get(Number(prefix[1])) : undefined) ?? textTags.get(`${message.info.id}:p${ordinal}`);
25893
+ ordinal++;
25894
+ if (tag && tag.type !== "tool")
25895
+ targets.set(tag.tagNumber, buildTextTarget(part, message, ""));
25896
+ } else if ((part.kind === "tool_use" || part.kind === "tool_result") && part.id) {
25897
+ const open = pending.get(part.id) ?? [];
25898
+ if (open.length > 1)
25899
+ ambiguousCalls.add(part.id);
25900
+ const key = (part.kind === "tool_result" ? resultRun?.callId === part.id ? resultRun.key : open.at(-1) ?? resolved.get(part.id) : undefined) ?? makeToolCompositeKey(message.info.id ?? "", part.id);
25901
+ if (part.kind === "tool_use")
25902
+ open.push(key);
25903
+ else {
25904
+ const position = open.lastIndexOf(key);
25905
+ if (position >= 0)
25906
+ open.splice(position, 1);
25907
+ resultRun = { callId: part.id, key };
25908
+ resolved.set(part.id, key);
25909
+ }
25910
+ pending.set(part.id, open);
25911
+ let aggregate = aggregates.get(key);
25912
+ if (!aggregate) {
25913
+ aggregate = {
25914
+ tag: tools.get(key),
25915
+ occurrences: [],
25916
+ reasoning: false
25917
+ };
25918
+ aggregates.set(key, aggregate);
25919
+ }
25920
+ aggregate.occurrences.push({ message, part, kind: part.kind });
25921
+ aggregate.reasoning ||= reasoning;
25922
+ }
25923
+ }
25924
+ }
25925
+ const arcsByCall = new Map;
25926
+ for (const { occurrences } of aggregates.values()) {
25927
+ const callId = occurrences[0]?.part.id ?? "";
25928
+ arcsByCall.set(callId, (arcsByCall.get(callId) ?? 0) + 1);
25929
+ }
25930
+ for (const aggregate of aggregates.values()) {
25931
+ const { occurrences, reasoning } = aggregate;
25932
+ const callId = occurrences[0]?.part.id ?? "";
25933
+ if (ambiguousCalls.has(callId))
25934
+ continue;
25935
+ const tag = aggregate.tag ?? (arcsByCall.get(callId) === 1 ? tools.get(makeToolCompositeKey("", callId)) : undefined);
25936
+ if (tag)
25937
+ targets.set(tag.tagNumber, buildAggregateTarget(tag.tagNumber, occurrences, reasoning, end));
25938
+ }
25939
+ if (!options.allowReasoningPrefixRewrite) {
25940
+ let lastThinking = -1;
25941
+ transcript.messages.forEach((message, index) => {
25942
+ if (message.parts.some((part) => part.kind === "thinking"))
25943
+ lastThinking = index;
25944
+ });
25945
+ const protectedIds = new Set(transcript.messages.slice(0, lastThinking + 1).map((message) => message.info.id));
25946
+ for (const [number, target] of targets) {
25947
+ if (target.mutationParts?.some(({ message }) => protectedIds.has(message.info.id)))
25948
+ targets.delete(number);
25949
+ }
25950
+ }
25951
+ return targets;
25952
+ }
25505
25953
  function textIdentityDigest(value) {
25506
25954
  return createHash11("sha256").update(value).digest("hex");
25507
25955
  }
@@ -26196,6 +26644,7 @@ function buildAggregateTarget(tagId, occurrences, requiresToolArcSkeleton, conve
26196
26644
  }
26197
26645
  return any ? "truncated" : "absent";
26198
26646
  },
26647
+ prepareDrop: () => !occurrences.some((occ) => occ.part.canRemove?.() === "defer"),
26199
26648
  canDrop: () => complete() && !occurrences.some((occ) => occ.part.hasUserAnswer?.()),
26200
26649
  requiresToolArcSkeleton,
26201
26650
  readInput() {
@@ -26546,7 +26995,7 @@ function queueAutoSearchBackfill(db, sessionId, projectPath, query) {
26546
26995
  pending.delete(key);
26547
26996
  resolve();
26548
26997
  };
26549
- const queued = setTimeout(() => {
26998
+ const queued = inRootAsyncContext2(() => setTimeout(() => {
26550
26999
  lifecycle.signal.removeEventListener("abort", cancel);
26551
27000
  if (lifecycle.signal.aborted)
26552
27001
  return done();
@@ -26557,7 +27006,7 @@ function queueAutoSearchBackfill(db, sessionId, projectPath, query) {
26557
27006
  clearTimeout(timeout);
26558
27007
  done();
26559
27008
  });
26560
- }, 0);
27009
+ }, 0));
26561
27010
  const cancel = () => {
26562
27011
  clearTimeout(queued);
26563
27012
  done();
@@ -26604,7 +27053,7 @@ async function executeAutoSearchWorker(db, sessionId, projectPath, query, option
26604
27053
  deadlineUnixMs: autoSearchDeadlineUnixMs(deadlineSignal)
26605
27054
  };
26606
27055
  return new Promise((resolve, reject) => {
26607
- const worker = new Worker(entry, { workerData: input });
27056
+ const worker = inRootAsyncContext2(() => new Worker(entry, { workerData: input }));
26608
27057
  let settled = false;
26609
27058
  const finish = (results, error) => {
26610
27059
  if (settled)
@@ -28225,18 +28674,27 @@ function buildPiCompactionSummary(compartments) {
28225
28674
  return `Magic Context compacted ${titles.length} segments: ${shown}; …and ${titles.length - MAX_SUMMARY_TITLES} more`;
28226
28675
  }
28227
28676
  function findFirstKeptEntryId(entries, lastCompactedOrdinal, ordinalOffset = 0) {
28677
+ const boundary = resolvePiFirstKeptEntry(entries, lastCompactedOrdinal, ordinalOffset);
28678
+ return boundary.kind === "resolved" ? boundary.entryId : null;
28679
+ }
28680
+ function resolvePiFirstKeptEntry(entries, lastCompactedOrdinal, ordinalOffset = 0) {
28228
28681
  const target = lastCompactedOrdinal + 1;
28229
28682
  const afterOrdinal = Number.isNaN(lastCompactedOrdinal) ? 0 : Math.max(0, lastCompactedOrdinal);
28683
+ let syntheticKeptId;
28230
28684
  for (const message of iterateEntriesToRawMessageRange(entries, afterOrdinal, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, ordinalOffset)) {
28231
28685
  if (message.ordinal < target || isPiSystemEntry(message))
28232
28686
  continue;
28233
- if (message.id.startsWith(SYNTH_USER_ID_PREFIX))
28234
- return null;
28687
+ if (syntheticKeptId)
28688
+ return { kind: "unplaceable", rawId: syntheticKeptId };
28689
+ if (message.id.startsWith(SYNTH_USER_ID_PREFIX)) {
28690
+ syntheticKeptId = message.id;
28691
+ continue;
28692
+ }
28235
28693
  if (message.id.length === 0)
28236
28694
  continue;
28237
- return message.id;
28695
+ return { kind: "resolved", entryId: message.id };
28238
28696
  }
28239
- return null;
28697
+ return { kind: "waiting" };
28240
28698
  }
28241
28699
 
28242
28700
  // src/compaction-marker-manager-pi.ts
@@ -28257,6 +28715,7 @@ function logPiCompactionMarkerDrainGateOnce(sessionId, pending, gate, fields = {
28257
28715
  `endMessageId=${pending.endMessageId}`,
28258
28716
  `ordinal=${pending.ordinal}`,
28259
28717
  `firstKeptEntryId=${firstKeptEntryId ?? "<none>"}`,
28718
+ `boundaryKind=${pending.endMessageId.startsWith(SYNTH_USER_ID_PREFIX) ? "folded-tool-result" : "session-entry"}`,
28260
28719
  `renderedCoverageOrdinal=${fields.renderedCoverageOrdinal ?? "<none>"}`
28261
28720
  ];
28262
28721
  if (fields.renderedCoverageEndMessageId !== undefined)
@@ -28269,6 +28728,17 @@ function logPiCompactionMarkerDrainGateOnce(sessionId, pending, gate, fields = {
28269
28728
  parts.push(`matchCount=${fields.matchCount}`);
28270
28729
  if (fields.alignment !== undefined)
28271
28730
  parts.push(`alignment=${fields.alignment}`);
28731
+ if (fields.alignmentReason !== undefined)
28732
+ parts.push(`alignmentReason=${fields.alignmentReason}`);
28733
+ for (const key of [
28734
+ "historyConsumed",
28735
+ "materializationSatisfied",
28736
+ "drainSuppressed",
28737
+ "casLost"
28738
+ ]) {
28739
+ if (fields[key] !== undefined)
28740
+ parts.push(`${key}=${fields[key]}`);
28741
+ }
28272
28742
  if (fields.foldingOnlyTools !== undefined)
28273
28743
  parts.push(`foldingOnlyTools=${JSON.stringify(fields.foldingOnlyTools)}`);
28274
28744
  if (fields.persistedOnlyTools !== undefined)
@@ -28318,10 +28788,20 @@ function applyDeferredPiCompactionMarker(deps, sessionId, pending) {
28318
28788
  if (!firstKeptEntryId) {
28319
28789
  const alignment = resolvePiOrdinalAlignment(deps.db, sessionId, branchEntries);
28320
28790
  if (isPiOrdinalAlignmentUnanchored(alignment)) {
28321
- logPiCompactionMarkerDrainGateOnce(sessionId, pending, "ordinal-alignment-unavailable", { alignment: alignment.kind });
28791
+ if (alignment.kind === "unanchored" && alignment.reason === "anchor-missing" && hasMergedSyntheticBoundary(branchEntries, pending.endMessageId)) {
28792
+ return { kind: "stale-skip", reason: "synthetic-boundary-merged" };
28793
+ }
28794
+ logPiCompactionMarkerDrainGateOnce(sessionId, pending, "ordinal-alignment-unavailable", {
28795
+ alignment: alignment.kind,
28796
+ ...alignment.kind === "unanchored" ? { alignmentReason: alignment.reason } : {}
28797
+ });
28322
28798
  return { kind: "waiting-for-entry" };
28323
28799
  }
28324
- firstKeptEntryId = findFirstKeptEntryId(branchEntries, pending.ordinal, alignment.offset);
28800
+ const boundary = resolvePiFirstKeptEntry(branchEntries, pending.ordinal, alignment.offset);
28801
+ if (boundary.kind === "unplaceable") {
28802
+ return { kind: "stale-skip", reason: "synthetic-kept-entry" };
28803
+ }
28804
+ firstKeptEntryId = boundary.kind === "resolved" ? boundary.entryId : null;
28325
28805
  }
28326
28806
  if (!firstKeptEntryId) {
28327
28807
  logPiCompactionMarkerDrainGateOnce(sessionId, pending, "no-replay-safe-kept-entry", { firstKeptEntryId: null });
@@ -28376,6 +28856,27 @@ function applyDeferredPiCompactionMarker(deps, sessionId, pending) {
28376
28856
  return { kind: "retryable-failure", error };
28377
28857
  }
28378
28858
  }
28859
+ function hasMergedSyntheticBoundary(entries, endMessageId) {
28860
+ if (!endMessageId.startsWith(SYNTH_USER_ID_PREFIX))
28861
+ return false;
28862
+ const resultId = endMessageId.slice(SYNTH_USER_ID_PREFIX.length);
28863
+ let foundResult = false;
28864
+ for (const entry of entries) {
28865
+ const row = entry;
28866
+ if (row?.type !== "message")
28867
+ continue;
28868
+ const message = row.message;
28869
+ if (!foundResult) {
28870
+ foundResult = row.id === resultId && message?.role === "toolResult" && typeof message.toolCallId === "string" && message.toolCallId.length > 0;
28871
+ continue;
28872
+ }
28873
+ if (message?.role === "user")
28874
+ return true;
28875
+ if (message?.role === "assistant")
28876
+ return false;
28877
+ }
28878
+ return false;
28879
+ }
28379
28880
  function findLatestCompaction(branchEntries) {
28380
28881
  for (let i = branchEntries.length - 1;i >= 0; i--) {
28381
28882
  const entry = branchEntries[i];
@@ -29729,30 +30230,26 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
29729
30230
  const tPersist = performance.now();
29730
30231
  const toDrop = new Set(plan.tagNumbers);
29731
30232
  const newestEmergencyTags = recentTags;
29732
- db.transaction(() => {
29733
- for (const tag of tags) {
29734
- if (!toDrop.has(tag.tagNumber))
29735
- continue;
29736
- if (tag.status !== "active" || tag.type !== "tool")
29737
- continue;
29738
- const target = targets.get(tag.tagNumber);
29739
- const recent = (emergency.usagePercentage ?? 0) < 95 && newestEmergencyTags.has(tag.tagNumber);
29740
- const { result, mode } = applyNewToolDrop(target, {
29741
- inWindow: recent,
29742
- keepSkeleton: target?.requiresToolArcSkeleton === true
30233
+ for (const tag of tags) {
30234
+ if (!toDrop.has(tag.tagNumber))
30235
+ continue;
30236
+ if (tag.status !== "active" || tag.type !== "tool")
30237
+ continue;
30238
+ const target = targets.get(tag.tagNumber);
30239
+ const recent = (emergency.usagePercentage ?? 0) < 95 && newestEmergencyTags.has(tag.tagNumber);
30240
+ const { result, mode } = applyPersistedToolDrop(db, sessionId, tag.tagNumber, target, {
30241
+ inWindow: recent,
30242
+ keepSkeleton: target?.requiresToolArcSkeleton === true
30243
+ });
30244
+ if (result === "removed" || result === "truncated") {
30245
+ droppedTools++;
30246
+ emergencyDroppedTools++;
30247
+ droppedTokenReductions.push({
30248
+ tagNumber: tag.tagNumber,
30249
+ mode: mode === "full" ? "full" : "truncated"
29743
30250
  });
29744
- if (result === "removed" || result === "truncated") {
29745
- updateTagStatus2(db, sessionId, tag.tagNumber, "dropped");
29746
- updateTagDropMode2(db, sessionId, tag.tagNumber, mode);
29747
- droppedTools++;
29748
- emergencyDroppedTools++;
29749
- droppedTokenReductions.push({
29750
- tagNumber: tag.tagNumber,
29751
- mode: mode === "full" ? "full" : "truncated"
29752
- });
29753
- }
29754
30251
  }
29755
- }).immediate();
30252
+ }
29756
30253
  logStep("applyEmergencyDrops", tPersist, `dropped=${emergencyDroppedTools}`);
29757
30254
  sessionLog2(sessionId, `emergency tiered drop: ${plan.reason}`);
29758
30255
  } else {
@@ -29761,33 +30258,29 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
29761
30258
  }
29762
30259
  const staleReduce = routine && config.staleReduceStripEnabled ? collectStaleReduceCallIds(piMessages, buildMessageIdToMaxTagFromTargets(targets), buildCtxReduceTagNumbers(tags), toolAgeCutoff, resolveStableId) : { composite: new Set, bareCallIds: new Set };
29763
30260
  if (routine && config.staleReduceStripEnabled && (staleReduce.composite.size > 0 || staleReduce.bareCallIds.size > 0)) {
29764
- db.transaction(() => {
29765
- for (const tag of tags) {
29766
- if (tag.status !== "active")
29767
- continue;
29768
- if (tag.type !== "tool")
29769
- continue;
29770
- if (protectedTools.has(tag.tagNumber))
29771
- continue;
29772
- if (!tag.messageId)
29773
- continue;
29774
- const matched = tag.toolOwnerMessageId ? staleReduce.composite.has(`${tag.toolOwnerMessageId}\x00${tag.messageId}`) : staleReduce.bareCallIds.has(tag.messageId);
29775
- if (!matched)
29776
- continue;
29777
- const target = targets.get(tag.tagNumber);
29778
- if (target?.canDrop?.() === false)
29779
- continue;
29780
- const { result, mode } = applyNewToolDrop(target, { inWindow: false });
29781
- if (result === "incomplete")
29782
- continue;
29783
- updateTagDropMode2(db, sessionId, tag.tagNumber, mode);
29784
- updateTagStatus2(db, sessionId, tag.tagNumber, "dropped");
29785
- if (result === "removed" || result === "truncated") {
29786
- droppedStaleReduceCalls++;
29787
- config.onStaleReduceEdit?.(tag);
29788
- }
30261
+ for (const tag of tags) {
30262
+ if (tag.status !== "active")
30263
+ continue;
30264
+ if (tag.type !== "tool")
30265
+ continue;
30266
+ if (protectedTools.has(tag.tagNumber))
30267
+ continue;
30268
+ if (!tag.messageId)
30269
+ continue;
30270
+ const matched = tag.toolOwnerMessageId ? staleReduce.composite.has(`${tag.toolOwnerMessageId}\x00${tag.messageId}`) : staleReduce.bareCallIds.has(tag.messageId);
30271
+ if (!matched)
30272
+ continue;
30273
+ const target = targets.get(tag.tagNumber);
30274
+ if (target?.canDrop?.() === false)
30275
+ continue;
30276
+ const { result } = applyPersistedToolDrop(db, sessionId, tag.tagNumber, target, { inWindow: false });
30277
+ if (result === "incomplete")
30278
+ continue;
30279
+ if (result === "removed" || result === "truncated") {
30280
+ droppedStaleReduceCalls++;
30281
+ config.onStaleReduceEdit?.(tag);
29789
30282
  }
29790
- }).immediate();
30283
+ }
29791
30284
  }
29792
30285
  if (routine) {
29793
30286
  db.transaction(() => {
@@ -29856,37 +30349,33 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
29856
30349
  group.push(tag);
29857
30350
  fingerprintGroups.set(fingerprint, group);
29858
30351
  }
29859
- db.transaction(() => {
29860
- for (const [, group] of fingerprintGroups) {
29861
- if (group.length <= 1)
30352
+ for (const [, group] of fingerprintGroups) {
30353
+ if (group.length <= 1)
30354
+ continue;
30355
+ group.sort((a, b) => a.tagNumber - b.tagNumber);
30356
+ for (let i = 0;i < group.length - 1; i++) {
30357
+ const tag = group[i];
30358
+ if (tag.tagNumber > protectedCutoff)
29862
30359
  continue;
29863
- group.sort((a, b) => a.tagNumber - b.tagNumber);
29864
- for (let i = 0;i < group.length - 1; i++) {
29865
- const tag = group[i];
29866
- if (tag.tagNumber > protectedCutoff)
29867
- continue;
29868
- const target = targets.get(tag.tagNumber);
29869
- if (protectedTools.has(tag.tagNumber))
29870
- continue;
29871
- if (target?.canDrop?.() === false)
29872
- continue;
29873
- const { result, mode } = applyNewToolDrop(target, {
29874
- inWindow: false
30360
+ const target = targets.get(tag.tagNumber);
30361
+ if (protectedTools.has(tag.tagNumber))
30362
+ continue;
30363
+ if (target?.canDrop?.() === false)
30364
+ continue;
30365
+ const { result, mode } = applyPersistedToolDrop(db, sessionId, tag.tagNumber, target, {
30366
+ inWindow: false
30367
+ });
30368
+ if (result === "incomplete")
30369
+ continue;
30370
+ if (result === "removed" || result === "truncated") {
30371
+ deduplicatedTools++;
30372
+ droppedTokenReductions.push({
30373
+ tagNumber: tag.tagNumber,
30374
+ mode: mode === "full" ? "full" : "truncated"
29875
30375
  });
29876
- if (result === "incomplete")
29877
- continue;
29878
- updateTagDropMode2(db, sessionId, tag.tagNumber, mode);
29879
- updateTagStatus2(db, sessionId, tag.tagNumber, "dropped");
29880
- if (result === "removed" || result === "truncated") {
29881
- deduplicatedTools++;
29882
- droppedTokenReductions.push({
29883
- tagNumber: tag.tagNumber,
29884
- mode: mode === "full" ? "full" : "truncated"
29885
- });
29886
- }
29887
30376
  }
29888
30377
  }
29889
- }).immediate();
30378
+ }
29890
30379
  }
29891
30380
  if (droppedTools > 0 || deduplicatedTools > 0 || droppedInjections > 0 || droppedStaleReduceCalls > 0) {
29892
30381
  sessionLog2(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, stale ctx_reduce=${droppedStaleReduceCalls}, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
@@ -30300,6 +30789,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
30300
30789
  sequenceOffset: candidateCompartments.length,
30301
30790
  dumpLabelBase: `recomp-${sessionId}-${chunk.startIndex}-${chunk.endIndex}-pass-${passCount + 1}`,
30302
30791
  timeoutMs: historianTimeoutMs,
30792
+ maxOutputTokens: deps.historianMaxOutputTokens,
30303
30793
  model: deps.model,
30304
30794
  fallbackModelId: deps.fallbackModelId,
30305
30795
  fallbackModels: deps.fallbackModels,
@@ -30814,7 +31304,6 @@ ${renderUserFacingFailure("recomp_unavailable")}`;
30814
31304
  }
30815
31305
  }
30816
31306
  }
30817
-
30818
31307
  // ../plugin/src/hooks/magic-context/compartment-runner.ts
30819
31308
  var activeRuns = new Map;
30820
31309
  function getActiveCompartmentRun(sessionId) {
@@ -30835,7 +31324,7 @@ function withPublishedCallback(deps) {
30835
31324
  };
30836
31325
  }
30837
31326
  function startLeaseRenewal(deps, holderId) {
30838
- return setInterval(() => {
31327
+ return inRootAsyncContext2(() => setInterval(() => {
30839
31328
  try {
30840
31329
  if (!renewCompartmentLease(deps.db, deps.sessionId, holderId)) {
30841
31330
  sessionLog2(deps.sessionId, "compartment lease renewal failed; publish will be skipped if holder is stale");
@@ -30843,7 +31332,7 @@ function startLeaseRenewal(deps, holderId) {
30843
31332
  } catch (err) {
30844
31333
  sessionLog2(deps.sessionId, `compartment lease renewal threw; publish will be skipped if holder is stale (${err instanceof Error ? err.message : String(err)})`);
30845
31334
  }
30846
- }, COMPARTMENT_LEASE_RENEWAL_MS);
31335
+ }, COMPARTMENT_LEASE_RENEWAL_MS));
30847
31336
  }
30848
31337
  async function executeContextRecompWithResult(deps, options = {}) {
30849
31338
  const { sessionId } = deps;
@@ -30933,6 +31422,7 @@ function forgetCachedPrefix(sessionId) {
30933
31422
  function dropCompartmentsAfter(db, sessionId, anchor, holderId, activeBranch = false, onRetire) {
30934
31423
  db.exec("BEGIN IMMEDIATE");
30935
31424
  let finished = false;
31425
+ let clearedMarker = null;
30936
31426
  try {
30937
31427
  if (!isCompartmentLeaseHeld(db, sessionId, holderId)) {
30938
31428
  db.exec("ROLLBACK");
@@ -30949,14 +31439,16 @@ function dropCompartmentsAfter(db, sessionId, anchor, holderId, activeBranch = f
30949
31439
  if (activeBranch) {
30950
31440
  db.prepare("UPDATE session_meta SET prior_boundary_ordinal = ? WHERE session_id = ?").run((anchor?.endMessage ?? 0) + 1, sessionId);
30951
31441
  const marker = getPendingPiCompactionMarkerState(db, sessionId);
30952
- if (marker)
30953
- clearPendingPiCompactionMarkerStateIf(db, sessionId, marker);
31442
+ if (marker && clearPendingPiCompactionMarkerStateIf(db, sessionId, marker, null))
31443
+ clearedMarker = marker;
30954
31444
  }
30955
31445
  deleteChunkEmbedBackoffForSession(db, sessionId);
30956
31446
  queueM0Mutation(db, { sessionId, mutationType: "recomp_boundary_change" });
30957
31447
  clearCachedM0M1(db, sessionId);
30958
31448
  db.exec("COMMIT");
30959
31449
  finished = true;
31450
+ if (clearedMarker)
31451
+ logPiCompactionMarkerClear(sessionId, clearedMarker, "active-branch-retired");
30960
31452
  return dropped.map((row) => row.sequence);
30961
31453
  } finally {
30962
31454
  if (!finished) {
@@ -31072,6 +31564,29 @@ function trimPiMessagesToBoundary(piMessages, entryIds, cutoffMessageId, trimMut
31072
31564
  break;
31073
31565
  }
31074
31566
  }
31567
+ if (cutoffIndex < 0 && sessionId && hasRawMessageProvider2(sessionId)) {
31568
+ try {
31569
+ const branch = readRawSessionMessages2(sessionId);
31570
+ const positions = new Map(branch.map((message, index) => [message.id, index]));
31571
+ const boundaryPosition = positions.get(cutoffMessageId);
31572
+ if (boundaryPosition === undefined)
31573
+ return 0;
31574
+ let previous = -1;
31575
+ for (const [index, message] of piMessages.entries()) {
31576
+ if (isPiSystemEntry(message))
31577
+ continue;
31578
+ const id = resolveStableId(message, index, entryIds);
31579
+ const position = positions.get(id) ?? positions.get(`${SYNTH_USER_ID_PREFIX}${id}`);
31580
+ if (position === undefined || position <= previous)
31581
+ return 0;
31582
+ previous = position;
31583
+ if (position <= boundaryPosition)
31584
+ cutoffIndex = index;
31585
+ }
31586
+ } catch {
31587
+ return 0;
31588
+ }
31589
+ }
31075
31590
  if (cutoffIndex < 0)
31076
31591
  return 0;
31077
31592
  const remove = new Set;
@@ -32575,6 +33090,54 @@ function rewriteNativeToolInput(message, toolCallId, input) {
32575
33090
  items[match.index] = { ...match.item, [field]: nextValue };
32576
33091
  envelope.message.providerPayload = { ...envelope.payload, items };
32577
33092
  }
33093
+ function textIdentity(part) {
33094
+ if (!isRecord(part) || part.type !== "text" || typeof part.textSignature !== "string")
33095
+ return;
33096
+ try {
33097
+ const signature = JSON.parse(part.textSignature);
33098
+ if (isRecord(signature) && signature.v === 1 && typeof signature.id === "string" && signature.id.length > 0 && (signature.phase === undefined || signature.phase === "commentary" || signature.phase === "final_answer")) {
33099
+ return { id: signature.id, phase: signature.phase };
33100
+ }
33101
+ } catch {}
33102
+ return;
33103
+ }
33104
+ function rewriteNativeAssistantTexts(message, previous, sessionId) {
33105
+ const envelope = getNativeEnvelope(message);
33106
+ if (envelope?.message.role !== "assistant" || !isRecord(previous) || !Array.isArray(previous.content) || !Array.isArray(envelope.message.content))
33107
+ return;
33108
+ const content = envelope.message.content;
33109
+ for (let index = 0;index < content.length; index++) {
33110
+ const part = content[index];
33111
+ const before = previous.content[index];
33112
+ if (!isRecord(part) || part.type !== "text" || typeof part.text !== "string" || !isRecord(before) || before.type !== "text" || typeof before.text !== "string")
33113
+ continue;
33114
+ if (part.text.replace(/^§\d+§ /u, "") === before.text.replace(/^§\d+§ /u, ""))
33115
+ continue;
33116
+ const identity = textIdentity(part);
33117
+ const sameIdentity = (value) => {
33118
+ const other = textIdentity(value);
33119
+ return identity !== undefined && other?.id === identity.id && other.phase === identity.phase;
33120
+ };
33121
+ const matches = envelope.items.flatMap((item, itemIndex) => isRecord(item) && item.type === "message" && item.role === "assistant" && identity !== undefined && item.id === identity.id && item.phase === identity.phase ? [{ item, itemIndex }] : []);
33122
+ const match = matches[0];
33123
+ const outputs = match && Array.isArray(match.item.content) ? match.item.content.flatMap((output, outputIndex) => isRecord(output) && output.type === "output_text" && typeof output.text === "string" ? [{ output, outputIndex }] : []) : [];
33124
+ if (!identity || content.filter(sameIdentity).length !== 1 || previous.content.filter(sameIdentity).length !== 1 || matches.length !== 1 || outputs.length !== 1) {
33125
+ if (sessionId)
33126
+ sessionLog2(sessionId, `native assistant text replay skipped: missing or ambiguous id/phase match at part ${index} (items=${matches.length}, outputs=${outputs.length}); retaining native history for dispatch validation`);
33127
+ continue;
33128
+ }
33129
+ const { output, outputIndex } = outputs[0];
33130
+ if (output.text === part.text)
33131
+ continue;
33132
+ const nextContent = match.item.content.slice();
33133
+ nextContent[outputIndex] = { ...output, text: part.text };
33134
+ const items = envelope.items.slice();
33135
+ items[match.itemIndex] = { ...match.item, content: nextContent };
33136
+ envelope.items = items;
33137
+ envelope.payload = { ...envelope.payload, items };
33138
+ envelope.message.providerPayload = envelope.payload;
33139
+ }
33140
+ }
32578
33141
  function hasRedactedThinkingContent(message) {
32579
33142
  if (!Array.isArray(message.content))
32580
33143
  return false;
@@ -32886,9 +33449,9 @@ class PiContextBudget {
32886
33449
  elapsed() {
32887
33450
  return this.now() - this.startedAt;
32888
33451
  }
32889
- remainingWork() {
33452
+ remainingWork = () => {
32890
33453
  return Math.max(0, PI_CONTEXT_BUDGET.workMs - this.elapsed());
32891
- }
33454
+ };
32892
33455
  remainingOutcome = () => Math.max(0, PI_CONTEXT_BUDGET.outcomeMs - this.elapsed());
32893
33456
  writerAllowance() {
32894
33457
  return Math.max(0, Math.min(PI_CONTEXT_BUDGET.writerMs, this.remainingWork() - PI_CONTEXT_BUDGET.completionReserveMs));
@@ -33670,7 +34233,7 @@ function object4(value) {
33670
34233
  function wellFormed(text) {
33671
34234
  return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "�");
33672
34235
  }
33673
- function textIdentity(text, phase) {
34236
+ function textIdentity2(text, phase) {
33674
34237
  return JSON.stringify([
33675
34238
  text,
33676
34239
  phase === "commentary" || phase === "final_answer" ? phase : null
@@ -33728,7 +34291,7 @@ function piResponsesAssistantTexts(value, model) {
33728
34291
  units += text.length;
33729
34292
  if (units > MAX_TEXT_UNITS)
33730
34293
  return;
33731
- texts.push(textIdentity(text, undefined));
34294
+ texts.push(textIdentity2(text, undefined));
33732
34295
  continue;
33733
34296
  }
33734
34297
  if (block?.type !== "text" || typeof block.text !== "string")
@@ -33736,7 +34299,7 @@ function piResponsesAssistantTexts(value, model) {
33736
34299
  units += block.text.length;
33737
34300
  if (units > MAX_TEXT_UNITS)
33738
34301
  return;
33739
- texts.push(textIdentity(wellFormed(block.text), sameModel ? signaturePhase(block) : undefined));
34302
+ texts.push(textIdentity2(wellFormed(block.text), sameModel ? signaturePhase(block) : undefined));
33740
34303
  blocks.push(block);
33741
34304
  }
33742
34305
  const payload = object4(message.providerPayload);
@@ -33761,7 +34324,7 @@ function piResponsesAssistantTexts(value, model) {
33761
34324
  }
33762
34325
  if (signature?.v !== 1 || typeof signature.id !== "string" || item.id !== signature.id || item.phase !== signature.phase || part.text !== block.text.replace(/^§\d+§ /u, ""))
33763
34326
  return texts;
33764
- replay.push(textIdentity(wellFormed(part.text), item.phase));
34327
+ replay.push(textIdentity2(wellFormed(part.text), item.phase));
33765
34328
  }
33766
34329
  }
33767
34330
  return replay.length === texts.length ? replay : texts;
@@ -33770,7 +34333,7 @@ function piResponsesWireTexts(value, phase) {
33770
34333
  if (phase !== undefined && phase !== null && phase !== "commentary" && phase !== "final_answer")
33771
34334
  return;
33772
34335
  if (typeof value === "string")
33773
- return [textIdentity(value, phase)];
34336
+ return [textIdentity2(value, phase)];
33774
34337
  if (!Array.isArray(value))
33775
34338
  return;
33776
34339
  const texts = [];
@@ -33782,7 +34345,7 @@ function piResponsesWireTexts(value, phase) {
33782
34345
  units += part.text.length;
33783
34346
  if (units > MAX_TEXT_UNITS)
33784
34347
  return;
33785
- texts.push(textIdentity(part.text, phase));
34348
+ texts.push(textIdentity2(part.text, phase));
33786
34349
  }
33787
34350
  return texts;
33788
34351
  }
@@ -35775,17 +36338,26 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
35775
36338
  return { rebuildReason: "lkg_recovery_input_rebuild" };
35776
36339
  return { prefix };
35777
36340
  };
35778
- const noteRecovery = (snapshot, messages) => {
35779
- const state = stateFor(snapshot.sessionId);
36341
+ const recoveryEntryIds = (snapshot) => {
35780
36342
  const anchor = snapshot.replayAnchorInputIndex;
35781
36343
  const slot = getSlot(snapshot.sessionId);
35782
36344
  if (snapshot.preparationFailure || anchor === null || !slot?.piOutputEntryIds)
35783
36345
  throw new PiLkgUncapturable("lkg_output_mapping_unavailable", "Cannot retain recovered output ownership");
35784
36346
  const surviving = new Set(snapshot.inputs.map((input) => input.id));
35785
- const outputEntryIds = [
36347
+ return [
35786
36348
  ...slot.piOutputEntryIds.filter((id) => id === null || surviving.has(id)),
35787
36349
  ...snapshot.inputs.slice(anchor + 1).map((input) => input.id)
35788
36350
  ];
36351
+ };
36352
+ const noteRecovery = (snapshot, messages, entryIds) => {
36353
+ const state = stateFor(snapshot.sessionId);
36354
+ const slot = getSlot(snapshot.sessionId);
36355
+ if (!slot)
36356
+ throw new PiLkgUncapturable("lkg_miss", "Cannot retain recovery without a saved request");
36357
+ const anchor = snapshot.replayAnchorInputIndex;
36358
+ if (anchor === null)
36359
+ throw new PiLkgUncapturable("lkg_miss", "Cannot retain recovery without an input anchor");
36360
+ const outputEntryIds = entryIds ?? recoveryEntryIds(snapshot);
35789
36361
  if (outputEntryIds.length !== messages.length)
35790
36362
  throw new PiLkgUncapturable("lkg_output_mapping_unavailable", "Recovered output ownership does not match the served array");
35791
36363
  state.recoveryPrefix = {
@@ -36202,6 +36774,7 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
36202
36774
  };
36203
36775
  return {
36204
36776
  noteRecovery,
36777
+ recoveryEntryIds,
36205
36778
  recoveryFor,
36206
36779
  beginPass(args) {
36207
36780
  return {
@@ -36603,7 +37176,7 @@ function flushPiServedArrayLedger() {
36603
37176
  function scheduleFlush() {
36604
37177
  if (flushTimer)
36605
37178
  return;
36606
- flushTimer = setTimeout(flushPiServedArrayLedger, FLUSH_DELAY_MS);
37179
+ flushTimer = inRootAsyncContext2(() => setTimeout(flushPiServedArrayLedger, FLUSH_DELAY_MS));
36607
37180
  flushTimer.unref?.();
36608
37181
  }
36609
37182
  function enqueue(filePath, line, assertCurrentPass) {
@@ -38557,31 +39130,61 @@ function isLargeImagePart(value) {
38557
39130
  const part = value;
38558
39131
  return part.type === "image" && typeof part.mimeType === "string" && part.mimeType.startsWith("image/") && typeof part.data === "string" && part.data.length > 200;
38559
39132
  }
38560
- function stripPiProcessedImages(args) {
38561
- const frozenIds = getProcessedImageStrippedIds(args.db, args.sessionId);
38562
- const newlyStrippedIds = [];
38563
- let hasAssistantResponse = false;
38564
- if (args.detect && args.watermark > 0) {
38565
- for (let i = args.messages.length - 1;i >= 0; i--) {
38566
- const raw = args.messages[i];
38567
- if (!raw || typeof raw !== "object")
38568
- continue;
38569
- const message = raw;
38570
- if (message.role === "assistant") {
38571
- hasAssistantResponse = true;
38572
- continue;
39133
+ function planPiProcessedImageIds(args, frozenIds) {
39134
+ if (!args.detect || args.watermark <= 0)
39135
+ return [];
39136
+ const ids = args.messages.map(args.stableId);
39137
+ const ownTags = ids.map((id) => id ? args.messageIdToMaxTag.get(id) ?? 0 : 0);
39138
+ const completeToolResults = new Set;
39139
+ const openCalls = new Map;
39140
+ for (let i = 0;i < args.messages.length; i++) {
39141
+ const message = args.messages[i];
39142
+ if (!message || typeof message !== "object")
39143
+ continue;
39144
+ if (message.role === "assistant" && Array.isArray(message.content)) {
39145
+ for (const part of message.content) {
39146
+ if (part?.type === "toolCall" && typeof part.id === "string") {
39147
+ openCalls.set(part.id, `${ids[i] ?? ""}\x00${part.id}`);
39148
+ }
38573
39149
  }
38574
- if (message.role !== "user" && message.role !== "toolResult" || !hasAssistantResponse || !Array.isArray(message.content) || !message.content.some(isLargeImagePart)) {
38575
- continue;
39150
+ } else if (message.role === "toolResult" && message.toolCallId) {
39151
+ const key = openCalls.get(message.toolCallId);
39152
+ if (key !== undefined && !args.protectedToolKeys?.has(key) && !args.protectedToolKeys?.has(message.toolCallId)) {
39153
+ completeToolResults.add(i);
38576
39154
  }
38577
- const id = args.stableId(raw, i);
38578
- if (!id || frozenIds.has(id))
38579
- continue;
38580
- const maxTag = args.messageIdToMaxTag.get(id) ?? 0;
38581
- if (maxTag <= args.watermark)
38582
- newlyStrippedIds.push(id);
39155
+ openCalls.delete(message.toolCallId);
38583
39156
  }
38584
39157
  }
39158
+ const selected = [];
39159
+ let hasAssistantResponse = false;
39160
+ let nextTag;
39161
+ for (let i = args.messages.length - 1;i >= 0; i--) {
39162
+ const ownTag = ownTags[i];
39163
+ const positionTag = ownTag > 0 ? ownTag : nextTag;
39164
+ if (ownTag > 0)
39165
+ nextTag = ownTag;
39166
+ const raw = args.messages[i];
39167
+ if (!raw || typeof raw !== "object")
39168
+ continue;
39169
+ const message = raw;
39170
+ if (message.role === "assistant") {
39171
+ hasAssistantResponse = true;
39172
+ continue;
39173
+ }
39174
+ if (message.role !== "user" && message.role !== "toolResult" || message.role === "toolResult" && !completeToolResults.has(i) || !hasAssistantResponse || !Array.isArray(message.content) || !message.content.some(isLargeImagePart))
39175
+ continue;
39176
+ const id = ids[i];
39177
+ if (!id || frozenIds.has(id) || positionTag === undefined || positionTag > args.watermark)
39178
+ continue;
39179
+ if ((args.usagePercentage ?? 0) < 95 && positionTag >= (args.protectedCutoff ?? Infinity))
39180
+ continue;
39181
+ selected.push(id);
39182
+ }
39183
+ return selected;
39184
+ }
39185
+ function stripPiProcessedImages(args) {
39186
+ const frozenIds = getProcessedImageStrippedIds(args.db, args.sessionId);
39187
+ const newlyStrippedIds = planPiProcessedImageIds(args, frozenIds);
38585
39188
  if (newlyStrippedIds.length > 0) {
38586
39189
  const persisted = (args.addIds ?? addProcessedImageStrippedIds)(args.db, args.sessionId, newlyStrippedIds);
38587
39190
  if (persisted) {
@@ -38833,6 +39436,9 @@ function createPiTranscript(source, sessionId, entryIds, options = {}) {
38833
39436
  if (committed)
38834
39437
  return;
38835
39438
  committed = true;
39439
+ for (const index of dirtyMessages) {
39440
+ rewriteNativeAssistantTexts(working[index], source[index], sessionId);
39441
+ }
38836
39442
  for (const [index, parts] of removals) {
38837
39443
  const message = working[index];
38838
39444
  if (message.role !== "assistant" && message.role !== "toolResult" || !Array.isArray(message.content))
@@ -39225,6 +39831,8 @@ function createPiAssistantPart(working, messageIndex, partIndex, markDirty) {
39225
39831
  ...existing,
39226
39832
  arguments: { dropped: sentinelText }
39227
39833
  };
39834
+ } else if (existing?.type === "text") {
39835
+ newContent[partIndex] = { ...existing, text: sentinelText };
39228
39836
  } else {
39229
39837
  newContent[partIndex] = { type: "text", text: sentinelText };
39230
39838
  }
@@ -41043,7 +41651,7 @@ function registerPiContextHandler(pi, baseOptions) {
41043
41651
  const passGuard = { assert: assertCurrentPass, wait: guardAwait };
41044
41652
  guardSqliteTransformPass2({
41045
41653
  assert: assertCurrentPass,
41046
- remainingMs: () => budget.remainingWork()
41654
+ remainingMs: budget.remainingWork
41047
41655
  });
41048
41656
  budget.stage = "schema";
41049
41657
  const toolWireSchema = await guardAwait(loadPiToolWireSchema());
@@ -41053,6 +41661,7 @@ function registerPiContextHandler(pi, baseOptions) {
41053
41661
  let sessionIdForError;
41054
41662
  let lkgPassSnapshot;
41055
41663
  let lkgCompactionOff = baseOptions.compactionOff === true;
41664
+ let lkgOptions = baseOptions;
41056
41665
  let lkgEmergencyRecoveryArmed = false;
41057
41666
  const captureServedEvidence = (...args) => {
41058
41667
  try {
@@ -41083,6 +41692,7 @@ function registerPiContextHandler(pi, baseOptions) {
41083
41692
  rawMessageCount = event.messages.length;
41084
41693
  const fullWireMessageCount = rawMessageCount;
41085
41694
  const options = baseOptions.resolveForProject?.(projectDirectory) ?? baseOptions;
41695
+ lkgOptions = options;
41086
41696
  const deliveryModelKey = resolvePiContextModelKey(ctx);
41087
41697
  if (!budget.sideTurn && deliveryModelKey) {
41088
41698
  const parentId = ctx.sessionManager.getLeafId?.();
@@ -41682,7 +42292,7 @@ function registerPiContextHandler(pi, baseOptions) {
41682
42292
  if (historian && !options.compactionOff && budget.remainingWork() > 0) {
41683
42293
  guardSqliteTransformPass2({
41684
42294
  assert: budget.assert,
41685
- remainingMs: () => budget.remainingWork()
42295
+ remainingMs: budget.remainingWork
41686
42296
  });
41687
42297
  try {
41688
42298
  budget.assert();
@@ -41762,7 +42372,7 @@ function registerPiContextHandler(pi, baseOptions) {
41762
42372
  if (options.autoSearch?.enabled && !options.compactionOff && result.prefixMutationAllowed && budget.remainingWork() > PI_CONTEXT_BUDGET.searchMs) {
41763
42373
  guardSqliteTransformPass2({
41764
42374
  assert: budget.assert,
41765
- remainingMs: () => budget.remainingWork()
42375
+ remainingMs: budget.remainingWork
41766
42376
  });
41767
42377
  try {
41768
42378
  outputMessages = await guardAwait(runAutoSearchHintForPi({
@@ -42172,7 +42782,7 @@ function registerPiContextHandler(pi, baseOptions) {
42172
42782
  budget.assertOutcome();
42173
42783
  guardSqliteTransformPass2({
42174
42784
  assert: budget.assertOutcome,
42175
- remainingMs: () => Math.max(0, PI_CONTEXT_BUDGET.outcomeMs - budget.elapsed())
42785
+ remainingMs: budget.remainingOutcome
42176
42786
  });
42177
42787
  if (!budget.sideTurn && !lkgCompactionOff && lkgPassSnapshot && (rawMessageCount > 0 || result.servedTagNumbers?.size)) {
42178
42788
  let hostEnvelopeSignature;
@@ -42301,14 +42911,38 @@ function registerPiContextHandler(pi, baseOptions) {
42301
42911
  reason: lkgPassSnapshot.replayFailure ?? "lkg_recovery_unproven"
42302
42912
  } : lkgCoordinator.replay(lkgPassSnapshot, (id) => ctx.sessionManager.getEntry?.(id)?.parentId);
42303
42913
  if (replay.ok) {
42914
+ const reasoningPrefixes = recoveryReasoningPrefixes(replay.messages, "content");
42915
+ const entryIds = lkgCoordinator.recoveryEntryIds(lkgPassSnapshot);
42916
+ const recoverySessionId = sessionIdForError;
42917
+ const recoveryTranscript = createPiTranscript(replay.messages, sessionIdForError, entryIds.map((id) => id ?? undefined), {
42918
+ preserveReasoningToolArcs: ctx.model?.api !== "openai-codex-responses" && ctx.model?.api !== "openai-responses",
42919
+ authorizeToolRemoval: (callId) => authorizePiToolRemoval({
42920
+ db: lkgOptions.db,
42921
+ sessionId: recoverySessionId,
42922
+ callId,
42923
+ saved: getNativeReplayState(lkgOptions.db, recoverySessionId).toolInputs,
42924
+ canApply: false
42925
+ })
42926
+ });
42927
+ const drops = getTagsBySession2(lkgOptions.db, sessionIdForError).filter((tag) => tag.status === "dropped");
42928
+ const changed = applyFlushedStatuses(sessionIdForError, lkgOptions.db, buildPersistedDropTargets(recoveryTranscript, drops, {
42929
+ allowReasoningPrefixRewrite: true
42930
+ }), drops);
42931
+ const working = recoveryTranscript.getWorkingMessages();
42932
+ recoveryTranscript.commit();
42933
+ const idsByOutput = new Map(working.map((message, index) => [message, entryIds[index]]));
42934
+ recoveryTranscript.finalizeToolRemovals();
42935
+ if (JSON.stringify(recoveryReasoningPrefixes(replay.messages, "content")) !== JSON.stringify(reasoningPrefixes))
42936
+ throw new DegradedPassRefusalError("lkg-reasoning-prefix-changed", { cause: err });
42937
+ const recoveryIds = replay.messages.map((message) => idsByOutput.get(message) ?? null);
42304
42938
  assertPiRawFallbackFits(replay.messages, rawFallbackLimit, (line) => {
42305
42939
  if (sessionIdForError)
42306
42940
  logPiLkgRecovery(sessionIdForError, line);
42307
- }, err, readPiLkgFitEnvelope(ctx, pi, resolvePiContextModelKey(ctx), sessionDecisionCalibration(baseOptions.db, sessionIdForError), toolWireSchema), replay.measuredPrefix);
42941
+ }, err, readPiLkgFitEnvelope(ctx, pi, resolvePiContextModelKey(ctx), sessionDecisionCalibration(baseOptions.db, sessionIdForError), toolWireSchema), changed ? undefined : replay.measuredPrefix);
42308
42942
  const reason = piStorageErrorReason(err);
42309
42943
  logPiLkgRecovery(sessionIdForError, `${failureLabel} ${reason}: LKG replay served ${replay.messages.length} messages instead of raw ${rawMessageCount}`);
42310
42944
  budget.assertOutcome();
42311
- lkgCoordinator.noteRecovery(lkgPassSnapshot, replay.messages);
42945
+ lkgCoordinator.noteRecovery(lkgPassSnapshot, replay.messages, recoveryIds);
42312
42946
  if (!budget.sideTurn)
42313
42947
  captureServedEvidence(sessionIdForError, replay.messages, {
42314
42948
  recoveryWithoutTagState: true,
@@ -42321,6 +42955,8 @@ function registerPiContextHandler(pi, baseOptions) {
42321
42955
  logPiLkgRecovery(sessionIdForError, `${failureLabel} ${piStorageErrorReason(err)}: LKG unavailable (${replay.reason}); refusing unreduced ${rawMessageCount}-message input`);
42322
42956
  } catch (replayError) {
42323
42957
  budget.assertOutcome();
42958
+ if (replayError instanceof DegradedPassRefusalError)
42959
+ throw replayError;
42324
42960
  if (replayError instanceof PiStorageBusyError)
42325
42961
  throw replayError;
42326
42962
  logPiLkgRecovery(sessionIdForError, `${failureLabel} ${piStorageErrorReason(err)}: LKG replay unavailable (${replayError instanceof Error ? replayError.message : String(replayError)}); refusing unreduced ${rawMessageCount}-message input`);
@@ -42415,7 +43051,7 @@ function resolveHistoryBudgetTokensForPi(args) {
42415
43051
  }) / 100) * historyBudgetPercentage);
42416
43052
  }
42417
43053
  function startPiCompartmentLeaseRenewal(db, sessionId, holderId) {
42418
- return setInterval(() => {
43054
+ return inRootAsyncContext2(() => setInterval(() => {
42419
43055
  try {
42420
43056
  if (!renewCompartmentLease(db, sessionId, holderId)) {
42421
43057
  sessionLog2(sessionId, "compartment lease renewal failed; publish will be skipped if holder is stale");
@@ -42423,7 +43059,7 @@ function startPiCompartmentLeaseRenewal(db, sessionId, holderId) {
42423
43059
  } catch (err) {
42424
43060
  sessionLog2(sessionId, `compartment lease renewal threw; publish will be skipped if holder is stale (${err instanceof Error ? err.message : String(err)})`);
42425
43061
  }
42426
- }, COMPARTMENT_LEASE_RENEWAL_MS);
43062
+ }, COMPARTMENT_LEASE_RENEWAL_MS));
42427
43063
  }
42428
43064
  function ensureRunnablePiBoundaryForTests(snapshot) {
42429
43065
  if (true) {
@@ -42483,7 +43119,7 @@ function spawnPiHistorianRun(args) {
42483
43119
  const holderId = crypto3.randomUUID();
42484
43120
  const controller = new AbortController;
42485
43121
  historianAbortControllers.set(sessionId, controller);
42486
- const runPromise = withoutSqliteTransformPass2(() => (async () => {
43122
+ const runPromise = inRootAsyncContext2(() => (async () => {
42487
43123
  await new Promise((resolve) => setTimeout(resolve, 0));
42488
43124
  if (controller.signal.aborted)
42489
43125
  return;
@@ -43638,7 +44274,7 @@ async function runPipeline(args) {
43638
44274
  protectedToolTags,
43639
44275
  protectedCutoff: usesTokenProtection ? protectionWindowForPass.cutoff : undefined,
43640
44276
  routine: routineCleanupApplied,
43641
- staleReduceStripEnabled: args.canUseEmptySentinels,
44277
+ staleReduceStripEnabled: true,
43642
44278
  onStaleReduceEdit: recordStaleReduceEdit,
43643
44279
  emergency: args.forceMaterialization === true && args.emergencyCeilingTokens !== undefined && args.emergencyCeilingTokens > 0 ? {
43644
44280
  currentTotalInputTokens: args.contextUsage.inputTokens,
@@ -43654,7 +44290,7 @@ async function runPipeline(args) {
43654
44290
  protectedToolTags,
43655
44291
  protectedCutoff: usesTokenProtection ? protectionWindowForPass.cutoff : undefined,
43656
44292
  routine: true,
43657
- staleReduceStripEnabled: args.canUseEmptySentinels,
44293
+ staleReduceStripEnabled: true,
43658
44294
  onStaleReduceEdit: recordStaleReduceEdit,
43659
44295
  caveman: args.isSubagent ? undefined : args.heuristics.caveman
43660
44296
  }, getActiveTagsBySession2(args.db, args.sessionId), stableIdResolver);
@@ -43840,16 +44476,50 @@ async function runPipeline(args) {
43840
44476
  }
43841
44477
  }
43842
44478
  }
43843
- if (args.canUseEmptySentinels) {
44479
+ {
43844
44480
  const tProcessedImages = performance.now();
43845
44481
  try {
44482
+ const imagePositionTags = new Map;
44483
+ const noteImagePosition = (id, tag) => {
44484
+ if (id && tag > (imagePositionTags.get(id) ?? 0))
44485
+ imagePositionTags.set(id, tag);
44486
+ };
44487
+ for (const [tagNumber, target] of targets) {
44488
+ for (const { message, part } of target.mutationParts ?? []) {
44489
+ if (typeof part === "object" && part !== null && "kind" in part && part.kind === "text")
44490
+ noteImagePosition(message.info.id, tagNumber);
44491
+ }
44492
+ }
44493
+ const imageToolTags = new Map(allTagsForPass.filter((tag) => tag.type === "tool").map((tag) => [
44494
+ `${tag.toolOwnerMessageId ?? ""}\x00${tag.messageId}`,
44495
+ tag.tagNumber
44496
+ ]));
44497
+ const imageToolOwners = new Map;
44498
+ for (let index = 0;index < workingMessages.length; index++) {
44499
+ const message = workingMessages[index];
44500
+ const id = stableIdResolver(message, index);
44501
+ if (message.role === "assistant" && Array.isArray(message.content)) {
44502
+ for (const part of message.content) {
44503
+ if (part.type !== "toolCall")
44504
+ continue;
44505
+ imageToolOwners.set(part.id, id ?? "");
44506
+ noteImagePosition(id, imageToolTags.get(`${id ?? ""}\x00${part.id}`) ?? 0);
44507
+ }
44508
+ } else if (message.role === "toolResult") {
44509
+ noteImagePosition(id, imageToolTags.get(`${imageToolOwners.get(message.toolCallId) ?? ""}\x00${message.toolCallId}`) ?? 0);
44510
+ }
44511
+ }
44512
+ const protectedImageToolKeys = new Set(activeTags.filter((tag) => tag.type === "tool" && protectedToolTags.has(tag.tagNumber)).map((tag) => tag.toolOwnerMessageId ? `${tag.toolOwnerMessageId}\x00${tag.messageId}` : tag.messageId));
43846
44513
  const imageResult = stripPiProcessedImages({
43847
44514
  db: args.db,
43848
44515
  sessionId: args.sessionId,
43849
44516
  messages: workingMessages,
43850
44517
  detect: isCacheBustingPass && !protectedSignedPrefix,
43851
44518
  watermark: getMaxDroppedTagNumber2(args.db, args.sessionId),
43852
- messageIdToMaxTag,
44519
+ messageIdToMaxTag: imagePositionTags,
44520
+ protectedCutoff: usesTokenProtection ? protectionWindowForPass.cutoff : protectedTagNumbersForPass.size > 0 ? Math.min(...protectedTagNumbersForPass) : null,
44521
+ usagePercentage: args.contextUsage.percentage,
44522
+ protectedToolKeys: protectedImageToolKeys,
43853
44523
  stableId: stableIdResolver,
43854
44524
  onFirstApplication: recordFirstApplicationAt
43855
44525
  });
@@ -43999,6 +44669,18 @@ async function runPipeline(args) {
43999
44669
  const deferredHistoryDrainEligible = historyWasConsumedThisPass && materializationSatisfiedThisPass && (deferredHistoryWasPendingAtPassStart || hasPendingMaterializeSignal) && !suppressDeferredHistoryDrain && !casLost;
44000
44670
  let preserveDeferredMaterializationForMarkerDrain = false;
44001
44671
  let pendingForDrain = null;
44672
+ if (!deferredHistoryDrainEligible && (deferredHistoryWasPendingAtPassStart || hasPendingMaterializeSignal)) {
44673
+ try {
44674
+ const pending = getPendingPiCompactionMarkerState(args.db, args.sessionId);
44675
+ if (pending)
44676
+ logPiCompactionMarkerDrainGateOnce(args.sessionId, pending, "drain-not-eligible", {
44677
+ historyConsumed: historyWasConsumedThisPass,
44678
+ materializationSatisfied: materializationSatisfiedThisPass,
44679
+ drainSuppressed: suppressDeferredHistoryDrain,
44680
+ casLost
44681
+ });
44682
+ } catch {}
44683
+ }
44002
44684
  if (deferredHistoryDrainEligible) {
44003
44685
  try {
44004
44686
  args.assertCurrentPass?.();
@@ -44073,7 +44755,7 @@ async function runPipeline(args) {
44073
44755
  if (outcome.kind === "waiting-for-entry") {
44074
44756
  suppressDeferredHistoryDrain = true;
44075
44757
  preserveDeferredMaterializationForMarkerDrain = true;
44076
- } else if (outcome.kind === "retryable-failure") {} else if (markerEquivalent && clearPendingPiCompactionMarkerStateIf(args.db, args.sessionId, pending)) {
44758
+ } else if (outcome.kind === "retryable-failure") {} else if (markerEquivalent && clearPendingPiCompactionMarkerStateIf(args.db, args.sessionId, pending, outcome.kind === "stale-skip" ? outcome.reason : outcome.kind)) {
44077
44759
  consumeDeferredHistoryRefresh(args.sessionId);
44078
44760
  } else if (markerEquivalent) {
44079
44761
  casLost = true;
@@ -44291,12 +44973,6 @@ function applyNoteNudges(args) {
44291
44973
  for (const anchor of noteAnchors) {
44292
44974
  appendReminderToUserMessageByIdPi(messages, replayMessageIdByIndex, anchor.messageId, anchor.text);
44293
44975
  }
44294
- const autoSearchDecisions = args.postTransformSnapshot?.autoSearchDecisions ?? getAutoSearchHintDecisions(db, sessionId);
44295
- for (const decision of autoSearchDecisions) {
44296
- if (decision.decision === "hint" && getUnpublishedAutoSearchNoHint(sessionId, decision.messageId) === undefined) {
44297
- appendReminderToUserMessageByIdPi(messages, replayMessageIdByIndex, decision.messageId, decision.text);
44298
- }
44299
- }
44300
44976
  logTransformTiming(sessionId, "stickyReplayDecisions", tStickyReplay);
44301
44977
  const latestUser = findLatestUserMessageIdPi(messages, messageIdByIndex);
44302
44978
  const latestUserId = latestUser?.messageId ?? null;
@@ -44306,7 +44982,7 @@ function applyNoteNudges(args) {
44306
44982
  userMessageIds: [...messageIdByIndex].filter(([index]) => messages[index]?.role === "user").map(([, id]) => id),
44307
44983
  anchorMessageId: latestUserId,
44308
44984
  isLiveTail: latestUser?.index === messages.length - 1,
44309
- isCacheBustingPass: args.isCacheBusting
44985
+ prefixRewriteCoversAnchor: false
44310
44986
  });
44311
44987
  const noteReadStillVisible = hasVisibleNoteReadCallPi(messages);
44312
44988
  const deferredNoteText = args.postTransformSnapshot?.noteTriggerPending === false ? null : peekNoteNudgeText(db, sessionId, latestUserId, projectIdentity, noteReadStillVisible, eligibility);
@@ -44330,6 +45006,12 @@ function applyNoteNudges(args) {
44330
45006
  sessionLog2(sessionId, `Pi note-nudge delivery skipped wire append: ${outcome.kind}`);
44331
45007
  }
44332
45008
  }
45009
+ const autoSearchDecisions = args.postTransformSnapshot?.autoSearchDecisions ?? getAutoSearchHintDecisions(db, sessionId);
45010
+ for (const decision of autoSearchDecisions) {
45011
+ if (decision.decision === "hint" && getUnpublishedAutoSearchNoHint(sessionId, decision.messageId) === undefined) {
45012
+ appendReminderToUserMessageByIdPi(messages, replayMessageIdByIndex, decision.messageId, decision.text);
45013
+ }
45014
+ }
44333
45015
  if (args.isCacheBusting) {
44334
45016
  const visibleIds = new Set(messageIdByIndex.values());
44335
45017
  const realMessageCount = Math.max(0, messages.length - (args.syntheticLeadingCount ?? 0));
@@ -44588,6 +45270,7 @@ function createPiHistorianClient(args) {
44588
45270
  model: modelOverride?.model ?? args.model,
44589
45271
  fallbackModels: modelOverride ? undefined : args.fallbackModels,
44590
45272
  timeoutMs: args.timeoutMs,
45273
+ maxOutputTokens: args.maxOutputTokens,
44591
45274
  cwd: args.directory,
44592
45275
  thinkingLevel: modelOverride ? modelOverride.qualifier : args.thinkingLevel,
44593
45276
  accountingSessionId: args.accountingSessionId,
@@ -45002,6 +45685,7 @@ Recomp started. Rebuilding the compressed history from raw session history now;
45002
45685
  systemPrompt: withContentLanguageDirective(COMPARTMENT_STRUCTURAL_SYSTEM_PROMPT, currentDeps.language, { preserveUserQuotes: true }),
45003
45686
  fallbackModels: currentDeps.historianFallbacks,
45004
45687
  timeoutMs: currentDeps.historianTimeoutMs,
45688
+ maxOutputTokens: currentDeps.historianMaxOutputTokens,
45005
45689
  thinkingLevel: currentDeps.historianThinkingLevel,
45006
45690
  directory: cwd,
45007
45691
  accountingSessionId: sessionId,
@@ -46227,14 +46911,14 @@ class StatusDialogComponent {
46227
46911
  this.detailRefresh = new PiStatusDetailRefresh(props.pi, props.ctx, props.deps, props.sessionId);
46228
46912
  this.detail = this.detailRefresh.refresh();
46229
46913
  openStatusDialogs.add(this);
46230
- this.refreshTimer = setInterval(() => {
46914
+ this.refreshTimer = inRootAsyncContext2(() => setInterval(() => {
46231
46915
  if (this.closed)
46232
46916
  return;
46233
46917
  try {
46234
46918
  this.detail = this.detailRefresh.refresh();
46235
46919
  this.props.tui.requestRender();
46236
46920
  } catch {}
46237
- }, REFRESH_INTERVAL_MS);
46921
+ }, REFRESH_INTERVAL_MS));
46238
46922
  }
46239
46923
  handleInput(data) {
46240
46924
  if (this.closed)
@@ -47149,7 +47833,7 @@ No runnable wrapup boundary is available yet; wrapped up 0 messages into 0 compa
47149
47833
  }
47150
47834
  return true;
47151
47835
  };
47152
- const renewal = setInterval(() => {
47836
+ const renewal = inRootAsyncContext2(() => setInterval(() => {
47153
47837
  try {
47154
47838
  renewWrapupMarker({
47155
47839
  lastCompartmentEnd: getLastCompartmentEndMessage(deps.db, sessionId)
@@ -47157,7 +47841,7 @@ No runnable wrapup boundary is available yet; wrapped up 0 messages into 0 compa
47157
47841
  } catch (err) {
47158
47842
  sessionLog2(sessionId, `[pi] /ctx-wrapup marker renewal failed: ${err instanceof Error ? err.message : String(err)}`);
47159
47843
  }
47160
- }, 60000);
47844
+ }, 60000));
47161
47845
  const stopRenewal = () => clearInterval(renewal);
47162
47846
  deps.shutdownSignal?.addEventListener("abort", stopRenewal, { once: true });
47163
47847
  try {
@@ -47232,13 +47916,13 @@ Chunk ${chunkIndex}: wrapping messages ${plan.snapshot.offset}-${plan.snapshot.e
47232
47916
  break;
47233
47917
  }
47234
47918
  const leaseHolder = leaseResult.holderId;
47235
- const leaseRenewal = setInterval(() => {
47919
+ const leaseRenewal = inRootAsyncContext2(() => setInterval(() => {
47236
47920
  try {
47237
47921
  renewCompartmentLease(deps.db, sessionId, leaseHolder);
47238
47922
  } catch (err) {
47239
47923
  sessionLog2(sessionId, `[pi] /ctx-wrapup compartment lease renewal failed: ${err instanceof Error ? err.message : String(err)}`);
47240
47924
  }
47241
- }, COMPARTMENT_LEASE_RENEWAL_MS);
47925
+ }, COMPARTMENT_LEASE_RENEWAL_MS));
47242
47926
  const stopLeaseRenewal = () => clearInterval(leaseRenewal);
47243
47927
  deps.shutdownSignal?.addEventListener("abort", stopLeaseRenewal, {
47244
47928
  once: true
@@ -47272,6 +47956,7 @@ Chunk ${chunkIndex}: wrapping messages ${plan.snapshot.offset}-${plan.snapshot.e
47272
47956
  }).snapshot,
47273
47957
  currentContextLimit: contextLimit,
47274
47958
  historianTimeoutMs: deps.historianTimeoutMs,
47959
+ maxOutputTokens: deps.historianMaxOutputTokens,
47275
47960
  thinkingLevel: deps.historianThinkingLevel,
47276
47961
  memoryEnabled: deps.memoryEnabled,
47277
47962
  autoPromote: deps.autoPromote,
@@ -47403,9 +48088,9 @@ function readPiWrapupBranchEntries(ctx) {
47403
48088
  function observePiMessageActivity(db, sessionId, timestamp) {
47404
48089
  observeSessionActivity(db, sessionId, timestamp);
47405
48090
  }
47406
- function latestPiMessageTime(path) {
48091
+ async function latestPiMessageTime(path) {
47407
48092
  let latest = 0;
47408
- for (const entry of sessionEntries(path)) {
48093
+ for await (const entry of asyncSessionEntries(path)) {
47409
48094
  const row = entry;
47410
48095
  if (row.type === "message" && typeof row.message?.timestamp === "number")
47411
48096
  latest = Math.max(latest, row.message.timestamp);
@@ -47413,12 +48098,12 @@ function latestPiMessageTime(path) {
47413
48098
  return latest || undefined;
47414
48099
  }
47415
48100
  async function backfillPiSessionActivity(db, harness, paths) {
47416
- await backfillSessionActivity(db, harness, (sessionId) => {
48101
+ await backfillSessionActivity(db, harness, async (sessionId) => {
47417
48102
  const path = paths.get(sessionId);
47418
48103
  if (!path)
47419
48104
  return;
47420
48105
  try {
47421
- return latestPiMessageTime(path);
48106
+ return await latestPiMessageTime(path);
47422
48107
  } catch {
47423
48108
  return;
47424
48109
  }
@@ -47625,6 +48310,18 @@ async function bootPiRuntimeWithDeadline(args) {
47625
48310
  return { status: "timed_out", lateAdoption };
47626
48311
  }
47627
48312
 
48313
+ // src/pi-historian-command-options.ts
48314
+ function buildPiHistorianCommandOptions(historian, config) {
48315
+ return {
48316
+ historianModel: historian?.model,
48317
+ historianChunkTokens: historian?.historianChunkTokens ?? deriveHistorianChunkTokens(resolveHistorianContextLimit(historian?.model)),
48318
+ historianFallbacks: historian?.fallbackModels,
48319
+ historianTimeoutMs: historian?.timeoutMs ?? config.historian_timeout_ms,
48320
+ historianMaxOutputTokens: historian?.maxOutputTokens,
48321
+ historianThinkingLevel: historian?.thinkingLevel
48322
+ };
48323
+ }
48324
+
47628
48325
  // src/stale-build-notice.ts
47629
48326
  function bindStaleBuildNotice(ctx, moduleUrl) {
47630
48327
  registerStalePluginBuildHost({
@@ -48754,11 +49451,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
48754
49451
  registerCtxRecompCommand(pi, {
48755
49452
  db,
48756
49453
  runner: recompRunner,
48757
- historianModel: bootProjectDeps.historianConfig?.model,
48758
- historianChunkTokens: deriveHistorianChunkTokens(resolveHistorianContextLimit(bootProjectDeps.historianConfig?.model)),
48759
- historianFallbacks: bootProjectDeps.historianConfig?.fallbackModels,
48760
- historianTimeoutMs: bootProjectDeps.config.historian_timeout_ms,
48761
- historianThinkingLevel: bootProjectDeps.historianConfig?.thinkingLevel,
49454
+ ...buildPiHistorianCommandOptions(bootProjectDeps.historianConfig, bootProjectDeps.config),
48762
49455
  language: bootProjectDeps.config.language,
48763
49456
  memoryEnabled: bootProjectDeps.config.memory.enabled,
48764
49457
  autoPromote: bootProjectDeps.config.memory.auto_promote,
@@ -48770,11 +49463,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
48770
49463
  return {
48771
49464
  db,
48772
49465
  runner: recompRunner,
48773
- historianModel: historian?.model,
48774
- historianChunkTokens: historian?.historianChunkTokens ?? deriveHistorianChunkTokens(resolveHistorianContextLimit(historian?.model)),
48775
- historianFallbacks: historian?.fallbackModels,
48776
- historianTimeoutMs: historian?.timeoutMs ?? current.config.historian_timeout_ms,
48777
- historianThinkingLevel: historian?.thinkingLevel,
49466
+ ...buildPiHistorianCommandOptions(historian, current.config),
48778
49467
  language: current.config.language,
48779
49468
  memoryEnabled: current.config.memory.enabled,
48780
49469
  autoPromote: fresh.memory.auto_promote,
@@ -48787,11 +49476,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
48787
49476
  shutdownSignal: commandLifecycleController.signal,
48788
49477
  db,
48789
49478
  runner: wrapupRunner,
48790
- historianModel: bootProjectDeps.historianConfig?.model,
48791
- historianChunkTokens: deriveHistorianChunkTokens(resolveHistorianContextLimit(bootProjectDeps.historianConfig?.model)),
48792
- historianFallbacks: bootProjectDeps.historianConfig?.fallbackModels,
48793
- historianTimeoutMs: bootProjectDeps.config.historian_timeout_ms,
48794
- historianThinkingLevel: bootProjectDeps.historianConfig?.thinkingLevel,
49479
+ ...buildPiHistorianCommandOptions(bootProjectDeps.historianConfig, bootProjectDeps.config),
48795
49480
  language: bootProjectDeps.config.language,
48796
49481
  memoryEnabled: bootProjectDeps.config.memory.enabled,
48797
49482
  autoPromote: bootProjectDeps.config.memory.auto_promote,
@@ -48806,11 +49491,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
48806
49491
  return {
48807
49492
  db,
48808
49493
  runner: wrapupRunner,
48809
- historianModel: historian?.model,
48810
- historianChunkTokens: historian?.historianChunkTokens ?? deriveHistorianChunkTokens(resolveHistorianContextLimit(historian?.model)),
48811
- historianFallbacks: historian?.fallbackModels,
48812
- historianTimeoutMs: historian?.timeoutMs ?? current.config.historian_timeout_ms,
48813
- historianThinkingLevel: historian?.thinkingLevel,
49494
+ ...buildPiHistorianCommandOptions(historian, current.config),
48814
49495
  language: current.config.language,
48815
49496
  memoryEnabled: current.config.memory.enabled,
48816
49497
  autoPromote: fresh.memory.auto_promote,