@cortexkit/opencode-magic-context 0.33.0 → 0.33.1

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 (99) hide show
  1. package/dist/config/schema/magic-context.d.ts +62 -22
  2. package/dist/config/schema/magic-context.d.ts.map +1 -1
  3. package/dist/{conflict-warning-hook-bwa99a04.js → conflict-warning-hook-n48c783v.js} +1 -1
  4. package/dist/features/magic-context/compaction-marker.d.ts +49 -0
  5. package/dist/features/magic-context/compaction-marker.d.ts.map +1 -1
  6. package/dist/features/magic-context/compartment-chunk-embedding.d.ts +0 -2
  7. package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
  8. package/dist/features/magic-context/dreamer/task-registry.d.ts +0 -1
  9. package/dist/features/magic-context/dreamer/task-registry.d.ts.map +1 -1
  10. package/dist/features/magic-context/git-commits/index.d.ts +2 -2
  11. package/dist/features/magic-context/git-commits/index.d.ts.map +1 -1
  12. package/dist/features/magic-context/git-commits/storage-git-commit-embeddings.d.ts +0 -2
  13. package/dist/features/magic-context/git-commits/storage-git-commit-embeddings.d.ts.map +1 -1
  14. package/dist/features/magic-context/git-commits/storage-git-commits.d.ts +0 -13
  15. package/dist/features/magic-context/git-commits/storage-git-commits.d.ts.map +1 -1
  16. package/dist/features/magic-context/memory/embedding.d.ts +1 -2
  17. package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
  18. package/dist/features/magic-context/memory/memory-migration.d.ts +0 -6
  19. package/dist/features/magic-context/memory/memory-migration.d.ts.map +1 -1
  20. package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts +0 -1
  21. package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts.map +1 -1
  22. package/dist/features/magic-context/memory/storage-memory.d.ts +1 -6
  23. package/dist/features/magic-context/memory/storage-memory.d.ts.map +1 -1
  24. package/dist/features/magic-context/memory/verification-paths.d.ts +0 -1
  25. package/dist/features/magic-context/memory/verification-paths.d.ts.map +1 -1
  26. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  27. package/dist/features/magic-context/mural/storage-mural.d.ts +0 -1
  28. package/dist/features/magic-context/mural/storage-mural.d.ts.map +1 -1
  29. package/dist/features/magic-context/project-embedding-registry.d.ts +16 -2
  30. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  31. package/dist/features/magic-context/smart-notes/capabilities.d.ts +0 -2
  32. package/dist/features/magic-context/smart-notes/capabilities.d.ts.map +1 -1
  33. package/dist/features/magic-context/smart-notes/compiler.d.ts +0 -1
  34. package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
  35. package/dist/features/magic-context/smart-notes/types.d.ts +0 -16
  36. package/dist/features/magic-context/smart-notes/types.d.ts.map +1 -1
  37. package/dist/features/magic-context/storage-db.d.ts +1 -1
  38. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  39. package/dist/features/magic-context/storage-meta-persisted.d.ts +0 -1
  40. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  41. package/dist/features/magic-context/user-memory/storage-user-memory.d.ts +0 -2
  42. package/dist/features/magic-context/user-memory/storage-user-memory.d.ts.map +1 -1
  43. package/dist/hooks/auto-update-checker/types.d.ts +0 -3
  44. package/dist/hooks/auto-update-checker/types.d.ts.map +1 -1
  45. package/dist/hooks/magic-context/cache-busting-signals.d.ts +0 -1
  46. package/dist/hooks/magic-context/cache-busting-signals.d.ts.map +1 -1
  47. package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
  48. package/dist/hooks/magic-context/compaction-marker-manager.d.ts +49 -0
  49. package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
  50. package/dist/hooks/magic-context/compartment-parser.d.ts +12 -0
  51. package/dist/hooks/magic-context/compartment-parser.d.ts.map +1 -1
  52. package/dist/hooks/magic-context/compartment-runner-mapping.d.ts +0 -12
  53. package/dist/hooks/magic-context/compartment-runner-mapping.d.ts.map +1 -1
  54. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  55. package/dist/hooks/magic-context/historian-prompt.generated.d.ts +1 -1
  56. package/dist/hooks/magic-context/historian-prompt.generated.d.ts.map +1 -1
  57. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  58. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  59. package/dist/hooks/magic-context/inject-compartments.d.ts +9 -0
  60. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  61. package/dist/hooks/magic-context/sentinel.d.ts +39 -0
  62. package/dist/hooks/magic-context/sentinel.d.ts.map +1 -1
  63. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  64. package/dist/hooks/magic-context/tool-drop-target.d.ts +19 -0
  65. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  66. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  67. package/dist/{index-kxz4tn6m.js → index-56yjaeb2.js} +56 -4
  68. package/dist/{index-h86twgsp.js → index-c2gahrks.js} +516 -177
  69. package/dist/{index-bmkea2dq.js → index-ey1bc14z.js} +36 -26
  70. package/dist/{index-k5y60ymm.js → index-xyaekybw.js} +21 -2
  71. package/dist/index.js +83 -23
  72. package/dist/plugin/hooks/create-session-hooks.d.ts +8 -8
  73. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  74. package/dist/{recomp-orchestrator-yh7pepvx.js → recomp-orchestrator-2yb2hfh2.js} +2 -3
  75. package/dist/shared/conflict-detector.d.ts.map +1 -1
  76. package/dist/shared/sqlite.d.ts +9 -4
  77. package/dist/shared/sqlite.d.ts.map +1 -1
  78. package/dist/{storage-meta-session-1phy0b1z.js → storage-meta-session-td6bwtky.js} +1 -1
  79. package/dist/{task-config-dn9k9szr.js → task-config-5nx3qfz8.js} +1 -2
  80. package/package.json +2 -2
  81. package/src/shared/conflict-detector.test.ts +130 -1
  82. package/src/shared/conflict-detector.ts +34 -2
  83. package/src/shared/sqlite.ts +13 -33
  84. package/dist/config/schema.d.ts +0 -3
  85. package/dist/config/schema.d.ts.map +0 -1
  86. package/dist/features/magic-context/plugin-messages.d.ts +0 -75
  87. package/dist/features/magic-context/plugin-messages.d.ts.map +0 -1
  88. package/dist/hooks/magic-context/compartment-runner-state-xml.d.ts +0 -21
  89. package/dist/hooks/magic-context/compartment-runner-state-xml.d.ts.map +0 -1
  90. package/dist/index-anhrfygk.js +0 -57
  91. package/dist/shared/opencode-compaction-detector.d.ts +0 -2
  92. package/dist/shared/opencode-compaction-detector.d.ts.map +0 -1
  93. package/dist/shared/transcript-opencode.d.ts +0 -71
  94. package/dist/shared/transcript-opencode.d.ts.map +0 -1
  95. package/dist/tools/index.d.ts +0 -6
  96. package/dist/tools/index.d.ts.map +0 -1
  97. package/src/shared/opencode-compaction-detector.test.ts +0 -239
  98. package/src/shared/opencode-compaction-detector.ts +0 -81
  99. package/src/shared/transcript-opencode.ts +0 -302
@@ -2,9 +2,6 @@ import {
2
2
  deriveTriggerBudget,
3
3
  modelSupportsVision
4
4
  } from "./index-63dq2g4d.js";
5
- import {
6
- AGENTIC_DREAM_TASKS
7
- } from "./index-anhrfygk.js";
8
5
  import {
9
6
  parseProviderModel
10
7
  } from "./index-td9depx5.js";
@@ -32,10 +29,9 @@ import {
32
29
  persistCachedM0,
33
30
  readRawSessionMessages,
34
31
  readSessionChunk,
35
- registerPrivilegedWriter,
36
32
  updateSessionMeta,
37
33
  withPrivilegedWriter
38
- } from "./index-bmkea2dq.js";
34
+ } from "./index-ey1bc14z.js";
39
35
  import {
40
36
  log,
41
37
  sessionLog
@@ -1955,6 +1951,184 @@ function pullMemoryMirrorOnce(args) {
1955
1951
  mirrorFlights.set(args.module, flight);
1956
1952
  return flight;
1957
1953
  }
1954
+ // src/hooks/magic-context/compartment-parser.ts
1955
+ var COMPARTMENT_REGEX = /<compartment\s+([^>]*?)\s*>(.*?)<\/compartment>/gs;
1956
+ var ATTR_START_REGEX = /\bstart="(\d+)"/;
1957
+ var ATTR_END_REGEX = /\bend="(\d+)"/;
1958
+ var ATTR_TITLE_REGEX = /\btitle="([^"]*)"/;
1959
+ var ATTR_EPISODE_REGEX = /\bepisode_type="([^"]*)"/;
1960
+ var ATTR_IMPORTANCE_REGEX = /\bimportance="(\d+)"/;
1961
+ function makeTierOpenRegex(n) {
1962
+ return new RegExp(`<p${n}\\s*(/?)>`);
1963
+ }
1964
+ var TIER_OPEN_REGEXES = [
1965
+ makeTierOpenRegex(1),
1966
+ makeTierOpenRegex(2),
1967
+ makeTierOpenRegex(3),
1968
+ makeTierOpenRegex(4)
1969
+ ];
1970
+ var TIER_CLOSE_ANY_REGEX = /<\/p\d/;
1971
+ var TIER_OPEN_ANY_REGEX = /<p\d/;
1972
+ var CATEGORY_BLOCK_REGEX = /<(PROJECT_RULES|ARCHITECTURE|CONSTRAINTS|CONFIG_VALUES|NAMING)>(.*?)<\/\1>/gs;
1973
+ var FACT_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
1974
+ var UNPROCESSED_REGEX = /<unprocessed_from>(\d+)<\/unprocessed_from>/;
1975
+ var USER_OBSERVATIONS_REGEX = /<user_observations>(.*?)<\/user_observations>/s;
1976
+ var USER_OBS_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
1977
+ var PRIMER_CANDIDATES_REGEX = /<primer_candidates>(.*?)<\/primer_candidates>/s;
1978
+ var PRIMER_ELEMENT_REGEX = /<primer\s+at_compartment="(\d+)"\s*>(.*?)<\/primer>/gs;
1979
+ var PRIMER_ITEM_REGEX = /^\s*(?:\*|-|\d+\.)\s*(.+)$/gm;
1980
+ var FACTS_BLOCK_REGEX = /<facts>(.*?)<\/facts>/s;
1981
+ var EVENTS_BLOCK_REGEX = /<events>(.*?)<\/events>/s;
1982
+ var EVENT_ELEMENT_REGEX = /<([a-z_]+)\s+at_compartment="(\d+)"\s*>(.*?)<\/\1>/gs;
1983
+ var EVENT_FIELD_REGEX = /<([a-z_]+)\s*>(.*?)<\/\1>/gs;
1984
+ function extractTier(inner, index) {
1985
+ const openMatch = TIER_OPEN_REGEXES[index].exec(inner);
1986
+ if (!openMatch)
1987
+ return;
1988
+ if (openMatch[1] === "/")
1989
+ return "";
1990
+ const rest = inner.slice(openMatch.index + openMatch[0].length);
1991
+ const closeAt = rest.search(TIER_CLOSE_ANY_REGEX);
1992
+ let body = closeAt === -1 ? rest : rest.slice(0, closeAt);
1993
+ const openInside = body.search(TIER_OPEN_ANY_REGEX);
1994
+ if (openInside !== -1)
1995
+ body = body.slice(0, openInside);
1996
+ return unescapeXml(body.trim());
1997
+ }
1998
+ function extractTiersFromInner(inner) {
1999
+ return {
2000
+ p1: extractTier(inner, 0),
2001
+ p2: extractTier(inner, 1),
2002
+ p3: extractTier(inner, 2),
2003
+ p4: extractTier(inner, 3)
2004
+ };
2005
+ }
2006
+ function parseCompartmentOutput(text) {
2007
+ const compartments = [];
2008
+ const facts = [];
2009
+ for (const match of text.matchAll(COMPARTMENT_REGEX)) {
2010
+ const attrs = match[1];
2011
+ const inner = match[2];
2012
+ const startMatch = attrs.match(ATTR_START_REGEX);
2013
+ const endMatch = attrs.match(ATTR_END_REGEX);
2014
+ const titleMatch = attrs.match(ATTR_TITLE_REGEX);
2015
+ if (!startMatch || !endMatch || !titleMatch)
2016
+ continue;
2017
+ const startMessage = parseInt(startMatch[1], 10);
2018
+ const endMessage = parseInt(endMatch[1], 10);
2019
+ const title = unescapeXml(titleMatch[1]);
2020
+ if (Number.isNaN(startMessage) || Number.isNaN(endMessage) || !title)
2021
+ continue;
2022
+ const episodeMatch = attrs.match(ATTR_EPISODE_REGEX);
2023
+ const importanceMatch = attrs.match(ATTR_IMPORTANCE_REGEX);
2024
+ const episodeType = episodeMatch ? unescapeXml(episodeMatch[1]) : undefined;
2025
+ const importance = importanceMatch ? parseInt(importanceMatch[1], 10) : undefined;
2026
+ const p1 = extractTier(inner, 0);
2027
+ if (typeof p1 === "string" && p1.length > 0) {
2028
+ const p2 = extractTier(inner, 1);
2029
+ const p3 = extractTier(inner, 2);
2030
+ const p4 = extractTier(inner, 3);
2031
+ compartments.push({
2032
+ startMessage,
2033
+ endMessage,
2034
+ title,
2035
+ content: p1,
2036
+ p1,
2037
+ p2: typeof p2 === "string" ? p2 : p1,
2038
+ p3: typeof p3 === "string" ? p3 : typeof p2 === "string" ? p2 : p1,
2039
+ p4: typeof p4 === "string" ? p4 : "",
2040
+ importance,
2041
+ episodeType
2042
+ });
2043
+ continue;
2044
+ }
2045
+ const content = unescapeXml(inner.trim());
2046
+ if (content) {
2047
+ compartments.push({
2048
+ startMessage,
2049
+ endMessage,
2050
+ title,
2051
+ content,
2052
+ importance,
2053
+ episodeType
2054
+ });
2055
+ }
2056
+ }
2057
+ const factsBlockMatch = text.match(FACTS_BLOCK_REGEX);
2058
+ const factsScope = factsBlockMatch ? factsBlockMatch[1] : text.replace(EVENTS_BLOCK_REGEX, "").replace(/<compartment\s+[^>]*?\s*>.*?<\/compartment>/gs, "");
2059
+ for (const categoryMatch of factsScope.matchAll(CATEGORY_BLOCK_REGEX)) {
2060
+ const category = categoryMatch[1];
2061
+ const blockContent = categoryMatch[2];
2062
+ for (const itemMatch of blockContent.matchAll(FACT_ITEM_REGEX)) {
2063
+ const content = unescapeXml(itemMatch[1].trim());
2064
+ if (content) {
2065
+ facts.push({ category, content });
2066
+ }
2067
+ }
2068
+ }
2069
+ const unprocessedMatch = text.match(UNPROCESSED_REGEX);
2070
+ const unprocessedFrom = unprocessedMatch ? parseInt(unprocessedMatch[1], 10) : null;
2071
+ const userObservations = [];
2072
+ const userObsMatch = text.match(USER_OBSERVATIONS_REGEX);
2073
+ if (userObsMatch) {
2074
+ for (const itemMatch of userObsMatch[1].matchAll(USER_OBS_ITEM_REGEX)) {
2075
+ const obs = unescapeXml(itemMatch[1].trim());
2076
+ if (obs)
2077
+ userObservations.push(obs);
2078
+ }
2079
+ }
2080
+ const primerCandidates = [];
2081
+ const primerMatch = text.match(PRIMER_CANDIDATES_REGEX);
2082
+ if (primerMatch) {
2083
+ const block = primerMatch[1];
2084
+ let sawElement = false;
2085
+ for (const el of block.matchAll(PRIMER_ELEMENT_REGEX)) {
2086
+ sawElement = true;
2087
+ const question = unescapeXml(el[2].trim());
2088
+ if (question) {
2089
+ primerCandidates.push({
2090
+ question,
2091
+ originCompartmentIndex: Number.parseInt(el[1], 10)
2092
+ });
2093
+ }
2094
+ }
2095
+ if (!sawElement) {
2096
+ for (const itemMatch of block.matchAll(PRIMER_ITEM_REGEX)) {
2097
+ const question = unescapeXml(itemMatch[1].trim());
2098
+ if (question)
2099
+ primerCandidates.push({ question });
2100
+ }
2101
+ }
2102
+ }
2103
+ const events = parseEvents(text);
2104
+ compartments.sort((a, b) => a.startMessage - b.startMessage);
2105
+ return { compartments, facts, events, unprocessedFrom, userObservations, primerCandidates };
2106
+ }
2107
+ function parseEvents(text) {
2108
+ const blockMatch = text.match(EVENTS_BLOCK_REGEX);
2109
+ if (!blockMatch)
2110
+ return [];
2111
+ const block = blockMatch[1];
2112
+ const events = [];
2113
+ for (const elMatch of block.matchAll(EVENT_ELEMENT_REGEX)) {
2114
+ const kind = elMatch[1];
2115
+ const atRaw = parseInt(elMatch[2], 10);
2116
+ const atCompartment = Number.isNaN(atRaw) ? null : atRaw;
2117
+ const fields = {};
2118
+ for (const fieldMatch of elMatch[3].matchAll(EVENT_FIELD_REGEX)) {
2119
+ const name = fieldMatch[1];
2120
+ const value = unescapeXml(fieldMatch[2].trim());
2121
+ if (value)
2122
+ fields[name] = value;
2123
+ }
2124
+ events.push({ kind, atCompartment, fields });
2125
+ }
2126
+ return events;
2127
+ }
2128
+ function unescapeXml(s) {
2129
+ return s.replace(/&amp;/g, "&").replace(/&apos;/g, "'").replace(/&quot;/g, '"').replace(/&lt;/g, "<").replace(/&gt;/g, ">");
2130
+ }
2131
+
1958
2132
  // src/features/magic-context/storage-schema-helpers.ts
1959
2133
  function ensureColumn(db, table, column, definition) {
1960
2134
  if (!/^[a-z][a-z0-9_]*$/.test(table) || !/^[a-z][a-z0-9_]*$/.test(column) || !/^[A-Z0-9_"'(),[\]\s]+$/i.test(definition)) {
@@ -2704,15 +2878,38 @@ function isSqliteLockError(error) {
2704
2878
  function tableExists2(db, name) {
2705
2879
  return Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(name));
2706
2880
  }
2881
+ function healMismatchedTierClose(db, table, hasLegacy) {
2882
+ if (!tableExists2(db, table))
2883
+ return;
2884
+ const columns = new Set(db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name));
2885
+ for (const required of ["content", "p1", "p2", "p3", "p4"]) {
2886
+ if (!columns.has(required))
2887
+ return;
2888
+ }
2889
+ if (hasLegacy && !columns.has("legacy"))
2890
+ return;
2891
+ const predicate = hasLegacy ? "legacy = 1 AND p1 IS NULL AND content LIKE '%<p1%'" : "p1 IS NULL AND content LIKE '%<p1%'";
2892
+ const rows = db.prepare(`SELECT id, content FROM ${table} WHERE ${predicate}`).all();
2893
+ const update = db.prepare(`UPDATE ${table} SET p1 = ?, p2 = ?, p3 = ?, p4 = ?${hasLegacy ? ", legacy = 0" : ""} WHERE id = ?`);
2894
+ for (const row of rows) {
2895
+ const tiers = extractTiersFromInner(row.content);
2896
+ if (typeof tiers.p1 !== "string" || tiers.p1.length === 0)
2897
+ continue;
2898
+ const p1 = tiers.p1;
2899
+ const p2 = typeof tiers.p2 === "string" ? tiers.p2 : p1;
2900
+ const p3 = typeof tiers.p3 === "string" ? tiers.p3 : p2;
2901
+ const p4 = typeof tiers.p4 === "string" ? tiers.p4 : "";
2902
+ update.run(p1, p2, p3, p4, row.id);
2903
+ }
2904
+ }
2707
2905
  function assertForeignKeyIntegrity(db, table) {
2708
2906
  const rows = (table ? db.prepare(`PRAGMA foreign_key_check(${table})`) : db.prepare("PRAGMA foreign_key_check")).all();
2709
2907
  if (rows.length > 0) {
2710
2908
  throw new Error(`foreign_key_check failed after embedding table rebuild${table ? ` (${table})` : ""} (${rows.length} violation(s))`);
2711
2909
  }
2712
2910
  }
2713
- function authorityPrivilegeCheck(db) {
2714
- const native = db;
2715
- return typeof native.function === "function" || typeof native.createFunction === "function" ? "mc_privileged_writer() = 0" : "COALESCE((SELECT enabled FROM context_privilege_state WHERE id = 1), 0) = 0";
2911
+ function authorityPrivilegeCheck() {
2912
+ return "COALESCE((SELECT enabled FROM context_privilege_state WHERE id = 1), 0) = 0";
2716
2913
  }
2717
2914
  function managedAuthorityNoteRow(row) {
2718
2915
  return `(
@@ -2731,7 +2928,7 @@ function managedAuthorityNoteRow(row) {
2731
2928
  )`;
2732
2929
  }
2733
2930
  function installLatestAuthorityTriggers(db) {
2734
- const privilegeCheck = authorityPrivilegeCheck(db);
2931
+ const privilegeCheck = authorityPrivilegeCheck();
2735
2932
  if (tableExists2(db, "memories")) {
2736
2933
  db.exec(`
2737
2934
  DROP TRIGGER IF EXISTS memories_authority_guard_insert;
@@ -4657,6 +4854,21 @@ var MIGRATIONS = [
4657
4854
  ON memory_mutation_log(project_path, target_memory_id, id);
4658
4855
  `);
4659
4856
  }
4857
+ },
4858
+ {
4859
+ version: 70,
4860
+ description: "heal legacy compartments stranded by mismatched tier closing tags (issue #246)",
4861
+ up(db) {
4862
+ healMismatchedTierClose(db, "compartments", true);
4863
+ healMismatchedTierClose(db, "recomp_compartments", false);
4864
+ }
4865
+ },
4866
+ {
4867
+ version: 71,
4868
+ description: "rebuild authority guard triggers to the durable state-table form (issue #253)",
4869
+ up(db) {
4870
+ installLatestAuthorityTriggers(db);
4871
+ }
4660
4872
  }
4661
4873
  ];
4662
4874
  var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -5286,7 +5498,7 @@ var lastSchemaFenceRejection = null;
5286
5498
  function getSchemaFenceRejection() {
5287
5499
  return lastSchemaFenceRejection;
5288
5500
  }
5289
- var LATEST_SUPPORTED_VERSION = 69;
5501
+ var LATEST_SUPPORTED_VERSION = 71;
5290
5502
  var PERMISSIONS_ENFORCEABLE = process.platform !== "win32";
5291
5503
  function ensureSecureStorageDir(dir) {
5292
5504
  mkdirSync(dir, { recursive: true, mode: 448 });
@@ -6551,7 +6763,6 @@ function openDatabase(dbPathOrOptions) {
6551
6763
  if (!enforceSchemaFence(existing, dbPath, latestSupportedVersion)) {
6552
6764
  return null;
6553
6765
  }
6554
- registerPrivilegedWriter(existing);
6555
6766
  if (!persistenceByDatabase.has(existing)) {
6556
6767
  persistenceByDatabase.set(existing, true);
6557
6768
  }
@@ -6564,7 +6775,6 @@ function openDatabase(dbPathOrOptions) {
6564
6775
  }
6565
6776
  ensureSecureStorageDir(dbDir);
6566
6777
  const db = new Database(dbPath);
6567
- registerPrivilegedWriter(db);
6568
6778
  if (!enforceSchemaFence(db, dbPath, latestSupportedVersion)) {
6569
6779
  closeQuietly(db);
6570
6780
  return null;
@@ -6603,7 +6813,6 @@ async function openDatabaseAsync(dbPathOrOptions) {
6603
6813
  migrateLegacyStorageIfNeeded(dbPath, dbDir);
6604
6814
  ensureSecureStorageDir(dbDir);
6605
6815
  db = new Database(dbPath);
6606
- registerPrivilegedWriter(db);
6607
6816
  if (!enforceSchemaFence(db, dbPath, latestSupportedVersion)) {
6608
6817
  closeQuietly(db);
6609
6818
  return null;
@@ -9636,7 +9845,6 @@ class ModuleMemoryAuthorityError extends Error {
9636
9845
  }
9637
9846
  }
9638
9847
  function assertTsMemoryWriteAllowed(db, projectPath) {
9639
- registerPrivilegedWriter(db);
9640
9848
  try {
9641
9849
  const managed = db.prepare("SELECT 1 FROM authority_managed WHERE project_path = ? UNION SELECT 1 FROM authority_repair_pending WHERE project_path = ? LIMIT 1").get(projectPath, projectPath);
9642
9850
  if (managed)
@@ -25346,8 +25554,7 @@ var EXECUTE_THRESHOLD_CAP_MESSAGE = "execute_threshold is capped at 80% for cach
25346
25554
  var DEFAULT_HISTORIAN_TIMEOUT_MS = 300000;
25347
25555
  var DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15;
25348
25556
  var DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2";
25349
- var DreamingTaskSchema = exports_external.enum(AGENTIC_DREAM_TASKS);
25350
- var PiThinkingLevelSchema = exports_external.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional();
25557
+ var PiThinkingLevelSchema = exports_external.enum(["off", "minimal", "low", "medium", "high", "xhigh", "max"]).optional();
25351
25558
  var PiConfigSchema = exports_external.object({
25352
25559
  subagent_extensions: exports_external.array(exports_external.string().trim().min(1)).optional().describe("User-only allowlist of Pi extensions for Magic Context subagent children. When set, children use --no-extensions and load only these entries (plus Magic Context's scoped child extension where applicable). Relative paths resolve from ~/.pi/agent, matching Pi's settings.json package location. Unset preserves normal Pi extension discovery.")
25353
25560
  }).optional();
@@ -25420,7 +25627,7 @@ var SidekickConfigSchema = AgentOverrideConfigSchema.extend({
25420
25627
  }).optional();
25421
25628
  var HistorianConfigSchema = AgentOverrideConfigSchema.extend({
25422
25629
  two_pass: exports_external.boolean().default(false).describe("Run a second editor pass over historian output to clean low-signal U: lines and cross-compartment duplicates. Adds ~1 extra API call and ~1.3x cost per historian run. Useful for models without extended thinking support. (default: false)"),
25423
- thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level passed as --thinking <level> to Pi historian subagent invocations. Required when using reasoning models (e.g. github-copilot/gpt-5.4) because Pi's default thinking-level resolution can pick a value the provider rejects. OpenCode users set variant instead. Valid: off | minimal | low | medium | high | xhigh"),
25630
+ thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level passed as --thinking <level> to Pi historian subagent invocations. Required when using reasoning models (e.g. github-copilot/gpt-5.4) because Pi's default thinking-level resolution can pick a value the provider rejects. OpenCode users set variant instead. Valid: off | minimal | low | medium | high | xhigh | max"),
25424
25631
  disallowed_tools: exports_external.array(exports_external.enum(["*", "read", "aft_outline", "aft_zoom", "aft_search"])).default([]).describe(`OpenCode only. Tools to REMOVE from the historian's default allow-list [read, aft_outline, aft_zoom, aft_search]. Applies to both historian and historian-editor agents. Use ["*"] to strip all tool definitions from the model request — this prevents weak instruction-following models (e.g. mistral-small-latest) from entering tool-calling loops. Individual tool names remove just that tool. Note: a user-supplied historian.permission override can re-allow a tool that disallowed_tools removed — disallowed_tools sets the baseline, permission overrides take precedence. (default: [])`)
25425
25632
  }).optional();
25426
25633
  var EmbeddingFallbackProviderSchema = exports_external.enum(["local", "openai-compatible", "off"]);
@@ -29376,6 +29583,9 @@ var shadowWorker = null;
29376
29583
  var SHADOW_MAX_ITEMS_PER_TICK = 64;
29377
29584
  var SHADOW_MAX_BYTES_PER_TICK = 512 * 1024;
29378
29585
  var SHADOW_MAX_WALL_CLOCK_MS = 2000;
29586
+ var pendingShadowBackfills = new Map;
29587
+ var shadowBackfillLastIds = new Map;
29588
+ var shadowBackfillStopReasons = new Map;
29379
29589
  var loadUnembeddedMemoriesStatements = new WeakMap;
29380
29590
  var upsertActiveIdentityStatements = new WeakMap;
29381
29591
  var backfillActiveIdentityStatements = new Map;
@@ -29910,6 +30120,7 @@ function registerProjectShadowEmbedding(db, projectIdentity, config2, sourceDire
29910
30120
  recordScopeActiveIdentity(db, projectIdentity, "chunk", registration.chunkModelId, now);
29911
30121
  persistShadowDescriptor(db, registration);
29912
30122
  })();
30123
+ maybeArmShadowBackfill(db, projectIdentity, registration);
29913
30124
  return {
29914
30125
  projectIdentity,
29915
30126
  sourceDirectory,
@@ -29930,7 +30141,7 @@ function startShadowWorker() {
29930
30141
  return;
29931
30142
  shadowWorker = runShadowWorker().finally(() => {
29932
30143
  shadowWorker = null;
29933
- if (shadowQueue.length > 0)
30144
+ if (shadowQueue.length > 0 || hasPendingShadowBackfill())
29934
30145
  startShadowWorker();
29935
30146
  });
29936
30147
  }
@@ -29977,6 +30188,117 @@ function enqueueShadowEmbeddingItems(projectIdentity, scope, ids) {
29977
30188
  shadowQueue.push({ projectIdentity, scope, ids: [...ids] });
29978
30189
  startShadowWorker();
29979
30190
  }
30191
+ function shadowBackfillMissingBase(scope, primaryModelId, shadowModelId, projectIdentity) {
30192
+ if (scope === "memory") {
30193
+ return {
30194
+ sql: `SELECT m.id AS id
30195
+ FROM memories m
30196
+ JOIN memory_embeddings mp ON mp.memory_id = m.id AND mp.model_id = ?
30197
+ LEFT JOIN memory_embeddings ms ON ms.memory_id = m.id AND ms.model_id = ?
30198
+ WHERE m.project_path = ? AND m.status = 'active' AND ms.memory_id IS NULL`,
30199
+ params: [primaryModelId, shadowModelId, projectIdentity],
30200
+ orderBy: " ORDER BY m.id"
30201
+ };
30202
+ }
30203
+ if (scope === "commit") {
30204
+ return {
30205
+ sql: `SELECT gc.sha AS id
30206
+ FROM git_commits gc
30207
+ JOIN git_commit_embeddings gp ON gp.sha = gc.sha AND gp.model_id = ?
30208
+ LEFT JOIN git_commit_embeddings gs ON gs.sha = gc.sha AND gs.model_id = ?
30209
+ WHERE gc.project_path = ? AND gs.sha IS NULL`,
30210
+ params: [primaryModelId, shadowModelId, projectIdentity],
30211
+ orderBy: " ORDER BY gc.committed_at DESC, gc.sha"
30212
+ };
30213
+ }
30214
+ return {
30215
+ sql: `SELECT DISTINCT cp.compartment_id AS id
30216
+ FROM compartment_chunk_embeddings cp
30217
+ WHERE cp.project_path = ? AND cp.model_id = ?
30218
+ AND NOT EXISTS (
30219
+ SELECT 1 FROM compartment_chunk_embeddings cs
30220
+ WHERE cs.compartment_id = cp.compartment_id AND cs.model_id = ?
30221
+ )`,
30222
+ params: [projectIdentity, primaryModelId, shadowModelId],
30223
+ orderBy: " ORDER BY cp.compartment_id"
30224
+ };
30225
+ }
30226
+ function shadowBackfillMissingIds(db, projectIdentity, scope, primaryModelId, shadowModelId, limit) {
30227
+ const { sql, params, orderBy } = shadowBackfillMissingBase(scope, primaryModelId, shadowModelId, projectIdentity);
30228
+ const rows = db.prepare(`${sql}${orderBy} LIMIT ?`).all(...params, limit);
30229
+ return rows.map((row) => String(row.id));
30230
+ }
30231
+ function shadowModelIdForScope(registration, scope) {
30232
+ return scope === "chunk" ? registration.chunkModelId : registration.modelId;
30233
+ }
30234
+ function hasPendingShadowBackfill(projectIdentity) {
30235
+ if (projectIdentity === undefined)
30236
+ return pendingShadowBackfills.size > 0;
30237
+ const scopes = pendingShadowBackfills.get(projectIdentity);
30238
+ return scopes !== undefined && scopes.size > 0;
30239
+ }
30240
+ function pumpShadowBackfill() {
30241
+ for (const [projectIdentity, scopes] of pendingShadowBackfills) {
30242
+ const db = dbForShadowQueue.get(projectIdentity);
30243
+ const shadow = shadowRegistrations.get(projectIdentity);
30244
+ const primary = projectRegistrations.get(projectIdentity);
30245
+ if (!db || !shadow || !primary) {
30246
+ pendingShadowBackfills.delete(projectIdentity);
30247
+ continue;
30248
+ }
30249
+ for (const scope of [...scopes]) {
30250
+ const primaryModelId = shadowModelIdForScope(primary, scope);
30251
+ const shadowModelId = shadowModelIdForScope(shadow, scope);
30252
+ const stallKey = `${projectIdentity}:${scope}`;
30253
+ if (primaryModelId === "off" || shadowModelId === "off") {
30254
+ scopes.delete(scope);
30255
+ shadowBackfillLastIds.delete(stallKey);
30256
+ continue;
30257
+ }
30258
+ const ids = shadowBackfillMissingIds(db, projectIdentity, scope, primaryModelId, shadowModelId, SHADOW_MAX_ITEMS_PER_TICK);
30259
+ if (ids.length === 0) {
30260
+ shadowBackfillStopReasons.set(stallKey, "drained");
30261
+ scopes.delete(scope);
30262
+ shadowBackfillLastIds.delete(stallKey);
30263
+ continue;
30264
+ }
30265
+ const signature = ids.join(",");
30266
+ if (shadowBackfillLastIds.get(stallKey) === signature) {
30267
+ shadowBackfillStopReasons.set(stallKey, "stalled_no_progress");
30268
+ log(`[shadow] backfill scope ${scope} for ${projectIdentity} retired without progress — ` + `the last batch produced no writes (provider failure or timeout is the usual cause); ` + `${ids.length}+ items remain and retry on the next registration or manual --shadow run`);
30269
+ scopes.delete(scope);
30270
+ shadowBackfillLastIds.delete(stallKey);
30271
+ continue;
30272
+ }
30273
+ shadowBackfillLastIds.set(stallKey, signature);
30274
+ shadowQueue.push({ projectIdentity, scope, ids });
30275
+ }
30276
+ if (scopes.size === 0)
30277
+ pendingShadowBackfills.delete(projectIdentity);
30278
+ }
30279
+ }
30280
+ function maybeArmShadowBackfill(db, projectIdentity, shadow) {
30281
+ if (untrustedLoadProjects.has(projectIdentity))
30282
+ return;
30283
+ const primary = projectRegistrations.get(projectIdentity);
30284
+ if (!primary)
30285
+ return;
30286
+ const pending = new Set;
30287
+ for (const scope of ["memory", "commit", "chunk"]) {
30288
+ const primaryModelId = shadowModelIdForScope(primary, scope);
30289
+ const shadowModelId = shadowModelIdForScope(shadow, scope);
30290
+ if (primaryModelId === "off" || shadowModelId === "off")
30291
+ continue;
30292
+ const probe = shadowBackfillMissingIds(db, projectIdentity, scope, primaryModelId, shadowModelId, 1);
30293
+ if (probe.length > 0)
30294
+ pending.add(scope);
30295
+ }
30296
+ if (pending.size === 0)
30297
+ return;
30298
+ pendingShadowBackfills.set(projectIdentity, pending);
30299
+ pumpShadowBackfill();
30300
+ startShadowWorker();
30301
+ }
29980
30302
  async function embedShadowItems(registration, items, db, scope) {
29981
30303
  const raw = registration.config;
29982
30304
  const fingerprint = typeof raw.synapse_fingerprint === "string" ? raw.synapse_fingerprint : "";
@@ -30100,7 +30422,15 @@ async function runShadowWorker() {
30100
30422
  const startedAt = Date.now();
30101
30423
  let processed = 0;
30102
30424
  let processedBytes = 0;
30103
- while (shadowQueue.length > 0 && processed < SHADOW_MAX_ITEMS_PER_TICK && Date.now() - startedAt < SHADOW_MAX_WALL_CLOCK_MS) {
30425
+ for (;; ) {
30426
+ if (shadowQueue.length === 0) {
30427
+ pumpShadowBackfill();
30428
+ if (shadowQueue.length === 0)
30429
+ break;
30430
+ }
30431
+ if (processed >= SHADOW_MAX_ITEMS_PER_TICK || Date.now() - startedAt >= SHADOW_MAX_WALL_CLOCK_MS) {
30432
+ break;
30433
+ }
30104
30434
  const item = shadowQueue.shift();
30105
30435
  if (!item)
30106
30436
  break;
@@ -31508,6 +31838,53 @@ function injectCompactionMarker(args) {
31508
31838
  return null;
31509
31839
  }
31510
31840
  }
31841
+ function listSessionCompactionMarkers(sessionId) {
31842
+ const db = getWritableOpenCodeDb();
31843
+ const partRows = db.prepare(`SELECT id, message_id
31844
+ FROM part
31845
+ WHERE session_id = ?
31846
+ AND COALESCE(json_extract(data, '$.type'), '') = 'compaction'`).all(sessionId);
31847
+ const markers = [];
31848
+ const summaryStmt = db.prepare(`SELECT id
31849
+ FROM message
31850
+ WHERE session_id = ?
31851
+ AND COALESCE(json_extract(data, '$.parentID'), '') = ?
31852
+ AND COALESCE(json_extract(data, '$.summary'), 0) = 1
31853
+ AND COALESCE(json_extract(data, '$.finish'), '') = 'stop'
31854
+ AND COALESCE(json_extract(data, '$.providerID'), '') = 'magic-context'`);
31855
+ for (const row of partRows) {
31856
+ if (typeof row.id !== "string" || typeof row.message_id !== "string")
31857
+ continue;
31858
+ const summaryRows = summaryStmt.all(sessionId, row.message_id);
31859
+ const summaryMessageIds = summaryRows.flatMap((summaryRow) => typeof summaryRow.id === "string" ? [summaryRow.id] : []);
31860
+ markers.push({
31861
+ compactionPartId: row.id,
31862
+ boundaryMessageId: row.message_id,
31863
+ summaryMessageIds
31864
+ });
31865
+ }
31866
+ return markers;
31867
+ }
31868
+ function removeForeignCompactionMarker(sessionId, marker, protectedSummaryMessageId) {
31869
+ try {
31870
+ const db = getWritableOpenCodeDb();
31871
+ db.transaction(() => {
31872
+ const deletePartsOfMessage = db.prepare("DELETE FROM part WHERE session_id = ? AND message_id = ?");
31873
+ const deleteMessage = db.prepare("DELETE FROM message WHERE session_id = ? AND id = ?");
31874
+ for (const summaryMessageId of marker.summaryMessageIds) {
31875
+ if (summaryMessageId === protectedSummaryMessageId)
31876
+ continue;
31877
+ deletePartsOfMessage.run(sessionId, summaryMessageId);
31878
+ deleteMessage.run(sessionId, summaryMessageId);
31879
+ }
31880
+ db.prepare("DELETE FROM part WHERE session_id = ? AND id = ?").run(sessionId, marker.compactionPartId);
31881
+ })();
31882
+ return true;
31883
+ } catch (error51) {
31884
+ log(`[magic-context] compaction-marker: foreign-marker removal failed (${sessionId}, part ${marker.compactionPartId}): ${error51 instanceof Error ? error51.message : String(error51)}`);
31885
+ return false;
31886
+ }
31887
+ }
31511
31888
  function removeCompactionMarker(state) {
31512
31889
  try {
31513
31890
  const db = getWritableOpenCodeDb();
@@ -31708,6 +32085,50 @@ function removeCompactionMarkerForSession(db, sessionId) {
31708
32085
  }
31709
32086
  }
31710
32087
  }
32088
+ function reconcileForkOrphanedCompactionMarkers(db, sessionId) {
32089
+ if (getHarness() !== "opencode") {
32090
+ return { removed: 0, failed: false };
32091
+ }
32092
+ try {
32093
+ const owned = getPersistedCompactionMarkerState(db, sessionId);
32094
+ if (!owned) {
32095
+ return { removed: 0, failed: false };
32096
+ }
32097
+ const markers = listSessionCompactionMarkers(sessionId);
32098
+ let removed = 0;
32099
+ let failed = false;
32100
+ for (const marker of markers) {
32101
+ if (marker.compactionPartId === owned.compactionPartId) {
32102
+ continue;
32103
+ }
32104
+ if (marker.summaryMessageIds.length === 0) {
32105
+ continue;
32106
+ }
32107
+ const ordering = compareOpenCodeMessagesByCanonicalOrder(sessionId, marker.boundaryMessageId, owned.boundaryMessageId);
32108
+ if (ordering === null || ordering <= 0) {
32109
+ continue;
32110
+ }
32111
+ const currentOwned = getPersistedCompactionMarkerState(db, sessionId);
32112
+ if (!currentOwned || marker.compactionPartId === currentOwned.compactionPartId) {
32113
+ continue;
32114
+ }
32115
+ const ok = removeForeignCompactionMarker(sessionId, marker, currentOwned.summaryMessageId);
32116
+ if (ok) {
32117
+ removed += 1;
32118
+ sessionLog(sessionId, `compaction-marker hygiene: removed fork-orphaned marker part=${marker.compactionPartId} boundary=${marker.boundaryMessageId} (outranked owned boundary ${currentOwned.boundaryMessageId})`);
32119
+ } else {
32120
+ failed = true;
32121
+ }
32122
+ }
32123
+ if (removed > 0) {
32124
+ log(`[magic-context][${sessionId}] compaction-marker hygiene: removed ${removed} fork-orphaned marker(s); filterCompacted will now stop at this session's own marker`);
32125
+ }
32126
+ return { removed, failed };
32127
+ } catch (error51) {
32128
+ sessionLog(sessionId, "compaction-marker hygiene: scan failed (will retry on next degraded pass):", error51);
32129
+ return { removed: 0, failed: true };
32130
+ }
32131
+ }
31711
32132
  function checkCompactionMarkerConsistency(db) {
31712
32133
  const opencodeDbPath = join5(getDataDir(), "opencode", "opencode.db");
31713
32134
  let opencodeDb;
@@ -32468,6 +32889,8 @@ If no candidates, omit the \`<primer_candidates>\` section entirely.
32468
32889
 
32469
32890
  Output valid XML only in this shape:
32470
32891
 
32892
+ Closing tags must match their opening tier tag (e.g. \`<p1>...</p1>\`, never \`<p1>...</p2>\`).
32893
+
32471
32894
  \`\`\`xml
32472
32895
  <output>
32473
32896
  <compartments>
@@ -32778,161 +33201,6 @@ function recordChildInvocation(input) {
32778
33201
  }
32779
33202
  }
32780
33203
 
32781
- // src/hooks/magic-context/compartment-parser.ts
32782
- var COMPARTMENT_REGEX = /<compartment\s+([^>]*?)\s*>(.*?)<\/compartment>/gs;
32783
- var ATTR_START_REGEX = /\bstart="(\d+)"/;
32784
- var ATTR_END_REGEX = /\bend="(\d+)"/;
32785
- var ATTR_TITLE_REGEX = /\btitle="([^"]*)"/;
32786
- var ATTR_EPISODE_REGEX = /\bepisode_type="([^"]*)"/;
32787
- var ATTR_IMPORTANCE_REGEX = /\bimportance="(\d+)"/;
32788
- function makeTierRegex(n) {
32789
- return new RegExp(`<p${n}\\s*/>|<p${n}\\s*>(.*?)</p${n}>`, "s");
32790
- }
32791
- var TIER_REGEXES = [makeTierRegex(1), makeTierRegex(2), makeTierRegex(3), makeTierRegex(4)];
32792
- var CATEGORY_BLOCK_REGEX = /<(PROJECT_RULES|ARCHITECTURE|CONSTRAINTS|CONFIG_VALUES|NAMING)>(.*?)<\/\1>/gs;
32793
- var FACT_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
32794
- var UNPROCESSED_REGEX = /<unprocessed_from>(\d+)<\/unprocessed_from>/;
32795
- var USER_OBSERVATIONS_REGEX = /<user_observations>(.*?)<\/user_observations>/s;
32796
- var USER_OBS_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
32797
- var PRIMER_CANDIDATES_REGEX = /<primer_candidates>(.*?)<\/primer_candidates>/s;
32798
- var PRIMER_ELEMENT_REGEX = /<primer\s+at_compartment="(\d+)"\s*>(.*?)<\/primer>/gs;
32799
- var PRIMER_ITEM_REGEX = /^\s*(?:\*|-|\d+\.)\s*(.+)$/gm;
32800
- var FACTS_BLOCK_REGEX = /<facts>(.*?)<\/facts>/s;
32801
- var EVENTS_BLOCK_REGEX = /<events>(.*?)<\/events>/s;
32802
- var EVENT_ELEMENT_REGEX = /<([a-z_]+)\s+at_compartment="(\d+)"\s*>(.*?)<\/\1>/gs;
32803
- var EVENT_FIELD_REGEX = /<([a-z_]+)\s*>(.*?)<\/\1>/gs;
32804
- function extractTier(inner, index) {
32805
- const m = inner.match(TIER_REGEXES[index]);
32806
- if (!m)
32807
- return;
32808
- return unescapeXml((m[1] ?? "").trim());
32809
- }
32810
- function parseCompartmentOutput(text) {
32811
- const compartments = [];
32812
- const facts = [];
32813
- for (const match of text.matchAll(COMPARTMENT_REGEX)) {
32814
- const attrs = match[1];
32815
- const inner = match[2];
32816
- const startMatch = attrs.match(ATTR_START_REGEX);
32817
- const endMatch = attrs.match(ATTR_END_REGEX);
32818
- const titleMatch = attrs.match(ATTR_TITLE_REGEX);
32819
- if (!startMatch || !endMatch || !titleMatch)
32820
- continue;
32821
- const startMessage = parseInt(startMatch[1], 10);
32822
- const endMessage = parseInt(endMatch[1], 10);
32823
- const title = unescapeXml(titleMatch[1]);
32824
- if (Number.isNaN(startMessage) || Number.isNaN(endMessage) || !title)
32825
- continue;
32826
- const episodeMatch = attrs.match(ATTR_EPISODE_REGEX);
32827
- const importanceMatch = attrs.match(ATTR_IMPORTANCE_REGEX);
32828
- const episodeType = episodeMatch ? unescapeXml(episodeMatch[1]) : undefined;
32829
- const importance = importanceMatch ? parseInt(importanceMatch[1], 10) : undefined;
32830
- const p1 = extractTier(inner, 0);
32831
- if (typeof p1 === "string" && p1.length > 0) {
32832
- const p2 = extractTier(inner, 1);
32833
- const p3 = extractTier(inner, 2);
32834
- const p4 = extractTier(inner, 3);
32835
- compartments.push({
32836
- startMessage,
32837
- endMessage,
32838
- title,
32839
- content: p1,
32840
- p1,
32841
- p2: typeof p2 === "string" ? p2 : p1,
32842
- p3: typeof p3 === "string" ? p3 : typeof p2 === "string" ? p2 : p1,
32843
- p4: typeof p4 === "string" ? p4 : "",
32844
- importance,
32845
- episodeType
32846
- });
32847
- continue;
32848
- }
32849
- const content = unescapeXml(inner.trim());
32850
- if (content) {
32851
- compartments.push({
32852
- startMessage,
32853
- endMessage,
32854
- title,
32855
- content,
32856
- importance,
32857
- episodeType
32858
- });
32859
- }
32860
- }
32861
- const factsBlockMatch = text.match(FACTS_BLOCK_REGEX);
32862
- const factsScope = factsBlockMatch ? factsBlockMatch[1] : text.replace(EVENTS_BLOCK_REGEX, "").replace(/<compartment\s+[^>]*?\s*>.*?<\/compartment>/gs, "");
32863
- for (const categoryMatch of factsScope.matchAll(CATEGORY_BLOCK_REGEX)) {
32864
- const category = categoryMatch[1];
32865
- const blockContent = categoryMatch[2];
32866
- for (const itemMatch of blockContent.matchAll(FACT_ITEM_REGEX)) {
32867
- const content = unescapeXml(itemMatch[1].trim());
32868
- if (content) {
32869
- facts.push({ category, content });
32870
- }
32871
- }
32872
- }
32873
- const unprocessedMatch = text.match(UNPROCESSED_REGEX);
32874
- const unprocessedFrom = unprocessedMatch ? parseInt(unprocessedMatch[1], 10) : null;
32875
- const userObservations = [];
32876
- const userObsMatch = text.match(USER_OBSERVATIONS_REGEX);
32877
- if (userObsMatch) {
32878
- for (const itemMatch of userObsMatch[1].matchAll(USER_OBS_ITEM_REGEX)) {
32879
- const obs = unescapeXml(itemMatch[1].trim());
32880
- if (obs)
32881
- userObservations.push(obs);
32882
- }
32883
- }
32884
- const primerCandidates = [];
32885
- const primerMatch = text.match(PRIMER_CANDIDATES_REGEX);
32886
- if (primerMatch) {
32887
- const block = primerMatch[1];
32888
- let sawElement = false;
32889
- for (const el of block.matchAll(PRIMER_ELEMENT_REGEX)) {
32890
- sawElement = true;
32891
- const question = unescapeXml(el[2].trim());
32892
- if (question) {
32893
- primerCandidates.push({
32894
- question,
32895
- originCompartmentIndex: Number.parseInt(el[1], 10)
32896
- });
32897
- }
32898
- }
32899
- if (!sawElement) {
32900
- for (const itemMatch of block.matchAll(PRIMER_ITEM_REGEX)) {
32901
- const question = unescapeXml(itemMatch[1].trim());
32902
- if (question)
32903
- primerCandidates.push({ question });
32904
- }
32905
- }
32906
- }
32907
- const events = parseEvents(text);
32908
- compartments.sort((a, b) => a.startMessage - b.startMessage);
32909
- return { compartments, facts, events, unprocessedFrom, userObservations, primerCandidates };
32910
- }
32911
- function parseEvents(text) {
32912
- const blockMatch = text.match(EVENTS_BLOCK_REGEX);
32913
- if (!blockMatch)
32914
- return [];
32915
- const block = blockMatch[1];
32916
- const events = [];
32917
- for (const elMatch of block.matchAll(EVENT_ELEMENT_REGEX)) {
32918
- const kind = elMatch[1];
32919
- const atRaw = parseInt(elMatch[2], 10);
32920
- const atCompartment = Number.isNaN(atRaw) ? null : atRaw;
32921
- const fields = {};
32922
- for (const fieldMatch of elMatch[3].matchAll(EVENT_FIELD_REGEX)) {
32923
- const name = fieldMatch[1];
32924
- const value = unescapeXml(fieldMatch[2].trim());
32925
- if (value)
32926
- fields[name] = value;
32927
- }
32928
- events.push({ kind, atCompartment, fields });
32929
- }
32930
- return events;
32931
- }
32932
- function unescapeXml(s) {
32933
- return s.replace(/&amp;/g, "&").replace(/&apos;/g, "'").replace(/&quot;/g, '"').replace(/&lt;/g, "<").replace(/&gt;/g, ">");
32934
- }
32935
-
32936
33204
  // src/hooks/magic-context/compartment-runner-mapping.ts
32937
33205
  function tierFieldsOf(c) {
32938
33206
  return {
@@ -34540,6 +34808,38 @@ var INJECTION_CACHE_MAX = 100;
34540
34808
  var injectionCache = new BoundedSessionMap(INJECTION_CACHE_MAX);
34541
34809
  function clearInjectionCache(sessionId) {
34542
34810
  injectionCache.delete(sessionId);
34811
+ resetDegradedReanchorState(sessionId);
34812
+ }
34813
+ var degradedRebuildCountBySession = new BoundedSessionMap(INJECTION_CACHE_MAX);
34814
+ var reAnchorLoggedBySession = new BoundedSessionMap(INJECTION_CACHE_MAX);
34815
+ var REANCHOR_MIN_DEGRADED_PASSES = 2;
34816
+ function resetDegradedReanchorState(sessionId) {
34817
+ degradedRebuildCountBySession.delete(sessionId);
34818
+ reAnchorLoggedBySession.delete(sessionId);
34819
+ }
34820
+ function noteDegradedRebuild(sessionId) {
34821
+ const next = (degradedRebuildCountBySession.get(sessionId) ?? 0) + 1;
34822
+ degradedRebuildCountBySession.set(sessionId, next);
34823
+ return next;
34824
+ }
34825
+ function clearDegradedRebuild(sessionId) {
34826
+ degradedRebuildCountBySession.delete(sessionId);
34827
+ reAnchorLoggedBySession.delete(sessionId);
34828
+ }
34829
+ function logReanchorOnce(sessionId, message) {
34830
+ if (reAnchorLoggedBySession.get(sessionId))
34831
+ return;
34832
+ reAnchorLoggedBySession.set(sessionId, true);
34833
+ sessionLog(sessionId, message);
34834
+ }
34835
+ function findVisibleReanchorIndex(compartments, visibleMessageIds) {
34836
+ for (let index = compartments.length - 1;index >= 0; index -= 1) {
34837
+ const endMessageId = compartments[index]?.endMessageId;
34838
+ if (typeof endMessageId === "string" && endMessageId.length > 0 && visibleMessageIds.has(endMessageId)) {
34839
+ return index;
34840
+ }
34841
+ }
34842
+ return -1;
34543
34843
  }
34544
34844
  function getVisibleMemoryIds(db, sessionId) {
34545
34845
  try {
@@ -34729,24 +35029,63 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
34729
35029
  return result2;
34730
35030
  }
34731
35031
  let skippedVisibleMessages = 0;
35032
+ let needsFreshMaterialization = false;
35033
+ let resultEndMessage = lastEnd;
35034
+ let resultEndMessageId = null;
34732
35035
  const cutoffIndex = messages.findIndex((message) => message.info.id === trimEndMessageId);
34733
35036
  if (cutoffIndex >= 0) {
35037
+ clearDegradedRebuild(sessionId);
34734
35038
  skippedVisibleMessages = cutoffIndex + 1;
34735
35039
  const remaining = messages.slice(cutoffIndex + 1);
34736
35040
  messages.splice(0, messages.length, ...remaining);
35041
+ resultEndMessageId = trimEndMessageId;
34737
35042
  } else {
34738
- sessionLog(sessionId, `compartment injection entering degraded mode: boundary ${trimEndMessageId} not in visible messages`);
35043
+ const degradedCount = noteDegradedRebuild(sessionId);
35044
+ if (degradedCount === 1) {
35045
+ reconcileForkOrphanedCompactionMarkers(db, sessionId);
35046
+ }
35047
+ let reAnchored = false;
35048
+ if (degradedCount >= REANCHOR_MIN_DEGRADED_PASSES && isCacheBusting) {
35049
+ const visibleMessageIds = new Set;
35050
+ for (const message of messages) {
35051
+ if (typeof message.info.id === "string")
35052
+ visibleMessageIds.add(message.info.id);
35053
+ }
35054
+ const reAnchorIndex = findVisibleReanchorIndex(compartments, visibleMessageIds);
35055
+ if (reAnchorIndex >= 0) {
35056
+ const reAnchorCompartment = compartments[reAnchorIndex];
35057
+ const reAnchorCutoff = messages.findIndex((message) => message.info.id === reAnchorCompartment.endMessageId);
35058
+ if (reAnchorCutoff >= 0) {
35059
+ skippedVisibleMessages = reAnchorCutoff + 1;
35060
+ const remaining = messages.slice(reAnchorCutoff + 1);
35061
+ messages.splice(0, messages.length, ...remaining);
35062
+ resultEndMessage = reAnchorCompartment.endMessage;
35063
+ resultEndMessageId = reAnchorCompartment.endMessageId;
35064
+ reAnchored = true;
35065
+ logReanchorOnce(sessionId, `compartment injection re-anchored: natural boundary ${trimEndMessageId} not visible for ${degradedCount} passes; splicing at visible compartment boundary ${resultEndMessageId} (ordinal ${resultEndMessage})`);
35066
+ }
35067
+ }
35068
+ if (!reAnchored) {
35069
+ needsFreshMaterialization = true;
35070
+ logReanchorOnce(sessionId, `compartment injection degraded: boundary ${trimEndMessageId} not visible for ${degradedCount} passes and no compartment boundary is visible; requesting fresh materialization to re-cut the baseline`);
35071
+ }
35072
+ } else {
35073
+ sessionLog(sessionId, `compartment injection entering degraded mode: boundary ${trimEndMessageId} not in visible messages (consecutive degraded passes: ${degradedCount})`);
35074
+ }
34739
35075
  }
34740
35076
  const result = {
34741
35077
  block,
34742
- compartmentEndMessage: lastEnd,
34743
- compartmentEndMessageId: cutoffIndex >= 0 ? trimEndMessageId : null,
35078
+ compartmentEndMessage: resultEndMessage,
35079
+ compartmentEndMessageId: resultEndMessageId,
34744
35080
  compartmentCount: compartments.length,
34745
35081
  skippedVisibleMessages,
34746
35082
  factCount: facts.length,
34747
35083
  memoryCount,
34748
35084
  rebuiltFromDb: true
34749
35085
  };
35086
+ if (needsFreshMaterialization) {
35087
+ result.needsFreshMaterialization = true;
35088
+ }
34750
35089
  injectionCache.set(sessionId, { kind: "populated", injection: result });
34751
35090
  return result;
34752
35091
  }