@cortexkit/pi-magic-context 0.48.0 → 0.49.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.
Files changed (40) hide show
  1. package/dist/auto-search-worker.js +3 -3
  2. package/dist/embedding-backfill-b71dvfs8.js +20 -0
  3. package/dist/embedding-worker.js +4 -4
  4. package/dist/historian-calibration-extension.js +30 -5
  5. package/dist/{index-dkx0b1kt.js → index-041ex0cj.js} +7 -2
  6. package/dist/{index-fp37he9a.js → index-0vrkp1jq.js} +1 -1
  7. package/dist/{index-6pgq5ns2.js → index-1evzceyw.js} +2 -2
  8. package/dist/{index-419bndtm.js → index-21v1kfs7.js} +2 -2
  9. package/dist/{index-wrm4nbj7.js → index-290tzwp1.js} +1 -1
  10. package/dist/{index-72bdc1bd.js → index-3mgf68pn.js} +4 -4
  11. package/dist/{index-hrf13mn3.js → index-3rea6zd9.js} +16 -8
  12. package/dist/{index-0am8a7k1.js → index-904reqtg.js} +4 -4
  13. package/dist/{index-fm3ksh5e.js → index-bejpsj10.js} +4 -4
  14. package/dist/{index-98z1c1r0.js → index-ehybv5nr.js} +109 -35
  15. package/dist/{index-pvm4pd5d.js → index-hwqb3s7f.js} +811 -76
  16. package/dist/{index-5hd7h4rr.js → index-kjd1fj55.js} +39 -31
  17. package/dist/{index-8y5knskr.js → index-mcgehvgw.js} +93 -9
  18. package/dist/{index-603mt6nm.js → index-r024r8da.js} +1 -1
  19. package/dist/{index-9xb0axhc.js → index-r56xgha8.js} +1 -1
  20. package/dist/{index-r7xg4hvw.js → index-t7xfqtg6.js} +8 -8
  21. package/dist/{index-rt5jgzp1.js → index-vakawmbc.js} +160 -25
  22. package/dist/{index-f956htf3.js → index-x3dcnxh0.js} +6 -2
  23. package/dist/{index-544c0e0x.js → index-yarjn32j.js} +7 -7
  24. package/dist/{index-5jp1h0bz.js → index-yzcjxnt2.js} +48 -11
  25. package/dist/{index-zrr5t5a7.js → index-zh1jw8qp.js} +106 -1
  26. package/dist/index.js +1149 -680
  27. package/dist/{logger-v3dsxdnm.js → logger-j6tecd1h.js} +1 -1
  28. package/dist/{message-index-bounded-t7vvty76.js → message-index-bounded-qz5dz2se.js} +11 -11
  29. package/dist/{message-time-backfill-4ndf7hrq.js → message-time-backfill-05ppypkz.js} +2 -2
  30. package/dist/migration-worker.js +8 -8
  31. package/dist/{project-embedding-registry-jy2hmq28.js → project-embedding-registry-ky7wrkr1.js} +9 -9
  32. package/dist/raw-ordinal-warmup-worker.js +4 -4
  33. package/dist/{read-session-chunk-47pjn7fz.js → read-session-chunk-24sd89tf.js} +8 -6
  34. package/dist/{read-session-raw-yn8nawav.js → read-session-raw-npx3920c.js} +5 -3
  35. package/dist/{safe-notification-target-3c1ns931.js → safe-notification-target-vn9kpmv4.js} +2 -2
  36. package/dist/{search-42szfhwp.js → search-jbh28nwy.js} +17 -17
  37. package/dist/{storage-tags-jym5z6yk.js → storage-tags-kjnsqbdb.js} +6 -6
  38. package/dist/subagent-entry.js +20 -19
  39. package/package.json +1 -1
  40. package/dist/embedding-backfill-bnnytehy.js +0 -20
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  log2,
16
16
  sessionLog2,
17
17
  flushLogger2
18
- } from "./index-f956htf3.js";
18
+ } from "./index-x3dcnxh0.js";
19
19
  import {
20
20
  COMPACTION_ENABLED_PATH,
21
21
  isDreamerRunnable,
@@ -105,7 +105,8 @@ import {
105
105
  deliverNoteNudgeAtomic,
106
106
  appendAutoSearchHintDecision,
107
107
  pruneNoteNudgeAnchors,
108
- pruneAutoSearchHintDecisions,
108
+ queueAutoSearchHintDecisionRetirement,
109
+ flushAutoSearchHintDecisionRetirement,
109
110
  getPersistedTodoSyntheticAnchor,
110
111
  setPersistedTodoSyntheticAnchor,
111
112
  clearPersistedTodoSyntheticAnchor,
@@ -139,6 +140,9 @@ import {
139
140
  setSessionWorkMetrics,
140
141
  getSessionWorkMetrics,
141
142
  resolveEpochFloorForPass,
143
+ rememberUnpublishedAutoSearchNoHint,
144
+ getUnpublishedAutoSearchNoHint,
145
+ forgetUnpublishedAutoSearchNoHint,
142
146
  registerAutoSearchWork,
143
147
  cancelAutoSearchWork,
144
148
  readSessionMeta,
@@ -327,7 +331,7 @@ import {
327
331
  registerTodoStateLifecycle,
328
332
  syncCtxMemoryToolEnabled,
329
333
  registerMagicContextTools
330
- } from "./index-98z1c1r0.js";
334
+ } from "./index-ehybv5nr.js";
331
335
  import {
332
336
  isValidLanguageCode,
333
337
  withContentLanguageDirective,
@@ -338,7 +342,7 @@ import {
338
342
  DEFAULT_HISTORIAN_TIMEOUT_MS,
339
343
  DREAM_TASK_PROMOTION_DEFAULTS,
340
344
  LIVE_RELOAD_CONFIG_PATHS
341
- } from "./index-6pgq5ns2.js";
345
+ } from "./index-1evzceyw.js";
342
346
  import {
343
347
  canonicalModelIdentity,
344
348
  piModelRefToCanonical,
@@ -370,8 +374,9 @@ import {
370
374
  openCurrentDatabase,
371
375
  openDatabaseAsync,
372
376
  closeDatabase
373
- } from "./index-r7xg4hvw.js";
377
+ } from "./index-t7xfqtg6.js";
374
378
  import {
379
+ sameProjectDirectory,
375
380
  setHomeProjectPermission,
376
381
  ProjectIdentityError,
377
382
  resolveProjectIdentityStrict,
@@ -381,7 +386,7 @@ import {
381
386
  resolveProjectIdentityForSession,
382
387
  beginBootQuietPeriod,
383
388
  scheduleAfterBootQuiet
384
- } from "./index-dkx0b1kt.js";
389
+ } from "./index-041ex0cj.js";
385
390
  import {
386
391
  detectThinkingBindingMismatch,
387
392
  detectLatestTurnThinkingMismatch,
@@ -430,7 +435,7 @@ import {
430
435
  getRawSessionTagKeysThrough2,
431
436
  getLegacyProtectedTailStartOrdinal2,
432
437
  readSessionChunk2
433
- } from "./index-hrf13mn3.js";
438
+ } from "./index-3rea6zd9.js";
434
439
  import {
435
440
  UNKNOWN_FIT_RATIO,
436
441
  hasMeasuredDecisionCalibration,
@@ -512,7 +517,7 @@ import {
512
517
  getToolTagNumberByOwner2,
513
518
  getNullOwnerToolTag2,
514
519
  adoptNullOwnerToolTag2
515
- } from "./index-5hd7h4rr.js";
520
+ } from "./index-kjd1fj55.js";
516
521
  import {
517
522
  textMentionsRecentCommit,
518
523
  hasMeaningfulUserText2,
@@ -524,7 +529,7 @@ import {
524
529
  estimateTokens,
525
530
  estimateFixedPromptTokens,
526
531
  normalizeText
527
- } from "./index-9xb0axhc.js";
532
+ } from "./index-r56xgha8.js";
528
533
  import {
529
534
  Database2,
530
535
  getSqliteDatabasePath2,
@@ -533,6 +538,7 @@ import {
533
538
  withoutSqliteTransformPass2,
534
539
  guardSqliteTransformPass2,
535
540
  assertTransformWrite2,
541
+ isTransientSqliteError2,
536
542
  withAsyncPrivilegedWriter2,
537
543
  indexCoordinationInput2
538
544
  } from "./index-3cgbv47m.js";
@@ -541,8 +547,13 @@ import {
541
547
  closeQuietly,
542
548
  isKnownAutocommit,
543
549
  observeTemporalDecisions,
544
- freezeTemporalDecisions
545
- } from "./index-8y5knskr.js";
550
+ freezeTemporalDecisions,
551
+ tagRevisionIncarnationSql,
552
+ tagRevisionCountSql,
553
+ tagRevisionKey,
554
+ tagIdentityRevisionKey,
555
+ TAG_INSERT_CONFLICT_ROWS
556
+ } from "./index-mcgehvgw.js";
546
557
  import {
547
558
  resolveOpenCodeDbPath,
548
559
  assertOpenCodeStoreGeneration,
@@ -554,11 +565,11 @@ import {
554
565
  import {
555
566
  runSessionWarmupWorker,
556
567
  RAW_SUMMARY_TEXT_MAX_CHARS2
557
- } from "./index-rt5jgzp1.js";
568
+ } from "./index-vakawmbc.js";
558
569
  import {
559
570
  formatSynapseLaneDescriptor
560
- } from "./index-419bndtm.js";
561
- import"./index-603mt6nm.js";
571
+ } from "./index-21v1kfs7.js";
572
+ import"./index-r024r8da.js";
562
573
  import {
563
574
  buildCanonicalChunkTextFromFts,
564
575
  buildCompartmentSummaryFallbackText,
@@ -568,7 +579,7 @@ import {
568
579
  replaceCompartmentChunkEmbeddings,
569
580
  deleteChunkEmbedBackoffForSession,
570
581
  chunkWindowSourceKey
571
- } from "./index-fm3ksh5e.js";
582
+ } from "./index-bejpsj10.js";
572
583
  import {
573
584
  readSessionActivity,
574
585
  observeSessionActivity,
@@ -583,7 +594,7 @@ import {
583
594
  indexSingleMessage,
584
595
  indexMessagesAfterOrdinal,
585
596
  sweepOrphanedOpenCodeMessageIndexes
586
- } from "./index-5jp1h0bz.js";
597
+ } from "./index-yzcjxnt2.js";
587
598
  import {
588
599
  recordSessionProjectIdentity,
589
600
  drainBackgroundBatches,
@@ -609,7 +620,7 @@ import {
609
620
  embedSessionCompartmentChunks2,
610
621
  getEmbeddingCoverageStatus2,
611
622
  getAutoEmbeddingSessionCoverage2
612
- } from "./index-544c0e0x.js";
623
+ } from "./index-yarjn32j.js";
613
624
  import {
614
625
  V2_MEMORY_CATEGORIES
615
626
  } from "./index-135e8hvg.js";
@@ -623,7 +634,7 @@ import {
623
634
  expandWorkspaceIdentitySetWithAliases,
624
635
  sourceNameForMemory,
625
636
  computeWorkspaceEpochFingerprint
626
- } from "./index-72bdc1bd.js";
637
+ } from "./index-3mgf68pn.js";
627
638
  import {
628
639
  configureContextDatabasePragmas2
629
640
  } from "./index-fapenpch.js";
@@ -648,6 +659,16 @@ import {
648
659
  createPrimer,
649
660
  updatePrimerSupport,
650
661
  updatePrimerAnswer,
662
+ USER_FACING_FAILURES,
663
+ renderUserFacingFailure,
664
+ userFacingFailureCode,
665
+ renderCapabilityRefusal,
666
+ renderDreamFailure,
667
+ dreamFailureCode,
668
+ renderEmbeddingFailure,
669
+ renderSingleStoreMigrationRequiredRefusal,
670
+ SingleStoreMigrationRequiredError,
671
+ projectNeedsSingleStoreMigration,
651
672
  promoteSessionFactsDurable,
652
673
  embedPromotedFacts,
653
674
  recordMemoryMapping,
@@ -663,7 +684,7 @@ import {
663
684
  normalizeVerificationFiles,
664
685
  indexCommitsForProject,
665
686
  embedUnembeddedCommits
666
- } from "./index-pvm4pd5d.js";
687
+ } from "./index-hwqb3s7f.js";
667
688
  import {
668
689
  setEmbeddingSessionBusy2,
669
690
  isEmbeddingHostBusy2
@@ -696,14 +717,15 @@ import {
696
717
  } from "./index-q1yc6bs0.js";
697
718
  import {
698
719
  cosineSimilarity
699
- } from "./index-0am8a7k1.js";
720
+ } from "./index-904reqtg.js";
700
721
  import {
701
722
  buildHiddenAgentRegistrations,
723
+ logTemperatureDecision,
702
724
  subagentStepCap,
703
725
  SubagentTelemetry,
704
726
  readHostSystemPrompt
705
- } from "./index-zrr5t5a7.js";
706
- import"./index-wrm4nbj7.js";
727
+ } from "./index-zh1jw8qp.js";
728
+ import"./index-290tzwp1.js";
707
729
  import {
708
730
  __toESM
709
731
  } from "./index-22jkk6wg.js";
@@ -713,7 +735,7 @@ import {
713
735
  import {
714
736
  logSlowWriteTransaction,
715
737
  WriteTransactionSteps
716
- } from "./index-fp37he9a.js";
738
+ } from "./index-0vrkp1jq.js";
717
739
  import {
718
740
  setStoragePrivatePermissionEnforcement,
719
741
  ensureStorageDirectorySync,
@@ -727,7 +749,7 @@ import {
727
749
  // src/index.ts
728
750
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
729
751
  import { createRequire as createRequire2 } from "node:module";
730
- import { join as join16, resolve as resolve4 } from "node:path";
752
+ import { join as join16, resolve as resolve3 } from "node:path";
731
753
 
732
754
  // ../plugin/src/config/live-snapshot.ts
733
755
  import { createHash } from "node:crypto";
@@ -4061,335 +4083,9 @@ function evaluateTaskGate(task, ctx) {
4061
4083
  }
4062
4084
  }
4063
4085
 
4064
- // ../plugin/src/shared/user-facing-codes.ts
4065
- var USER_FACING_FAILURES = {
4066
- historian_unavailable: {
4067
- code: "MC-H01",
4068
- sentence: "History compression could not finish this turn.",
4069
- action: "It will retry automatically."
4070
- },
4071
- historian_saved_history_misaligned: {
4072
- code: "MC-H03",
4073
- sentence: "History compression is paused because this session's saved summaries no longer line up with its messages.",
4074
- action: "Run /ctx-recomp to rebuild them."
4075
- },
4076
- history_boundary_unresolved: {
4077
- code: "MC-H04",
4078
- sentence: "This request was not sent: the message that marks where this session's history summary ends is missing from the OpenCode store, and without it the request is larger than the model's context window.",
4079
- action: "Run /ctx-recomp to rebuild the history summary."
4080
- },
4081
- history_over_window_unmanaged: {
4082
- code: "MC-H06",
4083
- sentence: "This request was not sent: this session's history is larger than the model's context window, and Magic Context does not have a summary of it yet to send in its place.",
4084
- action: "Send your message again once history compression has caught up, or switch to a model with a larger context window."
4085
- },
4086
- frozen_history_over_window: {
4087
- code: "MC-H07",
4088
- sentence: "This request was not sent: the conversation is larger than the context window the provider reported, and the compressed history Magic Context has ready does not fit either.",
4089
- action: "Run /ctx-flush to compress it now, then send your message again."
4090
- },
4091
- historian_window_too_small: {
4092
- code: "MC-H05",
4093
- sentence: "History compression is paused because the history model's context window is too small for its instructions.",
4094
- action: "Set historian.model in magic-context.jsonc to a model with a larger context window."
4095
- },
4096
- recomp_unavailable: {
4097
- code: "MC-R01",
4098
- sentence: "History compression could not be rebuilt.",
4099
- action: "Run /ctx-recomp again."
4100
- },
4101
- dream_provider_timeout: {
4102
- code: "MC-D01",
4103
- sentence: "Memory maintenance took too long to respond.",
4104
- action: "Run /ctx-dream again."
4105
- },
4106
- dream_provider_error: {
4107
- code: "MC-D02",
4108
- sentence: "Memory maintenance could not reach its model.",
4109
- action: "Check the model connection, then run /ctx-dream again."
4110
- },
4111
- dream_local_refusal: {
4112
- code: "MC-D12",
4113
- sentence: "Memory maintenance was refused before reaching the model.",
4114
- action: "Check the hidden-request diagnostic in the Magic Context log before retrying."
4115
- },
4116
- dream_step_limit: {
4117
- code: "MC-D10",
4118
- sentence: "Memory maintenance stopped at its hidden agent step limit.",
4119
- action: "This task needs less work per run; changing the model connection will not help."
4120
- },
4121
- dream_token_budget: {
4122
- code: "MC-D11",
4123
- sentence: "Memory maintenance reached its prompt-token budget.",
4124
- action: "The unfinished items will be retried on the next run."
4125
- },
4126
- dream_empty_completion: {
4127
- code: "MC-D03",
4128
- sentence: "Memory maintenance received no usable response.",
4129
- action: "Run /ctx-dream again."
4130
- },
4131
- dream_no_models: {
4132
- code: "MC-D04",
4133
- sentence: "Memory maintenance has no model available.",
4134
- action: "Check the model settings, then run /ctx-dream again."
4135
- },
4136
- dream_child_aborted: {
4137
- code: "MC-D05",
4138
- sentence: "Memory maintenance was interrupted.",
4139
- action: "Run /ctx-dream again."
4140
- },
4141
- dream_parse_failed: {
4142
- code: "MC-D06",
4143
- sentence: "Memory maintenance could not use the model response.",
4144
- action: "Run /ctx-dream again."
4145
- },
4146
- dream_unknown: {
4147
- code: "MC-D07",
4148
- sentence: "Memory maintenance could not finish.",
4149
- action: "Run /ctx-dream again."
4150
- },
4151
- dreamer_tick_blocked: {
4152
- code: "MC-D09",
4153
- sentence: "Background maintenance is not running: its last pass stopped before it reached the scheduled tasks.",
4154
- action: "It is retried automatically; if it keeps happening, check the Magic Context log for the stage that stopped and run `npx @cortexkit/magic-context doctor`."
4155
- },
4156
- dream_task_needs_tool_loop: {
4157
- code: "MC-D08",
4158
- sentence: "Some memory maintenance tasks need a tool loop this host does not provide.",
4159
- action: "The remaining tasks still run; the listed ones are skipped on this host."
4160
- },
4161
- embedding_substitution_rejected: {
4162
- code: "MC-E01",
4163
- sentence: "Search indexing could not use the selected model.",
4164
- action: "Check the embedding model setting, then run /ctx-embed start again."
4165
- },
4166
- embedding_http_error: {
4167
- code: "MC-E02",
4168
- sentence: "The search indexing provider refused the request.",
4169
- action: "Check the provider connection and credentials, then run /ctx-embed start again."
4170
- },
4171
- embedding_transport_error: {
4172
- code: "MC-E03",
4173
- sentence: "Search indexing could not reach its provider.",
4174
- action: "Check the connection, then run /ctx-embed start again."
4175
- },
4176
- embedding_invalid_envelope: {
4177
- code: "MC-E04",
4178
- sentence: "Search indexing received an unsupported response.",
4179
- action: "Check the embedding endpoint, then run /ctx-embed start again."
4180
- },
4181
- embedding_empty_result: {
4182
- code: "MC-E05",
4183
- sentence: "Search indexing received no usable result.",
4184
- action: "Run /ctx-embed start again."
4185
- },
4186
- embedding_certification_refusal: {
4187
- code: "MC-E06",
4188
- sentence: "Search indexing is not ready for this provider.",
4189
- action: "Finish the provider setup, or set a fallback provider in the embedding settings, then run /ctx-embed start again."
4190
- },
4191
- embedding_credential_required: {
4192
- code: "MC-E07",
4193
- sentence: "Search indexing needs provider credentials.",
4194
- action: "Sign in to the provider, then run /ctx-embed start again."
4195
- },
4196
- embedding_local_binding_missing: {
4197
- code: "MC-E08",
4198
- sentence: "Local search indexing is unavailable on this system.",
4199
- action: "Run `npx @cortexkit/magic-context doctor`, then retry."
4200
- },
4201
- embedding_local_fs_unavailable: {
4202
- code: "MC-E09",
4203
- sentence: "Local search indexing cannot save its model files.",
4204
- action: "Update or reinstall Magic Context, then retry."
4205
- },
4206
- embedding_local_download_failure: {
4207
- code: "MC-E10",
4208
- sentence: "Local search indexing could not download its model.",
4209
- action: "Check the network connection, then retry."
4210
- },
4211
- embedding_local_runtime_error: {
4212
- code: "MC-E11",
4213
- sentence: "Local search indexing could not start.",
4214
- action: "Run `npx @cortexkit/magic-context doctor`, then retry."
4215
- },
4216
- embedding_unavailable: {
4217
- code: "MC-E12",
4218
- sentence: "Search indexing could not finish.",
4219
- action: "Run /ctx-embed start again."
4220
- },
4221
- status_unavailable: {
4222
- code: "MC-S01",
4223
- sentence: "Magic Context status is temporarily unavailable.",
4224
- action: "Retry /ctx-status in a moment."
4225
- },
4226
- transform_update_failed: {
4227
- code: "MC-S02",
4228
- sentence: "The last context update did not finish.",
4229
- action: "Send another message to retry."
4230
- },
4231
- transform_pass_degraded: {
4232
- code: "MC-S06",
4233
- sentence: "This request was not sent: Magic Context could not finish preparing it, and without that preparation it could be far larger than the previous request.",
4234
- action: "Send your message again."
4235
- },
4236
- configuration_warning: {
4237
- code: "MC-S03",
4238
- sentence: "Some configuration settings could not be applied.",
4239
- action: "Fix the configuration warning shown in /ctx-status, then restart."
4240
- },
4241
- status_log_unavailable: {
4242
- code: "MC-S04",
4243
- sentence: "Some diagnostic details could not be saved.",
4244
- action: "Retry /ctx-status in a moment."
4245
- },
4246
- memory_writes_paused: {
4247
- code: "MC-C01",
4248
- sentence: "Memory writes are paused while the engine syncs.",
4249
- action: "Retry in a moment."
4250
- },
4251
- memory_access_unavailable: {
4252
- code: "MC-C02",
4253
- sentence: "Memory access is temporarily unavailable.",
4254
- action: "Retry in a moment."
4255
- },
4256
- note_changes_paused: {
4257
- code: "MC-C03",
4258
- sentence: "Note changes are paused while the engine syncs.",
4259
- action: "Retry in a moment."
4260
- },
4261
- note_access_unavailable: {
4262
- code: "MC-C04",
4263
- sentence: "Notes are temporarily unavailable.",
4264
- action: "Retry in a moment."
4265
- },
4266
- context_cleanup_paused: {
4267
- code: "MC-C05",
4268
- sentence: "Context cleanup is paused while the engine syncs.",
4269
- action: "Retry in a moment."
4270
- },
4271
- partial_history_unavailable: {
4272
- code: "MC-C06",
4273
- sentence: "Partial history compression is not available in the current mode.",
4274
- action: "Run /ctx-recomp without a range."
4275
- },
4276
- smart_note_conditions_unavailable: {
4277
- code: "MC-C08",
4278
- sentence: "Conditional notes are not available in the current mode.",
4279
- action: "Save a regular note without a condition."
4280
- },
4281
- history_compression_paused: {
4282
- code: "MC-C09",
4283
- sentence: "History compression is paused while the engine syncs.",
4284
- action: "Retry in a moment."
4285
- },
4286
- history_compression_needs_message: {
4287
- code: "MC-C12",
4288
- sentence: "History compression has not seen this session since Magic Context reconnected.",
4289
- action: "Send a message in this session first, then run /ctx-wrapup again."
4290
- },
4291
- context_service_unavailable: {
4292
- code: "MC-C10",
4293
- sentence: "Magic Context is temporarily unavailable.",
4294
- action: "Retry in a moment."
4295
- },
4296
- context_db_missing: {
4297
- code: "MC-C15",
4298
- sentence: "Magic Context has no context.db.",
4299
- action: "Run `npx @cortexkit/magic-context doctor store init`, then restart ck-mc."
4300
- },
4301
- single_store_migration_required: {
4302
- code: "MC-C14",
4303
- sentence: "Magic Context's Rust mode needs a one-time migration of its store.",
4304
- action: "Quit OpenCode and every ck-mc process, then run `magic-context doctor single-store migrate`."
4305
- },
4306
- store_ahead_of_binary: {
4307
- code: "MC-C13",
4308
- sentence: "Magic Context refused to start: its store (store.db) was migrated by a newer ck-mc build than the one running.",
4309
- action: "Update ck-mc, or roll back by restoring ck-mc together with context.db and store.db from the same backup."
4310
- },
4311
- compaction_marker_missing: {
4312
- code: "MC-C11",
4313
- sentence: "The history boundary marker is missing from the OpenCode store, so requests carry the full session.",
4314
- action: "It is retried on every message; if this persists, run `/ctx-flush`."
4315
- },
4316
- memory_mirror_stalled: {
4317
- code: "MC-M01",
4318
- sentence: "Memory synchronization stopped before the host mirror caught up.",
4319
- action: "Send another message to resume it, or run `ck doctor drain-authority`."
4320
- },
4321
- memory_authority_mismatch: {
4322
- code: "MC-M02",
4323
- sentence: "Memory authority is inconsistent between the host and module.",
4324
- action: "Run `ck doctor drain-authority` before changing Rust mode."
4325
- },
4326
- dreamer_task_failing: {
4327
- code: "MC-S05",
4328
- sentence: "A background maintenance task keeps failing on its schedule.",
4329
- action: "Check the Magic Context log for the failing task and its error."
4330
- }
4331
- };
4332
- function renderUserFacingFailure(key, style = "markdown") {
4333
- const failure = USER_FACING_FAILURES[key];
4334
- const action = style === "plain" ? failure.action.replaceAll("`", "") : failure.action;
4335
- return `${failure.sentence} ${action} (${failure.code})`;
4336
- }
4337
- function userFacingFailureCode(key) {
4338
- return USER_FACING_FAILURES[key].code;
4339
- }
4340
- var CAPABILITY_FAILURES = {
4341
- memory_write: "memory_writes_paused",
4342
- memory_access: "memory_access_unavailable",
4343
- note_change: "note_changes_paused",
4344
- note_access: "note_access_unavailable",
4345
- context_cleanup: "context_cleanup_paused",
4346
- partial_history: "partial_history_unavailable",
4347
- smart_note_condition: "smart_note_conditions_unavailable",
4348
- history_compression: "history_compression_paused",
4349
- context_service: "context_service_unavailable"
4350
- };
4351
- function renderCapabilityRefusal(capability) {
4352
- return renderUserFacingFailure(CAPABILITY_FAILURES[capability]);
4353
- }
4354
- var DREAM_FAILURE_KEYS = {
4355
- provider_timeout: "dream_provider_timeout",
4356
- provider_error: "dream_provider_error",
4357
- local_refusal: "dream_local_refusal",
4358
- step_limit: "dream_step_limit",
4359
- token_budget: "dream_token_budget",
4360
- empty_completion: "dream_empty_completion",
4361
- no_models: "dream_no_models",
4362
- child_aborted: "dream_child_aborted",
4363
- parse_failed: "dream_parse_failed",
4364
- unknown: "dream_unknown"
4365
- };
4366
- var EMBEDDING_FAILURE_KEYS = {
4367
- substitution_rejected: "embedding_substitution_rejected",
4368
- http_error: "embedding_http_error",
4369
- transport_error: "embedding_transport_error",
4370
- invalid_envelope: "embedding_invalid_envelope",
4371
- empty_result: "embedding_empty_result",
4372
- certification_refusal: "embedding_certification_refusal",
4373
- credential_required: "embedding_credential_required",
4374
- local_binding_missing: "embedding_local_binding_missing",
4375
- local_fs_unavailable: "embedding_local_fs_unavailable",
4376
- local_download_failure: "embedding_local_download_failure",
4377
- local_runtime_error: "embedding_local_runtime_error"
4378
- };
4379
- function renderDreamFailure(failureClass, style = "markdown", refusalReason) {
4380
- const rendered = renderUserFacingFailure(DREAM_FAILURE_KEYS[failureClass], style);
4381
- return failureClass === "local_refusal" && refusalReason ? `${rendered} Reason: ${sanitizeDiagnosticText(refusalReason).slice(0, 500)}` : rendered;
4382
- }
4383
- function dreamFailureCode(failureClass) {
4384
- return userFacingFailureCode(DREAM_FAILURE_KEYS[failureClass]);
4385
- }
4386
- function renderEmbeddingFailure(failureClass, style = "markdown") {
4387
- return renderUserFacingFailure(EMBEDDING_FAILURE_KEYS[failureClass], style);
4388
- }
4389
-
4390
4086
  // src/dreamer/index.ts
4391
4087
  import { homedir as homedir2 } from "node:os";
4392
- import { parse, resolve as resolve3 } from "node:path";
4088
+ import { parse, resolve as resolve2 } from "node:path";
4393
4089
 
4394
4090
  // ../plugin/src/features/magic-context/dreamer/open-opencode-db.ts
4395
4091
  function openOpenCodeDb() {
@@ -4554,25 +4250,6 @@ class HiddenCompletionRefusal extends Error {
4554
4250
  this.name = "HiddenCompletionRefusal";
4555
4251
  }
4556
4252
  }
4557
-
4558
- // ../plugin/src/hooks/magic-context/single-store-refusal.ts
4559
- var SINGLE_STORE_MIGRATION_REQUIRED_CODE = "single_store_migration_required";
4560
- function renderSingleStoreMigrationRequiredRefusal() {
4561
- const failure = USER_FACING_FAILURES.single_store_migration_required;
4562
- return `${failure.sentence} ${failure.action} (${failure.code})`;
4563
- }
4564
-
4565
- class SingleStoreMigrationRequiredError extends Error {
4566
- code = SINGLE_STORE_MIGRATION_REQUIRED_CODE;
4567
- constructor(options) {
4568
- super(renderSingleStoreMigrationRequiredRefusal(), options);
4569
- this.name = "SingleStoreMigrationRequiredError";
4570
- }
4571
- }
4572
- function projectNeedsSingleStoreMigration(db, projectPath) {
4573
- const table = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'authority_managed'").get();
4574
- return Boolean(table && db.prepare("SELECT 1 FROM authority_managed WHERE project_path = ?").get(projectPath));
4575
- }
4576
4253
  // ../plugin/src/features/magic-context/dreamer/provider-output-failure.ts
4577
4254
  import { AsyncLocalStorage } from "node:async_hooks";
4578
4255
  import { createHash as createHash3 } from "node:crypto";
@@ -18018,12 +17695,14 @@ class PiSubagentRunner {
18018
17695
  } catch {}
18019
17696
  };
18020
17697
  let child;
17698
+ const parentEnv = { ...process.env };
17699
+ delete parentEnv.MAGIC_CONTEXT_HISTORIAN_TEMPERATURE;
18021
17700
  try {
18022
17701
  child = this.spawnImpl(this.invocation.command, [...this.invocation.prefixArgs, ...this.extraArgs, ...args], {
18023
17702
  windowsHide: true,
18024
17703
  cwd: options.cwd,
18025
17704
  env: {
18026
- ...process.env,
17705
+ ...parentEnv,
18027
17706
  [MAGIC_CONTEXT_PI_SUBAGENT_ENV]: "1",
18028
17707
  MAGIC_CONTEXT_SUBAGENT_PROMPT_FILE: systemPromptPath,
18029
17708
  MAGIC_CONTEXT_SUBAGENT_BUDGET_GUARD_FILE: budgetGuardFile,
@@ -18084,6 +17763,22 @@ class PiSubagentRunner {
18084
17763
  child.stdin.end(options.userMessage, "utf8");
18085
17764
  } catch {}
18086
17765
  }
17766
+ const observeTemperatureDecision = (event) => {
17767
+ if (!event || typeof event !== "object")
17768
+ return;
17769
+ const notice = event;
17770
+ if (notice.type !== "mc_temperature" || typeof notice.model !== "string")
17771
+ return;
17772
+ if (notice.source !== "host" && notice.source !== "catalog" && notice.source !== "none")
17773
+ return;
17774
+ if (notice.supported !== null && typeof notice.supported !== "boolean")
17775
+ return;
17776
+ logTemperatureDecision(notice.model, {
17777
+ supported: notice.supported ?? undefined,
17778
+ source: notice.source,
17779
+ ...typeof notice.entry === "string" ? { entry: notice.entry } : {}
17780
+ }, (message) => sessionLog2(options.accountingSessionId ?? "pi-subagent", message));
17781
+ };
18087
17782
  let stderr = "";
18088
17783
  let stderrLine = "";
18089
17784
  const stderrDecoder = new StringDecoder("utf8");
@@ -18096,11 +17791,13 @@ class PiSubagentRunner {
18096
17791
  `);
18097
17792
  stderrLine = (lines.pop() ?? "").slice(-4096);
18098
17793
  for (const line of lines) {
18099
- if (settled || !line.startsWith('{"type":"mc_system_prompt"'))
17794
+ if (settled || !(line.startsWith('{"type":"mc_system_prompt"') || line.startsWith('{"type":"mc_temperature"')))
18100
17795
  continue;
18101
17796
  const parsed = parsePiEventLine(line);
18102
- if (parsed.ok)
17797
+ if (parsed.ok) {
18103
17798
  telemetry.observe(parsed.event);
17799
+ observeTemperatureDecision(parsed.event);
17800
+ }
18104
17801
  }
18105
17802
  stderr += text;
18106
17803
  if (stderr.length > 16000) {
@@ -18157,6 +17854,7 @@ class PiSubagentRunner {
18157
17854
  return;
18158
17855
  }
18159
17856
  const event = parsed.event;
17857
+ observeTemperatureDecision(event);
18160
17858
  if (typeof event !== "object" || event === null)
18161
17859
  return;
18162
17860
  const e = event;
@@ -19264,7 +18962,7 @@ function createPiPrimerRawProviderFactory(deps = {}) {
19264
18962
  }
19265
18963
 
19266
18964
  // src/dreamer/retrospective-raw-provider-pi.ts
19267
- import { join as join14, resolve as resolve2 } from "node:path";
18965
+ import { join as join14 } from "node:path";
19268
18966
  class PiRetrospectiveRawProvider {
19269
18967
  deps;
19270
18968
  sessionPathById = new Map;
@@ -19280,7 +18978,7 @@ class PiRetrospectiveRawProvider {
19280
18978
  directory = join14(mod.getAgentDir(), "sessions");
19281
18979
  }
19282
18980
  const sessions = this.deps.listSessions ? await this.deps.listSessions(directory) : directory ? sessionHeaders(directory, !this.deps.sessionDir) : [];
19283
- const projectCwd = resolve2(this.deps.projectCwd);
18981
+ const projectCwd = this.deps.projectCwd;
19284
18982
  const result = [];
19285
18983
  this.sessionPathById.clear();
19286
18984
  for (const raw of sessions) {
@@ -19289,7 +18987,7 @@ class PiRetrospectiveRawProvider {
19289
18987
  continue;
19290
18988
  if (typeof info.id !== "string" || typeof info.path !== "string")
19291
18989
  continue;
19292
- if (typeof info.cwd !== "string" || resolve2(info.cwd) !== projectCwd)
18990
+ if (typeof info.cwd !== "string" || !sameProjectDirectory(info.cwd, projectCwd))
19293
18991
  continue;
19294
18992
  const activity = this.deps.contextDb ? readSessionActivity(this.deps.contextDb, info.id) : typeof info.modified === "number" ? info.modified : undefined;
19295
18993
  if (this.deps.contextDb && activity === undefined)
@@ -19455,7 +19153,7 @@ var sessionCounter = 0;
19455
19153
  var piSubagentRunnerFactory = () => new PiSubagentRunner;
19456
19154
  var startDreamScheduleTimerFn = startDreamScheduleTimer;
19457
19155
  function registerPiDreamerProject(opts) {
19458
- const directory = resolve3(opts.projectDir);
19156
+ const directory = resolve2(opts.projectDir);
19459
19157
  if (opts.config.disable === true || !isUsableProjectIdentity(opts.projectIdentity) || directory === parse(directory).root || directory === homedir2()) {
19460
19158
  return;
19461
19159
  }
@@ -20948,7 +20646,9 @@ function makeToolCompositeKey(ownerMsgId, callId) {
20948
20646
  return `${ownerMsgId}${TOOL_COMPOSITE_KEY_SEP}${callId}`;
20949
20647
  }
20950
20648
  var GET_COUNTER_SQL = `SELECT counter FROM session_meta WHERE session_id = ?`;
20951
- var GET_TAGS_VERSION_SQL = `SELECT tags_version FROM session_meta WHERE session_id = ?`;
20649
+ var GET_TAGS_VERSION_SQL = `SELECT
20650
+ (SELECT tags_version FROM session_meta WHERE session_id = ?) AS tags_version,
20651
+ (SELECT value FROM schema_migrations_meta WHERE key = ?) AS identity_revision`;
20952
20652
  var GET_ASSIGNMENTS_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id, byte_size, token_count, input_byte_size, input_token_count FROM tags WHERE session_id = ? ORDER BY tag_number ASC";
20953
20653
  var GET_ASSIGNMENTS_SCOPED_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id, byte_size, token_count, input_byte_size, input_token_count FROM tags WHERE session_id = ? AND tag_number >= ? ORDER BY tag_number ASC";
20954
20654
  var probeTagsVersionStatements = new WeakMap;
@@ -21069,10 +20769,16 @@ function createTagger() {
21069
20769
  const memCounter = heapHolder.counters.get(sessionId) ?? 0;
21070
20770
  const dbMax = getMaxTagNumberBySession2(db, sessionId);
21071
20771
  const next = Math.max(memCounter, dbMax) + 1;
20772
+ const loaded = heapHolder.loadSignatures.get(sessionId);
20773
+ let ownedInitialIdentity;
21072
20774
  try {
21073
20775
  db.transaction(() => {
20776
+ const admitted = loaded?.db === db && loaded.identityRevision === null ? probeSignature(sessionId, db) : null;
20777
+ const initializesOwnedEpoch = admitted !== null && loaded !== undefined && admitted.identityRevision === null && admitted.tagsVersion === loaded.tagsVersion;
21074
20778
  getUpsertCounterStatement(db).run(sessionId, next, getHarness2());
21075
20779
  insertTag2(db, sessionId, messageId, type, byteSize, next, reasoningByteSize, toolName, inputByteSize, toolOwnerMessageId, entryFingerprint, tokenCounts);
20780
+ if (initializesOwnedEpoch)
20781
+ ownedInitialIdentity = probeSignature(sessionId, db).identityRevision;
21076
20782
  }).immediate();
21077
20783
  } catch (error) {
21078
20784
  if (!isUniqueConstraintError(error)) {
@@ -21093,6 +20799,8 @@ function createTagger() {
21093
20799
  const signature = heapHolder.loadSignatures.get(sessionId);
21094
20800
  if (signature?.db === db) {
21095
20801
  signature.tagsVersion += 1;
20802
+ if (signature === loaded && ownedInitialIdentity !== undefined)
20803
+ signature.identityRevision = ownedInitialIdentity;
21096
20804
  }
21097
20805
  if (type === "tool") {
21098
20806
  getSessionToolAccounting(sessionId).set(next, {
@@ -21195,15 +20903,16 @@ function createTagger() {
21195
20903
  return heapHolder.counters.get(sessionId) ?? 0;
21196
20904
  }
21197
20905
  function probeSignature(sessionId, db) {
21198
- const row = getProbeTagsVersionStatement(db).get(sessionId);
20906
+ const row = getProbeTagsVersionStatement(db).get(sessionId, tagIdentityRevisionKey(sessionId));
21199
20907
  return {
21200
- tagsVersion: row?.tags_version ?? 0
20908
+ tagsVersion: row?.tags_version ?? 0,
20909
+ identityRevision: row?.identity_revision ?? null
21201
20910
  };
21202
20911
  }
21203
20912
  function initFromDb(sessionId, db, floor = 0) {
21204
20913
  const probe = probeSignature(sessionId, db);
21205
20914
  const cached = heapHolder.loadSignatures.get(sessionId);
21206
- if (cached !== undefined && cached.db === db && cached.tagsVersion === probe.tagsVersion && cached.floor === floor) {
20915
+ if (cached !== undefined && cached.db === db && cached.tagsVersion === probe.tagsVersion && cached.identityRevision === probe.identityRevision && cached.floor === floor) {
21207
20916
  return;
21208
20917
  }
21209
20918
  const row = db.prepare(GET_COUNTER_SQL).get(sessionId);
@@ -21236,6 +20945,7 @@ function createTagger() {
21236
20945
  heapHolder.loadSignatures.set(sessionId, {
21237
20946
  db,
21238
20947
  tagsVersion: probe.tagsVersion,
20948
+ identityRevision: probe.identityRevision,
21239
20949
  floor
21240
20950
  });
21241
20951
  }
@@ -21243,6 +20953,10 @@ function createTagger() {
21243
20953
  const signature = heapHolder.loadSignatures.get(sessionId);
21244
20954
  return signature?.db === db ? signature.tagsVersion : undefined;
21245
20955
  }
20956
+ function getLoadedTagIdentityRevision(sessionId, db) {
20957
+ const signature = heapHolder.loadSignatures.get(sessionId);
20958
+ return signature?.db === db ? signature.identityRevision : undefined;
20959
+ }
21246
20960
  function cleanup(sessionId) {
21247
20961
  heapHolder.counters.delete(sessionId);
21248
20962
  heapHolder.assignments.delete(sessionId);
@@ -21285,6 +20999,7 @@ function createTagger() {
21285
20999
  getCounter,
21286
21000
  initFromDb,
21287
21001
  getLoadedTagsVersion,
21002
+ getLoadedTagIdentityRevision,
21288
21003
  cleanup,
21289
21004
  getHeapStats
21290
21005
  };
@@ -21491,13 +21206,14 @@ function writeTransformDecisionRowOnDatabase(db, row, configureBusyTimeout) {
21491
21206
  const m0ModelKeyNew = row.materialized ? row.m0ModelKeyNew : null;
21492
21207
  const m0ToolSetHashPrev = row.m0ToolSetHashPrev ?? null;
21493
21208
  const m0ToolSetHashNew = row.m0ToolSetHashNew ?? null;
21209
+ const persistedMaterializeReason = row.lkgFrozenReleaseReason ? `lkg_frozen_release:${row.lkgFrozenReleaseReason}` + (row.materializeReason ? `;materialize_reason=${row.materializeReason}` : "") : row.materializeReason;
21494
21210
  db.prepare(`INSERT OR REPLACE INTO transform_decisions (
21495
21211
  session_id, harness, message_id, ts_ms, decision, materialized,
21496
21212
  materialize_reason, system_hash_prev, system_hash_new,
21497
21213
  m0_tool_set_hash_prev, m0_tool_set_hash_new,
21498
21214
  m0_model_key_prev, m0_model_key_new, emergency, dropped_tokens,
21499
21215
  dropped_count, input_tokens
21500
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(row.sessionId, row.harness, row.messageId, row.tsMs, row.decision, row.materialized ? 1 : 0, row.materializeReason, systemHashPrev, systemHashNew, m0ToolSetHashPrev, m0ToolSetHashNew, m0ModelKeyPrev, m0ModelKeyNew, row.emergency ? 1 : 0, Math.max(0, Math.floor(row.droppedTokens)), Math.max(0, Math.floor(row.droppedCount)), Math.max(0, Math.floor(row.inputTokens)));
21216
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(row.sessionId, row.harness, row.messageId, row.tsMs, row.decision, row.materialized ? 1 : 0, persistedMaterializeReason, systemHashPrev, systemHashNew, m0ToolSetHashPrev, m0ToolSetHashNew, m0ModelKeyPrev, m0ModelKeyNew, row.emergency ? 1 : 0, Math.max(0, Math.floor(row.droppedTokens)), Math.max(0, Math.floor(row.droppedCount)), Math.max(0, Math.floor(row.inputTokens)));
21501
21217
  db.prepare(`DELETE FROM transform_decisions
21502
21218
  WHERE session_id = ? AND harness = ?
21503
21219
  AND rowid NOT IN (
@@ -23388,9 +23104,11 @@ function estimateDroppedTokensFromTagReductions(db, sessionId, reductions) {
23388
23104
  // ../plugin/src/hooks/magic-context/emergency-fail-closed.ts
23389
23105
  var PROTECTED_TOOL_RESULTS_OVER_LIMIT = "The tool results kept by protected_tools are larger than this model's context window, so this turn was not sent. Lower the protected_tools counts.";
23390
23106
  var PROTECTED_TOOL_RESULTS_OVER_LIMIT_CODE = "protected_tool_results_over_limit";
23107
+ var SIGNED_TURN_OVER_LIMIT = "This turn's context is larger than the model's context window even after all of its thinking and reclaimable tool output were removed, so it was not sent. Send a new message or start a new session to continue.";
23108
+ var SIGNED_TURN_OVER_LIMIT_CODE = "signed_turn_over_limit";
23391
23109
  function contextRefusalError(message) {
23392
23110
  return new EmergencyFailClosedError(message, {
23393
- code: message === PROTECTED_TOOL_RESULTS_OVER_LIMIT ? PROTECTED_TOOL_RESULTS_OVER_LIMIT_CODE : undefined
23111
+ code: message === PROTECTED_TOOL_RESULTS_OVER_LIMIT ? PROTECTED_TOOL_RESULTS_OVER_LIMIT_CODE : message === SIGNED_TURN_OVER_LIMIT ? SIGNED_TURN_OVER_LIMIT_CODE : undefined
23394
23112
  });
23395
23113
  }
23396
23114
  function outgoingContextRefusal(estimate, limit, protectedToolTokens = 0) {
@@ -23398,7 +23116,6 @@ function outgoingContextRefusal(estimate, limit, protectedToolTokens = 0) {
23398
23116
  return;
23399
23117
  return PROTECTED_TOOL_RESULTS_OVER_LIMIT;
23400
23118
  }
23401
-
23402
23119
  class EmergencyFailClosedError extends Error {
23403
23120
  code;
23404
23121
  constructor(message, options) {
@@ -23699,6 +23416,39 @@ function opencodeReasoningBudgetStatus(messages, budget, prefixBound, proseRatio
23699
23416
  };
23700
23417
  }
23701
23418
 
23419
+ // ../plugin/src/hooks/magic-context/strict-thinking-prefix.ts
23420
+ function servedPrefixMatches(args) {
23421
+ const heads = args.messages?.slice(0, 2);
23422
+ if (!heads || heads.length !== 2 || args.m0Text == null || args.m1Text == null)
23423
+ return false;
23424
+ const text = (message) => {
23425
+ if (!isRecord(message))
23426
+ return;
23427
+ if (typeof message.content === "string")
23428
+ return message.content;
23429
+ const parts = Array.isArray(message.parts) ? message.parts : message.content;
23430
+ if (!Array.isArray(parts))
23431
+ return;
23432
+ return parts.filter(isRecord).filter((part) => part.type === "text").map((part) => String(part.text ?? "")).join("");
23433
+ };
23434
+ if (text(heads[0]) !== args.m0Text || text(heads[1]) !== args.m1Text)
23435
+ return false;
23436
+ const head = heads[0];
23437
+ if (!isRecord(head))
23438
+ return false;
23439
+ const parts = Array.isArray(head.parts) ? head.parts : head.content;
23440
+ if (!Array.isArray(parts))
23441
+ return !args.muralDataUrl;
23442
+ const images = parts.filter(isRecord).filter((part) => part.type === "image" || part.type === "file");
23443
+ if (!args.muralDataUrl)
23444
+ return images.length === 0;
23445
+ if (images.length !== 1)
23446
+ return false;
23447
+ const image = images[0];
23448
+ const url = typeof image.url === "string" ? image.url : typeof image.data === "string" && typeof image.mimeType === "string" ? `data:${image.mimeType};base64,${image.data}` : undefined;
23449
+ return url === args.muralDataUrl;
23450
+ }
23451
+
23702
23452
  // ../plugin/src/hooks/magic-context/supersession-reclaim.ts
23703
23453
  var SUPERSESSION_RECENT_MESSAGE_WINDOW = 20;
23704
23454
  function recentSupersessionOwnerMessageIds(db, sessionId) {
@@ -24707,6 +24457,8 @@ function persistAutoSearchSkip(db, sessionId, messageId, reason = "timeout") {
24707
24457
  ok = outcome.ok && outcome.decision.decision === "no-hint";
24708
24458
  } catch {}
24709
24459
  assertTransformWrite2();
24460
+ if (!ok)
24461
+ rememberUnpublishedAutoSearchNoHint(sessionId, messageId, reason);
24710
24462
  const persistence = Promise.resolve(ok);
24711
24463
  messages.set(messageId, persistence);
24712
24464
  return persistence;
@@ -24895,7 +24647,36 @@ function queueAutoSearchRegistration(db, projectPath, register) {
24895
24647
  function persistAutoSearchDecision(db, sessionId, decision, startedAt) {
24896
24648
  if (performance.now() - startedAt >= AUTO_SEARCH_TIMEOUT_MS)
24897
24649
  return null;
24898
- return appendAutoSearchHintDecision(db, sessionId, decision);
24650
+ let outcome;
24651
+ try {
24652
+ outcome = appendAutoSearchHintDecision(db, sessionId, decision);
24653
+ } catch (error) {
24654
+ assertTransformWrite2();
24655
+ rememberUnpublishedAutoSearchNoHint(sessionId, decision.messageId, decision.decision === "no-hint" ? decision.reason : "error");
24656
+ if (!isTransientSqliteError2(error))
24657
+ throw error;
24658
+ log2(`[auto-search] decision save hit a busy store for session ${sessionId}; serving without a hint and retrying the save on a later pass: ${error instanceof Error ? error.message : String(error)}`);
24659
+ return { ok: false, kind: "storage-busy" };
24660
+ }
24661
+ if (!outcome.ok) {
24662
+ assertTransformWrite2();
24663
+ rememberUnpublishedAutoSearchNoHint(sessionId, decision.messageId, decision.decision === "no-hint" ? decision.reason : "error");
24664
+ } else if (outcome.decision.decision === "no-hint") {
24665
+ forgetUnpublishedAutoSearchNoHint(sessionId, decision.messageId);
24666
+ }
24667
+ return outcome;
24668
+ }
24669
+ function retryUnpublishedAutoSearchNoHint(db, sessionId, messageId, startedAt) {
24670
+ const reason = getUnpublishedAutoSearchNoHint(sessionId, messageId);
24671
+ if (reason === undefined)
24672
+ return false;
24673
+ try {
24674
+ persistAutoSearchDecision(db, sessionId, { messageId, decision: "no-hint", reason }, startedAt);
24675
+ } catch (error) {
24676
+ assertTransformWrite2();
24677
+ log2(`[auto-search] frozen omission save still failed for session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`);
24678
+ }
24679
+ return true;
24899
24680
  }
24900
24681
  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)) {
24901
24682
  return executeAutoSearchWorker(db, sessionId, projectPath, query, options, entry, "search");
@@ -25157,6 +24938,10 @@ async function runAutoSearchHintForPi(args) {
25157
24938
  args.passGuard?.assert();
25158
24939
  if (lifecycleSignal.aborted)
25159
24940
  return messages;
24941
+ if (retryUnpublishedAutoSearchNoHint(db, sessionId, userMsgId, startedAt)) {
24942
+ args.passGuard?.assert();
24943
+ return messages;
24944
+ }
25160
24945
  if (wasAutoSearchSkipped(db, sessionId, userMsgId))
25161
24946
  return messages;
25162
24947
  const existing = args.decisions ?? getAutoSearchHintDecisions(db, sessionId);
@@ -25243,7 +25028,10 @@ async function runAutoSearchHintForPi(args) {
25243
25028
  }, startedAt));
25244
25029
  } catch (error) {
25245
25030
  args.passGuard?.assert();
25246
- log2(`[auto-search] unified search failed for session ${sessionId} (will retry next pass): ${error instanceof Error ? error.message : String(error)}`);
25031
+ if (lifecycleSignal.aborted)
25032
+ return messages;
25033
+ log2(`[auto-search] unified search failed for session ${sessionId} (no-hint frozen for this turn): ${error instanceof Error ? error.message : String(error)}`);
25034
+ await writeNoHintAndReconcile("error");
25247
25035
  return messages;
25248
25036
  }
25249
25037
  args.passGuard?.assert();
@@ -25301,6 +25089,8 @@ ${hintText}`;
25301
25089
  }
25302
25090
  }, () => {
25303
25091
  args.passGuard?.assert();
25092
+ if (getUnpublishedAutoSearchNoHint(sessionId, userMsgId) !== undefined)
25093
+ return;
25304
25094
  if (wasAutoSearchSkipped(db, sessionId, userMsgId))
25305
25095
  return;
25306
25096
  const decision = getAutoSearchHintDecisions(db, sessionId).find((entry) => entry.messageId === userMsgId);
@@ -28714,17 +28504,6 @@ function isSyntheticMessage(message, index, input) {
28714
28504
  const role = typeof message.role === "string" ? message.role : "";
28715
28505
  return isPiSystemEntry(message) || role === "custom" || role === "compactionSummary";
28716
28506
  }
28717
- function countRealPiUserMessages(input) {
28718
- let count = 0;
28719
- for (let index = 0;index < input.messages.length; index += 1) {
28720
- const message = input.messages[index];
28721
- if (!isRecord4(message) || message.role !== "user")
28722
- continue;
28723
- if (!isSyntheticMessage(message, index, input))
28724
- count += 1;
28725
- }
28726
- return count;
28727
- }
28728
28507
  function imageContentAndTokens(part) {
28729
28508
  const data = typeof part.data === "string" ? part.data : "";
28730
28509
  const mimeType = typeof part.mimeType === "string" ? part.mimeType : "image/png";
@@ -28825,40 +28604,145 @@ function collectToolArcs(input) {
28825
28604
  }
28826
28605
  return { arcs, arcByPart, messageIds };
28827
28606
  }
28828
- function attributeTags(input, arcs, messageIds) {
28829
- const messageTags = new Map;
28830
- const tagsByNumber = new Map;
28831
- const exactToolTags = new Map;
28832
- const orphanTagsByCall = new Map;
28833
- const messageIndexById = new Map;
28834
- for (let index = 0;index < messageIds.length; index += 1) {
28835
- const id = input.stableId?.(input.messages[index], index);
28836
- if (id)
28837
- messageIndexById.set(id, index);
28607
+ var tagAttributions = new WeakMap;
28608
+ function tagAttribution(input) {
28609
+ const cacheable = input.tagCacheKey;
28610
+ let cached = cacheable ? tagAttributions.get(input.tagCacheKey) : undefined;
28611
+ const wasSimple = cached?.simple === true;
28612
+ const added = [];
28613
+ const removed = [];
28614
+ if (cached) {
28615
+ if (cached.tags === input.tags)
28616
+ return cached;
28617
+ let oldIndex = 0;
28618
+ let newIndex = 0;
28619
+ while (oldIndex < cached.tags.length || newIndex < input.tags.length) {
28620
+ const old = cached.tags[oldIndex];
28621
+ const next = input.tags[newIndex];
28622
+ if (old && (!next || old.tagNumber < next.tagNumber)) {
28623
+ removed.push(old);
28624
+ oldIndex++;
28625
+ } else if (next && (!old || next.tagNumber < old.tagNumber)) {
28626
+ added.push(next);
28627
+ newIndex++;
28628
+ } else {
28629
+ if (old !== next) {
28630
+ if (old)
28631
+ removed.push(old);
28632
+ if (next)
28633
+ added.push(next);
28634
+ }
28635
+ oldIndex++;
28636
+ newIndex++;
28637
+ }
28638
+ }
28639
+ if (!cached.simple && (removed.length || added.length))
28640
+ cached = undefined;
28641
+ }
28642
+ if (!cached) {
28643
+ removed.length = 0;
28644
+ added.length = 0;
28645
+ added.push(...input.tags);
28646
+ cached = {
28647
+ tags: input.tags,
28648
+ messageTags: new Map,
28649
+ tagsByNumber: new Map,
28650
+ exactToolTags: new Map,
28651
+ orphanTagsByCall: new Map,
28652
+ boundsByOwner: new Map,
28653
+ rowsByOwner: new Map,
28654
+ simple: true
28655
+ };
28838
28656
  }
28839
- const boundsByMessageIndex = new Map;
28840
- for (const tag of input.tags) {
28841
- tagsByNumber.set(tag.tagNumber, tag);
28657
+ const dirtyOwners = new Set;
28658
+ for (const tag of removed) {
28659
+ cached.tagsByNumber.delete(tag.tagNumber);
28660
+ if (tag.type !== "tool")
28661
+ cached.messageTags.delete(tag.messageId);
28662
+ else if (tag.toolOwnerMessageId !== null)
28663
+ cached.exactToolTags.delete(`${tag.toolOwnerMessageId}\x00${tag.messageId}`);
28664
+ else {
28665
+ const rows = cached.orphanTagsByCall.get(tag.messageId)?.filter((row) => row !== tag) ?? [];
28666
+ if (rows.length)
28667
+ cached.orphanTagsByCall.set(tag.messageId, rows);
28668
+ else
28669
+ cached.orphanTagsByCall.delete(tag.messageId);
28670
+ }
28671
+ const owner = messageIdForTag(tag);
28672
+ if (owner !== null) {
28673
+ cached.rowsByOwner.get(owner)?.delete(tag);
28674
+ dirtyOwners.add(owner);
28675
+ }
28676
+ }
28677
+ for (const tag of added) {
28678
+ if (cached.tagsByNumber.has(tag.tagNumber))
28679
+ cached.simple = false;
28680
+ cached.tagsByNumber.set(tag.tagNumber, tag);
28842
28681
  if (tag.type === "tool") {
28843
28682
  if (tag.toolOwnerMessageId === null) {
28844
- const rows = orphanTagsByCall.get(tag.messageId) ?? [];
28683
+ const rows = cached.orphanTagsByCall.get(tag.messageId) ?? [];
28845
28684
  rows.push(tag);
28846
- orphanTagsByCall.set(tag.messageId, rows);
28685
+ cached.orphanTagsByCall.set(tag.messageId, rows);
28847
28686
  } else {
28848
- exactToolTags.set(`${tag.toolOwnerMessageId}\x00${tag.messageId}`, tag);
28687
+ const key = `${tag.toolOwnerMessageId}\x00${tag.messageId}`;
28688
+ if (cached.exactToolTags.has(key))
28689
+ cached.simple = false;
28690
+ cached.exactToolTags.set(key, tag);
28849
28691
  }
28850
28692
  } else {
28851
- messageTags.set(tag.messageId, tag);
28693
+ if (cached.messageTags.has(tag.messageId))
28694
+ cached.simple = false;
28695
+ cached.messageTags.set(tag.messageId, tag);
28852
28696
  }
28853
- const ownerId = messageIdForTag(tag);
28854
- const messageIndex = ownerId === null ? undefined : messageIndexById.get(ownerId);
28855
- if (messageIndex === undefined)
28697
+ const owner = messageIdForTag(tag);
28698
+ if (owner === null)
28856
28699
  continue;
28857
- const current = boundsByMessageIndex.get(messageIndex);
28858
- boundsByMessageIndex.set(messageIndex, {
28859
- min: current ? Math.min(current.min, tag.tagNumber) : tag.tagNumber,
28860
- max: current ? Math.max(current.max, tag.tagNumber) : tag.tagNumber
28861
- });
28700
+ const rows = cached.rowsByOwner.get(owner) ?? new Set;
28701
+ rows.add(tag);
28702
+ cached.rowsByOwner.set(owner, rows);
28703
+ dirtyOwners.add(owner);
28704
+ }
28705
+ for (const owner of dirtyOwners) {
28706
+ const rows = cached.rowsByOwner.get(owner);
28707
+ if (!rows?.size) {
28708
+ cached.rowsByOwner.delete(owner);
28709
+ cached.boundsByOwner.delete(owner);
28710
+ continue;
28711
+ }
28712
+ let min = Infinity;
28713
+ let max = -Infinity;
28714
+ for (const row of rows) {
28715
+ min = Math.min(min, row.tagNumber);
28716
+ max = Math.max(max, row.tagNumber);
28717
+ }
28718
+ cached.boundsByOwner.set(owner, { min, max });
28719
+ }
28720
+ if (wasSimple && !cached.simple)
28721
+ cached = tagAttribution({ ...input, tagCacheKey: undefined });
28722
+ cached.tags = input.tags;
28723
+ if (cacheable)
28724
+ tagAttributions.set(input.tagCacheKey, cached);
28725
+ return cached;
28726
+ }
28727
+ function attributeTags(input, arcs, messageIds) {
28728
+ const {
28729
+ messageTags,
28730
+ tagsByNumber,
28731
+ exactToolTags,
28732
+ orphanTagsByCall,
28733
+ boundsByOwner
28734
+ } = tagAttribution(input);
28735
+ const messageIndexById = new Map;
28736
+ for (let index = 0;index < messageIds.length; index += 1) {
28737
+ const id = input.stableId?.(input.messages[index], index);
28738
+ if (id)
28739
+ messageIndexById.set(id, index);
28740
+ }
28741
+ const boundsByMessageIndex = new Map;
28742
+ for (const [ownerId, index] of messageIndexById) {
28743
+ const bounds = boundsByOwner.get(ownerId);
28744
+ if (bounds)
28745
+ boundsByMessageIndex.set(index, bounds);
28862
28746
  }
28863
28747
  const orphanCandidates = new Map;
28864
28748
  for (const arc of arcs) {
@@ -28963,6 +28847,7 @@ function measurePiTailHygiene(input) {
28963
28847
  const { messageTags, tagsByNumber } = attributeTags(input, arcs, messageIds);
28964
28848
  const drafts = [];
28965
28849
  let newestMessagePartStart = 0;
28850
+ let realUserTurnCount = 0;
28966
28851
  for (let messageIndex = 0;messageIndex < input.messages.length; messageIndex += 1) {
28967
28852
  const raw = input.messages[messageIndex];
28968
28853
  if (messageIndex === input.messages.length - 1)
@@ -28977,6 +28862,8 @@ function measurePiTailHygiene(input) {
28977
28862
  continue;
28978
28863
  }
28979
28864
  const role = raw.role;
28865
+ if (role === "user")
28866
+ realUserTurnCount++;
28980
28867
  if (role !== "user" && role !== "assistant" && role !== "toolResult") {
28981
28868
  drafts.push(excludedDraft(`${messageKey}\x00excluded-role`, raw));
28982
28869
  continue;
@@ -29092,7 +28979,10 @@ function measurePiTailHygiene(input) {
29092
28979
  drafts.push(excludedDraft(`${key}\x00excluded`, part));
29093
28980
  }
29094
28981
  }
29095
- return finalizeParts(drafts, input.pendingDropTagNumbers ?? new Set, input.protectedTagNumbers, newestMessagePartStart, input.protectedToolTags);
28982
+ return {
28983
+ ...finalizeParts(drafts, input.pendingDropTagNumbers ?? new Set, input.protectedTagNumbers, newestMessagePartStart, input.protectedToolTags),
28984
+ realUserTurnCount
28985
+ };
29096
28986
  }
29097
28987
  function refreshPiTailHygieneBaseline(input) {
29098
28988
  const protectedToolsPolicy = adoptedProtectedToolsPolicy(input.protectedTools, input.previous?.protectedToolsPolicy, input.cacheBusting, !!input.previous);
@@ -29134,7 +29024,8 @@ function refreshPiTailHygieneBaseline(input) {
29134
29024
  generationInvalidated: false,
29135
29025
  contentSignature: measured.contentSignature,
29136
29026
  channel1PostReduceGrace: input.previous?.channel1PostReduceGrace,
29137
- lastPrefixMismatch: mismatch
29027
+ lastPrefixMismatch: mismatch,
29028
+ realUserTurnCount: rawMeasured.realUserTurnCount
29138
29029
  });
29139
29030
  if (input.cacheBusting || !input.previous)
29140
29031
  return refrozen();
@@ -29157,7 +29048,8 @@ function refreshPiTailHygieneBaseline(input) {
29157
29048
  evaluable: true,
29158
29049
  generationInvalidated: false,
29159
29050
  contentSignature: measured.contentSignature,
29160
- lastPrefixMismatch: undefined
29051
+ lastPrefixMismatch: undefined,
29052
+ realUserTurnCount: rawMeasured.realUserTurnCount
29161
29053
  };
29162
29054
  }
29163
29055
  function effectivePiTailHygiene(baseline) {
@@ -29399,53 +29291,100 @@ function createPiTagSnapshotReader(db) {
29399
29291
  db.exec(`
29400
29292
  CREATE TEMP TABLE IF NOT EXISTS pi_tag_revision (revision INTEGER NOT NULL);
29401
29293
  INSERT INTO pi_tag_revision SELECT 0 WHERE NOT EXISTS (SELECT 1 FROM pi_tag_revision);
29294
+ CREATE TEMP TABLE IF NOT EXISTS pi_tag_session_revision (session_id TEXT PRIMARY KEY, revision INTEGER NOT NULL);
29402
29295
  CREATE TEMP TABLE IF NOT EXISTS pi_tag_watch (session_id TEXT PRIMARY KEY);
29403
29296
  CREATE TEMP TABLE IF NOT EXISTS pi_tag_changes (session_id TEXT, tag_number INTEGER, PRIMARY KEY (session_id, tag_number));
29297
+ CREATE TEMP TABLE IF NOT EXISTS pi_tag_version_own (session_id TEXT PRIMARY KEY, count INTEGER NOT NULL);
29298
+ DROP TRIGGER IF EXISTS temp.pi_tag_version_ai;
29299
+ DROP TRIGGER IF EXISTS temp.pi_tag_version_ad;
29300
+ DROP TRIGGER IF EXISTS temp.pi_tag_version_au;
29404
29301
  DROP TRIGGER IF EXISTS temp.pi_tag_insert;
29405
29302
  DROP TRIGGER IF EXISTS temp.pi_tag_update;
29406
29303
  DROP TRIGGER IF EXISTS temp.pi_tag_delete;
29304
+ DROP TRIGGER IF EXISTS temp.pi_tag_insert_conflicts;
29305
+ CREATE TEMP TRIGGER pi_tag_insert_conflicts BEFORE INSERT ON main.tags BEGIN
29306
+ UPDATE pi_tag_revision SET revision = revision + (SELECT COUNT(*) FROM (${TAG_INSERT_CONFLICT_ROWS}));
29307
+ INSERT INTO pi_tag_session_revision
29308
+ SELECT session_id, COUNT(*) FROM (${TAG_INSERT_CONFLICT_ROWS}) GROUP BY session_id
29309
+ ON CONFLICT(session_id) DO UPDATE SET revision = revision + excluded.revision;
29310
+ INSERT INTO pi_tag_changes
29311
+ SELECT session_id, tag_number FROM (${TAG_INSERT_CONFLICT_ROWS}) AS conflicts
29312
+ WHERE EXISTS (SELECT 1 FROM pi_tag_watch WHERE session_id = conflicts.session_id)
29313
+ AND NOT EXISTS (SELECT 1 FROM pi_tag_changes WHERE session_id = conflicts.session_id AND tag_number = conflicts.tag_number)
29314
+ GROUP BY session_id, tag_number;
29315
+ END;
29407
29316
  CREATE TEMP TRIGGER IF NOT EXISTS pi_tag_insert AFTER INSERT ON main.tags BEGIN
29408
29317
  UPDATE pi_tag_revision SET revision = revision + 1;
29318
+ INSERT INTO pi_tag_session_revision VALUES (NEW.session_id, 1) ON CONFLICT(session_id) DO UPDATE SET revision = revision + 1;
29409
29319
  INSERT INTO pi_tag_changes SELECT NEW.session_id, NEW.tag_number WHERE EXISTS (SELECT 1 FROM pi_tag_watch WHERE session_id = NEW.session_id) AND NOT EXISTS (SELECT 1 FROM pi_tag_changes WHERE session_id = NEW.session_id AND tag_number = NEW.tag_number);
29410
29320
  END;
29411
29321
  CREATE TEMP TRIGGER IF NOT EXISTS pi_tag_update AFTER UPDATE ON main.tags BEGIN
29412
29322
  UPDATE pi_tag_revision SET revision = revision + 1;
29323
+ INSERT INTO pi_tag_session_revision VALUES (OLD.session_id, 1) ON CONFLICT(session_id) DO UPDATE SET revision = revision + 1;
29324
+ INSERT INTO pi_tag_session_revision SELECT NEW.session_id, 1 WHERE NEW.session_id IS NOT OLD.session_id ON CONFLICT(session_id) DO UPDATE SET revision = revision + 1;
29413
29325
  INSERT INTO pi_tag_changes SELECT OLD.session_id, OLD.tag_number WHERE EXISTS (SELECT 1 FROM pi_tag_watch WHERE session_id = OLD.session_id) AND NOT EXISTS (SELECT 1 FROM pi_tag_changes WHERE session_id = OLD.session_id AND tag_number = OLD.tag_number);
29414
29326
  INSERT INTO pi_tag_changes SELECT NEW.session_id, NEW.tag_number WHERE EXISTS (SELECT 1 FROM pi_tag_watch WHERE session_id = NEW.session_id) AND NOT EXISTS (SELECT 1 FROM pi_tag_changes WHERE session_id = NEW.session_id AND tag_number = NEW.tag_number);
29415
29327
  END;
29416
29328
  CREATE TEMP TRIGGER IF NOT EXISTS pi_tag_delete AFTER DELETE ON main.tags BEGIN
29417
29329
  UPDATE pi_tag_revision SET revision = revision + 1;
29330
+ INSERT INTO pi_tag_session_revision VALUES (OLD.session_id, 1) ON CONFLICT(session_id) DO UPDATE SET revision = revision + 1;
29418
29331
  INSERT INTO pi_tag_changes SELECT OLD.session_id, OLD.tag_number WHERE EXISTS (SELECT 1 FROM pi_tag_watch WHERE session_id = OLD.session_id) AND NOT EXISTS (SELECT 1 FROM pi_tag_changes WHERE session_id = OLD.session_id AND tag_number = OLD.tag_number);
29419
29332
  END;
29333
+ CREATE TEMP TRIGGER pi_tag_version_ai AFTER INSERT ON main.tags BEGIN
29334
+ INSERT INTO pi_tag_version_own VALUES (NEW.session_id, 1) ON CONFLICT(session_id) DO UPDATE SET count = count + 1;
29335
+ END;
29336
+ CREATE TEMP TRIGGER pi_tag_version_ad AFTER DELETE ON main.tags BEGIN
29337
+ INSERT INTO pi_tag_version_own VALUES (OLD.session_id, 1) ON CONFLICT(session_id) DO UPDATE SET count = count + 1;
29338
+ END;
29339
+ CREATE TEMP TRIGGER pi_tag_version_au
29340
+ AFTER UPDATE OF session_id, message_id, tag_number, type, tool_owner_message_id, status ON main.tags BEGIN
29341
+ INSERT INTO pi_tag_version_own VALUES (OLD.session_id, 1) ON CONFLICT(session_id) DO UPDATE SET count = count + 1;
29342
+ INSERT INTO pi_tag_version_own SELECT NEW.session_id, 1 WHERE NEW.session_id != OLD.session_id
29343
+ ON CONFLICT(session_id) DO UPDATE SET count = count + 1;
29344
+ END;
29420
29345
  `);
29421
- const revision = db.prepare("SELECT revision FROM pi_tag_revision");
29422
- const dataVersion = db.prepare("PRAGMA main.data_version");
29423
- revisionReaders.set(db, () => {
29346
+ const versionStamp = db.prepare(`SELECT
29347
+ COALESCE((SELECT tags_version FROM session_meta WHERE session_id = ?), 0) AS durable,
29348
+ COALESCE((SELECT count FROM pi_tag_version_own WHERE session_id = ?), 0) AS own`);
29349
+ const revision = db.prepare(`WITH stamp AS (SELECT
29350
+ COALESCE((SELECT revision FROM pi_tag_session_revision WHERE session_id = ?), 0) AS own,
29351
+ COALESCE((SELECT value FROM schema_migrations_meta WHERE key = ?), '0') AS total)
29352
+ SELECT CAST(own AS TEXT) AS local,
29353
+ (${tagRevisionIncarnationSql("total")}) || ':' || CAST(${tagRevisionCountSql("total")} - own AS TEXT) AS foreign_revision FROM stamp`);
29354
+ revisionReaders.set(db, (sessionId) => {
29424
29355
  const transaction = db;
29425
29356
  if (transaction.inTransaction || transaction.isTransaction)
29426
29357
  return null;
29427
- return [
29428
- revision.get().revision,
29429
- dataVersion.get().data_version
29430
- ];
29358
+ const row = revision.get(sessionId, tagRevisionKey(sessionId));
29359
+ return [row.local, row.foreign_revision];
29431
29360
  });
29432
29361
  const changed = db.prepare("SELECT tag_number FROM pi_tag_changes WHERE session_id = ? ORDER BY tag_number");
29433
29362
  const clearChanged = db.prepare("DELETE FROM pi_tag_changes WHERE session_id = ?");
29434
29363
  const watch = db.prepare("INSERT OR IGNORE INTO pi_tag_watch VALUES (?)");
29435
29364
  const unwatch = db.prepare("DELETE FROM pi_tag_watch WHERE session_id = ?");
29436
29365
  const cache = new Map;
29437
- const read = (sessionId) => {
29366
+ const fallbackRows = db.prepare(`SELECT ${FALLBACK_ROW_COLUMNS} FROM tags WHERE session_id = ? AND ${FALLBACK_ROW_PREDICATE}`);
29367
+ const fallbackRowsByNumbers = db.prepare(`SELECT ${FALLBACK_ROW_COLUMNS} FROM tags WHERE session_id = ?
29368
+ AND tag_number IN (SELECT value FROM json_each(?)) AND ${FALLBACK_ROW_PREDICATE}`);
29369
+ const sync = (sessionId) => {
29438
29370
  const transaction = db;
29439
29371
  if (transaction.inTransaction || transaction.isTransaction)
29440
- return getTagsBySession2(db, sessionId);
29441
- const local = revision.get().revision;
29442
- const external = dataVersion.get().data_version;
29372
+ return null;
29373
+ const [local, external] = revisionReaders.get(db)?.(sessionId) ?? [
29374
+ "0",
29375
+ "0:0"
29376
+ ];
29377
+ const stamp = versionStamp.get(sessionId, sessionId);
29443
29378
  let snapshot = cache.get(sessionId);
29444
- if (!snapshot || snapshot.dataVersion !== external) {
29379
+ if (!snapshot || snapshot.foreignRevision !== external) {
29380
+ const tags = snapshot ? reuseUnchangedRows(snapshot.tags, getTagsBySession2(db, sessionId)) : getTagsBySession2(db, sessionId);
29445
29381
  snapshot = {
29446
29382
  revision: local,
29447
- dataVersion: external,
29448
- tags: getTagsBySession2(db, sessionId)
29383
+ foreignRevision: external,
29384
+ tags,
29385
+ active: tags.filter(isActive),
29386
+ durableVersion: stamp.durable,
29387
+ ownVersion: stamp.own
29449
29388
  };
29450
29389
  if (cache.size >= 100 && !cache.has(sessionId)) {
29451
29390
  const oldest = cache.keys().next().value;
@@ -29456,82 +29395,140 @@ function createPiTagSnapshotReader(db) {
29456
29395
  }
29457
29396
  }
29458
29397
  cache.set(sessionId, snapshot);
29398
+ for (const tag of snapshot.tags)
29399
+ Object.freeze(tag);
29459
29400
  watch.run(sessionId);
29460
29401
  } else if (snapshot.revision !== local) {
29461
29402
  const numbers = changed.all(sessionId).map((row) => row.tag_number);
29462
29403
  if (numbers.length > 0) {
29463
29404
  const dirty = new Set(numbers);
29464
29405
  const replacements = getTagsByNumbers2(db, sessionId, numbers);
29465
- const unchanged = snapshot.tags.filter((tag) => !dirty.has(tag.tagNumber));
29466
- const merged = [];
29467
- let index = 0;
29468
- for (const tag of unchanged) {
29469
- while (index < replacements.length) {
29470
- const next = replacements[index];
29471
- if (!next)
29472
- break;
29473
- if (next.tagNumber > tag.tagNumber || next.tagNumber === tag.tagNumber && (next.id ?? 0) > (tag.id ?? 0))
29474
- break;
29475
- merged.push(next);
29476
- index += 1;
29477
- }
29478
- merged.push(tag);
29406
+ for (const tag of replacements)
29407
+ Object.freeze(tag);
29408
+ snapshot.tags = mergeChangedRows(snapshot.tags, dirty, replacements);
29409
+ snapshot.active = mergeChangedRows(snapshot.active, dirty, replacements.filter(isActive));
29410
+ if (!numbers.every(Number.isSafeInteger)) {
29411
+ snapshot.fallback = undefined;
29412
+ } else if (snapshot.fallback) {
29413
+ snapshot.fallback = [
29414
+ ...snapshot.fallback.filter((row) => !dirty.has(row.tag_number)),
29415
+ ...fallbackRowsByNumbers.all(sessionId, JSON.stringify(numbers))
29416
+ ];
29479
29417
  }
29480
- merged.push(...replacements.slice(index));
29481
- snapshot.tags = merged;
29482
29418
  }
29483
29419
  snapshot.revision = local;
29484
29420
  }
29421
+ if (stamp.durable - snapshot.durableVersion !== stamp.own - snapshot.ownVersion) {
29422
+ snapshot.fallback = undefined;
29423
+ }
29424
+ snapshot.durableVersion = stamp.durable;
29425
+ snapshot.ownVersion = stamp.own;
29485
29426
  clearChanged.run(sessionId);
29486
- return snapshot.tags.map((tag) => ({ ...tag }));
29427
+ return snapshot;
29487
29428
  };
29429
+ const read = (sessionId, borrowed = false) => {
29430
+ const snapshot = sync(sessionId);
29431
+ if (!snapshot)
29432
+ return getTagsBySession2(db, sessionId);
29433
+ return borrowed ? snapshot.tags : snapshot.tags.map((tag) => ({ ...tag }));
29434
+ };
29435
+ entryReaders.set(db, sync);
29436
+ fallbackReaders.set(db, (sessionId) => {
29437
+ const snapshot = sync(sessionId);
29438
+ if (!snapshot)
29439
+ return null;
29440
+ snapshot.fallback ??= fallbackRows.all(sessionId);
29441
+ return snapshot.fallback;
29442
+ });
29488
29443
  readers.set(db, read);
29489
29444
  return read;
29490
29445
  }
29446
+ var FALLBACK_ROW_COLUMNS = "id, tag_number, type, message_id, tool_owner_message_id, entry_fingerprint";
29447
+ var FALLBACK_ROW_PREDICATE = `((type = 'message' AND message_id LIKE 'pi-msg-%')
29448
+ OR (type = 'tool' AND tool_owner_message_id LIKE 'pi-msg-%'))`;
29449
+ var isActive = (tag) => tag.status === "active";
29450
+ function sameRow(left, right, keys) {
29451
+ for (const key of keys)
29452
+ if (!Object.is(left[key], right[key]))
29453
+ return false;
29454
+ return true;
29455
+ }
29456
+ function reuseUnchangedRows(cached, fresh) {
29457
+ const first = fresh[0];
29458
+ if (!first)
29459
+ return fresh;
29460
+ const keys = Object.keys(first);
29461
+ let index = 0;
29462
+ for (let position = 0;position < fresh.length; position += 1) {
29463
+ const row = fresh[position];
29464
+ if (!row)
29465
+ continue;
29466
+ while (index < cached.length) {
29467
+ const old = cached[index];
29468
+ if (!old || old.tagNumber > row.tagNumber || old.tagNumber === row.tagNumber && (old.id ?? 0) >= (row.id ?? 0))
29469
+ break;
29470
+ index += 1;
29471
+ }
29472
+ const old = cached[index];
29473
+ if (old && old.id === row.id && sameRow(old, row, keys))
29474
+ fresh[position] = old;
29475
+ }
29476
+ return fresh;
29477
+ }
29478
+ function mergeChangedRows(rows, dirty, replacements) {
29479
+ const highest = rows.at(-1)?.tagNumber ?? -1;
29480
+ let appendOnly = true;
29481
+ for (const number of dirty) {
29482
+ if (number <= highest) {
29483
+ appendOnly = false;
29484
+ break;
29485
+ }
29486
+ }
29487
+ if (appendOnly)
29488
+ return [...rows, ...replacements];
29489
+ const unchanged = rows.filter((tag) => !dirty.has(tag.tagNumber));
29490
+ const merged = [];
29491
+ let index = 0;
29492
+ for (const tag of unchanged) {
29493
+ while (index < replacements.length) {
29494
+ const next = replacements[index];
29495
+ if (!next)
29496
+ break;
29497
+ if (next.tagNumber > tag.tagNumber || next.tagNumber === tag.tagNumber && (next.id ?? 0) > (tag.id ?? 0))
29498
+ break;
29499
+ merged.push(next);
29500
+ index += 1;
29501
+ }
29502
+ merged.push(tag);
29503
+ }
29504
+ merged.push(...replacements.slice(index));
29505
+ return merged;
29506
+ }
29507
+ var entryReaders = new WeakMap;
29508
+ var fallbackReaders = new WeakMap;
29491
29509
  var readers = new WeakMap;
29492
29510
  var revisionReaders = new WeakMap;
29493
- function readPiTagSnapshotRevision(db) {
29494
- if (!readers.has(db))
29495
- createPiTagSnapshotReader(db);
29496
- return revisionReaders.get(db)?.() ?? null;
29511
+ function getPiTagSnapshotView(db, sessionId) {
29512
+ return createPiTagSnapshotReader(db)(sessionId, true);
29497
29513
  }
29498
- function getPiTagSnapshot(db, sessionId) {
29499
- let reader = readers.get(db);
29500
- if (!reader) {
29501
- reader = createPiTagSnapshotReader(db);
29502
- readers.set(db, reader);
29503
- }
29504
- return reader(sessionId);
29514
+ function getPiActiveTagSnapshotView(db, sessionId, passTags) {
29515
+ createPiTagSnapshotReader(db);
29516
+ const snapshot = entryReaders.get(db)?.(sessionId);
29517
+ if (snapshot && (!passTags || snapshot.tags === passTags))
29518
+ return snapshot.active.slice();
29519
+ return (passTags ?? getTagsBySession2(db, sessionId)).filter(isActive);
29520
+ }
29521
+ function readPiFallbackTagRows(db, sessionId) {
29522
+ createPiTagSnapshotReader(db);
29523
+ return fallbackReaders.get(db)?.(sessionId) ?? null;
29505
29524
  }
29506
29525
 
29507
29526
  // src/fallback-tag-probes-pi.ts
29508
- var probes = new WeakMap;
29509
29527
  function probe(db, sessionId, kind) {
29510
- const revision = readPiTagSnapshotRevision(db);
29511
- const read = () => kind === "message" ? hasPiFallbackMessageTags2(db, sessionId) : hasPiFallbackToolOwnerTags2(db, sessionId);
29512
- if (!revision)
29513
- return read();
29514
- let sessions = probes.get(db);
29515
- if (!sessions) {
29516
- sessions = new Map;
29517
- probes.set(db, sessions);
29518
- }
29519
- let snapshot = sessions.get(sessionId);
29520
- if (!snapshot || snapshot.local !== revision[0] || snapshot.external !== revision[1]) {
29521
- snapshot = { local: revision[0], external: revision[1] };
29522
- if (sessions.size >= 100 && !sessions.has(sessionId)) {
29523
- const oldest = sessions.keys().next().value;
29524
- if (oldest !== undefined)
29525
- sessions.delete(oldest);
29526
- }
29527
- sessions.set(sessionId, snapshot);
29528
- }
29529
- const cached = snapshot[kind];
29530
- if (cached !== undefined)
29531
- return cached;
29532
- const result = read();
29533
- snapshot[kind] = result;
29534
- return result;
29528
+ const rows = readPiFallbackTagRows(db, sessionId);
29529
+ if (!rows)
29530
+ return kind === "message" ? hasPiFallbackMessageTags2(db, sessionId) : hasPiFallbackToolOwnerTags2(db, sessionId);
29531
+ return rows.some((row) => row.type === kind);
29535
29532
  }
29536
29533
  function hasPiFallbackMessageTags(db, sessionId) {
29537
29534
  return probe(db, sessionId, "message");
@@ -29951,7 +29948,7 @@ function readPiHistorianTail(db, sessionId, provider) {
29951
29948
  function resolveStableId(msg, index, entryIds) {
29952
29949
  return resolvePiStableId(msg, index, entryIds) ?? "";
29953
29950
  }
29954
- function trimPiMessagesToBoundary(piMessages, entryIds, cutoffMessageId, trimMutableEntryIds = false, sessionId, preserveBoundary = false) {
29951
+ function trimPiMessagesToBoundary(piMessages, entryIds, cutoffMessageId, trimMutableEntryIds = false, sessionId, preserveBoundary = false, retireDecisions) {
29955
29952
  if (cutoffMessageId.length === 0 || preserveBoundary)
29956
29953
  return 0;
29957
29954
  const effectiveCutoffId = cutoffMessageId.startsWith(SYNTH_USER_ID_PREFIX) ? cutoffMessageId.slice(SYNTH_USER_ID_PREFIX.length) : cutoffMessageId;
@@ -30022,6 +30019,16 @@ function trimPiMessagesToBoundary(piMessages, entryIds, cutoffMessageId, trimMut
30022
30019
  }
30023
30020
  }
30024
30021
  }
30022
+ const retiredIds = new Set;
30023
+ if (retireDecisions) {
30024
+ for (const index of remove) {
30025
+ if (index > cutoffIndex)
30026
+ continue;
30027
+ const id = resolveStableId(piMessages[index], index, entryIds);
30028
+ if (id)
30029
+ retiredIds.add(id);
30030
+ }
30031
+ }
30025
30032
  const kept = piMessages.filter((_, index) => !remove.has(index));
30026
30033
  const removed = piMessages.length - kept.length;
30027
30034
  piMessages.splice(0, piMessages.length, ...kept);
@@ -30029,6 +30036,7 @@ function trimPiMessagesToBoundary(piMessages, entryIds, cutoffMessageId, trimMut
30029
30036
  const keptIds = entryIds.filter((_, index) => !remove.has(index));
30030
30037
  entryIds.splice(0, entryIds.length, ...keptIds);
30031
30038
  }
30039
+ retireDecisions?.(retiredIds);
30032
30040
  return removed;
30033
30041
  }
30034
30042
  function toolPairKey(callId, assistantIndex) {
@@ -30251,7 +30259,7 @@ function trimPiMessagesToCachedBoundary(db, sessionId, piMessages, entryIds, pas
30251
30259
  const boundaryIsLive = compartments.some((compartment) => compartment.endMessageId === boundary);
30252
30260
  if (!boundaryIsLive)
30253
30261
  return 0;
30254
- return trimPiMessagesToBoundary(piMessages, entryIds, boundary, true, sessionId, compartments.some((c) => c.endMessageId === boundary && c.endBlockIndex != null));
30262
+ return trimPiMessagesToBoundary(piMessages, entryIds, boundary, true, sessionId, compartments.some((c) => c.endMessageId === boundary && c.endBlockIndex != null), (ids) => queueAutoSearchHintDecisionRetirement(db, sessionId, ids));
30255
30263
  }
30256
30264
  function setCachedBoundary(db, sessionId, boundary) {
30257
30265
  db.prepare("UPDATE session_meta SET cached_m0_last_baseline_end_message_id = ? WHERE session_id = ?").run(boundary, sessionId);
@@ -30875,6 +30883,8 @@ function cachedPiRowMatchesSnapshot(args) {
30875
30883
  return bufferEqualsNullable(args.row.cached_m0_bytes, args.m0Bytes) && rowMarkers.projectMemoryEpoch === args.markers.projectMemoryEpoch && rowMarkers.projectUserProfileVersion === args.markers.projectUserProfileVersion && rowMarkers.maxCompartmentSeq === args.markers.maxCompartmentSeq && rowMarkers.maxMemoryId === args.markers.maxMemoryId && rowMarkers.maxMutationId === args.markers.maxMutationId && rowMarkers.maxMemoryMutationId === args.markers.maxMemoryMutationId && rowMarkers.materializedAt === args.markers.materializedAt && rowMarkers.sessionFactsVersion === args.markers.sessionFactsVersion && (rowMarkers.upgradeState ?? null) === (args.markers.upgradeState ?? null) && rowMarkers.compartmentRenderEpoch === args.markers.compartmentRenderEpoch && (rowMarkers.systemHash ?? "") === (args.markers.systemHash ?? "") && piModelRefToCanonical(rowMarkers.modelKey ?? "") === piModelRefToCanonical(args.markers.modelKey ?? "") && (rowMarkers.projectIdentity ?? null) === (args.markers.projectIdentity ?? null) && (rowMarkers.workspaceFingerprint ?? null) === (args.markers.workspaceFingerprint ?? null);
30876
30884
  }
30877
30885
  function adoptCachedPiProjectIdentity(db, state) {
30886
+ if (state.projectIdentity == null)
30887
+ return;
30878
30888
  db.prepare("UPDATE session_meta SET cached_m0_project_identity = ? WHERE session_id = ? AND cached_m0_project_identity IS NULL").run(state.projectIdentity, state.sessionId);
30879
30889
  }
30880
30890
  function decodeCachedM1(row, sessionId) {
@@ -31004,7 +31014,7 @@ function prependM0M1Messages(piMessages, m0, m1, mural, timestampHint) {
31004
31014
  });
31005
31015
  return insertionIndex;
31006
31016
  }
31007
- function replayCompletePiPrefix(state, row, compartments, messages, entryIds, reason) {
31017
+ function replayCompletePiPrefix(db, state, row, compartments, messages, entryIds, reason) {
31008
31018
  let m0 = decodeCachedM0(row.cached_m0_bytes) ?? "";
31009
31019
  const m1 = decodeCachedM1(row, state.sessionId);
31010
31020
  rememberPiMuralPayload(state.sessionId, row.cached_m0_mural_data_url, row.cached_m0_mural_hash);
@@ -31012,7 +31022,7 @@ function replayCompletePiPrefix(state, row, compartments, messages, entryIds, re
31012
31022
  if (!mural)
31013
31023
  m0 = stripMemoryMuralBlock(m0);
31014
31024
  const trimBoundaryId = row.cached_m0_last_baseline_end_message_id;
31015
- const skippedVisibleMessages = trimBoundaryId ? trimPiMessagesToBoundary(messages, entryIds, trimBoundaryId, false, state.sessionId, compartments.some((c) => c.endMessageId === trimBoundaryId && c.endBlockIndex != null)) : 0;
31025
+ const skippedVisibleMessages = trimBoundaryId ? trimPiMessagesToBoundary(messages, entryIds, trimBoundaryId, false, state.sessionId, compartments.some((c) => c.endMessageId === trimBoundaryId && c.endBlockIndex != null), (ids) => queueAutoSearchHintDecisionRetirement(db, state.sessionId, ids)) : 0;
31016
31026
  const head = [];
31017
31027
  prependM0M1Messages(head, m0, m1, mural, messages[0]?.timestamp);
31018
31028
  const insertionIndex = placePiInitialSystemAtHead(messages);
@@ -31045,13 +31055,13 @@ function prepareCachedM0M1PiReplay(state, db, rowOverride) {
31045
31055
  freezePrefixForPass: true,
31046
31056
  preparedPrefix: undefined
31047
31057
  };
31048
- replayCompletePiPrefix(snapshot, cached, [], [], undefined, "cache_hit");
31058
+ replayCompletePiPrefix(db, snapshot, cached, [], [], undefined, "cache_hit");
31049
31059
  return snapshot.preparedPrefix;
31050
31060
  }
31051
31061
  function injectM0M1Pi(state, db, piMessages, entryIds, recomputeM1ThisPass = false, passSnapshot) {
31052
31062
  if (state.preparedPrefix) {
31053
31063
  const prepared = state.preparedPrefix;
31054
- const skippedVisibleMessages = prepared.trimBoundaryId ? trimPiMessagesToBoundary(piMessages, entryIds, prepared.trimBoundaryId, false, state.sessionId, isPartialCompartmentEnd(db, state.sessionId, prepared.trimBoundaryId)) : 0;
31064
+ const skippedVisibleMessages = prepared.trimBoundaryId ? trimPiMessagesToBoundary(piMessages, entryIds, prepared.trimBoundaryId, false, state.sessionId, isPartialCompartmentEnd(db, state.sessionId, prepared.trimBoundaryId), (ids) => queueAutoSearchHintDecisionRetirement(db, state.sessionId, ids)) : 0;
31055
31065
  const head = structuredClone(prepared.messages);
31056
31066
  const timestamp = piMessages[0]?.timestamp;
31057
31067
  if (typeof timestamp === "number") {
@@ -31098,7 +31108,7 @@ function injectM0M1Pi(state, db, piMessages, entryIds, recomputeM1ThisPass = fal
31098
31108
  throw error;
31099
31109
  const cached = state.allowFreshContentionFallback ? null : snapshot.cachedRow;
31100
31110
  if (cached?.cached_m0_bytes && cached.cached_m1_bytes) {
31101
- return replayCompletePiPrefix(state, cached, currentCompartments, piMessages, entryIds, decision.reason);
31111
+ return replayCompletePiPrefix(db, state, cached, currentCompartments, piMessages, entryIds, decision.reason);
31102
31112
  }
31103
31113
  const fresh = renderFreshM0PiNonPersisted(state, db);
31104
31114
  m0 = fresh.m0;
@@ -31125,7 +31135,7 @@ function injectM0M1Pi(state, db, piMessages, entryIds, recomputeM1ThisPass = fal
31125
31135
  throw error;
31126
31136
  const cached = state.allowFreshContentionFallback ? null : snapshot.cachedRow;
31127
31137
  if (cached?.cached_m0_bytes && cached.cached_m1_bytes) {
31128
- return replayCompletePiPrefix(state, cached, currentCompartments, piMessages, entryIds, decision.reason);
31138
+ return replayCompletePiPrefix(db, state, cached, currentCompartments, piMessages, entryIds, decision.reason);
31129
31139
  }
31130
31140
  const fresh = renderFreshM0PiNonPersisted(state, db);
31131
31141
  m0 = fresh.m0;
@@ -31214,7 +31224,7 @@ function injectM0M1Pi(state, db, piMessages, entryIds, recomputeM1ThisPass = fal
31214
31224
  };
31215
31225
  }
31216
31226
  }
31217
- const skippedVisibleMessages = trimBoundaryId ? trimPiMessagesToBoundary(piMessages, entryIds, trimBoundaryId, false, state.sessionId, isPartialCompartmentEnd(db, state.sessionId, trimBoundaryId)) : 0;
31227
+ const skippedVisibleMessages = trimBoundaryId ? trimPiMessagesToBoundary(piMessages, entryIds, trimBoundaryId, false, state.sessionId, isPartialCompartmentEnd(db, state.sessionId, trimBoundaryId), (ids) => queueAutoSearchHintDecisionRetirement(db, state.sessionId, ids)) : 0;
31218
31228
  const muralWire = m0.includes("<memory-mural>") ? muralForWire(state.sessionId) : undefined;
31219
31229
  if (!muralWire)
31220
31230
  m0 = stripMemoryMuralBlock(m0);
@@ -31650,7 +31660,7 @@ class PiContextBudget {
31650
31660
  assertDecisionPublication = () => {
31651
31661
  if (!this.completed)
31652
31662
  this.assertOwner();
31653
- if (this.abandoned || this.elapsed() >= PI_CONTEXT_BUDGET.outcomeMs)
31663
+ if (this.abandoned || !this.completed && this.elapsed() >= PI_CONTEXT_BUDGET.outcomeMs)
31654
31664
  throw new PiContextDeadlineError(this.diagnostic());
31655
31665
  };
31656
31666
  diagnostic() {
@@ -31727,6 +31737,25 @@ function resolvePiUsableContextLimit(args) {
31727
31737
  // src/pi-context-refusal.ts
31728
31738
  import { Text as Text2 } from "@earendil-works/pi-tui";
31729
31739
 
31740
+ // src/omp-diagnostic-context.ts
31741
+ function isOmpDumpContextStack(stack) {
31742
+ if (!stack)
31743
+ return false;
31744
+ const frames = stack.replaceAll("\\", "/").split(`
31745
+ `);
31746
+ const hostFrame = (method) => frames.some((frame) => new RegExp(`^\\s*at (?:async )?(?:AgentSession\\.)?${method} \\(.*?/@oh-my-pi/pi-coding-agent/src/session/agent-session\\.ts:\\d+:\\d+\\)$`).test(frame));
31747
+ return hostFrame("#formatLlmRequestJson") && hostFrame("dumpLlmRequestToTmpDir");
31748
+ }
31749
+ function isOmpDumpContext() {
31750
+ const limit = Error.stackTraceLimit;
31751
+ try {
31752
+ Error.stackTraceLimit = Math.max(limit ?? 10, 64);
31753
+ return isOmpDumpContextStack(new Error().stack);
31754
+ } finally {
31755
+ Error.stackTraceLimit = limit;
31756
+ }
31757
+ }
31758
+
31730
31759
  // src/omp-request-kind.ts
31731
31760
  var SIDE_REMINDER = `<system-reminder>
31732
31761
  Ephemeral side-channel turn; reuses current conversation context.
@@ -31824,8 +31853,14 @@ function isOmpUnmanagedCaptureRequest(payload) {
31824
31853
  // src/pi-context-refusal.ts
31825
31854
  var ENTRY_TYPE = "magic-context-turn-refused";
31826
31855
  var RETRY_MESSAGE = "Magic Context could not safely prepare this turn; send your message again.";
31856
+ var MAX_UNSENDABLE_NONCES_PER_SESSION = 32;
31827
31857
 
31828
31858
  class PiContextSupersededError extends Error {
31859
+ origin;
31860
+ constructor(message, origin = "context ownership") {
31861
+ super(message);
31862
+ this.origin = origin;
31863
+ }
31829
31864
  }
31830
31865
  var receiptMaps = new Set;
31831
31866
  function clearPiContextReceipt(sessionId) {
@@ -31835,17 +31870,70 @@ function clearPiContextReceipt(sessionId) {
31835
31870
  function operationKey(ctx) {
31836
31871
  return ctx.sessionManager?.getSessionId?.() ?? ctx.sessionManager ?? "no-session";
31837
31872
  }
31873
+ function messageKey(message) {
31874
+ if (!message || typeof message !== "object")
31875
+ return;
31876
+ const { role, timestamp } = message;
31877
+ return typeof role === "string" && typeof timestamp === "number" ? `${role}:${timestamp}` : undefined;
31878
+ }
31879
+ function endBelongsToEarlierRun(operation, event) {
31880
+ if (!operation.pass || operation.dispatched)
31881
+ return false;
31882
+ const end = event;
31883
+ if (end?.willContinue === true)
31884
+ return true;
31885
+ if (!operation.inputKeys || !Array.isArray(end?.messages))
31886
+ return false;
31887
+ const ended = new Set;
31888
+ for (const message of end.messages) {
31889
+ const key = messageKey(message);
31890
+ if (key)
31891
+ ended.add(key);
31892
+ }
31893
+ if (ended.size === 0)
31894
+ return false;
31895
+ return operation.inputKeys.some((key) => !ended.has(key));
31896
+ }
31897
+ function inputMessageKeys(event) {
31898
+ try {
31899
+ const messages = event.messages;
31900
+ if (!Array.isArray(messages))
31901
+ return;
31902
+ const keys = [];
31903
+ for (const message of messages) {
31904
+ const key = messageKey(message);
31905
+ if (key)
31906
+ keys.push(key);
31907
+ }
31908
+ return keys;
31909
+ } catch {
31910
+ return;
31911
+ }
31912
+ }
31838
31913
  function registerPiGuardedContext(pi, handler, options = {}) {
31839
31914
  const operations = new Map;
31840
31915
  const sideSessions = new Map;
31841
31916
  receiptMaps.add(operations);
31842
31917
  const newOperation = () => ({
31918
+ dispatched: false,
31843
31919
  started: false,
31844
31920
  ready: false,
31845
31921
  refused: false,
31846
31922
  ended: false
31847
31923
  });
31924
+ const lifecycle = new Map;
31925
+ receiptMaps.add(lifecycle);
31926
+ const lifecycleCounts = (key) => {
31927
+ let counts = lifecycle.get(key);
31928
+ if (!counts) {
31929
+ counts = { starts: 0, ends: 0 };
31930
+ lifecycle.set(key, counts);
31931
+ }
31932
+ return counts;
31933
+ };
31848
31934
  const fenceEnabled = resolvePiHarnessKind() === "omp";
31935
+ const unsendableNonces = new Map;
31936
+ receiptMaps.add(unsendableNonces);
31849
31937
  const refuse = (ctx, budget, reason, isCurrent) => {
31850
31938
  if (!isCurrent()) {
31851
31939
  log2(`[magic-context][pi] DISCARDED CONTEXT RESULT ${budget.diagnostic()}`, reason);
@@ -31877,18 +31965,27 @@ function registerPiGuardedContext(pi, handler, options = {}) {
31877
31965
  if (fenceEnabled) {
31878
31966
  pi.on("agent_start", (_event, ctx) => {
31879
31967
  const key = operationKey(ctx);
31968
+ lifecycleCounts(key).starts++;
31880
31969
  const pending = operations.get(key);
31881
31970
  const active = pending && !pending.started && !pending.ended ? pending : newOperation();
31882
31971
  active.started = true;
31883
31972
  operations.set(key, active);
31884
31973
  });
31885
- pi.on("agent_end", (_event, ctx) => {
31974
+ pi.on("agent_end", (event, ctx) => {
31886
31975
  const key = operationKey(ctx);
31976
+ const lifecycleSeen = lifecycleCounts(key);
31977
+ lifecycleSeen.ends = Math.min(lifecycleSeen.ends + 1, lifecycleSeen.starts);
31887
31978
  const counts = sideSessions.get(key);
31888
31979
  if (counts)
31889
31980
  log2(`[magic-context][pi] OMP context counts session=${String(key)} side_contexts=${counts.sides} context_passes=${counts.contexts} dispatch_fence=${counts.sides ? "latched off" : "on"}`);
31890
31981
  const operation = operations.get(key);
31891
31982
  if (operation) {
31983
+ if (endBelongsToEarlierRun(operation, event)) {
31984
+ if (lifecycleSeen.starts <= lifecycleSeen.ends)
31985
+ operation.started = false;
31986
+ log2(`[magic-context][pi] agent_end attributed to an earlier run; keeping the latest context pass session=${String(key)} ${operation.pass?.diagnostic() ?? ""}`);
31987
+ return;
31988
+ }
31892
31989
  operation.ended = true;
31893
31990
  operation.ready = false;
31894
31991
  }
@@ -31897,11 +31994,21 @@ function registerPiGuardedContext(pi, handler, options = {}) {
31897
31994
  if (options.compactionOff?.(ctx))
31898
31995
  return;
31899
31996
  const payload = event.payload;
31900
- if (isOmpAdvisorRequest(payload))
31901
- return;
31902
31997
  const key = operationKey(ctx);
31903
31998
  if (sideSessions.get(key)?.sides)
31904
31999
  return;
32000
+ const nonces = unsendableNonces.get(key);
32001
+ if (nonces?.size) {
32002
+ const serializedPayload = JSON.stringify(payload);
32003
+ if (serializedPayload && [...nonces].some((nonce) => serializedPayload.includes(nonce))) {
32004
+ const budget = new PiContextBudget;
32005
+ budget.stage = "provider dispatch";
32006
+ refuse(ctx, budget, "superseded context fallback is not a managed request", () => true);
32007
+ return;
32008
+ }
32009
+ }
32010
+ if (isOmpAdvisorRequest(payload))
32011
+ return;
31905
32012
  let operation = operations.get(key);
31906
32013
  if (!operation) {
31907
32014
  operation = newOperation();
@@ -31911,25 +32018,32 @@ function registerPiGuardedContext(pi, handler, options = {}) {
31911
32018
  const isCurrent = () => operations.get(key) === active;
31912
32019
  const capture = isOmpUnmanagedCaptureRequest(payload);
31913
32020
  const budget = (capture ? undefined : active.pass) ?? new PiContextBudget;
32021
+ let refusalOrigin;
31914
32022
  try {
31915
32023
  if (capture)
31916
32024
  throw new Error("no context pass ran for OMP auto-learn capture; raw conversation refused");
31917
32025
  if (!active.pass)
31918
32026
  throw new Error("no context pass ran for this operation");
31919
- budget.assertOwner();
31920
- if (!budget.completed)
31921
- budget.assertOutcome();
31922
- if (!active.ready || active.refused || active.ended || budget.abandoned)
32027
+ if (!budget.completed && (budget.abandoned || budget.elapsed() >= PI_CONTEXT_BUDGET.outcomeMs))
32028
+ throw new PiContextDeadlineError(budget.diagnostic());
32029
+ if (active.ended) {
32030
+ refusalOrigin = "agent_end";
32031
+ throw new Error("the run that owned the latest context pass has ended; no managed result for this request");
32032
+ }
32033
+ if (!budget.completed || !active.ready || active.refused || budget.abandoned)
31923
32034
  throw new Error("context pass ran without a managed context receipt");
32035
+ active.dispatched = true;
31924
32036
  } catch (error) {
31925
32037
  active.refused = true;
31926
- budget.stage = "provider dispatch";
32038
+ budget.stage = refusalOrigin ? `provider dispatch (origin=${refusalOrigin}; context stage=${budget.stage})` : "provider dispatch";
31927
32039
  refuse(ctx, budget, error, isCurrent);
31928
32040
  }
31929
32041
  });
31930
32042
  }
31931
32043
  pi.registerEntryRenderer?.(ENTRY_TYPE, (entry) => new Text2(entry.data?.message ?? RETRY_MESSAGE, 0, 0));
31932
32044
  pi.on("context", async (event, ctx) => {
32045
+ if (fenceEnabled && isOmpDumpContext())
32046
+ return { messages: event.messages };
31933
32047
  const budget = new PiContextBudget;
31934
32048
  budget.sideTurn = fenceEnabled && isOmpSideContext(event);
31935
32049
  const key = operationKey(ctx);
@@ -31952,16 +32066,20 @@ function registerPiGuardedContext(pi, handler, options = {}) {
31952
32066
  }
31953
32067
  const active = operation;
31954
32068
  active.pass = budget;
32069
+ active.inputKeys = undefined;
32070
+ active.dispatched = false;
31955
32071
  active.ready = false;
32072
+ active.refused = false;
31956
32073
  const isOwned = () => budget.sideTurn ? operations.get(key) === owner : operations.get(key) === active && active.pass === budget;
31957
32074
  const isCurrent = () => isOwned() && (budget.sideTurn || !active.ended);
31958
32075
  budget.assertOwner = () => {
31959
32076
  if (!isCurrent())
31960
- throw new PiContextSupersededError("Pi context pass superseded before writer admission");
32077
+ throw new PiContextSupersededError("Pi context pass superseded: no longer valid for this operation", active.ended ? "agent_end" : operations.get(key) !== (budget.sideTurn ? owner : active) ? "operation replacement or cache eviction" : "newer context pass");
31961
32078
  };
31962
32079
  try {
31963
- const result = await withSqliteTransformPass2(() => handler(event, ctx, budget));
31964
- budget.assertOutcome();
32080
+ if (fenceEnabled && !budget.sideTurn)
32081
+ active.inputKeys = inputMessageKeys(event);
32082
+ const result = await budget.waitMandatory(Promise.resolve(withSqliteTransformPass2(() => handler(event, ctx, budget))));
31965
32083
  if (result?.messages) {
31966
32084
  budget.completed = true;
31967
32085
  active.ready = true;
@@ -31969,6 +32087,30 @@ function registerPiGuardedContext(pi, handler, options = {}) {
31969
32087
  return result;
31970
32088
  } catch (error) {
31971
32089
  if (error instanceof PiContextSupersededError || !isCurrent()) {
32090
+ if (fenceEnabled && !budget.sideTurn && !sideSessions.get(key)?.sides && !options.compactionOff?.(ctx)) {
32091
+ const nonce = crypto.randomUUID();
32092
+ let nonces = unsendableNonces.get(key);
32093
+ if (!nonces) {
32094
+ nonces = new Set;
32095
+ unsendableNonces.set(key, nonces);
32096
+ }
32097
+ nonces.add(nonce);
32098
+ if (nonces.size > MAX_UNSENDABLE_NONCES_PER_SESSION) {
32099
+ const oldest = nonces.values().next().value;
32100
+ if (oldest !== undefined)
32101
+ nonces.delete(oldest);
32102
+ }
32103
+ event.messages.push({
32104
+ role: "user",
32105
+ content: [
32106
+ {
32107
+ type: "text",
32108
+ text: `Magic Context did not prepare this request for the current turn. ${nonce}`
32109
+ }
32110
+ ],
32111
+ timestamp: Date.now()
32112
+ });
32113
+ }
31972
32114
  log2(`[magic-context][pi] DISCARDED CONTEXT RESULT ${budget.diagnostic()}`, error);
31973
32115
  throw error;
31974
32116
  }
@@ -31985,6 +32127,120 @@ function registerPiGuardedContext(pi, handler, options = {}) {
31985
32127
  });
31986
32128
  }
31987
32129
 
32130
+ // src/pi-fallback-index.ts
32131
+ function createPiFallbackIndex() {
32132
+ const prior = [];
32133
+ let priorHadDuplicateIds = false;
32134
+ const byHeader = new Map;
32135
+ const positions = new Map;
32136
+ const owners = new Map;
32137
+ let rebuilt = 0;
32138
+ return {
32139
+ get rebuiltMessages() {
32140
+ return rebuilt;
32141
+ },
32142
+ update(args) {
32143
+ const selected = new Map;
32144
+ const guardIndexes = [];
32145
+ const inputs = [];
32146
+ const seen = new Set;
32147
+ let duplicate = false;
32148
+ for (let index = 0;index < args.messages.length; index++) {
32149
+ const message = args.messages[index];
32150
+ const id = args.resolveId(message, index) ?? `unmapped:${index}`;
32151
+ if (seen.has(id))
32152
+ duplicate = true;
32153
+ seen.add(id);
32154
+ const fields = (message && typeof message === "object" ? args.fields?.get(message) : undefined) ?? lkgContentFields(message && typeof message === "object" ? {
32155
+ role: message.role,
32156
+ timestamp: message.timestamp,
32157
+ responseId: message.responseId,
32158
+ toolCallId: message.toolCallId,
32159
+ content: message.content
32160
+ } : message);
32161
+ inputs.push({ id, fields: fields ?? [Symbol()] });
32162
+ }
32163
+ let prefix = priorHadDuplicateIds ? 0 : exactReusablePrefix(inputs, prior);
32164
+ if (duplicate) {
32165
+ prefix = 0;
32166
+ selected.clear();
32167
+ }
32168
+ priorHadDuplicateIds = duplicate;
32169
+ for (let index = 0;index < prefix; index++) {
32170
+ const old = prior[index];
32171
+ if (old && !args.reusable?.has(old.id)) {
32172
+ guardIndexes.push(index);
32173
+ if (old.fingerprint)
32174
+ selected.set(old.id, old.fingerprint);
32175
+ }
32176
+ }
32177
+ for (const old of prior.slice(prefix)) {
32178
+ positions.delete(old.id);
32179
+ if (old.header) {
32180
+ const bucket = byHeader.get(old.header);
32181
+ bucket?.delete(old);
32182
+ if (!bucket?.size)
32183
+ byHeader.delete(old.header);
32184
+ }
32185
+ for (const [key, ids] of old.owners) {
32186
+ const bucket = owners.get(key);
32187
+ for (const id of ids)
32188
+ bucket?.delete(id);
32189
+ if (!bucket?.size)
32190
+ owners.delete(key);
32191
+ }
32192
+ }
32193
+ prior.length = prefix;
32194
+ for (let index = prefix;index < args.messages.length; index++) {
32195
+ guardIndexes.push(index);
32196
+ const message = args.messages[index];
32197
+ const input = inputs[index];
32198
+ if (!input)
32199
+ continue;
32200
+ const record = {
32201
+ ...input,
32202
+ header: args.header(message),
32203
+ fingerprint: input.id.startsWith("unmapped:") ? null : args.fingerprint(message),
32204
+ owners: input.id.startsWith("unmapped:") ? new Map : args.toolOwners(message, input.id)
32205
+ };
32206
+ rebuilt++;
32207
+ prior.push(record);
32208
+ if (!positions.has(record.id))
32209
+ positions.set(record.id, index);
32210
+ if (record.header) {
32211
+ const bucket = byHeader.get(record.header) ?? new Set;
32212
+ bucket.add(record);
32213
+ byHeader.set(record.header, bucket);
32214
+ }
32215
+ for (const [key, ids] of record.owners) {
32216
+ const bucket = owners.get(key) ?? new Set;
32217
+ for (const id of ids)
32218
+ bucket.add(id);
32219
+ owners.set(key, bucket);
32220
+ }
32221
+ if (!args.reusable?.has(record.id) && record.fingerprint)
32222
+ selected.set(record.id, record.fingerprint);
32223
+ }
32224
+ return {
32225
+ positions,
32226
+ owners,
32227
+ changedFrom: prefix,
32228
+ guardIndexes,
32229
+ fingerprints(headers) {
32230
+ const result = new Map(selected);
32231
+ const records = headers ? [...headers].flatMap((header) => [
32232
+ ...byHeader.get(header) ?? []
32233
+ ]) : headers === null ? prior : [];
32234
+ for (const record of records)
32235
+ if (record.fingerprint)
32236
+ result.set(record.id, record.fingerprint);
32237
+ return result;
32238
+ }
32239
+ };
32240
+ }
32241
+ };
32242
+ }
32243
+
31988
32244
  // ../plugin/src/hooks/magic-context/openai-compat-adjacency.ts
31989
32245
  function assertOpenAiCompatAdjacency(messages) {
31990
32246
  const violations = [];
@@ -32870,7 +33126,19 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
32870
33126
  ...reusePrior ? reusePrior.signatures.slice(0, reusablePrefix) : [],
32871
33127
  ...plan.inputs.slice(reusablePrefix).map((input) => signatureForFields(input.fields))
32872
33128
  ];
32873
- const incremental = incrementalLkgContentDigests(plan.inputs.map((input, index) => ({
33129
+ const verifiedOwnPrefix = reusePrior && digestPrior === state.acceptedSlot && reusePrior.slot.inputIdSeq.length === reusePrior.signatures.length && reusePrior.signatures.length === reusePrior.slot.inputContentDigests.length;
33130
+ let digestPrefix = 0;
33131
+ if (verifiedOwnPrefix && reusePrior) {
33132
+ while (digestPrefix < reusablePrefix && !isLegacyLkgDigest(reusePrior.slot.inputContentDigests[digestPrefix] ?? ""))
33133
+ digestPrefix++;
33134
+ }
33135
+ const incremental = verifiedOwnPrefix && reusePrior ? {
33136
+ reusedPrefix: digestPrefix,
33137
+ digests: [
33138
+ ...reusePrior.slot.inputContentDigests.slice(0, digestPrefix),
33139
+ ...plan.inputs.slice(digestPrefix).map((input) => lkgContentDigestFromFields(input.fields))
33140
+ ]
33141
+ } : incrementalLkgContentDigests(plan.inputs.map((input, index) => ({
32874
33142
  id: input.id,
32875
33143
  signature: inputContentSignatures[index] ?? "",
32876
33144
  fields: input.fields
@@ -34389,6 +34657,15 @@ function recordPiIdentityRecurrence(db, sessionId, key) {
34389
34657
  function queuePiIdentityRebuild(db, sessionId, repair) {
34390
34658
  return freezeIdentityDecision(db, sessionId, "tag-identity-repair-pending", JSON.stringify(repair));
34391
34659
  }
34660
+ function claimPiIdentityRebuildFailure(db, sessionId) {
34661
+ const decisions = readPiIdentityDecisions(db, sessionId);
34662
+ if (!decisions)
34663
+ return "unpersisted";
34664
+ const key = encodePiContentDecision("tag-identity-rebuild-failed", "m0-m1-clear");
34665
+ if (decisions.has(key))
34666
+ return "again";
34667
+ return freezeIdentityDecision(db, sessionId, "tag-identity-rebuild-failed", "m0-m1-clear") ? "first" : "unpersisted";
34668
+ }
34392
34669
  function readPiIdentityRebuilds(db, sessionId) {
34393
34670
  const repairs = [...readPiIdentityDecisions(db, sessionId) ?? []].flatMap((entry) => {
34394
34671
  const decoded = decodePiContentDecision(entry);
@@ -34732,7 +35009,7 @@ function applyPiProactiveThinkingStrip(args) {
34732
35009
  return null;
34733
35010
  if (!isPrefixBoundThinkingModel(args.provider, args.model))
34734
35011
  return null;
34735
- const protectedMessages = latestAssistantTurnMessages(args.messages);
35012
+ const protectedMessages = args.stripActiveTurn ? new Set : latestAssistantTurnMessages(args.messages);
34736
35013
  const entryIds = [];
34737
35014
  const indices = [];
34738
35015
  for (let index = 0;index < args.messages.length; index += 1) {
@@ -34759,8 +35036,9 @@ function applyPiProactiveThinkingStrip(args) {
34759
35036
  reportBindingRecovery(args.sessionId, args.report, "proactive thinking strip: persistence failed; serving the thinking unchanged");
34760
35037
  return null;
34761
35038
  }
34762
- for (const index of indices)
34763
- stripThinkingParts(args.messages[index]);
35039
+ if (!args.persistOnly)
35040
+ for (const index of indices)
35041
+ stripThinkingParts(args.messages[index]);
34764
35042
  reportBindingRecovery(args.sessionId, args.report, `proactive thinking strip: busting pass froze thinking of ${entryIds.length} entr${entryIds.length === 1 ? "y" : "ies"} [${entryIds.join(",")}]`);
34765
35043
  return { entryIds };
34766
35044
  }
@@ -36128,6 +36406,25 @@ function markToolRemoval(working, messageIndex, partIndex) {
36128
36406
 
36129
36407
  // src/context-handler.ts
36130
36408
  var EMERGENCY_BLOCK_PERCENTAGE = 95;
36409
+ function tagsWithNumber(sorted, number) {
36410
+ let low = 0;
36411
+ let high = sorted.length;
36412
+ while (low < high) {
36413
+ const middle = low + high >>> 1;
36414
+ if ((sorted[middle]?.tagNumber ?? Number.POSITIVE_INFINITY) < number)
36415
+ low = middle + 1;
36416
+ else
36417
+ high = middle;
36418
+ }
36419
+ const rows = [];
36420
+ for (let index = low;index < sorted.length; index += 1) {
36421
+ const row = sorted[index];
36422
+ if (row?.tagNumber !== number)
36423
+ break;
36424
+ rows.push(row);
36425
+ }
36426
+ return rows;
36427
+ }
36131
36428
  function newestActiveTagNumbersByCount(tags, count) {
36132
36429
  return new Set(tags.filter((tag) => tag.status === "active").sort((left, right) => right.tagNumber - left.tagNumber).slice(0, Math.max(0, count)).map((tag) => tag.tagNumber));
36133
36430
  }
@@ -36170,9 +36467,11 @@ var contextPassGeneration = new Map;
36170
36467
 
36171
36468
  class PiLkgAdmissionReplay extends Error {
36172
36469
  messages;
36173
- constructor(messages) {
36470
+ recovery;
36471
+ constructor(messages, recovery = "fitting LKG before writer retries") {
36174
36472
  super("Saved request served before writer wait");
36175
36473
  this.messages = messages;
36474
+ this.recovery = recovery;
36176
36475
  }
36177
36476
  }
36178
36477
  var liveModelBySession = new Map;
@@ -36201,6 +36500,7 @@ var piMessageTokenCacheBySession = new Map;
36201
36500
  var piTagTextTokenCacheBySession = new Map;
36202
36501
  var piTagToolTokenCacheBySession = new Map;
36203
36502
  var piTextIdentitySourceCacheBySession = new Map;
36503
+ var piFallbackIndexesBySession = new Map;
36204
36504
  function buildPiTextIdentityPlan(db, sessionId, tagger, transcript, reuseCandidates = new Set) {
36205
36505
  const currentSourcesByMessageId = new Map;
36206
36506
  for (const message of transcript.messages) {
@@ -37110,28 +37410,6 @@ function piMessageEntryFingerprint2(message) {
37110
37410
  piEntryFingerprintCount += 1;
37111
37411
  return piMessageEntryFingerprint(message);
37112
37412
  }
37113
- function buildEntryFingerprintMap(messages, resolveStableId, reusableMessageIds, includeReusable = true, reusableHeaders) {
37114
- const map = new Map;
37115
- for (let i = 0;i < messages.length; i++) {
37116
- const msg = messages[i];
37117
- const id = resolveStableId(msg, i);
37118
- if (!id)
37119
- continue;
37120
- if (reusableMessageIds?.has(id)) {
37121
- if (!includeReusable)
37122
- continue;
37123
- if (reusableHeaders) {
37124
- const header = piMessageEntryFingerprintHeader(msg);
37125
- if (header === null || !reusableHeaders.has(header))
37126
- continue;
37127
- }
37128
- }
37129
- const fp = piMessageEntryFingerprint2(msg);
37130
- if (fp)
37131
- map.set(id, fp);
37132
- }
37133
- return map;
37134
- }
37135
37413
  function piToolOwnerMapKey(timestamp, callId) {
37136
37414
  return `${timestamp}\x00${callId}`;
37137
37415
  }
@@ -37187,17 +37465,20 @@ var piToolGuardCallIds = new WeakMap;
37187
37465
  function fallbackPiToolCallIds(db, sessionId) {
37188
37466
  let statement = piToolGuardVersionStatements.get(db);
37189
37467
  if (!statement) {
37190
- statement = db.prepare("SELECT tags_version AS version FROM session_meta WHERE session_id = ?");
37468
+ statement = db.prepare(`SELECT COALESCE((SELECT tags_version FROM session_meta WHERE session_id = ?), 0) AS version,
37469
+ (SELECT value FROM schema_migrations_meta WHERE key = ?) AS identity_revision`);
37191
37470
  piToolGuardVersionStatements.set(db, statement);
37192
37471
  }
37193
- const version = statement.get(sessionId)?.version ?? 0;
37472
+ const stamp = statement.get(sessionId, tagIdentityRevisionKey(sessionId));
37473
+ const version = stamp?.version ?? 0;
37474
+ const identityRevision = stamp?.identity_revision ?? null;
37194
37475
  let sessions = piToolGuardCallIds.get(db);
37195
37476
  if (!sessions) {
37196
37477
  sessions = new Map;
37197
37478
  piToolGuardCallIds.set(db, sessions);
37198
37479
  }
37199
37480
  const cached = sessions.get(sessionId);
37200
- if (cached?.version === version)
37481
+ if (cached?.version === version && cached.identityRevision === identityRevision)
37201
37482
  return cached.ids;
37202
37483
  let fallbackStatement = piToolGuardFallbackStatements.get(db);
37203
37484
  if (!fallbackStatement) {
@@ -37207,7 +37488,7 @@ function fallbackPiToolCallIds(db, sessionId) {
37207
37488
  const ids = new Set(fallbackStatement.all(sessionId).map((row) => row.callId));
37208
37489
  if (!sessions.has(sessionId) && sessions.size >= MAX_TRACKED_SESSIONS)
37209
37490
  sessions.delete(sessions.keys().next().value);
37210
- sessions.set(sessionId, { version, ids });
37491
+ sessions.set(sessionId, { version, identityRevision, ids });
37211
37492
  return ids;
37212
37493
  }
37213
37494
  var piUnresolvedIdentityLogged = new Set;
@@ -37221,10 +37502,10 @@ function logPiUnresolvedIdentities(sessionId, unresolved) {
37221
37502
  piUnresolvedIdentityLogged.add(`${sessionId}\x00${item.kind}\x00${item.id}`);
37222
37503
  sessionLog2(sessionId, `tag identity unresolved: serving with a newly allocated tag number (one cache change per identity) session=${sessionId} ${fresh.map((item) => `${item.kind}=${item.id}`).join(" ")}`);
37223
37504
  }
37224
- function guardPiToolAllocations(db, sessionId, messages, resolveId, tagger) {
37505
+ function guardPiToolAllocations(db, sessionId, messages, resolveId, tagger, messageIndexes) {
37225
37506
  const unresolved = [];
37226
37507
  const fallbackCalls = fallbackPiToolCallIds(db, sessionId);
37227
- for (let index = 0;index < messages.length; index++) {
37508
+ for (const index of messageIndexes ?? messages.keys()) {
37228
37509
  const message = messages[index];
37229
37510
  if (message?.role !== "assistant" || !Array.isArray(message.content))
37230
37511
  continue;
@@ -37467,6 +37748,8 @@ function adoptPiFallbackTags(db, sessionId, tagger, fingerprintById, options = {
37467
37748
  const recurrences = [];
37468
37749
  const plan = (rows, key, legacyKeys, served, cached) => {
37469
37750
  const proven = cached !== undefined || rows.filter((row) => served.has(row.tagNumber)).length === 1;
37751
+ if (!proven && options.deferUnprovenRebuild)
37752
+ return null;
37470
37753
  if (proven && options.allowUnprovenRebuild) {
37471
37754
  const decisions = readPiIdentityDecisions(db, sessionId);
37472
37755
  let unmerged = decisions === null || decisions.has(encodePiContentDecision("tag-identity-recurring", key));
@@ -37545,12 +37828,14 @@ function adoptPiFallbackTags(db, sessionId, tagger, fingerprintById, options = {
37545
37828
  return adoption;
37546
37829
  };
37547
37830
  let { realIdsByFingerprint, fallbackIdsByFingerprint, batches } = preparePiAdoptionFingerprints(fingerprintById);
37548
- const ownerMap = options.messages && options.resolveStableId ? buildPiToolOwnerMap(options.messages, options.resolveStableId) : null;
37831
+ const ownerMap = options.toolOwners ?? (options.messages && options.resolveStableId ? buildPiToolOwnerMap(options.messages, options.resolveStableId) : null);
37549
37832
  if (!batches.length && !ownerMap?.size && !options.rebuildFingerprints)
37550
37833
  return outcome;
37551
37834
  const revision = () => readPiAdoptionRevision(db);
37552
37835
  const beforeDiscovery = revision();
37553
- const discovered = hasPiFallbackMessageTags2(db, sessionId) ? readAdoptablePiFallbackFingerprints(db, sessionId, batches) : new Set;
37836
+ const discoveredMessageFallback = hasPiFallbackMessageTags2(db, sessionId);
37837
+ const discoveredToolOwnerFallback = ownerMap?.size ? hasPiFallbackToolOwnerTags2(db, sessionId) : false;
37838
+ const discovered = discoveredMessageFallback ? readAdoptablePiFallbackFingerprints(db, sessionId, batches) : new Set;
37554
37839
  const targetsFor = (fingerprints) => [...fingerprints].flatMap((fingerprint) => (realIdsByFingerprint.get(fingerprint) ?? []).map((id) => [id, fingerprint]));
37555
37840
  const fallbackTargetsFor = (fingerprints) => [...fingerprints].flatMap((fingerprint) => (fallbackIdsByFingerprint.get(fingerprint) ?? []).map((id) => [id, fingerprint]));
37556
37841
  const discoveredTargets = targetsFor(discovered);
@@ -37592,32 +37877,34 @@ function adoptPiFallbackTags(db, sessionId, tagger, fingerprintById, options = {
37592
37877
  };
37593
37878
  runImmediateTransaction(db, () => {
37594
37879
  const admitted = revision();
37880
+ const unchangedSinceDiscovery = beforeDiscovery.external.data_version === admitted.external.data_version && beforeDiscovery.local.changes === admitted.local.changes;
37595
37881
  const preflight = options.preflightRevision;
37596
37882
  const rebuild = options.rebuildFingerprints;
37597
37883
  const expanded = Boolean(preflight && rebuild && (preflight.external.data_version !== admitted.external.data_version || preflight.local.changes !== admitted.local.changes));
37598
37884
  if (expanded && rebuild) {
37599
37885
  ({ realIdsByFingerprint, fallbackIdsByFingerprint, batches } = preparePiAdoptionFingerprints(rebuild()));
37600
37886
  }
37601
- if (batches.length && hasPiFallbackMessageTags2(db, sessionId)) {
37602
- const unchanged = !expanded && beforeDiscovery.external.data_version === admitted.external.data_version && beforeDiscovery.local.changes === admitted.local.changes;
37887
+ if (batches.length && (unchangedSinceDiscovery ? discoveredMessageFallback : hasPiFallbackMessageTags2(db, sessionId))) {
37888
+ const unchanged = !expanded && unchangedSinceDiscovery;
37603
37889
  const adoptable = unchanged ? discovered : readAdoptablePiFallbackFingerprints(db, sessionId, batches);
37604
37890
  const targets = expanded ? targetsFor(adoptable) : discoveredTargets.filter(([, fp]) => adoptable.has(fp));
37605
37891
  if (!expanded)
37606
37892
  targets.push(...targetsFor([...adoptable].filter((fp) => !discovered.has(fp))));
37607
37893
  const pairedBaseByRealId = new Map;
37608
- let positions;
37894
+ let positions = options.realIdPositions;
37609
37895
  const realIdPositions = () => {
37610
37896
  if (positions)
37611
37897
  return positions;
37612
37898
  const { messages, resolveStableId } = options;
37613
37899
  if (!messages || !resolveStableId)
37614
37900
  return;
37615
- positions = new Map;
37901
+ const computed = new Map;
37616
37902
  for (let index = 0;index < messages.length; index += 1) {
37617
37903
  const id = resolveStableId(messages[index], index);
37618
- if (id !== undefined && !positions.has(id))
37619
- positions.set(id, index);
37904
+ if (id !== undefined && !computed.has(id))
37905
+ computed.set(id, index);
37620
37906
  }
37907
+ positions = computed;
37621
37908
  return positions;
37622
37909
  };
37623
37910
  for (const [realMessageId, fingerprint] of targets) {
@@ -37657,7 +37944,7 @@ function adoptPiFallbackTags(db, sessionId, tagger, fingerprintById, options = {
37657
37944
  adoptMessageCandidates(fallbackId, fingerprint, legacy.filter((c) => piFallbackBaseId(c.messageId) === base));
37658
37945
  }
37659
37946
  }
37660
- if (ownerMap && hasPiFallbackToolOwnerTags2(db, sessionId)) {
37947
+ if (ownerMap && (unchangedSinceDiscovery ? discoveredToolOwnerFallback : hasPiFallbackToolOwnerTags2(db, sessionId))) {
37661
37948
  for (const row of findPiFallbackToolOwnerTags2(db, sessionId)) {
37662
37949
  const parsed = parsePiFallbackToolOwnerId(row.toolOwnerMessageId);
37663
37950
  if (parsed?.role !== "assistant")
@@ -37769,6 +38056,7 @@ function registerPiContextHandler(pi, baseOptions) {
37769
38056
  sessionLog2(sessionId, message);
37770
38057
  });
37771
38058
  const contextHandler = async (event, ctx, budget) => {
38059
+ budget.assertOutcome();
37772
38060
  const passSessionId = ctx.sessionManager ? resolveSessionId2(ctx) : undefined;
37773
38061
  const generation = Symbol();
37774
38062
  if (passSessionId && !budget.sideTurn)
@@ -37778,7 +38066,7 @@ function registerPiContextHandler(pi, baseOptions) {
37778
38066
  budget.assertOwner = () => {
37779
38067
  assertOperation();
37780
38068
  if (!budget.sideTurn && passSessionId && contextPassGeneration.get(passSessionId) !== generation)
37781
- throw new PiContextSupersededError("Pi context pass superseded before writer admission");
38069
+ throw new PiContextSupersededError("Pi context pass superseded by a newer context generation", "context generation");
37782
38070
  signal?.throwIfAborted();
37783
38071
  };
37784
38072
  let assertCurrentPass = budget.assert;
@@ -37879,6 +38167,10 @@ function registerPiContextHandler(pi, baseOptions) {
37879
38167
  transport: ctx.model?.transport
37880
38168
  });
37881
38169
  lkgPassSnapshot = snapshot;
38170
+ const lkgInputFields = new WeakMap(snapshot.inputs.flatMap((input) => {
38171
+ const message = event.messages[input.messageIndex];
38172
+ return message && typeof message === "object" ? [[message, input.fields]] : [];
38173
+ }));
37882
38174
  let checkedReplay = false;
37883
38175
  budget.stage = "writer admission";
37884
38176
  assertCurrentPass();
@@ -38010,6 +38302,8 @@ function registerPiContextHandler(pi, baseOptions) {
38010
38302
  if (currentModelKey !== undefined) {
38011
38303
  liveModelBySession.set(sessionId, currentModelKey);
38012
38304
  }
38305
+ logTransformTiming(sessionId, "modelChangeDetection", tModelDetect);
38306
+ const tPressureResolution = performance.now();
38013
38307
  const compactionTransition = reconcilePiCompactionMode({
38014
38308
  db: options.db,
38015
38309
  sessionId,
@@ -38104,6 +38398,7 @@ function registerPiContextHandler(pi, baseOptions) {
38104
38398
  let sessionMeta = sessionMetaForUsage;
38105
38399
  const modelKey = liveModelBySession.get(sessionId);
38106
38400
  const providerId = typeof ctx.model?.provider === "string" ? ctx.model.provider : undefined;
38401
+ const fallbackInputFields = bindingStripOrder === "end" && !options.injection?.temporalAwareness && !beforePipelineForTests ? lkgInputFields : undefined;
38107
38402
  const canUseEmptySentinels = modelAcceptsEmptyContent(providerId);
38108
38403
  if (usageContextLimit === undefined) {
38109
38404
  const modelWindow = ctx.model?.contextWindow;
@@ -38186,7 +38481,7 @@ function registerPiContextHandler(pi, baseOptions) {
38186
38481
  schedulerDecision = "execute";
38187
38482
  sessionLog2(sessionId, `transform: large imported session detected (${piMessageCount} messages, no usage baseline) — forcing execute on first pass`);
38188
38483
  }
38189
- logTransformTiming(sessionId, "modelChangeDetection", tModelDetect);
38484
+ logTransformTiming(sessionId, "pressureResolution", tPressureResolution);
38190
38485
  const storedStableIdScheme = sessionMeta.piStableIdScheme ?? 0;
38191
38486
  const realEntryIdsAvailable = strictEntryIds?.some((id) => typeof id === "string" && id.length > 0) ?? false;
38192
38487
  const stableIdSchemeCutover = storedStableIdScheme < PI_STABLE_ID_SCHEME && realEntryIdsAvailable;
@@ -38263,7 +38558,8 @@ function registerPiContextHandler(pi, baseOptions) {
38263
38558
  if (beforePipelineForTests)
38264
38559
  await budget.waitMandatory(beforePipelineForTests());
38265
38560
  assertCurrentPass();
38266
- const result = await budget.waitMandatory(runPipeline({
38561
+ const inputForThinkingFit = isPrefixBoundThinkingModel(ctx.model?.provider, ctx.model?.id) ? structuredClone(event.messages) : null;
38562
+ const pipelineArgs = {
38267
38563
  assertCurrentPass,
38268
38564
  db: options.db,
38269
38565
  tagger,
@@ -38298,6 +38594,7 @@ function registerPiContextHandler(pi, baseOptions) {
38298
38594
  entryIds,
38299
38595
  entryIdByRef,
38300
38596
  reusableMessageIds,
38597
+ fallbackInputFields,
38301
38598
  stableIdSchemeCutover,
38302
38599
  schedulerDecision,
38303
38600
  schedulerDeferReason,
@@ -38322,8 +38619,32 @@ function registerPiContextHandler(pi, baseOptions) {
38322
38619
  readBranchEntries: resolvePiReadBranchEntries(ctx),
38323
38620
  isSubagent: sessionMeta.isSubagent,
38324
38621
  compactionOff: options.compactionOff === true,
38325
- injectionPassSnapshot: piM0M1PassSnapshot
38326
- }));
38622
+ injectionPassSnapshot: piM0M1PassSnapshot,
38623
+ readLastServedMessages: () => {
38624
+ const serialized = getPiLastServedArray(sessionId);
38625
+ try {
38626
+ return serialized ? JSON.parse(serialized) : null;
38627
+ } catch {
38628
+ return null;
38629
+ }
38630
+ },
38631
+ tryReplayHeldIdentityRebuild: () => {
38632
+ const replay = lkgCoordinator.replay(snapshot, (id) => ctx.sessionManager.getEntry?.(id)?.parentId);
38633
+ if (!replay.ok) {
38634
+ sessionLog2(sessionId, `Pi held identity rebuild: prior prefix unavailable (${replay.reason}); applying the declared rebuild`);
38635
+ return;
38636
+ }
38637
+ try {
38638
+ assertPiRawFallbackFits(replay.messages, rawFallbackLimit, (line) => logPiLkgRecovery(sessionId, line), undefined, readPiLkgFitEnvelope(ctx, pi, lkgModelKey, sessionDecisionCalibration(options.db, sessionId), toolWireSchema), replay.measuredPrefix);
38639
+ } catch (error) {
38640
+ sessionLog2(sessionId, `Pi held identity rebuild: prior prefix cannot fit (${String(error)}); applying the declared rebuild`);
38641
+ return;
38642
+ }
38643
+ logPiLkgRecovery(sessionId, "Pi held identity rebuild: replaying the served prefix and retaining the pending repair");
38644
+ throw new PiLkgAdmissionReplay(replay.messages, "held tag-identity rebuild: replaying the served prefix");
38645
+ }
38646
+ };
38647
+ let result = await budget.waitMandatory(runPipeline(pipelineArgs));
38327
38648
  logTransformTiming(sessionId, "runPipeline", tRunPipeline);
38328
38649
  if (result.identityRebuilt)
38329
38650
  schedulerDecision = "execute";
@@ -38424,7 +38745,7 @@ function registerPiContextHandler(pi, baseOptions) {
38424
38745
  projectIdentity,
38425
38746
  entryIds: strictEntryIds,
38426
38747
  entryIdByRef: result.postCommitEntryIdByRef,
38427
- isCacheBusting: isCacheBusting || result.executedWorkThisPass,
38748
+ isCacheBusting: result.prefixBustPermitted && !result.strictThinkingKept,
38428
38749
  syntheticLeadingCount: result.syntheticLeadingCount,
38429
38750
  postTransformSnapshot
38430
38751
  });
@@ -38435,7 +38756,7 @@ function registerPiContextHandler(pi, baseOptions) {
38435
38756
  logTransformTiming(sessionId, "noteNudges", tNoteNudges);
38436
38757
  const tAutoSearch = performance.now();
38437
38758
  budget.stage = "auto-search";
38438
- if (options.autoSearch?.enabled && !options.compactionOff && budget.remainingWork() > PI_CONTEXT_BUDGET.searchMs) {
38759
+ if (options.autoSearch?.enabled && !options.compactionOff && result.prefixMutationAllowed && budget.remainingWork() > PI_CONTEXT_BUDGET.searchMs) {
38439
38760
  guardSqliteTransformPass2({
38440
38761
  assert: budget.assert,
38441
38762
  remainingMs: () => budget.remainingWork()
@@ -38474,7 +38795,7 @@ function registerPiContextHandler(pi, baseOptions) {
38474
38795
  try {
38475
38796
  const sessionMetaForTodo = sessionMeta;
38476
38797
  if (options.todowriteEnabled === true && !options.compactionOff && !sessionMetaForTodo.isSubagent && sessionMetaForTodo.lastTodoState !== "") {
38477
- const isCacheBustingForTodo = isCacheBusting || result.executedWorkThisPass;
38798
+ const isCacheBustingForTodo = result.prefixBustPermitted && !result.strictThinkingKept;
38478
38799
  outputMessages = injectSyntheticTodowriteForPi({
38479
38800
  db: options.db,
38480
38801
  sessionId,
@@ -38495,7 +38816,7 @@ function registerPiContextHandler(pi, baseOptions) {
38495
38816
  hostEditBeforeNewestThinking = stickyBefore === null || [...stickyAfter].some((key) => !stickyBefore.has(key)) || JSON.stringify(getPersistedTodoSyntheticAnchor(options.db, sessionId)) !== todoAnchorBefore;
38496
38817
  } catch {}
38497
38818
  const outputEntryIds = resolvePiLkgOutputEntryIds(outputMessages, result.syntheticLeadingCount, (message) => result.lkgEntryIdByRef.get(message) ?? result.postCommitEntryIdByRef.get(message) ?? bindingEntryIdByRef.get(message)).map((entryId) => entryId ?? undefined);
38498
- const thinkingBindingRecoveryApplied = bindingStripOrder === "end" ? applyPiThinkingBindingRecovery({
38819
+ const thinkingBindingRecoveryApplied = bindingStripOrder === "end" && !result.signedPrefixRewritten ? applyPiThinkingBindingRecovery({
38499
38820
  db: options.db,
38500
38821
  sessionId,
38501
38822
  messages: outputMessages,
@@ -38517,7 +38838,8 @@ function registerPiContextHandler(pi, baseOptions) {
38517
38838
  entryIds: outputEntryIds,
38518
38839
  provider: lkgProviderKey ?? undefined,
38519
38840
  model: ctx.model?.id,
38520
- cacheBustingPass: isCacheBusting || result.executedWorkThisPass && (result.prefixEditBesidesReasoningTrim || hostEditBeforeNewestThinking),
38841
+ cacheBustingPass: result.signedPrefixRewritten || result.prefixBustPermitted && !result.strictThinkingKept && (isCacheBusting || result.prefixRebuildAlreadyPriced || result.executedWorkThisPass && (result.prefixEditBesidesReasoningTrim || hostEditBeforeNewestThinking)),
38842
+ stripActiveTurn: result.signedPrefixRewritten,
38521
38843
  report: (line) => sessionLog2(sessionId, line)
38522
38844
  });
38523
38845
  } catch (err) {
@@ -38537,11 +38859,12 @@ function registerPiContextHandler(pi, baseOptions) {
38537
38859
  const baseline = refreshPiTailHygieneBaseline({
38538
38860
  messages: outputMessages,
38539
38861
  tags,
38862
+ tagCacheKey: result.channelBaselineSnapshot.tagCacheKey,
38540
38863
  protectedTagNumbers,
38541
38864
  protectedTools: options.protectedTools,
38542
38865
  pendingDropTagNumbers,
38543
38866
  stableId,
38544
- protectedToolTags: protectedToolTagNumbers(tags, options.protectedTools),
38867
+ protectedToolTags: protectedToolTagNumbers(result.channelBaselineSnapshot.activeTags, options.protectedTools),
38545
38868
  syntheticLeadingCount: result.syntheticLeadingCount,
38546
38869
  cacheBusting: result.bustedThisPass,
38547
38870
  previous: previousBaseline,
@@ -38562,14 +38885,7 @@ function registerPiContextHandler(pi, baseOptions) {
38562
38885
  const channelState = {
38563
38886
  ...baseline,
38564
38887
  usableWindow: usageContextLimit ?? 0,
38565
- realUserTurnCount: countRealPiUserMessages({
38566
- messages: outputMessages,
38567
- tags,
38568
- protectedTagNumbers,
38569
- pendingDropTagNumbers,
38570
- stableId,
38571
- syntheticLeadingCount: result.syntheticLeadingCount
38572
- }),
38888
+ realUserTurnCount: baseline.realUserTurnCount,
38573
38889
  reducedSinceRefresh: false,
38574
38890
  agentDropsAppliedThisPass: result.agentDropsAppliedThisPass,
38575
38891
  oldestReclaimableToolTags
@@ -38652,7 +38968,34 @@ function registerPiContextHandler(pi, baseOptions) {
38652
38968
  runPiDebugAssertion(assertTailHygieneLastWriter);
38653
38969
  if (!options.compactionOff && (result.executedWorkThisPass || isCacheBusting || schedulerDecision === "execute")) {
38654
38970
  const envelope = readPiLkgFitEnvelope(ctx, pi, resolvePiContextModelKey(ctx), sessionDecisionCalibration(options.db, sessionId), toolWireSchema);
38655
- const estimate = estimatePiOutgoingInputTokens(outputMessages, envelope, lkgPassSnapshot ? lkgCoordinator.measureOutgoingPrefix(lkgPassSnapshot, outputMessages, (id) => ctx.sessionManager.getEntry?.(id)?.parentId) : undefined, rawFallbackLimit);
38971
+ let estimate = estimatePiOutgoingInputTokens(outputMessages, envelope, lkgPassSnapshot ? lkgCoordinator.measureOutgoingPrefix(lkgPassSnapshot, outputMessages, (id) => ctx.sessionManager.getEntry?.(id)?.parentId) : undefined, rawFallbackLimit);
38972
+ if (inputForThinkingFit && result.strictThinkingKept && rawFallbackLimit && estimate.refusalGrade && (estimate.refusalTokens ?? estimate.tokens) > rawFallbackLimit) {
38973
+ result = await budget.waitMandatory(runPipeline({
38974
+ ...pipelineArgs,
38975
+ messages: inputForThinkingFit,
38976
+ forceSignedRebuildForFit: true,
38977
+ forceMaterialization: true,
38978
+ contextUsage: {
38979
+ ...pipelineArgs.contextUsage,
38980
+ percentage: Math.max(95, pipelineArgs.contextUsage.percentage)
38981
+ }
38982
+ }));
38983
+ outputMessages = result.messages;
38984
+ const retryIds = resolvePiLkgOutputEntryIds(outputMessages, result.syntheticLeadingCount, (message) => result.lkgEntryIdByRef.get(message) ?? result.postCommitEntryIdByRef.get(message));
38985
+ applyPiProactiveThinkingStrip({
38986
+ db: options.db,
38987
+ sessionId,
38988
+ messages: outputMessages,
38989
+ entryIds: retryIds.map((id) => id ?? undefined),
38990
+ provider: ctx.model?.provider,
38991
+ model: ctx.model?.id,
38992
+ cacheBustingPass: true,
38993
+ stripActiveTurn: true
38994
+ });
38995
+ estimate = estimatePiOutgoingInputTokens(outputMessages, envelope, undefined, rawFallbackLimit);
38996
+ }
38997
+ if (result.signedPrefixRewritten && rawFallbackLimit && estimate.refusalGrade && (estimate.refusalTokens ?? estimate.tokens) > rawFallbackLimit)
38998
+ throw contextRefusalError("The request remains over the model window after stripping signed thinking and reclaiming context");
38656
38999
  const refusal = outgoingContextRefusal(estimate, rawFallbackLimit, protectedToolTokenCount(getActiveTagsBySession2(options.db, sessionId), options.protectedTools, envelope ? calibrationForModelKey(envelope.modelKey) : undefined));
38657
39000
  if (refusal)
38658
39001
  throw contextRefusalError(refusal);
@@ -38750,7 +39093,7 @@ function registerPiContextHandler(pi, baseOptions) {
38750
39093
  assertCurrentPass: budget.assertOutcome
38751
39094
  });
38752
39095
  budget.assertOutcome();
38753
- budget.recovery = "fitting LKG before writer retries";
39096
+ budget.recovery = err.recovery;
38754
39097
  return { messages: err.messages };
38755
39098
  }
38756
39099
  if ((isFailClosedBlockingError(err) || err instanceof EmergencyFailClosedError) && !baseOptions.compactionOff)
@@ -39394,6 +39737,11 @@ async function runCompactionOffPipeline(args) {
39394
39737
  }
39395
39738
  return {
39396
39739
  messages: args.messages,
39740
+ prefixBustPermitted: false,
39741
+ prefixMutationAllowed: false,
39742
+ strictThinkingKept: false,
39743
+ signedPrefixRewritten: false,
39744
+ prefixRebuildAlreadyPriced: false,
39397
39745
  heuristicsExecuted: false,
39398
39746
  executedWorkThisPass: false,
39399
39747
  historyInjected: false,
@@ -39413,6 +39761,7 @@ async function runCompactionOffPipeline(args) {
39413
39761
  activeTags: [],
39414
39762
  channelBaselineSnapshot: {
39415
39763
  tags: [],
39764
+ activeTags: [],
39416
39765
  protectedTagNumbers: new Set,
39417
39766
  pendingDropTagNumbers: new Set
39418
39767
  },
@@ -39434,8 +39783,31 @@ async function runPipeline(args) {
39434
39783
  if (thinkingRecovery.ended)
39435
39784
  signalPiPendingMaterialization(args.sessionId);
39436
39785
  let restoreOriginals;
39437
- const activeThinkingTurn = hasActiveAnthropicThinkingTurn(args.messages, args.resolvedProviderID, args.resolvedModelID);
39438
- const protectedSignedPrefix = activeThinkingTurn && args.reasoningClearing?.prefixBound === true;
39786
+ let activeThinkingTurn = hasActiveAnthropicThinkingTurn(args.messages, args.resolvedProviderID, args.resolvedModelID);
39787
+ let protectedSignedPrefix = activeThinkingTurn && args.reasoningClearing?.prefixBound === true;
39788
+ let rewritingSignedPrefix = false;
39789
+ let workingForRewrite;
39790
+ const stripForPrefixRewrite = () => {
39791
+ const messages = workingForRewrite ?? args.messages;
39792
+ const result = applyPiProactiveThinkingStrip({
39793
+ db: args.db,
39794
+ sessionId: args.sessionId,
39795
+ messages,
39796
+ entryIds: messages.map((message, index) => resolvePiStableId(message, index, args.entryIds, args.entryIdByRef ?? undefined)),
39797
+ provider: args.resolvedProviderID,
39798
+ model: args.resolvedModelID,
39799
+ cacheBustingPass: true,
39800
+ stripActiveTurn: true,
39801
+ persistOnly: true
39802
+ });
39803
+ if (activeThinkingTurn && !result)
39804
+ throw new Error("Cannot persist signed thinking omission before prefix rewrite");
39805
+ rewritingSignedPrefix = true;
39806
+ activeThinkingTurn = false;
39807
+ protectedSignedPrefix = false;
39808
+ };
39809
+ if (args.forceSignedRebuildForFit && protectedSignedPrefix)
39810
+ stripForPrefixRewrite();
39439
39811
  const realStableId = (msg, index) => resolvePiRealStableId(msg, index, args.entryIds, args.entryIdByRef ?? undefined);
39440
39812
  const passFallbackIds = settlePiFallbackOccurrences(args.db, args.sessionId, piContentFallbackIds(args.messages, realStableId));
39441
39813
  const stableIdResolver = (msg, index) => realStableId(msg, index) ?? passFallbackIds[index] ?? resolvePiStableId(msg, index);
@@ -39510,57 +39882,6 @@ async function runPipeline(args) {
39510
39882
  let casLost = false;
39511
39883
  let injectionPassSnapshot = args.injectionPassSnapshot;
39512
39884
  const deferredHistoryWasPendingAtPassStart = deferredHistoryRefreshSessions.has(args.sessionId);
39513
- const temporalCandidates = args.temporalAwareness ? collectPiTemporalCandidates(args.messages, args.entryIds ?? []) : new Map;
39514
- for (const entry of readPiIdentityDecisions(args.db, args.sessionId) ?? []) {
39515
- const decision = decodePiContentDecision(entry);
39516
- if (decision?.[0] === "seam-temporal-strip")
39517
- temporalCandidates.set(decision[1], "");
39518
- }
39519
- let temporalDecisions = args.temporalAwareness ? observeTemporalDecisions(args.db, args.sessionId, temporalCandidates, (ids) => readServedTemporalDecisions(args.db, args.sessionId, "pi", ids)) : new Map;
39520
- if (args.temporalAwareness) {
39521
- const tTemporal = performance.now();
39522
- try {
39523
- const injected = injectPiTemporalMarkers(args.messages, temporalDecisions, stableIdResolver);
39524
- if (injected > 0) {
39525
- sessionLog2(args.sessionId, `temporal-awareness: injected ${injected} gap markers`);
39526
- }
39527
- } catch (err) {
39528
- sessionLog2(args.sessionId, `temporal-awareness failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
39529
- }
39530
- logTransformTiming(args.sessionId, "injectTemporalMarkers", tTemporal);
39531
- }
39532
- let nativeRemovalInputs;
39533
- try {
39534
- nativeRemovalInputs = getNativeReplayState(args.db, args.sessionId).toolInputs;
39535
- } catch {}
39536
- const tTranscriptBuild = performance.now();
39537
- const transcript = createPiTranscript(args.messages, args.sessionId, args.messages.map(stableIdResolver), {
39538
- preserveReasoningToolArcs: args.reasoningClearing?.preserveReasoningToolArcs,
39539
- authorizeToolRemoval: (callId) => authorizePiToolRemoval({
39540
- db: args.db,
39541
- sessionId: args.sessionId,
39542
- callId,
39543
- saved: nativeRemovalInputs,
39544
- canApply: isCacheBustingPass
39545
- }),
39546
- authorizeToolRemovals: (callIds) => preparePiToolRemovalMeasurements({
39547
- db: args.db,
39548
- sessionId: args.sessionId,
39549
- callIds,
39550
- saved: nativeRemovalInputs,
39551
- canApply: isCacheBustingPass
39552
- })
39553
- });
39554
- logTransformTiming(args.sessionId, "transcriptBuild", tTranscriptBuild);
39555
- const workingMessages = transcript.getWorkingMessages();
39556
- const currentTurnId = (() => {
39557
- const ids = buildPiMessageIdByIndex(args.messages, args.entryIds ?? null);
39558
- return findLatestUserMessageIdPi(args.messages, ids)?.messageId ?? null;
39559
- })();
39560
- const alreadyRanHeuristicsThisTurn = currentTurnId !== null && lastHeuristicsTurnIdBySession.get(args.sessionId) === currentTurnId;
39561
- const ctxReduceCallable = !args.sessionMeta.isSubagent;
39562
- const canConsumeDeferredLate = args.schedulerDecision === "execute" || args.forceMaterialization === true || args.contextUsage.percentage >= forceMaterializationPercentage;
39563
- const deferredMaterializeEligible = canConsumeDeferredLate && deferredMaterializationSessions.has(args.sessionId);
39564
39885
  const piHardSignals = args.injection ? (() => {
39565
39886
  const hardMeta = args.sessionMeta;
39566
39887
  let piTtlMs = 5 * 60 * 1000;
@@ -39600,18 +39921,28 @@ async function runPipeline(args) {
39600
39921
  let preFoldInjectionResult = null;
39601
39922
  const persistedM0BeforeFold = injectionPassSnapshot?.sessionMeta ?? args.sessionMeta;
39602
39923
  const m0CoverageBeforeFold = persistedM0BeforeFold.cachedM0Bytes === null ? -1 : persistedM0BeforeFold.cachedM0MaxCompartmentSeq;
39603
- if (protectedSignedPrefix && piM0State) {
39924
+ if (protectedSignedPrefix && !firstRenderBust && piM0State) {
39604
39925
  piM0State.preparedPrefix = prepareCachedM0M1PiReplay(piM0State, args.db, injectionPassSnapshot?.cachedRow);
39605
39926
  }
39606
- if (!protectedSignedPrefix && (foldDueDecision.value || softRefreshOpportunity) && piM0State) {
39927
+ if ((!protectedSignedPrefix || firstRenderBust) && (foldDueDecision.value || softRefreshOpportunity) && piM0State) {
39607
39928
  try {
39608
39929
  preFoldInjectionResult = injectM0M1PiForRun(piM0State, args.db, [], undefined, softRefreshOpportunity, injectionPassSnapshot);
39930
+ if (protectedSignedPrefix && firstRenderBust) {
39931
+ const candidate = getOrCreateSessionMeta(args.db, args.sessionId);
39932
+ if (!servedPrefixMatches({
39933
+ messages: args.readLastServedMessages?.(),
39934
+ m0Text: candidate.cachedM0Bytes?.toString(),
39935
+ m1Text: candidate.cachedM1Bytes?.toString(),
39936
+ muralDataUrl: candidate.cachedM0MuralDataUrl
39937
+ }))
39938
+ stripForPrefixRewrite();
39939
+ }
39609
39940
  prefixPreflightContended = preFoldInjectionResult.contentionExhausted === true;
39610
39941
  publishedM1RefreshedThisPass = !prefixPreflightContended && persistedM0BeforeFold.cachedM1Bytes?.toString("utf8") !== getOrCreateSessionMeta(args.db, args.sessionId).cachedM1Bytes?.toString("utf8");
39611
39942
  foldExecutedThisPass = foldExecutesThisPass(foldDueDecision.value || softRefreshOpportunity, preFoldInjectionResult.m0Materialized === true);
39612
39943
  if (foldExecutedThisPass) {
39613
39944
  const afterFold = getOrCreateSessionMeta(args.db, args.sessionId);
39614
- foldBustsServedPrefixThisPass = foldBustsServedPrefix(foldDueDecision.reason, {
39945
+ foldBustsServedPrefixThisPass = !protectedSignedPrefix && foldBustsServedPrefix(foldDueDecision.reason, {
39615
39946
  m0Bytes: persistedM0BeforeFold.cachedM0Bytes ?? null,
39616
39947
  m1Bytes: persistedM0BeforeFold.cachedM1Bytes ?? null,
39617
39948
  muralDataUrl: persistedM0BeforeFold.cachedM0MuralDataUrl ?? null
@@ -39650,6 +39981,58 @@ async function runPipeline(args) {
39650
39981
  const mismatch = foldDueDecision.mismatch ? ` mismatch=${JSON.stringify(foldDueDecision.mismatch)}` : "";
39651
39982
  sessionLog2(args.sessionId, `pi m[0] HARD fold decision: reason=${preFoldInjectionResult?.m0Reason ?? foldDueDecision.reason ?? "soft_refresh"}${mismatch} executed=${foldExecutedThisPass} bustsServedPrefix=${foldBustsServedPrefixThisPass}`);
39652
39983
  }
39984
+ const temporalCandidates = args.temporalAwareness ? collectPiTemporalCandidates(args.messages, args.entryIds ?? []) : new Map;
39985
+ for (const entry of readPiIdentityDecisions(args.db, args.sessionId) ?? []) {
39986
+ const decision = decodePiContentDecision(entry);
39987
+ if (decision?.[0] === "seam-temporal-strip")
39988
+ temporalCandidates.set(decision[1], "");
39989
+ }
39990
+ let temporalDecisions = args.temporalAwareness ? observeTemporalDecisions(args.db, args.sessionId, temporalCandidates, (ids) => readServedTemporalDecisions(args.db, args.sessionId, "pi", ids)) : new Map;
39991
+ if (args.temporalAwareness) {
39992
+ const tTemporal = performance.now();
39993
+ try {
39994
+ const injected = injectPiTemporalMarkers(args.messages, temporalDecisions, stableIdResolver);
39995
+ if (injected > 0) {
39996
+ sessionLog2(args.sessionId, `temporal-awareness: injected ${injected} gap markers`);
39997
+ }
39998
+ } catch (err) {
39999
+ sessionLog2(args.sessionId, `temporal-awareness failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
40000
+ }
40001
+ logTransformTiming(args.sessionId, "injectTemporalMarkers", tTemporal);
40002
+ }
40003
+ let nativeRemovalInputs;
40004
+ try {
40005
+ nativeRemovalInputs = getNativeReplayState(args.db, args.sessionId).toolInputs;
40006
+ } catch {}
40007
+ const tTranscriptBuild = performance.now();
40008
+ const transcript = createPiTranscript(args.messages, args.sessionId, args.messages.map(stableIdResolver), {
40009
+ preserveReasoningToolArcs: args.reasoningClearing?.preserveReasoningToolArcs,
40010
+ authorizeToolRemoval: (callId) => authorizePiToolRemoval({
40011
+ db: args.db,
40012
+ sessionId: args.sessionId,
40013
+ callId,
40014
+ saved: nativeRemovalInputs,
40015
+ canApply: isCacheBustingPass
40016
+ }),
40017
+ authorizeToolRemovals: (callIds) => preparePiToolRemovalMeasurements({
40018
+ db: args.db,
40019
+ sessionId: args.sessionId,
40020
+ callIds,
40021
+ saved: nativeRemovalInputs,
40022
+ canApply: isCacheBustingPass
40023
+ })
40024
+ });
40025
+ logTransformTiming(args.sessionId, "transcriptBuild", tTranscriptBuild);
40026
+ const workingMessages = transcript.getWorkingMessages();
40027
+ workingForRewrite = workingMessages;
40028
+ const currentTurnId = (() => {
40029
+ const ids = buildPiMessageIdByIndex(args.messages, args.entryIds ?? null);
40030
+ return findLatestUserMessageIdPi(args.messages, ids)?.messageId ?? null;
40031
+ })();
40032
+ const alreadyRanHeuristicsThisTurn = currentTurnId !== null && lastHeuristicsTurnIdBySession.get(args.sessionId) === currentTurnId;
40033
+ const ctxReduceCallable = !args.sessionMeta.isSubagent;
40034
+ const canConsumeDeferredLate = args.schedulerDecision === "execute" || args.forceMaterialization === true || args.contextUsage.percentage >= forceMaterializationPercentage;
40035
+ let deferredMaterializeEligible = canConsumeDeferredLate && deferredMaterializationSessions.has(args.sessionId) && !protectedSignedPrefix;
39653
40036
  const executePressureEligible = args.schedulerDecision === "execute" || emergencyDropEligible;
39654
40037
  if (!executePressureEligible) {
39655
40038
  routinePressureAppliedBySession.delete(args.sessionId);
@@ -39657,21 +40040,17 @@ async function runPipeline(args) {
39657
40040
  routinePressureAppliedBySession.set(args.sessionId, true);
39658
40041
  }
39659
40042
  const routinePressureAlreadyApplied = !args.sessionMeta.isSubagent && executePressureEligible && routinePressureAppliedBySession.get(args.sessionId) === true;
39660
- const hasPendingMaterializeSignal = hasPendingMaterialization(args.sessionId);
40043
+ let hasPendingMaterializeSignal = !protectedSignedPrefix && hasPendingMaterialization(args.sessionId);
40044
+ const heldIdleExpiryRebuild = protectedSignedPrefix && piHardSignals?.cacheExpired === true;
39661
40045
  const rideSignals = {
39662
40046
  subagentExecute: args.sessionMeta.isSubagent && args.schedulerDecision === "execute",
39663
- hardFold: foldBustsServedPrefixThisPass || firstRenderBust,
40047
+ hardFold: foldBustsServedPrefixThisPass || firstRenderBust || heldIdleExpiryRebuild,
39664
40048
  force: (args.forceMaterialization === true || emergencyDropEligible) && (args.contextUsage.percentage >= 95 || getEmergencyInputSample(args.db, args.sessionId) === 0),
39665
40049
  explicitFlush: hasPendingMaterializeSignal || deferredMaterializeEligible && !prefixPreflightContended,
39666
40050
  publishedHistory: !prefixPreflightContended && (args.isCacheBusting || publishedM1RefreshedThisPass || canConsumeDeferredLate && deferredHistoryWasPendingAtPassStart)
39667
40051
  };
39668
- let isCacheBustingPass = hasReclaimRide(rideSignals);
39669
- if (args.temporalAwareness && isCacheBustingPass) {
39670
- temporalDecisions = freezeTemporalDecisions(args.db, args.sessionId, temporalCandidates);
39671
- if (injectPiTemporalMarkers(args.messages, temporalDecisions, stableIdResolver) > 0) {
39672
- recordFirstApplicationWireEdit(true);
39673
- }
39674
- }
40052
+ let prefixMutationAllowed = !protectedSignedPrefix || firstRenderBust || heldIdleExpiryRebuild || emergencyDropEligible;
40053
+ let isCacheBustingPass = prefixMutationAllowed && hasReclaimRide(rideSignals);
39675
40054
  let publishedWorkDrainAllowed = isCacheBustingPass;
39676
40055
  const usesTokenProtection = args.protectedTokenTierOverrides !== undefined || args.protectedTokens !== undefined;
39677
40056
  const resolveProtectionFloor = () => resolveEpochFloorForPass(args.db, args.sessionId, {
@@ -39682,44 +40061,99 @@ async function runPipeline(args) {
39682
40061
  onRejectedProjectOverride: (warning) => sessionLog2(args.sessionId, warning)
39683
40062
  });
39684
40063
  let protectionFloorResolution = resolveProtectionFloor();
39685
- const shouldRunHeuristics = args.heuristics !== undefined && isCacheBustingPass && (rideSignals.publishedHistory || args.forceMaterialization === true || hasPendingMaterializeSignal || deferredMaterializeEligible || foldBustsServedPrefixThisPass || firstRenderBust || rideSignals.subagentExecute || args.schedulerDecision === "execute" && !alreadyRanHeuristicsThisTurn);
40064
+ let shouldRunHeuristics = args.heuristics !== undefined && isCacheBustingPass && (rideSignals.publishedHistory || args.forceMaterialization === true || hasPendingMaterializeSignal || deferredMaterializeEligible || foldBustsServedPrefixThisPass || firstRenderBust || heldIdleExpiryRebuild || rideSignals.subagentExecute || args.schedulerDecision === "execute" && !alreadyRanHeuristicsThisTurn);
40065
+ let unavoidableCarriedIdentityRebuild = false;
40066
+ if (protectedSignedPrefix && readPiIdentityRebuilds(args.db, args.sessionId).length > 0) {
40067
+ args.tryReplayHeldIdentityRebuild?.();
40068
+ stripForPrefixRewrite();
40069
+ unavoidableCarriedIdentityRebuild = true;
40070
+ prefixMutationAllowed = true;
40071
+ args.tagger.cleanup(args.sessionId);
40072
+ args.tagger.initFromDb(args.sessionId, args.db);
40073
+ }
40074
+ const tTagSnapshotSync = performance.now();
40075
+ readPiFallbackTagRows(args.db, args.sessionId);
40076
+ logTransformTiming(args.sessionId, "tagSnapshotSync", tTagSnapshotSync);
39686
40077
  const tFallbackIdentity = performance.now();
39687
40078
  const preflightRevision = readPiAdoptionRevision(args.db);
39688
40079
  const hasFallbackMessageTags = hasPiFallbackMessageTags(args.db, args.sessionId);
39689
- const entryFingerprintByMessageId = buildEntryFingerprintMap(args.messages, stableIdResolver, args.reusableMessageIds, hasFallbackMessageTags, hasFallbackMessageTags ? readPiFallbackFingerprintHeaders(args.db, args.sessionId) : undefined);
40080
+ let fallbackCache = piFallbackIndexesBySession.get(args.sessionId);
40081
+ if (!fallbackCache || fallbackCache.db !== args.db) {
40082
+ fallbackCache = { db: args.db, index: createPiFallbackIndex() };
40083
+ piFallbackIndexesBySession.set(args.sessionId, fallbackCache);
40084
+ }
40085
+ const fallbackIndex = fallbackCache.index.update({
40086
+ messages: args.messages,
40087
+ fields: args.fallbackInputFields,
40088
+ resolveId: stableIdResolver,
40089
+ header: piMessageEntryFingerprintHeader,
40090
+ fingerprint: piMessageEntryFingerprint2,
40091
+ toolOwners: (message, id) => buildPiToolOwnerMap([message], () => id),
40092
+ reusable: args.reusableMessageIds
40093
+ });
40094
+ const entryFingerprintByMessageId = fallbackIndex.fingerprints(hasFallbackMessageTags ? readPiFallbackFingerprintHeaders(args.db, args.sessionId) : undefined);
39690
40095
  args.assertCurrentPass?.();
39691
40096
  const identityAdoption = adoptPiFallbackTags(args.db, args.sessionId, args.tagger, entryFingerprintByMessageId, {
39692
40097
  allowUnprovenRebuild: true,
40098
+ deferUnprovenRebuild: !prefixMutationAllowed,
39693
40099
  messages: args.messages,
39694
40100
  resolveStableId: stableIdResolver,
40101
+ toolOwners: fallbackIndex.owners,
40102
+ realIdPositions: fallbackIndex.positions,
39695
40103
  hasFallbackMessageTags,
39696
40104
  preflightRevision,
39697
40105
  rebuildFingerprints: args.reusableMessageIds?.size ? () => {
39698
40106
  args.assertCurrentPass?.();
39699
- const full = buildEntryFingerprintMap(args.messages, stableIdResolver, args.reusableMessageIds, true, readPiFallbackFingerprintHeaders(args.db, args.sessionId));
40107
+ const full = fallbackIndex.fingerprints(readPiFallbackFingerprintHeaders(args.db, args.sessionId));
39700
40108
  for (const [id, fingerprint] of full)
39701
40109
  entryFingerprintByMessageId.set(id, fingerprint);
39702
40110
  return entryFingerprintByMessageId;
39703
40111
  } : undefined
39704
40112
  });
39705
- const identityRebuild = identityAdoption.rebuilds.length > 0;
40113
+ let identityRebuild = unavoidableCarriedIdentityRebuild || identityAdoption.rebuilds.length > 0;
39706
40114
  if (identityRebuild) {
40115
+ if (!prefixMutationAllowed) {
40116
+ if (!unavoidableCarriedIdentityRebuild) {
40117
+ args.tryReplayHeldIdentityRebuild?.();
40118
+ stripForPrefixRewrite();
40119
+ args.tagger.cleanup(args.sessionId);
40120
+ args.tagger.initFromDb(args.sessionId, args.db);
40121
+ }
40122
+ prefixMutationAllowed = true;
40123
+ }
39707
40124
  args.schedulerDecision = "execute";
39708
- isCacheBustingPass = true;
39709
- publishedWorkDrainAllowed = true;
39710
- if (piM0State) {
39711
- clearM0M1PiCache(args.db, args.sessionId, PI_TAG_IDENTITY_REPAIR_REASON);
39712
- piM0State.preparedPrefix = undefined;
39713
- piM0State.freezePrefixForPass = false;
39714
- injectionPassSnapshot = createPiM0M1PassSnapshot({
39715
- db: args.db,
39716
- sessionId: args.sessionId,
39717
- compactionOff: false
39718
- });
40125
+ hasPendingMaterializeSignal = hasPendingMaterialization(args.sessionId);
40126
+ deferredMaterializeEligible = canConsumeDeferredLate && deferredMaterializationSessions.has(args.sessionId);
40127
+ isCacheBustingPass = prefixMutationAllowed;
40128
+ publishedWorkDrainAllowed = isCacheBustingPass;
40129
+ shouldRunHeuristics = args.heuristics !== undefined;
40130
+ if (piM0State && prefixMutationAllowed) {
40131
+ try {
40132
+ clearM0M1PiCache(args.db, args.sessionId, PI_TAG_IDENTITY_REPAIR_REASON);
40133
+ piM0State.preparedPrefix = undefined;
40134
+ piM0State.freezePrefixForPass = false;
40135
+ injectionPassSnapshot = createPiM0M1PassSnapshot({
40136
+ db: args.db,
40137
+ sessionId: args.sessionId,
40138
+ compactionOff: false
40139
+ });
40140
+ } catch (error) {
40141
+ if (claimPiIdentityRebuildFailure(args.db, args.sessionId) === "first")
40142
+ throw error;
40143
+ identityRebuild = false;
40144
+ piM0State.preparedPrefix ??= prepareCachedM0M1PiReplay(piM0State, args.db, injectionPassSnapshot?.cachedRow);
40145
+ piM0State.freezePrefixForPass = true;
40146
+ sessionLog2(args.sessionId, "Pi identity rebuild failed again; serving with the repair still pending");
40147
+ }
39719
40148
  }
39720
40149
  }
40150
+ if (args.temporalAwareness && isCacheBustingPass && !protectedSignedPrefix) {
40151
+ temporalDecisions = freezeTemporalDecisions(args.db, args.sessionId, temporalCandidates);
40152
+ if (injectPiTemporalMarkers(args.messages, temporalDecisions, stableIdResolver) > 0)
40153
+ recordFirstApplicationWireEdit(true);
40154
+ }
39721
40155
  logPiUnresolvedIdentities(args.sessionId, [
39722
- ...guardPiToolAllocations(args.db, args.sessionId, args.messages, stableIdResolver, args.tagger),
40156
+ ...guardPiToolAllocations(args.db, args.sessionId, args.messages, stableIdResolver, args.tagger, fallbackIndex.guardIndexes),
39723
40157
  ...guardPiMessageAllocations(args.db, args.sessionId, entryFingerprintByMessageId)
39724
40158
  ]);
39725
40159
  logTransformTiming(args.sessionId, "fallbackIdentityAndAdoption", tFallbackIdentity);
@@ -39764,26 +40198,45 @@ async function runPipeline(args) {
39764
40198
  const frozenThinkingForProtection = activeThinkingTurn ? frozenBindingEntryIds(args.db, args.sessionId) : new Set;
39765
40199
  const protectedThinkingProxies = new Set;
39766
40200
  if (activeThinkingTurn) {
39767
- for (let i = latestAssistantTurnStart(workingMessages);i < mutationView.length; i++) {
39768
- const id = stableIdResolver(workingMessages[i], i);
40201
+ const rawById = new Map(workingMessages.map((message, index) => [
40202
+ stableIdResolver(message, index),
40203
+ message
40204
+ ]));
40205
+ for (let i = latestAssistantTurnStart(mutationView);i < mutationView.length; i++) {
40206
+ const id = mutationView[i].info.id;
39769
40207
  if (!thinkingRecovery.restore && id && frozenThinkingForProtection.has(id))
39770
40208
  continue;
39771
40209
  for (let p = 0;p < mutationView[i].parts.length; p++) {
39772
40210
  const part = mutationView[i].parts[p];
39773
40211
  if (part.kind !== "thinking")
39774
40212
  continue;
39775
- const local = workingMessages[i].content?.[p];
39776
- if (!thinkingRecovery.restore && !local?.redacted && id && (reasoningTagsForProtection.get(id) ?? Infinity) <= (args.sessionMeta.clearedReasoningThroughTag ?? 0))
40213
+ const raw = rawById.get(id);
40214
+ const redacted = raw?.content?.some((part) => part.redacted === true);
40215
+ if (!thinkingRecovery.restore && !redacted && id && (reasoningTagsForProtection.get(id) ?? Infinity) <= (args.sessionMeta.clearedReasoningThroughTag ?? 0))
39777
40216
  continue;
39778
40217
  protectedThinkingProxies.add(part);
39779
40218
  }
39780
40219
  }
39781
40220
  }
39782
- const newTargets = new Map(protectNewTagMutations(mutationView.map((message) => ({
40221
+ let newTargets = new Map(protectNewTagMutations(mutationView.map((message) => ({
39783
40222
  info: message.info,
39784
40223
  parts: message.parts
39785
40224
  })), targets, protectedThinkingProxies, args.reasoningClearing?.prefixBound === true));
40225
+ if (protectedSignedPrefix && args.contextUsage.percentage >= 95) {
40226
+ const candidates = getActiveTagsBySession2(args.db, args.sessionId).filter((tag) => tag.type === "tool" && targets.get(tag.tagNumber)?.canDrop?.());
40227
+ if (candidates.length > 0 && candidates.every((tag) => newTargets.get(tag.tagNumber)?.thinkingRewriteProtected === true)) {
40228
+ stripForPrefixRewrite();
40229
+ newTargets = targets;
40230
+ prefixMutationAllowed = true;
40231
+ isCacheBustingPass = true;
40232
+ publishedWorkDrainAllowed = true;
40233
+ shouldRunHeuristics = args.heuristics !== undefined;
40234
+ hasPendingMaterializeSignal = hasPendingMaterialization(args.sessionId);
40235
+ }
40236
+ }
39786
40237
  for (const number of identityAdoption.protectedTagNumbers) {
40238
+ if (rewritingSignedPrefix)
40239
+ break;
39787
40240
  const target = newTargets.get(number);
39788
40241
  if (!target)
39789
40242
  continue;
@@ -39801,7 +40254,7 @@ async function runPipeline(args) {
39801
40254
  setContent: () => false
39802
40255
  });
39803
40256
  }
39804
- if (thinkingRecovery.restore) {
40257
+ if (thinkingRecovery.restore && !rewritingSignedPrefix) {
39805
40258
  transcript.commit();
39806
40259
  restoreOriginals = captureOriginalTurn(args.messages, (message) => message.content, (message, content) => {
39807
40260
  message.content = content;
@@ -39947,8 +40400,8 @@ async function runPipeline(args) {
39947
40400
  let routineCleanupApplied = false;
39948
40401
  let heuristicsResult = null;
39949
40402
  const tActiveTags = performance.now();
39950
- const allTagsForPass = getPiTagSnapshot(args.db, args.sessionId);
39951
- const activeTags = allTagsForPass.filter((tag) => tag.status === "active");
40403
+ const allTagsForPass = getPiTagSnapshotView(args.db, args.sessionId);
40404
+ const activeTags = getPiActiveTagSnapshotView(args.db, args.sessionId, allTagsForPass);
39952
40405
  logTransformTiming(args.sessionId, "getTagsBySessionSnapshot", tActiveTags, `all=${allTagsForPass.length} active=${activeTags.length}`);
39953
40406
  if (shouldRunHeuristics) {
39954
40407
  const reason = args.forceMaterialization ? "force_materialization" : foldBustsServedPrefixThisPass && args.schedulerDecision !== "execute" ? `m0_hard_fold (drain folded into executed m[0] bust, scheduler=${args.schedulerDecision})` : `${reclaimRideLabel(rideSignals)} (pendingOps=${pendingOps.length}, scheduler=${args.schedulerDecision})`;
@@ -39964,7 +40417,7 @@ async function runPipeline(args) {
39964
40417
  if (shouldRunHeuristics && args.heuristics) {
39965
40418
  try {
39966
40419
  const tHeuristic = performance.now();
39967
- const independentMutationBeforeHeuristics = pendingOpsDidMutate || foldBustsServedPrefixThisPass;
40420
+ const independentMutationBeforeHeuristics = pendingOpsDidMutate || foldBustsServedPrefixThisPass || identityRebuild || firstRenderBust || heldIdleExpiryRebuild;
39968
40421
  if (args.forceMaterialization === true && independentMutationBeforeHeuristics && getEmergencyInputSample(args.db, args.sessionId) > 0) {
39969
40422
  clearEmergencyDropSample(args.db, args.sessionId);
39970
40423
  }
@@ -40044,7 +40497,8 @@ async function runPipeline(args) {
40044
40497
  }
40045
40498
  let reasoningPersistenceFailed = false;
40046
40499
  let reasoningBudgetCutoffThisPass = 0;
40047
- if (args.reasoningClearing && shouldRunHeuristics && routineCleanupApplied) {
40500
+ const nativeBudgetClearingAllowed = Boolean(args.reasoningClearing && args.reasoningClearing.prefixBound !== true);
40501
+ if (args.reasoningClearing && nativeBudgetClearingAllowed && shouldRunHeuristics && routineCleanupApplied && !protectedSignedPrefix) {
40048
40502
  const rollbackReasoning = captureReasoningMutationRollback(workingMessages);
40049
40503
  try {
40050
40504
  const tClearReasoning = performance.now();
@@ -40183,7 +40637,7 @@ async function runPipeline(args) {
40183
40637
  db: args.db,
40184
40638
  sessionId: args.sessionId,
40185
40639
  messages: workingMessages,
40186
- detect: isCacheBustingPass,
40640
+ detect: isCacheBustingPass && !protectedSignedPrefix,
40187
40641
  watermark: getMaxDroppedTagNumber2(args.db, args.sessionId),
40188
40642
  messageIdToMaxTag,
40189
40643
  stableId: stableIdResolver,
@@ -40201,7 +40655,7 @@ async function runPipeline(args) {
40201
40655
  db: args.db,
40202
40656
  sessionId: args.sessionId,
40203
40657
  activeTags,
40204
- targets,
40658
+ targets: newTargets,
40205
40659
  legacyReminderTagNumbers: textIdentityPlan.legacyReminderTagNumbers,
40206
40660
  cacheBusting: isCacheBustingPass
40207
40661
  });
@@ -40295,11 +40749,11 @@ async function runPipeline(args) {
40295
40749
  stripPiLeadingTemporalMarker(message);
40296
40750
  }
40297
40751
  }
40298
- if (args.isCacheBusting) {
40752
+ if (args.isCacheBusting && prefixMutationAllowed) {
40299
40753
  historyRefreshSessions.delete(args.sessionId);
40300
40754
  historyWasConsumedThisPass = true;
40301
40755
  }
40302
- if (deferredHistoryRefresh) {
40756
+ if (deferredHistoryRefresh && prefixMutationAllowed) {
40303
40757
  historyWasConsumedThisPass = true;
40304
40758
  }
40305
40759
  logTransformTiming(args.sessionId, "prepareCompartmentInjection", tInjection);
@@ -40314,9 +40768,9 @@ async function runPipeline(args) {
40314
40768
  db: args.db,
40315
40769
  sessionId: args.sessionId,
40316
40770
  messages: args.messages,
40317
- isCacheBusting: args.isCacheBusting,
40771
+ isCacheBusting: args.isCacheBusting && prefixMutationAllowed,
40318
40772
  stableIdByRef: postCommitStableIdByRef,
40319
- forceDiscovery: args.stableIdSchemeCutover === true,
40773
+ forceDiscovery: args.stableIdSchemeCutover === true && prefixMutationAllowed,
40320
40774
  canFirstApply: isCacheBustingPass,
40321
40775
  onFirstApplication: (message, index) => recordFirstApplicationAt(message, index, 0, (item) => item && typeof item === "object" ? postCommitStableIdByRef.get(item) : undefined)
40322
40776
  });
@@ -40466,10 +40920,12 @@ async function runPipeline(args) {
40466
40920
  onAdopt: (message) => sessionLog2(args.sessionId, message)
40467
40921
  });
40468
40922
  protectionFloorResolution = resolveProtectionFloor();
40469
- const protectedTagNumbers = usesTokenProtection ? computeProtectionWindow(allTagsForPass, protectionFloorResolution.floor, activeCalibration.toolsRatio).protectedTagNumbers : newestActiveTagNumbersByCount(allTagsForPass, args.protectedTags);
40923
+ const protectedTagNumbers = usesTokenProtection ? computeProtectionWindow(allTagsForPass, protectionFloorResolution.floor, activeCalibration.toolsRatio).protectedTagNumbers : newestActiveTagNumbersByCount(activeTags, args.protectedTags);
40470
40924
  const channelPendingOps = shouldApplyPendingOps ? getPendingOps(args.db, args.sessionId) : shouldReadPendingOps ? pendingOps : getPendingOps(args.db, args.sessionId);
40471
40925
  const channelBaselineSnapshot = {
40926
+ tagCacheKey: fallbackCache.index,
40472
40927
  tags: allTagsForPass,
40928
+ activeTags,
40473
40929
  protectedTagNumbers,
40474
40930
  pendingDropTagNumbers: new Set(channelPendingOps.filter((operation) => operation.operation === "drop").map((operation) => operation.tagId))
40475
40931
  };
@@ -40487,9 +40943,20 @@ async function runPipeline(args) {
40487
40943
  }
40488
40944
  }
40489
40945
  }
40490
- 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));
40946
+ const servedTagNumbers = new Set;
40947
+ for (const number of [...targets.keys()].sort((a, b) => a - b)) {
40948
+ for (const tag of tagsWithNumber(allTagsForPass, number)) {
40949
+ if (tag.type === "message" ? ctxReduceCallable || tag.status !== "active" : tag.type === "tool" && visibleCallIds.has(tag.messageId) && (ctxReduceCallable || tag.status !== "active"))
40950
+ servedTagNumbers.add(tag.tagNumber);
40951
+ }
40952
+ }
40491
40953
  return {
40492
40954
  messages: outputMessages,
40955
+ prefixBustPermitted: isCacheBustingPass,
40956
+ prefixMutationAllowed,
40957
+ strictThinkingKept: protectedSignedPrefix,
40958
+ signedPrefixRewritten: rewritingSignedPrefix,
40959
+ prefixRebuildAlreadyPriced: firstRenderBust || heldIdleExpiryRebuild,
40493
40960
  identityRebuilt: identityRebuild,
40494
40961
  servedTagNumbers,
40495
40962
  heuristicsExecuted,
@@ -40585,7 +41052,7 @@ function applyNoteNudges(args) {
40585
41052
  }
40586
41053
  const autoSearchDecisions = args.postTransformSnapshot?.autoSearchDecisions ?? getAutoSearchHintDecisions(db, sessionId);
40587
41054
  for (const decision of autoSearchDecisions) {
40588
- if (decision.decision === "hint") {
41055
+ if (decision.decision === "hint" && getUnpublishedAutoSearchNoHint(sessionId, decision.messageId) === undefined) {
40589
41056
  appendReminderToUserMessageByIdPi(messages, replayMessageIdByIndex, decision.messageId, decision.text);
40590
41057
  }
40591
41058
  }
@@ -40628,7 +41095,7 @@ function applyNoteNudges(args) {
40628
41095
  const allResolved = messageIdByIndex.size === realMessageCount;
40629
41096
  if (allResolved && visibleIds.size > 0) {
40630
41097
  pruneNoteNudgeAnchors(db, sessionId, visibleIds);
40631
- pruneAutoSearchHintDecisions(db, sessionId, visibleIds);
41098
+ flushAutoSearchHintDecisionRetirement(db, sessionId, visibleIds);
40632
41099
  }
40633
41100
  }
40634
41101
  return messages;
@@ -40766,6 +41233,7 @@ function clearContextHandlerSession(sessionId) {
40766
41233
  piTagTextTokenCacheBySession.delete(sessionId);
40767
41234
  piTagToolTokenCacheBySession.delete(sessionId);
40768
41235
  piTextIdentitySourceCacheBySession.delete(sessionId);
41236
+ piFallbackIndexesBySession.delete(sessionId);
40769
41237
  piBranchProjectionBySession.delete(sessionId);
40770
41238
  piAlignmentMismatchLoggedSessions.delete(sessionId);
40771
41239
  clearPiOrdinalAlignmentSession(sessionId);
@@ -43415,7 +43883,7 @@ function buildStatusViewFor(check, options) {
43415
43883
  // package.json
43416
43884
  var package_default = {
43417
43885
  name: "@cortexkit/pi-magic-context",
43418
- version: "0.48.0",
43886
+ version: "0.49.0",
43419
43887
  type: "module",
43420
43888
  description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
43421
43889
  main: "dist/index.js",
@@ -45194,7 +45662,7 @@ function ensureConfigLocationsMigrated(dir) {
45194
45662
  });
45195
45663
  }
45196
45664
  function logPiConfigLoad(args) {
45197
- const key = resolve4(args.dir);
45665
+ const key = resolve3(args.dir);
45198
45666
  if (args.dedupe && loggedPiConfigDirs.has(key))
45199
45667
  return;
45200
45668
  if (args.dedupe) {
@@ -45916,6 +46384,7 @@ async function startPiMagicContextRuntime(pi, database, dbPath) {
45916
46384
  const todowriteEnabled = bootProjectDeps.config.todowrite.enabled;
45917
46385
  const todowriteOverlayEnabled = todowriteEnabled && bootProjectDeps.config.todowrite.overlay;
45918
46386
  registerMagicContextTools(pi, {
46387
+ rerankConnectionFile: config.subc?.connection_file,
45919
46388
  expandTools: config.historian?.expand_tools,
45920
46389
  db,
45921
46390
  ensureProjectRegistered: ensureProjectRegisteredFromPiDirectory,