@cortexkit/pi-magic-context 0.46.1 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -33,6 +33,7 @@ import {
33
33
  hasTrustedAbsoluteWall,
34
34
  applyProvenInputFloor,
35
35
  formatWindowDerivationLine,
36
+ formatWindowSource,
36
37
  isSaneLimit,
37
38
  resolveOutputReserve,
38
39
  getSdkWindowGeometry,
@@ -46,12 +47,14 @@ import {
46
47
  BoundedSessionMap,
47
48
  MAX_EXECUTE_THRESHOLD,
48
49
  escalationBands,
50
+ decodeMergedReasoningParts,
49
51
  HYGIENE_PROVIDER_UNITS_VERSION,
50
52
  sessionDecisionCalibration,
51
53
  transitionSessionHygieneUnits,
52
54
  computeProtectionWindow,
53
55
  readEpochFloorSnapshot,
54
56
  getProtectionWindowForSession,
57
+ ensureSessionMetaRow,
55
58
  persistCachedM0,
56
59
  clearCachedM0M1,
57
60
  isRustMarkerAdmissionFenced,
@@ -72,6 +75,7 @@ import {
72
75
  resolveCompactionModeRecord,
73
76
  getCompactionModeRecord,
74
77
  setCompactionModeRecord,
78
+ getProtectedTailDrainBudgetSkip,
75
79
  reserveProtectedTailDrainTokens,
76
80
  clearEmergencyDrainLatch,
77
81
  recordHistorianDrainFailure,
@@ -135,23 +139,13 @@ import {
135
139
  setSessionWorkMetrics,
136
140
  getSessionWorkMetrics,
137
141
  resolveEpochFloorForPass,
138
- buildCanonicalChunkTextFromFts,
139
- buildCompartmentSummaryFallbackText,
140
- canonicalizeInMemoryChunkTextForEmbedding,
141
- chunkCanonicalText,
142
- chunkEmbeddingWindowsAreCurrent,
143
- replaceCompartmentChunkEmbeddings,
144
- deleteChunkEmbedBackoffForSession,
145
- chunkWindowSourceKey,
146
- readSessionActivity,
147
- observeSessionActivity,
148
- backfillSessionActivity,
142
+ registerAutoSearchWork,
143
+ cancelAutoSearchWork,
149
144
  getOrCreateSessionMeta,
150
145
  updateSessionMeta,
151
146
  advanceToolReclaimWatermark,
152
147
  retryPendingSessionCleanups,
153
148
  retryPendingRustSessionCleanupsForProject,
154
- recordSessionProjectIdentity,
155
149
  embedPauseBySession,
156
150
  embedRunStateBySession,
157
151
  autoEmbedAttemptedBySession,
@@ -164,8 +158,6 @@ import {
164
158
  releaseCompartmentLease,
165
159
  releaseCompartmentLeaseBestEffort,
166
160
  isCompartmentLeaseHeld,
167
- clearCompressionDepth,
168
- clearCompressionDepthRange,
169
161
  isNoContentCompartment,
170
162
  HAS_COMPARTMENT_CONTENT_SQL,
171
163
  queueM0Mutation,
@@ -184,14 +176,6 @@ import {
184
176
  escapeXmlContent,
185
177
  getUncoveredCompartmentEndThrough,
186
178
  isPartialCompartmentEnd,
187
- getMessageIndexSourceIdentity,
188
- isMessageIndexSourceCurrent,
189
- getLastIndexedOrdinal,
190
- getMessageIndexReconciliationStartOrdinal,
191
- isMessageIndexReconciledThrough,
192
- indexSingleMessage,
193
- indexMessagesAfterOrdinal,
194
- sweepOrphanedOpenCodeMessageIndexes,
195
179
  readProjectDocsCanonical,
196
180
  providerResponseFailed,
197
181
  isSuccessfulProviderCompletion,
@@ -215,6 +199,7 @@ import {
215
199
  getSlot,
216
200
  dropSlot,
217
201
  noteEntry,
202
+ TOOL_SWEEP_SCOPED_MARKER,
218
203
  encodePiContentDecision,
219
204
  decodePiContentDecision,
220
205
  getPiContentDecisions,
@@ -223,31 +208,15 @@ import {
223
208
  saveNativeToolInputs,
224
209
  addNativeReasoningIds,
225
210
  copySessionStateForClone,
211
+ countHistorianRuns,
212
+ recordHistorianRun,
213
+ summarizeImportance,
214
+ tallyFactsByCategory,
226
215
  queueMemoryMutation,
227
216
  getMemoryMutationsForRender,
228
217
  getMemoryMutationsForRenderByProjects,
229
218
  getMaxMemoryMutationId,
230
219
  getMaxMemoryMutationIdForProjects,
231
- getNotes,
232
- addNote,
233
- getSessionNotes,
234
- getPendingSmartNotes,
235
- getReadySmartNotes,
236
- markNoteReady,
237
- markNoteChecked,
238
- PRIMER_CANDIDATE_TTL_MS,
239
- PRIMER_CANDIDATE_MAX_AGE_MS,
240
- primerOccurrenceKey,
241
- primerOccurrenceUtcDay,
242
- insertPrimerCandidates,
243
- updatePrimerCandidateEmbedding,
244
- getPrimerCandidatesByIds,
245
- getPrimerCandidatesForPromotion,
246
- countPrimerCandidatesForProject,
247
- getActivePrimers,
248
- createPrimer,
249
- updatePrimerSupport,
250
- updatePrimerAnswer,
251
220
  GLOBAL_USER_PROFILE_PROJECT_PATH,
252
221
  getProjectState,
253
222
  bumpProjectUserProfileVersion,
@@ -258,7 +227,6 @@ import {
258
227
  noteTouchedAt,
259
228
  noteNudgePickIndex,
260
229
  formatNoteNudge,
261
- setEmbeddingSessionBusy,
262
230
  promptSurfaceHashMaterial,
263
231
  createPromptSurfaceRuntime,
264
232
  createPromptSurfaceGuidanceEpochCache,
@@ -273,25 +241,6 @@ import {
273
241
  convertPiAssistantEntryById,
274
242
  convertEntriesToRawMessagePage,
275
243
  countPiRawMessages,
276
- computeCueContentHash,
277
- hasMuralCueColumns,
278
- getMuralCueState,
279
- memoryNeedsCue,
280
- setMuralCue,
281
- recordMuralCueRejection,
282
- invalidateMemory,
283
- hasMemoryShareableColumn,
284
- hasMemoryClassifiedAtColumn,
285
- getUnclassifiedMemoryIds,
286
- insertMemory,
287
- getMemoryByHash,
288
- getMemoriesByProject,
289
- getMemoriesByProjects,
290
- getMaxMemoryIdForProjects,
291
- getMemoryById,
292
- setMemoryClassification,
293
- archiveMemory,
294
- getMemoryCountsByStatus,
295
244
  USER_MEMORY_CANDIDATE_TTL_MS,
296
245
  insertUserMemoryCandidates,
297
246
  getUserMemoryCandidates,
@@ -315,48 +264,9 @@ import {
315
264
  peekCurateCategoryScope,
316
265
  beginCurateCategoryRun,
317
266
  curateTaskStateAfterSuccess,
318
- cosineSimilarity,
319
- drainBackgroundBatches,
320
- GIT_SWEEP_LEASE_RENEWAL_MS,
321
- acquireGitSweepLease,
322
- renewGitSweepLease,
323
- markGitSweepSuccessAndRelease,
324
- parkGitSweepNonIndexable,
325
- releaseGitSweepLease,
326
- describeShadowBackfillWriteRefusal,
327
- contentSha256,
328
- drainProjectEmbeddingIdentityMaintenance,
329
- drainStaleEmbeddingIdentitiesForProject,
330
- enqueueShadowEmbeddingItems,
331
- getProjectEmbeddingSnapshot,
332
- getProjectChunkEmbeddingModelId,
333
- getProjectEmbeddingMaxInputTokens,
334
- embedTextForProject,
335
- embedBatchForProject,
336
- embedItemsForProject,
337
- embedUnembeddedMemoriesForProject,
338
- drainCommitBacklogForProject,
339
- embedSessionCompartmentChunks,
340
- getEmbeddingCoverageStatus,
341
- getAutoEmbeddingSessionCoverage,
342
- promoteSessionFactsDurable,
343
- embedPromotedFacts,
344
- recordMemoryMapping,
345
- recordMemoryVerifications,
346
- getUnmappedMemoryIds,
347
- clearMemoryVerifications,
348
- getMemoryVerifications,
349
- resolveGitTopLevel,
350
- readGitHead,
351
- readGitChangedFilesSince,
352
- readGitFileChangeTimesSince,
353
- verificationFileExists,
354
- normalizeVerificationFiles,
355
267
  isDirectiveShapedProjectRule,
356
268
  takeCurateSafetyRefusalCount,
357
269
  wakePlaneStatus,
358
- indexCommitsForProject,
359
- embedUnembeddedCommits,
360
270
  loadPiConfig,
361
271
  loadPiConfigDetailed,
362
272
  unregisterPiProjectEmbeddings,
@@ -395,7 +305,6 @@ import {
395
305
  trimUserMemoriesToBudget,
396
306
  renderMemoryBlockV2,
397
307
  stripMemoryMuralBlock,
398
- unifiedSearch,
399
308
  rememberTodowriteToolCallTodos,
400
309
  parseTodos,
401
310
  setTodoSnapshot,
@@ -403,7 +312,7 @@ import {
403
312
  registerTodoStateLifecycle,
404
313
  syncCtxMemoryToolEnabled,
405
314
  registerMagicContextTools
406
- } from "./index-ykb8kkm3.js";
315
+ } from "./index-k3ezsjng.js";
407
316
  import {
408
317
  isValidLanguageCode,
409
318
  withContentLanguageDirective,
@@ -418,7 +327,7 @@ import {
418
327
  connectionFileExists,
419
328
  formatSynapseLaneDescriptor,
420
329
  computeNormalizedHash
421
- } from "./index-r01b1b78.js";
330
+ } from "./index-vgcemg0n.js";
422
331
  import {
423
332
  canonicalModelIdentity,
424
333
  piModelRefToCanonical,
@@ -427,7 +336,7 @@ import {
427
336
  ompModelRefToCanonical,
428
337
  resolveModelRefForOmp,
429
338
  normalizeProtectedToolName
430
- } from "./index-phh7e055.js";
339
+ } from "./index-spk5vrhy.js";
431
340
  import {
432
341
  FAIL_CLOSED_DOCTOR_COMMAND,
433
342
  formatFailClosedBlockingMessage,
@@ -435,6 +344,25 @@ import {
435
344
  isFailClosedBlockingError,
436
345
  shouldBypassFailClosedBlock,
437
346
  createFailClosedController,
347
+ piHarnessKindFromExecutable,
348
+ getErrorMessage,
349
+ describeError,
350
+ configureContextDatabasePragmas,
351
+ startBootDeadline,
352
+ getSchemaFenceRejection,
353
+ getMigrationOnOpenRefusal,
354
+ LATEST_SUPPORTED_VERSION,
355
+ getDatabasePath,
356
+ getPersistedSchemaVersion,
357
+ setSqlitePragmaConfig,
358
+ applySqliteTuningPragmas,
359
+ runSqliteOptimize,
360
+ openDatabase,
361
+ openCurrentDatabase,
362
+ openDatabaseAsync,
363
+ closeDatabase
364
+ } from "./index-kztsbkqq.js";
365
+ import {
438
366
  setHomeProjectPermission,
439
367
  ProjectIdentityError,
440
368
  resolveProjectIdentityStrict,
@@ -442,7 +370,6 @@ import {
442
370
  resolveProjectIdentity,
443
371
  isUsableProjectIdentity,
444
372
  resolveProjectIdentityForSession,
445
- piHarnessKindFromExecutable,
446
373
  V2_MEMORY_CATEGORIES,
447
374
  unescapeXml,
448
375
  parseCompartmentOutput,
@@ -454,26 +381,11 @@ import {
454
381
  sourceNameForMemory,
455
382
  computeWorkspaceEpochFingerprint,
456
383
  beginBootQuietPeriod,
457
- scheduleAfterBootQuiet,
458
- getErrorMessage,
459
- describeError,
460
- configureContextDatabasePragmas,
461
- startBootDeadline,
462
- getSchemaFenceRejection,
463
- getMigrationOnOpenRefusal,
464
- LATEST_SUPPORTED_VERSION,
465
- getDatabasePath,
466
- getPersistedSchemaVersion,
467
- setSqlitePragmaConfig,
468
- applySqliteTuningPragmas,
469
- runSqliteOptimize,
470
- openDatabase,
471
- openCurrentDatabase,
472
- openDatabaseAsync,
473
- closeDatabase
474
- } from "./index-4vfrjwf6.js";
384
+ scheduleAfterBootQuiet
385
+ } from "./index-xpbh1ypk.js";
475
386
  import {
476
387
  detectThinkingBindingMismatch,
388
+ detectLatestTurnThinkingMismatch,
477
389
  isPrefixBoundThinkingModel,
478
390
  detectOverflow,
479
391
  UNKNOWN_FIT_RATIO,
@@ -526,6 +438,7 @@ import {
526
438
  updateTagTokenCount,
527
439
  getPersistedToolTagAccounting,
528
440
  getAllStatusTagTokenTotalsFlat,
441
+ getReasoningTokenEstimatesByMessage,
529
442
  updateTagInputTokenCount,
530
443
  tagTokenCountIsNull,
531
444
  backfillTagTokenCounts,
@@ -575,6 +488,7 @@ import {
575
488
  isEditTool,
576
489
  applyEditMarkerToInput,
577
490
  modelAcceptsEmptyContent,
491
+ isAnthropicFamilyRoute,
578
492
  variantChangeBustsProviderCache,
579
493
  SKELETON_REAL_INPUT_MAX_BYTES,
580
494
  toolInputStringBytes,
@@ -594,7 +508,7 @@ import {
594
508
  getRawSessionTagKeysThrough2,
595
509
  getLegacyProtectedTailStartOrdinal2,
596
510
  readSessionChunk2
597
- } from "./index-xvw8zptx.js";
511
+ } from "./index-7s8vw2y0.js";
598
512
  import {
599
513
  textMentionsRecentCommit,
600
514
  hasMeaningfulUserText2,
@@ -608,6 +522,7 @@ import {
608
522
  normalizeText
609
523
  } from "./index-266qrgss.js";
610
524
  import {
525
+ stableStringify,
611
526
  closeQuietly,
612
527
  isKnownAutocommit,
613
528
  observeTemporalDecisions,
@@ -621,10 +536,118 @@ import {
621
536
  } from "./index-td885aah.js";
622
537
  import {
623
538
  Database,
539
+ getSqliteDatabasePath,
624
540
  withSqliteBackgroundWriter,
625
541
  withSqliteTransformPass,
626
- withoutSqliteTransformPass
627
- } from "./index-wv28nczk.js";
542
+ withoutSqliteTransformPass,
543
+ guardSqliteTransformPass,
544
+ assertTransformWrite,
545
+ withAsyncPrivilegedWriter
546
+ } from "./index-0qejsd7f.js";
547
+ import {
548
+ buildCanonicalChunkTextFromFts,
549
+ buildCompartmentSummaryFallbackText,
550
+ canonicalizeInMemoryChunkTextForEmbedding,
551
+ chunkCanonicalText,
552
+ chunkEmbeddingWindowsAreCurrent,
553
+ replaceCompartmentChunkEmbeddings,
554
+ deleteChunkEmbedBackoffForSession,
555
+ chunkWindowSourceKey,
556
+ readSessionActivity,
557
+ observeSessionActivity,
558
+ backfillSessionActivity,
559
+ recordSessionProjectIdentity,
560
+ clearCompressionDepth,
561
+ clearCompressionDepthRange,
562
+ getMessageIndexSourceIdentity,
563
+ isMessageIndexSourceCurrent,
564
+ getLastIndexedOrdinal,
565
+ getMessageIndexReconciliationStartOrdinal,
566
+ isMessageIndexReconciledThrough,
567
+ indexSingleMessage,
568
+ indexMessagesAfterOrdinal,
569
+ sweepOrphanedOpenCodeMessageIndexes,
570
+ getNotes,
571
+ addNote,
572
+ getSessionNotes,
573
+ getPendingSmartNotes,
574
+ getReadySmartNotes,
575
+ markNoteReady,
576
+ markNoteChecked,
577
+ PRIMER_CANDIDATE_TTL_MS,
578
+ PRIMER_CANDIDATE_MAX_AGE_MS,
579
+ primerOccurrenceKey,
580
+ primerOccurrenceUtcDay,
581
+ insertPrimerCandidates,
582
+ updatePrimerCandidateEmbedding,
583
+ getPrimerCandidatesByIds,
584
+ getPrimerCandidatesForPromotion,
585
+ countPrimerCandidatesForProject,
586
+ getActivePrimers,
587
+ createPrimer,
588
+ updatePrimerSupport,
589
+ updatePrimerAnswer,
590
+ setEmbeddingSessionBusy,
591
+ isEmbeddingHostBusy,
592
+ computeCueContentHash,
593
+ hasMuralCueColumns,
594
+ getMuralCueState,
595
+ memoryNeedsCue,
596
+ setMuralCue,
597
+ recordMuralCueRejection,
598
+ invalidateMemory,
599
+ hasMemoryShareableColumn,
600
+ hasMemoryClassifiedAtColumn,
601
+ getUnclassifiedMemoryIds,
602
+ insertMemory,
603
+ getMemoryByHash,
604
+ getMemoriesByProject,
605
+ getMemoriesByProjects,
606
+ getMaxMemoryIdForProjects,
607
+ getMemoryById,
608
+ setMemoryClassification,
609
+ archiveMemory,
610
+ getMemoryCountsByStatus,
611
+ cosineSimilarity,
612
+ drainBackgroundBatches,
613
+ GIT_SWEEP_LEASE_RENEWAL_MS,
614
+ acquireGitSweepLease,
615
+ renewGitSweepLease,
616
+ markGitSweepSuccessAndRelease,
617
+ parkGitSweepNonIndexable,
618
+ releaseGitSweepLease,
619
+ describeShadowBackfillWriteRefusal,
620
+ contentSha256,
621
+ drainProjectEmbeddingIdentityMaintenance,
622
+ drainStaleEmbeddingIdentitiesForProject,
623
+ enqueueShadowEmbeddingItems,
624
+ getProjectEmbeddingSnapshot,
625
+ getProjectChunkEmbeddingModelId,
626
+ getProjectEmbeddingMaxInputTokens,
627
+ embedTextForProject,
628
+ embedBatchForProject,
629
+ embedItemsForProject,
630
+ embedUnembeddedMemoriesForProject,
631
+ drainCommitBacklogForProject,
632
+ embedSessionCompartmentChunks,
633
+ getEmbeddingCoverageStatus,
634
+ getAutoEmbeddingSessionCoverage,
635
+ promoteSessionFactsDurable,
636
+ embedPromotedFacts,
637
+ recordMemoryMapping,
638
+ recordMemoryVerifications,
639
+ getUnmappedMemoryIds,
640
+ clearMemoryVerifications,
641
+ getMemoryVerifications,
642
+ resolveGitTopLevel,
643
+ readGitHead,
644
+ readGitChangedFilesSince,
645
+ readGitFileChangeTimesSince,
646
+ verificationFileExists,
647
+ normalizeVerificationFiles,
648
+ indexCommitsForProject,
649
+ embedUnembeddedCommits
650
+ } from "./index-qaa5j277.js";
628
651
  import {
629
652
  pushNotification2
630
653
  } from "./index-q1yc6bs0.js";
@@ -635,6 +658,9 @@ import {
635
658
  readHostSystemPrompt
636
659
  } from "./index-w76x3af7.js";
637
660
  import"./index-n3g5mwa8.js";
661
+ import {
662
+ __toESM
663
+ } from "./index-22jkk6wg.js";
638
664
  import {
639
665
  setStoragePrivatePermissionEnforcement,
640
666
  ensureStorageDirectorySync,
@@ -645,9 +671,6 @@ import {
645
671
  logSlowWriteTransaction,
646
672
  WriteTransactionSteps
647
673
  } from "./index-2p1e8706.js";
648
- import {
649
- __toESM
650
- } from "./index-22jkk6wg.js";
651
674
 
652
675
  // src/index.ts
653
676
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
@@ -798,6 +821,10 @@ function sampleLiveConfig(boot, fresh) {
798
821
  let value = fresh;
799
822
  for (const part of parts)
800
823
  value = value?.[part];
824
+ if (value === undefined) {
825
+ removeLeaf(result, parts);
826
+ continue;
827
+ }
801
828
  let target = result;
802
829
  for (const part of parts.slice(0, -1)) {
803
830
  const child = target[part];
@@ -811,6 +838,26 @@ function sampleLiveConfig(boot, fresh) {
811
838
  }
812
839
  return result;
813
840
  }
841
+ function removeLeaf(root, parts) {
842
+ const parents = parts.slice(0, -1);
843
+ let node = root;
844
+ for (const part of parents) {
845
+ const child = node[part];
846
+ if (!child || typeof child !== "object" || Array.isArray(child))
847
+ return;
848
+ node = child;
849
+ }
850
+ const leaf = parts.at(-1);
851
+ if (leaf === undefined || !Object.hasOwn(node, leaf))
852
+ return;
853
+ let target = root;
854
+ for (const part of parents) {
855
+ const copy = { ...target[part] };
856
+ target[part] = copy;
857
+ target = copy;
858
+ }
859
+ delete target[leaf];
860
+ }
814
861
  function changedLiveKeys(previous, next) {
815
862
  const at = (config, path) => path.split(".").reduce((node, part) => node?.[part], config);
816
863
  return LIVE_RELOAD_CONFIG_PATHS.filter((path) => JSON.stringify(at(previous, path)) !== JSON.stringify(at(next, path)));
@@ -3590,9 +3637,11 @@ function countCompartmentsSince(db, projectPath, since) {
3590
3637
  }
3591
3638
  function countProjectSessionsSince(db, projectPath, since) {
3592
3639
  const row = since === null ? db.prepare("SELECT COUNT(*) AS cnt FROM session_projects WHERE project_path = ?").get(projectPath) : db.prepare(`SELECT COUNT(*) AS cnt FROM session_projects sp
3593
- JOIN schema_migrations_meta activity
3640
+ LEFT JOIN schema_migrations_meta activity
3594
3641
  ON activity.key = 'retrospective_activity:' || sp.session_id
3595
- WHERE sp.project_path = ? AND CAST(activity.value AS INTEGER) > ?`).get(projectPath, since);
3642
+ LEFT JOIN message_history_index history ON history.session_id = sp.session_id
3643
+ WHERE sp.project_path = ?
3644
+ AND COALESCE(CAST(activity.value AS INTEGER), history.updated_at) > ?`).get(projectPath, since);
3596
3645
  return row?.cnt ?? 0;
3597
3646
  }
3598
3647
  function countMappedMemories(db, projectPath) {
@@ -3667,10 +3716,11 @@ function hasActiveMemories(db, projectPath) {
3667
3716
  return db.prepare("SELECT 1 AS one FROM memories WHERE project_path = ? AND status IN ('active','permanent') LIMIT 1").get(projectPath) != null;
3668
3717
  }
3669
3718
  var RECENT_PROJECT_SESSIONS_SQL = `SELECT sp.session_id FROM session_projects sp
3670
- LEFT JOIN schema_migrations_meta activity
3671
- ON activity.key = 'retrospective_activity:' || sp.session_id
3719
+ LEFT JOIN schema_migrations_meta activity
3720
+ ON activity.key = 'retrospective_activity:' || sp.session_id
3721
+ LEFT JOIN message_history_index history ON history.session_id = sp.session_id
3672
3722
  WHERE sp.project_path = ?
3673
- AND (sp.updated_at > ? OR CAST(activity.value AS INTEGER) > ?)`;
3723
+ AND (sp.updated_at > ? OR COALESCE(CAST(activity.value AS INTEGER), history.updated_at) > ?)`;
3674
3724
  function hasRecentProjectSession(db, projectPath, since) {
3675
3725
  return db.prepare(`${RECENT_PROJECT_SESSIONS_SQL} LIMIT 1`).get(projectPath, since, since) != null;
3676
3726
  }
@@ -10045,7 +10095,7 @@ function getAsyncModule() {
10045
10095
  await beforeModuleLoad();
10046
10096
  const [{ default: singlefileAsyncifyVariant }, { newQuickJSAsyncWASMModuleFromVariant }] = await importPluginModule(() => Promise.all([
10047
10097
  import("./index-e7vx3byk.js").then((m)=>__toESM(m.default,1)),
10048
- import("./index-901ekbga.js")
10098
+ import("./index-en1k8h8m.js")
10049
10099
  ]));
10050
10100
  const module = await newQuickJSAsyncWASMModuleFromVariant(singlefileAsyncifyVariant);
10051
10101
  return module;
@@ -17553,6 +17603,7 @@ class PiSubagentRunner {
17553
17603
  let finalErrorMessage = null;
17554
17604
  let finalStopReason = null;
17555
17605
  let sawAgentEnd = false;
17606
+ let preserveDrainedResult = false;
17556
17607
  let parseError = null;
17557
17608
  let agentEndMessages = null;
17558
17609
  let drainTimerStarted = false;
@@ -17576,6 +17627,15 @@ class PiSubagentRunner {
17576
17627
  let lastEventTimestamp = 0;
17577
17628
  const accumulatedMessages = [];
17578
17629
  accountingMessages = accumulatedMessages;
17630
+ const captureFinalAssistant = (messages) => {
17631
+ const result = extractFinalAssistant(messages);
17632
+ finalAssistantText = result.text;
17633
+ finalStopReason = result.stopReason;
17634
+ finalErrorMessage = result.errorMessage;
17635
+ const lastAssistant = [...messages].reverse().find((message) => typeof message === "object" && message !== null && message.role === "assistant");
17636
+ preserveDrainedResult = (result.stopReason === "stop" || result.stopReason === "length") && !!result.text?.trim() && countToolCalls([lastAssistant]) === 0;
17637
+ return result;
17638
+ };
17579
17639
  rl.on("line", (line) => {
17580
17640
  if (settled || line.length === 0)
17581
17641
  return;
@@ -17703,6 +17763,10 @@ class PiSubagentRunner {
17703
17763
  });
17704
17764
  if (e.type === "auto_retry_start" || e.type === "agent_end" && event.willRetry === true) {
17705
17765
  sawAgentEnd = false;
17766
+ preserveDrainedResult = false;
17767
+ finalAssistantText = null;
17768
+ finalStopReason = null;
17769
+ finalErrorMessage = null;
17706
17770
  agentEndMessages = null;
17707
17771
  drainTimerStarted = false;
17708
17772
  if (drainTimerHandle) {
@@ -17712,14 +17776,11 @@ class PiSubagentRunner {
17712
17776
  armHardTimeout();
17713
17777
  return;
17714
17778
  }
17715
- if (e.type === "agent_end" && Array.isArray(e.messages)) {
17779
+ if (e.type === "agent_end" && Array.isArray(e.messages) && !preserveDrainedResult) {
17716
17780
  sawAgentEnd = true;
17717
17781
  agentEndMessages = e.messages;
17718
17782
  accountingMessages = e.messages;
17719
- const result = extractFinalAssistant(e.messages);
17720
- finalAssistantText = result.text;
17721
- finalStopReason = result.stopReason;
17722
- finalErrorMessage = result.errorMessage;
17783
+ const result = captureFinalAssistant(e.messages);
17723
17784
  emitProgress({
17724
17785
  type: "terminal",
17725
17786
  stopReason: result.stopReason ?? undefined,
@@ -17727,7 +17788,6 @@ class PiSubagentRunner {
17727
17788
  hasToolCall: false,
17728
17789
  ms: elapsedMs
17729
17790
  });
17730
- return;
17731
17791
  }
17732
17792
  if (e.type === "message_end" && e.message) {
17733
17793
  accumulatedMessages.push(e.message);
@@ -17735,12 +17795,9 @@ class PiSubagentRunner {
17735
17795
  if (m.role === "assistant") {
17736
17796
  const hasToolCall = Array.isArray(m.content) && m.content.some((c) => typeof c === "object" && c !== null && c.type === "toolCall");
17737
17797
  const isTerminalStopReason = typeof m.stopReason === "string" && (m.stopReason === "stop" || m.stopReason === "length" || m.stopReason === "error" || m.stopReason === "aborted");
17738
- if (isTerminalStopReason && !hasToolCall) {
17798
+ if (isTerminalStopReason && !hasToolCall && !preserveDrainedResult) {
17739
17799
  sawAgentEnd = true;
17740
- const result = extractFinalAssistant(accumulatedMessages);
17741
- finalAssistantText = result.text;
17742
- finalStopReason = result.stopReason;
17743
- finalErrorMessage = result.errorMessage;
17800
+ const result = captureFinalAssistant(accumulatedMessages);
17744
17801
  emitProgress({
17745
17802
  type: "terminal",
17746
17803
  stopReason: m.stopReason,
@@ -20704,12 +20761,14 @@ function normalizeMaterializeReason(harness, reason, rematerialized) {
20704
20761
  }
20705
20762
  return rematerialized ? "pressure_refold" : null;
20706
20763
  }
20707
- function recordPendingPiTransformDecision(sessionId, decision, snapshotNewestAssistantEntryId) {
20764
+ function recordPendingPiTransformDecision(sessionId, decision, snapshotNewestAssistantEntryId, publicationGuard) {
20765
+ publicationGuard?.();
20708
20766
  if (!decision.bustedThisPass)
20709
20767
  return;
20710
20768
  pendingPiDecisionBySession.set(sessionId, {
20711
20769
  ...decision,
20712
- snapshotNewestAssistantEntryId
20770
+ snapshotNewestAssistantEntryId,
20771
+ publicationGuard
20713
20772
  });
20714
20773
  }
20715
20774
  function findNewestPiAssistantEntryId(entries) {
@@ -20734,6 +20793,14 @@ function schedulePiTransformDecisionResolve(args) {
20734
20793
  const pending = pendingPiDecisionBySession.get(args.sessionId);
20735
20794
  if (!pending)
20736
20795
  return false;
20796
+ try {
20797
+ pending.publicationGuard?.();
20798
+ args.assertCurrentPass?.();
20799
+ } catch {
20800
+ pendingPiDecisionBySession.delete(args.sessionId);
20801
+ sessionLog(args.sessionId, "DISCARDED CONTEXT RESULT: pending Pi transform decision");
20802
+ return false;
20803
+ }
20737
20804
  const targetMessageId = findNewestPiAssistantEntryIdAfter(args.branchEntries, pending.snapshotNewestAssistantEntryId);
20738
20805
  if (!targetMessageId)
20739
20806
  return false;
@@ -20746,6 +20813,13 @@ function schedulePiTransformDecisionResolve(args) {
20746
20813
  try {
20747
20814
  if (!hasScheduledWriteToken(args.sessionId, token))
20748
20815
  return;
20816
+ try {
20817
+ pending.publicationGuard?.();
20818
+ args.assertCurrentPass?.();
20819
+ } catch {
20820
+ sessionLog(args.sessionId, "DISCARDED CONTEXT RESULT: queued Pi transform decision");
20821
+ return;
20822
+ }
20749
20823
  writeTransformDecisionBestEffort(dbPath, {
20750
20824
  ...pending,
20751
20825
  sessionId: args.sessionId,
@@ -21040,6 +21114,10 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
21040
21114
  continue;
21041
21115
  }
21042
21116
  const target = targets.get(pendingOp.tagId);
21117
+ if (target?.thinkingDropProtected) {
21118
+ reject("active_thinking");
21119
+ continue;
21120
+ }
21043
21121
  const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
21044
21122
  if (synthetic) {
21045
21123
  if (!isToolTag) {
@@ -21292,7 +21370,7 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config) {
21292
21370
  const eligible = tags.filter((tag) => tag.type === "message" && tag.status === "active" && (protectedCutoff === null || tag.tagNumber < protectedCutoff) && tag.byteSize >= config.minChars).sort((a, b) => a.tagNumber - b.tagNumber);
21293
21371
  const tagsNeedingCompression = eligible.filter((tag, index) => {
21294
21372
  const target = targets.get(tag.tagNumber);
21295
- if (!target?.getContent || !target.setContent)
21373
+ if (!target?.getContent || !target.setContent || target.thinkingRewriteProtected)
21296
21374
  return false;
21297
21375
  const targetDepth = computeTargetDepth(index, eligible.length);
21298
21376
  return targetDepth > tag.cavemanDepth;
@@ -21301,7 +21379,7 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config) {
21301
21379
  let state = getCavemanReplayState(db, sessionId);
21302
21380
  if (!state.currentRules || state.englishWordRules !== englishWordRules) {
21303
21381
  const compressedBefore = tags.filter((tag) => tag.type === "message" && tag.status === "active" && tag.cavemanDepth > 0);
21304
- const switched = recordCavemanCurrentRules(db, sessionId, englishWordRules, compressedBefore.map((tag) => tag.tagNumber));
21382
+ const switched = compressedBefore.some((tag) => targets.get(tag.tagNumber)?.thinkingRewriteProtected) ? null : recordCavemanCurrentRules(db, sessionId, englishWordRules, compressedBefore.map((tag) => tag.tagNumber));
21305
21383
  if (switched) {
21306
21384
  state = switched;
21307
21385
  const deepened = new Set(tagsNeedingCompression.map((tag) => tag.tagNumber));
@@ -21637,12 +21715,14 @@ function applyHeadCap(args) {
21637
21715
  diagnostics.completedRefence.tokenMass = index.rangeTokens(Math.min(...component.map((arc) => arc.invocation)), Math.max(...component.map((arc) => arc.result)) + 1);
21638
21716
  });
21639
21717
  diagnostics.completedRefence.to = end;
21640
- diagnostics.eligibleEnd = end <= offset ? offset : Math.min(end, protectedTailStart);
21718
+ if (end > protectedTailStart)
21719
+ end = offset;
21720
+ diagnostics.eligibleEnd = end <= offset ? offset : end;
21641
21721
  args.observeDiagnostics?.(diagnostics);
21642
21722
  if (end <= offset && offset < protectedTailStart) {
21643
21723
  return { eligibleEndOrdinal: offset, oversizeAtomicUnit };
21644
21724
  }
21645
- return { eligibleEndOrdinal: Math.min(end, protectedTailStart), oversizeAtomicUnit };
21725
+ return { eligibleEndOrdinal: end, oversizeAtomicUnit };
21646
21726
  }
21647
21727
  function resolveProtectedTailBoundary(ctx) {
21648
21728
  const createdAt = ctx.createdAt ?? Date.now();
@@ -21797,6 +21877,7 @@ function resolveProtectedTailBoundary(ctx) {
21797
21877
  }
21798
21878
  protectedTailStart = clampOrdinal(protectedTailStart, rawMessageCount);
21799
21879
  protectedTailStart = retreatPastHostUnservedRows(messages, protectedTailStart, offset);
21880
+ protectedTailStart = Math.max(offset, fenceBoundaryForCompletedToolArcs(protectedTailStart, arcs, 1));
21800
21881
  const perRunCap = selectPerRunCap({
21801
21882
  usagePercentage,
21802
21883
  N: scaledN,
@@ -21816,7 +21897,8 @@ function resolveProtectedTailBoundary(ctx) {
21816
21897
  capTokens: perRunCap,
21817
21898
  recentOpenArcCutoff
21818
21899
  });
21819
- const eligibleEndOrdinal = retreatPastHostUnservedRows(messages, head.eligibleEndOrdinal, offset);
21900
+ const servedEndOrdinal = retreatPastHostUnservedRows(messages, head.eligibleEndOrdinal, offset);
21901
+ const eligibleEndOrdinal = Math.max(offset, fenceBoundaryForCompletedToolArcs(servedEndOrdinal, arcs, 1));
21820
21902
  const rawRangeFingerprint = computeRawRangeFingerprint(messages, offset, eligibleEndOrdinal);
21821
21903
  return {
21822
21904
  sessionId: ctx.sessionId,
@@ -21844,7 +21926,7 @@ function resolveProtectedTailBoundary(ctx) {
21844
21926
  cacheNamespace: ctx.cacheNamespace,
21845
21927
  createdAt,
21846
21928
  rawRangeFingerprint,
21847
- trueRawEligibleTokens: index.rangeTokens(offset, protectedTailStart),
21929
+ trueRawEligibleTokens: index.rangeTokens(offset, eligibleEndOrdinal),
21848
21930
  oversizeAtomicUnit: head.oversizeAtomicUnit,
21849
21931
  boundaryReason,
21850
21932
  diagnostics: boundaryDiagnostics({
@@ -22075,6 +22157,8 @@ function getRawHistoryEligibility(db, sessionId) {
22075
22157
  function hasRunnableCompartmentWindow(snapshot) {
22076
22158
  if (snapshot.offset >= snapshot.protectedTailStart)
22077
22159
  return false;
22160
+ if (snapshot.eligibleEndOrdinal <= snapshot.offset)
22161
+ return false;
22078
22162
  const forceMaterializationPercentage = escalationBands(snapshot.executeThresholdPercentage).forceMaterializationPercentage;
22079
22163
  if (snapshot.usagePercentage >= forceMaterializationPercentage || snapshot.emergencyTailScale) {
22080
22164
  return snapshot.trueRawEligibleTokens >= deriveMinForceEligibleTokens(snapshot.N) || snapshot.eligibleEndOrdinal > snapshot.offset;
@@ -22145,6 +22229,187 @@ function recordHighPressureNoEligibleHead(db, snapshot) {
22145
22229
  return recordProtectedTailNoEligibleHead(db, snapshot.sessionId);
22146
22230
  }
22147
22231
 
22232
+ // ../plugin/src/hooks/magic-context/historian-drain-gate.ts
22233
+ var loggedWindows = new WeakMap;
22234
+ function isHistorianDrainBudgetSpent(args) {
22235
+ const target = deriveProtectedTailTokenTarget(args);
22236
+ const state = getProtectedTailDrainBudgetSkip({
22237
+ ...args,
22238
+ usable: target.usable,
22239
+ perRunCap: selectPerRunCap({ ...args, N: target.N })
22240
+ });
22241
+ if (!state)
22242
+ return false;
22243
+ let windows = loggedWindows.get(args.db);
22244
+ if (!windows) {
22245
+ windows = new Map;
22246
+ loggedWindows.set(args.db, windows);
22247
+ }
22248
+ if (windows.get(args.sessionId) !== state.resetsAt) {
22249
+ windows.set(args.sessionId, state.resetsAt);
22250
+ sessionLog(args.sessionId, `historian skip: internal drain budget spent (${state.spentTokens}/${state.limitTokens} tokens); next eligible at ${new Date(state.nextEligibleAt).toISOString()}`);
22251
+ }
22252
+ return true;
22253
+ }
22254
+
22255
+ // ../plugin/src/hooks/magic-context/active-anthropic-turn.ts
22256
+ function isInActiveAnthropicTurn(messages, index, anthropic) {
22257
+ if (!anthropic)
22258
+ return false;
22259
+ for (let user = messages.length - 1;user >= 0; user--) {
22260
+ const message = messages[user];
22261
+ if (!isRecord(message))
22262
+ continue;
22263
+ const info = isRecord(message.info) ? message.info : message;
22264
+ if (info.role !== "user" || info.synthetic === true || message.synthetic === true)
22265
+ continue;
22266
+ const id = typeof info.id === "string" ? info.id : "";
22267
+ if (id.startsWith("synth-user-"))
22268
+ continue;
22269
+ const parts = Array.isArray(message.parts) ? message.parts : Array.isArray(message.content) ? message.content : [];
22270
+ if (parts.length > 0 && parts.every((part) => isRecord(part) && (part.synthetic === true || part.ignored === true || ["tool_result", "toolResult", "tool-result"].includes(String(part.type)))))
22271
+ continue;
22272
+ return index > user;
22273
+ }
22274
+ return false;
22275
+ }
22276
+ function hasAnthropicReasoning(messages) {
22277
+ return messages.some((message) => {
22278
+ if (!isRecord(message))
22279
+ return false;
22280
+ const info = isRecord(message.info) ? message.info : message;
22281
+ if (isAnthropicFamilyRoute(typeof info.providerID === "string" ? info.providerID : typeof info.provider === "string" ? info.provider : undefined, typeof info.modelID === "string" ? info.modelID : typeof info.model === "string" ? info.model : undefined))
22282
+ return true;
22283
+ const parts = Array.isArray(message.parts) ? message.parts : [];
22284
+ return parts.some((part) => isRecord(part) && isRecord(part.metadata) && isRecord(part.metadata.anthropic));
22285
+ });
22286
+ }
22287
+
22288
+ // ../plugin/src/hooks/magic-context/strip-content.ts
22289
+ var METADATA_PART_TYPES = new Set([
22290
+ "step-start",
22291
+ "step-finish",
22292
+ "snapshot",
22293
+ "patch",
22294
+ "agent",
22295
+ "retry",
22296
+ "subtask",
22297
+ "compaction"
22298
+ ]);
22299
+ var CLEARED_REASONING_TYPES = new Set(["thinking", "reasoning"]);
22300
+ var REASONING_IGNORED_PART_TYPES = new Set([
22301
+ "step-start",
22302
+ "step-finish",
22303
+ "snapshot",
22304
+ "patch",
22305
+ "agent",
22306
+ "retry",
22307
+ "subtask",
22308
+ "compaction"
22309
+ ]);
22310
+ var REASONING_PART_TYPES = new Set(["reasoning", "thinking", "redacted_thinking"]);
22311
+ function hasReasoningReplayContent(message) {
22312
+ return message.parts.some((part) => {
22313
+ if (!isRecord(part) || part.ignored === true)
22314
+ return false;
22315
+ const type = typeof part.type === "string" ? part.type : "";
22316
+ if (REASONING_IGNORED_PART_TYPES.has(type))
22317
+ return false;
22318
+ return type !== "text" || typeof part.text !== "string" || part.text.trim().length > 0;
22319
+ });
22320
+ }
22321
+ function findLatestAssistantReasoningMutationExemptMessage(messages) {
22322
+ for (let index = messages.length - 1;index >= 0; index -= 1) {
22323
+ const message = messages[index];
22324
+ if (message.info.role === "assistant" && hasReasoningReplayContent(message))
22325
+ return message;
22326
+ }
22327
+ return;
22328
+ }
22329
+
22330
+ // ../plugin/src/hooks/magic-context/reasoning-budget.ts
22331
+ var DEFAULT_KEEP_REASONING_TOKENS = 1e4;
22332
+ var UNKNOWN_REASONING_STEP_TOKENS = 1000;
22333
+ function resolveKeepReasoningTokens(config, modelKey) {
22334
+ const values = config && typeof config === "object" ? Object.assign(Object.create(null), config) : undefined;
22335
+ return typeof config === "number" ? config : resolveModelConfigValue(values, modelKey)?.value ?? values?.default ?? DEFAULT_KEEP_REASONING_TOKENS;
22336
+ }
22337
+ function reasoningStepCost(reported, textEstimate, opaque) {
22338
+ if (reported !== undefined && Number.isFinite(reported) && reported > 0)
22339
+ return reported;
22340
+ if (textEstimate > 0)
22341
+ return Math.ceil(textEstimate);
22342
+ return opaque ? UNKNOWN_REASONING_STEP_TOKENS : 0;
22343
+ }
22344
+ function reasoningBudgetCutoff(steps, budget) {
22345
+ const cost = (step) => step.alreadyRemoved ? 0 : typeof step.cost === "function" ? step.cost() : step.cost;
22346
+ let kept = 0;
22347
+ let upperBound = Number.POSITIVE_INFINITY;
22348
+ for (const step of steps) {
22349
+ if (!step.exempt)
22350
+ continue;
22351
+ kept += cost(step);
22352
+ if (step.tag > 0)
22353
+ upperBound = Math.min(upperBound, step.tag - 1);
22354
+ }
22355
+ for (let index = steps.length - 1;index >= 0; index--) {
22356
+ const step = steps[index];
22357
+ if (step.exempt || step.alreadyRemoved)
22358
+ continue;
22359
+ const tokens = cost(step);
22360
+ if (kept + tokens <= budget) {
22361
+ kept += tokens;
22362
+ if (step.tag > 0)
22363
+ upperBound = Math.min(upperBound, step.tag - 1);
22364
+ continue;
22365
+ }
22366
+ let tag = step.tag;
22367
+ for (let older = index - 1;tag <= 0 && older >= 0; older--)
22368
+ tag = steps[older].tag;
22369
+ return Math.max(0, Math.min(tag, upperBound));
22370
+ }
22371
+ return 0;
22372
+ }
22373
+ function reasoningTextAndOpaque(parts) {
22374
+ let text = "";
22375
+ let inlineText = "";
22376
+ let opaque = false;
22377
+ let hasReasoning = false;
22378
+ const detailText = new Set;
22379
+ for (const part of parts) {
22380
+ if (!isRecord(part))
22381
+ continue;
22382
+ if (part.ignored === true)
22383
+ continue;
22384
+ if (isRecord(part.metadata)) {
22385
+ const router = part.metadata.openrouter;
22386
+ if (isRecord(router) && Array.isArray(router.reasoning_details) && router.reasoning_details.length > 0) {
22387
+ hasReasoning = true;
22388
+ opaque = true;
22389
+ for (const detail of router.reasoning_details) {
22390
+ if (!isRecord(detail))
22391
+ continue;
22392
+ if (detail.type === "reasoning.text" && typeof detail.text === "string")
22393
+ detailText.add(detail.text);
22394
+ if (detail.type === "reasoning.summary" && typeof detail.summary === "string")
22395
+ detailText.add(detail.summary);
22396
+ }
22397
+ }
22398
+ }
22399
+ if (["reasoning", "thinking", "redacted_thinking"].includes(String(part.type))) {
22400
+ hasReasoning = true;
22401
+ const visible = part.type === "redacted_thinking" || part.redacted === true ? "" : typeof part.thinking === "string" ? part.thinking : typeof part.text === "string" ? part.text : "";
22402
+ if (visible !== "[cleared]")
22403
+ text += visible;
22404
+ opaque ||= part.type === "redacted_thinking" || part.redacted === true || visible === "" && (part.metadata !== undefined || part.signature !== undefined || part.thinkingSignature !== undefined);
22405
+ } else if (part.type === "text" && typeof part.text === "string") {
22406
+ for (const match of part.text.matchAll(/<(?:thinking|think)>([\s\S]*?)<\/(?:thinking|think)>/gi))
22407
+ inlineText += match[1];
22408
+ }
22409
+ }
22410
+ return { text: text || [...detailText].join(""), inlineText, opaque, hasReasoning };
22411
+ }
22412
+
22148
22413
  // ../plugin/src/hooks/magic-context/compartment-trigger.ts
22149
22414
  var PROACTIVE_TRIGGER_OFFSET_PERCENTAGE = 2;
22150
22415
  var POST_DROP_TARGET_RATIO = 0.75;
@@ -22187,7 +22452,7 @@ function estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, tag
22187
22452
  function getProactiveCompartmentTriggerPercentage(executeThresholdPercentage) {
22188
22453
  return Math.max(0, executeThresholdPercentage - PROACTIVE_TRIGGER_OFFSET_PERCENTAGE);
22189
22454
  }
22190
- function estimateProjectedPostDropPercentage(db, sessionId, usage, activeTags, clearReasoningAge, clearedReasoningThroughTag, canClearReasoning) {
22455
+ function estimateProjectedPostDropPercentage(db, sessionId, usage, activeTags, keepReasoningTokens, clearedReasoningThroughTag, canClearReasoning, liveBudgetCutoff) {
22191
22456
  const totalActiveBytes = activeTags.reduce((sum, tag) => sum + tag.byteSize + tag.reasoningByteSize, 0);
22192
22457
  if (totalActiveBytes === 0)
22193
22458
  return null;
@@ -22197,9 +22462,25 @@ function estimateProjectedPostDropPercentage(db, sessionId, usage, activeTags, c
22197
22462
  if (pendingDrops.length > 0) {
22198
22463
  droppableBytes += activeTags.filter((tag) => pendingDropTagIds.has(tag.tagNumber)).reduce((sum, tag) => sum + tag.byteSize + tag.reasoningByteSize, 0);
22199
22464
  }
22200
- const maxTag = activeTags.reduce((max, t) => Math.max(max, t.tagNumber), 0);
22201
- if (canClearReasoning && clearReasoningAge !== undefined && clearedReasoningThroughTag !== undefined) {
22202
- const reasoningAgeCutoff = maxTag - clearReasoningAge;
22465
+ if (canClearReasoning && keepReasoningTokens !== undefined && clearedReasoningThroughTag !== undefined) {
22466
+ const estimates = getReasoningTokenEstimatesByMessage(db, sessionId, sessionDecisionCalibration(db, sessionId).proseRatio);
22467
+ const groups = new Map;
22468
+ for (const tag of activeTags) {
22469
+ const id = tag.type === "tool" ? tag.toolOwnerMessageId : tag.messageId;
22470
+ if (!id)
22471
+ continue;
22472
+ const group = groups.get(id) ?? { tag: 0, cost: estimates.get(id) ?? 0 };
22473
+ if (!estimates.has(id))
22474
+ group.cost += tag.reasoningByteSize / 4;
22475
+ group.tag = Math.max(group.tag, tag.tagNumber);
22476
+ groups.set(id, group);
22477
+ }
22478
+ const steps = [...groups.values()].sort((a, b) => a.tag - b.tag);
22479
+ const reasoningAgeCutoff = liveBudgetCutoff ?? reasoningBudgetCutoff(steps.map((step, index) => ({
22480
+ ...step,
22481
+ exempt: index === steps.length - 1,
22482
+ alreadyRemoved: step.tag <= clearedReasoningThroughTag
22483
+ })), keepReasoningTokens);
22203
22484
  for (const tag of activeTags) {
22204
22485
  if (tag.type !== "message")
22205
22486
  continue;
@@ -22269,7 +22550,7 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
22269
22550
  usageSource: "live",
22270
22551
  taggerFloor
22271
22552
  });
22272
- const hasProtectedEligibleHead = boundary.offset < boundary.protectedTailStart;
22553
+ const hasProtectedEligibleHead = boundary.offset < Math.min(boundary.protectedTailStart, boundary.eligibleEndOrdinal);
22273
22554
  if (!hasProtectedEligibleHead) {
22274
22555
  return {
22275
22556
  ...TAIL_INFO_DEFAULTS,
@@ -22281,7 +22562,7 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
22281
22562
  const seed = sessionDecisionCalibration(db, sessionId);
22282
22563
  const sourceRatio = Math.max(seed.proseRatio, seed.toolsRatio);
22283
22564
  const scanBudget = Math.floor(Math.max(MIN_PROACTIVE_TAIL_TOKEN_ESTIMATE, triggerBudget * TAIL_SIZE_TRIGGER_MULTIPLIER) / sourceRatio);
22284
- const chunk = readSessionChunk2(sessionId, scanBudget, rawEligibility.offset, boundary.protectedTailStart);
22565
+ const chunk = readSessionChunk2(sessionId, scanBudget, rawEligibility.offset, boundary.eligibleEndOrdinal);
22285
22566
  const isMeaningful = chunk.hasMore || boundary.trueRawEligibleTokens >= MIN_PROACTIVE_TAIL_TOKEN_ESTIMATE || Math.ceil(chunk.tokenEstimate * sourceRatio) >= MIN_PROACTIVE_TAIL_TOKEN_ESTIMATE || chunk.messageCount >= MIN_PROACTIVE_TAIL_MESSAGE_COUNT;
22286
22567
  return {
22287
22568
  nextStartOrdinal: rawEligibility.offset,
@@ -22303,11 +22584,19 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
22303
22584
  function formatProjectedPostDropPercentage(value) {
22304
22585
  return value === null ? "none" : `${value.toFixed(1)}%`;
22305
22586
  }
22306
- function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail, taggerFloorOverride, reasoningProjection, reclaimRide) {
22587
+ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, keepReasoningTokens, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail, taggerFloorOverride, reasoningProjection, reclaimRide) {
22307
22588
  if (sessionMeta.compartmentInProgress) {
22308
22589
  sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
22309
22590
  return { shouldFire: false };
22310
22591
  }
22592
+ if (isHistorianDrainBudgetSpent({
22593
+ db,
22594
+ sessionId,
22595
+ usagePercentage: usage.percentage,
22596
+ contextLimit: resolveBoundaryContextLimit(usage, contextLimit),
22597
+ executeThresholdPercentage
22598
+ }))
22599
+ return { shouldFire: false };
22311
22600
  const lazyInMemoryTail = typeof inMemoryTail === "function" ? inMemoryTail : undefined;
22312
22601
  let resolvedInMemoryTail = typeof inMemoryTail === "function" ? undefined : inMemoryTail;
22313
22602
  if (resolvedInMemoryTail) {
@@ -22359,7 +22648,7 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
22359
22648
  return { shouldFire: false };
22360
22649
  }
22361
22650
  const canClearReasoning = reasoningProjection?.canClearReasoning ?? modelAcceptsEmptyContent(reasoningProjection?.providerID);
22362
- const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage, preloadedActiveTags ?? getActiveTagsBySession(db, sessionId), clearReasoningAge, sessionMeta.clearedReasoningThroughTag, canClearReasoning);
22651
+ const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage, preloadedActiveTags ?? getActiveTagsBySession(db, sessionId), keepReasoningTokens, sessionMeta.clearedReasoningThroughTag, canClearReasoning, reasoningProjection?.budgetCutoff);
22363
22652
  const relativePostDropTarget = executeThresholdPercentage * POST_DROP_TARGET_RATIO;
22364
22653
  const rideLabel = reclaimRide ? reclaimRideLabel(reclaimRide) : "ride=none";
22365
22654
  const dropsCanLand = reclaimRide !== undefined && hasReclaimRide(reclaimRide);
@@ -22537,6 +22826,292 @@ function foldExecutesThisPass(foldDue, materialized) {
22537
22826
  return foldDue && materialized;
22538
22827
  }
22539
22828
 
22829
+ // ../plugin/src/hooks/magic-context/reasoning-removal.ts
22830
+ var REMOVABLE_REASONING_TYPES = new Set(["reasoning", "thinking", "redacted_thinking"]);
22831
+
22832
+ // ../plugin/src/hooks/magic-context/latest-assistant-turn.ts
22833
+ var THINKING_TYPES = new Set(["reasoning", "thinking", "redacted_thinking"]);
22834
+ function role(message) {
22835
+ if (!isRecord(message))
22836
+ return;
22837
+ return isRecord(message.info) ? message.info.role : message.role;
22838
+ }
22839
+ function content(message) {
22840
+ if (!isRecord(message))
22841
+ return;
22842
+ return Array.isArray(message.parts) ? message.parts : message.content;
22843
+ }
22844
+ function latestAssistantTurnStart(messages) {
22845
+ let lo = 0;
22846
+ let hi = messages.length;
22847
+ while (lo < hi) {
22848
+ const mid = lo + hi >>> 1;
22849
+ if (isInActiveAnthropicTurn(messages, mid, true))
22850
+ hi = mid;
22851
+ else
22852
+ lo = mid + 1;
22853
+ }
22854
+ return lo;
22855
+ }
22856
+ function latestAssistantTurnMessages(messages) {
22857
+ return new Set(messages.slice(latestAssistantTurnStart(messages)));
22858
+ }
22859
+ function hasActiveAnthropicThinkingTurn(messages, provider, model) {
22860
+ const familyRoute = isAnthropicFamilyRoute(provider, model);
22861
+ for (const message of messages.slice(latestAssistantTurnStart(messages))) {
22862
+ if (role(message) !== "assistant")
22863
+ continue;
22864
+ const parts = content(message);
22865
+ if (!Array.isArray(parts))
22866
+ continue;
22867
+ if (parts.some((part) => isRecord(part) && THINKING_TYPES.has(String(part.type ?? part.kind)) && (familyRoute || isRecord(part.metadata) && isRecord(part.metadata.anthropic))))
22868
+ return true;
22869
+ }
22870
+ return false;
22871
+ }
22872
+ function protectNewTagMutations(messages, targets, protectedParts, prefixBound) {
22873
+ if (protectedParts.size === 0)
22874
+ return targets;
22875
+ const positions = new Map;
22876
+ let ordinal = 0;
22877
+ for (const message of messages)
22878
+ for (const part of message.parts)
22879
+ positions.set(part, ordinal++);
22880
+ let lastProtected = -1;
22881
+ for (const part of protectedParts)
22882
+ lastProtected = Math.max(lastProtected, positions.get(part) ?? -1);
22883
+ const result = new Map;
22884
+ for (const [tag, target] of targets) {
22885
+ const coordinates = target.mutationParts ?? (target.message ? target.message.parts.map((part) => ({ message: target.message, part })) : []);
22886
+ const prefixEdit = prefixBound && coordinates.some(({ part }) => (positions.get(part) ?? Infinity) < lastProtected);
22887
+ const dropsThinking = target.dropReasoningParts?.some((part) => protectedParts.has(part)) === true;
22888
+ if (!prefixEdit && !dropsThinking) {
22889
+ result.set(tag, target);
22890
+ continue;
22891
+ }
22892
+ result.set(tag, {
22893
+ ...target,
22894
+ thinkingDropProtected: true,
22895
+ thinkingRewriteProtected: prefixEdit,
22896
+ canDrop: () => false,
22897
+ drop: () => "incomplete",
22898
+ truncate: () => "incomplete",
22899
+ skeletonReal: () => "incomplete",
22900
+ skeletonStripped: () => "incomplete",
22901
+ editMarker: () => "incomplete",
22902
+ editMarkerStripped: () => "incomplete",
22903
+ setContent: (content, options) => prefixEdit || options?.keepReasoning !== true ? false : target.setContent(content, options)
22904
+ });
22905
+ }
22906
+ return result;
22907
+ }
22908
+
22909
+ // ../plugin/src/hooks/magic-context/latest-thinking-recovery.ts
22910
+ var LATEST_THINKING_RESTORE = "latest_thinking_original";
22911
+ var knownRejectedSessions = new Set;
22912
+ var lastPassTurnAnchor = new Map;
22913
+ var ACTIVE = `${LATEST_THINKING_RESTORE}:`;
22914
+ var ARMED = `${LATEST_THINKING_RESTORE}_armed:`;
22915
+ var UNRECOVERABLE = `${LATEST_THINKING_RESTORE}_unavailable`;
22916
+ var LATEST_THINKING_UNSAFE = "ANTHROPIC_LATEST_TURN_EDIT_UNSAFE: The provider rejected edits to this thinking turn and its original context cannot be safely replayed. Send a new user message or /clear to continue.";
22917
+ function armLatestThinkingRecovery(db, sessionId) {
22918
+ knownRejectedSessions.add(sessionId);
22919
+ ensureSessionMetaRow(db, sessionId);
22920
+ const current = getThinkingBindingRecoveryTarget(db, sessionId);
22921
+ const anchor = lastPassTurnAnchor.get(sessionId);
22922
+ const next = current?.startsWith(ACTIVE) ? UNRECOVERABLE + ":" + current.slice(ACTIVE.length) : current?.startsWith(UNRECOVERABLE) || current?.startsWith(ARMED) ? current : anchor !== undefined ? ARMED + anchor : LATEST_THINKING_RESTORE;
22923
+ db.prepare("UPDATE session_meta SET thinking_binding_recovery_target = ? WHERE session_id = ?").run(next, sessionId);
22924
+ }
22925
+ function clone(value) {
22926
+ if (Array.isArray(value))
22927
+ return value.map(clone);
22928
+ if (!isRecord(value))
22929
+ return value;
22930
+ const proto = Object.getPrototypeOf(value);
22931
+ if (proto !== Object.prototype && proto !== null)
22932
+ return value;
22933
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, clone(item)]));
22934
+ }
22935
+ function isThinkingPart(part) {
22936
+ return isRecord(part) && ["thinking", "reasoning", "redacted_thinking"].includes(String(part.type));
22937
+ }
22938
+ function replayableOriginal(part) {
22939
+ if (!isThinkingPart(part) || !isRecord(part))
22940
+ return false;
22941
+ const metadata = isRecord(part.metadata) && isRecord(part.metadata.anthropic) ? part.metadata.anthropic : {};
22942
+ const signature = part.signature ?? part.thinkingSignature ?? metadata.signature ?? part.data ?? metadata.redactedData;
22943
+ return typeof signature === "string" && signature.length > 0 && part.text !== "[cleared]" && part.thinking !== "[cleared]";
22944
+ }
22945
+ function prepareLatestThinkingRecovery(args) {
22946
+ const start = latestAssistantTurnStart(args.messages);
22947
+ const anchor = start > 0 ? args.id(args.messages[start - 1], start - 1) : undefined;
22948
+ if (anchor === undefined)
22949
+ lastPassTurnAnchor.delete(args.sessionId);
22950
+ else
22951
+ lastPassTurnAnchor.set(args.sessionId, anchor);
22952
+ let target;
22953
+ try {
22954
+ target = getThinkingBindingRecoveryTarget(args.db, args.sessionId);
22955
+ } catch (error) {
22956
+ if (knownRejectedSessions.has(args.sessionId))
22957
+ throw contextRefusalError(LATEST_THINKING_UNSAFE);
22958
+ return { restore: false, ended: false };
22959
+ }
22960
+ if (!target || !target.startsWith(LATEST_THINKING_RESTORE)) {
22961
+ knownRejectedSessions.delete(args.sessionId);
22962
+ return { restore: false, ended: false };
22963
+ }
22964
+ const armedAnchor = target.startsWith(ARMED) ? target.slice(ARMED.length) : undefined;
22965
+ if (target === LATEST_THINKING_RESTORE || armedAnchor !== undefined && armedAnchor !== anchor) {
22966
+ args.db.prepare("UPDATE session_meta SET thinking_binding_recovery_target = '' WHERE session_id = ? AND thinking_binding_recovery_target = ?").run(args.sessionId, target);
22967
+ knownRejectedSessions.delete(args.sessionId);
22968
+ return { restore: false, ended: false };
22969
+ }
22970
+ knownRejectedSessions.add(args.sessionId);
22971
+ const savedAnchor = target.startsWith(ACTIVE) ? target.slice(ACTIVE.length) : target.startsWith(UNRECOVERABLE + ":") ? target.slice(UNRECOVERABLE.length + 1) : undefined;
22972
+ if (savedAnchor !== undefined && savedAnchor !== anchor) {
22973
+ args.db.prepare("UPDATE session_meta SET thinking_binding_recovery_target = '' WHERE session_id = ? AND thinking_binding_recovery_target = ?").run(args.sessionId, target);
22974
+ knownRejectedSessions.delete(args.sessionId);
22975
+ return { restore: false, ended: true };
22976
+ }
22977
+ if (target.startsWith(UNRECOVERABLE) || !anchor)
22978
+ throw contextRefusalError(LATEST_THINKING_UNSAFE);
22979
+ if (armedAnchor !== undefined) {
22980
+ args.db.prepare("UPDATE session_meta SET thinking_binding_recovery_target = ? WHERE session_id = ? AND thinking_binding_recovery_target = ?").run(ACTIVE + anchor, args.sessionId, target);
22981
+ }
22982
+ const active = args.messages.slice(start);
22983
+ if (!active.some((message) => args.parts(message).some(replayableOriginal)))
22984
+ throw contextRefusalError(LATEST_THINKING_UNSAFE + " [missing-thinking-original]");
22985
+ const ids = new Map(args.messages.map((message, index) => [args.id(message, index), index]));
22986
+ for (const decision of getMergedReasoningStrippedIds(args.db, args.sessionId)) {
22987
+ if (decision === "binding_mismatch_order:end" || decision === TOOL_SWEEP_SCOPED_MARKER)
22988
+ continue;
22989
+ const id = decodeMergedReasoningParts(decision)?.[0] ?? (decision.startsWith("binding_mismatch:") ? decision.slice("binding_mismatch:".length) : decision);
22990
+ const index = ids.get(id);
22991
+ if (index === undefined || index >= start && !args.parts(args.messages[index]).some(replayableOriginal))
22992
+ throw contextRefusalError(LATEST_THINKING_UNSAFE + ` [missing-original:${id}]`);
22993
+ }
22994
+ return { restore: true, ended: false };
22995
+ }
22996
+ function latestThinkingRecoveryCoversActiveTurn(args) {
22997
+ let target;
22998
+ try {
22999
+ target = getThinkingBindingRecoveryTarget(args.db, args.sessionId);
23000
+ } catch {
23001
+ return false;
23002
+ }
23003
+ const bound = target?.startsWith(ARMED) ? target.slice(ARMED.length) : target?.startsWith(ACTIVE) ? target.slice(ACTIVE.length) : undefined;
23004
+ if (bound === undefined)
23005
+ return false;
23006
+ const start = latestAssistantTurnStart(args.messages);
23007
+ return start > 0 && args.id(args.messages[start - 1], start - 1) === bound;
23008
+ }
23009
+ function captureOriginalTurn(messages, parts, setParts) {
23010
+ const start = Math.max(0, latestAssistantTurnStart(messages) - 1);
23011
+ const originals = messages.slice(start).map((message) => ({ message, parts: clone(parts(message)) }));
23012
+ const before = messages.slice(0, start);
23013
+ return (target = messages) => {
23014
+ const first = target.findIndex((message) => originals.some((original) => original.message === message || isRecord(message) && isRecord(original.message) && (isRecord(message.info) && isRecord(original.message.info) && message.info.id === original.message.info.id || message.role === original.message.role && message.timestamp !== undefined && message.timestamp === original.message.timestamp)));
23015
+ const prefix = first >= 0 ? target.slice(0, first) : target === messages ? before : target.slice();
23016
+ for (const original of originals) {
23017
+ setParts(original.message, clone(original.parts));
23018
+ }
23019
+ target.length = 0;
23020
+ target.push(...prefix, ...originals.map((original) => original.message));
23021
+ };
23022
+ }
23023
+ function armBindingRecoverySafely(db, sessionId) {
23024
+ if (getThinkingBindingRecoveryTarget(db, sessionId)?.startsWith(LATEST_THINKING_RESTORE))
23025
+ armLatestThinkingRecovery(db, sessionId);
23026
+ else
23027
+ armThinkingBindingRecovery(db, sessionId);
23028
+ }
23029
+
23030
+ // ../plugin/src/hooks/magic-context/reasoning-budget-status.ts
23031
+ var sources = new Map;
23032
+ function captureOpencodeReasoningBudgetStatus(sessionId, messages, budget, prefixBound, proseRatio = 1) {
23033
+ if (messages.some((message) => !isRecord(message) || !isRecord(message.info) || typeof message.info.role !== "string" || !Array.isArray(message.parts))) {
23034
+ sources.delete(sessionId);
23035
+ return;
23036
+ }
23037
+ const compact = messages.filter((message) => message.info.role === "assistant").map((message) => {
23038
+ const info = message.info;
23039
+ const visible = reasoningTextAndOpaque(message.parts);
23040
+ const parts = message.parts.filter(isRecord).map((part) => ({
23041
+ type: typeof part.type === "string" ? part.type : "",
23042
+ text: typeof part.text === "string" && part.type !== "redacted_thinking" && part.redacted !== true ? part.text : undefined,
23043
+ thinking: typeof part.thinking === "string" && part.redacted !== true ? part.thinking : undefined,
23044
+ ignored: part.ignored === true,
23045
+ redacted: part.redacted === true,
23046
+ metadata: part.metadata === undefined ? undefined : {},
23047
+ signature: part.signature === undefined ? undefined : true,
23048
+ thinkingSignature: part.thinkingSignature === undefined ? undefined : true
23049
+ }));
23050
+ if (visible.hasReasoning && !reasoningTextAndOpaque(parts).hasReasoning)
23051
+ parts.push({
23052
+ type: "reasoning",
23053
+ text: visible.text,
23054
+ thinking: undefined,
23055
+ ignored: false,
23056
+ redacted: false,
23057
+ metadata: visible.opaque ? {} : undefined,
23058
+ signature: undefined,
23059
+ thinkingSignature: undefined
23060
+ });
23061
+ return {
23062
+ info: {
23063
+ role: info.role,
23064
+ tokens: isRecord(info.tokens) && typeof info.tokens.reasoning === "number" ? { reasoning: info.tokens.reasoning } : undefined
23065
+ },
23066
+ parts
23067
+ };
23068
+ });
23069
+ sources.delete(sessionId);
23070
+ sources.set(sessionId, { messages: compact, budget, prefixBound, proseRatio });
23071
+ if (sources.size > 32)
23072
+ sources.delete(sources.keys().next().value);
23073
+ }
23074
+ function reasoningBudgetStatusLine(sessionId) {
23075
+ try {
23076
+ const source = sources.get(sessionId);
23077
+ if (!source)
23078
+ return;
23079
+ source.status ??= opencodeReasoningBudgetStatus(source.messages, source.budget, source.prefixBound, source.proseRatio);
23080
+ return formatReasoningBudgetStatus(source.status);
23081
+ } catch {
23082
+ return;
23083
+ }
23084
+ }
23085
+ function formatReasoningBudgetStatus(status) {
23086
+ const count = (tokens) => tokens >= 1000 ? `${Number((tokens / 1000).toFixed(1))}k` : String(Math.round(tokens));
23087
+ return `Reasoning kept: ${count(status.kept)} of ${count(status.budget)} (${status.estimated ? "estimated" : "reported"})${status.overrun ? ` (over budget: ${status.overrun})` : ""}`;
23088
+ }
23089
+ function opencodeReasoningBudgetStatus(messages, budget, prefixBound, proseRatio = 1) {
23090
+ const assistants = messages.filter((message) => message.info.role === "assistant");
23091
+ const newest = assistants.at(-1);
23092
+ const exempt = findLatestAssistantReasoningMutationExemptMessage(messages);
23093
+ let kept = 0;
23094
+ let exemptCost = 0;
23095
+ let estimated = false;
23096
+ for (const message of assistants) {
23097
+ const visible = reasoningTextAndOpaque(message.parts);
23098
+ const info = message.info;
23099
+ const reported = isRecord(info.tokens) && typeof info.tokens.reasoning === "number" ? info.tokens.reasoning : undefined;
23100
+ const cost = (visible.hasReasoning ? reported !== undefined && Number.isFinite(reported) && reported > 0 ? reported : reasoningStepCost(reported, estimateTokens(visible.text) * proseRatio, visible.opaque) : 0) + Math.ceil(estimateTokens(visible.inlineText) * proseRatio);
23101
+ kept += cost;
23102
+ if (message === newest || message === exempt)
23103
+ exemptCost += cost;
23104
+ if (cost > 0 && (!(reported && Number.isFinite(reported) && reported > 0) || visible.inlineText !== ""))
23105
+ estimated = true;
23106
+ }
23107
+ return {
23108
+ kept,
23109
+ budget,
23110
+ estimated,
23111
+ overrun: kept > budget ? exemptCost > budget ? "newest step" : prefixBound ? "signed prefix" : "awaiting rebuild" : undefined
23112
+ };
23113
+ }
23114
+
22540
23115
  // ../plugin/src/hooks/magic-context/supersession-reclaim.ts
22541
23116
  var SUPERSESSION_RECENT_MESSAGE_WINDOW = 20;
22542
23117
  function recentSupersessionOwnerMessageIds(db, sessionId) {
@@ -23337,6 +23912,10 @@ function buildAggregateTarget(tagId, occurrences, requiresToolArcSkeleton, conve
23337
23912
  return !occurrences.every((occ, index) => occ.part.remove && authorization[index] !== false);
23338
23913
  };
23339
23914
  return {
23915
+ mutationParts: occurrences.map((occ) => ({
23916
+ message: { info: occ.message.info, parts: [] },
23917
+ part: occ.part
23918
+ })),
23340
23919
  measureReclaim(skeleton) {
23341
23920
  let beforeTools = 0;
23342
23921
  let afterTools = 0;
@@ -23450,6 +24029,7 @@ function buildAggregateTarget(tagId, occurrences, requiresToolArcSkeleton, conve
23450
24029
  }
23451
24030
  function buildTextTarget(part, message, textPrefix) {
23452
24031
  return {
24032
+ mutationParts: [{ message: { info: message.info, parts: [] }, part }],
23453
24033
  textPrefix,
23454
24034
  setContent(content) {
23455
24035
  return part.setText(content);
@@ -23465,6 +24045,7 @@ function buildTextTarget(part, message, textPrefix) {
23465
24045
  }
23466
24046
  function buildToolTarget(part, message, tagId) {
23467
24047
  return {
24048
+ mutationParts: [{ message: { info: message.info, parts: [] }, part }],
23468
24049
  setContent(content) {
23469
24050
  return setToolContentOrText(part, content);
23470
24051
  },
@@ -23498,12 +24079,58 @@ function buildToolTarget(part, message, tagId) {
23498
24079
 
23499
24080
  // ../plugin/src/hooks/magic-context/auto-search-deadline.ts
23500
24081
  var AUTO_SEARCH_TIMEOUT_MS = 3000;
24082
+ var operationDeadlines = new WeakMap;
24083
+ function autoSearchDeadlineUnixMs(signal) {
24084
+ const deadline = signal && operationDeadlines.get(signal);
24085
+ return Date.now() + (deadline === undefined ? AUTO_SEARCH_TIMEOUT_MS : Math.max(0, deadline - performance.now()));
24086
+ }
24087
+ var skippedTurns = new Map;
24088
+ function wasAutoSearchSkipped(db, sessionId, messageId) {
24089
+ return skippedTurns.get(sessionId)?.get(db)?.has(messageId) ?? false;
24090
+ }
24091
+ function clearAutoSearchTimeoutForSession(sessionId) {
24092
+ cancelAutoSearchWork(sessionId);
24093
+ if (sessionId === undefined)
24094
+ skippedTurns.clear();
24095
+ else
24096
+ skippedTurns.delete(sessionId);
24097
+ }
24098
+ function persistAutoSearchSkip(db, sessionId, messageId, reason = "timeout") {
24099
+ assertTransformWrite();
24100
+ let turns = skippedTurns.get(sessionId);
24101
+ if (!turns) {
24102
+ turns = new WeakMap;
24103
+ skippedTurns.set(sessionId, turns);
24104
+ }
24105
+ let messages = turns.get(db);
24106
+ if (!messages) {
24107
+ messages = new Map;
24108
+ turns.set(db, messages);
24109
+ }
24110
+ const prior = messages.get(messageId);
24111
+ if (prior)
24112
+ return prior;
24113
+ let ok = false;
24114
+ try {
24115
+ const outcome = appendAutoSearchHintDecision(db, sessionId, {
24116
+ messageId,
24117
+ decision: "no-hint",
24118
+ reason
24119
+ });
24120
+ ok = outcome.ok && outcome.decision.decision === "no-hint";
24121
+ } catch {}
24122
+ assertTransformWrite();
24123
+ const persistence = Promise.resolve(ok);
24124
+ messages.set(messageId, persistence);
24125
+ return persistence;
24126
+ }
23501
24127
  async function withAutoSearchDeadline(operation, startedAt = performance.now()) {
23502
24128
  const deadline = startedAt + AUTO_SEARCH_TIMEOUT_MS;
23503
24129
  const remaining = deadline - performance.now();
23504
24130
  if (remaining <= 0)
23505
24131
  return null;
23506
24132
  const controller = new AbortController;
24133
+ operationDeadlines.set(controller.signal, deadline);
23507
24134
  const checkDeadline = () => {
23508
24135
  if (!controller.signal.aborted && performance.now() >= deadline)
23509
24136
  controller.abort();
@@ -23622,6 +24249,205 @@ ${trimmedBody}…
23622
24249
  return wrapped;
23623
24250
  }
23624
24251
 
24252
+ // ../plugin/src/hooks/magic-context/auto-search-worker-client.ts
24253
+ import { Worker } from "node:worker_threads";
24254
+ var pendingRegistrations = new WeakMap;
24255
+ var pendingTurns = new WeakMap;
24256
+ async function coalesceAutoSearchTurn(db, sessionId, messageId, operation, replay) {
24257
+ let sessions = pendingTurns.get(db);
24258
+ if (!sessions) {
24259
+ sessions = new Map;
24260
+ pendingTurns.set(db, sessions);
24261
+ }
24262
+ let turns = sessions.get(sessionId);
24263
+ if (!turns) {
24264
+ turns = new Map;
24265
+ sessions.set(sessionId, turns);
24266
+ }
24267
+ const pending = turns.get(messageId);
24268
+ if (pending && !pending.signal.aborted) {
24269
+ const result = await pending.promise;
24270
+ if (!pending.signal.aborted)
24271
+ replay();
24272
+ return result;
24273
+ }
24274
+ const lifecycle = registerAutoSearchWork(sessionId);
24275
+ const work = Promise.resolve().then(() => operation(lifecycle.signal));
24276
+ const turn = { promise: work, signal: lifecycle.signal };
24277
+ turns.set(messageId, turn);
24278
+ try {
24279
+ return await work;
24280
+ } finally {
24281
+ lifecycle.done();
24282
+ if (turns.get(messageId) === turn)
24283
+ turns.delete(messageId);
24284
+ if (turns.size === 0)
24285
+ sessions.delete(sessionId);
24286
+ }
24287
+ }
24288
+ function queueAutoSearchRegistration(db, projectPath, register) {
24289
+ if (!register)
24290
+ return;
24291
+ let pending = pendingRegistrations.get(db);
24292
+ if (!pending) {
24293
+ pending = new Set;
24294
+ pendingRegistrations.set(db, pending);
24295
+ }
24296
+ if (pending.has(projectPath))
24297
+ return;
24298
+ pending.add(projectPath);
24299
+ const timer = setTimeout(() => {
24300
+ if (getProjectEmbeddingSnapshot(projectPath)) {
24301
+ pending.delete(projectPath);
24302
+ return;
24303
+ }
24304
+ Promise.resolve().then(register).catch((error) => log(`[auto-search] background registration failed: ${error instanceof Error ? error.message : String(error)}`)).finally(() => pending.delete(projectPath));
24305
+ }, 0);
24306
+ timer.unref?.();
24307
+ }
24308
+ function persistAutoSearchDecision(db, sessionId, decision, startedAt) {
24309
+ if (performance.now() - startedAt >= AUTO_SEARCH_TIMEOUT_MS)
24310
+ return null;
24311
+ return appendAutoSearchHintDecision(db, sessionId, decision);
24312
+ }
24313
+ async function searchAutoHint(db, sessionId, projectPath, query, options, entry = new URL(new URL(import.meta.url).pathname.endsWith(".ts") ? "./auto-search-worker.ts" : "./auto-search-worker.js", import.meta.url)) {
24314
+ return executeAutoSearchWorker(db, sessionId, projectPath, query, options, entry, "search");
24315
+ }
24316
+ var pendingBackfills = new WeakMap;
24317
+ function queueAutoSearchBackfill(db, sessionId, projectPath, query) {
24318
+ if (isEmbeddingHostBusy())
24319
+ return Promise.resolve();
24320
+ const key = `${sessionId}\x00${projectPath}`;
24321
+ const pending = pendingBackfills.get(db) ?? new Map;
24322
+ pendingBackfills.set(db, pending);
24323
+ const prior = pending.get(key);
24324
+ if (prior)
24325
+ return prior;
24326
+ const lifecycle = registerAutoSearchWork(sessionId);
24327
+ const work = new Promise((resolve) => {
24328
+ const done = () => {
24329
+ lifecycle.done();
24330
+ pending.delete(key);
24331
+ resolve();
24332
+ };
24333
+ const queued = setTimeout(() => {
24334
+ lifecycle.signal.removeEventListener("abort", cancel);
24335
+ if (lifecycle.signal.aborted)
24336
+ return done();
24337
+ const controller = new AbortController;
24338
+ const timeout = setTimeout(() => controller.abort(), 1e4);
24339
+ timeout.unref?.();
24340
+ executeAutoSearchWorker(db, sessionId, projectPath, query, { signal: AbortSignal.any([controller.signal, lifecycle.signal]) }, new URL(new URL(import.meta.url).pathname.endsWith(".ts") ? "./auto-search-worker.ts" : "./auto-search-worker.js", import.meta.url), "backfill").catch((error) => log("[auto-search] background backfill failed:", error)).finally(() => {
24341
+ clearTimeout(timeout);
24342
+ done();
24343
+ });
24344
+ }, 0);
24345
+ const cancel = () => {
24346
+ clearTimeout(queued);
24347
+ done();
24348
+ };
24349
+ lifecycle.signal.addEventListener("abort", cancel, { once: true });
24350
+ queued.unref?.();
24351
+ });
24352
+ pending.set(key, work);
24353
+ return work;
24354
+ }
24355
+ async function executeAutoSearchWorker(db, sessionId, projectPath, query, options, entry, job) {
24356
+ if (options.signal?.aborted)
24357
+ return [];
24358
+ const path = getSqliteDatabasePath(db);
24359
+ if (!path)
24360
+ throw new Error("auto-search requires a file-backed database for off-thread search");
24361
+ const {
24362
+ embedQuery,
24363
+ isEmbeddingRuntimeEnabled,
24364
+ signal: deadlineSignal,
24365
+ readMessages: _readMessages,
24366
+ ...serializable
24367
+ } = options;
24368
+ const lifecycle = registerAutoSearchWork(sessionId);
24369
+ const signal = deadlineSignal ? AbortSignal.any([deadlineSignal, lifecycle.signal]) : lifecycle.signal;
24370
+ const snapshot = getProjectEmbeddingSnapshot(projectPath);
24371
+ const input = {
24372
+ job,
24373
+ path,
24374
+ harness: getHarness(),
24375
+ sessionId,
24376
+ projectPath,
24377
+ query,
24378
+ options: {
24379
+ ...serializable,
24380
+ backfillMemoryEmbeddings: false,
24381
+ countRetrievals: false,
24382
+ measurementDisabled: true
24383
+ },
24384
+ embeddingRuntimeEnabled: isEmbeddingRuntimeEnabled?.() ?? false,
24385
+ embeddingHostBusy: isEmbeddingHostBusy(),
24386
+ snapshot,
24387
+ deadlineUnixMs: autoSearchDeadlineUnixMs(deadlineSignal)
24388
+ };
24389
+ return new Promise((resolve, reject) => {
24390
+ const worker = new Worker(entry, { workerData: input });
24391
+ let settled = false;
24392
+ const finish = (results, error) => {
24393
+ if (settled)
24394
+ return;
24395
+ settled = true;
24396
+ signal?.removeEventListener("abort", abort);
24397
+ lifecycle.signal.removeEventListener("abort", abort);
24398
+ lifecycle.done();
24399
+ worker.terminate();
24400
+ worker.unref();
24401
+ if (error)
24402
+ reject(error);
24403
+ else
24404
+ resolve(results);
24405
+ };
24406
+ const abort = () => finish([]);
24407
+ signal?.addEventListener("abort", abort, { once: true });
24408
+ lifecycle.signal.addEventListener("abort", abort, { once: true });
24409
+ worker.on("error", (error) => finish([], error));
24410
+ worker.on("exit", (code) => finish([], new Error(`auto-search worker exited (${code})`)));
24411
+ worker.on("message", async (reply) => {
24412
+ if (settled)
24413
+ return;
24414
+ if (reply.kind === "result") {
24415
+ if (snapshot && getProjectEmbeddingSnapshot(projectPath)?.generation !== snapshot.generation)
24416
+ finish([]);
24417
+ else
24418
+ finish(reply.results);
24419
+ return;
24420
+ }
24421
+ if (reply.kind === "error") {
24422
+ finish([], new Error(reply.error));
24423
+ return;
24424
+ }
24425
+ const response = { id: reply.id };
24426
+ try {
24427
+ if (reply.kind === "query")
24428
+ response.result = await (job === "backfill" ? embedTextForProject(projectPath, reply.text, signal, "query") : embedQuery?.(reply.text, signal)) ?? null;
24429
+ else {
24430
+ const passage = await embedBatchForProject(projectPath, reply.texts, signal, reply.purpose);
24431
+ const current = getProjectEmbeddingSnapshot(projectPath);
24432
+ response.passage = passage && current ? {
24433
+ ...passage,
24434
+ providerIdentity: current.providerIdentity,
24435
+ runtimeFingerprint: current.runtimeFingerprint,
24436
+ dimensions: passage.vectors.find((vector) => vector !== null)?.length ?? null
24437
+ } : null;
24438
+ }
24439
+ } catch (error) {
24440
+ response.error = error instanceof Error ? error.message : String(error);
24441
+ }
24442
+ response.embeddingHostBusy = isEmbeddingHostBusy();
24443
+ if (!settled && !signal?.aborted)
24444
+ worker.postMessage(response);
24445
+ });
24446
+ if (signal?.aborted)
24447
+ abort();
24448
+ });
24449
+ }
24450
+
23625
24451
  // src/auto-search-pi.ts
23626
24452
  var DEFAULT_SCORE_THRESHOLD = 0.55;
23627
24453
  var DEFAULT_MIN_PROMPT_CHARS = 20;
@@ -23716,6 +24542,8 @@ function appendHintToUserMessage(message, hint) {
23716
24542
  }
23717
24543
  async function runAutoSearchHintForPi(args) {
23718
24544
  const startedAt = performance.now();
24545
+ args.passGuard?.assert();
24546
+ const wait = args.passGuard?.wait ?? ((pending) => pending);
23719
24547
  const { sessionId, db, messages, options, entryIdByRef } = args;
23720
24548
  const entryIds = args.entryIds === undefined ? messages.map((message, index) => {
23721
24549
  const timestamp = message.timestamp;
@@ -23732,118 +24560,165 @@ async function runAutoSearchHintForPi(args) {
23732
24560
  if (found === null)
23733
24561
  return messages;
23734
24562
  const { message: userMsg, messageId: userMsgId } = found;
23735
- const existing = args.decisions ?? getAutoSearchHintDecisions(db, sessionId);
23736
- const existingForMessage = existing.find((decision) => decision.messageId === userMsgId);
23737
- if (existingForMessage) {
23738
- if (existingForMessage.decision === "hint") {
23739
- appendHintToUserMessage(userMsg, existingForMessage.text);
24563
+ await wait(coalesceAutoSearchTurn(db, sessionId, userMsgId, async (lifecycleSignal) => {
24564
+ args.passGuard?.assert();
24565
+ if (lifecycleSignal.aborted)
24566
+ return messages;
24567
+ if (wasAutoSearchSkipped(db, sessionId, userMsgId))
24568
+ return messages;
24569
+ const existing = args.decisions ?? getAutoSearchHintDecisions(db, sessionId);
24570
+ const existingForMessage = existing.find((decision) => decision.messageId === userMsgId) ?? (args.decisions ? getAutoSearchHintDecisions(db, sessionId).find((decision) => decision.messageId === userMsgId) : undefined);
24571
+ if (existingForMessage) {
24572
+ if (existingForMessage.decision === "hint") {
24573
+ appendHintToUserMessage(userMsg, existingForMessage.text);
24574
+ }
24575
+ return messages;
23740
24576
  }
23741
- return messages;
23742
- }
23743
- if (strictResolutionFailed) {
23744
- sessionLog(sessionId, "Pi auto-search: strict entry-id resolution failed; replayed persisted decisions only");
23745
- return messages;
23746
- }
23747
- if (found.index !== messages.length - 1)
23748
- return messages;
23749
- if (!isUsableProjectIdentity(options.projectPath))
23750
- return messages;
23751
- const writeNoHintAndReconcile = (reason) => {
23752
- const outcome = appendAutoSearchHintDecision(db, sessionId, {
23753
- messageId: userMsgId,
23754
- decision: "no-hint",
23755
- reason
23756
- });
23757
- if (!outcome.ok)
23758
- return;
23759
- if (outcome.kind === "already-present" && outcome.decision.decision === "hint") {
23760
- appendHintToUserMessage(userMsg, outcome.decision.text);
24577
+ if (strictResolutionFailed) {
24578
+ sessionLog(sessionId, "Pi auto-search: strict entry-id resolution failed; replayed persisted decisions only");
24579
+ return messages;
23761
24580
  }
23762
- };
23763
- const rawPartsText = collectUserPromptParts(userMsg);
23764
- if (hasStackedAugmentation(rawPartsText)) {
23765
- sessionLog(sessionId, "auto-search: skipping — user message already carries augmentation/hint");
23766
- writeNoHintAndReconcile("stacked");
23767
- return messages;
23768
- }
23769
- const rawPrompt = extractUserPromptText(userMsg);
23770
- const minPromptChars = options.minPromptChars ?? DEFAULT_MIN_PROMPT_CHARS;
23771
- if (rawPrompt.length < minPromptChars) {
23772
- writeNoHintAndReconcile("too-short");
23773
- return messages;
23774
- }
23775
- let results;
23776
- try {
23777
- results = await withAutoSearchDeadline(async (signal, checkDeadline) => {
23778
- await args.ensureProjectRegistered?.();
23779
- if (checkDeadline())
23780
- return null;
23781
- const snapshot = getProjectEmbeddingSnapshot(options.projectPath);
23782
- const memoryEnabled = snapshot?.features.memoryEnabled ?? true;
23783
- const embeddingEnabled = snapshot ? snapshot.historyEnabled : true;
23784
- const gitCommitsEnabled = snapshot?.gitCommitEnabled ?? false;
23785
- const searchOptions = {
23786
- limit: 10,
23787
- memoryEnabled,
23788
- embeddingEnabled,
23789
- gitCommitsEnabled,
23790
- embedQuery: async (text, signal) => {
23791
- const result = await embedTextForProject(options.projectPath, text, signal, "query");
23792
- checkDeadline();
23793
- return result?.vector ?? null;
23794
- },
23795
- isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
23796
- visibleMemoryIds: options.visibleMemoryIds ?? null,
23797
- sources: ["memory", "message", "git_commit"]
23798
- };
23799
- return unifiedSearch(db, sessionId, options.projectPath, rawPrompt, {
23800
- ...searchOptions,
23801
- signal,
23802
- countRetrievals: false
24581
+ if (found.index !== messages.length - 1)
24582
+ return messages;
24583
+ if (!isUsableProjectIdentity(options.projectPath))
24584
+ return messages;
24585
+ const snapshot = getProjectEmbeddingSnapshot(options.projectPath);
24586
+ if (!snapshot) {
24587
+ queueAutoSearchRegistration(db, options.projectPath, args.ensureProjectRegistered);
24588
+ persistAutoSearchSkip(db, sessionId, userMsgId, "empty");
24589
+ return messages;
24590
+ }
24591
+ const writeNoHintAndReconcile = async (reason) => {
24592
+ args.passGuard?.assert();
24593
+ const outcome = persistAutoSearchDecision(db, sessionId, {
24594
+ messageId: userMsgId,
24595
+ decision: "no-hint",
24596
+ reason
24597
+ }, startedAt);
24598
+ args.passGuard?.assert();
24599
+ if (outcome === null) {
24600
+ persistAutoSearchSkip(db, sessionId, userMsgId);
24601
+ return;
24602
+ }
24603
+ if (!outcome.ok)
24604
+ return;
24605
+ if (outcome.kind === "already-present" && outcome.decision.decision === "hint") {
24606
+ appendHintToUserMessage(userMsg, outcome.decision.text);
24607
+ }
24608
+ };
24609
+ const rawPartsText = collectUserPromptParts(userMsg);
24610
+ if (hasStackedAugmentation(rawPartsText)) {
24611
+ sessionLog(sessionId, "auto-search: skipping — user message already carries augmentation/hint");
24612
+ await writeNoHintAndReconcile("stacked");
24613
+ return messages;
24614
+ }
24615
+ const rawPrompt = extractUserPromptText(userMsg);
24616
+ const minPromptChars = options.minPromptChars ?? DEFAULT_MIN_PROMPT_CHARS;
24617
+ if (rawPrompt.length < minPromptChars) {
24618
+ await writeNoHintAndReconcile("too-short");
24619
+ return messages;
24620
+ }
24621
+ let results;
24622
+ try {
24623
+ results = await wait(withAutoSearchDeadline(async (signal, checkDeadline) => {
24624
+ args.passGuard?.assert();
24625
+ if (checkDeadline())
24626
+ return null;
24627
+ const memoryEnabled = snapshot?.features.memoryEnabled ?? true;
24628
+ const embeddingEnabled = snapshot ? snapshot.historyEnabled : true;
24629
+ const gitCommitsEnabled = snapshot?.gitCommitEnabled ?? false;
24630
+ const searchOptions = {
24631
+ limit: 10,
24632
+ memoryEnabled,
24633
+ embeddingEnabled,
24634
+ gitCommitsEnabled,
24635
+ embedQuery: async (text, signal) => {
24636
+ const result = await wait(embedTextForProject(options.projectPath, text, signal, "query"));
24637
+ args.passGuard?.assert();
24638
+ checkDeadline();
24639
+ return result;
24640
+ },
24641
+ isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
24642
+ visibleMemoryIds: options.visibleMemoryIds ?? null,
24643
+ sources: ["memory", "message", "git_commit"]
24644
+ };
24645
+ return searchAutoHint(db, sessionId, options.projectPath, rawPrompt, {
24646
+ ...searchOptions,
24647
+ signal,
24648
+ countRetrievals: false
24649
+ });
24650
+ }, startedAt));
24651
+ } catch (error) {
24652
+ args.passGuard?.assert();
24653
+ log(`[auto-search] unified search failed for session ${sessionId} (will retry next pass): ${error instanceof Error ? error.message : String(error)}`);
24654
+ return messages;
24655
+ }
24656
+ args.passGuard?.assert();
24657
+ if (lifecycleSignal.aborted)
24658
+ return messages;
24659
+ try {
24660
+ if (results === null) {
24661
+ sessionLog(sessionId, `auto-search: timed out after ${AUTO_SEARCH_TIMEOUT_MS}ms, skipping hint for this turn`);
24662
+ persistAutoSearchSkip(db, sessionId, userMsgId);
24663
+ return messages;
24664
+ }
24665
+ if (results.length === 0) {
24666
+ await writeNoHintAndReconcile("empty");
24667
+ return messages;
24668
+ }
24669
+ const scoreThreshold = options.scoreThreshold ?? DEFAULT_SCORE_THRESHOLD;
24670
+ if (results[0].score < scoreThreshold) {
24671
+ sessionLog(sessionId, `auto-search: top score ${results[0].score.toFixed(3)} below threshold ${scoreThreshold}`);
24672
+ await writeNoHintAndReconcile("below-threshold");
24673
+ return messages;
24674
+ }
24675
+ const hintText = buildAutoSearchHint(results, {
24676
+ wordRules: options.wordRules
23803
24677
  });
23804
- }, startedAt);
23805
- } catch (error) {
23806
- log(`[auto-search] unified search failed for session ${sessionId} (will retry next pass): ${error instanceof Error ? error.message : String(error)}`);
23807
- return messages;
23808
- }
23809
- if (results === null) {
23810
- sessionLog(sessionId, `auto-search: timed out after ${AUTO_SEARCH_TIMEOUT_MS}ms, skipping hint for this turn (will retry)`);
23811
- return messages;
23812
- }
23813
- if (results.length === 0) {
23814
- writeNoHintAndReconcile("empty");
23815
- return messages;
23816
- }
23817
- const scoreThreshold = options.scoreThreshold ?? DEFAULT_SCORE_THRESHOLD;
23818
- if (results[0].score < scoreThreshold) {
23819
- sessionLog(sessionId, `auto-search: top score ${results[0].score.toFixed(3)} below threshold ${scoreThreshold}`);
23820
- writeNoHintAndReconcile("below-threshold");
23821
- return messages;
23822
- }
23823
- const hintText = buildAutoSearchHint(results, {
23824
- wordRules: options.wordRules
23825
- });
23826
- if (!hintText) {
23827
- writeNoHintAndReconcile("empty");
23828
- return messages;
23829
- }
23830
- const payload = `
24678
+ if (!hintText) {
24679
+ await writeNoHintAndReconcile("empty");
24680
+ return messages;
24681
+ }
24682
+ const payload = `
23831
24683
 
23832
24684
  ${hintText}`;
23833
- const outcome = appendAutoSearchHintDecision(db, sessionId, {
23834
- messageId: userMsgId,
23835
- decision: "hint",
23836
- text: payload
23837
- });
23838
- if (!outcome.ok)
23839
- return messages;
23840
- if (outcome.decision.decision === "hint") {
23841
- appendHintToUserMessage(userMsg, outcome.decision.text);
23842
- }
23843
- sessionLog(sessionId, `auto-search: attached hint to ${userMsgId} (${results.length} fragments, top score ${results[0].score.toFixed(3)})`);
24685
+ args.passGuard?.assert();
24686
+ const outcome = persistAutoSearchDecision(db, sessionId, {
24687
+ messageId: userMsgId,
24688
+ decision: "hint",
24689
+ text: payload
24690
+ }, startedAt);
24691
+ args.passGuard?.assert();
24692
+ if (outcome === null) {
24693
+ persistAutoSearchSkip(db, sessionId, userMsgId);
24694
+ return messages;
24695
+ }
24696
+ if (!outcome.ok)
24697
+ return messages;
24698
+ if (outcome.decision.decision === "hint") {
24699
+ appendHintToUserMessage(userMsg, outcome.decision.text);
24700
+ }
24701
+ sessionLog(sessionId, `auto-search: attached hint to ${userMsgId} (${results.length} fragments, top score ${results[0].score.toFixed(3)})`);
24702
+ return messages;
24703
+ } finally {
24704
+ if (!lifecycleSignal.aborted && results !== null) {
24705
+ args.passGuard?.assert();
24706
+ queueAutoSearchBackfill(db, sessionId, options.projectPath, rawPrompt);
24707
+ }
24708
+ }
24709
+ }, () => {
24710
+ args.passGuard?.assert();
24711
+ if (wasAutoSearchSkipped(db, sessionId, userMsgId))
24712
+ return;
24713
+ const decision = getAutoSearchHintDecisions(db, sessionId).find((entry) => entry.messageId === userMsgId);
24714
+ if (decision?.decision === "hint")
24715
+ appendHintToUserMessage(userMsg, decision.text);
24716
+ }));
23844
24717
  return messages;
23845
24718
  }
23846
- function clearAutoSearchForPiSession(_sessionId) {}
24719
+ function clearAutoSearchForPiSession(sessionId) {
24720
+ clearAutoSearchTimeoutForSession(sessionId);
24721
+ }
23847
24722
 
23848
24723
  // src/pi-historian-runner.ts
23849
24724
  import * as crypto2 from "node:crypto";
@@ -23908,51 +24783,6 @@ async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compar
23908
24783
  }
23909
24784
  }
23910
24785
 
23911
- // ../plugin/src/features/magic-context/storage-historian-runs.ts
23912
- function recordHistorianRun(db, input) {
23913
- try {
23914
- const result = db.prepare(`INSERT INTO historian_runs (
23915
- session_id, harness, subagent_invocation_id, run_kind, status,
23916
- failure_reason, chunk_start_ordinal, chunk_end_ordinal, unprocessed_from,
23917
- compartments_produced, compartment_id_min, compartment_id_max,
23918
- facts_emitted, facts_by_category_json, events_emitted,
23919
- importance_min, importance_max, importance_avg,
23920
- discarded_last, legacy, created_at
23921
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.sessionId, input.harness, input.subagentInvocationId ?? null, input.runKind, input.status, input.failureReason ?? null, input.chunkStartOrdinal ?? null, input.chunkEndOrdinal ?? null, input.unprocessedFrom ?? null, input.compartmentsProduced ?? 0, input.compartmentIdMin ?? null, input.compartmentIdMax ?? null, input.factsEmitted ?? 0, JSON.stringify({
23922
- ...input.factsByCategory ?? {},
23923
- facts_promoted: input.factsPromoted ?? 0,
23924
- events_published: input.eventsPublished ?? 0
23925
- }), input.eventsEmitted ?? 0, input.importanceMin ?? null, input.importanceMax ?? null, input.importanceAvg ?? null, input.discardedLast ? 1 : 0, input.legacy ? 1 : 0, Date.now());
23926
- return Number(result.lastInsertRowid);
23927
- } catch {
23928
- return null;
23929
- }
23930
- }
23931
- function summarizeImportance(values) {
23932
- const nums = values.filter((v) => typeof v === "number" && Number.isFinite(v));
23933
- if (nums.length === 0)
23934
- return { min: null, max: null, avg: null };
23935
- let min = nums[0];
23936
- let max = nums[0];
23937
- let sum = 0;
23938
- for (const v of nums) {
23939
- if (v < min)
23940
- min = v;
23941
- if (v > max)
23942
- max = v;
23943
- sum += v;
23944
- }
23945
- return { min, max, avg: sum / nums.length };
23946
- }
23947
- function tallyFactsByCategory(facts) {
23948
- const out = {};
23949
- for (const f of facts) {
23950
- const cat = (f.category ?? "UNKNOWN").trim() || "UNKNOWN";
23951
- out[cat] = (out[cat] ?? 0) + 1;
23952
- }
23953
- return out;
23954
- }
23955
-
23956
24786
  // ../plugin/src/hooks/magic-context/compartment-runner-drop-queue.ts
23957
24787
  function prepareCompartmentDrops(db, sessionId, upToMessageIndex, observedKeys, fromMessageIndex = 1) {
23958
24788
  const candidates = getActiveTagsBySession(db, sessionId).filter((tag) => {
@@ -28103,9 +28933,6 @@ function probe(db, sessionId, kind) {
28103
28933
  function hasPiFallbackMessageTags2(db, sessionId) {
28104
28934
  return probe(db, sessionId, "message");
28105
28935
  }
28106
- function hasPiFallbackToolOwnerTags2(db, sessionId) {
28107
- return probe(db, sessionId, "tool");
28108
- }
28109
28936
 
28110
28937
  // src/heuristic-cleanup-pi.ts
28111
28938
  var DEDUP_SAFE_TOOLS = new Set([
@@ -28366,6 +29193,8 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
28366
29193
  continue;
28367
29194
  const strippedSource = stripTagPrefix(stripped);
28368
29195
  if (strippedSource.trim().length === 0) {
29196
+ if (target.thinkingDropProtected)
29197
+ continue;
28369
29198
  const dropResult = target.drop?.() ?? "absent";
28370
29199
  const didReplace = dropResult === "absent" ? target.setContent(`[dropped §${tag.tagNumber}§]`) : false;
28371
29200
  if (dropResult === "removed" || dropResult === "absent") {
@@ -28380,7 +29209,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
28380
29209
  }
28381
29210
  } else {
28382
29211
  if (freezePiContentDecision(db, sessionId, "reminder-strip", tag.messageId)) {
28383
- if (target.setContent(stripped)) {
29212
+ if (target.setContent(stripped, { keepReasoning: true })) {
28384
29213
  droppedInjections++;
28385
29214
  droppedTokenReductions.push({
28386
29215
  tagNumber: tag.tagNumber,
@@ -30053,10 +30882,7 @@ function applyNativeToolInputReplayPi(args, saved) {
30053
30882
  function applyNativeReasoningReplayPi(args, saved) {
30054
30883
  if (!args.omissionAllowed)
30055
30884
  return 0;
30056
- let maxTag = 0;
30057
- for (const tag of args.messageIdToMaxTag.values())
30058
- maxTag = Math.max(maxTag, tag);
30059
- const cutoff = args.detectAged ? Math.max(args.localWatermark, maxTag - args.clearReasoningAge) : args.localWatermark;
30885
+ const cutoff = args.detectAged ? Math.max(args.localWatermark, args.budgetCutoff) : args.localWatermark;
30060
30886
  const nextIds = new Set;
30061
30887
  const pending = new Map;
30062
30888
  for (let index = 0;index < args.messages.length; index++) {
@@ -30169,6 +30995,83 @@ function isSentinelPart(part) {
30169
30995
  return p.type === "text" && typeof p.text === "string" && (p.text === "" || p.text === WHOLE_MESSAGE_PLACEHOLDER_TEXT);
30170
30996
  }
30171
30997
 
30998
+ // src/pi-context-budget.ts
30999
+ var PI_CONTEXT_BUDGET = {
31000
+ hostDeadlineMs: 30000,
31001
+ hostMarginMs: 5000,
31002
+ outcomeMs: 25000,
31003
+ fallbackObservedMs: 4,
31004
+ workMs: 21000,
31005
+ writerMs: 16500,
31006
+ completionReserveMs: 2000,
31007
+ searchMs: 3000
31008
+ };
31009
+
31010
+ class PiContextDeadlineError extends Error {
31011
+ }
31012
+
31013
+ class PiContextBudget {
31014
+ startedAt;
31015
+ now;
31016
+ sideTurn = false;
31017
+ stage = "entry";
31018
+ recovery = "not attempted";
31019
+ abandoned = false;
31020
+ completed = false;
31021
+ failureReason;
31022
+ assertOwner = () => {};
31023
+ constructor(startedAt = performance.now(), now = () => performance.now()) {
31024
+ this.startedAt = startedAt;
31025
+ this.now = now;
31026
+ }
31027
+ elapsed() {
31028
+ return this.now() - this.startedAt;
31029
+ }
31030
+ remainingWork() {
31031
+ return Math.max(0, PI_CONTEXT_BUDGET.workMs - this.elapsed());
31032
+ }
31033
+ remainingOutcome = () => Math.max(0, PI_CONTEXT_BUDGET.outcomeMs - this.elapsed());
31034
+ writerAllowance() {
31035
+ return Math.max(0, Math.min(PI_CONTEXT_BUDGET.writerMs, this.remainingWork() - PI_CONTEXT_BUDGET.completionReserveMs));
31036
+ }
31037
+ assertOutcome = () => {
31038
+ this.assertOwner();
31039
+ if (this.abandoned || this.elapsed() >= PI_CONTEXT_BUDGET.outcomeMs)
31040
+ throw new PiContextDeadlineError(this.diagnostic());
31041
+ };
31042
+ assert = () => {
31043
+ this.assertOutcome();
31044
+ if (this.elapsed() >= PI_CONTEXT_BUDGET.workMs)
31045
+ throw new PiContextDeadlineError(this.diagnostic());
31046
+ };
31047
+ assertDecisionPublication = () => {
31048
+ if (!this.completed)
31049
+ this.assertOwner();
31050
+ if (this.abandoned || this.elapsed() >= PI_CONTEXT_BUDGET.outcomeMs)
31051
+ throw new PiContextDeadlineError(this.diagnostic());
31052
+ };
31053
+ diagnostic() {
31054
+ return `stage=${this.stage} elapsed=${Math.round(this.elapsed())}ms recovery=${this.recovery}`;
31055
+ }
31056
+ waitUntil = async (pending, assert, remaining) => {
31057
+ assert();
31058
+ let timer;
31059
+ try {
31060
+ return await Promise.race([
31061
+ pending,
31062
+ new Promise((_, reject) => {
31063
+ timer = setTimeout(() => reject(new PiContextDeadlineError(this.diagnostic())), remaining());
31064
+ })
31065
+ ]);
31066
+ } finally {
31067
+ clearTimeout(timer);
31068
+ assert();
31069
+ }
31070
+ };
31071
+ wait = (pending) => this.waitUntil(pending, this.assert, () => this.remainingWork());
31072
+ waitMandatory = (pending) => this.waitUntil(pending, this.assertOutcome, this.remainingOutcome);
31073
+ }
31074
+
30172
31075
  // src/pi-context-limit.ts
30173
31076
  var MIN_SANE_LIMIT = 16000;
30174
31077
  var MAX_SANE_LIMIT = 1e7;
@@ -30179,9 +31082,7 @@ function resolvePiWindowGeometry(args) {
30179
31082
  const rawWindow = isSaneLimit2(args.rawContextWindow) ? args.rawContextWindow : undefined;
30180
31083
  const observedWindow = args.rawContextWindowSource === "catalog" ? undefined : rawWindow;
30181
31084
  const catalogWindow = args.rawContextWindowSource === "catalog" && rawWindow !== undefined ? rawWindow : isSaneLimit2(args.model?.contextWindow) ? args.model.contextWindow : undefined;
30182
- const persistedUsable = isSaneLimit2(args.persistedInputTokens) && typeof args.persistedPercentage === "number" && Number.isFinite(args.persistedPercentage) && args.persistedPercentage > 0 ? args.persistedInputTokens / (args.persistedPercentage / 100) : undefined;
30183
- const persistedWindow = isSaneLimit2(args.persistedInputTokens) && typeof args.persistedPercentage === "number" && Number.isFinite(args.persistedPercentage) && args.persistedPercentage > 0 ? args.persistedInputTokens / (args.persistedPercentage / 100) : undefined;
30184
- const context = observedWindow ?? catalogWindow ?? persistedWindow;
31085
+ const context = observedWindow ?? catalogWindow ?? args.provenInputTokens;
30185
31086
  if (!isSaneLimit2(context))
30186
31087
  return;
30187
31088
  const providerID = args.model?.provider ?? "unknown";
@@ -30200,20 +31101,16 @@ function resolvePiWindowGeometry(args) {
30200
31101
  if (!result)
30201
31102
  return result;
30202
31103
  let resolved = result;
30203
- if (outputReserveOverride === undefined && isSaneLimit2(persistedUsable)) {
30204
- const persistedDenominator = Math.round(persistedUsable);
30205
- if (persistedDenominator < resolved.usableSoft) {
30206
- resolved = {
30207
- ...resolved,
30208
- usableSoft: persistedDenominator,
30209
- derivation: {
30210
- ...resolved.derivation,
30211
- reserve: Math.max(0, resolved.derivation.window - persistedDenominator)
30212
- }
30213
- };
30214
- } else {
30215
- resolved = applyProvenInputFloor(resolved, persistedDenominator).geometry;
30216
- }
31104
+ const persistedUsable = isSaneLimit2(args.persistedInputTokens) && typeof args.persistedPercentage === "number" && Number.isFinite(args.persistedPercentage) && args.persistedPercentage > 0 ? Math.round(args.persistedInputTokens / (args.persistedPercentage / 100)) : undefined;
31105
+ if (outputReserveOverride === undefined && isSaneLimit2(persistedUsable) && persistedUsable < resolved.usableSoft) {
31106
+ resolved = {
31107
+ ...resolved,
31108
+ usableSoft: persistedUsable,
31109
+ derivation: {
31110
+ ...resolved.derivation,
31111
+ reserve: Math.max(0, resolved.derivation.window - persistedUsable)
31112
+ }
31113
+ };
30217
31114
  }
30218
31115
  if (!isSaneLimit2(args.detectedContextLimit) && isSaneLimit2(args.provenInputTokens)) {
30219
31116
  resolved = applyProvenInputFloor(resolved, args.provenInputTokens).geometry;
@@ -30226,32 +31123,183 @@ function resolvePiUsableContextLimit(args) {
30226
31123
 
30227
31124
  // src/pi-context-refusal.ts
30228
31125
  import { Text as Text2 } from "@earendil-works/pi-tui";
31126
+
31127
+ // src/omp-request-kind.ts
31128
+ var SIDE_REMINDER = `<system-reminder>
31129
+ Ephemeral side-channel turn; reuses current conversation context.
31130
+ Tool catalog attached only to keep prompt cache warm; tools NOT available this turn.
31131
+ Do NOT emit tool calls; reply plain text only. Tool calls discarded without execution.
31132
+ </system-reminder>`;
31133
+ function textContainsReminder(value) {
31134
+ if (typeof value === "string")
31135
+ return value.trim() === SIDE_REMINDER;
31136
+ if (!Array.isArray(value))
31137
+ return false;
31138
+ return value.some((block) => {
31139
+ if (!block || typeof block !== "object")
31140
+ return false;
31141
+ const part = block;
31142
+ return typeof part.text === "string" && part.text.trim() === SIDE_REMINDER;
31143
+ });
31144
+ }
31145
+ function isOmpSideContext(event) {
31146
+ const tail = event.messages.at(-1);
31147
+ if (tail?.role !== "user" || tail.attribution !== "agent")
31148
+ return false;
31149
+ return event.messages.some((message) => {
31150
+ const row = message;
31151
+ return row.role === "developer" && row.attribution === "agent" && textContainsReminder(row.content);
31152
+ });
31153
+ }
31154
+
31155
+ // src/pi-context-refusal.ts
30229
31156
  var ENTRY_TYPE = "magic-context-turn-refused";
30230
31157
  var RETRY_MESSAGE = "Magic Context could not safely prepare this turn; send your message again.";
30231
- function registerPiGuardedContext(pi, handler) {
31158
+
31159
+ class PiContextSupersededError extends Error {
31160
+ }
31161
+ var receiptMaps = new Set;
31162
+ function clearPiContextReceipt(sessionId) {
31163
+ for (const receipts of receiptMaps)
31164
+ receipts.delete(sessionId);
31165
+ }
31166
+ function operationKey(ctx) {
31167
+ return ctx.sessionManager?.getSessionId?.() ?? ctx.sessionManager ?? "no-session";
31168
+ }
31169
+ function registerPiGuardedContext(pi, handler, options = {}) {
31170
+ const operations = new Map;
31171
+ const sideSessions = new Map;
31172
+ receiptMaps.add(operations);
31173
+ const newOperation = () => ({
31174
+ ready: false,
31175
+ refused: false,
31176
+ ended: false
31177
+ });
31178
+ const fenceEnabled = resolvePiHarnessKind() === "omp";
31179
+ const refuse = (ctx, budget, reason, isCurrent) => {
31180
+ if (!isCurrent()) {
31181
+ log(`[magic-context][pi] DISCARDED CONTEXT RESULT ${budget.diagnostic()}`, reason);
31182
+ return;
31183
+ }
31184
+ budget.abandoned = true;
31185
+ budget.recovery = budget.recovery === "not attempted" ? "no managed result; refused" : budget.recovery;
31186
+ const message = isCheckoutClaimRefusalError(reason) ? reason.message : `${RETRY_MESSAGE} ${budget.diagnostic()} (${reason instanceof Error ? reason.message : String(reason)})`;
31187
+ try {
31188
+ ctx.ui?.notify(message, "error");
31189
+ } catch (error) {
31190
+ log("[magic-context][pi] refusal notice failed", error);
31191
+ }
31192
+ try {
31193
+ pi.appendEntry(ENTRY_TYPE, { message });
31194
+ } catch (error) {
31195
+ log("[magic-context][pi] refusal entry failed", error);
31196
+ } finally {
31197
+ if (!budget.sideTurn)
31198
+ ctx.abort();
31199
+ }
31200
+ if (isCheckoutClaimRefusalError(reason))
31201
+ return;
31202
+ try {
31203
+ options.onRefusal?.(ctx, budget, message, isCurrent);
31204
+ } catch (error) {
31205
+ log("[magic-context][pi] refusal diagnostic failed", error);
31206
+ }
31207
+ };
31208
+ if (fenceEnabled) {
31209
+ pi.on("agent_start", (_event, ctx) => {
31210
+ operations.set(operationKey(ctx), newOperation());
31211
+ });
31212
+ pi.on("agent_end", (_event, ctx) => {
31213
+ const key = operationKey(ctx);
31214
+ const counts = sideSessions.get(key);
31215
+ if (counts)
31216
+ log(`[magic-context][pi] OMP context counts session=${String(key)} side_contexts=${counts.sides} context_passes=${counts.contexts} dispatch_fence=${counts.sides ? "latched off" : "on"}`);
31217
+ const operation = operations.get(key);
31218
+ if (operation) {
31219
+ operation.ended = true;
31220
+ operation.ready = false;
31221
+ }
31222
+ });
31223
+ pi.on("before_provider_request", (_event, ctx) => {
31224
+ if (options.compactionOff?.(ctx))
31225
+ return;
31226
+ const key = operationKey(ctx);
31227
+ if (sideSessions.get(key)?.sides)
31228
+ return;
31229
+ let operation = operations.get(key);
31230
+ if (!operation) {
31231
+ operation = newOperation();
31232
+ operations.set(key, operation);
31233
+ }
31234
+ const active = operation;
31235
+ const isCurrent = () => operations.get(key) === active;
31236
+ const budget = active.pass ?? new PiContextBudget;
31237
+ try {
31238
+ budget.assertOwner();
31239
+ if (!budget.completed)
31240
+ budget.assertOutcome();
31241
+ if (!active.ready || active.refused || active.ended || budget.abandoned)
31242
+ throw new Error("missing managed context receipt");
31243
+ } catch (error) {
31244
+ active.refused = true;
31245
+ budget.stage = "provider dispatch";
31246
+ refuse(ctx, budget, error, isCurrent);
31247
+ }
31248
+ });
31249
+ }
30232
31250
  pi.registerEntryRenderer?.(ENTRY_TYPE, (entry) => new Text2(entry.data?.message ?? RETRY_MESSAGE, 0, 0));
30233
31251
  pi.on("context", async (event, ctx) => {
31252
+ const budget = new PiContextBudget;
31253
+ budget.sideTurn = fenceEnabled && isOmpSideContext(event);
31254
+ const key = operationKey(ctx);
31255
+ if (fenceEnabled) {
31256
+ let counts = sideSessions.get(key);
31257
+ if (!counts) {
31258
+ counts = { contexts: 0, sides: 0 };
31259
+ sideSessions.set(key, counts);
31260
+ }
31261
+ counts.contexts++;
31262
+ if (budget.sideTurn && ++counts.sides === 1)
31263
+ log(`[magic-context][pi] OMP dispatch fence disabled for session ${String(key)}: verified ephemeral context; session-wide abort cannot distinguish provider operations (side_contexts=${counts.sides} context_passes=${counts.contexts})`);
31264
+ }
31265
+ const owner = operations.get(key);
31266
+ let operation = operations.get(key);
31267
+ if (budget.sideTurn || !operation || !fenceEnabled) {
31268
+ operation = newOperation();
31269
+ if (!budget.sideTurn)
31270
+ operations.set(key, operation);
31271
+ }
31272
+ const active = operation;
31273
+ active.pass = budget;
31274
+ active.ready = false;
31275
+ const isOwned = () => budget.sideTurn ? operations.get(key) === owner : operations.get(key) === active && active.pass === budget;
31276
+ const isCurrent = () => isOwned() && (budget.sideTurn || !active.ended);
31277
+ budget.assertOwner = () => {
31278
+ if (!isCurrent())
31279
+ throw new PiContextSupersededError("Pi context pass superseded before writer admission");
31280
+ };
30234
31281
  try {
30235
- return await withSqliteTransformPass(() => handler(event, ctx));
31282
+ const result = await withSqliteTransformPass(() => handler(event, ctx, budget));
31283
+ budget.assertOutcome();
31284
+ if (result?.messages) {
31285
+ budget.completed = true;
31286
+ active.ready = true;
31287
+ }
31288
+ return result;
30236
31289
  } catch (error) {
30237
- log("[magic-context][pi] turn refused", error);
30238
- const message = isCheckoutClaimRefusalError(error) ? error.message : RETRY_MESSAGE;
30239
- if (typeof ctx.abort !== "function")
31290
+ if (error instanceof PiContextSupersededError || !isCurrent()) {
31291
+ log(`[magic-context][pi] DISCARDED CONTEXT RESULT ${budget.diagnostic()}`, error);
30240
31292
  throw error;
30241
- if (message !== RETRY_MESSAGE && ctx.hasUI) {
30242
- try {
30243
- ctx.ui.notify(message, "error");
30244
- } catch (notifyError) {
30245
- log("[magic-context][pi] refusal notice failed", notifyError);
30246
- }
30247
31293
  }
30248
- try {
30249
- pi.appendEntry(ENTRY_TYPE, { message });
30250
- } catch (displayError) {
30251
- log("[magic-context][pi] refusal entry failed", displayError);
30252
- } finally {
30253
- ctx.abort();
31294
+ log("[magic-context][pi] turn refused", error);
31295
+ if (typeof ctx.abort !== "function") {
31296
+ budget.abandoned = true;
31297
+ if (!isCheckoutClaimRefusalError(error))
31298
+ options.onRefusal?.(ctx, budget, error instanceof Error ? error.message : String(error), isOwned);
31299
+ throw error;
30254
31300
  }
31301
+ active.refused = true;
31302
+ refuse(ctx, budget, error, isOwned);
30255
31303
  return { messages: event.messages };
30256
31304
  }
30257
31305
  });
@@ -30786,7 +31834,7 @@ function plainOutputFields(messages) {
30786
31834
  }
30787
31835
  return outputs;
30788
31836
  }
30789
- function snapshotInputs(messages, entryIds) {
31837
+ function snapshotInputs(messages, entryIds, omitsBookkeeping) {
30790
31838
  if (!entryIds || entryIds.length !== messages.length) {
30791
31839
  return { inputs: [], failure: "lkg_entry_ids_unavailable" };
30792
31840
  }
@@ -30797,7 +31845,7 @@ function snapshotInputs(messages, entryIds) {
30797
31845
  const inputs = [];
30798
31846
  const seen = new Set;
30799
31847
  for (let index = 0;index < entryIds.length; index += 1) {
30800
- const fields = lkgContentFields(messages[index]);
31848
+ const fields = lkgContentFields(omitsBookkeeping ? piProviderInput(messages[index]) : messages[index]);
30801
31849
  if (!fields)
30802
31850
  return { inputs: [], failure: "lkg_content_snapshot_failed" };
30803
31851
  const id = entryIds[index] || `pi-lkg-unmapped:${lkgContentDigestFromFields(fields)}`;
@@ -30813,6 +31861,35 @@ function snapshotInputs(messages, entryIds) {
30813
31861
  }
30814
31862
  return { inputs, failure: null };
30815
31863
  }
31864
+ var bookkeepingOmittingApis = new Set([
31865
+ "anthropic-messages",
31866
+ "google-generative-ai",
31867
+ "openai-completions",
31868
+ "openai-responses",
31869
+ "openai-codex-responses"
31870
+ ]);
31871
+ function omitsRootBookkeeping(args) {
31872
+ if (args.transport != null)
31873
+ return false;
31874
+ let api = args.apiKey;
31875
+ if (api === undefined) {
31876
+ const apis = new Set(args.messages.flatMap((message) => {
31877
+ const value = message;
31878
+ return value?.role === "assistant" && typeof value.api === "string" ? [value.api] : [];
31879
+ }));
31880
+ if (apis.size === 1)
31881
+ api = [...apis][0];
31882
+ }
31883
+ return typeof api === "string" && bookkeepingOmittingApis.has(api);
31884
+ }
31885
+ function piProviderInput(message) {
31886
+ if (!message || typeof message !== "object" || Array.isArray(message))
31887
+ return message;
31888
+ const descriptors = Object.getOwnPropertyDescriptors(message);
31889
+ delete descriptors.completedAt;
31890
+ delete descriptors.contextSnapshot;
31891
+ return Object.create(Object.getPrototypeOf(message), descriptors);
31892
+ }
30816
31893
  function extendsMeasuredRequest(measuredJson, nextJson) {
30817
31894
  return measuredJson === nextJson || measuredJson.length > 2 && nextJson.startsWith(`${measuredJson.slice(0, -1)},`);
30818
31895
  }
@@ -30833,7 +31910,7 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
30833
31910
  return state;
30834
31911
  };
30835
31912
  const beginPass = (args) => {
30836
- const snapped = snapshotInputs(args.messages, args.entryIds);
31913
+ const snapped = snapshotInputs(args.messages, args.entryIds, omitsRootBookkeeping(args));
30837
31914
  const slot = getSlot(args.sessionId);
30838
31915
  if (snapped.failure || !slot) {
30839
31916
  return {
@@ -30987,6 +32064,7 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
30987
32064
  return result.ok && measuredPrefix ? { ...result, measuredPrefix } : result;
30988
32065
  };
30989
32066
  const captureAppliedPass = (args) => {
32067
+ args.assertCurrentPass?.();
30990
32068
  const { snapshot } = args;
30991
32069
  if (snapshot.preparationFailure || snapshot.inputs.length === 0)
30992
32070
  return;
@@ -31003,12 +32081,14 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
31003
32081
  ...plainOutput ? args.outputMessages.slice(prefix).map((message) => JSON.stringify(message) ?? "null") : []
31004
32082
  ];
31005
32083
  jsonPrefix = !plainOutput ? JSON.stringify(args.outputMessages) : priorOutput && prefix === outputs.length && prefix === priorOutput.inputs.length ? priorOutput.json : `[${jsonMessages.join(",")}]`;
32084
+ args.assertCurrentPass?.();
31006
32085
  state.outputSnapshot = plainOutput ? {
31007
32086
  inputs: outputs,
31008
32087
  jsonMessages,
31009
32088
  json: jsonPrefix
31010
32089
  } : null;
31011
32090
  } catch (error) {
32091
+ args.assertCurrentPass?.();
31012
32092
  dropSlot(snapshot.sessionId, "lkg_snapshot_serialize_failed");
31013
32093
  const failedState = stateFor(snapshot.sessionId);
31014
32094
  failedState.syncCaptureRequired = true;
@@ -31032,6 +32112,7 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
31032
32112
  const outputIds = args.outputEntryIds ?? inferredIds;
31033
32113
  const inputIds = new Set(snapshot.inputs.map((input) => input.id));
31034
32114
  const ownership = outputIds.length === args.outputMessages.length && outputIds.every((id) => id === null || typeof id === "string" && inputIds.has(id)) ? [...outputIds] : undefined;
32115
+ args.assertCurrentPass?.();
31035
32116
  state.captureSequence += 1;
31036
32117
  const plan = {
31037
32118
  sessionId: snapshot.sessionId,
@@ -31047,6 +32128,7 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
31047
32128
  state.capturedRequest = args.hostEnvelopeSignature ? { ...plan, envelopeSignature: args.hostEnvelopeSignature } : undefined;
31048
32129
  const livePrior = getSlot(snapshot.sessionId);
31049
32130
  const unchanged = livePrior && livePrior.modelKey === plan.modelKey && livePrior.providerKey === plan.providerKey && livePrior.jsonPrefix === plan.jsonPrefix && livePrior.inputIdSeq.length === plan.inputs.length && exactReusablePrefix(plan.inputs, state.acceptedInputs) === plan.inputs.length && JSON.stringify(livePrior.piOutputEntryIds) === JSON.stringify(ownership);
32131
+ args.assertCurrentPass?.();
31050
32132
  if (unchanged && !state.syncCaptureRequired) {
31051
32133
  const kept = {
31052
32134
  ...livePrior,
@@ -31062,6 +32144,12 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
31062
32144
  dropSlot(snapshot.sessionId, "lkg_cache_bust_pending_capture");
31063
32145
  }
31064
32146
  const commit = () => {
32147
+ try {
32148
+ args.assertCurrentPass?.();
32149
+ } catch (error) {
32150
+ sessionLog(plan.sessionId, "DISCARDED CONTEXT RESULT: queued LKG capture", error);
32151
+ return;
32152
+ }
31065
32153
  if (plan.captureSequence !== state.captureSequence)
31066
32154
  return;
31067
32155
  const startedAt = performance.now();
@@ -31090,6 +32178,7 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
31090
32178
  } : undefined);
31091
32179
  reusedPrefix = incremental.reusedPrefix;
31092
32180
  if (!state.syncCaptureRequired && prior && reusedPrefix === plan.inputs.length && prior.inputIdSeq.length === plan.inputs.length && prior.jsonPrefix === plan.jsonPrefix && JSON.stringify(prior.piOutputEntryIds) === JSON.stringify(ownership)) {
32181
+ args.assertCurrentPass?.();
31093
32182
  state.acceptedInputs = plan.inputs;
31094
32183
  return;
31095
32184
  }
@@ -31105,6 +32194,7 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
31105
32194
  capturedAt: plan.capturedAt,
31106
32195
  captureSequence: plan.captureSequence
31107
32196
  };
32197
+ args.assertCurrentPass?.();
31108
32198
  if (!captureSlot(plan.sessionId, slot)) {
31109
32199
  throw new Error("LKG slot rejected the Pi snapshot");
31110
32200
  }
@@ -31113,6 +32203,12 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
31113
32203
  const persisted = saveLkgSlotToDb(db, plan.sessionId, slot);
31114
32204
  state.syncCaptureRequired = !persisted;
31115
32205
  } catch (error) {
32206
+ try {
32207
+ args.assertCurrentPass?.();
32208
+ } catch (discarded) {
32209
+ sessionLog(plan.sessionId, "DISCARDED CONTEXT RESULT: LKG publication", discarded);
32210
+ return;
32211
+ }
31116
32212
  if (plan.captureSequence !== state.captureSequence)
31117
32213
  return;
31118
32214
  dropSlot(plan.sessionId, "lkg_async_capture_failed");
@@ -31209,7 +32305,7 @@ function resolvePiLkgOutputEntryIds(messages, syntheticLeadingCount, entryId) {
31209
32305
 
31210
32306
  // src/pi-lkg-fit-envelope.ts
31211
32307
  import { createHash as createHash13 } from "node:crypto";
31212
- function readPiLkgFitEnvelope(ctx, pi, modelKey, frozen) {
32308
+ function readPiLkgFitEnvelope(ctx, pi, modelKey, frozen, wireSchema) {
31213
32309
  try {
31214
32310
  if (!modelKey || typeof pi.getAllTools !== "function")
31215
32311
  return;
@@ -31221,7 +32317,8 @@ function readPiLkgFitEnvelope(ctx, pi, modelKey, frozen) {
31221
32317
  for (const tool of allTools) {
31222
32318
  if (!tool || typeof tool !== "object")
31223
32319
  return;
31224
- const { name, description, parameters } = tool;
32320
+ const { name, description } = tool;
32321
+ const parameters = wireSchema ? wireSchema(tool) : tool.parameters;
31225
32322
  if (typeof name !== "string" || !name.length || typeof description !== "string" || !parameters || typeof parameters !== "object" || Array.isArray(parameters))
31226
32323
  return;
31227
32324
  tools.push({ name, description, parameters });
@@ -31278,6 +32375,27 @@ function readPiLkgFitEnvelope(ctx, pi, modelKey, frozen) {
31278
32375
  }
31279
32376
 
31280
32377
  // src/pi-pressure.ts
32378
+ var MAX_UNKNOWN_PI_INPUT_TOKENS = 3000000;
32379
+ function piPressureEvidenceLimit(geometry) {
32380
+ return geometry && hasTrustedAbsoluteWall(geometry) ? geometry.derivation.absoluteWall : MAX_UNKNOWN_PI_INPUT_TOKENS;
32381
+ }
32382
+ var rejectedUsageLogged = new Set;
32383
+ var rejectedProviderUsage = new Set;
32384
+ function recordPiProviderUsageValidity(sessionId, rejected) {
32385
+ if (rejected)
32386
+ rejectedProviderUsage.add(sessionId);
32387
+ else
32388
+ rejectedProviderUsage.delete(sessionId);
32389
+ }
32390
+ function piProviderUsageWasRejected(sessionId) {
32391
+ return rejectedProviderUsage.has(sessionId);
32392
+ }
32393
+ function noteRejectedPiUsage(sessionId, reading, limit, source) {
32394
+ if (rejectedUsageLogged.has(sessionId))
32395
+ return;
32396
+ rejectedUsageLogged.add(sessionId);
32397
+ sessionLog(sessionId, `provider_usage_not_single_request: ${source} input ${reading} exceeds pressure evidence bound ${limit}; ignoring possible turn-aggregate billing usage and using request estimates`);
32398
+ }
31281
32399
  function extractAssistantUsage(message) {
31282
32400
  if (!message || typeof message !== "object")
31283
32401
  return null;
@@ -31301,6 +32419,13 @@ function extractAssistantUsage(message) {
31301
32419
  return result;
31302
32420
  }
31303
32421
  function computePiPressure(usage, contextLimit, absoluteWall) {
32422
+ const inputTokens = piUsageInputTokens(usage);
32423
+ if (inputTokens === null || inputTokens > (absoluteWall ?? MAX_UNKNOWN_PI_INPUT_TOKENS))
32424
+ return null;
32425
+ const percentage = contextLimit > 0 ? inputTokens / contextLimit * 100 : 0;
32426
+ return { inputTokens, percentage };
32427
+ }
32428
+ function piUsageInputTokens(usage) {
31304
32429
  if (!usage)
31305
32430
  return null;
31306
32431
  const input = usage.input ?? 0;
@@ -31308,14 +32433,10 @@ function computePiPressure(usage, contextLimit, absoluteWall) {
31308
32433
  const cacheWrite = usage.cacheWrite ?? 0;
31309
32434
  const componentPromptTokens = input + cacheRead + cacheWrite;
31310
32435
  const totalPromptTokens = typeof usage.totalTokens === "number" && Number.isFinite(usage.totalTokens) && typeof usage.output === "number" && Number.isFinite(usage.output) ? Math.max(0, usage.totalTokens - usage.output) : undefined;
31311
- let inputTokens = totalPromptTokens !== undefined && totalPromptTokens > 0 ? Math.min(componentPromptTokens, totalPromptTokens) : componentPromptTokens;
32436
+ const inputTokens = totalPromptTokens !== undefined && totalPromptTokens > 0 ? Math.min(componentPromptTokens, totalPromptTokens) : componentPromptTokens;
31312
32437
  if (inputTokens <= 0 || !Number.isFinite(inputTokens))
31313
32438
  return null;
31314
- if (typeof absoluteWall === "number" && Number.isFinite(absoluteWall) && absoluteWall > 0 && inputTokens > absoluteWall) {
31315
- inputTokens = absoluteWall;
31316
- }
31317
- const percentage = contextLimit > 0 ? inputTokens / contextLimit * 100 : 0;
31318
- return { inputTokens, percentage };
32439
+ return inputTokens;
31319
32440
  }
31320
32441
  function resolvePiPressureSnapshot(args) {
31321
32442
  const persistedInputTokens = Number.isFinite(args.persistedInputTokens) && args.persistedInputTokens > 0 ? args.persistedInputTokens : 0;
@@ -31381,11 +32502,31 @@ function isPiContextUsageRawBranchEstimate(sessionManager) {
31381
32502
  function resolveGuardedPiPressureSnapshot(args) {
31382
32503
  const live = typeof args.liveInputTokens === "number" && Number.isFinite(args.liveInputTokens) ? args.liveInputTokens : 0;
31383
32504
  const setAside = args.liveIsRawBranchEstimate === true && live > 0;
31384
- const snapshot = resolvePiPressureSnapshot(setAside ? {
32505
+ const bound = args.providerInputLimit ?? MAX_UNKNOWN_PI_INPUT_TOKENS;
32506
+ const rejectedPersisted = args.persistedInputTokens > bound;
32507
+ const rejectedLive = live > bound;
32508
+ const sanitized = {
31385
32509
  ...args,
32510
+ persistedInputTokens: rejectedPersisted ? 0 : args.persistedInputTokens,
32511
+ persistedPercentage: rejectedPersisted ? 0 : args.persistedPercentage,
32512
+ liveInputTokens: rejectedLive ? undefined : args.liveInputTokens
32513
+ };
32514
+ const snapshot = resolvePiPressureSnapshot(setAside ? {
32515
+ ...sanitized,
31386
32516
  liveInputTokens: undefined,
31387
32517
  ...args.persistedFromLive ? { persistedInputTokens: 0, persistedPercentage: 0 } : {}
31388
- } : args);
32518
+ } : sanitized);
32519
+ if (args.fallbackInputTokens !== undefined) {
32520
+ return {
32521
+ snapshot: resolvePiPressureSnapshot({
32522
+ ...args,
32523
+ persistedInputTokens: snapshot.inputTokens,
32524
+ persistedPercentage: snapshot.percentage,
32525
+ liveInputTokens: args.fallbackInputTokens
32526
+ }),
32527
+ setAsideEstimate: setAside ? live : undefined
32528
+ };
32529
+ }
31389
32530
  return { snapshot, setAsideEstimate: setAside ? live : undefined };
31390
32531
  }
31391
32532
  var lastLiveUsageClassification = new Map;
@@ -31394,6 +32535,8 @@ function recordPiLiveUsageClassification(sessionId, isRawBranchEstimate) {
31394
32535
  }
31395
32536
  function clearPiLiveUsageClassification(sessionId) {
31396
32537
  lastLiveUsageClassification.delete(sessionId);
32538
+ rejectedUsageLogged.delete(sessionId);
32539
+ rejectedProviderUsage.delete(sessionId);
31397
32540
  }
31398
32541
  function resolvePiStatusPressureSnapshot(args) {
31399
32542
  const { sessionId, ...pressureArgs } = args;
@@ -31410,6 +32553,10 @@ function noteRawBranchEstimateSetAside(sessionId, reading, source) {
31410
32553
  }
31411
32554
  function resolvePiPressureSnapshotWithEstimateGuard(args) {
31412
32555
  const { snapshot, setAsideEstimate } = resolveGuardedPiPressureSnapshot(args);
32556
+ const bound = args.providerInputLimit ?? MAX_UNKNOWN_PI_INPUT_TOKENS;
32557
+ const rejected = Math.max(args.persistedInputTokens, args.liveInputTokens ?? 0);
32558
+ if (rejected > bound)
32559
+ noteRejectedPiUsage(args.sessionId, rejected, bound, args.source);
31413
32560
  if (setAsideEstimate !== undefined) {
31414
32561
  noteRawBranchEstimateSetAside(args.sessionId, setAsideEstimate, args.source);
31415
32562
  } else if (snapshot.inputTokens > 0) {
@@ -31424,6 +32571,38 @@ function formatPiPressureForLog(snapshot) {
31424
32571
  // src/pi-proven-floor.ts
31425
32572
  var FLOOR_STATE_KEY = "piProvenInputFloor";
31426
32573
  var mismatchLogged = new Set;
32574
+ function largestAcceptedInput(entries, modelKey, providerInputLimit) {
32575
+ let largest = 0;
32576
+ for (const entry of entries) {
32577
+ if (!entry || typeof entry !== "object")
32578
+ continue;
32579
+ const row = entry;
32580
+ if (row.type !== "message" || !row.message || typeof row.message !== "object")
32581
+ continue;
32582
+ const message = row.message;
32583
+ if (piProvenFloorModelKey({
32584
+ provider: message.provider,
32585
+ id: message.model
32586
+ }) !== modelKey || providerResponseFailed({
32587
+ finish: message.stopReason,
32588
+ error: message.errorMessage
32589
+ }))
32590
+ continue;
32591
+ const pressure = computePiPressure(extractAssistantUsage(message), 0, providerInputLimit);
32592
+ if (pressure)
32593
+ largest = Math.max(largest, pressure.inputTokens);
32594
+ }
32595
+ return largest;
32596
+ }
32597
+ function hasMeasuredBasis(raw) {
32598
+ if (typeof raw !== "string")
32599
+ return false;
32600
+ try {
32601
+ return JSON.parse(raw)?.[FLOOR_STATE_KEY]?.basis === "provider_usage_v1";
32602
+ } catch {
32603
+ return false;
32604
+ }
32605
+ }
31427
32606
  function piProvenFloorModelKey(model) {
31428
32607
  if (!model)
31429
32608
  return;
@@ -31468,7 +32647,7 @@ function recordPiProvenFloorModel(db, sessionId, modelKey, tokens) {
31468
32647
  AND json_type(deferred_execute_state) = 'object'
31469
32648
  THEN deferred_execute_state ELSE '{}' END,
31470
32649
  '$.${FLOOR_STATE_KEY}', json(?))
31471
- WHERE session_id = ?`).run(JSON.stringify({ modelKey, tokens }), sessionId);
32650
+ WHERE session_id = ?`).run(JSON.stringify({ modelKey, tokens, basis: "provider_usage_v1" }), sessionId);
31472
32651
  }
31473
32652
  function resolvePiProvenInputFloor(args) {
31474
32653
  const row = readFloorRow(args.db, args.sessionId);
@@ -31492,6 +32671,21 @@ function resolvePiProvenInputFloor(args) {
31492
32671
  }
31493
32672
  return 0;
31494
32673
  }
32674
+ const providerInputLimit = args.providerInputLimit ?? MAX_UNKNOWN_PI_INPUT_TOKENS;
32675
+ if (!hasMeasuredBasis(row.state) || observed > providerInputLimit) {
32676
+ let measured = 0;
32677
+ try {
32678
+ measured = largestAcceptedInput(args.readBranch?.() ?? [], record.modelKey, providerInputLimit);
32679
+ } catch {}
32680
+ updateSessionMeta(args.db, args.sessionId, {
32681
+ observedSafeInputTokens: measured,
32682
+ cacheAlertSent: false,
32683
+ lastUsageContextLimit: 0
32684
+ });
32685
+ recordPiProvenFloorModel(args.db, args.sessionId, record.modelKey, measured);
32686
+ sessionLog(args.sessionId, `legacy proven input floor ${observed} re-derived from accepted provider usage: ${measured}`);
32687
+ return measured;
32688
+ }
31495
32689
  return observed;
31496
32690
  }
31497
32691
 
@@ -32067,6 +33261,29 @@ function injectSyntheticTodowriteForPi(args) {
32067
33261
  return args.messages;
32068
33262
  }
32069
33263
 
33264
+ // src/pi-tool-wire-schema.ts
33265
+ import { isAbsolute as isAbsolute2 } from "node:path";
33266
+ import { pathToFileURL as pathToFileURL2 } from "node:url";
33267
+ var resolver;
33268
+ function loadPiToolWireSchema() {
33269
+ if (resolver)
33270
+ return resolver;
33271
+ resolver = (async () => {
33272
+ const specifier = "@oh-my-pi/pi-ai/utils/schema/wire";
33273
+ for (const base of [process.argv[1], import.meta.url]) {
33274
+ if (!base || !base.startsWith("file:") && !isAbsolute2(base))
33275
+ continue;
33276
+ try {
33277
+ const path = import.meta.resolve(specifier, base.startsWith("file:") ? base : pathToFileURL2(base).href);
33278
+ const host = await import(path);
33279
+ if (typeof host.toolWireSchema === "function")
33280
+ return host.toolWireSchema;
33281
+ } catch {}
33282
+ }
33283
+ })();
33284
+ return resolver;
33285
+ }
33286
+
32070
33287
  // src/provider-error-recovery-pi.ts
32071
33288
  function reportBindingRecovery(sessionId, report, message) {
32072
33289
  if (report)
@@ -32084,10 +33301,16 @@ function handlePiProviderFailure(args) {
32084
33301
  const provider = typeof message.provider === "string" ? message.provider : undefined;
32085
33302
  const model = typeof message.model === "string" ? message.model : undefined;
32086
33303
  const binding = detectThinkingBindingMismatch(message.errorMessage);
33304
+ if (detectLatestTurnThinkingMismatch(message.errorMessage) && !args.compactionOff && isAnthropicFamilyRoute(provider, model)) {
33305
+ armLatestThinkingRecovery(args.db, args.sessionId);
33306
+ clearPiLkgSessionState(args.sessionId);
33307
+ dropSlot(args.sessionId, "latest-thinking-recovery-arm");
33308
+ return { kind: "thinking_binding", armed: true };
33309
+ }
32087
33310
  if (binding.isBindingMismatch) {
32088
33311
  const enabled = args.thinkingBindingRecoveryEnabled !== false && !args.compactionOff && isPrefixBoundThinkingModel(provider, model);
32089
33312
  if (enabled) {
32090
- armThinkingBindingRecovery(args.db, args.sessionId);
33313
+ armBindingRecoverySafely(args.db, args.sessionId);
32091
33314
  clearPiLkgSessionState(args.sessionId);
32092
33315
  dropSlot(args.sessionId, "thinking-binding-recovery-arm");
32093
33316
  reportBindingRecovery(args.sessionId, args.report, `thinking-binding recovery armed from message_end (provider paths: failing=${binding.failingBlockPath ?? "?"} firstChanged=${binding.firstChangedPath ?? "?"})`);
@@ -32137,12 +33360,16 @@ function stripThinkingParts(message) {
32137
33360
  return before - strippedContent.length;
32138
33361
  }
32139
33362
  function applyPiThinkingBindingRecovery(args) {
33363
+ const protectedMessages = latestAssistantTurnMessages(args.messages);
32140
33364
  const frozenEntryIds = frozenBindingEntryIds(args.db, args.sessionId);
32141
- const flagTarget = isPrefixBoundThinkingModel(args.provider, args.model) ? getThinkingBindingRecoveryTarget(args.db, args.sessionId) : null;
33365
+ const flagTargetCandidate = isPrefixBoundThinkingModel(args.provider, args.model) ? getThinkingBindingRecoveryTarget(args.db, args.sessionId) : null;
33366
+ const flagTarget = flagTargetCandidate?.startsWith(LATEST_THINKING_RESTORE) ? null : flagTargetCandidate;
32142
33367
  let applied = null;
32143
33368
  if (flagTarget) {
32144
33369
  const entryIds = new Set;
32145
33370
  for (let index = 0;index < args.messages.length; index += 1) {
33371
+ if (protectedMessages.has(args.messages[index]))
33372
+ continue;
32146
33373
  const entryId = args.entryIds[index];
32147
33374
  if (entryId && hasThinkingPart(args.messages[index]))
32148
33375
  entryIds.add(entryId);
@@ -32158,7 +33385,15 @@ function applyPiThinkingBindingRecovery(args) {
32158
33385
  reportBindingRecovery(args.sessionId, args.report, `thinking-binding recovery consumed on context pass target=${flagTarget} entries=${applied.entryIds.length} [${applied.entryIds.join(",")}]`);
32159
33386
  }
32160
33387
  }
33388
+ const restoringActiveTurn = latestThinkingRecoveryCoversActiveTurn({
33389
+ db: args.db,
33390
+ sessionId: args.sessionId,
33391
+ messages: args.messages,
33392
+ id: (message, index) => resolvePiStableId(message, index, args.entryIds)
33393
+ });
32161
33394
  for (let index = 0;index < args.messages.length; index += 1) {
33395
+ if (restoringActiveTurn && protectedMessages.has(args.messages[index]))
33396
+ continue;
32162
33397
  const entryId = args.entryIds[index];
32163
33398
  if (entryId && frozenEntryIds.has(entryId))
32164
33399
  stripThinkingParts(args.messages[index]);
@@ -32170,9 +33405,12 @@ function applyPiProactiveThinkingStrip(args) {
32170
33405
  return null;
32171
33406
  if (!isPrefixBoundThinkingModel(args.provider, args.model))
32172
33407
  return null;
33408
+ const protectedMessages = latestAssistantTurnMessages(args.messages);
32173
33409
  const entryIds = [];
32174
33410
  const indices = [];
32175
33411
  for (let index = 0;index < args.messages.length; index += 1) {
33412
+ if (protectedMessages.has(args.messages[index]))
33413
+ continue;
32176
33414
  const entryId = args.entryIds[index];
32177
33415
  if (!entryId || !hasThinkingPart(args.messages[index]))
32178
33416
  continue;
@@ -32264,27 +33502,65 @@ function buildMessageIdToMaxTag(targets) {
32264
33502
  }
32265
33503
  return out;
32266
33504
  }
33505
+ function piReasoningStatusMessages(messages) {
33506
+ return messages.filter(isRecord).map((message) => {
33507
+ const usage = isRecord(message.usage) ? message.usage : {};
33508
+ const parts = Array.isArray(message.content) ? message.content.filter(isRecord).map((part) => part.type === "thinking" ? {
33509
+ ...part,
33510
+ type: part.redacted === true ? "redacted_thinking" : "thinking"
33511
+ } : part) : [];
33512
+ const payload = isRecord(message.providerPayload) ? message.providerPayload : undefined;
33513
+ if (payload && Array.isArray(payload.items) && payload.items.some((item) => isRecord(item) && item.type === "reasoning"))
33514
+ parts.push({ type: "reasoning", text: "", metadata: {} });
33515
+ return {
33516
+ info: { role: message.role, tokens: { reasoning: usage.reasoning } },
33517
+ parts
33518
+ };
33519
+ });
33520
+ }
32267
33521
  function piReasoningClearCutoff(args) {
32268
33522
  if (args.prefixBound)
32269
33523
  return piPrefixBoundReasoningCutoff(args);
32270
- let maxTag = 0;
32271
- for (const t of args.messageIdToMaxTag.values())
32272
- if (t > maxTag)
32273
- maxTag = t;
32274
- let cutoff = maxTag - args.clearReasoningAge;
32275
- if (maxTag === 0 || cutoff <= 0)
32276
- return 0;
32277
- for (let i = args.messages.length - 1;i >= 0; i--) {
32278
- const raw = args.messages[i];
32279
- if (!raw || typeof raw !== "object" || raw.role !== "assistant")
32280
- continue;
32281
- const id = args.piMessageStableId(raw, i);
32282
- const newestTag = id ? args.messageIdToMaxTag.get(id) ?? 0 : 0;
32283
- if (newestTag > 0)
32284
- cutoff = Math.min(cutoff, newestTag - 1);
32285
- break;
32286
- }
32287
- return Math.max(0, cutoff);
33524
+ return piBudgetCutoff(args);
33525
+ }
33526
+ function piBudgetCutoff(args) {
33527
+ const assistants = args.messages.map((message, index) => ({ message, index })).filter((entry) => isAssistantWithContent(entry.message));
33528
+ const newest = assistants.at(-1);
33529
+ const exempt = [...assistants].reverse().find((entry) => entry.message.content.some((part) => part.type !== "text" || part.text.trim() !== ""));
33530
+ return reasoningBudgetCutoff(assistants.map((entry) => {
33531
+ const id = args.piMessageStableId(entry.message, entry.index);
33532
+ return {
33533
+ tag: id ? args.messageIdToMaxTag.get(id) ?? 0 : 0,
33534
+ exempt: entry === newest || entry === exempt || isInActiveAnthropicTurn(args.messages, entry.index, args.anthropic === true || args.prefixBound === true || hasAnthropicReasoning(args.messages)),
33535
+ alreadyRemoved: id !== undefined && args.alreadyGone?.(id) === true && !hasInlineThinkingMarkup(entry.message),
33536
+ cost: () => {
33537
+ let text = "";
33538
+ let inlineText = "";
33539
+ let opaque = false;
33540
+ const typedGone = id !== undefined && args.alreadyGone?.(id) === true;
33541
+ const hasTypedReasoning = !typedGone && thinkingParts(entry.message).length > 0;
33542
+ for (const part of typedGone ? [] : thinkingParts(entry.message).filter(isLiveThinking)) {
33543
+ if (part.redacted)
33544
+ opaque = true;
33545
+ else
33546
+ text += part.thinking;
33547
+ opaque ||= !!part.thinkingSignature && !part.thinking;
33548
+ }
33549
+ for (const part of entry.message.content) {
33550
+ if (part.type !== "text")
33551
+ continue;
33552
+ for (const match of part.text.matchAll(/<(?:think|thinking)>([\s\S]*?)<\/(?:think|thinking)>/gi))
33553
+ inlineText += match[1];
33554
+ }
33555
+ opaque ||= !typedGone && entry.message.providerPayload?.items?.some((item) => isRecord(item) && item.type === "reasoning") === true;
33556
+ if (!hasTypedReasoning && !text && !opaque && !inlineText)
33557
+ return 0;
33558
+ const reported = entry.message.usage?.reasoning;
33559
+ const typed = hasTypedReasoning || text || opaque ? reported !== undefined && reported > 0 ? reported : reasoningStepCost(reported, estimateTokens(text) * (args.proseRatio ?? 1), opaque) : 0;
33560
+ return typed + Math.ceil(estimateTokens(inlineText) * (args.proseRatio ?? 1));
33561
+ }
33562
+ };
33563
+ }), args.keepReasoningTokens);
32288
33564
  }
32289
33565
  function isAssistantWithContent(raw) {
32290
33566
  return !!raw && typeof raw === "object" && raw.role === "assistant" && Array.isArray(raw.content);
@@ -32299,13 +33575,10 @@ function hasInlineThinkingMarkup(msg) {
32299
33575
  return msg.content.some((part) => !!part && typeof part === "object" && part.type === "text" && typeof part.text === "string" && stripInlineThinkingMarkup(part.text) !== part.text);
32300
33576
  }
32301
33577
  function piPrefixBoundReasoningCutoff(args) {
32302
- let maxTag = 0;
32303
- for (const t of args.messageIdToMaxTag.values())
32304
- if (t > maxTag)
32305
- maxTag = t;
32306
- const ageCutoff = maxTag - args.clearReasoningAge;
32307
- if (maxTag === 0 || ageCutoff <= 0)
33578
+ const ageCutoff = piBudgetCutoff({ ...args, prefixBound: true });
33579
+ if (ageCutoff <= 0)
32308
33580
  return 0;
33581
+ const protectedMessages = args.protectLatestTurn ? latestAssistantTurnMessages(args.messages) : new Set;
32309
33582
  let newestIndex = -1;
32310
33583
  for (let i = args.messages.length - 1;i >= 0; i--) {
32311
33584
  if (isAssistantWithContent(args.messages[i])) {
@@ -32330,7 +33603,7 @@ function piPrefixBoundReasoningCutoff(args) {
32330
33603
  if (id && !inline && args.alreadyGone?.(id))
32331
33604
  continue;
32332
33605
  const tag = tagAt(i);
32333
- const removable = i !== newestIndex && id !== undefined && tag > 0 && tag <= ageCutoff && !inline && !live.some((part) => part.redacted === true);
33606
+ const removable = !protectedMessages.has(raw) && i !== newestIndex && id !== undefined && tag > 0 && tag <= ageCutoff && !inline && !live.some((part) => part.redacted === true);
32334
33607
  walk.push({ index: i, tag, removable });
32335
33608
  }
32336
33609
  let stop = walk.findIndex((entry) => !entry.removable);
@@ -32358,20 +33631,17 @@ function piPrefixBoundReasoningCutoff(args) {
32358
33631
  }
32359
33632
  }
32360
33633
  function clearOldReasoningPi(args) {
32361
- const { messages, messageIdToMaxTag, clearReasoningAge, piMessageStableId } = args;
32362
- let maxTag = 0;
32363
- for (const t of messageIdToMaxTag.values())
32364
- if (t > maxTag)
32365
- maxTag = t;
32366
- if (maxTag === 0)
32367
- return { cleared: 0, newWatermark: 0 };
32368
- const ageCutoff = Math.min(maxTag - clearReasoningAge, args.maxCutoff ?? Number.POSITIVE_INFINITY);
32369
- if (ageCutoff <= 0)
33634
+ const { messages, messageIdToMaxTag, piMessageStableId } = args;
33635
+ const ageCutoff = args.maxCutoff;
33636
+ if (!Number.isFinite(ageCutoff) || ageCutoff <= 0)
32370
33637
  return { cleared: 0, newWatermark: 0 };
32371
33638
  let cleared = 0;
32372
33639
  let newWatermark = 0;
33640
+ const protectedMessages = args.protectLatestTurn ? latestAssistantTurnMessages(messages) : new Set;
32373
33641
  for (let i = 0;i < messages.length; i++) {
32374
33642
  const raw = messages[i];
33643
+ if (protectedMessages.has(raw))
33644
+ continue;
32375
33645
  if (!raw || typeof raw !== "object")
32376
33646
  continue;
32377
33647
  const msg = raw;
@@ -32404,20 +33674,17 @@ function clearOldReasoningPi(args) {
32404
33674
  return { cleared, newWatermark };
32405
33675
  }
32406
33676
  function stripInlineThinkingPi(args) {
32407
- const { messages, messageIdToMaxTag, clearReasoningAge, piMessageStableId } = args;
32408
- let maxTag = 0;
32409
- for (const t of messageIdToMaxTag.values())
32410
- if (t > maxTag)
32411
- maxTag = t;
32412
- if (maxTag === 0)
32413
- return { stripped: 0, newWatermark: 0 };
32414
- const ageCutoff = Math.min(maxTag - clearReasoningAge, args.maxCutoff ?? Number.POSITIVE_INFINITY);
32415
- if (ageCutoff <= 0)
33677
+ const { messages, messageIdToMaxTag, piMessageStableId } = args;
33678
+ const ageCutoff = args.maxCutoff;
33679
+ if (!Number.isFinite(ageCutoff) || ageCutoff <= 0)
32416
33680
  return { stripped: 0, newWatermark: 0 };
32417
33681
  let stripped = 0;
32418
33682
  let newWatermark = 0;
33683
+ const protectedMessages = args.protectLatestTurn ? latestAssistantTurnMessages(messages) : new Set;
32419
33684
  for (let i = 0;i < messages.length; i++) {
32420
33685
  const raw = messages[i];
33686
+ if (protectedMessages.has(raw))
33687
+ continue;
32421
33688
  if (!raw || typeof raw !== "object")
32422
33689
  continue;
32423
33690
  const msg = raw;
@@ -32455,8 +33722,11 @@ function replayClearedReasoningPi(args) {
32455
33722
  if (watermark <= 0)
32456
33723
  return 0;
32457
33724
  let cleared = 0;
33725
+ const protectedMessages = args.protectLatestTurn ? latestAssistantTurnMessages(messages) : new Set;
32458
33726
  for (let i = 0;i < messages.length; i++) {
32459
33727
  const raw = messages[i];
33728
+ if (protectedMessages.has(raw))
33729
+ continue;
32460
33730
  if (!raw || typeof raw !== "object")
32461
33731
  continue;
32462
33732
  const msg = raw;
@@ -32490,8 +33760,11 @@ function replayStrippedInlineThinkingPi(args) {
32490
33760
  if (watermark <= 0)
32491
33761
  return 0;
32492
33762
  let stripped = 0;
33763
+ const protectedMessages = args.protectLatestTurn ? latestAssistantTurnMessages(messages) : new Set;
32493
33764
  for (let i = 0;i < messages.length; i++) {
32494
33765
  const raw = messages[i];
33766
+ if (protectedMessages.has(raw))
33767
+ continue;
32495
33768
  if (!raw || typeof raw !== "object")
32496
33769
  continue;
32497
33770
  const msg = raw;
@@ -32697,13 +33970,25 @@ function replayPiReminderStrips(args) {
32697
33970
 
32698
33971
  // src/served-array-ledger.ts
32699
33972
  import { createHash as createHash14 } from "node:crypto";
33973
+ import { readFileSync as readFileSync8 } from "node:fs";
32700
33974
  import * as path7 from "node:path";
32701
33975
  var PI_SERVED_ARRAY_TAIL_MESSAGES = 40;
32702
33976
  var PI_SERVED_ARRAY_BODY_CAPTURE_ENV = "MAGIC_CONTEXT_PI_SERVED_BODY_CAPTURE";
32703
33977
  var LEDGER_DIRECTORY = "pi-served-array-digests";
32704
33978
  var BODY_DIRECTORY = "pi-served-array-bodies";
33979
+ var IDENTITY_DIRECTORY = "pi-served-tag-numbers";
32705
33980
  var FLUSH_DELAY_MS = 25;
33981
+
33982
+ class PiServedIdentityError extends Error {
33983
+ constructor(cause) {
33984
+ super("Pi served-number identity could not be persisted or restored", {
33985
+ cause
33986
+ });
33987
+ this.name = "PiServedIdentityError";
33988
+ }
33989
+ }
32706
33990
  var previousBySession = new Map;
33991
+ var servedTagNumbersBySession = new Map;
32707
33992
  var sequenceBySession = new Map;
32708
33993
  var pendingLinesByPath = new Map;
32709
33994
  var flushTimer = null;
@@ -32711,8 +33996,37 @@ var swallowedWriteCount = 0;
32711
33996
  var lastWriteError = null;
32712
33997
  function clearPiServedArraySession(sessionId) {
32713
33998
  previousBySession.delete(sessionId);
33999
+ servedTagNumbersBySession.delete(sessionId);
32714
34000
  sequenceBySession.delete(sessionId);
32715
34001
  }
34002
+ function getPiServedTagNumbers(sessionId, storageDir = getMagicContextStorageDir()) {
34003
+ const filePath = getPiServedTagNumbersPath(sessionId, storageDir);
34004
+ const cached = servedTagNumbersBySession.get(sessionId);
34005
+ if (cached?.path === filePath)
34006
+ return cached.numbers;
34007
+ let text;
34008
+ try {
34009
+ text = readFileSync8(filePath, "utf8");
34010
+ } catch (error) {
34011
+ if (error.code !== "ENOENT")
34012
+ throw error;
34013
+ text = "";
34014
+ }
34015
+ const numbers = new Set;
34016
+ for (const line of text.split(`
34017
+ `)) {
34018
+ if (!line.trim())
34019
+ continue;
34020
+ const row = JSON.parse(line);
34021
+ if (row.version !== 1 || row.session_id !== sessionId || !Array.isArray(row.tag_numbers) || !row.tag_numbers.every((number) => Number.isSafeInteger(number) && number > 0)) {
34022
+ throw new Error("Invalid durable Pi served-number record; refusing identity adoption");
34023
+ }
34024
+ for (const number of row.tag_numbers)
34025
+ numbers.add(number);
34026
+ }
34027
+ servedTagNumbersBySession.set(sessionId, { path: filePath, numbers });
34028
+ return numbers;
34029
+ }
32716
34030
  function sha256(value) {
32717
34031
  return createHash14("sha256").update(value).digest("hex");
32718
34032
  }
@@ -32726,6 +34040,9 @@ function getPiServedArrayLedgerPath(sessionId, storageDir = getMagicContextStora
32726
34040
  function getPiServedArrayBodyPath(sessionId, storageDir = getMagicContextStorageDir()) {
32727
34041
  return path7.join(storageDir, BODY_DIRECTORY, `${safeSessionFileStem(sessionId)}.jsonl`);
32728
34042
  }
34043
+ function getPiServedTagNumbersPath(sessionId, storageDir = getMagicContextStorageDir()) {
34044
+ return path7.join(storageDir, IDENTITY_DIRECTORY, `${safeSessionFileStem(sessionId)}.jsonl`);
34045
+ }
32729
34046
  function fullBodyCaptureEnabled() {
32730
34047
  const value = process.env[PI_SERVED_ARRAY_BODY_CAPTURE_ENV]?.trim().toLowerCase();
32731
34048
  return value === "1" || value === "true" || value === "yes" || value === "on";
@@ -32791,8 +34108,19 @@ function flushPiServedArrayLedger() {
32791
34108
  return;
32792
34109
  const pending = [...pendingLinesByPath.entries()];
32793
34110
  pendingLinesByPath.clear();
32794
- for (const [filePath, lines] of pending)
32795
- appendPendingLines(filePath, lines);
34111
+ for (const [filePath, lines] of pending) {
34112
+ const admitted = lines.filter((item) => {
34113
+ try {
34114
+ item.assertCurrentPass?.();
34115
+ return true;
34116
+ } catch (error) {
34117
+ log("[magic-context][pi] DISCARDED CONTEXT RESULT: queued served capture", error);
34118
+ return false;
34119
+ }
34120
+ }).map((item) => item.line);
34121
+ if (admitted.length)
34122
+ appendPendingLines(filePath, admitted);
34123
+ }
32796
34124
  }
32797
34125
  function scheduleFlush() {
32798
34126
  if (flushTimer)
@@ -32800,19 +34128,30 @@ function scheduleFlush() {
32800
34128
  flushTimer = setTimeout(flushPiServedArrayLedger, FLUSH_DELAY_MS);
32801
34129
  flushTimer.unref?.();
32802
34130
  }
32803
- function enqueue(filePath, line) {
34131
+ function enqueue(filePath, line, assertCurrentPass) {
32804
34132
  const pending = pendingLinesByPath.get(filePath);
32805
34133
  if (pending)
32806
- pending.push(line);
34134
+ pending.push({ line, assertCurrentPass });
32807
34135
  else
32808
- pendingLinesByPath.set(filePath, [line]);
34136
+ pendingLinesByPath.set(filePath, [{ line, assertCurrentPass }]);
32809
34137
  scheduleFlush();
32810
34138
  }
32811
34139
  function capturePiServedArray(sessionId, messages, options = {}) {
34140
+ options.assertCurrentPass?.();
34141
+ let identityPersistenceFailed = false;
32812
34142
  try {
32813
34143
  const serializedMessages = options.serializedOutput?.jsonMessages ?? messages.map(serializeMessage);
32814
34144
  const serializedArray = options.serializedOutput?.json ?? `[${serializedMessages.join(",")}]`;
32815
34145
  const digest = sha256(serializedArray);
34146
+ const storageDir = options.storageDir ?? getMagicContextStorageDir();
34147
+ identityPersistenceFailed = true;
34148
+ const previousNumbers = getPiServedTagNumbers(sessionId, storageDir);
34149
+ identityPersistenceFailed = false;
34150
+ const served = new Set(previousNumbers);
34151
+ for (const number of options.servedTagNumbers ?? []) {
34152
+ if (Number.isSafeInteger(number) && number > 0)
34153
+ served.add(number);
34154
+ }
32816
34155
  const previous = previousBySession.get(sessionId);
32817
34156
  const divergence = previous ? firstDivergence(previous.serializedMessages, serializedMessages) : null;
32818
34157
  const sequence = (sequenceBySession.get(sessionId) ?? 0) + 1;
@@ -32829,9 +34168,27 @@ function capturePiServedArray(sessionId, messages, options = {}) {
32829
34168
  block_vector_start: tailStart,
32830
34169
  block_vectors: messages.slice(tailStart).map(blockVector)
32831
34170
  };
32832
- const storageDir = options.storageDir ?? getMagicContextStorageDir();
34171
+ options.assertCurrentPass?.();
34172
+ const newNumbers = [...served].filter((number) => !previousNumbers.has(number));
34173
+ const identityPath = getPiServedTagNumbersPath(sessionId, storageDir);
34174
+ if (newNumbers.length) {
34175
+ identityPersistenceFailed = true;
34176
+ ensureStorageDirectorySync(path7.dirname(identityPath));
34177
+ options.assertCurrentPass?.();
34178
+ writeStorageFileSync(identityPath, `${JSON.stringify({ version: 1, session_id: sessionId, tag_numbers: newNumbers })}
34179
+ `, {
34180
+ encoding: "utf8",
34181
+ flag: "a"
34182
+ });
34183
+ identityPersistenceFailed = false;
34184
+ }
34185
+ options.assertCurrentPass?.();
34186
+ servedTagNumbersBySession.set(sessionId, {
34187
+ path: identityPath,
34188
+ numbers: served
34189
+ });
32833
34190
  enqueue(getPiServedArrayLedgerPath(sessionId, storageDir), `${JSON.stringify(record)}
32834
- `);
34191
+ `, options.assertCurrentPass);
32835
34192
  if (options.fullBodyCapture ?? fullBodyCaptureEnabled()) {
32836
34193
  const bodyHeader = JSON.stringify({
32837
34194
  version: 1,
@@ -32841,12 +34198,15 @@ function capturePiServedArray(sessionId, messages, options = {}) {
32841
34198
  sha256: digest
32842
34199
  });
32843
34200
  enqueue(getPiServedArrayBodyPath(sessionId, storageDir), `${bodyHeader.slice(0, -1)},"messages":${serializedArray}}
32844
- `);
34201
+ `, options.assertCurrentPass);
32845
34202
  }
32846
34203
  previousBySession.set(sessionId, { digest, serializedMessages });
32847
34204
  sequenceBySession.set(sessionId, sequence);
32848
34205
  return record;
32849
34206
  } catch (error) {
34207
+ options.assertCurrentPass?.();
34208
+ if (identityPersistenceFailed)
34209
+ throw new PiServedIdentityError(error);
32850
34210
  recordWriteFailure(error);
32851
34211
  return;
32852
34212
  }
@@ -33115,19 +34475,6 @@ function clearPiSystemPromptSession(sessionId) {
33115
34475
  stickyDateBySession.delete(sessionId);
33116
34476
  }
33117
34477
 
33118
- // src/timeout.ts
33119
- function withTimeout(p, ms) {
33120
- let handle;
33121
- const timeout = new Promise((resolve) => {
33122
- handle = setTimeout(() => resolve(undefined), ms);
33123
- handle.unref?.();
33124
- });
33125
- return Promise.race([p, timeout]).finally(() => {
33126
- if (handle)
33127
- clearTimeout(handle);
33128
- });
33129
- }
33130
-
33131
34478
  // src/transcript-pi.ts
33132
34479
  function createPiTranscript(source, sessionId, entryIds, options = {}) {
33133
34480
  const working = source.slice();
@@ -33711,11 +35058,11 @@ var pendingDecisionLogObserverForTests;
33711
35058
  var mutationGateObserverForTests;
33712
35059
  var lkgRecoveryLogObserverForTests;
33713
35060
  var variantChangeLogObserverForTests;
35061
+ var beforePipelineForTests;
33714
35062
  function logPiLkgRecovery(sessionId, message) {
33715
35063
  lkgRecoveryLogObserverForTests?.(message);
33716
35064
  sessionLog(sessionId, message);
33717
35065
  }
33718
- var DEFAULT_CLEAR_REASONING_AGE = 50;
33719
35066
  var PI_STABLE_ID_SCHEME = 1;
33720
35067
  var lastEmergencyNotificationAtMs = new Map;
33721
35068
  var EMERGENCY_NOTIFICATION_COOLDOWN_MS = 60000;
@@ -33728,11 +35075,22 @@ var deferredMaterializationSessions = new Set;
33728
35075
  var sessionsByProject = new Map;
33729
35076
  var lastSeenProjectIdentityBySession = new Map;
33730
35077
  var persistedProjectIdentityBySession = new Map;
35078
+ var sessionProjectBindingFailureLogAtBySession = new Map;
35079
+ var SESSION_PROJECT_BINDING_FAILURE_LOG_INTERVAL_MS = 10 * 60 * 1000;
33731
35080
  var rawMessageProviderUnregistersBySession = new Map;
33732
35081
  var activeContextHandlerSessions = new Set;
33733
35082
  var lastHeuristicsTurnIdBySession = new Map;
33734
35083
  var routinePressureAppliedBySession = new Map;
33735
35084
  var firstContextPassSeenBySession = new Set;
35085
+ var contextPassGeneration = new Map;
35086
+
35087
+ class PiLkgAdmissionReplay extends Error {
35088
+ messages;
35089
+ constructor(messages) {
35090
+ super("Saved request served before writer wait");
35091
+ this.messages = messages;
35092
+ }
35093
+ }
33736
35094
  var liveModelBySession = new Map;
33737
35095
  var latestAssistantModelTimestampBySession = new Map;
33738
35096
  var taggedStableMessageIdsBySession = new Map;
@@ -33938,7 +35296,7 @@ function trackSessionForProject(projectIdentity, sessionId) {
33938
35296
  function isContextHandlerSessionActive(sessionId) {
33939
35297
  return activeContextHandlerSessions.has(sessionId);
33940
35298
  }
33941
- function updateSessionProjectTracking(sessionId, projectIdentity, db) {
35299
+ function updateSessionProjectTracking(sessionId, projectIdentity, db, observedCwd) {
33942
35300
  if (!projectIdentity)
33943
35301
  return;
33944
35302
  const prev = lastSeenProjectIdentityBySession.get(sessionId);
@@ -33951,9 +35309,22 @@ function updateSessionProjectTracking(sessionId, projectIdentity, db) {
33951
35309
  }
33952
35310
  if (db && persistedProjectIdentityBySession.get(sessionId) !== projectIdentity) {
33953
35311
  try {
33954
- recordSessionProjectIdentity(db, sessionId, projectIdentity);
35312
+ const change = recordSessionProjectIdentity(db, sessionId, projectIdentity);
33955
35313
  persistedProjectIdentityBySession.set(sessionId, projectIdentity);
33956
- } catch {}
35314
+ if (change) {
35315
+ sessionLog(sessionId, `project binding changed harness=${getHarness()} cwd=${observedCwd ?? "unknown"} old=${change.previousProjectPath ?? "none"} new=${change.projectPath}`);
35316
+ }
35317
+ } catch (error) {
35318
+ const now = Date.now();
35319
+ const lastLoggedAt = sessionProjectBindingFailureLogAtBySession.get(sessionId);
35320
+ if (lastLoggedAt === undefined || now - lastLoggedAt >= SESSION_PROJECT_BINDING_FAILURE_LOG_INTERVAL_MS) {
35321
+ sessionProjectBindingFailureLogAtBySession.set(sessionId, now);
35322
+ sessionLog(sessionId, `project binding write failed harness=${getHarness()} cwd=${observedCwd ?? "unknown"} attempted=${projectIdentity}: ${error instanceof Error ? error.message : String(error)}`);
35323
+ }
35324
+ }
35325
+ }
35326
+ if (db && persistedProjectIdentityBySession.get(sessionId) === projectIdentity && sessionProjectBindingFailureLogAtBySession.delete(sessionId)) {
35327
+ sessionLog(sessionId, `project binding write recovered harness=${getHarness()} cwd=${observedCwd ?? "unknown"} identity=${projectIdentity}`);
33957
35328
  }
33958
35329
  trackSessionForProject(projectIdentity, sessionId);
33959
35330
  lastSeenProjectIdentityBySession.set(sessionId, projectIdentity);
@@ -33976,11 +35347,6 @@ function recordPiLiveModel(sessionId, modelKey, assistantTimestamp) {
33976
35347
  liveModelBySession.set(sessionId, modelKey);
33977
35348
  return true;
33978
35349
  }
33979
- function summarizeTransformError(error) {
33980
- const raw = error instanceof Error ? error.message : String(error);
33981
- const normalized = raw.replace(/\s+/g, " ").trim();
33982
- return normalized.length > 180 ? `${normalized.slice(0, 177).trimEnd()}...` : normalized || "Unknown transform error";
33983
- }
33984
35350
  function persistLastTransformErrorIfChanged(db, sessionId, summary, knownCurrent) {
33985
35351
  try {
33986
35352
  const current = knownCurrent === undefined ? getOrCreateSessionMeta(db, sessionId).lastTransformError : knownCurrent;
@@ -34293,31 +35659,15 @@ function piMessageEntryFingerprint(message) {
34293
35659
  const record = message;
34294
35660
  if (typeof record.role !== "string")
34295
35661
  return null;
34296
- const firstText = firstPiTextContent(record.content);
34297
- const firstTextHash = crypto3.createHash("sha256").update(firstText ?? "").digest("hex").slice(0, 16);
35662
+ const contentHash = crypto3.createHash("sha256").update(stableStringify(record.content)).digest("hex");
34298
35663
  return JSON.stringify([
34299
35664
  typeof record.responseId === "string" ? record.responseId : null,
34300
35665
  typeof record.timestamp === "number" || typeof record.timestamp === "string" ? record.timestamp : null,
34301
35666
  record.role,
34302
35667
  typeof record.toolCallId === "string" ? record.toolCallId : null,
34303
- firstTextHash
35668
+ contentHash
34304
35669
  ]);
34305
35670
  }
34306
- function firstPiTextContent(content) {
34307
- if (typeof content === "string")
34308
- return content;
34309
- if (!Array.isArray(content))
34310
- return null;
34311
- for (const part of content) {
34312
- if (!part || typeof part !== "object")
34313
- continue;
34314
- const record = part;
34315
- if (record.type === "text" && typeof record.text === "string") {
34316
- return record.text;
34317
- }
34318
- }
34319
- return null;
34320
- }
34321
35671
  function buildEntryFingerprintMap(messages, resolveStableId, reusableMessageIds, includeReusable = true) {
34322
35672
  const map = new Map;
34323
35673
  for (let i = 0;i < messages.length; i++) {
@@ -34401,47 +35751,64 @@ function runImmediateTransaction(db, fn) {
34401
35751
  throw error;
34402
35752
  }
34403
35753
  }
34404
- function hasAdoptablePiFallbackMessageTags(db, sessionId, fingerprintById) {
34405
- const fingerprints = [
34406
- ...new Set([...fingerprintById].filter(([id]) => !id.startsWith("pi-msg-")).map(([, fingerprint]) => fingerprint))
34407
- ];
34408
- for (let index = 0;index < fingerprints.length; index += 900) {
34409
- const batch = fingerprints.slice(index, index + 900);
34410
- const placeholders = batch.map(() => "?").join(",");
34411
- const match = db.prepare(`SELECT 1 FROM tags WHERE session_id = ? AND type = 'message'
34412
- AND entry_fingerprint IN (${placeholders})
34413
- AND message_id LIKE 'pi-msg-%' LIMIT 1`).get(sessionId, ...batch);
34414
- if (match != null)
34415
- return true;
35754
+ function readPiAdoptionRevision(db) {
35755
+ return {
35756
+ external: db.prepare("PRAGMA data_version").get(),
35757
+ local: db.prepare("SELECT total_changes() AS changes").get()
35758
+ };
35759
+ }
35760
+ function preparePiAdoptionFingerprints(fingerprintById) {
35761
+ const realIdsByFingerprint = new Map;
35762
+ for (const [id, fingerprint] of fingerprintById) {
35763
+ if (id.startsWith("pi-msg-"))
35764
+ continue;
35765
+ const ids = realIdsByFingerprint.get(fingerprint) ?? [];
35766
+ ids.push(id);
35767
+ realIdsByFingerprint.set(fingerprint, ids);
34416
35768
  }
34417
- return false;
35769
+ const fingerprints = [...realIdsByFingerprint.keys()];
35770
+ const batches = [];
35771
+ for (let i = 0;i < fingerprints.length; i += 900) {
35772
+ const values = fingerprints.slice(i, i + 900);
35773
+ batches.push({ values, placeholders: values.map(() => "?").join(",") });
35774
+ }
35775
+ return { realIdsByFingerprint, batches };
34418
35776
  }
34419
- function readAdoptablePiFallbackFingerprints(db, sessionId, fingerprintById) {
34420
- const fingerprints = [
34421
- ...new Set([...fingerprintById].filter(([id]) => !id.startsWith("pi-msg-")).map(([, fingerprint]) => fingerprint))
34422
- ];
35777
+ function readAdoptablePiFallbackFingerprints(db, sessionId, batches) {
34423
35778
  const found = new Set;
34424
- for (let index = 0;index < fingerprints.length; index += 900) {
34425
- const batch = fingerprints.slice(index, index + 900);
34426
- const placeholders = batch.map(() => "?").join(",");
35779
+ for (const { values, placeholders } of batches) {
34427
35780
  const rows = db.prepare(`SELECT DISTINCT entry_fingerprint FROM tags WHERE session_id = ? AND type = 'message'
34428
- AND entry_fingerprint IN (${placeholders}) AND message_id LIKE 'pi-msg-%'`).all(sessionId, ...batch);
35781
+ AND entry_fingerprint IN (${placeholders}) AND message_id LIKE 'pi-msg-%'`).all(sessionId, ...values);
34429
35782
  for (const row of rows)
34430
35783
  found.add(row.entry_fingerprint);
34431
35784
  }
34432
35785
  return found;
34433
35786
  }
34434
35787
  function adoptPiFallbackTags(db, sessionId, tagger, fingerprintById, options = {}) {
34435
- const hasFallbackMessageTags = options.hasFallbackMessageTags === true || hasPiFallbackMessageTags2(db, sessionId);
34436
- const hasFallbackToolOwnerTags = options.hasFallbackToolOwnerTags === true || hasPiFallbackToolOwnerTags2(db, sessionId);
34437
- const shouldRunMessageMigration = hasFallbackMessageTags && hasAdoptablePiFallbackMessageTags(db, sessionId, fingerprintById);
34438
- const shouldRunToolOwnerMigration = Boolean(options.messages && options.resolveStableId && hasFallbackToolOwnerTags);
34439
- if (!shouldRunMessageMigration && !shouldRunToolOwnerMigration)
35788
+ let { realIdsByFingerprint, batches } = preparePiAdoptionFingerprints(fingerprintById);
35789
+ const ownerMap = options.messages && options.resolveStableId ? buildPiToolOwnerMap(options.messages, options.resolveStableId) : null;
35790
+ if (!batches.length && !ownerMap?.size && !options.rebuildFingerprints)
34440
35791
  return;
35792
+ const revision = () => readPiAdoptionRevision(db);
35793
+ const beforeDiscovery = revision();
35794
+ const discovered = hasPiFallbackMessageTags(db, sessionId) ? readAdoptablePiFallbackFingerprints(db, sessionId, batches) : new Set;
35795
+ const targetsFor = (fingerprints) => [...fingerprints].flatMap((fingerprint) => (realIdsByFingerprint.get(fingerprint) ?? []).map((id) => [id, fingerprint]));
35796
+ const discoveredTargets = targetsFor(discovered);
34441
35797
  runImmediateTransaction(db, () => {
34442
- if (shouldRunMessageMigration) {
34443
- const adoptable = readAdoptablePiFallbackFingerprints(db, sessionId, fingerprintById);
34444
- for (const [realMessageId, fingerprint] of fingerprintById) {
35798
+ const admitted = revision();
35799
+ const preflight = options.preflightRevision;
35800
+ const rebuild = options.rebuildFingerprints;
35801
+ const expanded = Boolean(preflight && rebuild && (preflight.external.data_version !== admitted.external.data_version || preflight.local.changes !== admitted.local.changes));
35802
+ if (expanded && rebuild) {
35803
+ ({ realIdsByFingerprint, batches } = preparePiAdoptionFingerprints(rebuild()));
35804
+ }
35805
+ if (batches.length && hasPiFallbackMessageTags(db, sessionId)) {
35806
+ const unchanged = !expanded && beforeDiscovery.external.data_version === admitted.external.data_version && beforeDiscovery.local.changes === admitted.local.changes;
35807
+ const adoptable = unchanged ? discovered : readAdoptablePiFallbackFingerprints(db, sessionId, batches);
35808
+ const targets = expanded ? targetsFor(adoptable) : discoveredTargets.filter(([, fp]) => adoptable.has(fp));
35809
+ if (!expanded)
35810
+ targets.push(...targetsFor([...adoptable].filter((fp) => !discovered.has(fp))));
35811
+ for (const [realMessageId, fingerprint] of targets) {
34445
35812
  if (realMessageId.startsWith("pi-msg-"))
34446
35813
  continue;
34447
35814
  if (!adoptable.has(fingerprint))
@@ -34461,7 +35828,7 @@ function adoptPiFallbackTags(db, sessionId, tagger, fingerprintById, options = {
34461
35828
  if (!ordinalMatch)
34462
35829
  continue;
34463
35830
  const realContentId = `${realMessageId}:p${ordinalMatch[1]}`;
34464
- const adoption = adoptPiFallbackMessageTag(db, sessionId, c.tagNumber, c.messageId, realContentId);
35831
+ const adoption = adoptPiFallbackMessageTag(db, sessionId, c.tagNumber, c.messageId, realContentId, getPiServedTagNumbers(sessionId));
34465
35832
  if (adoption.action !== "skipped") {
34466
35833
  tagger.unbindTag(sessionId, c.messageId);
34467
35834
  if (adoption.action === "folded") {
@@ -34472,8 +35839,7 @@ function adoptPiFallbackTags(db, sessionId, tagger, fingerprintById, options = {
34472
35839
  }
34473
35840
  }
34474
35841
  }
34475
- if (shouldRunToolOwnerMigration && options.messages && options.resolveStableId) {
34476
- const ownerMap = buildPiToolOwnerMap(options.messages, options.resolveStableId);
35842
+ if (ownerMap && hasPiFallbackToolOwnerTags(db, sessionId)) {
34477
35843
  for (const row of findPiFallbackToolOwnerTags(db, sessionId)) {
34478
35844
  const parsed = parsePiFallbackToolOwnerId(row.toolOwnerMessageId);
34479
35845
  if (parsed?.role !== "assistant")
@@ -34484,7 +35850,7 @@ function adoptPiFallbackTags(db, sessionId, tagger, fingerprintById, options = {
34484
35850
  const [realOwnerId] = owners;
34485
35851
  if (!realOwnerId || realOwnerId.startsWith("pi-msg-"))
34486
35852
  continue;
34487
- const adoption = adoptPiFallbackToolOwnerTag(db, sessionId, row.tagNumber, row.callId, row.toolOwnerMessageId, realOwnerId);
35853
+ const adoption = adoptPiFallbackToolOwnerTag(db, sessionId, row.tagNumber, row.callId, row.toolOwnerMessageId, realOwnerId, getPiServedTagNumbers(sessionId));
34488
35854
  if (adoption.action !== "skipped") {
34489
35855
  tagger.unbindToolTag(sessionId, row.toolOwnerMessageId, row.callId);
34490
35856
  if (adoption.action === "folded") {
@@ -34555,20 +35921,40 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
34555
35921
  variantChangeLogObserverForTests?.(message);
34556
35922
  sessionLog(sessionId, message);
34557
35923
  });
34558
- registerPiGuardedContext(pi, async (event, ctx) => {
35924
+ const contextHandler = async (event, ctx, budget) => {
35925
+ const passSessionId = ctx.sessionManager ? resolveSessionId2(ctx) : undefined;
35926
+ const generation = Symbol();
35927
+ if (passSessionId && !budget.sideTurn)
35928
+ contextPassGeneration.set(passSessionId, generation);
35929
+ const signal = event.signal ?? ctx.signal;
35930
+ const assertOperation = budget.assertOwner;
35931
+ budget.assertOwner = () => {
35932
+ assertOperation();
35933
+ if (!budget.sideTurn && passSessionId && contextPassGeneration.get(passSessionId) !== generation)
35934
+ throw new PiContextSupersededError("Pi context pass superseded before writer admission");
35935
+ signal?.throwIfAborted();
35936
+ };
35937
+ let assertCurrentPass = budget.assert;
35938
+ const guardAwait = budget.wait;
35939
+ const passGuard = { assert: assertCurrentPass, wait: guardAwait };
35940
+ guardSqliteTransformPass({
35941
+ assert: assertCurrentPass,
35942
+ remainingMs: () => budget.remainingWork()
35943
+ });
35944
+ budget.stage = "schema/claim";
35945
+ const toolWireSchema = await guardAwait(loadPiToolWireSchema());
34559
35946
  if (processOptions.checkoutClaim) {
34560
35947
  const claimSessionId = resolveSessionId2(ctx);
34561
35948
  if (claimSessionId) {
34562
- const refusal = await processOptions.checkoutClaim.refusal(claimSessionId, ctx.cwd);
35949
+ const refusal = await guardAwait(processOptions.checkoutClaim.refusal(claimSessionId, ctx.cwd));
34563
35950
  if (refusal)
34564
35951
  throw refusal;
34565
35952
  }
34566
35953
  }
34567
- const transformStartTime = performance.now();
35954
+ const transformStartTime = budget.startedAt;
34568
35955
  let rawMessageCount = 0;
34569
35956
  let rawFallbackLimit;
34570
35957
  let sessionIdForError;
34571
- let sessionMetaForPass;
34572
35958
  let lkgPassSnapshot;
34573
35959
  let lkgCompactionOff = baseOptions.compactionOff === true;
34574
35960
  let lkgEmergencyRecoveryArmed = false;
@@ -34596,17 +35982,21 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
34596
35982
  const scheduler = schedulerFor(options);
34597
35983
  lkgCompactionOff = options.compactionOff === true;
34598
35984
  const projectIdentity = resolveProjectIdentityForSession(projectDirectory, options.allowHomeProject) ?? "";
34599
- updateSessionProjectTracking(sessionId, projectIdentity, options.db);
34600
35985
  logTransformTiming(sessionId, "findSessionId", tFindSession, `messages=${event.messages.length}`);
35986
+ budget.stage = "branch projection";
34601
35987
  const tEntryBranch = performance.now();
34602
35988
  const branchEntries = readPiBranchEntriesForContext(ctx, sessionId);
35989
+ assertCurrentPass();
34603
35990
  const piLiveUsageIsRawBranchEstimate = isPiLiveUsageRawBranchEstimate(branchEntries);
34604
35991
  recordPiLiveUsageClassification(sessionId, piLiveUsageIsRawBranchEstimate);
34605
- schedulePiTransformDecisionResolve({
34606
- db: options.db,
34607
- sessionId,
34608
- branchEntries
34609
- });
35992
+ assertCurrentPass();
35993
+ if (!budget.sideTurn)
35994
+ schedulePiTransformDecisionResolve({
35995
+ db: options.db,
35996
+ sessionId,
35997
+ branchEntries,
35998
+ assertCurrentPass
35999
+ });
34610
36000
  let rawOrdinalCount;
34611
36001
  const rawMessageProvider = {
34612
36002
  getMessageCount: () => rawOrdinalCount ??= branchEntries !== null ? countPiRawMessages(branchEntries) : readPiSessionMessages(ctx).length,
@@ -34626,13 +36016,56 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
34626
36016
  const lkgModelKey = resolvePiContextModelKey(ctx) ?? null;
34627
36017
  const lkgContextModel = ctx.model;
34628
36018
  const lkgProviderKey = typeof lkgContextModel?.provider === "string" ? lkgContextModel.provider : null;
34629
- lkgPassSnapshot = lkgCoordinator.beginPass({
36019
+ const snapshot = lkgCoordinator.beginPass({
34630
36020
  sessionId,
34631
36021
  messages: event.messages,
34632
36022
  entryIds: lkgEntryIds,
34633
36023
  modelKey: lkgModelKey,
34634
- providerKey: lkgProviderKey
36024
+ providerKey: lkgProviderKey,
36025
+ apiKey: ctx.model?.api ?? null,
36026
+ transport: ctx.model?.transport
34635
36027
  });
36028
+ lkgPassSnapshot = snapshot;
36029
+ let checkedReplay = false;
36030
+ budget.stage = "writer admission";
36031
+ assertCurrentPass();
36032
+ const sessionMetaForUsage = await guardAwait(withAsyncPrivilegedWriter(options.db, () => {
36033
+ assertCurrentPass();
36034
+ return getOrCreateSessionMeta(options.db, sessionId);
36035
+ }, {
36036
+ signal,
36037
+ budgetMs: budget.writerAllowance(),
36038
+ assertCurrentPass,
36039
+ jitter: true,
36040
+ beforeRetry: (error) => {
36041
+ assertCurrentPass();
36042
+ if (checkedReplay)
36043
+ return;
36044
+ checkedReplay = true;
36045
+ const state = loadPiHistorianStateSnapshot(options.db, sessionId);
36046
+ lkgEmergencyRecoveryArmed = state.needsEmergencyRecovery;
36047
+ if (state.detectedContextLimit > 0)
36048
+ rawFallbackLimit = resolvePiWindowGeometry({
36049
+ model: ctx.model,
36050
+ rawContextWindow: piUsage?.contextWindow,
36051
+ detectedContextLimit: state.detectedContextLimit
36052
+ })?.usableHard;
36053
+ if (lkgCompactionOff || lkgEmergencyRecoveryArmed || isProviderOverflowFailClosedProven(sessionId))
36054
+ return;
36055
+ const replay = lkgCoordinator.replay(snapshot, (id) => ctx.sessionManager.getEntry?.(id)?.parentId);
36056
+ if (!replay.ok)
36057
+ return;
36058
+ try {
36059
+ assertPiRawFallbackFits(replay.messages, rawFallbackLimit, (line) => logPiLkgRecovery(sessionId, line), error, readPiLkgFitEnvelope(ctx, pi, lkgModelKey, sessionDecisionCalibration(options.db, sessionId), toolWireSchema), replay.measuredPrefix);
36060
+ } catch {
36061
+ return;
36062
+ }
36063
+ logPiLkgRecovery(sessionId, `TRANSIENT STORAGE FAILURE ${piStorageErrorReason(error)}: LKG replay served ${replay.messages.length} messages instead of raw ${rawMessageCount}`);
36064
+ throw new PiLkgAdmissionReplay(replay.messages);
36065
+ }
36066
+ }));
36067
+ assertCurrentPass();
36068
+ updateSessionProjectTracking(sessionId, projectIdentity, options.db, projectDirectory);
34636
36069
  const lkgInputIdByRef = new Map;
34637
36070
  for (const input of lkgPassSnapshot.inputs)
34638
36071
  lkgInputIdByRef.set(event.messages[input.messageIndex], input.id);
@@ -34660,8 +36093,6 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
34660
36093
  }
34661
36094
  }
34662
36095
  const tMeta = performance.now();
34663
- const sessionMetaForUsage = getOrCreateSessionMeta(options.db, sessionId);
34664
- sessionMetaForPass = sessionMetaForUsage;
34665
36096
  const historianStateForPass = options.compactionOff ? EMPTY_PI_HISTORIAN_STATE_SNAPSHOT : (() => {
34666
36097
  try {
34667
36098
  return loadPiHistorianStateSnapshot(options.db, sessionId);
@@ -34817,7 +36248,7 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
34817
36248
  } catch (err) {
34818
36249
  sessionLog(sessionId, `transform: overflow state read failed: ${err instanceof Error ? err.message : String(err)}`);
34819
36250
  }
34820
- const sessionMeta = sessionMetaForUsage;
36251
+ let sessionMeta = sessionMetaForUsage;
34821
36252
  const modelKey = liveModelBySession.get(sessionId);
34822
36253
  const providerId = typeof ctx.model?.provider === "string" ? ctx.model.provider : undefined;
34823
36254
  const canUseEmptySentinels = modelAcceptsEmptyContent(providerId);
@@ -34835,28 +36266,14 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
34835
36266
  detectedContextLimit
34836
36267
  });
34837
36268
  rawFallbackLimit = baseWindowGeometry?.usableHard ?? rawFallbackLimit;
34838
- let provenInputTokens = resolvePiProvenInputFloor({
36269
+ const providerInputLimit = piPressureEvidenceLimit(baseWindowGeometry);
36270
+ const provenInputTokens = resolvePiProvenInputFloor({
34839
36271
  db: options.db,
34840
36272
  sessionId,
34841
- modelKey: currentModelKey
36273
+ modelKey: currentModelKey,
36274
+ readBranch: () => branchEntries ?? undefined,
36275
+ providerInputLimit
34842
36276
  });
34843
- if (baseWindowGeometry && hasTrustedAbsoluteWall(baseWindowGeometry) && provenInputTokens > baseWindowGeometry.derivation.absoluteWall) {
34844
- sessionLog(sessionId, `transform: persisted proven floor ${provenInputTokens} exceeds trusted absolute wall ${baseWindowGeometry.derivation.absoluteWall}; cleared and re-resolved to ${baseWindowGeometry.usableSoft}`);
34845
- updateSessionMeta(options.db, sessionId, {
34846
- observedSafeInputTokens: 0,
34847
- cacheAlertSent: false,
34848
- lastUsageContextLimit: baseWindowGeometry.usableSoft,
34849
- lastInputTokens: sessionMeta.lastInputTokens > baseWindowGeometry.derivation.absoluteWall ? baseWindowGeometry.derivation.absoluteWall : sessionMeta.lastInputTokens,
34850
- lastContextPercentage: sessionMeta.lastInputTokens > baseWindowGeometry.derivation.absoluteWall ? baseWindowGeometry.derivation.absoluteWall / baseWindowGeometry.usableSoft * 100 : sessionMeta.lastContextPercentage
34851
- });
34852
- provenInputTokens = 0;
34853
- sessionMeta.observedSafeInputTokens = 0;
34854
- sessionMeta.cacheAlertSent = false;
34855
- if (usageInputTokens > baseWindowGeometry.derivation.absoluteWall) {
34856
- usageInputTokens = baseWindowGeometry.derivation.absoluteWall;
34857
- usagePercentage = usageInputTokens / baseWindowGeometry.usableSoft * 100;
34858
- }
34859
- }
34860
36277
  const windowGeometry = resolvePiWindowGeometry({
34861
36278
  rawContextWindow: usageContextLimit,
34862
36279
  rawContextWindowSource: usageContextWindowSource,
@@ -34874,7 +36291,19 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
34874
36291
  if (!usedPersistedUsage && isSaneLimit(usageContextLimit) && usageInputTokens > 0) {
34875
36292
  usagePercentage = usageInputTokens / usageContextLimit * 100;
34876
36293
  }
36294
+ let fallbackInputTokens;
36295
+ const suspectReading = Math.max(usageInputTokens, piUsage?.tokens ?? 0);
36296
+ if (suspectReading > providerInputLimit || piProviderUsageWasRejected(sessionId)) {
36297
+ if (suspectReading > providerInputLimit)
36298
+ noteRejectedPiUsage(sessionId, suspectReading, providerInputLimit, "transform");
36299
+ const replay = !lkgCompactionOff && lkgPassSnapshot ? lkgCoordinator.replay(lkgPassSnapshot, (id) => ctx.sessionManager.getEntry?.(id)?.parentId) : undefined;
36300
+ const counts = tokenizePiMessages(replay?.ok ? replay.messages : event.messages);
36301
+ const envelope = readPiLkgFitEnvelope(ctx, pi, currentModelKey, sessionDecisionCalibration(options.db, sessionId));
36302
+ fallbackInputTokens = counts.conversation + counts.toolCall + (envelope?.systemTokens ?? 0) + (envelope?.toolDefinitionTokens ?? 0);
36303
+ }
34877
36304
  ({ percentage: usagePercentage, inputTokens: usageInputTokens } = resolvePiPressureSnapshotWithEstimateGuard({
36305
+ providerInputLimit,
36306
+ fallbackInputTokens,
34878
36307
  sessionId,
34879
36308
  source: "transform",
34880
36309
  liveIsRawBranchEstimate: piLiveUsageIsRawBranchEstimate,
@@ -34931,14 +36360,25 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
34931
36360
  if (now - lastNotifiedAt >= EMERGENCY_NOTIFICATION_COOLDOWN_MS) {
34932
36361
  lastEmergencyNotificationAtMs.set(sessionId, now);
34933
36362
  sendPiIgnoredNotification(ctx, "Context full — /ctx-flush or /clear to continue.");
34934
- sessionLog(sessionId, `EMERGENCY: usage=${usagePercentage.toFixed(1)}% — notified user, awaiting in-flight historian + evaluating tiered emergency reclaim`);
34935
- }
34936
- const histPromise = inFlightHistorian.get(sessionId);
34937
- if (histPromise) {
34938
- try {
34939
- await withTimeout(histPromise, 30000);
34940
- sessionLog(sessionId, "EMERGENCY: historian wait completed (or timed out)");
34941
- } catch {}
36363
+ sessionLog(sessionId, `EMERGENCY: usage=${usagePercentage.toFixed(1)}% — notified user, evaluating available emergency reclaim`);
36364
+ }
36365
+ const historian = inFlightHistorian.get(sessionId);
36366
+ const visibleToolResults = new Set(event.messages.flatMap((message) => message.role === "toolResult" ? [message.toolCallId] : []));
36367
+ const toolTags = getTagsBySession(options.db, sessionId).filter((tag) => tag.type === "tool");
36368
+ const knownTools = new Set(toolTags.map((tag) => tag.messageId));
36369
+ const activeTools = new Set(toolTags.filter((tag) => tag.status === "active").map((tag) => tag.messageId));
36370
+ const hasToolReclaim = event.messages.some((message) => message.role === "assistant" && message.content.some((part) => part.type === "toolCall" && visibleToolResults.has(part.id) && (!knownTools.has(part.id) || activeTools.has(part.id))));
36371
+ if (historian && !hasToolReclaim && getPendingOps(options.db, sessionId).length === 0 && !historyRefreshSessions.has(sessionId) && !deferredHistoryRefreshSessions.has(sessionId)) {
36372
+ budget.stage = "emergency historian";
36373
+ await budget.wait(historian);
36374
+ assertCurrentPass();
36375
+ sessionMeta = getOrCreateSessionMeta(options.db, sessionId);
36376
+ piM0M1PassSnapshot = options.injection ? createPiM0M1PassSnapshot({
36377
+ db: options.db,
36378
+ sessionId,
36379
+ compactionOff: false,
36380
+ sessionMeta
36381
+ }) : undefined;
34942
36382
  }
34943
36383
  if (emergencyRecoveryArmed && realUsagePercentageBeforeEmergencyBump < forceMaterializationPercentage && !inFlightHistorian.has(sessionId) && !hasEligiblePiCompartmentHistory(options.db, sessionId)) {
34944
36384
  try {
@@ -34961,7 +36401,17 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
34961
36401
  }) / 100)) : undefined;
34962
36402
  logTransformTiming(sessionId, "prePipelineTotal", transformStartTime);
34963
36403
  const tRunPipeline = performance.now();
34964
- const result = await runPipeline({
36404
+ budget.stage = "pipeline";
36405
+ assertCurrentPass = budget.assertOutcome;
36406
+ guardSqliteTransformPass({
36407
+ assert: assertCurrentPass,
36408
+ remainingMs: budget.remainingOutcome
36409
+ });
36410
+ if (beforePipelineForTests)
36411
+ await budget.waitMandatory(beforePipelineForTests());
36412
+ assertCurrentPass();
36413
+ const result = await budget.waitMandatory(runPipeline({
36414
+ assertCurrentPass,
34965
36415
  db: options.db,
34966
36416
  tagger,
34967
36417
  sessionId,
@@ -35006,27 +36456,30 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
35006
36456
  },
35007
36457
  isCacheBusting,
35008
36458
  reasoningClearing: {
35009
- clearReasoningAge: options.heuristics?.clearReasoningAge ?? DEFAULT_CLEAR_REASONING_AGE,
36459
+ keepReasoningTokens: resolveKeepReasoningTokens(options.heuristics?.keepReasoningTokens, resolvePiContextModelKey(ctx)),
35010
36460
  nativeReasoningMayClear: canClearNativeReasoning(ctx.model),
35011
36461
  prefixBound: isPrefixBoundThinkingModel(typeof ctx.model?.provider === "string" ? ctx.model.provider : undefined, typeof ctx.model?.id === "string" ? ctx.model.id : undefined),
35012
36462
  preserveReasoningToolArcs: ctx.model?.api !== "openai-codex-responses" && ctx.model?.api !== "openai-responses"
35013
36463
  },
36464
+ resolvedProviderID: ctx.model?.provider,
36465
+ resolvedModelID: ctx.model?.id,
35014
36466
  canUseEmptySentinels,
35015
36467
  temporalAwareness: options.injection?.temporalAwareness === true,
35016
- appendCompaction: resolvePiAppendCompaction(ctx),
36468
+ appendCompaction: guardedPiAppendCompaction(ctx, assertCurrentPass),
35017
36469
  readBranchEntries: resolvePiReadBranchEntries(ctx),
35018
36470
  isSubagent: sessionMeta.isSubagent,
35019
36471
  compactionOff: options.compactionOff === true,
35020
36472
  injectionPassSnapshot: piM0M1PassSnapshot
35021
- });
36473
+ }));
35022
36474
  logTransformTiming(sessionId, "runPipeline", tRunPipeline);
35023
36475
  const postPipelineStart = performance.now();
35024
36476
  const tTransformDecision = performance.now();
35025
36477
  if (strictEntryIds) {
36478
+ assertCurrentPass();
35026
36479
  recordSuccessfulTaggedMessageIds(sessionId, strictEntryIds);
35027
36480
  }
35028
36481
  const piDecisionSnapshotNewestAssistant = result.bustedThisPass ? findNewestPiAssistantEntryId(branchEntries) : undefined;
35029
- if (piDecisionSnapshotNewestAssistant !== undefined) {
36482
+ const publishTransformDecision = !budget.sideTurn && piDecisionSnapshotNewestAssistant !== undefined ? () => {
35030
36483
  recordPendingPiTransformDecision(sessionId, {
35031
36484
  tsMs: Date.now(),
35032
36485
  decision: schedulerDecision,
@@ -35041,28 +36494,55 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
35041
36494
  droppedCount: result.droppedCount,
35042
36495
  inputTokens: usageInputTokens,
35043
36496
  bustedThisPass: true
35044
- }, piDecisionSnapshotNewestAssistant);
35045
- }
36497
+ }, piDecisionSnapshotNewestAssistant, budget.assertDecisionPublication);
36498
+ } : undefined;
35046
36499
  logTransformTiming(sessionId, "transformDecisionAndReuseState", tTransformDecision);
35047
36500
  const tHistorianScheduling = performance.now();
35048
- if (options.historian && !options.compactionOff) {
35049
- maybeFireHistorian({
35050
- pi,
35051
- ctx,
35052
- sessionId,
35053
- db: options.db,
35054
- historian: options.historian,
35055
- isFirstContextPassForSession,
35056
- activeTags: result.activeTags,
35057
- rawMessageProvider,
35058
- taggerFloor,
35059
- sessionMeta,
35060
- piUsage,
35061
- minimumPercentage: usagePercentage,
35062
- liveIsRawBranchEstimate: piLiveUsageIsRawBranchEstimate,
35063
- historianStateSnapshot: historianStateForPass,
35064
- publishedHistoryRide: isCacheBusting
36501
+ const historian = options.historian;
36502
+ budget.stage = "historian scheduling";
36503
+ if (historian && !options.compactionOff && budget.remainingWork() > 0) {
36504
+ guardSqliteTransformPass({
36505
+ assert: budget.assert,
36506
+ remainingMs: () => budget.remainingWork()
35065
36507
  });
36508
+ try {
36509
+ budget.assert();
36510
+ maybeFireHistorian({
36511
+ startGuard: () => {
36512
+ if (!budget.completed)
36513
+ budget.assertOwner();
36514
+ if (budget.abandoned || budget.remainingWork() === 0)
36515
+ throw new PiContextDeadlineError(budget.diagnostic());
36516
+ },
36517
+ pi,
36518
+ ctx,
36519
+ sessionId,
36520
+ db: options.db,
36521
+ historian,
36522
+ isFirstContextPassForSession,
36523
+ activeTags: result.activeTags,
36524
+ rawMessageProvider,
36525
+ taggerFloor,
36526
+ sessionMeta,
36527
+ piUsage,
36528
+ pressureSnapshot: {
36529
+ percentage: usagePercentage,
36530
+ inputTokens: usageInputTokens
36531
+ },
36532
+ minimumPercentage: usagePercentage,
36533
+ liveIsRawBranchEstimate: piLiveUsageIsRawBranchEstimate,
36534
+ historianStateSnapshot: historianStateForPass,
36535
+ publishedHistoryRide: isCacheBusting
36536
+ });
36537
+ } catch (error) {
36538
+ budget.assertOutcome();
36539
+ sessionLog(sessionId, `historian scheduling skipped: ${String(error)}`);
36540
+ } finally {
36541
+ guardSqliteTransformPass({
36542
+ assert: budget.assertOutcome,
36543
+ remainingMs: budget.remainingOutcome
36544
+ });
36545
+ }
35066
36546
  }
35067
36547
  logTransformTiming(sessionId, "historianScheduling", tHistorianScheduling);
35068
36548
  const tPostTransform = performance.now();
@@ -35098,9 +36578,15 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
35098
36578
  }
35099
36579
  logTransformTiming(sessionId, "noteNudges", tNoteNudges);
35100
36580
  const tAutoSearch = performance.now();
35101
- if (options.autoSearch?.enabled && !options.compactionOff) {
36581
+ budget.stage = "auto-search";
36582
+ if (options.autoSearch?.enabled && !options.compactionOff && budget.remainingWork() > PI_CONTEXT_BUDGET.searchMs) {
36583
+ guardSqliteTransformPass({
36584
+ assert: budget.assert,
36585
+ remainingMs: () => budget.remainingWork()
36586
+ });
35102
36587
  try {
35103
- outputMessages = await runAutoSearchHintForPi({
36588
+ outputMessages = await guardAwait(runAutoSearchHintForPi({
36589
+ passGuard,
35104
36590
  sessionId,
35105
36591
  db: options.db,
35106
36592
  messages: outputMessages,
@@ -35116,9 +36602,15 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
35116
36602
  visibleMemoryIds: getVisibleMemoryIds(options.db, sessionId) ?? null,
35117
36603
  wordRules: cavemanWordRulesForLanguage(options.language)
35118
36604
  }
35119
- });
36605
+ }));
35120
36606
  } catch (err) {
36607
+ assertCurrentPass();
35121
36608
  sessionLog(sessionId, `auto-search failed: ${err instanceof Error ? err.message : String(err)}`);
36609
+ } finally {
36610
+ guardSqliteTransformPass({
36611
+ assert: budget.assertOutcome,
36612
+ remainingMs: budget.remainingOutcome
36613
+ });
35122
36614
  }
35123
36615
  }
35124
36616
  logTransformTiming(sessionId, "autoSearch", tAutoSearch);
@@ -35268,6 +36760,7 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
35268
36760
  }
35269
36761
  logTransformTiming(sessionId, "channelNudgeAccounting", tChannelAccounting);
35270
36762
  const tWorkMetrics = performance.now();
36763
+ budget.stage = "bookkeeping";
35271
36764
  try {
35272
36765
  const metrics = computePiWorkMetrics(outputMessages);
35273
36766
  setSessionWorkMetrics(options.db, sessionId, metrics.newWorkTokens, metrics.totalInputTokens);
@@ -35289,7 +36782,8 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
35289
36782
  logTransformTiming(sessionId, "stableIdSchemePersist", tStableIdSchemePersist);
35290
36783
  logTransformTiming(sessionId, "postTransformPhase", tPostTransform);
35291
36784
  clearLastTransformErrorIfSet(options.db, sessionId, sessionMeta.lastTransformError);
35292
- options.maybeAutoEmbedSession?.(sessionId, projectDirectory, projectIdentity);
36785
+ if (budget.remainingWork() > PI_CONTEXT_BUDGET.completionReserveMs)
36786
+ withoutSqliteTransformPass(() => options.maybeAutoEmbedSession?.(sessionId, projectDirectory, projectIdentity));
35293
36787
  logTransformTiming(sessionId, "postPipelineTotal", postPipelineStart);
35294
36788
  const transformElapsedMs = performance.now() - transformStartTime;
35295
36789
  recordPiTransformTiming({
@@ -35301,19 +36795,27 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
35301
36795
  sessionLog(sessionId, `transform completed in ${transformElapsedMs.toFixed(1)}ms (${outputMessages.length} messages, ${result.targetCount} targets, watermark: ${result.reasoningWatermark})`);
35302
36796
  runPiDebugAssertion(assertTailHygieneLastWriter);
35303
36797
  if (!options.compactionOff && (result.executedWorkThisPass || isCacheBusting || schedulerDecision === "execute")) {
35304
- const envelope = readPiLkgFitEnvelope(ctx, pi, resolvePiContextModelKey(ctx), sessionDecisionCalibration(options.db, sessionId));
36798
+ const envelope = readPiLkgFitEnvelope(ctx, pi, resolvePiContextModelKey(ctx), sessionDecisionCalibration(options.db, sessionId), toolWireSchema);
35305
36799
  const estimate = estimatePiOutgoingInputTokens(outputMessages, envelope, lkgPassSnapshot ? lkgCoordinator.measureOutgoingPrefix(lkgPassSnapshot, outputMessages, (id) => ctx.sessionManager.getEntry?.(id)?.parentId) : undefined, rawFallbackLimit);
35306
36800
  const refusal = outgoingContextRefusal(estimate, rawFallbackLimit, protectedToolTokenCount(getActiveTagsBySession(options.db, sessionId), options.protectedTools, envelope ? calibrationForModelKey(envelope.modelKey) : undefined));
35307
36801
  if (refusal)
35308
36802
  throw contextRefusalError(refusal);
35309
36803
  }
35310
36804
  let serializedOutput;
35311
- if (!lkgCompactionOff && lkgPassSnapshot) {
36805
+ budget.stage = "LKG/served publication";
36806
+ budget.assertOutcome();
36807
+ guardSqliteTransformPass({
36808
+ assert: budget.assertOutcome,
36809
+ remainingMs: () => Math.max(0, PI_CONTEXT_BUDGET.outcomeMs - budget.elapsed())
36810
+ });
36811
+ if (!budget.sideTurn && !lkgCompactionOff && lkgPassSnapshot) {
35312
36812
  let hostEnvelopeSignature;
35313
36813
  try {
35314
- hostEnvelopeSignature = readPiLkgFitEnvelope(ctx, pi, resolvePiContextModelKey(ctx), sessionDecisionCalibration(baseOptions.db, sessionId))?.envelopeSignature;
36814
+ hostEnvelopeSignature = readPiLkgFitEnvelope(ctx, pi, resolvePiContextModelKey(ctx), sessionDecisionCalibration(baseOptions.db, sessionId), toolWireSchema)?.envelopeSignature;
35315
36815
  } catch {}
36816
+ budget.assertOutcome();
35316
36817
  serializedOutput = lkgCoordinator.captureAppliedPass({
36818
+ assertCurrentPass: budget.assertOutcome,
35317
36819
  hostEnvelopeSignature,
35318
36820
  snapshot: lkgPassSnapshot,
35319
36821
  outputMessages,
@@ -35321,7 +36823,15 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
35321
36823
  cacheBusting: result.bustedThisPass
35322
36824
  });
35323
36825
  }
35324
- capturePiServedArray(sessionId, outputMessages, { serializedOutput });
36826
+ budget.assertOutcome();
36827
+ if (!budget.sideTurn)
36828
+ capturePiServedArray(sessionId, outputMessages, {
36829
+ servedTagNumbers: result.servedTagNumbers,
36830
+ serializedOutput,
36831
+ assertCurrentPass: budget.assertOutcome
36832
+ });
36833
+ if (!budget.sideTurn)
36834
+ captureOpencodeReasoningBudgetStatus(sessionId, piReasoningStatusMessages(outputMessages), resolveKeepReasoningTokens(options.heuristics?.keepReasoningTokens, resolvePiContextModelKey(ctx)), isPrefixBoundThinkingModel(ctx.model?.provider, ctx.model?.id), sessionDecisionCalibration(options.db, sessionId).proseRatio);
35325
36835
  if (thinkingBindingRecoveryApplied) {
35326
36836
  try {
35327
36837
  clearThinkingBindingRecoveryIf(options.db, sessionId, thinkingBindingRecoveryApplied.flagTarget);
@@ -35329,14 +36839,34 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
35329
36839
  sessionLog(sessionId, `thinking binding recovery cleanup deferred: ${error instanceof Error ? error.message : String(error)}`);
35330
36840
  }
35331
36841
  }
36842
+ budget.assertOutcome();
36843
+ publishTransformDecision?.();
35332
36844
  return { messages: outputMessages };
35333
36845
  } catch (err) {
36846
+ budget.assertOutcome();
36847
+ budget.stage = "recovery";
36848
+ if (err instanceof PiServedIdentityError) {
36849
+ if (sessionIdForError)
36850
+ clearPiLkgSessionState(sessionIdForError);
36851
+ throw new PiStorageBusyError({ cause: err });
36852
+ }
36853
+ if (err instanceof PiLkgAdmissionReplay && sessionIdForError) {
36854
+ budget.assertOutcome();
36855
+ if (!budget.sideTurn)
36856
+ capturePiServedArray(sessionIdForError, err.messages, {
36857
+ assertCurrentPass: budget.assertOutcome
36858
+ });
36859
+ budget.assertOutcome();
36860
+ budget.recovery = "fitting LKG before writer retries";
36861
+ return { messages: err.messages };
36862
+ }
35334
36863
  if ((isFailClosedBlockingError(err) || err instanceof EmergencyFailClosedError) && !baseOptions.compactionOff)
35335
36864
  throw err;
35336
36865
  if (!lkgCompactionOff && sessionIdForError && isProviderOverflowFailClosedProven(sessionIdForError)) {
35337
36866
  throw new EmergencyFailClosedError("Emergency recovery transform failed; refusing an unbounded raw fallback", { cause: err });
35338
36867
  }
35339
36868
  const message = err instanceof Error ? err.message : String(err);
36869
+ budget.failureReason = message;
35340
36870
  const stack = err instanceof Error ? err.stack : undefined;
35341
36871
  const transientStorageFailure = isTransientPiStorageError(err);
35342
36872
  const degradedPass = !lkgCompactionOff;
@@ -35349,14 +36879,21 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
35349
36879
  assertPiRawFallbackFits(replay.messages, rawFallbackLimit, (line) => {
35350
36880
  if (sessionIdForError)
35351
36881
  logPiLkgRecovery(sessionIdForError, line);
35352
- }, err, readPiLkgFitEnvelope(ctx, pi, resolvePiContextModelKey(ctx), sessionDecisionCalibration(baseOptions.db, sessionIdForError)), replay.measuredPrefix);
36882
+ }, err, readPiLkgFitEnvelope(ctx, pi, resolvePiContextModelKey(ctx), sessionDecisionCalibration(baseOptions.db, sessionIdForError), toolWireSchema), replay.measuredPrefix);
35353
36883
  const reason = piStorageErrorReason(err);
35354
36884
  logPiLkgRecovery(sessionIdForError, `${failureLabel} ${reason}: LKG replay served ${replay.messages.length} messages instead of raw ${rawMessageCount}`);
35355
- capturePiServedArray(sessionIdForError, replay.messages);
36885
+ budget.assertOutcome();
36886
+ if (!budget.sideTurn)
36887
+ capturePiServedArray(sessionIdForError, replay.messages, {
36888
+ assertCurrentPass: budget.assertOutcome
36889
+ });
36890
+ budget.assertOutcome();
36891
+ budget.recovery = "fitting LKG replay";
35356
36892
  return { messages: replay.messages };
35357
36893
  }
35358
36894
  logPiLkgRecovery(sessionIdForError, `${failureLabel} ${piStorageErrorReason(err)}: LKG unavailable (${replay.reason}); refusing unreduced ${rawMessageCount}-message input`);
35359
36895
  } catch (replayError) {
36896
+ budget.assertOutcome();
35360
36897
  if (replayError instanceof PiStorageBusyError)
35361
36898
  throw replayError;
35362
36899
  logPiLkgRecovery(sessionIdForError, `${failureLabel} ${piStorageErrorReason(err)}: LKG replay unavailable (${replayError instanceof Error ? replayError.message : String(replayError)}); refusing unreduced ${rawMessageCount}-message input`);
@@ -35367,9 +36904,6 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
35367
36904
  }
35368
36905
  if (transientStorageFailure)
35369
36906
  throw new PiStorageBusyError({ cause: err });
35370
- if (sessionIdForError && !transientStorageFailure) {
35371
- persistLastTransformErrorIfChanged(baseOptions.db, sessionIdForError, summarizeTransformError(err), sessionMetaForPass?.lastTransformError);
35372
- }
35373
36907
  if (!lkgCompactionOff && sessionIdForError) {
35374
36908
  throw new PiStorageBusyError({
35375
36909
  cause: err instanceof PiDegradedPassError ? err : new PiDegradedPassError("context-handler-failed", {
@@ -35380,6 +36914,23 @@ function registerPiContextHandler(pi, baseOptions, processOptions = {}) {
35380
36914
  log(`[magic-context][pi] context handler failed (continuing without mutation): ${message}`, stack);
35381
36915
  return;
35382
36916
  }
36917
+ };
36918
+ registerPiGuardedContext(pi, contextHandler, {
36919
+ compactionOff: (ctx) => (baseOptions.resolveForProject?.(ctx.cwd) ?? baseOptions).compactionOff === true,
36920
+ onRefusal: (ctx, budget, message, isCurrent) => {
36921
+ const sessionId = resolveSessionId2(ctx);
36922
+ if (!sessionId)
36923
+ return;
36924
+ withoutSqliteTransformPass(() => setImmediate(() => {
36925
+ if (!isCurrent())
36926
+ return;
36927
+ try {
36928
+ withSqliteBackgroundWriter(() => persistLastTransformErrorIfChanged(baseOptions.db, sessionId, budget.failureReason ?? message));
36929
+ } catch (error) {
36930
+ log(`[magic-context][pi] refused-turn diagnostic not persisted ${budget.diagnostic()}`, error);
36931
+ }
36932
+ }));
36933
+ }
35383
36934
  });
35384
36935
  log("[magic-context][pi] registered context handler (tagging + drops + nudges)");
35385
36936
  }
@@ -35405,7 +36956,7 @@ function resolvePiHistorianTriggerInputs(args) {
35405
36956
  executeThresholdPercentage,
35406
36957
  triggerBudget: deriveTriggerBudget(contextLimit, executeThresholdPercentage),
35407
36958
  protectedTags: args.historian.protectedTags,
35408
- clearReasoningAge: args.historian.clearReasoningAge ?? DEFAULT_CLEAR_REASONING_AGE,
36959
+ keepReasoningTokens: resolveKeepReasoningTokens(args.historian.keepReasoningTokens, args.modelKey),
35409
36960
  commitClusterTrigger: args.historian.commitClusterTrigger,
35410
36961
  contextLimit,
35411
36962
  emergencyCeilingTokens: Math.floor(contextLimit * (executeThresholdPercentage / 100))
@@ -35499,12 +37050,15 @@ function spawnPiHistorianRun(args) {
35499
37050
  currentContextLimit,
35500
37051
  fallbackModelId
35501
37052
  } = args;
37053
+ args.startGuard?.();
35502
37054
  const holderId = crypto3.randomUUID();
35503
37055
  const controller = new AbortController;
35504
37056
  historianAbortControllers.set(sessionId, controller);
35505
- const runPromise = (async () => {
37057
+ const runPromise = withoutSqliteTransformPass(() => (async () => {
37058
+ await new Promise((resolve) => setTimeout(resolve, 0));
35506
37059
  if (controller.signal.aborted)
35507
37060
  return;
37061
+ args.startGuard?.();
35508
37062
  const lease = acquireCompartmentLease(db, sessionId, holderId);
35509
37063
  if (!lease) {
35510
37064
  sessionLog(sessionId, "historian skipped: compartment lease held by another process");
@@ -35517,6 +37071,9 @@ function spawnPiHistorianRun(args) {
35517
37071
  }
35518
37072
  const renewal = startPiCompartmentLeaseRenewal(db, sessionId, holderId);
35519
37073
  try {
37074
+ const runBoundary = typeof boundarySnapshot === "function" ? boundarySnapshot() : boundarySnapshot;
37075
+ if (!runBoundary)
37076
+ return;
35520
37077
  await runPiHistorian({
35521
37078
  db,
35522
37079
  sessionId,
@@ -35544,7 +37101,7 @@ function spawnPiHistorianRun(args) {
35544
37101
  const window = ctx.modelRegistry?.find(model.slice(0, slash), model.slice(slash + 1))?.contextWindow;
35545
37102
  return isSaneLimit(window) ? window : undefined;
35546
37103
  },
35547
- boundarySnapshot,
37104
+ boundarySnapshot: runBoundary,
35548
37105
  refreshBoundarySnapshot,
35549
37106
  currentContextLimit,
35550
37107
  historianTimeoutMs: historian.timeoutMs,
@@ -35604,7 +37161,7 @@ function spawnPiHistorianRun(args) {
35604
37161
  } catch (error) {
35605
37162
  sessionLog(sessionId, `pi historian finalizer failed: ${error instanceof Error ? error.message : String(error)}`);
35606
37163
  }
35607
- });
37164
+ }));
35608
37165
  inFlightHistorian.set(sessionId, runPromise);
35609
37166
  historian.onStatusChange?.(ctx, sessionId);
35610
37167
  }
@@ -35614,6 +37171,13 @@ function resolvePiAppendCompaction(ctx) {
35614
37171
  return;
35615
37172
  return sm.appendCompaction.bind(sm);
35616
37173
  }
37174
+ function guardedPiAppendCompaction(ctx, assertCurrentPass) {
37175
+ const append = resolvePiAppendCompaction(ctx);
37176
+ return append ? (...args) => {
37177
+ assertCurrentPass();
37178
+ return append(...args);
37179
+ } : undefined;
37180
+ }
35617
37181
  function resolvePiReadBranchEntries(ctx) {
35618
37182
  const sm = ctx.sessionManager;
35619
37183
  if (typeof sm?.getBranch !== "function")
@@ -35671,10 +37235,20 @@ function maybeFireHistorian(args) {
35671
37235
  provenInputTokens: resolvePiProvenInputFloor({
35672
37236
  db,
35673
37237
  sessionId,
35674
- modelKey: resolvePiContextModelKey(ctx)
37238
+ modelKey: resolvePiContextModelKey(ctx),
37239
+ readBranch: () => ctx.sessionManager.getBranch(),
37240
+ providerInputLimit: piPressureEvidenceLimit(resolvePiWindowGeometry({
37241
+ rawContextWindow: usageContextLimit,
37242
+ rawContextWindowSource: usageContextWindowSource,
37243
+ model: ctx.model,
37244
+ detectedContextLimit
37245
+ }))
35675
37246
  }) || undefined
35676
37247
  });
35677
- if (sessionMeta.lastContextPercentage > 0 && sessionMeta.lastInputTokens > 0) {
37248
+ if (args.pressureSnapshot) {
37249
+ usage = args.pressureSnapshot;
37250
+ usageSource = "request snapshot";
37251
+ } else if (sessionMeta.lastContextPercentage > 0 && sessionMeta.lastInputTokens > 0) {
35678
37252
  usage = {
35679
37253
  percentage: sessionMeta.lastContextPercentage,
35680
37254
  inputTokens: sessionMeta.lastInputTokens
@@ -35692,7 +37266,7 @@ function maybeFireHistorian(args) {
35692
37266
  };
35693
37267
  usageSource = "piUsage fallback";
35694
37268
  }
35695
- usage = resolvePiPressureSnapshotWithEstimateGuard({
37269
+ usage = args.pressureSnapshot ?? resolvePiPressureSnapshotWithEstimateGuard({
35696
37270
  sessionId,
35697
37271
  source: "historian trigger",
35698
37272
  liveIsRawBranchEstimate: args.liveIsRawBranchEstimate,
@@ -35712,7 +37286,6 @@ function maybeFireHistorian(args) {
35712
37286
  readMessages: () => readPiSessionMessages(ctx),
35713
37287
  readMessagePage: (afterOrdinal, limit, finalWatermark) => readPiSessionMessagePage(ctx, afterOrdinal, limit, finalWatermark)
35714
37288
  };
35715
- const unregister = setRawMessageProvider2(sessionId, provider);
35716
37289
  const modelKey = liveModelBySession.get(sessionId);
35717
37290
  const triggerInputs = resolvePiHistorianTriggerInputs({
35718
37291
  db,
@@ -35723,6 +37296,20 @@ function maybeFireHistorian(args) {
35723
37296
  });
35724
37297
  const historianForceMaterializationPercentage = escalationBands(triggerInputs.executeThresholdPercentage).forceMaterializationPercentage;
35725
37298
  const boundaryContextLimit = triggerInputs.contextLimit;
37299
+ if (isHistorianDrainBudgetSpent({
37300
+ db,
37301
+ sessionId,
37302
+ contextLimit: boundaryContextLimit,
37303
+ executeThresholdPercentage: triggerInputs.executeThresholdPercentage,
37304
+ usagePercentage: usage.percentage
37305
+ })) {
37306
+ if (sessionMeta.compartmentInProgress) {
37307
+ updateSessionMeta(db, sessionId, { compartmentInProgress: false });
37308
+ sessionMeta.compartmentInProgress = false;
37309
+ }
37310
+ return;
37311
+ }
37312
+ const unregister = setRawMessageProvider2(sessionId, provider);
35726
37313
  const resolvePiBoundarySnapshot = (emergencyTailScale) => resolveProtectedTailBoundary(resolveBoundaryContext({
35727
37314
  db,
35728
37315
  sessionId,
@@ -35753,16 +37340,19 @@ function maybeFireHistorian(args) {
35753
37340
  }
35754
37341
  const failureCount = historianStateSnapshot.historianFailureCount;
35755
37342
  if (failureCount > 0) {
35756
- boundarySnapshot = resolveRunnablePiBoundarySnapshot();
35757
- }
35758
- const shouldRecoverOnFirstPass = failureCount > 0 && boundarySnapshot !== undefined && hasEligiblePiCompartmentHistory(db, sessionId, boundarySnapshot);
35759
- if (shouldRecoverOnFirstPass) {
35760
37343
  triggered = true;
35761
- sessionLog(sessionId, `historian recovery triggered on session load after ${failureCount} failure(s)`);
35762
- sendPiIgnoredNotification(ctx, `## Historian recovery
37344
+ const prepareRecoveryBoundary = () => {
37345
+ const snapshot = resolveRunnablePiBoundarySnapshot();
37346
+ if (!hasEligiblePiCompartmentHistory(db, sessionId, snapshot))
37347
+ return;
37348
+ sessionLog(sessionId, `historian recovery triggered on session load after ${failureCount} failure(s)`);
37349
+ sendPiIgnoredNotification(ctx, `## Historian recovery
35763
37350
 
35764
37351
  Historian previously failed ${failureCount} time(s), so Magic Context is retrying history comparting immediately after restart.`);
37352
+ return snapshot;
37353
+ };
35765
37354
  spawnPiHistorianRun({
37355
+ startGuard: args.startGuard,
35766
37356
  pi: args.pi,
35767
37357
  ctx,
35768
37358
  sessionId,
@@ -35770,7 +37360,7 @@ Historian previously failed ${failureCount} time(s), so Magic Context is retryin
35770
37360
  historian,
35771
37361
  provider,
35772
37362
  unregister,
35773
- boundarySnapshot,
37363
+ boundarySnapshot: prepareRecoveryBoundary,
35774
37364
  refreshBoundarySnapshot: resolveRunnablePiBoundarySnapshot,
35775
37365
  currentContextLimit: boundaryContextLimit,
35776
37366
  fallbackModelId: modelKey
@@ -35778,7 +37368,7 @@ Historian previously failed ${failureCount} time(s), so Magic Context is retryin
35778
37368
  return;
35779
37369
  }
35780
37370
  }
35781
- const trigger = checkCompartmentTrigger(db, sessionId, sessionMeta, usage, 0, triggerInputs.executeThresholdPercentage, triggerInputs.triggerBudget, triggerInputs.clearReasoningAge, triggerInputs.commitClusterTrigger, args.activeTags, boundaryContextLimit, () => readPiHistorianTail(db, sessionId, provider), args.taggerFloor, { canClearReasoning: true }, {
37371
+ const trigger = checkCompartmentTrigger(db, sessionId, sessionMeta, usage, 0, triggerInputs.executeThresholdPercentage, triggerInputs.triggerBudget, triggerInputs.keepReasoningTokens, triggerInputs.commitClusterTrigger, args.activeTags, boundaryContextLimit, () => readPiHistorianTail(db, sessionId, provider), args.taggerFloor, { canClearReasoning: true }, {
35782
37372
  hardFold: false,
35783
37373
  force: usage.percentage >= historianForceMaterializationPercentage && (usage.percentage >= 95 || getEmergencyInputSample(db, sessionId) === 0),
35784
37374
  explicitFlush: hasPendingMaterialization(sessionId),
@@ -35805,6 +37395,7 @@ Historian previously failed ${failureCount} time(s), so Magic Context is retryin
35805
37395
  triggered = true;
35806
37396
  sessionLog(sessionId, `historian trigger fired (reason=${trigger.reason ?? "unknown"}) usage=${usage.percentage.toFixed(1)}% — spawning subagent`);
35807
37397
  spawnPiHistorianRun({
37398
+ startGuard: args.startGuard,
35808
37399
  pi: args.pi,
35809
37400
  ctx,
35810
37401
  sessionId,
@@ -35926,8 +37517,21 @@ async function runCompactionOffPipeline(args) {
35926
37517
  };
35927
37518
  }
35928
37519
  async function runPipeline(args) {
37520
+ args.assertCurrentPass?.();
35929
37521
  if (args.compactionOff)
35930
37522
  return runCompactionOffPipeline(args);
37523
+ const thinkingRecovery = prepareLatestThinkingRecovery({
37524
+ db: args.db,
37525
+ sessionId: args.sessionId,
37526
+ messages: args.messages,
37527
+ id: (message, index) => resolvePiStableId(message, index, args.entryIds, args.entryIdByRef ?? undefined),
37528
+ parts: (message) => Array.isArray(message?.content) ? message.content : []
37529
+ });
37530
+ if (thinkingRecovery.ended)
37531
+ signalPiPendingMaterialization(args.sessionId);
37532
+ let restoreOriginals;
37533
+ const activeThinkingTurn = hasActiveAnthropicThinkingTurn(args.messages, args.resolvedProviderID, args.resolvedModelID);
37534
+ const protectedSignedPrefix = activeThinkingTurn && args.reasoningClearing?.prefixBound === true;
35931
37535
  const stableIdResolver = (msg, index) => resolvePiStableId(msg, index, args.entryIds, args.entryIdByRef ?? undefined);
35932
37536
  let foldingSystemState = null;
35933
37537
  try {
@@ -36090,7 +37694,10 @@ async function runPipeline(args) {
36090
37694
  let preFoldInjectionResult = null;
36091
37695
  const persistedM0BeforeFold = injectionPassSnapshot?.sessionMeta ?? args.sessionMeta;
36092
37696
  const m0CoverageBeforeFold = persistedM0BeforeFold.cachedM0Bytes === null ? -1 : persistedM0BeforeFold.cachedM0MaxCompartmentSeq;
36093
- if ((foldDueDecision.value || softRefreshOpportunity) && piM0State) {
37697
+ if (protectedSignedPrefix && piM0State) {
37698
+ piM0State.preparedPrefix = prepareCachedM0M1PiReplay(piM0State, args.db, injectionPassSnapshot?.cachedRow);
37699
+ }
37700
+ if (!protectedSignedPrefix && (foldDueDecision.value || softRefreshOpportunity) && piM0State) {
36094
37701
  try {
36095
37702
  preFoldInjectionResult = injectM0M1PiForRun(piM0State, args.db, [], undefined, softRefreshOpportunity, injectionPassSnapshot);
36096
37703
  prefixPreflightContended = preFoldInjectionResult.contentionExhausted === true;
@@ -36171,17 +37778,31 @@ async function runPipeline(args) {
36171
37778
  let protectionFloorResolution = resolveProtectionFloor();
36172
37779
  const shouldRunHeuristics = args.heuristics !== undefined && isCacheBustingPass && (rideSignals.publishedHistory || args.forceMaterialization === true || hasPendingMaterializeSignal || deferredMaterializeEligible || foldBustsServedPrefixThisPass || firstRenderBust || rideSignals.subagentExecute || args.schedulerDecision === "execute" && !alreadyRanHeuristicsThisTurn);
36173
37780
  const tFallbackIdentity = performance.now();
37781
+ const preflightRevision = readPiAdoptionRevision(args.db);
36174
37782
  const hasFallbackMessageTags = hasPiFallbackMessageTags2(args.db, args.sessionId);
36175
37783
  const entryFingerprintByMessageId = buildEntryFingerprintMap(args.messages, stableIdResolver, args.reusableMessageIds, hasFallbackMessageTags);
37784
+ args.assertCurrentPass?.();
36176
37785
  adoptPiFallbackTags(args.db, args.sessionId, args.tagger, entryFingerprintByMessageId, {
36177
37786
  messages: args.messages,
36178
37787
  resolveStableId: stableIdResolver,
36179
- hasFallbackMessageTags
37788
+ hasFallbackMessageTags,
37789
+ preflightRevision,
37790
+ rebuildFingerprints: !hasFallbackMessageTags && args.reusableMessageIds?.size ? () => {
37791
+ args.assertCurrentPass?.();
37792
+ const full = buildEntryFingerprintMap(args.messages, stableIdResolver);
37793
+ for (const [id, fingerprint] of full)
37794
+ entryFingerprintByMessageId.set(id, fingerprint);
37795
+ return entryFingerprintByMessageId;
37796
+ } : undefined
36180
37797
  });
36181
37798
  logTransformTiming(args.sessionId, "fallbackIdentityAndAdoption", tFallbackIdentity);
36182
37799
  afterFallbackAdoptionForTests?.(args.stableIdSchemeCutover === true);
36183
37800
  const textIdentityPlan = buildPiTextIdentityPlan(args.db, args.sessionId, args.tagger, transcript, args.reusableMessageIds);
36184
37801
  const tTag = performance.now();
37802
+ const mutationView = transcript.messages.map((message) => ({
37803
+ ...message,
37804
+ parts: message.parts
37805
+ }));
36185
37806
  let tagTextTokenCache = piTagTextTokenCacheBySession.get(args.sessionId);
36186
37807
  if (!tagTextTokenCache) {
36187
37808
  tagTextTokenCache = new Map;
@@ -36192,23 +37813,55 @@ async function runPipeline(args) {
36192
37813
  tagToolTokenCache = new Map;
36193
37814
  piTagToolTokenCacheBySession.set(args.sessionId, tagToolTokenCache);
36194
37815
  }
36195
- const { targets } = runPersistedReplayStage("tagging-persistence-failure", () => args.tagger.cleanup(args.sessionId), () => tagTranscript(args.sessionId, transcript, args.tagger, args.db, {
36196
- skipPrefixInjection: !ctxReduceCallable,
36197
- entryFingerprintByMessageId,
36198
- reuseMessageIds: textIdentityPlan.reusableMessageIds,
36199
- textIdentityDriftMessageIds: textIdentityPlan.driftedMessageIds,
36200
- textIdentitySourceCache: textIdentityPlan.sourceCache,
36201
- textTokenCache: tagTextTokenCache,
36202
- toolTokenCache: tagToolTokenCache,
36203
- onTiming: hasPiTransformTimingObserver() ? (phase, elapsedMs) => {
36204
- recordPiTransformTiming({
36205
- sessionId: args.sessionId,
36206
- stage: `tag:${phase}`,
36207
- elapsedMs
36208
- });
36209
- } : undefined
36210
- }));
37816
+ const { targets } = runPersistedReplayStage("tagging-persistence-failure", () => args.tagger.cleanup(args.sessionId), () => {
37817
+ args.assertCurrentPass?.();
37818
+ return tagTranscript(args.sessionId, { ...transcript, messages: mutationView }, args.tagger, args.db, {
37819
+ skipPrefixInjection: !ctxReduceCallable,
37820
+ entryFingerprintByMessageId,
37821
+ reuseMessageIds: textIdentityPlan.reusableMessageIds,
37822
+ textIdentityDriftMessageIds: textIdentityPlan.driftedMessageIds,
37823
+ textIdentitySourceCache: textIdentityPlan.sourceCache,
37824
+ textTokenCache: tagTextTokenCache,
37825
+ toolTokenCache: tagToolTokenCache,
37826
+ onTiming: hasPiTransformTimingObserver() ? (phase, elapsedMs) => {
37827
+ recordPiTransformTiming({
37828
+ sessionId: args.sessionId,
37829
+ stage: `tag:${phase}`,
37830
+ elapsedMs
37831
+ });
37832
+ } : undefined
37833
+ });
37834
+ });
36211
37835
  logTransformTiming(args.sessionId, "tagMessages", tTag);
37836
+ const reasoningTagsForProtection = buildMessageIdToMaxTag(targets);
37837
+ const frozenThinkingForProtection = activeThinkingTurn ? frozenBindingEntryIds(args.db, args.sessionId) : new Set;
37838
+ const protectedThinkingProxies = new Set;
37839
+ if (activeThinkingTurn) {
37840
+ for (let i = latestAssistantTurnStart(workingMessages);i < mutationView.length; i++) {
37841
+ const id = stableIdResolver(workingMessages[i], i);
37842
+ if (!thinkingRecovery.restore && id && frozenThinkingForProtection.has(id))
37843
+ continue;
37844
+ for (let p = 0;p < mutationView[i].parts.length; p++) {
37845
+ const part = mutationView[i].parts[p];
37846
+ if (part.kind !== "thinking")
37847
+ continue;
37848
+ const local = workingMessages[i].content?.[p];
37849
+ if (!thinkingRecovery.restore && !local?.redacted && id && (reasoningTagsForProtection.get(id) ?? Infinity) <= (args.sessionMeta.clearedReasoningThroughTag ?? 0))
37850
+ continue;
37851
+ protectedThinkingProxies.add(part);
37852
+ }
37853
+ }
37854
+ }
37855
+ const newTargets = protectNewTagMutations(mutationView.map((message) => ({
37856
+ info: message.info,
37857
+ parts: message.parts
37858
+ })), targets, protectedThinkingProxies, args.reasoningClearing?.prefixBound === true);
37859
+ if (thinkingRecovery.restore) {
37860
+ transcript.commit();
37861
+ restoreOriginals = captureOriginalTurn(args.messages, (message) => message.content, (message, content) => {
37862
+ message.content = content;
37863
+ });
37864
+ }
36212
37865
  if (foldBustsServedPrefixThisPass) {
36213
37866
  try {
36214
37867
  let converted = 0;
@@ -36264,7 +37917,7 @@ async function runPipeline(args) {
36264
37917
  pendingDecisionLogObserverForTests?.(pendingDecisionLog);
36265
37918
  try {
36266
37919
  const tApplyPending = performance.now();
36267
- pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, targets, new Set([
37920
+ pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, newTargets, new Set([
36268
37921
  ...args.contextUsage.percentage >= 95 ? [] : protectedTagNumbersForPass,
36269
37922
  ...protectedToolTags
36270
37923
  ]), pendingOperationTags, pendingOps, [], new Set, (reduction) => droppedTokenReductions.push(reduction));
@@ -36277,7 +37930,8 @@ async function runPipeline(args) {
36277
37930
  pendingOpsAppliedThisPass = true;
36278
37931
  if (hasPendingMaterializeSignal) {
36279
37932
  if (args.heuristics === undefined) {
36280
- consumePendingMaterialization(args.sessionId);
37933
+ if (!pendingOps.some((op) => newTargets.get(op.tagId)?.thinkingDropProtected))
37934
+ consumePendingMaterialization(args.sessionId);
36281
37935
  }
36282
37936
  }
36283
37937
  } catch (err) {
@@ -36317,6 +37971,7 @@ async function runPipeline(args) {
36317
37971
  try {
36318
37972
  const tReplayReasoning = performance.now();
36319
37973
  const clearedReplay = replayClearedReasoningPi({
37974
+ protectLatestTurn: false,
36320
37975
  db: args.db,
36321
37976
  sessionId: args.sessionId,
36322
37977
  messages: workingMessages,
@@ -36324,6 +37979,7 @@ async function runPipeline(args) {
36324
37979
  piMessageStableId: stableIdResolver
36325
37980
  });
36326
37981
  const inlineReplay = replayStrippedInlineThinkingPi({
37982
+ protectLatestTurn: false,
36327
37983
  db: args.db,
36328
37984
  sessionId: args.sessionId,
36329
37985
  messages: workingMessages,
@@ -36365,7 +38021,7 @@ async function runPipeline(args) {
36365
38021
  clearEmergencyDropSample(args.db, args.sessionId);
36366
38022
  }
36367
38023
  routineCleanupApplied = (emergencyDropEligible ? args.contextUsage.percentage >= 95 || getEmergencyInputSample(args.db, args.sessionId) === 0 : args.sessionMeta.isSubagent || !routinePressureAlreadyApplied) || hasPendingMaterializeSignal || deferredMaterialize || independentMutationBeforeHeuristics;
36368
- heuristicsResult = applyPiHeuristicCleanup(args.sessionId, args.db, targets, args.messages, {
38024
+ heuristicsResult = applyPiHeuristicCleanup(args.sessionId, args.db, newTargets, args.messages, {
36369
38025
  protectedTags: args.protectedTags,
36370
38026
  prepareToolRemovalMeasurements: transcript.prepareToolRemovalMeasurements,
36371
38027
  protectedToolTags,
@@ -36382,7 +38038,7 @@ async function runPipeline(args) {
36382
38038
  caveman: args.isSubagent ? undefined : args.heuristics.caveman
36383
38039
  }, activeTags, stableIdResolver);
36384
38040
  if (!routineCleanupApplied && heuristicsResult.emergencyDroppedTools > 0) {
36385
- const ridingCleanup = applyPiHeuristicCleanup(args.sessionId, args.db, targets, args.messages, {
38041
+ const ridingCleanup = applyPiHeuristicCleanup(args.sessionId, args.db, newTargets, args.messages, {
36386
38042
  protectedTags: args.protectedTags,
36387
38043
  protectedToolTags,
36388
38044
  protectedCutoff: usesTokenProtection ? protectionWindowForPass.cutoff : undefined,
@@ -36421,7 +38077,8 @@ async function runPipeline(args) {
36421
38077
  heuristicsExecuted = true;
36422
38078
  executedWorkThisPass = true;
36423
38079
  if (hasPendingMaterializeSignal) {
36424
- consumePendingMaterialization(args.sessionId);
38080
+ if (!pendingOps.some((op) => newTargets.get(op.tagId)?.thinkingDropProtected))
38081
+ consumePendingMaterialization(args.sessionId);
36425
38082
  }
36426
38083
  if (currentTurnId !== null) {
36427
38084
  lastHeuristicsTurnIdBySession.set(args.sessionId, currentTurnId);
@@ -36438,6 +38095,7 @@ async function runPipeline(args) {
36438
38095
  }
36439
38096
  }
36440
38097
  let reasoningPersistenceFailed = false;
38098
+ let reasoningBudgetCutoffThisPass = 0;
36441
38099
  if (args.reasoningClearing && shouldRunHeuristics && routineCleanupApplied) {
36442
38100
  const rollbackReasoning = captureReasoningMutationRollback(workingMessages);
36443
38101
  try {
@@ -36445,25 +38103,30 @@ async function runPipeline(args) {
36445
38103
  const prevWatermark = args.sessionMeta.clearedReasoningThroughTag ?? 0;
36446
38104
  const prefixBound = args.reasoningClearing.prefixBound === true;
36447
38105
  const bindingStripped = prefixBound ? frozenBindingEntryIds(args.db, args.sessionId) : new Set;
38106
+ const nativeGone = getNativeReplayState(args.db, args.sessionId).reasoningIds;
36448
38107
  const maxCutoff = piReasoningClearCutoff({
38108
+ protectLatestTurn: activeThinkingTurn,
36449
38109
  messages: workingMessages,
36450
38110
  messageIdToMaxTag,
36451
- clearReasoningAge: args.reasoningClearing.clearReasoningAge,
38111
+ keepReasoningTokens: args.reasoningClearing.keepReasoningTokens ?? DEFAULT_KEEP_REASONING_TOKENS,
38112
+ proseRatio: sessionDecisionCalibration(args.db, args.sessionId).proseRatio,
36452
38113
  piMessageStableId: stableIdResolver,
36453
38114
  prefixBound,
36454
- alreadyGone: (id) => bindingStripped.has(id)
38115
+ anthropic: args.canUseEmptySentinels || prefixBound,
38116
+ alreadyGone: (id) => bindingStripped.has(id) || nativeGone.has(id)
36455
38117
  });
38118
+ reasoningBudgetCutoffThisPass = maxCutoff;
36456
38119
  const clearOutcome = clearOldReasoningPi({
38120
+ protectLatestTurn: activeThinkingTurn,
36457
38121
  messages: workingMessages,
36458
38122
  messageIdToMaxTag,
36459
- clearReasoningAge: args.reasoningClearing.clearReasoningAge,
36460
38123
  piMessageStableId: stableIdResolver,
36461
38124
  maxCutoff
36462
38125
  });
36463
38126
  const stripOutcome = stripInlineThinkingPi({
38127
+ protectLatestTurn: activeThinkingTurn,
36464
38128
  messages: workingMessages,
36465
38129
  messageIdToMaxTag,
36466
- clearReasoningAge: args.reasoningClearing.clearReasoningAge,
36467
38130
  piMessageStableId: stableIdResolver,
36468
38131
  maxCutoff: prefixBound ? 0 : maxCutoff
36469
38132
  });
@@ -36498,6 +38161,7 @@ async function runPipeline(args) {
36498
38161
  if (args.reasoningClearing && shouldRunHeuristics && routineCleanupApplied) {
36499
38162
  try {
36500
38163
  replayStrippedInlineThinkingPi({
38164
+ protectLatestTurn: false,
36501
38165
  db: args.db,
36502
38166
  sessionId: args.sessionId,
36503
38167
  messages: workingMessages,
@@ -36516,7 +38180,7 @@ async function runPipeline(args) {
36516
38180
  const syntheticPendingOps = buildSyntheticToolReclaimOps({
36517
38181
  db: args.db,
36518
38182
  sessionId: args.sessionId,
36519
- targets,
38183
+ targets: newTargets,
36520
38184
  watermark: reclaimMeta.toolReclaimWatermark ?? 0,
36521
38185
  protectedToolTags,
36522
38186
  pendingOps
@@ -36528,7 +38192,7 @@ async function runPipeline(args) {
36528
38192
  const supersessionOps = buildSupersessionReclaimOps({
36529
38193
  db: args.db,
36530
38194
  sessionId: args.sessionId,
36531
- targets,
38195
+ targets: newTargets,
36532
38196
  pendingOps,
36533
38197
  recentMessageIds,
36534
38198
  protectedToolTags
@@ -36542,7 +38206,7 @@ async function runPipeline(args) {
36542
38206
  const editReclaim = buildEditSupersessionReclaim({
36543
38207
  db: args.db,
36544
38208
  sessionId: args.sessionId,
36545
- targets,
38209
+ targets: newTargets,
36546
38210
  pendingOps,
36547
38211
  recentMessageIds,
36548
38212
  protectedToolTags
@@ -36557,7 +38221,7 @@ async function runPipeline(args) {
36557
38221
  }
36558
38222
  autoReclaimTargetCount = syntheticPendingOps.length;
36559
38223
  if (syntheticPendingOps.length > 0) {
36560
- autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, targets, protectedTagNumbersForPass, undefined, [], syntheticPendingOps, editMarkerTagIds, (reduction) => droppedTokenReductions.push(reduction));
38224
+ autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, newTargets, protectedTagNumbersForPass, undefined, [], syntheticPendingOps, editMarkerTagIds, (reduction) => droppedTokenReductions.push(reduction));
36561
38225
  if (autoReclaimDidMutate) {
36562
38226
  droppedCount += syntheticPendingOps.length;
36563
38227
  autoReclaimDidMutateThisPass = true;
@@ -36620,7 +38284,7 @@ async function runPipeline(args) {
36620
38284
  messageIdToMaxTag,
36621
38285
  stableId: stableIdResolver,
36622
38286
  localWatermark: args.sessionMeta.clearedReasoningThroughTag ?? 0,
36623
- clearReasoningAge: args.reasoningClearing.clearReasoningAge,
38287
+ budgetCutoff: reasoningBudgetCutoffThisPass,
36624
38288
  omissionAllowed: args.reasoningClearing.nativeReasoningMayClear,
36625
38289
  canApply: isCacheBustingPass && !reasoningPersistenceFailed,
36626
38290
  detectAged: shouldRunHeuristics && routineCleanupApplied
@@ -36713,6 +38377,7 @@ async function runPipeline(args) {
36713
38377
  let preserveDeferredMaterializationForMarkerDrain = false;
36714
38378
  if (deferredHistoryDrainEligible) {
36715
38379
  try {
38380
+ args.assertCurrentPass?.();
36716
38381
  const pending = getPendingPiCompactionMarkerState(args.db, args.sessionId);
36717
38382
  if (!pending) {
36718
38383
  if (injectionResult?.contentionExhausted === true) {
@@ -36733,6 +38398,7 @@ async function runPipeline(args) {
36733
38398
  preserveDeferredMaterializationForMarkerDrain = true;
36734
38399
  sessionLog(args.sessionId, "Pi compaction-marker drain skipped: sessionManager appendCompaction/getBranch or folding system state unavailable; preserving deferred signals");
36735
38400
  } else {
38401
+ args.assertCurrentPass?.();
36736
38402
  const outcome = applyDeferredPiCompactionMarker({
36737
38403
  db: args.db,
36738
38404
  appendCompaction: args.appendCompaction,
@@ -36775,6 +38441,7 @@ async function runPipeline(args) {
36775
38441
  }
36776
38442
  }
36777
38443
  } catch (err) {
38444
+ args.assertCurrentPass?.();
36778
38445
  sessionLog(args.sessionId, `Pi compaction-marker drain failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
36779
38446
  }
36780
38447
  }
@@ -36812,6 +38479,7 @@ async function runPipeline(args) {
36812
38479
  } catch (err) {
36813
38480
  sessionLog(args.sessionId, `token accounting failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
36814
38481
  }
38482
+ restoreOriginals?.(workingMessages);
36815
38483
  const materialized = injectionResult?.m0Materialized === true;
36816
38484
  if (args.reasoningClearing?.prefixBound && firstEditSourceOrder && firstEditParts.size > 0 && !prefixEditBesidesReasoningTrim) {
36817
38485
  const frozenThinking = frozenBindingEntryIds(args.db, args.sessionId);
@@ -36860,8 +38528,21 @@ async function runPipeline(args) {
36860
38528
  if (bustedThisPass || isCacheBustingPass) {
36861
38529
  droppedTokens = estimateDroppedTokensFromTagReductions(args.db, args.sessionId, droppedTokenReductions);
36862
38530
  }
38531
+ const visibleCallIds = new Set;
38532
+ for (const message of outputMessages) {
38533
+ if (message.role === "toolResult")
38534
+ visibleCallIds.add(message.toolCallId);
38535
+ if (message.role === "assistant" && Array.isArray(message.content)) {
38536
+ for (const part of message.content) {
38537
+ if (part.type === "toolCall")
38538
+ visibleCallIds.add(part.id);
38539
+ }
38540
+ }
38541
+ }
38542
+ const servedTagNumbers = new Set(allTagsForPass.filter((tag) => targets.has(tag.tagNumber) && (tag.type === "message" ? ctxReduceCallable || tag.status !== "active" : tag.type === "tool" && visibleCallIds.has(tag.messageId) && (ctxReduceCallable || tag.status !== "active"))).map((tag) => tag.tagNumber));
36863
38543
  return {
36864
38544
  messages: outputMessages,
38545
+ servedTagNumbers,
36865
38546
  heuristicsExecuted,
36866
38547
  executedWorkThisPass,
36867
38548
  historyInjected: injectionResult?.injected ?? false,
@@ -37120,6 +38801,8 @@ function clearContextHandlerSession(sessionId) {
37120
38801
  deferredHistoryRefreshSessions.delete(sessionId);
37121
38802
  deferredMaterializationSessions.delete(sessionId);
37122
38803
  firstContextPassSeenBySession.delete(sessionId);
38804
+ contextPassGeneration.delete(sessionId);
38805
+ clearPiContextReceipt(sessionId);
37123
38806
  commitSeenLastPass.delete(sessionId);
37124
38807
  liveModelBySession.delete(sessionId);
37125
38808
  latestAssistantModelTimestampBySession.delete(sessionId);
@@ -37142,6 +38825,7 @@ function clearContextHandlerSession(sessionId) {
37142
38825
  routinePressureAppliedBySession.delete(sessionId);
37143
38826
  lastSeenProjectIdentityBySession.delete(sessionId);
37144
38827
  persistedProjectIdentityBySession.delete(sessionId);
38828
+ sessionProjectBindingFailureLogAtBySession.delete(sessionId);
37145
38829
  for (const [projectIdentity, sessions] of sessionsByProject) {
37146
38830
  sessions.delete(sessionId);
37147
38831
  if (sessions.size === 0)
@@ -38318,6 +40002,19 @@ function updateStatusLine(ctx, deps, force = false) {
38318
40002
  function renderStatusText(ctx, db, sessionId) {
38319
40003
  const usage = ctx.getContextUsage?.();
38320
40004
  const meta = readSessionMetaStatus(db, sessionId);
40005
+ const providerInputLimit = piPressureEvidenceLimit(resolvePiWindowGeometry({
40006
+ rawContextWindow: usage?.contextWindow ?? ctx.model?.contextWindow,
40007
+ rawContextWindowSource: "catalog",
40008
+ model: ctx.model,
40009
+ detectedContextLimit: meta?.detected_context_limit ?? undefined
40010
+ }));
40011
+ const provenInputTokens = resolvePiProvenInputFloor({
40012
+ db,
40013
+ sessionId,
40014
+ modelKey: piProvenFloorModelKey(ctx.model),
40015
+ readBranch: () => ctx.sessionManager.getBranch(),
40016
+ providerInputLimit
40017
+ });
38321
40018
  const liveInputTokens = typeof usage?.tokens === "number" && Number.isFinite(usage.tokens) ? usage.tokens : undefined;
38322
40019
  const persistedInputTokens = typeof meta?.last_input_tokens === "number" && Number.isFinite(meta.last_input_tokens) && meta.last_input_tokens >= 0 ? meta.last_input_tokens : undefined;
38323
40020
  const windowGeometry = resolvePiWindowGeometry({
@@ -38325,10 +40022,12 @@ function renderStatusText(ctx, db, sessionId) {
38325
40022
  rawContextWindowSource: "catalog",
38326
40023
  model: ctx.model,
38327
40024
  detectedContextLimit: typeof meta?.detected_context_limit === "number" && meta.detected_context_limit > 0 ? meta.detected_context_limit : undefined,
40025
+ provenInputTokens: provenInputTokens || undefined,
38328
40026
  persistedInputTokens,
38329
- persistedPercentage: typeof meta?.last_context_percentage === "number" ? meta.last_context_percentage : undefined
40027
+ persistedPercentage: meta?.last_context_percentage ?? undefined
38330
40028
  });
38331
40029
  const pressure = liveInputTokens !== undefined || persistedInputTokens !== undefined ? resolvePiStatusPressureSnapshot({
40030
+ providerInputLimit,
38332
40031
  sessionId,
38333
40032
  persistedPercentage: meta?.last_context_percentage ?? 0,
38334
40033
  persistedInputTokens: persistedInputTokens ?? 0,
@@ -39564,7 +41263,17 @@ function statusSections(source, now) {
39564
41263
  value: String(source.protectedTagCount),
39565
41264
  tone: "muted"
39566
41265
  },
39567
- { label: "Subagent", value: source.isSubagent ? "yes" : "no", tone: "muted" }
41266
+ { label: "Subagent", value: source.isSubagent ? "yes" : "no", tone: "muted" },
41267
+ {
41268
+ label: "Denominator",
41269
+ value: `${Math.round(source.contextLimit)} tokens`,
41270
+ tone: "muted"
41271
+ },
41272
+ {
41273
+ label: "Window source",
41274
+ value: formatWindowSource(source.windowGeometry),
41275
+ tone: "muted"
41276
+ }
39568
41277
  ]
39569
41278
  },
39570
41279
  { title: "Cache TTL", labelWidth: 14, rows: cacheRows(source, now) },
@@ -39736,7 +41445,7 @@ function buildStatusViewFor(check, options) {
39736
41445
  // package.json
39737
41446
  var package_default = {
39738
41447
  name: "@cortexkit/pi-magic-context",
39739
- version: "0.46.1",
41448
+ version: "0.47.0",
39740
41449
  type: "module",
39741
41450
  description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
39742
41451
  main: "dist/index.js",
@@ -39765,7 +41474,7 @@ var package_default = {
39765
41474
  ],
39766
41475
  "//": "The build intentionally bundles typebox (no --external typebox) and keeps it a regular dependency. OMP's extension loader rewrites every bare `typebox` import to its own omptype-based shim, whose schemas are callable objects rather than JSON Schema; they fail structuredClone during tool registration, so an external typebox import stops Magic Context loading on OMP. src/typebox-bundling.test.ts guards this.",
39767
41476
  scripts: {
39768
- build: "bun ../../scripts/check-bun.mjs && tsc -p ../retina-local-fs/tsconfig.build.json && bun ../../scripts/clean-dist-chunks.mjs dist index.js subagent-entry.js historian-calibration-extension.js transformers-web.js transformers-node-wasm.js && bun build ../plugin/src/features/magic-context/memory/transformers-web-entry.ts --outfile dist/transformers-web.js --target browser --format esm --external onnxruntime-web && bun ../plugin/scripts/build-transformers-node-wasm.ts dist && bun build src/index.ts src/subagent-entry.ts src/historian-calibration-extension.ts ../plugin/src/features/magic-context/memory/embedding-worker.ts ../plugin/src/features/magic-context/migration-worker.ts --outdir dist --entry-naming '[name].[ext]' --target node --format esm --splitting --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui --external onnxruntime-node --external onnxruntime-web --external sharp --external node:sqlite",
41477
+ build: "bun ../../scripts/check-bun.mjs && tsc -p ../retina-local-fs/tsconfig.build.json && bun ../../scripts/clean-dist-chunks.mjs dist index.js subagent-entry.js historian-calibration-extension.js transformers-web.js transformers-node-wasm.js && bun build ../plugin/src/features/magic-context/memory/transformers-web-entry.ts --outfile dist/transformers-web.js --target browser --format esm --external onnxruntime-web && bun ../plugin/scripts/build-transformers-node-wasm.ts dist && bun build src/index.ts src/subagent-entry.ts src/historian-calibration-extension.ts ../plugin/src/features/magic-context/memory/embedding-worker.ts ../plugin/src/features/magic-context/migration-worker.ts ../plugin/src/hooks/magic-context/auto-search-worker.ts --outdir dist --entry-naming '[name].[ext]' --target node --format esm --splitting --external @earendil-works/pi-coding-agent --external @earendil-works/pi-tui --external onnxruntime-node --external onnxruntime-web --external sharp --external node:sqlite",
39769
41478
  "build:e2e-argv": `bun ../../scripts/clean-dist-chunks.mjs ../../tests/docker/.generated subagent-runner-e2e.mjs && bun build src/subagent-runner.ts --outdir ../../tests/docker/.generated --entry-naming 'subagent-runner-e2e.mjs' --splitting --target node --format esm --external node:sqlite && node --input-type=module -e 'const { __test } = await import("../../tests/docker/.generated/subagent-runner-e2e.mjs"); if (typeof __test.buildArgs !== "function" || typeof __test.resolvePiInvocation !== "function") throw new Error("argv renderer exports missing"); console.log("Node argv renderer: 2 exports loaded")'`,
39770
41479
  typecheck: "tsc -p ../retina-local-fs/tsconfig.build.json && tsc --noEmit",
39771
41480
  test: "bun ../../scripts/check-bun.mjs && bun install --frozen-lockfile && BUN_JSC_useOMGJIT=0 bun test --parallel=4 --timeout 30000",
@@ -39978,7 +41687,7 @@ function piStatusWarnings(s) {
39978
41687
  return warnings;
39979
41688
  }
39980
41689
  function formatPiStatusSummary(s) {
39981
- const summary = renderUserStatusSummary({
41690
+ const summary = `${renderUserStatusSummary({
39982
41691
  inputTokens: s.inputTokens,
39983
41692
  usableContextTokens: s.contextLimit,
39984
41693
  usagePercentage: s.usagePercentage,
@@ -40001,7 +41710,9 @@ function formatPiStatusSummary(s) {
40001
41710
  embedding: s.embedding,
40002
41711
  warnings: piStatusWarnings(s),
40003
41712
  dreamerSkipped: s.dreamer.skipped
40004
- }, "plain");
41713
+ }, "plain")}
41714
+ Window source: ${formatWindowSource(s.windowGeometry)}; denominator: ${Math.round(s.contextLimit)} tokens${s.reasoningBudgetLine ? `
41715
+ ${s.reasoningBudgetLine}` : ""}`;
40005
41716
  return s.configGeneration === undefined ? summary : `${summary}
40006
41717
  Config generation: ${s.configGeneration} (adopted ${s.configAdoptedAt ? new Date(s.configAdoptedAt).toLocaleString() : "unknown"})${s.configReloadFailure ? `
40007
41718
  Config reload failed ${s.configReloadFailure.path}: ${s.configReloadFailure.message}` : ""}`;
@@ -40074,6 +41785,8 @@ function renderPiStatusOverlay(s, theme, innerWidth) {
40074
41785
  lines.push(renderSplitRow(theme.fg(PI_TONE_COLORS[view.headline.left.tone], theme.bold(view.headline.left.text)), theme.fg(PI_TONE_COLORS[view.headline.right.tone], view.headline.right.text), innerWidth));
40075
41786
  if (view.windowLine)
40076
41787
  lines.push(theme.fg("muted", view.windowLine));
41788
+ if (s.reasoningBudgetLine)
41789
+ lines.push(theme.fg("muted", s.reasoningBudgetLine));
40077
41790
  const bar = renderBar(view.bar, innerWidth);
40078
41791
  if (bar)
40079
41792
  lines.push(bar);
@@ -40083,6 +41796,13 @@ function renderPiStatusOverlay(s, theme, innerWidth) {
40083
41796
  if (view.hygiene) {
40084
41797
  lines.push(renderStatusRow(view.hygiene, 9, innerWidth, theme));
40085
41798
  }
41799
+ if (status.state === "ready") {
41800
+ lines.push(renderStatusRow({
41801
+ label: "Historian runs",
41802
+ value: s.historianRuns.toLocaleString(),
41803
+ tone: "muted"
41804
+ }, 9, innerWidth, theme));
41805
+ }
40086
41806
  const upgrade = status.state !== "ready" ? null : s.recompInFlight ? { label: "Recomp", value: "running…", tone: "warning" } : s.upgradeNeededCount > 0 ? {
40087
41807
  label: "Recomp",
40088
41808
  value: `${s.upgradeNeededCount} compartment${s.upgradeNeededCount === 1 ? "" : "s"} in the old layout · run /ctx-recomp`,
@@ -40237,9 +41957,13 @@ function refreshLiveStatus(detail, state, ctx, usage, now) {
40237
41957
  rawContextWindow: usage?.contextWindow ?? ctx.model?.contextWindow,
40238
41958
  rawContextWindowSource: "catalog",
40239
41959
  model: ctx.model,
40240
- ...live
41960
+ detectedContextLimit: live.detectedContextLimit,
41961
+ provenInputTokens: live.provenInputTokens,
41962
+ persistedInputTokens: live.persistedInputTokens,
41963
+ persistedPercentage: live.persistedPercentage
40241
41964
  });
40242
41965
  const pressure = resolvePiStatusPressureSnapshot({
41966
+ providerInputLimit: live.providerInputLimit,
40243
41967
  sessionId: detail.sessionId,
40244
41968
  persistedPercentage: live.persistedPercentage,
40245
41969
  persistedInputTokens: live.persistedInputTokens,
@@ -40290,20 +42014,37 @@ function buildStatusDetail(pi, ctx, deps, sessionId, state, host = readStatusHos
40290
42014
  if (detected > 0)
40291
42015
  detectedContextLimit = detected;
40292
42016
  } catch {}
42017
+ const providerInputLimit = piPressureEvidenceLimit(resolvePiWindowGeometry({
42018
+ rawContextWindow: usage?.contextWindow ?? ctx.model?.contextWindow,
42019
+ rawContextWindowSource: "catalog",
42020
+ model: ctx.model,
42021
+ detectedContextLimit
42022
+ }));
42023
+ const provenInputTokens = resolvePiProvenInputFloor({
42024
+ db: deps.db,
42025
+ sessionId,
42026
+ modelKey: piProvenFloorModelKey(ctx.model),
42027
+ readBranch: () => ctx.sessionManager.getBranch(),
42028
+ providerInputLimit
42029
+ });
40293
42030
  state.live = {
40294
42031
  persistedInputTokens: meta.lastInputTokens,
40295
42032
  persistedPercentage: meta.lastContextPercentage,
40296
- detectedContextLimit
42033
+ detectedContextLimit,
42034
+ provenInputTokens: provenInputTokens || undefined,
42035
+ providerInputLimit
40297
42036
  };
40298
42037
  const windowGeometry = resolvePiWindowGeometry({
40299
42038
  rawContextWindow: usage?.contextWindow ?? ctx.model?.contextWindow,
40300
42039
  rawContextWindowSource: "catalog",
40301
42040
  model: ctx.model,
40302
42041
  detectedContextLimit,
42042
+ provenInputTokens: provenInputTokens || undefined,
40303
42043
  persistedInputTokens: meta.lastInputTokens,
40304
42044
  persistedPercentage: meta.lastContextPercentage
40305
42045
  });
40306
42046
  const pressure = resolvePiStatusPressureSnapshot({
42047
+ providerInputLimit,
40307
42048
  sessionId,
40308
42049
  persistedPercentage: meta.lastContextPercentage,
40309
42050
  persistedInputTokens: meta.lastInputTokens,
@@ -40383,6 +42124,7 @@ function buildStatusDetail(pi, ctx, deps, sessionId, state, host = readStatusHos
40383
42124
  const compressionBudget = contextLimit > 0 ? Math.floor(contextLimit * (Math.min(threshold.percentage, 80) / 100) * historyBudgetPercentage) : null;
40384
42125
  return {
40385
42126
  sessionId,
42127
+ reasoningBudgetLine: reasoningBudgetStatusLine(sessionId),
40386
42128
  activeProfile: deps.activeProfile ?? null,
40387
42129
  configGeneration: deps.configGeneration,
40388
42130
  configAdoptedAt: deps.configAdoptedAt,
@@ -40411,7 +42153,7 @@ function buildStatusDetail(pi, ctx, deps, sessionId, state, host = readStatusHos
40411
42153
  pendingOpsCount: pendingOps,
40412
42154
  compactionMarker: getCompactionMarkerHealth(deps.db, sessionId),
40413
42155
  historianRunning: meta.compartmentInProgress,
40414
- timesExecuteThresholdReached: meta.timesExecuteThresholdReached,
42156
+ historianRuns: countHistorianRuns(deps.db, sessionId),
40415
42157
  historianFailureCount: Number(metaRow?.historian_failure_count ?? 0),
40416
42158
  historianLastFailureAt: typeof metaRow?.historian_last_failure_at === "number" ? metaRow.historian_last_failure_at : null,
40417
42159
  historianLastError: metaRow?.historian_last_error ?? null,
@@ -40578,14 +42320,13 @@ function buildStatusDetails(deps, status) {
40578
42320
  droppedTags: status.droppedTags,
40579
42321
  totalBytes: status.activeBytes,
40580
42322
  pendingOps: status.pendingOpsCount,
40581
- lastExecuteThreshold: status.timesExecuteThresholdReached,
40582
42323
  compartmentCount: status.compartmentCount,
40583
42324
  lastCompartmentRange: status.lastCompartmentRange,
40584
42325
  memoryCount: status.memoryCount,
40585
42326
  noteCount: status.sessionNoteCount + status.readySmartNoteCount,
40586
42327
  dreamer: status.dreamer,
40587
42328
  historian: {
40588
- lastFireCount: status.timesExecuteThresholdReached,
42329
+ runs: status.historianRuns,
40589
42330
  inProgress: status.historianRunning,
40590
42331
  lastFailureAt: status.historianLastFailureAt,
40591
42332
  lastError: status.historianLastError,
@@ -41291,6 +43032,19 @@ function registerPiSubagentHostTools(pi, harness) {
41291
43032
  });
41292
43033
  }
41293
43034
 
43035
+ // src/timeout.ts
43036
+ function withTimeout(p, ms) {
43037
+ let handle;
43038
+ const timeout = new Promise((resolve) => {
43039
+ handle = setTimeout(() => resolve(undefined), ms);
43040
+ handle.unref?.();
43041
+ });
43042
+ return Promise.race([p, timeout]).finally(() => {
43043
+ if (handle)
43044
+ clearTimeout(handle);
43045
+ });
43046
+ }
43047
+
41294
43048
  // src/index.ts
41295
43049
  var PI_HARNESS_DETECTION = await resolvePiHarnessDetection();
41296
43050
  var PI_HARNESS_KIND = PI_HARNESS_DETECTION.kind;
@@ -41517,21 +43271,6 @@ function getPiMessageModel(message) {
41517
43271
  model: typeof msg.model === "string" ? msg.model : undefined
41518
43272
  };
41519
43273
  }
41520
- var piUsageBoundLogSeen = new Set;
41521
- function logPiUsageBoundOnce(sessionId, reading, absoluteWall, reason) {
41522
- const key = `${sessionId}|${reason}`;
41523
- if (piUsageBoundLogSeen.has(key))
41524
- return;
41525
- piUsageBoundLogSeen.add(key);
41526
- info(`message_end: session=${sessionId} bounded ${reason} ${reading} at trusted absolute wall ${absoluteWall}; pressure_proof_not_capacity`);
41527
- }
41528
- function logPiUsageAboveWindowOnce(sessionId, reading, absoluteWall) {
41529
- const key = `${sessionId}|accepted above window`;
41530
- if (piUsageBoundLogSeen.has(key))
41531
- return;
41532
- piUsageBoundLogSeen.add(key);
41533
- info(`message_end: session=${sessionId} provider usage ${reading} exceeds configured window ${absoluteWall}; counted as real pressure against the configured limit`);
41534
- }
41535
43274
  function resolvePiPressureContextLimit(args) {
41536
43275
  let detectedContextLimit;
41537
43276
  try {
@@ -41561,25 +43300,29 @@ async function persistPiPressureFromMessageEnd(args) {
41561
43300
  const activeModel = args.piModel ?? { provider, id: model };
41562
43301
  const modelKey = activeModel.provider && activeModel.id ? piModelRefToCanonical(`${activeModel.provider}/${activeModel.id}`) : undefined;
41563
43302
  const usage = extractAssistantUsage(args.message);
43303
+ const detectedContextLimit = getOverflowState(args.db, args.sessionId, modelKey).detectedContextLimit;
41564
43304
  const reportedGeometry = resolvePiWindowGeometry({
41565
43305
  rawContextWindow: args.piContextWindow,
41566
43306
  rawContextWindowSource: args.piContextWindowSource,
41567
- model: activeModel
43307
+ model: activeModel,
43308
+ detectedContextLimit
41568
43309
  });
41569
- const trustedAbsoluteWall = reportedGeometry && hasTrustedAbsoluteWall(reportedGeometry) ? reportedGeometry.derivation.absoluteWall : undefined;
41570
- const unboundedPressure = computePiPressure(usage, args.piContextWindow);
43310
+ const providerInputLimit = piPressureEvidenceLimit(reportedGeometry);
43311
+ const reportedInput = piUsageInputTokens(usage);
43312
+ const rejectedUsage = reportedInput !== null && reportedInput > providerInputLimit;
43313
+ if (reportedInput !== null)
43314
+ recordPiProviderUsageValidity(args.sessionId, rejectedUsage);
43315
+ const unboundedPressure = computePiPressure(usage, args.piContextWindow, providerInputLimit);
43316
+ if (rejectedUsage) {
43317
+ noteRejectedPiUsage(args.sessionId, reportedInput, providerInputLimit, "message_end");
43318
+ }
41571
43319
  const msg = args.message && typeof args.message === "object" ? args.message : undefined;
41572
43320
  const messageHadOverflowError = typeof msg?.errorMessage === "string" && detectOverflow(msg.errorMessage).isOverflow;
41573
- const readingAboveTrustedWall = unboundedPressure !== null && trustedAbsoluteWall !== undefined && unboundedPressure.inputTokens > trustedAbsoluteWall;
41574
- const requestAccepted = !messageHadOverflowError;
41575
- if (readingAboveTrustedWall && unboundedPressure && trustedAbsoluteWall) {
41576
- if (requestAccepted) {
41577
- logPiUsageAboveWindowOnce(args.sessionId, unboundedPressure.inputTokens, trustedAbsoluteWall);
41578
- } else {
41579
- logPiUsageBoundOnce(args.sessionId, unboundedPressure.inputTokens, trustedAbsoluteWall, "current reading");
41580
- }
41581
- }
41582
- const requestSucceeded = requestAccepted && !readingAboveTrustedWall;
43321
+ const requestAccepted = !messageHadOverflowError && !providerResponseFailed({
43322
+ finish: args.message?.stopReason,
43323
+ error: msg?.errorMessage
43324
+ });
43325
+ const requestSucceeded = requestAccepted && !rejectedUsage;
41583
43326
  if (requestAccepted && unboundedPressure) {
41584
43327
  const rawOverflow = getOverflowState(args.db, args.sessionId);
41585
43328
  const detectedLimitMatchesModel = rawOverflow.detectedContextLimitModelKey === null || modelKey !== undefined && rawOverflow.detectedContextLimitModelKey === modelKey;
@@ -41589,10 +43332,12 @@ async function persistPiPressureFromMessageEnd(args) {
41589
43332
  }
41590
43333
  }
41591
43334
  const floorModelKey = piProvenFloorModelKey(activeModel);
41592
- let observedSafeInputTokens = resolvePiProvenInputFloor({
43335
+ const observedSafeInputTokens = resolvePiProvenInputFloor({
41593
43336
  db: args.db,
41594
43337
  sessionId: args.sessionId,
41595
- modelKey: floorModelKey
43338
+ modelKey: floorModelKey,
43339
+ readBranch: args.readBranch,
43340
+ providerInputLimit
41596
43341
  });
41597
43342
  const meta = getOrCreateSessionMeta(args.db, args.sessionId);
41598
43343
  const updates = {};
@@ -41604,17 +43349,6 @@ async function persistPiPressureFromMessageEnd(args) {
41604
43349
  if (assistant?.role === "assistant" && !providerResponseFailed(completion) && (unboundedPressure !== null || isSuccessfulProviderCompletion(completion))) {
41605
43350
  updates.lastResponseTime = Math.max(meta.lastResponseTime, Date.now());
41606
43351
  }
41607
- if (trustedAbsoluteWall !== undefined && observedSafeInputTokens > trustedAbsoluteWall) {
41608
- logPiUsageBoundOnce(args.sessionId, observedSafeInputTokens, trustedAbsoluteWall, "persisted floor");
41609
- observedSafeInputTokens = 0;
41610
- updates.observedSafeInputTokens = 0;
41611
- updates.cacheAlertSent = false;
41612
- updates.lastUsageContextLimit = reportedGeometry?.usableSoft ?? 0;
41613
- if (meta.lastInputTokens > trustedAbsoluteWall) {
41614
- updates.lastInputTokens = trustedAbsoluteWall;
41615
- updates.lastContextPercentage = reportedGeometry && reportedGeometry.usableSoft > 0 ? trustedAbsoluteWall / reportedGeometry.usableSoft * 100 : 0;
41616
- }
41617
- }
41618
43352
  const effectiveContextLimit = resolvePiPressureContextLimit({
41619
43353
  db: args.db,
41620
43354
  sessionId: args.sessionId,
@@ -41623,8 +43357,13 @@ async function persistPiPressureFromMessageEnd(args) {
41623
43357
  model: activeModel,
41624
43358
  provenInputTokens: observedSafeInputTokens
41625
43359
  });
43360
+ if (rejectedUsage) {
43361
+ updates.lastInputTokens = 0;
43362
+ updates.lastContextPercentage = 0;
43363
+ updates.lastUsageContextLimit = effectiveContextLimit;
43364
+ }
41626
43365
  const reportedContextLimit = reportedGeometry?.usableSoft ?? 0;
41627
- const pressure = computePiPressure(usage, effectiveContextLimit, requestAccepted ? undefined : trustedAbsoluteWall);
43366
+ const pressure = computePiPressure(usage, effectiveContextLimit, providerInputLimit);
41628
43367
  let cacheAlert;
41629
43368
  if (pressure) {
41630
43369
  notePiUsageReadingUsed(args.sessionId);
@@ -41649,7 +43388,7 @@ async function persistPiPressureFromMessageEnd(args) {
41649
43388
  if (requestSucceeded) {
41650
43389
  updates.observedSafeInputTokens = provenSafeInputTokens;
41651
43390
  }
41652
- } else if (usage === null && typeof args.piTokens === "number" && (trustedAbsoluteWall === undefined || args.piTokens <= trustedAbsoluteWall)) {
43391
+ } else if ((usage === null || rejectedUsage) && typeof args.piTokens === "number" && Number.isFinite(args.piTokens) && args.piTokens > 0 && args.piTokens <= providerInputLimit) {
41653
43392
  if (args.piTokensIsRawBranchEstimate === true) {
41654
43393
  noteRawBranchEstimateSetAside(args.sessionId, args.piTokens, "message_end estimate");
41655
43394
  } else {
@@ -41734,7 +43473,7 @@ function resolveHistorianFromConfig(config, harness = PI_HARNESS_KIND, modelRegi
41734
43473
  executeThresholdTokens: config.execute_threshold_tokens,
41735
43474
  commitClusterTrigger: config.commit_cluster_trigger,
41736
43475
  protectedTags: config.protected_tags,
41737
- clearReasoningAge: config.clear_reasoning_age,
43476
+ keepReasoningTokens: config.keep_reasoning_tokens,
41738
43477
  historyBudgetPercentage: config.history_budget_percentage,
41739
43478
  memoryEnabled: config.memory.enabled,
41740
43479
  autoPromote: config.memory.auto_promote,
@@ -41980,7 +43719,7 @@ async function startPiMagicContextRuntime(hostPi, database, dbPath) {
41980
43719
  minChars: cfg.caveman_text_compression.min_chars,
41981
43720
  wordRules: cavemanWordRulesForLanguage(cfg.language)
41982
43721
  } : undefined,
41983
- clearReasoningAge: cfg.clear_reasoning_age
43722
+ keepReasoningTokens: cfg.keep_reasoning_tokens
41984
43723
  },
41985
43724
  injection: {
41986
43725
  memoryEnabled: cfg.memory.enabled,
@@ -42675,6 +44414,7 @@ async function startPiMagicContextRuntime(hostPi, database, dbPath) {
42675
44414
  piContextWindow,
42676
44415
  piContextWindowSource: "catalog",
42677
44416
  piModel: ctx.model,
44417
+ readBranch: () => ctx.sessionManager.getBranch(),
42678
44418
  piTokens: piUsage && typeof piUsage.tokens === "number" ? piUsage.tokens : undefined,
42679
44419
  piTokensIsRawBranchEstimate: isPiContextUsageRawBranchEstimate(ctx.sessionManager),
42680
44420
  notifyIssue: async (message) => {