@cortexkit/magic-context 0.43.1 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1811 -792
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -4203,6 +4203,10 @@ function getOpenCodePluginCacheDir() {
4203
4203
  const xdg = process.env.XDG_CACHE_HOME || join5(homedir3(), ".cache");
4204
4204
  return join5(xdg, "opencode", "packages");
4205
4205
  }
4206
+ function getOpenCodeV2NpmCacheDir() {
4207
+ const xdg = process.env.XDG_CACHE_HOME || join5(homedir3(), ".cache");
4208
+ return join5(xdg, "opencode", "npm");
4209
+ }
4206
4210
  function isDir(path) {
4207
4211
  try {
4208
4212
  return statSync3(path).isDirectory();
@@ -4603,7 +4607,7 @@ class OmpAdapter {
4603
4607
  }
4604
4608
  getPluginCacheInfo() {
4605
4609
  const path = join8(getOmpPluginsDir(), "cache");
4606
- return { path, exists: existsSync6(path), sizeBytes: dirSizeBytes(path) };
4610
+ return [{ path, exists: existsSync6(path), sizeBytes: dirSizeBytes(path) }];
4607
4611
  }
4608
4612
  getLogPath() {
4609
4613
  return getMagicContextLogPath2("omp");
@@ -4646,7 +4650,7 @@ var init_omp = __esm(() => {
4646
4650
  // ../plugin/src/shared/opencode-db-path.ts
4647
4651
  import { existsSync as existsSync7, readdirSync as readdirSync2, statSync as statSync4 } from "node:fs";
4648
4652
  import { homedir as homedir6 } from "node:os";
4649
- import { isAbsolute as isAbsolute3, join as join9 } from "node:path";
4653
+ import { isAbsolute as isAbsolute3, join as join9, resolve as resolve3 } from "node:path";
4650
4654
  function openCodeHostGenerationFromVersion(version) {
4651
4655
  const text = version ?? "";
4652
4656
  if (/(?:^|\s|v)0\.0\.0-(?:beta|dev)-/.test(text))
@@ -4733,7 +4737,7 @@ function resolveV2Fresh(dataDir, channel, env) {
4733
4737
  const filename = sourceOpenCodeDatabaseFilename("v2", channel, env);
4734
4738
  const explicit = env.OPENCODE_DB !== undefined;
4735
4739
  return {
4736
- path: filename === ":memory:" ? filename : join9(dataDir, filename),
4740
+ path: filename === ":memory:" ? filename : resolve3(dataDir, filename),
4737
4741
  source: explicit ? "OPENCODE_DB" : env.OPENCODE_CHANNEL ? "channel" : "default",
4738
4742
  channel: explicit ? null : channel
4739
4743
  };
@@ -12652,6 +12656,323 @@ var require_src3 = __commonJS(function(exports, module) {
12652
12656
  };
12653
12657
  });
12654
12658
 
12659
+ // ../plugin/src/hooks/auto-update-checker/semver.ts
12660
+ function isValidSemver(version) {
12661
+ return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(version);
12662
+ }
12663
+ function compareSemverCore(a, b) {
12664
+ if (!isValidSemver(a) || !isValidSemver(b))
12665
+ return null;
12666
+ const core = (v) => v.split(/[-+]/, 1)[0].split(".").map((n) => Number.parseInt(n, 10));
12667
+ const [a0, a1, a2] = core(a);
12668
+ const [b0, b1, b2] = core(b);
12669
+ return a0 - b0 || a1 - b1 || a2 - b2;
12670
+ }
12671
+
12672
+ // src/lib/opencode-plugin-registration.ts
12673
+ function pluginConfigKeyFor(hostGeneration) {
12674
+ return hostGeneration === "v2" ? "plugins" : "plugin";
12675
+ }
12676
+ function readPluginEntries(config) {
12677
+ const out = [];
12678
+ for (const key of OPENCODE_PLUGIN_CONFIG_KEYS) {
12679
+ const list = config?.[key];
12680
+ if (!Array.isArray(list))
12681
+ continue;
12682
+ for (const [index, entry] of list.entries())
12683
+ out.push({ key, index, entry });
12684
+ }
12685
+ return out;
12686
+ }
12687
+ var OPENCODE_PLUGIN_CONFIG_KEYS;
12688
+ var init_opencode_plugin_registration = __esm(() => {
12689
+ OPENCODE_PLUGIN_CONFIG_KEYS = ["plugin", "plugins"];
12690
+ });
12691
+
12692
+ // src/lib/opencode-plugin-cache.ts
12693
+ import { readdirSync as readdirSync3, readFileSync as readFileSync5 } from "node:fs";
12694
+ import { join as join10 } from "node:path";
12695
+ function getOpenCodePluginCacheRoots() {
12696
+ const cacheDir = getOpenCodePluginCacheDir();
12697
+ return [
12698
+ join10(cacheDir, OPENCODE_PLUGIN_ENTRY_WITH_VERSION),
12699
+ join10(cacheDir, OPENCODE_PLUGIN_NAME)
12700
+ ];
12701
+ }
12702
+ function getOpenCodePluginPackageJsonPath(pluginCacheRoot) {
12703
+ return join10(pluginCacheRoot, "node_modules", ...OPENCODE_PLUGIN_NAME.split("/"), "package.json");
12704
+ }
12705
+ function getOpenCodePluginPackageJsonPaths() {
12706
+ return getOpenCodePluginCacheRoots().map(getOpenCodePluginPackageJsonPath);
12707
+ }
12708
+ function getOpenCodeV2PluginCacheSlot(npmCacheDir = getOpenCodeV2NpmCacheDir(), spec = "latest") {
12709
+ return join10(npmCacheDir, ...`${OPENCODE_PLUGIN_NAME}@${spec}`.split("/"));
12710
+ }
12711
+ function openCodeV2PluginSpecOf(specifier) {
12712
+ if (specifier === OPENCODE_PLUGIN_NAME)
12713
+ return "latest";
12714
+ const prefix = `${OPENCODE_PLUGIN_NAME}@`;
12715
+ if (!specifier.startsWith(prefix))
12716
+ return;
12717
+ const spec = specifier.slice(prefix.length);
12718
+ return spec.length > 0 ? spec : "latest";
12719
+ }
12720
+ function isOpenCodePluginDistTag(spec) {
12721
+ return /^[A-Za-z][A-Za-z0-9._-]*$/.test(spec) && !/^(v\d|[xX]$)/.test(spec);
12722
+ }
12723
+ function readConfiguredOpenCodePluginSpec(config) {
12724
+ for (const { entry } of readPluginEntries(config)) {
12725
+ const specifier = pluginEntrySpecifier(entry);
12726
+ const spec = specifier === undefined ? undefined : openCodeV2PluginSpecOf(specifier);
12727
+ if (spec !== undefined)
12728
+ return spec;
12729
+ }
12730
+ return "latest";
12731
+ }
12732
+ function pluginEntrySpecifier(entry) {
12733
+ if (typeof entry === "string")
12734
+ return entry;
12735
+ if (Array.isArray(entry) && typeof entry[0] === "string")
12736
+ return entry[0];
12737
+ if (entry !== null && typeof entry === "object") {
12738
+ const pkg = entry.package;
12739
+ if (typeof pkg === "string")
12740
+ return pkg;
12741
+ }
12742
+ return;
12743
+ }
12744
+ function listOpenCodeV2PluginCacheSlots(npmCacheDir = getOpenCodeV2NpmCacheDir()) {
12745
+ const [scope = "", name = ""] = OPENCODE_PLUGIN_NAME.split("/");
12746
+ const scopeDir = join10(npmCacheDir, scope);
12747
+ let names;
12748
+ try {
12749
+ names = readdirSync3(scopeDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith(`${name}@`)).map((entry) => entry.name);
12750
+ } catch {
12751
+ return [];
12752
+ }
12753
+ return names.map((dirName) => {
12754
+ const slot = join10(scopeDir, dirName);
12755
+ return {
12756
+ spec: dirName.slice(name.length + 1),
12757
+ slot,
12758
+ version: readOpenCodeV2CachedPluginVersion(slot)
12759
+ };
12760
+ }).sort((a, b) => {
12761
+ if (a.spec === "latest" || b.spec === "latest")
12762
+ return a.spec === "latest" ? -1 : 1;
12763
+ return a.spec.localeCompare(b.spec);
12764
+ });
12765
+ }
12766
+ function getOpenCodeV2ActiveGeneration(slot) {
12767
+ try {
12768
+ const generations = readdirSync3(slot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && /^\d+$/.test(entry.name)).map((entry) => entry.name).sort((a, b) => Number(a) - Number(b));
12769
+ const newest = generations.at(-1);
12770
+ return newest === undefined ? undefined : join10(slot, newest);
12771
+ } catch {
12772
+ return;
12773
+ }
12774
+ }
12775
+ function readOpenCodeV2CachedPluginVersion(slot) {
12776
+ const generation = getOpenCodeV2ActiveGeneration(slot);
12777
+ if (!generation)
12778
+ return;
12779
+ try {
12780
+ const pkg = JSON.parse(readFileSync5(getOpenCodePluginPackageJsonPath(generation), "utf-8"));
12781
+ return typeof pkg.version === "string" ? pkg.version : undefined;
12782
+ } catch {
12783
+ return;
12784
+ }
12785
+ }
12786
+ var OPENCODE_PLUGIN_NAME = "@cortexkit/opencode-magic-context", OPENCODE_PLUGIN_ENTRY_WITH_VERSION;
12787
+ var init_opencode_plugin_cache = __esm(() => {
12788
+ init_opencode_plugin_registration();
12789
+ init_paths();
12790
+ OPENCODE_PLUGIN_ENTRY_WITH_VERSION = `${OPENCODE_PLUGIN_NAME}@latest`;
12791
+ });
12792
+
12793
+ // src/commands/doctor-opencode2-cache.ts
12794
+ import { spawnSync as spawnSync3 } from "node:child_process";
12795
+ import { existsSync as existsSync8, rmSync as rmSync2 } from "node:fs";
12796
+ function runLsof(args, spawn) {
12797
+ const result = spawn("lsof", args, { encoding: "utf-8", timeout: 15000 });
12798
+ if (result.error)
12799
+ return { error: result.error.message };
12800
+ if (result.status !== 0 && result.status !== 1) {
12801
+ return { error: `lsof exited with status ${String(result.status)}` };
12802
+ }
12803
+ const pids = (result.stdout ?? "").split(/\s+/).filter((token) => /^\d+$/.test(token)).map((token) => Number(token)).filter((pid) => pid !== process.pid);
12804
+ return { pids };
12805
+ }
12806
+ function probeHostProcessesUsing(targets, spawn = spawnSync3) {
12807
+ const files = targets.files.filter((file) => existsSync8(file));
12808
+ const directories = targets.directories.filter((directory) => existsSync8(directory));
12809
+ const queries = [];
12810
+ if (files.length > 0)
12811
+ queries.push(["-w", "-t", "--", ...files]);
12812
+ for (const directory of directories)
12813
+ queries.push(["-w", "-t", "+D", directory]);
12814
+ const pids = new Set;
12815
+ for (const args of queries) {
12816
+ const result = runLsof(args, spawn);
12817
+ if ("error" in result) {
12818
+ return { status: "unknown", reason: `could not run lsof (${result.error})` };
12819
+ }
12820
+ for (const pid of result.pids)
12821
+ pids.add(pid);
12822
+ }
12823
+ if (pids.size > 0)
12824
+ return { status: "in_use", pids: [...pids].sort((a, b) => a - b) };
12825
+ return { status: "free" };
12826
+ }
12827
+ function comparePrerelease(a, b) {
12828
+ if (a === b)
12829
+ return 0;
12830
+ if (a === "")
12831
+ return 1;
12832
+ if (b === "")
12833
+ return -1;
12834
+ const left = a.split(".");
12835
+ const right = b.split(".");
12836
+ for (let i = 0;i < Math.max(left.length, right.length); i++) {
12837
+ const x = left[i];
12838
+ const y = right[i];
12839
+ if (x === undefined)
12840
+ return -1;
12841
+ if (y === undefined)
12842
+ return 1;
12843
+ if (x === y)
12844
+ continue;
12845
+ const xNumeric = /^\d+$/.test(x);
12846
+ const yNumeric = /^\d+$/.test(y);
12847
+ if (xNumeric && yNumeric)
12848
+ return Number(x) - Number(y);
12849
+ if (xNumeric !== yNumeric)
12850
+ return xNumeric ? -1 : 1;
12851
+ return x < y ? -1 : 1;
12852
+ }
12853
+ return 0;
12854
+ }
12855
+ function prereleaseOf(version) {
12856
+ const withoutBuild = version.split("+", 1)[0] ?? "";
12857
+ const dash = withoutBuild.indexOf("-");
12858
+ return dash === -1 ? "" : withoutBuild.slice(dash + 1);
12859
+ }
12860
+ function isStale(cached, latest) {
12861
+ if (cached === undefined)
12862
+ return true;
12863
+ if (cached === latest)
12864
+ return false;
12865
+ const comparison = compareSemverCore(cached, latest);
12866
+ if (comparison === null)
12867
+ return true;
12868
+ if (comparison !== 0)
12869
+ return comparison < 0;
12870
+ return comparePrerelease(prereleaseOf(cached), prereleaseOf(latest)) < 0;
12871
+ }
12872
+ function removeOpenCodeV2PluginCacheSlot(slot, hostFiles, deps = {}) {
12873
+ const probe = deps.probe ?? probeHostProcessesUsing;
12874
+ const use = probe({ files: hostFiles, directories: [slot] });
12875
+ if (use.status === "in_use")
12876
+ return { action: "in_use", pids: use.pids };
12877
+ if (use.status === "unknown")
12878
+ return { action: "in_use_unknown", reason: use.reason };
12879
+ const remove = deps.remove ?? ((path) => rmSync2(path, { recursive: true, force: true }));
12880
+ try {
12881
+ remove(slot);
12882
+ } catch (err) {
12883
+ return { action: "error", error: err instanceof Error ? err.message : String(err) };
12884
+ }
12885
+ return { action: "cleared" };
12886
+ }
12887
+ function openCodeHostDatabaseFiles(databasePaths) {
12888
+ return databasePaths.filter((path) => path !== ":memory:").flatMap((path) => ["", "-wal", "-shm"].map((suffix) => `${path}${suffix}`));
12889
+ }
12890
+ function configuredOpenCodeV2DistTag(config) {
12891
+ const spec = readConfiguredOpenCodePluginSpec(config);
12892
+ return spec !== "latest" && isOpenCodePluginDistTag(spec) ? spec : undefined;
12893
+ }
12894
+ function checkOpenCodeV2PluginCache(options, deps = {}) {
12895
+ const distTag = options.distTag && options.distTag !== "latest" ? options.distTag : undefined;
12896
+ const slot = deps.slot ?? getOpenCodeV2PluginCacheSlot(undefined, distTag ?? "latest");
12897
+ const tagged = distTag ? { distTag } : {};
12898
+ if (!existsSync8(slot))
12899
+ return { action: "not_found", slot, ...tagged };
12900
+ const cached = readOpenCodeV2CachedPluginVersion(slot);
12901
+ const latest = options.latestVersion ?? undefined;
12902
+ const force = options.force === true;
12903
+ if (latest === undefined && !force) {
12904
+ return { action: "check_unavailable", slot, cached, ...tagged };
12905
+ }
12906
+ const stale = latest !== undefined && isStale(cached, latest);
12907
+ if (!stale && !force)
12908
+ return { action: "up_to_date", slot, cached, latest, ...tagged };
12909
+ if (!force && options.fix !== true) {
12910
+ return { action: "stale", slot, cached, latest, ...tagged };
12911
+ }
12912
+ const removal = removeOpenCodeV2PluginCacheSlot(slot, options.hostFiles, deps);
12913
+ switch (removal.action) {
12914
+ case "in_use":
12915
+ return { action: "in_use", slot, cached, latest, pids: removal.pids, ...tagged };
12916
+ case "in_use_unknown":
12917
+ return {
12918
+ action: "in_use_unknown",
12919
+ slot,
12920
+ cached,
12921
+ latest,
12922
+ reason: removal.reason,
12923
+ ...tagged
12924
+ };
12925
+ case "error":
12926
+ return { action: "error", slot, cached, latest, error: removal.error, ...tagged };
12927
+ case "cleared":
12928
+ return { action: "cleared", slot, cached, latest, forced: !stale, ...tagged };
12929
+ }
12930
+ }
12931
+ function reportOpenCodeV2PluginCache(result, report, options) {
12932
+ const tag = result.distTag ?? "latest";
12933
+ const versions = `cached: ${result.cached ?? "unreadable"}${result.latest ? `, ${tag}: ${result.latest}` : ""}`;
12934
+ const slotName = result.distTag ? ` @${result.distTag}` : "";
12935
+ switch (result.action) {
12936
+ case "not_found":
12937
+ if (options.reportMissing) {
12938
+ report.pass(`OpenCode 2 plugin cache has no Magic Context${slotName} install yet (OpenCode installs it on next start)`);
12939
+ }
12940
+ return { fixed: false, issue: false };
12941
+ case "up_to_date":
12942
+ report.pass(`OpenCode 2 plugin cache${slotName} up to date (v${result.cached})`);
12943
+ return { fixed: false, issue: false };
12944
+ case "check_unavailable":
12945
+ report.warn(`OpenCode 2 plugin cache${slotName} version check unavailable; preserving cached plugin${result.cached ? ` (cached: ${result.cached})` : ""}. Use doctor --force to reinstall it.`);
12946
+ return { fixed: false, issue: false };
12947
+ case "stale":
12948
+ report.warn(`OpenCode 2 is running an outdated Magic Context${slotName} (${versions}); OpenCode does not install new releases on its own`);
12949
+ report.info(` To upgrade, ${OPENCODE_V2_PLUGIN_UPDATE_HINT},`);
12950
+ report.info(" or quit OpenCode (including `opencode service stop`) and run `magic-context doctor --fix`.");
12951
+ return { fixed: false, issue: false };
12952
+ case "in_use":
12953
+ report.warn(`Outdated Magic Context in the OpenCode 2 plugin cache (${versions}) was not cleared: OpenCode is running (pid ${result.pids?.join(", ")})`);
12954
+ report.info(` Quit OpenCode (and \`opencode service stop\`) and rerun doctor --fix, or ${OPENCODE_V2_PLUGIN_UPDATE_HINT}.`);
12955
+ report.info(` ${result.slot}`);
12956
+ return { fixed: false, issue: false };
12957
+ case "in_use_unknown":
12958
+ report.warn(`Outdated Magic Context in the OpenCode 2 plugin cache (${versions}) was not cleared: ${result.reason ?? "could not tell whether OpenCode is running"}`);
12959
+ report.info(` Quit OpenCode and delete ${result.slot} by hand, or ${OPENCODE_V2_PLUGIN_UPDATE_HINT}.`);
12960
+ return { fixed: false, issue: false };
12961
+ case "cleared":
12962
+ report.pass(`Cleared ${result.forced ? "" : "outdated "}Magic Context${slotName} from the OpenCode 2 plugin cache (${versions}${result.forced ? ", --force" : ""}) — OpenCode installs the ${result.distTag ? `current ${tag} release` : "latest"} on next start`);
12963
+ report.info(` ${result.slot}`);
12964
+ return { fixed: true, issue: false };
12965
+ case "error":
12966
+ report.warn(`Could not clear the OpenCode 2 plugin cache: ${result.error}`);
12967
+ report.info(` Manually delete: ${result.slot}`);
12968
+ return { fixed: false, issue: true };
12969
+ }
12970
+ }
12971
+ var OPENCODE_V2_PLUGIN_UPDATE_HINT = "update it from OpenCode: open /plugins, select Magic Context and press ctrl+u (ctrl+r only re-checks), or run `opencode plugin update`";
12972
+ var init_doctor_opencode2_cache = __esm(() => {
12973
+ init_opencode_plugin_cache();
12974
+ });
12975
+
12655
12976
  // src/lib/atomic-write.ts
12656
12977
  import { chmodSync, mkdirSync as mkdirSync2, renameSync as renameSync2, statSync as statSync5, writeFileSync as writeFileSync2 } from "node:fs";
12657
12978
  import { dirname as dirname3 } from "node:path";
@@ -12670,13 +12991,13 @@ function writeFileAtomic(targetPath, data) {
12670
12991
  var init_atomic_write = () => {};
12671
12992
 
12672
12993
  // src/lib/opencode-detect.ts
12673
- import { existsSync as existsSync8, realpathSync } from "node:fs";
12994
+ import { existsSync as existsSync9, realpathSync } from "node:fs";
12674
12995
  import { homedir as homedir7 } from "node:os";
12675
- import { join as join10 } from "node:path";
12996
+ import { join as join11 } from "node:path";
12676
12997
  function defaultDeps(overrides = {}) {
12677
12998
  const env = overrides.env ?? process.env;
12678
12999
  return {
12679
- exists: overrides.exists ?? existsSync8,
13000
+ exists: overrides.exists ?? existsSync9,
12680
13001
  isExecutable: overrides.isExecutable ?? isExecutableFile,
12681
13002
  home: overrides.home ?? (env.HOME?.trim() || homedir7()),
12682
13003
  platform: overrides.platform ?? process.platform,
@@ -12686,7 +13007,7 @@ function defaultDeps(overrides = {}) {
12686
13007
  };
12687
13008
  }
12688
13009
  function stockCliBinary(d) {
12689
- return d.platform === "win32" ? join10(d.home, ".opencode", "bin", "opencode.exe") : join10(d.home, ".opencode", "bin", "opencode");
13010
+ return d.platform === "win32" ? join11(d.home, ".opencode", "bin", "opencode.exe") : join11(d.home, ".opencode", "bin", "opencode");
12690
13011
  }
12691
13012
  function extraCliCandidates(d) {
12692
13013
  if (d.platform === "win32") {
@@ -12695,25 +13016,34 @@ function extraCliCandidates(d) {
12695
13016
  const userprofile = d.env.USERPROFILE ?? d.home;
12696
13017
  const out = [];
12697
13018
  if (appdata) {
12698
- out.push(join10(appdata, "npm", "opencode.cmd"));
12699
- out.push(join10(appdata, "npm", "opencode.exe"));
13019
+ out.push(join11(appdata, "npm", "opencode.cmd"));
13020
+ out.push(join11(appdata, "npm", "opencode.exe"));
12700
13021
  }
12701
13022
  if (localappdata) {
12702
- out.push(join10(localappdata, "Microsoft", "WinGet", "Links", "opencode.exe"));
12703
- out.push(join10(localappdata, "opencode", "bin", "opencode.exe"));
13023
+ out.push(join11(localappdata, "Microsoft", "WinGet", "Links", "opencode.exe"));
13024
+ out.push(join11(localappdata, "opencode", "bin", "opencode.exe"));
12704
13025
  }
12705
13026
  if (userprofile) {
12706
- out.push(join10(userprofile, "scoop", "shims", "opencode.exe"));
13027
+ out.push(join11(userprofile, "scoop", "shims", "opencode.exe"));
12707
13028
  }
12708
13029
  return out;
12709
13030
  }
12710
13031
  return [
12711
13032
  "/usr/local/bin/opencode",
12712
13033
  "/opt/homebrew/bin/opencode",
12713
- join10(d.home, ".local", "bin", "opencode"),
12714
- join10(d.home, ".local", "share", "mise", "shims", "opencode"),
12715
- join10(d.home, ".asdf", "shims", "opencode"),
12716
- join10(d.home, ".volta", "bin", "opencode")
13034
+ join11(d.home, ".local", "bin", "opencode"),
13035
+ join11(d.home, ".local", "share", "mise", "shims", "opencode"),
13036
+ join11(d.home, ".asdf", "shims", "opencode"),
13037
+ join11(d.home, ".volta", "bin", "opencode")
13038
+ ];
13039
+ }
13040
+ function openChamberCliCandidates(d) {
13041
+ if (d.platform !== "darwin")
13042
+ return [];
13043
+ const bundled = ["Contents", "Resources", "opencode-cli", "opencode"];
13044
+ return [
13045
+ join11("/Applications", "OpenChamber.app", ...bundled),
13046
+ join11(d.home, "Applications", "OpenChamber.app", ...bundled)
12717
13047
  ];
12718
13048
  }
12719
13049
  function canonicalPath(d, path) {
@@ -12734,29 +13064,29 @@ function xdgConfigHome(d) {
12734
13064
  const xdg = d.env.XDG_CONFIG_HOME;
12735
13065
  if (xdg && xdg.length > 0)
12736
13066
  return xdg;
12737
- return join10(d.home, ".config");
13067
+ return join11(d.home, ".config");
12738
13068
  }
12739
13069
  function desktopUserDataDir(d, appId) {
12740
13070
  switch (d.platform) {
12741
13071
  case "darwin":
12742
- return join10(d.home, "Library", "Application Support", appId);
13072
+ return join11(d.home, "Library", "Application Support", appId);
12743
13073
  case "win32":
12744
- return join10(d.env.APPDATA ?? join10(d.home, "AppData", "Roaming"), appId);
13074
+ return join11(d.env.APPDATA ?? join11(d.home, "AppData", "Roaming"), appId);
12745
13075
  default:
12746
- return join10(xdgConfigHome(d), appId);
13076
+ return join11(xdgConfigHome(d), appId);
12747
13077
  }
12748
13078
  }
12749
13079
  function desktopAppPaths(d) {
12750
13080
  switch (d.platform) {
12751
13081
  case "darwin":
12752
- return ["/Applications/OpenCode.app", join10(d.home, "Applications", "OpenCode.app")];
13082
+ return ["/Applications/OpenCode.app", join11(d.home, "Applications", "OpenCode.app")];
12753
13083
  case "win32": {
12754
- const localappdata = d.env.LOCALAPPDATA ?? join10(d.home, "AppData", "Local");
12755
- return [join10(localappdata, "Programs", "OpenCode", "OpenCode.exe")];
13084
+ const localappdata = d.env.LOCALAPPDATA ?? join11(d.home, "AppData", "Local");
13085
+ return [join11(localappdata, "Programs", "OpenCode", "OpenCode.exe")];
12756
13086
  }
12757
13087
  default: {
12758
- const dataHome = d.env.XDG_DATA_HOME && d.env.XDG_DATA_HOME.length > 0 ? d.env.XDG_DATA_HOME : join10(d.home, ".local", "share");
12759
- return OPENCODE_DESKTOP_APP_IDS.map((appId) => join10(dataHome, "applications", `${appId}.desktop`));
13088
+ const dataHome = d.env.XDG_DATA_HOME && d.env.XDG_DATA_HOME.length > 0 ? d.env.XDG_DATA_HOME : join11(d.home, ".local", "share");
13089
+ return OPENCODE_DESKTOP_APP_IDS.map((appId) => join11(dataHome, "applications", `${appId}.desktop`));
12760
13090
  }
12761
13091
  }
12762
13092
  }
@@ -12777,8 +13107,13 @@ function detectOpenCodeInstallations(deps) {
12777
13107
  addCandidate(installations, seenRealpaths, d, candidate, "PATH", "cli");
12778
13108
  }
12779
13109
  }
13110
+ for (const candidate of openChamberCliCandidates(d)) {
13111
+ if (d.isExecutable(candidate)) {
13112
+ addCandidate(installations, seenRealpaths, d, candidate, "openchamber", "cli");
13113
+ }
13114
+ }
12780
13115
  for (const appId of OPENCODE_DESKTOP_APP_IDS) {
12781
- const marker = join10(desktopUserDataDir(d, appId), OPENCODE_DESKTOP_SETTINGS_FILE);
13116
+ const marker = join11(desktopUserDataDir(d, appId), OPENCODE_DESKTOP_SETTINGS_FILE);
12782
13117
  if (d.exists(marker)) {
12783
13118
  addCandidate(installations, seenRealpaths, d, marker, "desktop", "desktop");
12784
13119
  }
@@ -12850,6 +13185,17 @@ function describeOpenCodeInstallations(installations, deps = {}) {
12850
13185
  active: index === 0
12851
13186
  }));
12852
13187
  }
13188
+ function selectOpenCodeStoreHost(reports, generationOf) {
13189
+ const active = reports[0];
13190
+ if (!active)
13191
+ return null;
13192
+ const hasVersion = (report) => /\d/.test(report.version);
13193
+ if (!hasVersion(active) || generationOf(active.version) === "v2") {
13194
+ return { host: active, shadowed: false };
13195
+ }
13196
+ const v2 = reports.find((report) => report.kind === "cli" && hasVersion(report) && generationOf(report.version) === "v2");
13197
+ return v2 ? { host: v2, shadowed: true } : { host: active, shadowed: false };
13198
+ }
12853
13199
  function getAvailableModels2(binary) {
12854
13200
  const output = runOpenCode(["models"], binary);
12855
13201
  if (output === null)
@@ -12860,50 +13206,9 @@ function getAvailableModels2(binary) {
12860
13206
  var OPENCODE_BINARY_ENV = "MAGIC_CONTEXT_OPENCODE_BINARY", OPENCODE_VERSION_PROBE_TIMEOUT_MS = 2000;
12861
13207
  var init_opencode_helpers = () => {};
12862
13208
 
12863
- // src/lib/opencode-plugin-cache.ts
12864
- import { join as join11 } from "node:path";
12865
- function getOpenCodePluginCacheRoots() {
12866
- const cacheDir = getOpenCodePluginCacheDir();
12867
- return [
12868
- join11(cacheDir, OPENCODE_PLUGIN_ENTRY_WITH_VERSION),
12869
- join11(cacheDir, OPENCODE_PLUGIN_NAME)
12870
- ];
12871
- }
12872
- function getOpenCodePluginPackageJsonPath(pluginCacheRoot) {
12873
- return join11(pluginCacheRoot, "node_modules", ...OPENCODE_PLUGIN_NAME.split("/"), "package.json");
12874
- }
12875
- function getOpenCodePluginPackageJsonPaths() {
12876
- return getOpenCodePluginCacheRoots().map(getOpenCodePluginPackageJsonPath);
12877
- }
12878
- var OPENCODE_PLUGIN_NAME = "@cortexkit/opencode-magic-context", OPENCODE_PLUGIN_ENTRY_WITH_VERSION;
12879
- var init_opencode_plugin_cache = __esm(() => {
12880
- init_paths();
12881
- OPENCODE_PLUGIN_ENTRY_WITH_VERSION = `${OPENCODE_PLUGIN_NAME}@latest`;
12882
- });
12883
-
12884
- // src/lib/opencode-plugin-registration.ts
12885
- function pluginConfigKeyFor(hostGeneration) {
12886
- return hostGeneration === "v2" ? "plugins" : "plugin";
12887
- }
12888
- function readPluginEntries(config) {
12889
- const out = [];
12890
- for (const key of OPENCODE_PLUGIN_CONFIG_KEYS) {
12891
- const list = config?.[key];
12892
- if (!Array.isArray(list))
12893
- continue;
12894
- for (const [index, entry] of list.entries())
12895
- out.push({ key, index, entry });
12896
- }
12897
- return out;
12898
- }
12899
- var OPENCODE_PLUGIN_CONFIG_KEYS;
12900
- var init_opencode_plugin_registration = __esm(() => {
12901
- OPENCODE_PLUGIN_CONFIG_KEYS = ["plugin", "plugins"];
12902
- });
12903
-
12904
13209
  // src/adapters/opencode.ts
12905
- import { existsSync as existsSync9, readFileSync as readFileSync5, statSync as statSync6 } from "node:fs";
12906
- import { dirname as dirname4, isAbsolute as isAbsolute4, parse as parsePath, resolve as resolve3 } from "node:path";
13210
+ import { existsSync as existsSync10, readFileSync as readFileSync6, statSync as statSync6 } from "node:fs";
13211
+ import { dirname as dirname4, isAbsolute as isAbsolute4, parse as parsePath, resolve as resolve4 } from "node:path";
12907
13212
  import { fileURLToPath } from "node:url";
12908
13213
 
12909
13214
  class OpenCodeAdapter {
@@ -12911,21 +13216,20 @@ class OpenCodeAdapter {
12911
13216
  displayName = "OpenCode";
12912
13217
  pluginPackageName = OPENCODE_PLUGIN_NAME;
12913
13218
  hostGeneration;
12914
- resolvedWriteKey;
13219
+ probeHostUse;
12915
13220
  constructor(options = {}) {
12916
13221
  this.hostGeneration = options.hostGeneration;
13222
+ this.probeHostUse = options.probeHostUse;
13223
+ }
13224
+ get resolvedHostGeneration() {
13225
+ if (this.hostGeneration)
13226
+ return this.hostGeneration;
13227
+ const detection = detectOpenCode();
13228
+ this.hostGeneration = detection.kind === "cli" ? openCodeHostGenerationFromVersion(getOpenCodeVersion(detection.binary)) : "v1";
13229
+ return this.hostGeneration;
12917
13230
  }
12918
13231
  get writeKey() {
12919
- if (this.resolvedWriteKey)
12920
- return this.resolvedWriteKey;
12921
- const generation = this.hostGeneration ?? (() => {
12922
- const detection = detectOpenCode();
12923
- if (detection.kind !== "cli")
12924
- return "v1";
12925
- return openCodeHostGenerationFromVersion(getOpenCodeVersion(detection.binary));
12926
- })();
12927
- this.resolvedWriteKey = pluginConfigKeyFor(generation);
12928
- return this.resolvedWriteKey;
13232
+ return pluginConfigKeyFor(this.resolvedHostGeneration);
12929
13233
  }
12930
13234
  isInstalled() {
12931
13235
  return detectOpenCode().kind !== "none";
@@ -12935,7 +13239,7 @@ class OpenCodeAdapter {
12935
13239
  if (paths.opencodeConfigFormat === "none")
12936
13240
  return false;
12937
13241
  try {
12938
- const raw = readFileSync5(paths.opencodeConfig, "utf-8");
13242
+ const raw = readFileSync6(paths.opencodeConfig, "utf-8");
12939
13243
  const cfg = import_comment_json.parse(raw);
12940
13244
  return readPluginEntries(cfg).some(({ entry }) => matchesPluginEntry(entry, OPENCODE_PLUGIN_NAME));
12941
13245
  } catch {
@@ -12971,7 +13275,7 @@ class OpenCodeAdapter {
12971
13275
  configPath: target
12972
13276
  };
12973
13277
  }
12974
- const raw = readFileSync5(target, "utf-8");
13278
+ const raw = readFileSync6(target, "utf-8");
12975
13279
  const cfg = import_comment_json.parse(raw);
12976
13280
  if (cfg === null || typeof cfg !== "object") {
12977
13281
  return {
@@ -13046,7 +13350,7 @@ class OpenCodeAdapter {
13046
13350
  };
13047
13351
  }
13048
13352
  try {
13049
- const raw = readFileSync5(target, "utf-8");
13353
+ const raw = readFileSync6(target, "utf-8");
13050
13354
  const cfg = import_comment_json.parse(raw);
13051
13355
  if (cfg === null || typeof cfg !== "object") {
13052
13356
  return {
@@ -13096,22 +13400,83 @@ class OpenCodeAdapter {
13096
13400
  return "Install OpenCode: curl -fsSL https://opencode.ai/install | bash";
13097
13401
  }
13098
13402
  getPluginCacheInfo() {
13099
- const path = getOpenCodePluginCacheDir();
13100
- return {
13101
- path,
13102
- exists: existsSync9(path),
13103
- sizeBytes: dirSizeBytes(path)
13104
- };
13403
+ const legacyPath = getOpenCodePluginCacheDir();
13404
+ const legacyExists = existsSync10(legacyPath);
13405
+ const slot = getOpenCodeV2PluginCacheSlot();
13406
+ const slotExists = existsSync10(slot);
13407
+ const caches = [];
13408
+ if (legacyExists || !slotExists) {
13409
+ caches.push({
13410
+ path: legacyPath,
13411
+ exists: legacyExists,
13412
+ sizeBytes: dirSizeBytes(legacyPath),
13413
+ label: "OpenCode 1 plugin packages"
13414
+ });
13415
+ }
13416
+ if (slotExists) {
13417
+ caches.push({
13418
+ path: slot,
13419
+ exists: true,
13420
+ sizeBytes: dirSizeBytes(slot),
13421
+ label: "OpenCode 2 Magic Context @latest install",
13422
+ clear: () => this.clearOpenCodeV2Slot(slot)
13423
+ });
13424
+ }
13425
+ return caches;
13426
+ }
13427
+ clearOpenCodeV2Slot(slot) {
13428
+ let databases;
13429
+ try {
13430
+ databases = [
13431
+ ...new Set([resolveOpenCodeDbPath("v1").path, resolveOpenCodeDbPath("v2").path])
13432
+ ];
13433
+ } catch (err) {
13434
+ return {
13435
+ cleared: false,
13436
+ reason: `could not locate the OpenCode database to check whether OpenCode is running (${err instanceof Error ? err.message : String(err)})`
13437
+ };
13438
+ }
13439
+ const removal = removeOpenCodeV2PluginCacheSlot(slot, openCodeHostDatabaseFiles(databases), {
13440
+ probe: this.probeHostUse
13441
+ });
13442
+ switch (removal.action) {
13443
+ case "cleared":
13444
+ return { cleared: true };
13445
+ case "in_use":
13446
+ return {
13447
+ cleared: false,
13448
+ reason: `OpenCode is running (pid ${removal.pids.join(", ")}); quit it (and \`opencode service stop\`) first`
13449
+ };
13450
+ case "in_use_unknown":
13451
+ return { cleared: false, reason: removal.reason };
13452
+ case "error":
13453
+ return { cleared: false, reason: removal.error };
13454
+ }
13105
13455
  }
13106
13456
  getLogPath() {
13107
13457
  return getMagicContextLogPath2("opencode");
13108
13458
  }
13459
+ configuredPluginSpec() {
13460
+ const paths = detectConfigPaths();
13461
+ if (paths.opencodeConfigFormat === "none")
13462
+ return "latest";
13463
+ try {
13464
+ const cfg = import_comment_json.parse(readFileSync6(paths.opencodeConfig, "utf-8"));
13465
+ return readConfiguredOpenCodePluginSpec(cfg);
13466
+ } catch {
13467
+ return "latest";
13468
+ }
13469
+ }
13109
13470
  getInstalledPluginVersion() {
13471
+ if (this.resolvedHostGeneration === "v2") {
13472
+ const slot = getOpenCodeV2PluginCacheSlot(undefined, this.configuredPluginSpec());
13473
+ return readOpenCodeV2CachedPluginVersion(slot) ?? null;
13474
+ }
13110
13475
  for (const candidate of getOpenCodePluginPackageJsonPaths()) {
13111
- if (!existsSync9(candidate))
13476
+ if (!existsSync10(candidate))
13112
13477
  continue;
13113
13478
  try {
13114
- const raw = readFileSync5(candidate, "utf-8");
13479
+ const raw = readFileSync6(candidate, "utf-8");
13115
13480
  const pkg = JSON.parse(raw);
13116
13481
  if (typeof pkg.version === "string")
13117
13482
  return pkg.version;
@@ -13147,15 +13512,15 @@ function isDevPathPluginEntry(entry) {
13147
13512
  if (candidate.startsWith("file://")) {
13148
13513
  localPath = fileURLToPath(candidate);
13149
13514
  } else {
13150
- localPath = resolve3(candidate);
13515
+ localPath = resolve4(candidate);
13151
13516
  }
13152
13517
  if (statSync6(localPath).isFile())
13153
13518
  localPath = dirname4(localPath);
13154
13519
  const root = parsePath(localPath).root;
13155
13520
  while (localPath !== root) {
13156
- const packagePath = resolve3(localPath, "package.json");
13157
- if (existsSync9(packagePath)) {
13158
- const pkg = JSON.parse(readFileSync5(packagePath, "utf8"));
13521
+ const packagePath = resolve4(localPath, "package.json");
13522
+ if (existsSync10(packagePath)) {
13523
+ const pkg = JSON.parse(readFileSync6(packagePath, "utf8"));
13159
13524
  return pkg.name === OPENCODE_PLUGIN_NAME;
13160
13525
  }
13161
13526
  localPath = dirname4(localPath);
@@ -13175,7 +13540,7 @@ function matchesPluginEntry(entry, pkgName) {
13175
13540
  }
13176
13541
  function ensureDir(filePath) {
13177
13542
  const dir = dirname4(filePath);
13178
- if (!existsSync9(dir)) {
13543
+ if (!existsSync10(dir)) {
13179
13544
  const { mkdirSync } = __require("node:fs");
13180
13545
  mkdirSync(dir, { recursive: true });
13181
13546
  }
@@ -13183,6 +13548,7 @@ function ensureDir(filePath) {
13183
13548
  var import_comment_json;
13184
13549
  var init_opencode = __esm(() => {
13185
13550
  init_opencode_db_path();
13551
+ init_doctor_opencode2_cache();
13186
13552
  init_atomic_write();
13187
13553
  init_opencode_detect();
13188
13554
  init_opencode_helpers();
@@ -13193,7 +13559,7 @@ var init_opencode = __esm(() => {
13193
13559
  });
13194
13560
 
13195
13561
  // ../plugin/src/shared/jsonc-parser.ts
13196
- import { existsSync as existsSync10, readFileSync as readFileSync6 } from "node:fs";
13562
+ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "node:fs";
13197
13563
  function stripJsonComments(content) {
13198
13564
  let result = "";
13199
13565
  let inString = false;
@@ -13368,7 +13734,7 @@ function parseJsonc2(content, options = {}) {
13368
13734
  }
13369
13735
  function readJsoncFile(filePath) {
13370
13736
  try {
13371
- return parseJsonc2(readFileSync6(filePath, "utf-8"));
13737
+ return parseJsonc2(readFileSync7(filePath, "utf-8"));
13372
13738
  } catch (_error) {
13373
13739
  return null;
13374
13740
  }
@@ -13376,10 +13742,10 @@ function readJsoncFile(filePath) {
13376
13742
  function detectConfigFile(basePath) {
13377
13743
  const jsoncPath = `${basePath}.jsonc`;
13378
13744
  const jsonPath = `${basePath}.json`;
13379
- if (existsSync10(jsoncPath)) {
13745
+ if (existsSync11(jsoncPath)) {
13380
13746
  return { format: "jsonc", path: jsoncPath };
13381
13747
  }
13382
- if (existsSync10(jsonPath)) {
13748
+ if (existsSync11(jsonPath)) {
13383
13749
  return { format: "json", path: jsonPath };
13384
13750
  }
13385
13751
  return { format: "none", path: jsoncPath };
@@ -13391,7 +13757,7 @@ var init_jsonc_parser = __esm(() => {
13391
13757
  });
13392
13758
 
13393
13759
  // src/lib/jsonc-config.ts
13394
- import { existsSync as existsSync11, readFileSync as readFileSync7 } from "node:fs";
13760
+ import { existsSync as existsSync12, readFileSync as readFileSync8 } from "node:fs";
13395
13761
  function parseErrorLocation(content, error) {
13396
13762
  const lines = content.split(`
13397
13763
  `);
@@ -13407,9 +13773,9 @@ function parseErrorLocation(content, error) {
13407
13773
  return { line: messageLine ? Number.parseInt(messageLine, 10) : 1, column: 1 };
13408
13774
  }
13409
13775
  function readJsoncConfig(path) {
13410
- if (!existsSync11(path))
13776
+ if (!existsSync12(path))
13411
13777
  return { kind: "missing" };
13412
- const content = readFileSync7(path, "utf-8");
13778
+ const content = readFileSync8(path, "utf-8");
13413
13779
  try {
13414
13780
  const rejectedKeyPaths = [];
13415
13781
  const parsed = sanitizeParsedJson(import_comment_json2.parse(content), {
@@ -13458,7 +13824,7 @@ var init_jsonc_config = __esm(() => {
13458
13824
  });
13459
13825
 
13460
13826
  // src/lib/pi-package-entry.ts
13461
- import { existsSync as existsSync12, readFileSync as readFileSync8 } from "node:fs";
13827
+ import { existsSync as existsSync13, readFileSync as readFileSync9 } from "node:fs";
13462
13828
  import { isAbsolute as isAbsolute5, join as join12 } from "node:path";
13463
13829
  function stripNpmPrefix(value) {
13464
13830
  return value.startsWith("npm:") ? value.slice("npm:".length) : value;
@@ -13515,10 +13881,10 @@ function hasPiMagicContextPackage(entries, agentDir = getPiAgentConfigDir()) {
13515
13881
  }
13516
13882
  function isPiMagicContextPackageDir(dir) {
13517
13883
  const packageJson = join12(dir, "package.json");
13518
- if (!existsSync12(packageJson))
13884
+ if (!existsSync13(packageJson))
13519
13885
  return false;
13520
13886
  try {
13521
- const pkg = JSON.parse(readFileSync8(packageJson, "utf-8"));
13887
+ const pkg = JSON.parse(readFileSync9(packageJson, "utf-8"));
13522
13888
  return typeof pkg.name === "string" && pkg.name === PI_MAGIC_CONTEXT_PACKAGE_NAME;
13523
13889
  } catch {
13524
13890
  return false;
@@ -13554,7 +13920,7 @@ var init_pi_package_entry = __esm(() => {
13554
13920
  });
13555
13921
 
13556
13922
  // src/adapters/pi.ts
13557
- import { existsSync as existsSync13 } from "node:fs";
13923
+ import { existsSync as existsSync14 } from "node:fs";
13558
13924
  import { dirname as dirname5 } from "node:path";
13559
13925
 
13560
13926
  class PiAdapter {
@@ -13656,7 +14022,7 @@ class PiAdapter {
13656
14022
  return "Install Pi: https://pi.coding/install (npm: @earendil-works/pi-coding-agent)";
13657
14023
  }
13658
14024
  getPluginCacheInfo() {
13659
- return { path: null, exists: false, sizeBytes: 0 };
14025
+ return [{ path: null, exists: false, sizeBytes: 0 }];
13660
14026
  }
13661
14027
  getLogPath() {
13662
14028
  return getMagicContextLogPath2("pi");
@@ -13693,7 +14059,7 @@ function writePiSettings(settings) {
13693
14059
  }
13694
14060
  function ensureDir2(filePath) {
13695
14061
  const dir = dirname5(filePath);
13696
- if (!existsSync13(dir)) {
14062
+ if (!existsSync14(dir)) {
13697
14063
  const { mkdirSync } = __require("node:fs");
13698
14064
  mkdirSync(dir, { recursive: true });
13699
14065
  }
@@ -13823,10 +14189,10 @@ var init_harness_provider_map = __esm(() => {
13823
14189
  // ../plugin/src/config/raw-loader.ts
13824
14190
  import {
13825
14191
  closeSync as closeSync2,
13826
- existsSync as existsSync14,
14192
+ existsSync as existsSync15,
13827
14193
  linkSync,
13828
14194
  openSync as openSync2,
13829
- readFileSync as readFileSync9,
14195
+ readFileSync as readFileSync10,
13830
14196
  renameSync as renameSync3,
13831
14197
  statSync as statSync7,
13832
14198
  unlinkSync as unlinkSync2,
@@ -14026,7 +14392,7 @@ function writeExclusiveBackup(backupPath, bytes, mode) {
14026
14392
  if (error.code !== "EEXIST")
14027
14393
  throw error;
14028
14394
  }
14029
- const existingBytes = readFileSync9(backupPath);
14395
+ const existingBytes = readFileSync10(backupPath);
14030
14396
  if (existingBytes.equals(bytes))
14031
14397
  return;
14032
14398
  if (bytes.subarray(0, existingBytes.length).equals(existingBytes)) {
@@ -14070,11 +14436,11 @@ function migrationWarning(diagnostic) {
14070
14436
  return diagnostic.message;
14071
14437
  }
14072
14438
  function loadRawConfigFile(options) {
14073
- if (!existsSync14(options.configPath))
14439
+ if (!existsSync15(options.configPath))
14074
14440
  return null;
14075
14441
  let observedBytes;
14076
14442
  try {
14077
- observedBytes = readFileSync9(options.configPath);
14443
+ observedBytes = readFileSync10(options.configPath);
14078
14444
  } catch (error) {
14079
14445
  throw new Error(`failed to read config: ${error instanceof Error ? error.message : String(error)}`);
14080
14446
  }
@@ -14118,7 +14484,7 @@ function loadRawConfigFile(options) {
14118
14484
  writeExclusiveBackup(backupPath, observedBytes, mode);
14119
14485
  temporaryPath = writeTemporaryCandidate(options.configPath, migration.bytes, mode);
14120
14486
  options.afterTemporaryWrite?.();
14121
- const currentBytes = readFileSync9(options.configPath);
14487
+ const currentBytes = readFileSync10(options.configPath);
14122
14488
  if (!hasFlatKeys(currentBytes)) {
14123
14489
  unlinkSync2(temporaryPath);
14124
14490
  return {
@@ -14490,10 +14856,10 @@ var init_dreamer_setup = __esm(() => {
14490
14856
  });
14491
14857
 
14492
14858
  // src/commands/setup-pi.ts
14493
- import { existsSync as existsSync15, mkdirSync as mkdirSync3 } from "node:fs";
14859
+ import { existsSync as existsSync16, mkdirSync as mkdirSync3 } from "node:fs";
14494
14860
  import { dirname as dirname7 } from "node:path";
14495
14861
  function ensureDir3(path) {
14496
- if (!existsSync15(path))
14862
+ if (!existsSync16(path))
14497
14863
  mkdirSync3(path, { recursive: true });
14498
14864
  }
14499
14865
  async function getDefaultPrompts() {
@@ -15182,6 +15548,12 @@ var init_redaction = __esm(() => {
15182
15548
  // ../plugin/src/shared/logger.ts
15183
15549
  import * as fs from "node:fs";
15184
15550
  import * as path2 from "node:path";
15551
+ function boundBuffer() {
15552
+ while (bufferedBytes > MAX_BUFFERED_BYTES && buffer.length > 0) {
15553
+ bufferedBytes -= Buffer.byteLength(buffer.shift() ?? "");
15554
+ droppedLines++;
15555
+ }
15556
+ }
15185
15557
  function recordSwallowedWrite(error) {
15186
15558
  try {
15187
15559
  swallowedWriteCount++;
@@ -15264,12 +15636,12 @@ function flush() {
15264
15636
  clearTimeout(flushTimer);
15265
15637
  flushTimer = null;
15266
15638
  }
15267
- if (buffer.length === 0)
15639
+ if (buffer.length === 0 && droppedLines === 0)
15268
15640
  return;
15269
- const bufferedData = buffer.join("");
15270
- buffer = [];
15641
+ const notice = droppedLines ? `[${new Date().toISOString()}] [magic-context][global] logger dropped ${droppedLines} ${droppedLines === 1 ? "line" : "lines"}: buffer exceeded ${MAX_BUFFERED_BYTES} bytes while writes were pending
15642
+ ` : "";
15271
15643
  try {
15272
- const data = capLogData(bufferedData);
15644
+ const data = capLogData(notice + buffer.join(""));
15273
15645
  const logFile = getMagicContextLogPath();
15274
15646
  ensureDir4(logFile);
15275
15647
  let currentSize = getCurrentLogSize(logFile);
@@ -15279,6 +15651,9 @@ function flush() {
15279
15651
  currentSize = 0;
15280
15652
  }
15281
15653
  fs.appendFileSync(logFile, data, { encoding: "utf8", mode: 384 });
15654
+ buffer = [];
15655
+ bufferedBytes = 0;
15656
+ droppedLines = 0;
15282
15657
  activeLogFile = logFile;
15283
15658
  activeLogSize = currentSize + dataSize;
15284
15659
  flushesSinceSizeCheck++;
@@ -15287,6 +15662,7 @@ function flush() {
15287
15662
  activeLogSize = null;
15288
15663
  flushesSinceSizeCheck = 0;
15289
15664
  recordSwallowedWrite(error);
15665
+ boundBuffer();
15290
15666
  }
15291
15667
  }
15292
15668
  function scheduleFlush() {
@@ -15304,8 +15680,11 @@ function log3(message, data) {
15304
15680
  const timestamp = new Date().toISOString();
15305
15681
  const serialized = data === undefined ? "" : data instanceof Error ? ` ${sanitizeDiagnosticText(`${data.message}${data.stack ? `
15306
15682
  ${data.stack}` : ""}`)}` : ` ${JSON.stringify(sanitizeConfigValue(data))}`;
15307
- buffer.push(`[${timestamp}] ${sanitizeDiagnosticText(message)}${serialized}
15308
- `);
15683
+ const line = `[${timestamp}] ${sanitizeDiagnosticText(message)}${serialized}
15684
+ `;
15685
+ buffer.push(line);
15686
+ bufferedBytes += Buffer.byteLength(line);
15687
+ boundBuffer();
15309
15688
  if (buffer.length >= BUFFER_SIZE_LIMIT) {
15310
15689
  flush();
15311
15690
  } else {
@@ -15313,11 +15692,12 @@ ${data.stack}` : ""}`)}` : ` ${JSON.stringify(sanitizeConfigValue(data))}`;
15313
15692
  }
15314
15693
  } catch {}
15315
15694
  }
15316
- var isTestEnv = false, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50, MAX_LOG_FILE_BYTES, SIZE_CHECK_INTERVAL_FLUSHES = 64, activeLogFile = null, activeLogSize = null, flushesSinceSizeCheck = 0, swallowedWriteCount = 0, lastErrorMessage = null, lastErrorTime = null;
15695
+ var isTestEnv = false, buffer, flushTimer = null, FLUSH_INTERVAL_MS = 500, BUFFER_SIZE_LIMIT = 50, MAX_BUFFERED_BYTES, bufferedBytes = 0, droppedLines = 0, MAX_LOG_FILE_BYTES, SIZE_CHECK_INTERVAL_FLUSHES = 64, activeLogFile = null, activeLogSize = null, flushesSinceSizeCheck = 0, swallowedWriteCount = 0, lastErrorMessage = null, lastErrorTime = null;
15317
15696
  var init_logger = __esm(() => {
15318
15697
  init_data_path();
15319
15698
  init_redaction();
15320
15699
  buffer = [];
15700
+ MAX_BUFFERED_BYTES = 1024 * 1024;
15321
15701
  MAX_LOG_FILE_BYTES = 32 * 1024 * 1024;
15322
15702
  if (!isTestEnv) {
15323
15703
  process.on("exit", flush);
@@ -21029,6 +21409,7 @@ var init_magic_context = __esm(() => {
21029
21409
  maxTokens: true
21030
21410
  });
21031
21411
  DreamerConfigSchema = AgentMetadataSchema.extend({
21412
+ runner: _enum(["broca", "host"]).optional().describe(`Which side runs the dreamer completions the Rust module routes (classify-memories) in Rust transform mode: "host" runs them on this process's carrier, "broca" routes them to the Broca module. When unset, historian.runner applies, and when that is unset too the harness decides the same way it does for the historian. User-level config only.`),
21032
21413
  opencode: DreamerOpenCodeHarnessBlockSchema.optional(),
21033
21414
  pi: DreamerPiHarnessBlockSchema.optional(),
21034
21415
  omp: DreamerOmpHarnessBlockSchema.optional(),
@@ -21039,12 +21420,16 @@ var init_magic_context = __esm(() => {
21039
21420
  opencode: OpenCodeHarnessBlockSchema.optional(),
21040
21421
  pi: PiHarnessBlockSchema.optional(),
21041
21422
  omp: OmpHarnessBlockSchema.optional(),
21423
+ runner: _enum(["broca", "host"]).optional().describe('Which side runs the historian completion in Rust transform mode: "host" queues it for this process to run on the configured historian model, "broca" routes it to the Broca module. When unset the harness decides: OpenCode 1 and OpenCode 2 use "host", Claude Code (through the Thalamus gateway, which has no host to run a completion) uses "broca". User-level config only — it decides whose provider account pays for the call.'),
21424
+ host_runner: object({
21425
+ enabled: boolean2().optional().describe("Whether this process answers historian runs queued for a claimant (default true). Setting it to false stops the pull loop without changing historian.runner, so an operator can take one machine out of the lane and leave the queued runs for another claimant or for the runner setting to be changed deliberately.")
21426
+ }).optional().describe("Controls for this process's historian pull loop, which answers runs the Rust module queues for the host runner (`historian.runner: \"host\"`, or unset on OpenCode 1 and OpenCode 2). User-level config only — it decides whether this machine's provider account is spent on folds."),
21042
21427
  two_pass: boolean2().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)"),
21043
21428
  disallowed_tools: array(_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: [])`)
21044
21429
  }).optional();
21045
21430
  EmbeddingFallbackProviderSchema = _enum(["local", "openai-compatible", "off"]);
21046
21431
  BaseEmbeddingConfigSchema = object({
21047
- provider: _enum(["local", "openai-compatible", "off", "synapse"]).default("local").describe("Embedding provider. 'local' uses Xenova/all-MiniLM-L6-v2, 'openai-compatible' requires endpoint and model, 'synapse' uses the certified local Synapse lane with an explicit fallback provider, and 'off' disables embeddings."),
21432
+ provider: _enum(["local", "openai-compatible", "off", "synapse"]).default("local").describe("Embedding provider. 'local' uses Xenova/all-MiniLM-L6-v2, 'openai-compatible' requires endpoint and model, 'synapse' uses the certified local Synapse lane with an explicit fallback provider, and 'off' disables embeddings. Session history is embedded for semantic ctx_search whenever the provider is not 'off', regardless of memory.enabled; memories are embedded only while memory.enabled is true."),
21048
21433
  fallback_provider: EmbeddingFallbackProviderSchema.optional().describe("Fallback provider for the Synapse lane. Required when provider is 'synapse'; local, openai-compatible, and off are valid."),
21049
21434
  model: string2().optional().describe("Embedding model name. Required for openai-compatible, ignored for local."),
21050
21435
  endpoint: string2().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
@@ -21225,7 +21610,7 @@ var init_magic_context = __esm(() => {
21225
21610
  min_chars: number2().min(100).max(1e4).default(500).describe("Text parts shorter than this (characters) stay untouched. Min 100, max 10000. Default: 500.")
21226
21611
  }).default({ enabled: false, min_chars: 500 }).describe("Age-tier caveman compression for long user/assistant text parts. Active for primary sessions when enabled; never for subagents. Oldest 20% of eligible tags (outside protected tail) go to ultra, next 20% to full, next 20% to lite, newest 40% untouched. Graduated from experimental.caveman_text_compression; opt-in, default off (lossy)."),
21227
21612
  memory: object({
21228
- enabled: boolean2().default(true).describe("Enable cross-session memory (default: true)"),
21613
+ enabled: boolean2().default(true).describe("Enable cross-session memory (default: true). Does not affect history embedding or semantic ctx_search over session history; set embedding.provider to 'off' to stop all embedding."),
21229
21614
  injection_budget_tokens: number2().min(500).max(20000).default(4000).describe("Token budget for memory injection on session start (min: 500, max: 20000, default: 4000)"),
21230
21615
  auto_promote: boolean2().default(true).describe("Automatically promote eligible session facts into memory (default: true)"),
21231
21616
  retrieval_count_promotion_threshold: number2().min(1).default(3).describe("retrieval_count threshold for promoting memory to permanent status (min: 1, default: 3)"),
@@ -21674,6 +22059,11 @@ function stripUnsafeProjectConfigFields(projectRaw) {
21674
22059
  warnings.push(`Ignoring ${removed.map((path) => `historian.${path}`).join(", ")} from project config ` + "(security: historian model selection is user-level only; a repository cannot force extra compaction cost).");
21675
22060
  }
21676
22061
  }
22062
+ const dreamer = projectRaw.dreamer;
22063
+ if (isPlainObject3(dreamer) && "runner" in dreamer) {
22064
+ delete dreamer.runner;
22065
+ warnings.push("Ignoring dreamer.runner from project config (security: which process and provider account run dreamer completions is a user-level setting).");
22066
+ }
21677
22067
  const mural = projectRaw.mural;
21678
22068
  if (isPlainObject3(mural) && "model" in mural) {
21679
22069
  delete mural.model;
@@ -21855,7 +22245,11 @@ var init_project_security = __esm(() => {
21855
22245
  init_magic_context();
21856
22246
  HIDDEN_AGENT_KEYS = ["historian", "dreamer"];
21857
22247
  HARNESS_KEYS = PER_HARNESS_MODEL_KEYS;
21858
- HISTORIAN_USER_ONLY_FIELDS = PER_HARNESS_MIGRATION_INVENTORY.historian.migrated_execution;
22248
+ HISTORIAN_USER_ONLY_FIELDS = [
22249
+ ...PER_HARNESS_MIGRATION_INVENTORY.historian.migrated_execution,
22250
+ "runner",
22251
+ "host_runner"
22252
+ ];
21859
22253
  PROMPT_SURFACE_USER_ONLY_FIELDS = ["guidance_override_path", "tool_descriptions"];
21860
22254
  AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools"];
21861
22255
  EMBEDDING_USER_ONLY_FIELDS = [
@@ -21919,16 +22313,16 @@ function resolveTransformMode(args) {
21919
22313
  var RUST_COMPACTION_OFF_WARNING = "compaction-off mode does not support rust transform mode; using the TypeScript transform.", RUST_REQUIRES_USER_SUBC_WARNING = "rust mode requires user-level subc configuration; running ts.";
21920
22314
 
21921
22315
  // ../plugin/src/config/variable.ts
21922
- import { existsSync as existsSync16, readFileSync as readFileSync10 } from "node:fs";
22316
+ import { existsSync as existsSync17, readFileSync as readFileSync11 } from "node:fs";
21923
22317
  import { homedir as homedir10 } from "node:os";
21924
- import { dirname as dirname9, isAbsolute as isAbsolute6, resolve as resolve4 } from "node:path";
22318
+ import { dirname as dirname9, isAbsolute as isAbsolute6, resolve as resolve5 } from "node:path";
21925
22319
  function sensitiveFilePathReason(resolvedPath) {
21926
22320
  const home = homedir10();
21927
22321
  const sensitiveDirs = [
21928
- { dir: resolve4(home, ".ssh"), label: "SSH keys" },
21929
- { dir: resolve4(home, ".aws"), label: "AWS credentials" },
21930
- { dir: resolve4(home, ".gnupg"), label: "GnuPG keyring" },
21931
- { dir: resolve4(home, ".config", "gh"), label: "GitHub CLI auth" }
22322
+ { dir: resolve5(home, ".ssh"), label: "SSH keys" },
22323
+ { dir: resolve5(home, ".aws"), label: "AWS credentials" },
22324
+ { dir: resolve5(home, ".gnupg"), label: "GnuPG keyring" },
22325
+ { dir: resolve5(home, ".config", "gh"), label: "GitHub CLI auth" }
21932
22326
  ];
21933
22327
  for (const { dir, label } of sensitiveDirs) {
21934
22328
  if (resolvedPath === dir || resolvedPath.startsWith(`${dir}/`)) {
@@ -21986,21 +22380,21 @@ function substituteConfigVariables(input) {
21986
22380
  }
21987
22381
  let filePath = rawPath.trim();
21988
22382
  if (filePath.startsWith("~/")) {
21989
- filePath = resolve4(homedir10(), filePath.slice(2));
22383
+ filePath = resolve5(homedir10(), filePath.slice(2));
21990
22384
  } else if (!isAbsolute6(filePath)) {
21991
- filePath = resolve4(configDir, filePath);
22385
+ filePath = resolve5(configDir, filePath);
21992
22386
  }
21993
22387
  const sensitiveReason = sensitiveFilePathReason(filePath);
21994
22388
  if (sensitiveReason) {
21995
22389
  warnings.push(`${token} resolves to a sensitive path (${sensitiveReason}: ${filePath}); ` + "inlining its contents into config — make sure this is intentional.");
21996
22390
  }
21997
- if (!existsSync16(filePath)) {
22391
+ if (!existsSync17(filePath)) {
21998
22392
  warnings.push(`File not found for ${token} (resolved to ${filePath}); using empty string`);
21999
22393
  continue;
22000
22394
  }
22001
22395
  let contents;
22002
22396
  try {
22003
- contents = readFileSync10(filePath, "utf-8").trim();
22397
+ contents = readFileSync11(filePath, "utf-8").trim();
22004
22398
  } catch (error) {
22005
22399
  const message = error instanceof Error ? error.message : String(error);
22006
22400
  warnings.push(`Failed to read file for ${token} (${filePath}): ${message}; using empty string`);
@@ -22019,7 +22413,7 @@ var init_variable = __esm(() => {
22019
22413
  });
22020
22414
 
22021
22415
  // ../plugin/src/config/index.ts
22022
- import { existsSync as existsSync17 } from "node:fs";
22416
+ import { existsSync as existsSync18 } from "node:fs";
22023
22417
  function getUserConfigBasePath() {
22024
22418
  return cortexKitUserConfigBasePath();
22025
22419
  }
@@ -22027,10 +22421,10 @@ function getProjectConfigBasePath(directory) {
22027
22421
  return cortexKitProjectConfigBasePath(directory);
22028
22422
  }
22029
22423
  function resolveLegacyReadFallback(sources) {
22030
- return { source: sources.find((s) => existsSync17(s.path)) ?? null };
22424
+ return { source: sources.find((s) => existsSync18(s.path)) ?? null };
22031
22425
  }
22032
22426
  function loadConfigFileDetailed(configPath, source) {
22033
- if (!existsSync17(configPath)) {
22427
+ if (!existsSync18(configPath)) {
22034
22428
  return null;
22035
22429
  }
22036
22430
  let rawText;
@@ -22170,6 +22564,25 @@ function redactConfigValue(value) {
22170
22564
  function formatProtectedTokensBelowMinWarning(value) {
22171
22565
  return `protected_tokens is a token floor (minimum ${PROTECTED_TOKENS_MIN}, default derived from the context window); ${value} looks like the old protected_tags count. Remove the key to use the default, or set a token count such as 16000.`;
22172
22566
  }
22567
+ function misplacedAgentModelWarnings(rawConfig) {
22568
+ const warnings = [];
22569
+ for (const agent of ["historian", "dreamer"]) {
22570
+ const block = rawConfig[agent];
22571
+ if (!block || typeof block !== "object" || Array.isArray(block))
22572
+ continue;
22573
+ const record = block;
22574
+ if (!Object.hasOwn(record, "model"))
22575
+ continue;
22576
+ const harnessWithModel = MODEL_HARNESS_BLOCKS.find((harness) => {
22577
+ const sub = record[harness];
22578
+ return sub !== null && typeof sub === "object" && !Array.isArray(sub) && Object.hasOwn(sub, "model");
22579
+ });
22580
+ if (harnessWithModel)
22581
+ continue;
22582
+ warnings.push(`${agent}.model is not read: the ${agent} model is resolved per harness, so it has to be ${agent}.opencode.model (OpenCode 1 and 2), ${agent}.pi.model, or ${agent}.omp.model. As written the ${agent} resolves to no models and never runs.`);
22583
+ }
22584
+ return warnings;
22585
+ }
22173
22586
  function warnProtectedTagsDeprecationOnce() {
22174
22587
  if (!warnedProtectedTagsDeprecation) {
22175
22588
  warnedProtectedTagsDeprecation = true;
@@ -22183,6 +22596,7 @@ function parsePluginConfig(rawConfig, recoveredTopLevelKeys = []) {
22183
22596
  warnProtectedTagsDeprecationOnce();
22184
22597
  preMigrationWarnings.push("protected_tags is deprecated and ignored; use protected_tokens instead.");
22185
22598
  }
22599
+ preMigrationWarnings.push(...misplacedAgentModelWarnings(rawConfig));
22186
22600
  const migratedExperimental = migrateLegacyExperimental(configWithoutRemovedAgent, preMigrationWarnings);
22187
22601
  const migratedDreamer = migrateDreamerV2(migratedExperimental, preMigrationWarnings);
22188
22602
  const migrated = migrateLegacyAgentEnabledInMemory(migratedDreamer, preMigrationWarnings);
@@ -22343,8 +22757,8 @@ function loadPluginConfigDetailed(directory, applyRuntimeGlobals = true) {
22343
22757
  const harnessLegacy = resolveLegacyConfigSourcesForHarness(directory, "opencode");
22344
22758
  const userLegacyFallback = userDetected.format === "none" ? resolveLegacyReadFallback(harnessLegacy.user) : { source: null };
22345
22759
  const projectLegacyFallback = projectDetected.format === "none" ? resolveLegacyReadFallback(harnessLegacy.project) : { source: null };
22346
- const legacyUserUnmigrated = userDetected.format === "none" && !userLegacyFallback.source && legacySources.user.some((source) => existsSync17(source.path));
22347
- const legacyProjectUnmigrated = projectDetected.format === "none" && !projectLegacyFallback.source && legacySources.project.some((source) => existsSync17(source.path));
22760
+ const legacyUserUnmigrated = userDetected.format === "none" && !userLegacyFallback.source && legacySources.user.some((source) => existsSync18(source.path));
22761
+ const legacyProjectUnmigrated = projectDetected.format === "none" && !projectLegacyFallback.source && legacySources.project.some((source) => existsSync18(source.path));
22348
22762
  const userLoaded = userDetected.format !== "none" ? loadConfigFileDetailed(userDetected.path, "user") : userLegacyFallback.source ? loadConfigFileDetailed(userLegacyFallback.source.path, "user") : null;
22349
22763
  const projectLoaded = projectDetected.format !== "none" ? loadConfigFileDetailed(projectDetected.path, "project") : projectLegacyFallback.source ? loadConfigFileDetailed(projectLegacyFallback.source.path, "project") : null;
22350
22764
  const allWarnings = [];
@@ -22463,7 +22877,7 @@ function loadPluginConfigDetailed(directory, applyRuntimeGlobals = true) {
22463
22877
  cacheTtlConfigured
22464
22878
  };
22465
22879
  }
22466
- var warnedProtectedTagsDeprecation = false;
22880
+ var warnedProtectedTagsDeprecation = false, MODEL_HARNESS_BLOCKS;
22467
22881
  var init_config = __esm(() => {
22468
22882
  init_config_diagnostics();
22469
22883
  init_jsonc_parser();
@@ -22478,11 +22892,12 @@ var init_config = __esm(() => {
22478
22892
  init_removed_agent_config();
22479
22893
  init_magic_context();
22480
22894
  init_variable();
22895
+ MODEL_HARNESS_BLOCKS = ["opencode", "pi", "omp"];
22481
22896
  });
22482
22897
 
22483
22898
  // ../plugin/src/shared/opencode-config-dir.ts
22484
22899
  import { homedir as homedir11 } from "node:os";
22485
- import { join as join14, resolve as resolve5 } from "node:path";
22900
+ import { join as join14, resolve as resolve6 } from "node:path";
22486
22901
  function resolveHomeDir() {
22487
22902
  return process.env.HOME?.trim() || homedir11();
22488
22903
  }
@@ -22494,7 +22909,7 @@ function getOpenCodeGlobalConfigDir() {
22494
22909
  }
22495
22910
  function getEnvConfigDir() {
22496
22911
  const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim();
22497
- return envConfigDir ? resolve5(envConfigDir) : undefined;
22912
+ return envConfigDir ? resolve6(envConfigDir) : undefined;
22498
22913
  }
22499
22914
  function getCliConfigDir() {
22500
22915
  return getEnvConfigDir() ?? getOpenCodeGlobalConfigDir();
@@ -22781,7 +23196,7 @@ var init_conflict_detector = __esm(() => {
22781
23196
  });
22782
23197
 
22783
23198
  // ../plugin/src/shared/conflict-fixer.ts
22784
- import { existsSync as existsSync18, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "node:fs";
23199
+ import { existsSync as existsSync19, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "node:fs";
22785
23200
  import { homedir as homedir13 } from "node:os";
22786
23201
  import { join as join16 } from "node:path";
22787
23202
  function isRecord2(value) {
@@ -22791,11 +23206,11 @@ function asStringArray(value) {
22791
23206
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
22792
23207
  }
22793
23208
  function readConfig(filePath) {
22794
- if (!existsSync18(filePath)) {
23209
+ if (!existsSync19(filePath)) {
22795
23210
  return null;
22796
23211
  }
22797
23212
  try {
22798
- const text = readFileSync11(filePath, "utf-8");
23213
+ const text = readFileSync12(filePath, "utf-8");
22799
23214
  const parsed = import_comment_json5.parse(text);
22800
23215
  return isRecord2(parsed) ? { config: parsed, text } : null;
22801
23216
  } catch {
@@ -22807,14 +23222,14 @@ function writeConfig(filePath, text) {
22807
23222
  }
22808
23223
  function resolveUserOpenCodeConfigPath() {
22809
23224
  const paths = getOpenCodeConfigPaths({ binary: "opencode" });
22810
- if (existsSync18(paths.configJsonc))
23225
+ if (existsSync19(paths.configJsonc))
22811
23226
  return paths.configJsonc;
22812
23227
  return paths.configJson;
22813
23228
  }
22814
23229
  function collectOpenCodeConfigPaths(directory) {
22815
23230
  const paths = new Set;
22816
23231
  const userConfig = resolveUserOpenCodeConfigPath();
22817
- if (existsSync18(userConfig)) {
23232
+ if (existsSync19(userConfig)) {
22818
23233
  paths.add(userConfig);
22819
23234
  }
22820
23235
  for (const filePath of [
@@ -22823,7 +23238,7 @@ function collectOpenCodeConfigPaths(directory) {
22823
23238
  join16(directory, "opencode.jsonc"),
22824
23239
  join16(directory, "opencode.json")
22825
23240
  ]) {
22826
- if (existsSync18(filePath)) {
23241
+ if (existsSync19(filePath)) {
22827
23242
  paths.add(filePath);
22828
23243
  }
22829
23244
  }
@@ -22835,10 +23250,10 @@ function collectOmoConfigPaths(directory) {
22835
23250
  for (const fileName of OMO_CONFIG_NAMES) {
22836
23251
  const userPath = join16(configDir, fileName);
22837
23252
  const projectPath = join16(directory, fileName);
22838
- if (existsSync18(userPath)) {
23253
+ if (existsSync19(userPath)) {
22839
23254
  paths.add(userPath);
22840
23255
  }
22841
- if (existsSync18(projectPath)) {
23256
+ if (existsSync19(projectPath)) {
22842
23257
  paths.add(projectPath);
22843
23258
  }
22844
23259
  }
@@ -22846,13 +23261,13 @@ function collectOmoConfigPaths(directory) {
22846
23261
  const omoHomeDir = join16(homeDir, ".omo");
22847
23262
  for (const name of OMO_UNIFIED_NAMES) {
22848
23263
  const userPath = join16(omoHomeDir, name);
22849
- if (existsSync18(userPath)) {
23264
+ if (existsSync19(userPath)) {
22850
23265
  paths.add(userPath);
22851
23266
  }
22852
23267
  }
22853
23268
  for (const name of OMO_UNIFIED_NAMES) {
22854
23269
  const projectPath = join16(directory, ".omo", name);
22855
- if (existsSync18(projectPath)) {
23270
+ if (existsSync19(projectPath)) {
22856
23271
  paths.add(projectPath);
22857
23272
  }
22858
23273
  }
@@ -22980,7 +23395,7 @@ var init_conflict_fixer = __esm(() => {
22980
23395
  });
22981
23396
 
22982
23397
  // src/commands/setup-opencode.ts
22983
- import { existsSync as existsSync19, mkdirSync as mkdirSync5, readFileSync as readFileSync12 } from "node:fs";
23398
+ import { existsSync as existsSync20, mkdirSync as mkdirSync5, readFileSync as readFileSync13 } from "node:fs";
22984
23399
  import { basename as basename3, dirname as dirname10 } from "node:path";
22985
23400
  function resolveCompactionEnabledForWriter() {
22986
23401
  try {
@@ -22992,7 +23407,7 @@ function resolveCompactionEnabledForWriter() {
22992
23407
  }
22993
23408
  }
22994
23409
  function ensureDir5(dir) {
22995
- if (!existsSync19(dir)) {
23410
+ if (!existsSync20(dir)) {
22996
23411
  mkdirSync5(dir, { recursive: true });
22997
23412
  }
22998
23413
  }
@@ -23021,7 +23436,7 @@ function readMagicContextConfigForSetup2(configPath) {
23021
23436
  }
23022
23437
  function addPluginToOpenCodeConfig(configPath, _format, removeDcp = false, compactionEnabled = true, hostGeneration = "v1") {
23023
23438
  const registrationKey = pluginConfigKeyFor(hostGeneration);
23024
- const existsAtCommit = existsSync19(configPath);
23439
+ const existsAtCommit = existsSync20(configPath);
23025
23440
  const existing = existsAtCommit ? readJsoncConfigForUpdate(configPath) : {};
23026
23441
  if (!existsAtCommit) {
23027
23442
  ensureDir5(dirname10(configPath));
@@ -23033,7 +23448,7 @@ function addPluginToOpenCodeConfig(configPath, _format, removeDcp = false, compa
23033
23448
  `);
23034
23449
  return;
23035
23450
  }
23036
- let text = readFileSync12(configPath, "utf-8");
23451
+ let text = readFileSync13(configPath, "utf-8");
23037
23452
  let changed = false;
23038
23453
  const rawPlugins = readPluginEntries(existing).map(({ entry }) => entry);
23039
23454
  const retainedPlugins = removeDcp ? rawPlugins.filter((plugin) => !matchesPluginEntry(plugin, DCP_PLUGIN_NAME)) : rawPlugins;
@@ -23079,7 +23494,7 @@ function addPluginToOpenCodeConfig(configPath, _format, removeDcp = false, compa
23079
23494
  writeFileAtomic(configPath, text);
23080
23495
  }
23081
23496
  function addPluginToTuiConfig(configPath, _format) {
23082
- const existsAtCommit = existsSync19(configPath);
23497
+ const existsAtCommit = existsSync20(configPath);
23083
23498
  const existing = existsAtCommit ? readJsoncConfigForUpdate(configPath) : {};
23084
23499
  if (!existsAtCommit) {
23085
23500
  ensureDir5(dirname10(configPath));
@@ -23095,7 +23510,7 @@ function addPluginToTuiConfig(configPath, _format) {
23095
23510
  const hasDevEntry = rawPlugins.some((plugin) => isDevPathPluginEntry(plugin));
23096
23511
  if (hasNpmEntry || hasDevEntry)
23097
23512
  return;
23098
- const text = Array.isArray(existing.plugin) ? appendJsoncArrayValues(readFileSync12(configPath, "utf-8"), ["plugin"], [OPENCODE_PLUGIN_ENTRY_WITH_VERSION]) : setJsoncValue(readFileSync12(configPath, "utf-8"), ["plugin"], [OPENCODE_PLUGIN_ENTRY_WITH_VERSION]);
23513
+ const text = Array.isArray(existing.plugin) ? appendJsoncArrayValues(readFileSync13(configPath, "utf-8"), ["plugin"], [OPENCODE_PLUGIN_ENTRY_WITH_VERSION]) : setJsoncValue(readFileSync13(configPath, "utf-8"), ["plugin"], [OPENCODE_PLUGIN_ENTRY_WITH_VERSION]);
23099
23514
  writeFileAtomic(configPath, text);
23100
23515
  }
23101
23516
  function findDcpPluginIndexes(plugins) {
@@ -23201,7 +23616,7 @@ async function runSetup3(dryRun = false) {
23201
23616
  log2.warn("You can configure models manually in magic-context.jsonc later");
23202
23617
  }
23203
23618
  const paths = detectConfigPaths();
23204
- const hadExistingSetup = paths.opencodeConfigFormat !== "none" || existsSync19(paths.magicContextConfig) || paths.tuiConfigFormat !== "none";
23619
+ const hadExistingSetup = paths.opencodeConfigFormat !== "none" || existsSync20(paths.magicContextConfig) || paths.tuiConfigFormat !== "none";
23205
23620
  if (!dryRun) {
23206
23621
  try {
23207
23622
  assertJsoncConfigsParseable([
@@ -24491,7 +24906,7 @@ var init_context_authority = __esm(async () => {
24491
24906
  // ../plugin/src/features/magic-context/memory/project-identity.ts
24492
24907
  import { execFileSync as execFileSync3 } from "node:child_process";
24493
24908
  import { createHash as createHash2 } from "node:crypto";
24494
- import { existsSync as existsSync20, realpathSync as realpathSync2, statSync as statSync9 } from "node:fs";
24909
+ import { existsSync as existsSync21, realpathSync as realpathSync2, statSync as statSync9 } from "node:fs";
24495
24910
  import path3 from "node:path";
24496
24911
  function asError(error) {
24497
24912
  return error instanceof Error ? error : undefined;
@@ -24729,7 +25144,7 @@ function hasGitDir(canonical) {
24729
25144
  function gitRootInAncestorChain(startDirectory) {
24730
25145
  let current = startDirectory;
24731
25146
  while (true) {
24732
- if (existsSync20(path3.join(current, ".git"))) {
25147
+ if (existsSync21(path3.join(current, ".git"))) {
24733
25148
  try {
24734
25149
  return realpathSync2.native(current);
24735
25150
  } catch {
@@ -26406,6 +26821,297 @@ function isRecord3(value) {
26406
26821
  return typeof value === "object" && value !== null && !Array.isArray(value);
26407
26822
  }
26408
26823
 
26824
+ // ../plugin/src/shared/user-facing-codes.ts
26825
+ function userFacingFailureCode(key) {
26826
+ return USER_FACING_FAILURES[key].code;
26827
+ }
26828
+ var USER_FACING_FAILURES;
26829
+ var init_user_facing_codes = __esm(() => {
26830
+ USER_FACING_FAILURES = {
26831
+ historian_unavailable: {
26832
+ code: "MC-H01",
26833
+ sentence: "History compression could not finish this turn.",
26834
+ action: "It will retry automatically."
26835
+ },
26836
+ hidden_cleanup_unbound: {
26837
+ code: "MC-H02",
26838
+ sentence: "Some finished background sessions could not be removed from this host.",
26839
+ action: "They are removed automatically the next time Magic Context runs inside a registered OpenCode service."
26840
+ },
26841
+ historian_saved_history_misaligned: {
26842
+ code: "MC-H03",
26843
+ sentence: "History compression is paused because this session's saved summaries no longer line up with its messages.",
26844
+ action: "Run /ctx-recomp to rebuild them."
26845
+ },
26846
+ recomp_unavailable: {
26847
+ code: "MC-R01",
26848
+ sentence: "History compression could not be rebuilt.",
26849
+ action: "Run /ctx-recomp again."
26850
+ },
26851
+ dream_provider_timeout: {
26852
+ code: "MC-D01",
26853
+ sentence: "Memory maintenance took too long to respond.",
26854
+ action: "Run /ctx-dream again."
26855
+ },
26856
+ dream_provider_error: {
26857
+ code: "MC-D02",
26858
+ sentence: "Memory maintenance could not reach its model.",
26859
+ action: "Check the model connection, then run /ctx-dream again."
26860
+ },
26861
+ dream_step_limit: {
26862
+ code: "MC-D10",
26863
+ sentence: "Memory maintenance stopped at its hidden agent step limit.",
26864
+ action: "This task needs less work per run; changing the model connection will not help."
26865
+ },
26866
+ dream_empty_completion: {
26867
+ code: "MC-D03",
26868
+ sentence: "Memory maintenance received no usable response.",
26869
+ action: "Run /ctx-dream again."
26870
+ },
26871
+ dream_no_models: {
26872
+ code: "MC-D04",
26873
+ sentence: "Memory maintenance has no model available.",
26874
+ action: "Check the model settings, then run /ctx-dream again."
26875
+ },
26876
+ dream_child_aborted: {
26877
+ code: "MC-D05",
26878
+ sentence: "Memory maintenance was interrupted.",
26879
+ action: "Run /ctx-dream again."
26880
+ },
26881
+ dream_parse_failed: {
26882
+ code: "MC-D06",
26883
+ sentence: "Memory maintenance could not use the model response.",
26884
+ action: "Run /ctx-dream again."
26885
+ },
26886
+ dream_unknown: {
26887
+ code: "MC-D07",
26888
+ sentence: "Memory maintenance could not finish.",
26889
+ action: "Run /ctx-dream again."
26890
+ },
26891
+ dreamer_tick_blocked: {
26892
+ code: "MC-D09",
26893
+ sentence: "Background maintenance is not running: its last pass stopped before it reached the scheduled tasks.",
26894
+ action: "It is retried automatically; if it keeps happening, check the Magic Context log for the stage that stopped and run `npx @cortexkit/magic-context doctor`."
26895
+ },
26896
+ dream_task_needs_tool_loop: {
26897
+ code: "MC-D08",
26898
+ sentence: "Some memory maintenance tasks need a tool loop this host does not provide.",
26899
+ action: "The remaining tasks still run; the listed ones are skipped on this host."
26900
+ },
26901
+ embedding_substitution_rejected: {
26902
+ code: "MC-E01",
26903
+ sentence: "Search indexing could not use the selected model.",
26904
+ action: "Check the embedding model setting, then run /ctx-embed start again."
26905
+ },
26906
+ embedding_http_error: {
26907
+ code: "MC-E02",
26908
+ sentence: "The search indexing provider refused the request.",
26909
+ action: "Check the provider connection and credentials, then run /ctx-embed start again."
26910
+ },
26911
+ embedding_transport_error: {
26912
+ code: "MC-E03",
26913
+ sentence: "Search indexing could not reach its provider.",
26914
+ action: "Check the connection, then run /ctx-embed start again."
26915
+ },
26916
+ embedding_invalid_envelope: {
26917
+ code: "MC-E04",
26918
+ sentence: "Search indexing received an unsupported response.",
26919
+ action: "Check the embedding endpoint, then run /ctx-embed start again."
26920
+ },
26921
+ embedding_empty_result: {
26922
+ code: "MC-E05",
26923
+ sentence: "Search indexing received no usable result.",
26924
+ action: "Run /ctx-embed start again."
26925
+ },
26926
+ embedding_certification_refusal: {
26927
+ code: "MC-E06",
26928
+ sentence: "Search indexing is not ready for this provider.",
26929
+ action: "Finish the provider setup, or set a fallback provider in the embedding settings, then run /ctx-embed start again."
26930
+ },
26931
+ embedding_credential_required: {
26932
+ code: "MC-E07",
26933
+ sentence: "Search indexing needs provider credentials.",
26934
+ action: "Sign in to the provider, then run /ctx-embed start again."
26935
+ },
26936
+ embedding_local_binding_missing: {
26937
+ code: "MC-E08",
26938
+ sentence: "Local search indexing is unavailable on this system.",
26939
+ action: "Run `npx @cortexkit/magic-context doctor`, then retry."
26940
+ },
26941
+ embedding_local_fs_unavailable: {
26942
+ code: "MC-E09",
26943
+ sentence: "Local search indexing cannot save its model files.",
26944
+ action: "Update or reinstall Magic Context, then retry."
26945
+ },
26946
+ embedding_local_download_failure: {
26947
+ code: "MC-E10",
26948
+ sentence: "Local search indexing could not download its model.",
26949
+ action: "Check the network connection, then retry."
26950
+ },
26951
+ embedding_local_runtime_error: {
26952
+ code: "MC-E11",
26953
+ sentence: "Local search indexing could not start.",
26954
+ action: "Run `npx @cortexkit/magic-context doctor`, then retry."
26955
+ },
26956
+ embedding_unavailable: {
26957
+ code: "MC-E12",
26958
+ sentence: "Search indexing could not finish.",
26959
+ action: "Run /ctx-embed start again."
26960
+ },
26961
+ status_unavailable: {
26962
+ code: "MC-S01",
26963
+ sentence: "Magic Context status is temporarily unavailable.",
26964
+ action: "Retry /ctx-status in a moment."
26965
+ },
26966
+ transform_update_failed: {
26967
+ code: "MC-S02",
26968
+ sentence: "The last context update did not finish.",
26969
+ action: "Send another message to retry."
26970
+ },
26971
+ configuration_warning: {
26972
+ code: "MC-S03",
26973
+ sentence: "Some configuration settings could not be applied.",
26974
+ action: "Fix the configuration warning shown in /ctx-status, then restart."
26975
+ },
26976
+ status_log_unavailable: {
26977
+ code: "MC-S04",
26978
+ sentence: "Some diagnostic details could not be saved.",
26979
+ action: "Retry /ctx-status in a moment."
26980
+ },
26981
+ memory_writes_paused: {
26982
+ code: "MC-C01",
26983
+ sentence: "Memory writes are paused while the engine syncs.",
26984
+ action: "Retry in a moment."
26985
+ },
26986
+ memory_access_unavailable: {
26987
+ code: "MC-C02",
26988
+ sentence: "Memory access is temporarily unavailable.",
26989
+ action: "Retry in a moment."
26990
+ },
26991
+ note_changes_paused: {
26992
+ code: "MC-C03",
26993
+ sentence: "Note changes are paused while the engine syncs.",
26994
+ action: "Retry in a moment."
26995
+ },
26996
+ note_access_unavailable: {
26997
+ code: "MC-C04",
26998
+ sentence: "Notes are temporarily unavailable.",
26999
+ action: "Retry in a moment."
27000
+ },
27001
+ context_cleanup_paused: {
27002
+ code: "MC-C05",
27003
+ sentence: "Context cleanup is paused while the engine syncs.",
27004
+ action: "Retry in a moment."
27005
+ },
27006
+ partial_history_unavailable: {
27007
+ code: "MC-C06",
27008
+ sentence: "Partial history compression is not available in the current mode.",
27009
+ action: "Run /ctx-recomp without a range."
27010
+ },
27011
+ smart_note_conditions_unavailable: {
27012
+ code: "MC-C08",
27013
+ sentence: "Conditional notes are not available in the current mode.",
27014
+ action: "Save a regular note without a condition."
27015
+ },
27016
+ history_compression_paused: {
27017
+ code: "MC-C09",
27018
+ sentence: "History compression is paused while the engine syncs.",
27019
+ action: "Retry in a moment."
27020
+ },
27021
+ history_compression_needs_message: {
27022
+ code: "MC-C12",
27023
+ sentence: "History compression has not seen this session since Magic Context reconnected.",
27024
+ action: "Send a message in this session first, then run /ctx-wrapup again."
27025
+ },
27026
+ context_service_unavailable: {
27027
+ code: "MC-C10",
27028
+ sentence: "Magic Context is temporarily unavailable.",
27029
+ action: "Retry in a moment."
27030
+ },
27031
+ store_ahead_of_binary: {
27032
+ code: "MC-C13",
27033
+ sentence: "Magic Context refused to start: its store (store.db) was migrated by a newer ck-mc build than the one running.",
27034
+ action: "Update ck-mc, or roll back by restoring ck-mc together with context.db and store.db from the same backup."
27035
+ },
27036
+ compaction_marker_missing: {
27037
+ code: "MC-C11",
27038
+ sentence: "The history boundary marker is missing from the OpenCode store, so requests carry the full session.",
27039
+ action: "It is retried on every message; if this persists, run `/ctx-flush`."
27040
+ },
27041
+ memory_mirror_stalled: {
27042
+ code: "MC-M01",
27043
+ sentence: "Memory synchronization stopped before the host mirror caught up.",
27044
+ action: "Send another message to resume it, or run `ck doctor drain-authority`."
27045
+ },
27046
+ memory_authority_mismatch: {
27047
+ code: "MC-M02",
27048
+ sentence: "Memory authority is inconsistent between the host and module.",
27049
+ action: "Run `ck doctor drain-authority` before changing Rust mode."
27050
+ },
27051
+ dreamer_task_failing: {
27052
+ code: "MC-S05",
27053
+ sentence: "A background maintenance task keeps failing on its schedule.",
27054
+ action: "Check the Magic Context log for the failing task and its error."
27055
+ }
27056
+ };
27057
+ });
27058
+
27059
+ // ../plugin/src/hooks/magic-context/store-ahead-refusal.ts
27060
+ function renderStoreAheadOfBinaryRefusal(versions) {
27061
+ const failure = USER_FACING_FAILURES.store_ahead_of_binary;
27062
+ const sentence = versions ? `Magic Context refused to start: its store (store.db) is at schema v${versions.dbVersion} but this ck-mc build only knows up to v${versions.binaryMax}.` : failure.sentence;
27063
+ return `${sentence} ${failure.action} (${failure.code})`;
27064
+ }
27065
+ function isRecord4(value) {
27066
+ return value !== null && typeof value === "object";
27067
+ }
27068
+ function nonNegativeInteger(value) {
27069
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
27070
+ }
27071
+ function versionsFromDetail(detail) {
27072
+ if (!isRecord4(detail))
27073
+ return null;
27074
+ const dbVersion = nonNegativeInteger(detail.db_version);
27075
+ const binaryMax = nonNegativeInteger(detail.binary_max);
27076
+ return dbVersion !== null && binaryMax !== null ? { dbVersion, binaryMax } : null;
27077
+ }
27078
+ function versionsFromMessage(message) {
27079
+ if (typeof message !== "string")
27080
+ return null;
27081
+ const match = /db_version=(\d+) binary_max=(\d+)/.exec(message) ?? /schema v(\d+) but this ck-mc build only knows up to v(\d+)/.exec(message);
27082
+ if (!match)
27083
+ return null;
27084
+ return { dbVersion: Number(match[1]), binaryMax: Number(match[2]) };
27085
+ }
27086
+ function storeAheadOfBinaryFailure(error) {
27087
+ let current = error;
27088
+ const seen = new Set;
27089
+ while (isRecord4(current) && !seen.has(current)) {
27090
+ seen.add(current);
27091
+ if (current instanceof StoreAheadOfBinaryError)
27092
+ return current;
27093
+ if (current.code === STORE_AHEAD_OF_BINARY_CODE) {
27094
+ const versions = versionsFromDetail(current.detail) ?? versionsFromMessage(current.message);
27095
+ return new StoreAheadOfBinaryError(versions, { cause: error });
27096
+ }
27097
+ current = current.cause;
27098
+ }
27099
+ return null;
27100
+ }
27101
+ var STORE_AHEAD_OF_BINARY_CODE = "store_ahead_of_binary", StoreAheadOfBinaryError;
27102
+ var init_store_ahead_refusal = __esm(() => {
27103
+ init_user_facing_codes();
27104
+ StoreAheadOfBinaryError = class StoreAheadOfBinaryError extends Error {
27105
+ code = STORE_AHEAD_OF_BINARY_CODE;
27106
+ versions;
27107
+ constructor(versions, options) {
27108
+ super(renderStoreAheadOfBinaryRefusal(versions), options);
27109
+ this.name = "StoreAheadOfBinaryError";
27110
+ this.versions = versions;
27111
+ }
27112
+ };
27113
+ });
27114
+
26409
27115
  // ../plugin/src/hooks/magic-context/module-transport.ts
26410
27116
  import { realpathSync as realpathSync3 } from "node:fs";
26411
27117
  import { join as join17 } from "node:path";
@@ -26676,7 +27382,8 @@ class SubcModuleTransport {
26676
27382
  const laneDeadlineMs = Date.now() + attemptTimeoutMs;
26677
27383
  let releaseLane = () => {};
26678
27384
  try {
26679
- if (!args.bypassSessionLane) {
27385
+ const outsideSessionOrder = args.method.startsWith("historian.");
27386
+ if (!args.bypassSessionLane && !outsideSessionOrder) {
26680
27387
  releaseLane = await this.acquireCorrectnessLane(args.sessionId, args.signal, laneDeadlineMs);
26681
27388
  }
26682
27389
  } catch (error) {
@@ -26736,6 +27443,9 @@ class SubcModuleTransport {
26736
27443
  } catch (error) {
26737
27444
  if (args.signal?.aborted)
26738
27445
  throw args.signal.reason ?? error;
27446
+ const storeAhead = storeAheadOfBinaryFailure(error);
27447
+ if (storeAhead)
27448
+ throw storeAhead;
26739
27449
  if (args.method === "state_sync" && isDeadlineFailure(error)) {
26740
27450
  const body = isRecord3(args.body) ? args.body : {};
26741
27451
  throw Object.assign(new Error(`state_sync timeout stage=module_ack page=${body.seed_batch_index ?? 0}/${body.seed_batch_total ?? 1} series=${body.seed_id ?? "delta"} budget_ms=${attemptTimeoutMs}`), {
@@ -27049,6 +27759,7 @@ var DEFAULT_MODULE_ID = "magic-context", CONNECT_BACKOFF_INITIAL_MS = 1000, CONN
27049
27759
  var init_module_transport = __esm(() => {
27050
27760
  init_dist5();
27051
27761
  init_data_path();
27762
+ init_store_ahead_refusal();
27052
27763
  });
27053
27764
  // ../plugin/src/shared/pi-executable.ts
27054
27765
  function piHarnessKindFromExecutable(value) {
@@ -27064,7 +27775,10 @@ var init_pi_executable = __esm(() => {
27064
27775
 
27065
27776
  // ../plugin/src/shared/rpc-utils.ts
27066
27777
  import { execFileSync as execFileSync4 } from "node:child_process";
27067
- import { readFileSync as readFileSync13 } from "node:fs";
27778
+ import { readFileSync as readFileSync14 } from "node:fs";
27779
+ function isOwnRpcServerRecord(record) {
27780
+ return record.pid === process.pid && record.instance_id !== undefined && ownRpcServerInstanceIds.has(record.instance_id);
27781
+ }
27068
27782
  function isPidAlive(pid) {
27069
27783
  if (!Number.isInteger(pid) || pid <= 0)
27070
27784
  return "dead";
@@ -27587,10 +28301,11 @@ function parseRpcPortFile(content, fallbackPid = 0) {
27587
28301
  function isValidPort(port) {
27588
28302
  return Number.isInteger(port) && port > 0 && port <= 65535;
27589
28303
  }
27590
- var RPC_IDENTITY_SKEW_TOLERANCE_MS = 120000, LINUX_CLOCK_TICKS_PER_SECOND = 100, PS_PROBE_TIMEOUT_MS = 1000, WINDOWS_CIM_PROBE_TIMEOUT_MS = 5000, MAX_ANCESTOR_WALK_DEPTH = 16, OPEN_CODE_COMMAND_MARKERS, TASKLIST_NO_TASKS_PATTERN, WINDOWS_CIM_COMMAND = "Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CommandLine,CreationDate | ConvertTo-Json -Compress", PI_HARNESS_ARC_MARKERS, rpcIdentityReadFileSync, rpcIdentityExecFileSync, rpcIdentityProcessKill, rpcProcessListExecFileSync, rpcIdentityPlatform, rpcIdentityNowMs = () => Date.now(), windowsProcessFactsCache = null;
28304
+ var ownRpcServerInstanceIds, RPC_IDENTITY_SKEW_TOLERANCE_MS = 120000, LINUX_CLOCK_TICKS_PER_SECOND = 100, PS_PROBE_TIMEOUT_MS = 1000, WINDOWS_CIM_PROBE_TIMEOUT_MS = 5000, MAX_ANCESTOR_WALK_DEPTH = 16, OPEN_CODE_COMMAND_MARKERS, TASKLIST_NO_TASKS_PATTERN, WINDOWS_CIM_COMMAND = "Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CommandLine,CreationDate | ConvertTo-Json -Compress", PI_HARNESS_ARC_MARKERS, rpcIdentityReadFileSync, rpcIdentityExecFileSync, rpcIdentityProcessKill, rpcProcessListExecFileSync, rpcIdentityPlatform, rpcIdentityNowMs = () => Date.now(), windowsProcessFactsCache = null;
27591
28305
  var init_rpc_utils = __esm(() => {
27592
28306
  init_logger();
27593
28307
  init_pi_executable();
28308
+ ownRpcServerInstanceIds = new Set;
27594
28309
  OPEN_CODE_COMMAND_MARKERS = ["opencode", "node", "bun", "electron"];
27595
28310
  TASKLIST_NO_TASKS_PATTERN = /^INFO:\s+No tasks are running which match the specified criteria\.?$/im;
27596
28311
  PI_HARNESS_ARC_MARKERS = [
@@ -27600,7 +28315,7 @@ var init_rpc_utils = __esm(() => {
27600
28315
  "cljs/dist",
27601
28316
  "dist/bundle/cli"
27602
28317
  ];
27603
- rpcIdentityReadFileSync = readFileSync13;
28318
+ rpcIdentityReadFileSync = readFileSync14;
27604
28319
  rpcIdentityExecFileSync = execFileSync4;
27605
28320
  rpcIdentityProcessKill = process.kill;
27606
28321
  rpcProcessListExecFileSync = execFileSync4;
@@ -27720,7 +28435,9 @@ function formatFailClosedBlockingMessage(reason) {
27720
28435
  }
27721
28436
  return [
27722
28437
  `Magic Context cannot migrate the shared database (one database serves every project on this machine) because ${formatFailClosedBlockingProcesses(reason.blockingProcesses)} may be running an older Magic Context build that would fail against the migrated database.`,
28438
+ `The database is at upstream migration v${reason.persistedVersion}; this build needs v${reason.supportedVersion}.`,
27723
28439
  "Restart the blocking process — even one from a different project — and it will pick up the new build and migrate on start; or shut it down and retry.",
28440
+ "If that host loads a pinned or cached older Magic Context build, a restart alone is not enough: stop it or update its Magic Context build, then start this host once so it migrates the database with nothing older attached.",
27724
28441
  `Recovery: ${FAIL_CLOSED_DOCTOR_COMMAND}`
27725
28442
  ].join(" ");
27726
28443
  }
@@ -27755,6 +28472,47 @@ var init_message_fts_rowid_map = __esm(() => {
27755
28472
  activeBackfills = new WeakMap;
27756
28473
  });
27757
28474
 
28475
+ // ../plugin/src/features/magic-context/memory/constants.ts
28476
+ var V2_MEMORY_CATEGORIES, PROMOTABLE_CATEGORIES, CATEGORY_PRIORITY, MEMORY_CATEGORY_ORDER_UNKNOWN = 99, MEMORY_CATEGORY_ORDER_PRIORITY, MEMORY_CATEGORY_ORDER_SQL, CATEGORY_DEFAULT_TTL;
28477
+ var init_constants = __esm(() => {
28478
+ V2_MEMORY_CATEGORIES = [
28479
+ "PROJECT_RULES",
28480
+ "ARCHITECTURE",
28481
+ "CONSTRAINTS",
28482
+ "CONFIG_VALUES",
28483
+ "NAMING"
28484
+ ];
28485
+ PROMOTABLE_CATEGORIES = [
28486
+ ...V2_MEMORY_CATEGORIES,
28487
+ "ARCHITECTURE_DECISIONS",
28488
+ "CONFIG_DEFAULTS",
28489
+ "USER_PREFERENCES",
28490
+ "USER_DIRECTIVES",
28491
+ "ENVIRONMENT",
28492
+ "WORKFLOW_RULES",
28493
+ "KNOWN_ISSUES"
28494
+ ];
28495
+ CATEGORY_PRIORITY = [
28496
+ ...V2_MEMORY_CATEGORIES,
28497
+ "USER_DIRECTIVES",
28498
+ "USER_PREFERENCES",
28499
+ "CONFIG_DEFAULTS",
28500
+ "ARCHITECTURE_DECISIONS",
28501
+ "ENVIRONMENT",
28502
+ "WORKFLOW_RULES",
28503
+ "KNOWN_ISSUES"
28504
+ ];
28505
+ MEMORY_CATEGORY_ORDER_PRIORITY = CATEGORY_PRIORITY.reduce((acc, category, index) => {
28506
+ acc[category] = index;
28507
+ return acc;
28508
+ }, {});
28509
+ MEMORY_CATEGORY_ORDER_SQL = `CASE category ${CATEGORY_PRIORITY.map((category, index) => `WHEN '${category}' THEN ${index}`).join(" ")} ELSE ${MEMORY_CATEGORY_ORDER_UNKNOWN} END`;
28510
+ CATEGORY_DEFAULT_TTL = {
28511
+ WORKFLOW_RULES: 90 * 24 * 60 * 60 * 1000,
28512
+ KNOWN_ISSUES: 30 * 24 * 60 * 60 * 1000
28513
+ };
28514
+ });
28515
+
27758
28516
  // ../plugin/src/hooks/magic-context/compartment-parser.ts
27759
28517
  function makeTierOpenRegex(n) {
27760
28518
  return new RegExp(`<p${n}\\s*(/?)>`);
@@ -27784,6 +28542,8 @@ function extractTiersFromInner(inner) {
27784
28542
  function parseCompartmentOutput(text) {
27785
28543
  const compartments = [];
27786
28544
  const facts = [];
28545
+ let droppedFactBlocks = 0;
28546
+ let droppedFacts = 0;
27787
28547
  for (const match of text.matchAll(COMPARTMENT_REGEX)) {
27788
28548
  const attrs = match[1];
27789
28549
  const inner = match[2];
@@ -27833,16 +28593,21 @@ function parseCompartmentOutput(text) {
27833
28593
  }
27834
28594
  }
27835
28595
  const factsBlockMatch = text.match(FACTS_BLOCK_REGEX);
27836
- const factsScope = factsBlockMatch ? factsBlockMatch[1] : text.replace(EVENTS_BLOCK_REGEX, "").replace(/<compartment\s+[^>]*?\s*>.*?<\/compartment>/gs, "");
28596
+ const factsScope = factsBlockMatch ? factsBlockMatch[1] : text.replace(EVENTS_BLOCK_REGEX, "").replace(/<compartment\s+[^>]*?\s*>.*?<\/compartment>/gs, "").replace(/<(meta|user_observations|primer_candidates)>.*?<\/\1>/gs, "").replace(/<\/?(?:output|compartments)>/g, "");
27837
28597
  for (const categoryMatch of factsScope.matchAll(CATEGORY_BLOCK_REGEX)) {
27838
28598
  const category = categoryMatch[1];
27839
28599
  const blockContent = categoryMatch[2];
27840
- for (const itemMatch of blockContent.matchAll(FACT_ITEM_REGEX)) {
27841
- const content = unescapeXml(itemMatch[1].trim());
27842
- if (content) {
27843
- facts.push({ category, content });
27844
- }
28600
+ const items = [...blockContent.matchAll(FACT_ITEM_REGEX)].map((match) => unescapeXml(match[1].trim())).filter(Boolean);
28601
+ if (!HISTORIAN_CATEGORIES.has(category)) {
28602
+ if (items.length === 0)
28603
+ continue;
28604
+ droppedFactBlocks++;
28605
+ droppedFacts += items.length;
28606
+ log3(`[historian] Dropped <facts> category ${category} (${items.length} facts)`);
28607
+ continue;
27845
28608
  }
28609
+ for (const content of items)
28610
+ facts.push({ category, content });
27846
28611
  }
27847
28612
  const unprocessedMatch = text.match(UNPROCESSED_REGEX);
27848
28613
  const unprocessedFrom = unprocessedMatch ? parseInt(unprocessedMatch[1], 10) : null;
@@ -27880,7 +28645,16 @@ function parseCompartmentOutput(text) {
27880
28645
  }
27881
28646
  const events = parseEvents(text);
27882
28647
  compartments.sort((a, b) => a.startMessage - b.startMessage);
27883
- return { compartments, facts, events, unprocessedFrom, userObservations, primerCandidates };
28648
+ return {
28649
+ compartments,
28650
+ facts,
28651
+ droppedFactBlocks,
28652
+ droppedFacts,
28653
+ events,
28654
+ unprocessedFrom,
28655
+ userObservations,
28656
+ primerCandidates
28657
+ };
27884
28658
  }
27885
28659
  function parseEvents(text) {
27886
28660
  const blockMatch = text.match(EVENTS_BLOCK_REGEX);
@@ -27906,8 +28680,10 @@ function parseEvents(text) {
27906
28680
  function unescapeXml(s) {
27907
28681
  return s.replace(/&amp;/g, "&").replace(/&apos;/g, "'").replace(/&quot;/g, '"').replace(/&lt;/g, "<").replace(/&gt;/g, ">");
27908
28682
  }
27909
- var COMPARTMENT_REGEX, ATTR_START_REGEX, ATTR_END_REGEX, ATTR_TITLE_REGEX, ATTR_EPISODE_REGEX, ATTR_IMPORTANCE_REGEX, TIER_OPEN_REGEXES, TIER_CLOSE_ANY_REGEX, TIER_OPEN_ANY_REGEX, CATEGORY_BLOCK_REGEX, FACT_ITEM_REGEX, UNPROCESSED_REGEX, USER_OBSERVATIONS_REGEX, USER_OBS_ITEM_REGEX, PRIMER_CANDIDATES_REGEX, PRIMER_ELEMENT_REGEX, PRIMER_ITEM_REGEX, FACTS_BLOCK_REGEX, EVENTS_BLOCK_REGEX, EVENT_ELEMENT_REGEX, EVENT_FIELD_REGEX;
28683
+ var COMPARTMENT_REGEX, ATTR_START_REGEX, ATTR_END_REGEX, ATTR_TITLE_REGEX, ATTR_EPISODE_REGEX, ATTR_IMPORTANCE_REGEX, TIER_OPEN_REGEXES, TIER_CLOSE_ANY_REGEX, TIER_OPEN_ANY_REGEX, CATEGORY_BLOCK_REGEX, HISTORIAN_CATEGORIES, FACT_ITEM_REGEX, UNPROCESSED_REGEX, USER_OBSERVATIONS_REGEX, USER_OBS_ITEM_REGEX, PRIMER_CANDIDATES_REGEX, PRIMER_ELEMENT_REGEX, PRIMER_ITEM_REGEX, FACTS_BLOCK_REGEX, EVENTS_BLOCK_REGEX, EVENT_ELEMENT_REGEX, EVENT_FIELD_REGEX;
27910
28684
  var init_compartment_parser = __esm(() => {
28685
+ init_constants();
28686
+ init_logger();
27911
28687
  COMPARTMENT_REGEX = /<compartment\s+([^>]*?)\s*>(.*?)<\/compartment>/gs;
27912
28688
  ATTR_START_REGEX = /\bstart="(\d+)"/;
27913
28689
  ATTR_END_REGEX = /\bend="(\d+)"/;
@@ -27922,7 +28698,8 @@ var init_compartment_parser = __esm(() => {
27922
28698
  ];
27923
28699
  TIER_CLOSE_ANY_REGEX = /<\/p\d/;
27924
28700
  TIER_OPEN_ANY_REGEX = /<p\d/;
27925
- CATEGORY_BLOCK_REGEX = /<(PROJECT_RULES|ARCHITECTURE|CONSTRAINTS|CONFIG_VALUES|NAMING)>(.*?)<\/\1>/gs;
28701
+ CATEGORY_BLOCK_REGEX = /<([A-Za-z_][A-Za-z0-9_-]*)>(.*?)<\/\1>/gs;
28702
+ HISTORIAN_CATEGORIES = new Set(V2_MEMORY_CATEGORIES);
27926
28703
  FACT_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
27927
28704
  UNPROCESSED_REGEX = /<unprocessed_from>(\d+)<\/unprocessed_from>/;
27928
28705
  USER_OBSERVATIONS_REGEX = /<user_observations>(.*?)<\/user_observations>/s;
@@ -28289,41 +29066,6 @@ function healMissingMemoryBlockIds(db, columns) {
28289
29066
  db.prepare("UPDATE session_meta SET memory_block_cache = '' WHERE memory_block_cache != '' AND (memory_block_ids IS NULL OR memory_block_ids = '') AND memory_block_count > 0").run();
28290
29067
  }
28291
29068
 
28292
- // ../plugin/src/features/magic-context/memory/constants.ts
28293
- var V2_MEMORY_CATEGORIES, CATEGORY_PRIORITY, MEMORY_CATEGORY_ORDER_UNKNOWN = 99, MEMORY_CATEGORY_ORDER_PRIORITY, MEMORY_CATEGORY_ORDER_SQL, CATEGORY_DEFAULT_TTL;
28294
- var init_constants = __esm(() => {
28295
- V2_MEMORY_CATEGORIES = [
28296
- "PROJECT_RULES",
28297
- "ARCHITECTURE",
28298
- "CONSTRAINTS",
28299
- "CONFIG_VALUES",
28300
- "NAMING"
28301
- ];
28302
- CATEGORY_PRIORITY = [
28303
- "PROJECT_RULES",
28304
- "ARCHITECTURE",
28305
- "CONSTRAINTS",
28306
- "CONFIG_VALUES",
28307
- "NAMING",
28308
- "USER_DIRECTIVES",
28309
- "USER_PREFERENCES",
28310
- "CONFIG_DEFAULTS",
28311
- "ARCHITECTURE_DECISIONS",
28312
- "ENVIRONMENT",
28313
- "WORKFLOW_RULES",
28314
- "KNOWN_ISSUES"
28315
- ];
28316
- MEMORY_CATEGORY_ORDER_PRIORITY = CATEGORY_PRIORITY.reduce((acc, category, index) => {
28317
- acc[category] = index;
28318
- return acc;
28319
- }, {});
28320
- MEMORY_CATEGORY_ORDER_SQL = `CASE category ${CATEGORY_PRIORITY.map((category, index) => `WHEN '${category}' THEN ${index}`).join(" ")} ELSE ${MEMORY_CATEGORY_ORDER_UNKNOWN} END`;
28321
- CATEGORY_DEFAULT_TTL = {
28322
- WORKFLOW_RULES: 90 * 24 * 60 * 60 * 1000,
28323
- KNOWN_ISSUES: 30 * 24 * 60 * 60 * 1000
28324
- };
28325
- });
28326
-
28327
29069
  // ../plugin/src/features/magic-context/project-identity.ts
28328
29070
  var init_project_identity2 = __esm(() => {
28329
29071
  init_project_identity();
@@ -30803,6 +31545,20 @@ var init_migrations = __esm(async () => {
30803
31545
  return;
30804
31546
  ensureColumn(db, "compartment_state_lease", "owner_pid", "INTEGER");
30805
31547
  }
31548
+ },
31549
+ {
31550
+ version: 91,
31551
+ description: "per-project embedding high-water mark for memories written outside this host",
31552
+ up(db) {
31553
+ db.exec(`
31554
+ CREATE TABLE IF NOT EXISTS memory_embedding_watermarks (
31555
+ project_path TEXT PRIMARY KEY,
31556
+ written_memory_id INTEGER NOT NULL DEFAULT 0,
31557
+ embedded_memory_id INTEGER NOT NULL DEFAULT 0,
31558
+ updated_at INTEGER NOT NULL DEFAULT 0
31559
+ );
31560
+ `);
31561
+ }
30806
31562
  }
30807
31563
  ];
30808
31564
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -30892,14 +31648,14 @@ import {
30892
31648
  chmodSync as chmodSync3,
30893
31649
  copyFileSync,
30894
31650
  cpSync,
30895
- existsSync as existsSync21,
31651
+ existsSync as existsSync22,
30896
31652
  mkdirSync as mkdirSync6,
30897
- readdirSync as readdirSync3,
30898
- readFileSync as readFileSync14,
31653
+ readdirSync as readdirSync4,
31654
+ readFileSync as readFileSync15,
30899
31655
  statSync as statSync10,
30900
31656
  unlinkSync as unlinkSync3
30901
31657
  } from "node:fs";
30902
- import { basename as basename4, dirname as dirname11, join as join18, resolve as resolve6 } from "node:path";
31658
+ import { basename as basename4, dirname as dirname11, join as join18, resolve as resolve7 } from "node:path";
30903
31659
  function resolveBootBusyTimeoutMs(value) {
30904
31660
  if (value === undefined)
30905
31661
  return BOOT_SQLITE_BUSY_TIMEOUT_MS;
@@ -31034,6 +31790,8 @@ function inspectRpcServerDiscovery(storageDir) {
31034
31790
  return junk;
31035
31791
  continue;
31036
31792
  }
31793
+ if (isOwnRpcServerRecord(record))
31794
+ continue;
31037
31795
  const liveness = isPidAlive(record.pid);
31038
31796
  if (liveness === "dead") {
31039
31797
  staleFiles.push(portFile);
@@ -31655,6 +32413,15 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
31655
32413
  (id, cursor_session_id, cursor_ordinal, completed, updated_at)
31656
32414
  VALUES (1, '', 0, 0, 0);
31657
32415
 
32416
+ -- Highest memory id another writer (the Rust module in single-store mode) put
32417
+ -- into memories for a project, and how far this host has embedded. Migration v91.
32418
+ CREATE TABLE IF NOT EXISTS memory_embedding_watermarks (
32419
+ project_path TEXT PRIMARY KEY,
32420
+ written_memory_id INTEGER NOT NULL DEFAULT 0,
32421
+ embedded_memory_id INTEGER NOT NULL DEFAULT 0,
32422
+ updated_at INTEGER NOT NULL DEFAULT 0
32423
+ );
32424
+
31658
32425
  CREATE TABLE IF NOT EXISTS message_history_index (
31659
32426
  session_id TEXT PRIMARY KEY,
31660
32427
  last_indexed_ordinal INTEGER NOT NULL DEFAULT 0,
@@ -32314,7 +33081,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
32314
33081
  `);
32315
33082
  ensureColumn(db, "workspaces", "share_categories", `TEXT NOT NULL DEFAULT '["CONSTRAINTS"]'`);
32316
33083
  }
32317
- var databases, pendingAsyncOpens, persistenceByDatabase, persistenceErrorByDatabase, pathByDatabase, LATEST_SUPPORTED_VERSION = 90, BOOT_SQLITE_BUSY_TIMEOUT_MS = 5000, PERMISSIONS_ENFORCEABLE, RPC_DISCOVERY_PARSE_GRACE_MS, defaultRpcDiscoveryFs, rpcDiscoveryFs, sqlitePragmaConfig, CHANNEL2_CLAIM_TTL_MS;
33084
+ var databases, pendingAsyncOpens, persistenceByDatabase, persistenceErrorByDatabase, pathByDatabase, LATEST_SUPPORTED_VERSION = 91, BOOT_SQLITE_BUSY_TIMEOUT_MS = 5000, PERMISSIONS_ENFORCEABLE, RPC_DISCOVERY_PARSE_GRACE_MS, defaultRpcDiscoveryFs, rpcDiscoveryFs, sqlitePragmaConfig, CHANNEL2_CLAIM_TTL_MS;
32318
33085
  var init_storage_db = __esm(async () => {
32319
33086
  init_data_path();
32320
33087
  init_logger();
@@ -32338,8 +33105,8 @@ var init_storage_db = __esm(async () => {
32338
33105
  PERMISSIONS_ENFORCEABLE = process.platform !== "win32";
32339
33106
  RPC_DISCOVERY_PARSE_GRACE_MS = 10 * 60 * 1000;
32340
33107
  defaultRpcDiscoveryFs = {
32341
- readdirSync: (path, options) => options?.withFileTypes ? readdirSync3(path, { withFileTypes: true }) : readdirSync3(path),
32342
- readFileSync: (path, encoding) => String(readFileSync14(path, encoding)),
33108
+ readdirSync: (path, options) => options?.withFileTypes ? readdirSync4(path, { withFileTypes: true }) : readdirSync4(path),
33109
+ readFileSync: (path, encoding) => String(readFileSync15(path, encoding)),
32343
33110
  statSync: (path) => ({ mtimeMs: statSync10(path).mtimeMs }),
32344
33111
  unlinkSync: (path) => unlinkSync3(path)
32345
33112
  };
@@ -32352,13 +33119,13 @@ var init_storage_db = __esm(async () => {
32352
33119
  });
32353
33120
 
32354
33121
  // src/lib/database-access.ts
32355
- import { existsSync as existsSync22, writeFileSync as writeFileSync5 } from "node:fs";
33122
+ import { existsSync as existsSync23, writeFileSync as writeFileSync5 } from "node:fs";
32356
33123
  import { pathToFileURL } from "node:url";
32357
33124
  function getPersistedSchemaVersion2(db) {
32358
33125
  return getPersistedSchemaVersion(db);
32359
33126
  }
32360
33127
  function openExistingDatabase(path, options) {
32361
- if (!existsSync22(path))
33128
+ if (!existsSync23(path))
32362
33129
  return null;
32363
33130
  if (options.readonly) {
32364
33131
  const db = new Database(path, { readonly: true });
@@ -32969,16 +33736,16 @@ var init_doctor_merge_identity = __esm(async () => {
32969
33736
  });
32970
33737
 
32971
33738
  // src/commands/doctor-repair-db.ts
32972
- import { spawnSync as spawnSync3 } from "node:child_process";
33739
+ import { spawnSync as spawnSync4 } from "node:child_process";
32973
33740
  import {
32974
33741
  chmodSync as chmodSync4,
32975
33742
  closeSync as closeSync4,
32976
33743
  constants as constants2,
32977
33744
  copyFileSync as copyFileSync2,
32978
- existsSync as existsSync23,
33745
+ existsSync as existsSync24,
32979
33746
  openSync as openSync4,
32980
33747
  renameSync as renameSync4,
32981
- rmSync as rmSync2,
33748
+ rmSync as rmSync3,
32982
33749
  statSync as statSync11
32983
33750
  } from "node:fs";
32984
33751
  import { dirname as dirname12, join as join20 } from "node:path";
@@ -33011,11 +33778,11 @@ function timestamp(date) {
33011
33778
  return date.toISOString().replaceAll("-", "").replaceAll(":", "").replace(".", "");
33012
33779
  }
33013
33780
  function uniqueBase(preferred) {
33014
- if (!DATABASE_SUFFIXES.some((suffix) => existsSync23(`${preferred}${suffix}`)))
33781
+ if (!DATABASE_SUFFIXES.some((suffix) => existsSync24(`${preferred}${suffix}`)))
33015
33782
  return preferred;
33016
33783
  for (let attempt = 1;attempt < 1e4; attempt++) {
33017
33784
  const candidate = `${preferred}-${attempt}`;
33018
- if (!DATABASE_SUFFIXES.some((suffix) => existsSync23(`${candidate}${suffix}`)))
33785
+ if (!DATABASE_SUFFIXES.some((suffix) => existsSync24(`${candidate}${suffix}`)))
33019
33786
  return candidate;
33020
33787
  }
33021
33788
  throw new Error(`Could not allocate a unique recovery path beside ${preferred}`);
@@ -33029,7 +33796,7 @@ function copyDatabaseBundle(sourceBase, destinationBase) {
33029
33796
  const copiedPaths = [];
33030
33797
  for (const suffix of DATABASE_SUFFIXES) {
33031
33798
  const source = `${sourceBase}${suffix}`;
33032
- if (!existsSync23(source))
33799
+ if (!existsSync24(source))
33033
33800
  continue;
33034
33801
  const destination = `${destinationBase}${suffix}`;
33035
33802
  copyFileSync2(source, destination, constants2.COPYFILE_EXCL);
@@ -33097,7 +33864,7 @@ function runRecoverShell(sqliteExecutable, sourcePath, recoveredPath, dumpPath)
33097
33864
  let dumpWriteFd = null;
33098
33865
  try {
33099
33866
  dumpWriteFd = openSync4(dumpPath, "wx", 384);
33100
- const recovered = spawnSync3(sqliteExecutable, [sourcePath, ".recover"], {
33867
+ const recovered = spawnSync4(sqliteExecutable, [sourcePath, ".recover"], {
33101
33868
  stdio: ["ignore", dumpWriteFd, "pipe"],
33102
33869
  encoding: "utf8",
33103
33870
  windowsHide: true
@@ -33116,7 +33883,7 @@ function runRecoverShell(sqliteExecutable, sourcePath, recoveredPath, dumpPath)
33116
33883
  }
33117
33884
  const dumpReadFd = openSync4(dumpPath, "r");
33118
33885
  try {
33119
- const replayed = spawnSync3(sqliteExecutable, [recoveredPath], {
33886
+ const replayed = spawnSync4(sqliteExecutable, [recoveredPath], {
33120
33887
  stdio: [dumpReadFd, "ignore", "pipe"],
33121
33888
  encoding: "utf8",
33122
33889
  windowsHide: true
@@ -33143,7 +33910,7 @@ function runRecoverShell(sqliteExecutable, sourcePath, recoveredPath, dumpPath)
33143
33910
  } finally {
33144
33911
  if (dumpWriteFd !== null)
33145
33912
  closeSync4(dumpWriteFd);
33146
- rmSync2(dumpPath, { force: true });
33913
+ rmSync3(dumpPath, { force: true });
33147
33914
  }
33148
33915
  }
33149
33916
  function migrateAndCheckRecoveredDatabase(path) {
@@ -33222,14 +33989,14 @@ function prepareFreshDatabase(path) {
33222
33989
  }
33223
33990
  function removeRecoveryBundle(basePath) {
33224
33991
  for (const suffix of DATABASE_SUFFIXES)
33225
- rmSync2(`${basePath}${suffix}`, { force: true });
33992
+ rmSync3(`${basePath}${suffix}`, { force: true });
33226
33993
  }
33227
33994
  function activateReplacement(dbPath, replacementPath, originalAsidePath) {
33228
33995
  const movedOriginals = [];
33229
33996
  try {
33230
33997
  for (const suffix of DATABASE_SUFFIXES) {
33231
33998
  const from = `${dbPath}${suffix}`;
33232
- if (!existsSync23(from))
33999
+ if (!existsSync24(from))
33233
34000
  continue;
33234
34001
  const to = `${originalAsidePath}${suffix}`;
33235
34002
  renameSync4(from, to);
@@ -33239,7 +34006,7 @@ function activateReplacement(dbPath, replacementPath, originalAsidePath) {
33239
34006
  return movedOriginals.map(({ to }) => to);
33240
34007
  } catch (error) {
33241
34008
  for (const moved of movedOriginals.reverse()) {
33242
- if (existsSync23(moved.to) && !existsSync23(moved.from))
34009
+ if (existsSync24(moved.to) && !existsSync24(moved.from))
33243
34010
  renameSync4(moved.to, moved.from);
33244
34011
  }
33245
34012
  throw error;
@@ -33275,7 +34042,7 @@ async function runRepairDb(options = {}) {
33275
34042
  const stamp = timestamp(deps.now());
33276
34043
  prompts.intro("Magic Context — Repair shared database");
33277
34044
  prompts.log.info(`Database: ${dbPath}`);
33278
- if (!existsSync23(dbPath)) {
34045
+ if (!existsSync24(dbPath)) {
33279
34046
  prompts.log.error(`Database not found: ${dbPath}`);
33280
34047
  prompts.log.info("Backup: not created (database does not exist)");
33281
34048
  prompts.outro("Database repair failed");
@@ -33562,7 +34329,7 @@ var init_migration_paths = __esm(() => {
33562
34329
 
33563
34330
  // src/commands/migrate.ts
33564
34331
  import { createHash as createHash4, randomBytes as randomBytes2 } from "node:crypto";
33565
- import { existsSync as existsSync24, mkdirSync as mkdirSync7, renameSync as renameSync5, unlinkSync as unlinkSync4 } from "node:fs";
34332
+ import { existsSync as existsSync25, mkdirSync as mkdirSync7, renameSync as renameSync5, unlinkSync as unlinkSync4 } from "node:fs";
33566
34333
  import { dirname as dirname13, join as join21 } from "node:path";
33567
34334
  function defaultOpenCodeDbPath() {
33568
34335
  return getOpenCodeDatabasePath();
@@ -33574,7 +34341,7 @@ function defaultPiSessionsRoot() {
33574
34341
  return getPiSessionsRoot();
33575
34342
  }
33576
34343
  function defaultFs() {
33577
- return { writeFileAtomic, unlinkSync: unlinkSync4, existsSync: existsSync24, renameSync: renameSync5, mkdirSync: mkdirSync7 };
34344
+ return { writeFileAtomic, unlinkSync: unlinkSync4, existsSync: existsSync25, renameSync: renameSync5, mkdirSync: mkdirSync7 };
33578
34345
  }
33579
34346
  function stmt(db, sql) {
33580
34347
  return db.prepare(sql);
@@ -34522,7 +35289,7 @@ var init_relocate_memory = __esm(() => {
34522
35289
  });
34523
35290
 
34524
35291
  // src/commands/migrate-session.ts
34525
- import { existsSync as existsSync25, realpathSync as realpathSync4 } from "node:fs";
35292
+ import { existsSync as existsSync26, realpathSync as realpathSync4 } from "node:fs";
34526
35293
  import { homedir as homedir14 } from "node:os";
34527
35294
  import path4, { join as join22 } from "node:path";
34528
35295
  function existingSessionColumns(db) {
@@ -34741,7 +35508,7 @@ function realDeps(opencodeDb, contextDb) {
34741
35508
  opencodeDb,
34742
35509
  contextDb,
34743
35510
  resolveIdentity: resolveProjectIdentity,
34744
- hasGitDir: (dir) => existsSync25(join22(dir, ".git")),
35511
+ hasGitDir: (dir) => existsSync26(join22(dir, ".git")),
34745
35512
  realpath: (p) => realpathSync4(p)
34746
35513
  };
34747
35514
  }
@@ -34820,7 +35587,7 @@ async function runMigrateSessionCli(args) {
34820
35587
  return 1;
34821
35588
  }
34822
35589
  const expandedTo = toDir.startsWith("~") ? join22(homedir14(), toDir.slice(1).replace(/^[/\\]/, "")) : toDir;
34823
- if (!existsSync25(expandedTo)) {
35590
+ if (!existsSync26(expandedTo)) {
34824
35591
  console.error(`Target directory does not exist: ${expandedTo}`);
34825
35592
  return 1;
34826
35593
  }
@@ -35099,7 +35866,7 @@ async function doctorRetryV22Backfill(db) {
35099
35866
  bumpProjectMemoryEpochInTransaction(db, identity, now);
35100
35867
  succeeded += 1;
35101
35868
  }
35102
- })();
35869
+ }).immediate();
35103
35870
  } catch (error) {
35104
35871
  const classified = classifyBackfillError(error);
35105
35872
  db.prepare(`UPDATE v22_backfill_failures
@@ -35132,7 +35899,7 @@ async function doctorRekeyV22DirIdentity(db, rawProjectPath) {
35132
35899
  if (changedRows > 0) {
35133
35900
  bumpProjectMemoryEpochInTransaction(db, newIdentity, now);
35134
35901
  }
35135
- })();
35902
+ }).immediate();
35136
35903
  return { oldIdentity, newIdentity, changedRows };
35137
35904
  }
35138
35905
  var BACKFILL_META_KEY = "v22_legacy_memory_backfill", BACKFILL_CURSOR_META_KEY = "v22_legacy_memory_backfill_cursor", MEMORIES_TABLE = "memories";
@@ -35205,226 +35972,6 @@ var init_v22_backfill_commands = __esm(async () => {
35205
35972
  await init_storage_db();
35206
35973
  });
35207
35974
 
35208
- // ../plugin/src/shared/user-facing-codes.ts
35209
- function userFacingFailureCode(key) {
35210
- return USER_FACING_FAILURES[key].code;
35211
- }
35212
- var USER_FACING_FAILURES;
35213
- var init_user_facing_codes = __esm(() => {
35214
- USER_FACING_FAILURES = {
35215
- historian_unavailable: {
35216
- code: "MC-H01",
35217
- sentence: "History compression could not finish this turn.",
35218
- action: "It will retry automatically."
35219
- },
35220
- hidden_cleanup_unbound: {
35221
- code: "MC-H02",
35222
- sentence: "Some finished background sessions could not be removed from this host.",
35223
- action: "They are removed automatically the next time Magic Context runs inside a registered OpenCode service."
35224
- },
35225
- recomp_unavailable: {
35226
- code: "MC-R01",
35227
- sentence: "History compression could not be rebuilt.",
35228
- action: "Run /ctx-recomp again."
35229
- },
35230
- dream_provider_timeout: {
35231
- code: "MC-D01",
35232
- sentence: "Memory maintenance took too long to respond.",
35233
- action: "Run /ctx-dream again."
35234
- },
35235
- dream_provider_error: {
35236
- code: "MC-D02",
35237
- sentence: "Memory maintenance could not reach its model.",
35238
- action: "Check the model connection, then run /ctx-dream again."
35239
- },
35240
- dream_empty_completion: {
35241
- code: "MC-D03",
35242
- sentence: "Memory maintenance received no usable response.",
35243
- action: "Run /ctx-dream again."
35244
- },
35245
- dream_no_models: {
35246
- code: "MC-D04",
35247
- sentence: "Memory maintenance has no model available.",
35248
- action: "Check the model settings, then run /ctx-dream again."
35249
- },
35250
- dream_child_aborted: {
35251
- code: "MC-D05",
35252
- sentence: "Memory maintenance was interrupted.",
35253
- action: "Run /ctx-dream again."
35254
- },
35255
- dream_parse_failed: {
35256
- code: "MC-D06",
35257
- sentence: "Memory maintenance could not use the model response.",
35258
- action: "Run /ctx-dream again."
35259
- },
35260
- dream_unknown: {
35261
- code: "MC-D07",
35262
- sentence: "Memory maintenance could not finish.",
35263
- action: "Run /ctx-dream again."
35264
- },
35265
- dreamer_tick_blocked: {
35266
- code: "MC-D09",
35267
- sentence: "Background maintenance is not running: its last pass stopped before it reached the scheduled tasks.",
35268
- action: "It is retried automatically; if it keeps happening, check the Magic Context log for the stage that stopped and run `npx @cortexkit/magic-context doctor`."
35269
- },
35270
- dream_task_needs_tool_loop: {
35271
- code: "MC-D08",
35272
- sentence: "Some memory maintenance tasks need a tool loop this host does not provide.",
35273
- action: "The remaining tasks still run; the listed ones are skipped on this host."
35274
- },
35275
- embedding_substitution_rejected: {
35276
- code: "MC-E01",
35277
- sentence: "Search indexing could not use the selected model.",
35278
- action: "Check the embedding model setting, then run /ctx-embed start again."
35279
- },
35280
- embedding_http_error: {
35281
- code: "MC-E02",
35282
- sentence: "The search indexing provider refused the request.",
35283
- action: "Check the provider connection and credentials, then run /ctx-embed start again."
35284
- },
35285
- embedding_transport_error: {
35286
- code: "MC-E03",
35287
- sentence: "Search indexing could not reach its provider.",
35288
- action: "Check the connection, then run /ctx-embed start again."
35289
- },
35290
- embedding_invalid_envelope: {
35291
- code: "MC-E04",
35292
- sentence: "Search indexing received an unsupported response.",
35293
- action: "Check the embedding endpoint, then run /ctx-embed start again."
35294
- },
35295
- embedding_empty_result: {
35296
- code: "MC-E05",
35297
- sentence: "Search indexing received no usable result.",
35298
- action: "Run /ctx-embed start again."
35299
- },
35300
- embedding_certification_refusal: {
35301
- code: "MC-E06",
35302
- sentence: "Search indexing is not ready for this provider.",
35303
- action: "Finish the provider setup, or set a fallback provider in the embedding settings, then run /ctx-embed start again."
35304
- },
35305
- embedding_credential_required: {
35306
- code: "MC-E07",
35307
- sentence: "Search indexing needs provider credentials.",
35308
- action: "Sign in to the provider, then run /ctx-embed start again."
35309
- },
35310
- embedding_local_binding_missing: {
35311
- code: "MC-E08",
35312
- sentence: "Local search indexing is unavailable on this system.",
35313
- action: "Run `npx @cortexkit/magic-context doctor`, then retry."
35314
- },
35315
- embedding_local_fs_unavailable: {
35316
- code: "MC-E09",
35317
- sentence: "Local search indexing cannot save its model files.",
35318
- action: "Update or reinstall Magic Context, then retry."
35319
- },
35320
- embedding_local_download_failure: {
35321
- code: "MC-E10",
35322
- sentence: "Local search indexing could not download its model.",
35323
- action: "Check the network connection, then retry."
35324
- },
35325
- embedding_local_runtime_error: {
35326
- code: "MC-E11",
35327
- sentence: "Local search indexing could not start.",
35328
- action: "Run `npx @cortexkit/magic-context doctor`, then retry."
35329
- },
35330
- embedding_unavailable: {
35331
- code: "MC-E12",
35332
- sentence: "Search indexing could not finish.",
35333
- action: "Run /ctx-embed start again."
35334
- },
35335
- status_unavailable: {
35336
- code: "MC-S01",
35337
- sentence: "Magic Context status is temporarily unavailable.",
35338
- action: "Retry /ctx-status in a moment."
35339
- },
35340
- transform_update_failed: {
35341
- code: "MC-S02",
35342
- sentence: "The last context update did not finish.",
35343
- action: "Send another message to retry."
35344
- },
35345
- configuration_warning: {
35346
- code: "MC-S03",
35347
- sentence: "Some configuration settings could not be applied.",
35348
- action: "Fix the configuration warning shown in /ctx-status, then restart."
35349
- },
35350
- status_log_unavailable: {
35351
- code: "MC-S04",
35352
- sentence: "Some diagnostic details could not be saved.",
35353
- action: "Retry /ctx-status in a moment."
35354
- },
35355
- memory_writes_paused: {
35356
- code: "MC-C01",
35357
- sentence: "Memory writes are paused while the engine syncs.",
35358
- action: "Retry in a moment."
35359
- },
35360
- memory_access_unavailable: {
35361
- code: "MC-C02",
35362
- sentence: "Memory access is temporarily unavailable.",
35363
- action: "Retry in a moment."
35364
- },
35365
- note_changes_paused: {
35366
- code: "MC-C03",
35367
- sentence: "Note changes are paused while the engine syncs.",
35368
- action: "Retry in a moment."
35369
- },
35370
- note_access_unavailable: {
35371
- code: "MC-C04",
35372
- sentence: "Notes are temporarily unavailable.",
35373
- action: "Retry in a moment."
35374
- },
35375
- context_cleanup_paused: {
35376
- code: "MC-C05",
35377
- sentence: "Context cleanup is paused while the engine syncs.",
35378
- action: "Retry in a moment."
35379
- },
35380
- partial_history_unavailable: {
35381
- code: "MC-C06",
35382
- sentence: "Partial history compression is not available in the current mode.",
35383
- action: "Run /ctx-recomp without a range."
35384
- },
35385
- smart_note_conditions_unavailable: {
35386
- code: "MC-C08",
35387
- sentence: "Conditional notes are not available in the current mode.",
35388
- action: "Save a regular note without a condition."
35389
- },
35390
- history_compression_paused: {
35391
- code: "MC-C09",
35392
- sentence: "History compression is paused while the engine syncs.",
35393
- action: "Retry in a moment."
35394
- },
35395
- context_service_unavailable: {
35396
- code: "MC-C10",
35397
- sentence: "Magic Context is temporarily unavailable.",
35398
- action: "Retry in a moment."
35399
- },
35400
- compaction_marker_missing: {
35401
- code: "MC-C11",
35402
- sentence: "The history boundary marker is missing from the OpenCode store, so requests carry the full session.",
35403
- action: "It is retried on every message; if this persists, run `/ctx-flush`."
35404
- },
35405
- memory_mirror_stalled: {
35406
- code: "MC-M01",
35407
- sentence: "Memory synchronization stopped before the host mirror caught up.",
35408
- action: "Send another message to resume it, or run `ck doctor drain-authority`."
35409
- },
35410
- memory_authority_mismatch: {
35411
- code: "MC-M02",
35412
- sentence: "Memory authority is inconsistent between the host and module.",
35413
- action: "Run `ck doctor drain-authority` before changing Rust mode."
35414
- },
35415
- rust_mode_unsupported: {
35416
- code: "MC-S06",
35417
- sentence: "Experimental Rust transform mode is not available on this OpenCode host.",
35418
- action: 'Magic Context is running its TypeScript mode instead; set `transform_mode` to "ts" to stop requesting Rust.'
35419
- },
35420
- dreamer_task_failing: {
35421
- code: "MC-S05",
35422
- sentence: "A background maintenance task keeps failing on its schedule.",
35423
- action: "Check the Magic Context log for the failing task and its error."
35424
- }
35425
- };
35426
- });
35427
-
35428
35975
  // ../plugin/src/features/magic-context/dreamer/tick-failure.ts
35429
35976
  function parseFailure(value) {
35430
35977
  if (!value)
@@ -35462,7 +36009,7 @@ var init_tick_failure = __esm(() => {
35462
36009
  });
35463
36010
 
35464
36011
  // ../pi-plugin/src/config/index.ts
35465
- import { existsSync as existsSync26 } from "node:fs";
36012
+ import { existsSync as existsSync27 } from "node:fs";
35466
36013
  function getProjectConfigPaths(cwd) {
35467
36014
  const basePath = cortexKitProjectConfigBasePath(cwd);
35468
36015
  return [`${basePath}.jsonc`, `${basePath}.json`];
@@ -35472,10 +36019,10 @@ function getUserConfigPaths() {
35472
36019
  return [`${basePath}.jsonc`, `${basePath}.json`];
35473
36020
  }
35474
36021
  function resolveFirstExisting(paths) {
35475
- return paths.find((path) => existsSync26(path));
36022
+ return paths.find((path) => existsSync27(path));
35476
36023
  }
35477
36024
  function resolvePiLegacyFallback(sources) {
35478
- return sources.find((source) => existsSync26(source.path)) ?? null;
36025
+ return sources.find((source) => existsSync27(source.path)) ?? null;
35479
36026
  }
35480
36027
  function loadConfigFile(path, scope) {
35481
36028
  let raw;
@@ -35770,7 +36317,7 @@ function loadPiConfigDetailed(opts = {}, applyRuntimeGlobals = true) {
35770
36317
  if (loaded)
35771
36318
  loadedFiles.push(loaded);
35772
36319
  }
35773
- const legacyProjectUnmigrated = !projectPath && !projectLegacyFallback && legacySources.project.some((source) => existsSync26(source.path));
36320
+ const legacyProjectUnmigrated = !projectPath && !projectLegacyFallback && legacySources.project.some((source) => existsSync27(source.path));
35774
36321
  const userPath = resolveFirstExisting(getUserConfigPaths());
35775
36322
  const userLegacyFallback = userPath ? null : resolvePiLegacyFallback(harnessLegacy.user);
35776
36323
  const userReadPath = userPath ?? userLegacyFallback?.path;
@@ -35779,7 +36326,7 @@ function loadPiConfigDetailed(opts = {}, applyRuntimeGlobals = true) {
35779
36326
  if (loaded)
35780
36327
  loadedFiles.push(loaded);
35781
36328
  }
35782
- const legacyUserUnmigrated = !userPath && !userLegacyFallback && legacySources.user.some((source) => existsSync26(source.path));
36329
+ const legacyUserUnmigrated = !userPath && !userLegacyFallback && legacySources.user.some((source) => existsSync27(source.path));
35783
36330
  if (userLegacyFallback) {
35784
36331
  warnings.push(`[user config] reading legacy config from ${userLegacyFallback.path} until migration completes; run \`npx @cortexkit/magic-context doctor\` to consolidate into the shared CortexKit location.`);
35785
36332
  } else if (legacyUserUnmigrated) {
@@ -35900,11 +36447,11 @@ function formatDatabaseRepairGuidance(dbPath) {
35900
36447
  var DATABASE_REPAIR_COMMAND = "bunx @cortexkit/magic-context@latest doctor repair-db";
35901
36448
 
35902
36449
  // src/lib/github-issue.ts
35903
- import { spawnSync as spawnSync4 } from "node:child_process";
35904
- import { readFileSync as readFileSync15 } from "node:fs";
36450
+ import { spawnSync as spawnSync5 } from "node:child_process";
36451
+ import { readFileSync as readFileSync16 } from "node:fs";
35905
36452
  function runGhCommand(args) {
35906
36453
  try {
35907
- const result = spawnSync4("gh", args, {
36454
+ const result = spawnSync5("gh", args, {
35908
36455
  encoding: "utf-8",
35909
36456
  stdio: ["ignore", "pipe", "pipe"]
35910
36457
  });
@@ -35927,7 +36474,7 @@ function failed(result) {
35927
36474
  function submitGithubIssue(title, bodyPath, runner = runGhCommand) {
35928
36475
  let body;
35929
36476
  try {
35930
- body = readFileSync15(bodyPath, "utf-8");
36477
+ body = readFileSync16(bodyPath, "utf-8");
35931
36478
  } catch (error) {
35932
36479
  return {
35933
36480
  ok: false,
@@ -35985,7 +36532,7 @@ var MAX_GITHUB_ISSUE_BODY_CHARS = 65536;
35985
36532
  var init_github_issue = () => {};
35986
36533
 
35987
36534
  // src/lib/log-lines.ts
35988
- import { existsSync as existsSync27, readFileSync as readFileSync16, statSync as statSync12 } from "node:fs";
36535
+ import { existsSync as existsSync28, readFileSync as readFileSync17, statSync as statSync12 } from "node:fs";
35989
36536
  import { tmpdir as tmpdir2 } from "node:os";
35990
36537
  import { join as join23 } from "node:path";
35991
36538
  function tokenize(input, limit = Number.POSITIVE_INFINITY) {
@@ -36282,11 +36829,11 @@ function getMagicContextLogPaths(harness, options = {}) {
36282
36829
  ].filter((path, index, paths) => paths.indexOf(path) === index);
36283
36830
  }
36284
36831
  function inspectLogFile(path) {
36285
- if (!existsSync27(path)) {
36832
+ if (!existsSync28(path)) {
36286
36833
  return { path, exists: false, sizeKb: 0, lineCount: 0, grammar: "unknown" };
36287
36834
  }
36288
36835
  try {
36289
- const content = readFileSync16(path, "utf8");
36836
+ const content = readFileSync17(path, "utf8");
36290
36837
  const lines = content.split(/\r?\n/).filter((line) => line.length > 0);
36291
36838
  const grammars = new Set(lines.flatMap((line) => {
36292
36839
  const parsed = parseLogLine(line);
@@ -36313,7 +36860,7 @@ function readLogLines(files) {
36313
36860
  return [];
36314
36861
  try {
36315
36862
  let precedingTimestamp = "";
36316
- return readFileSync16(file.path, "utf8").split(/\r?\n/).filter((line) => line.length > 0).map((line, lineIndex) => {
36863
+ return readFileSync17(file.path, "utf8").split(/\r?\n/).filter((line) => line.length > 0).map((line, lineIndex) => {
36317
36864
  precedingTimestamp = parseLogLine(line)?.ts ?? precedingTimestamp;
36318
36865
  return { line, timestamp: precedingTimestamp, fileIndex, lineIndex };
36319
36866
  });
@@ -36362,10 +36909,10 @@ var init_redaction2 = __esm(() => {
36362
36909
  });
36363
36910
 
36364
36911
  // src/commands/doctor-omp.ts
36365
- import { execFileSync as execFileSync5, spawnSync as spawnSync5 } from "node:child_process";
36366
- import { existsSync as existsSync28, mkdirSync as mkdirSync8, readFileSync as readFileSync17 } from "node:fs";
36912
+ import { execFileSync as execFileSync5, spawnSync as spawnSync6 } from "node:child_process";
36913
+ import { existsSync as existsSync29, mkdirSync as mkdirSync8, readFileSync as readFileSync18 } from "node:fs";
36367
36914
  import { createRequire as createRequire3 } from "node:module";
36368
- import { dirname as dirname14, join as join24, resolve as resolve7 } from "node:path";
36915
+ import { dirname as dirname14, join as join24, resolve as resolve8 } from "node:path";
36369
36916
  function parseSemver(value) {
36370
36917
  const match = value?.match(/(\d+)\.(\d+)\.(\d+)/);
36371
36918
  return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
@@ -36407,10 +36954,10 @@ function selfVersion() {
36407
36954
  return "unknown";
36408
36955
  }
36409
36956
  function readConfig2(path) {
36410
- if (!existsSync28(path))
36957
+ if (!existsSync29(path))
36411
36958
  return {};
36412
36959
  try {
36413
- const parsed = import_comment_json7.parse(readFileSync17(path, "utf-8"));
36960
+ const parsed = import_comment_json7.parse(readFileSync18(path, "utf-8"));
36414
36961
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? {} : { error: "top level is not an object" };
36415
36962
  } catch (error) {
36416
36963
  return { error: error instanceof Error ? error.message : String(error) };
@@ -36420,7 +36967,7 @@ function pluginDeclaresOmp(path) {
36420
36967
  if (!path)
36421
36968
  return null;
36422
36969
  try {
36423
- const pkg = JSON.parse(readFileSync17(join24(path, "package.json"), "utf-8"));
36970
+ const pkg = JSON.parse(readFileSync18(join24(path, "package.json"), "utf-8"));
36424
36971
  return Array.isArray(pkg.omp?.extensions) || Array.isArray(pkg.pi?.extensions);
36425
36972
  } catch {
36426
36973
  return null;
@@ -36491,8 +37038,8 @@ async function runHealthChecks(options) {
36491
37038
  if (!reportedAgentDir.ok) {
36492
37039
  add(results, "warn", "Could not verify OMP active agent directory");
36493
37040
  } else {
36494
- const reportedPath = resolve7(reportedAgentDir.stdout);
36495
- const expectedPath = resolve7(getOmpAgentDir());
37041
+ const reportedPath = resolve8(reportedAgentDir.stdout);
37042
+ const expectedPath = resolve8(getOmpAgentDir());
36496
37043
  if (reportedPath === expectedPath) {
36497
37044
  add(results, "pass", `OMP agent directory resolved to ${getOmpAgentDir()}`);
36498
37045
  } else {
@@ -36501,7 +37048,7 @@ async function runHealthChecks(options) {
36501
37048
  }
36502
37049
  }
36503
37050
  const userConfigPath = getOmpUserConfigPath();
36504
- if (!existsSync28(userConfigPath)) {
37051
+ if (!existsSync29(userConfigPath)) {
36505
37052
  add(results, "warn", `No Magic Context user config at ${userConfigPath}`);
36506
37053
  repairPlan.writeUserConfig = true;
36507
37054
  } else {
@@ -36520,7 +37067,7 @@ async function runHealthChecks(options) {
36520
37067
  const storage = getMagicContextStorageResolution();
36521
37068
  const dbPath = join24(storage.path, "context.db");
36522
37069
  add(results, "info", `Shared storage: ${storage.path} (source: ${storage.source})`);
36523
- if (!existsSync28(dbPath))
37070
+ if (!existsSync29(dbPath))
36524
37071
  add(results, "info", `Shared context DB will be created at ${dbPath}`);
36525
37072
  else {
36526
37073
  let db = null;
@@ -36584,7 +37131,7 @@ function writeDefaultConfig(path) {
36584
37131
  }
36585
37132
  async function repair(plan, deps, prompts, cwd) {
36586
37133
  let fixed = 0;
36587
- if (plan.writeUserConfig && !existsSync28(getOmpUserConfigPath())) {
37134
+ if (plan.writeUserConfig && !existsSync29(getOmpUserConfigPath())) {
36588
37135
  writeDefaultConfig(getOmpUserConfigPath());
36589
37136
  prompts.log.success(`Wrote default Magic Context config to ${getOmpUserConfigPath()}`);
36590
37137
  fixed += 1;
@@ -36741,7 +37288,7 @@ var init_doctor_omp = __esm(async () => {
36741
37288
  now: () => new Date,
36742
37289
  inspectMagicContextLogs,
36743
37290
  execFileSync: execFileSync5,
36744
- spawnSync: spawnSync5
37291
+ spawnSync: spawnSync6
36745
37292
  };
36746
37293
  });
36747
37294
 
@@ -37641,12 +38188,12 @@ function listShadowBackfillStalls(db, projectIdentity) {
37641
38188
  var SHADOW_BACKFILL_PROVENANCE_KEY = "_magicContextShadowBackfill";
37642
38189
 
37643
38190
  // ../plugin/src/shared/auto-update-provenance.ts
37644
- import { existsSync as existsSync29, readFileSync as readFileSync18 } from "node:fs";
38191
+ import { existsSync as existsSync30, readFileSync as readFileSync19 } from "node:fs";
37645
38192
  function readAutoUpdateCheckState(path) {
37646
- if (!existsSync29(path))
38193
+ if (!existsSync30(path))
37647
38194
  return null;
37648
38195
  try {
37649
- const value = JSON.parse(readFileSync18(path, "utf-8"));
38196
+ const value = JSON.parse(readFileSync19(path, "utf-8"));
37650
38197
  if (!value || typeof value !== "object" || Array.isArray(value))
37651
38198
  return null;
37652
38199
  const raw = value;
@@ -37674,9 +38221,9 @@ var init_auto_update_provenance = () => {};
37674
38221
  // ../plugin/src/shared/tui-config.ts
37675
38222
  import {
37676
38223
  chmodSync as chmodSync5,
37677
- existsSync as existsSync30,
38224
+ existsSync as existsSync31,
37678
38225
  mkdirSync as mkdirSync9,
37679
- readFileSync as readFileSync19,
38226
+ readFileSync as readFileSync20,
37680
38227
  renameSync as renameSync6,
37681
38228
  statSync as statSync13,
37682
38229
  writeFileSync as writeFileSync6
@@ -37724,9 +38271,9 @@ function resolveTuiConfigPath(configDirOverride) {
37724
38271
  const configDir = configDirOverride ?? getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
37725
38272
  const jsoncPath = join25(configDir, "tui.jsonc");
37726
38273
  const jsonPath = join25(configDir, "tui.json");
37727
- if (existsSync30(jsoncPath))
38274
+ if (existsSync31(jsoncPath))
37728
38275
  return jsoncPath;
37729
- if (existsSync30(jsonPath))
38276
+ if (existsSync31(jsonPath))
37730
38277
  return jsonPath;
37731
38278
  return jsoncPath;
37732
38279
  }
@@ -37734,8 +38281,8 @@ function ensureTuiPluginEntry(options = {}) {
37734
38281
  try {
37735
38282
  const configPath = resolveTuiConfigPath(options.configDir);
37736
38283
  let config = {};
37737
- if (existsSync30(configPath)) {
37738
- const raw = readFileSync19(configPath, "utf-8");
38284
+ if (existsSync31(configPath)) {
38285
+ const raw = readFileSync20(configPath, "utf-8");
37739
38286
  config = import_comment_json8.parse(raw) ?? {};
37740
38287
  }
37741
38288
  const plugins = Array.isArray(config.plugin) ? [...config.plugin] : [];
@@ -37787,7 +38334,7 @@ var init_tui_config = __esm(() => {
37787
38334
  });
37788
38335
 
37789
38336
  // src/lib/diagnostics-opencode.ts
37790
- import { existsSync as existsSync31, readdirSync as readdirSync4, readFileSync as readFileSync20, statSync as statSync14 } from "node:fs";
38337
+ import { existsSync as existsSync32, readdirSync as readdirSync5, readFileSync as readFileSync21, statSync as statSync14 } from "node:fs";
37791
38338
  import { createRequire as createRequire4 } from "node:module";
37792
38339
  import { join as join26 } from "node:path";
37793
38340
  function getSelfVersion() {
@@ -37802,29 +38349,52 @@ function getSelfVersion() {
37802
38349
  }
37803
38350
  return "unknown";
37804
38351
  }
37805
- function getPluginCacheInfo() {
37806
- const [path = ""] = getOpenCodePluginCacheRoots();
38352
+ function readLegacyCachedVersion(root) {
38353
+ try {
38354
+ const pkg = JSON.parse(readFileSync21(getOpenCodePluginPackageJsonPath(root), "utf-8"));
38355
+ return typeof pkg.version === "string" ? pkg.version : undefined;
38356
+ } catch {
38357
+ return;
38358
+ }
38359
+ }
38360
+ function collectOpenCodePluginCacheReport(hostGeneration, opencodeConfig) {
38361
+ const installs = [];
38362
+ for (const root of getOpenCodePluginCacheRoots()) {
38363
+ if (!existsSync32(root))
38364
+ continue;
38365
+ const spec = root.endsWith("@latest") ? "latest" : "(bare)";
38366
+ installs.push({
38367
+ layout: "opencode1",
38368
+ spec,
38369
+ path: root,
38370
+ cached: readLegacyCachedVersion(root)
38371
+ });
38372
+ }
38373
+ for (const slot of listOpenCodeV2PluginCacheSlots()) {
38374
+ installs.push({
38375
+ layout: "opencode2",
38376
+ spec: slot.spec,
38377
+ path: slot.slot,
38378
+ cached: slot.version
38379
+ });
38380
+ }
38381
+ let path;
37807
38382
  let cached;
37808
- for (const installedPkgPath of getOpenCodePluginPackageJsonPaths()) {
37809
- try {
37810
- if (existsSync31(installedPkgPath)) {
37811
- const pkg = JSON.parse(readFileSync20(installedPkgPath, "utf-8"));
37812
- cached = typeof pkg.version === "string" ? pkg.version : undefined;
37813
- if (cached)
37814
- break;
37815
- }
37816
- } catch {
37817
- cached = undefined;
37818
- }
38383
+ if (hostGeneration === "v2") {
38384
+ path = getOpenCodeV2PluginCacheSlot(undefined, readConfiguredOpenCodePluginSpec(opencodeConfig));
38385
+ cached = readOpenCodeV2CachedPluginVersion(path);
38386
+ } else {
38387
+ [path = ""] = getOpenCodePluginCacheRoots();
38388
+ cached = installs.find((install) => install.layout === "opencode1" && install.cached)?.cached;
37819
38389
  }
37820
- return { path, cached, latest: getSelfVersion() };
38390
+ return { path, cached, latest: getSelfVersion(), installs };
37821
38391
  }
37822
38392
  function getStorageResolution() {
37823
38393
  return getMagicContextStorageResolution();
37824
38394
  }
37825
38395
  function fileSize(path) {
37826
38396
  try {
37827
- return existsSync31(path) ? statSync14(path).size : 0;
38397
+ return existsSync32(path) ? statSync14(path).size : 0;
37828
38398
  } catch {
37829
38399
  return 0;
37830
38400
  }
@@ -37836,10 +38406,10 @@ function sanitizeValue(value) {
37836
38406
  return sanitizeConfigValue(value);
37837
38407
  }
37838
38408
  function readConfig3(path) {
37839
- if (!existsSync31(path))
38409
+ if (!existsSync32(path))
37840
38410
  return { value: null };
37841
38411
  try {
37842
- const raw = readFileSync20(path, "utf-8");
38412
+ const raw = readFileSync21(path, "utf-8");
37843
38413
  const value = import_comment_json9.parse(raw);
37844
38414
  return { value };
37845
38415
  } catch (error) {
@@ -37847,9 +38417,9 @@ function readConfig3(path) {
37847
38417
  }
37848
38418
  }
37849
38419
  function configHasPluginEntry(config) {
37850
- const plugins = readPluginEntries(config).map(({ entry }) => entry);
38420
+ const plugins = readPluginEntries(config).map(({ entry }) => pluginEntryPackage(entry));
37851
38421
  return plugins.some((entry) => {
37852
- if (typeof entry !== "string")
38422
+ if (entry === null)
37853
38423
  return false;
37854
38424
  if (entry === OPENCODE_PLUGIN_NAME)
37855
38425
  return true;
@@ -37864,7 +38434,7 @@ function configHasPluginEntry(config) {
37864
38434
  }
37865
38435
  function parseHistorianDumpMeta(path) {
37866
38436
  try {
37867
- const xml = readFileSync20(path, "utf-8");
38437
+ const xml = readFileSync21(path, "utf-8");
37868
38438
  const parsed = parseCompartmentOutput(xml);
37869
38439
  const factCountByCategory = {};
37870
38440
  for (const fact of parsed.facts) {
@@ -37897,10 +38467,10 @@ function parseHistorianDumpMeta(path) {
37897
38467
  }
37898
38468
  }
37899
38469
  function listDumpsInDir(dir, limit) {
37900
- if (!existsSync31(dir))
38470
+ if (!existsSync32(dir))
37901
38471
  return { count: 0, recent: [] };
37902
38472
  try {
37903
- const entries = readdirSync4(dir).filter((name) => name.endsWith(".xml")).map((name) => {
38473
+ const entries = readdirSync5(dir).filter((name) => name.endsWith(".xml")).map((name) => {
37904
38474
  const stat = statSync14(join26(dir, name));
37905
38475
  return {
37906
38476
  name,
@@ -38049,7 +38619,7 @@ async function collectRecentSessions(resolution, hostGeneration) {
38049
38619
  }
38050
38620
  async function collectHistorianFailures(storageDirPath) {
38051
38621
  const contextDbPath = join26(storageDirPath, "context.db");
38052
- if (!existsSync31(contextDbPath))
38622
+ if (!existsSync32(contextDbPath))
38053
38623
  return [];
38054
38624
  if (typeof globalThis.Bun === "undefined") {
38055
38625
  return [];
@@ -38083,7 +38653,7 @@ async function collectHistorianFailures(storageDirPath) {
38083
38653
  }
38084
38654
  async function collectHistorianRuns(storageDirPath) {
38085
38655
  const contextDbPath = join26(storageDirPath, "context.db");
38086
- if (!existsSync31(contextDbPath))
38656
+ if (!existsSync32(contextDbPath))
38087
38657
  return [];
38088
38658
  if (typeof globalThis.Bun === "undefined")
38089
38659
  return [];
@@ -38145,6 +38715,9 @@ async function collectHistorianRuns(storageDirPath) {
38145
38715
  } catch {}
38146
38716
  }
38147
38717
  }
38718
+ function openCodeIssueLogHarnesses(hostGeneration) {
38719
+ return hostGeneration === "v2" ? ["opencode2", "opencode"] : ["opencode"];
38720
+ }
38148
38721
  async function collectDiagnostics() {
38149
38722
  const pluginVersion = getSelfVersion();
38150
38723
  const configPaths = detectConfigPaths();
@@ -38154,8 +38727,6 @@ async function collectDiagnostics() {
38154
38727
  const storageResolution = getStorageResolution();
38155
38728
  const storageDirPath = storageResolution.path;
38156
38729
  const contextDbPath = join26(storageDirPath, "context.db");
38157
- const logFiles = inspectMagicContextLogs("opencode");
38158
- const primaryLog = logFiles.find((file) => file.exists) ?? logFiles[0];
38159
38730
  let compactionEnabled = false;
38160
38731
  try {
38161
38732
  compactionEnabled = isCompactionEnabled(loadPluginConfig(process.cwd()));
@@ -38165,6 +38736,8 @@ async function collectDiagnostics() {
38165
38736
  const opencodeInstallations = describeOpenCodeInstallations(detectOpenCodeInstallations());
38166
38737
  const activeInstallation = opencodeInstallations[0];
38167
38738
  const hostGeneration = openCodeHostGenerationFromVersion(activeInstallation?.version);
38739
+ const logFiles = openCodeIssueLogHarnesses(hostGeneration).flatMap((harness) => inspectMagicContextLogs(harness)).filter((file, index, all) => all.findIndex((other) => other.path === file.path) === index);
38740
+ const primaryLog = logFiles.find((file) => file.exists) ?? logFiles[0];
38168
38741
  const conflictResult = detectConflicts(process.cwd(), { compactionEnabled, hostGeneration });
38169
38742
  const openCodeDatabaseResolution = resolveOpenCodeDbPath(hostGeneration);
38170
38743
  const recentSessions = await collectRecentSessions(openCodeDatabaseResolution, hostGeneration);
@@ -38185,15 +38758,15 @@ async function collectDiagnostics() {
38185
38758
  opencodeConfigHasPlugin: configHasPluginEntry(opencodeConfig.value),
38186
38759
  tuiConfigHasPlugin: configHasPluginEntry(tuiConfig.value),
38187
38760
  magicContextConfig: {
38188
- exists: existsSync31(configPaths.magicContextConfig),
38761
+ exists: existsSync32(configPaths.magicContextConfig),
38189
38762
  ...magicContextConfig.error ? { parseError: magicContextConfig.error } : {},
38190
38763
  flags: sanitizeValue(magicContextConfig.value ?? {}) ?? {}
38191
38764
  },
38192
- pluginCache: getPluginCacheInfo(),
38765
+ pluginCache: collectOpenCodePluginCacheReport(hostGeneration, opencodeConfig.value),
38193
38766
  storageDir: {
38194
38767
  path: storageDirPath,
38195
38768
  source: storageResolution.source,
38196
- exists: existsSync31(storageDirPath),
38769
+ exists: existsSync32(storageDirPath),
38197
38770
  contextDbSizeBytes: fileSize(contextDbPath)
38198
38771
  },
38199
38772
  openCodeDatabase: {
@@ -38240,7 +38813,12 @@ function renderDiagnosticsMarkdown(report) {
38240
38813
  const pluginCache = {
38241
38814
  path: sanitizeString(report.pluginCache.path),
38242
38815
  cached: report.pluginCache.cached ?? null,
38243
- latest: report.pluginCache.latest ?? null
38816
+ latest: report.pluginCache.latest ?? null,
38817
+ installs: (report.pluginCache.installs ?? []).map((install) => ({
38818
+ ...install,
38819
+ path: sanitizeString(install.path),
38820
+ cached: install.cached ?? null
38821
+ }))
38244
38822
  };
38245
38823
  const storage = {
38246
38824
  path: sanitizeString(report.storageDir.path),
@@ -38356,6 +38934,7 @@ var init_diagnostics_opencode = __esm(() => {
38356
38934
  init_conflict_detector();
38357
38935
  init_data_path();
38358
38936
  init_opencode_db_path();
38937
+ init_opencode();
38359
38938
  init_log_lines();
38360
38939
  init_opencode_detect();
38361
38940
  init_opencode_helpers();
@@ -38419,8 +38998,8 @@ var init_embedding_local = __esm(() => {
38419
38998
  });
38420
38999
 
38421
39000
  // src/lib/embedding-runtime.ts
38422
- import { spawnSync as spawnSync6 } from "node:child_process";
38423
- import { existsSync as existsSync32, readFileSync as readFileSync21 } from "node:fs";
39001
+ import { spawnSync as spawnSync7 } from "node:child_process";
39002
+ import { existsSync as existsSync33, readFileSync as readFileSync22 } from "node:fs";
38424
39003
  import { createRequire as createRequire5 } from "node:module";
38425
39004
  import { dirname as dirname16, join as join27, sep } from "node:path";
38426
39005
  function describeError(error) {
@@ -38429,7 +39008,7 @@ function describeError(error) {
38429
39008
  return typeof code === "string" && code.length > 0 ? `${code}: ${message}` : message;
38430
39009
  }
38431
39010
  function runOnnxRuntimeNodeLoadProbeChild(packageDir) {
38432
- return spawnSync6(process.execPath, ["-e", ONNX_RUNTIME_NODE_LOAD_PROBE_SCRIPT], {
39011
+ return spawnSync7(process.execPath, ["-e", ONNX_RUNTIME_NODE_LOAD_PROBE_SCRIPT], {
38433
39012
  encoding: "utf8",
38434
39013
  env: { ...process.env, [ONNX_LOAD_PROBE_PACKAGE_DIR_ENV]: packageDir },
38435
39014
  stdio: ["ignore", "pipe", "pipe"],
@@ -38567,7 +39146,7 @@ function expectedBinaryRelPath(platform, arch) {
38567
39146
  }
38568
39147
  function onnxRuntimeNodeVersion(packageDir) {
38569
39148
  try {
38570
- const parsed = JSON.parse(readFileSync21(join27(packageDir, "package.json"), "utf8"));
39149
+ const parsed = JSON.parse(readFileSync22(join27(packageDir, "package.json"), "utf8"));
38571
39150
  return typeof parsed.version === "string" ? parsed.version : undefined;
38572
39151
  } catch {
38573
39152
  return;
@@ -38616,7 +39195,7 @@ function formatLocalEmbeddingRuntimeWasmSelected(status) {
38616
39195
  function requireNodeWasmBundle(probe, candidates) {
38617
39196
  if (probe.state === "failed")
38618
39197
  return probe;
38619
- const bundlePath = candidates.find((candidate) => existsSync32(candidate));
39198
+ const bundlePath = candidates.find((candidate) => existsSync33(candidate));
38620
39199
  return bundlePath ? probe : {
38621
39200
  state: "failed",
38622
39201
  reason: `functional Node WASM Transformers bundle is missing (checked ${candidates.join(", ")})`
@@ -38641,7 +39220,7 @@ function checkLocalEmbeddingRuntimeAt(installRoot, platform = process.platform,
38641
39220
  }
38642
39221
  const packageDir = join27(installRoot, "node_modules", "onnxruntime-node");
38643
39222
  let native;
38644
- if (!existsSync32(join27(packageDir, "package.json"))) {
39223
+ if (!existsSync33(join27(packageDir, "package.json"))) {
38645
39224
  native = { state: "package-missing", packageDir };
38646
39225
  } else {
38647
39226
  const rel = expectedBinaryRelPath(platform, arch);
@@ -38652,7 +39231,7 @@ function checkLocalEmbeddingRuntimeAt(installRoot, platform = process.platform,
38652
39231
  };
38653
39232
  } else {
38654
39233
  const binaryPath = join27(packageDir, rel);
38655
- native = existsSync32(binaryPath) ? probeOnnxRuntimeNodeLoad(packageDir) ?? { state: "ok", binaryPath } : {
39234
+ native = existsSync33(binaryPath) ? probeOnnxRuntimeNodeLoad(packageDir) ?? { state: "ok", binaryPath } : {
38656
39235
  state: "binary-missing",
38657
39236
  packageDir,
38658
39237
  expectedBinary: binaryPath,
@@ -38663,7 +39242,7 @@ function checkLocalEmbeddingRuntimeAt(installRoot, platform = process.platform,
38663
39242
  return withWasmFallback(native, () => probeWasmRuntimeAt(installRoot));
38664
39243
  }
38665
39244
  function checkLocalEmbeddingRuntime(installRoots, platform = process.platform, arch = process.arch, runtimePreference = "auto", host) {
38666
- const existing = installRoots.filter((root) => existsSync32(root));
39245
+ const existing = installRoots.filter((root) => existsSync33(root));
38667
39246
  if (existing.length === 0) {
38668
39247
  return {
38669
39248
  state: "unknown",
@@ -38710,7 +39289,7 @@ function probeWasmRuntimeByResolution(pluginDir) {
38710
39289
  }
38711
39290
  }
38712
39291
  function checkLocalEmbeddingRuntimeByResolution(pluginDir, platform = process.platform, arch = process.arch, runtimePreference = "auto", host) {
38713
- if (!existsSync32(join27(pluginDir, "package.json"))) {
39292
+ if (!existsSync33(join27(pluginDir, "package.json"))) {
38714
39293
  return { state: "unknown", reason: "plugin package dir not found" };
38715
39294
  }
38716
39295
  const selectedRuntime = resolveLocalEmbeddingRuntime(runtimePreference, host);
@@ -38752,7 +39331,7 @@ function checkLocalEmbeddingRuntimeByResolution(pluginDir, platform = process.pl
38752
39331
  native = probeOnnxRuntimeNodeLoad(onnxDir) ?? { state: "ok", binaryPath: onnxDir };
38753
39332
  } else {
38754
39333
  const binaryPath = join27(onnxDir, rel);
38755
- native = existsSync32(binaryPath) ? probeOnnxRuntimeNodeLoad(onnxDir) ?? { state: "ok", binaryPath } : {
39334
+ native = existsSync33(binaryPath) ? probeOnnxRuntimeNodeLoad(onnxDir) ?? { state: "ok", binaryPath } : {
38756
39335
  state: "binary-missing",
38757
39336
  packageDir: onnxDir,
38758
39337
  expectedBinary: binaryPath,
@@ -39291,6 +39870,7 @@ var init_constants2 = __esm(() => {
39291
39870
  USER_OPENCODE_CONFIG = join29(getOpenCodeConfigRoot(), "opencode.json");
39292
39871
  USER_OPENCODE_CONFIG_JSONC = join29(getOpenCodeConfigRoot(), "opencode.jsonc");
39293
39872
  });
39873
+
39294
39874
  // ../plugin/src/hooks/auto-update-checker/types.ts
39295
39875
  var NpmPackageEnvelopeSchema, OpencodePluginTupleSchema, OpencodeConfigSchema, PackageJsonSchema;
39296
39876
  var init_types = __esm(() => {
@@ -39312,14 +39892,14 @@ var init_types = __esm(() => {
39312
39892
  // ../plugin/src/hooks/auto-update-checker/checker.ts
39313
39893
  import {
39314
39894
  accessSync as accessSync2,
39315
- existsSync as existsSync33,
39895
+ existsSync as existsSync34,
39316
39896
  constants as fsConstants,
39317
- readFileSync as readFileSync22,
39897
+ readFileSync as readFileSync23,
39318
39898
  renameSync as renameSync7,
39319
39899
  statSync as statSync15,
39320
39900
  writeFileSync as writeFileSync8
39321
39901
  } from "node:fs";
39322
- import { dirname as dirname17, isAbsolute as isAbsolute7, join as join30, resolve as resolve8 } from "node:path";
39902
+ import { dirname as dirname17, isAbsolute as isAbsolute7, join as join30, resolve as resolve9 } from "node:path";
39323
39903
  function warn(message) {
39324
39904
  log3(`WARN: ${message}`);
39325
39905
  }
@@ -39366,9 +39946,9 @@ function getWinningConfig(directory, surface) {
39366
39946
  let winner = null;
39367
39947
  for (const path of getSurfaceConfigPaths(directory, surface)) {
39368
39948
  try {
39369
- if (!existsSync33(path))
39949
+ if (!existsSync34(path))
39370
39950
  continue;
39371
- const parsed = parseJsonConfig(readFileSync22(path, "utf-8"));
39951
+ const parsed = parseJsonConfig(readFileSync23(path, "utf-8"));
39372
39952
  const entries = getPluginEntries(parsed);
39373
39953
  if (entries.some((entry) => entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`))) {
39374
39954
  if (winner) {
@@ -39404,14 +39984,14 @@ var init_checker = __esm(() => {
39404
39984
  });
39405
39985
 
39406
39986
  // src/lib/opencode-plugin-schema-fence.ts
39407
- import { existsSync as existsSync34, readdirSync as readdirSync5, readFileSync as readFileSync23 } from "node:fs";
39987
+ import { existsSync as existsSync35, readdirSync as readdirSync6, readFileSync as readFileSync24 } from "node:fs";
39408
39988
  import { join as join31 } from "node:path";
39409
39989
  import { gunzipSync } from "node:zlib";
39410
39990
  function readPackageVersion(packageJsonPath) {
39411
39991
  try {
39412
- if (!existsSync34(packageJsonPath))
39992
+ if (!existsSync35(packageJsonPath))
39413
39993
  return null;
39414
- const packageJson = JSON.parse(readFileSync23(packageJsonPath, "utf-8"));
39994
+ const packageJson = JSON.parse(readFileSync24(packageJsonPath, "utf-8"));
39415
39995
  if (packageJson.name !== OPENCODE_PLUGIN_NAME || typeof packageJson.version !== "string") {
39416
39996
  return null;
39417
39997
  }
@@ -39431,7 +40011,7 @@ function readFenceValue(text) {
39431
40011
  }
39432
40012
  function readFenceFromDist(packageDirectory) {
39433
40013
  const distDirectory = join31(packageDirectory, "dist");
39434
- if (!existsSync34(distDirectory))
40014
+ if (!existsSync35(distDirectory))
39435
40015
  return null;
39436
40016
  const pending = [distDirectory];
39437
40017
  const fenceValues = new Set;
@@ -39440,7 +40020,7 @@ function readFenceFromDist(packageDirectory) {
39440
40020
  if (!directory)
39441
40021
  continue;
39442
40022
  try {
39443
- for (const entry of readdirSync5(directory, { withFileTypes: true })) {
40023
+ for (const entry of readdirSync6(directory, { withFileTypes: true })) {
39444
40024
  const path = join31(directory, entry.name);
39445
40025
  if (entry.isDirectory()) {
39446
40026
  pending.push(path);
@@ -39448,7 +40028,7 @@ function readFenceFromDist(packageDirectory) {
39448
40028
  }
39449
40029
  if (!entry.isFile() || !/\.(?:[cm]?js|ts)$/.test(entry.name))
39450
40030
  continue;
39451
- const fence = readFenceValue(readFileSync23(path, "utf-8"));
40031
+ const fence = readFenceValue(readFileSync24(path, "utf-8"));
39452
40032
  if (fence !== null)
39453
40033
  fenceValues.add(fence);
39454
40034
  }
@@ -39462,7 +40042,7 @@ function findInstalledPluginPackage(cacheDirectory, specifier, requestedVersion)
39462
40042
  const directCacheRoot = join31(cacheDirectory, specifier);
39463
40043
  const candidates = [directCacheRoot];
39464
40044
  try {
39465
- for (const entry of readdirSync5(cacheDirectory, { withFileTypes: true })) {
40045
+ for (const entry of readdirSync6(cacheDirectory, { withFileTypes: true })) {
39466
40046
  if (!entry.isDirectory())
39467
40047
  continue;
39468
40048
  const candidate = join31(cacheDirectory, entry.name);
@@ -39665,6 +40245,179 @@ var init_storage_versions = __esm(async () => {
39665
40245
  await init_storage_db();
39666
40246
  });
39667
40247
 
40248
+ // src/commands/doctor-opencode-cache.ts
40249
+ import { existsSync as existsSync36, readFileSync as readFileSync25, rmSync as rmSync4 } from "node:fs";
40250
+ function readCachedPluginVersion(pluginCacheDir) {
40251
+ try {
40252
+ const installedPkgPath = getOpenCodePluginPackageJsonPath(pluginCacheDir);
40253
+ if (!existsSync36(installedPkgPath))
40254
+ return;
40255
+ const pkg = JSON.parse(readFileSync25(installedPkgPath, "utf-8"));
40256
+ return typeof pkg.version === "string" ? pkg.version : undefined;
40257
+ } catch {
40258
+ return;
40259
+ }
40260
+ }
40261
+ async function clearPluginCache(options = {}, deps = {}) {
40262
+ const remove = deps.remove ?? ((path) => rmSync4(path, { recursive: true, force: true }));
40263
+ const pluginCacheRoots = getOpenCodePluginCacheRoots();
40264
+ const existingRoots = pluginCacheRoots.filter((root) => existsSync36(root));
40265
+ if (existingRoots.length === 0) {
40266
+ return { action: "not_found", path: pluginCacheRoots[0] ?? "" };
40267
+ }
40268
+ const latestVersion = options.latestVersion ?? undefined;
40269
+ const cacheEntries = existingRoots.map((path) => ({
40270
+ path,
40271
+ cached: readCachedPluginVersion(path)
40272
+ }));
40273
+ if (options.force !== true && latestVersion === undefined) {
40274
+ const firstEntry = cacheEntries[0];
40275
+ return {
40276
+ action: "check_unavailable",
40277
+ path: firstEntry?.path ?? pluginCacheRoots[0] ?? "",
40278
+ paths: cacheEntries.map((entry) => entry.path),
40279
+ cached: firstEntry?.cached
40280
+ };
40281
+ }
40282
+ const clearTargets = cacheEntries.filter((entry) => options.force === true || entry.cached === undefined || entry.cached !== latestVersion);
40283
+ if (clearTargets.length === 0) {
40284
+ const firstEntry = cacheEntries[0];
40285
+ return {
40286
+ action: "up_to_date",
40287
+ path: firstEntry?.path ?? pluginCacheRoots[0] ?? "",
40288
+ paths: cacheEntries.map((entry) => entry.path),
40289
+ cached: firstEntry?.cached,
40290
+ latest: latestVersion
40291
+ };
40292
+ }
40293
+ const cleared = [];
40294
+ const failed = [];
40295
+ for (const entry of clearTargets) {
40296
+ try {
40297
+ remove(entry.path);
40298
+ cleared.push(entry);
40299
+ } catch (err) {
40300
+ failed.push({
40301
+ path: entry.path,
40302
+ error: err instanceof Error ? err.message : String(err)
40303
+ });
40304
+ }
40305
+ }
40306
+ if (failed.length > 0) {
40307
+ const firstFailure = failed[0];
40308
+ return {
40309
+ action: "error",
40310
+ path: firstFailure?.path ?? clearTargets[0]?.path ?? existingRoots[0] ?? "",
40311
+ paths: failed.map((entry) => entry.path),
40312
+ clearedPaths: cleared.map((entry) => entry.path),
40313
+ failedPaths: failed.map((entry) => entry.path),
40314
+ error: firstFailure?.error
40315
+ };
40316
+ }
40317
+ const firstTarget = cleared[0];
40318
+ return {
40319
+ action: "cleared",
40320
+ path: firstTarget?.path ?? pluginCacheRoots[0] ?? "",
40321
+ paths: cleared.map((entry) => entry.path),
40322
+ cached: firstTarget?.cached,
40323
+ latest: latestVersion
40324
+ };
40325
+ }
40326
+ var init_doctor_opencode_cache = __esm(() => {
40327
+ init_opencode_plugin_cache();
40328
+ });
40329
+
40330
+ // src/commands/doctor-cached-plugin-fence.ts
40331
+ import { existsSync as existsSync37 } from "node:fs";
40332
+ import { dirname as dirname18 } from "node:path";
40333
+ function listCachedOpenCodePluginFences(sources = {}) {
40334
+ const fences = [];
40335
+ for (const root of sources.openCodeV1Roots ?? getOpenCodePluginCacheRoots()) {
40336
+ const packageJson = getOpenCodePluginPackageJsonPath(root);
40337
+ if (!existsSync37(packageJson))
40338
+ continue;
40339
+ fences.push({
40340
+ host: "opencode1",
40341
+ directory: root,
40342
+ version: readCachedPluginVersion(root),
40343
+ supportedVersion: readFenceFromDist(dirname18(packageJson))
40344
+ });
40345
+ }
40346
+ for (const slot of listOpenCodeV2PluginCacheSlots(sources.openCodeV2NpmCacheDir)) {
40347
+ if (!isOpenCodePluginDistTag(slot.spec))
40348
+ continue;
40349
+ const generation = getOpenCodeV2ActiveGeneration(slot.slot);
40350
+ if (!generation)
40351
+ continue;
40352
+ fences.push({
40353
+ host: "opencode2",
40354
+ directory: slot.slot,
40355
+ spec: slot.spec,
40356
+ version: slot.version,
40357
+ supportedVersion: readFenceFromDist(dirname18(getOpenCodePluginPackageJsonPath(generation)))
40358
+ });
40359
+ }
40360
+ return fences;
40361
+ }
40362
+ function readContextDbSchemaVersion(path) {
40363
+ let db = null;
40364
+ try {
40365
+ db = openExistingDatabase(path, { readonly: true });
40366
+ return db === null ? null : getPersistedSchemaVersion(db);
40367
+ } catch {
40368
+ return null;
40369
+ } finally {
40370
+ db?.close();
40371
+ }
40372
+ }
40373
+ function compareCachedPluginFences(fences, databaseVersion) {
40374
+ return fences.map((fence) => ({
40375
+ ...fence,
40376
+ databaseVersion,
40377
+ status: fence.supportedVersion === null ? "unknown" : fence.supportedVersion < databaseVersion ? "behind" : "supported"
40378
+ }));
40379
+ }
40380
+ function hostLabel(fence) {
40381
+ if (fence.host === "opencode1")
40382
+ return "OpenCode 1";
40383
+ return fence.spec && fence.spec !== "latest" ? `OpenCode 2 (@${fence.spec})` : "OpenCode 2";
40384
+ }
40385
+ function reportCachedPluginFences(findings, report) {
40386
+ const supported = [];
40387
+ let behind = 0;
40388
+ for (const finding of findings) {
40389
+ const label = hostLabel(finding);
40390
+ const version = finding.version ?? "unknown version";
40391
+ if (finding.status === "supported") {
40392
+ supported.push(`${label} ${version} (fence v${finding.supportedVersion})`);
40393
+ continue;
40394
+ }
40395
+ if (finding.status === "unknown") {
40396
+ report.info(`${label} plugin cache: could not read the schema fence of Magic Context ${version}, so doctor cannot tell whether it supports context.db v${finding.databaseVersion}`);
40397
+ report.info(` ${finding.directory}`);
40398
+ continue;
40399
+ }
40400
+ behind++;
40401
+ report.fail(`${label} has Magic Context ${version} cached, which supports context.db only through schema v${finding.supportedVersion}, but the shared context.db is at v${finding.databaseVersion}. A newer Magic Context (usually the other OpenCode host) migrated it, so ${label} fails closed on every prompt; an @latest entry does not refresh this cached copy.`);
40402
+ report.info(` ${finding.directory}`);
40403
+ report.info(finding.host === "opencode1" ? " To fix: quit OpenCode 1 and delete that directory; OpenCode 1 installs the current release on its next start." : ` To fix, ${OPENCODE_V2_PLUGIN_UPDATE_HINT}, then restart OpenCode 2.`);
40404
+ }
40405
+ if (supported.length > 0) {
40406
+ report.pass(`Cached Magic Context copies support context.db v${findings[0]?.databaseVersion}: ${supported.join(", ")}`);
40407
+ }
40408
+ return { behind };
40409
+ }
40410
+ var init_doctor_cached_plugin_fence = __esm(async () => {
40411
+ init_opencode_plugin_cache();
40412
+ init_opencode_plugin_schema_fence();
40413
+ init_doctor_opencode_cache();
40414
+ init_doctor_opencode2_cache();
40415
+ await __promiseAll([
40416
+ init_storage_db(),
40417
+ init_database_access()
40418
+ ]);
40419
+ });
40420
+
39668
40421
  // src/commands/doctor-compaction-markers.ts
39669
40422
  function safeData(column = "data") {
39670
40423
  return `CASE WHEN json_valid(${column}) THEN ${column} ELSE '{}' END`;
@@ -39720,6 +40473,21 @@ function countUnmatchedConvertedMarkers(db, migratedV2Schema) {
39720
40473
  )`).get();
39721
40474
  return Number(row?.count ?? 0);
39722
40475
  }
40476
+ function countPostConversionMessages(db, migratedV2Schema) {
40477
+ if (!migratedV2Schema || !tableExists4(db, "message") || !tableExists4(db, "session")) {
40478
+ return { messages: 0, sessions: 0 };
40479
+ }
40480
+ const row = db.prepare(`SELECT COUNT(*) AS messages, COUNT(DISTINCT converted.session_id) AS sessions
40481
+ FROM session_message converted
40482
+ WHERE EXISTS (SELECT 1 FROM session legacy WHERE legacy.id = converted.session_id)
40483
+ AND NOT EXISTS (
40484
+ SELECT 1
40485
+ FROM message source
40486
+ WHERE source.session_id = converted.session_id
40487
+ AND source.time_created = converted.time_created
40488
+ )`).get();
40489
+ return { messages: Number(row?.messages ?? 0), sessions: Number(row?.sessions ?? 0) };
40490
+ }
39723
40491
  function checkOpenCodeCompactionMarkerConversion(db, options = {}) {
39724
40492
  const missingBefore = countMissingCompleted(db);
39725
40493
  let repaired = 0;
@@ -39740,6 +40508,7 @@ function checkOpenCodeCompactionMarkerConversion(db, options = {}) {
39740
40508
  const migratedV2Schema = tableExists4(db, "session_v2") && tableExists4(db, "session_message");
39741
40509
  const migrationCompleted = migrationV1ToV2Completed(db);
39742
40510
  const unmatchedConvertedMarkers = countUnmatchedConvertedMarkers(db, migratedV2Schema);
40511
+ const postConversion = countPostConversionMessages(db, migratedV2Schema);
39743
40512
  return {
39744
40513
  missingBefore,
39745
40514
  missingAfter,
@@ -39747,21 +40516,23 @@ function checkOpenCodeCompactionMarkerConversion(db, options = {}) {
39747
40516
  migrationCompleted,
39748
40517
  migratedV2Schema,
39749
40518
  unmatchedConvertedMarkers,
39750
- recoveryRequired: migrationCompleted && migratedV2Schema && unmatchedConvertedMarkers > 0
40519
+ postConversionMessages: postConversion.messages,
40520
+ postConversionSessions: postConversion.sessions
39751
40521
  };
39752
40522
  }
39753
40523
  function formatOpenCodeCompactionMarkerConversion(report) {
39754
40524
  return `OpenCode compaction markers are convertible to OpenCode 2: before=${report.missingBefore} missing time.completed; after=${report.missingAfter}`;
39755
40525
  }
39756
- function formatOpenCodeV2ReconversionRecipe(databasePath) {
39757
- return [
39758
- "OpenCode 2 already completed its v1→v2 conversion, but one or more Magic Context markers are absent from session_message.",
39759
- "Recovery (with every OpenCode host stopped): clear only the kv.migration.v1-v2 marker, then restart the host once so it reconverts the v1 rows. Do not edit session_v2 or session_message by hand.",
39760
- `Database: ${databasePath}`,
39761
- "In sqlite3, run: DELETE FROM kv WHERE key = 'migration.v1-v2';",
39762
- "Large-store conversion can take several minutes. `opencode service start` may kill the server as unresponsive while it runs; start `opencode serve --port N` by hand instead.",
39763
- 'Wait until `SELECT value FROM kv WHERE key = \'migration.v1-v2\';` reads `{"phase":"completed"}` before stopping that manual server'
40526
+ function formatOpenCodeV2MissingMarkerNotice(report) {
40527
+ if (!report.migrationCompleted || !report.migratedV2Schema || report.unmatchedConvertedMarkers === 0) {
40528
+ return null;
40529
+ }
40530
+ const lines = [
40531
+ `OpenCode 2's conversion did not carry over ${report.unmatchedConvertedMarkers} Magic Context compaction marker(s). No action is needed.`,
40532
+ "Magic Context on OpenCode 2 does not use host compaction rows: it keeps its own boundary and re-derives compartment positions from the messages that survived. OpenCode reads these sessions' full history on each turn, as it does for any Magic Context session started on OpenCode 2."
39764
40533
  ];
40534
+ lines.push(report.postConversionMessages > 0 ? `Do not clear kv.migration.v1-v2 to reconvert: OpenCode 2 would rebuild every converted session from its OpenCode 1 rows and delete the ${report.postConversionMessages} message(s) it added after the conversion, in ${report.postConversionSessions} session(s).` : "Do not clear kv.migration.v1-v2 to reconvert: OpenCode 2 would rebuild every converted session from its OpenCode 1 rows, deleting anything added on OpenCode 2 since.", "If you already reconverted, restore opencode.db (with its -wal and -shm files) from a backup taken before the reconversion.");
40535
+ return lines;
39765
40536
  }
39766
40537
 
39767
40538
  // src/commands/doctor-compartment-boundaries.ts
@@ -39845,86 +40616,175 @@ var init_doctor_harness_relabel = __esm(async () => {
39845
40616
  await init_opencode2_relabel();
39846
40617
  });
39847
40618
 
39848
- // src/commands/doctor-opencode-cache.ts
39849
- import { existsSync as existsSync35, readFileSync as readFileSync24, rmSync as rmSync3 } from "node:fs";
39850
- function readCachedPluginVersion(pluginCacheDir) {
39851
- try {
39852
- const installedPkgPath = getOpenCodePluginPackageJsonPath(pluginCacheDir);
39853
- if (!existsSync35(installedPkgPath))
39854
- return;
39855
- const pkg = JSON.parse(readFileSync24(installedPkgPath, "utf-8"));
39856
- return typeof pkg.version === "string" ? pkg.version : undefined;
39857
- } catch {
39858
- return;
40619
+ // src/commands/doctor-opencode-plugin-duplicates.ts
40620
+ import { isDeepStrictEqual } from "node:util";
40621
+ function entryOptions(entry) {
40622
+ if (Array.isArray(entry))
40623
+ return entry[1];
40624
+ if (entry !== null && typeof entry === "object") {
40625
+ return entry.options;
39859
40626
  }
40627
+ return;
39860
40628
  }
39861
- async function clearPluginCache(options = {}, deps = {}) {
39862
- const remove = deps.remove ?? ((path) => rmSync3(path, { recursive: true, force: true }));
39863
- const pluginCacheRoots = getOpenCodePluginCacheRoots();
39864
- const existingRoots = pluginCacheRoots.filter((root) => existsSync35(root));
39865
- if (existingRoots.length === 0) {
39866
- return { action: "not_found", path: pluginCacheRoots[0] ?? "" };
40629
+ function hasOptions(options) {
40630
+ if (options === undefined || options === null)
40631
+ return false;
40632
+ if (typeof options === "object" && !Array.isArray(options)) {
40633
+ return Object.keys(options).length > 0;
39867
40634
  }
39868
- const latestVersion = options.latestVersion ?? undefined;
39869
- const cacheEntries = existingRoots.map((path) => ({
39870
- path,
39871
- cached: readCachedPluginVersion(path)
39872
- }));
39873
- if (options.force !== true && latestVersion === undefined) {
39874
- const firstEntry = cacheEntries[0];
39875
- return {
39876
- action: "check_unavailable",
39877
- path: firstEntry?.path ?? pluginCacheRoots[0] ?? "",
39878
- paths: cacheEntries.map((entry) => entry.path),
39879
- cached: firstEntry?.cached
39880
- };
40635
+ return true;
40636
+ }
40637
+ function followsLatest(specifier) {
40638
+ return specifier === OPENCODE_PLUGIN_NAME || specifier === OPENCODE_PLUGIN_ENTRY_WITH_VERSION;
40639
+ }
40640
+ function listMagicContextPluginEntries(config) {
40641
+ const out = [];
40642
+ for (const { key, index, entry } of readPluginEntries(config)) {
40643
+ const specifier = pluginEntryPackage(entry);
40644
+ if (specifier === null)
40645
+ continue;
40646
+ let kind = null;
40647
+ if (matchesPluginEntry(entry, OPENCODE_PLUGIN_NAME))
40648
+ kind = "npm";
40649
+ else if (isDevPathPluginEntry(entry))
40650
+ kind = "dev-path";
40651
+ if (kind === null)
40652
+ continue;
40653
+ out.push({ key, index, entry, specifier, kind, options: entryOptions(entry) });
39881
40654
  }
39882
- const clearTargets = cacheEntries.filter((entry) => options.force === true || entry.cached === undefined || entry.cached !== latestVersion);
39883
- if (clearTargets.length === 0) {
39884
- const firstEntry = cacheEntries[0];
39885
- return {
39886
- action: "up_to_date",
39887
- path: firstEntry?.path ?? pluginCacheRoots[0] ?? "",
39888
- paths: cacheEntries.map((entry) => entry.path),
39889
- cached: firstEntry?.cached,
39890
- latest: latestVersion
39891
- };
40655
+ return out;
40656
+ }
40657
+ function planPluginDuplicates(config) {
40658
+ const entries = listMagicContextPluginEntries(config);
40659
+ if (entries.length <= 1)
40660
+ return { action: "none", entries };
40661
+ if (entries.some((entry) => entry.kind === "dev-path")) {
40662
+ return { action: "leave", reason: "dev-path", entries };
39892
40663
  }
39893
- const cleared = [];
39894
- const failed = [];
39895
- for (const entry of clearTargets) {
39896
- try {
39897
- remove(entry.path);
39898
- cleared.push(entry);
39899
- } catch (err) {
39900
- failed.push({
39901
- path: entry.path,
39902
- error: err instanceof Error ? err.message : String(err)
39903
- });
40664
+ const optionSets = [];
40665
+ for (const entry of entries) {
40666
+ if (!hasOptions(entry.options))
40667
+ continue;
40668
+ if (!optionSets.some((seen) => isDeepStrictEqual(seen, entry.options))) {
40669
+ optionSets.push(entry.options);
39904
40670
  }
39905
40671
  }
39906
- if (failed.length > 0) {
39907
- const firstFailure = failed[0];
39908
- return {
39909
- action: "error",
39910
- path: firstFailure?.path ?? clearTargets[0]?.path ?? existingRoots[0] ?? "",
39911
- paths: failed.map((entry) => entry.path),
39912
- clearedPaths: cleared.map((entry) => entry.path),
39913
- failedPaths: failed.map((entry) => entry.path),
39914
- error: firstFailure?.error
39915
- };
40672
+ if (optionSets.length > 1) {
40673
+ return { action: "leave", reason: "conflicting-options", entries };
39916
40674
  }
39917
- const firstTarget = cleared[0];
39918
- return {
39919
- action: "cleared",
39920
- path: firstTarget?.path ?? pluginCacheRoots[0] ?? "",
39921
- paths: cleared.map((entry) => entry.path),
39922
- cached: firstTarget?.cached,
39923
- latest: latestVersion
39924
- };
40675
+ const rank = (entry) => (followsLatest(entry.specifier) ? 2 : 0) + (hasOptions(entry.options) ? 1 : 0);
40676
+ let keep = entries[0];
40677
+ for (const entry of entries) {
40678
+ if (rank(entry) > rank(keep))
40679
+ keep = entry;
40680
+ }
40681
+ const remove = entries.filter((entry) => entry !== keep);
40682
+ let replacement;
40683
+ const carried = optionSets[0];
40684
+ if (!hasOptions(keep.options) && carried !== undefined) {
40685
+ const donor = entries.find((entry) => isDeepStrictEqual(entry.options, carried));
40686
+ if (Array.isArray(donor?.entry)) {
40687
+ const tuple = [...donor.entry];
40688
+ tuple[0] = keep.specifier;
40689
+ replacement = tuple;
40690
+ } else {
40691
+ replacement = { ...donor?.entry, package: keep.specifier };
40692
+ }
40693
+ }
40694
+ return { action: "dedupe", entries, keep, remove, replacement };
39925
40695
  }
39926
- var init_doctor_opencode_cache = __esm(() => {
40696
+ function applyPluginDuplicatePlan(config, plan) {
40697
+ if (plan.replacement !== undefined) {
40698
+ const list = config[plan.keep.key];
40699
+ list[plan.keep.index] = plan.replacement;
40700
+ }
40701
+ const removals = [...plan.remove].sort((a, b) => a.key === b.key ? b.index - a.index : a.key.localeCompare(b.key));
40702
+ for (const entry of removals) {
40703
+ config[entry.key].splice(entry.index, 1);
40704
+ }
40705
+ }
40706
+ function describePluginEntry(entry) {
40707
+ const suffix = hasOptions(entry.options) ? " (with options)" : "";
40708
+ const kind = entry.kind === "dev-path" ? "dev path " : "";
40709
+ return `${entry.key}[${entry.index}]: ${kind}${entry.specifier}${suffix}`;
40710
+ }
40711
+ function checkPluginDuplicates(config, configName, options, report) {
40712
+ const plan = planPluginDuplicates(config);
40713
+ if (plan.action === "none")
40714
+ return false;
40715
+ report.warn(`Magic Context is registered ${plan.entries.length} times in ${configName}; OpenCode loads the first and fails the rest as duplicates`);
40716
+ for (const entry of plan.entries)
40717
+ report.info(` ${describePluginEntry(entry)}`);
40718
+ if (plan.action === "leave") {
40719
+ report.warn(plan.reason === "dev-path" ? "A local development checkout is registered next to another Magic Context entry; leaving every entry in place — remove the one you do not want by hand" : "Two Magic Context entries carry different options; leaving every entry in place — merge the options into one entry by hand");
40720
+ return false;
40721
+ }
40722
+ if (!options.fix) {
40723
+ report.info(` Run 'doctor --fix' to keep ${plan.keep.key}[${plan.keep.index}] (${plan.keep.specifier}) and remove the others`);
40724
+ return false;
40725
+ }
40726
+ applyPluginDuplicatePlan(config, plan);
40727
+ report.pass(`Removed ${plan.remove.length} duplicate Magic Context ${plan.remove.length === 1 ? "entry" : "entries"} from ${configName}; kept ${plan.keep.specifier} under "${plan.keep.key}"${plan.replacement !== undefined ? " with the removed entry's options" : ""}`);
40728
+ return true;
40729
+ }
40730
+ var init_doctor_opencode_plugin_duplicates = __esm(() => {
40731
+ init_opencode();
40732
+ init_opencode_plugin_cache();
40733
+ init_opencode_plugin_registration();
40734
+ });
40735
+
40736
+ // src/commands/doctor-opencode-plugin-entry.ts
40737
+ function isPinnedOpenCodePluginSpecifier(specifier) {
40738
+ if (specifier === OPENCODE_PLUGIN_NAME || specifier === OPENCODE_PLUGIN_ENTRY_WITH_VERSION) {
40739
+ return false;
40740
+ }
40741
+ return specifier.startsWith(`${OPENCODE_PLUGIN_NAME}@`);
40742
+ }
40743
+ function withPluginEntrySpecifier(entry, specifier) {
40744
+ if (Array.isArray(entry) && entry.length >= 1) {
40745
+ const replacement = [...entry];
40746
+ replacement[0] = specifier;
40747
+ return replacement;
40748
+ }
40749
+ if (entry !== null && typeof entry === "object" && !Array.isArray(entry)) {
40750
+ return { ...entry, package: specifier };
40751
+ }
40752
+ return specifier;
40753
+ }
40754
+ function checkOpenCodePluginEntry(config, configName, options, report) {
40755
+ const allEntries = readPluginEntries(config);
40756
+ const found = allEntries.find(({ entry }) => matchesPluginEntry(entry, OPENCODE_PLUGIN_NAME) || isDevPathPluginEntry(entry));
40757
+ if (!found) {
40758
+ const key = options.registrationKey;
40759
+ const list = Array.isArray(config[key]) ? config[key] : [];
40760
+ list.push(OPENCODE_PLUGIN_ENTRY_WITH_VERSION);
40761
+ config[key] = list;
40762
+ report.fixed(`Added plugin to ${configName}`);
40763
+ return true;
40764
+ }
40765
+ const list = config[found.key];
40766
+ const specifier = pluginEntryPackage(found.entry) ?? "";
40767
+ if (specifier === OPENCODE_PLUGIN_ENTRY_WITH_VERSION) {
40768
+ report.pass(`Plugin registered in ${configName}`);
40769
+ return false;
40770
+ }
40771
+ if (isDevPathPluginEntry(found.entry)) {
40772
+ report.pass(`Plugin registered in ${configName} (dev path: ${specifier})`);
40773
+ return false;
40774
+ }
40775
+ if (isPinnedOpenCodePluginSpecifier(specifier) && !options.force) {
40776
+ report.autoUpdateStall(specifier);
40777
+ report.warn(`Plugin pinned to ${specifier} in ${configName} — use 'doctor --force' to upgrade`);
40778
+ return false;
40779
+ }
40780
+ list[found.index] = withPluginEntrySpecifier(found.entry, OPENCODE_PLUGIN_ENTRY_WITH_VERSION);
40781
+ report.fixed(`Upgraded plugin entry in ${configName}: ${specifier} → ${OPENCODE_PLUGIN_ENTRY_WITH_VERSION}`);
40782
+ return true;
40783
+ }
40784
+ var init_doctor_opencode_plugin_entry = __esm(() => {
40785
+ init_opencode();
39927
40786
  init_opencode_plugin_cache();
40787
+ init_opencode_plugin_registration();
39928
40788
  });
39929
40789
 
39930
40790
  // src/commands/doctor-store-generation.ts
@@ -39982,11 +40842,22 @@ function formatUnresolvedCompartmentSession(session) {
39982
40842
  }
39983
40843
 
39984
40844
  // src/commands/doctor-opencode.ts
39985
- import { spawnSync as spawnSync7 } from "node:child_process";
39986
- import { existsSync as existsSync36, mkdtempSync, readFileSync as readFileSync25, rmSync as rmSync4 } from "node:fs";
40845
+ import { spawnSync as spawnSync8 } from "node:child_process";
40846
+ import { existsSync as existsSync38, mkdtempSync, readFileSync as readFileSync26, rmSync as rmSync5 } from "node:fs";
39987
40847
  import { createRequire as createRequire6 } from "node:module";
39988
40848
  import { homedir as homedir16, tmpdir as tmpdir3 } from "node:os";
39989
40849
  import { join as join32 } from "node:path";
40850
+ function formatSharedDbRowCounts(db) {
40851
+ return SHARED_DB_ROW_COUNT_TABLES.map((table) => {
40852
+ try {
40853
+ const row = db.prepare(`SELECT COUNT(*) AS c FROM ${table}`).get();
40854
+ return `${table}=${typeof row?.c === "number" ? row.c : "n/a"}`;
40855
+ } catch (error) {
40856
+ const reason = error instanceof Error ? error.message : String(error);
40857
+ return `${table}=n/a (${sanitizeDiagnosticText(reason)})`;
40858
+ }
40859
+ }).join(", ");
40860
+ }
39990
40861
  function findUndeclaredConfiguredVariants(configured, catalog) {
39991
40862
  return configured.filter((entry) => {
39992
40863
  const [providerID, id] = entry.model.split("/", 2);
@@ -39994,13 +40865,26 @@ function findUndeclaredConfiguredVariants(configured, catalog) {
39994
40865
  return model !== undefined && !Object.hasOwn(model.variants, entry.variant);
39995
40866
  });
39996
40867
  }
39997
- function checkConfiguredVariantCatalog(config, hostGeneration, warn, run = (args, cwd) => spawnSync7("opencode", args, {
40868
+ function findUnknownConfiguredModels(configured, catalog) {
40869
+ return configured.flatMap((entry) => {
40870
+ const [providerID, id] = entry.model.split("/", 2);
40871
+ if (!catalog.some((item) => item.providerID === providerID)) {
40872
+ return [{ ...entry, unknown: "provider" }];
40873
+ }
40874
+ if (!catalog.some((item) => item.providerID === providerID && item.id === id)) {
40875
+ return [{ ...entry, unknown: "model" }];
40876
+ }
40877
+ return [];
40878
+ });
40879
+ }
40880
+ function checkConfiguredVariantCatalog(config, hostGeneration, warn, run = (args, cwd) => spawnSync8("opencode", args, {
39998
40881
  cwd,
39999
40882
  encoding: "utf8",
40000
40883
  timeout: hostGeneration === "v2" ? 90000 : 45000,
40001
40884
  maxBuffer: 16 * 1024 * 1024
40002
40885
  }), projectDir = process.cwd()) {
40003
40886
  const configured = [];
40887
+ const models = [];
40004
40888
  const root = config && typeof config === "object" ? config : {};
40005
40889
  for (const agent of ["historian", "dreamer"]) {
40006
40890
  const section = root[agent];
@@ -40010,21 +40894,25 @@ function checkConfiguredVariantCatalog(config, hostGeneration, warn, run = (args
40010
40894
  if (!block || typeof block !== "object")
40011
40895
  continue;
40012
40896
  const record = block;
40013
- const add = (entry, defaultVariant) => {
40897
+ const add = (entry, defaultVariant, fallback) => {
40014
40898
  const value = entry && typeof entry === "object" ? entry : {};
40015
40899
  const model = typeof entry === "string" ? entry : value.model;
40016
40900
  const variant = value.variant ?? defaultVariant;
40017
- if (typeof model === "string" && typeof variant === "string" && model.includes("/")) {
40018
- configured.push({ agent, model, variant });
40901
+ if (typeof model !== "string" || !model.includes("/"))
40902
+ return;
40903
+ if (!models.some((known) => known.agent === agent && known.model === model)) {
40904
+ models.push({ agent, model, fallback });
40019
40905
  }
40906
+ if (typeof variant === "string")
40907
+ configured.push({ agent, model, variant });
40020
40908
  };
40021
- add(record.model, record.variant);
40909
+ add(record.model, record.variant, false);
40022
40910
  if (Array.isArray(record.fallback_models)) {
40023
40911
  for (const entry of record.fallback_models)
40024
- add(entry, undefined);
40912
+ add(entry, undefined, true);
40025
40913
  }
40026
40914
  }
40027
- if (configured.length === 0)
40915
+ if (models.length === 0)
40028
40916
  return;
40029
40917
  const tempRoot = mkdtempSync(join32(tmpdir3(), "magic-context-opencode-catalog-"));
40030
40918
  try {
@@ -40033,14 +40921,19 @@ function checkConfiguredVariantCatalog(config, hostGeneration, warn, run = (args
40033
40921
  const result = run(command, tempRoot);
40034
40922
  const catalog = hostGeneration === "v2" ? parseOpenCodeV2ModelCatalog(result.stdout ?? "") : parseOpenCodeModelCatalog(result.stdout ?? "");
40035
40923
  if (result.error || result.status !== 0 || catalog.length === 0) {
40036
- warn(`Could not verify configured hidden-agent variants: this OpenCode host did not provide a readable model catalog. ${hostGeneration === "v2" ? "Start the background service with opencode service start, then check" : "Check"} ${guidance}.`);
40924
+ warn(`Could not verify configured hidden-agent models and variants: this OpenCode host did not provide a readable model catalog. ${hostGeneration === "v2" ? "Start the background service with opencode service start, then check" : "Check"} ${guidance}.`);
40037
40925
  return;
40038
40926
  }
40927
+ for (const entry of findUnknownConfiguredModels(models, catalog)) {
40928
+ const [providerID] = entry.model.split("/", 1);
40929
+ const role = `${entry.agent}${entry.fallback ? " fallback" : ""} model ${entry.model}`;
40930
+ warn(entry.unknown === "provider" ? `${role} names provider '${providerID}', which this OpenCode host does not have. The ${entry.agent} cannot run on it; configure that provider in OpenCode or choose a model listed by ${guidance}.` : `${role} is not offered by provider '${providerID}' on this OpenCode host. The ${entry.agent} cannot run on it; choose a model listed by ${guidance}.`);
40931
+ }
40039
40932
  for (const entry of findUndeclaredConfiguredVariants(configured, catalog)) {
40040
40933
  warn(`${entry.agent} model ${entry.model} requests variant '${entry.variant}', which this host does not offer. Remove the variant or choose one listed by ${guidance}.`);
40041
40934
  }
40042
40935
  } finally {
40043
- rmSync4(tempRoot, { recursive: true, force: true });
40936
+ rmSync5(tempRoot, { recursive: true, force: true });
40044
40937
  }
40045
40938
  }
40046
40939
  function parseOpenCodeV2ModelCatalog(output) {
@@ -40159,12 +41052,12 @@ function checkUserMemoriesDreamerCompatibility(mcConfig) {
40159
41052
  return null;
40160
41053
  return 'dreamer.tasks["review-user-memories"] is scheduled but dreamer.disable=true, so new promotions will not run. Remove dreamer.disable or set dreamer.tasks["review-user-memories"].schedule="" to disable the task.';
40161
41054
  }
40162
- async function fetchNpmLatest(pkg, timeoutMs = 5000) {
41055
+ async function fetchNpmLatest(pkg, distTag = "latest", timeoutMs = 5000) {
40163
41056
  try {
40164
41057
  const controller = new AbortController;
40165
41058
  const timer = setTimeout(() => controller.abort(), timeoutMs);
40166
41059
  try {
40167
- const res = await fetch(`https://registry.npmjs.org/${pkg}/latest`, {
41060
+ const res = await fetch(`${resolveNpmRegistryUrl()}/${pkg}/${encodeURIComponent(distTag)}`, {
40168
41061
  signal: controller.signal,
40169
41062
  headers: { Accept: "application/json" }
40170
41063
  });
@@ -40179,6 +41072,10 @@ async function fetchNpmLatest(pkg, timeoutMs = 5000) {
40179
41072
  return null;
40180
41073
  }
40181
41074
  }
41075
+ function resolveNpmRegistryUrl(env = process.env) {
41076
+ const configured = (env.npm_config_registry ?? env.NPM_CONFIG_REGISTRY)?.trim();
41077
+ return (configured || "https://registry.npmjs.org").replace(/\/+$/, "");
41078
+ }
40182
41079
  function getSelfVersion2() {
40183
41080
  const req = createRequire6(import.meta.url);
40184
41081
  for (const relPath of ["../../package.json", "../package.json"]) {
@@ -40190,11 +41087,6 @@ function getSelfVersion2() {
40190
41087
  }
40191
41088
  return "0.0.0";
40192
41089
  }
40193
- function isPinnedOpenCodePluginSpecifier(specifier) {
40194
- if (specifier === OPENCODE_PLUGIN_NAME || specifier === OPENCODE_PLUGIN_ENTRY_WITH_VERSION)
40195
- return false;
40196
- return specifier.startsWith(`${OPENCODE_PLUGIN_NAME}@`);
40197
- }
40198
41090
  function describeAutoUpdateStall(specifier, autoUpdateEnabled, storageDir = getMagicContextStorageDir()) {
40199
41091
  if (!autoUpdateEnabled || !isPinnedOpenCodePluginSpecifier(specifier))
40200
41092
  return null;
@@ -40212,10 +41104,10 @@ function getUserNpmrcPath() {
40212
41104
  function collectNpmReleaseAgeWarnings() {
40213
41105
  const ageWarnings = [];
40214
41106
  const npmrcPath = getUserNpmrcPath();
40215
- if (!existsSync36(npmrcPath))
41107
+ if (!existsSync38(npmrcPath))
40216
41108
  return ageWarnings;
40217
41109
  try {
40218
- const npmrc = readFileSync25(npmrcPath, "utf-8");
41110
+ const npmrc = readFileSync26(npmrcPath, "utf-8");
40219
41111
  for (const line of npmrc.split(`
40220
41112
  `)) {
40221
41113
  const trimmed = line.trim();
@@ -40248,15 +41140,15 @@ function compareVersions(a, b) {
40248
41140
  function openBrowser(url) {
40249
41141
  try {
40250
41142
  if (process.platform === "darwin") {
40251
- const child = spawnSync7("open", [url], { stdio: "ignore" });
41143
+ const child = spawnSync8("open", [url], { stdio: "ignore" });
40252
41144
  if (child.status === 0)
40253
41145
  return;
40254
41146
  } else if (process.platform === "linux") {
40255
- const child = spawnSync7("xdg-open", [url], { stdio: "ignore" });
41147
+ const child = spawnSync8("xdg-open", [url], { stdio: "ignore" });
40256
41148
  if (child.status === 0)
40257
41149
  return;
40258
41150
  } else if (process.platform === "win32") {
40259
- const child = spawnSync7("cmd", ["/c", "start", "", url], { stdio: "ignore" });
41151
+ const child = spawnSync8("cmd", ["/c", "start", "", url], { stdio: "ignore" });
40260
41152
  if (child.status === 0)
40261
41153
  return;
40262
41154
  }
@@ -40349,7 +41241,7 @@ function checkLocalEmbeddingRuntimeForDoctor(runtimePreference = "auto") {
40349
41241
  return { issues: 0 };
40350
41242
  }
40351
41243
  async function checkEmbeddingConfig(magicContextConfigPath) {
40352
- if (!existsSync36(magicContextConfigPath)) {
41244
+ if (!existsSync38(magicContextConfigPath)) {
40353
41245
  return checkLocalEmbeddingRuntimeForDoctor();
40354
41246
  }
40355
41247
  let rawText;
@@ -40557,7 +41449,7 @@ async function runDoctor2(options = {}) {
40557
41449
  const activeInstallation = installationReports[0];
40558
41450
  if (!activeInstallation) {
40559
41451
  fail("OpenCode is not installed or not in PATH");
40560
- log2.info("Doctor checked ~/.opencode/bin/opencode and each entry in $PATH.");
41452
+ log2.info("Doctor checked ~/.opencode/bin/opencode, each entry in $PATH, and the OpenCode CLI bundled in OpenChamber.app.");
40561
41453
  log2.info("If `which opencode` succeeds outside doctor, your wrapper or shim may not be readable by Node — please share that wrapper in the issue.");
40562
41454
  outro2("Doctor failed — install OpenCode first");
40563
41455
  return 1;
@@ -40573,7 +41465,19 @@ async function runDoctor2(options = {}) {
40573
41465
  pass(installationReports.length > 1 ? `OpenCode ${activeInstallation.version} installed (active install marked above)` : `OpenCode ${activeInstallation.version} installed`);
40574
41466
  }
40575
41467
  const hostGeneration = openCodeHostGenerationFromVersion(activeInstallation.version);
40576
- const openCodeDbResolution = resolveOpenCodeDbPath(hostGeneration);
41468
+ const storeHostSelection = selectOpenCodeStoreHost(installationReports, openCodeHostGenerationFromVersion);
41469
+ const storeHost = storeHostSelection?.host ?? activeInstallation;
41470
+ const storeGeneration = openCodeHostGenerationFromVersion(storeHost.version);
41471
+ const openCodeDbResolution = resolveOpenCodeDbPath(storeGeneration);
41472
+ if (storeHostSelection?.shadowed) {
41473
+ const activeStore = resolveOpenCodeDbPath(hostGeneration).path;
41474
+ const shared = activeStore === openCodeDbResolution.path;
41475
+ const versionLabel = (version) => version.replace(/^opencode\s+v?/i, "");
41476
+ const activeVersion = versionLabel(activeInstallation.version);
41477
+ const storeVersion = versionLabel(storeHost.version);
41478
+ warn(`OpenCode ${activeVersion} (${activeInstallation.path}) is first on PATH, and OpenCode ${storeVersion} is also installed (${storeHost.path})${shared ? `; both use ${openCodeDbResolution.path}` : ""}.`);
41479
+ log2.warn(` Store and conversion checks below use OpenCode ${storeVersion}. Plugin configuration checks use OpenCode ${activeVersion}; to check OpenCode ${storeVersion}'s configuration instead, put its binary first on PATH and run doctor again.`);
41480
+ }
40577
41481
  const openCodeDbCheck = describeOpenCodeDatabaseDoctorCheck(openCodeDbResolution);
40578
41482
  if (openCodeDbCheck.ok)
40579
41483
  pass(openCodeDbCheck.message);
@@ -40597,14 +41501,16 @@ async function runDoctor2(options = {}) {
40597
41501
  fixed += report.repaired;
40598
41502
  } else if (options.fix) {
40599
41503
  warn(`${summary}; repaired=${report.repaired}, but some rows remain unconvertible`);
41504
+ } else if (report.migrationCompleted) {
41505
+ log2.info(`${summary}; OpenCode 2 already converted this store`);
40600
41506
  } else {
40601
41507
  warn(`${summary}; run \`magic-context doctor --fix\` before upgrading OpenCode`);
40602
41508
  }
40603
- if (report.recoveryRequired) {
40604
- const [heading, ...details] = formatOpenCodeV2ReconversionRecipe(openCodeDbResolution.path);
40605
- warn(heading ?? "OpenCode 2 conversion recovery is required");
40606
- for (const detail of details)
40607
- log2.warn(` ${detail}`);
41509
+ const notice = formatOpenCodeV2MissingMarkerNotice(report);
41510
+ if (notice) {
41511
+ for (const [index, line] of notice.entries()) {
41512
+ log2.info(index === 0 ? line : ` ${line}`);
41513
+ }
40608
41514
  }
40609
41515
  } catch (error) {
40610
41516
  warn(`OpenCode compaction marker conversion check unavailable: ${error instanceof Error ? error.message : String(error)}`);
@@ -40620,9 +41526,14 @@ async function runDoctor2(options = {}) {
40620
41526
  readonly: true,
40621
41527
  fileMustExist: true
40622
41528
  });
40623
- const dangling = listDanglingCompartmentBoundaries(contextDb, sessionDb, /\d/.test(activeInstallation.version) ? hostGeneration : undefined, (line) => log2.info(line));
41529
+ const dangling = listDanglingCompartmentBoundaries(contextDb, sessionDb, /\d/.test(storeHost.version) ? storeGeneration : undefined, (line) => log2.info(line));
40624
41530
  if (dangling.length === 0) {
40625
41531
  pass("Compartment boundary ids resolve in the OpenCode session store");
41532
+ } else if (storeGeneration === "v2" && /\d/.test(storeHost.version)) {
41533
+ log2.info(`${dangling.length} compartment(s) point at OpenCode message ids that are not in the OpenCode 2 store. Magic Context places these from the neighbouring compartments; any it cannot place are listed as excluded from range recovery below.`);
41534
+ for (const boundary of dangling) {
41535
+ log2.info(` ${formatDanglingCompartmentBoundary(boundary)}`);
41536
+ }
40626
41537
  } else {
40627
41538
  warn(`${dangling.length} compartment(s) have dangling OpenCode boundary ids`);
40628
41539
  for (const boundary of dangling) {
@@ -40631,11 +41542,11 @@ async function runDoctor2(options = {}) {
40631
41542
  }
40632
41543
  if (!supportsCoordinateGenerationReporting(contextDb)) {
40633
41544
  log2.info("Store projection check: this context database predates the coordinate columns");
40634
- } else if (!/\d/.test(activeInstallation.version)) {
41545
+ } else if (!/\d/.test(storeHost.version)) {
40635
41546
  log2.info("Store projection check: OpenCode reported no version, so the running projection is unknown");
40636
41547
  } else {
40637
- const pendingRebases = countPendingCoordinateRebases(contextDb, hostGeneration);
40638
- const pendingLine = formatPendingCoordinateRebases(pendingRebases, hostGeneration);
41548
+ const pendingRebases = countPendingCoordinateRebases(contextDb, storeGeneration);
41549
+ const pendingLine = formatPendingCoordinateRebases(pendingRebases, storeGeneration);
40639
41550
  if (pendingRebases.changed + pendingRebases.unrecorded === 0) {
40640
41551
  pass(pendingLine);
40641
41552
  } else {
@@ -40680,7 +41591,7 @@ async function runDoctor2(options = {}) {
40680
41591
  pass(`OpenCode config: ${paths.opencodeConfig}`);
40681
41592
  }
40682
41593
  let autoUpdateEnabled = true;
40683
- if (existsSync36(paths.magicContextConfig)) {
41594
+ if (existsSync38(paths.magicContextConfig)) {
40684
41595
  pass(`Magic Context config: ${paths.magicContextConfig}`);
40685
41596
  try {
40686
41597
  const raw = loadRawConfigFile({ configPath: paths.magicContextConfig, tier: "user" });
@@ -40716,7 +41627,7 @@ async function runDoctor2(options = {}) {
40716
41627
  warn(`No magic-context.jsonc found — using defaults`);
40717
41628
  log2.info(" Run 'setup' to create one with model recommendations");
40718
41629
  }
40719
- if (existsSync36(paths.magicContextConfig)) {
41630
+ if (existsSync38(paths.magicContextConfig)) {
40720
41631
  try {
40721
41632
  const raw = loadRawConfigFile({ configPath: paths.magicContextConfig, tier: "user" });
40722
41633
  if (!raw)
@@ -40846,59 +41757,35 @@ async function runDoctor2(options = {}) {
40846
41757
  };
40847
41758
  if (paths.opencodeConfigFormat !== "none") {
40848
41759
  try {
40849
- const raw = readFileSync25(paths.opencodeConfig, "utf-8");
41760
+ const raw = readFileSync26(paths.opencodeConfig, "utf-8");
40850
41761
  const config = import_comment_json11.parse(raw);
40851
- const registrationKey = pluginConfigKeyFor(hostGeneration);
41762
+ const configName = paths.opencodeConfigFormat === "jsonc" ? "opencode.jsonc" : "opencode.json";
41763
+ if (checkPluginDuplicates(config, configName, options, {
41764
+ warn,
41765
+ pass: (message) => {
41766
+ pass(message);
41767
+ fixed++;
41768
+ },
41769
+ info: (message) => log2.info(message)
41770
+ })) {
41771
+ writeFileAtomic(paths.opencodeConfig, `${import_comment_json11.stringify(config, null, 2)}
41772
+ `);
41773
+ }
40852
41774
  const allEntries = readPluginEntries(config);
40853
- const found = allEntries.find(({ entry }) => matchesPluginEntry(entry, OPENCODE_PLUGIN_NAME) || isDevPathPluginEntry(entry));
40854
- const targetKey = found?.key ?? registrationKey;
40855
- const rawPlugins = Array.isArray(config?.[targetKey]) ? config[targetKey] : [];
40856
- const existingIdx = found ? found.index : -1;
40857
41775
  if (allEntries.some(({ entry }) => isLocalPathPluginEntry(entry) && String(entry).includes("magic-context") && !isDevPathPluginEntry(entry))) {
40858
41776
  warn("An unverifiable local OpenCode plugin path was ignored because its package name is not Magic Context");
40859
41777
  }
40860
- const configName = paths.opencodeConfigFormat === "jsonc" ? "opencode.jsonc" : "opencode.json";
40861
- const entryAsString = (entry) => {
40862
- if (typeof entry === "string")
40863
- return entry;
40864
- if (Array.isArray(entry) && typeof entry[0] === "string")
40865
- return entry[0];
40866
- return null;
40867
- };
40868
- if (existingIdx >= 0 && entryAsString(rawPlugins[existingIdx]) === OPENCODE_PLUGIN_ENTRY_WITH_VERSION) {
40869
- pass(`Plugin registered in ${configName}`);
40870
- } else if (existingIdx >= 0) {
40871
- const oldEntry = rawPlugins[existingIdx];
40872
- const oldEntryStr = entryAsString(oldEntry) ?? "";
40873
- if (isDevPathPluginEntry(oldEntry)) {
40874
- pass(`Plugin registered in ${configName} (dev path: ${oldEntryStr})`);
40875
- } else {
40876
- const isPinned = isPinnedOpenCodePluginSpecifier(oldEntryStr);
40877
- if (isPinned && !options.force) {
40878
- reportAutoUpdateStall(oldEntryStr);
40879
- warn(`Plugin pinned to ${oldEntryStr} in ${configName} — use 'doctor --force' to upgrade`);
40880
- } else {
40881
- if (Array.isArray(oldEntry) && oldEntry.length >= 1) {
40882
- const replacement = [...oldEntry];
40883
- replacement[0] = OPENCODE_PLUGIN_ENTRY_WITH_VERSION;
40884
- rawPlugins[existingIdx] = replacement;
40885
- } else {
40886
- rawPlugins[existingIdx] = OPENCODE_PLUGIN_ENTRY_WITH_VERSION;
40887
- }
40888
- config[targetKey] = rawPlugins;
40889
- writeFileAtomic(paths.opencodeConfig, `${import_comment_json11.stringify(config, null, 2)}
40890
- `);
40891
- pass(`Upgraded plugin entry in ${configName}: ${oldEntryStr} → ${OPENCODE_PLUGIN_ENTRY_WITH_VERSION}`);
40892
- fixed++;
40893
- }
40894
- }
40895
- } else {
40896
- rawPlugins.push(OPENCODE_PLUGIN_ENTRY_WITH_VERSION);
40897
- config[targetKey] = rawPlugins;
41778
+ if (checkOpenCodePluginEntry(config, configName, { force: options.force, registrationKey: pluginConfigKeyFor(hostGeneration) }, {
41779
+ pass,
41780
+ warn,
41781
+ fixed: (message) => {
41782
+ pass(message);
41783
+ fixed++;
41784
+ },
41785
+ autoUpdateStall: reportAutoUpdateStall
41786
+ })) {
40898
41787
  writeFileAtomic(paths.opencodeConfig, `${import_comment_json11.stringify(config, null, 2)}
40899
41788
  `);
40900
- pass(`Added plugin to ${configName}`);
40901
- fixed++;
40902
41789
  }
40903
41790
  } catch {
40904
41791
  warn("Could not parse opencode config to verify plugin entry");
@@ -40939,25 +41826,18 @@ async function runDoctor2(options = {}) {
40939
41826
  pass("Added TUI sidebar plugin to tui.json");
40940
41827
  warn("Restart OpenCode to see the sidebar");
40941
41828
  fixed++;
40942
- } else if (existsSync36(paths.tuiConfig)) {
41829
+ } else if (existsSync38(paths.tuiConfig)) {
40943
41830
  try {
40944
- const tuiRaw = readFileSync25(paths.tuiConfig, "utf-8");
41831
+ const tuiRaw = readFileSync26(paths.tuiConfig, "utf-8");
40945
41832
  const tuiConfig = import_comment_json11.parse(tuiRaw);
40946
41833
  const tuiRawPlugins = Array.isArray(tuiConfig?.plugin) ? tuiConfig.plugin : [];
40947
41834
  const tuiIdx = tuiRawPlugins.findIndex((entry) => matchesPluginEntry(entry, OPENCODE_PLUGIN_NAME) || isDevPathPluginEntry(entry));
40948
41835
  if (tuiRawPlugins.some((entry) => isLocalPathPluginEntry(entry) && String(entry).includes("magic-context") && !isDevPathPluginEntry(entry))) {
40949
41836
  warn("An unverifiable local TUI plugin path was ignored because its package name is not Magic Context");
40950
41837
  }
40951
- const tuiEntryAsString = (entry) => {
40952
- if (typeof entry === "string")
40953
- return entry;
40954
- if (Array.isArray(entry) && typeof entry[0] === "string")
40955
- return entry[0];
40956
- return "";
40957
- };
40958
41838
  if (tuiIdx >= 0) {
40959
41839
  const tuiEntry = tuiRawPlugins[tuiIdx];
40960
- const tuiEntryStr = tuiEntryAsString(tuiEntry);
41840
+ const tuiEntryStr = pluginEntryPackage(tuiEntry) ?? "";
40961
41841
  if (isDevPathPluginEntry(tuiEntry)) {
40962
41842
  pass(`TUI sidebar plugin configured (dev path: ${tuiEntryStr})`);
40963
41843
  } else {
@@ -40966,13 +41846,7 @@ async function runDoctor2(options = {}) {
40966
41846
  reportAutoUpdateStall(tuiEntryStr);
40967
41847
  warn(`TUI plugin pinned to ${tuiEntryStr} — use 'doctor --force' to upgrade`);
40968
41848
  } else if (tuiPinned && options.force) {
40969
- if (Array.isArray(tuiEntry) && tuiEntry.length >= 1) {
40970
- const replacement = [...tuiEntry];
40971
- replacement[0] = OPENCODE_PLUGIN_ENTRY_WITH_VERSION;
40972
- tuiRawPlugins[tuiIdx] = replacement;
40973
- } else {
40974
- tuiRawPlugins[tuiIdx] = OPENCODE_PLUGIN_ENTRY_WITH_VERSION;
40975
- }
41849
+ tuiRawPlugins[tuiIdx] = withPluginEntrySpecifier(tuiEntry, OPENCODE_PLUGIN_ENTRY_WITH_VERSION);
40976
41850
  tuiConfig.plugin = tuiRawPlugins;
40977
41851
  writeFileAtomic(paths.tuiConfig, `${import_comment_json11.stringify(tuiConfig, null, 2)}
40978
41852
  `);
@@ -40992,7 +41866,7 @@ async function runDoctor2(options = {}) {
40992
41866
  fail("Could not create or verify the TUI sidebar config");
40993
41867
  }
40994
41868
  }
40995
- if (existsSync36(paths.magicContextConfig)) {
41869
+ if (existsSync38(paths.magicContextConfig)) {
40996
41870
  try {
40997
41871
  const raw = loadRawConfigFile({ configPath: paths.magicContextConfig, tier: "user" });
40998
41872
  if (!raw)
@@ -41016,7 +41890,7 @@ async function runDoctor2(options = {}) {
41016
41890
  const storage = getMagicContextStorageResolution();
41017
41891
  const dbPath = join32(storage.path, "context.db");
41018
41892
  log2.info(`Shared storage: ${storage.path} (source: ${storage.source})`);
41019
- if (!existsSync36(dbPath)) {
41893
+ if (!existsSync38(dbPath)) {
41020
41894
  log2.info(`Shared context DB not yet created at ${dbPath} (will be created on first run)`);
41021
41895
  } else {
41022
41896
  log2.info(`Shared context DB exists at ${dbPath}`);
@@ -41063,25 +41937,7 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
41063
41937
  fail(`SQLite integrity_check failed: ${err instanceof Error ? err.message : String(err)}
41064
41938
  ${formatDatabaseRepairGuidance(dbPath)}`);
41065
41939
  }
41066
- try {
41067
- const counts = {};
41068
- for (const table of [
41069
- "tags",
41070
- "compartments",
41071
- "memories",
41072
- "notes",
41073
- "dream_runs"
41074
- ]) {
41075
- try {
41076
- const row = db.prepare(`SELECT COUNT(*) as c FROM ${table}`).get();
41077
- counts[table] = row?.c ?? 0;
41078
- } catch {
41079
- counts[table] = 0;
41080
- }
41081
- }
41082
- const summary = Object.entries(counts).map(([k, v]) => `${k}=${v}`).join(", ");
41083
- log2.info(`Shared DB row counts: ${summary}`);
41084
- } catch {}
41940
+ log2.info(`Shared DB row counts: ${formatSharedDbRowCounts(db)}`);
41085
41941
  for (const stall of listShadowBackfillStalls(db)) {
41086
41942
  warn(formatShadowBackfillStall(stall));
41087
41943
  }
@@ -41130,9 +41986,30 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
41130
41986
  log2.info(` Manually delete: ${cacheResult.path}`);
41131
41987
  }
41132
41988
  issues++;
41133
- } else {
41989
+ } else if (hostGeneration !== "v2") {
41134
41990
  pass("Plugin cache clean (no cached version found)");
41135
41991
  }
41992
+ let v2DistTag;
41993
+ if (paths.opencodeConfigFormat !== "none") {
41994
+ try {
41995
+ v2DistTag = configuredOpenCodeV2DistTag(import_comment_json11.parse(readFileSync26(paths.opencodeConfig, "utf-8")));
41996
+ } catch {}
41997
+ }
41998
+ const v2Cache = reportOpenCodeV2PluginCache(checkOpenCodeV2PluginCache({
41999
+ fix: options.fix,
42000
+ force: options.force,
42001
+ latestVersion: v2DistTag ? await fetchNpmLatest(OPENCODE_PLUGIN_NAME, v2DistTag) : pluginNpmLatest,
42002
+ distTag: v2DistTag,
42003
+ hostFiles: openCodeHostDatabaseFiles([openCodeDbResolution.path])
42004
+ }), { pass, warn, info: (message) => log2.info(message) }, { reportMissing: hostGeneration === "v2" });
42005
+ if (v2Cache.fixed)
42006
+ fixed++;
42007
+ if (v2Cache.issue)
42008
+ issues++;
42009
+ const sharedDbVersion = readContextDbSchemaVersion(dbPath);
42010
+ if (sharedDbVersion !== null) {
42011
+ reportCachedPluginFences(compareCachedPluginFences(listCachedOpenCodePluginFences(), sharedDbVersion), { pass, fail, info: (message) => log2.info(message) });
42012
+ }
41136
42013
  {
41137
42014
  const ageWarnings = collectNpmReleaseAgeWarnings();
41138
42015
  if (ageWarnings.length > 0) {
@@ -41145,7 +42022,11 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
41145
42022
  issues++;
41146
42023
  }
41147
42024
  }
41148
- const logFiles = inspectMagicContextLogs("opencode");
42025
+ const logHarnesses = [
42026
+ ...hostGeneration === "v1" ? ["opencode"] : [],
42027
+ ...hostGeneration === "v2" || storeGeneration === "v2" ? ["opencode2"] : []
42028
+ ];
42029
+ const logFiles = logHarnesses.flatMap((harness) => inspectMagicContextLogs(harness)).filter((file, index, all) => all.findIndex((other) => other.path === file.path) === index);
41149
42030
  const existingLogFiles = logFiles.filter((file) => file.exists);
41150
42031
  if (existingLogFiles.length === 0) {
41151
42032
  log2.info(`No plugin log file yet; checked: ${logFiles.map((file) => file.path).join(", ")}`);
@@ -41193,7 +42074,7 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
41193
42074
  }
41194
42075
  return 0;
41195
42076
  }
41196
- var import_comment_json11, CLI_PACKAGE_NAME = "@cortexkit/magic-context";
42077
+ var import_comment_json11, CLI_PACKAGE_NAME = "@cortexkit/magic-context", SHARED_DB_ROW_COUNT_TABLES;
41197
42078
  var init_doctor_opencode = __esm(async () => {
41198
42079
  init_config();
41199
42080
  init_agent_disable();
@@ -41227,6 +42108,9 @@ var init_doctor_opencode = __esm(async () => {
41227
42108
  init_prompts();
41228
42109
  init_redaction2();
41229
42110
  init_doctor_opencode_cache();
42111
+ init_doctor_opencode_plugin_duplicates();
42112
+ init_doctor_opencode_plugin_entry();
42113
+ init_doctor_opencode2_cache();
41230
42114
  await __promiseAll([
41231
42115
  init_storage_db(),
41232
42116
  init_sqlite(),
@@ -41234,13 +42118,15 @@ var init_doctor_opencode = __esm(async () => {
41234
42118
  init_storage_versions(),
41235
42119
  init_v22_backfill_commands(),
41236
42120
  init_doctor_authority(),
42121
+ init_doctor_cached_plugin_fence(),
41237
42122
  init_doctor_harness_relabel()
41238
42123
  ]);
41239
42124
  import_comment_json11 = __toESM(require_src3(), 1);
42125
+ SHARED_DB_ROW_COUNT_TABLES = ["tags", "compartments", "memories", "notes", "dream_runs"];
41240
42126
  });
41241
42127
 
41242
42128
  // src/lib/diagnostics-pi.ts
41243
- import { existsSync as existsSync37, readdirSync as readdirSync6, readFileSync as readFileSync26, statSync as statSync16 } from "node:fs";
42129
+ import { existsSync as existsSync39, readdirSync as readdirSync7, readFileSync as readFileSync27, statSync as statSync16 } from "node:fs";
41244
42130
  import { createRequire as createRequire7 } from "node:module";
41245
42131
  import { join as join33 } from "node:path";
41246
42132
  function getSelfVersion3() {
@@ -41257,7 +42143,7 @@ function getSelfVersion3() {
41257
42143
  }
41258
42144
  function fileSize2(path) {
41259
42145
  try {
41260
- return existsSync37(path) ? statSync16(path).size : 0;
42146
+ return existsSync39(path) ? statSync16(path).size : 0;
41261
42147
  } catch {
41262
42148
  return 0;
41263
42149
  }
@@ -41269,11 +42155,11 @@ function sanitizeValue2(value) {
41269
42155
  return sanitizeConfigValue(value);
41270
42156
  }
41271
42157
  function readJsonc(path) {
41272
- if (!existsSync37(path))
42158
+ if (!existsSync39(path))
41273
42159
  return { value: {} };
41274
42160
  try {
41275
42161
  return {
41276
- value: import_comment_json12.parse(readFileSync26(path, "utf-8"))
42162
+ value: import_comment_json12.parse(readFileSync27(path, "utf-8"))
41277
42163
  };
41278
42164
  } catch (error) {
41279
42165
  return {
@@ -41289,7 +42175,7 @@ function readConfigDiagnostic(path) {
41289
42175
  const parsed = readJsonc(path);
41290
42176
  return {
41291
42177
  path,
41292
- exists: existsSync37(path),
42178
+ exists: existsSync39(path),
41293
42179
  ...parsed.parseError ? { parseError: parsed.parseError } : {},
41294
42180
  flags: sanitizeValue2(parsed.value)
41295
42181
  };
@@ -41307,10 +42193,10 @@ function reverseSlugToDirectory(slug) {
41307
42193
  }
41308
42194
  function collectPiRecentSessions() {
41309
42195
  const sessionsRoot = getPiSessionsRoot();
41310
- if (!existsSync37(sessionsRoot))
42196
+ if (!existsSync39(sessionsRoot))
41311
42197
  return [];
41312
42198
  try {
41313
- const slugs = readdirSync6(sessionsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
42199
+ const slugs = readdirSync7(sessionsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
41314
42200
  const candidates = [];
41315
42201
  for (const slug of slugs) {
41316
42202
  const directory = reverseSlugToDirectory(slug);
@@ -41319,7 +42205,7 @@ function collectPiRecentSessions() {
41319
42205
  const slugDir = join33(sessionsRoot, slug);
41320
42206
  let files;
41321
42207
  try {
41322
- files = readdirSync6(slugDir).filter((name) => name.endsWith(".jsonl"));
42208
+ files = readdirSync7(slugDir).filter((name) => name.endsWith(".jsonl"));
41323
42209
  } catch {
41324
42210
  continue;
41325
42211
  }
@@ -41343,7 +42229,7 @@ function collectPiRecentSessions() {
41343
42229
  }
41344
42230
  function parseHistorianDumpMeta2(path) {
41345
42231
  try {
41346
- const xml = readFileSync26(path, "utf-8");
42232
+ const xml = readFileSync27(path, "utf-8");
41347
42233
  const parsed = parseCompartmentOutput(xml);
41348
42234
  const factCountByCategory = {};
41349
42235
  for (const fact of parsed.facts) {
@@ -41376,10 +42262,10 @@ function parseHistorianDumpMeta2(path) {
41376
42262
  }
41377
42263
  }
41378
42264
  function listDumpsInDir2(dir, limit) {
41379
- if (!existsSync37(dir))
42265
+ if (!existsSync39(dir))
41380
42266
  return { count: 0, recent: [] };
41381
42267
  try {
41382
- const entries = readdirSync6(dir).filter((name) => name.endsWith(".xml")).map((name) => {
42268
+ const entries = readdirSync7(dir).filter((name) => name.endsWith(".xml")).map((name) => {
41383
42269
  const stat = statSync16(join33(dir, name));
41384
42270
  return {
41385
42271
  name,
@@ -41470,7 +42356,7 @@ async function collectDiagnostics2(cwd = process.cwd()) {
41470
42356
  piVersion: pi ? getPiVersion(pi.path) : null,
41471
42357
  settings: {
41472
42358
  path: settingsPath,
41473
- exists: existsSync37(settingsPath),
42359
+ exists: existsSync39(settingsPath),
41474
42360
  ...settingsParsed.parseError ? { parseError: settingsParsed.parseError } : {},
41475
42361
  hasMagicContextPackage: hasPiMagicContextPackage(packages),
41476
42362
  packages: sanitizeValue2(packages)
@@ -41487,7 +42373,7 @@ async function collectDiagnostics2(cwd = process.cwd()) {
41487
42373
  storageDir: {
41488
42374
  path: storageDirPath,
41489
42375
  source: storageResolution.source,
41490
- exists: existsSync37(storageDirPath),
42376
+ exists: existsSync39(storageDirPath),
41491
42377
  contextDbSizeBytes: fileSize2(dbPath)
41492
42378
  },
41493
42379
  conflicts: {
@@ -41593,7 +42479,7 @@ var init_diagnostics_pi = __esm(() => {
41593
42479
 
41594
42480
  // src/lib/logs-pi.ts
41595
42481
  import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "node:fs";
41596
- import { dirname as dirname18, join as join34 } from "node:path";
42482
+ import { dirname as dirname19, join as join34 } from "node:path";
41597
42483
  function sanitizeLogContent2(content) {
41598
42484
  return sanitizeString2(content);
41599
42485
  }
@@ -41664,7 +42550,7 @@ async function bundleIssueReport2(report, description, title, options = {}) {
41664
42550
  const cwd = options.cwd ?? process.cwd();
41665
42551
  const timestamp = formatTimestamp2(options.now ?? new Date);
41666
42552
  const path = options.outputPath ?? join34(cwd, `magic-context-pi-issue-${timestamp}.md`);
41667
- mkdirSync10(dirname18(path), { recursive: true });
42553
+ mkdirSync10(dirname19(path), { recursive: true });
41668
42554
  writeFileSync9(path, `${bodyMarkdown}
41669
42555
  `);
41670
42556
  const fullPath = truncated ? options.outputPath ? `${options.outputPath}.full.md` : join34(cwd, `magic-context-pi-issue-${timestamp}-full.md`) : undefined;
@@ -41680,11 +42566,11 @@ var init_logs_pi = __esm(() => {
41680
42566
  });
41681
42567
 
41682
42568
  // src/commands/doctor-pi.ts
41683
- import { execFileSync as execFileSync6, spawnSync as spawnSync8 } from "node:child_process";
41684
- import { existsSync as existsSync38, mkdirSync as mkdirSync11, readdirSync as readdirSync7, readFileSync as readFileSync27, rmSync as rmSync5, statSync as statSync17 } from "node:fs";
42569
+ import { execFileSync as execFileSync6, spawnSync as spawnSync9 } from "node:child_process";
42570
+ import { existsSync as existsSync40, mkdirSync as mkdirSync11, readdirSync as readdirSync8, readFileSync as readFileSync28, rmSync as rmSync6, statSync as statSync17 } from "node:fs";
41685
42571
  import { createRequire as createRequire8 } from "node:module";
41686
42572
  import { homedir as homedir17 } from "node:os";
41687
- import { dirname as dirname19, isAbsolute as isAbsolute8, join as join35 } from "node:path";
42573
+ import { dirname as dirname20, isAbsolute as isAbsolute8, join as join35 } from "node:path";
41688
42574
  function depsFrom(options) {
41689
42575
  return {
41690
42576
  ...DEFAULT_DEPS5,
@@ -41775,7 +42661,7 @@ function summarize(results) {
41775
42661
  function readJsonc2(path) {
41776
42662
  try {
41777
42663
  return {
41778
- value: import_comment_json13.parse(readFileSync27(path, "utf-8"))
42664
+ value: import_comment_json13.parse(readFileSync28(path, "utf-8"))
41779
42665
  };
41780
42666
  } catch (error) {
41781
42667
  return {
@@ -41827,13 +42713,13 @@ function piPluginDirCandidates(packages, cwd) {
41827
42713
  }
41828
42714
  dirs.push(join35(agentDir, "npm", "node_modules", PACKAGE_NAME2));
41829
42715
  dirs.push(join35(cwd, ".pi", "npm", "node_modules", PACKAGE_NAME2));
41830
- return dirs.filter((dir) => existsSync38(join35(dir, "package.json")));
42716
+ return dirs.filter((dir) => existsSync40(join35(dir, "package.json")));
41831
42717
  }
41832
42718
  function projectConfigPath(cwd) {
41833
42719
  return resolveCortexKitProjectConfigPath(cwd);
41834
42720
  }
41835
42721
  function readConfigForEmbedding(path, isProjectConfig) {
41836
- if (!existsSync38(path))
42722
+ if (!existsSync40(path))
41837
42723
  return null;
41838
42724
  try {
41839
42725
  const raw = loadRawConfigFile({
@@ -41934,7 +42820,7 @@ function cacheRoots(cwd) {
41934
42820
  function findPiMagicContextCacheDirs(cwd, expectedVersion, force = false) {
41935
42821
  const found = new Map;
41936
42822
  const visit = (path, depth) => {
41937
- if (depth < 0 || !existsSync38(path))
42823
+ if (depth < 0 || !existsSync40(path))
41938
42824
  return;
41939
42825
  let stat;
41940
42826
  try {
@@ -41945,9 +42831,9 @@ function findPiMagicContextCacheDirs(cwd, expectedVersion, force = false) {
41945
42831
  if (!stat.isDirectory())
41946
42832
  return;
41947
42833
  const packageJson = join35(path, "package.json");
41948
- if (existsSync38(packageJson)) {
42834
+ if (existsSync40(packageJson)) {
41949
42835
  try {
41950
- const pkg = JSON.parse(readFileSync27(packageJson, "utf-8"));
42836
+ const pkg = JSON.parse(readFileSync28(packageJson, "utf-8"));
41951
42837
  const name = typeof pkg.name === "string" ? pkg.name : "";
41952
42838
  const version = typeof pkg.version === "string" ? pkg.version : undefined;
41953
42839
  if (name === PACKAGE_NAME2) {
@@ -41962,7 +42848,7 @@ function findPiMagicContextCacheDirs(cwd, expectedVersion, force = false) {
41962
42848
  }
41963
42849
  let entries = [];
41964
42850
  try {
41965
- entries = readdirSync7(path);
42851
+ entries = readdirSync8(path);
41966
42852
  } catch {
41967
42853
  return;
41968
42854
  }
@@ -42012,7 +42898,7 @@ async function runHealthChecks2(options) {
42012
42898
  }
42013
42899
  const settingsPath = getPiUserExtensionsPath();
42014
42900
  let packages = [];
42015
- if (!existsSync38(settingsPath)) {
42901
+ if (!existsSync40(settingsPath)) {
42016
42902
  add2(results, "fail", `Pi settings not found at ${settingsPath}`);
42017
42903
  repairPlan.addPackageEntry = true;
42018
42904
  } else {
@@ -42036,7 +42922,7 @@ async function runHealthChecks2(options) {
42036
42922
  ["user", userConfigPath, true],
42037
42923
  ["project", projectPath, false]
42038
42924
  ]) {
42039
- if (!existsSync38(path)) {
42925
+ if (!existsSync40(path)) {
42040
42926
  if (required) {
42041
42927
  add2(results, "warn", `No ${label} magic-context.jsonc found at ${path}`);
42042
42928
  if (userConfigMigrationRefused) {
@@ -42078,7 +42964,7 @@ async function runHealthChecks2(options) {
42078
42964
  const storageDir = storage.path;
42079
42965
  const dbPath = join35(storageDir, "context.db");
42080
42966
  add2(results, "info", `Shared storage: ${storageDir} (source: ${storage.source})`);
42081
- const existedBeforeOpen = existsSync38(dbPath);
42967
+ const existedBeforeOpen = existsSync40(dbPath);
42082
42968
  if (existedBeforeOpen)
42083
42969
  add2(results, "pass", `Shared context DB exists at ${dbPath}`);
42084
42970
  else
@@ -42296,7 +43182,7 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
42296
43182
  return { results, repairPlan, ...summarize(results) };
42297
43183
  }
42298
43184
  function writeDefaultMagicContextConfig(path) {
42299
- mkdirSync11(dirname19(path), { recursive: true });
43185
+ mkdirSync11(dirname20(path), { recursive: true });
42300
43186
  const config = {
42301
43187
  $schema: "https://raw.githubusercontent.com/cortexkit/magic-context/master/assets/magic-context.schema.json",
42302
43188
  ...MagicContextConfigSchema.parse({})
@@ -42318,7 +43204,7 @@ function repair2(plan, prompts) {
42318
43204
  }
42319
43205
  if (plan.writeUserConfig) {
42320
43206
  const configPath = getPiUserConfigPath();
42321
- if (!existsSync38(configPath)) {
43207
+ if (!existsSync40(configPath)) {
42322
43208
  try {
42323
43209
  writeDefaultMagicContextConfig(configPath);
42324
43210
  prompts.log.success(`Wrote default Magic Context config to ${configPath}`);
@@ -42330,7 +43216,7 @@ function repair2(plan, prompts) {
42330
43216
  }
42331
43217
  for (const path of plan.clearCachePaths) {
42332
43218
  try {
42333
- rmSync5(path, { recursive: true, force: true });
43219
+ rmSync6(path, { recursive: true, force: true });
42334
43220
  prompts.log.success(`Cleared stale Pi extension cache: ${path}`);
42335
43221
  fixed += 1;
42336
43222
  } catch (error) {
@@ -42565,12 +43451,12 @@ var init_doctor_pi = __esm(async () => {
42565
43451
  openExistingContextDatabase,
42566
43452
  now: () => new Date,
42567
43453
  execFileSync: execFileSync6,
42568
- spawnSync: spawnSync8
43454
+ spawnSync: spawnSync9
42569
43455
  };
42570
43456
  });
42571
43457
 
42572
43458
  // src/commands/doctor.ts
42573
- import { existsSync as existsSync39, rmSync as rmSync6 } from "node:fs";
43459
+ import { existsSync as existsSync41, rmSync as rmSync7 } from "node:fs";
42574
43460
  import { join as join36 } from "node:path";
42575
43461
  async function runDoctor4(options) {
42576
43462
  if (options.clear)
@@ -42668,9 +43554,16 @@ async function runClear() {
42668
43554
  }
42669
43555
  const items = [];
42670
43556
  for (const adapter of installed) {
42671
- const cache = adapter.getPluginCacheInfo();
42672
- if (cache.path && cache.exists) {
42673
- items.push({ adapter, path: cache.path, sizeBytes: cache.sizeBytes });
43557
+ for (const cache of adapter.getPluginCacheInfo()) {
43558
+ if (cache.path && cache.exists) {
43559
+ items.push({
43560
+ adapter,
43561
+ path: cache.path,
43562
+ sizeBytes: cache.sizeBytes,
43563
+ label: cache.label,
43564
+ clear: cache.clear
43565
+ });
43566
+ }
42674
43567
  }
42675
43568
  }
42676
43569
  if (items.length === 0) {
@@ -42679,7 +43572,7 @@ async function runClear() {
42679
43572
  return 0;
42680
43573
  }
42681
43574
  const picks = await selectMany("Select caches to clear:", items.map((item, idx) => ({
42682
- label: `${item.adapter.displayName}: ${formatSize(item.sizeBytes)} — ${item.path}`,
43575
+ label: `${item.adapter.displayName}${item.label ? ` (${item.label})` : ""}: ${formatSize(item.sizeBytes)} — ${item.path}`,
42683
43576
  value: String(idx)
42684
43577
  })));
42685
43578
  if (picks.length === 0) {
@@ -42702,8 +43595,16 @@ async function runClear() {
42702
43595
  const s = spinner2();
42703
43596
  s.start(`Clearing ${item.path}`);
42704
43597
  try {
42705
- if (existsSync39(item.path)) {
42706
- rmSync6(item.path, { recursive: true, force: true });
43598
+ if (item.clear) {
43599
+ const result = item.clear();
43600
+ if (!result.cleared) {
43601
+ s.stop(`Left in place: ${item.path}`);
43602
+ log2.warn(result.reason);
43603
+ failed += 1;
43604
+ continue;
43605
+ }
43606
+ } else if (existsSync41(item.path)) {
43607
+ rmSync7(item.path, { recursive: true, force: true });
42707
43608
  }
42708
43609
  s.stop(`Cleared ${item.path}`);
42709
43610
  } catch (err) {
@@ -42739,9 +43640,121 @@ var init_doctor = __esm(async () => {
42739
43640
  });
42740
43641
 
42741
43642
  // src/index.ts
42742
- init_prompts();
42743
43643
  import { createRequire as createRequire9 } from "node:module";
42744
43644
 
43645
+ // src/lib/cli-help.ts
43646
+ var HELP_FLAGS = new Set(["--help", "-h"]);
43647
+ function hasHelpFlag(args) {
43648
+ return args.some((arg) => HELP_FLAGS.has(arg));
43649
+ }
43650
+ var DOCTOR_SUBCOMMANDS_WITH_OWN_HELP = new Set([
43651
+ "migrate",
43652
+ "migrate-session",
43653
+ "repair-db"
43654
+ ]);
43655
+ var HARNESS_LINES = [
43656
+ " Harness selection:",
43657
+ " --harness opencode Target OpenCode only",
43658
+ " --harness pi Target Pi only",
43659
+ " --harness omp Target Oh My Pi (OMP) only",
43660
+ " (default: auto-detect, prompt if multiple installed)"
43661
+ ];
43662
+ var SETUP_HELP = [
43663
+ "",
43664
+ " Usage: magic-context setup [--harness opencode|pi|omp] [--dry-run]",
43665
+ "",
43666
+ " Interactive setup wizard: registers the Magic Context plugin with the",
43667
+ " selected harness and writes its Magic Context config.",
43668
+ "",
43669
+ " Options:",
43670
+ " --dry-run Preview the wizard without writing any files",
43671
+ ...HARNESS_LINES,
43672
+ ""
43673
+ ].join(`
43674
+ `);
43675
+ var DOCTOR_HELP = [
43676
+ "",
43677
+ " Usage: magic-context doctor [options]",
43678
+ " magic-context doctor <subcommand> [options]",
43679
+ "",
43680
+ " Check the installed harnesses and fix configuration issues.",
43681
+ "",
43682
+ " Options:",
43683
+ " --fix Repair safe, Magic Context-owned store rows and clear an outdated OpenCode 2 plugin cache",
43684
+ " --force Force-clear the plugin cache",
43685
+ " --clear Interactive cache cleanup picker",
43686
+ " --issue Collect diagnostics and open a GitHub issue",
43687
+ " --report <path> Write the issue diagnostics to <path> without prompting",
43688
+ " --check-v22-backfill Show v22 memory backfill status",
43689
+ " --retry-v22-backfill Retry failed v22 memory backfill rows",
43690
+ " --rekey-v22-dir-identity <path> Re-key legacy dir identity rows",
43691
+ ...HARNESS_LINES,
43692
+ "",
43693
+ " Subcommands (each accepts --help):",
43694
+ " drain-authority <project> Drain module memory/note authority back to TypeScript",
43695
+ " migrate Migrate an OpenCode session to Pi or OMP JSONL",
43696
+ " migrate-session Re-home an OpenCode session to another directory",
43697
+ " merge-identity Merge project rows between identities",
43698
+ " repair-db Back up and salvage a corrupted shared database",
43699
+ " list-hidden-sessions List Magic Context OpenCode 2 roots",
43700
+ ""
43701
+ ].join(`
43702
+ `);
43703
+ var DRAIN_AUTHORITY_HELP = [
43704
+ "",
43705
+ " Usage: magic-context doctor drain-authority <project>",
43706
+ "",
43707
+ " Drain the module's memory and note authority for <project> back to TypeScript.",
43708
+ ""
43709
+ ].join(`
43710
+ `);
43711
+ var MERGE_IDENTITY_HELP = [
43712
+ "",
43713
+ " Usage: magic-context doctor merge-identity --from <identity> --to <identity> [--dry-run] [--yes] [--db <path>]",
43714
+ "",
43715
+ " Merge every project-scoped row from one project identity into another.",
43716
+ "",
43717
+ " Options:",
43718
+ " --from <identity> Identity whose rows are moved",
43719
+ " --to <identity> Identity that receives them",
43720
+ " --dry-run Report what would change without writing",
43721
+ " --yes Confirm the write (required unless --dry-run)",
43722
+ " --db <path> Use this context.db instead of the shared one",
43723
+ ""
43724
+ ].join(`
43725
+ `);
43726
+ var LIST_HIDDEN_SESSIONS_HELP = [
43727
+ "",
43728
+ " Usage: magic-context doctor list-hidden-sessions",
43729
+ "",
43730
+ " List the hidden OpenCode 2 root sessions Magic Context reuses for its",
43731
+ " historian and dreamer runs. Read-only.",
43732
+ ""
43733
+ ].join(`
43734
+ `);
43735
+ function subcommandHelp(argv) {
43736
+ const [command, ...rest] = argv;
43737
+ if (!hasHelpFlag(rest))
43738
+ return null;
43739
+ if (command === "setup")
43740
+ return SETUP_HELP;
43741
+ if (command !== "doctor")
43742
+ return null;
43743
+ const subcommand = rest[0];
43744
+ if (subcommand === "drain-authority")
43745
+ return DRAIN_AUTHORITY_HELP;
43746
+ if (subcommand === "merge-identity")
43747
+ return MERGE_IDENTITY_HELP;
43748
+ if (subcommand === "list-hidden-sessions")
43749
+ return LIST_HIDDEN_SESSIONS_HELP;
43750
+ if (subcommand !== undefined && DOCTOR_SUBCOMMANDS_WITH_OWN_HELP.has(subcommand))
43751
+ return null;
43752
+ return DOCTOR_HELP;
43753
+ }
43754
+
43755
+ // src/index.ts
43756
+ init_prompts();
43757
+
42745
43758
  // src/lib/sqlite-preflight.ts
42746
43759
  async function probeSqliteBackend() {
42747
43760
  await init_sqlite().then(() => ({}));
@@ -42751,7 +43764,7 @@ function formatSqlitePreflightFailure(error) {
42751
43764
  return [
42752
43765
  "Magic Context doctor cannot use SQLite in this runtime.",
42753
43766
  `SQLite probe: ${detail}`,
42754
- "Remediation: install Node.js >= 24 or use a Bun build with node:sqlite.",
43767
+ "Remediation: install Node.js 22.16+ (22.x) or >= 24, or use a Bun build with node:sqlite.",
42755
43768
  "For Docker, use node:24-slim or a two-runtime image."
42756
43769
  ].join(`
42757
43770
  `);
@@ -42818,6 +43831,7 @@ function printUsage() {
42818
43831
  console.log(" (default: auto-detect, prompt if multiple installed)");
42819
43832
  console.log("");
42820
43833
  console.log(" Usage:");
43834
+ console.log(" npx @cortexkit/magic-context@latest <command> --help # help for one command");
42821
43835
  console.log(" npx @cortexkit/magic-context@latest setup");
42822
43836
  console.log(" # add --dry-run to preview the wizard without writing any files");
42823
43837
  console.log(" npx @cortexkit/magic-context@latest doctor");
@@ -42837,6 +43851,11 @@ async function main(argv = process.argv.slice(2)) {
42837
43851
  }
42838
43852
  const command = argv[0];
42839
43853
  const rest = argv.slice(1);
43854
+ const help = subcommandHelp(argv);
43855
+ if (help !== null) {
43856
+ console.log(help);
43857
+ return 0;
43858
+ }
42840
43859
  try {
42841
43860
  if (command === "setup") {
42842
43861
  await Promise.resolve().then(() => init_setup());