@cortexkit/magic-context 0.47.0 → 0.49.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 +2088 -484
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2212,8 +2212,12 @@ function flush() {
2212
2212
  let currentSize = getCurrentLogSize(logFile);
2213
2213
  const dataSize = Buffer.byteLength(data);
2214
2214
  if (currentSize > 0 && currentSize + dataSize > MAX_LOG_FILE_BYTES) {
2215
- rotateLogFile(logFile, currentSize);
2216
- currentSize = 0;
2215
+ flushesSinceSizeCheck = SIZE_CHECK_INTERVAL_FLUSHES;
2216
+ currentSize = getCurrentLogSize(logFile);
2217
+ if (currentSize > 0 && currentSize + dataSize > MAX_LOG_FILE_BYTES) {
2218
+ rotateLogFile(logFile, currentSize);
2219
+ currentSize = 0;
2220
+ }
2217
2221
  }
2218
2222
  fs.appendFileSync(logFile, data, { encoding: "utf8", mode: 384 });
2219
2223
  buffer = [];
@@ -3476,6 +3480,15 @@ function migrateLegacyExperimental(rawConfig, warnings) {
3476
3480
  return patched;
3477
3481
  }
3478
3482
 
3483
+ // ../plugin/src/config/mural-model-deprecation.ts
3484
+ function warnDeprecatedMuralModel(config, warnings) {
3485
+ const mural = config.mural;
3486
+ if (typeof mural === "object" && mural !== null && Object.hasOwn(mural, "model")) {
3487
+ warnings.push(MURAL_MODEL_DEPRECATION_WARNING);
3488
+ }
3489
+ }
3490
+ var MURAL_MODEL_DEPRECATION_WARNING = "mural.model is deprecated and ignored; compress-cues now uses the dreamer model for each harness, like every other dreamer task. To override it, set dreamer.opencode.tasks.compress-cues.model (or the pi/omp equivalent). This key no longer does anything; remove it.";
3491
+
3479
3492
  // ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/core/core.js
3480
3493
  function $constructor(name, initializer, params) {
3481
3494
  function init(inst, def) {
@@ -9315,7 +9328,7 @@ var init_magic_context = __esm(() => {
9315
9328
  allow_home_project: boolean2().default(false).describe("Allow Magic Context sessions launched from the exact canonical home directory. A non-git home uses its deterministic dir: identity; a home repository uses its git: identity. USER-LEVEL ONLY: project config is ignored. The home identity is excluded from registry seed exports, never resolves descendants by containment, and cannot join a workspace."),
9316
9329
  mural: object({
9317
9330
  enabled: boolean2().default(false),
9318
- model: string2().trim().min(1).optional().describe("Model for the compress-cues task that compresses each memory into a mural cue. The mural image itself is rendered deterministically (no author model).")
9331
+ model: unknown().optional().describe("Deprecated: ignored. compress-cues now uses the dreamer model for each harness, like every other dreamer task; override it with dreamer.opencode.tasks.compress-cues.model (or the pi/omp equivalent). Remove this key; it no longer does anything.").meta({ deprecated: true })
9319
9332
  }).default({ enabled: false }).describe("Experimental mural: a single deterministically-rendered image of project memories that did not fit the context budget. Cues are compressed per-memory by the compress-cues dreamer task."),
9320
9333
  transform_mode: _enum(["ts", "rust"]).default("ts").describe('Experimental: routes the entire Magic Context runtime for the project through the ck-mc Rust module over subc (requires user-level `subc` config); "ts" is the current TypeScript pipeline.'),
9321
9334
  auto_update: boolean2().optional().describe("Enable automatic npm self-update checks for the OpenCode plugin. Security: USER-only in config loader, so hostile project configs cannot suppress updates."),
@@ -9325,8 +9338,9 @@ var init_magic_context = __esm(() => {
9325
9338
  historian: HistorianConfigSchema.describe("Historian metadata plus independent strict OpenCode, Pi, and OMP execution blocks. Retained metadata stays at historian; model, fallback_models, variant, and thinking_level belong only in historian.opencode, historian.pi, or historian.omp."),
9326
9339
  dreamer: DreamerConfigSchema.optional().describe("Dreamer metadata and scheduling plus independent strict OpenCode, Pi, and OMP execution blocks. schedule and promotion_threshold stay at dreamer.tasks; model, fallback_models, variant, thinking_level, and timeout_minutes belong only in the matching harness block."),
9327
9340
  smart_notes: object({
9341
+ enabled: boolean2().default(true).describe("Enable smart-note authoring and condition evaluation. User-level only: tool definitions are process-wide. Changes require a host restart and rebuild each session's cached prompt once. Pending notes remain stored and unchecked; already-ready notes still surface."),
9328
9342
  retina_handoff: boolean2().default(false).describe("When true, dreamer skips smart notes whose surface conditions compiled to retina provider configs at authoring time. Default false keeps both paths active until the retina consumer is deployed.")
9329
- }).default({ retina_handoff: false }).describe("Smart-note ownership transition controls."),
9343
+ }).default({ enabled: true, retina_handoff: false }).describe("Smart-note ownership transition controls."),
9330
9344
  cache_ttl: union([string2(), object({ default: string2() }).catchall(string2())]).default("5m").describe(`How long Magic Context assumes the provider's cached prefix stays valid. This is MC's own deferral gate — it does not change the provider's actual cache lifetime. String (e.g. "5m", "1h", "30s") or per-model object ({ default: "5m", "provider/model": "1h", "provider/*": "never" }); keys resolve most-specific first (exact provider/model, bare model ID, shorter dash-prefixes, then the provider/* wildcard, then default). User and project settings, including an explicit "5m" or object default, apply on the next pass. Only built-in defaults are frozen per session, including across restarts: when unset, GPT-5.6 and later (including gpt-6*, through any provider prefix) use 30m, other models use 5m. /ctx-status shows the effective TTL and whether it comes from your config or a frozen built-in default. Editing the TTL does not itself rewrite prompt bytes; a lowered TTL rebuilds only after normal idle expiry. OpenAI documents at least 30 minutes since the latest write or reuse: https://developers.openai.com/api/docs/guides/prompt-caching (Cache lifetime and Summary of model differences). Set to "never" to mean MC never assumes expiry (for lanes kept warm externally by a cache-keep tool) — disables the idle-TTL heuristic so MC never initiates a rebuild based on elapsed time. Provider-side extended TTL is a separate request-level concern (cache_control: { ttl } in the request body).`),
9331
9345
  prompt_surface: PromptSurfaceConfigSchema.default({ default: "full" }).describe("Prompt-surface presets: default is full; models use bare model IDs, provider/model, or provider/* routing keys. Guidance and tool-description overrides are user-level only. OpenCode 1.x, Pi, and OMP register tool descriptions once per process (they follow the default preset). OpenCode 2 rewrites the five ctx_* descriptions per request from the draft model."),
9332
9346
  output_reserve: union([
@@ -9431,7 +9445,6 @@ var init_magic_context = __esm(() => {
9431
9445
  });
9432
9446
  LIVE_RELOAD_CONFIG_PATHS = [
9433
9447
  "cache_ttl",
9434
- "mural.model",
9435
9448
  "toast_duration_ms",
9436
9449
  "historian.opencode.model",
9437
9450
  "historian.opencode.fallback_models",
@@ -9612,16 +9625,6 @@ function stripEscalationAtExecutableSite(block, path, removed) {
9612
9625
  }
9613
9626
  }
9614
9627
  }
9615
- function stripNestedMuralModels(node, path, removed) {
9616
- for (const [key, value] of Object.entries(node)) {
9617
- if (key === "mural" && isPlainObject2(value) && "model" in value) {
9618
- delete value.model;
9619
- removed.push(`${path}.${key}.model`);
9620
- } else if (isPlainObject2(value)) {
9621
- stripNestedMuralModels(value, `${path}.${key}`, removed);
9622
- }
9623
- }
9624
- }
9625
9628
  function isValidPercentageThreshold(value) {
9626
9629
  return typeof value === "number" && Number.isFinite(value) && value >= 20 && value <= 90;
9627
9630
  }
@@ -9762,6 +9765,11 @@ function stripUnsafeProjectConfigFields(projectRaw) {
9762
9765
  delete storage.enforce_private_permissions;
9763
9766
  warnings.push("Ignoring storage.enforce_private_permissions from project config (security: only user-level config may opt into externally managed shared storage permissions).");
9764
9767
  }
9768
+ const smartNotes = projectRaw.smart_notes;
9769
+ if (isPlainObject2(smartNotes) && "enabled" in smartNotes) {
9770
+ delete smartNotes.enabled;
9771
+ warnings.push("Ignoring smart_notes.enabled from project config (only user-level config may change process-wide tool definitions; restart required).");
9772
+ }
9765
9773
  const promptSurface = projectRaw.prompt_surface;
9766
9774
  if (isPlainObject2(promptSurface)) {
9767
9775
  const removed = [];
@@ -9860,27 +9868,6 @@ function stripUnsafeProjectConfigFields(projectRaw) {
9860
9868
  delete dreamer.runner;
9861
9869
  warnings.push("Ignoring dreamer.runner from project config (security: which process and provider account run dreamer completions is a user-level setting).");
9862
9870
  }
9863
- const mural = projectRaw.mural;
9864
- if (isPlainObject2(mural) && "model" in mural) {
9865
- delete mural.model;
9866
- warnings.push("Ignoring mural.model from project config (security: the mural cue-compressor model is a user-level setting; a repository cannot choose where project memory is sent).");
9867
- }
9868
- const experimental = projectRaw.experimental;
9869
- const legacyMural = isPlainObject2(experimental) ? experimental.mural : undefined;
9870
- if (isPlainObject2(legacyMural) && "model" in legacyMural) {
9871
- delete legacyMural.model;
9872
- warnings.push("Ignoring experimental.mural.model from project config (security: the mural cue-compressor model is a user-level setting; use user-level mural.model).");
9873
- }
9874
- const nestedMuralRemoved = [];
9875
- for (const agentKey of HIDDEN_AGENT_KEYS) {
9876
- const block = projectRaw[agentKey];
9877
- if (!isPlainObject2(block))
9878
- continue;
9879
- stripNestedMuralModels(block, agentKey, nestedMuralRemoved);
9880
- }
9881
- if (nestedMuralRemoved.length > 0) {
9882
- warnings.push(`Ignoring ${nestedMuralRemoved.join(", ")} from project config (security: the mural cue-compressor model is a user-level setting; a repository cannot choose where project memory is sent).`);
9883
- }
9884
9871
  return warnings;
9885
9872
  }
9886
9873
  function constrainProjectThresholdOverrides(args) {
@@ -10929,6 +10916,7 @@ function parsePluginConfig(rawConfig, recoveredTopLevelKeys = []) {
10929
10916
  }
10930
10917
  preMigrationWarnings.push(...misplacedAgentModelWarnings(rawConfig));
10931
10918
  const migratedExperimental = migrateLegacyExperimental(configWithoutRemovedAgent, preMigrationWarnings);
10919
+ warnDeprecatedMuralModel(migratedExperimental, preMigrationWarnings);
10932
10920
  const migratedDreamer = migrateDreamerV2(migratedExperimental, preMigrationWarnings);
10933
10921
  const migrated = migrateLegacyAgentEnabledInMemory(migratedDreamer, preMigrationWarnings);
10934
10922
  const parsed = MagicContextConfigSchema.safeParse(migrated);
@@ -12856,17 +12844,23 @@ __export(exports_sqlite, {
12856
12844
  detectSqliteRuntime: () => detectSqliteRuntime,
12857
12845
  getSqliteDatabasePath: () => getSqliteDatabasePath,
12858
12846
  getSqliteMemoryStats: () => getSqliteMemoryStats,
12847
+ getTagIdentityWriteGeneration: () => getTagIdentityWriteGeneration,
12859
12848
  guardSqliteTransformPass: () => guardSqliteTransformPass,
12849
+ indexCoordinationInput: () => indexCoordinationInput,
12860
12850
  isTransientSqliteError: () => isTransientSqliteError,
12861
12851
  loadSqliteModule: () => loadSqliteModule,
12852
+ mayChangeTagIdentity: () => mayChangeTagIdentity,
12862
12853
  prepareCachedStatement: () => prepareCachedStatement,
12863
12854
  registerSlowWriteReporter: () => registerSlowWriteReporter,
12864
12855
  registerSqliteDiagnosticSink: () => registerSqliteDiagnosticSink,
12856
+ releaseIndexOwner: () => releaseIndexOwner,
12865
12857
  withAsyncPrivilegedWriter: () => withAsyncPrivilegedWriter,
12858
+ withIndexChunk: () => withIndexChunk,
12866
12859
  withPrivilegedWriter: () => withPrivilegedWriter,
12867
12860
  withSqliteBackgroundWriter: () => withSqliteBackgroundWriter,
12868
12861
  withSqliteTransformPass: () => withSqliteTransformPass,
12869
- withoutSqliteTransformPass: () => withoutSqliteTransformPass
12862
+ withoutSqliteTransformPass: () => withoutSqliteTransformPass,
12863
+ yieldIndexChunk: () => yieldIndexChunk
12870
12864
  });
12871
12865
  import { AsyncLocalStorage } from "node:async_hooks";
12872
12866
  import { statSync as statSync4 } from "node:fs";
@@ -12900,10 +12894,31 @@ function isModuleNotFoundError(error, specifier) {
12900
12894
  return false;
12901
12895
  return code === "ERR_MODULE_NOT_FOUND" || code === "ERR_UNKNOWN_BUILTIN_MODULE" || code === "MODULE_NOT_FOUND" || name === "ResolveMessage" || details.includes("module not found") || details.includes("cannot find module") || details.includes("cannot find package") || details.includes("no such built-in module");
12902
12896
  }
12897
+ function isNodeSqliteExperimentalWarning(args) {
12898
+ const [warning, options] = args;
12899
+ const message = warning instanceof Error ? warning.message : warning;
12900
+ const type = typeof options === "string" ? options : options && typeof options === "object" ? options.type : undefined;
12901
+ return type === "ExperimentalWarning" && typeof message === "string" && message.startsWith("SQLite is an experimental feature");
12902
+ }
12903
+ async function importSqliteModuleForRuntime(runtime, specifier, importer) {
12904
+ if (runtime !== "Node.js")
12905
+ return importer(specifier);
12906
+ const originalEmitWarning = process.emitWarning;
12907
+ process.emitWarning = function(...args) {
12908
+ if (isNodeSqliteExperimentalWarning(args))
12909
+ return;
12910
+ Reflect.apply(originalEmitWarning, this, args);
12911
+ };
12912
+ try {
12913
+ return await importer(specifier);
12914
+ } finally {
12915
+ process.emitWarning = originalEmitWarning;
12916
+ }
12917
+ }
12903
12918
  async function loadSqliteModule(runtime = detectSqliteRuntime(), importer = importSqliteModule) {
12904
12919
  const specifier = runtime === "Bun" ? bunSpec : nodeSpec;
12905
12920
  try {
12906
- return await importer(specifier);
12921
+ return await importSqliteModuleForRuntime(runtime, specifier, importer);
12907
12922
  } catch (error) {
12908
12923
  if (isModuleNotFoundError(error, specifier)) {
12909
12924
  throw new SqliteRuntimeUnavailableError(runtime, specifier, error);
@@ -12911,6 +12926,67 @@ async function loadSqliteModule(runtime = detectSqliteRuntime(), importer = impo
12911
12926
  throw error;
12912
12927
  }
12913
12928
  }
12929
+ function mayChangeTagIdentity(sql) {
12930
+ if (!/\btags\b/i.test(sql) || /^\s*(?:SELECT|EXPLAIN|PRAGMA)\b/i.test(sql))
12931
+ return false;
12932
+ if (/^\s*WITH\b/i.test(sql) && !/\b(?:INSERT|UPDATE|DELETE|REPLACE)\b/i.test(sql))
12933
+ return false;
12934
+ if (/;\s*\S/.test(sql) || /\bREPLACE\b/i.test(sql) || /\bON\s+CONFLICT\b/i.test(sql))
12935
+ return true;
12936
+ const insert = /^\s*INSERT\s+INTO\s+(?:main\.)?["`]?tags["`]?\s*\(([^)]*)\)/i.exec(sql);
12937
+ if (insert) {
12938
+ const columns = (insert[1] ?? "").split(",").map((column) => column.trim().replace(/["`[\]]/g, "").toLowerCase());
12939
+ return columns.includes("id") || columns.includes("rowid");
12940
+ }
12941
+ const update = /^\s*UPDATE\s+(?:main\.)?["`]?tags["`]?\s+SET\s+([\s\S]*?)\bWHERE\b/i.exec(sql);
12942
+ if (update && !/\bSELECT\b/i.test(sql)) {
12943
+ const assignments = topLevelCommaSegments(update[1] ?? "");
12944
+ if (!assignments)
12945
+ return true;
12946
+ return !assignments.every((assignment) => {
12947
+ const column = /^\s*["`]?(\w+)["`]?\s*=(?!=)/.exec(assignment)?.[1];
12948
+ return column !== undefined && TAG_COLUMNS_OUTSIDE_IDENTITY.has(column.toLowerCase());
12949
+ });
12950
+ }
12951
+ return true;
12952
+ }
12953
+ function topLevelCommaSegments(text) {
12954
+ const segments = [];
12955
+ let depth = 0;
12956
+ let quote = null;
12957
+ let start = 0;
12958
+ for (let index = 0;index < text.length; index += 1) {
12959
+ const char = text[index];
12960
+ if (quote) {
12961
+ if (char === quote) {
12962
+ if (text[index + 1] === quote)
12963
+ index += 1;
12964
+ else
12965
+ quote = null;
12966
+ }
12967
+ continue;
12968
+ }
12969
+ if (char === "'" || char === '"' || char === "`")
12970
+ quote = char;
12971
+ else if (char === "(")
12972
+ depth += 1;
12973
+ else if (char === ")") {
12974
+ depth -= 1;
12975
+ if (depth < 0)
12976
+ return null;
12977
+ } else if (char === "," && depth === 0) {
12978
+ segments.push(text.slice(start, index));
12979
+ start = index + 1;
12980
+ }
12981
+ }
12982
+ if (quote || depth !== 0)
12983
+ return null;
12984
+ segments.push(text.slice(start));
12985
+ return segments;
12986
+ }
12987
+ function getTagIdentityWriteGeneration() {
12988
+ return tagIdentityWriteGeneration;
12989
+ }
12914
12990
  function installTransactionRouting(db, readonly) {
12915
12991
  const nativeExec = db.exec.bind(db);
12916
12992
  Object.defineProperty(db, "exec", {
@@ -12929,8 +13005,14 @@ function installTransactionRouting(db, readonly) {
12929
13005
  }
12930
13006
  if (WRITER_TRANSACTION_END.test(sql) && openWriterTransactions.has(db))
12931
13007
  return endWriterTransaction(db, () => nativeExec(sql), sql);
12932
- if (!/^\s*(?:SELECT|EXPLAIN|PRAGMA|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\b/i.test(sql))
12933
- return withAutocommitTimeout(db, () => nativeExec(sql));
13008
+ if (!/^\s*(?:SELECT|EXPLAIN|PRAGMA|COMMIT|END|ROLLBACK|SAVEPOINT|RELEASE)\b/i.test(sql)) {
13009
+ try {
13010
+ return withAutocommitTimeout(db, () => nativeExec(sql));
13011
+ } finally {
13012
+ if (mayChangeTagIdentity(sql))
13013
+ tagIdentityWriteGeneration += 1;
13014
+ }
13015
+ }
12934
13016
  return nativeExec(sql);
12935
13017
  }
12936
13018
  });
@@ -12941,13 +13023,23 @@ function installTransactionRouting(db, readonly) {
12941
13023
  value: (sql) => {
12942
13024
  const statement = nativePrepare(sql);
12943
13025
  if (!readonly && !/^\s*(?:SELECT|EXPLAIN|PRAGMA)\b/i.test(sql)) {
13026
+ const changesTagIdentity = mayChangeTagIdentity(sql);
12944
13027
  for (const method of ["run", "get", "all"]) {
12945
13028
  const execute = statement[method].bind(statement);
12946
13029
  Object.defineProperty(statement, method, {
12947
13030
  configurable: true,
12948
- value: (...args) => withAutocommitTimeout(db, () => execute(...args))
13031
+ value: (...args) => {
13032
+ try {
13033
+ return withAutocommitTimeout(db, () => execute(...args));
13034
+ } finally {
13035
+ if (changesTagIdentity)
13036
+ tagIdentityWriteGeneration += 1;
13037
+ }
13038
+ }
12949
13039
  });
12950
13040
  }
13041
+ if (changesTagIdentity)
13042
+ wrapRemainingTagWriteForms(statement);
12951
13043
  }
12952
13044
  return statement;
12953
13045
  }
@@ -12995,6 +13087,35 @@ function installTransactionRouting(db, readonly) {
12995
13087
  }
12996
13088
  });
12997
13089
  }
13090
+ function wrapRemainingTagWriteForms(statement) {
13091
+ if (typeof statement.iterate === "function") {
13092
+ const iterate = statement.iterate.bind(statement);
13093
+ Object.defineProperty(statement, "iterate", {
13094
+ configurable: true,
13095
+ value: function* (...args) {
13096
+ tagIdentityWriteGeneration += 1;
13097
+ try {
13098
+ yield* iterate(...args);
13099
+ } finally {
13100
+ tagIdentityWriteGeneration += 1;
13101
+ }
13102
+ }
13103
+ });
13104
+ }
13105
+ if (typeof statement.values === "function") {
13106
+ const values = statement.values.bind(statement);
13107
+ Object.defineProperty(statement, "values", {
13108
+ configurable: true,
13109
+ value: (...args) => {
13110
+ try {
13111
+ return values(...args);
13112
+ } finally {
13113
+ tagIdentityWriteGeneration += 1;
13114
+ }
13115
+ }
13116
+ });
13117
+ }
13118
+ }
12998
13119
  function trackSqliteConnection(db, filename, options) {
12999
13120
  const originalClose = db.close.bind(db);
13000
13121
  const sequence = nextSqliteConnectionSequence++;
@@ -13054,6 +13175,10 @@ function getSqliteDatabasePath(db) {
13054
13175
  return connection.filename === ":memory:" || connection.filename === "<buffer>" ? null : connection.filename;
13055
13176
  }
13056
13177
  }
13178
+ const native = db;
13179
+ const path = native.filename ?? native.name;
13180
+ if (typeof path === "string" && path !== ":memory:" && path !== "")
13181
+ return path;
13057
13182
  return null;
13058
13183
  }
13059
13184
  function pragmaValue(db, name) {
@@ -13135,6 +13260,15 @@ function getSqliteMemoryStats() {
13135
13260
  connections
13136
13261
  };
13137
13262
  }
13263
+ function closeReleasingStatements(close, args) {
13264
+ if (args[0] === true)
13265
+ return close(true);
13266
+ try {
13267
+ return close(true);
13268
+ } catch {
13269
+ return close(...args);
13270
+ }
13271
+ }
13138
13272
  function ownPreparedStatement(db, statement) {
13139
13273
  const finalizable = statement;
13140
13274
  if (typeof finalizable.finalize !== "function")
@@ -13158,7 +13292,7 @@ function ownPreparedStatement(db, statement) {
13158
13292
  }
13159
13293
  }
13160
13294
  references.clear();
13161
- const result = close(...args);
13295
+ const result = closeReleasingStatements(close, args);
13162
13296
  if (failure)
13163
13297
  throw failure;
13164
13298
  return result;
@@ -13188,22 +13322,29 @@ function withSqliteTransformPass(operation) {
13188
13322
  const existing = transformPassScope.getStore();
13189
13323
  if (existing?.active)
13190
13324
  return operation();
13191
- const lease = { active: true, remainingWaitMs: FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS };
13192
- return transformPassScope.run(lease, () => {
13193
- try {
13194
- const result = operation();
13195
- if (result && typeof result.then === "function") {
13196
- return Promise.resolve(result).finally(() => {
13197
- lease.active = false;
13198
- });
13325
+ const reservation = reserveForegroundSqliteTurn();
13326
+ const run = (release) => {
13327
+ const lease = { active: true, remainingWaitMs: FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS };
13328
+ return transformPassScope.run(lease, () => {
13329
+ try {
13330
+ const result = operation();
13331
+ if (result && typeof result.then === "function") {
13332
+ return Promise.resolve(result).finally(() => {
13333
+ lease.active = false;
13334
+ release();
13335
+ });
13336
+ }
13337
+ lease.active = false;
13338
+ release();
13339
+ return result;
13340
+ } catch (error) {
13341
+ lease.active = false;
13342
+ release();
13343
+ throw error;
13199
13344
  }
13200
- lease.active = false;
13201
- return result;
13202
- } catch (error) {
13203
- lease.active = false;
13204
- throw error;
13205
- }
13206
- });
13345
+ });
13346
+ };
13347
+ return typeof reservation === "function" ? run(reservation) : reservation.then(run);
13207
13348
  }
13208
13349
  function withoutSqliteTransformPass(operation) {
13209
13350
  return transformPassScope.run(undefined, operation);
@@ -13291,18 +13432,20 @@ function acquireShort(db, acquire, site) {
13291
13432
  }
13292
13433
  function endWriterTransaction(db, end, sql) {
13293
13434
  let ended = false;
13435
+ const endStarted = performance.now();
13294
13436
  try {
13295
13437
  const result = end();
13296
13438
  ended = true;
13297
13439
  return result;
13298
13440
  } finally {
13299
13441
  const open = openWriterTransactions.get(db);
13442
+ const endMs = performance.now() - endStarted;
13300
13443
  if (open && !isInTransaction(db)) {
13301
13444
  openWriterTransactions.delete(db);
13302
13445
  const holdMs = performance.now() - open.acquiredAt;
13303
13446
  if (open.acquireMs >= SLOW_WRITER_LOG_MS || holdMs >= SLOW_WRITER_LOG_MS) {
13304
13447
  const outcome = /^\s*ROLLBACK/i.test(sql) ? "rolled_back" : ended ? "committed" : "failed";
13305
- reportSqliteDiagnostic(`sqlite writer site=${open.site} lane=${open.lane} acquire_ms=${Math.round(open.acquireMs)} hold_ms=${Math.round(holdMs)} attempts=${open.attempts} outcome=${outcome}`);
13448
+ reportSqliteDiagnostic(`sqlite writer site=${open.site} lane=${open.lane} acquire_ms=${Math.round(open.acquireMs)} hold_ms=${Math.round(holdMs)} end_ms=${Math.round(endMs)} attempts=${open.attempts} outcome=${outcome}`);
13306
13449
  }
13307
13450
  }
13308
13451
  }
@@ -13442,7 +13585,55 @@ function withPrivilegedWriter(db, operation) {
13442
13585
  throw error;
13443
13586
  }
13444
13587
  }
13445
- var reportSlowPrivilegedWrite, sqliteDiagnosticSink, bunSpec, nodeSpec, SqliteRuntimeUnavailableError, detectedRuntime, isBun, sqliteModule, DatabaseImpl, trackedSqliteConnections, nextSqliteConnectionSequence = 1, TrackedDatabase, Database, statementOwners, FIRST_PRUNE_AT = 256, privilegeDepth, transformPassScope, admissionScope, backgroundWriterScope, SqliteAcquisitionBusyError, SHORT_BUSY_TIMEOUT_MS = 25, FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS = 250, FOREGROUND_ACQUISITION_BUDGET_MS = 16500, SLOW_WRITER_LOG_MS = 250, WRITER_TRANSACTION_END, writerAcquisitionScope, openWriterTransactions;
13588
+ function indexCoordinationInput() {
13589
+ return {
13590
+ indexCoordination: indexCoordinationBuffer,
13591
+ indexOwner: Atomics.add(indexCoordinationState, 2, 1) + 1
13592
+ };
13593
+ }
13594
+ function reserveForegroundSqliteTurn() {
13595
+ Atomics.add(indexCoordinationState, 0, 1);
13596
+ const release = () => {
13597
+ Atomics.sub(indexCoordinationState, 0, 1);
13598
+ Atomics.notify(indexCoordinationState, 0);
13599
+ };
13600
+ if (Atomics.load(indexCoordinationState, 1) === 0)
13601
+ return release;
13602
+ return (async () => {
13603
+ while (Atomics.load(indexCoordinationState, 1) !== 0)
13604
+ await yieldIndexTurn();
13605
+ return release;
13606
+ })();
13607
+ }
13608
+ function releaseIndexOwner(input) {
13609
+ if (!input.indexCoordination || !input.indexOwner)
13610
+ return;
13611
+ const shared = new Int32Array(input.indexCoordination);
13612
+ Atomics.compareExchange(shared, 1, input.indexOwner, 0);
13613
+ Atomics.notify(shared, 1);
13614
+ }
13615
+ async function withIndexChunk(input, operation) {
13616
+ if (!input.indexCoordination || !input.indexOwner)
13617
+ return operation();
13618
+ const shared = new Int32Array(input.indexCoordination);
13619
+ for (;; ) {
13620
+ if (Atomics.load(shared, 0) === 0 && Atomics.compareExchange(shared, 1, 0, input.indexOwner) === 0) {
13621
+ if (Atomics.load(shared, 0) === 0)
13622
+ break;
13623
+ releaseIndexOwner(input);
13624
+ }
13625
+ await yieldIndexTurn();
13626
+ }
13627
+ try {
13628
+ return await operation();
13629
+ } finally {
13630
+ releaseIndexOwner(input);
13631
+ }
13632
+ }
13633
+ async function yieldIndexChunk() {
13634
+ await yieldIndexTurn();
13635
+ }
13636
+ var reportSlowPrivilegedWrite, sqliteDiagnosticSink, bunSpec, nodeSpec, SqliteRuntimeUnavailableError, detectedRuntime, isBun, sqliteModule, DatabaseImpl, trackedSqliteConnections, nextSqliteConnectionSequence = 1, TAG_COLUMNS_OUTSIDE_IDENTITY, tagIdentityWriteGeneration = 0, TrackedDatabase, Database, statementOwners, FIRST_PRUNE_AT = 256, privilegeDepth, transformPassScope, admissionScope, backgroundWriterScope, SqliteAcquisitionBusyError, SHORT_BUSY_TIMEOUT_MS = 25, FOREGROUND_IN_PASS_BUSY_TIMEOUT_MS = 250, FOREGROUND_ACQUISITION_BUDGET_MS = 16500, SLOW_WRITER_LOG_MS = 250, WRITER_TRANSACTION_END, writerAcquisitionScope, openWriterTransactions, indexCoordinationKey, indexCoordinationHeap, indexCoordinationBuffer, indexCoordinationState, yieldIndexTurn = () => new Promise((resolve) => setTimeout(resolve, 1));
13446
13637
  var init_sqlite = __esm(async () => {
13447
13638
  bunSpec = "bun:" + "sqlite";
13448
13639
  nodeSpec = "node:" + "sqlite";
@@ -13462,6 +13653,19 @@ var init_sqlite = __esm(async () => {
13462
13653
  sqliteModule = await loadSqliteModule(detectedRuntime);
13463
13654
  DatabaseImpl = isBun ? sqliteModule.Database : buildNodeSqliteDatabaseClass(sqliteModule.DatabaseSync);
13464
13655
  trackedSqliteConnections = new Map;
13656
+ TAG_COLUMNS_OUTSIDE_IDENTITY = new Set([
13657
+ "status",
13658
+ "drop_mode",
13659
+ "byte_size",
13660
+ "input_byte_size",
13661
+ "reasoning_byte_size",
13662
+ "token_count",
13663
+ "input_token_count",
13664
+ "caveman_depth",
13665
+ "entry_fingerprint",
13666
+ "tool_name",
13667
+ "harness"
13668
+ ]);
13465
13669
  TrackedDatabase = new Proxy(DatabaseImpl, {
13466
13670
  construct(target, args) {
13467
13671
  const db = Reflect.construct(target, args, target);
@@ -13486,6 +13690,11 @@ var init_sqlite = __esm(async () => {
13486
13690
  WRITER_TRANSACTION_END = /^\s*(?:COMMIT|END|ROLLBACK)(?:\s+TRANSACTION)?\s*;?\s*$/i;
13487
13691
  writerAcquisitionScope = new AsyncLocalStorage;
13488
13692
  openWriterTransactions = new WeakMap;
13693
+ indexCoordinationKey = Symbol.for("magic-context.index-foreground-coordination.v1");
13694
+ indexCoordinationHeap = globalThis;
13695
+ indexCoordinationBuffer = indexCoordinationHeap[indexCoordinationKey] ?? new SharedArrayBuffer(3 * Int32Array.BYTES_PER_ELEMENT);
13696
+ indexCoordinationHeap[indexCoordinationKey] = indexCoordinationBuffer;
13697
+ indexCoordinationState = new Int32Array(indexCoordinationBuffer);
13489
13698
  });
13490
13699
 
13491
13700
  // src/lib/docs-proposal-gitignore.ts
@@ -22933,6 +23142,22 @@ function readConfiguredOpenCodePluginSpec(config) {
22933
23142
  }
22934
23143
  return "latest";
22935
23144
  }
23145
+ function readReferencedOpenCodePluginSpecs(config, host) {
23146
+ const specs = new Set;
23147
+ for (const { key, entry } of readPluginEntries(config)) {
23148
+ if (host === "opencode1" && key !== "plugin")
23149
+ continue;
23150
+ const specifier = pluginEntrySpecifier(entry);
23151
+ const spec = specifier === undefined ? undefined : openCodeV2PluginSpecOf(specifier);
23152
+ if (spec !== undefined)
23153
+ specs.add(spec);
23154
+ }
23155
+ return specs;
23156
+ }
23157
+ function describeUnusedOpenCodePluginCache(input) {
23158
+ const why = input.loadedFrom ? `the config loads Magic Context from ${input.loadedFrom}` : `no ${input.host} plugin entry in the config references it`;
23159
+ return `${input.host} plugin cache: unused cached copy of ${OPENCODE_PLUGIN_NAME}@${input.spec} (cached: ${input.cached ?? "unreadable"}); ${input.host} never loads it because ${why}. Informational, nothing breaks; safe to delete: ${input.paths.join(", ")}`;
23160
+ }
22936
23161
  function pluginEntrySpecifier(entry) {
22937
23162
  if (typeof entry === "string")
22938
23163
  return entry;
@@ -23158,6 +23383,9 @@ function checkOpenCodeV2PluginCache(options, deps = {}) {
23158
23383
  const distTag = options.distTag && options.distTag !== "latest" ? options.distTag : undefined;
23159
23384
  const slot = deps.slot ?? getOpenCodeV2PluginCacheSlot(undefined, distTag ?? "latest");
23160
23385
  const tagged = distTag ? { distTag } : {};
23386
+ if (options.referencedSpecs !== undefined && !options.referencedSpecs.has(distTag ?? "latest")) {
23387
+ return existsSync13(slot) ? { action: "unused", slot, cached: readOpenCodeV2CachedPluginVersion(slot), ...tagged } : { action: "not_found", slot, unused: true, ...tagged };
23388
+ }
23161
23389
  if (!existsSync13(slot))
23162
23390
  return { action: "not_found", slot, ...tagged };
23163
23391
  const cached = readOpenCodeV2CachedPluginVersion(slot);
@@ -23197,7 +23425,7 @@ function reportOpenCodeV2PluginCache(result, report, options) {
23197
23425
  const slotName = result.distTag ? ` @${result.distTag}` : "";
23198
23426
  switch (result.action) {
23199
23427
  case "not_found":
23200
- if (options.reportMissing) {
23428
+ if (options.reportMissing && !result.unused) {
23201
23429
  report.pass(`OpenCode 2 plugin cache has no Magic Context${slotName} install yet (OpenCode installs it on next start)`);
23202
23430
  }
23203
23431
  return { fixed: false, issue: false };
@@ -23225,6 +23453,15 @@ function reportOpenCodeV2PluginCache(result, report, options) {
23225
23453
  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`);
23226
23454
  report.info(` ${result.slot}`);
23227
23455
  return { fixed: true, issue: false };
23456
+ case "unused":
23457
+ report.info(describeUnusedOpenCodePluginCache({
23458
+ host: "OpenCode 2",
23459
+ spec: tag,
23460
+ cached: result.cached,
23461
+ paths: [result.slot],
23462
+ loadedFrom: options.loadedFrom
23463
+ }));
23464
+ return { fixed: false, issue: false };
23228
23465
  case "error":
23229
23466
  report.warn(`Could not clear the OpenCode 2 plugin cache: ${result.error}`);
23230
23467
  report.info(` Manually delete: ${result.slot}`);
@@ -28760,6 +28997,90 @@ var init_migrations = __esm(async () => {
28760
28997
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
28761
28998
  });
28762
28999
 
29000
+ // ../plugin/src/features/magic-context/storage-tag-identity-revision.ts
29001
+ function installTagIdentityRevisionTrigger(db) {
29002
+ db.exec(`DROP TRIGGER IF EXISTS temp.mc_tag_identity_revision_au;
29003
+ DROP TRIGGER IF EXISTS temp.mc_tag_revision_bi;
29004
+ DROP TRIGGER IF EXISTS temp.mc_tag_revision_ai;
29005
+ DROP TRIGGER IF EXISTS temp.mc_tag_revision_au;
29006
+ DROP TRIGGER IF EXISTS temp.mc_tag_revision_ad;
29007
+ DROP TRIGGER IF EXISTS temp.mc_tag_revision_seed_ai;`);
29008
+ db.exec(`CREATE TEMP TRIGGER mc_tag_revision_seed_ai AFTER INSERT ON main.schema_migrations_meta
29009
+ WHEN INSTR(NEW.value, ':') = 0 AND (
29010
+ SUBSTR(NEW.key, 1, ${TAG_REVISION_PREFIX.length}) = '${TAG_REVISION_PREFIX}'
29011
+ OR SUBSTR(NEW.key, 1, ${TAG_IDENTITY_REVISION_PREFIX.length}) = '${TAG_IDENTITY_REVISION_PREFIX}')
29012
+ BEGIN
29013
+ INSERT INTO schema_migrations_meta (key, value) VALUES ('${TAG_REVISION_INCARNATION_KEY}', '1')
29014
+ ON CONFLICT(key) DO UPDATE SET value = CAST(CASE
29015
+ WHEN CAST(value AS INTEGER) < 9223372036854775807 THEN CAST(value AS INTEGER) + 1
29016
+ ELSE NULL END AS TEXT);
29017
+ UPDATE schema_migrations_meta SET value =
29018
+ (SELECT value FROM schema_migrations_meta WHERE key = '${TAG_REVISION_INCARNATION_KEY}')
29019
+ || ':' || CAST(NEW.value AS TEXT) WHERE key = NEW.key;
29020
+ INSERT INTO schema_migrations_meta (key, value)
29021
+ SELECT '${TAG_REVISION_INCARNATION_KEY}', '1'
29022
+ WHERE SUBSTR(NEW.key, 1, ${TAG_REVISION_PREFIX.length}) = '${TAG_REVISION_PREFIX}'
29023
+ AND NOT EXISTS (SELECT 1 FROM schema_migrations_meta
29024
+ WHERE key = '${TAG_IDENTITY_REVISION_PREFIX}' || SUBSTR(NEW.key, ${TAG_REVISION_PREFIX.length + 1}))
29025
+ ON CONFLICT(key) DO UPDATE SET value = CAST(CASE
29026
+ WHEN CAST(value AS INTEGER) < 9223372036854775807 THEN CAST(value AS INTEGER) + 1
29027
+ ELSE NULL END AS TEXT);
29028
+ INSERT INTO schema_migrations_meta (key, value)
29029
+ SELECT '${TAG_IDENTITY_REVISION_PREFIX}' || SUBSTR(NEW.key, ${TAG_REVISION_PREFIX.length + 1}),
29030
+ (SELECT value FROM schema_migrations_meta WHERE key = '${TAG_REVISION_INCARNATION_KEY}') || ':0'
29031
+ WHERE SUBSTR(NEW.key, 1, ${TAG_REVISION_PREFIX.length}) = '${TAG_REVISION_PREFIX}'
29032
+ AND NOT EXISTS (SELECT 1 FROM schema_migrations_meta
29033
+ WHERE key = '${TAG_IDENTITY_REVISION_PREFIX}' || SUBSTR(NEW.key, ${TAG_REVISION_PREFIX.length + 1}));
29034
+ END;`);
29035
+ db.exec(TRIGGER_SQL);
29036
+ db.exec(`CREATE TEMP TRIGGER IF NOT EXISTS mc_tag_revision_bi BEFORE INSERT ON main.tags BEGIN
29037
+ ${BUMP_INSERT_CONFLICTS(TAG_REVISION_PREFIX)};
29038
+ ${BUMP_INSERT_CONFLICTS(TAG_IDENTITY_REVISION_PREFIX)};
29039
+ UPDATE session_meta SET tags_version = tags_version + 1
29040
+ WHERE session_id IN (SELECT session_id FROM (${TAG_INSERT_CONFLICT_ROWS}));
29041
+ END;`);
29042
+ db.exec(`CREATE TEMP TRIGGER IF NOT EXISTS mc_tag_revision_ai AFTER INSERT ON main.tags BEGIN
29043
+ ${BUMP("NEW.session_id", TAG_REVISION_PREFIX)};
29044
+ END;
29045
+ CREATE TEMP TRIGGER IF NOT EXISTS mc_tag_revision_au AFTER UPDATE ON main.tags BEGIN
29046
+ ${BUMP("OLD.session_id", TAG_REVISION_PREFIX)};
29047
+ INSERT INTO schema_migrations_meta (key, value)
29048
+ SELECT '${TAG_REVISION_PREFIX}' || NEW.session_id, '1'
29049
+ WHERE NEW.session_id IS NOT OLD.session_id
29050
+ ON CONFLICT(key) DO UPDATE SET value = ${ADVANCE()};
29051
+ END;
29052
+ CREATE TEMP TRIGGER IF NOT EXISTS mc_tag_revision_ad AFTER DELETE ON main.tags BEGIN
29053
+ ${BUMP("OLD.session_id", TAG_REVISION_PREFIX)};
29054
+ END;`);
29055
+ }
29056
+ var TAG_IDENTITY_REVISION_PREFIX = "tag_identity_revision:", TAG_REVISION_PREFIX = "tag_revision:", TAG_REVISION_INCARNATION_KEY = "tag_revision_incarnation", tagRevisionIncarnationSql = (value) => `CASE WHEN INSTR(${value}, ':') > 0 THEN SUBSTR(${value}, 1, INSTR(${value}, ':') - 1) ELSE '0' END`, tagRevisionCountSql = (value) => `CAST(CASE WHEN INSTR(${value}, ':') > 0 THEN SUBSTR(${value}, INSTR(${value}, ':') + 1) ELSE ${value} END AS INTEGER)`, ADVANCE = (delta = "1") => `CASE
29057
+ WHEN ${tagRevisionCountSql("value")} <= 9223372036854775807 - ${delta}
29058
+ THEN (${tagRevisionIncarnationSql("value")}) || ':' || CAST(${tagRevisionCountSql("value")} + ${delta} AS TEXT)
29059
+ ELSE NULL END`, BUMP = (session, prefix = TAG_IDENTITY_REVISION_PREFIX) => `INSERT INTO schema_migrations_meta (key, value)
29060
+ VALUES ('${prefix}' || ${session}, '1')
29061
+ ON CONFLICT(key) DO UPDATE SET value = ${ADVANCE()}`, TAG_INSERT_CONFLICT_ROWS = `SELECT id, session_id, tag_number FROM tags WHERE id = NEW.id
29062
+ UNION SELECT id, session_id, tag_number FROM tags WHERE session_id = NEW.session_id AND tag_number = NEW.tag_number
29063
+ UNION SELECT id, session_id, tag_number FROM tags
29064
+ WHERE NEW.type = 'tool' AND NEW.tool_owner_message_id IS NOT NULL
29065
+ AND type = 'tool' AND tool_owner_message_id IS NOT NULL
29066
+ AND session_id = NEW.session_id AND message_id = NEW.message_id
29067
+ AND tool_owner_message_id = NEW.tool_owner_message_id`, BUMP_INSERT_CONFLICTS = (prefix) => `INSERT INTO schema_migrations_meta (key, value)
29068
+ SELECT '${prefix}' || session_id, CAST(COUNT(*) AS TEXT)
29069
+ FROM (${TAG_INSERT_CONFLICT_ROWS}) GROUP BY session_id
29070
+ ON CONFLICT(key) DO UPDATE SET value = ${ADVANCE("CAST(excluded.value AS INTEGER)")}`, TRIGGER_SQL;
29071
+ var init_storage_tag_identity_revision = __esm(() => {
29072
+ TRIGGER_SQL = `CREATE TEMP TRIGGER IF NOT EXISTS mc_tag_identity_revision_au
29073
+ AFTER UPDATE OF id, session_id, message_id, tag_number, type, tool_owner_message_id, reasoning_token_count
29074
+ ON main.tags
29075
+ BEGIN
29076
+ ${BUMP("OLD.session_id")};
29077
+ INSERT INTO schema_migrations_meta (key, value)
29078
+ SELECT '${TAG_IDENTITY_REVISION_PREFIX}' || NEW.session_id, '1'
29079
+ WHERE NEW.session_id IS NOT OLD.session_id
29080
+ ON CONFLICT(key) DO UPDATE SET value = ${ADVANCE()};
29081
+ END`;
29082
+ });
29083
+
28763
29084
  // ../plugin/src/shared/commit-detection.ts
28764
29085
  function createCommitHashExtractPattern() {
28765
29086
  return new RegExp(`\`?\\b(${HASH_HEX})\\b\`?`, "gi");
@@ -28782,6 +29103,271 @@ function removeSystemReminders(text) {
28782
29103
  }
28783
29104
  var SYSTEM_DIRECTIVE_PREFIX = "[SYSTEM DIRECTIVE: MAGIC-CONTEXT";
28784
29105
 
29106
+ // ../plugin/src/hooks/magic-context/token-count-exact.ts
29107
+ function hasInternals(tokenizer) {
29108
+ const value = tokenizer;
29109
+ return !!value && value.patternRegex instanceof RegExp && value.patternRegex.global && typeof value.stringRankEncoder === "object" && value.stringRankEncoder !== null && Array.isArray(value.binaryFirstByteIndex);
29110
+ }
29111
+ function isValidUtf8(bytes) {
29112
+ let index = 0;
29113
+ while (index < bytes.length) {
29114
+ const first = bytes[index];
29115
+ let size = 0;
29116
+ let codePoint = 0;
29117
+ if (first <= 127) {
29118
+ size = 1;
29119
+ codePoint = first;
29120
+ } else if ((first & 224) === 192) {
29121
+ size = 2;
29122
+ codePoint = first & 31;
29123
+ if (first <= 193)
29124
+ return false;
29125
+ } else if ((first & 240) === 224) {
29126
+ size = 3;
29127
+ codePoint = first & 15;
29128
+ } else if ((first & 248) === 240) {
29129
+ size = 4;
29130
+ codePoint = first & 7;
29131
+ if (first > 244)
29132
+ return false;
29133
+ } else
29134
+ return false;
29135
+ if (index + size > bytes.length)
29136
+ return false;
29137
+ for (let offset = 1;offset < size; offset += 1) {
29138
+ const byte = bytes[index + offset];
29139
+ if ((byte & 192) !== 128)
29140
+ return false;
29141
+ codePoint = codePoint << 6 | byte & 63;
29142
+ }
29143
+ if (size === 2 && codePoint < 128)
29144
+ return false;
29145
+ if (size === 3 && codePoint < 2048)
29146
+ return false;
29147
+ if (size === 4 && codePoint < 65536)
29148
+ return false;
29149
+ if (codePoint >= 55296 && codePoint <= 57343)
29150
+ return false;
29151
+ if (codePoint > 1114111)
29152
+ return false;
29153
+ index += size;
29154
+ }
29155
+ return true;
29156
+ }
29157
+ function binarySearchBytes(sorted, key) {
29158
+ let low = 0;
29159
+ let high = sorted.length - 1;
29160
+ while (low <= high) {
29161
+ const mid = low + high >>> 1;
29162
+ const midKey = sorted[mid][0];
29163
+ let compare = 0;
29164
+ const shared = Math.min(midKey.length, key.length);
29165
+ for (let index = 0;index < shared; index += 1) {
29166
+ compare = midKey[index] - key[index];
29167
+ if (compare !== 0)
29168
+ break;
29169
+ }
29170
+ if (compare === 0)
29171
+ compare = midKey.length - key.length;
29172
+ if (compare === 0)
29173
+ return sorted[mid][1];
29174
+ if (compare < 0)
29175
+ low = mid + 1;
29176
+ else
29177
+ high = mid - 1;
29178
+ }
29179
+ return -1;
29180
+ }
29181
+ function rankOf(tokenizer, slice) {
29182
+ if (isValidUtf8(slice)) {
29183
+ const rank = tokenizer.stringRankEncoder[textDecoder.decode(slice)];
29184
+ if (rank !== undefined)
29185
+ return rank;
29186
+ }
29187
+ const bucket = tokenizer.binaryFirstByteIndex[slice[0]];
29188
+ if (bucket !== null && bucket !== undefined) {
29189
+ const rank = binarySearchBytes(bucket, slice);
29190
+ if (rank !== -1)
29191
+ return rank;
29192
+ }
29193
+ return NO_RANK;
29194
+ }
29195
+ function mergeByScanning(tokenizer, bytes) {
29196
+ const starts = [];
29197
+ const ranks = [];
29198
+ const pairRank = (index) => {
29199
+ const end = starts[index + 2];
29200
+ return end === undefined ? NO_RANK : rankOf(tokenizer, bytes.subarray(starts[index], end));
29201
+ };
29202
+ for (let index = 0;index <= bytes.length; index += 1)
29203
+ starts.push(index);
29204
+ for (let index = 0;index < starts.length; index += 1)
29205
+ ranks.push(pairRank(index));
29206
+ while (starts.length > 1) {
29207
+ let minRank = NO_RANK;
29208
+ let minIndex = -1;
29209
+ for (let index = 0;index < ranks.length - 1; index += 1) {
29210
+ if (ranks[index] < minRank) {
29211
+ minRank = ranks[index];
29212
+ minIndex = index;
29213
+ }
29214
+ }
29215
+ if (minIndex === -1)
29216
+ break;
29217
+ starts.splice(minIndex + 1, 1);
29218
+ ranks.splice(minIndex, 1);
29219
+ ranks[minIndex] = pairRank(minIndex);
29220
+ if (minIndex > 0)
29221
+ ranks[minIndex - 1] = pairRank(minIndex - 1);
29222
+ }
29223
+ const tokens = [];
29224
+ for (let index = 0;index < starts.length - 1; index += 1) {
29225
+ const rank = rankOf(tokenizer, bytes.subarray(starts[index], starts[index + 1]));
29226
+ if (rank !== NO_RANK)
29227
+ tokens.push(rank);
29228
+ }
29229
+ return tokens;
29230
+ }
29231
+ function mergeWithHeap(tokenizer, bytes) {
29232
+ const length = bytes.length;
29233
+ const next = new Int32Array(length + 1);
29234
+ const prev = new Int32Array(length + 1);
29235
+ const alive = new Uint8Array(length + 1).fill(1);
29236
+ const rank = new Float64Array(length + 1).fill(NO_RANK);
29237
+ for (let index = 0;index <= length; index += 1) {
29238
+ next[index] = index + 1;
29239
+ prev[index] = index - 1;
29240
+ }
29241
+ const heapRank = [];
29242
+ const heapAt = [];
29243
+ const less = (a, b) => heapRank[a] < heapRank[b] || heapRank[a] === heapRank[b] && heapAt[a] < heapAt[b];
29244
+ const swap = (a, b) => {
29245
+ [heapRank[a], heapRank[b]] = [heapRank[b], heapRank[a]];
29246
+ [heapAt[a], heapAt[b]] = [heapAt[b], heapAt[a]];
29247
+ };
29248
+ const push = (value, at) => {
29249
+ heapRank.push(value);
29250
+ heapAt.push(at);
29251
+ let child = heapRank.length - 1;
29252
+ while (child > 0) {
29253
+ const parent = child - 1 >> 1;
29254
+ if (!less(child, parent))
29255
+ break;
29256
+ swap(child, parent);
29257
+ child = parent;
29258
+ }
29259
+ };
29260
+ const pop = () => {
29261
+ const last = heapRank.length - 1;
29262
+ swap(0, last);
29263
+ heapRank.pop();
29264
+ heapAt.pop();
29265
+ let parent = 0;
29266
+ for (;; ) {
29267
+ const left = parent * 2 + 1;
29268
+ const right = left + 1;
29269
+ let smallest = parent;
29270
+ if (left < heapRank.length && less(left, smallest))
29271
+ smallest = left;
29272
+ if (right < heapRank.length && less(right, smallest))
29273
+ smallest = right;
29274
+ if (smallest === parent)
29275
+ break;
29276
+ swap(parent, smallest);
29277
+ parent = smallest;
29278
+ }
29279
+ };
29280
+ const pairRank = (at) => {
29281
+ const middle = next[at];
29282
+ if (middle >= length)
29283
+ return NO_RANK;
29284
+ return rankOf(tokenizer, bytes.subarray(at, next[middle]));
29285
+ };
29286
+ const update = (at) => {
29287
+ rank[at] = pairRank(at);
29288
+ if (rank[at] !== NO_RANK)
29289
+ push(rank[at], at);
29290
+ };
29291
+ for (let at = 0;at < length; at += 1)
29292
+ update(at);
29293
+ while (heapRank.length > 0) {
29294
+ const value = heapRank[0];
29295
+ const at = heapAt[0];
29296
+ pop();
29297
+ if (!alive[at] || rank[at] !== value)
29298
+ continue;
29299
+ const removed = next[at];
29300
+ alive[removed] = 0;
29301
+ next[at] = next[removed];
29302
+ prev[next[removed]] = at;
29303
+ update(at);
29304
+ if (at > 0)
29305
+ update(prev[at]);
29306
+ }
29307
+ const tokens = [];
29308
+ for (let at = 0;at < length; at = next[at]) {
29309
+ const value = rankOf(tokenizer, bytes.subarray(at, next[at]));
29310
+ if (value !== NO_RANK)
29311
+ tokens.push(value);
29312
+ }
29313
+ return tokens;
29314
+ }
29315
+ function mergePiece(tokenizer, piece) {
29316
+ let cache = mergeCaches.get(tokenizer);
29317
+ if (!cache) {
29318
+ cache = { current: new Map, previous: new Map };
29319
+ mergeCaches.set(tokenizer, cache);
29320
+ }
29321
+ let tokens = cache.current.get(piece);
29322
+ if (tokens)
29323
+ return tokens;
29324
+ tokens = cache.previous.get(piece);
29325
+ if (!tokens) {
29326
+ const bytes = textEncoder.encode(piece);
29327
+ mergedPieces += 1;
29328
+ tokens = bytes.length > LONG_PIECE_BYTES ? mergeWithHeap(tokenizer, bytes) : mergeByScanning(tokenizer, bytes);
29329
+ }
29330
+ if (cache.current.size >= MERGE_CACHE_GENERATION) {
29331
+ cache.previous = cache.current;
29332
+ cache.current = new Map;
29333
+ }
29334
+ cache.current.set(piece, tokens);
29335
+ return tokens;
29336
+ }
29337
+ function encodeTokensExactly(tokenizer, text) {
29338
+ if (!hasInternals(tokenizer)) {
29339
+ return tokenizer.encode(text, "all");
29340
+ }
29341
+ if (text.length < 10) {
29342
+ const direct = tokenizer.stringRankEncoder[text];
29343
+ if (direct !== undefined)
29344
+ return [direct];
29345
+ }
29346
+ const tokens = [];
29347
+ const regex = tokenizer.patternRegex;
29348
+ regex.lastIndex = 0;
29349
+ for (let match = regex.exec(text);match !== null; match = regex.exec(text)) {
29350
+ const piece = match[0];
29351
+ const direct = tokenizer.stringRankEncoder[piece];
29352
+ if (direct !== undefined) {
29353
+ tokens.push(direct);
29354
+ continue;
29355
+ }
29356
+ for (const token of mergePiece(tokenizer, piece))
29357
+ tokens.push(token);
29358
+ }
29359
+ return tokens;
29360
+ }
29361
+ function countTokensExactly(tokenizer, text) {
29362
+ return encodeTokensExactly(tokenizer, text).length;
29363
+ }
29364
+ var NO_RANK = 4294967295, LONG_PIECE_BYTES = 256, MERGE_CACHE_GENERATION = 50000, textEncoder, textDecoder, mergeCaches, mergedPieces = 0;
29365
+ var init_token_count_exact = __esm(() => {
29366
+ textEncoder = new TextEncoder;
29367
+ textDecoder = new TextDecoder("utf-8");
29368
+ mergeCaches = new WeakMap;
29369
+ });
29370
+
28785
29371
  // ../plugin/src/hooks/magic-context/read-session-formatting.ts
28786
29372
  import { createRequire as createRequire2 } from "node:module";
28787
29373
  function hasMeaningfulUserText(parts) {
@@ -28924,7 +29510,7 @@ function estimateTokens(text) {
28924
29510
  if (!activeTokenizer)
28925
29511
  return estimateTokensHeuristically(text);
28926
29512
  try {
28927
- return activeTokenizer.encode(text, "all").length;
29513
+ return countTokensExactly(activeTokenizer, text);
28928
29514
  } catch (error) {
28929
29515
  tokenizer = undefined;
28930
29516
  tokenizerLoadAttempted = true;
@@ -29015,21 +29601,41 @@ var MAX_COMMITS_PER_BLOCK = 5, tokenizer, tokenizerLoadAttempted = false, tokeni
29015
29601
  var init_read_session_formatting = __esm(() => {
29016
29602
  init_commit_detection();
29017
29603
  init_logger();
29604
+ init_token_count_exact();
29018
29605
  promptTokenCounts = new Map;
29019
29606
  PROMPT_TOKEN_COUNT_BYTES = 8 * 1024 * 1024;
29020
29607
  });
29021
29608
 
29022
29609
  // ../plugin/src/shared/stable-json.ts
29023
29610
  function stableStringify(value, seen = new WeakSet) {
29024
- if (value === undefined)
29025
- return "undefined";
29026
- if (value === null || typeof value !== "object")
29027
- return JSON.stringify(value) ?? String(value);
29028
- if (seen.has(value))
29029
- return '"[Circular]"';
29611
+ const pieces = [];
29612
+ writeStable(value, seen, pieces);
29613
+ return pieces.join("");
29614
+ }
29615
+ function writeStable(value, seen, pieces) {
29616
+ if (value === undefined) {
29617
+ pieces.push("undefined");
29618
+ return;
29619
+ }
29620
+ if (value === null || typeof value !== "object") {
29621
+ pieces.push(JSON.stringify(value) ?? String(value));
29622
+ return;
29623
+ }
29624
+ if (seen.has(value)) {
29625
+ pieces.push('"[Circular]"');
29626
+ return;
29627
+ }
29030
29628
  seen.add(value);
29031
29629
  if (Array.isArray(value)) {
29032
- return `[${value.map((item) => stableStringify(item, seen)).join(",")}]`;
29630
+ pieces.push("[");
29631
+ for (let index = 0;index < value.length; index += 1) {
29632
+ if (index > 0)
29633
+ pieces.push(",");
29634
+ if (index in value)
29635
+ writeStable(value[index], seen, pieces);
29636
+ }
29637
+ pieces.push("]");
29638
+ return;
29033
29639
  }
29034
29640
  const entries = Object.entries(value).sort(([a], [b]) => {
29035
29641
  if (a < b)
@@ -29038,7 +29644,14 @@ function stableStringify(value, seen = new WeakSet) {
29038
29644
  return 1;
29039
29645
  return 0;
29040
29646
  });
29041
- return `{${entries.map(([key, child]) => `${JSON.stringify(key)}:${stableStringify(child, seen)}`).join(",")}}`;
29647
+ pieces.push("{");
29648
+ entries.forEach(([key, child], index) => {
29649
+ if (index > 0)
29650
+ pieces.push(",");
29651
+ pieces.push(JSON.stringify(key), ":");
29652
+ writeStable(child, seen, pieces);
29653
+ });
29654
+ pieces.push("}");
29042
29655
  }
29043
29656
 
29044
29657
  // ../plugin/src/features/magic-context/tool-definition-tokens.ts
@@ -29724,6 +30337,19 @@ var init_image_token_estimate = __esm(() => {
29724
30337
  JPEG_SCAN_LIMIT_BYTES = 256 * 1024;
29725
30338
  });
29726
30339
 
30340
+ // ../plugin/src/hooks/magic-context/provider-visible-parts.ts
30341
+ var KEPT_TOOL_STATE_FIELDS;
30342
+ var init_provider_visible_parts = __esm(() => {
30343
+ KEPT_TOOL_STATE_FIELDS = new Set([
30344
+ "status",
30345
+ "input",
30346
+ "output",
30347
+ "error",
30348
+ "attachments",
30349
+ "content"
30350
+ ]);
30351
+ });
30352
+
29727
30353
  // ../plugin/src/hooks/magic-context/todo-view.ts
29728
30354
  var TODO_STATUS_PENDING = "pending", TODO_STATUS_IN_PROGRESS = "in_progress", TODO_STATUS_COMPLETED = "completed", TODO_STATUS_CANCELLED = "cancelled", TODO_PRIORITY_HIGH = "high", TODO_PRIORITY_MEDIUM = "medium", TODO_PRIORITY_LOW = "low", TODO_STATUSES, TODO_PRIORITIES, TODO_STATUS_SET, TODO_PRIORITY_SET, TERMINAL_STATUSES, TITLE_DONE_STATUSES;
29729
30355
  var init_todo_view = __esm(() => {
@@ -29753,10 +30379,11 @@ var init_tail_hygiene_walk = __esm(() => {
29753
30379
  init_reclaim_protection();
29754
30380
  init_tag_owner_id();
29755
30381
  init_image_token_estimate();
30382
+ init_provider_visible_parts();
29756
30383
  init_read_session_formatting();
29757
30384
  init_todo_view();
29758
30385
  init_reclaim_protection();
29759
- MAX_CONTENT_MEMO_BYTES = 64 * 1024 * 1024;
30386
+ MAX_CONTENT_MEMO_BYTES = 256 * 1024 * 1024;
29760
30387
  contentMemo = new Map;
29761
30388
  contentMemoOrder = new Set;
29762
30389
  baselineMeasurementMemo = new Map;
@@ -29822,12 +30449,14 @@ function pickNearestPriorOwner(candidates, currentMessageId, times) {
29822
30449
  }
29823
30450
  return best?.id ?? null;
29824
30451
  }
29825
- var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, getAssignableTagNumberByMessageIdStatements, hasPiFallbackMessageTagStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements, recentTagOwnerStatements, RECLAIM_HINT_EXCLUDED_TOOLS, getActiveToolTagsForAgeReclaimStatements, scopedActiveTokenTotalStatements, updateTagTokenCountStatements, updateTagInputTokenCountStatements, inertWhitespaceStatements, foldStatements, getOwnerScopedToolTagNumbersStatements, getMinMessageTagNumberForRawIdStatements, getActiveTagsBySessionStatements, getNullOwnerToolTagsBySessionStatements, getDroppedTagsBySessionStatements, getMaxDroppedTagNumberStatements, newestToolTagNumberStatements, droppedNumberStatements, tagByIdStatements, getToolTagNumberByOwnerStatements, getNullOwnerToolTagStatements, adoptNullOwnerToolTagStatements, getToolOwnerByTagIdStatements, deleteToolTagsByOwnerStatements;
29826
- var init_storage_tags = __esm(() => {
30452
+ var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, getAssignableTagNumberByMessageIdStatements, hasPiFallbackMessageTagStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements, recentTagOwnerStatements, RECLAIM_HINT_EXCLUDED_TOOLS, getActiveToolTagsForAgeReclaimStatements, scopedActiveTokenTotalStatements, updateTagTokenCountStatements, updateTagInputTokenCountStatements, tagOwnerSummaries, TAG_OWNER_COLUMNS = "id, type, message_id, tool_owner_message_id, tag_number, reasoning_token_count", TAG_OWNER_APPENDED_WITH_SHAPE_SQL, inertWhitespaceStatements, inertWhitespaceByNumberStatements, foldStatements, getOwnerScopedToolTagNumbersStatements, getMinMessageTagNumberForRawIdStatements, getActiveTagsBySessionStatements, getNullOwnerToolTagsBySessionStatements, getDroppedTagsBySessionStatements, getMaxDroppedTagNumberStatements, newestToolTagNumberStatements, droppedNumberStatements, tagByIdStatements, getToolTagNumberByOwnerStatements, getNullOwnerToolTagStatements, adoptNullOwnerToolTagStatements, getToolOwnerByTagIdStatements, deleteToolTagsByOwnerStatements;
30453
+ var init_storage_tags = __esm(async () => {
29827
30454
  init_emergency_drop();
29828
30455
  init_tag_owner_id();
29829
30456
  init_migration_v95_perf_indexes();
29830
30457
  init_storage_ops();
30458
+ init_storage_tag_identity_revision();
30459
+ await init_sqlite();
29831
30460
  insertTagStatements = new WeakMap;
29832
30461
  updateTagStatusStatements = new WeakMap;
29833
30462
  updateTagDropModeStatements = new WeakMap;
@@ -29855,7 +30484,14 @@ var init_storage_tags = __esm(() => {
29855
30484
  scopedActiveTokenTotalStatements = new WeakMap;
29856
30485
  updateTagTokenCountStatements = new WeakMap;
29857
30486
  updateTagInputTokenCountStatements = new WeakMap;
30487
+ tagOwnerSummaries = new WeakMap;
30488
+ TAG_OWNER_APPENDED_WITH_SHAPE_SQL = `SELECT ${TAG_OWNER_COLUMNS}, 0 AS shape, NULL AS tag_count, NULL AS max_id, NULL AS revision
30489
+ FROM tags WHERE id > ? AND +session_id = ?
30490
+ UNION ALL
30491
+ SELECT NULL, NULL, NULL, NULL, NULL, NULL, 1, COUNT(*), MAX(id), (SELECT value FROM schema_migrations_meta WHERE key = ?)
30492
+ FROM tags WHERE session_id = ?`;
29858
30493
  inertWhitespaceStatements = new WeakMap;
30494
+ inertWhitespaceByNumberStatements = new WeakMap;
29859
30495
  foldStatements = new WeakMap;
29860
30496
  getOwnerScopedToolTagNumbersStatements = new WeakMap;
29861
30497
  getMinMessageTagNumberForRawIdStatements = new WeakMap;
@@ -29973,92 +30609,28 @@ function isHostUnservedRow(value) {
29973
30609
  }
29974
30610
  var HOST_UNSERVED_ROW = "hostUnservedRow";
29975
30611
 
29976
- // ../plugin/src/hooks/magic-context/read-session-db.ts
29977
- function openCodeDbExists() {
29978
- return harnessOwnsOpenCodeStore() && openCodeDbPathExists(resolveOpenCodeDbPath());
29979
- }
29980
- function closeCachedReadOnlyDb() {
29981
- if (!cachedReadOnlyDb) {
29982
- return;
29983
- }
29984
- try {
29985
- closeQuietly(cachedReadOnlyDb.db);
29986
- } catch (error) {
29987
- log("[magic-context] failed to close cached OpenCode read-only DB:", error);
29988
- } finally {
29989
- cachedReadOnlyDb = null;
29990
- }
29991
- }
29992
- function getReadOnlySessionDb() {
29993
- if (!harnessOwnsOpenCodeStore()) {
29994
- throw new Error("OpenCode session database is not readable from a Pi-compatible process; its history lives in Pi sessions");
29995
- }
29996
- const resolution = resolveOpenCodeDbPath();
29997
- const dbPath = resolution.path;
29998
- if (!openCodeDbPathExists(resolution)) {
29999
- throw new Error(`OpenCode session database is unavailable at ${dbPath} (source=${resolution.source})`);
30000
- }
30001
- if (cachedReadOnlyDb?.path === dbPath) {
30002
- return cachedReadOnlyDb.db;
30003
- }
30004
- closeCachedReadOnlyDb();
30005
- const db = new Database(dbPath, { readonly: true });
30006
- try {
30007
- assertOpenCodeStoreGeneration(db, "v1", dbPath);
30008
- } catch (error) {
30009
- closeQuietly(db);
30010
- throw error;
30612
+ // ../plugin/src/shared/exit-abort-registry.ts
30613
+ function abortAll() {
30614
+ for (const controller of controllers) {
30615
+ try {
30616
+ controller.abort();
30617
+ } catch {}
30011
30618
  }
30012
- cachedReadOnlyDb = { path: dbPath, db };
30013
- clearOpenCodeDbReadFailure();
30014
- return db;
30015
- }
30016
- function withReadOnlySessionDb(fn) {
30017
- return fn(getReadOnlySessionDb());
30018
30619
  }
30019
- function getRawSessionMessageCountFromDb(db, sessionId) {
30020
- const row = db.prepare(`SELECT COUNT(*) as count FROM message WHERE session_id = ?
30021
- AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
30022
- AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')`).get(sessionId);
30023
- return typeof row?.count === "number" ? row.count : 0;
30024
- }
30025
- function logProbeFailureOnce(resolution, error) {
30026
- const failure = recordOpenCodeDbReadFailure(resolution, error);
30027
- if (!claimOpenCodeDbDiagnosticOnce("session-state-probe", resolution))
30620
+ function registerExitAbort(controller) {
30621
+ controllers.add(controller);
30622
+ if (listenerRegistered)
30028
30623
  return;
30029
- const message = `[magic-context] OpenCode DB probe failed: path=${resolution.path} source=${resolution.source} cause=${failure.message}`;
30030
- probeLogObserverForTests?.(message);
30031
- log(message);
30624
+ listenerRegistered = true;
30625
+ process.once("exit", abortAll);
30032
30626
  }
30033
- function getMessageTimesFromOpenCodeDb(sessionId, messageIds) {
30034
- const result = new Map;
30035
- if (messageIds.length === 0 || !harnessOwnsOpenCodeStore())
30036
- return result;
30037
- try {
30038
- withReadOnlySessionDb((db) => {
30039
- const placeholders = messageIds.map(() => "?").join(",");
30040
- const rows = db.prepare(`SELECT id, time_created FROM message WHERE session_id = ? AND id IN (${placeholders})`).all(sessionId, ...messageIds);
30041
- for (const row of rows) {
30042
- if (typeof row.id === "string" && typeof row.time_created === "number") {
30043
- result.set(row.id, row.time_created);
30044
- }
30045
- }
30046
- });
30047
- } catch (error) {
30048
- logProbeFailureOnce(resolveOpenCodeDbPath(), error);
30049
- }
30050
- return result;
30051
- }
30052
- var cachedReadOnlyDb = null, trackedSessions, pendingParts, probeLogObserverForTests;
30053
- var init_read_session_db = __esm(async () => {
30054
- init_logger();
30055
- init_opencode_db_path();
30056
- await init_sqlite();
30057
- trackedSessions = new Map;
30058
- pendingParts = new Map;
30627
+ var controllers, listenerRegistered = false;
30628
+ var init_exit_abort_registry = __esm(() => {
30629
+ controllers = new Set;
30059
30630
  });
30060
30631
 
30061
30632
  // ../plugin/src/hooks/magic-context/read-session-raw.ts
30633
+ import { isMainThread as isMainThread2 } from "node:worker_threads";
30062
30634
  function isRawMessageRow(row) {
30063
30635
  if (row === null || typeof row !== "object")
30064
30636
  return false;
@@ -30118,7 +30690,7 @@ function readRawSessionMessagesFromDb(db, sessionId) {
30118
30690
  if (messageIds.length === 0)
30119
30691
  continue;
30120
30692
  const placeholders = messageIds.map(() => "?").join(", ");
30121
- const partRows = db.prepare(`SELECT message_id, data, time_updated
30693
+ const partRows = db.prepare(`SELECT message_id, ${RAW_PART_DATA_SQL} AS data, time_updated
30122
30694
  FROM part
30123
30695
  WHERE +session_id = ?
30124
30696
  AND likelihood(message_id IN (${placeholders}), 0.000001)
@@ -30150,7 +30722,7 @@ function readRawSessionMessagePageFromDb(db, sessionId, afterOrdinal, limit, fin
30150
30722
  if (messageRows.length === 0)
30151
30723
  return [];
30152
30724
  const placeholders = messageRows.map(() => "?").join(", ");
30153
- const partRows = db.prepare(`SELECT message_id, data, time_updated
30725
+ const partRows = db.prepare(`SELECT message_id, ${RAW_PART_DATA_SQL} AS data, time_updated
30154
30726
  FROM part
30155
30727
  WHERE +session_id = ?
30156
30728
  AND likelihood(message_id IN (${placeholders}), 0.000001)
@@ -30162,29 +30734,44 @@ function readRawMessagePageRows(db, sessionId, afterOrdinal, limit, finalWaterma
30162
30734
  const pageSize = Math.min(Math.max(1, Math.floor(limit)), remaining);
30163
30735
  if (pageSize === 0)
30164
30736
  return [];
30165
- const parameters = [sessionId];
30166
- if (after)
30167
- parameters.push(after.timeCreated, after.timeCreated, after.timeCreated, after.id);
30168
- parameters.push(pageSize);
30169
- if (!after)
30170
- parameters.push(Math.max(0, Math.floor(afterOrdinal)));
30171
- return db.prepare(`SELECT id, data, time_created, time_updated
30172
- FROM message
30173
- WHERE session_id = ?
30174
- ${after ? "AND time_created >= ? AND (time_created > ? OR (time_created = ? AND id > ?))" : ""}
30175
- AND NOT (
30176
- CASE WHEN json_valid(data) = 1
30177
- THEN COALESCE(json_extract(data, '$.summary'), 0)
30178
- ELSE 0 END = 1
30179
- AND CASE WHEN json_valid(data) = 1
30180
- THEN COALESCE(json_extract(data, '$.finish'), '')
30181
- ELSE '' END = 'stop'
30182
- )
30183
- ORDER BY time_created ASC, id ASC
30184
- LIMIT ? ${after ? "" : "OFFSET ?"}`).all(...parameters).filter(isRawMessageRow).map((row, index) => ({
30185
- ...row,
30186
- ordinal: Math.floor(afterOrdinal) + index + 1
30187
- }));
30737
+ return withReadSnapshot(db, () => {
30738
+ const ordinal = Math.max(0, Math.floor(afterOrdinal));
30739
+ const points = currentOrdinalCheckpoints(db, sessionId)?.scan.checkpoints ?? [];
30740
+ let low = 0;
30741
+ let high = points.length;
30742
+ while (low < high) {
30743
+ const middle = Math.floor((low + high) / 2);
30744
+ if (points[middle].ordinal <= ordinal)
30745
+ low = middle + 1;
30746
+ else
30747
+ high = middle;
30748
+ }
30749
+ const floor = points[low - 1];
30750
+ const useFloor = floor && (!after || compareOrdinalKeys(floor, after) > 0);
30751
+ const anchor = useFloor ? floor : after;
30752
+ const skip = anchor === after && after ? 0 : ordinal - (floor?.ordinal ?? 0);
30753
+ if (skip > ORDINAL_LOOKUP_LIMIT) {
30754
+ prewarmRawSessionOrdinalsForDb(db, sessionId);
30755
+ throw new RawOrdinalsNotReady;
30756
+ }
30757
+ const rawLimit = skip + pageSize + ORDINAL_LOOKUP_LIMIT;
30758
+ const rows = db.prepare(`SELECT id, data, time_created, time_updated, ${EXCLUDED_ROW_SQL} AS excluded
30759
+ FROM message WHERE id IN (
30760
+ SELECT id FROM message WHERE session_id = ?
30761
+ ${anchor ? "AND (time_created, id) > (?, ?)" : ""}
30762
+ ORDER BY time_created, id LIMIT ?)
30763
+ ORDER BY time_created, id`).all(sessionId, ...anchor ? [anchor.timeCreated, anchor.id] : [], rawLimit);
30764
+ rawMessagePageRowsVisited += rows.length;
30765
+ const eligible = rows.filter((row) => row.excluded !== 1);
30766
+ if (eligible.length <= skip && rows.length === rawLimit) {
30767
+ prewarmRawSessionOrdinalsForDb(db, sessionId);
30768
+ throw new RawOrdinalsNotReady;
30769
+ }
30770
+ return eligible.slice(skip, skip + pageSize).filter(isRawMessageRow).map((row, index) => ({
30771
+ ...row,
30772
+ ordinal: ordinal + index + 1
30773
+ }));
30774
+ });
30188
30775
  }
30189
30776
  function assembleRawMessagePage(messageRows, partRows) {
30190
30777
  const partsByMessageId = new Map;
@@ -30223,6 +30810,9 @@ function readRawSessionMessageSummaryPageFromDb(db, sessionId, afterOrdinal, lim
30223
30810
  return assembleRawMessagePage(messageRows, partRows);
30224
30811
  }
30225
30812
  function countRawSessionMessageOrdinalsFromDb(db, sessionId) {
30813
+ const indexed = countCanonicalOrdinalsIndexed(db, sessionId, null, null);
30814
+ if (indexed !== null)
30815
+ return indexed;
30226
30816
  const row = db.prepare(`SELECT COUNT(*) AS count
30227
30817
  FROM message
30228
30818
  WHERE session_id = ?
@@ -30309,7 +30899,7 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
30309
30899
  for (let i = 0;i < ids.length; i += CHUNK) {
30310
30900
  const slice = ids.slice(i, i + CHUNK);
30311
30901
  const placeholders = slice.map(() => "?").join(",");
30312
- const partRows = db.prepare(`SELECT message_id, data, time_updated FROM part WHERE +session_id = ? AND likelihood(message_id IN (${placeholders}), 0.000001) ORDER BY time_created ASC, id ASC`).all(sessionId, ...slice).filter(isRawPartRow);
30902
+ const partRows = db.prepare(`SELECT message_id, ${RAW_PART_DATA_SQL} AS data, time_updated FROM part WHERE +session_id = ? AND likelihood(message_id IN (${placeholders}), 0.000001) ORDER BY time_created ASC, id ASC`).all(sessionId, ...slice).filter(isRawPartRow);
30313
30903
  for (const part of partRows) {
30314
30904
  const list = partsByMessageId.get(part.message_id) ?? [];
30315
30905
  list.push(attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated));
@@ -30355,49 +30945,431 @@ function readRawSessionMessagePartsByIdFromDb(db, sessionId, messageId, onQuery)
30355
30945
  version: row.time_updated ?? null
30356
30946
  };
30357
30947
  }
30358
- function readRawSessionMessageOrdinalByIdFromDb(db, sessionId, messageId) {
30359
- const row = db.prepare(`SELECT COUNT(candidate.id) AS ordinal
30360
- FROM message AS target
30361
- JOIN message AS candidate
30362
- ON candidate.session_id = target.session_id
30363
- AND NOT (
30364
- CASE WHEN json_valid(candidate.data) = 1
30365
- THEN COALESCE(json_extract(candidate.data, '$.summary'), 0)
30366
- ELSE 0 END = 1
30367
- AND CASE WHEN json_valid(candidate.data) = 1
30368
- THEN COALESCE(json_extract(candidate.data, '$.finish'), '')
30369
- ELSE '' END = 'stop'
30370
- )
30371
- AND (candidate.time_created < target.time_created
30372
- OR (candidate.time_created = target.time_created AND candidate.id <= target.id))
30373
- WHERE target.session_id = ?
30374
- AND target.id = ?
30375
- AND NOT (
30376
- CASE WHEN json_valid(target.data) = 1
30377
- THEN COALESCE(json_extract(target.data, '$.summary'), 0)
30378
- ELSE 0 END = 1
30379
- AND CASE WHEN json_valid(target.data) = 1
30380
- THEN COALESCE(json_extract(target.data, '$.finish'), '')
30381
- ELSE '' END = 'stop'
30382
- )`).get(sessionId, messageId);
30383
- const ordinal = row?.ordinal;
30384
- return typeof ordinal === "number" && ordinal > 0 ? ordinal : null;
30948
+ function ordinalRangeSql(lowerBound) {
30949
+ return `SELECT COUNT(*) AS visited,
30950
+ COALESCE(SUM(CASE WHEN ${ORDINAL_SUMMARY_FLAG_SQL} AND ${ORDINAL_STOP_FLAG_SQL}
30951
+ THEN 0 ELSE 1 END), 0) AS eligible
30952
+ FROM message
30953
+ WHERE session_id = ?
30954
+ ${lowerBound ? "AND (time_created, id) > (?, ?)" : ""}
30955
+ AND (time_created, id) <= (?, ?)`;
30385
30956
  }
30386
- function readRawSessionMessageByIdFromDb(db, sessionId, messageId) {
30387
- const row = db.prepare("SELECT id, data, time_created, time_updated FROM message WHERE session_id = ? AND id = ?").get(sessionId, messageId);
30388
- if (!row || !isRawMessageRow(row) || typeof row.time_created !== "number") {
30957
+ function readStoreStamp(db) {
30958
+ try {
30959
+ const row = db.prepare(`SELECT data_version AS dataVersion, total_changes() AS totalChanges,
30960
+ (SELECT schema_version FROM pragma_schema_version) AS schemaVersion
30961
+ FROM pragma_data_version`).get();
30962
+ if (typeof row?.dataVersion !== "number" || typeof row.totalChanges !== "number" || typeof row.schemaVersion !== "number")
30963
+ return null;
30964
+ return {
30965
+ dataVersion: row.dataVersion,
30966
+ totalChanges: row.totalChanges,
30967
+ schemaVersion: row.schemaVersion
30968
+ };
30969
+ } catch {
30389
30970
  return null;
30390
30971
  }
30391
- const info = parseJsonRecord(row.data);
30392
- if (!info || isRawCompactionSummaryInfo(info)) {
30972
+ }
30973
+ function sameStoreStamp(left, right) {
30974
+ return left !== null && right !== null && left.dataVersion === right.dataVersion && left.totalChanges === right.totalChanges && left.schemaVersion === right.schemaVersion;
30975
+ }
30976
+ function compareOrdinalKeys(left, right) {
30977
+ if (left.timeCreated !== right.timeCreated)
30978
+ return left.timeCreated < right.timeCreated ? -1 : 1;
30979
+ if (left.id === right.id)
30980
+ return 0;
30981
+ return Buffer.compare(Buffer.from(left.id, "utf8"), Buffer.from(right.id, "utf8"));
30982
+ }
30983
+ function countEligibleOrdinals(db, sessionId, after, through) {
30984
+ const indexed = countCanonicalOrdinalsIndexed(db, sessionId, after, through);
30985
+ if (indexed !== null)
30986
+ return indexed;
30987
+ const row = after ? db.prepare(ORDINAL_RANGE_BETWEEN_SQL).get(sessionId, after.timeCreated, after.id, through.timeCreated, through.id) : db.prepare(ORDINAL_RANGE_FROM_START_SQL).get(sessionId, through.timeCreated, through.id);
30988
+ const visited = typeof row?.visited === "number" ? row.visited : 0;
30989
+ ordinalRowsVisited += visited;
30990
+ ordinalJsonRowsRead += visited;
30991
+ return typeof row?.eligible === "number" ? row.eligible : 0;
30992
+ }
30993
+ function survivingAnchor(db, set) {
30994
+ if (set.anchors.length === 0)
30393
30995
  return null;
30996
+ const current = new Map(db.prepare(ANCHOR_CHECK_SQL).all(JSON.stringify(set.anchors.map((anchor) => anchor.rowid))).map((row) => [row.rid, row.id]));
30997
+ return set.anchors.find((anchor) => current.get(anchor.rowid) === anchor.id);
30998
+ }
30999
+ function currentOrdinalCheckpoints(db, sessionId) {
31000
+ const cached = ordinalCheckpoints.get(db)?.get(sessionId);
31001
+ if (!cached)
31002
+ return;
31003
+ return withReadSnapshot(db, () => {
31004
+ if (!extendOrdinalCheckpoints(db, sessionId, cached.scan, ORDINAL_LOOKUP_LIMIT))
31005
+ return;
31006
+ cached.epoch = getRawSessionSummaryEpoch(sessionId);
31007
+ return cached;
31008
+ });
31009
+ }
31010
+ function extendOrdinalCheckpoints(db, sessionId, scan, limit) {
31011
+ const points = scan.checkpoints ?? [];
31012
+ const exists = (point) => Boolean(db.prepare("SELECT 1 FROM message WHERE id=? AND session_id=? AND time_created=?").get(point.id, sessionId, point.timeCreated));
31013
+ if (points.length && !exists(points[points.length - 1])) {
31014
+ let low = 0;
31015
+ let high = points.length;
31016
+ while (low < high) {
31017
+ const middle = Math.floor((low + high) / 2);
31018
+ if (exists(points[middle]))
31019
+ low = middle + 1;
31020
+ else
31021
+ high = middle;
31022
+ }
31023
+ points.length = low;
31024
+ scan.total = points.at(-1)?.ordinal ?? 0;
31025
+ scan.sessionRows = points.at(-1)?.storedCount ?? 0;
31026
+ }
31027
+ const tail = points.at(-1);
31028
+ const rows = db.prepare(`SELECT id, time_created AS timeCreated, ${EXCLUDED_ROW_SQL} AS excluded FROM message
31029
+ WHERE session_id=?${tail ? " AND (time_created,id) > (?,?)" : ""}
31030
+ ORDER BY time_created,id${limit === undefined ? "" : ` LIMIT ${limit + 1}`}`).all(sessionId, ...tail ? [tail.timeCreated, tail.id] : []);
31031
+ ordinalJsonRowsRead += rows.length;
31032
+ if (limit !== undefined && rows.length > limit)
31033
+ return false;
31034
+ let total = scan.total ?? 0;
31035
+ if (rows.length && tail && (tail.storedCount ?? 0) % 256 !== 0)
31036
+ points.pop();
31037
+ for (let index = 0;index < rows.length; index++) {
31038
+ const row = rows[index];
31039
+ if (row.excluded !== 1)
31040
+ total++;
31041
+ const storedCount = scan.sessionRows + index + 1;
31042
+ if (storedCount % 256 === 0 || index === rows.length - 1)
31043
+ points.push({ id: row.id, timeCreated: row.timeCreated, ordinal: total, storedCount });
31044
+ }
31045
+ scan.checkpoints = points;
31046
+ scan.sessionRows += rows.length;
31047
+ scan.total = total;
31048
+ return true;
31049
+ }
31050
+ function rawOrdinalSummarySeed(db, sessionId) {
31051
+ const retained = ordinalCheckpoints.get(db)?.get(sessionId);
31052
+ if (retained)
31053
+ return retained.scan;
31054
+ const seed = ordinalSummarySeeds.get(db)?.get(sessionId);
31055
+ if (!seed || seed.epoch !== getRawSessionSummaryEpoch(sessionId))
31056
+ return;
31057
+ return {
31058
+ ...seed.scan,
31059
+ ids: [...new Set([...seed.scan.ids, ...writtenSummaryIds.get(sessionId) ?? []])]
31060
+ };
31061
+ }
31062
+ function boundedOrdinalPrefix(db, sessionId, through) {
31063
+ const cached = currentOrdinalCheckpoints(db, sessionId);
31064
+ if (cached && through === null)
31065
+ return cached.scan.total ?? 0;
31066
+ let floor;
31067
+ if (cached && through) {
31068
+ const points = cached.scan.checkpoints ?? [];
31069
+ let low = 0;
31070
+ let high = points.length;
31071
+ while (low < high) {
31072
+ const middle = Math.floor((low + high) / 2);
31073
+ if (compareOrdinalKeys(points[middle], through) <= 0)
31074
+ low = middle + 1;
31075
+ else
31076
+ high = middle;
31077
+ }
31078
+ floor = points[low - 1];
31079
+ if (floor && compareOrdinalKeys(floor, through) === 0)
31080
+ return floor.ordinal;
31081
+ }
31082
+ const bindings = [sessionId];
31083
+ if (floor)
31084
+ bindings.push(floor.timeCreated, floor.id);
31085
+ if (through)
31086
+ bindings.push(through.timeCreated, through.id);
31087
+ const rows = db.prepare(`SELECT ${EXCLUDED_ROW_SQL} AS excluded FROM message WHERE id IN (
31088
+ SELECT id FROM message WHERE session_id = ?${floor ? " AND (time_created, id) > (?, ?)" : ""}${through ? " AND (time_created, id) <= (?, ?)" : ""}
31089
+ ORDER BY time_created, id LIMIT ${ORDINAL_LOOKUP_LIMIT + 1})`).all(...bindings);
31090
+ ordinalRowsVisited += rows.length;
31091
+ ordinalJsonRowsRead += rows.length;
31092
+ if (rows.length > ORDINAL_LOOKUP_LIMIT) {
31093
+ prewarmRawSessionOrdinalsForDb(db, sessionId);
31094
+ throw new RawOrdinalsNotReady;
31095
+ }
31096
+ return (floor?.ordinal ?? 0) + rows.reduce((sum, row) => sum + (row.excluded === 1 ? 0 : 1), 0);
31097
+ }
31098
+ function scanRawSessionSummaryRows(db, sessionId, previous) {
31099
+ return withReadSnapshot(db, () => {
31100
+ if (previous?.checkpoints) {
31101
+ extendOrdinalCheckpoints(db, sessionId, previous);
31102
+ return previous;
31103
+ }
31104
+ if (previous) {
31105
+ const sessions = summaryCandidateSets.get(db) ?? new Map;
31106
+ sessions.set(sessionId, {
31107
+ epoch: getRawSessionSummaryEpoch(sessionId),
31108
+ anchors: previous.anchors,
31109
+ ids: new Set(previous.ids)
31110
+ });
31111
+ summaryCandidateSets.set(db, sessions);
31112
+ }
31113
+ const incremental = previous && isRawSessionSummaryWarm(db, sessionId) ? summaryCandidatesOf(db, sessionId) : undefined;
31114
+ const rows = incremental ? [] : db.prepare(SUMMARY_FULL_SCAN_SQL).all(sessionId, sessionId);
31115
+ const anchors = [];
31116
+ const ids = [];
31117
+ let sessionRows = 0;
31118
+ for (const row of rows) {
31119
+ if (row.kind === 1 && typeof row.rid === "number" && typeof row.id === "string")
31120
+ anchors.push({ rowid: row.rid, id: row.id });
31121
+ else if (row.kind === 2 && typeof row.id === "string")
31122
+ ids.push(row.id);
31123
+ else if (row.kind === 4 && typeof row.rid === "number")
31124
+ sessionRows = row.rid;
31125
+ }
31126
+ if (incremental) {
31127
+ ids.push(...incremental.ids);
31128
+ anchors.push(...summaryCandidateSets.get(db)?.get(sessionId)?.anchors ?? []);
31129
+ sessionRows = countStoredRawSessionRowsIndexed(db, sessionId);
31130
+ }
31131
+ anchors.sort((left, right) => right.rowid - left.rowid);
31132
+ anchors.length = Math.min(anchors.length, SUMMARY_ANCHOR_LIMIT);
31133
+ const keys = db.prepare(`SELECT id, time_created AS timeCreated, ${EXCLUDED_ROW_SQL} AS excluded FROM message WHERE session_id = ?
31134
+ ORDER BY time_created, id`).all(sessionId);
31135
+ const checkpoints = [];
31136
+ let total = 0;
31137
+ for (let index = 0;index < keys.length; index++) {
31138
+ const key = keys[index];
31139
+ if (key.excluded !== 1)
31140
+ total++;
31141
+ if (index % 256 === 255 || index === keys.length - 1)
31142
+ checkpoints.push({
31143
+ id: key.id,
31144
+ timeCreated: key.timeCreated,
31145
+ ordinal: total,
31146
+ storedCount: index + 1
31147
+ });
31148
+ }
31149
+ return { anchors, ids, sessionRows, checkpoints, total };
31150
+ });
31151
+ }
31152
+ function getRawSessionSummaryEpoch(sessionId) {
31153
+ return summaryCandidateEpochs.get(sessionId) ?? 0;
31154
+ }
31155
+ function installRawSessionSummaryScan(db, sessionId, scan, epoch, workerPrepared = false) {
31156
+ if (epoch !== getRawSessionSummaryEpoch(sessionId))
31157
+ return false;
31158
+ const seeds = ordinalSummarySeeds.get(db) ?? new Map;
31159
+ seeds.set(sessionId, { epoch, scan });
31160
+ ordinalSummarySeeds.set(db, seeds);
31161
+ if (scan.checkpoints && workerPrepared) {
31162
+ const counts = ordinalCheckpoints.get(db) ?? new Map;
31163
+ counts.set(sessionId, { epoch, scan });
31164
+ ordinalCheckpoints.set(db, counts);
31165
+ } else if (getSqliteDatabasePath(db) && isMainThread2)
31166
+ return false;
31167
+ if (isRawSessionSummaryWarm(db, sessionId))
31168
+ return true;
31169
+ let sessions = summaryCandidateSets.get(db);
31170
+ if (!sessions) {
31171
+ sessions = new Map;
31172
+ summaryCandidateSets.set(db, sessions);
30394
31173
  }
30395
- const ordinalRow = db.prepare(`SELECT COUNT(*) AS ordinal FROM message
30396
- WHERE session_id = ?
30397
- AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
30398
- AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')
30399
- AND (time_created < ? OR (time_created = ? AND id <= ?))`).get(sessionId, row.time_created, row.time_created, messageId);
30400
- const ordinal = typeof ordinalRow?.ordinal === "number" ? ordinalRow.ordinal : 0;
31174
+ sessions.set(sessionId, { epoch, anchors: [...scan.anchors], ids: new Set(scan.ids) });
31175
+ return isRawSessionSummaryWarm(db, sessionId);
31176
+ }
31177
+ function isRawSessionSummaryWarm(db, sessionId) {
31178
+ if (isMainThread2 && getSqliteDatabasePath(db)) {
31179
+ return Boolean(currentOrdinalCheckpoints(db, sessionId));
31180
+ }
31181
+ const set = summaryCandidateSets.get(db)?.get(sessionId);
31182
+ if (!set || set.epoch !== getRawSessionSummaryEpoch(sessionId))
31183
+ return false;
31184
+ try {
31185
+ return survivingAnchor(db, set) !== undefined;
31186
+ } catch {
31187
+ return false;
31188
+ }
31189
+ }
31190
+ function countStoredRawSessionRowsIndexed(db, sessionId) {
31191
+ const row = db.prepare("SELECT COUNT(*) AS count FROM message WHERE session_id = ?").get(sessionId);
31192
+ return typeof row?.count === "number" ? row.count : 0;
31193
+ }
31194
+ function summaryCandidatesOf(db, sessionId) {
31195
+ let sessions = summaryCandidateSets.get(db);
31196
+ if (!sessions) {
31197
+ sessions = new Map;
31198
+ summaryCandidateSets.set(db, sessions);
31199
+ }
31200
+ const epoch = getRawSessionSummaryEpoch(sessionId);
31201
+ const cached = sessions.get(sessionId);
31202
+ const floor = cached && cached.epoch === epoch ? survivingAnchor(db, cached) : undefined;
31203
+ let ids;
31204
+ let anchors;
31205
+ if (floor === undefined) {
31206
+ const scan = scanRawSessionSummaryRows(db, sessionId);
31207
+ summaryFullScans += 1;
31208
+ ordinalJsonRowsRead += scan.sessionRows;
31209
+ ids = new Set(scan.ids);
31210
+ anchors = scan.anchors;
31211
+ } else {
31212
+ const rows = db.prepare(SUMMARY_SCAN_AFTER_SQL).all(floor?.rowid ?? 0, sessionId);
31213
+ ids = cached?.ids ?? new Set;
31214
+ anchors = [];
31215
+ for (const row of rows) {
31216
+ if (row.kind === 1 && typeof row.rid === "number" && typeof row.id === "string")
31217
+ anchors.push({ rowid: row.rid, id: row.id });
31218
+ else if (row.kind === 2 && typeof row.id === "string")
31219
+ ids.add(row.id);
31220
+ if (row.kind === 2 || row.kind === 3)
31221
+ ordinalJsonRowsRead += 1;
31222
+ }
31223
+ anchors.sort((left, right) => right.rowid - left.rowid);
31224
+ anchors.length = Math.min(anchors.length, SUMMARY_ANCHOR_LIMIT);
31225
+ }
31226
+ for (const id of writtenSummaryIds.get(sessionId) ?? [])
31227
+ ids.add(id);
31228
+ sessions.set(sessionId, { epoch, anchors, ids });
31229
+ const top = anchors[0];
31230
+ return { ids: [...ids], floorRowid: top?.rowid ?? 0, floorId: top?.id ?? null };
31231
+ }
31232
+ function canonicalCountSql(lower, upper) {
31233
+ const range = `${lower ? " AND (time_created, id) > (?, ?)" : ""}${upper ? " AND (time_created, id) <= (?, ?)" : ""}`;
31234
+ return `SELECT
31235
+ (SELECT COUNT(*) FROM message WHERE session_id = ?${range}) AS stored,
31236
+ (SELECT COUNT(*) FROM message
31237
+ WHERE id IN (SELECT value FROM json_each(?)) AND +session_id = ?${range}
31238
+ AND ${EXCLUDED_ROW_SQL}) AS knownExcluded,
31239
+ (SELECT COUNT(*) FROM message
31240
+ WHERE rowid > ? AND +session_id = ? AND id NOT IN (SELECT value FROM json_each(?))${range}
31241
+ AND ${EXCLUDED_ROW_SQL}) AS newExcluded,
31242
+ (SELECT id FROM message WHERE rowid = ?) AS floorId`;
31243
+ }
31244
+ function canonicalCountStatementText(lower, upper) {
31245
+ const key = `${lower}:${upper}`;
31246
+ let sql = CANONICAL_COUNT_SQL.get(key);
31247
+ if (!sql) {
31248
+ sql = canonicalCountSql(lower, upper);
31249
+ CANONICAL_COUNT_SQL.set(key, sql);
31250
+ }
31251
+ return sql;
31252
+ }
31253
+ function countCanonicalOrdinalsIndexed(db, sessionId, after, through) {
31254
+ if (isMainThread2 && getSqliteDatabasePath(db)) {
31255
+ return withReadSnapshot(db, () => boundedOrdinalPrefix(db, sessionId, through) - (after ? boundedOrdinalPrefix(db, sessionId, after) : 0));
31256
+ }
31257
+ if (indexedCountUnsupported.has(db))
31258
+ return null;
31259
+ const range = [];
31260
+ if (after)
31261
+ range.push(after.timeCreated, after.id);
31262
+ if (through)
31263
+ range.push(through.timeCreated, through.id);
31264
+ try {
31265
+ return withReadSnapshot(db, () => {
31266
+ for (let attempt = 0;attempt < STABLE_STORE_ATTEMPTS; attempt += 1) {
31267
+ const view = summaryCandidatesOf(db, sessionId);
31268
+ const ids = JSON.stringify(view.ids);
31269
+ const row = db.prepare(canonicalCountStatementText(after !== null, through !== null)).get(sessionId, ...range, ids, sessionId, ...range, view.floorRowid, sessionId, ids, ...range, view.floorRowid);
31270
+ if (view.floorId !== null && row?.floorId !== view.floorId)
31271
+ continue;
31272
+ const stored = typeof row?.stored === "number" ? row.stored : 0;
31273
+ const excluded = (typeof row?.knownExcluded === "number" ? row.knownExcluded : 0) + (typeof row?.newExcluded === "number" ? row.newExcluded : 0);
31274
+ ordinalRowsVisited += stored;
31275
+ ordinalJsonRowsRead += view.ids.length;
31276
+ return stored - excluded;
31277
+ }
31278
+ return null;
31279
+ });
31280
+ } catch (error) {
31281
+ if (/no such (column|table|function)|json_each/i.test(String(error))) {
31282
+ indexedCountUnsupported.add(db);
31283
+ return null;
31284
+ }
31285
+ throw error;
31286
+ }
31287
+ }
31288
+ function connectionHasOpenTransaction(db) {
31289
+ const state = db;
31290
+ return state.inTransaction === true || state.isTransaction === true;
31291
+ }
31292
+ function withReadSnapshot(db, read) {
31293
+ if (connectionHasOpenTransaction(db))
31294
+ return read();
31295
+ db.exec("BEGIN");
31296
+ try {
31297
+ return read();
31298
+ } finally {
31299
+ if (connectionHasOpenTransaction(db))
31300
+ db.exec("ROLLBACK");
31301
+ }
31302
+ }
31303
+ function reusableWatermark(db, sessionId, stamp) {
31304
+ const watermark = ordinalWatermarks.get(db)?.get(sessionId);
31305
+ if (!watermark || watermark.epoch !== (ordinalWatermarkEpochs.get(sessionId) ?? 0) || !sameStoreStamp(watermark.stamp, stamp))
31306
+ return null;
31307
+ return watermark;
31308
+ }
31309
+ function rememberOrdinalWatermark(db, sessionId, key, ordinal, stamp) {
31310
+ let sessions = ordinalWatermarks.get(db);
31311
+ if (!sessions) {
31312
+ sessions = new Map;
31313
+ ordinalWatermarks.set(db, sessions);
31314
+ }
31315
+ const current = sessions.get(sessionId);
31316
+ if (current && sameStoreStamp(current.stamp, stamp) && compareOrdinalKeys(current, key) >= 0)
31317
+ return;
31318
+ sessions.set(sessionId, {
31319
+ timeCreated: key.timeCreated,
31320
+ id: key.id,
31321
+ ordinal,
31322
+ stamp,
31323
+ epoch: ordinalWatermarkEpochs.get(sessionId) ?? 0
31324
+ });
31325
+ }
31326
+ function canonicalOrdinalOf(db, sessionId, readTarget) {
31327
+ for (let attempt = 1;; attempt += 1) {
31328
+ const before = readStoreStamp(db);
31329
+ const target = readTarget();
31330
+ if (!target)
31331
+ return null;
31332
+ const watermark = attempt < STABLE_STORE_ATTEMPTS ? reusableWatermark(db, sessionId, before) : null;
31333
+ if (!watermark) {
31334
+ const ordinal = countEligibleOrdinals(db, sessionId, null, target);
31335
+ if (before && sameStoreStamp(before, readStoreStamp(db)))
31336
+ rememberOrdinalWatermark(db, sessionId, target, ordinal, before);
31337
+ return { target, ordinal };
31338
+ }
31339
+ const order = compareOrdinalKeys(target, watermark);
31340
+ const ordinal = order === 0 ? watermark.ordinal : order < 0 ? watermark.ordinal - countEligibleOrdinals(db, sessionId, target, watermark) : watermark.ordinal + countEligibleOrdinals(db, sessionId, watermark, target);
31341
+ if (sameStoreStamp(before, readStoreStamp(db))) {
31342
+ rememberOrdinalWatermark(db, sessionId, target, ordinal, before);
31343
+ return { target, ordinal };
31344
+ }
31345
+ }
31346
+ }
31347
+ function readRawSessionMessageOrdinalByIdFromDb(db, sessionId, messageId) {
31348
+ const counted = canonicalOrdinalOf(db, sessionId, () => {
31349
+ const row = db.prepare(`SELECT id, time_created, ${ORDINAL_SUMMARY_FLAG_SQL} AND ${ORDINAL_STOP_FLAG_SQL} AS excluded
31350
+ FROM message WHERE session_id = ? AND id = ?`).get(sessionId, messageId);
31351
+ if (typeof row?.id !== "string" || typeof row.time_created !== "number")
31352
+ return null;
31353
+ return { id: row.id, timeCreated: row.time_created, excluded: row.excluded === 1 };
31354
+ });
31355
+ if (!counted || counted.target.excluded)
31356
+ return null;
31357
+ return counted.ordinal > 0 ? counted.ordinal : null;
31358
+ }
31359
+ function readRawSessionMessageByIdFromDb(db, sessionId, messageId) {
31360
+ const counted = canonicalOrdinalOf(db, sessionId, () => {
31361
+ const row = db.prepare("SELECT id, data, time_created, time_updated FROM message WHERE session_id = ? AND id = ?").get(sessionId, messageId);
31362
+ if (!row || !isRawMessageRow(row) || typeof row.time_created !== "number")
31363
+ return null;
31364
+ const parsed = parseJsonRecord(row.data);
31365
+ if (!parsed || isRawCompactionSummaryInfo(parsed))
31366
+ return null;
31367
+ return { ...row, timeCreated: row.time_created, info: parsed };
31368
+ });
31369
+ if (!counted)
31370
+ return null;
31371
+ const { target: row, ordinal } = counted;
31372
+ const info = row.info;
30401
31373
  if (ordinal <= 0) {
30402
31374
  return null;
30403
31375
  }
@@ -30461,8 +31433,147 @@ function readRawSeedTailFromDb(db, sessionId, boundaryId) {
30461
31433
  throw new Error("state_sync materialized boundary is missing from raw storage");
30462
31434
  return messages;
30463
31435
  }
30464
- var RAW_MESSAGE_PARTS_BY_ID_SQL = "SELECT message_id, data, time_updated FROM part WHERE +session_id = ? AND likelihood(message_id = ?, 0.000001) ORDER BY time_created ASC, id ASC", RAW_SUMMARY_TEXT_MAX_CHARS = 8192, RAW_SUMMARY_ARG_MAX_CHARS = 512, RAW_SUMMARY_TOOL_INPUT_KEYS, RAW_SUMMARY_PART_DATA_SQL;
30465
- var init_read_session_raw = __esm(() => {
31436
+ function readBoundaryEndpointRows(db, sessionId, ids, blockIds, into) {
31437
+ if (ids.length > 0) {
31438
+ boundaryEndpointRowsRead += ids.length;
31439
+ const wanted = JSON.stringify(ids);
31440
+ const ordinals = new Map;
31441
+ for (const row of db.prepare(BOUNDARY_ORDINAL_SQL).all(sessionId, wanted)) {
31442
+ ordinals.set(row.id, row.ordinal);
31443
+ }
31444
+ const rows = db.prepare(BOUNDARY_MESSAGE_SQL).all(sessionId, wanted);
31445
+ for (const id of ids)
31446
+ into.set(id, null);
31447
+ for (const row of rows) {
31448
+ if (!isRawMessageRow(row) || typeof row.time_created !== "number")
31449
+ continue;
31450
+ const info = parseJsonRecord(row.data);
31451
+ if (!info || isRawCompactionSummaryInfo(info))
31452
+ continue;
31453
+ into.set(row.id, {
31454
+ ordinal: ordinals.get(row.id) ?? null,
31455
+ createdAt: row.time_created
31456
+ });
31457
+ }
31458
+ }
31459
+ const servable = blockIds.filter((id) => {
31460
+ const endpoint = into.get(id);
31461
+ return endpoint && endpoint.lastBlockIndex === undefined;
31462
+ });
31463
+ if (servable.length === 0)
31464
+ return;
31465
+ const counts = new Map;
31466
+ for (const row of db.prepare(BOUNDARY_BLOCK_COUNT_SQL).all(sessionId, JSON.stringify(servable))) {
31467
+ counts.set(row.id, row.blocks);
31468
+ }
31469
+ for (const id of servable) {
31470
+ const endpoint = into.get(id);
31471
+ if (!endpoint)
31472
+ continue;
31473
+ const blocks = counts.get(id) ?? 0;
31474
+ into.set(id, { ...endpoint, lastBlockIndex: blocks > 0 ? blocks - 1 : 0 });
31475
+ }
31476
+ }
31477
+ function readRawSessionBoundaryEndpointsFromDb(db, sessionId, ids, blockIds = []) {
31478
+ const wanted = [...new Set(ids)];
31479
+ const wantedBlocks = [...new Set(blockIds)];
31480
+ let sessions = boundaryEndpointCaches.get(db);
31481
+ if (!sessions) {
31482
+ sessions = new Map;
31483
+ boundaryEndpointCaches.set(db, sessions);
31484
+ }
31485
+ for (let attempt = 1;; attempt += 1) {
31486
+ const before = readStoreStamp(db);
31487
+ const cached = sessions.get(sessionId);
31488
+ const reuse = attempt === 1 && cached && sameStoreStamp(cached.stamp, before);
31489
+ const endpoints = new Map(reuse ? cached.endpoints : []);
31490
+ const missing = wanted.filter((id) => !endpoints.has(id));
31491
+ withReadSnapshot(db, () => readBoundaryEndpointRows(db, sessionId, missing, wantedBlocks, endpoints));
31492
+ const unchanged = before !== null && sameStoreStamp(before, readStoreStamp(db));
31493
+ if (unchanged) {
31494
+ sessions.delete(sessionId);
31495
+ sessions.set(sessionId, { stamp: before, endpoints });
31496
+ while (sessions.size > BOUNDARY_ENDPOINT_CACHE_SESSIONS) {
31497
+ const oldest = sessions.keys().next().value;
31498
+ if (oldest === undefined)
31499
+ break;
31500
+ sessions.delete(oldest);
31501
+ }
31502
+ } else if (reuse) {
31503
+ continue;
31504
+ }
31505
+ return new Map(wanted.map((id) => [id, endpoints.get(id) ?? null]));
31506
+ }
31507
+ }
31508
+ var RAW_MESSAGE_PARTS_BY_ID_SQL = "SELECT message_id, data, time_updated FROM part WHERE +session_id = ? AND likelihood(message_id = ?, 0.000001) ORDER BY time_created ASC, id ASC", RAW_METADATA_KEEP_CHARS = 16384, RAW_METADATA_KEPT_KEYS, RAW_PART_DATA_SQL, rawMessagePageRowsVisited = 0, RAW_SUMMARY_TEXT_MAX_CHARS = 8192, RAW_SUMMARY_ARG_MAX_CHARS = 512, RAW_SUMMARY_TOOL_INPUT_KEYS, RAW_SUMMARY_PART_DATA_SQL, ORDINAL_SUMMARY_FLAG_SQL = `(CASE WHEN json_valid(data) = 1
31509
+ THEN COALESCE(json_extract(data, '$.summary'), 0)
31510
+ ELSE 0 END = 1)`, ORDINAL_STOP_FLAG_SQL = `(CASE WHEN json_valid(data) = 1
31511
+ THEN COALESCE(json_extract(data, '$.finish'), '')
31512
+ ELSE '' END = 'stop')`, ORDINAL_RANGE_FROM_START_SQL, ORDINAL_RANGE_BETWEEN_SQL, ordinalWatermarks, ordinalWatermarkEpochs, ordinalRowsVisited = 0, STABLE_STORE_ATTEMPTS = 3, EXCLUDED_ROW_SQL, SUMMARY_ANCHOR_SPAN = 256, SUMMARY_ANCHOR_LIMIT = 64, summaryCandidateSets, summaryCandidateEpochs, writtenSummaryIds, indexedCountUnsupported, ordinalJsonRowsRead = 0, summaryFullScans = 0, ANCHOR_ROWS_SQL, SUMMARY_FULL_SCAN_SQL, SUMMARY_SCAN_AFTER_SQL, ANCHOR_CHECK_SQL = "SELECT rowid AS rid, id FROM message WHERE rowid IN (SELECT value FROM json_each(?))", ordinalCheckpoints, ordinalSummarySeeds, ORDINAL_LOOKUP_LIMIT = 512, RawOrdinalsNotReady, CANONICAL_COUNT_SQL, BOUNDARY_ORDINAL_SQL = `
31513
+ SELECT id, ordinal FROM (
31514
+ SELECT id, ROW_NUMBER() OVER (ORDER BY time_created, id) AS ordinal
31515
+ FROM message
31516
+ WHERE session_id = ?
31517
+ AND typeof(id) = 'text' AND typeof(data) = 'text'
31518
+ AND CASE WHEN json_valid(data) THEN NOT (
31519
+ COALESCE(json_type(data, '$.summary'), '') = 'true'
31520
+ AND COALESCE(json_extract(data, '$.finish'), '') = 'stop'
31521
+ ) ELSE 1 END
31522
+ )
31523
+ WHERE id IN (SELECT value FROM json_each(?))`, BOUNDARY_MESSAGE_SQL = `
31524
+ SELECT id, data, time_created FROM message
31525
+ WHERE +session_id = ? AND id IN (SELECT value FROM json_each(?))`, BOUNDARY_BLOCK_COUNT_SQL = `
31526
+ SELECT message_id AS id, SUM(
31527
+ CASE
31528
+ WHEN NOT json_valid(data) OR json_type(data) <> 'object' THEN 0
31529
+ ELSE CASE (CASE WHEN json_type(data, '$.type') = 'text'
31530
+ THEN json_extract(data, '$.type') ELSE 'unknown' END)
31531
+ WHEN 'text' THEN CASE WHEN json_type(data, '$.ignored') = 'true' THEN 0 ELSE 1 END
31532
+ WHEN 'reasoning' THEN 1
31533
+ WHEN 'thinking' THEN 1
31534
+ WHEN 'redacted_thinking' THEN 1
31535
+ WHEN 'tool' THEN 1 + CASE
31536
+ WHEN json_type(data, '$.state') = 'object'
31537
+ AND json_type(data, '$.state.status') = 'text'
31538
+ AND json_extract(data, '$.state.status') IN ('completed', 'error')
31539
+ THEN 1 ELSE 0 END
31540
+ WHEN 'compaction' THEN 0
31541
+ WHEN 'step-finish' THEN 0
31542
+ WHEN 'snapshot' THEN 0
31543
+ WHEN 'patch' THEN 0
31544
+ WHEN 'agent' THEN 0
31545
+ WHEN 'retry' THEN 0
31546
+ ELSE 1
31547
+ END
31548
+ END) AS blocks
31549
+ FROM part
31550
+ WHERE +session_id = ?
31551
+ AND message_id IN (SELECT value FROM json_each(?))
31552
+ AND typeof(message_id) = 'text' AND typeof(data) = 'text'
31553
+ GROUP BY message_id`, boundaryEndpointCaches, BOUNDARY_ENDPOINT_CACHE_SESSIONS = 16, boundaryEndpointRowsRead = 0;
31554
+ var init_read_session_raw = __esm(async () => {
31555
+ await __promiseAll([
31556
+ init_sqlite(),
31557
+ init_raw_ordinal_warmup()
31558
+ ]);
31559
+ RAW_METADATA_KEPT_KEYS = [
31560
+ "description",
31561
+ "title",
31562
+ "userAnswer",
31563
+ "answers",
31564
+ "answer",
31565
+ "selectedOptions",
31566
+ "customInput",
31567
+ "results"
31568
+ ];
31569
+ RAW_PART_DATA_SQL = `CASE
31570
+ WHEN json_valid(data) = 1
31571
+ AND json_type(data, '$.state.metadata') = 'object'
31572
+ AND COALESCE(json_extract(data, '$.state.status'), '') <> 'error'
31573
+ AND length(json_extract(data, '$.state.metadata')) > ${RAW_METADATA_KEEP_CHARS}
31574
+ THEN json_set(data, '$.state.metadata', json_patch('{}', json_object(${RAW_METADATA_KEPT_KEYS.map((key) => `'${key}', json_extract(data, '$.state.metadata.${key}')`).join(", ")})))
31575
+ ELSE data
31576
+ END`;
30466
31577
  RAW_SUMMARY_TOOL_INPUT_KEYS = [
30467
31578
  "description",
30468
31579
  "filePath",
@@ -30487,6 +31598,269 @@ var init_read_session_raw = __esm(() => {
30487
31598
  )
30488
31599
  )
30489
31600
  END`;
31601
+ ORDINAL_RANGE_FROM_START_SQL = ordinalRangeSql(false);
31602
+ ORDINAL_RANGE_BETWEEN_SQL = ordinalRangeSql(true);
31603
+ ordinalWatermarks = new WeakMap;
31604
+ ordinalWatermarkEpochs = new Map;
31605
+ EXCLUDED_ROW_SQL = `${ORDINAL_SUMMARY_FLAG_SQL} AND ${ORDINAL_STOP_FLAG_SQL}`;
31606
+ summaryCandidateSets = new WeakMap;
31607
+ summaryCandidateEpochs = new Map;
31608
+ writtenSummaryIds = new Map;
31609
+ indexedCountUnsupported = new WeakSet;
31610
+ ANCHOR_ROWS_SQL = `SELECT rowid AS rid, id, 1 AS kind FROM message
31611
+ WHERE rowid > (SELECT MAX(rowid) FROM message) - ${SUMMARY_ANCHOR_SPAN}`;
31612
+ SUMMARY_FULL_SCAN_SQL = `${ANCHOR_ROWS_SQL}
31613
+ UNION ALL
31614
+ SELECT rowid, id, 2 FROM message WHERE session_id = ? AND ${ORDINAL_SUMMARY_FLAG_SQL}
31615
+ UNION ALL
31616
+ SELECT COUNT(*), NULL, 4 FROM message WHERE session_id = ?`;
31617
+ SUMMARY_SCAN_AFTER_SQL = `${ANCHOR_ROWS_SQL}
31618
+ UNION ALL
31619
+ SELECT rowid, id, CASE WHEN ${ORDINAL_SUMMARY_FLAG_SQL} THEN 2 ELSE 3 END
31620
+ FROM message WHERE rowid > ? AND +session_id = ?`;
31621
+ ordinalCheckpoints = new WeakMap;
31622
+ ordinalSummarySeeds = new WeakMap;
31623
+ RawOrdinalsNotReady = class RawOrdinalsNotReady extends Error {
31624
+ constructor() {
31625
+ super("canonical ordinals are warming off-thread; retry after preparation");
31626
+ }
31627
+ };
31628
+ CANONICAL_COUNT_SQL = new Map;
31629
+ boundaryEndpointCaches = new WeakMap;
31630
+ });
31631
+
31632
+ // ../plugin/src/hooks/magic-context/raw-ordinal-warmup.ts
31633
+ import { Worker } from "node:worker_threads";
31634
+ function stopRawSessionOrdinalWarmups() {
31635
+ for (const stop of [...activeWorkers.values()])
31636
+ stop("stopped");
31637
+ }
31638
+ function defaultEntry() {
31639
+ return new URL(new URL(import.meta.url).pathname.endsWith(".ts") ? "./raw-ordinal-warmup-worker.ts" : "./raw-ordinal-warmup-worker.js", import.meta.url);
31640
+ }
31641
+ function ensureExitHook() {
31642
+ if (exitHookRegistered)
31643
+ return;
31644
+ exitHookRegistered = true;
31645
+ const controller = new AbortController;
31646
+ controller.signal.addEventListener("abort", stopRawSessionOrdinalWarmups, { once: true });
31647
+ registerExitAbort(controller);
31648
+ }
31649
+ function runSessionWarmupWorker(input, options) {
31650
+ ensureExitHook();
31651
+ return new Promise((resolve) => {
31652
+ let worker;
31653
+ let deadline;
31654
+ let settled = false;
31655
+ const finish = (result) => {
31656
+ if (settled)
31657
+ return;
31658
+ settled = true;
31659
+ clearTimeout(deadline);
31660
+ if (worker) {
31661
+ activeWorkers.delete(worker);
31662
+ const stopped = worker.terminate();
31663
+ worker.unref();
31664
+ stopped.finally(() => {
31665
+ releaseIndexOwner(input);
31666
+ resolve(result);
31667
+ });
31668
+ return;
31669
+ }
31670
+ releaseIndexOwner(input);
31671
+ resolve(result);
31672
+ };
31673
+ const arm = (failure, ms) => {
31674
+ clearTimeout(deadline);
31675
+ deadline = setTimeout(() => finish({ failure, detail: `no reply within ${ms} ms` }), ms);
31676
+ deadline.unref?.();
31677
+ };
31678
+ try {
31679
+ worker = new Worker(options.entry ?? defaultEntry(), { workerData: input });
31680
+ } catch (error) {
31681
+ finish({ failure: "start-failed", detail: String(error) });
31682
+ return;
31683
+ }
31684
+ worker.unref();
31685
+ activeWorkers.set(worker, (failure) => finish({ failure, detail: "stopped" }));
31686
+ arm("not-accepted", options.acceptMs ?? ACCEPT_TIMEOUT_MS);
31687
+ worker.on("message", (reply) => {
31688
+ if (reply.kind === "accepted")
31689
+ arm("no-result", options.resultMs ?? RESULT_TIMEOUT_MS);
31690
+ else if (reply.kind === "scan")
31691
+ finish({ scan: reply.scan });
31692
+ else if (reply.kind === "tag-summary")
31693
+ finish({ summary: reply.summary });
31694
+ else if (reply.kind === "tag-delta")
31695
+ finish({ delta: reply.delta });
31696
+ else if (reply.kind === "indexed")
31697
+ finish({ inserted: reply.inserted, measurements: reply.measurements });
31698
+ else
31699
+ finish({ failure: "worker-error", detail: reply.error });
31700
+ });
31701
+ worker.on("error", (error) => finish({ failure: "worker-error", detail: String(error) }));
31702
+ worker.on("exit", (code) => finish({ failure: "exited", detail: `exit code ${code}` }));
31703
+ });
31704
+ }
31705
+ function prewarmRawSessionOrdinalsForDb(db, sessionId, options = {}) {
31706
+ if (isRawSessionSummaryWarm(db, sessionId))
31707
+ return Promise.resolve("warm");
31708
+ if (!db.prepare("SELECT 1 FROM sqlite_master WHERE name = 'message' AND type IN ('table', 'view')").get())
31709
+ return Promise.resolve("warm");
31710
+ const path = getSqliteDatabasePath(db);
31711
+ if (!path) {
31712
+ countRawSessionMessageOrdinalsFromDb(db, sessionId);
31713
+ return Promise.resolve("warm");
31714
+ }
31715
+ let connectionId = connectionIds.get(db);
31716
+ if (connectionId === undefined) {
31717
+ connectionId = ++nextConnectionId;
31718
+ connectionIds.set(db, connectionId);
31719
+ }
31720
+ const key = `${path}\x00${sessionId}\x00${connectionId}`;
31721
+ let shared = inFlight.get(key);
31722
+ if (!shared) {
31723
+ const failed = failedAt.get(key);
31724
+ if (failed !== undefined && Date.now() - failed < FAILURE_BACKOFF_MS)
31725
+ return Promise.resolve("failed");
31726
+ const epoch = getRawSessionSummaryEpoch(sessionId);
31727
+ const started = performance.now();
31728
+ const result = runSessionWarmupWorker({ path, sessionId, previous: rawOrdinalSummarySeed(db, sessionId) }, options).then((outcome) => {
31729
+ if ("failure" in outcome)
31730
+ recordFailure(key, sessionId, outcome.failure, outcome.detail);
31731
+ return outcome;
31732
+ }).finally(() => {
31733
+ if (inFlight.get(key) === scan)
31734
+ inFlight.delete(key);
31735
+ });
31736
+ const scan = { epoch, started, result };
31737
+ inFlight.set(key, scan);
31738
+ shared = scan;
31739
+ }
31740
+ const awaited = shared;
31741
+ return awaited.result.then((result) => {
31742
+ if (!("scan" in result))
31743
+ return "failed";
31744
+ try {
31745
+ if (installRawSessionSummaryScan(db, sessionId, result.scan, awaited.epoch, true)) {
31746
+ failedAt.delete(key);
31747
+ log(`[magic-context] canonical ordinals of ${sessionId} warmed off-thread (${result.scan.sessionRows} messages, ${Math.round(performance.now() - awaited.started)} ms)`);
31748
+ return "warm";
31749
+ }
31750
+ } catch {}
31751
+ recordFailure(key, sessionId, "superseded", `session removed during scan: epoch=${awaited.epoch}/${getRawSessionSummaryEpoch(sessionId)}`);
31752
+ return "failed";
31753
+ });
31754
+ }
31755
+ function recordFailure(key, sessionId, failure, detail) {
31756
+ failures.set(failure, (failures.get(failure) ?? 0) + 1);
31757
+ if (failure !== "superseded")
31758
+ failedAt.set(key, Date.now());
31759
+ log(`[magic-context] WARN canonical ordinal warm-up failed for ${sessionId} (${failure}: ${detail}); counts remain bounded and callers must defer or retry`);
31760
+ }
31761
+ var ACCEPT_TIMEOUT_MS = 2000, RESULT_TIMEOUT_MS, FAILURE_BACKOFF_MS, inFlight, connectionIds, nextConnectionId = 0, failedAt, activeWorkers, failures, exitHookRegistered = false;
31762
+ var init_raw_ordinal_warmup = __esm(async () => {
31763
+ init_exit_abort_registry();
31764
+ init_logger();
31765
+ await __promiseAll([
31766
+ init_sqlite(),
31767
+ init_read_session_raw()
31768
+ ]);
31769
+ RESULT_TIMEOUT_MS = 10 * 60000;
31770
+ FAILURE_BACKOFF_MS = 5 * 60000;
31771
+ inFlight = new Map;
31772
+ connectionIds = new WeakMap;
31773
+ failedAt = new Map;
31774
+ activeWorkers = new Map;
31775
+ failures = new Map;
31776
+ });
31777
+
31778
+ // ../plugin/src/hooks/magic-context/read-session-db.ts
31779
+ function openCodeDbExists() {
31780
+ return harnessOwnsOpenCodeStore() && openCodeDbPathExists(resolveOpenCodeDbPath());
31781
+ }
31782
+ function closeCachedReadOnlyDb() {
31783
+ if (!cachedReadOnlyDb) {
31784
+ return;
31785
+ }
31786
+ try {
31787
+ closeQuietly(cachedReadOnlyDb.db);
31788
+ } catch (error) {
31789
+ log("[magic-context] failed to close cached OpenCode read-only DB:", error);
31790
+ } finally {
31791
+ cachedReadOnlyDb = null;
31792
+ }
31793
+ }
31794
+ function getReadOnlySessionDb() {
31795
+ if (!harnessOwnsOpenCodeStore()) {
31796
+ throw new Error("OpenCode session database is not readable from a Pi-compatible process; its history lives in Pi sessions");
31797
+ }
31798
+ const resolution = resolveOpenCodeDbPath();
31799
+ const dbPath = resolution.path;
31800
+ if (!openCodeDbPathExists(resolution)) {
31801
+ throw new Error(`OpenCode session database is unavailable at ${dbPath} (source=${resolution.source})`);
31802
+ }
31803
+ if (cachedReadOnlyDb?.path === dbPath) {
31804
+ return cachedReadOnlyDb.db;
31805
+ }
31806
+ closeCachedReadOnlyDb();
31807
+ const db = new Database(dbPath, { readonly: true });
31808
+ try {
31809
+ assertOpenCodeStoreGeneration(db, "v1", dbPath);
31810
+ } catch (error) {
31811
+ closeQuietly(db);
31812
+ throw error;
31813
+ }
31814
+ cachedReadOnlyDb = { path: dbPath, db };
31815
+ clearOpenCodeDbReadFailure();
31816
+ return db;
31817
+ }
31818
+ function withReadOnlySessionDb(fn) {
31819
+ return fn(getReadOnlySessionDb());
31820
+ }
31821
+ function getRawSessionMessageCountFromDb(db, sessionId) {
31822
+ const row = db.prepare(`SELECT COUNT(*) as count FROM message WHERE session_id = ?
31823
+ AND NOT (COALESCE(json_extract(data, '$.summary'), 0) = 1
31824
+ AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')`).get(sessionId);
31825
+ return typeof row?.count === "number" ? row.count : 0;
31826
+ }
31827
+ function logProbeFailureOnce(resolution, error) {
31828
+ const failure = recordOpenCodeDbReadFailure(resolution, error);
31829
+ if (!claimOpenCodeDbDiagnosticOnce("session-state-probe", resolution))
31830
+ return;
31831
+ const message = `[magic-context] OpenCode DB probe failed: path=${resolution.path} source=${resolution.source} cause=${failure.message}`;
31832
+ probeLogObserverForTests?.(message);
31833
+ log(message);
31834
+ }
31835
+ function getMessageTimesFromOpenCodeDb(sessionId, messageIds) {
31836
+ const result = new Map;
31837
+ if (messageIds.length === 0 || !harnessOwnsOpenCodeStore())
31838
+ return result;
31839
+ try {
31840
+ withReadOnlySessionDb((db) => {
31841
+ const placeholders = messageIds.map(() => "?").join(",");
31842
+ const rows = db.prepare(`SELECT id, time_created FROM message WHERE +session_id = ? AND id IN (${placeholders})`).all(sessionId, ...messageIds);
31843
+ for (const row of rows) {
31844
+ if (typeof row.id === "string" && typeof row.time_created === "number") {
31845
+ result.set(row.id, row.time_created);
31846
+ }
31847
+ }
31848
+ });
31849
+ } catch (error) {
31850
+ logProbeFailureOnce(resolveOpenCodeDbPath(), error);
31851
+ }
31852
+ return result;
31853
+ }
31854
+ var cachedReadOnlyDb = null, trackedSessions, pendingParts, probeLogObserverForTests;
31855
+ var init_read_session_db = __esm(async () => {
31856
+ init_logger();
31857
+ init_opencode_db_path();
31858
+ await __promiseAll([
31859
+ init_sqlite(),
31860
+ init_read_session_raw()
31861
+ ]);
31862
+ trackedSessions = new Map;
31863
+ pendingParts = new Map;
30490
31864
  });
30491
31865
 
30492
31866
  // ../plugin/src/hooks/magic-context/read-session-true-raw-tokens.ts
@@ -30538,7 +31912,7 @@ function callIdFromPart(part) {
30538
31912
  const state = isRecord2(part.state) ? part.state : null;
30539
31913
  return state ? firstStringField(state, ["callID", "callId", "toolCallId", "tool_call_id", "id"]) ?? "" : "";
30540
31914
  }
30541
- function toolSignalFromPart(part) {
31915
+ function toolSignalFromPart(part, withText = true) {
30542
31916
  if (!isRecord2(part))
30543
31917
  return null;
30544
31918
  const type = partType(part);
@@ -30557,8 +31931,8 @@ function toolSignalFromPart(part) {
30557
31931
  callId,
30558
31932
  hasInput: hasInput || openInvocation,
30559
31933
  hasOutput,
30560
- inputText: hasInput && state ? stringValue(state.input) : "",
30561
- outputText: hasOutput ? stringValue(outputValue) : ""
31934
+ inputText: withText && hasInput && state ? stringValue(state.input) : "",
31935
+ outputText: withText && hasOutput ? stringValue(outputValue) : ""
30562
31936
  };
30563
31937
  }
30564
31938
  if (type === "tool-invocation") {
@@ -30567,7 +31941,7 @@ function toolSignalFromPart(part) {
30567
31941
  callId,
30568
31942
  hasInput: args !== undefined,
30569
31943
  hasOutput: false,
30570
- inputText: args !== undefined ? stringValue(args) : "",
31944
+ inputText: withText && args !== undefined ? stringValue(args) : "",
30571
31945
  outputText: ""
30572
31946
  };
30573
31947
  }
@@ -30577,7 +31951,7 @@ function toolSignalFromPart(part) {
30577
31951
  callId,
30578
31952
  hasInput: input !== undefined,
30579
31953
  hasOutput: false,
30580
- inputText: input !== undefined ? stringValue(input) : "",
31954
+ inputText: withText && input !== undefined ? stringValue(input) : "",
30581
31955
  outputText: ""
30582
31956
  };
30583
31957
  }
@@ -30588,7 +31962,7 @@ function toolSignalFromPart(part) {
30588
31962
  hasInput: false,
30589
31963
  hasOutput: content !== undefined,
30590
31964
  inputText: "",
30591
- outputText: content !== undefined ? textFromToolResultContent(content) : ""
31965
+ outputText: withText && content !== undefined ? textFromToolResultContent(content) : ""
30592
31966
  };
30593
31967
  }
30594
31968
  return null;
@@ -30598,7 +31972,7 @@ function buildToolArcs(messages) {
30598
31972
  const arcs = [];
30599
31973
  for (const message of messages) {
30600
31974
  for (const part of message.parts) {
30601
- const signal = toolSignalFromPart(part);
31975
+ const signal = toolSignalFromPart(part, false);
30602
31976
  if (!signal || signal.callId.length === 0)
30603
31977
  continue;
30604
31978
  if (signal.hasInput && signal.hasOutput) {
@@ -30637,6 +32011,7 @@ function buildToolArcs(messages) {
30637
32011
  }
30638
32012
  var MAX_MESSAGE_CACHE_KEY_BYTES, messageEstimateCache, messageEstimateKeysByField;
30639
32013
  var init_read_session_true_raw_tokens = __esm(() => {
32014
+ init_provider_visible_parts();
30640
32015
  init_read_session_formatting();
30641
32016
  MAX_MESSAGE_CACHE_KEY_BYTES = 64 * 1024 * 1024;
30642
32017
  messageEstimateCache = new Map;
@@ -30798,8 +32173,10 @@ __export(exports_read_session_chunk, {
30798
32173
  hasMeaningfulUserText: () => hasMeaningfulUserText,
30799
32174
  hasRawMessageProvider: () => hasRawMessageProvider,
30800
32175
  hasRawSessionMessageById: () => hasRawSessionMessageById,
32176
+ prepareRawSessionOrdinals: () => prepareRawSessionOrdinals,
30801
32177
  primeInMemoryTailRawMessageCache: () => primeInMemoryTailRawMessageCache,
30802
32178
  primeTailRawMessageCache: () => primeTailRawMessageCache,
32179
+ readRawSessionBoundaryEndpoints: () => readRawSessionBoundaryEndpoints,
30803
32180
  readRawSessionMessageById: () => readRawSessionMessageById,
30804
32181
  readRawSessionMessageIdOrdinals: () => readRawSessionMessageIdOrdinals,
30805
32182
  readRawSessionMessageIdOrdinalsForRange: () => readRawSessionMessageIdOrdinalsForRange,
@@ -31068,6 +32445,36 @@ function readRawSessionMessageRange(sessionId, fromOrdinal, toOrdinal) {
31068
32445
  }
31069
32446
  return messages;
31070
32447
  }
32448
+ async function prepareRawSessionOrdinals(sessionId) {
32449
+ if (sessionProviders.get(sessionId)?.provider)
32450
+ return true;
32451
+ if (!openCodeDbExists())
32452
+ return true;
32453
+ const db = withReadOnlySessionDb((connection) => connection);
32454
+ let timer;
32455
+ const deadline = Date.now() + 250;
32456
+ const prepare = async () => {
32457
+ for (let attempt = 0;attempt < 3 && Date.now() < deadline; attempt++) {
32458
+ if (await prewarmRawSessionOrdinalsForDb(db, sessionId) === "warm")
32459
+ return true;
32460
+ }
32461
+ return false;
32462
+ };
32463
+ try {
32464
+ const ready = await Promise.race([
32465
+ prepare(),
32466
+ new Promise((resolve) => {
32467
+ timer = setTimeout(() => resolve(false), 250);
32468
+ })
32469
+ ]);
32470
+ if (!ready) {
32471
+ sessionLog(sessionId, "WARN canonical ordinal preparation unavailable within 250ms; exact reads must defer or retry");
32472
+ }
32473
+ return ready;
32474
+ } finally {
32475
+ clearTimeout(timer);
32476
+ }
32477
+ }
31071
32478
  function primeTailRawMessageCache(args) {
31072
32479
  const { sessionId, lastCompartmentEnd, anchorMessageId } = args;
31073
32480
  if (!activeRawMessageCache)
@@ -31182,6 +32589,13 @@ function readRawSessionMessagePartsById(sessionId, messageId, onQuery) {
31182
32589
  return null;
31183
32590
  return withReadOnlySessionDb((db) => readRawSessionMessagePartsByIdFromDb(db, sessionId, messageId, onQuery));
31184
32591
  }
32592
+ function readRawSessionBoundaryEndpoints(sessionId, ids, blockIds) {
32593
+ if (sessionProviders.get(sessionId)?.provider)
32594
+ return null;
32595
+ if (!openCodeDbExists())
32596
+ return new Map(ids.map((id) => [id, null]));
32597
+ return withReadOnlySessionDb((db) => readRawSessionBoundaryEndpointsFromDb(db, sessionId, ids, blockIds));
32598
+ }
31185
32599
  function hasRawSessionMessageById(sessionId, messageId) {
31186
32600
  const provider = sessionProviders.get(sessionId)?.provider;
31187
32601
  if (provider?.hasMessageById)
@@ -31702,22 +33116,28 @@ function readRawSessionSeedTail(sessionId, boundaryId, onQuery) {
31702
33116
  }
31703
33117
  var BLOCK_TOKEN_MEMO_MAX = 2048, blockTokenMemo, activeRawMessageCache = null, activeAbsoluteCountCache = null, sessionProviders, boundedProviderWrappers, SYSTEM_REMINDER_BLOCK_REGEX, RAW_MESSAGE_RANGE_PAGE_SIZE = 100, RAW_MESSAGE_VISIT_PAGE_SIZE = 50, RAW_SESSION_TAG_KEY_PAGE_SIZE = 32, PROTECTED_TAIL_USER_TURNS = 5;
31704
33118
  var init_read_session_chunk = __esm(async () => {
31705
- init_storage_tags();
31706
33119
  init_historian_tool_expansions();
33120
+ init_logger();
31707
33121
  init_read_session_formatting();
31708
- init_read_session_raw();
31709
33122
  init_read_session_true_raw_tokens();
31710
33123
  init_system_injection_stripper();
31711
33124
  init_tag_part_guards();
31712
33125
  init_tool_drop_target();
31713
33126
  init_read_session_formatting();
31714
- await init_read_session_db();
33127
+ await __promiseAll([
33128
+ init_storage_tags(),
33129
+ init_raw_ordinal_warmup(),
33130
+ init_read_session_db(),
33131
+ init_read_session_raw()
33132
+ ]);
31715
33133
  blockTokenMemo = new Map;
31716
33134
  sessionProviders = new Map;
31717
33135
  boundedProviderWrappers = new WeakMap;
31718
33136
  SYSTEM_REMINDER_BLOCK_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
31719
33137
  readRawSessionMessages.readPage = readRawSessionMessagePage;
31720
33138
  readRawSessionMessages.getCount = getRawSessionMessageOrdinalCount;
33139
+ readRawSessionMessages.prepare = prepareRawSessionOrdinals;
33140
+ readRawSessionMessageById.prepare = prepareRawSessionOrdinals;
31721
33141
  });
31722
33142
 
31723
33143
  // ../plugin/src/features/magic-context/message-time-backfill.ts
@@ -33578,6 +34998,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
33578
34998
  const version = getPersistedSchemaVersion(db);
33579
34999
  if (version === 0 || version >= 95)
33580
35000
  installV95PerfSchema(db, false, version === 0);
35001
+ installTagIdentityRevisionTrigger(db);
33581
35002
  }
33582
35003
  function healWedgedChannel2Claims(db) {
33583
35004
  try {
@@ -33657,6 +35078,7 @@ function openDatabase(dbPathOrOptions) {
33657
35078
  } else
33658
35079
  runMigrations(db);
33659
35080
  ensureContextStoreUuid(db);
35081
+ installTagIdentityRevisionTrigger(db);
33660
35082
  return finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion);
33661
35083
  } catch (error) {
33662
35084
  if (db)
@@ -33691,6 +35113,7 @@ var init_storage_db = __esm(async () => {
33691
35113
  init_migration_v95_perf_indexes();
33692
35114
  init_migration_worker_client();
33693
35115
  init_storage_schema_helpers();
35116
+ init_storage_tag_identity_revision();
33694
35117
  init_tool_definition_tokens();
33695
35118
  init_tool_owner_backfill();
33696
35119
  await __promiseAll([
@@ -33896,6 +35319,9 @@ var init_config_location_migration = __esm(() => {
33896
35319
  });
33897
35320
 
33898
35321
  // ../plugin/src/features/magic-context/dreamer/task-registry.ts
35322
+ function availableDreamTasks(smartNotesEnabled = true) {
35323
+ return CANONICAL_DREAM_TASKS.filter((task) => smartNotesEnabled || task !== "evaluate-smart-notes");
35324
+ }
33899
35325
  var CANONICAL_DREAM_TASKS, DREAM_TOOL_LOOP_TOKEN_BUDGETS, MEMORY_DOMAIN_TASKS, MEMORY_DOMAIN_SET;
33900
35326
  var init_task_registry = __esm(() => {
33901
35327
  CANONICAL_DREAM_TASKS = [
@@ -34646,8 +36072,10 @@ function openCodeConfigFileChain(directory, hostGeneration) {
34646
36072
  const globalSupplementary = getOpenCodeConfigDirs().filter((dir) => dir !== globalDir).flatMap((dir) => [join17(dir, "opencode.json"), join17(dir, "opencode.jsonc")]);
34647
36073
  const explicitConfig = process.env.OPENCODE_CONFIG?.trim();
34648
36074
  const explicit = explicitConfig ? [explicitConfig] : [];
34649
- const direct = [join17(directory, "opencode.json"), join17(directory, "opencode.jsonc")];
34650
- const projectSupplementary = [
36075
+ const disableProjectConfig = process.env.OPENCODE_DISABLE_PROJECT_CONFIG?.toLowerCase();
36076
+ const skipProject = hostGeneration === "v1" && (disableProjectConfig === "true" || disableProjectConfig === "1");
36077
+ const direct = skipProject ? [] : [join17(directory, "opencode.json"), join17(directory, "opencode.jsonc")];
36078
+ const projectSupplementary = skipProject ? [] : [
34651
36079
  join17(directory, ".opencode", "opencode.json"),
34652
36080
  join17(directory, ".opencode", "opencode.jsonc")
34653
36081
  ];
@@ -35877,6 +37305,24 @@ function readCachedPluginVersion(pluginCacheDir) {
35877
37305
  return;
35878
37306
  }
35879
37307
  }
37308
+ function readOpenCodePluginConfigUse(config, configDir) {
37309
+ const use = {
37310
+ referencedSpecs: {
37311
+ opencode1: readReferencedOpenCodePluginSpecs(config, "opencode1"),
37312
+ opencode2: readReferencedOpenCodePluginSpecs(config, "opencode2")
37313
+ },
37314
+ loadedFrom: {}
37315
+ };
37316
+ for (const { key, entry } of readPluginEntries(config)) {
37317
+ if (!isDevPathPluginEntry(entry, configDir))
37318
+ continue;
37319
+ const specifier = pluginEntryPackage(entry) ?? undefined;
37320
+ if (key === "plugin")
37321
+ use.loadedFrom.opencode1 ??= specifier;
37322
+ use.loadedFrom.opencode2 ??= specifier;
37323
+ }
37324
+ return use;
37325
+ }
35880
37326
  async function clearPluginCache(options = {}, deps = {}) {
35881
37327
  const remove = deps.remove ?? ((path) => rmSync6(path, { recursive: true, force: true }));
35882
37328
  const pluginCacheRoots = getOpenCodePluginCacheRoots();
@@ -35889,6 +37335,15 @@ async function clearPluginCache(options = {}, deps = {}) {
35889
37335
  path,
35890
37336
  cached: readCachedPluginVersion(path)
35891
37337
  }));
37338
+ if (options.referencedSpecs !== undefined && !options.referencedSpecs.has("latest")) {
37339
+ const firstEntry = cacheEntries[0];
37340
+ return {
37341
+ action: "unused",
37342
+ path: firstEntry?.path ?? pluginCacheRoots[0] ?? "",
37343
+ paths: cacheEntries.map((entry) => entry.path),
37344
+ cached: firstEntry?.cached
37345
+ };
37346
+ }
35892
37347
  if (options.force !== true && latestVersion === undefined) {
35893
37348
  const firstEntry = cacheEntries[0];
35894
37349
  return {
@@ -35960,15 +37415,24 @@ async function clearPluginCache(options = {}, deps = {}) {
35960
37415
  }
35961
37416
  var init_doctor_opencode_cache = __esm(async () => {
35962
37417
  init_opencode_plugin_cache();
35963
- await init_doctor_opencode2_cache();
37418
+ init_opencode_plugin_registration();
37419
+ await __promiseAll([
37420
+ init_opencode(),
37421
+ init_doctor_opencode2_cache()
37422
+ ]);
35964
37423
  });
35965
37424
 
35966
37425
  // src/commands/doctor-cached-plugin-fence.ts
35967
37426
  import { existsSync as existsSync29 } from "node:fs";
35968
37427
  import { dirname as dirname15 } from "node:path";
37428
+ function isReferenced(sources, host, spec) {
37429
+ const specs = sources.referencedSpecs?.[host];
37430
+ return specs === undefined || specs.has(spec);
37431
+ }
35969
37432
  function listCachedOpenCodePluginFences(sources = {}) {
35970
37433
  const fences = [];
35971
- for (const root of sources.openCodeV1Roots ?? getOpenCodePluginCacheRoots()) {
37434
+ const v1Referenced = isReferenced(sources, "opencode1", "latest");
37435
+ for (const root of v1Referenced ? sources.openCodeV1Roots ?? getOpenCodePluginCacheRoots() : []) {
35972
37436
  const packageJson = getOpenCodePluginPackageJsonPath(root);
35973
37437
  if (!existsSync29(packageJson))
35974
37438
  continue;
@@ -35982,6 +37446,8 @@ function listCachedOpenCodePluginFences(sources = {}) {
35982
37446
  for (const slot of listOpenCodeV2PluginCacheSlots(sources.openCodeV2NpmCacheDir)) {
35983
37447
  if (!isOpenCodePluginDistTag(slot.spec))
35984
37448
  continue;
37449
+ if (!isReferenced(sources, "opencode2", slot.spec))
37450
+ continue;
35985
37451
  const generation = getOpenCodeV2ActiveGeneration(slot.slot);
35986
37452
  if (!generation)
35987
37453
  continue;
@@ -41170,11 +42636,6 @@ var init_user_facing_codes = __esm(() => {
41170
42636
  sentence: "Magic Context has no context.db.",
41171
42637
  action: "Run `npx @cortexkit/magic-context doctor store init`, then restart ck-mc."
41172
42638
  },
41173
- checkout_claim_held_elsewhere: {
41174
- code: "MC-C16",
41175
- sentence: "This request was not sent: this agent is checked out on another machine, so Magic Context will not work on its sessions here.",
41176
- action: "Move the agent back to this machine, then send your message again."
41177
- },
41178
42639
  single_store_migration_required: {
41179
42640
  code: "MC-C14",
41180
42641
  sentence: "Magic Context's Rust mode needs a one-time migration of its store.",
@@ -42765,7 +44226,9 @@ function parsePiConfig(rawConfig, recoveredTopLevelKeys = []) {
42765
44226
  delete configToMigrate.protected_tags;
42766
44227
  }
42767
44228
  const agentMigrated = migrateLegacyAgentEnabledInMemory(configToMigrate, preMigrationWarnings);
42768
- const migrated = migrateDreamerV2(migrateLegacyExperimental(agentMigrated, preMigrationWarnings), preMigrationWarnings);
44229
+ const migratedExperimental = migrateLegacyExperimental(agentMigrated, preMigrationWarnings);
44230
+ warnDeprecatedMuralModel(migratedExperimental, preMigrationWarnings);
44231
+ const migrated = migrateDreamerV2(migratedExperimental, preMigrationWarnings);
42769
44232
  const parsed = MagicContextConfigSchema.safeParse(migrated);
42770
44233
  if (parsed.success) {
42771
44234
  return { config: parsed.data, warnings: preMigrationWarnings };
@@ -43607,64 +45070,66 @@ async function runHealthChecks(options) {
43607
45070
  add(results, "fail", "OMP binary not found on PATH or in standard user bin directories");
43608
45071
  } else {
43609
45072
  const version = options.deps.getOmpVersion(omp.path);
43610
- if (!version)
43611
- add(results, "fail", `OMP at ${omp.path} could not report its version`);
43612
- else if (isOlderThan(version, MIN_OMP_VERSION)) {
43613
- add(results, "fail", `OMP ${version} is older than tested minimum ${MIN_OMP_VERSION}`);
43614
- } else
43615
- add(results, "pass", `OMP ${version} detected at ${omp.path}`);
43616
45073
  const plugins = options.deps.listOmpPlugins(omp.path);
43617
45074
  if (!plugins) {
43618
45075
  add(results, "fail", "`omp plugin list --json` failed or returned invalid JSON");
43619
45076
  } else {
43620
45077
  const plugin = plugins.find((entry) => entry.name === OMP_PLUGIN_PACKAGE);
43621
45078
  if (!plugin) {
43622
- add(results, "fail", `${OMP_PLUGIN_PACKAGE} is not installed in OMP`);
43623
- repairPlan.installPlugin = true;
43624
- } else if (!plugin.enabled) {
43625
- add(results, "fail", `${OMP_PLUGIN_PACKAGE} is installed but disabled in OMP`);
45079
+ add(results, "info", `${version ? `Oh My Pi ${version}` : "Oh My Pi"} found; Magic Context is not installed there (run setup to add it)`);
43626
45080
  repairPlan.installPlugin = true;
43627
45081
  } else {
43628
- add(results, "pass", `${OMP_PLUGIN_PACKAGE} ${plugin.version} is enabled`);
43629
- const manifest = pluginDeclaresOmp(plugin.path);
43630
- if (manifest === true)
43631
- add(results, "pass", "Plugin exposes an OMP/Pi extension manifest");
43632
- else if (manifest === false)
43633
- add(results, "fail", "Installed plugin has no OMP/Pi extension manifest");
43634
- else
43635
- add(results, "warn", "Could not inspect the installed plugin manifest");
43636
- }
43637
- }
43638
- const compaction = options.deps.getOmpSetting(omp.path, "compaction.enabled");
43639
- if (compaction === false)
43640
- add(results, "pass", "OMP native compaction is disabled");
43641
- else if (compaction === true) {
43642
- add(results, "fail", "OMP native compaction is enabled and conflicts with Magic Context");
43643
- repairPlan.disableCompaction = true;
43644
- } else
43645
- add(results, "fail", "Could not read OMP compaction.enabled");
43646
- const memory = options.deps.getOmpSetting(omp.path, "memory.backend");
43647
- if (memory === "off")
43648
- add(results, "pass", "OMP automatic memory backend is disabled");
43649
- else if (typeof memory === "string") {
43650
- add(results, "fail", `OMP memory.backend=${memory} duplicates Magic Context memory injection`);
43651
- repairPlan.disableMemory = true;
43652
- } else
43653
- add(results, "fail", "Could not read OMP memory.backend");
43654
- const nonGlobalSources = getOmpNonGlobalConfigSources(options.cwd);
43655
- if (nonGlobalSources.length > 0 && (repairPlan.disableCompaction || repairPlan.disableMemory)) {
43656
- add(results, "warn", "OMP project/overlay config owns effective conflicting settings; automatic global repair is disabled: " + nonGlobalSources.join(", "));
43657
- }
43658
- const reportedAgentDir = options.deps.runOmpCommand(omp.path, ["config", "path"], 1e4);
43659
- if (!reportedAgentDir.ok) {
43660
- add(results, "warn", "Could not verify OMP active agent directory");
43661
- } else {
43662
- const reportedPath = resolve15(reportedAgentDir.stdout);
43663
- const expectedPath = resolve15(getOmpAgentDir());
43664
- if (reportedPath === expectedPath) {
43665
- add(results, "pass", `OMP agent directory resolved to ${getOmpAgentDir()}`);
43666
- } else {
43667
- add(results, "fail", `OMP reports agent directory ${reportedAgentDir.stdout}, but Magic Context resolved ${getOmpAgentDir()}`);
45082
+ if (!version) {
45083
+ add(results, "fail", `OMP at ${omp.path} could not report its version`);
45084
+ } else if (isOlderThan(version, MIN_OMP_VERSION)) {
45085
+ add(results, "fail", `OMP ${version} is older than tested minimum ${MIN_OMP_VERSION}`);
45086
+ } else
45087
+ add(results, "pass", `OMP ${version} detected at ${omp.path}`);
45088
+ if (!plugin.enabled) {
45089
+ add(results, "fail", `${OMP_PLUGIN_PACKAGE} is installed but disabled in OMP`);
45090
+ repairPlan.installPlugin = true;
45091
+ } else {
45092
+ add(results, "pass", `${OMP_PLUGIN_PACKAGE} ${plugin.version} is enabled`);
45093
+ const manifest = pluginDeclaresOmp(plugin.path);
45094
+ if (manifest === true)
45095
+ add(results, "pass", "Plugin exposes an OMP/Pi extension manifest");
45096
+ else if (manifest === false)
45097
+ add(results, "fail", "Installed plugin has no OMP/Pi extension manifest");
45098
+ else
45099
+ add(results, "warn", "Could not inspect the installed plugin manifest");
45100
+ }
45101
+ const compaction = options.deps.getOmpSetting(omp.path, "compaction.enabled");
45102
+ if (compaction === false)
45103
+ add(results, "pass", "OMP native compaction is disabled");
45104
+ else if (compaction === true) {
45105
+ add(results, "fail", "OMP native compaction is enabled and conflicts with Magic Context");
45106
+ repairPlan.disableCompaction = true;
45107
+ } else
45108
+ add(results, "fail", "Could not read OMP compaction.enabled");
45109
+ const memory = options.deps.getOmpSetting(omp.path, "memory.backend");
45110
+ if (memory === "off")
45111
+ add(results, "pass", "OMP automatic memory backend is disabled");
45112
+ else if (typeof memory === "string") {
45113
+ add(results, "fail", `OMP memory.backend=${memory} duplicates Magic Context memory injection`);
45114
+ repairPlan.disableMemory = true;
45115
+ } else
45116
+ add(results, "fail", "Could not read OMP memory.backend");
45117
+ const nonGlobalSources = getOmpNonGlobalConfigSources(options.cwd);
45118
+ if (nonGlobalSources.length > 0 && (repairPlan.disableCompaction || repairPlan.disableMemory)) {
45119
+ add(results, "warn", "OMP project/overlay config owns effective conflicting settings; automatic global repair is disabled: " + nonGlobalSources.join(", "));
45120
+ }
45121
+ const reportedAgentDir = options.deps.runOmpCommand(omp.path, ["config", "path"], 1e4);
45122
+ if (!reportedAgentDir.ok) {
45123
+ add(results, "warn", "Could not verify OMP active agent directory");
45124
+ } else {
45125
+ const reportedPath = resolve15(reportedAgentDir.stdout);
45126
+ const expectedPath = resolve15(getOmpAgentDir());
45127
+ if (reportedPath === expectedPath) {
45128
+ add(results, "pass", `OMP agent directory resolved to ${getOmpAgentDir()}`);
45129
+ } else {
45130
+ add(results, "fail", `OMP reports agent directory ${reportedAgentDir.stdout}, but Magic Context resolved ${getOmpAgentDir()}`);
45131
+ }
45132
+ }
43668
45133
  }
43669
45134
  }
43670
45135
  }
@@ -46665,6 +48130,16 @@ function listDanglingCompartmentBoundaries(contextDb, openCodeDb, hostGeneration
46665
48130
  ORDER BY c.session_id ASC, c.sequence ASC`).all();
46666
48131
  const statement = openCodeDb.prepare(`SELECT 1 AS found FROM ${selection.table} WHERE session_id = ? AND id = ? LIMIT 1`);
46667
48132
  const exists = (sessionId, messageId) => statement.get(sessionId, messageId) != null;
48133
+ const sessionStatement = openCodeDb.prepare(`SELECT 1 AS found FROM ${selection.table} WHERE session_id = ? LIMIT 1`);
48134
+ const sessionPresence = new Map;
48135
+ const sessionInStore = (sessionId) => {
48136
+ let present = sessionPresence.get(sessionId);
48137
+ if (present === undefined) {
48138
+ present = sessionStatement.get(sessionId) != null;
48139
+ sessionPresence.set(sessionId, present);
48140
+ }
48141
+ return present;
48142
+ };
46668
48143
  return rows.flatMap((row) => {
46669
48144
  const missingStart = !exists(row.session_id, row.start_message_id);
46670
48145
  const missingEnd = !exists(row.session_id, row.end_message_id);
@@ -46675,7 +48150,8 @@ function listDanglingCompartmentBoundaries(contextDb, openCodeDb, hostGeneration
46675
48150
  sessionId: row.session_id,
46676
48151
  sequence: row.sequence,
46677
48152
  missingStartMessageId: missingStart ? row.start_message_id : null,
46678
- missingEndMessageId: missingEnd ? row.end_message_id : null
48153
+ missingEndMessageId: missingEnd ? row.end_message_id : null,
48154
+ sessionInStore: sessionInStore(row.session_id)
46679
48155
  }
46680
48156
  ];
46681
48157
  });
@@ -46687,6 +48163,52 @@ function formatDanglingCompartmentBoundary(boundary) {
46687
48163
  ].filter((value) => value !== null);
46688
48164
  return `session=${boundary.sessionId} sequence=${boundary.sequence} missing ${missing.join(" ")}`;
46689
48165
  }
48166
+ function countDanglingCompartmentBoundariesBySession(boundaries) {
48167
+ const bySession = new Map;
48168
+ for (const boundary of boundaries) {
48169
+ const entry = bySession.get(boundary.sessionId);
48170
+ if (entry)
48171
+ entry.count++;
48172
+ else
48173
+ bySession.set(boundary.sessionId, {
48174
+ sessionId: boundary.sessionId,
48175
+ count: 1,
48176
+ sessionInStore: boundary.sessionInStore
48177
+ });
48178
+ }
48179
+ return [...bySession.values()].sort((a, b) => b.count - a.count || a.sessionId.localeCompare(b.sessionId));
48180
+ }
48181
+ function formatDanglingCompartmentBoundaryHeadline(boundaries, store) {
48182
+ const sessions = countDanglingCompartmentBoundariesBySession(boundaries);
48183
+ const absent = sessions.filter((session) => !session.sessionInStore).length;
48184
+ const scope = `${boundaries.length} compartment(s) in ${sessions.length} session(s)`;
48185
+ const absentNote = absent > 0 ? ` ${absent} of those session(s) have no messages in this store at all, so OpenCode cannot open them from it and their compartments are not used.` : "";
48186
+ if (store === "opencode2") {
48187
+ return `Informational, no action needed: ${scope} point at OpenCode message ids that are not in the OpenCode 2 store.${absentNote} Magic Context places these from the neighbouring compartments; any it cannot place are listed as excluded from range recovery below.`;
48188
+ }
48189
+ return `${scope} have dangling OpenCode boundary ids.${absentNote}`;
48190
+ }
48191
+ function formatDanglingCompartmentBoundaryDetails(boundaries, options = {}) {
48192
+ const limit = options.verbose ? Number.POSITIVE_INFINITY : options.limit ?? DANGLING_BOUNDARY_EXAMPLE_LIMIT;
48193
+ const sessions = countDanglingCompartmentBoundariesBySession(boundaries);
48194
+ const lines = [];
48195
+ lines.push(options.verbose || sessions.length <= limit ? "By session:" : `By session (largest ${limit} of ${sessions.length}):`);
48196
+ for (const session of sessions.slice(0, limit)) {
48197
+ lines.push(` session=${session.sessionId}: ${session.count} compartment(s)${session.sessionInStore ? "" : " (session has no messages in this store)"}`);
48198
+ }
48199
+ if (sessions.length > limit) {
48200
+ lines.push(` … and ${sessions.length - limit} more session(s)`);
48201
+ }
48202
+ lines.push(options.verbose || boundaries.length <= limit ? "Compartments:" : `Examples (${limit} of ${boundaries.length}):`);
48203
+ for (const boundary of boundaries.slice(0, limit)) {
48204
+ lines.push(` ${formatDanglingCompartmentBoundary(boundary)}`);
48205
+ }
48206
+ if (sessions.length > limit || boundaries.length > limit) {
48207
+ lines.push(`Run \`magic-context doctor --verbose\` for the full list of ${boundaries.length} compartment(s).`);
48208
+ }
48209
+ return lines;
48210
+ }
48211
+ var DANGLING_BOUNDARY_EXAMPLE_LIMIT = 10;
46690
48212
 
46691
48213
  // src/commands/doctor-harness-relabel.ts
46692
48214
  function reportUnresolvedHarnessRelabel(args) {
@@ -46840,6 +48362,9 @@ function withPluginEntrySpecifier(entry, specifier) {
46840
48362
  }
46841
48363
  return specifier;
46842
48364
  }
48365
+ function isOpenCodePluginRegistered(config, configDir) {
48366
+ return readPluginEntries(config).some(({ entry }) => matchesPluginEntry(entry, OPENCODE_PLUGIN_NAME) || isDevPathPluginEntry(entry, configDir));
48367
+ }
46843
48368
  function checkOpenCodePluginEntry(config, configName, options, report) {
46844
48369
  const allEntries = readPluginEntries(config);
46845
48370
  const found = allEntries.find(({ entry }) => matchesPluginEntry(entry, OPENCODE_PLUGIN_NAME) || isDevPathPluginEntry(entry, options.configDir));
@@ -47497,6 +49022,21 @@ function logOpenCodeInstallationTable(installations) {
47497
49022
  function describeOpenCode2SessionAPIRequirement(hostVersion) {
47498
49023
  return `OpenCode host ${hostVersion}; Magic Context requires OpenCode 2.0.22 or newer with session.remove and session.compact.`;
47499
49024
  }
49025
+ function getOpenCodePluginHostStatus(hostLabel, hostVersion, installed) {
49026
+ if (!installed) {
49027
+ return {
49028
+ status: "info",
49029
+ message: `${hostLabel} found; Magic Context is not installed there (run setup to add it)`
49030
+ };
49031
+ }
49032
+ if (openCodeHostGenerationFromVersion(hostVersion) === "v2" && compareVersions(hostVersion, "2.0.22") === -1) {
49033
+ return {
49034
+ status: "fail",
49035
+ message: describeOpenCode2SessionAPIRequirement(hostVersion)
49036
+ };
49037
+ }
49038
+ return null;
49039
+ }
47500
49040
  async function runDoctor2(options = {}) {
47501
49041
  migrateConfigLocationsForCli(process.cwd(), log3);
47502
49042
  if (options.issue) {
@@ -47572,8 +49112,15 @@ async function runDoctor2(options = {}) {
47572
49112
  pass(installationReports.length > 1 ? `OpenCode ${activeInstallation.version} installed (active install marked above)` : `OpenCode ${activeInstallation.version} installed`);
47573
49113
  }
47574
49114
  const hostGeneration = openCodeHostGenerationFromVersion(activeInstallation.version);
47575
- if (hostGeneration === "v2") {
47576
- log3.info(describeOpenCode2SessionAPIRequirement(activeInstallation.version));
49115
+ const paths = detectConfigPaths();
49116
+ let magicContextInstalled = false;
49117
+ let magicContextStatusKnown = paths.opencodeConfigFormat === "none";
49118
+ if (!magicContextStatusKnown) {
49119
+ try {
49120
+ const config = import_comment_json11.parse(readFileSync28(paths.opencodeConfig, "utf-8"));
49121
+ magicContextInstalled = isOpenCodePluginRegistered(config, dirname25(paths.opencodeConfig));
49122
+ magicContextStatusKnown = true;
49123
+ } catch {}
47577
49124
  }
47578
49125
  const storeHostSelection = selectOpenCodeStoreHost(installationReports, openCodeHostGenerationFromVersion);
47579
49126
  const storeHost = storeHostSelection?.host ?? activeInstallation;
@@ -47591,7 +49138,9 @@ async function runDoctor2(options = {}) {
47591
49138
  const openCodeDbCheck = describeOpenCodeDatabaseDoctorCheck(openCodeDbResolution);
47592
49139
  if (openCodeDbCheck.ok)
47593
49140
  pass(openCodeDbCheck.message);
47594
- else
49141
+ else if (magicContextStatusKnown && !magicContextInstalled) {
49142
+ log3.info("OpenCode session database not yet created (will be created on first OpenCode session)");
49143
+ } else
47595
49144
  fail(openCodeDbCheck.message);
47596
49145
  if (openCodeDbCheck.ok) {
47597
49146
  let markerDb = null;
@@ -47612,7 +49161,7 @@ async function runDoctor2(options = {}) {
47612
49161
  } else if (options.fix) {
47613
49162
  warn(`${summary}; repaired=${report.repaired}, but some rows remain unconvertible`);
47614
49163
  } else if (report.migrationCompleted) {
47615
- log3.info(`${summary}; OpenCode 2 already converted this store`);
49164
+ log3.info(`${summary}; OpenCode 2 already converted this store, so these markers no longer matter (informational, no action needed)`);
47616
49165
  } else {
47617
49166
  warn(`${summary}; run \`magic-context doctor --fix\` before upgrading OpenCode`);
47618
49167
  }
@@ -47640,14 +49189,18 @@ async function runDoctor2(options = {}) {
47640
49189
  if (dangling.length === 0) {
47641
49190
  pass("Compartment boundary ids resolve in the OpenCode session store");
47642
49191
  } else if (storeGeneration === "v2" && /\d/.test(storeHost.version)) {
47643
- log3.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.`);
47644
- for (const boundary of dangling) {
47645
- log3.info(` ${formatDanglingCompartmentBoundary(boundary)}`);
49192
+ log3.info(formatDanglingCompartmentBoundaryHeadline(dangling, "opencode2"));
49193
+ for (const line of formatDanglingCompartmentBoundaryDetails(dangling, {
49194
+ verbose: options.verbose
49195
+ })) {
49196
+ log3.info(` ${line}`);
47646
49197
  }
47647
49198
  } else {
47648
- warn(`${dangling.length} compartment(s) have dangling OpenCode boundary ids`);
47649
- for (const boundary of dangling) {
47650
- log3.warn(` ${formatDanglingCompartmentBoundary(boundary)}`);
49199
+ warn(formatDanglingCompartmentBoundaryHeadline(dangling, "other"));
49200
+ for (const line of formatDanglingCompartmentBoundaryDetails(dangling, {
49201
+ verbose: options.verbose
49202
+ })) {
49203
+ log3.warn(` ${line}`);
47651
49204
  }
47652
49205
  }
47653
49206
  if (!supportsCoordinateGenerationReporting(contextDb)) {
@@ -47683,10 +49236,7 @@ async function runDoctor2(options = {}) {
47683
49236
  }
47684
49237
  }
47685
49238
  const selfVersion = getSelfVersion2();
47686
- const [npmLatest, pluginNpmLatest] = await Promise.all([
47687
- fetchNpmLatest(CLI_PACKAGE_NAME),
47688
- fetchNpmLatest(OPENCODE_PLUGIN_NAME)
47689
- ]);
49239
+ const npmLatest = await fetchNpmLatest(CLI_PACKAGE_NAME);
47690
49240
  const cliComparison = npmLatest ? compareVersions(selfVersion, npmLatest) : null;
47691
49241
  if (!npmLatest) {
47692
49242
  log3.info(`Magic Context CLI v${selfVersion}; npm latest check unavailable`);
@@ -47697,9 +49247,8 @@ async function runDoctor2(options = {}) {
47697
49247
  } else {
47698
49248
  pass(`Magic Context CLI v${selfVersion} is current (npm latest v${npmLatest})`);
47699
49249
  }
47700
- const paths = detectConfigPaths();
47701
49250
  if (paths.opencodeConfigFormat === "none") {
47702
- fail(`No opencode.json found at ${paths.opencodeConfig}`);
49251
+ magicContextStatusKnown = true;
47703
49252
  } else {
47704
49253
  pass(`OpenCode config: ${paths.opencodeConfig}`);
47705
49254
  }
@@ -47869,6 +49418,7 @@ async function runDoctor2(options = {}) {
47869
49418
  log3.warn("Could not migrate deprecated config keys in magic-context.jsonc");
47870
49419
  }
47871
49420
  }
49421
+ const openCodeHostLabel = activeInstallation.kind === "desktop" ? "OpenCode Desktop" : activeInstallation.version === "unknown" ? "OpenCode" : `OpenCode ${activeInstallation.version}`;
47872
49422
  const reportedAutoUpdateStalls = new Set;
47873
49423
  const reportAutoUpdateStall = (specifier) => {
47874
49424
  const message = describeAutoUpdateStall(specifier, autoUpdateEnabled);
@@ -47883,71 +49433,90 @@ async function runDoctor2(options = {}) {
47883
49433
  const config = import_comment_json11.parse(raw);
47884
49434
  const configName = paths.opencodeConfigFormat === "jsonc" ? "opencode.jsonc" : "opencode.json";
47885
49435
  const configDir = dirname25(paths.opencodeConfig);
47886
- if (checkPluginDuplicates(config, configName, { fix: options.fix, configDir }, {
47887
- warn,
47888
- pass: (message) => {
47889
- pass(message);
47890
- fixed++;
47891
- },
47892
- info: (message) => log3.info(message)
47893
- })) {
47894
- writeFileAtomic(paths.opencodeConfig, `${import_comment_json11.stringify(config, null, 2)}
49436
+ magicContextInstalled = isOpenCodePluginRegistered(config, configDir);
49437
+ magicContextStatusKnown = true;
49438
+ if (magicContextInstalled) {
49439
+ if (checkPluginDuplicates(config, configName, { fix: options.fix, configDir }, {
49440
+ warn,
49441
+ pass: (message) => {
49442
+ pass(message);
49443
+ fixed++;
49444
+ },
49445
+ info: (message) => log3.info(message)
49446
+ })) {
49447
+ writeFileAtomic(paths.opencodeConfig, `${import_comment_json11.stringify(config, null, 2)}
47895
49448
  `);
47896
- }
47897
- const allEntries = readPluginEntries(config);
47898
- if (allEntries.some(({ entry }) => isLocalPathPluginEntry(entry) && String(entry).includes("magic-context") && !isDevPathPluginEntry(entry, configDir))) {
47899
- warn("An unverifiable local OpenCode plugin path was ignored because its package name is not Magic Context");
47900
- }
47901
- if (checkOpenCodePluginEntry(config, configName, {
47902
- force: options.force,
47903
- registrationKey: pluginConfigKeyFor(hostGeneration),
47904
- configDir
47905
- }, {
47906
- pass,
47907
- warn,
47908
- fixed: (message) => {
47909
- pass(message);
47910
- fixed++;
47911
- },
47912
- autoUpdateStall: reportAutoUpdateStall
47913
- })) {
47914
- writeFileAtomic(paths.opencodeConfig, `${import_comment_json11.stringify(config, null, 2)}
49449
+ }
49450
+ const allEntries = readPluginEntries(config);
49451
+ if (allEntries.some(({ entry }) => isLocalPathPluginEntry(entry) && String(entry).includes("magic-context") && !isDevPathPluginEntry(entry, configDir))) {
49452
+ warn("An unverifiable local OpenCode plugin path was ignored because its package name is not Magic Context");
49453
+ }
49454
+ if (checkOpenCodePluginEntry(config, configName, {
49455
+ force: options.force,
49456
+ registrationKey: pluginConfigKeyFor(hostGeneration),
49457
+ configDir
49458
+ }, {
49459
+ pass,
49460
+ warn,
49461
+ fixed: (message) => {
49462
+ pass(message);
49463
+ fixed++;
49464
+ },
49465
+ autoUpdateStall: reportAutoUpdateStall
49466
+ })) {
49467
+ writeFileAtomic(paths.opencodeConfig, `${import_comment_json11.stringify(config, null, 2)}
47915
49468
  `);
49469
+ }
47916
49470
  }
47917
49471
  } catch {
47918
49472
  warn("Could not parse opencode config to verify plugin entry");
47919
49473
  }
47920
- }
47921
- const cwd = process.cwd();
47922
- const compactionEnabled = resolveCompactionEnabledForDoctor();
47923
- const conflictResult = detectConflicts(cwd, { compactionEnabled });
47924
- log3.info("Compaction check: file-based; the running server's resolved config may differ — `opencode debug config` is authoritative");
47925
- if (conflictResult.hasConflict) {
47926
- for (const reason of conflictResult.reasons) {
47927
- fail(`Conflict: ${reason}`);
47928
- }
47929
- const actions = fixConflicts(cwd, conflictResult.conflicts, { compactionEnabled });
47930
- for (const action of actions) {
47931
- pass(`Fixed: ${action}`);
47932
- fixed++;
47933
- }
47934
- if (actions.length > 0) {
47935
- warn("Restart OpenCode for conflict fixes to take effect");
47936
- }
47937
49474
  } else {
47938
- if (!compactionEnabled) {
47939
- if (conflictResult.nativeCompaction.auto || conflictResult.nativeCompaction.prune) {
47940
- pass("No conflicts detected (compaction, DCP, OMO hooks) — native compaction active (compaction-off mode)");
47941
- } else {
47942
- warn("No compaction manager is active: Magic Context compaction is off and OpenCode auto-compaction is disabled");
49475
+ magicContextStatusKnown = true;
49476
+ }
49477
+ if (!magicContextStatusKnown)
49478
+ magicContextInstalled = true;
49479
+ const hostPluginStatus = magicContextStatusKnown ? getOpenCodePluginHostStatus(openCodeHostLabel, activeInstallation.version, magicContextInstalled) : null;
49480
+ if (hostPluginStatus?.status === "info")
49481
+ log3.info(hostPluginStatus.message);
49482
+ else if (hostPluginStatus?.status === "fail")
49483
+ fail(hostPluginStatus.message);
49484
+ else if (magicContextInstalled && hostGeneration === "v2") {
49485
+ log3.info(describeOpenCode2SessionAPIRequirement(activeInstallation.version));
49486
+ }
49487
+ const pluginNpmLatest = magicContextInstalled ? await fetchNpmLatest(OPENCODE_PLUGIN_NAME) : null;
49488
+ if (magicContextInstalled) {
49489
+ const cwd = process.cwd();
49490
+ const compactionEnabled = resolveCompactionEnabledForDoctor();
49491
+ const conflictResult = detectConflicts(cwd, { compactionEnabled });
49492
+ log3.info("Compaction check: file-based; the running server's resolved config may differ — `opencode debug config` is authoritative");
49493
+ if (conflictResult.hasConflict) {
49494
+ for (const reason of conflictResult.reasons) {
49495
+ fail(`Conflict: ${reason}`);
49496
+ }
49497
+ const actions = fixConflicts(cwd, conflictResult.conflicts, { compactionEnabled });
49498
+ for (const action of actions) {
49499
+ pass(`Fixed: ${action}`);
49500
+ fixed++;
49501
+ }
49502
+ if (actions.length > 0) {
49503
+ warn("Restart OpenCode for conflict fixes to take effect");
47943
49504
  }
47944
49505
  } else {
47945
- pass("No conflicts detected (compaction, DCP, OMO hooks)");
49506
+ if (!compactionEnabled) {
49507
+ if (conflictResult.nativeCompaction.auto || conflictResult.nativeCompaction.prune) {
49508
+ pass("No conflicts detected (compaction, DCP, OMO hooks) — native compaction active (compaction-off mode)");
49509
+ } else {
49510
+ warn("No compaction manager is active: Magic Context compaction is off and OpenCode auto-compaction is disabled");
49511
+ }
49512
+ } else {
49513
+ pass("No conflicts detected (compaction, DCP, OMO hooks)");
49514
+ }
47946
49515
  }
47947
49516
  }
47948
- if (hostGeneration === "v2") {
49517
+ if (magicContextInstalled && hostGeneration === "v2") {
47949
49518
  pass("TUI sidebar loads from the plugin entry on OpenCode 2 (tui.json not used)");
47950
- } else {
49519
+ } else if (magicContextInstalled) {
47951
49520
  const tuiAdded = ensureTuiPluginEntry();
47952
49521
  if (tuiAdded) {
47953
49522
  pass("Added TUI sidebar plugin to tui.json");
@@ -48097,65 +49666,88 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
48097
49666
  warn(`RPC discovery check unavailable: ${error instanceof Error ? error.message : String(error)}`);
48098
49667
  issues++;
48099
49668
  }
48100
- const cacheResult = await clearPluginCache({
48101
- force: options.force,
48102
- latestVersion: pluginNpmLatest,
48103
- hostFiles: openCodeHostDatabaseFiles([openCodeDbResolution.path])
48104
- });
48105
- if (cacheResult.action === "cleared") {
48106
- const versionInfo = cacheResult.cached ? ` (cached: ${cacheResult.cached}${cacheResult.latest ? `, latest: ${cacheResult.latest}` : ""})` : "";
48107
- const reason = cacheResult.latest ? "outdated plugin cache" : "plugin cache (latest version check unavailable)";
48108
- pass(`Cleared ${reason}${versionInfo} — latest will download on restart`);
48109
- log3.info(` ${cacheResult.path}`);
48110
- fixed++;
48111
- } else if (cacheResult.action === "up_to_date") {
48112
- pass(`Plugin cache up to date (v${cacheResult.cached})`);
48113
- } else if (cacheResult.action === "check_unavailable") {
48114
- warn(`Plugin cache version check unavailable; preserving cached plugin${cacheResult.cached ? ` (cached: ${cacheResult.cached})` : ""}. Use doctor --force to reinstall it.`);
48115
- } else if (cacheResult.action === "in_use" || cacheResult.action === "in_use_unknown") {
48116
- const versionInfo = cacheResult.cached ? ` (cached: ${cacheResult.cached}${cacheResult.latest ? `, latest: ${cacheResult.latest}` : ""})` : "";
48117
- const why = cacheResult.action === "in_use" ? `OpenCode is running (pid ${cacheResult.pids?.join(", ")})` : cacheResult.reason ?? "could not tell whether OpenCode is running";
48118
- warn(`Plugin cache${versionInfo} was not cleared: ${why}`);
48119
- log3.info(" Quit OpenCode and rerun doctor to clear it.");
48120
- for (const path of cacheResult.paths ?? [cacheResult.path])
48121
- log3.info(` ${path}`);
48122
- issues++;
48123
- } else if (cacheResult.action === "error") {
48124
- warn(`Could not clear plugin cache: ${cacheResult.error}`);
48125
- if (cacheResult.clearedPaths && cacheResult.clearedPaths.length > 0) {
48126
- log3.info(` Cleared roots: ${cacheResult.clearedPaths.join(", ")}`);
49669
+ if (magicContextInstalled) {
49670
+ let pluginConfigUse;
49671
+ if (paths.opencodeConfigFormat !== "none") {
49672
+ try {
49673
+ pluginConfigUse = readOpenCodePluginConfigUse(import_comment_json11.parse(readFileSync28(paths.opencodeConfig, "utf-8")), dirname25(paths.opencodeConfig));
49674
+ } catch {}
48127
49675
  }
48128
- if (cacheResult.failedPaths && cacheResult.failedPaths.length > 0) {
48129
- log3.info(` Failed roots: ${cacheResult.failedPaths.join(", ")}`);
48130
- } else {
48131
- log3.info(` Manually delete: ${cacheResult.path}`);
49676
+ const cacheResult = await clearPluginCache({
49677
+ force: options.force,
49678
+ latestVersion: pluginNpmLatest,
49679
+ hostFiles: openCodeHostDatabaseFiles([openCodeDbResolution.path]),
49680
+ referencedSpecs: pluginConfigUse?.referencedSpecs.opencode1
49681
+ });
49682
+ if (cacheResult.action === "unused") {
49683
+ log3.info(describeUnusedOpenCodePluginCache({
49684
+ host: "OpenCode 1",
49685
+ spec: "latest",
49686
+ cached: cacheResult.cached,
49687
+ paths: cacheResult.paths ?? [cacheResult.path],
49688
+ loadedFrom: pluginConfigUse?.loadedFrom.opencode1
49689
+ }));
49690
+ } else if (cacheResult.action === "cleared") {
49691
+ const versionInfo = cacheResult.cached ? ` (cached: ${cacheResult.cached}${cacheResult.latest ? `, latest: ${cacheResult.latest}` : ""})` : "";
49692
+ const reason = cacheResult.latest ? "outdated plugin cache" : "plugin cache (latest version check unavailable)";
49693
+ pass(`Cleared ${reason}${versionInfo} — latest will download on restart`);
49694
+ log3.info(` ${cacheResult.path}`);
49695
+ fixed++;
49696
+ } else if (cacheResult.action === "up_to_date") {
49697
+ pass(`Plugin cache up to date (v${cacheResult.cached})`);
49698
+ } else if (cacheResult.action === "check_unavailable") {
49699
+ warn(`Plugin cache version check unavailable; preserving cached plugin${cacheResult.cached ? ` (cached: ${cacheResult.cached})` : ""}. Use doctor --force to reinstall it.`);
49700
+ } else if (cacheResult.action === "in_use" || cacheResult.action === "in_use_unknown") {
49701
+ const versionInfo = cacheResult.cached ? ` (cached: ${cacheResult.cached}${cacheResult.latest ? `, latest: ${cacheResult.latest}` : ""})` : "";
49702
+ const why = cacheResult.action === "in_use" ? `OpenCode is running (pid ${cacheResult.pids?.join(", ")})` : cacheResult.reason ?? "could not tell whether OpenCode is running";
49703
+ warn(`Plugin cache${versionInfo} was not cleared: ${why}`);
49704
+ log3.info(" Quit OpenCode and rerun doctor to clear it.");
49705
+ for (const path of cacheResult.paths ?? [cacheResult.path])
49706
+ log3.info(` ${path}`);
49707
+ issues++;
49708
+ } else if (cacheResult.action === "error") {
49709
+ warn(`Could not clear plugin cache: ${cacheResult.error}`);
49710
+ if (cacheResult.clearedPaths && cacheResult.clearedPaths.length > 0) {
49711
+ log3.info(` Cleared roots: ${cacheResult.clearedPaths.join(", ")}`);
49712
+ }
49713
+ if (cacheResult.failedPaths && cacheResult.failedPaths.length > 0) {
49714
+ log3.info(` Failed roots: ${cacheResult.failedPaths.join(", ")}`);
49715
+ } else {
49716
+ log3.info(` Manually delete: ${cacheResult.path}`);
49717
+ }
49718
+ issues++;
49719
+ } else if (hostGeneration !== "v2") {
49720
+ pass("Plugin cache clean (no cached version found)");
49721
+ }
49722
+ let v2DistTag;
49723
+ if (paths.opencodeConfigFormat !== "none") {
49724
+ try {
49725
+ v2DistTag = configuredOpenCodeV2DistTag(import_comment_json11.parse(readFileSync28(paths.opencodeConfig, "utf-8")));
49726
+ } catch {}
49727
+ }
49728
+ const v2Cache = reportOpenCodeV2PluginCache(checkOpenCodeV2PluginCache({
49729
+ fix: options.fix,
49730
+ force: options.force,
49731
+ latestVersion: v2DistTag ? await fetchNpmLatest(OPENCODE_PLUGIN_NAME, v2DistTag) : pluginNpmLatest,
49732
+ distTag: v2DistTag,
49733
+ hostFiles: openCodeHostDatabaseFiles([openCodeDbResolution.path]),
49734
+ referencedSpecs: pluginConfigUse?.referencedSpecs.opencode2
49735
+ }), { pass, warn, info: (message) => log3.info(message) }, {
49736
+ reportMissing: hostGeneration === "v2",
49737
+ loadedFrom: pluginConfigUse?.loadedFrom.opencode2
49738
+ });
49739
+ if (v2Cache.fixed)
49740
+ fixed++;
49741
+ if (v2Cache.issue)
49742
+ issues++;
49743
+ const sharedDbVersion = readContextDbSchemaVersion(dbPath);
49744
+ if (sharedDbVersion !== null) {
49745
+ reportCachedPluginFences(compareCachedPluginFences(listCachedOpenCodePluginFences({
49746
+ referencedSpecs: pluginConfigUse?.referencedSpecs
49747
+ }), sharedDbVersion), { pass, fail, info: (message) => log3.info(message) });
48132
49748
  }
48133
- issues++;
48134
- } else if (hostGeneration !== "v2") {
48135
- pass("Plugin cache clean (no cached version found)");
48136
- }
48137
- let v2DistTag;
48138
- if (paths.opencodeConfigFormat !== "none") {
48139
- try {
48140
- v2DistTag = configuredOpenCodeV2DistTag(import_comment_json11.parse(readFileSync28(paths.opencodeConfig, "utf-8")));
48141
- } catch {}
48142
- }
48143
- const v2Cache = reportOpenCodeV2PluginCache(checkOpenCodeV2PluginCache({
48144
- fix: options.fix,
48145
- force: options.force,
48146
- latestVersion: v2DistTag ? await fetchNpmLatest(OPENCODE_PLUGIN_NAME, v2DistTag) : pluginNpmLatest,
48147
- distTag: v2DistTag,
48148
- hostFiles: openCodeHostDatabaseFiles([openCodeDbResolution.path])
48149
- }), { pass, warn, info: (message) => log3.info(message) }, { reportMissing: hostGeneration === "v2" });
48150
- if (v2Cache.fixed)
48151
- fixed++;
48152
- if (v2Cache.issue)
48153
- issues++;
48154
- const sharedDbVersion = readContextDbSchemaVersion(dbPath);
48155
- if (sharedDbVersion !== null) {
48156
- reportCachedPluginFences(compareCachedPluginFences(listCachedOpenCodePluginFences(), sharedDbVersion), { pass, fail, info: (message) => log3.info(message) });
48157
49749
  }
48158
- {
49750
+ if (magicContextInstalled) {
48159
49751
  const ageWarnings = collectNpmReleaseAgeWarnings();
48160
49752
  if (ageWarnings.length > 0) {
48161
49753
  log3.warn("npm min-release-age restriction detected — this can prevent OpenCode from installing the latest plugin version");
@@ -48167,17 +49759,19 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
48167
49759
  issues++;
48168
49760
  }
48169
49761
  }
48170
- const logHarnesses = [
48171
- ...hostGeneration === "v1" ? ["opencode"] : [],
48172
- ...hostGeneration === "v2" || storeGeneration === "v2" ? ["opencode2"] : []
48173
- ];
48174
- const logFiles = logHarnesses.flatMap((harness) => inspectMagicContextLogs(harness)).filter((file, index, all) => all.findIndex((other) => other.path === file.path) === index);
48175
- const existingLogFiles = logFiles.filter((file) => file.exists);
48176
- if (existingLogFiles.length === 0) {
48177
- log3.info(`No plugin log file yet; checked: ${logFiles.map((file) => file.path).join(", ")}`);
48178
- } else {
48179
- for (const file of existingLogFiles) {
48180
- log3.info(`Log file read: ${formatLogFileInspection(file)}`);
49762
+ if (magicContextInstalled) {
49763
+ const logHarnesses = [
49764
+ ...hostGeneration === "v1" ? ["opencode"] : [],
49765
+ ...hostGeneration === "v2" || storeGeneration === "v2" ? ["opencode2"] : []
49766
+ ];
49767
+ const logFiles = logHarnesses.flatMap((harness) => inspectMagicContextLogs(harness)).filter((file, index, all) => all.findIndex((other) => other.path === file.path) === index);
49768
+ const existingLogFiles = logFiles.filter((file) => file.exists);
49769
+ if (existingLogFiles.length === 0) {
49770
+ log3.info(`No plugin log file yet; checked: ${logFiles.map((file) => file.path).join(", ")}`);
49771
+ } else {
49772
+ for (const file of existingLogFiles) {
49773
+ log3.info(`Log file read: ${formatLogFileInspection(file)}`);
49774
+ }
48181
49775
  }
48182
49776
  }
48183
49777
  const diagnostics = await collectDiagnostics();
@@ -48358,13 +49952,12 @@ function resolveDreamerTaskModel(args) {
48358
49952
  const taskQualifier = taskBlock?.[args.harness === "opencode" ? "variant" : "thinking_level"];
48359
49953
  const harnessQualifier = harnessBlock[args.harness === "opencode" ? "variant" : "thinking_level"] ?? normalizeModelEntry(harnessBlock.model, args.harness)?.qualifier;
48360
49954
  const taskHasModel = taskBlock !== undefined && hasOwn2(taskBlock, "model");
48361
- const usesMuralModel = !taskHasModel && args.task === "compress-cues" && Boolean(readString(args.muralModel));
48362
- const primarySource = taskHasModel ? taskBlock?.model : usesMuralModel ? args.muralModel : harnessBlock.model;
49955
+ const primarySource = taskHasModel ? taskBlock?.model : harnessBlock.model;
48363
49956
  const fallbackSource = taskBlock !== undefined && hasOwn2(taskBlock, "fallback_models") ? taskBlock.fallback_models : harnessBlock.fallback_models;
48364
49957
  return {
48365
49958
  primary: resolvePrimaryEntry({
48366
49959
  entry: primarySource,
48367
- defaultQualifier: usesMuralModel ? harnessQualifier : taskQualifier ?? harnessQualifier,
49960
+ defaultQualifier: taskQualifier ?? harnessQualifier,
48368
49961
  harness: args.harness
48369
49962
  }),
48370
49963
  fallbacks: resolveFallbackEntries(fallbackSource, args.harness),
@@ -48375,13 +49968,12 @@ function resolveDreamerTaskModel(args) {
48375
49968
  }
48376
49969
 
48377
49970
  // ../plugin/src/features/magic-context/dreamer/task-config.ts
48378
- function buildDreamTaskRuntimeConfigs(dreamer, harness, language, muralModel) {
48379
- return CANONICAL_DREAM_TASKS.map((task) => {
49971
+ function buildDreamTaskRuntimeConfigs(dreamer, harness, language, smartNotesEnabled = true) {
49972
+ return availableDreamTasks(smartNotesEnabled).map((task) => {
48380
49973
  const resolved = resolveDreamerTaskModel({
48381
49974
  config: { dreamer },
48382
49975
  harness,
48383
- task,
48384
- muralModel
49976
+ task
48385
49977
  });
48386
49978
  return {
48387
49979
  task,
@@ -48489,7 +50081,7 @@ function runnablePiModelChains(config, harness) {
48489
50081
  });
48490
50082
  }
48491
50083
  if (isDreamerRunnable(config)) {
48492
- for (const task of buildDreamTaskRuntimeConfigs(config.dreamer, harness, config.language, config.mural?.model)) {
50084
+ for (const task of buildDreamTaskRuntimeConfigs(config.dreamer, harness, config.language, config.smart_notes?.enabled)) {
48493
50085
  if (task.schedule.trim() === "")
48494
50086
  continue;
48495
50087
  const chain = [task.model, ...task.fallbackModels ?? []].filter((entry) => entry !== undefined);
@@ -49283,22 +50875,9 @@ async function runHealthChecks2(options) {
49283
50875
  };
49284
50876
  const self = options.deps.selfVersion();
49285
50877
  const pi = options.deps.detectPiBinary();
49286
- if (!pi) {
50878
+ const piVersion = pi ? options.deps.getPiVersion(pi.path) : null;
50879
+ if (!pi)
49287
50880
  add2(results, "fail", "Pi binary not found on PATH or at ~/.pi/bin/pi");
49288
- } else {
49289
- const version = options.deps.getPiVersion(pi.path);
49290
- if (version === null) {
49291
- add2(results, "fail", `Pi CLI was found at ${pi.path} but could not be executed`);
49292
- } else {
49293
- add2(results, "pass", `Pi ${version} detected at ${pi.path}`);
49294
- }
49295
- const compare = compareSemver(version, MIN_PI_VERSION);
49296
- if (compare !== null && compare < 0) {
49297
- add2(results, "fail", `Pi ${version} is older than required ${MIN_PI_VERSION}. Subagents (historian/dreamer) use the long-form \`--extension\` flag introduced in Pi 0.71.0; older versions hard-fail with "Unknown option". Run \`pi update\` (or \`npm install -g @earendil-works/pi-coding-agent@latest\`).`);
49298
- } else if (version) {
49299
- add2(results, "pass", `Pi version meets minimum ${MIN_PI_VERSION} requirement`);
49300
- }
49301
- }
49302
50881
  const latest = options.deps.getLatestNpmVersion();
49303
50882
  const latestCompare = latest ? compareSemver(self, latest) : null;
49304
50883
  if (latest && latestCompare === -1) {
@@ -49312,8 +50891,11 @@ async function runHealthChecks2(options) {
49312
50891
  }
49313
50892
  const settingsPath = getPiUserExtensionsPath();
49314
50893
  let packages = [];
50894
+ let piPluginInstalled = false;
49315
50895
  if (!existsSync47(settingsPath)) {
49316
- add2(results, "fail", `Pi settings not found at ${settingsPath}`);
50896
+ if (pi) {
50897
+ add2(results, "info", `${piVersion ? `Pi ${piVersion}` : "Pi"} found; Magic Context is not installed there (run setup to add it)`);
50898
+ }
49317
50899
  repairPlan.addPackageEntry = true;
49318
50900
  } else {
49319
50901
  const parsed = readJsonc2(settingsPath);
@@ -49321,15 +50903,31 @@ async function runHealthChecks2(options) {
49321
50903
  add2(results, "fail", `Could not parse Pi settings ${settingsPath}: ${parsed.error}`);
49322
50904
  } else {
49323
50905
  packages = packagesFrom(parsed.value);
49324
- add2(results, "pass", `Pi settings found at ${settingsPath}`);
49325
50906
  if (hasPiMagicContextPackage(packages)) {
50907
+ piPluginInstalled = true;
50908
+ add2(results, "pass", `Pi settings found at ${settingsPath}`);
49326
50909
  add2(results, "pass", `${PI_PACKAGE_SOURCE} is registered in packages[]`);
49327
50910
  } else {
49328
- add2(results, "fail", `${PI_PACKAGE_SOURCE} is missing from packages[]`);
50911
+ if (pi) {
50912
+ add2(results, "info", `${piVersion ? `Pi ${piVersion}` : "Pi"} found; Magic Context is not installed there (run setup to add it)`);
50913
+ }
49329
50914
  repairPlan.addPackageEntry = true;
49330
50915
  }
49331
50916
  }
49332
50917
  }
50918
+ if (pi && piPluginInstalled) {
50919
+ if (piVersion === null) {
50920
+ add2(results, "fail", `Pi CLI was found at ${pi.path} but could not be executed`);
50921
+ } else {
50922
+ add2(results, "pass", `Pi ${piVersion} detected at ${pi.path}`);
50923
+ }
50924
+ const compare = compareSemver(piVersion, MIN_PI_VERSION);
50925
+ if (compare !== null && compare < 0) {
50926
+ add2(results, "fail", `Pi ${piVersion} is older than required ${MIN_PI_VERSION}. Subagents (historian/dreamer) use the long-form \`--extension\` flag introduced in Pi 0.71.0; older versions hard-fail with "Unknown option". Run \`pi update\` (or \`npm install -g @earendil-works/pi-coding-agent@latest\`).`);
50927
+ } else if (piVersion) {
50928
+ add2(results, "pass", `Pi version meets minimum ${MIN_PI_VERSION} requirement`);
50929
+ }
50930
+ }
49333
50931
  const userConfigPath = getPiUserConfigPath();
49334
50932
  const projectPath = projectConfigPath(options.cwd);
49335
50933
  for (const [label, path, required] of [
@@ -49378,7 +50976,8 @@ async function runHealthChecks2(options) {
49378
50976
  } else if (historianModel.startsWith("github-copilot/") && historianThinkingLevel) {
49379
50977
  add2(results, "pass", `historian.model "${historianModel}" has thinking_level "${historianThinkingLevel}" configured`);
49380
50978
  }
49381
- checkPiModelChains(results, pi, loadedConfig.config, options.deps);
50979
+ if (piPluginInstalled)
50980
+ checkPiModelChains(results, pi, loadedConfig.config, options.deps);
49382
50981
  const storage = getMagicContextStorageResolution();
49383
50982
  const storageDir = storage.path;
49384
50983
  const dbPath = join41(storageDir, "context.db");
@@ -49501,7 +51100,7 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
49501
51100
  }
49502
51101
  } else if (loadedConfig.config.embedding.provider === "off") {
49503
51102
  add2(results, "info", "Embedding provider disabled");
49504
- } else {
51103
+ } else if (piPluginInstalled) {
49505
51104
  let runtimeReported = false;
49506
51105
  let firstFallback = null;
49507
51106
  const brokenWarnings = [];
@@ -49540,38 +51139,40 @@ ${formatDatabaseRepairGuidance(dbPath)}`);
49540
51139
  }
49541
51140
  }
49542
51141
  }
49543
- const piEntries = packages.filter(isConfiguredPiMagicContextEntry2).map(describePiPackageEntry);
49544
- if (piEntries.length > 1) {
49545
- add2(results, "fail", `Multiple magic-context entries in Pi packages[] — this loads the plugin twice: ${piEntries.join(", ")}`);
49546
- } else {
49547
- add2(results, "pass", "No conflicting magic-context entries in Pi packages[]");
49548
- }
49549
- const otherExtensions = packages.filter((entry) => !isConfiguredPiMagicContextEntry2(entry)).map(describePiPackageEntry);
49550
- if (otherExtensions.length > 0) {
49551
- add2(results, "info", `Other Pi extensions registered: ${otherExtensions.join(", ")}`);
49552
- } else {
49553
- add2(results, "info", "No other Pi extensions listed in settings.json");
49554
- }
49555
- const configuredEntry = packages.find(isPiMagicContextPackageEntry);
49556
- const configuredSpecifier = getPiMagicContextPackageSpecifier(configuredEntry);
49557
- const expectedPluginVersion = pinnedVersionFromPackageSpecifier(configuredSpecifier) ?? latest ?? null;
49558
- const staleCaches = findPiMagicContextCacheDirs(options.cwd, expectedPluginVersion, options.force === true);
49559
- if (staleCaches.length > 0) {
49560
- repairPlan.clearCachePaths = staleCaches.map((entry) => entry.path);
49561
- add2(results, "warn", `Stale Pi extension cache found: ${staleCaches.map((entry) => `${entry.path}${entry.version ? ` (v${entry.version})` : ""}`).join(", ")}`);
49562
- } else {
49563
- add2(results, "pass", "Pi extension cache clean (no stale cached package found)");
49564
- }
49565
- const logFiles = inspectMagicContextLogs("pi");
49566
- const existingLogFiles = logFiles.filter((file) => file.exists);
49567
- if (existingLogFiles.length === 0) {
49568
- add2(results, "info", `No plugin log file yet; checked: ${logFiles.map((file) => file.path).join(", ")}`);
49569
- } else {
49570
- for (const file of existingLogFiles) {
49571
- add2(results, "info", `Log file read: ${formatLogFileInspection(file)}`);
51142
+ if (piPluginInstalled) {
51143
+ const piEntries = packages.filter(isConfiguredPiMagicContextEntry2).map(describePiPackageEntry);
51144
+ if (piEntries.length > 1) {
51145
+ add2(results, "fail", `Multiple magic-context entries in Pi packages[] — this loads the plugin twice: ${piEntries.join(", ")}`);
51146
+ } else {
51147
+ add2(results, "pass", "No conflicting magic-context entries in Pi packages[]");
51148
+ }
51149
+ const otherExtensions = packages.filter((entry) => !isConfiguredPiMagicContextEntry2(entry)).map(describePiPackageEntry);
51150
+ if (otherExtensions.length > 0) {
51151
+ add2(results, "info", `Other Pi extensions registered: ${otherExtensions.join(", ")}`);
51152
+ } else {
51153
+ add2(results, "info", "No other Pi extensions listed in settings.json");
51154
+ }
51155
+ const configuredEntry = packages.find(isPiMagicContextPackageEntry);
51156
+ const configuredSpecifier = getPiMagicContextPackageSpecifier(configuredEntry);
51157
+ const expectedPluginVersion = pinnedVersionFromPackageSpecifier(configuredSpecifier) ?? latest ?? null;
51158
+ const staleCaches = findPiMagicContextCacheDirs(options.cwd, expectedPluginVersion, options.force === true);
51159
+ if (staleCaches.length > 0) {
51160
+ repairPlan.clearCachePaths = staleCaches.map((entry) => entry.path);
51161
+ add2(results, "warn", `Stale Pi extension cache found: ${staleCaches.map((entry) => `${entry.path}${entry.version ? ` (v${entry.version})` : ""}`).join(", ")}`);
51162
+ } else {
51163
+ add2(results, "pass", "Pi extension cache clean (no stale cached package found)");
51164
+ }
51165
+ const logFiles = inspectMagicContextLogs("pi");
51166
+ const existingLogFiles = logFiles.filter((file) => file.exists);
51167
+ if (existingLogFiles.length === 0) {
51168
+ add2(results, "info", `No plugin log file yet; checked: ${logFiles.map((file) => file.path).join(", ")}`);
51169
+ } else {
51170
+ for (const file of existingLogFiles) {
51171
+ add2(results, "info", `Log file read: ${formatLogFileInspection(file)}`);
51172
+ }
51173
+ const lastLine = readLogLines(existingLogFiles).at(-1);
51174
+ add2(results, "info", `Last plugin log line: ${lastLine ? sanitizeDiagnosticText(lastLine) : "<empty log>"}`);
49572
51175
  }
49573
- const lastLine = readLogLines(existingLogFiles).at(-1);
49574
- add2(results, "info", `Last plugin log line: ${lastLine ? sanitizeDiagnosticText(lastLine) : "<empty log>"}`);
49575
51176
  }
49576
51177
  const diagnosticsForDumps = await collectDiagnostics2(options.cwd);
49577
51178
  const dumpBuckets = diagnosticsForDumps.historianDumps.byProject;
@@ -49967,7 +51568,8 @@ async function dispatchDoctor(adapter, options) {
49967
51568
  force: options.force,
49968
51569
  fix: options.fix,
49969
51570
  issue: options.issue,
49970
- report: options.report
51571
+ report: options.report,
51572
+ verbose: options.verbose
49971
51573
  });
49972
51574
  }
49973
51575
  case "pi": {
@@ -50131,6 +51733,7 @@ var DOCTOR_HELP = [
50131
51733
  " --fix Repair safe, Magic Context-owned store rows and clear an outdated OpenCode 2 plugin cache",
50132
51734
  " --force Force-clear the plugin cache",
50133
51735
  " --clear Interactive cache cleanup picker",
51736
+ " --verbose Print long listings in full instead of a capped summary",
50134
51737
  " --prune-discovery [--yes] List RPC discovery records that block offline maintenance, then remove them after confirmation",
50135
51738
  " --issue Collect diagnostics and open a GitHub issue",
50136
51739
  " --report <path> Write the issue diagnostics to <path> without prompting",
@@ -50389,6 +51992,7 @@ async function runCommand(command, rest) {
50389
51992
  issue: rest.includes("--issue") || report !== null,
50390
51993
  ...report !== null ? { report } : {},
50391
51994
  clear: rest.includes("--clear"),
51995
+ verbose: rest.includes("--verbose"),
50392
51996
  checkV22Backfill: rest.includes("--check-v22-backfill"),
50393
51997
  retryV22Backfill: rest.includes("--retry-v22-backfill"),
50394
51998
  ...rekeyV22DirIdentity !== null ? { rekeyV22DirIdentity } : {},