@elvisw/dsh-magic-context 0.0.0-bootstrap.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/NOTICE +93 -0
- package/README.md +453 -0
- package/README.zh-CN.md +368 -0
- package/cordis.patch.yml +20 -0
- package/lib/client.js +2270 -0
- package/lib/core/compartments.js +424 -0
- package/lib/core/corpus-store.js +176 -0
- package/lib/core/directive-detect.js +147 -0
- package/lib/core/domain-reconcile.js +394 -0
- package/lib/core/domains.js +127 -0
- package/lib/core/embedding-local.js +247 -0
- package/lib/core/global-autostage.js +223 -0
- package/lib/core/global-domains.js +302 -0
- package/lib/core/journal.js +248 -0
- package/lib/core/llm-deadline.js +53 -0
- package/lib/core/machine-domain.js +106 -0
- package/lib/core/maintenance.js +380 -0
- package/lib/core/memory-bridge.js +818 -0
- package/lib/core/memory-categories.js +84 -0
- package/lib/core/memory-scoring.js +65 -0
- package/lib/core/memory-search.js +414 -0
- package/lib/core/memory-write.js +1176 -0
- package/lib/core/policy.js +111 -0
- package/lib/core/profile-confirmation.js +55 -0
- package/lib/core/profile-review.js +375 -0
- package/lib/core/profile-write.js +283 -0
- package/lib/core/reference-seeds.js +63 -0
- package/lib/core/route-health.js +106 -0
- package/lib/core/stores.js +1021 -0
- package/lib/core/tagger.js +7 -0
- package/lib/core/types.js +15 -0
- package/lib/core/vector-backfill.js +140 -0
- package/lib/core/vector-lane.js +184 -0
- package/lib/dsh/command-surface.js +376 -0
- package/lib/dsh/compaction-engine.js +231 -0
- package/lib/dsh/config-switches.js +56 -0
- package/lib/dsh/config.js +13 -0
- package/lib/dsh/context-pressure.js +36 -0
- package/lib/dsh/drop-landing.js +42 -0
- package/lib/dsh/emergency.js +89 -0
- package/lib/dsh/flush-command.js +148 -0
- package/lib/dsh/global-review.js +412 -0
- package/lib/dsh/historian.js +907 -0
- package/lib/dsh/live-journal.js +81 -0
- package/lib/dsh/maintenance-wiring.js +229 -0
- package/lib/dsh/management-pass.js +564 -0
- package/lib/dsh/memory-lane.js +535 -0
- package/lib/dsh/pre-step.js +675 -0
- package/lib/dsh/project-drift.js +116 -0
- package/lib/dsh/recomp.js +192 -0
- package/lib/dsh/reduce-guidance.js +188 -0
- package/lib/dsh/seams.js +588 -0
- package/lib/dsh/search-hint.js +186 -0
- package/lib/dsh/session-notice.js +100 -0
- package/lib/dsh/status-command.js +75 -0
- package/lib/dsh/telemetry.js +349 -0
- package/lib/dsh/tool-bridge.js +1009 -0
- package/lib/dsh/volatile.js +89 -0
- package/lib/index.js +931 -0
- package/package.json +110 -0
- package/tools/migrate-profile-package-name.mjs +317 -0
- package/vendor/onnx/LICENSE +27 -0
- package/vendor/onnx/ort-wasm-simd-threaded.mjs +59 -0
- package/vendor/onnx/ort-wasm-simd-threaded.wasm +0 -0
- 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
|
+
}
|