@cortexkit/opencode-magic-context 0.49.29 → 0.49.31

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 (82) hide show
  1. package/dist/auto-search-worker.js +1 -1
  2. package/dist/chunk-embedding-write-worker.js +1 -1
  3. package/dist/{context-836j3vte.js → context-9a0f8mzh.js} +294 -41
  4. package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts +3 -3
  5. package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts.map +1 -1
  6. package/dist/features/magic-context/dreamer/task-executor.d.ts.map +1 -1
  7. package/dist/features/magic-context/dreamer/verify.d.ts +13 -17
  8. package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
  9. package/dist/features/magic-context/storage-native-replay.d.ts +2 -0
  10. package/dist/features/magic-context/storage-native-replay.d.ts.map +1 -1
  11. package/dist/features/magic-context/work-metrics.d.ts.map +1 -1
  12. package/dist/hooks/magic-context/apply-operations.d.ts +11 -1
  13. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  14. package/dist/hooks/magic-context/history-boundary-repair.d.ts +1 -1
  15. package/dist/hooks/magic-context/history-boundary-repair.d.ts.map +1 -1
  16. package/dist/hooks/magic-context/strip-content.d.ts +1 -1
  17. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  18. package/dist/hooks/magic-context/tag-messages.d.ts +2 -0
  19. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  20. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +2 -0
  21. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  22. package/dist/hooks/magic-context/transform.d.ts +11 -0
  23. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  24. package/dist/{index-wyt5m81v.js → index-0bjktrjt.js} +3 -3
  25. package/dist/{index-zhgqgxtt.js → index-0fe4q77z.js} +7 -5
  26. package/dist/{index-wpcdk76y.js → index-22e7khnr.js} +37 -29
  27. package/dist/{index-a1h316s9.js → index-3jmx3fae.js} +1 -1
  28. package/dist/{index-tx68g9nc.js → index-4s4zq97n.js} +1 -1
  29. package/dist/{index-h4s5jk7f.js → index-hbt0sbv2.js} +1 -1
  30. package/dist/{index-q9zvphbq.js → index-kmayq4kq.js} +39 -7
  31. package/dist/{index-bps8vn9r.js → index-qathsvz2.js} +5 -3
  32. package/dist/{index-wvffze8y.js → index-vtz789ex.js} +1 -1
  33. package/dist/{index-093c4g1c.js → index-y6ynjghv.js} +2 -2
  34. package/dist/index.js +13 -13
  35. package/dist/{message-index-bounded-7qsn4fd3.js → message-index-bounded-zq7a5nm6.js} +2 -2
  36. package/dist/migration-worker.js +1 -1
  37. package/dist/raw-ordinal-warmup-worker.js +2 -2
  38. package/dist/{read-session-chunk-a5d0q3m0.js → read-session-chunk-1ndphmb8.js} +1 -1
  39. package/dist/{recomp-orchestrator-na34m4c5.js → recomp-orchestrator-ynehxbya.js} +5 -5
  40. package/dist/{search-archive-66pm17gm.js → search-archive-sr07c6aj.js} +2 -2
  41. package/dist/{search-0cd6hx7a.js → search-fbaz1m8r.js} +3 -3
  42. package/dist/shared/marker-only-text.d.ts +2 -0
  43. package/dist/shared/marker-only-text.d.ts.map +1 -0
  44. package/dist/shared/tag-transcript.d.ts +4 -1
  45. package/dist/shared/tag-transcript.d.ts.map +1 -1
  46. package/dist/shared/transcript.d.ts +3 -1
  47. package/dist/shared/transcript.d.ts.map +1 -1
  48. package/dist/{transform-postprocess-phase-27we33af.js → transform-postprocess-phase-50h70ft8.js} +5 -5
  49. package/dist/v2/auto-search-worker.js +1 -1
  50. package/dist/v2/chunk-embedding-write-worker.js +1 -1
  51. package/dist/v2/{context-g4ptae4x.js → context-cdqw9jex.js} +320 -58
  52. package/dist/v2/fold/owner.d.ts +4 -0
  53. package/dist/v2/fold/owner.d.ts.map +1 -1
  54. package/dist/v2/hidden-child-cleanup.d.ts.map +1 -1
  55. package/dist/v2/hidden-child-native.d.ts.map +1 -1
  56. package/dist/v2/hooks/context.d.ts.map +1 -1
  57. package/dist/v2/hooks/types.d.ts +3 -0
  58. package/dist/v2/hooks/types.d.ts.map +1 -1
  59. package/dist/v2/{message-index-bounded-7363502m.js → message-index-bounded-jvgz7vk9.js} +2 -2
  60. package/dist/v2/message-times.d.ts +17 -0
  61. package/dist/v2/message-times.d.ts.map +1 -0
  62. package/dist/v2/migration-worker.js +1 -1
  63. package/dist/v2/raw-ordinal-warmup-worker.js +2 -2
  64. package/dist/v2/{read-session-chunk-kkppfhft.js → read-session-chunk-djzgm4jg.js} +1 -1
  65. package/dist/v2/{recomp-orchestrator-f1df1gb8.js → recomp-orchestrator-kdxr4tx8.js} +4 -4
  66. package/dist/v2/{search-archive-v07jpw25.js → search-archive-n3dhns4r.js} +2 -2
  67. package/dist/v2/{search-2v3xyzse.js → search-q3jjw6zp.js} +3 -3
  68. package/dist/v2/{server-976dxaxh.js → server-42nebt63.js} +1 -1
  69. package/dist/v2/{server-77gznpmf.js → server-d01pe8q5.js} +1 -1
  70. package/dist/v2/{server-k9je35ev.js → server-hwag0khr.js} +1 -1
  71. package/dist/v2/{server-mxzj3r0y.js → server-rb8m4vry.js} +7 -5
  72. package/dist/v2/{server-q50ttc2g.js → server-rdjgz95f.js} +39 -7
  73. package/dist/v2/{server-w79f8ejn.js → server-rr4cbkr0.js} +1 -1
  74. package/dist/v2/server.js +2 -2
  75. package/dist/v2/store-reader.d.ts +14 -2
  76. package/dist/v2/store-reader.d.ts.map +1 -1
  77. package/dist/v2/{wrapup-orchestrator-jq33fda8.js → wrapup-orchestrator-prfvyqbb.js} +4 -4
  78. package/dist/{wrapup-orchestrator-f0xr7pft.js → wrapup-orchestrator-4zhkcsz0.js} +6 -6
  79. package/package.json +1 -1
  80. package/src/shared/marker-only-text.ts +8 -0
  81. package/src/shared/tag-transcript.ts +34 -5
  82. package/src/shared/transcript.ts +1 -1
@@ -28,7 +28,7 @@ var [
28
28
  import("./embedding-cache-mgyr73ef.js"),
29
29
  import("./storage-memory-e6a7mb0k.js"),
30
30
  import("./project-embedding-registry-cg281d9j.js"),
31
- import("./search-0cd6hx7a.js"),
31
+ import("./search-fbaz1m8r.js"),
32
32
  import("./compartment-chunk-embedding-qgp0tbf7.js"),
33
33
  import("./embedding-activity-g1dhes6g.js"),
34
34
  import("./harness-e4avh192.js"),
@@ -35,7 +35,7 @@ import"./index-c7phnbvk.js";
35
35
  import {
36
36
  LATEST_SUPPORTED_VERSION,
37
37
  enforceSchemaFence
38
- } from "./index-h4s5jk7f.js";
38
+ } from "./index-hbt0sbv2.js";
39
39
  import"./index-7kkphb6c.js";
40
40
  import"./index-xrnsygfq.js";
41
41
  import"./index-fr0wbhdn.js";
@@ -12,7 +12,6 @@ import {
12
12
  releaseCompartmentLeaseBestEffort,
13
13
  isCompartmentLeaseHeld,
14
14
  queueM0Mutation,
15
- getCompartments,
16
15
  getUncoveredCompartmentEndsThrough,
17
16
  providerResponseFailed,
18
17
  isSuccessfulProviderCompletion,
@@ -65,7 +64,7 @@ import {
65
64
  rebaseSessionCoordinatesAsync,
66
65
  resolveOpenCodeProtectedTailBoundary,
67
66
  getActiveCompartmentRun
68
- } from "./index-q9zvphbq.js";
67
+ } from "./index-kmayq4kq.js";
69
68
  import"./index-sd3egckz.js";
70
69
  import {
71
70
  getHarness2
@@ -157,7 +156,7 @@ import {
157
156
  applyJsonSchemaParameterDescriptions,
158
157
  MagicContextRpcServer,
159
158
  subagentTemperature
160
- } from "./index-wpcdk76y.js";
159
+ } from "./index-22e7khnr.js";
161
160
  import {
162
161
  resolveHistorianModel2
163
162
  } from "./index-5kym00ag.js";
@@ -172,7 +171,7 @@ import {
172
171
  openDatabaseAsync,
173
172
  isDatabasePersisted,
174
173
  getDatabasePersistenceError
175
- } from "./index-h4s5jk7f.js";
174
+ } from "./index-hbt0sbv2.js";
176
175
  import {
177
176
  setHomeProjectPermission,
178
177
  resolveProjectIdentityForSession,
@@ -202,7 +201,9 @@ import {
202
201
  preloadTokenizer,
203
202
  estimateTokens
204
203
  } from "./index-b88dwwrh.js";
205
- import"./index-xrnsygfq.js";
204
+ import {
205
+ getTableColumnNames
206
+ } from "./index-xrnsygfq.js";
206
207
  import"./index-58e3y90n.js";
207
208
  import"./index-y1qwzvxn.js";
208
209
  import {
@@ -240,13 +241,13 @@ import {
240
241
  cleanUserText2,
241
242
  readRawSessionMessageIdOrdinalsForRange2,
242
243
  readRawSessionMessageOrdinalById2
243
- } from "./index-zhgqgxtt.js";
244
+ } from "./index-0fe4q77z.js";
244
245
  import {
245
246
  BoundedSessionMap,
246
247
  recordToolDefinitionSet,
247
248
  getCurrentToolSetHash
248
249
  } from "./index-s1n8dd2n.js";
249
- import"./index-tx68g9nc.js";
250
+ import"./index-4s4zq97n.js";
250
251
  import"./index-v15ne3a3.js";
251
252
  import {
252
253
  invalidateAutoEmbedSession,
@@ -273,7 +274,7 @@ import {
273
274
  EmergencyFailClosedError,
274
275
  DegradedPassRefusalError,
275
276
  isSyntheticHeadMessage2
276
- } from "./index-bps8vn9r.js";
277
+ } from "./index-qathsvz2.js";
277
278
  import"./index-qgh1qwjj.js";
278
279
  import"./index-qt1qqasc.js";
279
280
  import"./index-kfyb3k6n.js";
@@ -287,15 +288,15 @@ import {
287
288
  } from "./index-x8a7jmkh.js";
288
289
  import"./index-rrv26qjz.js";
289
290
  import"./index-waktfbjy.js";
290
- import"./index-wvffze8y.js";
291
- import"./index-093c4g1c.js";
291
+ import"./index-vtz789ex.js";
292
+ import"./index-y6ynjghv.js";
292
293
  import"./index-gp3kcpc4.js";
293
294
  import {
294
295
  detectConflicts
295
296
  } from "./index-4pnsgvfk.js";
296
297
  import"./index-fmsr7v69.js";
297
298
  import"./index-kaqs6h31.js";
298
- import"./index-a1h316s9.js";
299
+ import"./index-3jmx3fae.js";
299
300
  import {
300
301
  isDreamerRunnable,
301
302
  isSmartNoteSurfaceAvailable,
@@ -313,6 +314,26 @@ import"./index-t5pck3ss.js";
313
314
  import { setTimeout as delay } from "node:timers/promises";
314
315
 
315
316
  // src/hooks/magic-context/history-boundary-repair.ts
317
+ function readStoreBoundaries(db, sessionId) {
318
+ const columns = getTableColumnNames(db, "compartments");
319
+ const numbers = ["id", "sequence", "start_message", "end_message", "created_at"];
320
+ const strings = ["session_id", "start_message_id", "end_message_id", "title", "content"];
321
+ const predicates = [...numbers, ...strings].every((column) => columns.has(column)) ? [
322
+ ...numbers.map((column) => `typeof(${column}) IN ('integer', 'real')`),
323
+ ...strings.map((column) => `typeof(${column}) = 'text'`)
324
+ ] : ["0"];
325
+ for (const column of ["p1", "p2", "p3", "p4", "episode_type"]) {
326
+ if (columns.has(column))
327
+ predicates.push(`typeof(${column}) IN ('text', 'null')`);
328
+ }
329
+ for (const column of ["importance", "legacy"]) {
330
+ if (columns.has(column))
331
+ predicates.push(`typeof(${column}) IN ('integer', 'real', 'null')`);
332
+ }
333
+ return db.prepare(`SELECT sequence, ${columns.has("end_message_id") ? "end_message_id" : "NULL"} AS endMessageId
334
+ FROM compartments WHERE session_id = ? AND ${predicates.join(" AND ")}
335
+ ORDER BY sequence ASC`).all(sessionId);
336
+ }
316
337
  function readBaseline(db, sessionId) {
317
338
  const row = db.prepare("SELECT cached_m0_bytes IS NOT NULL AS hasM0, cached_m0_last_baseline_end_message_id AS boundary FROM session_meta WHERE session_id = ?").get(sessionId);
318
339
  return {
@@ -379,7 +400,7 @@ function dropCompartmentsAfter(db, sessionId, anchor, holderId, activeBranch = f
379
400
  }
380
401
  function repairMissingHistoryBoundary(args) {
381
402
  const { db, sessionId, isInHostStore, scope = "store" } = args;
382
- const compartments = scope === "active-branch" ? db.prepare("SELECT sequence, COALESCE(end_message_id, '') AS endMessageId FROM compartments WHERE session_id = ? ORDER BY sequence").all(sessionId) : getCompartments(db, sessionId);
403
+ const compartments = scope === "active-branch" ? db.prepare("SELECT sequence, COALESCE(end_message_id, '') AS endMessageId FROM compartments WHERE session_id = ? ORDER BY sequence").all(sessionId) : readStoreBoundaries(db, sessionId);
383
404
  let latestIndex = compartments.length - 1;
384
405
  while (latestIndex >= 0 && !compartments[latestIndex]?.endMessageId)
385
406
  latestIndex -= 1;
@@ -423,7 +444,7 @@ function repairMissingHistoryBoundary(args) {
423
444
  let dropped;
424
445
  let anchorForDrop = anchor;
425
446
  try {
426
- if (scope === "active-branch" && anchor) {
447
+ if (anchor) {
427
448
  const row = db.prepare("SELECT end_message AS endMessage FROM compartments WHERE session_id = ? AND sequence = ?").get(sessionId, anchor.sequence);
428
449
  if (!row)
429
450
  return { kind: "unresolved", reason: "busy", missingEndMessageId: missing };
@@ -774,8 +795,14 @@ class FoldOwner {
774
795
  async observe(args) {
775
796
  return this.serial(args.sessionID, async () => {
776
797
  let state = await this.read(args.sessionID);
798
+ if (state?.rendered && state.rendered.id !== args.rendered.id)
799
+ state = undefined;
777
800
  let changed = !state;
778
- const renderedSha = foldDigest(JSON.stringify(args.rendered));
801
+ const observedSha = foldDigest(JSON.stringify(args.rendered));
802
+ const rendered = structuredClone(args.rendered);
803
+ if (state?.withoutRecentContext && state.withoutRecentContext === rendered.id)
804
+ stripCheckpointRecentContext(rendered, args.summary);
805
+ const renderedSha = foldDigest(JSON.stringify(rendered));
779
806
  if (!state) {
780
807
  args.onHard("boot_recovery");
781
808
  state = {
@@ -786,10 +813,10 @@ class FoldOwner {
786
813
  };
787
814
  }
788
815
  const reason = args.cutSeq < state.watermark ? "host_cut_before_watermark" : state.submittedSha !== foldDigest(args.summary) || state.renderedSha && state.renderedSha !== renderedSha ? "host_rerender" : undefined;
789
- if (reason && state.rejectedSha !== renderedSha) {
816
+ if (reason && state.rejectedSha !== observedSha) {
790
817
  args.onHard(reason);
791
818
  state.reason = reason;
792
- state.rejectedSha = renderedSha;
819
+ state.rejectedSha = observedSha;
793
820
  changed = true;
794
821
  }
795
822
  if (state.cutSeq === undefined && args.cutSeq >= state.watermark) {
@@ -797,7 +824,7 @@ class FoldOwner {
797
824
  changed = true;
798
825
  }
799
826
  if (!state.rendered && reason !== "host_cut_before_watermark") {
800
- state.rendered = structuredClone(args.rendered);
827
+ state.rendered = rendered;
801
828
  state.renderedSha = renderedSha;
802
829
  state.renderedSummary = args.summary;
803
830
  changed = true;
@@ -807,6 +834,50 @@ class FoldOwner {
807
834
  return state;
808
835
  });
809
836
  }
837
+ async stripRecentContext(sessionID, checkpointID) {
838
+ return this.serial(sessionID, async () => {
839
+ const state = await this.read(sessionID);
840
+ if (!state?.rendered || state.rendered.id !== checkpointID)
841
+ return;
842
+ if (state.withoutRecentContext === checkpointID)
843
+ return state;
844
+ if (!stripCheckpointRecentContext(state.rendered, state.renderedSummary ?? state.submitted))
845
+ return state;
846
+ state.withoutRecentContext = checkpointID;
847
+ state.renderedSha = foldDigest(JSON.stringify(state.rendered));
848
+ await this.storage.set(this.key(sessionID), state);
849
+ return state;
850
+ });
851
+ }
852
+ }
853
+ function stripCheckpointRecentContext(message, summary) {
854
+ let changed = false;
855
+ for (const part of message.content) {
856
+ if (part.type !== "text" || typeof part.text !== "string")
857
+ continue;
858
+ const summaryStart = part.text.indexOf("<summary>");
859
+ if (summaryStart < 0)
860
+ continue;
861
+ const summaryText = part.text.indexOf(summary, summaryStart + "<summary>".length);
862
+ if (summaryText < 0)
863
+ continue;
864
+ const start = part.text.indexOf("</summary>", summaryText + summary.length) + "</summary>".length;
865
+ const end = part.text.lastIndexOf("</conversation-checkpoint>");
866
+ if (start < "</summary>".length || end < start)
867
+ continue;
868
+ const suffix = part.text.slice(start, end);
869
+ if (!/^\s*<recent-context>[\s\S]*<\/recent-context>\s*$/.test(suffix))
870
+ continue;
871
+ part.text = `${part.text.slice(0, start)}
872
+ ${part.text.slice(end)}`;
873
+ changed = true;
874
+ }
875
+ if (changed && message.metadata && typeof message.metadata === "object") {
876
+ const metadata = { ...message.metadata };
877
+ delete metadata.recent;
878
+ message.metadata = metadata;
879
+ }
880
+ return changed;
810
881
  }
811
882
 
812
883
  // src/v2/fold/memory-cache.ts
@@ -2451,6 +2522,7 @@ function createNativeHiddenChildren(host, removeSession, options) {
2451
2522
  const inFlight = removing.get(child.id);
2452
2523
  if (inFlight)
2453
2524
  return inFlight;
2525
+ options.log(`[magic-context] hidden child ${child.id} removing`);
2454
2526
  const done = (async () => {
2455
2527
  try {
2456
2528
  let timer;
@@ -2465,6 +2537,7 @@ function createNativeHiddenChildren(host, removeSession, options) {
2465
2537
  if (timer)
2466
2538
  clearTimeout(timer);
2467
2539
  }
2540
+ options.log(`[magic-context] hidden child ${child.id} removed`);
2468
2541
  } catch (error) {
2469
2542
  if (isSessionNotFound(error))
2470
2543
  return;
@@ -2476,14 +2549,19 @@ function createNativeHiddenChildren(host, removeSession, options) {
2476
2549
  removing.set(child.id, done);
2477
2550
  return done;
2478
2551
  };
2479
- const confirmPlacement = async (sessionID, parentID) => {
2552
+ const noteCreated = (sessionID, role, parentID) => {
2553
+ options.log(`[magic-context] hidden child ${sessionID} created role=${role} parent=${parentID} keep_subagents=${options.keepSubagents}`);
2554
+ };
2555
+ const confirmPlacement = async (sessionID, parentID, role) => {
2480
2556
  let stored;
2481
2557
  try {
2482
2558
  stored = await host.get({ sessionID });
2483
2559
  } catch (error) {
2560
+ noteCreated(sessionID, role, "unknown");
2484
2561
  options.log(`[magic-context] could not read back hidden child ${sessionID} to confirm its parent: ${errorText(error)}`);
2485
2562
  return;
2486
2563
  }
2564
+ noteCreated(sessionID, role, stored.parentID ?? "none");
2487
2565
  const directory = stored.location?.directory;
2488
2566
  if (options.directory !== undefined && directory !== undefined && !sameProjectDirectory(directory, options.directory)) {
2489
2567
  throw new HiddenCompletionRefusal("hidden_prompt_unrecognized", `hidden child ${sessionID} is bound to ${directory}, not to this instance's ${options.directory}. Reopen the parent session in ${directory}, then run /ctx-recomp so its owning instance can rebuild history.`, true);
@@ -2512,11 +2590,13 @@ function createNativeHiddenChildren(host, removeSession, options) {
2512
2590
  };
2513
2591
  if (parentID) {
2514
2592
  try {
2515
- await confirmPlacement(created.id, parentID);
2593
+ await confirmPlacement(created.id, parentID, role);
2516
2594
  } catch (error) {
2517
2595
  await remove(child);
2518
2596
  throw error;
2519
2597
  }
2598
+ } else {
2599
+ noteCreated(created.id, role, "none");
2520
2600
  }
2521
2601
  return child;
2522
2602
  };
@@ -2525,16 +2605,22 @@ function createNativeHiddenChildren(host, removeSession, options) {
2525
2605
  open: create,
2526
2606
  create,
2527
2607
  retire(child) {
2528
- if (!kept(child))
2608
+ if (kept(child)) {
2609
+ options.log(`[magic-context] hidden child ${child.id} kept (keep_subagents)`);
2610
+ } else {
2529
2611
  remove(child);
2612
+ }
2530
2613
  },
2531
2614
  async finish(child, retired) {
2532
2615
  if (retired) {
2533
2616
  await removing.get(child.id);
2534
2617
  return;
2535
2618
  }
2536
- if (!kept(child))
2619
+ if (kept(child)) {
2620
+ options.log(`[magic-context] hidden child ${child.id} kept (keep_subagents)`);
2621
+ } else {
2537
2622
  await remove(child);
2623
+ }
2538
2624
  },
2539
2625
  updateModel: (child, model) => ({ ...child, model }),
2540
2626
  markEverSettled: (child) => ({ ...child, ever_settled: true })
@@ -2581,6 +2667,7 @@ async function cleanupLegacyHiddenChildren(db, remove, note, options = {}) {
2581
2667
  timer = setTimeout(() => reject(new Error("session.remove timed out")), Math.min(options.timeoutMs ?? 1000, Math.max(1, deadline - now())));
2582
2668
  })
2583
2669
  ]);
2670
+ note(`[magic-context] legacy hidden child ${child.id} removed`);
2584
2671
  } catch (error) {
2585
2672
  if (!isSessionNotFound(error))
2586
2673
  throw error;
@@ -3383,6 +3470,96 @@ class HiddenModelActivity {
3383
3470
  }
3384
3471
  }
3385
3472
 
3473
+ // src/v2/message-times.ts
3474
+ function isSettled(entry, readAt) {
3475
+ return (entry.version.type === "user" || entry.time.completed !== undefined) && typeof entry.version.updated === "number" && Number.isFinite(entry.version.updated) && entry.version.updated <= readAt - 1000;
3476
+ }
3477
+ function sameVersion(a, b) {
3478
+ return a.type === b.type && a.seq === b.seq && a.created === b.created && a.updated === b.updated;
3479
+ }
3480
+
3481
+ class V2MessageTimesCache {
3482
+ capacity;
3483
+ sessions = new Map;
3484
+ constructor(capacity = 64) {
3485
+ this.capacity = capacity;
3486
+ }
3487
+ delete(sessionID) {
3488
+ this.sessions.delete(sessionID);
3489
+ }
3490
+ deleteMessage(sessionID, messageID) {
3491
+ this.sessions.get(sessionID)?.entries.delete(messageID);
3492
+ }
3493
+ clear() {
3494
+ this.sessions.clear();
3495
+ }
3496
+ pass(sessionID) {
3497
+ const previous = this.sessions.get(sessionID);
3498
+ const current = { entries: new Map };
3499
+ this.sessions.delete(sessionID);
3500
+ this.sessions.set(sessionID, current);
3501
+ while (this.sessions.size > this.capacity) {
3502
+ const oldest = this.sessions.keys().next().value;
3503
+ if (oldest === undefined)
3504
+ break;
3505
+ this.sessions.delete(oldest);
3506
+ }
3507
+ const passTimes = new Map;
3508
+ return (reader, ids) => {
3509
+ const missing = [...new Set(ids)].filter((id) => !passTimes.has(id));
3510
+ if (missing.length > 0) {
3511
+ const readAt = Date.now();
3512
+ const identity = reader.hostIdentity();
3513
+ current.identity = identity;
3514
+ const reusable = previous?.identity === identity ? previous.entries : undefined;
3515
+ const known = [];
3516
+ const refresh = [];
3517
+ let first = Infinity;
3518
+ let last = -Infinity;
3519
+ for (const id of missing) {
3520
+ const entry = reusable?.get(id);
3521
+ if (entry && isSettled(entry, readAt)) {
3522
+ known.push(id);
3523
+ first = Math.min(first, entry.version.seq);
3524
+ last = Math.max(last, entry.version.seq);
3525
+ } else
3526
+ refresh.push(id);
3527
+ }
3528
+ const versions = reader.messageTimeVersions(sessionID, known, { first, last });
3529
+ for (const id of known) {
3530
+ const version = versions.get(id);
3531
+ const cached = reusable?.get(id);
3532
+ if (!version) {
3533
+ passTimes.set(id, null);
3534
+ } else if (cached && sameVersion(cached.version, version) && (version.type === "user" || cached.time.completed !== undefined)) {
3535
+ current.entries.set(id, cached);
3536
+ passTimes.set(id, cached.time);
3537
+ } else
3538
+ refresh.push(id);
3539
+ }
3540
+ const times = reader.messageTimes(sessionID, refresh, versions);
3541
+ for (const id of refresh) {
3542
+ const time = times.get(id);
3543
+ const version = versions.get(id);
3544
+ passTimes.set(id, time ?? null);
3545
+ if (time && version) {
3546
+ const entry = { version, time };
3547
+ if (isSettled(entry, readAt))
3548
+ current.entries.set(id, entry);
3549
+ }
3550
+ }
3551
+ }
3552
+ const result = new Map;
3553
+ for (const id of ids) {
3554
+ const time = passTimes.get(id);
3555
+ if (time)
3556
+ result.set(id, { ...time });
3557
+ }
3558
+ return result;
3559
+ };
3560
+ }
3561
+ }
3562
+
3386
3563
  // src/v2/hooks/channel2.ts
3387
3564
  import { randomBytes } from "node:crypto";
3388
3565
  var pendingCandidates = new WeakMap;
@@ -4871,6 +5048,28 @@ ${baseline}
4871
5048
  draft.messages.splice(0, draft.messages.length, checkpoint, ...draft.messages.filter((message) => message !== first));
4872
5049
  return true;
4873
5050
  }
5051
+ function estimateCheckpointRequest(draft) {
5052
+ let toolDefinitionTokens = 0;
5053
+ for (const tool of Object.values(draft.tools)) {
5054
+ const schema = JSON.stringify(tool.input);
5055
+ if (schema === undefined)
5056
+ throw new Error("Checkpoint request has an unserializable tool schema");
5057
+ toolDefinitionTokens += estimateTokens(tool.description) + estimateTokens(schema);
5058
+ }
5059
+ const estimateDraft = {
5060
+ ...draft,
5061
+ messages: draft.messages.map((message) => HEAD_IDS.some((id) => message.id === id) ? { ...message, id: undefined } : message)
5062
+ };
5063
+ return estimateFinalWireInputTokens({
5064
+ messages: adaptPayload(estimateDraft).messages,
5065
+ toolDefinitionTokens,
5066
+ systemPromptTokens: estimateTokens(JSON.stringify(draft.system)),
5067
+ providerID: draft.model.providerID,
5068
+ modelID: draft.model.id,
5069
+ agentName: draft.agent,
5070
+ sessionId: draft.sessionID
5071
+ });
5072
+ }
4874
5073
  var HIDDEN_SESSION_ERROR_GRACE_MS = 50;
4875
5074
  var STORE_POOL_DISPOSE_WAIT_MS = 1000;
4876
5075
  function isBlockingV2TransformError(error) {
@@ -5068,6 +5267,7 @@ function coveredInstructionRows(reader, sessionID, throughSeq, model) {
5068
5267
  async function registerContext(context) {
5069
5268
  const directory = context.location.directory;
5070
5269
  const config = loadPluginConfigDetailed(directory).config;
5270
+ log2(`[magic-context] OpenCode 2 keep_subagents=${config.keep_subagents === true}`);
5071
5271
  setHomeProjectPermission(config.allow_home_project);
5072
5272
  if (!config.enabled)
5073
5273
  return;
@@ -5200,6 +5400,7 @@ async function registerContext(context) {
5200
5400
  });
5201
5401
  let hiddenAgentsReady;
5202
5402
  const readerPool = new V2StoreReaderPool;
5403
+ const messageTimesCache = new V2MessageTimesCache;
5203
5404
  const openStoreReader = () => readerPool.open(gaDatabasePath(getDataDir(), process.env.OPENCODE_CHANNEL ?? "latest"));
5204
5405
  const createHiddenExecutor = async (database) => {
5205
5406
  await cleanupLegacyOnce(database);
@@ -5563,6 +5764,12 @@ async function registerContext(context) {
5563
5764
  if (event.type?.startsWith("session.revert.") || event.type === "session.message.content.updated") {
5564
5765
  nativeFoldReplay?.onEvent(event);
5565
5766
  restoredRows.forget(sessionID);
5767
+ messageTimesCache.delete(sessionID);
5768
+ }
5769
+ if (event.type === "session.step.started" || event.type === "session.step.ended" || event.type === "session.step.failed") {
5770
+ const messageID = event.data.assistantMessageID;
5771
+ if (typeof messageID === "string")
5772
+ messageTimesCache.deleteMessage(sessionID, messageID);
5566
5773
  }
5567
5774
  if (event.type === "session.error" || event.type === "session.execution.failed") {
5568
5775
  const error = hiddenTerminalError(event);
@@ -5601,6 +5808,7 @@ async function registerContext(context) {
5601
5808
  rawProviders.get(sessionID)?.();
5602
5809
  rawProviders.delete(sessionID);
5603
5810
  usage.delete(sessionID);
5811
+ messageTimesCache.delete(sessionID);
5604
5812
  hiddenSessionErrors.delete(sessionID);
5605
5813
  storageNoticeBySession.delete(sessionID);
5606
5814
  liveModels.delete(sessionID);
@@ -5694,7 +5902,7 @@ async function registerContext(context) {
5694
5902
  await context.session.hook("compaction", async (draft) => {
5695
5903
  const hiddenSummary = hiddenChildHook.compactionSummary(draft.sessionID, draft.agent);
5696
5904
  if (hiddenSummary !== undefined) {
5697
- draft.result = { summary: hiddenSummary };
5905
+ draft.result = { summary: hiddenSummary, metadata: { keepID: undefined } };
5698
5906
  return;
5699
5907
  }
5700
5908
  if (compactionOff)
@@ -5719,7 +5927,7 @@ async function registerContext(context) {
5719
5927
  previousCut: reader.latestCompaction(draft.sessionID),
5720
5928
  summary
5721
5929
  })) {
5722
- draft.result = { summary };
5930
+ draft.result = { summary, metadata: { keepID: undefined } };
5723
5931
  return;
5724
5932
  }
5725
5933
  }
@@ -5735,7 +5943,7 @@ async function registerContext(context) {
5735
5943
  materialize: () => moduleBaseline ?? materialize(draft)
5736
5944
  });
5737
5945
  sessionLog2(draft.sessionID, `v2 compaction hook: fired answered=true source=${source}`);
5738
- draft.result = { summary: fold.submitted };
5946
+ draft.result = { summary: fold.submitted, metadata: { keepID: undefined } };
5739
5947
  } catch (cause) {
5740
5948
  await refuseBeforeProvider(context.session, draft.sessionID, "compaction-fold", cause);
5741
5949
  throw new V2ContextRefusal("Magic Context could not preserve the host checkpoint.", {
@@ -5781,6 +5989,8 @@ async function registerContext(context) {
5781
5989
  ...args.messages.filter((message) => message !== args.incoming)
5782
5990
  ];
5783
5991
  };
5992
+ const checkpointPassPermissions = new Map;
5993
+ const checkpointInputEstimates = new Map;
5784
5994
  const initializeTransform = (database) => transform ??= createTransform({
5785
5995
  ...createV2TransformFeatureDeps(config, compactionOff, directory, embeddings.ensureRegistered),
5786
5996
  cacheTtlConfig: config.cache_ttl,
@@ -5790,6 +6000,10 @@ async function registerContext(context) {
5790
6000
  tagger,
5791
6001
  ...createV2ThresholdDeps(config),
5792
6002
  contextUsageMap: usage,
6003
+ hostInputEstimate: (sessionID) => checkpointInputEstimates.get(sessionID),
6004
+ onPassPermission: (sessionID, permission) => {
6005
+ checkpointPassPermissions.set(sessionID, permission);
6006
+ },
5793
6007
  compactionOff,
5794
6008
  storeGeneration: "v2",
5795
6009
  normalizeRestoredAssistantText: (text, providerID) => stripCopiedTagNotation(text, providerID === "anthropic"),
@@ -5895,22 +6109,9 @@ async function registerContext(context) {
5895
6109
  }
5896
6110
  await dropStorageNotices(draft.messages, isMagicContextSynthetic);
5897
6111
  const hostMessagesAtEntry = [...draft.messages];
5898
- const passTimes = new Map;
6112
+ const cachedTimes = messageTimesCache.pass(draft.sessionID);
5899
6113
  const readTimes = (reader, messages) => {
5900
- const ids = timeIds(messages);
5901
- const missing = ids.filter((id) => !passTimes.has(id));
5902
- if (missing.length > 0) {
5903
- const read = reader.messageTimes(draft.sessionID, missing);
5904
- for (const id of missing)
5905
- passTimes.set(id, read.get(id) ?? null);
5906
- }
5907
- const times = new Map;
5908
- for (const id of ids) {
5909
- const time = passTimes.get(id);
5910
- if (time)
5911
- times.set(id, time);
5912
- }
5913
- return times;
6114
+ return cachedTimes(reader, timeIds(messages));
5914
6115
  };
5915
6116
  liveModels.set(draft.sessionID, {
5916
6117
  providerID: draft.model.providerID,
@@ -6224,6 +6425,21 @@ async function registerContext(context) {
6224
6425
  }
6225
6426
  const mapped = adaptPayload(draft, admitted, messageTimes, anchorTimesOnly);
6226
6427
  bindSystemDateRequestIdentity(mapped, draft);
6428
+ checkpointPassPermissions.delete(draft.sessionID);
6429
+ checkpointInputEstimates.delete(draft.sessionID);
6430
+ if (checkpoint && submitted !== undefined) {
6431
+ const projected = structuredClone(checkpoint);
6432
+ if (stripCheckpointRecentContext(projected, submitted)) {
6433
+ const estimate = estimateCheckpointRequest({
6434
+ ...draft,
6435
+ messages: [checkpoint, ...draft.messages]
6436
+ });
6437
+ checkpointInputEstimates.set(draft.sessionID, {
6438
+ tokens: estimate.tokens,
6439
+ pressureTokens: estimate.pressureTokens ?? estimate.tokens
6440
+ });
6441
+ }
6442
+ }
6227
6443
  await createMessagesTransformHandler({
6228
6444
  magicContext: { "experimental.chat.messages.transform": transform },
6229
6445
  compactionOff,
@@ -6232,6 +6448,14 @@ async function registerContext(context) {
6232
6448
  rustReplayParticipant: () => transform?.getRustReplayParticipant() ?? null
6233
6449
  })({}, mapped);
6234
6450
  mapped.commit();
6451
+ const checkpointPermission = checkpointPassPermissions.get(draft.sessionID);
6452
+ checkpointPassPermissions.delete(draft.sessionID);
6453
+ checkpointInputEstimates.delete(draft.sessionID);
6454
+ if (checkpoint?.id && checkpointPermission && replayHead === undefined) {
6455
+ const stripped = await folds.stripRecentContext(draft.sessionID, checkpoint.id);
6456
+ if (stripped?.rendered)
6457
+ checkpoint = structuredClone(stripped.rendered);
6458
+ }
6235
6459
  stageOpenCodeV2TransformDecision({
6236
6460
  sessionId: draft.sessionID,
6237
6461
  responseAfterSequence
@@ -6310,17 +6534,45 @@ async function registerContext(context) {
6310
6534
  }
6311
6535
  }
6312
6536
  };
6537
+ const refuseOversizedCheckpoint = async (draft) => {
6538
+ const hasCheckpoint = draft.messages.some((message) => message.content.some((part) => part.type === "text" && typeof part.text === "string" && part.text.startsWith("<conversation-checkpoint>")));
6539
+ const limit = draft.model.limit?.input ?? draft.model.limit?.context;
6540
+ if (!hasCheckpoint || !limit)
6541
+ return;
6542
+ let estimate;
6543
+ try {
6544
+ estimate = estimateCheckpointRequest(draft);
6545
+ } catch (cause) {
6546
+ await refuseBeforeProvider(context.session, draft.sessionID, "checkpoint-fit-estimate", cause);
6547
+ throw new V2ContextRefusal("Magic Context could not size the host checkpoint.", {
6548
+ cause
6549
+ });
6550
+ }
6551
+ if (estimate.tokens <= limit)
6552
+ return;
6553
+ await refuseBeforeProvider(context.session, draft.sessionID, "checkpoint-over-window");
6554
+ throw new V2ContextRefusal("Magic Context refused an oversized host checkpoint.");
6555
+ };
6313
6556
  await context.session.hook("context", (draft) => withSqliteTransformPass2(async () => {
6314
6557
  const anchor = generateReplayAnchor(draft.messages);
6315
- await runManagedContext(draft);
6558
+ try {
6559
+ await runManagedContext(draft);
6560
+ if (!compactionOff)
6561
+ await refuseOversizedCheckpoint(draft);
6562
+ } finally {
6563
+ checkpointInputEstimates.delete(draft.sessionID);
6564
+ checkpointPassPermissions.delete(draft.sessionID);
6565
+ }
6316
6566
  if (!compactionOff)
6317
6567
  generateReplay.capture(draft, anchor);
6318
6568
  else
6319
6569
  sessionLog2(draft.sessionID, "generate replay capture skipped: compaction-disabled");
6320
6570
  }));
6321
6571
  await context.session.hook("generate", async (draft) => {
6322
- if (!compactionOff && !deletedSessions.has(draft.sessionID))
6572
+ if (!compactionOff && !deletedSessions.has(draft.sessionID)) {
6323
6573
  generateReplay.apply(draft);
6574
+ await refuseOversizedCheckpoint(draft);
6575
+ }
6324
6576
  });
6325
6577
  withoutSqliteTransformPass2(() => warmModelLimitCacheFromCatalog(context));
6326
6578
  const rpcLiveSessionState = createV2RpcLiveSessionState({
@@ -6435,6 +6687,7 @@ async function registerContext(context) {
6435
6687
  }, 0));
6436
6688
  return {
6437
6689
  async dispose() {
6690
+ messageTimesCache.clear();
6438
6691
  usageController.abort();
6439
6692
  await hiddenCompletionExecutor?.dispose();
6440
6693
  await usageDone;
@@ -38,9 +38,9 @@ export interface ParsedMemoryMapping {
38
38
  * as file-independent (that silently excluded memories from verify). Nested
39
39
  * `<file path="…"/>` children are accepted as an unambiguous alias. */
40
40
  export declare function parseMapMemoriesManifest(text: string): ParsedMemoryMapping[];
41
- /** Retry responses must contain a non-empty manifest with syntactically valid
42
- * entries and a closing root element, so the response is complete rather than
43
- * truncated. The mapper commits valid expected ids and retries only omissions.
41
+ /** Responses must contain a closed manifest with syntactically valid entries.
42
+ * A closed empty manifest is valid when no memory's mapping could be confirmed.
43
+ * The mapper commits valid expected ids; omissions follow its retry/backoff policy.
44
44
  * Duplicate expected ids are rejected; duplicate unknown ids are ignored. */
45
45
  export declare function validateMapMemoriesManifest(text: string, expectedIds: ReadonlySet<number>): ParsedMemoryMapping[];
46
46
  /** Recover only closed, individually valid decisions from an interrupted manifest.
@@ -1 +1 @@
1
- {"version":3,"file":"map-memories-prompt.d.ts","sourceRoot":"","sources":["../../../../src/features/magic-context/dreamer/map-memories-prompt.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;GAaG;AAEH,eAAO,MAAM,0BAA0B,8/GAsBsF,CAAC;AAM9H,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAW3C;oFACoF;AACpF,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAgBtF;AAED,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;CACxB;AAID;kFACkF;AAClF,wBAAsB,wBAAwB,CAC1C,QAAQ,EAAE,cAAc,EAAE,EAC1B,OAAO,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,EAAE,CAAC,CAmE3B;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,CAkB9F;AAED,MAAM,WAAW,mBAAmB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACxB;AA4BD;;;;wEAIwE;AACxE,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,EAAE,CAmC5E;AAED;;;8EAG8E;AAC9E,wBAAgB,2BAA2B,CACvC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,GACjC,mBAAmB,EAAE,CAQvB;AAED;qFACqF;AACrF,wBAAgB,+BAA+B,CAC3C,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,GACjC,mBAAmB,EAAE,CAqBvB"}
1
+ {"version":3,"file":"map-memories-prompt.d.ts","sourceRoot":"","sources":["../../../../src/features/magic-context/dreamer/map-memories-prompt.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;GAaG;AAEH,eAAO,MAAM,0BAA0B,8/GAsBsF,CAAC;AAM9H,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAW3C;oFACoF;AACpF,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAgBtF;AAED,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;CACxB;AAID;kFACkF;AAClF,wBAAsB,wBAAwB,CAC1C,QAAQ,EAAE,cAAc,EAAE,EAC1B,OAAO,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,EAAE,CAAC,CAmE3B;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,MAAM,CAkB9F;AAED,MAAM,WAAW,mBAAmB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACxB;AA4BD;;;;wEAIwE;AACxE,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,EAAE,CAmC5E;AAED;;;8EAG8E;AAC9E,wBAAgB,2BAA2B,CACvC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,GACjC,mBAAmB,EAAE,CAOvB;AAED;qFACqF;AACrF,wBAAgB,+BAA+B,CAC3C,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,GACjC,mBAAmB,EAAE,CAqBvB"}