@elvisw/dsh-magic-context 0.0.0-stage → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/LICENSE +23 -0
  2. package/NOTICE +93 -0
  3. package/README.md +501 -2
  4. package/README.zh-CN.md +398 -0
  5. package/cordis.patch.yml +20 -0
  6. package/lib/client.js +2753 -0
  7. package/lib/core/compartments.js +424 -0
  8. package/lib/core/corpus-store.js +176 -0
  9. package/lib/core/directive-detect.js +147 -0
  10. package/lib/core/domain-reconcile.js +394 -0
  11. package/lib/core/domains.js +127 -0
  12. package/lib/core/embedding-local.js +247 -0
  13. package/lib/core/global-autostage.js +223 -0
  14. package/lib/core/global-domains.js +302 -0
  15. package/lib/core/journal.js +248 -0
  16. package/lib/core/llm-deadline.js +53 -0
  17. package/lib/core/machine-domain.js +106 -0
  18. package/lib/core/maintenance.js +380 -0
  19. package/lib/core/memory-bridge.js +818 -0
  20. package/lib/core/memory-categories.js +84 -0
  21. package/lib/core/memory-scoring.js +65 -0
  22. package/lib/core/memory-search.js +414 -0
  23. package/lib/core/memory-write.js +1176 -0
  24. package/lib/core/policy.js +111 -0
  25. package/lib/core/profile-confirmation.js +55 -0
  26. package/lib/core/profile-review.js +375 -0
  27. package/lib/core/profile-write.js +283 -0
  28. package/lib/core/reference-seeds.js +63 -0
  29. package/lib/core/route-health.js +106 -0
  30. package/lib/core/stores.js +1021 -0
  31. package/lib/core/tagger.js +7 -0
  32. package/lib/core/types.js +15 -0
  33. package/lib/core/vector-backfill.js +140 -0
  34. package/lib/core/vector-lane.js +184 -0
  35. package/lib/dsh/command-surface.js +376 -0
  36. package/lib/dsh/compaction-engine.js +231 -0
  37. package/lib/dsh/config-switches.js +56 -0
  38. package/lib/dsh/config.js +13 -0
  39. package/lib/dsh/context-pressure.js +36 -0
  40. package/lib/dsh/drop-landing.js +42 -0
  41. package/lib/dsh/emergency.js +89 -0
  42. package/lib/dsh/flush-command.js +148 -0
  43. package/lib/dsh/global-review.js +412 -0
  44. package/lib/dsh/historian.js +907 -0
  45. package/lib/dsh/live-journal.js +81 -0
  46. package/lib/dsh/maintenance-wiring.js +229 -0
  47. package/lib/dsh/management-pass.js +564 -0
  48. package/lib/dsh/memory-lane.js +535 -0
  49. package/lib/dsh/pre-step.js +675 -0
  50. package/lib/dsh/project-drift.js +116 -0
  51. package/lib/dsh/recomp.js +192 -0
  52. package/lib/dsh/reduce-guidance.js +188 -0
  53. package/lib/dsh/seams.js +588 -0
  54. package/lib/dsh/search-hint.js +186 -0
  55. package/lib/dsh/session-notice.js +100 -0
  56. package/lib/dsh/status-command.js +75 -0
  57. package/lib/dsh/telemetry.js +349 -0
  58. package/lib/dsh/tool-bridge.js +1009 -0
  59. package/lib/dsh/volatile.js +89 -0
  60. package/lib/index.js +931 -0
  61. package/package.json +108 -4
  62. package/tools/migrate-profile-package-name.mjs +317 -0
  63. package/vendor/onnx/LICENSE +27 -0
  64. package/vendor/onnx/ort-wasm-simd-threaded.mjs +59 -0
  65. package/vendor/onnx/ort-wasm-simd-threaded.wasm +0 -0
  66. package/vendor/onnx/ort.node.min.mjs +8 -0
@@ -0,0 +1,564 @@
1
+ /**
2
+ * The management pass — everything the adapter does inside the execute band,
3
+ * in one place, in Magic Context's order:
4
+ *
5
+ * 1. land queued ctx_reduce marks as `[dropped §N§]` placeholders;
6
+ * 2. publish historian compartments as `[compacted §C<n>§]` range
7
+ * replacements (the digest itself travels in the baseline node);
8
+ * 3. force reclaim when pressure reaches the force band;
9
+ * 4. refresh the m[0]/m[1] baseline node — but ONLY when its rendered
10
+ * content actually changed (a no-op pass must not bust the cache);
11
+ * 5. fire the historian out of band (it writes a store row; nothing on the
12
+ * wire changes because of it).
13
+ *
14
+ * All surface work happens here and only here: the execute band is the one
15
+ * bust permission, and every step above is a durable mutation that spends it.
16
+ */
17
+ import { compactedPlaceholder, estimateTokens, findBaselineRange, renderBaseline, renderMemoryUpdates, stripBaseline, } from "../core/compartments.js";
18
+ import { renderMemoryLine } from "../core/memory-bridge.js";
19
+ import { errText } from "../core/types.js";
20
+ import { reclaimForEmergency } from "./emergency.js";
21
+ import { runHistorian } from "./historian.js";
22
+ import { landPendingDrops } from "./drop-landing.js";
23
+ import { enumerateSurfaceNodes, enumerateSurfaceToolResults, magicContextSource, pairingClosedWindow, replaceSurfaceRange, syntheticNodeId, wireStrandedResults, } from "./seams.js";
24
+ /** Estimated tokens of every current surface node (shared with the emergency
25
+ * lane in index.ts — the #731 fit guard needs the whole-surface estimate). */
26
+ export function surfaceTokens(session) {
27
+ return enumerateSurfaceNodes(session).reduce((total, node) => total + (node.hasText ? estimateTokens(node.text) : 0), 0);
28
+ }
29
+ /** The baseline block currently carried by a node, when it has one. */
30
+ function baselineBlockOf(text) {
31
+ const range = findBaselineRange(text);
32
+ return range === undefined ? undefined : text.slice(range.start, range.end);
33
+ }
34
+ /** Compose the baseline block ahead of the node's own (baseline-free) content. */
35
+ function composeBaselineContent(node, block) {
36
+ const stripped = stripBaseline(node.text);
37
+ const content = [{ type: 'text', text: block }];
38
+ if (stripped.length > 0)
39
+ content.push({ type: 'text', text: stripped });
40
+ return content;
41
+ }
42
+ /** Split stored compartments into the frozen m[0] set and the m[1] delta. */
43
+ export function splitBaselineSets(compartments, state) {
44
+ if (state === undefined)
45
+ return { baseline: [...compartments], delta: [] };
46
+ return {
47
+ baseline: compartments.filter(compartment => compartment.createdAt <= state.frozenAt),
48
+ delta: compartments.filter(compartment => compartment.createdAt > state.frozenAt),
49
+ };
50
+ }
51
+ function publishCompartments(input) {
52
+ const published = [];
53
+ let factsPromoted = 0;
54
+ let factsSeenAgain = 0;
55
+ const baselineNodeSeq = input.stores.baselineState(input.sessionId)?.nodeSeq;
56
+ for (const compartment of input.stores.listUnlandedCompartments(input.sessionId)) {
57
+ const covered = new Set(compartment.coveredSeqs);
58
+ // The host shadows every node in the POSITIONAL range between the two
59
+ // endpoints, not the subset the compartment covered
60
+ // (`host: packages/core/session/src/surface.ts:433`, asserted at `:363`).
61
+ // Citing only the covered subset threw on the first node that had entered
62
+ // the range since the compartment was drafted, and that threw the whole
63
+ // pass — every pass, so nothing ever landed. Enumerate the live surface here
64
+ // (the snapshot at the call site predates the previous replacement) and cite
65
+ // the whole range.
66
+ const live = enumerateSurfaceNodes(input.session);
67
+ const positions = [];
68
+ live.forEach((node, index) => {
69
+ if (covered.has(node.seq))
70
+ positions.push(index);
71
+ });
72
+ if (positions.length === 0 || (baselineNodeSeq !== undefined && covered.has(baselineNodeSeq))) {
73
+ input.stores.markCompartmentLanded(input.sessionId, compartment.compartmentId, 0);
74
+ continue;
75
+ }
76
+ const rawFirst = positions[0];
77
+ const rawLast = positions[positions.length - 1];
78
+ // Drop placeholders re-sequence a result to the surface tail while its
79
+ // tool_use stays inside the covered span; a range that cuts between them
80
+ // strands the result and the next provider request fails validation
81
+ // (live 2026-10-09). Widen to tool-pairing closure before cutting.
82
+ const span = pairingClosedWindow(live, rawFirst, rawLast);
83
+ const range = live.slice(span.first, span.last + 1);
84
+ // m[0] is frozen at the head of the surface: folding it into a compartment
85
+ // would leave the next refresh rewriting an unrelated `user` node as the
86
+ // baseline. Defer instead; the guard clears once the surface moves on.
87
+ if (baselineNodeSeq !== undefined && range.some(node => node.seq === baselineNodeSeq)) {
88
+ input.journal?.warn('compartment-published', `compartment §C${String(compartment.compartmentId)}§ deferred: its range now contains the m[0] baseline node`, {
89
+ compartmentId: compartment.compartmentId,
90
+ reason: 'baseline-in-range',
91
+ nodes: range.length,
92
+ }, input.sessionId);
93
+ continue;
94
+ }
95
+ // NOTE (2026-10-16): the fusion-v7 publish guard stood here (observe /
96
+ // enforce over drift + no-gain verdicts). RETIRED by user decision: across
97
+ // its whole observation window (2026-10-09 commit 96337e00b → 2026-10-16)
98
+ // the journal recorded 0 `compartment-guard` hits against 58 clean
99
+ // `compartment-published` rows, so even `enforce` would never have acted —
100
+ // zero benefit, one more way to hold up a publish. The positional range +
101
+ // pairing closure above remains the whole landing discipline.
102
+ const first = range[0];
103
+ const last = range[range.length - 1];
104
+ // A host validator refusal here (2026-10-08 live incident: the pre-fix
105
+ // shadow list missed range entrants, 69 journal errors across 2 sessions —
106
+ // the same poisoned draft threw on EVERY pass) must be terminal for THIS
107
+ // draft, never a retry loop, and must never starve the drafts behind it:
108
+ // retire it as rejected (covered claims stay claimed, no re-digest flood;
109
+ // the span stays on the wire, honestly) and let the loop move on.
110
+ let landedSeq;
111
+ try {
112
+ landedSeq = replaceSurfaceRange(input.session, first.seq, last.seq, [{ type: 'text', text: compactedPlaceholder(compartment.compartmentId) }], range.map(node => node.seq), syntheticNodeId(`mc-compacted-${String(compartment.compartmentId)}`), magicContextSource(`Magic Context · compartment §C${String(compartment.compartmentId)}§ replaced ${String(range.length)} node(s) on the wire`));
113
+ }
114
+ catch (error) {
115
+ input.stores.markCompartmentRejected(input.sessionId, compartment.compartmentId, 'replace-failed');
116
+ input.journal?.error('compartment-published', `compartment §C${String(compartment.compartmentId)}§ replace refused; retired as rejected (span stays on the wire)`, {
117
+ compartmentId: compartment.compartmentId,
118
+ reason: 'replace-failed',
119
+ nodes: range.length,
120
+ error: errText(error),
121
+ }, input.sessionId);
122
+ continue;
123
+ }
124
+ input.stores.markCompartmentLanded(input.sessionId, compartment.compartmentId, landedSeq);
125
+ published.push(compartment.compartmentId);
126
+ input.journal?.info('compartment-published', `compartment §C${String(compartment.compartmentId)}§ replaced ${String(range.length)} nodes on the wire`, {
127
+ compartmentId: compartment.compartmentId,
128
+ nodes: range.length,
129
+ coveredTokens: compartment.coveredTokens,
130
+ estTokensReclaimed: range.reduce((total, node) => total + estimateTokens(node.text), 0),
131
+ nodeSeq: landedSeq,
132
+ importance: compartment.importance,
133
+ ...((span.first !== rawFirst || span.last !== rawLast)
134
+ ? { pairingClosure: { rawFirst, rawLast, first: span.first, last: span.last } }
135
+ : {}),
136
+ }, input.sessionId);
137
+ // Magic Context promotes the compartment's facts in the same publish step
138
+ // (`compartment-runner-incremental.ts:1077`). Ours is not one SQLite
139
+ // transaction (the compartment store and the memory store are different
140
+ // databases and different owners), so a failure is journaled and the facts
141
+ // stay durably in the compartment row for a later retry — reclaim must not
142
+ // stall on a memory problem.
143
+ if (input.memoryPromote !== undefined && input.memoryIdentity !== undefined && compartment.facts.length > 0) {
144
+ try {
145
+ const outcome = input.memoryPromote({
146
+ identity: input.memoryIdentity,
147
+ facts: compartment.facts,
148
+ sessionId: input.sessionId,
149
+ });
150
+ factsPromoted += outcome.inserted.length;
151
+ factsSeenAgain += outcome.seenAgain.length;
152
+ input.journal?.info('memory-promote', `promoted ${String(outcome.inserted.length)} fact(s) into project memory`
153
+ + `${outcome.seenAgain.length > 0 ? `, ${String(outcome.seenAgain.length)} already known` : ''}`, {
154
+ compartmentId: compartment.compartmentId,
155
+ identity: input.memoryIdentity,
156
+ inserted: outcome.inserted.map(row => ({ id: row.id, category: row.category })),
157
+ seenAgain: outcome.seenAgain.map(row => row.id),
158
+ skipped: outcome.skipped,
159
+ }, input.sessionId);
160
+ }
161
+ catch (error) {
162
+ const detail = errText(error);
163
+ input.journal?.error('error', `memory promotion failed: ${detail}`, {
164
+ scope: 'memory-promote',
165
+ detail,
166
+ compartmentId: compartment.compartmentId,
167
+ }, input.sessionId);
168
+ }
169
+ }
170
+ }
171
+ return { ids: published, factsPromoted, factsSeenAgain };
172
+ }
173
+ /**
174
+ * Publish ready compartments OUTSIDE a management pass — the provider-overflow
175
+ * path (index.ts `agent/request-error`) lands the bank before it drops tool
176
+ * results: one replacement reclaims a whole digested span, an emergency drop
177
+ * only individual tool results, and the request has already failed once, so
178
+ * the bust cannot lose more than the failed retry did.
179
+ *
180
+ * The request-error lane is a forced execute band in everything but name —
181
+ * the same exception family as the wire-repair iron rule (the provider
182
+ * rejected the wire as-is). Same surface discipline as the pass's publish
183
+ * lane (pairing closure, m[0]-baseline defer). NO baseline refresh and NO
184
+ * fact promotion here: both belong to the next execute pass, the facts stay
185
+ * durable in the compartment row, and the promotion retry is idempotent
186
+ * (seen-again dedup).
187
+ */
188
+ export function publishReadyCompartments(input) {
189
+ return { ids: publishCompartments(input).ids };
190
+ }
191
+ /**
192
+ * Refresh the baseline node: the first current `user` node carries the
193
+ * m[0]/m[1] block ahead of its own content. Returns whether the surface
194
+ * changed. A HARD rebuild (no state yet, route change, memory-digest change)
195
+ * freezes m[0] again and empties the delta; otherwise the delta grows.
196
+ */
197
+ function refreshBaseline(input, nodes) {
198
+ const now = input.now?.() ?? Date.now();
199
+ const compartments = input.stores.listCompartments(input.sessionId);
200
+ const memories = input.memories;
201
+ const profile = input.profile;
202
+ const updates = input.memoryUpdates;
203
+ const previous = input.stores.baselineState(input.sessionId);
204
+ const routeChanged = previous !== undefined
205
+ && (previous.provider !== input.route?.provider || previous.model !== input.route?.model);
206
+ const memoriesChanged = previous !== undefined
207
+ && previous.memoriesDigest !== undefined
208
+ && memories !== undefined
209
+ && previous.memoriesDigest !== memories.digest;
210
+ // The external-writer signal: Magic Context bumps
211
+ // `project_state.project_memory_epoch` when an external edit (or a domain
212
+ // /identity change) invalidates a running session's snapshot. Both the m[0]
213
+ // pool AND the mutation cursor were built against the old epoch, so this is
214
+ // a HARD rebuild — reusing the old cursor would render a misaligned delta.
215
+ const epochChanged = previous !== undefined
216
+ && previous.projectEpochFingerprint !== undefined
217
+ && updates?.projectEpochFingerprint !== undefined
218
+ && previous.projectEpochFingerprint !== updates.projectEpochFingerprint;
219
+ // One-time upgrade fold: a baseline written before the cursor column existed
220
+ // cannot order its delta, so the first pass that actually has pending change
221
+ // rows folds them into m[0] and stamps the cursor instead of rendering a
222
+ // delta read from zero.
223
+ const cursorUpgrade = previous !== undefined
224
+ && previous.maxMemoryMutationId === undefined
225
+ && updates !== undefined
226
+ && updates.updates.length > 0;
227
+ // WP8: the profile's fold trigger. An entry ABOVE the frozen watermark is an
228
+ // addition and rides m[1] without touching the prefix. A row AT or below it
229
+ // being rewritten or dismissed changes the digest — and here, unlike Magic
230
+ // Context whose version bump deliberately never folded m[0], the stale block
231
+ // is rebuilt: a profile the agent keeps reading after it was retracted is
232
+ // worse than one cache bust. The `''` sentinel recorded by an emptying fold
233
+ // keeps a switched-off profile from rebuilding on every later pass.
234
+ const profileChanged = previous !== undefined && (profile === undefined
235
+ ? previous.profileM0Digest !== undefined && previous.profileM0Digest !== ''
236
+ : previous.profileM0Digest === undefined
237
+ ? true
238
+ : previous.profileM0Digest !== profile.digest);
239
+ const hard = previous === undefined || routeChanged || memoriesChanged || epochChanged || cursorUpgrade || profileChanged;
240
+ // Freeze targets for a HARD fold. Falling back to the previous values keeps a
241
+ // pass that has no mutation lane (bridge unavailable) from rewinding them.
242
+ const frozenCursor = updates?.cursor ?? previous?.maxMemoryMutationId;
243
+ const frozenEpochFingerprint = updates?.projectEpochFingerprint ?? previous?.projectEpochFingerprint;
244
+ const state = hard
245
+ ? {
246
+ sessionId: input.sessionId,
247
+ epoch: (previous?.epoch ?? 0) + 1,
248
+ ...previous?.nodeSeq === undefined ? {} : { nodeSeq: previous.nodeSeq },
249
+ ...input.route === undefined ? {} : { provider: input.route.provider, model: input.route.model },
250
+ frozenAt: now,
251
+ ...memories === undefined ? {} : { memoriesDigest: memories.digest },
252
+ // The watermark moves only on a HARD rebuild: everything written since
253
+ // the previous fold becomes part of the frozen m[0] pool here.
254
+ ...memories?.maxMemoryId === undefined ? {} : { maxMemoryId: memories.maxMemoryId },
255
+ // WP8: the profile freeze. The digest recorded is the FOLD digest (every
256
+ // active row), because this fold renders the whole profile and the next
257
+ // pass's pool is exactly that set — recording the pool digest here would
258
+ // buy a second, useless rebuild.
259
+ profileM0Digest: profile?.foldDigest ?? '',
260
+ ...profile === undefined
261
+ ? previous?.profileWatermark === undefined ? {} : { profileWatermark: previous.profileWatermark }
262
+ : { profileWatermark: profile.currentMaxId },
263
+ // Same rule for the mutation cursor (§7.5 step 4): folding m[0] bakes the
264
+ // pending updates in, so the `<memory-updates>` block disappears.
265
+ ...frozenCursor === undefined ? {} : { maxMemoryMutationId: frozenCursor },
266
+ ...frozenEpochFingerprint === undefined ? {} : { projectEpochFingerprint: frozenEpochFingerprint },
267
+ updatedAt: now,
268
+ }
269
+ : { ...previous, updatedAt: now };
270
+ const sets = hard
271
+ ? { baseline: compartments, delta: [] }
272
+ : splitBaselineSets(compartments, previous);
273
+ // A1 (MC inject-compartments.ts:2822-2869): collect the forced ids first,
274
+ // render their lines from the eligible rows, and hand both to renderBaseline.
275
+ const forcedRender = renderMemoryUpdates(hard ? [] : updates?.updates ?? [], {
276
+ ...memories?.newMemoryIds === undefined ? {} : { renderedIds: new Set(memories.newMemoryIds) },
277
+ ...memories?.baselineIds === undefined ? {} : { baselineIds: new Set(memories.baselineIds) },
278
+ ...memories?.eligibleIds === undefined ? {} : { eligibleIds: new Set(memories.eligibleIds) },
279
+ });
280
+ const forcedRowsById = new Set((memories?.eligibleRows ?? []).map(row => row.id));
281
+ const forcedMemoryLines = forcedRender.forcedMemoryIds
282
+ .filter(id => forcedRowsById.has(id))
283
+ .map(id => renderMemoryLine((memories?.eligibleRows ?? []).find(row => row.id === id)));
284
+ // WP8: a HARD fold swallows the profile delta (the whole profile becomes
285
+ // m[0]); a soft pass renders the pool in m[0] and the new entries in m[1].
286
+ const profilePoolLines = hard
287
+ ? [...(profile?.lines ?? []), ...(profile?.newLines ?? [])]
288
+ : profile?.lines ?? [];
289
+ const profileDeltaLines = hard ? [] : profile?.newLines ?? [];
290
+ const block = renderBaseline({
291
+ now,
292
+ baseline: sets.baseline,
293
+ delta: sets.delta,
294
+ memories: memories?.lines ?? [],
295
+ ...profilePoolLines.length === 0 ? {} : { profileLines: profilePoolLines },
296
+ ...profileDeltaLines.length === 0 ? {} : { newProfileLines: profileDeltaLines },
297
+ ...memories?.newLines === undefined ? {} : { newMemories: memories.newLines },
298
+ ...memories?.newMemoryIds === undefined ? {} : { newMemoryIds: memories.newMemoryIds },
299
+ ...memories?.baselineIds === undefined ? {} : { baselineIds: memories.baselineIds },
300
+ ...memories?.eligibleIds === undefined ? {} : { eligibleIds: memories.eligibleIds },
301
+ ...(forcedMemoryLines.length > 0 ? { forcedMemoryLines } : {}),
302
+ // A HARD fold swallows the delta: the changes are part of the rebuilt m[0].
303
+ // Otherwise the pending (post-cursor) changes render in m[1].
304
+ ...hard || updates === undefined ? {} : { memoryUpdates: updates.updates },
305
+ budgetTokens: input.policy.baselineTokens,
306
+ });
307
+ if (block.length === 0)
308
+ return false;
309
+ // The baseline rides the first ordinary user node — never a compaction
310
+ // checkpoint (host or engine). Attaching it to a checkpoint let that
311
+ // checkpoint's own text ride inside m[0] forever: every later refresh
312
+ // re-emitted the foreign summary and rewrote the node, and the GUI lost the
313
+ // card it correlates by `source.kind === 'compact-checkpoint'` + compactionId.
314
+ const target = nodes.find(node => node.role === 'user' && node.sourceKind !== 'compact-checkpoint');
315
+ if (target === undefined)
316
+ return false;
317
+ if (baselineBlockOf(target.text) === block) {
318
+ // Byte-identical: no durable mutation, no priced bust (MC's defer pass).
319
+ if (state.nodeSeq !== target.seq || state.updatedAt !== previous?.updatedAt) {
320
+ input.stores.saveBaselineState({
321
+ ...state,
322
+ nodeSeq: target.seq,
323
+ ...input.route === undefined ? {} : { provider: input.route.provider, model: input.route.model },
324
+ ...memories === undefined ? {} : { memoriesDigest: memories.digest },
325
+ });
326
+ }
327
+ return false;
328
+ }
329
+ const landedSeq = replaceSurfaceRange(input.session, target.seq, target.seq, composeBaselineContent(target, block), [target.seq], syntheticNodeId('mc-baseline'), magicContextSource(`Magic Context · baseline ${hard ? 'HARD rebuild' : 'soft refresh'} `
330
+ + `(m[0] ${String(sets.baseline.length)} · m[1] ${String(sets.delta.length)} · ${String(memories?.lines.length ?? 0)} memories)`));
331
+ input.stores.saveBaselineState({
332
+ ...state,
333
+ nodeSeq: landedSeq,
334
+ ...input.route === undefined ? {} : { provider: input.route.provider, model: input.route.model },
335
+ ...memories === undefined ? {} : { memoriesDigest: memories.digest },
336
+ });
337
+ input.journal?.info('baseline', `baseline ${hard ? 'HARD rebuild' : 'soft refresh'} (epoch ${String(state.epoch)})`, {
338
+ mode: hard ? 'hard' : 'soft',
339
+ epoch: state.epoch,
340
+ nodeSeq: landedSeq,
341
+ compartments: { baseline: sets.baseline.length, delta: sets.delta.length },
342
+ memories: { rows: memories?.lines.length ?? 0, digest: memories?.digest.slice(0, 12) },
343
+ profile: {
344
+ rows: profile?.lines.length ?? 0,
345
+ newRows: hard ? 0 : profile?.newLines.length ?? 0,
346
+ digest: profile?.digest.slice(0, 12),
347
+ watermark: state.profileWatermark,
348
+ },
349
+ memoryUpdates: {
350
+ pending: updates?.updates.length ?? 0,
351
+ rendered: hard ? 0 : updates?.updates.length ?? 0,
352
+ cursor: state.maxMemoryMutationId,
353
+ epochFingerprint: state.projectEpochFingerprint,
354
+ },
355
+ bytes: block.length,
356
+ estTokens: estimateTokens(block),
357
+ ...memoriesChanged ? { reason: 'memory-digest-changed' } : {},
358
+ ...profileChanged ? { reason: 'profile-digest-changed' } : {},
359
+ ...routeChanged ? { reason: 'route-changed' } : {},
360
+ ...epochChanged ? { reason: 'project-memory-epoch-changed' } : {},
361
+ ...cursorUpgrade ? { reason: 'mutation-cursor-upgrade' } : {},
362
+ ...previous === undefined ? { reason: 'first-baseline' } : {},
363
+ }, input.sessionId);
364
+ return true;
365
+ }
366
+ /** Upper bound of wire repairs per pass, so a pathological surface cannot monopolize the bust. */
367
+ const WIRE_REPAIR_CAP = 16;
368
+ /**
369
+ * The iron-rule repair: sweep live `tool/result` nodes whose tool_use left
370
+ * the wire. One of them 400s every strict-provider request — the only adapter
371
+ * failure mode that can take the whole conversation down — so they are
372
+ * replaced with a tiny user note no matter what else this pass does. The
373
+ * original content stays recoverable in the event log (the replacement cites
374
+ * it); only the wire copy goes.
375
+ * @returns the number of results swept this pass.
376
+ */
377
+ function repairWirePairing(input) {
378
+ const stranded = wireStrandedResults(input.session);
379
+ if (stranded.length === 0)
380
+ return 0;
381
+ const sweep = stranded.slice(0, WIRE_REPAIR_CAP);
382
+ for (const defect of sweep) {
383
+ replaceSurfaceRange(input.session, defect.seq, defect.seq, [{ type: 'text', text: '[magic-context] removed a tool result whose call left the wire' }], [defect.seq], syntheticNodeId(`mc-wire-repair-${String(defect.seq)}`), magicContextSource(`Magic Context · wire repair · removed stranded tool result ${String(defect.seq)}`));
384
+ }
385
+ input.journal?.warn('wire-repair', `swept ${String(sweep.length)} stranded tool result(s) off the wire`, {
386
+ swept: sweep.length,
387
+ seqs: sweep.map(defect => defect.seq),
388
+ callIds: sweep.map(defect => defect.callId),
389
+ remaining: stranded.length - sweep.length,
390
+ }, input.sessionId);
391
+ return sweep.length;
392
+ }
393
+ /**
394
+ * Run the management pass. Every step is independently guarded: a failure in
395
+ * one must not wedge the turn (MC per-turn fail-open), and the next pass
396
+ * re-decides from durable state.
397
+ */
398
+ export function runManagementPass(input) {
399
+ const landedDrops = [];
400
+ let wireRepaired = 0;
401
+ const tokensBefore = surfaceTokens(input.session);
402
+ const result = {
403
+ landedDrops,
404
+ published: [],
405
+ forceDropped: 0,
406
+ wireRepaired: 0,
407
+ baselineUpdated: false,
408
+ historianStarted: false,
409
+ tokensBefore,
410
+ tokensAfter: tokensBefore,
411
+ factsPromoted: 0,
412
+ factsSeenAgain: 0,
413
+ };
414
+ // 1. Queued ctx_reduce marks.
415
+ let forceToolNodes = [];
416
+ try {
417
+ // Snapshot before landing: the force band historically reclaims from the
418
+ // pre-landing surface (and must not start dropping the placeholders it
419
+ // just created). Flush has already landed before this pass, so the
420
+ // snapshot is naturally post-flush for that path.
421
+ forceToolNodes = enumerateSurfaceToolResults(input.session, callId => input.stores.getTag(input.sessionId, { kind: 'tool-result', callId }));
422
+ const landing = landPendingDrops({
423
+ session: input.session,
424
+ sessionId: input.sessionId,
425
+ stores: input.stores,
426
+ tags: input.stores.listPendingDrops(input.sessionId),
427
+ journal: input.journal,
428
+ // Record each replacement before the durable write. If a later tag
429
+ // throws, the pass result and error logs still name what the surface
430
+ // already lost.
431
+ onReplaced: tag => {
432
+ landedDrops.push(tag);
433
+ },
434
+ });
435
+ // Filter the force-band snapshot by the tags this pass touched; their old
436
+ // seqs are shadowed by the placeholder replacement and must not be handed
437
+ // to reclaimForEmergency.
438
+ const touched = new Set([...landing.landed, ...landing.already]);
439
+ forceToolNodes = forceToolNodes.filter(node => node.tagNumber === undefined || !touched.has(node.tagNumber));
440
+ // 1.5 Wire-pairing repair (the iron rule): sweep stranded results off the
441
+ // wire before anything else reads the surface.
442
+ wireRepaired = repairWirePairing(input);
443
+ // 3. Force-band reclaim (before publishing, so a compartment range that
444
+ // lost its content to a drop still replaces the same nodes).
445
+ let forceDropped = 0;
446
+ if (input.pressureStale === true && input.pressure !== undefined && input.pressure >= input.policy.forceThreshold) {
447
+ // The reading says "over the force band" but it was recorded before the
448
+ // surface changed: reclaiming again would drop against a prompt that no
449
+ // longer exists (upstream MC v0.49.49's half of issue 828). Reclaim is
450
+ // irreversible, so it waits for a reading that describes the current wire.
451
+ input.journal?.info('emergency-reclaim', `force band: skipped — the ${input.pressure.toFixed(1)}% reading predates a surface change; no reclaim against a stale measurement`, {
452
+ dropped: 0,
453
+ reason: 'stale-reading',
454
+ tier: 'skipped-stale-reading',
455
+ pressure: input.pressure,
456
+ }, input.sessionId);
457
+ }
458
+ else if (input.pressure !== undefined && input.pressure >= input.policy.forceThreshold) {
459
+ const generationBefore = input.session.surface.replaceGeneration;
460
+ // #731: pass the whole-surface estimate so the drop lane can refuse a
461
+ // reclaim that cannot fit the request even at zero tool results. The
462
+ // node snapshot is the pre-landing one from step 1, preserving the
463
+ // historical force-band set.
464
+ const reclaim = reclaimForEmergency(input.session, forceToolNodes, input.policy, input.contextWindow, surfaceTokens(input.session));
465
+ forceDropped = reclaim.dropped.length;
466
+ if (forceDropped > 0) {
467
+ input.journal?.warn('emergency-reclaim', `force band: dropped ${String(forceDropped)} tool output(s) beyond the protected window`, {
468
+ dropped: forceDropped,
469
+ protected: reclaim.protectedCount,
470
+ reason: 'force-threshold',
471
+ pressure: input.pressure,
472
+ generationBefore,
473
+ generationAfter: input.session.surface.replaceGeneration,
474
+ }, input.sessionId);
475
+ }
476
+ else if (reclaim.skipped === 'cannot-fit') {
477
+ input.journal?.warn('emergency-reclaim', `force band: reclaim skipped — dropping every reclaimable output still leaves ~${String(reclaim.residualTokens)} tokens against a ${String(input.contextWindow)}-token window (#731)`, {
478
+ dropped: 0,
479
+ protected: reclaim.protectedCount,
480
+ reason: 'force-threshold',
481
+ pressure: input.pressure,
482
+ skipped: reclaim.skipped,
483
+ residualTokens: reclaim.residualTokens,
484
+ contextWindow: input.contextWindow,
485
+ generationBefore,
486
+ generationAfter: input.session.surface.replaceGeneration,
487
+ }, input.sessionId);
488
+ }
489
+ }
490
+ // 2 + 4: publish compartments, then refresh the baseline over the result.
491
+ const nodes = enumerateSurfaceNodes(input.session);
492
+ const { ids: published, factsPromoted, factsSeenAgain } = publishCompartments(input);
493
+ const afterPublish = published.length === 0 ? nodes : enumerateSurfaceNodes(input.session);
494
+ const baselineUpdated = refreshBaseline(input, afterPublish);
495
+ // 5. Fire the historian out of band; it only writes a store row.
496
+ let historianStarted = false;
497
+ if (input.historian !== undefined) {
498
+ const deps = {
499
+ stores: input.stores,
500
+ policy: input.policy,
501
+ llm: input.historian.llm,
502
+ logger: input.logger,
503
+ ...input.journal === undefined ? {} : { journal: input.journal },
504
+ ...input.historian.route === undefined ? {} : { route: input.historian.route },
505
+ ...input.historian.now === undefined ? {} : { now: input.historian.now },
506
+ ...input.profileCollect === undefined ? {} : { profileCollect: input.profileCollect },
507
+ };
508
+ void runHistorian(deps, {
509
+ sessionId: input.sessionId,
510
+ session: input.session,
511
+ nodes: enumerateSurfaceNodes(input.session),
512
+ ...input.stores.baselineState(input.sessionId)?.nodeSeq === undefined
513
+ ? {}
514
+ : { baselineNodeSeq: input.stores.baselineState(input.sessionId)?.nodeSeq },
515
+ ...input.contextWindow === undefined ? {} : { contextWindow: input.contextWindow },
516
+ ...input.route === undefined ? {} : { route: input.route },
517
+ }).catch((error) => {
518
+ const why = errText(error);
519
+ input.logger.warn(`magic-context historian failed (continuing): ${why}`);
520
+ // runHistorian journals its own terminal events; this row fires only
521
+ // if the promise itself rejects — a bug in the guard plumbing. Better
522
+ // a phase-less error row than a silent failure.
523
+ input.journal?.error('error', `historian promise rejected: ${why}`, {
524
+ scope: 'historian-unhandled',
525
+ detail: why,
526
+ errorName: error instanceof Error ? error.name : typeof error,
527
+ }, input.sessionId);
528
+ });
529
+ historianStarted = true;
530
+ }
531
+ input.logger.info(`magic-context: execute pass (${input.pass.reason}) pressure=${input.pressure?.toFixed(1) ?? '?'}% `
532
+ + `landed=[${landedDrops.map(tag => `§${String(tag)}§`).join(', ')}] `
533
+ + `published=[${published.map(id => `§C${String(id)}§`).join(', ')}] `
534
+ + `forceDropped=${String(forceDropped)} baseline=${baselineUpdated ? 'refreshed' : 'unchanged'} `
535
+ + `tokens ${String(tokensBefore)}→${String(surfaceTokens(input.session))}`);
536
+ return {
537
+ ...result,
538
+ published,
539
+ forceDropped,
540
+ wireRepaired,
541
+ baselineUpdated,
542
+ historianStarted,
543
+ tokensAfter: surfaceTokens(input.session),
544
+ factsPromoted,
545
+ factsSeenAgain,
546
+ };
547
+ }
548
+ catch (error) {
549
+ // A failed management pass must never wedge the turn: log and continue
550
+ // with the tagged messages (MC per-turn fail-open). The journal row is
551
+ // what makes the failure diagnosable — the logger line alone is why a
552
+ // run of silent failures could survive unnoticed (test-design audit P1-3).
553
+ const why = errText(error);
554
+ input.logger.warn(`magic-context: management pass failed (continuing): ${why}`);
555
+ input.journal?.error('error', `management pass failed: ${why}`, {
556
+ scope: 'management-pass',
557
+ detail: why,
558
+ errorName: error instanceof Error ? error.name : typeof error,
559
+ pressure: input.pressure,
560
+ trigger: input.pass.reason,
561
+ }, input.sessionId);
562
+ return result;
563
+ }
564
+ }