@cortexkit/opencode-magic-context 0.49.30 → 0.49.32

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 (79) hide show
  1. package/dist/auto-search-worker.js +4 -4
  2. package/dist/chunk-embedding-write-worker.js +1 -1
  3. package/dist/{compartment-chunk-embedding-qgp0tbf7.js → compartment-chunk-embedding-5q5gj67r.js} +1 -1
  4. package/dist/{context-vnfr6d7t.js → context-n27jdyyh.js} +150 -19
  5. package/dist/{embedding-backfill-mms9nb0h.js → embedding-backfill-0jgtqxen.js} +3 -3
  6. package/dist/features/magic-context/chunk-scope-repair-revision.d.ts +4 -0
  7. package/dist/features/magic-context/chunk-scope-repair-revision.d.ts.map +1 -0
  8. package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
  9. package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts +3 -3
  10. package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts.map +1 -1
  11. package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
  12. package/dist/features/magic-context/dreamer/task-gates.d.ts +2 -0
  13. package/dist/features/magic-context/dreamer/task-gates.d.ts.map +1 -1
  14. package/dist/features/magic-context/dreamer/verify.d.ts +13 -17
  15. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  16. package/dist/features/magic-context/message-index-async.d.ts +3 -0
  17. package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
  18. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  19. package/dist/features/magic-context/search.d.ts.map +1 -1
  20. package/dist/features/magic-context/storage-native-replay.d.ts +2 -0
  21. package/dist/features/magic-context/storage-native-replay.d.ts.map +1 -1
  22. package/dist/hooks/magic-context/apply-operations.d.ts +11 -1
  23. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  24. package/dist/hooks/magic-context/auto-search-branch-index.fixture.d.ts +2 -0
  25. package/dist/hooks/magic-context/auto-search-branch-index.fixture.d.ts.map +1 -0
  26. package/dist/hooks/magic-context/auto-search-worker-client.d.ts.map +1 -1
  27. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  28. package/dist/hooks/magic-context/tag-messages.d.ts +2 -0
  29. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  30. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +2 -0
  31. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  32. package/dist/hooks/magic-context/transform.d.ts +11 -0
  33. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  34. package/dist/{index-gxkhzmjq.js → index-2743x98y.js} +65 -2
  35. package/dist/{index-adr4vfys.js → index-2agkcr3y.js} +2 -2
  36. package/dist/{index-ekzzw66a.js → index-3drcgsnf.js} +74 -385
  37. package/dist/index-94ef0qe2.js +429 -0
  38. package/dist/{index-pe6b4bhs.js → index-as2amvn3.js} +1 -1
  39. package/dist/{index-k0dpkyn7.js → index-bzwh06cp.js} +4 -3
  40. package/dist/{index-vzz570j8.js → index-g0kqnc6y.js} +13 -4
  41. package/dist/{index-vtz789ex.js → index-qr2mb02j.js} +16 -5
  42. package/dist/{index-vnn5405n.js → index-rfxzazzy.js} +1 -1
  43. package/dist/{index-69gd2scx.js → index-t6b3px80.js} +2 -2
  44. package/dist/{index-mm93m7y6.js → index-v7287tzq.js} +47 -7
  45. package/dist/index.js +16 -14
  46. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  47. package/dist/{project-embedding-registry-cg281d9j.js → project-embedding-registry-6yex0r9f.js} +2 -2
  48. package/dist/{recomp-orchestrator-w3pjp33d.js → recomp-orchestrator-6ncc6wrw.js} +6 -6
  49. package/dist/{search-fbaz1m8r.js → search-040pac3a.js} +6 -5
  50. package/dist/shared/tag-transcript.d.ts +4 -1
  51. package/dist/shared/tag-transcript.d.ts.map +1 -1
  52. package/dist/shared/transcript.d.ts +3 -1
  53. package/dist/shared/transcript.d.ts.map +1 -1
  54. package/dist/{transform-postprocess-phase-6ccazq85.js → transform-postprocess-phase-wvt1k03d.js} +7 -6
  55. package/dist/v2/auto-search-worker.js +4 -4
  56. package/dist/v2/chunk-embedding-write-worker.js +1 -1
  57. package/dist/v2/{compartment-chunk-embedding-63dzs31c.js → compartment-chunk-embedding-rc31e6md.js} +1 -1
  58. package/dist/v2/{context-wx4sfhgr.js → context-ehews84p.js} +229 -286
  59. package/dist/v2/{embedding-backfill-35k6czrr.js → embedding-backfill-f43hd8y6.js} +3 -3
  60. package/dist/v2/fold/owner.d.ts +4 -0
  61. package/dist/v2/fold/owner.d.ts.map +1 -1
  62. package/dist/v2/hooks/context.d.ts.map +1 -1
  63. package/dist/v2/hooks/types.d.ts +3 -0
  64. package/dist/v2/hooks/types.d.ts.map +1 -1
  65. package/dist/v2/{project-embedding-registry-p1p10ks7.js → project-embedding-registry-1x5qe2fy.js} +2 -2
  66. package/dist/v2/{recomp-orchestrator-wb28kr9k.js → recomp-orchestrator-3r9kcs61.js} +5 -5
  67. package/dist/v2/{search-q3jjw6zp.js → search-q3wkpe8t.js} +5 -5
  68. package/dist/v2/{server-p0snacz3.js → server-2aasmpsp.js} +1 -1
  69. package/dist/v2/{server-k33t88h3.js → server-4n176b5m.js} +4 -3
  70. package/dist/v2/{server-zh3esb0z.js → server-ab8017gw.js} +65 -2
  71. package/dist/v2/{server-4sn3at8p.js → server-h1byrhwx.js} +47 -7
  72. package/dist/v2/{server-42nebt63.js → server-n14edf07.js} +336 -25
  73. package/dist/v2/{server-ph300d6m.js → server-tkdabx8q.js} +1 -1
  74. package/dist/v2/server.js +2 -2
  75. package/dist/v2/{wrapup-orchestrator-2t0pdqzm.js → wrapup-orchestrator-ddmvbkra.js} +5 -5
  76. package/dist/{wrapup-orchestrator-kjmkms5g.js → wrapup-orchestrator-fyvyzcbm.js} +7 -7
  77. package/package.json +1 -1
  78. package/src/shared/tag-transcript.ts +34 -5
  79. package/src/shared/transcript.ts +1 -1
@@ -1,3 +1,31 @@
1
+ import {
2
+ getHarness2
3
+ } from "./server-pavzsdc6.js";
4
+ import {
5
+ getDataDir,
6
+ log2,
7
+ sessionLog2
8
+ } from "./server-6495nmn9.js";
9
+ import {
10
+ inRootAsyncContext2,
11
+ withSqliteBackgroundWriter2
12
+ } from "./server-gzy5w8ya.js";
13
+ import {
14
+ scheduleAfterBootQuiet
15
+ } from "./server-qjvk4a9t.js";
16
+ import {
17
+ isRecord
18
+ } from "./server-nxas8v1d.js";
19
+ import {
20
+ cleanSearchText,
21
+ keywordRelevance,
22
+ matchedSearchSnippet,
23
+ getIndexedMessageCorpusSize,
24
+ getMessageIndexReconciliationStartOrdinal,
25
+ isMessageIndexReconciledThrough,
26
+ clearIndexedMessages,
27
+ indexMessagesAfterOrdinal
28
+ } from "./server-hwag0khr.js";
1
29
  import {
2
30
  Priority,
3
31
  AdmissionClass,
@@ -7,22 +35,12 @@ import {
7
35
  SubcClient,
8
36
  isConsumerReconnectTransient
9
37
  } from "./server-ab1g0wn9.js";
10
- import {
11
- getHarness2
12
- } from "./server-pavzsdc6.js";
13
- import {
14
- getDataDir,
15
- log2
16
- } from "./server-6495nmn9.js";
17
- import {
18
- inRootAsyncContext2
19
- } from "./server-gzy5w8ya.js";
20
38
  import {
21
39
  buildCanonicalChunkTextFromFts2,
22
40
  loadCompartmentChunkEmbeddingsForSearch2,
23
41
  peekCompartmentChunkSearchPool2,
24
42
  warmCompartmentChunkSearchPool2
25
- } from "./server-4sn3at8p.js";
43
+ } from "./server-h1byrhwx.js";
26
44
  import {
27
45
  recordEmbeddingMeasurement,
28
46
  saveCommitEmbedding,
@@ -39,7 +57,7 @@ import {
39
57
  enqueueShadowEmbeddingItems2,
40
58
  getProjectEmbeddingSnapshot2,
41
59
  embedItemsForProject2
42
- } from "./server-zh3esb0z.js";
60
+ } from "./server-ab8017gw.js";
43
61
  import {
44
62
  getProjectEmbeddings2
45
63
  } from "./server-4dhv7mqz.js";
@@ -48,7 +66,7 @@ import {
48
66
  isEmbeddingEnabled,
49
67
  embedText,
50
68
  ensureMemoryEmbeddings2
51
- } from "./server-p0snacz3.js";
69
+ } from "./server-2aasmpsp.js";
52
70
  import {
53
71
  ModuleMemoryAuthorityError2,
54
72
  getMemoriesByProject2,
@@ -63,20 +81,11 @@ import {
63
81
  sanitizeFtsQuery,
64
82
  searchMemoriesFTS,
65
83
  searchMemoriesFTSUnion
66
- } from "./server-ph300d6m.js";
67
- import {
68
- cleanSearchText,
69
- keywordRelevance,
70
- matchedSearchSnippet,
71
- getIndexedMessageCorpusSize
72
- } from "./server-hwag0khr.js";
84
+ } from "./server-tkdabx8q.js";
73
85
  import {
74
86
  MESSAGE_FTS_SESSION_FILTER_SQL,
75
87
  withMessageFtsSessionFilter
76
88
  } from "./server-kaqs6h31.js";
77
- import {
78
- isRecord
79
- } from "./server-nxas8v1d.js";
80
89
  import {
81
90
  resolveWorkspaceShareCategories,
82
91
  resolveWorkspaceIdentitySet,
@@ -88,6 +97,301 @@ import {
88
97
  isKnownAutocommit
89
98
  } from "./server-8ajstryz.js";
90
99
 
100
+ // src/features/magic-context/message-index-async.ts
101
+ var indexCancellations = new Map;
102
+ var sessionEpochs = new Map;
103
+ var rebuildingSessions = new Set;
104
+ function getMessageIndexEpoch(sessionId) {
105
+ return sessionEpochs.get(sessionId) ?? 0;
106
+ }
107
+ function isMessageIndexSearchable(sessionId) {
108
+ return !rebuildingSessions.has(sessionId);
109
+ }
110
+ var workerMeasurements = new WeakMap;
111
+ function isDatabaseLockedError(error) {
112
+ if (!error || typeof error !== "object")
113
+ return false;
114
+ const e = error;
115
+ if (typeof e.code === "string") {
116
+ if (e.code === "SQLITE_BUSY" || e.code === "SQLITE_LOCKED")
117
+ return true;
118
+ }
119
+ if (typeof e.message === "string") {
120
+ if (/database is locked/i.test(e.message))
121
+ return true;
122
+ if (/sqlite_(busy|locked)/i.test(e.message))
123
+ return true;
124
+ }
125
+ return false;
126
+ }
127
+ var RECONCILIATION_BATCH_SIZE = 100;
128
+
129
+ class MagicContextMessageIndexHeapHolder {
130
+ reconciledSessions = new Set;
131
+ reconciledSnapshots = new Map;
132
+ stalledSessions = new Set;
133
+ reconciliationScheduledSessions = new Set;
134
+ clearAndReindexScheduledSessions = new Map;
135
+ sessionLocks = new Map;
136
+ incrementalTimers = new Map;
137
+ pendingIncrementalKeys = new Set;
138
+ completedIncrementalKeys = new Set;
139
+ activeReconcilerBuffers = new Map;
140
+ }
141
+ var heapHolder = new MagicContextMessageIndexHeapHolder;
142
+ function clearCompletedIncrementalKeys(sessionId) {
143
+ const prefix = `${sessionId}\x00`;
144
+ for (const key of heapHolder.completedIncrementalKeys) {
145
+ if (key.startsWith(prefix))
146
+ heapHolder.completedIncrementalKeys.delete(key);
147
+ }
148
+ }
149
+ var PREPARE_RETRY_MS = 60000;
150
+ async function sourceReady(source, sessionId) {
151
+ const prepare = source?.prepare;
152
+ return typeof prepare === "function" ? prepare(sessionId) : true;
153
+ }
154
+ function defer(fn) {
155
+ const immediate = globalThis.setImmediate;
156
+ if (typeof immediate === "function") {
157
+ inRootAsyncContext2(() => immediate(fn));
158
+ return;
159
+ }
160
+ inRootAsyncContext2(() => setTimeout(fn, 0));
161
+ }
162
+ function yieldToEventLoop() {
163
+ return new Promise((resolve) => defer(resolve));
164
+ }
165
+ function runWithSessionLock(sessionId, operation) {
166
+ const previous = heapHolder.sessionLocks.get(sessionId) ?? Promise.resolve();
167
+ const run = previous.catch(() => {
168
+ return;
169
+ }).then(async () => {
170
+ await operation();
171
+ });
172
+ heapHolder.sessionLocks.set(sessionId, run);
173
+ run.finally(() => {
174
+ if (heapHolder.sessionLocks.get(sessionId) === run) {
175
+ heapHolder.sessionLocks.delete(sessionId);
176
+ }
177
+ }).catch(() => {
178
+ return;
179
+ });
180
+ return run;
181
+ }
182
+ function logIndexingError(sessionId, action, error) {
183
+ if (isDatabaseLockedError(error)) {
184
+ sessionLog2(sessionId, `message FTS async ${action} skipped (database busy; will retry on next reconciliation)`);
185
+ return;
186
+ }
187
+ sessionLog2(sessionId, `message FTS async ${action} failed: ${error instanceof Error ? error.message : String(error)}`);
188
+ log2(`[message-index-async] ${action} failed for ${sessionId}:`, error);
189
+ }
190
+ function serializedMessageBytes(messages) {
191
+ try {
192
+ return Buffer.byteLength(JSON.stringify(messages));
193
+ } catch {
194
+ return 0;
195
+ }
196
+ }
197
+ function logReconciliationStall(sessionId, reason) {
198
+ if (heapHolder.stalledSessions.has(sessionId))
199
+ return;
200
+ heapHolder.stalledSessions.add(sessionId);
201
+ sessionLog2(sessionId, `message FTS reconciliation stalled: ${reason}; will retry`);
202
+ }
203
+ async function reconcileSessionIndex(db, sessionId, readMessages, epoch = getMessageIndexEpoch(sessionId)) {
204
+ if (!await sourceReady(readMessages, sessionId)) {
205
+ logReconciliationStall(sessionId, "raw message reader is not ready");
206
+ return false;
207
+ }
208
+ await runWithSessionLock(sessionId, async () => {
209
+ if (getMessageIndexEpoch(sessionId) !== epoch)
210
+ return;
211
+ if (!readMessages.getRevision && heapHolder.reconciledSessions.has(sessionId))
212
+ return;
213
+ let fallbackSnapshot = null;
214
+ try {
215
+ const fullReader = typeof readMessages === "function" ? readMessages : null;
216
+ const revision = readMessages.getRevision?.(sessionId);
217
+ const previous = heapHolder.reconciledSnapshots.get(sessionId);
218
+ const finalWatermark = previous && revision === previous.revision ? previous.count : revision !== undefined && readMessages.getCountAfterRevision ? previous && revision > previous.revision ? previous.count + readMessages.getCountAfterRevision(sessionId, previous.revision, revision) : readMessages.getCountAfterRevision(sessionId, -1, revision) : readMessages.getCount ? readMessages.getCount(sessionId) : (fallbackSnapshot = fullReader?.(sessionId) ?? []).length;
219
+ let cursor = getMessageIndexReconciliationStartOrdinal(db, sessionId);
220
+ let after = cursor === previous?.count ? previous.after : undefined;
221
+ while (cursor < finalWatermark) {
222
+ if (getMessageIndexEpoch(sessionId) !== epoch)
223
+ return;
224
+ const pageEnd = Math.min(finalWatermark, cursor + RECONCILIATION_BATCH_SIZE);
225
+ const messages = readMessages.readPage ? readMessages.readPage(sessionId, cursor, RECONCILIATION_BATCH_SIZE, finalWatermark, after) : (fallbackSnapshot ?? []).filter((message) => message.ordinal > cursor && message.ordinal <= pageEnd);
226
+ const retainedMessages = fallbackSnapshot ?? messages;
227
+ heapHolder.activeReconcilerBuffers.set(sessionId, retainedMessages);
228
+ if (getMessageIndexEpoch(sessionId) !== epoch)
229
+ return;
230
+ withSqliteBackgroundWriter2(() => indexMessagesAfterOrdinal(db, sessionId, messages, cursor, pageEnd));
231
+ const nextCursor = getMessageIndexReconciliationStartOrdinal(db, sessionId);
232
+ if (nextCursor <= cursor)
233
+ break;
234
+ cursor = nextCursor;
235
+ const last = messages.at(-1);
236
+ after = last?.ordinal === cursor && typeof last.createdAt === "number" ? { timeCreated: last.createdAt, id: last.id } : undefined;
237
+ if (cursor < finalWatermark) {
238
+ await yieldToEventLoop();
239
+ }
240
+ }
241
+ if (getMessageIndexEpoch(sessionId) !== epoch)
242
+ return;
243
+ if (isMessageIndexReconciledThrough(db, sessionId, finalWatermark)) {
244
+ heapHolder.reconciledSessions.add(sessionId);
245
+ rebuildingSessions.delete(sessionId);
246
+ if (revision !== undefined) {
247
+ if (!after && finalWatermark > 0 && readMessages.readPage) {
248
+ const last = readMessages.readPage(sessionId, finalWatermark - 1, 1, finalWatermark).at(-1);
249
+ if (last?.ordinal === finalWatermark && typeof last.createdAt === "number")
250
+ after = { timeCreated: last.createdAt, id: last.id };
251
+ }
252
+ heapHolder.reconciledSnapshots.set(sessionId, {
253
+ revision,
254
+ count: finalWatermark,
255
+ after
256
+ });
257
+ }
258
+ } else {
259
+ logReconciliationStall(sessionId, `gap after ordinal ${cursor} (store count ${finalWatermark})`);
260
+ }
261
+ } finally {
262
+ heapHolder.activeReconcilerBuffers.delete(sessionId);
263
+ }
264
+ });
265
+ return true;
266
+ }
267
+ function scheduleReconciliation(db, sessionId, readMessages) {
268
+ if (rebuildingSessions.has(sessionId)) {
269
+ if (!heapHolder.clearAndReindexScheduledSessions.has(sessionId))
270
+ scheduleClearAndReindex(db, sessionId, readMessages);
271
+ return;
272
+ }
273
+ const epoch = getMessageIndexEpoch(sessionId);
274
+ if (!readMessages.getRevision && heapHolder.reconciledSessions.has(sessionId) || heapHolder.reconciliationScheduledSessions.has(sessionId)) {
275
+ return;
276
+ }
277
+ heapHolder.reconciliationScheduledSessions.add(sessionId);
278
+ scheduleAfterBootQuiet(() => {
279
+ defer(() => {
280
+ let appendedDuringReconciliation = false;
281
+ const previousSnapshot = heapHolder.reconciledSnapshots.get(sessionId);
282
+ if (getMessageIndexEpoch(sessionId) !== epoch)
283
+ return;
284
+ reconcileSessionIndex(db, sessionId, readMessages, epoch).then((ran) => {
285
+ if (getMessageIndexEpoch(sessionId) !== epoch)
286
+ return;
287
+ if (ran) {
288
+ const snapshot = heapHolder.reconciledSnapshots.get(sessionId);
289
+ appendedDuringReconciliation = snapshot !== undefined && snapshot !== previousSnapshot && readMessages.getRevision !== undefined && readMessages.getRevision(sessionId) !== snapshot.revision;
290
+ return;
291
+ }
292
+ inRootAsyncContext2(() => setTimeout(() => {
293
+ if (getMessageIndexEpoch(sessionId) !== epoch)
294
+ return;
295
+ heapHolder.reconciliationScheduledSessions.delete(sessionId);
296
+ scheduleReconciliation(db, sessionId, readMessages);
297
+ }, PREPARE_RETRY_MS)).unref?.();
298
+ }).catch((error) => {
299
+ if (getMessageIndexEpoch(sessionId) !== epoch)
300
+ return;
301
+ logReconciliationStall(sessionId, error instanceof Error ? error.message : String(error));
302
+ if (isDatabaseLockedError(error))
303
+ inRootAsyncContext2(() => setTimeout(() => {
304
+ if (getMessageIndexEpoch(sessionId) === epoch)
305
+ scheduleReconciliation(db, sessionId, readMessages);
306
+ }, 1000)).unref();
307
+ }).finally(() => {
308
+ if (getMessageIndexEpoch(sessionId) !== epoch)
309
+ return;
310
+ heapHolder.reconciliationScheduledSessions.delete(sessionId);
311
+ if (appendedDuringReconciliation)
312
+ scheduleReconciliation(db, sessionId, readMessages);
313
+ });
314
+ });
315
+ });
316
+ }
317
+ function scheduleClearAndReindex(db, sessionId, readMessages) {
318
+ rebuildingSessions.add(sessionId);
319
+ invalidateIncrementalWork(sessionId);
320
+ heapHolder.reconciledSessions.delete(sessionId);
321
+ heapHolder.reconciledSnapshots.delete(sessionId);
322
+ heapHolder.reconciliationScheduledSessions.add(sessionId);
323
+ clearCompletedIncrementalKeys(sessionId);
324
+ const existing = heapHolder.clearAndReindexScheduledSessions.get(sessionId);
325
+ if (existing) {
326
+ existing.source = readMessages;
327
+ return;
328
+ }
329
+ const pending = { source: readMessages };
330
+ heapHolder.clearAndReindexScheduledSessions.set(sessionId, pending);
331
+ scheduleAfterBootQuiet(() => {
332
+ defer(() => {
333
+ let cleared = false;
334
+ let ownsSchedule = false;
335
+ let epoch = getMessageIndexEpoch(sessionId);
336
+ runWithSessionLock(sessionId, () => {
337
+ if (heapHolder.clearAndReindexScheduledSessions.get(sessionId) !== pending)
338
+ return;
339
+ ownsSchedule = true;
340
+ epoch = getMessageIndexEpoch(sessionId);
341
+ heapHolder.clearAndReindexScheduledSessions.delete(sessionId);
342
+ heapHolder.reconciledSessions.delete(sessionId);
343
+ heapHolder.reconciledSnapshots.delete(sessionId);
344
+ clearCompletedIncrementalKeys(sessionId);
345
+ clearIndexedMessages(db, sessionId);
346
+ cleared = true;
347
+ }).then(() => cleared && getMessageIndexEpoch(sessionId) === epoch ? reconcileSessionIndex(db, sessionId, pending.source, epoch) : undefined).catch((error) => {
348
+ if (getMessageIndexEpoch(sessionId) !== epoch)
349
+ return;
350
+ heapHolder.reconciledSessions.delete(sessionId);
351
+ if (heapHolder.clearAndReindexScheduledSessions.get(sessionId) === pending)
352
+ heapHolder.clearAndReindexScheduledSessions.delete(sessionId);
353
+ logIndexingError(sessionId, "clear and reindex", error);
354
+ }).finally(() => {
355
+ if (ownsSchedule && getMessageIndexEpoch(sessionId) === epoch)
356
+ heapHolder.reconciliationScheduledSessions.delete(sessionId);
357
+ });
358
+ });
359
+ });
360
+ }
361
+ function getMessageIndexQueueHeapStats() {
362
+ return {
363
+ queueLength: heapHolder.reconciliationScheduledSessions.size + heapHolder.incrementalTimers.size + heapHolder.pendingIncrementalKeys.size,
364
+ reconciliationScheduled: heapHolder.reconciliationScheduledSessions.size,
365
+ incrementalTimers: heapHolder.incrementalTimers.size,
366
+ pendingIncremental: heapHolder.pendingIncrementalKeys.size,
367
+ activeSessionLocks: heapHolder.sessionLocks.size,
368
+ completedIncrementalKeys: heapHolder.completedIncrementalKeys.size,
369
+ activeBufferMessages: [...heapHolder.activeReconcilerBuffers.values()].reduce((sum, messages) => sum + messages.length, 0),
370
+ activeBufferBytes: [...heapHolder.activeReconcilerBuffers.values()].reduce((sum, messages) => sum + serializedMessageBytes(messages), 0)
371
+ };
372
+ }
373
+ function invalidateIncrementalWork(sessionId) {
374
+ sessionEpochs.set(sessionId, (sessionEpochs.get(sessionId) ?? 0) + 1);
375
+ for (const cancellation of indexCancellations.get(sessionId) ?? [])
376
+ Atomics.store(cancellation, 0, 1);
377
+ const prefix = `${sessionId}\x00`;
378
+ for (const [key, timer] of heapHolder.incrementalTimers) {
379
+ if (key.startsWith(prefix)) {
380
+ clearTimeout(timer);
381
+ heapHolder.incrementalTimers.delete(key);
382
+ }
383
+ }
384
+ for (const key of heapHolder.pendingIncrementalKeys) {
385
+ if (key.startsWith(prefix)) {
386
+ heapHolder.pendingIncrementalKeys.delete(key);
387
+ }
388
+ }
389
+ for (const key of heapHolder.completedIncrementalKeys) {
390
+ if (key.startsWith(prefix))
391
+ heapHolder.completedIncrementalKeys.delete(key);
392
+ }
393
+ }
394
+
91
395
  // src/hooks/magic-context/module-transport.ts
92
396
  import { realpathSync } from "node:fs";
93
397
  import { join } from "node:path";
@@ -1799,6 +2103,8 @@ function createUnifiedSearchDiagnostics2() {
1799
2103
  };
1800
2104
  }
1801
2105
  function searchMessageHistory2(db, request) {
2106
+ if (!isMessageIndexSearchable(request.sessionId))
2107
+ return { results: [], suppressedLiveMessageMatches: 0 };
1802
2108
  const diagnostics = request.collectDiagnostics ? createUnifiedSearchDiagnostics2() : undefined;
1803
2109
  const capture = request.captureLimit ? { limit: request.captureLimit, results: [] } : undefined;
1804
2110
  const results = searchMessages({
@@ -3021,6 +3327,9 @@ function resolveMemoriesByIdsForSearch2(args) {
3021
3327
  });
3022
3328
  }
3023
3329
  async function unifiedSearch2(db, sessionId, projectPath, query, options = {}) {
3330
+ const messageIndexEpoch = getMessageIndexEpoch(sessionId);
3331
+ const messageIndexWasSearchable = isMessageIndexSearchable(sessionId);
3332
+ const messageIndexStillCurrent = () => messageIndexWasSearchable && isMessageIndexSearchable(sessionId) && getMessageIndexEpoch(sessionId) === messageIndexEpoch;
3024
3333
  const trimmedQuery = query.trim();
3025
3334
  const measurementStartedAt = Date.now();
3026
3335
  if (trimmedQuery.length === 0 || options.signal?.aborted) {
@@ -3063,7 +3372,7 @@ async function unifiedSearch2(db, sessionId, projectPath, query, options = {}) {
3063
3372
  const timings = options.stepTimings;
3064
3373
  let stepStartedAt = performance.now();
3065
3374
  let messageResults = [];
3066
- if (runMessages) {
3375
+ if (runMessages && messageIndexStillCurrent()) {
3067
3376
  const messageRequest = {
3068
3377
  sessionId,
3069
3378
  query: trimmedQuery,
@@ -3237,6 +3546,8 @@ async function unifiedSearch2(db, sessionId, projectPath, query, options = {}) {
3237
3546
  }).immediate();
3238
3547
  }
3239
3548
  }
3549
+ if (!messageIndexStillCurrent())
3550
+ results = results.filter((result) => result.source !== "message");
3240
3551
  if (options.autoHint) {
3241
3552
  return results.map((result) => ({
3242
3553
  ...result,
@@ -3363,4 +3674,4 @@ ${bodyParts.join(`
3363
3674
  `)}`;
3364
3675
  }
3365
3676
 
3366
- export { renderSingleStoreMigrationRequiredRefusal, SingleStoreMigrationRequiredError, singleStoreMigrationRequiredFailure, projectNeedsSingleStoreMigration, STORE_AHEAD_OF_BINARY_CODE, storeAheadOfBinaryFailure, TRANSFORM_PAGE_UPLOAD_TIMEOUT_MS, transformColdStartExecuteTimeoutMs, getDefaultSubcConnectionFile, isModuleTransportGenerationChangedResult, SubcModuleTransport, indexCommitsForProject, embedUnembeddedCommits, createUnifiedSearchDiagnostics2, searchMessageHistory2, parseIdShapedQuery2, resolveMemoriesByIdsForSearch2, unifiedSearch2, formatSearchResults2 };
3677
+ export { getMessageIndexEpoch, isMessageIndexSearchable, scheduleReconciliation, scheduleClearAndReindex, getMessageIndexQueueHeapStats, renderSingleStoreMigrationRequiredRefusal, SingleStoreMigrationRequiredError, singleStoreMigrationRequiredFailure, projectNeedsSingleStoreMigration, STORE_AHEAD_OF_BINARY_CODE, storeAheadOfBinaryFailure, TRANSFORM_PAGE_UPLOAD_TIMEOUT_MS, transformColdStartExecuteTimeoutMs, getDefaultSubcConnectionFile, isModuleTransportGenerationChangedResult, SubcModuleTransport, indexCommitsForProject, embedUnembeddedCommits, createUnifiedSearchDiagnostics2, searchMessageHistory2, parseIdShapedQuery2, resolveMemoriesByIdsForSearch2, unifiedSearch2, formatSearchResults2 };
@@ -17,7 +17,7 @@ import {
17
17
  } from "./server-6g98c227.js";
18
18
  import {
19
19
  embedTextForProject2
20
- } from "./server-zh3esb0z.js";
20
+ } from "./server-ab8017gw.js";
21
21
  import {
22
22
  saveEmbeddingIfHashMatches
23
23
  } from "./server-4dhv7mqz.js";
package/dist/v2/server.js CHANGED
@@ -7874,7 +7874,7 @@ var require_src2 = __commonJS(function(exports, module) {
7874
7874
  // package.json
7875
7875
  var package_default = {
7876
7876
  name: "@cortexkit/opencode-magic-context",
7877
- version: "0.49.30",
7877
+ version: "0.49.32",
7878
7878
  type: "module",
7879
7879
  description: "OpenCode plugin for Magic Context — cross-session memory and context management",
7880
7880
  main: "dist/index.js",
@@ -8195,7 +8195,7 @@ async function setup(context) {
8195
8195
  if (claimStorageBackupWarningCheck() && isMagicContextStorageOutsideEngram(storageDir)) {
8196
8196
  log2(`[magic-context] Magic Context storage is at ${storageDir}, outside the folder Engram backs up (~/.local/share/cortexkit/magic-context). It is not included in CortexKit backups.`);
8197
8197
  }
8198
- const { registerContext } = await import("./context-wx4sfhgr.js");
8198
+ const { registerContext } = await import("./context-ehews84p.js");
8199
8199
  setHarness2("opencode2");
8200
8200
  bindStaleBuildNotice({}, import.meta.url, "opencode2");
8201
8201
  const duties = await registerContext(context);
@@ -19,8 +19,8 @@ import {
19
19
  registerActiveCompartmentRun,
20
20
  setRecompStarting2,
21
21
  setRecompTerminal2
22
- } from "./server-k33t88h3.js";
23
- import"./server-zh3esb0z.js";
22
+ } from "./server-4n176b5m.js";
23
+ import"./server-ab8017gw.js";
24
24
  import"./server-3mz2qh5a.js";
25
25
  import"./server-ab1g0wn9.js";
26
26
  import {
@@ -30,7 +30,7 @@ import"./server-51779zs2.js";
30
30
  import"./server-cpjk5jd2.js";
31
31
  import"./server-6vd2n183.js";
32
32
  import"./server-awr8qa4c.js";
33
- import"./server-4sn3at8p.js";
33
+ import"./server-h1byrhwx.js";
34
34
  import"./server-hwag0khr.js";
35
35
  import {
36
36
  getWrapupInProgressState,
@@ -53,7 +53,7 @@ import"./server-fr0wbhdn.js";
53
53
  import"./server-6g98c227.js";
54
54
  import"./server-qjvk4a9t.js";
55
55
  import"./server-fb3zxn06.js";
56
- import"./server-ph300d6m.js";
56
+ import"./server-tkdabx8q.js";
57
57
  import {
58
58
  pushNotification2
59
59
  } from "./server-x8a7jmkh.js";
@@ -62,7 +62,7 @@ import"./server-qt1qqasc.js";
62
62
  import"./server-bsgd4dk1.js";
63
63
  import"./server-4dhv7mqz.js";
64
64
  import"./server-vr4szghm.js";
65
- import"./server-p0snacz3.js";
65
+ import"./server-2aasmpsp.js";
66
66
  import"./server-jjvbk92s.js";
67
67
  import"./server-835q39kd.js";
68
68
  import"./server-29qp8nz4.js";
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  runManagedWrapup2
3
- } from "./index-69gd2scx.js";
4
- import"./index-adr4vfys.js";
5
- import"./index-k0dpkyn7.js";
3
+ } from "./index-t6b3px80.js";
4
+ import"./index-2agkcr3y.js";
5
+ import"./index-bzwh06cp.js";
6
6
  import"./index-qgh1qwjj.js";
7
- import"./index-pe6b4bhs.js";
7
+ import"./index-as2amvn3.js";
8
8
  import"./index-4s4zq97n.js";
9
9
  import"./index-sd3egckz.js";
10
- import"./index-vnn5405n.js";
11
- import"./index-gxkhzmjq.js";
10
+ import"./index-rfxzazzy.js";
11
+ import"./index-2743x98y.js";
12
12
  import"./index-jjvbk92s.js";
13
13
  import"./index-4dhv7mqz.js";
14
14
  import"./index-spwjbkc7.js";
@@ -23,7 +23,7 @@ import"./index-rrv26qjz.js";
23
23
  import"./index-51rdvtkz.js";
24
24
  import"./index-nxrsns5x.js";
25
25
  import"./index-kfyb3k6n.js";
26
- import"./index-mm93m7y6.js";
26
+ import"./index-v7287tzq.js";
27
27
  import"./index-wfybj1dw.js";
28
28
  import"./index-hbt0sbv2.js";
29
29
  import"./index-fb3zxn06.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/opencode-magic-context",
3
- "version": "0.49.30",
3
+ "version": "0.49.32",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin for Magic Context — cross-session memory and context management",
6
6
  "main": "dist/index.js",
@@ -76,6 +76,8 @@ import type { Transcript, TranscriptMessage, TranscriptPart } from "./transcript
76
76
  export { TEXT_TAG_IDENTITY_MARKER };
77
77
 
78
78
  export interface TagTranscriptOptions {
79
+ /** Independently validated text sources; does not authorize tool accounting reuse. */
80
+ reuseTextMessageIds?: ReadonlySet<string>;
79
81
  /** Keep tag-only assistant slots inert when their original text must survive stripping. */
80
82
  preserveTagOnlyAssistantText?: boolean;
81
83
  /**
@@ -125,6 +127,8 @@ export interface TagTranscriptResult {
125
127
  targets: Map<number, TagTarget>;
126
128
  }
127
129
 
130
+ type PersistedDropTag = Pick<TagEntry, "tagNumber" | "messageId" | "type" | "toolOwnerMessageId">;
131
+
128
132
  /**
129
133
  * Bind durable drops to a saved request without tagging it again. Recovery must
130
134
  * not allocate identities, inject prefixes, or write accounting while the store
@@ -132,7 +136,7 @@ export interface TagTranscriptResult {
132
136
  */
133
137
  export function buildPersistedDropTargets(
134
138
  transcript: Transcript,
135
- tags: readonly TagEntry[],
139
+ tags: readonly PersistedDropTag[],
136
140
  options: { allowReasoningPrefixRewrite?: boolean } = {},
137
141
  ): Map<number, TagTarget> {
138
142
  const targets = new Map<number, TagTarget>();
@@ -147,7 +151,7 @@ export function buildPersistedDropTargets(
147
151
  const byNumber = new Map(tags.map((tag) => [tag.tagNumber, tag]));
148
152
  const aggregates = new Map<
149
153
  string,
150
- { tag?: TagEntry; occurrences: ToolOccurrence[]; reasoning: boolean }
154
+ { tag?: PersistedDropTag; occurrences: ToolOccurrence[]; reasoning: boolean }
151
155
  >();
152
156
  const pending = new Map<string, string[]>();
153
157
  const resolved = new Map<string, string>();
@@ -421,7 +425,10 @@ export function tagTranscript(
421
425
  skipPrefixInjection,
422
426
  preserveTagOnlyAssistantText: options.preserveTagOnlyAssistantText,
423
427
  entryFingerprint: options.entryFingerprintByMessageId?.get(messageId) ?? null,
424
- reuseIdentity: reuseIdentity || contentDerivedTextIds !== undefined,
428
+ reuseIdentity:
429
+ reuseIdentity ||
430
+ options.reuseTextMessageIds?.has(messageId) === true ||
431
+ contentDerivedTextIds !== undefined,
425
432
  timing,
426
433
  textIdentitySourceCache: options.textIdentitySourceCache,
427
434
  textTokenCache: options.textTokenCache,
@@ -512,6 +519,11 @@ export function tagTranscript(
512
519
  timing,
513
520
  options.toolTokenCache,
514
521
  tokenCacheKey,
522
+ {
523
+ byteSize: existing.maxByteSize,
524
+ tokenCount: existing.maxTokenCount,
525
+ inputTokenCount: existing.inputTokenCount,
526
+ },
515
527
  );
516
528
  text = accounting.text;
517
529
  if (part.kind === "tool_result") {
@@ -627,6 +639,7 @@ export function tagTranscript(
627
639
  timing,
628
640
  options.toolTokenCache,
629
641
  tokenCacheKey,
642
+ tagger.getToolTagAccounting(sessionId, callId, messageId),
630
643
  );
631
644
  text = accounting.text;
632
645
  const outputByteSize = part.kind === "tool_result" ? accounting.byteSize : 0;
@@ -784,12 +797,23 @@ function readAggregateToolAccounting(
784
797
  timing: TagTranscriptTiming | undefined,
785
798
  tokenCache?: Map<string, { text: string; tokenCount: number }>,
786
799
  tokenCacheKey?: string,
800
+ persisted?: { byteSize: number; tokenCount: number | null; inputTokenCount: number | null },
787
801
  ): AggregateToolAccounting {
788
802
  const text = part.getText() ?? "";
789
803
  const byteSize = getToolPartByteSize(part, text);
790
- const metadata = part.getToolMetadata();
804
+ // Persisted tool-result accounting tracks the largest byte size seen. Skip
805
+ // output tokenization when that size has not grown and a count is stored;
806
+ // known input counts also need no tokenization. Growth and missing counts still
807
+ // use the counting path, even if the owning message ID was not eligible for
808
+ // tag reuse through options.reuseMessageIds.
809
+ const metadata = part.getToolMetadata({
810
+ skipInputTokenCount: persisted?.inputTokenCount != null,
811
+ });
791
812
  let tokenCount = 0;
792
- if (part.kind === "tool_result") {
813
+ if (
814
+ part.kind === "tool_result" &&
815
+ (!persisted || persisted.tokenCount === null || byteSize > persisted.byteSize)
816
+ ) {
793
817
  const tokenStart = timing ? performance.now() : 0;
794
818
  tokenCount = getCachedToolPartTokenCount(part, text, tokenCache, tokenCacheKey);
795
819
  if (timing) timing.tokenCounting += performance.now() - tokenStart;
@@ -1423,6 +1447,11 @@ function buildAggregateTarget(
1423
1447
  },
1424
1448
  // Open invocations still belong to the current turn; only complete arcs reclaim.
1425
1449
  prepareDrop: () => !occurrences.some((occ) => occ.part.canRemove?.() === "defer"),
1450
+ prepareReplayDrop: () => {
1451
+ if (complete() && !requiresToolArcSkeleton) {
1452
+ for (const occ of occurrences) occ.part.canRemove?.();
1453
+ }
1454
+ },
1426
1455
  canDrop: () => complete() && !occurrences.some((occ) => occ.part.hasUserAnswer?.()),
1427
1456
  requiresToolArcSkeleton,
1428
1457
  // Non-mutating read of the invocation input (the tool_use occurrence
@@ -139,7 +139,7 @@ export interface TranscriptPart {
139
139
  *
140
140
  * For non-tool parts both byte fields are undefined/0.
141
141
  */
142
- getToolMetadata(): {
142
+ getToolMetadata(options?: { skipInputTokenCount?: boolean }): {
143
143
  toolName: string | undefined;
144
144
  inputByteSize: number;
145
145
  inputTokenCount: number;