@cortexkit/opencode-magic-context 0.41.1 → 0.41.2

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 (97) hide show
  1. package/dist/{derive-budgets-bb7mrpzs.js → derive-budgets-9q2d3r93.js} +1 -1
  2. package/dist/features/magic-context/compartment-chunk-embedding.d.ts +4 -1
  3. package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
  4. package/dist/features/magic-context/compartment-embedding.d.ts.map +1 -1
  5. package/dist/features/magic-context/dreamer/curate-memory-safety.d.ts +21 -0
  6. package/dist/features/magic-context/dreamer/curate-memory-safety.d.ts.map +1 -0
  7. package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
  8. package/dist/features/magic-context/dreamer/task-prompts.d.ts +1 -6
  9. package/dist/features/magic-context/dreamer/task-prompts.d.ts.map +1 -1
  10. package/dist/features/magic-context/memory/embedding-failure.d.ts +10 -2
  11. package/dist/features/magic-context/memory/embedding-failure.d.ts.map +1 -1
  12. package/dist/features/magic-context/memory/embedding-local.d.ts +7 -0
  13. package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
  14. package/dist/features/magic-context/memory/onnxruntime-node-wasm-shim.d.ts +3 -0
  15. package/dist/features/magic-context/memory/onnxruntime-node-wasm-shim.d.ts.map +1 -0
  16. package/dist/features/magic-context/memory/sharp-wasm-shim.d.ts +2 -0
  17. package/dist/features/magic-context/memory/sharp-wasm-shim.d.ts.map +1 -0
  18. package/dist/features/magic-context/memory/transformers-node-wasm-entry.d.ts +3 -0
  19. package/dist/features/magic-context/memory/transformers-node-wasm-entry.d.ts.map +1 -0
  20. package/dist/features/magic-context/message-fts-rowid-map.d.ts +28 -0
  21. package/dist/features/magic-context/message-fts-rowid-map.d.ts.map +1 -0
  22. package/dist/features/magic-context/message-index.d.ts.map +1 -1
  23. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  24. package/dist/features/magic-context/overflow-detection.d.ts +15 -2
  25. package/dist/features/magic-context/overflow-detection.d.ts.map +1 -1
  26. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  27. package/dist/features/magic-context/storage-db.d.ts +2 -1
  28. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  29. package/dist/features/magic-context/storage-meta-persisted.d.ts +19 -2
  30. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  31. package/dist/features/magic-context/storage-meta-session.d.ts +3 -2
  32. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  33. package/dist/features/magic-context/storage-meta.d.ts +2 -2
  34. package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
  35. package/dist/features/magic-context/storage-schema-helpers.d.ts.map +1 -1
  36. package/dist/features/magic-context/storage-session-tables.d.ts +9 -1
  37. package/dist/features/magic-context/storage-session-tables.d.ts.map +1 -1
  38. package/dist/features/magic-context/storage-tags.d.ts +13 -0
  39. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  40. package/dist/features/magic-context/storage.d.ts +1 -1
  41. package/dist/features/magic-context/storage.d.ts.map +1 -1
  42. package/dist/hooks/magic-context/event-handler.d.ts +4 -1
  43. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  44. package/dist/hooks/magic-context/format-embed-failure.d.ts.map +1 -1
  45. package/dist/hooks/magic-context/hook-handlers.d.ts +3 -0
  46. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  47. package/dist/hooks/magic-context/hook.d.ts +4 -2
  48. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  49. package/dist/hooks/magic-context/live-session-state.d.ts +3 -1
  50. package/dist/hooks/magic-context/live-session-state.d.ts.map +1 -1
  51. package/dist/hooks/magic-context/rust-mode-transform.d.ts +9 -1
  52. package/dist/hooks/magic-context/rust-mode-transform.d.ts.map +1 -1
  53. package/dist/hooks/magic-context/sentinel.d.ts +13 -37
  54. package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
  55. package/dist/hooks/magic-context/strip-content.d.ts +12 -5
  56. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  57. package/dist/hooks/magic-context/tag-id-fallback.d.ts +3 -1
  58. package/dist/hooks/magic-context/tag-id-fallback.d.ts.map +1 -1
  59. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  60. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +25 -2
  61. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  62. package/dist/hooks/magic-context/transform.d.ts +1 -1
  63. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  64. package/dist/{index-2d4kf2ve.js → index-cvda5jb3.js} +6 -1
  65. package/dist/{index-dh0vfghk.js → index-j8r0ejb3.js} +923 -127
  66. package/dist/{index-z893kjyt.js → index-y1yr57fw.js} +79 -15
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +1503 -1059
  69. package/dist/plugin/dream-timer.d.ts +4 -0
  70. package/dist/plugin/dream-timer.d.ts.map +1 -1
  71. package/dist/plugin/hooks/create-session-hooks.d.ts +2 -2
  72. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  73. package/dist/{recomp-orchestrator-w7xg48e3.js → recomp-orchestrator-m37p3fq6.js} +3 -3
  74. package/dist/shared/index.d.ts +1 -0
  75. package/dist/shared/index.d.ts.map +1 -1
  76. package/dist/shared/rpc-utils.d.ts +3 -1
  77. package/dist/shared/rpc-utils.d.ts.map +1 -1
  78. package/dist/shared/summarize-child-stderr.d.ts +2 -0
  79. package/dist/shared/summarize-child-stderr.d.ts.map +1 -0
  80. package/dist/shared/window-geometry.d.ts +2 -0
  81. package/dist/shared/window-geometry.d.ts.map +1 -1
  82. package/dist/{storage-meta-session-gr4zn7sq.js → storage-meta-session-dse3qv6s.js} +3 -1
  83. package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
  84. package/dist/tools/ctx-memory/types.d.ts +2 -0
  85. package/dist/tools/ctx-memory/types.d.ts.map +1 -1
  86. package/dist/tools/ctx-reduce/tools.d.ts.map +1 -1
  87. package/dist/transformers-node-wasm.js +25676 -0
  88. package/package.json +4 -3
  89. package/src/config/index.test.ts +41 -0
  90. package/src/config/latch-permanence-guard.test.ts +5 -0
  91. package/src/shared/index.ts +1 -0
  92. package/src/shared/model-resolution.test.ts +17 -13
  93. package/src/shared/rpc-utils.test.ts +246 -5
  94. package/src/shared/rpc-utils.ts +340 -49
  95. package/src/shared/summarize-child-stderr.test.ts +55 -0
  96. package/src/shared/summarize-child-stderr.ts +33 -0
  97. package/src/shared/window-geometry.ts +9 -0
package/dist/index.js CHANGED
@@ -55,6 +55,7 @@ import {
55
55
  MAX_EXECUTE_THRESHOLD,
56
56
  MagicContextConfigSchema,
57
57
  ModuleMemoryAuthorityError,
58
+ NEWEST_REASONING_BEARING_ASSISTANT,
58
59
  PER_HARNESS_MIGRATION_INVENTORY,
59
60
  PRIMER_CANDIDATE_MAX_AGE_MS,
60
61
  PRIMER_CANDIDATE_TTL_MS,
@@ -67,6 +68,7 @@ import {
67
68
  StaleRouteHandleError,
68
69
  SubcClient,
69
70
  SynapseEmbeddingProvider,
71
+ THINKING_BINDING_RECOVERY_FROZEN_PREFIX,
70
72
  ToolMutationBatch,
71
73
  USER_MEMORY_CANDIDATE_TTL_MS,
72
74
  V2_MEMORY_CATEGORIES,
@@ -85,6 +87,7 @@ import {
85
87
  applyMirroredNoteCompileFields,
86
88
  applyStrippedPlaceholderDelta,
87
89
  archiveMemory,
90
+ armThinkingBindingRecovery,
88
91
  assertTailHygieneContentUnchanged,
89
92
  backfillTagTokenCounts,
90
93
  beginBootQuietPeriod,
@@ -123,6 +126,7 @@ import {
123
126
  clearPersistedReasoningWatermark,
124
127
  clearPersistedTodoSyntheticAnchor,
125
128
  clearRecompStaging,
129
+ clearThinkingBindingRecoveryIf,
126
130
  computeCueContentHash,
127
131
  computeNormalizedHash,
128
132
  computeSyntheticCallId,
@@ -148,6 +152,7 @@ import {
148
152
  demoteTrailingBlankKeepDecisions,
149
153
  deriveTagLoadFloor,
150
154
  detectOverflow,
155
+ detectThinkingBindingMismatch,
151
156
  directoryHasGitMetadata,
152
157
  dismissNote,
153
158
  dismissUserMemory,
@@ -187,6 +192,7 @@ import {
187
192
  getActiveTagsBySession,
188
193
  getActiveToolTagsForAgeReclaim,
189
194
  getActiveUserMemories,
195
+ getAssignableTagNumberByMessageId,
190
196
  getAuthorityManagedMarker,
191
197
  getAutoSearchHintDecisions,
192
198
  getCandidateToolOwners,
@@ -206,6 +212,7 @@ import {
206
212
  getEmergencyRecoveryArmedAt,
207
213
  getHistorianFailureState,
208
214
  getIndexedMessageCorpusSize,
215
+ getInertWhitespaceAssistantTags,
209
216
  getLastCompartmentEndMessage,
210
217
  getLastCompartmentEndMessageId,
211
218
  getLastHookInitFailure,
@@ -269,10 +276,12 @@ import {
269
276
  getSourceContents,
270
277
  getStaleReduceStrippedIds,
271
278
  getStrippedPlaceholderIds,
279
+ getTagById,
272
280
  getTagNumberByMessageId,
273
281
  getTagsByNumbers,
274
282
  getTagsBySession,
275
283
  getTailHygieneTags,
284
+ getThinkingBindingRecoveryTarget,
276
285
  getToolTagNumberByOwner,
277
286
  getTrailingBlankDecisions,
278
287
  getTriggerTagTokenUpperBound,
@@ -305,6 +314,7 @@ import {
305
314
  isEditTool,
306
315
  isEmbeddingEnabled,
307
316
  isEmergencyRecoveryArmed,
317
+ isFable51ThinkingBindingModel,
308
318
  isFailClosedBlockingError,
309
319
  isFilePart,
310
320
  isLinkedGitWorktree,
@@ -331,6 +341,7 @@ import {
331
341
  markNoteNudgeDelivered,
332
342
  markNoteReady,
333
343
  markProjectLoadUntrusted,
344
+ markWhitespaceAssistantTagInert,
334
345
  memoryNeedsCue,
335
346
  mergeMemoryStats,
336
347
  migrateMagicContextConfigLocations,
@@ -486,6 +497,7 @@ import {
486
497
  tailHygieneStructuralSignature,
487
498
  takeDubiousOwnershipProjectIdentityWarning,
488
499
  textMentionsRecentCommit,
500
+ thinkingBindingRecoveryFrozenId,
489
501
  toModelEntry,
490
502
  toolOutputTokens,
491
503
  trimMemoriesToBudgetV2,
@@ -505,7 +517,7 @@ import {
505
517
  verificationFileExists,
506
518
  withContentLanguageDirective,
507
519
  withRawSessionMessageCache
508
- } from "./index-dh0vfghk.js";
520
+ } from "./index-j8r0ejb3.js";
509
521
  import {
510
522
  deriveHistorianChunkTokens,
511
523
  deriveTriggerBudget,
@@ -515,12 +527,13 @@ import {
515
527
  isSaneLimit,
516
528
  refreshModelLimitsAfterAuthOnce,
517
529
  refreshModelLimitsFromApi,
530
+ reloadWindowOverlay,
518
531
  resolveHistorianContextLimit,
519
532
  setOutputReserveConfig,
520
533
  setStoragePrivatePermissionEnforcement,
521
534
  setWindowOverlayPath,
522
535
  shouldEnforcePrivateStoragePermissions
523
- } from "./index-2d4kf2ve.js";
536
+ } from "./index-cvda5jb3.js";
524
537
  import {
525
538
  clearIgnoredMessages,
526
539
  describeError,
@@ -535,9 +548,10 @@ import {
535
548
  getOrCreateSessionMeta,
536
549
  logSlowWriteTransaction,
537
550
  markSessionCleanupPending,
551
+ retryPendingRustSessionCleanupsForProject,
538
552
  retryPendingSessionCleanups,
539
553
  updateSessionMeta
540
- } from "./index-z893kjyt.js";
554
+ } from "./index-y1yr57fw.js";
541
555
  import {
542
556
  modelRefLookupOrder,
543
557
  piModelRefToCanonical
@@ -11863,15 +11877,15 @@ Project memory uses exactly 5 categories. Every memory belongs to one:
11863
11877
  - **CONFIG_VALUES** — stable configuration keys/values and conventions. Not transient measurements (test counts, sizes, versions).
11864
11878
  - **NAMING** — naming conventions and canonical names. Not inventories.
11865
11879
 
11866
- **Legacy categories during transition:** older memories may still carry pre-v2 category names. When you touch one, map it to its 5-category home with \`action="update"\` (or \`merge\`): WORKFLOW_RULES→PROJECT_RULES, ARCHITECTURE_DECISIONS→ARCHITECTURE, CONFIG_DEFAULTS→CONFIG_VALUES, ENVIRONMENT→CONFIG_VALUES (paths) or CONSTRAINTS, KNOWN_ISSUES→CONSTRAINTS only if it's an external-system limit (otherwise archive — our own fixed bugs are not world facts). USER_DIRECTIVES / USER_PREFERENCES are NOT project categories — they live in the global user profile; archive project copies only when they add zero project-specific detail.`;
11880
+ **Legacy categories during transition:** older memories may still carry pre-v2 category names. When you touch one, map it to its 5-category home with \`action="update"\` (or \`merge\`): WORKFLOW_RULES→PROJECT_RULES, ARCHITECTURE_DECISIONS→ARCHITECTURE, CONFIG_DEFAULTS→CONFIG_VALUES, ENVIRONMENT→CONFIG_VALUES (paths) or CONSTRAINTS, KNOWN_ISSUES→CONSTRAINTS only if it's an external-system limit. USER_DIRECTIVES / USER_PREFERENCES are NOT project categories. Do not compare project memories with the global user profile or use it to justify an archive.`;
11867
11881
  var CURATE_SYSTEM_PROMPT = `You are a memory-pool curator for the magic-context system. You run during a scheduled dream window to keep a project's cross-session memory store lean and well-formed.
11868
11882
 
11869
11883
  ## Memory operations (ctx_memory)
11870
11884
  - \`action="list"\` — browse active memories, optionally filter by category
11871
11885
  - \`action="merge", ids=[N,M,...], content="...", category="..."\` — consolidate duplicates into one canonical memory
11872
- - \`action="update", ids=[N], content="..."\` — rewrite a memory's content
11886
+ - \`action="update", ids=[N], content="...", superseded_by=M\` — rewrite content; name where removed detail survives when cutting more than half
11873
11887
  - \`action="write", category="...", content="..."\` — create a memory (SPLITS ONLY — never mint new facts)
11874
- - \`action="archive", ids=[N], reason="..."\` — soft-archive a stale or low-value memory
11888
+ - \`action="archive", ids=[N], superseded_by=M, reason="..."\` — consolidate a redundant memory into the named active same-category survivor
11875
11889
 
11876
11890
  ## Rules
11877
11891
  1. **Assume the pool is accurate.** A separate verify task checks memories against code. You handle QUALITY only — duplicates, wording, low-value entries — never correctness, and you do NOT read the codebase.
@@ -11914,34 +11928,25 @@ Mapped files: ${files}${memory.hasNoFileSentinel ? " (file-independent)" : ""}`;
11914
11928
 
11915
11929
  `);
11916
11930
  }
11917
- function formatUserProfileList(userMemories) {
11918
- if (!userMemories || userMemories.length === 0)
11919
- return;
11920
- return userMemories.map((um) => `- [U${um.id}] ${um.content}`).join(`
11921
- `);
11922
- }
11923
11931
  function buildCuratePrompt(args) {
11924
11932
  return `## Task: Curate Project Memory Pool (hygiene)
11925
11933
 
11926
11934
  **Project:** ${args.projectPath}
11927
11935
 
11928
- The memories below are assumed ACCURATE (a separate verify task keeps them true). Your job is pool QUALITY: remove duplicates, tighten wording, and archive low-value entries that waste the ~6000-token injection budget. Explain each action in one line first. Do NOT mint new facts (that is the historian's job).
11936
+ The memories below are assumed ACCURATE (a separate verify task keeps them true). Your job is pool QUALITY: remove duplicates, tighten wording, and consolidate redundant entries that waste the ~6000-token injection budget. Explain each action in one line first. Do NOT mint new facts (that is the historian's job).
11929
11937
 
11930
11938
  Work ALL THREE phases below in order (A → B → C) over the whole pool. Do NOT stop after consolidating — a run that only merges and never improves or archives is incomplete.
11931
11939
 
11932
11940
  ### Phase A — Consolidate duplicates
11933
- Group by category, then merge near-identical / superset-subset / same-fact-different-angle clusters into one canonical memory with \`ctx_memory(action="merge", ids=[...], content="...", category="...")\`. Preserve every unique detail; terse present tense; paths/keys verbatim. Every id in a merge MUST share the same category — the system rejects cross-category merges. If two similar memories sit in different categories they are NOT duplicates (one is miscategorized — archive the redundant one in Phase C instead). One fact per memory.
11941
+ Group by category, then merge near-identical / superset-subset / same-fact-different-angle clusters into one canonical memory with \`ctx_memory(action="merge", ids=[...], content="...", category="...")\`. Preserve every unique detail; terse present tense; paths/keys verbatim. Every id in a merge MUST share the same category — the system rejects cross-category merges. If two similar memories sit in different categories they are NOT duplicates; do not archive either as a consolidation. One fact per memory.
11934
11942
 
11935
11943
  ### Phase B — Improve wording
11936
- Rewrite narrative/historical → operational present tense ("X uses Y because Z", not "we switched to Y"); drop session-local context and commit hashes (unless the hash is the point); add specifics where vague. \`write\` is for SPLITS ONLY (update the original down to its first fact, write the second) — a healthy run is net-neutral or net-shrinking, never net-adds facts.
11944
+ Rewrite narrative/historical → operational present tense ("X uses Y because Z", not "we switched to Y"); drop session-local context and commit hashes (unless the hash is the point); add specifics where vague. A rewrite that removes more than half the content must name the active same-category memory preserving that detail with \`superseded_by\`. \`write\` is for SPLITS ONLY (update the original down to its first fact, write the second) — a healthy run is net-neutral or net-shrinking, never net-adds facts.
11937
11945
 
11938
- ### Phase C — Archive stale / low-value
11939
- Archive (with a specific reason) memories that: restate code without rationale · are redundant with a better memory · are stale implementation detail (line numbers/internals) · low signal (seen_count=1, retrieval_count=0, no constraint language) · bare config value · transient measurement · a solved bug in OUR OWN code · redundant with the global user profile (zero added project detail).
11946
+ ### Phase C — Archive only into a surviving project memory
11947
+ Archive a redundant memory only when a better ACTIVE memory in the same project and category preserves its information; name that survivor with \`superseded_by\`. A bare "redundant" verdict is deletion and will be refused. Leave standalone low-value or stale entries unchanged for a human to review. The global user profile describes the operator and is never a substitute for project knowledge, so it cannot justify an archive.
11940
11948
  KEEP (overrides archive): constraint/rule language (must/never/always) · explains WHY (because/so that/to prevent) · EXTERNAL-system limit (CONSTRAINTS: archive only if word-for-word duplicated) · path/config WITH context · retrieval_count>0 · priority/philosophy.
11941
- ${args.userProfile ? `
11942
- ### Global user profile (for the redundancy check)
11943
- ${args.userProfile}
11944
- ` : ""}
11949
+
11945
11950
  ### Memory pool
11946
11951
  ${renderMemoryList(args.memories)}`;
11947
11952
  }
@@ -12150,8 +12155,7 @@ function buildDreamTaskPrompt(task, args) {
12150
12155
  case "curate":
12151
12156
  return buildCuratePrompt({
12152
12157
  projectPath: args.projectPath,
12153
- memories: args.curate?.memories ?? [],
12154
- userProfile: formatUserProfileList(args.userMemories)
12158
+ memories: args.curate?.memories ?? []
12155
12159
  });
12156
12160
  case "maintain-docs":
12157
12161
  return buildMaintainDocsPrompt(args.projectPath, args.lastDreamAt ?? null, args.existingDocs ?? { architecture: false, structure: false });
@@ -13403,6 +13407,7 @@ function showToast(ctx, title, message, variant = "info", duration = 3000) {
13403
13407
  function createLiveSessionState() {
13404
13408
  return {
13405
13409
  liveModelBySession: new Map,
13410
+ latestAssistantMessageIdBySession: new Map,
13406
13411
  variantBySession: new Map,
13407
13412
  agentBySession: new Map,
13408
13413
  channel1StateBySession: new Map,
@@ -16117,6 +16122,87 @@ function recordInvocation(args, startedAt, params) {
16117
16122
  });
16118
16123
  }
16119
16124
 
16125
+ // src/features/magic-context/dreamer/memory-claim-safety.ts
16126
+ var POLICY_SENTENCE_START = /(?:^|[.!?]\s+|\n\s*)(?:[-*]\s+|\d+[.)]\s+)?(?:you\s+|we\s+)?(?:must(?:\s+not)?|never|always|do\s+not|don't|shall\s+not)\b/i;
16127
+ var ACTOR_POLICY = /\b(?:you|we|agents?|masons?|workers?|operators?|users?|maintainers?)\s+(?:must(?:\s+not)?|should(?:\s+not)?|need\s+to|shall(?:\s+not)?|cannot|can't|may\s+not)\b/i;
16128
+ var BEHAVIORAL_WHEN_CLAUSE = /\bwhen\s+(?:you(?:'re|\s+are)?|we(?:'re|\s+are)?|told|asked|requested|working|debugging|reviewing|checking|verifying|investigating|using|running|editing|changing)\b/i;
16129
+ var WORKFLOW_IMPERATIVE = /(?:^|[.!?]\s+|\n\s*)(?:[-*]\s+|\d+[.)]\s+)?(?:please\s+)?(?:run|use|check|ask|avoid|prefer|ensure|keep|follow|brief|report|inspect|search|open|read|review|validate|confirm|delegate|stop|start|remember)\b/i;
16130
+ var DECISION_AUTHORITY = /\b(?:the\s+)?(?:user|operator|maintainer|owner)\s+(?:decides|chooses|approves|has\s+(?:the\s+)?final\s+say)\b/i;
16131
+ function isDirectiveShapedProjectRule(category, content) {
16132
+ if (category !== "PROJECT_RULES")
16133
+ return false;
16134
+ const text = content.trim();
16135
+ if (!text)
16136
+ return false;
16137
+ return POLICY_SENTENCE_START.test(text) || ACTOR_POLICY.test(text) || BEHAVIORAL_WHEN_CLAUSE.test(text) || WORKFLOW_IMPERATIVE.test(text) || DECISION_AUTHORITY.test(text);
16138
+ }
16139
+
16140
+ // src/features/magic-context/dreamer/curate-memory-safety.ts
16141
+ var PROJECT_SCOPED_CATEGORIES = new Set([
16142
+ "PROJECT_RULES",
16143
+ "ARCHITECTURE",
16144
+ "CONSTRAINTS",
16145
+ "CONFIG_VALUES",
16146
+ "NAMING"
16147
+ ]);
16148
+ var USER_PROFILE_REFERENCE = /\buser(?:[\s_-]+)(?:profile|preferences?)\b|\bU\d+\b/i;
16149
+ var refusalCountsBySession = new Map;
16150
+ function isSurvivingConsolidationTarget(memory, successor, projectIdentity) {
16151
+ return successor !== null && successor.id !== memory.id && successor.status === "active" && successor.supersededByMemoryId === null && (successor.expiresAt === null || successor.expiresAt > Date.now()) && projectIdentity(successor) === projectIdentity(memory) && successor.category === memory.category;
16152
+ }
16153
+ function assessCurateMutationSafety(args) {
16154
+ const { memory, successor, verdict } = args;
16155
+ if (verdict === "archive") {
16156
+ if (!isSurvivingConsolidationTarget(memory, successor, args.projectIdentity)) {
16157
+ return {
16158
+ memoryId: memory.id,
16159
+ verdict,
16160
+ reason: "missing-active-same-category-successor"
16161
+ };
16162
+ }
16163
+ if (PROJECT_SCOPED_CATEGORIES.has(memory.category) && USER_PROFILE_REFERENCE.test(args.reason ?? "")) {
16164
+ return {
16165
+ memoryId: memory.id,
16166
+ verdict,
16167
+ reason: "user-profile-is-not-project-memory"
16168
+ };
16169
+ }
16170
+ }
16171
+ if (isDirectiveShapedProjectRule(memory.category, memory.content)) {
16172
+ return {
16173
+ memoryId: memory.id,
16174
+ verdict,
16175
+ reason: "directive-shaped-project-rule"
16176
+ };
16177
+ }
16178
+ if (verdict === "update") {
16179
+ const originalChars = memory.content.trim().length;
16180
+ const replacementChars = args.replacementContent?.trim().length ?? 0;
16181
+ if (replacementChars * 2 < originalChars && !isSurvivingConsolidationTarget(memory, successor, args.projectIdentity)) {
16182
+ return {
16183
+ memoryId: memory.id,
16184
+ verdict,
16185
+ reason: "content-loss",
16186
+ originalChars,
16187
+ replacementChars
16188
+ };
16189
+ }
16190
+ }
16191
+ return null;
16192
+ }
16193
+ function recordCurateSafetyRefusal(sessionId, refusal) {
16194
+ const count = (refusalCountsBySession.get(sessionId) ?? 0) + 1;
16195
+ refusalCountsBySession.set(sessionId, count);
16196
+ const lengths = refusal.originalChars === undefined ? "" : ` original_chars=${refusal.originalChars} replacement_chars=${refusal.replacementChars ?? 0}`;
16197
+ log(`[dreamer] curate safety refusal: session_id=${sessionId} memory_id=${refusal.memoryId} verdict=${refusal.verdict} reason=${refusal.reason}${lengths} refused=${count}`);
16198
+ return count;
16199
+ }
16200
+ function takeCurateSafetyRefusalCount(sessionId) {
16201
+ const count = refusalCountsBySession.get(sessionId) ?? 0;
16202
+ refusalCountsBySession.delete(sessionId);
16203
+ return count;
16204
+ }
16205
+
16120
16206
  // src/agents/smart-note-compiler.ts
16121
16207
  var SMART_NOTE_COMPILER_AGENT = "smart-note-compiler";
16122
16208
 
@@ -18140,21 +18226,6 @@ function validateMapMemoriesManifest(text, expectedIds) {
18140
18226
  return parsed;
18141
18227
  }
18142
18228
 
18143
- // src/features/magic-context/dreamer/memory-claim-safety.ts
18144
- var POLICY_SENTENCE_START = /(?:^|[.!?]\s+|\n\s*)(?:[-*]\s+|\d+[.)]\s+)?(?:you\s+|we\s+)?(?:must(?:\s+not)?|never|always|do\s+not|don't|shall\s+not)\b/i;
18145
- var ACTOR_POLICY = /\b(?:you|we|agents?|masons?|workers?|operators?|users?|maintainers?)\s+(?:must(?:\s+not)?|should(?:\s+not)?|need\s+to|shall(?:\s+not)?|cannot|can't|may\s+not)\b/i;
18146
- var BEHAVIORAL_WHEN_CLAUSE = /\bwhen\s+(?:you(?:'re|\s+are)?|we(?:'re|\s+are)?|told|asked|requested|working|debugging|reviewing|checking|verifying|investigating|using|running|editing|changing)\b/i;
18147
- var WORKFLOW_IMPERATIVE = /(?:^|[.!?]\s+|\n\s*)(?:[-*]\s+|\d+[.)]\s+)?(?:please\s+)?(?:run|use|check|ask|avoid|prefer|ensure|keep|follow|brief|report|inspect|search|open|read|review|validate|confirm|delegate|stop|start|remember)\b/i;
18148
- var DECISION_AUTHORITY = /\b(?:the\s+)?(?:user|operator|maintainer|owner)\s+(?:decides|chooses|approves|has\s+(?:the\s+)?final\s+say)\b/i;
18149
- function isDirectiveShapedProjectRule(category, content) {
18150
- if (category !== "PROJECT_RULES")
18151
- return false;
18152
- const text = content.trim();
18153
- if (!text)
18154
- return false;
18155
- return POLICY_SENTENCE_START.test(text) || ACTOR_POLICY.test(text) || BEHAVIORAL_WHEN_CLAUSE.test(text) || WORKFLOW_IMPERATIVE.test(text) || DECISION_AUTHORITY.test(text);
18156
- }
18157
-
18158
18229
  // src/features/magic-context/dreamer/map-memories.ts
18159
18230
  var MAP_BATCH_SIZE = 80;
18160
18231
  var MAP_BATCH_FLOOR_MS = 240000;
@@ -20425,7 +20496,8 @@ function createDreamTaskExecutor(deps) {
20425
20496
  deadline,
20426
20497
  parent,
20427
20498
  recordRun,
20428
- computeMemoryDelta
20499
+ computeMemoryDelta,
20500
+ reportProgress
20429
20501
  });
20430
20502
  } catch (error) {
20431
20503
  const { transient, brief } = classifyFailure(error);
@@ -20710,7 +20782,6 @@ async function runAgenticTask(config, ctx, helpers) {
20710
20782
  architecture: existsSync10(`${docsDir}/ARCHITECTURE.md`),
20711
20783
  structure: existsSync10(`${docsDir}/STRUCTURE.md`)
20712
20784
  } : undefined;
20713
- const userMemories = task === "curate" ? getActiveUserMemories(db).map((um) => ({ id: um.id, content: um.content })) : undefined;
20714
20785
  let curateMemories;
20715
20786
  if (task === "curate") {
20716
20787
  curateMemories = loadActiveMemoryPromptMemories(db, projectIdentity);
@@ -20720,7 +20791,6 @@ async function runAgenticTask(config, ctx, helpers) {
20720
20791
  projectPath: projectIdentity,
20721
20792
  lastDreamAt: lastRunAt ? String(lastRunAt) : null,
20722
20793
  existingDocs,
20723
- userMemories,
20724
20794
  curate: curateMemories ? { memories: curateMemories } : undefined
20725
20795
  });
20726
20796
  const abortController = new AbortController;
@@ -20778,6 +20848,11 @@ async function runAgenticTask(config, ctx, helpers) {
20778
20848
  });
20779
20849
  if (leaseLost)
20780
20850
  throw new Error("Dream lease lost during task");
20851
+ const curateRefused = task === "curate" ? takeCurateSafetyRefusalCount(sessionId) : 0;
20852
+ if (curateRefused > 0) {
20853
+ helpers.reportProgress(curateRefused, curateRefused);
20854
+ log(`[dreamer] curate safety summary: refused=${curateRefused}`);
20855
+ }
20781
20856
  if (parent) {
20782
20857
  recordChildInvocation({
20783
20858
  db,
@@ -20798,11 +20873,14 @@ async function runAgenticTask(config, ctx, helpers) {
20798
20873
  }
20799
20874
  }
20800
20875
  helpers.recordRun("completed", null, {
20801
- memoryChanges: helpers.computeMemoryDelta(memoryBefore)
20876
+ memoryChanges: helpers.computeMemoryDelta(memoryBefore),
20877
+ progress: curateRefused > 0 ? `curate: refused ${curateRefused} unsafe mutation(s)` : null
20802
20878
  });
20803
20879
  return { status: "completed" };
20804
20880
  } finally {
20805
20881
  heartbeat.stop();
20882
+ if (childSessionId)
20883
+ takeCurateSafetyRefusalCount(childSessionId);
20806
20884
  if (childSessionId) {
20807
20885
  await deps.client.session.delete({ path: { id: childSessionId } }).catch(() => {});
20808
20886
  }
@@ -21522,7 +21600,7 @@ function runTick(origin) {
21522
21600
  const db = openTimerDatabaseOrNull("maintenance tick");
21523
21601
  if (!db)
21524
21602
  return;
21525
- runMessageHistoryMaintenance(db);
21603
+ await runMessageHistoryMaintenance(db);
21526
21604
  for (const reg of registeredProjects.values()) {
21527
21605
  if (origin === "startup") {
21528
21606
  scheduleInitialProjectRun(reg, db);
@@ -21538,11 +21616,26 @@ function runTick(origin) {
21538
21616
  }
21539
21617
  })();
21540
21618
  }
21541
- function runMessageHistoryMaintenance(db) {
21619
+ async function runMessageHistoryMaintenance(db) {
21542
21620
  const cleanup = retryPendingSessionCleanups(db);
21543
21621
  if (cleanup.cleared > 0 || cleanup.failedSessionIds.length > 0) {
21544
21622
  log(`[message-index] pending session cleanup: cleared=${cleanup.cleared} failed=${cleanup.failedSessionIds.length}`);
21545
21623
  }
21624
+ for (const registration of registeredProjects.values()) {
21625
+ const moduleClient = registration.sessionCleanupModuleClient;
21626
+ if (!moduleClient)
21627
+ continue;
21628
+ const rustCleanup = await retryPendingRustSessionCleanupsForProject(db, registration.projectIdentity, async (sessionId) => {
21629
+ try {
21630
+ await moduleClient.deleteSession(sessionId, registration.directory);
21631
+ } finally {
21632
+ moduleClient.closeSession?.(sessionId);
21633
+ }
21634
+ });
21635
+ if (rustCleanup.cleared > 0 || rustCleanup.failedSessionIds.length > 0) {
21636
+ log(`[message-index] pending Rust session cleanup: cleared=${rustCleanup.cleared} failed=${rustCleanup.failedSessionIds.length}`);
21637
+ }
21638
+ }
21546
21639
  const sweep = sweepOrphanedOpenCodeMessageIndexes(db, openOpenCodeDb);
21547
21640
  if (sweep.deleted > 0) {
21548
21641
  log(`[message-index] orphan sweep: scanned=${sweep.scanned} deleted=${sweep.deleted} cursor=${sweep.cursor || "<complete>"}`);
@@ -22542,7 +22635,7 @@ function createTagger() {
22542
22635
  if (type === "tool") {
22543
22636
  throw new Error("tagger.assignTag: type='tool' is forbidden — use assignToolTag(sessionId, callId, ownerMsgId, ...)");
22544
22637
  }
22545
- return allocateTag(sessionId, messageId, type, byteSize2, db, reasoningByteSize, toolName, inputByteSize, null, messageId, () => getTagNumberByMessageId(db, sessionId, messageId), entryFingerprint, tokenThunk);
22638
+ return allocateTag(sessionId, messageId, type, byteSize2, db, reasoningByteSize, toolName, inputByteSize, null, messageId, () => getAssignableTagNumberByMessageId(db, sessionId, messageId), entryFingerprint, tokenThunk);
22546
22639
  }
22547
22640
  function backfillToolTokensIfNull(db, sessionId, tagNumber, tokenThunk) {
22548
22641
  if (!tokenThunk)
@@ -25422,16 +25515,14 @@ var WHOLE_MESSAGE_PLACEHOLDER_TEXT = "[dropped]";
25422
25515
  function modelAcceptsEmptyContent(providerID) {
25423
25516
  return providerID === "anthropic";
25424
25517
  }
25425
- function variantChangeBustsProviderCache(providerID) {
25426
- if (providerID === undefined)
25427
- return true;
25428
- if (providerID === "anthropic")
25429
- return true;
25430
- if (providerID === "google-vertex-anthropic")
25431
- return true;
25432
- if (providerID.includes("bedrock"))
25433
- return true;
25434
- return false;
25518
+ function variantChangeBustsProviderCache(providerID, modelID) {
25519
+ if (!providerID || !modelID)
25520
+ return false;
25521
+ const normalizedProviderID = providerID.toLowerCase();
25522
+ const isAnthropicFamily = normalizedProviderID === "anthropic" || normalizedProviderID === "google-vertex-anthropic" || normalizedProviderID.includes("bedrock");
25523
+ if (!isAnthropicFamily)
25524
+ return false;
25525
+ return !isFable51ThinkingBindingModel("anthropic", modelID);
25435
25526
  }
25436
25527
  function makeSentinel(originalPart) {
25437
25528
  const sentinel = {
@@ -26783,20 +26874,23 @@ function createChatMessageHook(args) {
26783
26874
  args.agentBySession.set(sessionId, input.agent);
26784
26875
  }
26785
26876
  if (previousVariant !== undefined && input.variant !== undefined && previousVariant !== input.variant) {
26786
- const providerID = input.model?.providerID ?? args.liveModelBySession.get(sessionId)?.providerID;
26787
- if (variantChangeBustsProviderCache(providerID)) {
26877
+ const liveModel = args.liveModelBySession.get(sessionId);
26878
+ const providerID = input.model?.providerID ?? liveModel?.providerID;
26879
+ const modelID = input.model?.modelID ?? liveModel?.modelID;
26880
+ if (variantChangeBustsProviderCache(providerID, modelID)) {
26788
26881
  sessionLog(sessionId, `variant changed (${previousVariant} -> ${input.variant}), triggering flush`);
26789
26882
  args.historyRefreshSessions.add(sessionId);
26790
26883
  args.systemPromptRefreshSessions.add(sessionId);
26791
26884
  args.pendingMaterializationSessions.add(sessionId);
26792
26885
  args.lastHeuristicsTurnId.delete(sessionId);
26793
26886
  } else {
26794
- sessionLog(sessionId, `variant changed (${previousVariant} -> ${input.variant}) on provider ${providerID} whose cache ignores request params; deferring flush to next natural bust`);
26887
+ sessionLog(sessionId, `variant changed (${previousVariant} -> ${input.variant}) on ${providerID ?? "unknown"}/${modelID ?? "unknown"} without a proven natural cache bust; deferring flush to next natural bust`);
26795
26888
  }
26796
26889
  }
26797
26890
  };
26798
26891
  }
26799
26892
  function createEventHook(args) {
26893
+ const latestAssistantMessageIdBySession = args.latestAssistantMessageIdBySession ?? new Map;
26800
26894
  return async (input) => {
26801
26895
  await args.eventHandler(input);
26802
26896
  if (input.event.type === "message.updated") {
@@ -26810,24 +26904,31 @@ function createEventHook(args) {
26810
26904
  }
26811
26905
  const assistantInfo = getMessageUpdatedAssistantInfo(input.event.properties);
26812
26906
  if (assistantInfo?.providerID && assistantInfo?.modelID) {
26813
- const previous = args.liveModelBySession.get(assistantInfo.sessionID);
26814
- args.liveModelBySession.set(assistantInfo.sessionID, {
26815
- providerID: assistantInfo.providerID,
26816
- modelID: assistantInfo.modelID
26817
- });
26818
- if (previous && (previous.providerID !== assistantInfo.providerID || previous.modelID !== assistantInfo.modelID)) {
26819
- dropSlot(assistantInfo.sessionID, "model-change");
26820
- sessionLog(assistantInfo.sessionID, `model changed (${previous.providerID}/${previous.modelID} -> ${assistantInfo.providerID}/${assistantInfo.modelID}), clearing historian failure state and reasoning watermark`);
26821
- clearHistorianFailureState(args.db, assistantInfo.sessionID);
26822
- clearPersistedReasoningWatermark(args.db, assistantInfo.sessionID);
26823
- clearDetectedContextLimit(args.db, assistantInfo.sessionID);
26824
- clearEmergencyRecovery(args.db, assistantInfo.sessionID);
26825
- clearEmergencyDropSample(args.db, assistantInfo.sessionID);
26826
- updateSessionMeta(args.db, assistantInfo.sessionID, {
26827
- clearedReasoningThroughTag: 0,
26828
- observedSafeInputTokens: 0,
26829
- cacheAlertSent: false
26907
+ const latestMessageID = latestAssistantMessageIdBySession.get(assistantInfo.sessionID);
26908
+ const acceptsModelUpdate = latestMessageID === undefined || assistantInfo.messageID !== undefined && assistantInfo.messageID >= latestMessageID;
26909
+ if (acceptsModelUpdate) {
26910
+ if (assistantInfo.messageID !== undefined) {
26911
+ latestAssistantMessageIdBySession.set(assistantInfo.sessionID, assistantInfo.messageID);
26912
+ }
26913
+ const previous = args.liveModelBySession.get(assistantInfo.sessionID);
26914
+ args.liveModelBySession.set(assistantInfo.sessionID, {
26915
+ providerID: assistantInfo.providerID,
26916
+ modelID: assistantInfo.modelID
26830
26917
  });
26918
+ if (previous && (previous.providerID !== assistantInfo.providerID || previous.modelID !== assistantInfo.modelID)) {
26919
+ dropSlot(assistantInfo.sessionID, "model-change");
26920
+ sessionLog(assistantInfo.sessionID, `model changed (${previous.providerID}/${previous.modelID} -> ${assistantInfo.providerID}/${assistantInfo.modelID}), clearing historian failure state and reasoning watermark`);
26921
+ clearHistorianFailureState(args.db, assistantInfo.sessionID);
26922
+ clearPersistedReasoningWatermark(args.db, assistantInfo.sessionID);
26923
+ clearDetectedContextLimit(args.db, assistantInfo.sessionID);
26924
+ clearEmergencyRecovery(args.db, assistantInfo.sessionID);
26925
+ clearEmergencyDropSample(args.db, assistantInfo.sessionID);
26926
+ updateSessionMeta(args.db, assistantInfo.sessionID, {
26927
+ clearedReasoningThroughTag: 0,
26928
+ observedSafeInputTokens: 0,
26929
+ cacheAlertSent: false
26930
+ });
26931
+ }
26831
26932
  }
26832
26933
  }
26833
26934
  }
@@ -26840,6 +26941,7 @@ function createEventHook(args) {
26840
26941
  }
26841
26942
  if (input.event.type === "session.deleted") {
26842
26943
  args.liveModelBySession.delete(sessionId);
26944
+ latestAssistantMessageIdBySession.delete(sessionId);
26843
26945
  args.variantBySession.delete(sessionId);
26844
26946
  args.agentBySession.delete(sessionId);
26845
26947
  args.sessionDirectoryBySession.delete(sessionId);
@@ -27807,7 +27909,7 @@ function registerRpcHandlers(rpcServer, args) {
27807
27909
  return { count: buildCompartmentCount(db, sessionId, moduleStatus) };
27808
27910
  });
27809
27911
  const buildManagedCtx = async (db) => {
27810
- const { deriveHistorianChunkTokens: deriveHistorianChunkTokens2, resolveHistorianContextLimit: resolveHistorianContextLimit2 } = await import("./derive-budgets-bb7mrpzs.js");
27912
+ const { deriveHistorianChunkTokens: deriveHistorianChunkTokens2, resolveHistorianContextLimit: resolveHistorianContextLimit2 } = await import("./derive-budgets-9q2d3r93.js");
27811
27913
  const { resolveHistorianModel: resolveHistorianModel2 } = await import("./model-resolution-6w6wzw18.js");
27812
27914
  const { userMemoryCollectionEnabled: userMemoryCollectionEnabled2 } = await import("./task-config-gv6s04fp.js");
27813
27915
  const DEFAULT_HISTORIAN_TIMEOUT_MS2 = 600000;
@@ -27840,7 +27942,7 @@ function registerRpcHandlers(rpcServer, args) {
27840
27942
  const db = getDb();
27841
27943
  if (!db)
27842
27944
  return { ok: false, error: "db unavailable" };
27843
- const { runManagedRecomp: runManagedRecomp2 } = await import("./recomp-orchestrator-w7xg48e3.js");
27945
+ const { runManagedRecomp: runManagedRecomp2 } = await import("./recomp-orchestrator-m37p3fq6.js");
27844
27946
  const { sendIgnoredMessage: sendIgnoredMessage2 } = await import("./send-session-notification-bw7eex6w.js");
27845
27947
  log(`[rpc] recomp requested for session ${sessionId}`);
27846
27948
  const ctx = await buildManagedCtx(db);
@@ -27859,7 +27961,7 @@ function registerRpcHandlers(rpcServer, args) {
27859
27961
  const db = getDb();
27860
27962
  if (!db)
27861
27963
  return { ok: false, error: "db unavailable" };
27862
- const { runManagedUpgrade: runManagedUpgrade2 } = await import("./recomp-orchestrator-w7xg48e3.js");
27964
+ const { runManagedUpgrade: runManagedUpgrade2 } = await import("./recomp-orchestrator-m37p3fq6.js");
27863
27965
  const { sendIgnoredMessage: sendIgnoredMessage2 } = await import("./send-session-notification-bw7eex6w.js");
27864
27966
  log(`[rpc] session-upgrade requested for session ${sessionId}`);
27865
27967
  const ctx = await buildManagedCtx(db);
@@ -27876,7 +27978,7 @@ function registerRpcHandlers(rpcServer, args) {
27876
27978
  if (!db)
27877
27979
  return { ok: false, error: "db unavailable" };
27878
27980
  try {
27879
- const { updateSessionMeta: updateSessionMeta2 } = await import("./storage-meta-session-gr4zn7sq.js");
27981
+ const { updateSessionMeta: updateSessionMeta2 } = await import("./storage-meta-session-dse3qv6s.js");
27880
27982
  updateSessionMeta2(db, sessionId, { upgradeRemindedAt: Date.now() });
27881
27983
  return { ok: true };
27882
27984
  } catch (error) {
@@ -31077,1001 +31179,1036 @@ class RawFallbackContextLimitError extends Error {
31077
31179
  }
31078
31180
  }
31079
31181
 
31080
- // src/hooks/magic-context/transform-context-state.ts
31081
- function loadPersistedUsageWatermark(db, sessionId) {
31082
- const result = db.prepare("SELECT last_response_time FROM session_meta WHERE session_id = ?").get(sessionId);
31083
- if (result === null || typeof result !== "object")
31084
- return null;
31085
- const lastResponseTime = result.last_response_time;
31086
- return typeof lastResponseTime === "number" ? lastResponseTime : null;
31182
+ // src/hooks/magic-context/strip-content.ts
31183
+ var DROPPED_PLACEHOLDER_PATTERN = /^\[dropped §\d+§\]$/;
31184
+ var TAG_PREFIX_PATTERN = /^§\d+§\s*/;
31185
+ var SYSTEM_INJECTION_PATTERNS = [
31186
+ /^<!-- OMO_INTERNAL_INITIATOR -->$/,
31187
+ /^<system-reminder>[\s\S]*<\/system-reminder>$/,
31188
+ /^\[SYSTEM DIRECTIVE:/,
31189
+ /^\[Category\+Skill Reminder\]/,
31190
+ /^\[EDIT ERROR - IMMEDIATE ACTION REQUIRED\]/,
31191
+ /^\[task CALL FAILED/,
31192
+ /^\[EMERGENCY CONTEXT WINDOW WARNING\]/
31193
+ ];
31194
+ function isSystemInjectedText(text) {
31195
+ const stripped = text.trim().replace(TAG_PREFIX_PATTERN, "").trim();
31196
+ if (stripped.length === 0)
31197
+ return false;
31198
+ return SYSTEM_INJECTION_PATTERNS.some((pattern) => pattern.test(stripped));
31087
31199
  }
31088
- function loadContextUsage(contextUsageMap, db, sessionId) {
31089
- const contextUsageEntry = contextUsageMap.get(sessionId);
31090
- try {
31091
- const persistedLastResponseTime = loadPersistedUsageWatermark(db, sessionId);
31092
- const cachedLastResponseTime = contextUsageEntry?.lastResponseTime ?? contextUsageEntry?.updatedAt;
31093
- if (contextUsageEntry && contextUsageEntry.lastResponseTime === undefined && (persistedLastResponseTime === null || persistedLastResponseTime === 0)) {
31094
- return contextUsageEntry.usage;
31095
- }
31096
- if (contextUsageEntry && cachedLastResponseTime === persistedLastResponseTime) {
31097
- return contextUsageEntry.usage;
31200
+ function stripSystemInjectedMessages(messages, protectedTailStart, providerID) {
31201
+ let stripped = 0;
31202
+ const sentineledIds = [];
31203
+ for (let i = 0;i < messages.length; i++) {
31204
+ if (i >= protectedTailStart)
31205
+ continue;
31206
+ const msg = messages[i];
31207
+ if (msg.parts.length === 0)
31208
+ continue;
31209
+ if (msg.info.role === "user")
31210
+ continue;
31211
+ if (msg.parts.length === 1 && isSentinel(msg.parts[0]))
31212
+ continue;
31213
+ let hasContentPart = false;
31214
+ let allContentIsSystemInjection = true;
31215
+ for (const part of msg.parts) {
31216
+ if (!isRecord(part))
31217
+ continue;
31218
+ const partType = part.type;
31219
+ if (METADATA_PART_TYPES.has(partType))
31220
+ continue;
31221
+ if (part.ignored === true)
31222
+ continue;
31223
+ if (partType === "tool") {
31224
+ allContentIsSystemInjection = false;
31225
+ break;
31226
+ }
31227
+ if (partType === "text" && typeof part.text === "string") {
31228
+ hasContentPart = true;
31229
+ if (!isSystemInjectedText(part.text)) {
31230
+ allContentIsSystemInjection = false;
31231
+ break;
31232
+ }
31233
+ continue;
31234
+ }
31235
+ allContentIsSystemInjection = false;
31236
+ break;
31098
31237
  }
31099
- const persisted = loadPersistedUsage(db, sessionId);
31100
- if (persisted) {
31101
- contextUsageMap.set(sessionId, {
31102
- ...persisted,
31103
- lastResponseTime: persistedLastResponseTime ?? persisted.updatedAt,
31104
- hasUsageTokens: false
31105
- });
31106
- return persisted.usage;
31238
+ if (hasContentPart && allContentIsSystemInjection) {
31239
+ msg.parts.length = 0;
31240
+ msg.parts.push(makeWholeMessageSentinel(providerID));
31241
+ stripped++;
31242
+ if (typeof msg.info.id === "string")
31243
+ sentineledIds.push(msg.info.id);
31107
31244
  }
31108
- contextUsageMap.delete(sessionId);
31109
- } catch (error) {
31110
- sessionLog(sessionId, "transform failed loading persisted usage:", error);
31111
- return contextUsageEntry?.usage ?? { percentage: 0, inputTokens: 0 };
31112
- }
31113
- return { percentage: 0, inputTokens: 0 };
31114
- }
31115
- function resolveSchedulerDecision(scheduler, sessionMeta, contextUsage, sessionId, modelKey, contextLimit) {
31116
- try {
31117
- const schedulerDecision = scheduler.shouldExecute(sessionMeta, contextUsage, undefined, sessionId, modelKey, contextLimit);
31118
- sessionLog(sessionId, `transform scheduler: percentage=${contextUsage.percentage.toFixed(1)}% inputTokens=${contextUsage.inputTokens} cacheTtl=${sessionMeta.cacheTtl} lastResponseTime=${sessionMeta.lastResponseTime} decision=${schedulerDecision}`);
31119
- return schedulerDecision;
31120
- } catch (error) {
31121
- sessionLog(sessionId, "transform scheduler failed; defaulting to defer:", error);
31122
- return "defer";
31123
- }
31124
- }
31125
-
31126
- // src/hooks/magic-context/channel2-cycle.ts
31127
- function rearmChannel2AfterCoverageAdvancingHardFold(input) {
31128
- if (input.compactionOff || !input.foldExecuted || input.previousCoverage === null || input.currentCoverage === null || input.currentCoverage <= input.previousCoverage) {
31129
- return false;
31130
31245
  }
31131
- return casChannel2NudgeState(input.db, input.sessionId, "delivered", "");
31132
- }
31133
- function rearmChannel2AfterMeasuredCollapse(input) {
31134
- if (!input.baseline.evaluable || input.baseline.generationInvalidated)
31135
- return false;
31136
- if (effectiveTailHygiene(input.baseline).u >= CHANNEL1_FLOOR_TOKENS)
31137
- return false;
31138
- return casChannel2NudgeState(input.db, input.sessionId, "delivered", "");
31139
- }
31140
-
31141
- // src/hooks/magic-context/drop-stale-reduce-calls.ts
31142
- var STALE_TOOL_NAMES = new Set(["ctx_reduce"]);
31143
- function isReduceToolPart(part) {
31144
- if (!isRecord(part))
31145
- return false;
31146
- if (part.type === "tool" && typeof part.tool === "string" && STALE_TOOL_NAMES.has(part.tool))
31147
- return true;
31148
- if (part.type === "tool-invocation" && typeof part.toolName === "string" && STALE_TOOL_NAMES.has(part.toolName))
31149
- return true;
31150
- if (part.type === "tool_use" && typeof part.name === "string" && STALE_TOOL_NAMES.has(part.name))
31151
- return true;
31152
- return false;
31246
+ return { stripped, sentineledIds };
31153
31247
  }
31154
- function hasAnyMeaningfulPart(parts) {
31155
- for (const part of parts) {
31156
- if (!isRecord(part))
31248
+ var METADATA_PART_TYPES = new Set([
31249
+ "step-start",
31250
+ "step-finish",
31251
+ "snapshot",
31252
+ "patch",
31253
+ "agent",
31254
+ "retry",
31255
+ "subtask",
31256
+ "compaction"
31257
+ ]);
31258
+ function stripDroppedPlaceholderMessages(messages, providerID) {
31259
+ let stripped = 0;
31260
+ const sentineledIds = [];
31261
+ for (let i = 0;i < messages.length; i++) {
31262
+ const msg = messages[i];
31263
+ if (msg.parts.length === 0)
31157
31264
  continue;
31158
- if (part.type === "text" && typeof part.text === "string" && part.text.trim().length > 0)
31159
- return true;
31160
- if (part.type === "thinking" || part.type === "reasoning" || part.type === "redacted_thinking")
31265
+ if (msg.info.role === "user")
31161
31266
  continue;
31162
- if (part.type === "meta" || part.type === "step-start" || part.type === "step-finish")
31267
+ if (msg.parts.length === 1 && isSentinel(msg.parts[0]))
31163
31268
  continue;
31164
- if (part.type !== "tool" || !isReduceToolPart(part))
31165
- return true;
31269
+ let hasContentPart = false;
31270
+ let hasNonDroppedContent = false;
31271
+ for (const part of msg.parts) {
31272
+ if (!isRecord(part))
31273
+ continue;
31274
+ const partType = part.type;
31275
+ if (METADATA_PART_TYPES.has(partType))
31276
+ continue;
31277
+ if (partType === "tool") {
31278
+ hasNonDroppedContent = true;
31279
+ break;
31280
+ }
31281
+ if (partType === "text" && typeof part.text === "string") {
31282
+ hasContentPart = true;
31283
+ const trimmed = part.text.trim();
31284
+ if (trimmed.length === 0)
31285
+ continue;
31286
+ if (!trimmed.includes("[dropped §")) {
31287
+ hasNonDroppedContent = true;
31288
+ break;
31289
+ }
31290
+ const allSegmentsDropped = trimmed.split(/(?=\[dropped §)/).filter((s) => s.trim().length > 0).every((segment) => DROPPED_PLACEHOLDER_PATTERN.test(segment.trim()));
31291
+ if (!allSegmentsDropped) {
31292
+ hasNonDroppedContent = true;
31293
+ break;
31294
+ }
31295
+ continue;
31296
+ }
31297
+ if (partType === "reasoning" && typeof part.text === "string") {
31298
+ hasContentPart = true;
31299
+ const trimmed = part.text.trim();
31300
+ if (trimmed.length === 0)
31301
+ continue;
31302
+ if (!trimmed.includes("[dropped §")) {
31303
+ hasNonDroppedContent = true;
31304
+ break;
31305
+ }
31306
+ const allSegmentsDropped = trimmed.split(/(?=\[dropped §)/).filter((s) => s.trim().length > 0).every((segment) => DROPPED_PLACEHOLDER_PATTERN.test(segment.trim()));
31307
+ if (!allSegmentsDropped) {
31308
+ hasNonDroppedContent = true;
31309
+ break;
31310
+ }
31311
+ continue;
31312
+ }
31313
+ hasNonDroppedContent = true;
31314
+ break;
31315
+ }
31316
+ if (hasContentPart && !hasNonDroppedContent) {
31317
+ msg.parts.length = 0;
31318
+ msg.parts.push(makeWholeMessageSentinel(providerID));
31319
+ stripped++;
31320
+ if (typeof msg.info.id === "string")
31321
+ sentineledIds.push(msg.info.id);
31322
+ }
31166
31323
  }
31167
- return false;
31324
+ return { stripped, sentineledIds };
31168
31325
  }
31169
- function messageHasReducePart(message) {
31170
- for (const part of message.parts) {
31171
- if (isSentinel(part))
31326
+ function replayClearedReasoning(messages, reasoningByMessage, messageTagNumbers, persistedWatermark) {
31327
+ if (persistedWatermark <= 0)
31328
+ return 0;
31329
+ let cleared = 0;
31330
+ for (const message of messages) {
31331
+ const msgTag = messageTagNumbers.get(message) ?? 0;
31332
+ if (msgTag === 0 || msgTag > persistedWatermark)
31172
31333
  continue;
31173
- if (isReduceToolPart(part))
31174
- return true;
31334
+ const parts = reasoningByMessage.get(message);
31335
+ if (!parts)
31336
+ continue;
31337
+ for (const tp of parts) {
31338
+ if (tp.thinking !== undefined && tp.thinking !== "[cleared]") {
31339
+ tp.thinking = "[cleared]";
31340
+ cleared++;
31341
+ }
31342
+ if (tp.text !== undefined && tp.text !== "[cleared]") {
31343
+ tp.text = "[cleared]";
31344
+ cleared++;
31345
+ }
31346
+ }
31175
31347
  }
31176
- return false;
31348
+ return cleared;
31177
31349
  }
31178
- function sentinelizeReduceParts(message) {
31179
- let touched = false;
31180
- for (let j = 0;j < message.parts.length; j++) {
31181
- const part = message.parts[j];
31182
- if (isSentinel(part))
31350
+ function replayStrippedInlineThinking(messages, messageTagNumbers, persistedWatermark) {
31351
+ if (persistedWatermark <= 0)
31352
+ return 0;
31353
+ let stripped = 0;
31354
+ for (const message of messages) {
31355
+ if (message.info.role !== "assistant")
31183
31356
  continue;
31184
- if (isReduceToolPart(part)) {
31185
- message.parts[j] = makeSentinel(part);
31186
- touched = true;
31357
+ const msgTag = messageTagNumbers.get(message) ?? 0;
31358
+ if (msgTag === 0 || msgTag > persistedWatermark)
31359
+ continue;
31360
+ for (const part of message.parts) {
31361
+ if (!isRecord(part) || part.type !== "text" || typeof part.text !== "string")
31362
+ continue;
31363
+ if (!part.text.includes("<think"))
31364
+ continue;
31365
+ const cleaned = part.text.replace(INLINE_THINKING_PATTERN, "");
31366
+ if (cleaned !== part.text) {
31367
+ part.text = cleaned;
31368
+ stripped++;
31369
+ }
31187
31370
  }
31188
31371
  }
31189
- if (touched && !hasAnyMeaningfulPart(message.parts)) {
31190
- message.parts.length = 0;
31191
- message.parts.push(makeSentinel(undefined));
31192
- }
31193
- return touched;
31372
+ return stripped;
31194
31373
  }
31195
- function dropStaleReduceCalls(messages, frozenIds, options = {}) {
31196
- const detect = options.detect ?? false;
31197
- const protectedCount = options.protectedCount ?? 0;
31198
- const protectedStart = messages.length - protectedCount;
31199
- const newlyStrippedIds = [];
31200
- let didDrop = false;
31201
- for (let i = 0;i < messages.length; i++) {
31202
- const message = messages[i];
31203
- const id = typeof message.info.id === "string" ? message.info.id : undefined;
31204
- const inFrozen = id !== undefined && frozenIds.has(id);
31205
- const isNewDetection = !inFrozen && detect && i < protectedStart && id !== undefined && messageHasReducePart(message);
31206
- if (!inFrozen && !isNewDetection)
31374
+ function clearOldReasoning(messages, reasoningByMessage, messageTagNumbers, clearReasoningAge) {
31375
+ const maxTag = findMaxTag(messageTagNumbers);
31376
+ if (maxTag === 0)
31377
+ return 0;
31378
+ const ageCutoff = maxTag - clearReasoningAge;
31379
+ let cleared = 0;
31380
+ for (const message of messages) {
31381
+ const msgTag = messageTagNumbers.get(message) ?? 0;
31382
+ if (msgTag === 0 || msgTag > ageCutoff)
31207
31383
  continue;
31208
- const touched = sentinelizeReduceParts(message);
31209
- if (touched) {
31210
- didDrop = true;
31211
- if (isNewDetection && id !== undefined)
31212
- newlyStrippedIds.push(id);
31384
+ const parts = reasoningByMessage.get(message);
31385
+ if (!parts)
31386
+ continue;
31387
+ for (const tp of parts) {
31388
+ if (tp.thinking !== undefined && tp.thinking !== "[cleared]") {
31389
+ tp.thinking = "[cleared]";
31390
+ cleared++;
31391
+ }
31392
+ if (tp.text !== undefined && tp.text !== "[cleared]") {
31393
+ tp.text = "[cleared]";
31394
+ cleared++;
31395
+ }
31213
31396
  }
31214
31397
  }
31215
- return { didDrop, newlyStrippedIds };
31398
+ return cleared;
31216
31399
  }
31217
-
31218
- // src/hooks/magic-context/fold-execution-gate.ts
31219
- function foldExecutesThisPass(foldDue, materialized) {
31220
- return foldDue && materialized;
31400
+ function findMaxTag(messageTagNumbers) {
31401
+ let max = 0;
31402
+ for (const tag of messageTagNumbers.values()) {
31403
+ if (tag > max)
31404
+ max = tag;
31405
+ }
31406
+ return max;
31221
31407
  }
31222
-
31223
- // src/hooks/magic-context/system-injection-stripper.ts
31224
- var SYSTEM_INJECTION_MARKERS = [
31225
- "<!-- OMO_INTERNAL_INITIATOR -->",
31226
- "[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
31227
- "[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
31228
- "[Category+Skill Reminder]",
31229
- "[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
31230
- "[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
31231
- "[EMERGENCY CONTEXT WINDOW WARNING]",
31232
- "Unstable background agent appears idle",
31233
- "**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
31234
- ];
31235
- var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
31236
- var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
31237
- function stripSystemInjection(text) {
31238
- let hasInjection = false;
31239
- for (const marker of SYSTEM_INJECTION_MARKERS) {
31240
- if (text.includes(marker)) {
31241
- hasInjection = true;
31242
- break;
31408
+ var CLEARED_REASONING_TYPES = new Set(["thinking", "reasoning"]);
31409
+ function stripClearedReasoning(messages) {
31410
+ let stripped = 0;
31411
+ for (const message of messages) {
31412
+ if (message.info.role !== "assistant")
31413
+ continue;
31414
+ for (let i = 0;i < message.parts.length; i++) {
31415
+ const part = message.parts[i];
31416
+ if (!isRecord(part))
31417
+ continue;
31418
+ const partType = part.type;
31419
+ if (!CLEARED_REASONING_TYPES.has(partType))
31420
+ continue;
31421
+ if (!("thinking" in part) && !("text" in part))
31422
+ continue;
31423
+ const thinking = "thinking" in part ? part.thinking : undefined;
31424
+ const text = "text" in part ? part.text : undefined;
31425
+ const isCleared = (thinking === undefined || thinking === "[cleared]") && (text === undefined || text === "[cleared]");
31426
+ if (!isCleared)
31427
+ continue;
31428
+ message.parts[i] = makeSentinel(part);
31429
+ stripped++;
31243
31430
  }
31244
31431
  }
31245
- if (SYSTEM_REMINDER_REGEX.test(text))
31246
- hasInjection = true;
31247
- SYSTEM_REMINDER_REGEX.lastIndex = 0;
31248
- if (!hasInjection)
31249
- return null;
31250
- let cleaned = text;
31251
- cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
31252
- cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
31253
- cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
31254
- for (const marker of SYSTEM_INJECTION_MARKERS) {
31255
- if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
31432
+ return stripped;
31433
+ }
31434
+ var INLINE_THINKING_PATTERN = /<(?:thinking|think)>[\s\S]*?<\/(?:thinking|think)>\s*/g;
31435
+ function stripInlineThinking(messages, messageTagNumbers, clearReasoningAge) {
31436
+ const maxTag = findMaxTag(messageTagNumbers);
31437
+ if (maxTag === 0)
31438
+ return 0;
31439
+ const ageCutoff = maxTag - clearReasoningAge;
31440
+ let stripped = 0;
31441
+ for (const message of messages) {
31442
+ if (message.info.role !== "assistant")
31256
31443
  continue;
31257
- const idx = cleaned.indexOf(marker);
31258
- if (idx === -1)
31444
+ const msgTag = messageTagNumbers.get(message) ?? 0;
31445
+ if (msgTag === 0 || msgTag > ageCutoff)
31259
31446
  continue;
31260
- const blockEnd = cleaned.indexOf(`
31261
-
31262
- `, idx + marker.length);
31263
- cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
31447
+ for (const part of message.parts) {
31448
+ if (!isRecord(part) || part.type !== "text" || typeof part.text !== "string")
31449
+ continue;
31450
+ const cleaned = part.text.replace(INLINE_THINKING_PATTERN, "");
31451
+ if (cleaned !== part.text) {
31452
+ part.text = cleaned;
31453
+ stripped++;
31454
+ }
31455
+ }
31264
31456
  }
31265
- return cleaned.trim();
31457
+ return stripped;
31266
31458
  }
31267
-
31268
- // src/hooks/magic-context/heuristic-cleanup.ts
31269
- var DEDUP_SAFE_TOOLS = new Set([
31270
- "mcp_grep",
31271
- "mcp_read",
31272
- "mcp_glob",
31273
- "mcp_ast_grep_search",
31274
- "mcp_lsp_diagnostics",
31275
- "mcp_lsp_symbols",
31276
- "mcp_lsp_find_references",
31277
- "mcp_lsp_goto_definition",
31278
- "mcp_lsp_prepare_rename"
31279
- ]);
31280
- function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config, preloadedTags) {
31281
- const tags = preloadedTags ?? getActiveTagsBySession(db, sessionId);
31282
- const maxTag = getMaxTagNumberBySession(db, sessionId);
31283
- const protectedCutoff = maxTag - config.protectedTags;
31284
- let droppedTools = 0;
31285
- let emergencyDroppedTools = 0;
31286
- let emergencyReclaimedTokens = 0;
31287
- let deduplicatedTools = 0;
31288
- let droppedInjections = 0;
31289
- if (config.emergency) {
31290
- const emergency = config.emergency;
31291
- const priorInputSample = getEmergencyInputSample(db, sessionId);
31292
- const droppableTags = tags.filter((t) => t.status === "active" && t.type === "tool" && targets.get(t.tagNumber)?.canDrop?.());
31293
- const activeTags = tags.filter((t) => t.status === "active");
31294
- const plan = planEmergencyDrop({
31295
- tags: droppableTags,
31296
- floorTags: activeTags,
31297
- maxTag,
31298
- protectedTags: config.protectedTags,
31299
- currentTotalInputTokens: emergency.currentTotalInputTokens,
31300
- ceilingTokens: emergency.ceilingTokens,
31301
- priorInputSample,
31302
- hasPriorDrop: priorInputSample > 0
31303
- });
31304
- if (plan.shouldDrop) {
31305
- const toDrop = new Set(plan.tagNumbers);
31306
- const newestEmergencyTags = new Set(droppableTags.slice().sort((left, right) => right.tagNumber - left.tagNumber).slice(0, 20).map((tag) => tag.tagNumber));
31307
- db.transaction(() => {
31308
- for (const tag of tags) {
31309
- if (!toDrop.has(tag.tagNumber))
31310
- continue;
31311
- if (tag.status !== "active" || tag.type !== "tool")
31312
- continue;
31313
- const target = targets.get(tag.tagNumber);
31314
- const recent = newestEmergencyTags.has(tag.tagNumber);
31315
- const result = recent ? target?.truncate?.() ?? target?.drop?.() ?? "absent" : target?.drop?.() ?? "absent";
31316
- if (result === "removed" || result === "truncated") {
31317
- updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
31318
- updateTagDropMode(db, sessionId, tag.tagNumber, recent ? "truncated" : "full");
31319
- droppedTools++;
31320
- emergencyDroppedTools++;
31321
- emergencyReclaimedTokens += estimateEmergencyDropReclaimTokens(tag);
31322
- }
31323
- }
31324
- })();
31325
- sessionLog(sessionId, `emergency tiered drop: ${plan.reason}`);
31326
- } else {
31327
- sessionLog(sessionId, `emergency tiered drop skipped: ${plan.reason}`);
31459
+ var REASONING_IGNORED_PART_TYPES = new Set([
31460
+ "step-start",
31461
+ "step-finish",
31462
+ "snapshot",
31463
+ "patch",
31464
+ "agent",
31465
+ "retry",
31466
+ "subtask",
31467
+ "compaction"
31468
+ ]);
31469
+ var REASONING_PART_TYPES = new Set(["reasoning", "thinking", "redacted_thinking"]);
31470
+ function hasReasoningReplayContent(message) {
31471
+ return message.parts.some((part) => {
31472
+ if (!isRecord(part) || part.ignored === true)
31473
+ return false;
31474
+ const type = typeof part.type === "string" ? part.type : "";
31475
+ if (REASONING_IGNORED_PART_TYPES.has(type))
31476
+ return false;
31477
+ return type !== "text" || typeof part.text !== "string" || part.text.trim().length > 0;
31478
+ });
31479
+ }
31480
+ function findNewestReasoningBearingAssistantId(messages) {
31481
+ for (let index = messages.length - 1;index >= 0; index -= 1) {
31482
+ const message = messages[index];
31483
+ if (message.info.role !== "assistant")
31484
+ continue;
31485
+ if (!message.parts.some((part) => isRecord(part) && REASONING_PART_TYPES.has(String(part.type)))) {
31486
+ continue;
31328
31487
  }
31329
- setEmergencyDropSample(db, sessionId, emergency.currentTotalInputTokens);
31488
+ const id = message.info.id;
31489
+ if (typeof id === "string" && id.length > 0)
31490
+ return id;
31330
31491
  }
31331
- if (config.routine !== false) {
31332
- db.transaction(() => {
31333
- for (const tag of tags) {
31334
- if (tag.status !== "active")
31335
- continue;
31336
- if (tag.tagNumber > protectedCutoff)
31492
+ return;
31493
+ }
31494
+ function assistantHasReasoningPart(messages, messageId2) {
31495
+ return messages.some((message) => message.info.role === "assistant" && message.info.id === messageId2 && message.parts.some((part) => isRecord(part) && REASONING_PART_TYPES.has(String(part.type))));
31496
+ }
31497
+ function findLatestAssistantReasoningMutationExemptMessage(messages) {
31498
+ for (let index = messages.length - 1;index >= 0; index -= 1) {
31499
+ const message = messages[index];
31500
+ if (message.info.role === "assistant" && hasReasoningReplayContent(message))
31501
+ return message;
31502
+ }
31503
+ return;
31504
+ }
31505
+ function planMergedAssistantReasoningStrip(messages, mutationExemptMessage) {
31506
+ const plan = [];
31507
+ let prevRole;
31508
+ let keptReasoningInRun = false;
31509
+ for (const message of messages) {
31510
+ const role = message.info.role;
31511
+ if (role !== "assistant") {
31512
+ prevRole = role;
31513
+ keptReasoningInRun = false;
31514
+ continue;
31515
+ }
31516
+ const firstInRun = prevRole !== "assistant";
31517
+ if (firstInRun)
31518
+ keptReasoningInRun = false;
31519
+ if (message === mutationExemptMessage) {
31520
+ prevRole = role;
31521
+ continue;
31522
+ }
31523
+ let keepIndex = -1;
31524
+ if (firstInRun && !keptReasoningInRun) {
31525
+ for (let i = 0;i < message.parts.length; i++) {
31526
+ const part = message.parts[i];
31527
+ if (!isRecord(part))
31337
31528
  continue;
31338
- if (tag.type !== "message")
31529
+ const partType = part.type;
31530
+ if (REASONING_IGNORED_PART_TYPES.has(partType))
31339
31531
  continue;
31340
- const target = targets.get(tag.tagNumber);
31341
- if (!target)
31532
+ if (part.ignored === true)
31342
31533
  continue;
31343
- const content = target.getContent?.();
31344
- if (!content)
31534
+ if (isSentinel(part))
31345
31535
  continue;
31346
- const stripped = stripSystemInjection(content);
31347
- if (stripped === null)
31536
+ if (partType === "text" && typeof part.text === "string" && part.text.trim() === "") {
31348
31537
  continue;
31349
- const strippedSource = stripTagPrefix(stripped);
31350
- if (strippedSource.trim().length === 0) {
31351
- const dropResult = target.drop?.() ?? "absent";
31352
- const didReplace = dropResult === "absent" ? target.setContent(`[dropped §${tag.tagNumber}§]`) : false;
31353
- if (dropResult === "removed" || dropResult === "absent") {
31354
- replaceSourceContent(db, sessionId, tag.tagNumber, "");
31355
- updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
31356
- if (dropResult === "removed" || didReplace) {
31357
- droppedInjections++;
31358
- }
31359
- }
31360
- } else {
31361
- const didSet = target.setContent(stripped);
31362
- if (didSet) {
31363
- replaceSourceContent(db, sessionId, tag.tagNumber, strippedSource);
31364
- droppedInjections++;
31365
- }
31366
31538
  }
31367
- }
31368
- })();
31369
- }
31370
- const allMessages = Array.from(messageTagNumbers.keys());
31371
- const toolFingerprints = buildToolFingerprints(allMessages);
31372
- if (config.routine !== false && toolFingerprints.size > 0) {
31373
- const tagsByCompositeKey = new Map;
31374
- for (const tag of tags) {
31375
- if (tag.type === "tool" && tag.status === "active" && tag.messageId) {
31376
- const key = tag.toolOwnerMessageId ? `${tag.toolOwnerMessageId}\x00${tag.messageId}` : tag.messageId;
31377
- tagsByCompositeKey.set(key, tag);
31539
+ if (REASONING_PART_TYPES.has(partType)) {
31540
+ keepIndex = i;
31541
+ }
31542
+ break;
31378
31543
  }
31379
31544
  }
31380
- const fingerprintGroups = new Map;
31381
- for (const [compositeKey, fingerprint] of toolFingerprints) {
31382
- const tag = tagsByCompositeKey.get(compositeKey);
31383
- if (!tag || tag.tagNumber > protectedCutoff)
31545
+ const stripIndices = [];
31546
+ for (let i = 0;i < message.parts.length; i++) {
31547
+ const part = message.parts[i];
31548
+ if (!isRecord(part))
31549
+ continue;
31550
+ if (!REASONING_PART_TYPES.has(part.type))
31551
+ continue;
31552
+ if (part.cache_control !== undefined)
31553
+ continue;
31554
+ if (i === keepIndex) {
31555
+ keptReasoningInRun = true;
31384
31556
  continue;
31385
- const group = fingerprintGroups.get(fingerprint) ?? [];
31386
- group.push(tag);
31387
- fingerprintGroups.set(fingerprint, group);
31388
- }
31389
- db.transaction(() => {
31390
- for (const [, group] of fingerprintGroups) {
31391
- if (group.length <= 1)
31392
- continue;
31393
- group.sort((a, b) => a.tagNumber - b.tagNumber);
31394
- for (let i = 0;i < group.length - 1; i++) {
31395
- const tag = group[i];
31396
- const target = targets.get(tag.tagNumber);
31397
- const result = target?.drop?.() ?? "absent";
31398
- if (result === "incomplete")
31399
- continue;
31400
- updateTagDropMode(db, sessionId, tag.tagNumber, "full");
31401
- updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
31402
- if (result === "removed" || result === "truncated") {
31403
- deduplicatedTools++;
31404
- }
31405
- }
31406
31557
  }
31407
- })();
31408
- }
31409
- if (droppedTools > 0 || deduplicatedTools > 0 || droppedInjections > 0) {
31410
- sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
31411
- }
31412
- let compressedTextTags = 0;
31413
- let mutatedTextTags = 0;
31414
- if (config.routine !== false && config.caveman?.enabled) {
31415
- const cavemanResult = applyCavemanCleanup(sessionId, db, targets, tags, {
31416
- enabled: true,
31417
- minChars: config.caveman.minChars,
31418
- protectedTags: config.protectedTags
31419
- });
31420
- compressedTextTags = cavemanResult.compressedToLite + cavemanResult.compressedToFull + cavemanResult.compressedToUltra;
31421
- mutatedTextTags = cavemanResult.mutatedTextTags;
31558
+ stripIndices.push(i);
31559
+ }
31560
+ if (stripIndices.length > 0)
31561
+ plan.push({ message, stripIndices });
31562
+ prevRole = role;
31422
31563
  }
31423
- return {
31424
- droppedTools,
31425
- deduplicatedTools,
31426
- droppedInjections,
31427
- emergencyDroppedTools,
31428
- emergencyReclaimedTokens,
31429
- compressedTextTags,
31430
- mutatedTextTags
31431
- };
31564
+ return plan;
31432
31565
  }
31433
- function extractToolInfo(part) {
31434
- if (part.type === "tool" && typeof part.tool === "string" && DEDUP_SAFE_TOOLS.has(part.tool)) {
31435
- const state = typeof part.state === "object" && part.state !== null ? part.state : {};
31436
- return { toolName: part.tool, args: state.input ?? {} };
31566
+ var MAX_FROZEN_TRAILING_BLANKS = 1e4;
31567
+ var CANONICAL_BLANK_PART = { type: "text", text: "" };
31568
+ function isSentinelInvisibleTextPart(part) {
31569
+ return isRecord(part) && part.type === "text" && typeof part.text === "string" && part.text.trim() === "";
31570
+ }
31571
+ function isCanonicalBlankPart(part) {
31572
+ return isRecord(part) && Object.keys(part).length === 2 && part.type === "text" && part.text === "";
31573
+ }
31574
+ function trailingBlankKeepCount(decision) {
31575
+ if (decision === "keep")
31576
+ return 1;
31577
+ if (!decision.startsWith("keep:"))
31578
+ return;
31579
+ const countText = decision.slice("keep:".length);
31580
+ if (!/^[1-9]\d*$/.test(countText))
31581
+ return;
31582
+ const count = Number(countText);
31583
+ return Number.isSafeInteger(count) && count > 0 && count <= MAX_FROZEN_TRAILING_BLANKS ? count : undefined;
31584
+ }
31585
+ function trailingBlankDecisionForMessage(message) {
31586
+ const id = message.info.id;
31587
+ if (message.info.role !== "assistant" || typeof id !== "string" || id.length === 0) {
31588
+ return;
31437
31589
  }
31438
- if (part.type === "tool-invocation" && typeof part.toolName === "string" && DEDUP_SAFE_TOOLS.has(part.toolName)) {
31439
- return { toolName: part.toolName, args: part.args ?? {} };
31590
+ let trailingCount = 0;
31591
+ while (trailingCount < message.parts.length && isSentinelInvisibleTextPart(message.parts[message.parts.length - trailingCount - 1])) {
31592
+ trailingCount += 1;
31440
31593
  }
31441
- if (part.type === "tool_use" && typeof part.name === "string" && DEDUP_SAFE_TOOLS.has(part.name)) {
31442
- return { toolName: part.name, args: part.input ?? {} };
31594
+ if (trailingCount > MAX_FROZEN_TRAILING_BLANKS && trailingCount < message.parts.length) {
31595
+ return;
31443
31596
  }
31444
- return null;
31597
+ const decision = message.parts.length === 0 || trailingCount === message.parts.length ? "keep" : trailingCount === 0 ? "strip" : trailingCount === 1 ? "keep" : `keep:${trailingCount}`;
31598
+ return [id, decision];
31445
31599
  }
31446
- function buildToolFingerprints(messages) {
31447
- const fingerprints = new Map;
31600
+ function snapshotTrailingBlankSourceDecisions(messages) {
31601
+ const decisions = new Map;
31448
31602
  for (const message of messages) {
31449
- if (message.info.role !== "assistant")
31603
+ const observed = trailingBlankDecisionForMessage(message);
31604
+ if (observed)
31605
+ decisions.set(...observed);
31606
+ }
31607
+ return decisions;
31608
+ }
31609
+ function findTrailingBlankDecisionCandidates(messages, frozenDecisions, options) {
31610
+ const observedDecisions = options?.sourceDecisions ?? snapshotTrailingBlankSourceDecisions(messages);
31611
+ const visibleIds = new Set(messages.flatMap((message) => typeof message.info.id === "string" ? [message.info.id] : []));
31612
+ const decisions = [];
31613
+ for (const [id, decision] of observedDecisions) {
31614
+ if (!visibleIds.has(id))
31450
31615
  continue;
31451
- const ownerMsgId = typeof message.info.id === "string" ? message.info.id : null;
31452
- if (!ownerMsgId)
31616
+ const frozen = frozenDecisions.get(id);
31617
+ if (frozen !== undefined && (id !== options?.refreshMessageId || frozen === decision || frozen === "strip")) {
31453
31618
  continue;
31454
- for (const part of message.parts) {
31455
- const record = part;
31456
- const info = extractToolInfo(record);
31457
- if (!info)
31458
- continue;
31459
- const callId = extractCallId(record);
31460
- if (!callId)
31461
- continue;
31462
- try {
31463
- const fingerprint = `${ownerMsgId}:${info.toolName}:${JSON.stringify(info.args)}`;
31464
- const compositeKey = `${ownerMsgId}\x00${callId}`;
31465
- fingerprints.set(compositeKey, fingerprint);
31466
- } catch {}
31467
31619
  }
31620
+ decisions.push([id, decision]);
31468
31621
  }
31469
- return fingerprints;
31470
- }
31471
- function extractCallId(part) {
31472
- if (part.type === "tool" && typeof part.callID === "string")
31473
- return part.callID;
31474
- if (part.type === "tool-invocation" && typeof part.callID === "string")
31475
- return part.callID;
31476
- if (part.type === "tool_use" && typeof part.id === "string")
31477
- return part.id;
31478
- return null;
31622
+ return decisions;
31479
31623
  }
31480
-
31481
- // src/hooks/magic-context/note-visibility.ts
31482
- var NOTE_TOOL_NAMES = new Set(["ctx_note"]);
31483
- var READ_ACTION = "read";
31484
- function hasVisibleNoteReadCall(messages) {
31485
- for (let i = messages.length - 1;i >= 0; i -= 1) {
31486
- const parts = messages[i]?.parts;
31487
- if (!Array.isArray(parts))
31624
+ function applyFrozenTrailingBlankDecisions(messages, frozenDecisions) {
31625
+ let mutations = 0;
31626
+ for (let messageIndex = 0;messageIndex < messages.length; messageIndex += 1) {
31627
+ let message = messages[messageIndex];
31628
+ const id = message.info.id;
31629
+ if (message.info.role !== "assistant" || typeof id !== "string")
31488
31630
  continue;
31489
- for (const part of parts) {
31490
- if (isSentinel(part))
31491
- continue;
31492
- if (isVisibleNoteReadPart(part))
31493
- return true;
31631
+ const decision = frozenDecisions.get(id);
31632
+ if (!decision)
31633
+ continue;
31634
+ let lastMeaningfulIndex = message.parts.length - 1;
31635
+ while (lastMeaningfulIndex >= 0 && isSentinelInvisibleTextPart(message.parts[lastMeaningfulIndex])) {
31636
+ lastMeaningfulIndex -= 1;
31494
31637
  }
31495
- }
31496
- return false;
31497
- }
31498
- function isVisibleNoteReadPart(part) {
31499
- if (!isRecord(part))
31500
- return false;
31501
- if (part.type === "tool" && typeof part.tool === "string" && NOTE_TOOL_NAMES.has(part.tool)) {
31502
- const state = part.state;
31503
- if (isRecord(state) && isRecord(state.input)) {
31504
- return state.input.action === READ_ACTION;
31638
+ const replaceParts = (start, deleteCount, insertBlankCount) => {
31639
+ message = { ...message, parts: [...message.parts] };
31640
+ messages[messageIndex] = message;
31641
+ message.parts.splice(start, deleteCount, ...Array.from({ length: insertBlankCount }, () => ({ ...CANONICAL_BLANK_PART })));
31642
+ };
31643
+ if (lastMeaningfulIndex < 0) {
31644
+ if (message.parts.length === 0)
31645
+ continue;
31646
+ if (message.parts.length !== 1 || !isCanonicalBlankPart(message.parts[0])) {
31647
+ mutations += Math.max(1, message.parts.length);
31648
+ replaceParts(0, message.parts.length, 1);
31649
+ }
31650
+ continue;
31505
31651
  }
31506
- return false;
31507
- }
31508
- if (part.type === "tool_use" && typeof part.name === "string" && NOTE_TOOL_NAMES.has(part.name)) {
31509
- if (isRecord(part.input)) {
31510
- return part.input.action === READ_ACTION;
31652
+ const trailingCount = message.parts.length - lastMeaningfulIndex - 1;
31653
+ const keepCount = trailingBlankKeepCount(decision);
31654
+ if (keepCount !== undefined) {
31655
+ if (trailingCount === 0)
31656
+ continue;
31657
+ const blankIndex = lastMeaningfulIndex + 1;
31658
+ const suffixIsCanonical = trailingCount === keepCount && message.parts.slice(blankIndex).every(isCanonicalBlankPart);
31659
+ if (!suffixIsCanonical) {
31660
+ mutations += Math.max(1, trailingCount, keepCount);
31661
+ replaceParts(blankIndex, trailingCount, keepCount);
31662
+ }
31663
+ continue;
31511
31664
  }
31512
- return false;
31513
- }
31514
- if (part.type === "tool-invocation" && typeof part.toolName === "string" && NOTE_TOOL_NAMES.has(part.toolName)) {
31515
- const argsCandidate = part.args ?? part.input;
31516
- if (isRecord(argsCandidate)) {
31517
- return argsCandidate.action === READ_ACTION;
31665
+ if (trailingCount === 0)
31666
+ continue;
31667
+ const lastMeaningfulPart = message.parts[lastMeaningfulIndex];
31668
+ if (isRecord(lastMeaningfulPart) && REASONING_PART_TYPES.has(lastMeaningfulPart.type)) {
31669
+ const blankIndex = lastMeaningfulIndex + 1;
31670
+ if (trailingCount !== 1 || !isCanonicalBlankPart(message.parts[blankIndex])) {
31671
+ mutations += Math.max(1, trailingCount);
31672
+ replaceParts(blankIndex, trailingCount, 1);
31673
+ }
31674
+ continue;
31518
31675
  }
31519
- return false;
31676
+ mutations += trailingCount;
31677
+ replaceParts(lastMeaningfulIndex + 1, trailingCount, 0);
31520
31678
  }
31521
- return false;
31679
+ return mutations;
31522
31680
  }
31523
-
31524
- // src/hooks/magic-context/strip-content.ts
31525
- var DROPPED_PLACEHOLDER_PATTERN = /^\[dropped §\d+§\]$/;
31526
- var TAG_PREFIX_PATTERN = /^§\d+§\s*/;
31527
- var SYSTEM_INJECTION_PATTERNS = [
31528
- /^<!-- OMO_INTERNAL_INITIATOR -->$/,
31529
- /^<system-reminder>[\s\S]*<\/system-reminder>$/,
31530
- /^\[SYSTEM DIRECTIVE:/,
31531
- /^\[Category\+Skill Reminder\]/,
31532
- /^\[EDIT ERROR - IMMEDIATE ACTION REQUIRED\]/,
31533
- /^\[task CALL FAILED/,
31534
- /^\[EMERGENCY CONTEXT WINDOW WARNING\]/
31535
- ];
31536
- function isSystemInjectedText(text) {
31537
- const stripped = text.trim().replace(TAG_PREFIX_PATTERN, "").trim();
31538
- if (stripped.length === 0)
31539
- return false;
31540
- return SYSTEM_INJECTION_PATTERNS.some((pattern) => pattern.test(stripped));
31681
+ function findMergedReasoningStripCandidateIds(messages, providerID, options) {
31682
+ if (providerID !== "anthropic")
31683
+ return [];
31684
+ const ids = new Set;
31685
+ for (const entry of planMergedAssistantReasoningStrip(messages, options?.mutationExemptMessage)) {
31686
+ const id = entry.message.info.id;
31687
+ if (typeof id === "string" && id.length > 0)
31688
+ ids.add(id);
31689
+ }
31690
+ return [...ids];
31541
31691
  }
31542
- function stripSystemInjectedMessages(messages, protectedTailStart, providerID) {
31692
+ function stripReasoningFromAssistantIds(messages, providerID, messageIds) {
31693
+ if (providerID !== "anthropic" || messageIds.size === 0)
31694
+ return 0;
31543
31695
  let stripped = 0;
31544
- const sentineledIds = [];
31545
- for (let i = 0;i < messages.length; i++) {
31546
- if (i >= protectedTailStart)
31547
- continue;
31548
- const msg = messages[i];
31549
- if (msg.parts.length === 0)
31550
- continue;
31551
- if (msg.info.role === "user")
31552
- continue;
31553
- if (msg.parts.length === 1 && isSentinel(msg.parts[0]))
31696
+ for (const message of messages) {
31697
+ const id = message.info.id;
31698
+ if (typeof id !== "string" || !messageIds.has(id))
31554
31699
  continue;
31555
- let hasContentPart = false;
31556
- let allContentIsSystemInjection = true;
31557
- for (const part of msg.parts) {
31558
- if (!isRecord(part))
31559
- continue;
31560
- const partType = part.type;
31561
- if (METADATA_PART_TYPES.has(partType))
31562
- continue;
31563
- if (part.ignored === true)
31700
+ for (let index = 0;index < message.parts.length; index += 1) {
31701
+ const part = message.parts[index];
31702
+ if (!isRecord(part) || !REASONING_PART_TYPES.has(String(part.type)))
31564
31703
  continue;
31565
- if (partType === "tool") {
31566
- allContentIsSystemInjection = false;
31567
- break;
31568
- }
31569
- if (partType === "text" && typeof part.text === "string") {
31570
- hasContentPart = true;
31571
- if (!isSystemInjectedText(part.text)) {
31572
- allContentIsSystemInjection = false;
31573
- break;
31574
- }
31704
+ message.parts[index] = makeSentinel(part);
31705
+ stripped += 1;
31706
+ }
31707
+ }
31708
+ return stripped;
31709
+ }
31710
+ function stripReasoningFromMergedAssistants(messages, providerID, options) {
31711
+ if (providerID !== "anthropic")
31712
+ return 0;
31713
+ let stripped = 0;
31714
+ for (const entry of planMergedAssistantReasoningStrip(messages, options?.mutationExemptMessage)) {
31715
+ if (options?.frozenMessageIds) {
31716
+ const id = entry.message.info.id;
31717
+ if (typeof id !== "string" || !options.frozenMessageIds.has(id))
31575
31718
  continue;
31576
- }
31577
- allContentIsSystemInjection = false;
31578
- break;
31579
31719
  }
31580
- if (hasContentPart && allContentIsSystemInjection) {
31581
- msg.parts.length = 0;
31582
- msg.parts.push(makeWholeMessageSentinel(providerID));
31720
+ for (const index of entry.stripIndices) {
31721
+ entry.message.parts[index] = makeSentinel(entry.message.parts[index]);
31583
31722
  stripped++;
31584
- if (typeof msg.info.id === "string")
31585
- sentineledIds.push(msg.info.id);
31586
31723
  }
31587
31724
  }
31588
- return { stripped, sentineledIds };
31725
+ return stripped;
31589
31726
  }
31590
- var METADATA_PART_TYPES = new Set([
31591
- "step-start",
31592
- "step-finish",
31593
- "snapshot",
31594
- "patch",
31595
- "agent",
31596
- "retry",
31597
- "subtask",
31598
- "compaction"
31599
- ]);
31600
- function stripDroppedPlaceholderMessages(messages, providerID) {
31727
+ function stripProcessedImages(messages, frozenIds, options) {
31728
+ const { detect, watermark, messageTagNumbers } = options;
31601
31729
  let stripped = 0;
31602
- const sentineledIds = [];
31603
- for (let i = 0;i < messages.length; i++) {
31730
+ const newlyStrippedIds = [];
31731
+ let hasAssistantResponse = false;
31732
+ for (let i = messages.length - 1;i >= 0; i--) {
31604
31733
  const msg = messages[i];
31605
- if (msg.parts.length === 0)
31734
+ if (msg.info.role === "assistant") {
31735
+ hasAssistantResponse = true;
31606
31736
  continue;
31607
- if (msg.info.role === "user")
31737
+ }
31738
+ if (msg.info.role !== "user") {
31608
31739
  continue;
31609
- if (msg.parts.length === 1 && isSentinel(msg.parts[0]))
31740
+ }
31741
+ const id = typeof msg.info.id === "string" ? msg.info.id : undefined;
31742
+ const inFrozen = id !== undefined && frozenIds.has(id);
31743
+ const maxTag = messageTagNumbers.get(msg) ?? 0;
31744
+ const isNewDetection = !inFrozen && detect && hasAssistantResponse && id !== undefined && maxTag <= watermark;
31745
+ if (!inFrozen && !isNewDetection) {
31610
31746
  continue;
31611
- let hasContentPart = false;
31612
- let hasNonDroppedContent = false;
31613
- for (const part of msg.parts) {
31614
- if (!isRecord(part))
31615
- continue;
31616
- const partType = part.type;
31617
- if (METADATA_PART_TYPES.has(partType))
31747
+ }
31748
+ let touchedThisMsg = false;
31749
+ for (let j = 0;j < msg.parts.length; j++) {
31750
+ const part = msg.parts[j];
31751
+ if (!isRecord(part) || part.type !== "file") {
31618
31752
  continue;
31619
- if (partType === "tool") {
31620
- hasNonDroppedContent = true;
31621
- break;
31622
31753
  }
31623
- if (partType === "text" && typeof part.text === "string") {
31624
- hasContentPart = true;
31625
- const trimmed = part.text.trim();
31626
- if (trimmed.length === 0)
31627
- continue;
31628
- if (!trimmed.includes("[dropped §")) {
31629
- hasNonDroppedContent = true;
31630
- break;
31631
- }
31632
- const allSegmentsDropped = trimmed.split(/(?=\[dropped §)/).filter((s) => s.trim().length > 0).every((segment) => DROPPED_PLACEHOLDER_PATTERN.test(segment.trim()));
31633
- if (!allSegmentsDropped) {
31634
- hasNonDroppedContent = true;
31635
- break;
31636
- }
31754
+ if (typeof part.mime !== "string" || !part.mime.startsWith("image/")) {
31637
31755
  continue;
31638
31756
  }
31639
- if (partType === "reasoning" && typeof part.text === "string") {
31640
- hasContentPart = true;
31641
- const trimmed = part.text.trim();
31642
- if (trimmed.length === 0)
31643
- continue;
31644
- if (!trimmed.includes("[dropped §")) {
31645
- hasNonDroppedContent = true;
31646
- break;
31647
- }
31648
- const allSegmentsDropped = trimmed.split(/(?=\[dropped §)/).filter((s) => s.trim().length > 0).every((segment) => DROPPED_PLACEHOLDER_PATTERN.test(segment.trim()));
31649
- if (!allSegmentsDropped) {
31650
- hasNonDroppedContent = true;
31651
- break;
31652
- }
31653
- continue;
31757
+ if (typeof part.url === "string" && part.url.startsWith("data:") && part.url.length > 200) {
31758
+ msg.parts[j] = makeSentinel(part);
31759
+ stripped++;
31760
+ touchedThisMsg = true;
31654
31761
  }
31655
- hasNonDroppedContent = true;
31656
- break;
31657
31762
  }
31658
- if (hasContentPart && !hasNonDroppedContent) {
31659
- msg.parts.length = 0;
31660
- msg.parts.push(makeWholeMessageSentinel(providerID));
31661
- stripped++;
31662
- if (typeof msg.info.id === "string")
31663
- sentineledIds.push(msg.info.id);
31763
+ if (touchedThisMsg && isNewDetection && id !== undefined) {
31764
+ newlyStrippedIds.push(id);
31664
31765
  }
31665
31766
  }
31666
- return { stripped, sentineledIds };
31767
+ return { stripped, newlyStrippedIds };
31667
31768
  }
31668
- function replayClearedReasoning(messages, reasoningByMessage, messageTagNumbers, persistedWatermark) {
31669
- if (persistedWatermark <= 0)
31670
- return 0;
31671
- let cleared = 0;
31672
- for (const message of messages) {
31673
- const msgTag = messageTagNumbers.get(message) ?? 0;
31674
- if (msgTag === 0 || msgTag > persistedWatermark)
31675
- continue;
31676
- const parts = reasoningByMessage.get(message);
31677
- if (!parts)
31678
- continue;
31679
- for (const tp of parts) {
31680
- if (tp.thinking !== undefined && tp.thinking !== "[cleared]") {
31681
- tp.thinking = "[cleared]";
31682
- cleared++;
31683
- }
31684
- if (tp.text !== undefined && tp.text !== "[cleared]") {
31685
- tp.text = "[cleared]";
31686
- cleared++;
31687
- }
31769
+
31770
+ // src/hooks/magic-context/transform-context-state.ts
31771
+ function loadPersistedUsageWatermark(db, sessionId) {
31772
+ const result = db.prepare("SELECT last_response_time FROM session_meta WHERE session_id = ?").get(sessionId);
31773
+ if (result === null || typeof result !== "object")
31774
+ return null;
31775
+ const lastResponseTime = result.last_response_time;
31776
+ return typeof lastResponseTime === "number" ? lastResponseTime : null;
31777
+ }
31778
+ function loadContextUsage(contextUsageMap, db, sessionId) {
31779
+ const contextUsageEntry = contextUsageMap.get(sessionId);
31780
+ try {
31781
+ const persistedLastResponseTime = loadPersistedUsageWatermark(db, sessionId);
31782
+ const cachedLastResponseTime = contextUsageEntry?.lastResponseTime ?? contextUsageEntry?.updatedAt;
31783
+ if (contextUsageEntry && contextUsageEntry.lastResponseTime === undefined && (persistedLastResponseTime === null || persistedLastResponseTime === 0)) {
31784
+ return contextUsageEntry.usage;
31785
+ }
31786
+ if (contextUsageEntry && cachedLastResponseTime === persistedLastResponseTime) {
31787
+ return contextUsageEntry.usage;
31688
31788
  }
31789
+ const persisted = loadPersistedUsage(db, sessionId);
31790
+ if (persisted) {
31791
+ contextUsageMap.set(sessionId, {
31792
+ ...persisted,
31793
+ lastResponseTime: persistedLastResponseTime ?? persisted.updatedAt,
31794
+ hasUsageTokens: false
31795
+ });
31796
+ return persisted.usage;
31797
+ }
31798
+ contextUsageMap.delete(sessionId);
31799
+ } catch (error) {
31800
+ sessionLog(sessionId, "transform failed loading persisted usage:", error);
31801
+ return contextUsageEntry?.usage ?? { percentage: 0, inputTokens: 0 };
31689
31802
  }
31690
- return cleared;
31803
+ return { percentage: 0, inputTokens: 0 };
31691
31804
  }
31692
- function replayStrippedInlineThinking(messages, messageTagNumbers, persistedWatermark) {
31693
- if (persistedWatermark <= 0)
31694
- return 0;
31695
- let stripped = 0;
31696
- for (const message of messages) {
31697
- if (message.info.role !== "assistant")
31698
- continue;
31699
- const msgTag = messageTagNumbers.get(message) ?? 0;
31700
- if (msgTag === 0 || msgTag > persistedWatermark)
31701
- continue;
31702
- for (const part of message.parts) {
31703
- if (!isRecord(part) || part.type !== "text" || typeof part.text !== "string")
31704
- continue;
31705
- if (!part.text.includes("<think"))
31706
- continue;
31707
- const cleaned = part.text.replace(INLINE_THINKING_PATTERN, "");
31708
- if (cleaned !== part.text) {
31709
- part.text = cleaned;
31710
- stripped++;
31711
- }
31712
- }
31805
+ function resolveSchedulerDecision(scheduler, sessionMeta, contextUsage, sessionId, modelKey, contextLimit) {
31806
+ try {
31807
+ const schedulerDecision = scheduler.shouldExecute(sessionMeta, contextUsage, undefined, sessionId, modelKey, contextLimit);
31808
+ sessionLog(sessionId, `transform scheduler: percentage=${contextUsage.percentage.toFixed(1)}% inputTokens=${contextUsage.inputTokens} cacheTtl=${sessionMeta.cacheTtl} lastResponseTime=${sessionMeta.lastResponseTime} decision=${schedulerDecision}`);
31809
+ return schedulerDecision;
31810
+ } catch (error) {
31811
+ sessionLog(sessionId, "transform scheduler failed; defaulting to defer:", error);
31812
+ return "defer";
31713
31813
  }
31714
- return stripped;
31715
31814
  }
31716
- function clearOldReasoning(messages, reasoningByMessage, messageTagNumbers, clearReasoningAge) {
31717
- const maxTag = findMaxTag(messageTagNumbers);
31718
- if (maxTag === 0)
31719
- return 0;
31720
- const ageCutoff = maxTag - clearReasoningAge;
31721
- let cleared = 0;
31722
- for (const message of messages) {
31723
- const msgTag = messageTagNumbers.get(message) ?? 0;
31724
- if (msgTag === 0 || msgTag > ageCutoff)
31815
+
31816
+ // src/hooks/magic-context/channel2-cycle.ts
31817
+ function rearmChannel2AfterCoverageAdvancingHardFold(input) {
31818
+ if (input.compactionOff || !input.foldExecuted || input.previousCoverage === null || input.currentCoverage === null || input.currentCoverage <= input.previousCoverage) {
31819
+ return false;
31820
+ }
31821
+ return casChannel2NudgeState(input.db, input.sessionId, "delivered", "");
31822
+ }
31823
+ function rearmChannel2AfterMeasuredCollapse(input) {
31824
+ if (!input.baseline.evaluable || input.baseline.generationInvalidated)
31825
+ return false;
31826
+ if (effectiveTailHygiene(input.baseline).u >= CHANNEL1_FLOOR_TOKENS)
31827
+ return false;
31828
+ return casChannel2NudgeState(input.db, input.sessionId, "delivered", "");
31829
+ }
31830
+
31831
+ // src/hooks/magic-context/drop-stale-reduce-calls.ts
31832
+ var STALE_TOOL_NAMES = new Set(["ctx_reduce"]);
31833
+ function isReduceToolPart(part) {
31834
+ if (!isRecord(part))
31835
+ return false;
31836
+ if (part.type === "tool" && typeof part.tool === "string" && STALE_TOOL_NAMES.has(part.tool))
31837
+ return true;
31838
+ if (part.type === "tool-invocation" && typeof part.toolName === "string" && STALE_TOOL_NAMES.has(part.toolName))
31839
+ return true;
31840
+ if (part.type === "tool_use" && typeof part.name === "string" && STALE_TOOL_NAMES.has(part.name))
31841
+ return true;
31842
+ return false;
31843
+ }
31844
+ function hasAnyMeaningfulPart(parts) {
31845
+ for (const part of parts) {
31846
+ if (!isRecord(part))
31725
31847
  continue;
31726
- const parts = reasoningByMessage.get(message);
31727
- if (!parts)
31848
+ if (part.type === "text" && typeof part.text === "string" && part.text.trim().length > 0)
31849
+ return true;
31850
+ if (part.type === "thinking" || part.type === "reasoning" || part.type === "redacted_thinking")
31728
31851
  continue;
31729
- for (const tp of parts) {
31730
- if (tp.thinking !== undefined && tp.thinking !== "[cleared]") {
31731
- tp.thinking = "[cleared]";
31732
- cleared++;
31733
- }
31734
- if (tp.text !== undefined && tp.text !== "[cleared]") {
31735
- tp.text = "[cleared]";
31736
- cleared++;
31737
- }
31738
- }
31852
+ if (part.type === "meta" || part.type === "step-start" || part.type === "step-finish")
31853
+ continue;
31854
+ if (part.type !== "tool" || !isReduceToolPart(part))
31855
+ return true;
31739
31856
  }
31740
- return cleared;
31857
+ return false;
31741
31858
  }
31742
- function findMaxTag(messageTagNumbers) {
31743
- let max = 0;
31744
- for (const tag of messageTagNumbers.values()) {
31745
- if (tag > max)
31746
- max = tag;
31859
+ function messageHasReducePart(message) {
31860
+ for (const part of message.parts) {
31861
+ if (isSentinel(part))
31862
+ continue;
31863
+ if (isReduceToolPart(part))
31864
+ return true;
31747
31865
  }
31748
- return max;
31866
+ return false;
31749
31867
  }
31750
- var CLEARED_REASONING_TYPES = new Set(["thinking", "reasoning"]);
31751
- function stripClearedReasoning(messages) {
31752
- let stripped = 0;
31753
- for (const message of messages) {
31754
- if (message.info.role !== "assistant")
31868
+ function sentinelizeReduceParts(message) {
31869
+ let touched = false;
31870
+ for (let j = 0;j < message.parts.length; j++) {
31871
+ const part = message.parts[j];
31872
+ if (isSentinel(part))
31755
31873
  continue;
31756
- for (let i = 0;i < message.parts.length; i++) {
31757
- const part = message.parts[i];
31758
- if (!isRecord(part))
31759
- continue;
31760
- const partType = part.type;
31761
- if (!CLEARED_REASONING_TYPES.has(partType))
31762
- continue;
31763
- if (!("thinking" in part) && !("text" in part))
31764
- continue;
31765
- const thinking = "thinking" in part ? part.thinking : undefined;
31766
- const text = "text" in part ? part.text : undefined;
31767
- const isCleared = (thinking === undefined || thinking === "[cleared]") && (text === undefined || text === "[cleared]");
31768
- if (!isCleared)
31769
- continue;
31770
- message.parts[i] = makeSentinel(part);
31771
- stripped++;
31874
+ if (isReduceToolPart(part)) {
31875
+ message.parts[j] = makeSentinel(part);
31876
+ touched = true;
31772
31877
  }
31773
31878
  }
31774
- return stripped;
31879
+ if (touched && !hasAnyMeaningfulPart(message.parts)) {
31880
+ message.parts.length = 0;
31881
+ message.parts.push(makeSentinel(undefined));
31882
+ }
31883
+ return touched;
31775
31884
  }
31776
- var INLINE_THINKING_PATTERN = /<(?:thinking|think)>[\s\S]*?<\/(?:thinking|think)>\s*/g;
31777
- function stripInlineThinking(messages, messageTagNumbers, clearReasoningAge) {
31778
- const maxTag = findMaxTag(messageTagNumbers);
31779
- if (maxTag === 0)
31780
- return 0;
31781
- const ageCutoff = maxTag - clearReasoningAge;
31782
- let stripped = 0;
31783
- for (const message of messages) {
31784
- if (message.info.role !== "assistant")
31785
- continue;
31786
- const msgTag = messageTagNumbers.get(message) ?? 0;
31787
- if (msgTag === 0 || msgTag > ageCutoff)
31885
+ function dropStaleReduceCalls(messages, frozenIds, options = {}) {
31886
+ const detect = options.detect ?? false;
31887
+ const protectedCount = options.protectedCount ?? 0;
31888
+ const protectedStart = messages.length - protectedCount;
31889
+ const newlyStrippedIds = [];
31890
+ let didDrop = false;
31891
+ for (let i = 0;i < messages.length; i++) {
31892
+ const message = messages[i];
31893
+ const id = typeof message.info.id === "string" ? message.info.id : undefined;
31894
+ const inFrozen = id !== undefined && frozenIds.has(id);
31895
+ const isNewDetection = !inFrozen && detect && i < protectedStart && id !== undefined && messageHasReducePart(message);
31896
+ if (!inFrozen && !isNewDetection)
31788
31897
  continue;
31789
- for (const part of message.parts) {
31790
- if (!isRecord(part) || part.type !== "text" || typeof part.text !== "string")
31791
- continue;
31792
- const cleaned = part.text.replace(INLINE_THINKING_PATTERN, "");
31793
- if (cleaned !== part.text) {
31794
- part.text = cleaned;
31795
- stripped++;
31796
- }
31898
+ const touched = sentinelizeReduceParts(message);
31899
+ if (touched) {
31900
+ didDrop = true;
31901
+ if (isNewDetection && id !== undefined)
31902
+ newlyStrippedIds.push(id);
31797
31903
  }
31798
31904
  }
31799
- return stripped;
31905
+ return { didDrop, newlyStrippedIds };
31800
31906
  }
31801
- var REASONING_IGNORED_PART_TYPES = new Set([
31802
- "step-start",
31803
- "step-finish",
31804
- "snapshot",
31805
- "patch",
31806
- "agent",
31807
- "retry",
31808
- "subtask",
31809
- "compaction"
31810
- ]);
31811
- var REASONING_PART_TYPES = new Set(["reasoning", "thinking", "redacted_thinking"]);
31812
- function hasReasoningReplayContent(message) {
31813
- return message.parts.some((part) => {
31814
- if (!isRecord(part) || part.ignored === true)
31815
- return false;
31816
- const type = typeof part.type === "string" ? part.type : "";
31817
- if (REASONING_IGNORED_PART_TYPES.has(type))
31818
- return false;
31819
- return type !== "text" || typeof part.text !== "string" || part.text.trim().length > 0;
31820
- });
31907
+
31908
+ // src/hooks/magic-context/fold-execution-gate.ts
31909
+ function foldExecutesThisPass(foldDue, materialized) {
31910
+ return foldDue && materialized;
31821
31911
  }
31822
- function findLatestAssistantReasoningMutationExemptMessage(messages) {
31823
- for (let index = messages.length - 1;index >= 0; index -= 1) {
31824
- const message = messages[index];
31825
- if (message.info.role === "assistant" && hasReasoningReplayContent(message))
31826
- return message;
31912
+
31913
+ // src/hooks/magic-context/system-injection-stripper.ts
31914
+ var SYSTEM_INJECTION_MARKERS = [
31915
+ "<!-- OMO_INTERNAL_INITIATOR -->",
31916
+ "[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
31917
+ "[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
31918
+ "[Category+Skill Reminder]",
31919
+ "[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
31920
+ "[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
31921
+ "[EMERGENCY CONTEXT WINDOW WARNING]",
31922
+ "Unstable background agent appears idle",
31923
+ "**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
31924
+ ];
31925
+ var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
31926
+ var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
31927
+ function stripSystemInjection(text) {
31928
+ let hasInjection = false;
31929
+ for (const marker of SYSTEM_INJECTION_MARKERS) {
31930
+ if (text.includes(marker)) {
31931
+ hasInjection = true;
31932
+ break;
31933
+ }
31827
31934
  }
31828
- return;
31829
- }
31830
- function planMergedAssistantReasoningStrip(messages, mutationExemptMessage) {
31831
- const plan = [];
31832
- let prevRole;
31833
- let keptReasoningInRun = false;
31834
- for (const message of messages) {
31835
- const role = message.info.role;
31836
- if (role !== "assistant") {
31837
- prevRole = role;
31838
- keptReasoningInRun = false;
31935
+ if (SYSTEM_REMINDER_REGEX.test(text))
31936
+ hasInjection = true;
31937
+ SYSTEM_REMINDER_REGEX.lastIndex = 0;
31938
+ if (!hasInjection)
31939
+ return null;
31940
+ let cleaned = text;
31941
+ cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
31942
+ cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
31943
+ cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
31944
+ for (const marker of SYSTEM_INJECTION_MARKERS) {
31945
+ if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
31839
31946
  continue;
31840
- }
31841
- const firstInRun = prevRole !== "assistant";
31842
- if (firstInRun)
31843
- keptReasoningInRun = false;
31844
- if (message === mutationExemptMessage) {
31845
- prevRole = role;
31947
+ const idx = cleaned.indexOf(marker);
31948
+ if (idx === -1)
31846
31949
  continue;
31950
+ const blockEnd = cleaned.indexOf(`
31951
+
31952
+ `, idx + marker.length);
31953
+ cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
31954
+ }
31955
+ return cleaned.trim();
31956
+ }
31957
+
31958
+ // src/hooks/magic-context/heuristic-cleanup.ts
31959
+ var DEDUP_SAFE_TOOLS = new Set([
31960
+ "mcp_grep",
31961
+ "mcp_read",
31962
+ "mcp_glob",
31963
+ "mcp_ast_grep_search",
31964
+ "mcp_lsp_diagnostics",
31965
+ "mcp_lsp_symbols",
31966
+ "mcp_lsp_find_references",
31967
+ "mcp_lsp_goto_definition",
31968
+ "mcp_lsp_prepare_rename"
31969
+ ]);
31970
+ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config, preloadedTags) {
31971
+ const tags = preloadedTags ?? getActiveTagsBySession(db, sessionId);
31972
+ const maxTag = getMaxTagNumberBySession(db, sessionId);
31973
+ const protectedCutoff = maxTag - config.protectedTags;
31974
+ let droppedTools = 0;
31975
+ let emergencyDroppedTools = 0;
31976
+ let emergencyReclaimedTokens = 0;
31977
+ let deduplicatedTools = 0;
31978
+ let droppedInjections = 0;
31979
+ if (config.emergency) {
31980
+ const emergency = config.emergency;
31981
+ const priorInputSample = getEmergencyInputSample(db, sessionId);
31982
+ const droppableTags = tags.filter((t) => t.status === "active" && t.type === "tool" && targets.get(t.tagNumber)?.canDrop?.());
31983
+ const activeTags = tags.filter((t) => t.status === "active");
31984
+ const plan = planEmergencyDrop({
31985
+ tags: droppableTags,
31986
+ floorTags: activeTags,
31987
+ maxTag,
31988
+ protectedTags: config.protectedTags,
31989
+ currentTotalInputTokens: emergency.currentTotalInputTokens,
31990
+ ceilingTokens: emergency.ceilingTokens,
31991
+ priorInputSample,
31992
+ hasPriorDrop: priorInputSample > 0
31993
+ });
31994
+ if (plan.shouldDrop) {
31995
+ const toDrop = new Set(plan.tagNumbers);
31996
+ const newestEmergencyTags = new Set(droppableTags.slice().sort((left, right) => right.tagNumber - left.tagNumber).slice(0, 20).map((tag) => tag.tagNumber));
31997
+ db.transaction(() => {
31998
+ for (const tag of tags) {
31999
+ if (!toDrop.has(tag.tagNumber))
32000
+ continue;
32001
+ if (tag.status !== "active" || tag.type !== "tool")
32002
+ continue;
32003
+ const target = targets.get(tag.tagNumber);
32004
+ const recent = newestEmergencyTags.has(tag.tagNumber);
32005
+ const result = recent ? target?.truncate?.() ?? target?.drop?.() ?? "absent" : target?.drop?.() ?? "absent";
32006
+ if (result === "removed" || result === "truncated") {
32007
+ updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
32008
+ updateTagDropMode(db, sessionId, tag.tagNumber, recent ? "truncated" : "full");
32009
+ droppedTools++;
32010
+ emergencyDroppedTools++;
32011
+ emergencyReclaimedTokens += estimateEmergencyDropReclaimTokens(tag);
32012
+ }
32013
+ }
32014
+ })();
32015
+ sessionLog(sessionId, `emergency tiered drop: ${plan.reason}`);
32016
+ } else {
32017
+ sessionLog(sessionId, `emergency tiered drop skipped: ${plan.reason}`);
31847
32018
  }
31848
- let keepIndex = -1;
31849
- if (firstInRun && !keptReasoningInRun) {
31850
- for (let i = 0;i < message.parts.length; i++) {
31851
- const part = message.parts[i];
31852
- if (!isRecord(part))
32019
+ setEmergencyDropSample(db, sessionId, emergency.currentTotalInputTokens);
32020
+ }
32021
+ if (config.routine !== false) {
32022
+ db.transaction(() => {
32023
+ for (const tag of tags) {
32024
+ if (tag.status !== "active")
31853
32025
  continue;
31854
- const partType = part.type;
31855
- if (REASONING_IGNORED_PART_TYPES.has(partType))
32026
+ if (tag.tagNumber > protectedCutoff)
31856
32027
  continue;
31857
- if (part.ignored === true)
32028
+ if (tag.type !== "message")
31858
32029
  continue;
31859
- if (isSentinel(part))
32030
+ const target = targets.get(tag.tagNumber);
32031
+ if (!target)
31860
32032
  continue;
31861
- if (partType === "text" && typeof part.text === "string" && part.text.trim() === "") {
32033
+ const content = target.getContent?.();
32034
+ if (!content)
31862
32035
  continue;
32036
+ const stripped = stripSystemInjection(content);
32037
+ if (stripped === null)
32038
+ continue;
32039
+ const strippedSource = stripTagPrefix(stripped);
32040
+ if (strippedSource.trim().length === 0) {
32041
+ const dropResult = target.drop?.() ?? "absent";
32042
+ const didReplace = dropResult === "absent" ? target.setContent(`[dropped §${tag.tagNumber}§]`) : false;
32043
+ if (dropResult === "removed" || dropResult === "absent") {
32044
+ replaceSourceContent(db, sessionId, tag.tagNumber, "");
32045
+ updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
32046
+ if (dropResult === "removed" || didReplace) {
32047
+ droppedInjections++;
32048
+ }
32049
+ }
32050
+ } else {
32051
+ const didSet = target.setContent(stripped);
32052
+ if (didSet) {
32053
+ replaceSourceContent(db, sessionId, tag.tagNumber, strippedSource);
32054
+ droppedInjections++;
32055
+ }
31863
32056
  }
31864
- if (REASONING_PART_TYPES.has(partType)) {
31865
- keepIndex = i;
31866
- }
31867
- break;
32057
+ }
32058
+ })();
32059
+ }
32060
+ const allMessages = Array.from(messageTagNumbers.keys());
32061
+ const toolFingerprints = buildToolFingerprints(allMessages);
32062
+ if (config.routine !== false && toolFingerprints.size > 0) {
32063
+ const tagsByCompositeKey = new Map;
32064
+ for (const tag of tags) {
32065
+ if (tag.type === "tool" && tag.status === "active" && tag.messageId) {
32066
+ const key = tag.toolOwnerMessageId ? `${tag.toolOwnerMessageId}\x00${tag.messageId}` : tag.messageId;
32067
+ tagsByCompositeKey.set(key, tag);
31868
32068
  }
31869
32069
  }
31870
- const stripIndices = [];
31871
- for (let i = 0;i < message.parts.length; i++) {
31872
- const part = message.parts[i];
31873
- if (!isRecord(part))
31874
- continue;
31875
- if (!REASONING_PART_TYPES.has(part.type))
31876
- continue;
31877
- if (part.cache_control !== undefined)
31878
- continue;
31879
- if (i === keepIndex) {
31880
- keptReasoningInRun = true;
32070
+ const fingerprintGroups = new Map;
32071
+ for (const [compositeKey, fingerprint] of toolFingerprints) {
32072
+ const tag = tagsByCompositeKey.get(compositeKey);
32073
+ if (!tag || tag.tagNumber > protectedCutoff)
31881
32074
  continue;
31882
- }
31883
- stripIndices.push(i);
32075
+ const group = fingerprintGroups.get(fingerprint) ?? [];
32076
+ group.push(tag);
32077
+ fingerprintGroups.set(fingerprint, group);
31884
32078
  }
31885
- if (stripIndices.length > 0)
31886
- plan.push({ message, stripIndices });
31887
- prevRole = role;
31888
- }
31889
- return plan;
31890
- }
31891
- var MAX_FROZEN_TRAILING_BLANKS = 1e4;
31892
- var CANONICAL_BLANK_PART = { type: "text", text: "" };
31893
- function isSentinelInvisibleTextPart(part) {
31894
- return isRecord(part) && part.type === "text" && typeof part.text === "string" && part.text.trim() === "";
31895
- }
31896
- function isCanonicalBlankPart(part) {
31897
- return isRecord(part) && Object.keys(part).length === 2 && part.type === "text" && part.text === "";
31898
- }
31899
- function trailingBlankKeepCount(decision) {
31900
- if (decision === "keep")
31901
- return 1;
31902
- if (!decision.startsWith("keep:"))
31903
- return;
31904
- const countText = decision.slice("keep:".length);
31905
- if (!/^[1-9]\d*$/.test(countText))
31906
- return;
31907
- const count = Number(countText);
31908
- return Number.isSafeInteger(count) && count > 0 && count <= MAX_FROZEN_TRAILING_BLANKS ? count : undefined;
31909
- }
31910
- function trailingBlankDecisionForMessage(message) {
31911
- const id = message.info.id;
31912
- if (message.info.role !== "assistant" || typeof id !== "string" || id.length === 0) {
31913
- return;
32079
+ db.transaction(() => {
32080
+ for (const [, group] of fingerprintGroups) {
32081
+ if (group.length <= 1)
32082
+ continue;
32083
+ group.sort((a, b) => a.tagNumber - b.tagNumber);
32084
+ for (let i = 0;i < group.length - 1; i++) {
32085
+ const tag = group[i];
32086
+ const target = targets.get(tag.tagNumber);
32087
+ const result = target?.drop?.() ?? "absent";
32088
+ if (result === "incomplete")
32089
+ continue;
32090
+ updateTagDropMode(db, sessionId, tag.tagNumber, "full");
32091
+ updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
32092
+ if (result === "removed" || result === "truncated") {
32093
+ deduplicatedTools++;
32094
+ }
32095
+ }
32096
+ }
32097
+ })();
31914
32098
  }
31915
- let trailingCount = 0;
31916
- while (trailingCount < message.parts.length && isSentinelInvisibleTextPart(message.parts[message.parts.length - trailingCount - 1])) {
31917
- trailingCount += 1;
32099
+ if (droppedTools > 0 || deduplicatedTools > 0 || droppedInjections > 0) {
32100
+ sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
31918
32101
  }
31919
- if (trailingCount > MAX_FROZEN_TRAILING_BLANKS && trailingCount < message.parts.length) {
31920
- return;
32102
+ let compressedTextTags = 0;
32103
+ let mutatedTextTags = 0;
32104
+ if (config.routine !== false && config.caveman?.enabled) {
32105
+ const cavemanResult = applyCavemanCleanup(sessionId, db, targets, tags, {
32106
+ enabled: true,
32107
+ minChars: config.caveman.minChars,
32108
+ protectedTags: config.protectedTags
32109
+ });
32110
+ compressedTextTags = cavemanResult.compressedToLite + cavemanResult.compressedToFull + cavemanResult.compressedToUltra;
32111
+ mutatedTextTags = cavemanResult.mutatedTextTags;
31921
32112
  }
31922
- const decision = message.parts.length === 0 || trailingCount === message.parts.length ? "keep" : trailingCount === 0 ? "strip" : trailingCount === 1 ? "keep" : `keep:${trailingCount}`;
31923
- return [id, decision];
32113
+ return {
32114
+ droppedTools,
32115
+ deduplicatedTools,
32116
+ droppedInjections,
32117
+ emergencyDroppedTools,
32118
+ emergencyReclaimedTokens,
32119
+ compressedTextTags,
32120
+ mutatedTextTags
32121
+ };
31924
32122
  }
31925
- function snapshotTrailingBlankSourceDecisions(messages) {
31926
- const decisions = new Map;
31927
- for (const message of messages) {
31928
- const observed = trailingBlankDecisionForMessage(message);
31929
- if (observed)
31930
- decisions.set(...observed);
32123
+ function extractToolInfo(part) {
32124
+ if (part.type === "tool" && typeof part.tool === "string" && DEDUP_SAFE_TOOLS.has(part.tool)) {
32125
+ const state = typeof part.state === "object" && part.state !== null ? part.state : {};
32126
+ return { toolName: part.tool, args: state.input ?? {} };
31931
32127
  }
31932
- return decisions;
31933
- }
31934
- function findTrailingBlankDecisionCandidates(messages, frozenDecisions, options) {
31935
- const observedDecisions = options?.sourceDecisions ?? snapshotTrailingBlankSourceDecisions(messages);
31936
- const visibleIds = new Set(messages.flatMap((message) => typeof message.info.id === "string" ? [message.info.id] : []));
31937
- const decisions = [];
31938
- for (const [id, decision] of observedDecisions) {
31939
- if (!visibleIds.has(id))
31940
- continue;
31941
- const frozen = frozenDecisions.get(id);
31942
- if (frozen !== undefined && (id !== options?.refreshMessageId || frozen === decision)) {
31943
- continue;
31944
- }
31945
- decisions.push([id, decision]);
32128
+ if (part.type === "tool-invocation" && typeof part.toolName === "string" && DEDUP_SAFE_TOOLS.has(part.toolName)) {
32129
+ return { toolName: part.toolName, args: part.args ?? {} };
31946
32130
  }
31947
- return decisions;
32131
+ if (part.type === "tool_use" && typeof part.name === "string" && DEDUP_SAFE_TOOLS.has(part.name)) {
32132
+ return { toolName: part.name, args: part.input ?? {} };
32133
+ }
32134
+ return null;
31948
32135
  }
31949
- function applyFrozenTrailingBlankDecisions(messages, newestAssistantId, frozenDecisions) {
31950
- let mutations = 0;
31951
- for (let messageIndex = 0;messageIndex < messages.length; messageIndex += 1) {
31952
- let message = messages[messageIndex];
31953
- const id = message.info.id;
31954
- if (message.info.role !== "assistant" || typeof id !== "string")
32136
+ function buildToolFingerprints(messages) {
32137
+ const fingerprints = new Map;
32138
+ for (const message of messages) {
32139
+ if (message.info.role !== "assistant")
31955
32140
  continue;
31956
- const decision = frozenDecisions.get(id);
31957
- if (!decision)
32141
+ const ownerMsgId = typeof message.info.id === "string" ? message.info.id : null;
32142
+ if (!ownerMsgId)
31958
32143
  continue;
31959
- let lastMeaningfulIndex = message.parts.length - 1;
31960
- while (lastMeaningfulIndex >= 0 && isSentinelInvisibleTextPart(message.parts[lastMeaningfulIndex])) {
31961
- lastMeaningfulIndex -= 1;
31962
- }
31963
- const replaceParts = (start, deleteCount, insertBlankCount) => {
31964
- message = { ...message, parts: [...message.parts] };
31965
- messages[messageIndex] = message;
31966
- message.parts.splice(start, deleteCount, ...Array.from({ length: insertBlankCount }, () => ({ ...CANONICAL_BLANK_PART })));
31967
- };
31968
- if (lastMeaningfulIndex < 0) {
31969
- if (message.parts.length === 0)
32144
+ for (const part of message.parts) {
32145
+ const record = part;
32146
+ const info = extractToolInfo(record);
32147
+ if (!info)
31970
32148
  continue;
31971
- if (message.parts.length !== 1 || !isCanonicalBlankPart(message.parts[0])) {
31972
- mutations += Math.max(1, message.parts.length);
31973
- replaceParts(0, message.parts.length, 1);
31974
- }
31975
- continue;
31976
- }
31977
- const trailingCount = message.parts.length - lastMeaningfulIndex - 1;
31978
- const keepCount = trailingBlankKeepCount(decision);
31979
- if (keepCount !== undefined) {
31980
- if (trailingCount === 0)
32149
+ const callId = extractCallId(record);
32150
+ if (!callId)
31981
32151
  continue;
31982
- const blankIndex = lastMeaningfulIndex + 1;
31983
- const suffixIsCanonical = trailingCount === keepCount && message.parts.slice(blankIndex).every(isCanonicalBlankPart);
31984
- if (!suffixIsCanonical) {
31985
- mutations += Math.max(1, trailingCount, keepCount);
31986
- replaceParts(blankIndex, trailingCount, keepCount);
31987
- }
31988
- continue;
31989
- }
31990
- if (id === newestAssistantId || trailingCount === 0)
31991
- continue;
31992
- const lastMeaningfulPart = message.parts[lastMeaningfulIndex];
31993
- if (isRecord(lastMeaningfulPart) && REASONING_PART_TYPES.has(lastMeaningfulPart.type)) {
31994
- const blankIndex = lastMeaningfulIndex + 1;
31995
- if (trailingCount !== 1 || !isCanonicalBlankPart(message.parts[blankIndex])) {
31996
- mutations += Math.max(1, trailingCount);
31997
- replaceParts(blankIndex, trailingCount, 1);
31998
- }
31999
- continue;
32152
+ try {
32153
+ const fingerprint = `${ownerMsgId}:${info.toolName}:${JSON.stringify(info.args)}`;
32154
+ const compositeKey = `${ownerMsgId}\x00${callId}`;
32155
+ fingerprints.set(compositeKey, fingerprint);
32156
+ } catch {}
32000
32157
  }
32001
- mutations += trailingCount;
32002
- replaceParts(lastMeaningfulIndex + 1, trailingCount, 0);
32003
32158
  }
32004
- return mutations;
32159
+ return fingerprints;
32005
32160
  }
32006
- function findMergedReasoningStripCandidateIds(messages, providerID, options) {
32007
- if (providerID !== "anthropic")
32008
- return [];
32009
- const ids = new Set;
32010
- for (const entry of planMergedAssistantReasoningStrip(messages, options?.mutationExemptMessage)) {
32011
- const id = entry.message.info.id;
32012
- if (typeof id === "string" && id.length > 0)
32013
- ids.add(id);
32014
- }
32015
- return [...ids];
32161
+ function extractCallId(part) {
32162
+ if (part.type === "tool" && typeof part.callID === "string")
32163
+ return part.callID;
32164
+ if (part.type === "tool-invocation" && typeof part.callID === "string")
32165
+ return part.callID;
32166
+ if (part.type === "tool_use" && typeof part.id === "string")
32167
+ return part.id;
32168
+ return null;
32016
32169
  }
32017
- function stripReasoningFromMergedAssistants(messages, providerID, options) {
32018
- if (providerID !== "anthropic")
32019
- return 0;
32020
- let stripped = 0;
32021
- for (const entry of planMergedAssistantReasoningStrip(messages, options?.mutationExemptMessage)) {
32022
- if (options?.frozenMessageIds) {
32023
- const id = entry.message.info.id;
32024
- if (typeof id !== "string" || !options.frozenMessageIds.has(id))
32170
+
32171
+ // src/hooks/magic-context/note-visibility.ts
32172
+ var NOTE_TOOL_NAMES = new Set(["ctx_note"]);
32173
+ var READ_ACTION = "read";
32174
+ function hasVisibleNoteReadCall(messages) {
32175
+ for (let i = messages.length - 1;i >= 0; i -= 1) {
32176
+ const parts = messages[i]?.parts;
32177
+ if (!Array.isArray(parts))
32178
+ continue;
32179
+ for (const part of parts) {
32180
+ if (isSentinel(part))
32025
32181
  continue;
32026
- }
32027
- for (const index of entry.stripIndices) {
32028
- entry.message.parts[index] = makeSentinel(entry.message.parts[index]);
32029
- stripped++;
32182
+ if (isVisibleNoteReadPart(part))
32183
+ return true;
32030
32184
  }
32031
32185
  }
32032
- return stripped;
32186
+ return false;
32033
32187
  }
32034
- function stripProcessedImages(messages, frozenIds, options) {
32035
- const { detect, watermark, messageTagNumbers } = options;
32036
- let stripped = 0;
32037
- const newlyStrippedIds = [];
32038
- let hasAssistantResponse = false;
32039
- for (let i = messages.length - 1;i >= 0; i--) {
32040
- const msg = messages[i];
32041
- if (msg.info.role === "assistant") {
32042
- hasAssistantResponse = true;
32043
- continue;
32044
- }
32045
- if (msg.info.role !== "user") {
32046
- continue;
32047
- }
32048
- const id = typeof msg.info.id === "string" ? msg.info.id : undefined;
32049
- const inFrozen = id !== undefined && frozenIds.has(id);
32050
- const maxTag = messageTagNumbers.get(msg) ?? 0;
32051
- const isNewDetection = !inFrozen && detect && hasAssistantResponse && id !== undefined && maxTag <= watermark;
32052
- if (!inFrozen && !isNewDetection) {
32053
- continue;
32188
+ function isVisibleNoteReadPart(part) {
32189
+ if (!isRecord(part))
32190
+ return false;
32191
+ if (part.type === "tool" && typeof part.tool === "string" && NOTE_TOOL_NAMES.has(part.tool)) {
32192
+ const state = part.state;
32193
+ if (isRecord(state) && isRecord(state.input)) {
32194
+ return state.input.action === READ_ACTION;
32054
32195
  }
32055
- let touchedThisMsg = false;
32056
- for (let j = 0;j < msg.parts.length; j++) {
32057
- const part = msg.parts[j];
32058
- if (!isRecord(part) || part.type !== "file") {
32059
- continue;
32060
- }
32061
- if (typeof part.mime !== "string" || !part.mime.startsWith("image/")) {
32062
- continue;
32063
- }
32064
- if (typeof part.url === "string" && part.url.startsWith("data:") && part.url.length > 200) {
32065
- msg.parts[j] = makeSentinel(part);
32066
- stripped++;
32067
- touchedThisMsg = true;
32068
- }
32196
+ return false;
32197
+ }
32198
+ if (part.type === "tool_use" && typeof part.name === "string" && NOTE_TOOL_NAMES.has(part.name)) {
32199
+ if (isRecord(part.input)) {
32200
+ return part.input.action === READ_ACTION;
32069
32201
  }
32070
- if (touchedThisMsg && isNewDetection && id !== undefined) {
32071
- newlyStrippedIds.push(id);
32202
+ return false;
32203
+ }
32204
+ if (part.type === "tool-invocation" && typeof part.toolName === "string" && NOTE_TOOL_NAMES.has(part.toolName)) {
32205
+ const argsCandidate = part.args ?? part.input;
32206
+ if (isRecord(argsCandidate)) {
32207
+ return argsCandidate.action === READ_ACTION;
32072
32208
  }
32209
+ return false;
32073
32210
  }
32074
- return { stripped, newlyStrippedIds };
32211
+ return false;
32075
32212
  }
32076
32213
 
32077
32214
  // src/hooks/magic-context/supersession-reclaim.ts
@@ -32395,14 +32532,15 @@ function createExistingTagResolver(sessionId, tagger, db) {
32395
32532
  return cachedScopedAssignments;
32396
32533
  }
32397
32534
  return {
32398
- resolve(messageId2, type, currentContentId, ordinal) {
32535
+ resolve(messageId2, type, currentContentId, ordinal, options) {
32536
+ const accept = options?.accept ?? (() => true);
32399
32537
  const exactTagId = assignments.get(currentContentId);
32400
- if (exactTagId !== undefined) {
32538
+ if (exactTagId !== undefined && accept(exactTagId)) {
32401
32539
  usedTagNumbers.add(exactTagId);
32402
32540
  return exactTagId;
32403
32541
  }
32404
32542
  const fallback = getScopedAssignments().get(messageId2)?.[type][ordinal];
32405
- if (!fallback || usedTagNumbers.has(fallback.tagNumber)) {
32543
+ if (!fallback || usedTagNumbers.has(fallback.tagNumber) || !accept(fallback.tagNumber)) {
32406
32544
  return;
32407
32545
  }
32408
32546
  updateTagMessageId(db, sessionId, fallback.tagNumber, currentContentId);
@@ -32594,6 +32732,21 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
32594
32732
  };
32595
32733
  const ownerByPartKey = new Map;
32596
32734
  const batch = new ToolMutationBatch(messages);
32735
+ const inertWhitespaceTagNumbers = new Set;
32736
+ const inertWhitespaceTagsByMessage = new Map;
32737
+ for (const tag of getInertWhitespaceAssistantTags(db, sessionId)) {
32738
+ inertWhitespaceTagNumbers.add(tag.tagNumber);
32739
+ tagger.bindTag(sessionId, tag.contentId, tag.tagNumber);
32740
+ const scoped = /^(.*):p(\d+)$/.exec(tag.contentId);
32741
+ if (!scoped)
32742
+ continue;
32743
+ const list = inertWhitespaceTagsByMessage.get(scoped[1]) ?? [];
32744
+ list.push({ partIndex: Number(scoped[2]), tagNumber: tag.tagNumber });
32745
+ inertWhitespaceTagsByMessage.set(scoped[1], list);
32746
+ }
32747
+ for (const list of inertWhitespaceTagsByMessage.values()) {
32748
+ list.sort((left, right) => left.partIndex - right.partIndex);
32749
+ }
32597
32750
  const assignments = tagger.getAssignments(sessionId);
32598
32751
  const resolver = createExistingTagResolver(sessionId, tagger, db);
32599
32752
  const tGetSourceContents = performance.now();
@@ -32622,6 +32775,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
32622
32775
  const messageHasTextPart = message.parts.some(isTextPart);
32623
32776
  let textOrdinal = 0;
32624
32777
  let fileOrdinal = 0;
32778
+ let whitespaceRank = 0;
32625
32779
  for (let partIndex = 0;partIndex < message.parts.length; partIndex += 1) {
32626
32780
  const part = message.parts[partIndex];
32627
32781
  if (isReduceToolPart(part)) {
@@ -32677,7 +32831,47 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
32677
32831
  const textPart = part;
32678
32832
  const thinkingParts = messageThinkingParts;
32679
32833
  const contentId = `${messageId2}:p${partIndex}`;
32680
- resolver.resolve(messageId2, "message", contentId, textOrdinal);
32834
+ const whitespaceOnlyAssistant = message.info.role === "assistant" && stripTagPrefix(textPart.text).trim().length === 0;
32835
+ let existingTagId;
32836
+ if (whitespaceOnlyAssistant) {
32837
+ const persistedWhitespaceTag = getTagNumberByMessageId(db, sessionId, contentId);
32838
+ const rankedInertTag = inertWhitespaceTagsByMessage.get(messageId2)?.[whitespaceRank];
32839
+ const canReplayRankedTag = rankedInertTag?.partIndex === partIndex || isThinkingPart(message.parts[partIndex - 1]) || isThinkingPart(message.parts[partIndex + 1]);
32840
+ const inertForThisPart = canReplayRankedTag ? rankedInertTag?.tagNumber : undefined;
32841
+ whitespaceRank += 1;
32842
+ if (inertForThisPart !== undefined) {
32843
+ tagger.bindTag(sessionId, contentId, inertForThisPart);
32844
+ existingTagId = inertForThisPart;
32845
+ } else {
32846
+ existingTagId = resolver.resolve(messageId2, "message", contentId, textOrdinal, { accept: (tagNumber) => tagNumber === persistedWhitespaceTag });
32847
+ }
32848
+ if (existingTagId === undefined) {
32849
+ const persisted = getTagNumberByMessageId(db, sessionId, contentId);
32850
+ if (persisted !== null && !getSourceContents(db, sessionId, [persisted]).has(persisted)) {
32851
+ tagger.bindTag(sessionId, contentId, persisted);
32852
+ existingTagId = persisted;
32853
+ }
32854
+ }
32855
+ if (existingTagId === undefined) {
32856
+ textOrdinal += 1;
32857
+ continue;
32858
+ }
32859
+ const existingTag = getTagById(db, sessionId, existingTagId);
32860
+ if (!existingTag || existingTag.type !== "message") {
32861
+ tagger.unbindTag(sessionId, contentId);
32862
+ textOrdinal += 1;
32863
+ continue;
32864
+ }
32865
+ if (existingTag.status === "active") {
32866
+ markWhitespaceAssistantTagInert(db, sessionId, existingTagId, contentId);
32867
+ inertWhitespaceTagNumbers.add(existingTagId);
32868
+ }
32869
+ } else {
32870
+ const resolved = resolver.resolve(messageId2, "message", contentId, textOrdinal, { accept: (tagNumber) => !inertWhitespaceTagNumbers.has(tagNumber) });
32871
+ if (resolved === undefined && inertWhitespaceTagNumbers.has(tagger.getTag(sessionId, contentId, "message") ?? -1)) {
32872
+ tagger.unbindTag(sessionId, contentId);
32873
+ }
32874
+ }
32681
32875
  const reasoningBytes = textOrdinal === 0 ? getReasoningByteSize(thinkingParts) : 0;
32682
32876
  const reasoningTokens = textOrdinal === 0 ? getReasoningTokenCount(thinkingParts) : 0;
32683
32877
  const _tAssignText = performance.now();
@@ -32947,11 +33141,24 @@ async function applyTodoSynthesis(args) {
32947
33141
  }
32948
33142
  return 0;
32949
33143
  }
33144
+ function replayRustModeBindingMismatchStrips(args) {
33145
+ if (!modelAcceptsEmptyContent(args.resolvedProviderID))
33146
+ return;
33147
+ const recoveryMessageIds = new Set;
33148
+ for (const id of getMergedReasoningStrippedIds(args.db, args.sessionId)) {
33149
+ if (!id.startsWith(THINKING_BINDING_RECOVERY_FROZEN_PREFIX))
33150
+ continue;
33151
+ const messageId2 = id.slice(THINKING_BINDING_RECOVERY_FROZEN_PREFIX.length);
33152
+ if (messageId2.length > 0)
33153
+ recoveryMessageIds.add(messageId2);
33154
+ }
33155
+ stripReasoningFromAssistantIds(args.messages, args.resolvedProviderID, recoveryMessageIds);
33156
+ }
32950
33157
  function runRustModePostprocess(args) {
32951
33158
  if (!args.fullFeatureMode || args.compactionOff)
32952
- return;
33159
+ return { thinkingBindingRecovery: null };
32953
33160
  if (args.messages.some((message) => !message || typeof message !== "object" || !isRecord(message.info))) {
32954
- return;
33161
+ return { thinkingBindingRecovery: null };
32955
33162
  }
32956
33163
  reconcileMarkerRepresentation(args.messages, getPersistedCompactionMarkerState(args.db, args.sessionId), {
32957
33164
  db: args.db,
@@ -32967,21 +33174,80 @@ function runRustModePostprocess(args) {
32967
33174
  appendReminderToUserMessageById(args.messages, decision.messageId, decision.text);
32968
33175
  }
32969
33176
  }
33177
+ const trailingBlankDecisions = new Map;
33178
+ if (modelAcceptsEmptyContent(args.resolvedProviderID)) {
33179
+ try {
33180
+ for (const [id, decision] of getTrailingBlankDecisions(args.db, args.sessionId)) {
33181
+ trailingBlankDecisions.set(id, decision);
33182
+ }
33183
+ const candidates = findTrailingBlankDecisionCandidates(args.messages, trailingBlankDecisions, { sourceDecisions: args.trailingBlankSourceDecisions }).filter(([id]) => id === args.trailingBlankNewestAssistantId);
33184
+ if (candidates.length > 0) {
33185
+ const persisted = addTrailingBlankDecisions(args.db, args.sessionId, candidates, {
33186
+ overwriteMessageId: args.trailingBlankNewestAssistantId
33187
+ });
33188
+ if (persisted) {
33189
+ const committed = getTrailingBlankDecisions(args.db, args.sessionId);
33190
+ for (const [id] of candidates) {
33191
+ const decision = committed.get(id);
33192
+ if (decision)
33193
+ trailingBlankDecisions.set(id, decision);
33194
+ }
33195
+ } else {
33196
+ sessionLog(args.sessionId, "rust trailing blank decision: persistence failed; serving only frozen decisions");
33197
+ }
33198
+ }
33199
+ } catch (error) {
33200
+ sessionLog(args.sessionId, "rust trailing blank decision failed:", error);
33201
+ }
33202
+ }
33203
+ const absorbingStripDecisions = new Map([...trailingBlankDecisions].filter(([, decision]) => decision === "strip"));
33204
+ applyFrozenTrailingBlankDecisions(args.messages, absorbingStripDecisions);
32970
33205
  const currentUserMessageId = findLastUserMessageId(args.messages);
32971
33206
  const noteReadStillVisible = hasVisibleNoteReadCall(args.messages);
32972
33207
  const deferredNoteText = peekNoteNudgeText(args.db, args.sessionId, currentUserMessageId, args.projectPath, noteReadStillVisible);
32973
- if (!deferredNoteText)
32974
- return;
32975
- const instruction = `
33208
+ if (deferredNoteText) {
33209
+ const instruction = `
32976
33210
 
32977
33211
  <instruction name="deferred_notes">${deferredNoteText}</instruction>`;
32978
- const anchoredMessageId = findLastUserMessageId(args.messages);
32979
- const outcome = markNoteNudgeDelivered(args.db, args.sessionId, instruction, anchoredMessageId);
32980
- if (anchoredMessageId && outcome.ok) {
32981
- appendReminderToUserMessageById(args.messages, anchoredMessageId, instruction);
32982
- } else if (anchoredMessageId && !outcome.ok) {
32983
- sessionLog(args.sessionId, `rust note-nudge delivery skipped wire append: ${outcome.kind}`);
33212
+ const anchoredMessageId = findLastUserMessageId(args.messages);
33213
+ const outcome = markNoteNudgeDelivered(args.db, args.sessionId, instruction, anchoredMessageId);
33214
+ if (anchoredMessageId && outcome.ok) {
33215
+ appendReminderToUserMessageById(args.messages, anchoredMessageId, instruction);
33216
+ } else if (anchoredMessageId && !outcome.ok) {
33217
+ sessionLog(args.sessionId, `rust note-nudge delivery skipped wire append: ${outcome.kind}`);
33218
+ }
33219
+ }
33220
+ const recoveryMessageIds = new Set;
33221
+ let thinkingBindingRecovery = null;
33222
+ if (modelAcceptsEmptyContent(args.resolvedProviderID)) {
33223
+ try {
33224
+ for (const id of getMergedReasoningStrippedIds(args.db, args.sessionId)) {
33225
+ if (!id.startsWith(THINKING_BINDING_RECOVERY_FROZEN_PREFIX))
33226
+ continue;
33227
+ const messageId2 = id.slice(THINKING_BINDING_RECOVERY_FROZEN_PREFIX.length);
33228
+ if (messageId2.length > 0)
33229
+ recoveryMessageIds.add(messageId2);
33230
+ }
33231
+ const flagTarget = args.thinkingBindingRecoveryEnabledForModel ? getThinkingBindingRecoveryTarget(args.db, args.sessionId) : null;
33232
+ if (flagTarget) {
33233
+ const messageId2 = flagTarget === NEWEST_REASONING_BEARING_ASSISTANT ? findNewestReasoningBearingAssistantId(args.messages) : flagTarget;
33234
+ if (messageId2 && assistantHasReasoningPart(args.messages, messageId2)) {
33235
+ const frozenId = thinkingBindingRecoveryFrozenId(messageId2);
33236
+ const persisted = recoveryMessageIds.has(messageId2) || addMergedReasoningStrippedIds(args.db, args.sessionId, [frozenId]);
33237
+ if (persisted) {
33238
+ recoveryMessageIds.add(messageId2);
33239
+ thinkingBindingRecovery = { flagTarget, messageId: messageId2 };
33240
+ } else {
33241
+ sessionLog(args.sessionId, "rust thinking binding recovery: persistence failed; leaving the bound block intact");
33242
+ }
33243
+ }
33244
+ }
33245
+ } catch (error) {
33246
+ sessionLog(args.sessionId, "rust thinking binding recovery failed:", error);
33247
+ }
33248
+ stripReasoningFromAssistantIds(args.messages, args.resolvedProviderID, recoveryMessageIds);
32984
33249
  }
33250
+ return { thinkingBindingRecovery };
32985
33251
  }
32986
33252
  function dropMarkerSummaryTag(db, sessionId, summaryMessageId) {
32987
33253
  const tagNumber = getTagNumberByMessageId(db, sessionId, `${summaryMessageId}:p0`);
@@ -33105,22 +33371,13 @@ function finalizeMessageRepresentation(messages, resolvedProviderID, options) {
33105
33371
  clearedParts = stripClearedReasoning(targetedMessages);
33106
33372
  }
33107
33373
  }
33108
- let newestAssistant = options?.trailingBlankNewestAssistant;
33109
- if (!newestAssistant) {
33110
- for (let index = messages.length - 1;index >= 0; index -= 1) {
33111
- const message = messages[index];
33112
- if (message.info.role !== "assistant")
33113
- continue;
33114
- newestAssistant = message;
33115
- break;
33116
- }
33117
- }
33118
- const mergedReasoningParts = options?.skipMergedReasoningStrip ? 0 : stripReasoningFromMergedAssistants(messages, resolvedProviderID, {
33374
+ const bindingRecoveryParts = options?.skipMergedReasoningStrip ? 0 : stripReasoningFromAssistantIds(messages, resolvedProviderID, options?.thinkingBindingRecoveryMessageIds ?? new Set);
33375
+ const mergedReasoningParts = bindingRecoveryParts + (options?.skipMergedReasoningStrip ? 0 : stripReasoningFromMergedAssistants(messages, resolvedProviderID, {
33119
33376
  mutationExemptMessage: options?.reasoningMutationExemptMessage,
33120
33377
  frozenMessageIds: options?.mergedReasoningStrippedIds
33121
- });
33378
+ }));
33122
33379
  if (!options?.skipTrailingWhitespaceStrip && modelAcceptsEmptyContent(resolvedProviderID)) {
33123
- applyFrozenTrailingBlankDecisions(messages, typeof newestAssistant?.info.id === "string" ? newestAssistant.info.id : undefined, options?.trailingBlankDecisions ?? new Map);
33380
+ applyFrozenTrailingBlankDecisions(messages, options?.trailingBlankDecisions ?? new Map);
33124
33381
  }
33125
33382
  return { clearedParts, mergedReasoningParts };
33126
33383
  }
@@ -33778,10 +34035,34 @@ async function runPostTransformPhase(args) {
33778
34035
  }
33779
34036
  }
33780
34037
  const mergedReasoningStrippedIds = new Set;
34038
+ const thinkingBindingRecoveryMessageIds = new Set;
34039
+ let thinkingBindingRecovery = null;
33781
34040
  if (canUseEmptySentinels && !compactionOff) {
33782
34041
  try {
33783
34042
  for (const id of getMergedReasoningStrippedIds(args.db, args.sessionId)) {
33784
34043
  mergedReasoningStrippedIds.add(id);
34044
+ if (id.startsWith(THINKING_BINDING_RECOVERY_FROZEN_PREFIX)) {
34045
+ const messageId2 = id.slice(THINKING_BINDING_RECOVERY_FROZEN_PREFIX.length);
34046
+ if (messageId2.length > 0)
34047
+ thinkingBindingRecoveryMessageIds.add(messageId2);
34048
+ }
34049
+ }
34050
+ const flagTarget = args.thinkingBindingRecoveryEnabledForModel ? getThinkingBindingRecoveryTarget(args.db, args.sessionId) : null;
34051
+ if (flagTarget) {
34052
+ const messageId2 = flagTarget === NEWEST_REASONING_BEARING_ASSISTANT ? findNewestReasoningBearingAssistantId(args.messages) : flagTarget;
34053
+ if (messageId2 && assistantHasReasoningPart(args.messages, messageId2)) {
34054
+ const frozenId = thinkingBindingRecoveryFrozenId(messageId2);
34055
+ const persisted = mergedReasoningStrippedIds.has(frozenId) || addMergedReasoningStrippedIds(args.db, args.sessionId, [frozenId]);
34056
+ if (persisted) {
34057
+ mergedReasoningStrippedIds.add(frozenId);
34058
+ thinkingBindingRecoveryMessageIds.add(messageId2);
34059
+ thinkingBindingRecovery = { flagTarget, messageId: messageId2 };
34060
+ bustedThisPass = true;
34061
+ } else {
34062
+ args.passOutcome?.record("thinking-binding-recovery-persistence-failure");
34063
+ sessionLog(args.sessionId, "thinking binding recovery: persistence failed; leaving the bound block intact");
34064
+ }
34065
+ }
33785
34066
  }
33786
34067
  if (isCacheBustingPass) {
33787
34068
  const candidates = findMergedReasoningStripCandidateIds(args.messages, args.resolvedProviderID, { mutationExemptMessage: reasoningMutationExemptMessage });
@@ -33845,17 +34126,6 @@ async function runPostTransformPhase(args) {
33845
34126
  }
33846
34127
  }
33847
34128
  }
33848
- const tFinalRepresentation = performance.now();
33849
- const finalRepresentation = finalizeMessageRepresentation(args.messages, args.resolvedProviderID, {
33850
- prependedMessageCount,
33851
- reasoningMutatedMessages,
33852
- reasoningMutationExemptMessage,
33853
- trailingBlankNewestAssistant,
33854
- mergedReasoningStrippedIds,
33855
- trailingBlankDecisions,
33856
- skipMergedReasoningStrip: compactionOff,
33857
- skipTrailingWhitespaceStrip: compactionOff
33858
- });
33859
34129
  if (canUseEmptySentinels && !compactionOff) {
33860
34130
  const detectedCandidates = findTrailingBlankDecisionCandidates(args.messages, trailingBlankDecisions, {
33861
34131
  refreshMessageId: newestAssistantId,
@@ -33869,20 +34139,16 @@ async function runPostTransformPhase(args) {
33869
34139
  });
33870
34140
  if (persisted) {
33871
34141
  const committed = getTrailingBlankDecisions(args.db, args.sessionId);
33872
- const newlyFrozen = new Map;
33873
34142
  for (const [id] of candidates) {
33874
34143
  const decision = committed.get(id);
33875
- if (!decision)
33876
- continue;
33877
- trailingBlankDecisions.set(id, decision);
33878
- newlyFrozen.set(id, decision);
34144
+ if (decision)
34145
+ trailingBlankDecisions.set(id, decision);
33879
34146
  }
33880
- applyFrozenTrailingBlankDecisions(args.messages, newestAssistantId, newlyFrozen);
33881
34147
  if (isCacheBustingPass)
33882
34148
  bustedThisPass = true;
33883
34149
  } else {
33884
34150
  args.passOutcome?.record("trailing-blank-decision-persistence-failure");
33885
- sessionLog(args.sessionId, "trailing blank decision: persistence failed; leaving newly observed assistants intact");
34151
+ sessionLog(args.sessionId, "trailing blank decision: persistence failed; serving the frozen decisions");
33886
34152
  }
33887
34153
  } catch (error) {
33888
34154
  args.passOutcome?.record("trailing-blank-decision-exception");
@@ -33890,6 +34156,17 @@ async function runPostTransformPhase(args) {
33890
34156
  }
33891
34157
  }
33892
34158
  }
34159
+ const tFinalRepresentation = performance.now();
34160
+ const finalRepresentation = finalizeMessageRepresentation(args.messages, args.resolvedProviderID, {
34161
+ prependedMessageCount,
34162
+ reasoningMutatedMessages,
34163
+ reasoningMutationExemptMessage,
34164
+ mergedReasoningStrippedIds,
34165
+ thinkingBindingRecoveryMessageIds,
34166
+ trailingBlankDecisions,
34167
+ skipMergedReasoningStrip: compactionOff,
34168
+ skipTrailingWhitespaceStrip: compactionOff
34169
+ });
33893
34170
  sessionLog(args.sessionId, `final representation: clearedParts=${finalRepresentation.clearedParts} mergedReasoningParts=${finalRepresentation.mergedReasoningParts}`);
33894
34171
  logTransformTiming(args.sessionId, "finalizeMessageRepresentation", tFinalRepresentation, `clearedParts=${finalRepresentation.clearedParts} mergedReasoningParts=${finalRepresentation.mergedReasoningParts}`);
33895
34172
  let assertedBaseline;
@@ -33984,7 +34261,8 @@ async function runPostTransformPhase(args) {
33984
34261
  emergencyReclaimedTokens,
33985
34262
  droppedCount,
33986
34263
  emergency,
33987
- bustedThisPass
34264
+ bustedThisPass,
34265
+ thinkingBindingRecovery
33988
34266
  };
33989
34267
  }
33990
34268
  function checkM0MutationDriftAndSignal(args) {
@@ -34268,7 +34546,9 @@ function formatRustPassLog(args) {
34268
34546
  const measured = timings.prefixGuard + timings.ordinalResolve + timings.stateSync + timings.clone + timings.wireBuild + timings.transport + timings.apply + timings.lkgSnapshot;
34269
34547
  const unattributed = Math.max(0, args.elapsedMs - measured);
34270
34548
  const rowVersion = Number.isSafeInteger(args.rowVersion) ? args.rowVersion : 0;
34271
- return `rust pass: decision=${args.decision} reason=${args.reason} served_from=${args.servedFrom} in=${args.inputCount} out=${args.outputCount} applied=${args.applied} row_version=${rowVersion} elapsed=${args.elapsedMs.toFixed(1)} ms module=${args.moduleElapsedMs.toFixed(1)} ms stages=prefix_guard:${timings.prefixGuard.toFixed(1)} ordinal_resolve:${timings.ordinalResolve.toFixed(1)} state_sync:${timings.stateSync.toFixed(1)} clone:${timings.clone.toFixed(1)} wire_build:${timings.wireBuild.toFixed(1)} wire_messages:${timings.wireMessages} transport:${timings.transport.toFixed(1)} transport_pages:${timings.transportPages} transport_bytes:${timings.transportBytes} apply:${timings.apply.toFixed(1)} lkg_snapshot:${timings.lkgSnapshot.toFixed(1)} mirror_pull:${timings.mirrorPull.toFixed(1)} compartment_mirror:${timings.compartmentMirror.toFixed(1)} other:${unattributed.toFixed(1)}`;
34549
+ const schedulerFields = args.schedulerDecision ? ` scheduler=${args.schedulerDecision}${args.schedulerDeferReason ? ` defer_reason=${args.schedulerDeferReason}` : ""}` : "";
34550
+ const historianFields = args.historianCanonicalCause ? ` historian_no_fire=${args.historianNoFire ?? "unknown"} canonical_cause=${args.historianCanonicalCause}` : "";
34551
+ return `rust pass: decision=${args.decision} reason=${args.reason}${schedulerFields}${historianFields} served_from=${args.servedFrom} in=${args.inputCount} out=${args.outputCount} applied=${args.applied} row_version=${rowVersion} elapsed=${args.elapsedMs.toFixed(1)} ms module=${args.moduleElapsedMs.toFixed(1)} ms stages=prefix_guard:${timings.prefixGuard.toFixed(1)} ordinal_resolve:${timings.ordinalResolve.toFixed(1)} state_sync:${timings.stateSync.toFixed(1)} clone:${timings.clone.toFixed(1)} wire_build:${timings.wireBuild.toFixed(1)} wire_messages:${timings.wireMessages} transport:${timings.transport.toFixed(1)} transport_pages:${timings.transportPages} transport_bytes:${timings.transportBytes} apply:${timings.apply.toFixed(1)} lkg_snapshot:${timings.lkgSnapshot.toFixed(1)} mirror_pull:${timings.mirrorPull.toFixed(1)} compartment_mirror:${timings.compartmentMirror.toFixed(1)} other:${unattributed.toFixed(1)}`;
34272
34552
  }
34273
34553
  function isSyntheticUserMessage(message) {
34274
34554
  if (!message || messageInfo2(message).role !== "user" || !Array.isArray(message.parts)) {
@@ -34816,6 +35096,7 @@ function createRustModeTransform(deps, options) {
34816
35096
  const states = new Map;
34817
35097
  const wireCaches = new Map;
34818
35098
  const scheduleLkgCapture = options.scheduleLkgCapture ?? ((capture) => setImmediate(capture));
35099
+ const installNativeMessages = options.installNativeMessagesForTests ?? replaceMessagesInPlace;
34819
35100
  const rawFallbackEstimator = options.rawFallbackEstimatorForTests ?? estimateFinalWireInputTokens;
34820
35101
  const timeoutMs = Math.max(1, options.moduleTimeoutMs ?? RUST_SEND_TIMEOUT_MS);
34821
35102
  const logStage = (sessionId, stage, startedAt, timings, extra) => {
@@ -34903,6 +35184,12 @@ function createRustModeTransform(deps, options) {
34903
35184
  return false;
34904
35185
  }
34905
35186
  const replayModel = modelFromMessages(currentMessages) ?? findLastAssistantModelFromOpenCodeDb(sessionId);
35187
+ replayRustModeBindingMismatchStrips({
35188
+ db: deps.db,
35189
+ sessionId,
35190
+ messages: replay.messages,
35191
+ resolvedProviderID: replayModel?.providerID
35192
+ });
34906
35193
  const trustedReplayLimit = replayModel ? resolveTrustedContextLimit(replayModel.providerID, replayModel.modelID, {
34907
35194
  db: deps.db,
34908
35195
  sessionID: sessionId
@@ -34935,23 +35222,21 @@ function createRustModeTransform(deps, options) {
34935
35222
  sessionLog(sessionId, "lkg_replay_served");
34936
35223
  return true;
34937
35224
  };
34938
- const prepareRustCapture = (state, sessionId, input, inputSnapshots, nativeMessages, responseRowVersion) => {
35225
+ const prepareRustCapture = (state, sessionId, inputIds, inputKeys, inputSnapshots, nativeMessages, responseRowVersion) => {
34939
35226
  state.lkgCaptureSequence += 1;
34940
- const inputIds = input.map((message) => message.info.id);
34941
35227
  if (inputIds.some((id) => typeof id !== "string") || new Set(inputIds).size !== inputIds.length || inputIds.length === 0 || inputSnapshots.length !== inputIds.length) {
34942
35228
  return null;
34943
35229
  }
34944
35230
  const jsonPrefix = JSON.stringify(nativeMessages);
34945
35231
  if (typeof jsonPrefix !== "string")
34946
35232
  return null;
34947
- const keys = resolveLkgModelKeys(input);
34948
35233
  return {
34949
35234
  sessionId,
34950
35235
  inputIds,
34951
35236
  inputSnapshots,
34952
35237
  jsonPrefix,
34953
- modelKey: keys.modelKey,
34954
- providerKey: keys.providerKey,
35238
+ modelKey: inputKeys.modelKey,
35239
+ providerKey: inputKeys.providerKey,
34955
35240
  capturedAt: Date.now(),
34956
35241
  rowVersion: responseRowVersion,
34957
35242
  captureSequence: state.lkgCaptureSequence
@@ -34996,6 +35281,8 @@ function createRustModeTransform(deps, options) {
34996
35281
  const passStartedAt = performance.now();
34997
35282
  const passObservedAtMs = Date.now();
34998
35283
  const state = ensureState(states, sessionId);
35284
+ const trailingBlankSourceDecisions = snapshotTrailingBlankSourceDecisions(messages);
35285
+ const trailingBlankNewestAssistantId = [...messages].reverse().find((message) => message.info.role === "assistant")?.info.id;
34999
35286
  const timings = emptyRustPassTimings();
35000
35287
  state.passCount += 1;
35001
35288
  const syntheticTurn = observeSyntheticTurn(state, messages);
@@ -35008,6 +35295,10 @@ function createRustModeTransform(deps, options) {
35008
35295
  let requestInputTokens = 0;
35009
35296
  let decision = "error";
35010
35297
  let materializeReason = "none";
35298
+ let schedulerDecision;
35299
+ let schedulerDeferReason;
35300
+ let historianNoFire;
35301
+ let historianCanonicalCause;
35011
35302
  let servedFrom = "none";
35012
35303
  let moduleElapsedMs = 0;
35013
35304
  let rowVersion = 0;
@@ -35086,6 +35377,10 @@ function createRustModeTransform(deps, options) {
35086
35377
  sessionLog(sessionId, formatRustPassLog({
35087
35378
  decision,
35088
35379
  reason: materializeReason,
35380
+ schedulerDecision,
35381
+ schedulerDeferReason,
35382
+ historianNoFire,
35383
+ historianCanonicalCause,
35089
35384
  servedFrom,
35090
35385
  inputCount,
35091
35386
  outputCount: output.messages.length,
@@ -35108,6 +35403,11 @@ function createRustModeTransform(deps, options) {
35108
35403
  const captureResponseTelemetry = (response) => {
35109
35404
  decision = typeof response.decision === "string" ? response.decision : typeof response.action === "string" ? response.action : typeof response.status === "string" ? response.status : "unknown";
35110
35405
  servedFrom = typeof response.served_from === "string" ? response.served_from : "unknown";
35406
+ schedulerDecision = typeof response.scheduler_decision === "string" ? response.scheduler_decision : undefined;
35407
+ schedulerDeferReason = typeof response.scheduler_defer_reason === "string" ? response.scheduler_defer_reason : undefined;
35408
+ const historian = isRecord4(response.historian) ? response.historian : undefined;
35409
+ historianNoFire = typeof historian?.no_fire === "string" ? historian.no_fire : undefined;
35410
+ historianCanonicalCause = typeof historian?.canonical_cause === "string" ? historian.canonical_cause : undefined;
35111
35411
  materializeReason = typeof response.materialize_reason === "string" && response.materialize_reason.length > 0 ? response.materialize_reason : "none";
35112
35412
  const timings2 = isRecord4(response.timings) ? response.timings : undefined;
35113
35413
  const applyOnceTotal = timings2?.total;
@@ -35630,6 +35930,7 @@ function createRustModeTransform(deps, options) {
35630
35930
  const decisionUpper = decision.toUpperCase();
35631
35931
  const cacheBustingPass = response.cache_bust === true || decisionUpper === "HARD" || decisionUpper === "MIGRATE_HARD" || decisionUpper === "EXECUTE" || decisionUpper === "SOFT" || !explicitDecision;
35632
35932
  let appliedMessages;
35933
+ let thinkingBindingRecovery = null;
35633
35934
  const applyStartedAt = performance.now();
35634
35935
  try {
35635
35936
  const moduleMessages = applyNativeMessagesVerbatim({ messages: [] }, response, previousWireCache?.nativeOutput ? {
@@ -35657,16 +35958,20 @@ function createRustModeTransform(deps, options) {
35657
35958
  }
35658
35959
  pendingWireCache.nativeOutput = appliedMessages;
35659
35960
  if (!replayedFrozenRepresentation) {
35660
- runRustModePostprocess({
35961
+ thinkingBindingRecovery = runRustModePostprocess({
35661
35962
  db: deps.db,
35662
35963
  sessionId,
35663
35964
  messages: appliedMessages,
35664
35965
  projectPath: memoryProjectPath,
35665
35966
  fullFeatureMode: !sessionMeta.isSubagent,
35666
35967
  compactionOff: deps.compactionOff,
35968
+ resolvedProviderID: model?.providerID,
35969
+ thinkingBindingRecoveryEnabledForModel: isFable51ThinkingBindingModel(model?.providerID, model?.modelID),
35970
+ trailingBlankSourceDecisions,
35971
+ trailingBlankNewestAssistantId: typeof trailingBlankNewestAssistantId === "string" ? trailingBlankNewestAssistantId : undefined,
35667
35972
  tagger: deps.tagger,
35668
35973
  ctxReduceAvailability: reduceAvailability
35669
- });
35974
+ }).thinkingBindingRecovery;
35670
35975
  }
35671
35976
  const boundaryId = response.boundary_id;
35672
35977
  if (typeof boundaryId === "string" && boundaryId.length > 0) {
@@ -35690,11 +35995,20 @@ function createRustModeTransform(deps, options) {
35690
35995
  });
35691
35996
  }
35692
35997
  logStage(sessionId, "apply", applyStartedAt, timings);
35998
+ const lkgInputIds = messages.map((message) => message.info.id);
35999
+ const lkgInputKeys = resolveLkgModelKeys(messages);
36000
+ const applyReplaceStartedAt = performance.now();
36001
+ installNativeMessages(output, appliedMessages);
36002
+ logStage(sessionId, "apply", applyReplaceStartedAt, timings);
36003
+ if (thinkingBindingRecovery) {
36004
+ const cleared = clearThinkingBindingRecoveryIf(deps.db, sessionId, thinkingBindingRecovery.flagTarget);
36005
+ sessionLog(sessionId, `rust thinking binding recovery: stripped bound reasoning from assistant ${thinkingBindingRecovery.messageId}; flag=${cleared ? "cleared" : "rearmed"}`);
36006
+ }
35693
36007
  const lkgSnapshotStartedAt = performance.now();
35694
36008
  if (cacheBustingPass) {
35695
36009
  dropSlot(sessionId, "lkg_cache_bust_pending_capture");
35696
36010
  }
35697
- const capturePlan = prepareRustCapture(state, sessionId, messages, pendingWireCache.rawContentSnapshots, appliedMessages, rowVersion);
36011
+ const capturePlan = prepareRustCapture(state, sessionId, lkgInputIds, lkgInputKeys, pendingWireCache.rawContentSnapshots, output.messages, rowVersion);
35698
36012
  let captureMode = "async";
35699
36013
  const captureFailed = (mode, error) => {
35700
36014
  if (states.get(sessionId) !== state || capturePlan && capturePlan.captureSequence !== state.lkgCaptureSequence) {
@@ -35731,9 +36045,6 @@ function createRustModeTransform(deps, options) {
35731
36045
  }
35732
36046
  }
35733
36047
  logStage(sessionId, "lkgSnapshot", lkgSnapshotStartedAt, timings, `mode=${captureMode} row_version=${rowVersion}`);
35734
- const applyReplaceStartedAt = performance.now();
35735
- replaceMessagesInPlace(output, appliedMessages);
35736
- logStage(sessionId, "apply", applyReplaceStartedAt, timings);
35737
36048
  } catch (error) {
35738
36049
  logStage(sessionId, "apply", applyStartedAt, timings, "failed=true");
35739
36050
  try {
@@ -35879,17 +36190,24 @@ function createRustModeTransform(deps, options) {
35879
36190
  };
35880
36191
  return {
35881
36192
  run,
35882
- clearSession(sessionId) {
36193
+ async clearSession(sessionId) {
35883
36194
  const projectRoot = states.get(sessionId)?.memoryAuthorityRoot ?? options.projectRoot ?? null;
35884
- dropSlot(sessionId, "session-deleted");
35885
- states.delete(sessionId);
35886
- wireCaches.delete(sessionId);
35887
- clearCompartmentMirrorCursor(sessionId);
35888
- if (projectRoot && options.moduleClient.deleteSession) {
35889
- options.moduleClient.deleteSession(sessionId, projectRoot).catch((error) => {
35890
- sessionLog(sessionId, "rust module session deletion failed:", error);
35891
- }).finally(() => options.moduleClient.closeSession?.(sessionId));
35892
- } else {
36195
+ const clearLocalState = () => {
36196
+ dropSlot(sessionId, "session-deleted");
36197
+ states.delete(sessionId);
36198
+ wireCaches.delete(sessionId);
36199
+ clearCompartmentMirrorCursor(sessionId);
36200
+ };
36201
+ clearLocalState();
36202
+ try {
36203
+ if (projectRoot && options.moduleClient.deleteSession) {
36204
+ await options.moduleClient.deleteSession(sessionId, projectRoot);
36205
+ }
36206
+ } catch (error) {
36207
+ sessionLog(sessionId, "rust module session deletion failed:", error);
36208
+ throw error;
36209
+ } finally {
36210
+ clearLocalState();
35893
36211
  options.moduleClient.closeSession?.(sessionId);
35894
36212
  }
35895
36213
  },
@@ -36968,6 +37286,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
36968
37286
  cavemanTextCompression: !reducedMode ? deps.cavemanTextCompression : undefined,
36969
37287
  smartDrops: deps.smartDrops === true,
36970
37288
  resolvedProviderID,
37289
+ thinkingBindingRecoveryEnabledForModel: isFable51ThinkingBindingModel(modelForBudget?.providerID, modelForBudget?.modelID),
36971
37290
  trailingBlankSourceDecisions,
36972
37291
  passOutcome,
36973
37292
  historyRefreshSessions: deps.historyRefreshSessions,
@@ -37154,13 +37473,18 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
37154
37473
  const elapsed = (performance.now() - startTime).toFixed(1);
37155
37474
  sessionLog(sessionId, `transform completed in ${elapsed}ms (${messages.length} messages, ${targets.size} targets, watermark: ${watermark})`);
37156
37475
  deps.maybeAutoEmbedSession?.(sessionId);
37476
+ const bindingRecovery = postTransformResult.thinkingBindingRecovery;
37477
+ if (bindingRecovery) {
37478
+ const cleared = clearThinkingBindingRecoveryIf(db, sessionId, bindingRecovery.flagTarget);
37479
+ sessionLog(sessionId, `thinking binding recovery: stripped bound reasoning from assistant ${bindingRecovery.messageId}; flag=${cleared ? "cleared" : "rearmed"}`);
37480
+ }
37157
37481
  };
37158
37482
  return Object.assign(transform, {
37159
37483
  invalidateRustWireState(sessionId) {
37160
37484
  rustModeTransform?.invalidateWireState(sessionId);
37161
37485
  },
37162
- clearRustSession(sessionId) {
37163
- rustModeTransform?.clearSession(sessionId);
37486
+ async clearRustSession(sessionId) {
37487
+ await rustModeTransform?.clearSession(sessionId);
37164
37488
  }
37165
37489
  });
37166
37490
  }
@@ -37278,6 +37602,16 @@ function createEventHandler2(deps) {
37278
37602
  return;
37279
37603
  }
37280
37604
  try {
37605
+ const bindingMismatch = detectThinkingBindingMismatch(errInfo.error);
37606
+ if (bindingMismatch.isBindingMismatch) {
37607
+ const model = findLastAssistantModelFromOpenCodeDb(errInfo.sessionID);
37608
+ if (deps.thinkingBindingRecoveryEnabled !== false && !deps.compactionOff && isFable51ThinkingBindingModel(model?.providerID, model?.modelID)) {
37609
+ armThinkingBindingRecovery(deps.db, errInfo.sessionID, bindingMismatch.messageId);
37610
+ dropSlot(errInfo.sessionID, "thinking-binding-recovery-arm");
37611
+ deps.onSessionCacheInvalidated?.(errInfo.sessionID);
37612
+ }
37613
+ return;
37614
+ }
37281
37615
  const detection = detectOverflow(errInfo.error);
37282
37616
  if (!detection.isOverflow) {
37283
37617
  return;
@@ -37341,6 +37675,18 @@ function createEventHandler2(deps) {
37341
37675
  });
37342
37676
  }
37343
37677
  let messageHadOverflowError = false;
37678
+ if (info.error !== undefined && info.error !== null) {
37679
+ const bindingMismatch = detectThinkingBindingMismatch(info.error);
37680
+ if (bindingMismatch.isBindingMismatch && deps.thinkingBindingRecoveryEnabled !== false && !deps.compactionOff && isFable51ThinkingBindingModel(info.providerID, info.modelID)) {
37681
+ try {
37682
+ armThinkingBindingRecovery(deps.db, info.sessionID, bindingMismatch.messageId);
37683
+ dropSlot(info.sessionID, "thinking-binding-recovery-arm");
37684
+ deps.onSessionCacheInvalidated?.(info.sessionID);
37685
+ } catch (error) {
37686
+ sessionLog(info.sessionID, "event message.updated thinking binding recovery persistence failed:", error);
37687
+ }
37688
+ }
37689
+ }
37344
37690
  if (info.error !== undefined && info.error !== null) {
37345
37691
  const detection = detectOverflow(info.error);
37346
37692
  if (detection.isOverflow) {
@@ -37551,15 +37897,17 @@ function createEventHandler2(deps) {
37551
37897
  }
37552
37898
  dropSlot(sessionId, "session.deleted");
37553
37899
  try {
37554
- markSessionCleanupPending(deps.db, sessionId);
37555
- removeCompactionMarkerForSession(deps.db, sessionId);
37556
- clearSession(deps.db, sessionId);
37900
+ const rustCleanupPending = markSessionCleanupPending(deps.db, sessionId, deps.rustSessionCleanup === true);
37901
+ await deps.onSessionDeleted?.(sessionId);
37902
+ if (!rustCleanupPending || deps.rustSessionCleanup === true) {
37903
+ removeCompactionMarkerForSession(deps.db, sessionId);
37904
+ clearSession(deps.db, sessionId, deps.rustSessionCleanup === true);
37905
+ }
37557
37906
  } catch (error) {
37558
37907
  sessionLog(sessionId, "event session.deleted persistence failed:", error);
37559
37908
  }
37560
37909
  resetDegradedCacheCount(sessionId);
37561
37910
  deps.onSessionCacheInvalidated?.(sessionId);
37562
- deps.onSessionDeleted?.(sessionId);
37563
37911
  deps.contextUsageMap.delete(sessionId);
37564
37912
  deps.tagger.cleanup(sessionId);
37565
37913
  clearTransformDecisionSession(sessionId);
@@ -37590,6 +37938,14 @@ function formatEmbedFailureSummary(embedded, remaining, failure) {
37590
37938
  return `Embedded ${embedded} ${compartment}; ${remaining} rejected: ${failure.reason}. Fix: recertify SYNAPSE or configure embedding.fallback_provider, then run /ctx-embed start again.`;
37591
37939
  case "credential_required":
37592
37940
  return `Embedded ${embedded} ${compartment}; ${remaining} rejected: ${failure.reason}. Fix: reauthenticate SYNAPSE, then run /ctx-embed start again.`;
37941
+ case "local_binding_missing":
37942
+ return `Embedded ${embedded} ${compartment}; ${remaining} failed: ${failure.reason}. Fix: run \`npx @cortexkit/magic-context@latest doctor\` to verify the WASM fallback. On Intel macOS, onnxruntime-node@1.23.0 may be pinned manually for optional native speed.`;
37943
+ case "local_fs_unavailable":
37944
+ return `Embedded ${embedded} ${compartment}; ${remaining} failed: ${failure.reason}. Fix: update or reinstall Magic Context so its Node WASM bundle can persist the model, then run /ctx-embed start again.`;
37945
+ case "local_download_failure":
37946
+ return `Embedded ${embedded} ${compartment}; ${remaining} failed: ${failure.reason}. Fix: check network/proxy access to the model host.${retry}`;
37947
+ case "local_runtime_error":
37948
+ return `Embedded ${embedded} ${compartment}; ${remaining} failed: ${failure.reason}. Fix: run \`npx @cortexkit/magic-context@latest doctor\` for local runtime diagnostics.`;
37593
37949
  case "empty_result":
37594
37950
  case "transport_error":
37595
37951
  return `Embedded ${embedded} ${compartment}; ${remaining} failed: ${failure.reason}.${failure.retryable ? retry : ""}`;
@@ -38519,6 +38875,7 @@ function createMagicContextHook(deps) {
38519
38875
  const commitSeenLastPass = new Map;
38520
38876
  const variantBySession = deps.liveSessionState?.variantBySession ?? new Map;
38521
38877
  const liveModelBySession = deps.liveSessionState?.liveModelBySession ?? new Map;
38878
+ const latestAssistantMessageIdBySession = deps.liveSessionState?.latestAssistantMessageIdBySession ?? new Map;
38522
38879
  const agentBySession = deps.liveSessionState?.agentBySession ?? new Map;
38523
38880
  const sessionDirectoryBySession = deps.liveSessionState?.sessionDirectoryBySession ?? new Map;
38524
38881
  const internalChildSessions = deps.liveSessionState?.internalChildSessions ?? new Set;
@@ -38555,6 +38912,7 @@ function createMagicContextHook(deps) {
38555
38912
  db,
38556
38913
  liveSessionState: {
38557
38914
  liveModelBySession,
38915
+ latestAssistantMessageIdBySession,
38558
38916
  channel1StateBySession,
38559
38917
  variantBySession,
38560
38918
  agentBySession,
@@ -38713,7 +39071,7 @@ function createMagicContextHook(deps) {
38713
39071
  autoEmbedAttemptedBySession.add(sessionId);
38714
39072
  const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
38715
39073
  (async () => {
38716
- let completedDrainWithWork = false;
39074
+ let drainReachedTerminal = false;
38717
39075
  try {
38718
39076
  await new Promise((resolve3) => setTimeout(resolve3, 0));
38719
39077
  await ensureProjectRegisteredFromOpenCodeDirectory(directory, db);
@@ -38727,25 +39085,19 @@ function createMagicContextHook(deps) {
38727
39085
  const remaining = coverage.session.total - coverage.session.embedded;
38728
39086
  if (remaining <= 0)
38729
39087
  return;
38730
- const notifyParams = getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession);
38731
- if (!isTuiConnected(sessionId)) {
38732
- const startMsg = `Embedding ${remaining} compartment${remaining === 1 ? "" : "s"} of history in the background…`;
38733
- await sendIgnoredMessage(deps.client, sessionId, startMsg, {
38734
- ...notifyParams
38735
- });
38736
- }
38737
- const summary = await executeEmbedHistory(sessionId);
39088
+ const embeddedBefore = coverage.session.embedded;
39089
+ await executeEmbedHistory(sessionId, { silent: true });
39090
+ drainReachedTerminal = true;
38738
39091
  const completedCoverage = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
38739
- completedDrainWithWork = completedCoverage.session.total - completedCoverage.session.embedded <= 0;
38740
- if (!isTuiConnected(sessionId)) {
38741
- await sendIgnoredMessage(deps.client, sessionId, summary, {
38742
- ...notifyParams
38743
- });
39092
+ const embeddedNow = completedCoverage.session.embedded - embeddedBefore;
39093
+ if (embeddedNow > 0 && !isTuiConnected(sessionId)) {
39094
+ const notifyParams = getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession);
39095
+ await sendIgnoredMessage(deps.client, sessionId, `Embedded ${embeddedNow} compartment${embeddedNow === 1 ? "" : "s"} of history for semantic search.`, { ...notifyParams });
38744
39096
  }
38745
39097
  } catch (error) {
38746
39098
  log("[magic-context] auto-embed drain failed:", error);
38747
39099
  } finally {
38748
- if (!completedDrainWithWork)
39100
+ if (!drainReachedTerminal)
38749
39101
  autoEmbedAttemptedBySession.delete(sessionId);
38750
39102
  }
38751
39103
  })();
@@ -39075,6 +39427,7 @@ function createMagicContextHook(deps) {
39075
39427
  compactionHandler: deps.compactionHandler,
39076
39428
  config: deps.config,
39077
39429
  compactionOff,
39430
+ thinkingBindingRecoveryEnabled: deps.config.transform_mode !== "rust",
39078
39431
  tagger: deps.tagger,
39079
39432
  db,
39080
39433
  client: deps.client,
@@ -39090,23 +39443,27 @@ function createMagicContextHook(deps) {
39090
39443
  onRustWireInvalidated: (sessionId) => {
39091
39444
  transform.invalidateRustWireState(sessionId);
39092
39445
  },
39093
- onSessionDeleted: (sessionId) => {
39446
+ rustSessionCleanup: rustModeModuleClient !== undefined,
39447
+ onSessionDeleted: async (sessionId) => {
39094
39448
  dropSlot(sessionId, "session-deleted");
39095
- transform.clearRustSession(sessionId);
39096
- systemPromptHash.clearSession(sessionId);
39097
- lastHeuristicsTurnId.delete(sessionId);
39098
- clearToolPermissionDenied(sessionId);
39099
- commitSeenLastPass.delete(sessionId);
39100
- variantBySession.delete(sessionId);
39101
- liveModelBySession.delete(sessionId);
39102
- agentBySession.delete(sessionId);
39103
- sessionDirectoryBySession.delete(sessionId);
39104
- recompProgressBySession.delete(sessionId);
39105
- internalChildSessions.delete(sessionId);
39106
- rustMemorySyncRequestedSessions.delete(sessionId);
39107
- channel1StateBySession.delete(sessionId);
39108
- channel2DirectiveTextBySession.delete(sessionId);
39109
- clearEmbedSessionState(sessionId);
39449
+ try {
39450
+ await transform.clearRustSession(sessionId);
39451
+ } finally {
39452
+ systemPromptHash.clearSession(sessionId);
39453
+ lastHeuristicsTurnId.delete(sessionId);
39454
+ clearToolPermissionDenied(sessionId);
39455
+ commitSeenLastPass.delete(sessionId);
39456
+ variantBySession.delete(sessionId);
39457
+ liveModelBySession.delete(sessionId);
39458
+ agentBySession.delete(sessionId);
39459
+ sessionDirectoryBySession.delete(sessionId);
39460
+ recompProgressBySession.delete(sessionId);
39461
+ internalChildSessions.delete(sessionId);
39462
+ rustMemorySyncRequestedSessions.delete(sessionId);
39463
+ channel1StateBySession.delete(sessionId);
39464
+ channel2DirectiveTextBySession.delete(sessionId);
39465
+ clearEmbedSessionState(sessionId);
39466
+ }
39110
39467
  }
39111
39468
  });
39112
39469
  const runDreamQueueInBackground = () => {
@@ -39274,6 +39631,7 @@ function createMagicContextHook(deps) {
39274
39631
  contextUsageMap,
39275
39632
  db,
39276
39633
  liveModelBySession,
39634
+ latestAssistantMessageIdBySession,
39277
39635
  variantBySession,
39278
39636
  agentBySession,
39279
39637
  sessionDirectoryBySession,
@@ -40173,6 +40531,48 @@ function projectIdentityForStoredPath(rawProjectPath) {
40173
40531
  function memoryBelongsToProject(memory, projectPath) {
40174
40532
  return storedPathBelongsToIdentity(memory.projectPath, projectPath);
40175
40533
  }
40534
+ function preflightCurateMutation(args) {
40535
+ const { params } = args;
40536
+ if (params.action !== "archive" && params.action !== "update") {
40537
+ return { skip: null, successor: null };
40538
+ }
40539
+ const ids = params.ids;
40540
+ const content = params.content?.trim();
40541
+ if (!ids || ids.length === 0 || !ids.every(Number.isInteger) || params.action === "update" && (ids.length !== 1 || !content)) {
40542
+ return { skip: null, successor: null };
40543
+ }
40544
+ const uniqueIds = [...new Set(ids)];
40545
+ const memories = uniqueIds.map((id) => getMemoryById(args.db, id));
40546
+ if (memories.some((memory) => !memory)) {
40547
+ return { skip: null, successor: null };
40548
+ }
40549
+ const successor = Number.isInteger(params.superseded_by) ? getMemoryById(args.db, params.superseded_by) : null;
40550
+ const refusals = memories.flatMap((memory) => {
40551
+ if (!memory)
40552
+ return [];
40553
+ const refusal = assessCurateMutationSafety({
40554
+ memory,
40555
+ verdict: params.action,
40556
+ reason: params.reason,
40557
+ replacementContent: content,
40558
+ successor,
40559
+ projectIdentity: (candidate) => projectIdentityForStoredPath(candidate.projectPath)
40560
+ });
40561
+ return refusal ? [refusal] : [];
40562
+ });
40563
+ if (refusals.length === 0)
40564
+ return { skip: null, successor };
40565
+ let refused = 0;
40566
+ for (const refusal of refusals) {
40567
+ refused = recordCurateSafetyRefusal(args.sessionId, refusal);
40568
+ }
40569
+ const memoryIds = refusals.map((refusal) => refusal.memoryId).join(", ");
40570
+ const reasons = [...new Set(refusals.map((refusal) => refusal.reason))].join(",");
40571
+ return {
40572
+ skip: `Skipped ${params.action} for memory [ID: ${memoryIds}]: curate safety refusal (${reasons}); refused=${refused}.`,
40573
+ successor
40574
+ };
40575
+ }
40176
40576
  function isPrimaryMutableMemory(memory) {
40177
40577
  return (memory.status === "active" || memory.status === "permanent") && memory.supersededByMemoryId === null;
40178
40578
  }
@@ -40199,7 +40599,10 @@ var ctxMemoryArgsShape = {
40199
40599
  limit: tool2.schema.number().optional().describe("Max results for list (default: 10)"),
40200
40600
  reason: tool2.schema.string().optional().describe("Why the memory is being archived (optional, recommended)")
40201
40601
  };
40202
- var ctxMemoryArgsSchema = tool2.schema.object(ctxMemoryArgsShape).passthrough();
40602
+ var ctxMemoryArgsSchema = tool2.schema.object({
40603
+ ...ctxMemoryArgsShape,
40604
+ superseded_by: tool2.schema.number().optional()
40605
+ }).passthrough();
40203
40606
  function createCtxMemoryTool(deps) {
40204
40607
  const allowedActions = getAllowedActions(deps);
40205
40608
  return tool2({
@@ -40214,7 +40617,8 @@ function createCtxMemoryTool(deps) {
40214
40617
  category: { type: "enum", values: V2_MEMORY_CATEGORIES },
40215
40618
  ids: { type: "array", items: "number", maxItems: 100 },
40216
40619
  limit: "number",
40217
- reason: "string"
40620
+ reason: "string",
40621
+ superseded_by: "number"
40218
40622
  });
40219
40623
  if (toolContext.agent === SIDEKICK_AGENT) {
40220
40624
  return "Error: ctx_memory is not available to the sidekick agent.";
@@ -40227,6 +40631,13 @@ function createCtxMemoryTool(deps) {
40227
40631
  return "Error: Could not resolve project identity for memory action.";
40228
40632
  }
40229
40633
  await deps.ensureProjectRegistered?.(toolContext.directory, deps.db);
40634
+ const curatePreflight = toolContext.agent === DREAMER_AGENT ? preflightCurateMutation({
40635
+ db: deps.db,
40636
+ params: args,
40637
+ sessionId: toolContext.sessionID
40638
+ }) : { skip: null, successor: null };
40639
+ if (curatePreflight.skip)
40640
+ return curatePreflight.skip;
40230
40641
  if (args.action !== "list") {
40231
40642
  const marker = getAuthorityManagedMarker(deps.db, projectPath);
40232
40643
  let authorityState = null;
@@ -40249,18 +40660,29 @@ function createCtxMemoryTool(deps) {
40249
40660
  }
40250
40661
  try {
40251
40662
  const commandId = toolCallIdFromContext(toolContext);
40663
+ const moduleArgs = args.action === "archive" && curatePreflight.successor ? {
40664
+ action: "merge",
40665
+ ids: [
40666
+ ...new Set([
40667
+ ...args.ids ?? [],
40668
+ curatePreflight.successor.id
40669
+ ])
40670
+ ],
40671
+ content: curatePreflight.successor.content,
40672
+ category: curatePreflight.successor.category
40673
+ } : args;
40252
40674
  const text = moduleMemoryText(await memoryBackend({
40253
40675
  ...commandId ? { commandId } : {},
40254
40676
  sessionId: toolContext.sessionID,
40255
40677
  projectRoot: toolContext.directory,
40256
40678
  projectPath,
40257
40679
  memoryProject: projectPath,
40258
- action: args.action,
40259
- content: args.content,
40260
- category: args.category,
40261
- ids: args.ids,
40262
- reason: args.reason
40263
- }), args);
40680
+ action: moduleArgs.action,
40681
+ content: moduleArgs.content,
40682
+ category: moduleArgs.category,
40683
+ ids: moduleArgs.ids,
40684
+ reason: moduleArgs.reason
40685
+ }), moduleArgs);
40264
40686
  return text ?? "Error: Rust module returned an invalid ctx_memory response.";
40265
40687
  } catch (error) {
40266
40688
  if (isRustAuthorityDrainingError(error)) {
@@ -40539,11 +40961,21 @@ function createCtxMemoryTool(deps) {
40539
40961
  runImmediateTransaction(deps.db, () => {
40540
40962
  for (const target of targets) {
40541
40963
  archiveMemory(deps.db, target.memoryId, args.reason);
40542
- queueMemoryMutation(deps.db, {
40543
- projectPath: target.projectIdentity,
40544
- mutationType: "archive",
40545
- targetMemoryId: target.memoryId
40546
- });
40964
+ if (toolContext.agent === DREAMER_AGENT && curatePreflight.successor) {
40965
+ supersededMemory(deps.db, target.memoryId, curatePreflight.successor.id);
40966
+ queueMemoryMutation(deps.db, {
40967
+ projectPath: target.projectIdentity,
40968
+ mutationType: "superseded",
40969
+ targetMemoryId: target.memoryId,
40970
+ supersededById: curatePreflight.successor.id
40971
+ });
40972
+ } else {
40973
+ queueMemoryMutation(deps.db, {
40974
+ projectPath: target.projectIdentity,
40975
+ mutationType: "archive",
40976
+ targetMemoryId: target.memoryId
40977
+ });
40978
+ }
40547
40979
  }
40548
40980
  });
40549
40981
  requestRustMemorySync(deps, toolContext.sessionID);
@@ -41578,11 +42010,16 @@ function createCtxReduceTool(deps) {
41578
42010
  const protectedTagIds = activeTags.map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0, deps.protectedTags);
41579
42011
  const protectedSet = new Set(protectedTagIds);
41580
42012
  const tagStatusMap = new Map(allTags.map((tag) => [tag.tagNumber, tag.status]));
42013
+ const inertWhitespaceTagNumbers = new Set(getInertWhitespaceAssistantTags(deps.db, sessionId).map((tag) => tag.tagNumber));
42014
+ const inertDropIds = [
42015
+ ...new Set(dropIds.filter((id) => inertWhitespaceTagNumbers.has(id)))
42016
+ ];
42017
+ const inertNote = inertDropIds.length > 0 ? `Skipped: ${inertDropIds.map((id) => `§${id}§ is provider framing, nothing to reclaim`).join("; ")}.` : "";
41581
42018
  const pendingOps = getPendingOps(deps.db, sessionId);
41582
42019
  const pendingMap = new Map(pendingOps.map((op) => [op.tagId, op.operation]));
41583
42020
  const conflicts = [];
41584
42021
  for (const id of dropIds) {
41585
- if (tagStatusMap.get(id) === "compacted") {
42022
+ if (tagStatusMap.get(id) === "compacted" && !inertWhitespaceTagNumbers.has(id)) {
41586
42023
  conflicts.push(`§${id}§ is from before compaction`);
41587
42024
  }
41588
42025
  }
@@ -41590,10 +42027,13 @@ function createCtxReduceTool(deps) {
41590
42027
  return `Error: Conflicting operations — ${conflicts.join("; ")}.`;
41591
42028
  }
41592
42029
  const preFilterDropCount = dropIds.length;
41593
- dropIds = dropIds.filter((id) => tagStatusMap.get(id) !== "dropped" && pendingMap.get(id) !== "drop");
42030
+ dropIds = dropIds.filter((id) => !inertWhitespaceTagNumbers.has(id) && tagStatusMap.get(id) !== "dropped" && pendingMap.get(id) !== "drop");
41594
42031
  const skippedCount = preFilterDropCount - dropIds.length;
41595
42032
  if (dropIds.length === 0) {
41596
- return "All requested tags were already queued or processed. No new action is needed.";
42033
+ return [
42034
+ inertNote,
42035
+ "All requested tags were already queued or processed. No new action is needed."
42036
+ ].filter(Boolean).join(" ");
41597
42037
  }
41598
42038
  try {
41599
42039
  deps.db.transaction(() => {
@@ -41616,7 +42056,7 @@ function createCtxReduceTool(deps) {
41616
42056
  parts.push(`drop ${formatIds(immediateDropIds)}`);
41617
42057
  if (deferredDropIds.length > 0)
41618
42058
  parts.push(`deferred drop ${formatIds(deferredDropIds)}`);
41619
- return `Queued: ${parts.join(", ")}.${skippedNote}`;
42059
+ return `Queued: ${parts.join(", ")}.${skippedNote}${inertNote ? ` ${inertNote}` : ""}`;
41620
42060
  }
41621
42061
  });
41622
42062
  }
@@ -42163,6 +42603,7 @@ var server = async (ctx) => {
42163
42603
  });
42164
42604
  const loadedPluginConfig = loadPluginConfigDetailed(ctx.directory);
42165
42605
  const pluginConfig = loadedPluginConfig.config;
42606
+ reloadWindowOverlay(pluginConfig.models?.window_overlay_path);
42166
42607
  const promptSurfaceRuntime = createPromptSurfaceRuntime({
42167
42608
  harness: "opencode",
42168
42609
  directory: ctx.directory,
@@ -42227,7 +42668,9 @@ var server = async (ctx) => {
42227
42668
  }
42228
42669
  }
42229
42670
  const liveSessionState = createLiveSessionState();
42230
- const rustModeModuleClient = pluginConfig.transform_mode === "rust" ? new SubcModuleTransport(pluginConfig.subc?.connection_file) : undefined;
42671
+ const rustModeModuleTransport = pluginConfig.transform_mode === "rust" ? new SubcModuleTransport(pluginConfig.subc?.connection_file) : undefined;
42672
+ const rustModeModuleClient = rustModeModuleTransport;
42673
+ const sessionCleanupModuleClient = pluginConfig.enabled ? rustModeModuleTransport ?? new SubcModuleTransport(pluginConfig.subc?.connection_file) : undefined;
42231
42674
  const hooks = await createSessionHooksAsync({
42232
42675
  ctx,
42233
42676
  pluginConfig,
@@ -42367,7 +42810,8 @@ var server = async (ctx) => {
42367
42810
  liveSessionState.dreamerProgressByProject.delete(timerProjectIdentity);
42368
42811
  }
42369
42812
  },
42370
- moduleClient: classifyModuleClient
42813
+ moduleClient: classifyModuleClient,
42814
+ sessionCleanupModuleClient
42371
42815
  };
42372
42816
  try {
42373
42817
  stopDreamTimerRegistration = await startDreamScheduleTimer(timerRegistration);