@cortexkit/opencode-magic-context 0.24.1 → 0.26.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 (142) hide show
  1. package/README.md +4 -2
  2. package/dist/agents/magic-context-prompt.d.ts.map +1 -1
  3. package/dist/agents/permissions.d.ts +6 -0
  4. package/dist/agents/permissions.d.ts.map +1 -1
  5. package/dist/config/schema/magic-context.d.ts +22 -0
  6. package/dist/config/schema/magic-context.d.ts.map +1 -1
  7. package/dist/features/magic-context/compartment-chunk-embedding.d.ts +25 -0
  8. package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
  9. package/dist/features/magic-context/compartment-embedding.d.ts.map +1 -1
  10. package/dist/features/magic-context/dreamer/task-prompts.d.ts.map +1 -1
  11. package/dist/features/magic-context/memory/embedding-local.d.ts +7 -3
  12. package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
  13. package/dist/features/magic-context/memory/embedding-openai.d.ts +8 -4
  14. package/dist/features/magic-context/memory/embedding-openai.d.ts.map +1 -1
  15. package/dist/features/magic-context/memory/embedding-provider.d.ts +8 -4
  16. package/dist/features/magic-context/memory/embedding-provider.d.ts.map +1 -1
  17. package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
  18. package/dist/features/magic-context/memory/relocate-memory.d.ts +58 -0
  19. package/dist/features/magic-context/memory/relocate-memory.d.ts.map +1 -0
  20. package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts +6 -0
  21. package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts.map +1 -1
  22. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  23. package/dist/features/magic-context/project-embedding-registry.d.ts +41 -3
  24. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  25. package/dist/features/magic-context/storage-db.d.ts +2 -1
  26. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  27. package/dist/features/magic-context/storage-meta-persisted.d.ts +37 -0
  28. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  29. package/dist/features/magic-context/storage-meta-session.d.ts +1 -0
  30. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  31. package/dist/features/magic-context/storage-meta-shared.d.ts +4 -1
  32. package/dist/features/magic-context/storage-meta-shared.d.ts.map +1 -1
  33. package/dist/features/magic-context/storage-meta.d.ts +2 -2
  34. package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
  35. package/dist/features/magic-context/storage-tags.d.ts +58 -2
  36. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  37. package/dist/features/magic-context/storage.d.ts +3 -3
  38. package/dist/features/magic-context/storage.d.ts.map +1 -1
  39. package/dist/features/magic-context/tagger.d.ts +1 -1
  40. package/dist/features/magic-context/tagger.d.ts.map +1 -1
  41. package/dist/features/magic-context/transform-decision-log.d.ts +49 -0
  42. package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -0
  43. package/dist/features/magic-context/types.d.ts +1 -0
  44. package/dist/features/magic-context/types.d.ts.map +1 -1
  45. package/dist/features/magic-context/v22-deferred-backfill.d.ts.map +1 -1
  46. package/dist/hooks/magic-context/apply-operations.d.ts +3 -25
  47. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  48. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  49. package/dist/hooks/magic-context/caveman-cleanup.d.ts +1 -0
  50. package/dist/hooks/magic-context/caveman-cleanup.d.ts.map +1 -1
  51. package/dist/hooks/magic-context/channel2-delivery.d.ts +2 -0
  52. package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
  53. package/dist/hooks/magic-context/command-handler.d.ts +7 -5
  54. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  55. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  56. package/dist/hooks/magic-context/compartment-trigger.d.ts +1 -1
  57. package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
  58. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts +7 -2
  59. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts.map +1 -1
  60. package/dist/hooks/magic-context/derive-budgets.d.ts +5 -9
  61. package/dist/hooks/magic-context/derive-budgets.d.ts.map +1 -1
  62. package/dist/hooks/magic-context/embed-session-state.d.ts +14 -0
  63. package/dist/hooks/magic-context/embed-session-state.d.ts.map +1 -0
  64. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  65. package/dist/hooks/magic-context/event-payloads.d.ts +1 -0
  66. package/dist/hooks/magic-context/event-payloads.d.ts.map +1 -1
  67. package/dist/hooks/magic-context/format-embed-status.d.ts +9 -0
  68. package/dist/hooks/magic-context/format-embed-status.d.ts.map +1 -0
  69. package/dist/hooks/magic-context/heuristic-cleanup.d.ts +2 -0
  70. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  71. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  72. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  73. package/dist/hooks/magic-context/protected-tail-boundary.d.ts +10 -0
  74. package/dist/hooks/magic-context/protected-tail-boundary.d.ts.map +1 -1
  75. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  76. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts +1 -1
  77. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
  78. package/dist/hooks/magic-context/strip-content.d.ts +0 -1
  79. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  80. package/dist/hooks/magic-context/tag-content-primitives.d.ts +2 -0
  81. package/dist/hooks/magic-context/tag-content-primitives.d.ts.map +1 -1
  82. package/dist/hooks/magic-context/tag-id-fallback.d.ts.map +1 -1
  83. package/dist/hooks/magic-context/tag-messages.d.ts +10 -0
  84. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  85. package/dist/hooks/magic-context/tool-drop-target.d.ts +1 -1
  86. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  87. package/dist/hooks/magic-context/tool-reclaim.d.ts +12 -0
  88. package/dist/hooks/magic-context/tool-reclaim.d.ts.map +1 -0
  89. package/dist/hooks/magic-context/transform-compartment-phase.d.ts +32 -1
  90. package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
  91. package/dist/hooks/magic-context/transform-operations.d.ts +1 -1
  92. package/dist/hooks/magic-context/transform-operations.d.ts.map +1 -1
  93. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +6 -0
  94. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  95. package/dist/hooks/magic-context/transform.d.ts +2 -0
  96. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +1767 -613
  99. package/dist/plugin/conflict-warning-hook.d.ts.map +1 -1
  100. package/dist/plugin/dream-timer.d.ts.map +1 -1
  101. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  102. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  103. package/dist/shared/announcement.d.ts +1 -1
  104. package/dist/shared/index.d.ts +0 -1
  105. package/dist/shared/index.d.ts.map +1 -1
  106. package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
  107. package/dist/shared/resolve-fallbacks.d.ts +16 -16
  108. package/dist/shared/resolve-fallbacks.d.ts.map +1 -1
  109. package/dist/shared/rpc-types.d.ts +20 -0
  110. package/dist/shared/rpc-types.d.ts.map +1 -1
  111. package/dist/shared/sqlite.d.ts +5 -1
  112. package/dist/shared/sqlite.d.ts.map +1 -1
  113. package/dist/tools/ctx-expand/constants.d.ts +1 -1
  114. package/dist/tools/ctx-expand/constants.d.ts.map +1 -1
  115. package/dist/tools/ctx-expand/render.d.ts +43 -0
  116. package/dist/tools/ctx-expand/render.d.ts.map +1 -0
  117. package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
  118. package/dist/tools/ctx-expand/types.d.ts +6 -2
  119. package/dist/tools/ctx-expand/types.d.ts.map +1 -1
  120. package/dist/tools/ctx-reduce/constants.d.ts +1 -1
  121. package/dist/tools/ctx-reduce/constants.d.ts.map +1 -1
  122. package/dist/tools/ctx-search/tools.d.ts.map +1 -1
  123. package/dist/tui/data/context-db.d.ts +4 -2
  124. package/dist/tui/data/context-db.d.ts.map +1 -1
  125. package/package.json +1 -1
  126. package/src/shared/announcement.ts +6 -6
  127. package/src/shared/index.ts +0 -1
  128. package/src/shared/model-suggestion-retry.test.ts +61 -1
  129. package/src/shared/model-suggestion-retry.ts +22 -0
  130. package/src/shared/resolve-fallbacks.test.ts +37 -71
  131. package/src/shared/resolve-fallbacks.ts +16 -26
  132. package/src/shared/rpc-types.ts +11 -0
  133. package/src/shared/sqlite-bind-style.test.ts +82 -0
  134. package/src/shared/sqlite.ts +30 -1
  135. package/src/shared/tag-transcript.test.ts +3 -1
  136. package/src/shared/tag-transcript.ts +19 -17
  137. package/src/tui/data/context-db.ts +34 -2
  138. package/src/tui/index.tsx +58 -4
  139. package/src/tui/slots/sidebar-content.tsx +18 -9
  140. package/dist/shared/model-requirements.d.ts +0 -26
  141. package/dist/shared/model-requirements.d.ts.map +0 -1
  142. package/src/shared/model-requirements.ts +0 -86
package/dist/index.js CHANGED
@@ -47,20 +47,9 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
47
47
  var __promiseAll = (args) => Promise.all(args);
48
48
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
49
49
 
50
- // src/agents/dreamer.ts
51
- var DREAMER_AGENT = "dreamer";
52
-
53
50
  // src/agents/historian.ts
54
- var exports_historian = {};
55
- __export(exports_historian, {
56
- HISTORIAN_EDITOR_AGENT: () => HISTORIAN_EDITOR_AGENT,
57
- HISTORIAN_AGENT: () => HISTORIAN_AGENT
58
- });
59
51
  var HISTORIAN_AGENT = "historian", HISTORIAN_EDITOR_AGENT = "historian-editor";
60
52
 
61
- // src/agents/sidekick.ts
62
- var SIDEKICK_AGENT = "sidekick";
63
-
64
53
  // src/shared/jsonc-parser.ts
65
54
  import { existsSync, readFileSync } from "node:fs";
66
55
  function stripJsonComments(content) {
@@ -14879,14 +14868,16 @@ var init_magic_context = __esm(() => {
14879
14868
  }).optional();
14880
14869
  HistorianConfigSchema = AgentOverrideConfigSchema.extend({
14881
14870
  two_pass: exports_external.boolean().default(false).describe("Run a second editor pass over historian output to clean low-signal U: lines and cross-compartment duplicates. Adds ~1 extra API call and ~1.3x cost per historian run. Useful for models without extended thinking support. (default: false)"),
14882
- thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level passed as --thinking <level> to Pi historian subagent invocations. Required when using reasoning models (e.g. github-copilot/gpt-5.4) because Pi's default thinking-level resolution can pick a value the provider rejects. OpenCode users set variant instead. Valid: off | minimal | low | medium | high | xhigh")
14871
+ thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level passed as --thinking <level> to Pi historian subagent invocations. Required when using reasoning models (e.g. github-copilot/gpt-5.4) because Pi's default thinking-level resolution can pick a value the provider rejects. OpenCode users set variant instead. Valid: off | minimal | low | medium | high | xhigh"),
14872
+ disallowed_tools: exports_external.array(exports_external.enum(["*", "read", "aft_outline", "aft_zoom", "aft_search"])).default([]).describe(`OpenCode only. Tools to REMOVE from the historian's default allow-list [read, aft_outline, aft_zoom, aft_search]. Applies to both historian and historian-editor agents. Use ["*"] to strip all tool definitions from the model request — this prevents weak instruction-following models (e.g. mistral-small-latest) from entering tool-calling loops. Individual tool names remove just that tool. Note: a user-supplied historian.permission override can re-allow a tool that disallowed_tools removed — disallowed_tools sets the baseline, permission overrides take precedence. (default: [])`)
14883
14873
  }).optional();
14884
14874
  BaseEmbeddingConfigSchema = exports_external.object({
14885
14875
  provider: exports_external.enum(["local", "openai-compatible", "off"]).default("local").describe("Embedding provider. 'local' uses Xenova/all-MiniLM-L6-v2, 'openai-compatible' requires endpoint and model, 'off' disables embeddings."),
14886
14876
  model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible, ignored for local."),
14887
14877
  endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
14888
14878
  api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
14889
- input_type: exports_external.string().optional().describe("Optional input_type sent in the embedding request body. Required by some openai-compatible providers (e.g. NVIDIA NIM expects 'query' or 'passage'). Omitted from the request when unset."),
14879
+ input_type: exports_external.string().optional().describe("Default input_type for stored/indexed (passage) embeddings in the request body. Required by some openai-compatible providers (e.g. NVIDIA NIM). Omitted from the request when unset."),
14880
+ query_input_type: exports_external.string().optional().describe("Optional input_type for query (search) embeddings on asymmetric models (e.g. NVIDIA NIM 'query'). When unset, query embeddings use embedding.input_type. Passage/stored content always uses embedding.input_type."),
14890
14881
  truncate: exports_external.string().optional().describe("Optional truncate mode sent in the embedding request body (e.g. NVIDIA NIM accepts 'NONE' | 'START' | 'END'). Omitted from the request when unset."),
14891
14882
  max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted.")
14892
14883
  }).superRefine((data, ctx) => {
@@ -14916,6 +14907,7 @@ var init_magic_context = __esm(() => {
14916
14907
  if (data.provider === "openai-compatible") {
14917
14908
  const apiKey = data.api_key?.trim();
14918
14909
  const inputType = data.input_type?.trim();
14910
+ const queryInputType = data.query_input_type?.trim();
14919
14911
  const truncate = data.truncate?.trim();
14920
14912
  return {
14921
14913
  provider: "openai-compatible",
@@ -14923,6 +14915,7 @@ var init_magic_context = __esm(() => {
14923
14915
  endpoint: data.endpoint?.trim() ?? "",
14924
14916
  ...apiKey ? { api_key: apiKey } : {},
14925
14917
  ...inputType ? { input_type: inputType } : {},
14918
+ ...queryInputType ? { query_input_type: queryInputType } : {},
14926
14919
  ...truncate ? { truncate } : {},
14927
14920
  ...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
14928
14921
  };
@@ -15281,58 +15274,6 @@ var init_logger = __esm(() => {
15281
15274
  }
15282
15275
  });
15283
15276
 
15284
- // src/shared/model-requirements.ts
15285
- function expandFallbackChain(chain) {
15286
- const models = [];
15287
- for (const entry of chain) {
15288
- for (const provider of entry.providers) {
15289
- models.push(`${provider}/${entry.model}`);
15290
- }
15291
- }
15292
- return models;
15293
- }
15294
- function getAgentFallbackModels(agent) {
15295
- const requirement = AGENT_MODEL_REQUIREMENTS[agent];
15296
- if (!requirement)
15297
- return;
15298
- return expandFallbackChain(requirement.fallbackChain);
15299
- }
15300
- var HISTORIAN_FALLBACK_CHAIN, DREAMER_FALLBACK_CHAIN, SIDEKICK_FALLBACK_CHAIN, AGENT_MODEL_REQUIREMENTS;
15301
- var init_model_requirements = __esm(() => {
15302
- HISTORIAN_FALLBACK_CHAIN = [
15303
- { providers: ["github-copilot", "anthropic", "opencode"], model: "claude-sonnet-4-6" },
15304
- { providers: ["opencode-go"], model: "minimax-m2.7" },
15305
- {
15306
- providers: ["zai-coding-plan", "bailian-coding-plan", "opencode-go", "opencode"],
15307
- model: "glm-5"
15308
- },
15309
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.4" },
15310
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" }
15311
- ];
15312
- DREAMER_FALLBACK_CHAIN = [
15313
- { providers: ["github-copilot", "anthropic", "opencode"], model: "claude-sonnet-4-6" },
15314
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
15315
- {
15316
- providers: ["zai-coding-plan", "bailian-coding-plan", "opencode-go", "opencode"],
15317
- model: "glm-5"
15318
- },
15319
- { providers: ["opencode-go"], model: "minimax-m2.7" },
15320
- { providers: ["github-copilot", "openai", "opencode"], model: "gpt-5.4-mini" }
15321
- ];
15322
- SIDEKICK_FALLBACK_CHAIN = [
15323
- { providers: ["cerebras"], model: "qwen-3-235b-a22b-instruct-2507" },
15324
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
15325
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.4-mini" },
15326
- { providers: ["opencode"], model: "gpt-5-nano" }
15327
- ];
15328
- AGENT_MODEL_REQUIREMENTS = {
15329
- [HISTORIAN_AGENT]: { fallbackChain: HISTORIAN_FALLBACK_CHAIN },
15330
- [HISTORIAN_EDITOR_AGENT]: { fallbackChain: HISTORIAN_FALLBACK_CHAIN },
15331
- [DREAMER_AGENT]: { fallbackChain: DREAMER_FALLBACK_CHAIN },
15332
- [SIDEKICK_AGENT]: { fallbackChain: SIDEKICK_FALLBACK_CHAIN }
15333
- };
15334
- });
15335
-
15336
15277
  // src/features/magic-context/overflow-detection.ts
15337
15278
  function extractErrorMessage(error51) {
15338
15279
  if (!error51)
@@ -15446,15 +15387,9 @@ __export(exports_resolve_fallbacks, {
15446
15387
  resolveFallbackChain: () => resolveFallbackChain,
15447
15388
  parseProviderModel: () => parseProviderModel
15448
15389
  });
15449
- function resolveFallbackChain(agentName, userFallbacks) {
15390
+ function resolveFallbackChain(userFallbacks) {
15450
15391
  const userList = normalizeUserFallbacks(userFallbacks);
15451
- if (userList.length > 0) {
15452
- return dedupe(userList.filter(isValidModelSpec));
15453
- }
15454
- const builtin = getAgentFallbackModels(agentName);
15455
- if (!builtin || builtin.length === 0)
15456
- return [];
15457
- return dedupe(builtin.filter(isValidModelSpec));
15392
+ return dedupe(userList.filter(isValidModelSpec));
15458
15393
  }
15459
15394
  function normalizeUserFallbacks(userFallbacks) {
15460
15395
  if (!userFallbacks)
@@ -15489,9 +15424,6 @@ function parseProviderModel(spec) {
15489
15424
  modelID: spec.slice(slash + 1).trim()
15490
15425
  };
15491
15426
  }
15492
- var init_resolve_fallbacks = __esm(() => {
15493
- init_model_requirements();
15494
- });
15495
15427
 
15496
15428
  // src/shared/model-suggestion-retry.ts
15497
15429
  function extractMessage(error51) {
@@ -15562,9 +15494,11 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
15562
15494
  });
15563
15495
  } catch (error51) {
15564
15496
  if (signal?.aborted) {
15497
+ await abortChildRun(client, args.path.id);
15565
15498
  throw new Error("prompt aborted by external signal");
15566
15499
  }
15567
15500
  if (controller.signal.aborted) {
15501
+ await abortChildRun(client, args.path.id);
15568
15502
  throw new Error(`prompt timed out after ${timeoutMs}ms`);
15569
15503
  }
15570
15504
  throw error51;
@@ -15573,6 +15507,13 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
15573
15507
  signal?.removeEventListener("abort", onExternalAbort);
15574
15508
  }
15575
15509
  }
15510
+ async function abortChildRun(client, sessionId) {
15511
+ try {
15512
+ await client.session.abort({ path: { id: sessionId } });
15513
+ } catch (error51) {
15514
+ log(`[model-retry] child session abort failed for ${sessionId}: ${String(error51)}`);
15515
+ }
15516
+ }
15576
15517
  function isNonRetryable(error51, externalSignal) {
15577
15518
  if (externalSignal?.aborted)
15578
15519
  return true;
@@ -15670,7 +15611,6 @@ async function promptSyncWithModelSuggestionRetry(client, args, options = {}) {
15670
15611
  var init_model_suggestion_retry = __esm(() => {
15671
15612
  init_overflow_detection();
15672
15613
  init_logger();
15673
- init_resolve_fallbacks();
15674
15614
  });
15675
15615
 
15676
15616
  // src/shared/normalize-sdk-response.ts
@@ -15700,9 +15640,7 @@ function normalizeSDKResponse(response, fallback, options) {
15700
15640
  // src/shared/index.ts
15701
15641
  var init_shared = __esm(() => {
15702
15642
  init_logger();
15703
- init_model_requirements();
15704
15643
  init_model_suggestion_retry();
15705
- init_resolve_fallbacks();
15706
15644
  });
15707
15645
 
15708
15646
  // src/shared/record-type-guard.ts
@@ -15844,7 +15782,7 @@ function isSessionMetaRow(row) {
15844
15782
  if (row === null || typeof row !== "object")
15845
15783
  return false;
15846
15784
  const r = row;
15847
- return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.observed_safe_input_tokens) && isNumberOrNull(r.cache_alert_sent) && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state) && isBlobOrNull(r.cached_m0_bytes) && isBlobOrNull(r.cached_m1_bytes) && isNumberOrNull(r.cached_m0_project_memory_epoch) && isStringOrNull(r.cached_m0_workspace_fingerprint) && isNumberOrNull(r.cached_m0_project_user_profile_version) && isNumberOrNull(r.cached_m0_max_compartment_seq) && isNumberOrNull(r.cached_m0_max_memory_id) && isNumberOrNull(r.cached_m0_max_mutation_id) && isNumberOrNull(r.cached_m0_max_memory_mutation_id) && isStringOrNull(r.cached_m0_project_docs_hash) && isNumberOrNull(r.cached_m0_materialized_at) && isNumberOrNull(r.cached_m0_session_facts_version) && isStringOrNull(r.cached_m0_upgrade_state) && isStringOrNull(r.cached_m0_system_hash) && isStringOrNull(r.cached_m0_tool_set_hash) && isStringOrNull(r.cached_m0_model_key) && isStringOrNull(r.last_observed_model_key) && isNumberOrNull(r.last_usage_context_limit) && isNumberOrNull(r.prior_boundary_ordinal) && isNumberOrNull(r.protected_tail_policy_version) && isNumberOrNull(r.protected_tail_drain_window_started_at) && isNumberOrNull(r.protected_tail_drain_tokens) && isNumberOrNull(r.recovery_no_eligible_head_count) && isNumberOrNull(r.force_emergency_bypass_window_start) && isNumberOrNull(r.force_emergency_bypass_used) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme);
15785
+ return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.observed_safe_input_tokens) && isNumberOrNull(r.cache_alert_sent) && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state) && isBlobOrNull(r.cached_m0_bytes) && isBlobOrNull(r.cached_m1_bytes) && isNumberOrNull(r.cached_m0_project_memory_epoch) && isStringOrNull(r.cached_m0_workspace_fingerprint) && isNumberOrNull(r.cached_m0_project_user_profile_version) && isNumberOrNull(r.cached_m0_max_compartment_seq) && isNumberOrNull(r.cached_m0_max_memory_id) && isNumberOrNull(r.cached_m0_max_mutation_id) && isNumberOrNull(r.cached_m0_max_memory_mutation_id) && isStringOrNull(r.cached_m0_project_docs_hash) && isNumberOrNull(r.cached_m0_materialized_at) && isNumberOrNull(r.cached_m0_session_facts_version) && isStringOrNull(r.cached_m0_upgrade_state) && isStringOrNull(r.cached_m0_system_hash) && isStringOrNull(r.cached_m0_tool_set_hash) && isStringOrNull(r.cached_m0_model_key) && isStringOrNull(r.last_observed_model_key) && isNumberOrNull(r.last_usage_context_limit) && isNumberOrNull(r.prior_boundary_ordinal) && isNumberOrNull(r.protected_tail_policy_version) && isNumberOrNull(r.protected_tail_drain_window_started_at) && isNumberOrNull(r.protected_tail_drain_tokens) && isNumberOrNull(r.recovery_no_eligible_head_count) && isNumberOrNull(r.force_emergency_bypass_window_start) && isNumberOrNull(r.force_emergency_bypass_used) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme) && isNumberOrNull(r.tool_reclaim_watermark);
15848
15786
  }
15849
15787
  function getDefaultSessionMeta(sessionId) {
15850
15788
  return {
@@ -15867,6 +15805,7 @@ function getDefaultSessionMeta(sessionId) {
15867
15805
  conversationTokens: 0,
15868
15806
  toolCallTokens: 0,
15869
15807
  clearedReasoningThroughTag: 0,
15808
+ toolReclaimWatermark: 0,
15870
15809
  lastTodoState: "",
15871
15810
  cachedM0Bytes: null,
15872
15811
  cachedM1Bytes: null,
@@ -15930,6 +15869,7 @@ function toSessionMeta(row) {
15930
15869
  conversationTokens: numOrZero(row.conversation_tokens),
15931
15870
  toolCallTokens: numOrZero(row.tool_call_tokens),
15932
15871
  clearedReasoningThroughTag: numOrZero(row.cleared_reasoning_through_tag),
15872
+ toolReclaimWatermark: numOrZero(row.tool_reclaim_watermark),
15933
15873
  lastTodoState: lastTodoStateRaw,
15934
15874
  cachedM0Bytes: toBufferOrNull(row.cached_m0_bytes),
15935
15875
  cachedM1Bytes: toBufferOrNull(row.cached_m1_bytes),
@@ -16039,6 +15979,7 @@ var init_storage_meta_shared = __esm(() => {
16039
15979
  "conversation_tokens",
16040
15980
  "tool_call_tokens",
16041
15981
  "cleared_reasoning_through_tag",
15982
+ "tool_reclaim_watermark",
16042
15983
  "last_todo_state",
16043
15984
  "cached_m0_bytes",
16044
15985
  "cached_m1_bytes",
@@ -16065,6 +16006,8 @@ var init_storage_meta_shared = __esm(() => {
16065
16006
  "recovery_no_eligible_head_count",
16066
16007
  "force_emergency_bypass_window_start",
16067
16008
  "force_emergency_bypass_used",
16009
+ "emergency_drain_active",
16010
+ "historian_drain_failure_at",
16068
16011
  "upgrade_reminded_at",
16069
16012
  "pi_stable_id_scheme"
16070
16013
  ];
@@ -16087,6 +16030,7 @@ var init_storage_meta_shared = __esm(() => {
16087
16030
  conversationTokens: "conversation_tokens",
16088
16031
  toolCallTokens: "tool_call_tokens",
16089
16032
  clearedReasoningThroughTag: "cleared_reasoning_through_tag",
16033
+ toolReclaimWatermark: "tool_reclaim_watermark",
16090
16034
  lastTodoState: "last_todo_state",
16091
16035
  cachedM0Bytes: "cached_m0_bytes",
16092
16036
  cachedM1Bytes: "cached_m1_bytes",
@@ -16113,6 +16057,8 @@ var init_storage_meta_shared = __esm(() => {
16113
16057
  recoveryNoEligibleHeadCount: "recovery_no_eligible_head_count",
16114
16058
  forceEmergencyBypassWindowStart: "force_emergency_bypass_window_start",
16115
16059
  forceEmergencyBypassUsed: "force_emergency_bypass_used",
16060
+ emergencyDrainActive: "emergency_drain_active",
16061
+ historianDrainFailureAt: "historian_drain_failure_at",
16116
16062
  upgradeRemindedAt: "upgrade_reminded_at",
16117
16063
  piStableIdScheme: "pi_stable_id_scheme"
16118
16064
  };
@@ -16363,6 +16309,14 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
16363
16309
  }
16364
16310
  super(typeof filename === "string" ? filename : ":memory:", translated);
16365
16311
  }
16312
+ prepare(sql) {
16313
+ const stmt = super.prepare(sql);
16314
+ for (const method of ["run", "get", "all"]) {
16315
+ const original = stmt[method].bind(stmt);
16316
+ stmt[method] = (...args) => args.length === 1 && Array.isArray(args[0]) ? original(...args[0]) : original(...args);
16317
+ }
16318
+ return stmt;
16319
+ }
16366
16320
  transaction(fn) {
16367
16321
  const self = this;
16368
16322
  const wrapped = function(...args) {
@@ -149339,6 +149293,9 @@ function stripCompleteTagPairsGlobally(value) {
149339
149293
  function stripMalformedTagNotationGlobally(value) {
149340
149294
  return value.replace(MALFORMED_TAG_GLOBAL_REGEX, "");
149341
149295
  }
149296
+ function stripDanglingTagNotationGlobally(value) {
149297
+ return value.replace(DANGLING_TAG_GLOBAL_REGEX, "");
149298
+ }
149342
149299
  function stripTagSectionCharacters(value) {
149343
149300
  return value.replace(STRAY_SECTION_CHAR_REGEX, "");
149344
149301
  }
@@ -149346,6 +149303,7 @@ function stripPersistedAssistantText(value) {
149346
149303
  let text = stripWellFormedLeadingTagPrefix(value);
149347
149304
  text = stripCompleteTagPairsGlobally(text);
149348
149305
  text = stripMalformedTagNotationGlobally(text);
149306
+ text = stripDanglingTagNotationGlobally(text);
149349
149307
  text = stripTagSectionCharacters(text);
149350
149308
  return text.trim();
149351
149309
  }
@@ -149358,6 +149316,7 @@ function stripTagPrefix(value) {
149358
149316
  const prev = stripped;
149359
149317
  stripped = stripped.replace(MALFORMED_TAG_PREFIX_REGEX, "");
149360
149318
  stripped = stripped.replace(TAG_PREFIX_REGEX, "");
149319
+ stripped = stripped.replace(DANGLING_TAG_PREFIX_REGEX, "");
149361
149320
  if (stripped === prev)
149362
149321
  break;
149363
149322
  }
@@ -149379,11 +149338,13 @@ function isThinkingPart(part) {
149379
149338
  const candidate = part;
149380
149339
  return candidate.type === "thinking" || candidate.type === "reasoning";
149381
149340
  }
149382
- var encoder, TAG_PREFIX_REGEX, MALFORMED_TAG_PREFIX_REGEX, COMPLETE_TAG_PAIR_GLOBAL_REGEX, MALFORMED_TAG_GLOBAL_REGEX, STRAY_SECTION_CHAR_REGEX;
149341
+ var encoder, TAG_PREFIX_REGEX, MALFORMED_TAG_PREFIX_REGEX, DANGLING_TAG_GLOBAL_REGEX, DANGLING_TAG_PREFIX_REGEX, COMPLETE_TAG_PAIR_GLOBAL_REGEX, MALFORMED_TAG_GLOBAL_REGEX, STRAY_SECTION_CHAR_REGEX;
149383
149342
  var init_tag_content_primitives = __esm(() => {
149384
149343
  encoder = new TextEncoder;
149385
149344
  TAG_PREFIX_REGEX = /^(?:§\d+§\s*)+/;
149386
149345
  MALFORMED_TAG_PREFIX_REGEX = /^(?:§\d+">§(?:\d+§)?\s*)+/;
149346
+ DANGLING_TAG_GLOBAL_REGEX = /\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?/g;
149347
+ DANGLING_TAG_PREFIX_REGEX = /^(?:\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?\s*)+/;
149387
149348
  COMPLETE_TAG_PAIR_GLOBAL_REGEX = /\u00a7\d+\u00a7/g;
149388
149349
  MALFORMED_TAG_GLOBAL_REGEX = /\u00a7\d+">(?:\u00a7(?:\d+\u00a7)?)?/g;
149389
149350
  STRAY_SECTION_CHAR_REGEX = /\u00a7/g;
@@ -149447,12 +149408,13 @@ function setToolContent(part, content) {
149447
149408
  part.content = content;
149448
149409
  }
149449
149410
  }
149450
- function truncateToolPart(part) {
149411
+ function truncateToolPart(part, tagId) {
149451
149412
  if (!isRecord(part))
149452
149413
  return;
149414
+ const sentinel = `[dropped §${tagId}§]`;
149453
149415
  if (part.type === "tool" && isRecord(part.state)) {
149454
149416
  const state = part.state;
149455
- state.output = "[truncated]";
149417
+ state.output = sentinel;
149456
149418
  if (isRecord(state.input)) {
149457
149419
  const inputSize = estimateInputSize(state.input);
149458
149420
  if (inputSize > 500) {
@@ -149462,7 +149424,7 @@ function truncateToolPart(part) {
149462
149424
  return;
149463
149425
  }
149464
149426
  if (part.type === "tool_result") {
149465
- part.content = "[truncated]";
149427
+ part.content = sentinel;
149466
149428
  return;
149467
149429
  }
149468
149430
  if (part.type === "tool-invocation" && isRecord(part.args)) {
@@ -149579,7 +149541,7 @@ class ToolMutationBatch {
149579
149541
  this.affectedMessages.clear();
149580
149542
  }
149581
149543
  }
149582
- function createToolDropTarget(compositeKey, thinkingParts, index, batch) {
149544
+ function createToolDropTarget(compositeKey, thinkingParts, index, batch, tagId) {
149583
149545
  const drop = () => {
149584
149546
  const entry = index.get(compositeKey);
149585
149547
  if (!entry || entry.occurrences.length === 0)
@@ -149600,7 +149562,7 @@ function createToolDropTarget(compositeKey, thinkingParts, index, batch) {
149600
149562
  if (!entry.hasResult)
149601
149563
  return "incomplete";
149602
149564
  for (const occurrence of entry.occurrences) {
149603
- truncateToolPart(occurrence.part);
149565
+ truncateToolPart(occurrence.part, tagId);
149604
149566
  }
149605
149567
  clearThinkingParts(thinkingParts);
149606
149568
  return "truncated";
@@ -149648,6 +149610,22 @@ var init_tool_drop_target = __esm(() => {
149648
149610
  });
149649
149611
 
149650
149612
  // src/hooks/magic-context/read-session-chunk.ts
149613
+ function estimateBlockTokens(blockText) {
149614
+ const cached2 = blockTokenMemo.get(blockText);
149615
+ if (cached2 !== undefined) {
149616
+ blockTokenMemo.delete(blockText);
149617
+ blockTokenMemo.set(blockText, cached2);
149618
+ return cached2;
149619
+ }
149620
+ const count = estimateTokens(blockText);
149621
+ if (blockTokenMemo.size >= BLOCK_TOKEN_MEMO_MAX) {
149622
+ const oldest = blockTokenMemo.keys().next().value;
149623
+ if (oldest !== undefined)
149624
+ blockTokenMemo.delete(oldest);
149625
+ }
149626
+ blockTokenMemo.set(blockText, count);
149627
+ return count;
149628
+ }
149651
149629
  function cleanUserText(text) {
149652
149630
  return removeSystemReminders(text).replace(OMO_INTERNAL_INITIATOR_MARKER, "").trim();
149653
149631
  }
@@ -149815,7 +149793,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
149815
149793
  if (!currentBlock)
149816
149794
  return true;
149817
149795
  const blockText = formatBlock(currentBlock);
149818
- const blockTokens = estimateTokens(blockText);
149796
+ const blockTokens = estimateBlockTokens(blockText);
149819
149797
  if (totalTokens + blockTokens > tokenBudget && totalTokens > 0) {
149820
149798
  return false;
149821
149799
  }
@@ -149933,13 +149911,14 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
149933
149911
  toolOnlyRanges
149934
149912
  };
149935
149913
  }
149936
- var activeRawMessageCache = null, activeAbsoluteCountCache = null, sessionProviders, PROTECTED_TAIL_USER_TURNS = 5;
149914
+ var BLOCK_TOKEN_MEMO_MAX = 2048, blockTokenMemo, activeRawMessageCache = null, activeAbsoluteCountCache = null, sessionProviders, PROTECTED_TAIL_USER_TURNS = 5;
149937
149915
  var init_read_session_chunk = __esm(async () => {
149938
149916
  init_read_session_formatting();
149939
149917
  init_tag_part_guards();
149940
149918
  init_tool_drop_target();
149941
149919
  init_read_session_formatting();
149942
149920
  await init_read_session_db();
149921
+ blockTokenMemo = new Map;
149943
149922
  sessionProviders = new Map;
149944
149923
  });
149945
149924
 
@@ -150684,6 +150663,9 @@ function resolveDatabasePath(dbPathOverride) {
150684
150663
  const dbDir = getMagicContextStorageDir();
150685
150664
  return { dbDir, dbPath: join6(dbDir, "context.db") };
150686
150665
  }
150666
+ function getDatabasePath(db) {
150667
+ return pathByDatabase.get(db) ?? null;
150668
+ }
150687
150669
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
150688
150670
  if (existsSync7(targetDbPath))
150689
150671
  return;
@@ -151194,6 +151176,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151194
151176
  recovery_no_eligible_head_count INTEGER NOT NULL DEFAULT 0,
151195
151177
  force_emergency_bypass_window_start INTEGER NOT NULL DEFAULT 0,
151196
151178
  force_emergency_bypass_used INTEGER NOT NULL DEFAULT 0,
151179
+ emergency_drain_active INTEGER NOT NULL DEFAULT 0,
151180
+ historian_drain_failure_at INTEGER NOT NULL DEFAULT 0,
151197
151181
  cached_m0_materialized_at INTEGER,
151198
151182
  cached_m0_session_facts_version INTEGER,
151199
151183
  cached_m0_upgrade_state TEXT,
@@ -151257,6 +151241,23 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151257
151241
  CREATE INDEX IF NOT EXISTS idx_historian_runs_status
151258
151242
  ON historian_runs(status, created_at DESC);
151259
151243
 
151244
+ CREATE TABLE IF NOT EXISTS transform_decisions (
151245
+ session_id TEXT NOT NULL,
151246
+ harness TEXT NOT NULL DEFAULT 'opencode',
151247
+ message_id TEXT NOT NULL,
151248
+ ts_ms INTEGER NOT NULL,
151249
+ decision TEXT NOT NULL,
151250
+ materialized INTEGER NOT NULL DEFAULT 0,
151251
+ materialize_reason TEXT,
151252
+ emergency INTEGER NOT NULL DEFAULT 0,
151253
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
151254
+ dropped_count INTEGER NOT NULL DEFAULT 0,
151255
+ input_tokens INTEGER NOT NULL DEFAULT 0,
151256
+ PRIMARY KEY (session_id, harness, message_id)
151257
+ );
151258
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
151259
+ ON transform_decisions(session_id, harness);
151260
+
151260
151261
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
151261
151262
  CREATE INDEX IF NOT EXISTS idx_tags_session_message_id ON tags(session_id, message_id);
151262
151263
  CREATE INDEX IF NOT EXISTS idx_pending_ops_session ON pending_ops(session_id);
@@ -151334,6 +151335,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151334
151335
  ensureColumn(db, "session_meta", "historian_last_failure_at", "INTEGER DEFAULT NULL");
151335
151336
  ensureColumn(db, "session_meta", "system_prompt_hash", "TEXT DEFAULT ''");
151336
151337
  ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
151338
+ ensureColumn(db, "session_meta", "tool_reclaim_watermark", "INTEGER DEFAULT 0");
151337
151339
  ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
151338
151340
  ensureColumn(db, "session_meta", "stale_reduce_stripped_ids", "TEXT DEFAULT ''");
151339
151341
  ensureColumn(db, "session_meta", "processed_image_stripped_ids", "TEXT DEFAULT ''");
@@ -151407,6 +151409,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151407
151409
  ensureColumn(db, "session_meta", "recovery_no_eligible_head_count", "INTEGER NOT NULL DEFAULT 0");
151408
151410
  ensureColumn(db, "session_meta", "force_emergency_bypass_window_start", "INTEGER NOT NULL DEFAULT 0");
151409
151411
  ensureColumn(db, "session_meta", "force_emergency_bypass_used", "INTEGER NOT NULL DEFAULT 0");
151412
+ ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
151413
+ ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
151410
151414
  ensureColumn(db, "session_meta", "cached_m0_materialized_at", "INTEGER");
151411
151415
  ensureColumn(db, "session_meta", "cached_m0_session_facts_version", "INTEGER");
151412
151416
  ensureColumn(db, "session_meta", "cached_m0_upgrade_state", "TEXT");
@@ -151485,6 +151489,22 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151485
151489
  failed_at INTEGER NOT NULL,
151486
151490
  UNIQUE(table_name, row_id)
151487
151491
  );
151492
+ CREATE TABLE IF NOT EXISTS transform_decisions (
151493
+ session_id TEXT NOT NULL,
151494
+ harness TEXT NOT NULL DEFAULT 'opencode',
151495
+ message_id TEXT NOT NULL,
151496
+ ts_ms INTEGER NOT NULL,
151497
+ decision TEXT NOT NULL,
151498
+ materialized INTEGER NOT NULL DEFAULT 0,
151499
+ materialize_reason TEXT,
151500
+ emergency INTEGER NOT NULL DEFAULT 0,
151501
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
151502
+ dropped_count INTEGER NOT NULL DEFAULT 0,
151503
+ input_tokens INTEGER NOT NULL DEFAULT 0,
151504
+ PRIMARY KEY (session_id, harness, message_id)
151505
+ );
151506
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
151507
+ ON transform_decisions(session_id, harness);
151488
151508
  `);
151489
151509
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
151490
151510
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -151570,7 +151590,9 @@ function healNullIntegerColumns(db) {
151570
151590
  ["protected_tail_drain_tokens", 0],
151571
151591
  ["recovery_no_eligible_head_count", 0],
151572
151592
  ["force_emergency_bypass_window_start", 0],
151573
- ["force_emergency_bypass_used", 0]
151593
+ ["force_emergency_bypass_used", 0],
151594
+ ["emergency_drain_active", 0],
151595
+ ["historian_drain_failure_at", 0]
151574
151596
  ];
151575
151597
  for (const [column, fallback] of columns) {
151576
151598
  try {
@@ -151646,6 +151668,7 @@ function openDatabase(dbPathOrOptions) {
151646
151668
  setDatabase(db);
151647
151669
  loadToolDefinitionMeasurements(db);
151648
151670
  databases.set(dbPath, db);
151671
+ pathByDatabase.set(db, dbPath);
151649
151672
  persistenceByDatabase.set(db, true);
151650
151673
  persistenceErrorByDatabase.delete(db);
151651
151674
  return db;
@@ -151665,7 +151688,7 @@ function getDatabasePersistenceError(db) {
151665
151688
  return null;
151666
151689
  return persistenceErrorByDatabase.get(db) ?? null;
151667
151690
  }
151668
- var databases, persistenceByDatabase, persistenceErrorByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 36, sqlitePragmaConfig, CHANNEL2_CLAIM_TTL_MS = 120000;
151691
+ var databases, persistenceByDatabase, persistenceErrorByDatabase, pathByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 38, sqlitePragmaConfig, CHANNEL2_CLAIM_TTL_MS = 120000;
151669
151692
  var init_storage_db = __esm(async () => {
151670
151693
  init_data_path();
151671
151694
  init_logger();
@@ -151679,6 +151702,7 @@ var init_storage_db = __esm(async () => {
151679
151702
  databases = new Map;
151680
151703
  persistenceByDatabase = new WeakMap;
151681
151704
  persistenceErrorByDatabase = new WeakMap;
151705
+ pathByDatabase = new WeakMap;
151682
151706
  sqlitePragmaConfig = {
151683
151707
  cacheSizeMb: 64,
151684
151708
  mmapSizeMb: 0
@@ -152997,6 +153021,41 @@ var init_migrations = __esm(async () => {
152997
153021
  `);
152998
153022
  }
152999
153023
  }
153024
+ },
153025
+ {
153026
+ version: 37,
153027
+ description: "emergency drain catch-up latch + historian drain failure backoff",
153028
+ up: (db) => {
153029
+ const hasSessionMeta = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='session_meta'").get();
153030
+ if (!hasSessionMeta)
153031
+ return;
153032
+ ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
153033
+ ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
153034
+ }
153035
+ },
153036
+ {
153037
+ version: 38,
153038
+ description: "durable transform decisions for cache-event cause attribution",
153039
+ up: (db) => {
153040
+ db.exec(`
153041
+ CREATE TABLE IF NOT EXISTS transform_decisions (
153042
+ session_id TEXT NOT NULL,
153043
+ harness TEXT NOT NULL DEFAULT 'opencode',
153044
+ message_id TEXT NOT NULL,
153045
+ ts_ms INTEGER NOT NULL,
153046
+ decision TEXT NOT NULL,
153047
+ materialized INTEGER NOT NULL DEFAULT 0,
153048
+ materialize_reason TEXT,
153049
+ emergency INTEGER NOT NULL DEFAULT 0,
153050
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
153051
+ dropped_count INTEGER NOT NULL DEFAULT 0,
153052
+ input_tokens INTEGER NOT NULL DEFAULT 0,
153053
+ PRIMARY KEY (session_id, harness, message_id)
153054
+ );
153055
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
153056
+ ON transform_decisions(session_id, harness);
153057
+ `);
153058
+ }
153000
153059
  }
153001
153060
  ];
153002
153061
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -153401,7 +153460,9 @@ function toProtectedTailMeta(row) {
153401
153460
  protectedTailDrainTokens: numberOr(r.protected_tail_drain_tokens, 0),
153402
153461
  recoveryNoEligibleHeadCount: numberOr(r.recovery_no_eligible_head_count, 0),
153403
153462
  forceEmergencyBypassWindowStart: numberOr(r.force_emergency_bypass_window_start, 0),
153404
- forceEmergencyBypassUsed: numberOr(r.force_emergency_bypass_used, 0)
153463
+ forceEmergencyBypassUsed: numberOr(r.force_emergency_bypass_used, 0),
153464
+ emergencyDrainActive: numberOr(r.emergency_drain_active, 0),
153465
+ historianDrainFailureAt: numberOr(r.historian_drain_failure_at, 0)
153405
153466
  };
153406
153467
  }
153407
153468
  function loadProtectedTailMeta(db, sessionId) {
@@ -153409,7 +153470,7 @@ function loadProtectedTailMeta(db, sessionId) {
153409
153470
  const row = db.prepare(`SELECT prior_boundary_ordinal, protected_tail_policy_version,
153410
153471
  protected_tail_drain_window_started_at, protected_tail_drain_tokens,
153411
153472
  recovery_no_eligible_head_count, force_emergency_bypass_window_start,
153412
- force_emergency_bypass_used
153473
+ force_emergency_bypass_used, emergency_drain_active, historian_drain_failure_at
153413
153474
  FROM session_meta WHERE session_id = ?`).get(sessionId);
153414
153475
  return toProtectedTailMeta(row);
153415
153476
  }
@@ -153458,6 +153519,12 @@ function protectedTailWindowBudget(usagePercentage, usable, perRunCap) {
153458
153519
  return Math.min(750000, Math.max(3 * perRunCap, Math.round(0.35 * usable)));
153459
153520
  return Math.min(500000, Math.max(perRunCap, Math.round(0.2 * usable)));
153460
153521
  }
153522
+ function emergencyDrainExitThreshold(executeThresholdPercentage) {
153523
+ if (!Number.isFinite(executeThresholdPercentage) || executeThresholdPercentage <= 0) {
153524
+ return EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE;
153525
+ }
153526
+ return Math.max(0, executeThresholdPercentage - EMERGENCY_DRAIN_EXIT_MARGIN);
153527
+ }
153461
153528
  function reserveProtectedTailDrainTokens(args) {
153462
153529
  const now = args.now ?? Date.now();
153463
153530
  const requested = Math.max(0, Math.floor(args.trueRawTokens));
@@ -153476,18 +153543,30 @@ function reserveProtectedTailDrainTokens(args) {
153476
153543
  let meta3 = loadProtectedTailMeta(args.db, args.sessionId);
153477
153544
  if (now - meta3.protectedTailDrainWindowStartedAt > DRAIN_WINDOW_MS) {
153478
153545
  args.db.prepare(`UPDATE session_meta
153479
- SET protected_tail_drain_window_started_at = ?, protected_tail_drain_tokens = 0,
153480
- force_emergency_bypass_window_start = ?, force_emergency_bypass_used = 0
153481
- WHERE session_id = ?`).run(now, now, args.sessionId);
153546
+ SET protected_tail_drain_window_started_at = ?, protected_tail_drain_tokens = 0
153547
+ WHERE session_id = ?`).run(now, args.sessionId);
153482
153548
  meta3 = loadProtectedTailMeta(args.db, args.sessionId);
153483
153549
  }
153550
+ const exitThreshold = emergencyDrainExitThreshold(args.executeThresholdPercentage);
153551
+ let latchActiveSince = meta3.emergencyDrainActive;
153552
+ if (args.usagePercentage >= EMERGENCY_DRAIN_ENTER_PERCENTAGE) {
153553
+ if (latchActiveSince <= 0)
153554
+ latchActiveSince = now;
153555
+ } else if (latchActiveSince > 0) {
153556
+ const expired = now - latchActiveSince > EMERGENCY_DRAIN_MAX_LATCH_MS;
153557
+ if (args.usagePercentage < exitThreshold || expired)
153558
+ latchActiveSince = 0;
153559
+ }
153560
+ if (latchActiveSince !== meta3.emergencyDrainActive) {
153561
+ args.db.prepare("UPDATE session_meta SET emergency_drain_active = ? WHERE session_id = ?").run(latchActiveSince, args.sessionId);
153562
+ }
153563
+ const latchActive = latchActiveSince > 0;
153484
153564
  const budget = protectedTailWindowBudget(args.usagePercentage, args.usable, args.perRunCap);
153485
153565
  const remaining = Math.max(0, budget - meta3.protectedTailDrainTokens);
153486
153566
  let reserved = Math.min(requested, args.perRunCap, remaining);
153487
153567
  let bypass = false;
153488
- const bypassWindowExpired = now - meta3.forceEmergencyBypassWindowStart > DRAIN_WINDOW_MS;
153489
- const bypassUsed = bypassWindowExpired ? 0 : meta3.forceEmergencyBypassUsed;
153490
- if (reserved <= 0 && args.usagePercentage >= 95 && bypassUsed === 0) {
153568
+ const inFailureBackoff = meta3.historianDrainFailureAt > 0 && now - meta3.historianDrainFailureAt < EMERGENCY_DRAIN_FAILURE_BACKOFF_MS;
153569
+ if (reserved <= 0 && latchActive && !inFailureBackoff) {
153491
153570
  reserved = Math.min(requested, args.perRunCap);
153492
153571
  bypass = true;
153493
153572
  }
@@ -153495,10 +153574,8 @@ function reserveProtectedTailDrainTokens(args) {
153495
153574
  return;
153496
153575
  args.db.prepare(`UPDATE session_meta
153497
153576
  SET protected_tail_drain_window_started_at = CASE WHEN protected_tail_drain_window_started_at = 0 THEN ? ELSE protected_tail_drain_window_started_at END,
153498
- protected_tail_drain_tokens = COALESCE(protected_tail_drain_tokens, 0) + ?,
153499
- force_emergency_bypass_window_start = CASE WHEN ? THEN ? ELSE force_emergency_bypass_window_start END,
153500
- force_emergency_bypass_used = CASE WHEN ? THEN 1 ELSE force_emergency_bypass_used END
153501
- WHERE session_id = ?`).run(now, reserved, bypass ? 1 : 0, now, bypass ? 1 : 0, args.sessionId);
153577
+ protected_tail_drain_tokens = COALESCE(protected_tail_drain_tokens, 0) + ?
153578
+ WHERE session_id = ?`).run(now, reserved, args.sessionId);
153502
153579
  result = {
153503
153580
  ok: true,
153504
153581
  reservedTokens: reserved,
@@ -153508,6 +153585,25 @@ function reserveProtectedTailDrainTokens(args) {
153508
153585
  })();
153509
153586
  return result;
153510
153587
  }
153588
+ function clearEmergencyDrainLatch(db, sessionId) {
153589
+ db.transaction(() => {
153590
+ ensureSessionMetaRow(db, sessionId);
153591
+ db.prepare("UPDATE session_meta SET emergency_drain_active = 0 WHERE session_id = ?").run(sessionId);
153592
+ })();
153593
+ }
153594
+ function recordHistorianDrainFailure(db, sessionId, now) {
153595
+ const ts = now ?? Date.now();
153596
+ db.transaction(() => {
153597
+ ensureSessionMetaRow(db, sessionId);
153598
+ db.prepare("UPDATE session_meta SET historian_drain_failure_at = ? WHERE session_id = ?").run(ts, sessionId);
153599
+ })();
153600
+ }
153601
+ function clearHistorianDrainFailure(db, sessionId) {
153602
+ db.transaction(() => {
153603
+ ensureSessionMetaRow(db, sessionId);
153604
+ db.prepare("UPDATE session_meta SET historian_drain_failure_at = 0 WHERE session_id = ?").run(sessionId);
153605
+ })();
153606
+ }
153511
153607
  function rollbackProtectedTailDrainReservation(db, reservation) {
153512
153608
  if (!reservation || reservation.tokens <= 0)
153513
153609
  return;
@@ -154075,7 +154171,7 @@ function setSessionWorkMetrics(db, sessionId, newWorkTokens, totalInputTokens) {
154075
154171
  SET new_work_tokens = ?, total_input_tokens = ?
154076
154172
  WHERE session_id = ?`).run(Math.max(0, Math.floor(newWorkTokens)), Math.max(0, Math.floor(totalInputTokens)), sessionId);
154077
154173
  }
154078
- var CAS_RETRY_LIMIT = 5, AUTO_SEARCH_NO_HINT_REASONS, DEFAULT_PROTECTED_TAIL_META, DRAIN_WINDOW_MS;
154174
+ var CAS_RETRY_LIMIT = 5, AUTO_SEARCH_NO_HINT_REASONS, DEFAULT_PROTECTED_TAIL_META, DRAIN_WINDOW_MS, EMERGENCY_DRAIN_ENTER_PERCENTAGE = 95, EMERGENCY_DRAIN_EXIT_MARGIN = 10, EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE = 55, EMERGENCY_DRAIN_FAILURE_BACKOFF_MS = 60000, EMERGENCY_DRAIN_MAX_LATCH_MS;
154079
154175
  var init_storage_meta_persisted = __esm(() => {
154080
154176
  init_logger();
154081
154177
  init_storage_meta_shared();
@@ -154094,9 +154190,12 @@ var init_storage_meta_persisted = __esm(() => {
154094
154190
  protectedTailDrainTokens: 0,
154095
154191
  recoveryNoEligibleHeadCount: 0,
154096
154192
  forceEmergencyBypassWindowStart: 0,
154097
- forceEmergencyBypassUsed: 0
154193
+ forceEmergencyBypassUsed: 0,
154194
+ emergencyDrainActive: 0,
154195
+ historianDrainFailureAt: 0
154098
154196
  };
154099
154197
  DRAIN_WINDOW_MS = 10 * 60 * 1000;
154198
+ EMERGENCY_DRAIN_MAX_LATCH_MS = 30 * 60 * 1000;
154100
154199
  });
154101
154200
 
154102
154201
  // src/features/magic-context/resolve-subagent-fallback.ts
@@ -154123,7 +154222,8 @@ var exports_storage_meta_session = {};
154123
154222
  __export(exports_storage_meta_session, {
154124
154223
  updateSessionMeta: () => updateSessionMeta,
154125
154224
  getOrCreateSessionMeta: () => getOrCreateSessionMeta,
154126
- clearSession: () => clearSession
154225
+ clearSession: () => clearSession,
154226
+ advanceToolReclaimWatermark: () => advanceToolReclaimWatermark
154127
154227
  });
154128
154228
  import { Buffer as Buffer3 } from "node:buffer";
154129
154229
  function getSessionMetaSelectColumns(db) {
@@ -154184,6 +154284,14 @@ function updateSessionMeta(db, sessionId, updates) {
154184
154284
  db.prepare(`UPDATE session_meta SET ${setClauses.join(", ")} WHERE session_id = ?`).run(...values, sessionId);
154185
154285
  })();
154186
154286
  }
154287
+ function advanceToolReclaimWatermark(db, sessionId, maxTagNumber) {
154288
+ if (maxTagNumber <= 0)
154289
+ return;
154290
+ db.transaction(() => {
154291
+ ensureSessionMetaRow(db, sessionId);
154292
+ db.prepare("UPDATE session_meta SET tool_reclaim_watermark = MAX(COALESCE(tool_reclaim_watermark, 0), ?) WHERE session_id = ?").run(maxTagNumber, sessionId);
154293
+ })();
154294
+ }
154187
154295
  function clearSession(db, sessionId) {
154188
154296
  db.transaction(() => {
154189
154297
  db.prepare("DELETE FROM pending_ops WHERE session_id = ?").run(sessionId);
@@ -154205,6 +154313,7 @@ function clearSession(db, sessionId) {
154205
154313
  db.prepare("DELETE FROM subagent_invocations WHERE session_id = ?").run(sessionId);
154206
154314
  db.prepare("DELETE FROM historian_runs WHERE session_id = ?").run(sessionId);
154207
154315
  db.prepare("DELETE FROM plugin_messages WHERE session_id = ?").run(sessionId);
154316
+ db.prepare("DELETE FROM transform_decisions WHERE session_id = ?").run(sessionId);
154208
154317
  clearIndexedMessages(db, sessionId);
154209
154318
  })();
154210
154319
  }
@@ -154222,6 +154331,7 @@ var init_storage_meta_session = __esm(async () => {
154222
154331
  last_transform_error: "'' AS last_transform_error",
154223
154332
  system_prompt_hash: "'' AS system_prompt_hash",
154224
154333
  last_todo_state: "'' AS last_todo_state",
154334
+ tool_reclaim_watermark: "0 AS tool_reclaim_watermark",
154225
154335
  cached_m0_bytes: "NULL AS cached_m0_bytes",
154226
154336
  cached_m1_bytes: "NULL AS cached_m1_bytes",
154227
154337
  cached_m0_project_memory_epoch: "NULL AS cached_m0_project_memory_epoch",
@@ -154629,12 +154739,37 @@ function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
154629
154739
  nullCount: row?.null_count ?? 0
154630
154740
  };
154631
154741
  }
154632
- function getTriggerTagTokenUpperBound(db, sessionId) {
154633
- const row = db.prepare(`SELECT
154742
+ function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, limit = 4) {
154743
+ if (limit <= 0)
154744
+ return [];
154745
+ const boundedLimit = Math.max(1, Math.min(10, Math.floor(limit)));
154746
+ const whereProtected = protectedTags > 0 ? `AND tag_number < (
154747
+ SELECT tag_number FROM tags
154748
+ WHERE session_id = ? AND status = 'active'
154749
+ ORDER BY tag_number DESC LIMIT 1 OFFSET ?
154750
+ )` : "";
154751
+ const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
154752
+ const rows = db.prepare(`SELECT tag_number, tool_name
154753
+ FROM tags
154754
+ WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
154755
+ ORDER BY tag_number ASC, id ASC
154756
+ LIMIT ?`).all(...params);
154757
+ return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
154758
+ tagNumber: row.tag_number,
154759
+ toolName: typeof row.tool_name === "string" ? row.tool_name : null
154760
+ }));
154761
+ }
154762
+ function getTriggerTagTokenUpperBound(db, sessionId, floor = 0) {
154763
+ const sql = floor > 0 ? `SELECT
154764
+ COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
154765
+ COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
154766
+ FROM tags
154767
+ WHERE session_id = ? AND status IN ('active', 'dropped') AND tag_number >= ?` : `SELECT
154634
154768
  COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
154635
154769
  COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
154636
154770
  FROM tags
154637
- WHERE session_id = ? AND status IN ('active', 'dropped')`).get(sessionId);
154771
+ WHERE session_id = ? AND status IN ('active', 'dropped')`;
154772
+ const row = floor > 0 ? db.prepare(sql).get(sessionId, floor) : db.prepare(sql).get(sessionId);
154638
154773
  return { bound: row?.bound ?? 0, nullCount: row?.null_count ?? 0 };
154639
154774
  }
154640
154775
  function getActiveTagTokenTotalsByMessage(db, sessionId) {
@@ -154663,10 +154798,12 @@ function getActiveTagTokenTotalsByMessage(db, sessionId) {
154663
154798
  }
154664
154799
  return out;
154665
154800
  }
154666
- function getAllStatusTagTokenTotalsFlat(db, sessionId) {
154667
- const rows = db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
154668
- FROM tags
154669
- WHERE session_id = ?`).all(sessionId);
154801
+ function getAllStatusTagTokenTotalsFlat(db, sessionId, floor = 0) {
154802
+ const rows = floor > 0 ? db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
154803
+ FROM tags
154804
+ WHERE session_id = ? AND tag_number >= ?`).all(sessionId, floor) : db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
154805
+ FROM tags
154806
+ WHERE session_id = ?`).all(sessionId);
154670
154807
  const totals = new Map;
154671
154808
  const nullMessageIds = new Set;
154672
154809
  for (const row of rows) {
@@ -154825,6 +154962,47 @@ function getTagNumberByMessageId(db, sessionId, messageId) {
154825
154962
  const row = getTagNumberByMessageIdStatement(db).get(sessionId, messageId);
154826
154963
  return isTagNumberRow(row) ? row.tag_number : null;
154827
154964
  }
154965
+ function isMinTagNumberRow(row) {
154966
+ return row !== null && typeof row === "object" && "m" in row;
154967
+ }
154968
+ function getMinMessageTagNumberForRawId(db, sessionId, rawId) {
154969
+ if (rawId.includes(":"))
154970
+ return null;
154971
+ let stmt = getMinMessageTagNumberForRawIdStatements.get(db);
154972
+ if (!stmt) {
154973
+ stmt = db.prepare("SELECT MIN(tag_number) AS m FROM tags WHERE session_id = ? AND message_id >= ? AND message_id < ?");
154974
+ getMinMessageTagNumberForRawIdStatements.set(db, stmt);
154975
+ }
154976
+ const row = stmt.get(sessionId, `${rawId}:`, `${rawId};`);
154977
+ return isMinTagNumberRow(row) && typeof row.m === "number" ? row.m : null;
154978
+ }
154979
+ function deriveTagLoadFloor(db, sessionId, rawIds) {
154980
+ let min = Number.POSITIVE_INFINITY;
154981
+ let probes = 0;
154982
+ let hits = 0;
154983
+ let skippedBeforeFirstHit = 0;
154984
+ for (const rawId of rawIds) {
154985
+ if (typeof rawId !== "string" || rawId.length === 0)
154986
+ continue;
154987
+ if (probes >= TAGGER_FLOOR_MAX_PROBES)
154988
+ break;
154989
+ probes++;
154990
+ const m = getMinMessageTagNumberForRawId(db, sessionId, rawId);
154991
+ if (m === null) {
154992
+ if (hits === 0)
154993
+ skippedBeforeFirstHit++;
154994
+ continue;
154995
+ }
154996
+ if (m < min)
154997
+ min = m;
154998
+ if (++hits >= TAGGER_FLOOR_SCAN_MESSAGES)
154999
+ break;
155000
+ }
155001
+ if (!Number.isFinite(min))
155002
+ return 0;
155003
+ const margin = TAGGER_FLOOR_SAFETY_MARGIN + skippedBeforeFirstHit * TAGGER_FLOOR_PER_SKIP_MARGIN;
155004
+ return Math.max(0, min - margin);
155005
+ }
154828
155006
  function getTagsBySession(db, sessionId) {
154829
155007
  const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? ORDER BY tag_number ASC, id ASC`).all(sessionId).filter(isTagRow);
154830
155008
  return rows.map(toTagEntry);
@@ -154963,7 +155141,7 @@ function deleteToolTagsByOwner(db, sessionId, ownerMsgId) {
154963
155141
  const result = getDeleteToolTagsByOwnerStatement(db).run(sessionId, ownerMsgId);
154964
155142
  return result.changes ?? 0;
154965
155143
  }
154966
- var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements, CONTENT_ID_SUFFIX, updateTagTokenCountStatements, updateTagInputTokenCountStatements, getOwnerScopedToolTagNumbersStatements, TAG_SELECT_COLUMNS = "id, message_id, type, status, drop_mode, tool_name, input_byte_size, byte_size, reasoning_byte_size, session_id, tag_number, caveman_depth, tool_owner_message_id", getActiveTagsBySessionStatements, getMaxDroppedTagNumberStatements, getToolTagNumberByOwnerStatements, getNullOwnerToolTagStatements, adoptNullOwnerToolTagStatements, deleteToolTagsByOwnerStatements;
155144
+ var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements, CONTENT_ID_SUFFIX, updateTagTokenCountStatements, updateTagInputTokenCountStatements, getOwnerScopedToolTagNumbersStatements, getMinMessageTagNumberForRawIdStatements, TAGGER_FLOOR_SCAN_MESSAGES = 8, TAGGER_FLOOR_MAX_PROBES = 64, TAGGER_FLOOR_SAFETY_MARGIN = 256, TAGGER_FLOOR_PER_SKIP_MARGIN = 64, TAG_SELECT_COLUMNS = "id, message_id, type, status, drop_mode, tool_name, input_byte_size, byte_size, reasoning_byte_size, session_id, tag_number, caveman_depth, tool_owner_message_id", getActiveTagsBySessionStatements, getMaxDroppedTagNumberStatements, getToolTagNumberByOwnerStatements, getNullOwnerToolTagStatements, adoptNullOwnerToolTagStatements, deleteToolTagsByOwnerStatements;
154967
155145
  var init_storage_tags = __esm(() => {
154968
155146
  insertTagStatements = new WeakMap;
154969
155147
  updateTagStatusStatements = new WeakMap;
@@ -154979,6 +155157,7 @@ var init_storage_tags = __esm(() => {
154979
155157
  updateTagTokenCountStatements = new WeakMap;
154980
155158
  updateTagInputTokenCountStatements = new WeakMap;
154981
155159
  getOwnerScopedToolTagNumbersStatements = new WeakMap;
155160
+ getMinMessageTagNumberForRawIdStatements = new WeakMap;
154982
155161
  getActiveTagsBySessionStatements = new WeakMap;
154983
155162
  getMaxDroppedTagNumberStatements = new WeakMap;
154984
155163
  getToolTagNumberByOwnerStatements = new WeakMap;
@@ -163972,6 +164151,58 @@ var init_safe_notification_target = __esm(() => {
163972
164151
  DEFAULT_TITLE_RE = /^(New session - |Child session - )\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
163973
164152
  });
163974
164153
 
164154
+ // src/shared/rpc-notifications.ts
164155
+ var exports_rpc_notifications = {};
164156
+ __export(exports_rpc_notifications, {
164157
+ pushNotification: () => pushNotification,
164158
+ isTuiConnected: () => isTuiConnected,
164159
+ drainNotifications: () => drainNotifications
164160
+ });
164161
+ function pushNotification(type, payload, sessionId) {
164162
+ queue.push({ id: nextNotificationId++, type, payload, sessionId });
164163
+ if (queue.length > 100) {
164164
+ const newestPerSession = new Map;
164165
+ for (const n of queue) {
164166
+ const prev = newestPerSession.get(n.sessionId);
164167
+ if (prev === undefined || n.id > prev) {
164168
+ newestPerSession.set(n.sessionId, n.id);
164169
+ }
164170
+ }
164171
+ const mustKeep = new Set(newestPerSession.values());
164172
+ const byNewest = [...queue].sort((a, b) => b.id - a.id);
164173
+ const kept = [];
164174
+ for (const n of byNewest) {
164175
+ if (kept.length < 50 || mustKeep.has(n.id))
164176
+ kept.push(n);
164177
+ }
164178
+ queue = kept.sort((a, b) => a.id - b.id);
164179
+ }
164180
+ }
164181
+ function drainNotifications(lastReceivedId = 0, sessionId) {
164182
+ const now = Date.now();
164183
+ lastDrainAtAny = now;
164184
+ if (sessionId !== undefined)
164185
+ lastDrainAtBySession.set(sessionId, now);
164186
+ const matchesClient = (notification) => sessionId === undefined || notification.sessionId === undefined || notification.sessionId === sessionId;
164187
+ if (lastReceivedId > 0) {
164188
+ queue = queue.filter((notification) => !(notification.id <= lastReceivedId && matchesClient(notification)));
164189
+ }
164190
+ return queue.filter((notification) => notification.id > lastReceivedId && matchesClient(notification));
164191
+ }
164192
+ function isTuiConnected(sessionId) {
164193
+ const now = Date.now();
164194
+ if (sessionId !== undefined) {
164195
+ const at = lastDrainAtBySession.get(sessionId) ?? 0;
164196
+ return at > 0 && now - at < TUI_CONNECTED_WINDOW_MS;
164197
+ }
164198
+ return lastDrainAtAny > 0 && now - lastDrainAtAny < TUI_CONNECTED_WINDOW_MS;
164199
+ }
164200
+ var queue, nextNotificationId = 1, lastDrainAtBySession, lastDrainAtAny = 0, TUI_CONNECTED_WINDOW_MS = 3000;
164201
+ var init_rpc_notifications = __esm(() => {
164202
+ queue = [];
164203
+ lastDrainAtBySession = new Map;
164204
+ });
164205
+
163975
164206
  // src/plugin/conflict-warning-hook.ts
163976
164207
  var exports_conflict_warning_hook = {};
163977
164208
  __export(exports_conflict_warning_hook, {
@@ -164306,6 +164537,9 @@ async function sendStartupAnnouncement(client, directory, version2, features, fo
164306
164537
  if (!sessionId) {
164307
164538
  return;
164308
164539
  }
164540
+ const { isTuiConnected: isTuiConnected2 } = await Promise.resolve().then(() => (init_rpc_notifications(), exports_rpc_notifications));
164541
+ if (isTuiConnected2(sessionId) || isTuiConnected2())
164542
+ return;
164309
164543
  if (await waitForSafeNotificationTarget(client, sessionId) === "skip")
164310
164544
  return;
164311
164545
  const bullets = features.map((line) => ` • ${line}`).join(`
@@ -164528,6 +164762,20 @@ function getDistinctStoredModelIds(db, projectPath) {
164528
164762
  const rows = getDistinctStoredModelIdsStatement(db).all(projectPath);
164529
164763
  return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
164530
164764
  }
164765
+ function getMemoryEmbedCoverage(db, projectPath, modelId) {
164766
+ const row = db.prepare(`SELECT
164767
+ COUNT(*) AS total,
164768
+ SUM(CASE WHEN EXISTS (
164769
+ SELECT 1 FROM memory_embeddings e
164770
+ WHERE e.memory_id = m.id AND e.model_id = ?
164771
+ ) THEN 1 ELSE 0 END) AS embedded
164772
+ FROM memories m
164773
+ WHERE m.project_path = ? AND m.status = 'active'`).get(modelId, projectPath);
164774
+ return {
164775
+ total: typeof row?.total === "number" ? row.total : 0,
164776
+ embedded: typeof row?.embedded === "number" ? row.embedded : 0
164777
+ };
164778
+ }
164531
164779
  var saveEmbeddingStatements, loadAllEmbeddingsStatements, deleteEmbeddingStatements, getStoredModelIdStatements, clearAllEmbeddingsStatements, getDistinctStoredModelIdsStatements;
164532
164780
  var init_storage_memory_embeddings = __esm(() => {
164533
164781
  saveEmbeddingStatements = new WeakMap;
@@ -165330,6 +165578,16 @@ function buildCanonicalChunkTextFromFts(db, sessionId, startOrdinal, endOrdinal)
165330
165578
  return lines.join(`
165331
165579
  `);
165332
165580
  }
165581
+ function buildCompartmentSummaryFallbackText(db, compartmentId) {
165582
+ const row = db.prepare("SELECT title, p1, content FROM compartments WHERE id = ?").get(compartmentId);
165583
+ if (!row)
165584
+ return "";
165585
+ const title = typeof row.title === "string" ? row.title.trim() : "";
165586
+ const p1 = typeof row.p1 === "string" ? row.p1.trim() : "";
165587
+ const body = p1.length > 0 ? p1 : typeof row.content === "string" ? row.content.trim() : "";
165588
+ return [title, body].filter((s) => s.length > 0).join(`
165589
+ `);
165590
+ }
165333
165591
  function canonicalizeInMemoryChunkTextForEmbedding(chunkText, startOrdinal, endOrdinal) {
165334
165592
  const lines = [];
165335
165593
  for (const rawLine of chunkText.split(/\r?\n/)) {
@@ -165572,6 +165830,28 @@ function countUnembeddedSessionCompartments(db, projectPath, sessionId, modelId)
165572
165830
  )`).get(projectPath, sessionId, projectPath, modelId);
165573
165831
  return typeof row?.n === "number" ? row.n : 0;
165574
165832
  }
165833
+ function countSessionCompartmentEmbedCoverage(db, projectPath, sessionId, modelId) {
165834
+ const row = db.prepare(`SELECT
165835
+ COUNT(*) AS total,
165836
+ SUM(CASE WHEN EXISTS (
165837
+ SELECT 1 FROM compartment_chunk_embeddings e
165838
+ WHERE e.compartment_id = c.id
165839
+ AND e.project_path = ?
165840
+ AND e.model_id = ?
165841
+ ) THEN 1 ELSE 0 END) AS embedded
165842
+ FROM compartments c
165843
+ JOIN session_projects sp
165844
+ ON sp.session_id = c.session_id
165845
+ AND sp.harness = c.harness
165846
+ AND sp.project_path = ?
165847
+ WHERE c.session_id = ?
165848
+ AND c.start_message IS NOT NULL
165849
+ AND c.end_message IS NOT NULL`).get(projectPath, modelId, projectPath, sessionId);
165850
+ return {
165851
+ total: typeof row?.total === "number" ? row.total : 0,
165852
+ embedded: typeof row?.embedded === "number" ? row.embedded : 0
165853
+ };
165854
+ }
165575
165855
  var DEFAULT_COMPARTMENT_CHUNK_MAX_INPUT_TOKENS = 512, CHUNK_WINDOW_SAFETY_RATIO = 0.9, loadFtsRowsStatements, existingHashStatements, existingHashByProjectStatements, deleteByCompartmentStatements, insertEmbeddingStatements, distinctModelStatements, clearProjectStatements, clearProjectModelStatements, searchRowsStatements, searchRowsByModelStatements, backfillCandidateStatements, sessionBackfillCandidateStatements;
165576
165856
  var init_compartment_chunk_embedding = __esm(() => {
165577
165857
  init_read_session_formatting();
@@ -165729,6 +166009,18 @@ async function withQuietConsole(fn) {
165729
166009
  console.error = origError;
165730
166010
  }
165731
166011
  }
166012
+ function isNativeRuntimeMissingError(error51) {
166013
+ const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
166014
+ const lower = message.toLowerCase();
166015
+ const code = error51?.code;
166016
+ const name2 = error51?.name;
166017
+ if (code === "ERR_DLOPEN_FAILED" && lower.includes("onnxruntime")) {
166018
+ return true;
166019
+ }
166020
+ if (!lower.includes("onnxruntime-node"))
166021
+ return false;
166022
+ return code === "ERR_MODULE_NOT_FOUND" || name2 === "ResolveMessage" || lower.includes("cannot find package") || lower.includes("cannot find module") || lower.includes("err_module_not_found");
166023
+ }
165732
166024
  function isTransientLoadError(error51) {
165733
166025
  const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
165734
166026
  if (!message)
@@ -165793,6 +166085,9 @@ class LocalEmbeddingProvider {
165793
166085
  if (this.pipeline) {
165794
166086
  return true;
165795
166087
  }
166088
+ if (nativeRuntimeMissing) {
166089
+ return false;
166090
+ }
165796
166091
  if (this.initPromise) {
165797
166092
  await this.initPromise;
165798
166093
  return this.pipeline !== null;
@@ -165859,7 +166154,12 @@ class LocalEmbeddingProvider {
165859
166154
  await releaseLock();
165860
166155
  }
165861
166156
  } catch (error51) {
165862
- log("[magic-context] embedding model failed to load:", error51);
166157
+ if (isNativeRuntimeMissingError(error51)) {
166158
+ nativeRuntimeMissing = true;
166159
+ log("[magic-context] local embedding runtime is not installed (onnxruntime-node missing from this install). Local embeddings are disabled. Fix: reinstall the plugin (run `npx @cortexkit/magic-context@latest doctor --force`), or configure an `openai-compatible`/`ollama` embedding endpoint instead. Existing memories are unaffected.");
166160
+ } else {
166161
+ log("[magic-context] embedding model failed to load:", error51);
166162
+ }
165863
166163
  this.pipeline = null;
165864
166164
  } finally {
165865
166165
  this.initPromise = null;
@@ -165885,7 +166185,7 @@ class LocalEmbeddingProvider {
165885
166185
  waiter();
165886
166186
  }
165887
166187
  }
165888
- async embed(text, signal) {
166188
+ async embed(text, signal, _purpose) {
165889
166189
  if (signal?.aborted)
165890
166190
  return null;
165891
166191
  if (this.disposing)
@@ -165911,7 +166211,7 @@ class LocalEmbeddingProvider {
165911
166211
  this.finishInFlight();
165912
166212
  }
165913
166213
  }
165914
- async embedBatch(texts, signal) {
166214
+ async embedBatch(texts, signal, _purpose) {
165915
166215
  if (texts.length === 0) {
165916
166216
  return [];
165917
166217
  }
@@ -165970,7 +166270,7 @@ class LocalEmbeddingProvider {
165970
166270
  return this.pipeline !== null;
165971
166271
  }
165972
166272
  }
165973
- var LOCK_POLL_MS = 150, STALE_LOCK_MS, MAX_LOCK_WAIT_MS;
166273
+ var LOCK_POLL_MS = 150, STALE_LOCK_MS, MAX_LOCK_WAIT_MS, nativeRuntimeMissing = false;
165974
166274
  var init_embedding_local = __esm(() => {
165975
166275
  init_magic_context();
165976
166276
  init_data_path();
@@ -166055,6 +166355,7 @@ class OpenAICompatibleEmbeddingProvider {
166055
166355
  model;
166056
166356
  apiKey;
166057
166357
  inputType;
166358
+ queryInputType;
166058
166359
  truncate;
166059
166360
  initialized = false;
166060
166361
  failureTimes = [];
@@ -166067,6 +166368,7 @@ class OpenAICompatibleEmbeddingProvider {
166067
166368
  this.model = options.model?.trim() ?? "";
166068
166369
  this.apiKey = options.apiKey?.trim() ?? "";
166069
166370
  this.inputType = options.inputType?.trim() ?? "";
166371
+ this.queryInputType = options.queryInputType?.trim() ?? "";
166070
166372
  this.truncate = options.truncate?.trim() ?? "";
166071
166373
  this.maxInputTokens = typeof options.maxInputTokens === "number" && Number.isFinite(options.maxInputTokens) ? Math.max(1, Math.floor(options.maxInputTokens)) : 512;
166072
166374
  this.modelId = getEmbeddingProviderIdentity({
@@ -166094,11 +166396,17 @@ class OpenAICompatibleEmbeddingProvider {
166094
166396
  this.initialized = true;
166095
166397
  return true;
166096
166398
  }
166097
- async embed(text, signal) {
166098
- const [embedding] = await this.embedBatch([text], signal);
166399
+ resolveInputTypeForPurpose(purpose = "passage") {
166400
+ if (purpose === "query") {
166401
+ return this.queryInputType || this.inputType;
166402
+ }
166403
+ return this.inputType;
166404
+ }
166405
+ async embed(text, signal, purpose) {
166406
+ const [embedding] = await this.embedBatch([text], signal, purpose);
166099
166407
  return embedding ?? null;
166100
166408
  }
166101
- async embedBatch(texts, signal) {
166409
+ async embedBatch(texts, signal, purpose) {
166102
166410
  if (texts.length === 0) {
166103
166411
  return [];
166104
166412
  }
@@ -166124,6 +166432,7 @@ class OpenAICompatibleEmbeddingProvider {
166124
166432
  if (signal) {
166125
166433
  signal.addEventListener("abort", onOuterAbort, { once: true });
166126
166434
  }
166435
+ const inputTypeForRequest = this.resolveInputTypeForPurpose(purpose);
166127
166436
  const response = await fetch(`${this.endpoint}/embeddings`, {
166128
166437
  method: "POST",
166129
166438
  headers: {
@@ -166133,7 +166442,7 @@ class OpenAICompatibleEmbeddingProvider {
166133
166442
  body: JSON.stringify({
166134
166443
  model: this.model,
166135
166444
  input: texts,
166136
- ...this.inputType ? { input_type: this.inputType } : {},
166445
+ ...inputTypeForRequest ? { input_type: inputTypeForRequest } : {},
166137
166446
  ...this.truncate ? { truncate: this.truncate } : {}
166138
166447
  }),
166139
166448
  redirect: "error",
@@ -166384,6 +166693,121 @@ var init_storage_git_commit_embeddings = __esm(() => {
166384
166693
  distinctModelIdStatements = new WeakMap;
166385
166694
  });
166386
166695
 
166696
+ // src/features/magic-context/git-commits/storage-git-commits.ts
166697
+ function getInsertStatement(db) {
166698
+ let stmt = insertStatements.get(db);
166699
+ if (!stmt) {
166700
+ stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
166701
+ VALUES (?, ?, ?, ?, ?, ?, ?)
166702
+ ON CONFLICT(sha) DO UPDATE SET
166703
+ project_path = excluded.project_path,
166704
+ short_sha = excluded.short_sha,
166705
+ message = excluded.message,
166706
+ author = excluded.author,
166707
+ committed_at = excluded.committed_at,
166708
+ indexed_at = excluded.indexed_at
166709
+ WHERE git_commits.message != excluded.message`);
166710
+ insertStatements.set(db, stmt);
166711
+ }
166712
+ return stmt;
166713
+ }
166714
+ function getExistingShasStatement(db) {
166715
+ let stmt = existingShasStatements.get(db);
166716
+ if (!stmt) {
166717
+ stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
166718
+ existingShasStatements.set(db, stmt);
166719
+ }
166720
+ return stmt;
166721
+ }
166722
+ function getProjectCountStatement(db) {
166723
+ let stmt = projectCountStatements.get(db);
166724
+ if (!stmt) {
166725
+ stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
166726
+ projectCountStatements.set(db, stmt);
166727
+ }
166728
+ return stmt;
166729
+ }
166730
+ function getLatestCommitTimeStatement(db) {
166731
+ let stmt = latestCommitTimeStatements.get(db);
166732
+ if (!stmt) {
166733
+ stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
166734
+ latestCommitTimeStatements.set(db, stmt);
166735
+ }
166736
+ return stmt;
166737
+ }
166738
+ function getEvictOverflowStatement(db) {
166739
+ let stmt = evictOverflowStatements.get(db);
166740
+ if (!stmt) {
166741
+ stmt = db.prepare(`DELETE FROM git_commits
166742
+ WHERE rowid IN (
166743
+ SELECT rowid FROM git_commits
166744
+ WHERE project_path = ?
166745
+ ORDER BY committed_at DESC, sha DESC
166746
+ LIMIT -1 OFFSET ?
166747
+ )`);
166748
+ evictOverflowStatements.set(db, stmt);
166749
+ }
166750
+ return stmt;
166751
+ }
166752
+ function upsertCommits(db, projectPath, commits) {
166753
+ if (commits.length === 0)
166754
+ return { inserted: 0, updated: 0 };
166755
+ const existing = new Set;
166756
+ for (const row of getExistingShasStatement(db).all(projectPath)) {
166757
+ existing.add(row.sha);
166758
+ }
166759
+ let inserted = 0;
166760
+ let updated = 0;
166761
+ const now = Date.now();
166762
+ const insertStmt = getInsertStatement(db);
166763
+ db.transaction(() => {
166764
+ for (const commit of commits) {
166765
+ const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
166766
+ if (result.changes > 0) {
166767
+ if (existing.has(commit.sha)) {
166768
+ updated++;
166769
+ } else {
166770
+ inserted++;
166771
+ existing.add(commit.sha);
166772
+ }
166773
+ }
166774
+ }
166775
+ })();
166776
+ return { inserted, updated };
166777
+ }
166778
+ function getCommitCount(db, projectPath) {
166779
+ const row = getProjectCountStatement(db).get(projectPath);
166780
+ return row?.count ?? 0;
166781
+ }
166782
+ function getLatestIndexedCommitTimeMs(db, projectPath) {
166783
+ const row = getLatestCommitTimeStatement(db).get(projectPath);
166784
+ return row?.latest ?? null;
166785
+ }
166786
+ function enforceProjectCap(db, projectPath, maxCommits) {
166787
+ if (maxCommits <= 0)
166788
+ return 0;
166789
+ const count = getCommitCount(db, projectPath);
166790
+ if (count <= maxCommits)
166791
+ return 0;
166792
+ getEvictOverflowStatement(db).run(projectPath, maxCommits);
166793
+ const after = getCommitCount(db, projectPath);
166794
+ const evicted = Math.max(0, count - after);
166795
+ if (evicted > 0) {
166796
+ log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
166797
+ }
166798
+ return evicted;
166799
+ }
166800
+ var insertStatements, existingShasStatements, projectCountStatements, evictStatements, evictOverflowStatements, latestCommitTimeStatements;
166801
+ var init_storage_git_commits = __esm(() => {
166802
+ init_logger();
166803
+ insertStatements = new WeakMap;
166804
+ existingShasStatements = new WeakMap;
166805
+ projectCountStatements = new WeakMap;
166806
+ evictStatements = new WeakMap;
166807
+ evictOverflowStatements = new WeakMap;
166808
+ latestCommitTimeStatements = new WeakMap;
166809
+ });
166810
+
166387
166811
  // src/features/magic-context/git-commits/sweep-coordinator.ts
166388
166812
  function runImmediate2(db, body) {
166389
166813
  db.exec("BEGIN IMMEDIATE");
@@ -166595,6 +167019,7 @@ function resolveEmbeddingConfig(config2) {
166595
167019
  if (config2.provider === "openai-compatible") {
166596
167020
  const apiKey = config2.api_key?.trim();
166597
167021
  const inputType = config2.input_type?.trim();
167022
+ const queryInputType = config2.query_input_type?.trim();
166598
167023
  const truncate = config2.truncate?.trim();
166599
167024
  return {
166600
167025
  provider: "openai-compatible",
@@ -166602,6 +167027,7 @@ function resolveEmbeddingConfig(config2) {
166602
167027
  endpoint: config2.endpoint.trim(),
166603
167028
  ...apiKey ? { api_key: apiKey } : {},
166604
167029
  ...inputType ? { input_type: inputType } : {},
167030
+ ...queryInputType ? { query_input_type: queryInputType } : {},
166605
167031
  ...truncate ? { truncate } : {},
166606
167032
  ...config2.max_input_tokens ? {
166607
167033
  max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
@@ -166623,6 +167049,7 @@ function createProvider(config2) {
166623
167049
  model: config2.model,
166624
167050
  apiKey: config2.api_key,
166625
167051
  inputType: config2.input_type,
167052
+ queryInputType: config2.query_input_type,
166626
167053
  truncate: config2.truncate,
166627
167054
  maxInputTokens: config2.max_input_tokens
166628
167055
  });
@@ -166677,7 +167104,9 @@ function snapshotFor(registration) {
166677
167104
  enabled,
166678
167105
  gitCommitEnabled,
166679
167106
  modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
166680
- chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId
167107
+ chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId,
167108
+ model: registration.observationMode || !providerIsOn ? "off" : ("model" in registration.config) && registration.config.model.trim() ? registration.config.model.trim() : registration.modelId,
167109
+ provider: registration.observationMode || !providerIsOn ? "off" : registration.config.provider ?? "local"
166681
167110
  };
166682
167111
  }
166683
167112
  function disposeProvider(provider) {
@@ -166803,7 +167232,7 @@ function getOrCreateProjectProvider(registration) {
166803
167232
  registration.provider = provider;
166804
167233
  return provider;
166805
167234
  }
166806
- async function embedTextForProject(projectIdentity, text, signal) {
167235
+ async function embedTextForProject(projectIdentity, text, signal, purpose = "passage") {
166807
167236
  const registration = projectRegistrations.get(projectIdentity);
166808
167237
  if (!registration)
166809
167238
  return null;
@@ -166812,7 +167241,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
166812
167241
  const provider = getOrCreateProjectProvider(registration);
166813
167242
  if (!provider)
166814
167243
  return null;
166815
- const vector = await provider.embed(text, signal);
167244
+ const vector = await provider.embed(text, signal, purpose);
166816
167245
  if (!vector)
166817
167246
  return null;
166818
167247
  const current = projectRegistrations.get(projectIdentity);
@@ -166821,7 +167250,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
166821
167250
  }
166822
167251
  return { vector, modelId, generation };
166823
167252
  }
166824
- async function embedBatchForProject(projectIdentity, texts, signal) {
167253
+ async function embedBatchForProject(projectIdentity, texts, signal, purpose = "passage") {
166825
167254
  if (texts.length === 0) {
166826
167255
  const registration2 = projectRegistrations.get(projectIdentity);
166827
167256
  if (!registration2 || registration2.observationMode)
@@ -166837,7 +167266,7 @@ async function embedBatchForProject(projectIdentity, texts, signal) {
166837
167266
  const provider = getOrCreateProjectProvider(registration);
166838
167267
  if (!provider)
166839
167268
  return null;
166840
- const vectors = await provider.embedBatch(texts, signal);
167269
+ const vectors = await provider.embedBatch(texts, signal, purpose);
166841
167270
  const current = projectRegistrations.get(projectIdentity);
166842
167271
  if (!current || current.generation !== generation || current.runtimeFingerprint !== runtimeFingerprint) {
166843
167272
  return null;
@@ -166888,12 +167317,13 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
166888
167317
  }
166889
167318
  async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates, signal) {
166890
167319
  const noWork = [];
167320
+ const failed = [];
166891
167321
  if (candidates.length === 0)
166892
- return { embedded: 0, noWork };
167322
+ return { embedded: 0, noWork, failed };
166893
167323
  const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
166894
167324
  const prepared = [];
166895
167325
  for (const candidate of candidates) {
166896
- const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage);
167326
+ const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage) || buildCompartmentSummaryFallbackText(db, candidate.id);
166897
167327
  if (canonicalText.length === 0) {
166898
167328
  noWork.push(candidate.id);
166899
167329
  continue;
@@ -166906,7 +167336,7 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
166906
167336
  prepared.push({ candidate, windows });
166907
167337
  }
166908
167338
  if (prepared.length === 0)
166909
- return { embedded: 0, noWork };
167339
+ return { embedded: 0, noWork, failed };
166910
167340
  let embedded = 0;
166911
167341
  let i = 0;
166912
167342
  while (i < prepared.length) {
@@ -166923,35 +167353,60 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
166923
167353
  const texts = [];
166924
167354
  for (const item of slice)
166925
167355
  texts.push(...item.windows.map((w) => w.text));
166926
- try {
166927
- const result = await embedBatchForProject(projectIdentity, texts, signal);
166928
- if (!result)
166929
- continue;
167356
+ const persistedIds = new Set;
167357
+ for (let attempt = 0;attempt < EMBED_SLICE_RETRY_ATTEMPTS; attempt++) {
166930
167358
  if (signal?.aborted)
166931
167359
  break;
166932
- let offset = 0;
166933
- for (const item of slice) {
166934
- const vectors = result.vectors.slice(offset, offset + item.windows.length);
166935
- offset += item.windows.length;
166936
- if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
166937
- continue;
167360
+ let result = null;
167361
+ const attemptStart = Date.now();
167362
+ try {
167363
+ result = await embedBatchForProject(projectIdentity, texts, signal);
167364
+ } catch (error51) {
167365
+ log("[magic-context] failed to proactively embed compartment chunks:", error51);
167366
+ }
167367
+ if (signal?.aborted)
167368
+ break;
167369
+ if (result) {
167370
+ let offset = 0;
167371
+ for (const item of slice) {
167372
+ const vectors = result.vectors.slice(offset, offset + item.windows.length);
167373
+ offset += item.windows.length;
167374
+ if (persistedIds.has(item.candidate.id))
167375
+ continue;
167376
+ if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
167377
+ continue;
167378
+ }
167379
+ const rows = item.windows.map((window, index) => ({
167380
+ compartmentId: item.candidate.id,
167381
+ sessionId: item.candidate.sessionId,
167382
+ projectPath: projectIdentity,
167383
+ window,
167384
+ modelId,
167385
+ vector: vectors[index]
167386
+ }));
167387
+ replaceCompartmentChunkEmbeddings(db, rows);
167388
+ persistedIds.add(item.candidate.id);
166938
167389
  }
166939
- const rows = item.windows.map((window, index) => ({
166940
- compartmentId: item.candidate.id,
166941
- sessionId: item.candidate.sessionId,
166942
- projectPath: projectIdentity,
166943
- window,
166944
- modelId,
166945
- vector: vectors[index]
166946
- }));
166947
- replaceCompartmentChunkEmbeddings(db, rows);
166948
- embedded += 1;
166949
167390
  }
166950
- } catch (error51) {
166951
- log("[magic-context] failed to proactively embed compartment chunks:", error51);
167391
+ if (persistedIds.size === slice.length)
167392
+ break;
167393
+ if (persistedIds.size > 0)
167394
+ break;
167395
+ if (Date.now() - attemptStart >= EMBED_SLOW_FAILURE_NO_RETRY_MS)
167396
+ break;
167397
+ if (attempt < EMBED_SLICE_RETRY_ATTEMPTS - 1) {
167398
+ await new Promise((resolve7) => setTimeout(resolve7, EMBED_SLICE_RETRY_BASE_MS * 2 ** attempt));
167399
+ }
167400
+ }
167401
+ embedded += persistedIds.size;
167402
+ if (!signal?.aborted) {
167403
+ for (const item of slice) {
167404
+ if (!persistedIds.has(item.candidate.id))
167405
+ failed.push(item.candidate.id);
167406
+ }
166952
167407
  }
166953
167408
  }
166954
- return { embedded, noWork };
167409
+ return { embedded, noWork, failed };
166955
167410
  }
166956
167411
  async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, options) {
166957
167412
  const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
@@ -166974,9 +167429,11 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
166974
167429
  renewal.unref?.();
166975
167430
  const batchSize = Math.max(1, options?.batchSize ?? CHUNK_DRAIN_BATCH_SIZE);
166976
167431
  const skipIds = [];
167432
+ const failedIds = [];
166977
167433
  let embedded = 0;
166978
167434
  let aborted2 = false;
166979
- let providerStalled = false;
167435
+ let providerDown = false;
167436
+ let consecutiveFailedBatches = 0;
166980
167437
  try {
166981
167438
  options?.onProgress?.({ embedded, total });
166982
167439
  for (;; ) {
@@ -166984,15 +167441,26 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
166984
167441
  aborted2 = true;
166985
167442
  break;
166986
167443
  }
166987
- const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, skipIds);
167444
+ const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, [...skipIds, ...failedIds]);
166988
167445
  if (candidates.length === 0)
166989
167446
  break;
166990
- const { embedded: n, noWork } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
167447
+ const {
167448
+ embedded: n,
167449
+ noWork,
167450
+ failed
167451
+ } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
166991
167452
  for (const id of noWork)
166992
167453
  skipIds.push(id);
167454
+ for (const id of failed)
167455
+ failedIds.push(id);
166993
167456
  if (n === 0 && noWork.length === 0) {
166994
- providerStalled = true;
166995
- break;
167457
+ consecutiveFailedBatches += 1;
167458
+ if (consecutiveFailedBatches >= MAX_CONSECUTIVE_FAILED_BATCHES) {
167459
+ providerDown = true;
167460
+ break;
167461
+ }
167462
+ } else {
167463
+ consecutiveFailedBatches = 0;
166996
167464
  }
166997
167465
  embedded += n;
166998
167466
  options?.onProgress?.({ embedded: Math.min(embedded, total), total });
@@ -167000,23 +167468,58 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
167000
167468
  }
167001
167469
  } finally {
167002
167470
  clearInterval(renewal);
167003
- releaseGitSweepLease(db, projectIdentity, holderId);
167471
+ try {
167472
+ releaseGitSweepLease(db, projectIdentity, holderId);
167473
+ } catch (error51) {
167474
+ log("[magic-context] embed drain: lease release failed (will TTL-expire):", error51);
167475
+ }
167004
167476
  }
167005
167477
  if (aborted2)
167006
- return { status: "aborted", embedded, total };
167007
- if (providerStalled) {
167478
+ return { status: "aborted", embedded, total, failed: failedIds.length };
167479
+ if (providerDown || failedIds.length > 0) {
167008
167480
  const remaining = Math.max(0, countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId) - skipIds.length);
167009
- if (remaining > 0)
167010
- return { status: "stalled", embedded, total, remaining };
167481
+ if (remaining > 0) {
167482
+ return { status: "stalled", embedded, total, remaining, failed: failedIds.length };
167483
+ }
167011
167484
  }
167012
- return { status: "done", embedded, total };
167485
+ return { status: "done", embedded, total, failed: failedIds.length };
167013
167486
  }
167014
- var OFF_PROVIDER_IDENTITY = "embedding-provider:off", SWEEP_MAX_WALL_CLOCK_MS, CHUNK_DRAIN_BATCH_SIZE = 8, MAX_WINDOWS_PER_EMBED_CALL = 16, SESSION_EMBED_LEASE_RENEWAL_MS, projectRegistrations, loadUnembeddedMemoriesStatements, globalRegistrationGeneration = 0, testProviderFactory = null;
167487
+ function getEmbeddingCoverageStatus(db, projectIdentity, sessionId) {
167488
+ const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
167489
+ if (!snapshot?.enabled || snapshot.chunkModelId === "off") {
167490
+ return {
167491
+ enabled: false,
167492
+ model: snapshot?.model ?? "off",
167493
+ provider: snapshot?.provider ?? "off",
167494
+ session: { embedded: 0, total: 0 },
167495
+ memories: { embedded: 0, total: 0 },
167496
+ commits: { embedded: 0, total: 0, gitEnabled: false }
167497
+ };
167498
+ }
167499
+ const session = countSessionCompartmentEmbedCoverage(db, projectIdentity, sessionId, snapshot.chunkModelId);
167500
+ const memories = getMemoryEmbedCoverage(db, projectIdentity, snapshot.modelId);
167501
+ const gitEnabled = snapshot.gitCommitEnabled;
167502
+ const commits = gitEnabled ? {
167503
+ embedded: countEmbeddedCommits(db, projectIdentity),
167504
+ total: getCommitCount(db, projectIdentity),
167505
+ gitEnabled: true
167506
+ } : { embedded: 0, total: 0, gitEnabled: false };
167507
+ return {
167508
+ enabled: true,
167509
+ model: snapshot.model,
167510
+ provider: snapshot.provider,
167511
+ session,
167512
+ memories,
167513
+ commits
167514
+ };
167515
+ }
167516
+ var OFF_PROVIDER_IDENTITY = "embedding-provider:off", SWEEP_MAX_WALL_CLOCK_MS, CHUNK_DRAIN_BATCH_SIZE = 8, MAX_WINDOWS_PER_EMBED_CALL = 2, SESSION_EMBED_LEASE_RENEWAL_MS, EMBED_SLICE_RETRY_ATTEMPTS = 3, EMBED_SLICE_RETRY_BASE_MS = 250, EMBED_SLOW_FAILURE_NO_RETRY_MS = 1e4, MAX_CONSECUTIVE_FAILED_BATCHES = 3, projectRegistrations, loadUnembeddedMemoriesStatements, globalRegistrationGeneration = 0, testProviderFactory = null;
167015
167517
  var init_project_embedding_registry = __esm(() => {
167016
167518
  init_magic_context();
167017
167519
  init_logger();
167018
167520
  init_compartment_chunk_embedding();
167019
167521
  init_storage_git_commit_embeddings();
167522
+ init_storage_git_commits();
167020
167523
  init_sweep_coordinator();
167021
167524
  init_embedding_cache();
167022
167525
  init_embedding_identity();
@@ -167041,6 +167544,7 @@ function createProvider2(config2) {
167041
167544
  model: config2.model,
167042
167545
  apiKey: config2.api_key,
167043
167546
  inputType: config2.input_type,
167547
+ queryInputType: config2.query_input_type,
167044
167548
  truncate: config2.truncate,
167045
167549
  maxInputTokens: config2.max_input_tokens
167046
167550
  });
@@ -167297,58 +167801,6 @@ var init_models_dev_cache = __esm(() => {
167297
167801
  init_logger();
167298
167802
  });
167299
167803
 
167300
- // src/shared/rpc-notifications.ts
167301
- var exports_rpc_notifications = {};
167302
- __export(exports_rpc_notifications, {
167303
- pushNotification: () => pushNotification,
167304
- isTuiConnected: () => isTuiConnected,
167305
- drainNotifications: () => drainNotifications
167306
- });
167307
- function pushNotification(type, payload, sessionId) {
167308
- queue2.push({ id: nextNotificationId++, type, payload, sessionId });
167309
- if (queue2.length > 100) {
167310
- const newestPerSession = new Map;
167311
- for (const n of queue2) {
167312
- const prev = newestPerSession.get(n.sessionId);
167313
- if (prev === undefined || n.id > prev) {
167314
- newestPerSession.set(n.sessionId, n.id);
167315
- }
167316
- }
167317
- const mustKeep = new Set(newestPerSession.values());
167318
- const byNewest = [...queue2].sort((a, b) => b.id - a.id);
167319
- const kept = [];
167320
- for (const n of byNewest) {
167321
- if (kept.length < 50 || mustKeep.has(n.id))
167322
- kept.push(n);
167323
- }
167324
- queue2 = kept.sort((a, b) => a.id - b.id);
167325
- }
167326
- }
167327
- function drainNotifications(lastReceivedId = 0, sessionId) {
167328
- const now = Date.now();
167329
- lastDrainAtAny = now;
167330
- if (sessionId !== undefined)
167331
- lastDrainAtBySession.set(sessionId, now);
167332
- const matchesClient = (notification) => sessionId === undefined || notification.sessionId === undefined || notification.sessionId === sessionId;
167333
- if (lastReceivedId > 0) {
167334
- queue2 = queue2.filter((notification) => !(notification.id <= lastReceivedId && matchesClient(notification)));
167335
- }
167336
- return queue2.filter((notification) => notification.id > lastReceivedId && matchesClient(notification));
167337
- }
167338
- function isTuiConnected(sessionId) {
167339
- const now = Date.now();
167340
- if (sessionId !== undefined) {
167341
- const at = lastDrainAtBySession.get(sessionId) ?? 0;
167342
- return at > 0 && now - at < TUI_CONNECTED_WINDOW_MS;
167343
- }
167344
- return lastDrainAtAny > 0 && now - lastDrainAtAny < TUI_CONNECTED_WINDOW_MS;
167345
- }
167346
- var queue2, nextNotificationId = 1, lastDrainAtBySession, lastDrainAtAny = 0, TUI_CONNECTED_WINDOW_MS = 3000;
167347
- var init_rpc_notifications = __esm(() => {
167348
- queue2 = [];
167349
- lastDrainAtBySession = new Map;
167350
- });
167351
-
167352
167804
  // src/features/magic-context/compartment-embedding.ts
167353
167805
  async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compartments) {
167354
167806
  if (compartments.length === 0)
@@ -167357,7 +167809,7 @@ async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compar
167357
167809
  for (const compartment of compartments) {
167358
167810
  try {
167359
167811
  const fromMemory = compartment.sourceChunkText ? canonicalizeInMemoryChunkTextForEmbedding(compartment.sourceChunkText, compartment.startMessage, compartment.endMessage) : "";
167360
- const canonicalText = fromMemory || buildCanonicalChunkTextFromFts(db, sessionId, compartment.startMessage, compartment.endMessage);
167812
+ const canonicalText = fromMemory || buildCanonicalChunkTextFromFts(db, sessionId, compartment.startMessage, compartment.endMessage) || buildCompartmentSummaryFallbackText(db, compartment.id);
167361
167813
  if (canonicalText.length === 0)
167362
167814
  continue;
167363
167815
  const windows = chunkCanonicalText(canonicalText, compartment.startMessage, compartment.endMessage, maxInputTokens);
@@ -170557,29 +171009,12 @@ function resolveHistorianContextLimit(historianModelOverride) {
170557
171009
  return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
170558
171010
  }
170559
171011
  if (typeof historianModelOverride === "string" && historianModelOverride.trim() !== "") {
170560
- console.warn(`[magic-context] historian.model "${historianModelOverride}" lacks provider prefix ("provider/model-id"); using fallback chain for chunk-budget derivation.`);
170561
- }
170562
- const chain = AGENT_MODEL_REQUIREMENTS[HISTORIAN_AGENT]?.fallbackChain;
170563
- if (!chain || chain.length === 0)
170564
- return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
170565
- const expanded = expandFallbackChain(chain);
170566
- let minLimit;
170567
- for (const key of expanded) {
170568
- const [providerID, ...rest] = key.split("/");
170569
- const modelID = rest.join("/");
170570
- if (!providerID || !modelID)
170571
- continue;
170572
- const limit = getSdkContextLimit(providerID, modelID);
170573
- if (typeof limit !== "number" || limit <= 0)
170574
- continue;
170575
- if (minLimit === undefined || limit < minLimit)
170576
- minLimit = limit;
171012
+ console.warn(`[magic-context] historian.model "${historianModelOverride}" lacks provider prefix ("provider/model-id"); using the default context limit for chunk-budget derivation.`);
170577
171013
  }
170578
- return minLimit ?? DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
171014
+ return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
170579
171015
  }
170580
171016
  var TRIGGER_BUDGET_PERCENTAGE = 0.05, TRIGGER_BUDGET_MIN = 5000, TRIGGER_BUDGET_MAX = 50000, HISTORIAN_CHUNK_PERCENTAGE = 0.25, HISTORIAN_CHUNK_MIN = 8000, HISTORIAN_CHUNK_MAX = 50000, DEFAULT_HISTORIAN_CONTEXT_FALLBACK = 128000;
170581
171017
  var init_derive_budgets = __esm(() => {
170582
- init_model_requirements();
170583
171018
  init_models_dev_cache();
170584
171019
  });
170585
171020
 
@@ -170900,7 +171335,7 @@ function buildToolArcs(messages) {
170900
171335
  }
170901
171336
  return arcs.sort((a, b) => a.invOrdinal - b.invOrdinal || (a.resOrdinal ?? Number.MAX_SAFE_INTEGER) - (b.resOrdinal ?? Number.MAX_SAFE_INTEGER));
170902
171337
  }
170903
- function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
171338
+ function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal, recentOpenArcCutoff) {
170904
171339
  let boundary = candidate;
170905
171340
  for (const arc of arcs) {
170906
171341
  if (arc.resOrdinal !== null) {
@@ -170909,6 +171344,8 @@ function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
170909
171344
  }
170910
171345
  continue;
170911
171346
  }
171347
+ if (arc.invOrdinal < recentOpenArcCutoff)
171348
+ continue;
170912
171349
  if (arc.invOrdinal >= lastCompartmentEndOrdinal + 1 && arc.invOrdinal < boundary) {
170913
171350
  return arc.invOrdinal;
170914
171351
  }
@@ -171148,7 +171585,7 @@ function semanticSnapBoundary(args) {
171148
171585
  return snapped;
171149
171586
  }
171150
171587
  function applyHeadCap(args) {
171151
- const { index, protectedTailStart, offset, arcs, capTokens } = args;
171588
+ const { index, protectedTailStart, offset, arcs, capTokens, recentOpenArcCutoff } = args;
171152
171589
  if (offset >= protectedTailStart)
171153
171590
  return { eligibleEndOrdinal: offset, oversizeAtomicUnit: false };
171154
171591
  let end = index.findHeadEndForCap(offset, protectedTailStart, capTokens);
@@ -171156,7 +171593,7 @@ function applyHeadCap(args) {
171156
171593
  for (const arc of arcs) {
171157
171594
  const resOrdinal = arc.resOrdinal;
171158
171595
  if (resOrdinal === null) {
171159
- if (arc.invOrdinal >= offset && arc.invOrdinal < end) {
171596
+ if (arc.invOrdinal >= recentOpenArcCutoff && arc.invOrdinal >= offset && arc.invOrdinal < end) {
171160
171597
  end = Math.min(end, arc.invOrdinal);
171161
171598
  }
171162
171599
  continue;
@@ -171223,7 +171660,14 @@ function resolveProtectedTailBoundary(ctx) {
171223
171660
  }
171224
171661
  if (ctx.mode === "manual-full-recomp") {
171225
171662
  const arcs2 = buildToolArcs(messages);
171226
- const firstOpenArc = arcs2.find((arc) => arc.resOrdinal === null && arc.invOrdinal >= offset);
171663
+ const recompTarget = deriveProtectedTailTokenTarget({
171664
+ contextLimit: ctx.contextLimit,
171665
+ executeThresholdPercentage: ctx.executeThresholdPercentage,
171666
+ usagePercentage: 0,
171667
+ triggerBudget: ctx.triggerBudget
171668
+ });
171669
+ const recentOpenArcCutoff2 = index.findSuffixStartForTokens(recompTarget.N);
171670
+ const firstOpenArc = arcs2.find((arc) => arc.resOrdinal === null && arc.invOrdinal >= offset && arc.invOrdinal >= recentOpenArcCutoff2);
171227
171671
  const protectedTailStart2 = firstOpenArc?.invOrdinal ?? rawMessageCount + 1;
171228
171672
  const rawRangeFingerprint2 = computeRawRangeFingerprint(messages, offset, protectedTailStart2);
171229
171673
  return {
@@ -171265,13 +171709,14 @@ function resolveProtectedTailBoundary(ctx) {
171265
171709
  const scaledN = ctx.emergencyTailScale ? Math.max(1, Math.floor(target.N * ctx.emergencyTailScale)) : target.N;
171266
171710
  const arcs = buildToolArcs(messages);
171267
171711
  let boundary = index.findSuffixStartForTokens(scaledN);
171712
+ const recentOpenArcCutoff = boundary;
171268
171713
  let boundaryReason = boundary === 1 ? "whole-session-smaller-than-tail" : "size-walk";
171269
171714
  const tokenAtBoundary = index.tokenForOrdinal(boundary);
171270
171715
  if (boundary <= rawMessageCount && tokenAtBoundary > Math.max(2 * scaledN, 64000) && boundary < rawMessageCount) {
171271
171716
  boundary += 1;
171272
171717
  boundaryReason = "huge-message-exception";
171273
171718
  }
171274
- boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal);
171719
+ boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
171275
171720
  const snapped = semanticSnapBoundary({
171276
171721
  messages,
171277
171722
  index,
@@ -171281,7 +171726,7 @@ function resolveProtectedTailBoundary(ctx) {
171281
171726
  });
171282
171727
  if (snapped !== boundary)
171283
171728
  boundaryReason = "semantic-snap";
171284
- boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal);
171729
+ boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
171285
171730
  let runtimeFloor = offset;
171286
171731
  if (ctx.migrationFloorActive)
171287
171732
  runtimeFloor = Math.max(runtimeFloor, ctx.priorBoundaryOrdinal);
@@ -171317,7 +171762,8 @@ function resolveProtectedTailBoundary(ctx) {
171317
171762
  offset,
171318
171763
  arcs,
171319
171764
  lastCompartmentEndOrdinal: ctx.lastCompartmentEndOrdinal,
171320
- capTokens: perRunCap
171765
+ capTokens: perRunCap,
171766
+ recentOpenArcCutoff
171321
171767
  });
171322
171768
  const rawRangeFingerprint = computeRawRangeFingerprint(messages, offset, head.eligibleEndOrdinal);
171323
171769
  return {
@@ -171368,7 +171814,7 @@ function resolveBoundaryContext(args) {
171368
171814
  }
171369
171815
  let storedTokenTotals;
171370
171816
  try {
171371
- storedTokenTotals = getAllStatusTagTokenTotalsFlat(args.db, args.sessionId).totals;
171817
+ storedTokenTotals = getAllStatusTagTokenTotalsFlat(args.db, args.sessionId, args.taggerFloor ?? 0).totals;
171372
171818
  } catch (error51) {
171373
171819
  sessionLog(args.sessionId, "protected-tail stored-token map unavailable (live fallback):", error51);
171374
171820
  }
@@ -173572,6 +174018,7 @@ async function runCompartmentAgent(deps) {
173572
174018
  const count = recordHighPressureNoEligibleHead(db, boundarySnapshot);
173573
174019
  sessionLog(sessionId, `historian high-pressure no-op: recovery remains armed (noEligibleHeadCount=${count})`);
173574
174020
  }
174021
+ clearEmergencyDrainLatch(db, sessionId);
173575
174022
  telemetry.status = "noop";
173576
174023
  telemetry.failureReason = "nothing to compact before protected tail";
173577
174024
  rollbackDrainReservation();
@@ -173586,7 +174033,8 @@ async function runCompartmentAgent(deps) {
173586
174033
  trueRawTokens: boundarySnapshot.trueRawEligibleTokens,
173587
174034
  usagePercentage: boundarySnapshot.usagePercentage,
173588
174035
  usable,
173589
- perRunCap
174036
+ perRunCap,
174037
+ executeThresholdPercentage: boundarySnapshot.executeThresholdPercentage
173590
174038
  });
173591
174039
  if (!reserve.ok) {
173592
174040
  sessionLog(sessionId, `historian rate-limit skip: ${reserve.skippedReason ?? "quota exhausted"}`);
@@ -173605,6 +174053,7 @@ async function runCompartmentAgent(deps) {
173605
174053
  } else {
173606
174054
  recordHighPressureNoEligibleHead(db, boundarySnapshot);
173607
174055
  }
174056
+ clearEmergencyDrainLatch(db, sessionId);
173608
174057
  telemetry.status = "noop";
173609
174058
  telemetry.failureReason = "chunk empty after filtering";
173610
174059
  rollbackDrainReservation();
@@ -173712,6 +174161,7 @@ ${chunkText}`,
173712
174161
  }
173713
174162
  appendCompartments(db, sessionId, persistedCompartments);
173714
174163
  clearHistorianFailureState(db, sessionId);
174164
+ clearHistorianDrainFailure(db, sessionId);
173715
174165
  recordProtectedTailPublicationFloor(db, sessionId, lastCompartmentEnd + 1);
173716
174166
  clearEmergencyRecovery(db, sessionId);
173717
174167
  drainReservation = null;
@@ -173815,8 +174265,11 @@ ${chunkText}`,
173815
174265
  }
173816
174266
  } finally {
173817
174267
  if (!completedSuccessfully) {
173818
- if (!retainDrainReservationForRetryThrottle)
174268
+ if (!retainDrainReservationForRetryThrottle) {
173819
174269
  rollbackDrainReservation();
174270
+ } else {
174271
+ recordHistorianDrainFailure(db, sessionId);
174272
+ }
173820
174273
  updateSessionMeta(db, sessionId, { compartmentInProgress: false });
173821
174274
  }
173822
174275
  recordTelemetry();
@@ -176958,7 +177411,6 @@ var init_memory_migration = __esm(async () => {
176958
177411
  init_shared();
176959
177412
  init_assistant_message_extractor();
176960
177413
  init_logger();
176961
- init_resolve_fallbacks();
176962
177414
  init_project_identity();
176963
177415
  init_storage_memory();
176964
177416
  await init_storage();
@@ -177274,15 +177726,15 @@ function shouldShowAnnouncement() {
177274
177726
  }
177275
177727
  return state.version !== ANNOUNCEMENT_VERSION;
177276
177728
  }
177277
- var ANNOUNCEMENT_VERSION = "0.24.0", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
177729
+ var ANNOUNCEMENT_VERSION = "0.26.0", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
177278
177730
  var init_announcement = __esm(() => {
177279
177731
  init_data_path();
177280
177732
  ANNOUNCEMENT_FEATURES = [
177281
- "Searchable session history: ctx_search can now find older discussion by meaning, not just keywords. New history is embedded automatically to backfill an EXISTING session's older history, run /ctx-embed-history once (it works in the background).",
177282
- "Cross-project workspaces: group related repos and share project memories across them, with per-category control over what's shared. Set them up in the dashboard's Workspaces panel.",
177283
- "Pi: fixed sessions overflowing the model context while still showing moderate usage Pi now sheds context before a tool-heavy turn overflows.",
177284
- "Fewer prompt-cache busts: doc edits, processed screenshots, and a rebuild-then-bust-again case no longer re-bill large prompt prefixes.",
177285
- "Setup wizard now lists your actual models with type-ahead instead of fixed recommendations, and explains the historian/dreamer roles (issue #144). Plus a GitHub Copilot tool-pairing fix (#135)."
177733
+ "Faster on large sessions: per-message transform overhead is at least 2x lower on typical passes and up to ~10x lower when history summarization fires (no more multi-second pause on big sessions).",
177734
+ "No more surprise models: the built-in fallback chain is gone. Hidden agents only use the model (and fallback_models) you configure — no confusing 'model not found' for providers you never set up. `doctor` now records every historian run so real failures are visible.",
177735
+ "Anthropic thinking-block fix: clearing old reasoning no longer risks a stale-signature rejection on Claude / Bedrock / proxied-Claude routes. Plus fewer prompt-cache busts.",
177736
+ "Community fixes: TUI crash on the upgrade progress panel (#168), historian.disallowed_tools for weak models that loop on tool calls (#166), and a Pi-only config key leak (#167).",
177737
+ "New: doctor migrate-session re-homes a session (and optionally its memories) to another project, with a dry-run preview."
177286
177738
  ];
177287
177739
  });
177288
177740
 
@@ -177355,6 +177807,9 @@ var init_tui_config = __esm(() => {
177355
177807
  import_comment_json4 = __toESM(require_src2(), 1);
177356
177808
  PLUGIN_ENTRY = `${PLUGIN_NAME}@latest`;
177357
177809
  });
177810
+
177811
+ // src/agents/dreamer.ts
177812
+ var DREAMER_AGENT = "dreamer";
177358
177813
  // src/agents/permissions.ts
177359
177814
  function buildAllowOnlyPermission(allowedTools) {
177360
177815
  const permission = { "*": "deny" };
@@ -177364,6 +177819,11 @@ function buildAllowOnlyPermission(allowedTools) {
177364
177819
  return permission;
177365
177820
  }
177366
177821
  var HISTORIAN_ALLOWED_TOOLS = ["read", "aft_outline", "aft_zoom", "aft_search"];
177822
+ function applyDisallowedTools(defaults, disallowed) {
177823
+ if (disallowed.includes("*"))
177824
+ return [];
177825
+ return defaults.filter((t) => !disallowed.includes(t));
177826
+ }
177367
177827
  var DREAMER_ALLOWED_TOOLS = [
177368
177828
  "read",
177369
177829
  "grep",
@@ -177379,6 +177839,10 @@ var DREAMER_ALLOWED_TOOLS = [
177379
177839
  "ctx_note"
177380
177840
  ];
177381
177841
  var SIDEKICK_ALLOWED_TOOLS = ["ctx_search", "aft_outline", "aft_zoom"];
177842
+
177843
+ // src/agents/sidekick.ts
177844
+ var SIDEKICK_AGENT = "sidekick";
177845
+
177382
177846
  // src/config/index.ts
177383
177847
  init_jsonc_parser();
177384
177848
  import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
@@ -178077,9 +178541,9 @@ function getMagicContextBuiltinCommands() {
178077
178541
  template: "ctx-dream",
178078
178542
  description: "Run the hidden dreamer maintenance pass for this project now"
178079
178543
  },
178080
- "ctx-embed-history": {
178081
- template: "ctx-embed-history",
178082
- description: "Embed all of this session's history compartments for semantic search, in one pass"
178544
+ "ctx-embed": {
178545
+ template: "ctx-embed",
178546
+ description: "Embedding status, or start/pause history compartment embedding (start | pause)"
178083
178547
  }
178084
178548
  };
178085
178549
  }
@@ -178328,6 +178792,7 @@ ${modeIntro}
178328
178792
  4. **Write or update** using the Write tool. Always write to project root, NOT to .planning/.
178329
178793
 
178330
178794
  ### Rules
178795
+ - **NEVER touch protected regions**: any content between \`<!-- mc:protected START ... -->\` and \`<!-- mc:protected END -->\` is hand-authored and cache-critical. Reproduce it BYTE-FOR-BYTE in your rewrite — do not edit, reword, reorder, summarize, trim, or drop a single line of it, and keep the marker comments themselves. Only a human edits that region.
178331
178796
  - **Be prescriptive**: "Use X pattern" not "X pattern is used"
178332
178797
  - **Always include file paths** in backticks
178333
178798
  - **Write current state only**: no temporal language, no history
@@ -178464,7 +178929,6 @@ init_project_identity();
178464
178929
  init_shared();
178465
178930
  init_assistant_message_extractor();
178466
178931
  init_logger();
178467
- init_resolve_fallbacks();
178468
178932
  init_subagent_token_capture();
178469
178933
  await init_storage();
178470
178934
 
@@ -178486,7 +178950,7 @@ function stripThinkingBlocks(text) {
178486
178950
 
178487
178951
  // src/features/magic-context/sidekick/agent.ts
178488
178952
  async function runSidekick(deps) {
178489
- const fallbackModels = resolveFallbackChain(SIDEKICK_AGENT, deps.config.fallback_models);
178953
+ const fallbackModels = resolveFallbackChain(deps.config.fallback_models);
178490
178954
  let agentSessionId = null;
178491
178955
  const startedAt = Date.now();
178492
178956
  let invocationRecorded = false;
@@ -178576,6 +179040,11 @@ init_project_identity();
178576
179040
  import { createHash as createHash6 } from "node:crypto";
178577
179041
  import { realpathSync as realpathSync2 } from "node:fs";
178578
179042
  import path5 from "node:path";
179043
+
179044
+ // src/features/magic-context/memory/relocate-memory.ts
179045
+ var memoryCopyColumnsCache = new WeakMap;
179046
+
179047
+ // src/features/magic-context/v22-deferred-backfill.ts
178579
179048
  var BATCH_SIZE = 25;
178580
179049
  var YIELD_EVERY_N_ROWS = 5;
178581
179050
  var BACKFILL_META_KEY = "v22_legacy_memory_backfill";
@@ -179410,6 +179879,7 @@ function createLiveSessionState() {
179410
179879
 
179411
179880
  // src/index.ts
179412
179881
  init_conflict_warning_hook();
179882
+
179413
179883
  // src/features/magic-context/dreamer/storage-dream-state.ts
179414
179884
  var getDreamStateStatements = new WeakMap;
179415
179885
  var setDreamStateStatements = new WeakMap;
@@ -179547,7 +180017,7 @@ function enqueueDream(db, projectIdentity, reason, force = false) {
179547
180017
  return db.transaction(() => {
179548
180018
  if (!hasActiveDreamLease(db)) {
179549
180019
  const staleThresholdMs = force ? 2 * 60 * 1000 : 120 * 60 * 1000;
179550
- db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run([projectIdentity, now - staleThresholdMs]);
180020
+ db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run(projectIdentity, now - staleThresholdMs);
179551
180021
  }
179552
180022
  const existing = db.prepare("SELECT id FROM dream_queue WHERE project_path = ?").get(projectIdentity);
179553
180023
  if (existing) {
@@ -179603,21 +180073,21 @@ function clearStaleEntries(db, maxAgeMs, projectIdentity) {
179603
180073
  return result.changes;
179604
180074
  }
179605
180075
  // src/features/magic-context/dreamer/runner.ts
180076
+ import { existsSync as existsSync13 } from "node:fs";
180077
+ import { join as join16 } from "node:path";
179606
180078
  init_shared();
179607
180079
  init_assistant_message_extractor();
179608
180080
  init_data_path();
179609
180081
  init_logger();
179610
180082
  await init_sqlite();
179611
- import { existsSync as existsSync13 } from "node:fs";
179612
- import { join as join16 } from "node:path";
179613
180083
 
179614
180084
  // src/features/magic-context/key-files/identify-key-files.ts
180085
+ import { readFileSync as readFileSync11 } from "node:fs";
180086
+ import { isAbsolute as isAbsolute4, join as join15, relative as relative2 } from "node:path";
179615
180087
  init_read_session_formatting();
179616
180088
  init_shared();
179617
180089
  init_assistant_message_extractor();
179618
180090
  init_logger();
179619
- import { readFileSync as readFileSync11 } from "node:fs";
179620
- import { isAbsolute as isAbsolute4, join as join15, relative as relative2 } from "node:path";
179621
180091
  init_subagent_token_capture();
179622
180092
  init_aft_availability();
179623
180093
  init_project_key_files();
@@ -181242,120 +181712,7 @@ ${body}` : subject;
181242
181712
  init_logger();
181243
181713
  init_embedding();
181244
181714
  init_storage_git_commit_embeddings();
181245
-
181246
- // src/features/magic-context/git-commits/storage-git-commits.ts
181247
- init_logger();
181248
- var insertStatements = new WeakMap;
181249
- var existingShasStatements = new WeakMap;
181250
- var projectCountStatements = new WeakMap;
181251
- var evictStatements = new WeakMap;
181252
- var evictOverflowStatements = new WeakMap;
181253
- var latestCommitTimeStatements = new WeakMap;
181254
- function getInsertStatement(db) {
181255
- let stmt = insertStatements.get(db);
181256
- if (!stmt) {
181257
- stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
181258
- VALUES (?, ?, ?, ?, ?, ?, ?)
181259
- ON CONFLICT(sha) DO UPDATE SET
181260
- project_path = excluded.project_path,
181261
- short_sha = excluded.short_sha,
181262
- message = excluded.message,
181263
- author = excluded.author,
181264
- committed_at = excluded.committed_at,
181265
- indexed_at = excluded.indexed_at
181266
- WHERE git_commits.message != excluded.message`);
181267
- insertStatements.set(db, stmt);
181268
- }
181269
- return stmt;
181270
- }
181271
- function getExistingShasStatement(db) {
181272
- let stmt = existingShasStatements.get(db);
181273
- if (!stmt) {
181274
- stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
181275
- existingShasStatements.set(db, stmt);
181276
- }
181277
- return stmt;
181278
- }
181279
- function getProjectCountStatement(db) {
181280
- let stmt = projectCountStatements.get(db);
181281
- if (!stmt) {
181282
- stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
181283
- projectCountStatements.set(db, stmt);
181284
- }
181285
- return stmt;
181286
- }
181287
- function getLatestCommitTimeStatement(db) {
181288
- let stmt = latestCommitTimeStatements.get(db);
181289
- if (!stmt) {
181290
- stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
181291
- latestCommitTimeStatements.set(db, stmt);
181292
- }
181293
- return stmt;
181294
- }
181295
- function getEvictOverflowStatement(db) {
181296
- let stmt = evictOverflowStatements.get(db);
181297
- if (!stmt) {
181298
- stmt = db.prepare(`DELETE FROM git_commits
181299
- WHERE rowid IN (
181300
- SELECT rowid FROM git_commits
181301
- WHERE project_path = ?
181302
- ORDER BY committed_at DESC, sha DESC
181303
- LIMIT -1 OFFSET ?
181304
- )`);
181305
- evictOverflowStatements.set(db, stmt);
181306
- }
181307
- return stmt;
181308
- }
181309
- function upsertCommits(db, projectPath, commits) {
181310
- if (commits.length === 0)
181311
- return { inserted: 0, updated: 0 };
181312
- const existing = new Set;
181313
- for (const row of getExistingShasStatement(db).all(projectPath)) {
181314
- existing.add(row.sha);
181315
- }
181316
- let inserted = 0;
181317
- let updated = 0;
181318
- const now = Date.now();
181319
- const insertStmt = getInsertStatement(db);
181320
- db.transaction(() => {
181321
- for (const commit of commits) {
181322
- const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
181323
- if (result.changes > 0) {
181324
- if (existing.has(commit.sha)) {
181325
- updated++;
181326
- } else {
181327
- inserted++;
181328
- existing.add(commit.sha);
181329
- }
181330
- }
181331
- }
181332
- })();
181333
- return { inserted, updated };
181334
- }
181335
- function getCommitCount(db, projectPath) {
181336
- const row = getProjectCountStatement(db).get(projectPath);
181337
- return row?.count ?? 0;
181338
- }
181339
- function getLatestIndexedCommitTimeMs(db, projectPath) {
181340
- const row = getLatestCommitTimeStatement(db).get(projectPath);
181341
- return row?.latest ?? null;
181342
- }
181343
- function enforceProjectCap(db, projectPath, maxCommits) {
181344
- if (maxCommits <= 0)
181345
- return 0;
181346
- const count = getCommitCount(db, projectPath);
181347
- if (count <= maxCommits)
181348
- return 0;
181349
- getEvictOverflowStatement(db).run(projectPath, maxCommits);
181350
- const after = getCommitCount(db, projectPath);
181351
- const evicted = Math.max(0, count - after);
181352
- if (evicted > 0) {
181353
- log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
181354
- }
181355
- return evicted;
181356
- }
181357
-
181358
- // src/features/magic-context/git-commits/indexer.ts
181715
+ init_storage_git_commits();
181359
181716
  var MS_PER_DAY = 24 * 60 * 60 * 1000;
181360
181717
  var EMBED_BATCH_SIZE = 16;
181361
181718
  var EMBED_MAX_PER_SWEEP = 500;
@@ -181593,12 +181950,12 @@ function searchGitCommitsSync(db, projectPath, query, options) {
181593
181950
 
181594
181951
  // src/features/magic-context/git-commits/index.ts
181595
181952
  init_storage_git_commit_embeddings();
181953
+ init_storage_git_commits();
181596
181954
  init_sweep_coordinator();
181597
181955
 
181598
181956
  // src/plugin/dream-timer.ts
181599
181957
  init_embedding();
181600
181958
  init_logger();
181601
- init_resolve_fallbacks();
181602
181959
  await init_storage();
181603
181960
  var DREAM_TIMER_INTERVAL_MS = 15 * 60 * 1000;
181604
181961
  var activeTimer = null;
@@ -181701,7 +182058,7 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
181701
182058
  experimentalPinKeyFiles: reg.experimentalPinKeyFiles,
181702
182059
  projectIdentity: reg.projectIdentity,
181703
182060
  sessionDirectoryOverride: reg.directory,
181704
- fallbackModels: resolveFallbackChain(DREAMER_AGENT, reg.dreamerConfig.fallback_models)
182061
+ fallbackModels: resolveFallbackChain(reg.dreamerConfig.fallback_models)
181705
182062
  });
181706
182063
  } catch (error51) {
181707
182064
  log(`[dreamer] timer-triggered queue processing failed for ${reg.projectIdentity}:`, error51);
@@ -182151,10 +182508,9 @@ function makeToolCompositeKey(ownerMsgId, callId) {
182151
182508
  }
182152
182509
  var GET_COUNTER_SQL = `SELECT counter FROM session_meta WHERE session_id = ?`;
182153
182510
  var GET_ASSIGNMENTS_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id FROM tags WHERE session_id = ? ORDER BY tag_number ASC";
182511
+ var GET_ASSIGNMENTS_SCOPED_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id FROM tags WHERE session_id = ? AND tag_number >= ? ORDER BY tag_number ASC";
182154
182512
  var PROBE_DATA_VERSION_SQL = "PRAGMA main.data_version";
182155
- var PROBE_TOTAL_CHANGES_SQL = "SELECT total_changes() AS tc";
182156
182513
  var probeDataVersionStatements = new WeakMap;
182157
- var probeTotalChangesStatements = new WeakMap;
182158
182514
  function getProbeDataVersionStatement(db) {
182159
182515
  let stmt = probeDataVersionStatements.get(db);
182160
182516
  if (!stmt) {
@@ -182163,14 +182519,6 @@ function getProbeDataVersionStatement(db) {
182163
182519
  }
182164
182520
  return stmt;
182165
182521
  }
182166
- function getProbeTotalChangesStatement(db) {
182167
- let stmt = probeTotalChangesStatements.get(db);
182168
- if (!stmt) {
182169
- stmt = db.prepare(PROBE_TOTAL_CHANGES_SQL);
182170
- probeTotalChangesStatements.set(db, stmt);
182171
- }
182172
- return stmt;
182173
- }
182174
182522
  function isAssignmentRow(row) {
182175
182523
  if (row === null || typeof row !== "object") {
182176
182524
  return false;
@@ -182363,20 +182711,18 @@ function createTagger() {
182363
182711
  }
182364
182712
  function probeSignature(db) {
182365
182713
  const dvRow = getProbeDataVersionStatement(db).get();
182366
- const tcRow = getProbeTotalChangesStatement(db).get();
182367
182714
  return {
182368
- dataVersion: dvRow?.data_version ?? 0,
182369
- totalChanges: tcRow?.tc ?? 0
182715
+ dataVersion: dvRow?.data_version ?? 0
182370
182716
  };
182371
182717
  }
182372
- function initFromDb(sessionId, db) {
182718
+ function initFromDb(sessionId, db, floor = 0) {
182373
182719
  const probe = probeSignature(db);
182374
182720
  const cached2 = loadSignatures.get(sessionId);
182375
- if (cached2 !== undefined && cached2.db === db && cached2.dataVersion === probe.dataVersion && cached2.totalChanges === probe.totalChanges) {
182721
+ if (cached2 !== undefined && cached2.db === db && cached2.dataVersion === probe.dataVersion && cached2.floor === floor) {
182376
182722
  return;
182377
182723
  }
182378
182724
  const row = db.prepare(GET_COUNTER_SQL).get(sessionId);
182379
- const assignmentRows = db.prepare(GET_ASSIGNMENTS_SQL).all(sessionId).filter(isAssignmentRow);
182725
+ const assignmentRows = (floor > 0 ? db.prepare(GET_ASSIGNMENTS_SCOPED_SQL).all(sessionId, floor) : db.prepare(GET_ASSIGNMENTS_SQL).all(sessionId)).filter(isAssignmentRow);
182380
182726
  const sessionAssignments = getSessionAssignments(sessionId);
182381
182727
  sessionAssignments.clear();
182382
182728
  let maxTagNumber = 0;
@@ -182397,7 +182743,7 @@ function createTagger() {
182397
182743
  loadSignatures.set(sessionId, {
182398
182744
  db,
182399
182745
  dataVersion: probe.dataVersion,
182400
- totalChanges: probe.totalChanges
182746
+ floor
182401
182747
  });
182402
182748
  }
182403
182749
  function cleanup(sessionId) {
@@ -182420,13 +182766,13 @@ function createTagger() {
182420
182766
  cleanup
182421
182767
  };
182422
182768
  }
182769
+
182423
182770
  // src/hooks/magic-context/hook.ts
182424
182771
  init_magic_context();
182425
182772
  init_project_identity();
182426
182773
  init_project_embedding_registry();
182427
182774
  await init_storage();
182428
182775
  init_logger();
182429
- init_resolve_fallbacks();
182430
182776
  init_rpc_notifications();
182431
182777
 
182432
182778
  // src/hooks/magic-context/command-handler.ts
@@ -182477,6 +182823,7 @@ await __promiseAll([
182477
182823
  // src/hooks/magic-context/compartment-trigger.ts
182478
182824
  init_compartment_storage();
182479
182825
  init_logger();
182826
+ init_read_session_true_raw_tokens();
182480
182827
  await __promiseAll([
182481
182828
  init_storage(),
182482
182829
  init_protected_tail_boundary(),
@@ -182491,6 +182838,38 @@ var TAIL_SIZE_TRIGGER_MULTIPLIER = 3;
182491
182838
  var FORCE_COMPARTMENT_PERCENTAGE = 80;
182492
182839
  var BLOCK_UNTIL_DONE_PERCENTAGE = 95;
182493
182840
  var FORCE_MATERIALIZE_PERCENTAGE = 85;
182841
+ var CONTENT_TAG_OWNER_SUFFIX = /:(?:p|file)\d+$/;
182842
+ function tagOwnerMessageId(row) {
182843
+ if (row.type === "tool")
182844
+ return row.tool_owner_message_id ?? row.message_id;
182845
+ return row.message_id.replace(CONTENT_TAG_OWNER_SUFFIX, "");
182846
+ }
182847
+ function getActiveOrDroppedTagOwnerMessageIds(db, sessionId, floor = 0) {
182848
+ const rows = floor > 0 ? db.prepare(`SELECT type, message_id, tool_owner_message_id
182849
+ FROM tags
182850
+ WHERE session_id = ? AND status IN ('active', 'dropped') AND tag_number >= ?`).all(sessionId, floor) : db.prepare(`SELECT type, message_id, tool_owner_message_id
182851
+ FROM tags
182852
+ WHERE session_id = ? AND status IN ('active', 'dropped')`).all(sessionId);
182853
+ const owners = new Set;
182854
+ for (const row of rows)
182855
+ owners.add(tagOwnerMessageId(row));
182856
+ return owners;
182857
+ }
182858
+ function estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, taggerFloor = 0) {
182859
+ const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
182860
+ const coveredOwnerMessageIds = getActiveOrDroppedTagOwnerMessageIds(db, sessionId, taggerFloor);
182861
+ let total = 0;
182862
+ for (const message of inMemoryTail.messages) {
182863
+ if (message.ordinal <= lastCompartmentEnd)
182864
+ continue;
182865
+ if (coveredOwnerMessageIds.has(message.id))
182866
+ continue;
182867
+ total += estimateTrueRawMessageTokens(message, {
182868
+ providerShapeVersion: "opencode-v1"
182869
+ }).total;
182870
+ }
182871
+ return total;
182872
+ }
182494
182873
  function buildTriggerInMemoryTail(db, sessionId, messages) {
182495
182874
  if (messages.length === 0)
182496
182875
  return;
@@ -182562,7 +182941,7 @@ function resolveBoundaryContextLimit(usage, fallbackContextLimit) {
182562
182941
  }
182563
182942
  return 128000;
182564
182943
  }
182565
- function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail) {
182944
+ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail, taggerFloor = 0) {
182566
182945
  return withRawSessionMessageCache(() => {
182567
182946
  try {
182568
182947
  const memoryPrimed = inMemoryTail ? primeInMemoryTailRawMessageCache({
@@ -182591,7 +182970,8 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
182591
182970
  contextLimit: resolveBoundaryContextLimit(usage, contextLimit),
182592
182971
  executeThresholdPercentage,
182593
182972
  usage,
182594
- usageSource: "live"
182973
+ usageSource: "live",
182974
+ taggerFloor
182595
182975
  });
182596
182976
  const hasProtectedEligibleHead = boundary.offset < boundary.protectedTailStart;
182597
182977
  if (!hasProtectedEligibleHead) {
@@ -182622,7 +183002,7 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
182622
183002
  }
182623
183003
  });
182624
183004
  }
182625
- function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail) {
183005
+ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail, taggerFloorOverride) {
182626
183006
  if (sessionMeta.compartmentInProgress) {
182627
183007
  sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
182628
183008
  return { shouldFire: false };
@@ -182636,14 +183016,17 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
182636
183016
  inMemoryTail = undefined;
182637
183017
  }
182638
183018
  }
183019
+ const taggerFloor = taggerFloorOverride !== undefined && taggerFloorOverride > 0 ? taggerFloorOverride : inMemoryTail ? deriveTagLoadFloor(db, sessionId, inMemoryTail.messages.map((m) => m.id)) : 0;
182639
183020
  const proactiveFloorForGate = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
182640
- if (!inMemoryTail && usage.percentage < proactiveFloorForGate) {
183021
+ if (usage.percentage < proactiveFloorForGate) {
182641
183022
  try {
182642
- const { bound, nullCount } = getTriggerTagTokenUpperBound(db, sessionId);
183023
+ const { bound: persistedBound, nullCount } = getTriggerTagTokenUpperBound(db, sessionId, taggerFloor);
182643
183024
  if (nullCount === 0) {
182644
- const eligibleUpperBound = bound;
183025
+ const untaggedUpperBound = inMemoryTail ? estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, taggerFloor) : 0;
183026
+ const eligibleUpperBound = persistedBound + untaggedUpperBound;
182645
183027
  if (eligibleUpperBound < triggerBudget) {
182646
- sessionLog(sessionId, `compartment trigger: cheap-skip at ${usage.percentage.toFixed(1)}% (below proactive floor ${proactiveFloorForGate}%) — live-tail upper bound ${eligibleUpperBound} < triggerBudget ${triggerBudget}; no size trigger possible, skipped full raw read`);
183028
+ const memorySuffix = inMemoryTail ? ` (persisted=${persistedBound}, untagged-memory≤${untaggedUpperBound})` : "";
183029
+ sessionLog(sessionId, `compartment trigger: cheap-skip at ${usage.percentage.toFixed(1)}% (below proactive floor ${proactiveFloorForGate}%) — live-tail upper bound ${eligibleUpperBound}${memorySuffix} < triggerBudget ${triggerBudget}; no size trigger possible, skipped full raw read`);
182647
183030
  return { shouldFire: false };
182648
183031
  }
182649
183032
  }
@@ -182651,7 +183034,7 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
182651
183034
  sessionLog(sessionId, `compartment trigger: cheap-gate skipped (falling through to full read): ${error51 instanceof Error ? error51.message : String(error51)}`);
182652
183035
  }
182653
183036
  }
182654
- const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail);
183037
+ const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail, taggerFloor);
182655
183038
  if (!tailInfo.hasNewRawHistory) {
182656
183039
  try {
182657
183040
  const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
@@ -183017,7 +183400,7 @@ function createMagicContextCommandHandler(deps) {
183017
183400
  const isAugCommand = (command) => command === "ctx-aug";
183018
183401
  const isDreamCommand = (command) => command === "ctx-dream";
183019
183402
  const isSessionUpgradeCommand = (command) => command === "ctx-session-upgrade";
183020
- const isEmbedHistoryCommand = (command) => command === "ctx-embed-history";
183403
+ const isEmbedCommand = (command) => command === "ctx-embed";
183021
183404
  return {
183022
183405
  "command.execute.before": async (input, _output, _params) => {
183023
183406
  const isStatus = isStatusCommand(input.command);
@@ -183026,8 +183409,8 @@ function createMagicContextCommandHandler(deps) {
183026
183409
  const isAug = isAugCommand(input.command);
183027
183410
  const isDream = isDreamCommand(input.command);
183028
183411
  const isSessionUpgrade = isSessionUpgradeCommand(input.command);
183029
- const isEmbedHistory = isEmbedHistoryCommand(input.command);
183030
- if (!isStatus && !isFlush && !isRecomp && !isAug && !isDream && !isSessionUpgrade && !isEmbedHistory) {
183412
+ const isEmbed = isEmbedCommand(input.command);
183413
+ if (!isStatus && !isFlush && !isRecomp && !isAug && !isDream && !isSessionUpgrade && !isEmbed) {
183031
183414
  return;
183032
183415
  }
183033
183416
  const sessionId = input.sessionID;
@@ -183040,15 +183423,50 @@ function createMagicContextCommandHandler(deps) {
183040
183423
  await executeDreaming(deps, sessionId);
183041
183424
  return;
183042
183425
  }
183043
- if (isEmbedHistory) {
183044
- const summary = deps.executeEmbedHistory ? await deps.executeEmbedHistory(sessionId) : "Semantic embedding is not configured for this project, so there is nothing to embed.";
183045
- await deps.sendNotification(sessionId, summary, {});
183046
- throwSentinel(input.command);
183426
+ if (isEmbed) {
183427
+ const sub = input.arguments.trim().toLowerCase();
183428
+ if (sub === "pause") {
183429
+ const summary = deps.pauseEmbedDrain ? deps.pauseEmbedDrain(sessionId) : "Embedding pause is unavailable.";
183430
+ if (isTuiConnected(sessionId)) {
183431
+ pushNotification("action", { action: "show-result-dialog", title: "Embed", message: summary }, sessionId);
183432
+ } else {
183433
+ await deps.sendNotification(sessionId, summary, {});
183434
+ }
183435
+ throwSentinel(input.command);
183436
+ }
183437
+ if (sub === "start") {
183438
+ const summary = deps.executeEmbedHistory ? await deps.executeEmbedHistory(sessionId) : "Semantic embedding is not configured for this project, so there is nothing to embed.";
183439
+ if (isTuiConnected(sessionId)) {
183440
+ pushNotification("action", { action: "show-result-dialog", title: "Embed", message: summary }, sessionId);
183441
+ } else {
183442
+ await deps.sendNotification(sessionId, summary, {});
183443
+ }
183444
+ throwSentinel(input.command);
183445
+ }
183446
+ if (sub !== "") {
183447
+ await deps.sendNotification(sessionId, "Usage: `/ctx-embed` (status), `/ctx-embed start`, or `/ctx-embed pause`.", {});
183448
+ throwSentinel(input.command);
183449
+ }
183450
+ if (isTuiConnected(sessionId)) {
183451
+ pushNotification("action", { action: "show-embed-dialog" }, sessionId);
183452
+ sessionLog(sessionId, "command ctx-embed: pushed show-embed-dialog to TUI");
183453
+ throwSentinel(input.command);
183454
+ }
183455
+ result = deps.getEmbedStatusText ? `## Embedding Status
183456
+
183457
+ ${deps.getEmbedStatusText(sessionId)}` : `## Embedding Status
183458
+
183459
+ Embedding status is unavailable.`;
183047
183460
  }
183048
183461
  if (isFlush) {
183049
183462
  result = executeFlush(deps.db, sessionId);
183050
183463
  clearCachedM0M1(deps.db, sessionId);
183051
183464
  deps.onFlush?.(sessionId);
183465
+ if (isTuiConnected(sessionId)) {
183466
+ pushNotification("action", { action: "show-flush-dialog", message: result }, sessionId);
183467
+ sessionLog(sessionId, "command ctx-flush: pushed show-flush-dialog to TUI");
183468
+ throwSentinel(input.command);
183469
+ }
183052
183470
  }
183053
183471
  if (isStatus) {
183054
183472
  if (isTuiConnected(sessionId)) {
@@ -183175,6 +183593,34 @@ ${snap.error}`;
183175
183593
  // src/hooks/magic-context/hook.ts
183176
183594
  init_derive_budgets();
183177
183595
 
183596
+ // src/hooks/magic-context/embed-session-state.ts
183597
+ var embedPauseBySession = new Set;
183598
+ var embedRunStateBySession = new Map;
183599
+ var autoEmbedAttemptedBySession = new Set;
183600
+ function getEmbedDrainUiStatus(sessionId, progress) {
183601
+ if (embedPauseBySession.has(sessionId)) {
183602
+ return { status: "paused" };
183603
+ }
183604
+ if (progress?.kind === "embed" && progress.phase === "recomp") {
183605
+ return { status: "running" };
183606
+ }
183607
+ if (progress?.kind === "embed" && (progress.phase === "failed" || progress.phase === "skipped") && progress.message) {
183608
+ if (/provider/i.test(progress.message)) {
183609
+ return { status: "stopped", detail: progress.message };
183610
+ }
183611
+ }
183612
+ return { status: "idle" };
183613
+ }
183614
+ function clearEmbedSessionState(sessionId) {
183615
+ embedPauseBySession.delete(sessionId);
183616
+ const ctrl = embedRunStateBySession.get(sessionId);
183617
+ if (ctrl) {
183618
+ ctrl.abort();
183619
+ embedRunStateBySession.delete(sessionId);
183620
+ }
183621
+ autoEmbedAttemptedBySession.delete(sessionId);
183622
+ }
183623
+
183178
183624
  // src/features/magic-context/message-index-async.ts
183179
183625
  init_logger();
183180
183626
  await init_message_index();
@@ -183323,9 +183769,142 @@ function clearSessionTracking(sessionId) {
183323
183769
  // src/hooks/magic-context/event-handler.ts
183324
183770
  init_overflow_detection();
183325
183771
  init_storage_meta_persisted();
183772
+ await init_storage();
183773
+
183774
+ // src/features/magic-context/transform-decision-log.ts
183775
+ await __promiseAll([
183776
+ init_sqlite(),
183777
+ init_storage_db()
183778
+ ]);
183779
+ var canonicalReasons = new Set([
183780
+ "system_hash",
183781
+ "model_change",
183782
+ "project_memory_epoch",
183783
+ "ttl_idle",
183784
+ "explicit_flush",
183785
+ "max_mutation_id",
183786
+ "first_render",
183787
+ "pressure_refold",
183788
+ "upgrade_state",
183789
+ "cached_m1_missing"
183790
+ ]);
183791
+ var piReasonAliases = {
183792
+ project_memory_change: "project_memory_epoch",
183793
+ pending_mutations: "max_mutation_id",
183794
+ renderer_upgrade: "upgrade_state",
183795
+ cache_invalid: "cached_m1_missing",
183796
+ drift: "pressure_refold"
183797
+ };
183798
+ var sharedReasonAliases = {
183799
+ model_key: "model_change",
183800
+ pressure: "pressure_refold"
183801
+ };
183802
+ var pendingDecisionBySession = new Map;
183803
+ var pendingPiDecisionBySession = new Map;
183804
+ var lastBoundMessageIdBySession = new Map;
183805
+ var scheduledWriteTokensBySession = new Map;
183806
+ var writerOverrideForTests = null;
183807
+ function normalizeMaterializeReason(harness, reason, rematerialized) {
183808
+ const raw = typeof reason === "string" ? reason.trim() : "";
183809
+ if (raw.length > 0) {
183810
+ const alias = sharedReasonAliases[raw] ?? (harness === "pi" ? piReasonAliases[raw] : undefined) ?? undefined;
183811
+ if (alias)
183812
+ return alias;
183813
+ if (canonicalReasons.has(raw))
183814
+ return raw;
183815
+ return null;
183816
+ }
183817
+ return rematerialized ? "pressure_refold" : null;
183818
+ }
183819
+ function clearOpenCodePendingTransformDecision(sessionId) {
183820
+ pendingDecisionBySession.delete(sessionId);
183821
+ }
183822
+ function clearTransformDecisionSession(sessionId) {
183823
+ pendingDecisionBySession.delete(sessionId);
183824
+ pendingPiDecisionBySession.delete(sessionId);
183825
+ lastBoundMessageIdBySession.delete(sessionId);
183826
+ scheduledWriteTokensBySession.delete(sessionId);
183827
+ }
183828
+ function recordPendingTransformDecision(sessionId, decision) {
183829
+ if (!decision.bustedThisPass) {
183830
+ pendingDecisionBySession.delete(sessionId);
183831
+ return;
183832
+ }
183833
+ pendingDecisionBySession.set(sessionId, decision);
183834
+ }
183835
+ function scheduleOpenCodeTransformDecisionWrite(args) {
183836
+ const pending = pendingDecisionBySession.get(args.sessionId);
183837
+ if (!pending)
183838
+ return false;
183839
+ if (lastBoundMessageIdBySession.get(args.sessionId) === args.messageId) {
183840
+ return false;
183841
+ }
183842
+ const dbPath = getDatabasePath(args.db);
183843
+ if (!dbPath)
183844
+ return false;
183845
+ lastBoundMessageIdBySession.set(args.sessionId, args.messageId);
183846
+ pendingDecisionBySession.delete(args.sessionId);
183847
+ const token = addScheduledWriteToken(args.sessionId);
183848
+ setTimeout(() => {
183849
+ try {
183850
+ if (!hasScheduledWriteToken(args.sessionId, token))
183851
+ return;
183852
+ writeTransformDecisionBestEffort(dbPath, {
183853
+ ...pending,
183854
+ sessionId: args.sessionId,
183855
+ harness: "opencode",
183856
+ messageId: args.messageId,
183857
+ inputTokens: args.inputTokens
183858
+ });
183859
+ } finally {
183860
+ deleteScheduledWriteToken(args.sessionId, token);
183861
+ }
183862
+ }, 0);
183863
+ return true;
183864
+ }
183865
+ function addScheduledWriteToken(sessionId) {
183866
+ const token = Symbol(sessionId);
183867
+ let tokens = scheduledWriteTokensBySession.get(sessionId);
183868
+ if (!tokens) {
183869
+ tokens = new Set;
183870
+ scheduledWriteTokensBySession.set(sessionId, tokens);
183871
+ }
183872
+ tokens.add(token);
183873
+ return token;
183874
+ }
183875
+ function hasScheduledWriteToken(sessionId, token) {
183876
+ return scheduledWriteTokensBySession.get(sessionId)?.has(token) === true;
183877
+ }
183878
+ function deleteScheduledWriteToken(sessionId, token) {
183879
+ const tokens = scheduledWriteTokensBySession.get(sessionId);
183880
+ if (!tokens)
183881
+ return;
183882
+ tokens.delete(token);
183883
+ if (tokens.size === 0)
183884
+ scheduledWriteTokensBySession.delete(sessionId);
183885
+ }
183886
+ function writeTransformDecisionBestEffort(dbPath, row) {
183887
+ try {
183888
+ const writer = writerOverrideForTests ?? writeTransformDecisionRow;
183889
+ writer(dbPath, row);
183890
+ } catch {}
183891
+ }
183892
+ function writeTransformDecisionRow(dbPath, row) {
183893
+ const db = new Database(dbPath);
183894
+ try {
183895
+ db.exec("PRAGMA busy_timeout=0");
183896
+ db.prepare(`INSERT OR REPLACE INTO transform_decisions (
183897
+ session_id, harness, message_id, ts_ms, decision, materialized,
183898
+ materialize_reason, emergency, dropped_tokens, dropped_count, input_tokens
183899
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(row.sessionId, row.harness, row.messageId, row.tsMs, row.decision, row.materialized ? 1 : 0, row.materializeReason, row.emergency ? 1 : 0, Math.max(0, Math.floor(row.droppedTokens)), Math.max(0, Math.floor(row.droppedCount)), Math.max(0, Math.floor(row.inputTokens)));
183900
+ } finally {
183901
+ closeQuietly(db);
183902
+ }
183903
+ }
183904
+
183905
+ // src/hooks/magic-context/event-handler.ts
183326
183906
  init_logger();
183327
183907
  init_models_dev_cache();
183328
- await init_storage();
183329
183908
 
183330
183909
  // src/hooks/magic-context/channel2-delivery.ts
183331
183910
  init_storage_meta_persisted();
@@ -183535,6 +184114,13 @@ function computePressure(input) {
183535
184114
  function approxThousands(tokens) {
183536
184115
  return `${Math.round(tokens / 1000)}k`;
183537
184116
  }
184117
+ function formatOldestReclaimableHint(hint) {
184118
+ if (!hint || hint.length === 0)
184119
+ return "";
184120
+ const rendered = hint.slice(0, 4).map((tag) => `§${tag.tagNumber}§ ${tag.toolName ?? "tool"}`).join(" · ");
184121
+ return rendered.length > 0 ? `
184122
+ oldest reclaimable: ${rendered}.` : "";
184123
+ }
183538
184124
  var CHANNEL2_USABLE_FRACTION = 1 / 3;
183539
184125
  var CHANNEL2_MIN_RECLAIMABLE = 1e4;
183540
184126
  function shouldTriggerChannel2(input) {
@@ -183544,14 +184130,16 @@ function shouldTriggerChannel2(input) {
183544
184130
  return true;
183545
184131
  return input.reclaimableTokens >= input.usableTokens * CHANNEL2_USABLE_FRACTION;
183546
184132
  }
183547
- function buildChannel2Reminder(undroppedTokens) {
184133
+ function buildChannel2Reminder(undroppedTokens, hint) {
183548
184134
  const amount = approxThousands(undroppedTokens);
184135
+ const hintText = formatOldestReclaimableHint(hint);
183549
184136
  return `<system-reminder>
183550
- ` + `Routine context housekeeping is near: a large span of this session will be comparted soon, ` + `and ~${amount} tokens of tool output remain unreduced. Drop spent outputs with ctx_reduce ` + `first so the archived span is the part that matters.
184137
+ ` + `Routine context housekeeping is near: a large span of this session will be comparted soon, ` + `and ~${amount} tokens of tool output remain unreduced. Drop spent outputs with ctx_reduce ` + `first so the archived span is the part that matters.${hintText}
183551
184138
  ` + `</system-reminder>`;
183552
184139
  }
183553
- function buildChannel1Reminder(level, undroppedTokens) {
184140
+ function buildChannel1Reminder(level, undroppedTokens, hint) {
183554
184141
  const amount = approxThousands(undroppedTokens);
184142
+ const hintText = formatOldestReclaimableHint(hint);
183555
184143
  let body;
183556
184144
  switch (level) {
183557
184145
  case "gentle":
@@ -183567,7 +184155,7 @@ function buildChannel1Reminder(level, undroppedTokens) {
183567
184155
  return `
183568
184156
 
183569
184157
  <system-reminder>
183570
- ${body}
184158
+ ${body}${hintText}
183571
184159
  </system-reminder>`;
183572
184160
  }
183573
184161
 
@@ -183621,10 +184209,10 @@ async function maybeDeliverChannel2(sessionId, deps) {
183621
184209
  try {
183622
184210
  const client3 = getLiveServerClient(serverUrl, deps.directory);
183623
184211
  const promptContext = await resolvePromptContext(client3, sessionId);
183624
- const reminder = buildChannel2Reminder(deps.reclaimableTokens);
184212
+ const reminder = buildChannel2Reminder(deps.reclaimableTokens, deps.oldestReclaimableToolTags);
183625
184213
  const body = {
183626
184214
  noReply: false,
183627
- parts: [{ type: "text", text: reminder }]
184215
+ parts: [{ type: "text", text: reminder, synthetic: true }]
183628
184216
  };
183629
184217
  if (promptContext?.agent)
183630
184218
  body.agent = promptContext.agent;
@@ -183720,11 +184308,13 @@ function getMessageUpdatedAssistantInfo(properties) {
183720
184308
  }
183721
184309
  const tokens = isRecord(info.tokens) ? info.tokens : undefined;
183722
184310
  const cache = tokens && isRecord(tokens.cache) ? tokens.cache : undefined;
184311
+ const time3 = isRecord(info.time) ? info.time : undefined;
183723
184312
  return {
183724
184313
  role: "assistant",
183725
184314
  finish: typeof info.finish === "string" ? info.finish : undefined,
183726
184315
  sessionID: info.sessionID,
183727
184316
  messageID: typeof info.id === "string" ? info.id : undefined,
184317
+ completedAt: typeof time3?.completed === "number" ? time3.completed : undefined,
183728
184318
  providerID: typeof info.providerID === "string" ? info.providerID : undefined,
183729
184319
  modelID: typeof info.modelID === "string" ? info.modelID : undefined,
183730
184320
  tokens: {
@@ -183787,8 +184377,8 @@ init_project_identity();
183787
184377
  import * as crypto2 from "node:crypto";
183788
184378
  init_session_project_storage();
183789
184379
  init_storage_meta_persisted();
183790
- init_logger();
183791
184380
  await init_storage();
184381
+ init_logger();
183792
184382
 
183793
184383
  // src/hooks/magic-context/boundary-execution.ts
183794
184384
  var FORCE_MATERIALIZE_PERCENTAGE2 = 85;
@@ -184082,7 +184672,8 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
184082
184672
  const result = {
184083
184673
  compressedToLite: 0,
184084
184674
  compressedToFull: 0,
184085
- compressedToUltra: 0
184675
+ compressedToUltra: 0,
184676
+ mutatedTextTags: 0
184086
184677
  };
184087
184678
  if (!config2.enabled)
184088
184679
  return result;
@@ -184123,7 +184714,9 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
184123
184714
  const target = targets.get(tag.tagNumber);
184124
184715
  if (!target)
184125
184716
  continue;
184126
- target.setContent(compressed);
184717
+ const didMutate = target.setContent(compressed);
184718
+ if (didMutate)
184719
+ result.mutatedTextTags += 1;
184127
184720
  updateCavemanDepth(db, sessionId, tag.tagNumber, targetDepth);
184128
184721
  if (targetDepth === DEPTH_LITE)
184129
184722
  result.compressedToLite += 1;
@@ -184711,28 +185304,6 @@ function stripInlineThinking(messages, messageTagNumbers, clearReasoningAge) {
184711
185304
  }
184712
185305
  return stripped;
184713
185306
  }
184714
- function truncateErroredTools(messages, watermark, messageTagNumbers) {
184715
- let truncated = 0;
184716
- for (let i = 0;i < messages.length; i++) {
184717
- const maxTag = messageTagNumbers.get(messages[i]) ?? 0;
184718
- if (maxTag > watermark) {
184719
- continue;
184720
- }
184721
- for (const part of messages[i].parts) {
184722
- if (!isRecord(part) || part.type !== "tool" || !isRecord(part.state)) {
184723
- continue;
184724
- }
184725
- if (part.state.status !== "error") {
184726
- continue;
184727
- }
184728
- if (typeof part.state.error === "string" && part.state.error.length > 100) {
184729
- part.state.error = `${part.state.error.slice(0, 100)}... [truncated]`;
184730
- truncated++;
184731
- }
184732
- }
184733
- }
184734
- return truncated;
184735
- }
184736
185307
  var REASONING_IGNORED_PART_TYPES = new Set([
184737
185308
  "step-start",
184738
185309
  "step-finish",
@@ -184843,6 +185414,7 @@ function stripProcessedImages(messages, frozenIds, options) {
184843
185414
  init_temporal_awareness();
184844
185415
 
184845
185416
  // src/hooks/magic-context/transform-compartment-phase.ts
185417
+ init_compartment_storage();
184846
185418
  init_logger();
184847
185419
  await __promiseAll([
184848
185420
  init_storage(),
@@ -184851,9 +185423,29 @@ await __promiseAll([
184851
185423
  init_send_session_notification();
184852
185424
  await __promiseAll([
184853
185425
  init_inject_compartments(),
184854
- init_protected_tail_boundary()
185426
+ init_protected_tail_boundary(),
185427
+ init_read_session_chunk()
184855
185428
  ]);
184856
- async function runCompartmentPhase(args) {
185429
+ function runCompartmentPhase(args) {
185430
+ const historianRunnable = args.historianRunnable !== false;
185431
+ const willReadRawHistory = historianRunnable && args.canRunCompartments && getActiveCompartmentRun(args.sessionId) === undefined && (args.sessionMeta.compartmentInProgress || !args.skipAwaitForThisPass && args.contextUsage.percentage >= BLOCK_UNTIL_DONE_PERCENTAGE);
185432
+ if (!willReadRawHistory) {
185433
+ return runCompartmentPhaseImpl(args);
185434
+ }
185435
+ return withRawSessionMessageCache(() => {
185436
+ try {
185437
+ primeTailRawMessageCache({
185438
+ sessionId: args.resolvedSessionId,
185439
+ lastCompartmentEnd: getLastCompartmentEndMessage(args.db, args.resolvedSessionId),
185440
+ anchorMessageId: getLastCompartmentEndMessageId(args.db, args.resolvedSessionId)
185441
+ });
185442
+ } catch (error51) {
185443
+ sessionLog(args.sessionId, "compartment phase: tail prime failed (non-fatal):", error51);
185444
+ }
185445
+ return runCompartmentPhaseImpl(args);
185446
+ });
185447
+ }
185448
+ async function runCompartmentPhaseImpl(args) {
184857
185449
  let pendingCompartmentInjection = args.pendingCompartmentInjection;
184858
185450
  let compartmentInProgress = args.sessionMeta.compartmentInProgress;
184859
185451
  let published = false;
@@ -185150,78 +185742,11 @@ function appendReminderToUserMessage(message, reminder) {
185150
185742
 
185151
185743
  // src/hooks/magic-context/apply-operations.ts
185152
185744
  await init_storage();
185153
-
185154
- // src/hooks/magic-context/system-injection-stripper.ts
185155
- var SYSTEM_INJECTION_MARKERS = [
185156
- "<!-- OMO_INTERNAL_INITIATOR -->",
185157
- "[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
185158
- "[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
185159
- "[Category+Skill Reminder]",
185160
- "[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
185161
- "[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
185162
- "[EMERGENCY CONTEXT WINDOW WARNING]",
185163
- "Unstable background agent appears idle",
185164
- "**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
185165
- ];
185166
- var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
185167
- var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
185168
- function stripSystemInjection(text) {
185169
- let hasInjection = false;
185170
- for (const marker of SYSTEM_INJECTION_MARKERS) {
185171
- if (text.includes(marker)) {
185172
- hasInjection = true;
185173
- break;
185174
- }
185175
- }
185176
- if (SYSTEM_REMINDER_REGEX.test(text))
185177
- hasInjection = true;
185178
- SYSTEM_REMINDER_REGEX.lastIndex = 0;
185179
- if (!hasInjection)
185180
- return null;
185181
- let cleaned = text;
185182
- cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
185183
- cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
185184
- cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
185185
- for (const marker of SYSTEM_INJECTION_MARKERS) {
185186
- if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
185187
- continue;
185188
- const idx = cleaned.indexOf(marker);
185189
- if (idx === -1)
185190
- continue;
185191
- const blockEnd = cleaned.indexOf(`
185192
-
185193
- `, idx + marker.length);
185194
- cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
185195
- }
185196
- return cleaned.trim();
185197
- }
185198
-
185199
- // src/hooks/magic-context/apply-operations.ts
185200
- init_tag_part_guards();
185201
- var USER_DROP_PREVIEW_CHARS = 250;
185202
185745
  var RECENT_TOOL_SKELETON_WINDOW = 20;
185203
- function buildReplacementContent(tagId, target) {
185204
- const role = target.message?.info.role;
185205
- if (role !== "user") {
185206
- return `[dropped §${tagId}§]`;
185207
- }
185208
- const currentContent = target.getContent?.() ?? "";
185209
- const strippedInjection = stripSystemInjection(currentContent);
185210
- if (strippedInjection !== null && stripTagPrefix(strippedInjection).trim().length === 0) {
185211
- return `[dropped §${tagId}§]`;
185212
- }
185213
- const originalText = stripTagPrefix(currentContent);
185214
- if (originalText.length <= USER_DROP_PREVIEW_CHARS) {
185215
- return `[truncated §${tagId}§]
185216
- ${originalText}`;
185217
- }
185218
- const hardCut = originalText.slice(0, USER_DROP_PREVIEW_CHARS);
185219
- const softCutIndex = hardCut.search(/\s\S*$/);
185220
- const preview = softCutIndex > USER_DROP_PREVIEW_CHARS - 30 ? hardCut.slice(0, softCutIndex) : hardCut;
185221
- return `[truncated §${tagId}§]
185222
- ${preview}…`;
185223
- }
185224
- function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps) {
185746
+ function buildReplacementContent(tagId) {
185747
+ return `[dropped §${tagId}§]`;
185748
+ }
185749
+ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps, syntheticPendingOps = []) {
185225
185750
  let didMutateMessage = false;
185226
185751
  db.transaction(() => {
185227
185752
  const tags = preloadedTags ?? getTagsBySession(db, sessionId);
@@ -185229,11 +185754,16 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
185229
185754
  const tagTypeById = new Map(tags.map((tag) => [tag.tagNumber, tag.type]));
185230
185755
  const protectedTagIds = protectedTags > 0 ? new Set(tags.filter((tag) => tag.status === "active").map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0, protectedTags)) : new Set;
185231
185756
  const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
185757
+ const opsToApply = [
185758
+ ...pendingOps.map((op) => ({ op, synthetic: false })),
185759
+ ...syntheticPendingOps.map((op) => ({ op, synthetic: true }))
185760
+ ];
185232
185761
  const skeletonWindow = new Set(tags.filter((tag) => tag.type === "tool").map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0, RECENT_TOOL_SKELETON_WINDOW));
185233
- for (const pendingOp of pendingOps) {
185762
+ for (const { op: pendingOp, synthetic } of opsToApply) {
185234
185763
  const tagStatus = tagStatusById.get(pendingOp.tagId);
185235
185764
  if (tagStatus === "compacted" || tagStatus === "dropped") {
185236
- removePendingOp(db, sessionId, pendingOp.tagId);
185765
+ if (!synthetic)
185766
+ removePendingOp(db, sessionId, pendingOp.tagId);
185237
185767
  continue;
185238
185768
  }
185239
185769
  if (protectedTagIds.has(pendingOp.tagId)) {
@@ -185241,33 +185771,46 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
185241
185771
  }
185242
185772
  const target = targets.get(pendingOp.tagId);
185243
185773
  const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
185774
+ if (synthetic) {
185775
+ if (!isToolTag || target?.canDrop?.() !== true)
185776
+ continue;
185777
+ }
185778
+ let shouldPersistDrop = false;
185244
185779
  if (isToolTag) {
185245
185780
  if (skeletonWindow.has(pendingOp.tagId)) {
185246
185781
  const truncResult = target?.truncate?.() ?? "absent";
185247
- if (truncResult === "incomplete") {
185782
+ if (truncResult === "incomplete" || synthetic && truncResult !== "truncated") {
185248
185783
  continue;
185249
185784
  }
185250
185785
  if (truncResult === "truncated") {
185251
185786
  didMutateMessage = true;
185252
185787
  }
185253
185788
  updateTagDropMode(db, sessionId, pendingOp.tagId, "truncated");
185789
+ shouldPersistDrop = true;
185254
185790
  } else {
185255
185791
  const dropResult = target?.drop?.() ?? "absent";
185256
- if (dropResult === "incomplete") {
185792
+ if (dropResult === "incomplete" || synthetic && dropResult !== "removed") {
185257
185793
  continue;
185258
185794
  }
185259
185795
  if (dropResult === "removed") {
185260
185796
  didMutateMessage = true;
185261
185797
  }
185262
185798
  updateTagDropMode(db, sessionId, pendingOp.tagId, "full");
185799
+ shouldPersistDrop = true;
185263
185800
  }
185264
185801
  } else if (target) {
185265
- const changed = target.setContent(buildReplacementContent(pendingOp.tagId, target));
185802
+ const changed = target.setContent(buildReplacementContent(pendingOp.tagId));
185266
185803
  if (changed)
185267
185804
  didMutateMessage = true;
185805
+ shouldPersistDrop = true;
185806
+ } else if (!synthetic) {
185807
+ shouldPersistDrop = true;
185268
185808
  }
185809
+ if (!shouldPersistDrop)
185810
+ continue;
185269
185811
  updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
185270
- removePendingOp(db, sessionId, pendingOp.tagId);
185812
+ if (!synthetic)
185813
+ removePendingOp(db, sessionId, pendingOp.tagId);
185271
185814
  }
185272
185815
  })();
185273
185816
  return didMutateMessage;
@@ -185291,7 +185834,7 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
185291
185834
  }
185292
185835
  }
185293
185836
  } else if (target) {
185294
- const changed = target.setContent(buildReplacementContent(tag.tagNumber, target));
185837
+ const changed = target.setContent(buildReplacementContent(tag.tagNumber));
185295
185838
  if (changed)
185296
185839
  didMutateMessage = true;
185297
185840
  }
@@ -185468,6 +186011,7 @@ function createExistingTagResolver(sessionId, tagger, db) {
185468
186011
  return;
185469
186012
  }
185470
186013
  updateTagMessageId(db, sessionId, fallback.tagNumber, currentContentId);
186014
+ tagger.unbindTag(sessionId, fallback.contentId);
185471
186015
  tagger.bindTag(sessionId, currentContentId, fallback.tagNumber);
185472
186016
  usedTagNumbers.add(fallback.tagNumber);
185473
186017
  return fallback.tagNumber;
@@ -185488,7 +186032,45 @@ function logTransformTiming(sessionId, stage, startMs, extra) {
185488
186032
  }
185489
186033
 
185490
186034
  // src/hooks/magic-context/tag-messages.ts
185491
- function deriveToolOwnerMessageId(sessionId, db, message, obs, unpaired) {
186035
+ var TOOL_OWNER_CACHE_KEY_SEP = "\x00";
186036
+ function makeToolOwnerCacheKey(sessionId, callId) {
186037
+ return `${sessionId}${TOOL_OWNER_CACHE_KEY_SEP}${callId}`;
186038
+ }
186039
+ function getCachedCandidateToolOwners(db, sessionId, callId, cache, onLookup) {
186040
+ const key = makeToolOwnerCacheKey(sessionId, callId);
186041
+ const cached2 = cache.candidateOwnersByCallId.get(key);
186042
+ if (cached2 !== undefined)
186043
+ return cached2;
186044
+ onLookup?.({ kind: "candidates", callId });
186045
+ const candidates = getCandidateToolOwners(db, sessionId, callId);
186046
+ cache.candidateOwnersByCallId.set(key, candidates);
186047
+ return candidates;
186048
+ }
186049
+ function getCachedMessageTimesFromOpenCodeDb(sessionId, messageIds, cache, onLookup) {
186050
+ const uncached = [...new Set(messageIds)].filter((id) => !cache.messageTimesById.has(id));
186051
+ if (uncached.length > 0) {
186052
+ onLookup?.({ kind: "messageTimes", messageIds: uncached });
186053
+ const resolved = getMessageTimesFromOpenCodeDb(sessionId, uncached);
186054
+ for (const id of uncached) {
186055
+ cache.messageTimesById.set(id, resolved.get(id) ?? null);
186056
+ }
186057
+ }
186058
+ const times = new Map;
186059
+ for (const id of messageIds) {
186060
+ const time3 = cache.messageTimesById.get(id);
186061
+ if (typeof time3 === "number")
186062
+ times.set(id, time3);
186063
+ }
186064
+ return times;
186065
+ }
186066
+ function invalidateCachedCandidateToolOwnersIfNewOwner(cache, sessionId, callId, ownerMsgId) {
186067
+ const key = makeToolOwnerCacheKey(sessionId, callId);
186068
+ const cached2 = cache.candidateOwnersByCallId.get(key);
186069
+ if (cached2 !== undefined && !cached2.includes(ownerMsgId)) {
186070
+ cache.candidateOwnersByCallId.delete(key);
186071
+ }
186072
+ }
186073
+ function deriveToolOwnerMessageId(sessionId, db, message, obs, unpaired, cache, onFallbackLookup) {
185492
186074
  const messageId = typeof message.info.id === "string" ? message.info.id : "";
185493
186075
  if (obs.kind === "invocation") {
185494
186076
  if (messageId) {
@@ -185508,10 +186090,10 @@ function deriveToolOwnerMessageId(sessionId, db, message, obs, unpaired) {
185508
186090
  return popped;
185509
186091
  }
185510
186092
  if (messageId) {
185511
- const candidates = getCandidateToolOwners(db, sessionId, obs.callId);
186093
+ const candidates = getCachedCandidateToolOwners(db, sessionId, obs.callId, cache, onFallbackLookup);
185512
186094
  if (candidates.length > 0) {
185513
186095
  const ids = [...candidates, messageId];
185514
- const times = getMessageTimesFromOpenCodeDb(sessionId, ids);
186096
+ const times = getCachedMessageTimesFromOpenCodeDb(sessionId, ids, cache, onFallbackLookup);
185515
186097
  const persisted = pickNearestPriorOwner(candidates, messageId, times);
185516
186098
  if (persisted !== null)
185517
186099
  return persisted;
@@ -185592,6 +186174,7 @@ function extractToolTagMetadata(part) {
185592
186174
  }
185593
186175
  function tagMessages(sessionId, messages, tagger, db, options = {}) {
185594
186176
  const skipPrefixInjection = options.skipPrefixInjection === true;
186177
+ const onToolOwnerFallbackLookup = options.onToolOwnerFallbackLookup;
185595
186178
  const targets = new Map;
185596
186179
  const reasoningByMessage = new Map;
185597
186180
  const messageTagNumbers = new Map;
@@ -185599,6 +186182,10 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
185599
186182
  const toolThinkingByCallId = new Map;
185600
186183
  const toolCallIndex = new Map;
185601
186184
  const unpairedInvocations = new Map;
186185
+ const ownerDerivationCache = {
186186
+ candidateOwnersByCallId: new Map,
186187
+ messageTimesById: new Map
186188
+ };
185602
186189
  const ownerByPartKey = new Map;
185603
186190
  const batch = new ToolMutationBatch(messages);
185604
186191
  const assignments = tagger.getAssignments(sessionId);
@@ -185639,7 +186226,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
185639
186226
  const toolObservation = extractToolCallObservation(part);
185640
186227
  if (toolObservation) {
185641
186228
  const _tDerive = performance.now();
185642
- const ownerMsgId = deriveToolOwnerMessageId(sessionId, db, message, toolObservation, unpairedInvocations);
186229
+ const ownerMsgId = deriveToolOwnerMessageId(sessionId, db, message, toolObservation, unpairedInvocations, ownerDerivationCache, onToolOwnerFallbackLookup);
185643
186230
  accDerive += performance.now() - _tDerive;
185644
186231
  const compositeKey = makeToolCompositeKey(ownerMsgId, toolObservation.callId);
185645
186232
  const entry = toolCallIndex.get(compositeKey) ?? {
@@ -185658,6 +186245,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
185658
186245
  if (orphan !== null) {
185659
186246
  const claimed = adoptNullOwnerToolTag(db, orphan.id, ownerMsgId);
185660
186247
  if (claimed) {
186248
+ invalidateCachedCandidateToolOwnersIfNewOwner(ownerDerivationCache, sessionId, toolObservation.callId, ownerMsgId);
185661
186249
  tagger.bindToolTag(sessionId, toolObservation.callId, ownerMsgId, orphan.tagNumber);
185662
186250
  existingTagId = orphan.tagNumber;
185663
186251
  } else {
@@ -185665,6 +186253,13 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
185665
186253
  }
185666
186254
  }
185667
186255
  }
186256
+ if (existingTagId === undefined) {
186257
+ const persisted = getToolTagNumberByOwner(db, sessionId, toolObservation.callId, ownerMsgId);
186258
+ if (persisted !== null) {
186259
+ tagger.bindToolTag(sessionId, toolObservation.callId, ownerMsgId, persisted);
186260
+ existingTagId = persisted;
186261
+ }
186262
+ }
185668
186263
  if (existingTagId !== undefined) {
185669
186264
  toolTagByCallId.set(compositeKey, existingTagId);
185670
186265
  messageTagNumbers.set(message, Math.max(messageTagNumbers.get(message) ?? 0, existingTagId));
@@ -185737,6 +186332,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
185737
186332
  inputTokenCount,
185738
186333
  reasoningTokenCount: reasoningTokens
185739
186334
  }));
186335
+ invalidateCachedCandidateToolOwnersIfNewOwner(ownerDerivationCache, sessionId, toolPart.callID, ownerMsgId);
185740
186336
  accAssignToolTag += performance.now() - _tAssignTool;
185741
186337
  messageTagNumbers.set(message, Math.max(messageTagNumbers.get(message) ?? 0, tagId));
185742
186338
  if (!skipPrefixInjection) {
@@ -185808,7 +186404,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
185808
186404
  logTransformTiming(sessionId, "tag.saveSource", performance.now() - accSaveSource);
185809
186405
  for (const [compositeKey, tagId] of toolTagByCallId) {
185810
186406
  const thinkingParts = toolThinkingByCallId.get(compositeKey) ?? [];
185811
- targets.set(tagId, createToolDropTarget(compositeKey, thinkingParts, toolCallIndex, batch));
186407
+ targets.set(tagId, createToolDropTarget(compositeKey, thinkingParts, toolCallIndex, batch, tagId));
185812
186408
  }
185813
186409
  const hasRecentReduceCall = lastReduceMessageIndex >= 0 && messages.length - lastReduceMessageIndex <= RECENT_REDUCE_LOOKBACK;
185814
186410
  return {
@@ -186735,7 +187331,7 @@ async function runAutoSearchHint(args) {
186735
187331
  embeddingEnabled,
186736
187332
  gitCommitsEnabled,
186737
187333
  embedQuery: async (text, signal) => {
186738
- const result = await embedTextForProject(options.projectPath, text, signal);
187334
+ const result = await embedTextForProject(options.projectPath, text, signal, "query");
186739
187335
  return result?.vector ?? null;
186740
187336
  },
186741
187337
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
@@ -186916,6 +187512,51 @@ function planEmergencyDrop(input) {
186916
187512
  };
186917
187513
  }
186918
187514
 
187515
+ // src/hooks/magic-context/system-injection-stripper.ts
187516
+ var SYSTEM_INJECTION_MARKERS = [
187517
+ "<!-- OMO_INTERNAL_INITIATOR -->",
187518
+ "[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
187519
+ "[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
187520
+ "[Category+Skill Reminder]",
187521
+ "[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
187522
+ "[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
187523
+ "[EMERGENCY CONTEXT WINDOW WARNING]",
187524
+ "Unstable background agent appears idle",
187525
+ "**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
187526
+ ];
187527
+ var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
187528
+ var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
187529
+ function stripSystemInjection(text) {
187530
+ let hasInjection = false;
187531
+ for (const marker of SYSTEM_INJECTION_MARKERS) {
187532
+ if (text.includes(marker)) {
187533
+ hasInjection = true;
187534
+ break;
187535
+ }
187536
+ }
187537
+ if (SYSTEM_REMINDER_REGEX.test(text))
187538
+ hasInjection = true;
187539
+ SYSTEM_REMINDER_REGEX.lastIndex = 0;
187540
+ if (!hasInjection)
187541
+ return null;
187542
+ let cleaned = text;
187543
+ cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
187544
+ cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
187545
+ cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
187546
+ for (const marker of SYSTEM_INJECTION_MARKERS) {
187547
+ if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
187548
+ continue;
187549
+ const idx = cleaned.indexOf(marker);
187550
+ if (idx === -1)
187551
+ continue;
187552
+ const blockEnd = cleaned.indexOf(`
187553
+
187554
+ `, idx + marker.length);
187555
+ cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
187556
+ }
187557
+ return cleaned.trim();
187558
+ }
187559
+
186919
187560
  // src/hooks/magic-context/heuristic-cleanup.ts
186920
187561
  init_tag_part_guards();
186921
187562
  var DEDUP_SAFE_TOOLS = new Set([
@@ -186934,6 +187575,7 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
186934
187575
  const maxTag = getMaxTagNumberBySession(db, sessionId);
186935
187576
  const protectedCutoff = maxTag - config2.protectedTags;
186936
187577
  let droppedTools = 0;
187578
+ let emergencyDroppedTools = 0;
186937
187579
  let deduplicatedTools = 0;
186938
187580
  let droppedInjections = 0;
186939
187581
  if (config2.emergency) {
@@ -186965,6 +187607,7 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
186965
187607
  updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
186966
187608
  updateTagDropMode(db, sessionId, tag.tagNumber, "full");
186967
187609
  droppedTools++;
187610
+ emergencyDroppedTools++;
186968
187611
  }
186969
187612
  }
186970
187613
  setEmergencyDropSample(db, sessionId, emergency.currentTotalInputTokens);
@@ -187043,7 +187686,9 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
187043
187686
  continue;
187044
187687
  updateTagDropMode(db, sessionId, tag.tagNumber, "full");
187045
187688
  updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
187046
- deduplicatedTools++;
187689
+ if (result === "removed" || result === "truncated") {
187690
+ deduplicatedTools++;
187691
+ }
187047
187692
  }
187048
187693
  }
187049
187694
  })();
@@ -187052,6 +187697,7 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
187052
187697
  sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
187053
187698
  }
187054
187699
  let compressedTextTags = 0;
187700
+ let mutatedTextTags = 0;
187055
187701
  if (config2.caveman?.enabled) {
187056
187702
  const cavemanResult = applyCavemanCleanup(sessionId, db, targets, tags, {
187057
187703
  enabled: true,
@@ -187059,8 +187705,16 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
187059
187705
  protectedTags: config2.protectedTags
187060
187706
  });
187061
187707
  compressedTextTags = cavemanResult.compressedToLite + cavemanResult.compressedToFull + cavemanResult.compressedToUltra;
187708
+ mutatedTextTags = cavemanResult.mutatedTextTags;
187062
187709
  }
187063
- return { droppedTools, deduplicatedTools, droppedInjections, compressedTextTags };
187710
+ return {
187711
+ droppedTools,
187712
+ deduplicatedTools,
187713
+ droppedInjections,
187714
+ emergencyDroppedTools,
187715
+ compressedTextTags,
187716
+ mutatedTextTags
187717
+ };
187064
187718
  }
187065
187719
  function extractToolInfo(part) {
187066
187720
  if (part.type === "tool" && typeof part.tool === "string" && DEDUP_SAFE_TOOLS.has(part.tool)) {
@@ -187234,6 +187888,42 @@ function isTodoItem(value) {
187234
187888
  return typeof todo.content === "string" && typeof todo.status === "string" && (todo.priority === undefined || typeof todo.priority === "string");
187235
187889
  }
187236
187890
 
187891
+ // src/hooks/magic-context/tool-reclaim.ts
187892
+ await init_storage();
187893
+ function buildSyntheticToolReclaimOps(input) {
187894
+ const watermark = Math.max(0, input.watermark);
187895
+ if (watermark <= 0)
187896
+ return [];
187897
+ const realPendingTagIds = new Set((input.pendingOps ?? []).map((op) => op.tagId));
187898
+ const tags = getActiveTagsBySession(input.db, input.sessionId);
187899
+ const synthetic = [];
187900
+ for (const tag of tags) {
187901
+ if (tag.type !== "tool")
187902
+ continue;
187903
+ if (tag.status !== "active")
187904
+ continue;
187905
+ if (tag.tagNumber > watermark)
187906
+ continue;
187907
+ if (realPendingTagIds.has(tag.tagNumber))
187908
+ continue;
187909
+ if (input.targets.get(tag.tagNumber)?.canDrop?.() !== true)
187910
+ continue;
187911
+ synthetic.push({
187912
+ id: 0,
187913
+ sessionId: input.sessionId,
187914
+ tagId: tag.tagNumber,
187915
+ operation: "drop",
187916
+ queuedAt: 0
187917
+ });
187918
+ }
187919
+ return synthetic;
187920
+ }
187921
+ function advanceToolReclaimWatermarkToCurrentMax(db, sessionId) {
187922
+ const maxTagNumber = getMaxTagNumberBySession(db, sessionId);
187923
+ advanceToolReclaimWatermark(db, sessionId, maxTagNumber);
187924
+ return maxTagNumber;
187925
+ }
187926
+
187237
187927
  // src/hooks/magic-context/transform-postprocess-phase.ts
187238
187928
  var DEGRADE_CACHE_WARNING_THRESHOLD = 10;
187239
187929
  var degradedCacheCountBySession = new BoundedSessionMap(100);
@@ -187250,7 +187940,6 @@ async function runPostTransformPhase(args) {
187250
187940
  const emergencyDropEligible = args.contextUsage.percentage >= args.forceMaterializationPercentage;
187251
187941
  const activeCompartmentRun = args.canRunCompartments ? getActiveCompartmentRun(args.sessionId) : undefined;
187252
187942
  const compartmentRunning = args.canRunCompartments && !args.awaitedCompartmentRun && activeCompartmentRun !== undefined;
187253
- const emergencyBypassCompartmentGate = forceMaterialization;
187254
187943
  const deferredMaterialize = args.canConsumeDeferredLate && deferredMaterializationWasPending;
187255
187944
  const materializationRequested = isExplicitFlush || deferredMaterialize;
187256
187945
  const m0M1EnabledForFold = args.fullFeatureMode && args.m0M1 !== undefined && (!!args.m0M1.projectPath || !!args.m0M1.projectDirectory);
@@ -187262,11 +187951,12 @@ async function runPostTransformPhase(args) {
187262
187951
  projectDirectory: args.m0M1.projectDirectory,
187263
187952
  hardSignals: args.m0M1.hardSignals
187264
187953
  }).value : false;
187954
+ const bypassCompartmentGate = forceMaterialization || m0HardFoldThisPass;
187265
187955
  const shouldReadPendingOps = materializationRequested || args.schedulerDecision === "execute" || forceMaterialization || m0HardFoldThisPass || compartmentRunning;
187266
187956
  const pendingOps = shouldReadPendingOps ? getPendingOps(args.db, args.sessionId) : [];
187267
187957
  const hasPendingUserOps = pendingOps.length > 0;
187268
- const shouldApplyPendingOps = (args.schedulerDecision === "execute" || materializationRequested || forceMaterialization || m0HardFoldThisPass) && (!compartmentRunning || emergencyBypassCompartmentGate);
187269
- const shouldRunHeuristics = (!compartmentRunning || emergencyBypassCompartmentGate) && (materializationRequested || forceMaterialization || m0HardFoldThisPass || emergencyDropEligible || args.schedulerDecision === "execute" && (!alreadyRanThisTurn || !args.fullFeatureMode));
187958
+ const shouldApplyPendingOps = (args.schedulerDecision === "execute" || materializationRequested || forceMaterialization || m0HardFoldThisPass) && (!compartmentRunning || bypassCompartmentGate);
187959
+ const shouldRunHeuristics = (!compartmentRunning || bypassCompartmentGate) && (materializationRequested || forceMaterialization || m0HardFoldThisPass || emergencyDropEligible || args.schedulerDecision === "execute" && (!alreadyRanThisTurn || !args.fullFeatureMode));
187270
187960
  const isCacheBustingPass = shouldApplyPendingOps || shouldRunHeuristics;
187271
187961
  const canUseEmptySentinels = modelAcceptsEmptyContent(args.resolvedProviderID);
187272
187962
  if (shouldRunHeuristics) {
@@ -187278,8 +187968,9 @@ async function runPostTransformPhase(args) {
187278
187968
  sessionLog(args.sessionId, `transform: skipping heuristics (already ran for turn ${args.currentTurnId})`);
187279
187969
  }
187280
187970
  if (compartmentRunning && hasPendingUserOps) {
187281
- if (emergencyBypassCompartmentGate) {
187282
- sessionLog(args.sessionId, `transform: emergency bypass applying ${pendingOps.length} pending ops while compartment agent runs (${args.contextUsage.percentage.toFixed(1)}%)`);
187971
+ if (bypassCompartmentGate) {
187972
+ const bypassReason = forceMaterialization ? "emergency >=85%" : "m0 hard fold";
187973
+ sessionLog(args.sessionId, `transform: compartment-gate bypass (${bypassReason}) — applying ${pendingOps.length} pending ops while compartment agent runs (${args.contextUsage.percentage.toFixed(1)}%)`);
187283
187974
  } else {
187284
187975
  sessionLog(args.sessionId, "transform: deferring pending ops — compartment agent in progress");
187285
187976
  }
@@ -187288,12 +187979,24 @@ async function runPostTransformPhase(args) {
187288
187979
  let deferredMaterializedSuccessfully = false;
187289
187980
  let heuristicsRanSuccessfully = false;
187290
187981
  let pendingOpsRanSuccessfully = false;
187982
+ let pendingOpsDidMutate = false;
187983
+ let heuristicOrReasoningDidMutate = false;
187984
+ let droppedCount = 0;
187985
+ const droppedTokens = 0;
187986
+ let emergency = false;
187987
+ let m0RematerializedThisPass = false;
187988
+ let m0MaterializeReason = null;
187989
+ let m0M1InjectedThisPass = false;
187990
+ let autoReclaimDidMutateThisPass = false;
187291
187991
  try {
187292
187992
  if (shouldApplyPendingOps) {
187293
187993
  const applyReason = isExplicitFlush ? "explicit_flush" : deferredMaterialize ? "deferred_materialization" : `scheduler_execute (scheduler=${args.schedulerDecision})`;
187294
187994
  sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOps.length}, context=${args.contextUsage.percentage.toFixed(1)}%`);
187295
187995
  const tApply = performance.now();
187296
- applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, pendingOps);
187996
+ pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, pendingOps);
187997
+ if (pendingOpsDidMutate) {
187998
+ droppedCount += pendingOps.length;
187999
+ }
187297
188000
  logTransformTiming(args.sessionId, "applyPendingOperations", tApply);
187298
188001
  }
187299
188002
  if (shouldRunHeuristics) {
@@ -187311,9 +188014,12 @@ async function runPostTransformPhase(args) {
187311
188014
  } : undefined,
187312
188015
  caveman: cavemanConfig
187313
188016
  }, heuristicTags);
187314
- logTransformTiming(args.sessionId, "applyHeuristicCleanup", t5, `droppedTools=${cleanup.droppedTools} deduplicatedTools=${cleanup.deduplicatedTools} droppedInjections=${cleanup.droppedInjections} compressedTextTags=${cleanup.compressedTextTags}`);
188017
+ logTransformTiming(args.sessionId, "applyHeuristicCleanup", t5, `droppedTools=${cleanup.droppedTools} deduplicatedTools=${cleanup.deduplicatedTools} droppedInjections=${cleanup.droppedInjections} compressedTextTags=${cleanup.compressedTextTags} mutatedTextTags=${cleanup.mutatedTextTags}`);
188018
+ const heuristicMutationCount = cleanup.droppedTools + cleanup.deduplicatedTools + cleanup.droppedInjections + cleanup.mutatedTextTags;
188019
+ droppedCount += cleanup.droppedTools + cleanup.deduplicatedTools + cleanup.droppedInjections + cleanup.mutatedTextTags;
188020
+ emergency ||= cleanup.emergencyDroppedTools > 0;
187315
188021
  const t7 = performance.now();
187316
- const clearedReasoning = clearOldReasoning(args.messages, args.reasoningByMessage, args.messageTagNumbers, args.clearReasoningAge);
188022
+ const clearedReasoning = canUseEmptySentinels ? clearOldReasoning(args.messages, args.reasoningByMessage, args.messageTagNumbers, args.clearReasoningAge) : 0;
187317
188023
  if (canUseEmptySentinels) {
187318
188024
  stripClearedReasoning(args.messages);
187319
188025
  }
@@ -187337,6 +188043,8 @@ async function runPostTransformPhase(args) {
187337
188043
  }
187338
188044
  }
187339
188045
  logTransformTiming(args.sessionId, "clearOldReasoning", t7);
188046
+ heuristicOrReasoningDidMutate = heuristicMutationCount + clearedReasoning + strippedInline > 0;
188047
+ droppedCount += clearedReasoning + strippedInline;
187340
188048
  if (pendingMaterializationAtPassStart) {
187341
188049
  args.pendingMaterializationSessions.delete(args.sessionId);
187342
188050
  }
@@ -187347,7 +188055,35 @@ async function runPostTransformPhase(args) {
187347
188055
  if (args.schedulerDecision === "execute" && !materializationRequested) {
187348
188056
  updateSessionMeta(args.db, args.sessionId, { lastResponseTime: Date.now() });
187349
188057
  }
188058
+ const toolReclaimExecutePass = args.schedulerDecision === "execute";
188059
+ const alreadyMutatingThisPass = pendingOpsDidMutate || heuristicOrReasoningDidMutate;
188060
+ let autoReclaimTargetCount = 0;
188061
+ let autoReclaimDidMutate = false;
188062
+ if (toolReclaimExecutePass && alreadyMutatingThisPass && !emergencyDropEligible) {
188063
+ const syntheticPendingOps = buildSyntheticToolReclaimOps({
188064
+ db: args.db,
188065
+ sessionId: args.sessionId,
188066
+ targets: args.targets,
188067
+ watermark: args.sessionMeta.toolReclaimWatermark ?? 0,
188068
+ pendingOps
188069
+ });
188070
+ autoReclaimTargetCount = syntheticPendingOps.length;
188071
+ if (syntheticPendingOps.length > 0) {
188072
+ autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, [], syntheticPendingOps);
188073
+ if (autoReclaimDidMutate) {
188074
+ droppedCount += syntheticPendingOps.length;
188075
+ autoReclaimDidMutateThisPass = true;
188076
+ }
188077
+ }
188078
+ }
187350
188079
  args.batch?.finalize();
188080
+ if (toolReclaimExecutePass) {
188081
+ const maxTagNumber = advanceToolReclaimWatermarkToCurrentMax(args.db, args.sessionId);
188082
+ args.sessionMeta.toolReclaimWatermark = Math.max(args.sessionMeta.toolReclaimWatermark ?? 0, maxTagNumber);
188083
+ }
188084
+ if (autoReclaimTargetCount > 0) {
188085
+ sessionLog(args.sessionId, `tool reclaim auto-drop: targets=${autoReclaimTargetCount} mutated=${autoReclaimDidMutate}`);
188086
+ }
187351
188087
  logTransformTiming(args.sessionId, "batchFinalize:heuristics", performance.now());
187352
188088
  if (args.sessionMeta.lastTransformError !== null) {
187353
188089
  updateSessionMeta(args.db, args.sessionId, { lastTransformError: null });
@@ -187359,11 +188095,6 @@ async function runPostTransformPhase(args) {
187359
188095
  deferredMaterializedSuccessfully = true;
187360
188096
  heuristicsRanSuccessfully = true;
187361
188097
  }
187362
- if (args.watermark > 0) {
187363
- const tWatermarkCleanup = performance.now();
187364
- truncateErroredTools(args.messages, args.watermark, args.messageTagNumbers);
187365
- logTransformTiming(args.sessionId, "watermarkCleanup", tWatermarkCleanup);
187366
- }
187367
188098
  if (shouldApplyPendingOps) {
187368
188099
  pendingOpsRanSuccessfully = true;
187369
188100
  }
@@ -187422,6 +188153,9 @@ async function runPostTransformPhase(args) {
187422
188153
  hardSignals: args.m0M1.hardSignals
187423
188154
  });
187424
188155
  if (result.injected) {
188156
+ m0M1InjectedThisPass = true;
188157
+ m0RematerializedThisPass ||= result.m0RematerializedThisPass;
188158
+ m0MaterializeReason = result.decision.reason ?? m0MaterializeReason;
187425
188159
  sessionLog(args.sessionId, `transform: injected m[0]/m[1] (rematerialized=${result.m0RematerializedThisPass}, reason=${result.decision.reason ?? "cache_hit"})`);
187426
188160
  }
187427
188161
  } catch (error51) {
@@ -187630,7 +188364,19 @@ async function runPostTransformPhase(args) {
187630
188364
  sessionLog(args.sessionId, `sticky-injection GC: pruned ${prunedAnchors} note-nudge anchor(s), ${prunedDecisions} auto-search decision(s)`);
187631
188365
  }
187632
188366
  }
187633
- return { explicitMaterializedSuccessfully, deferredMaterializedSuccessfully };
188367
+ const materializeReason = m0MaterializeReason ?? (explicitMaterializedSuccessfully ? "explicit_flush" : null);
188368
+ const materialized = m0RematerializedThisPass || explicitMaterializedSuccessfully || deferredMaterializedSuccessfully;
188369
+ const bustedThisPass = args.didMutateFromFlushedStatuses || pendingOpsDidMutate || heuristicOrReasoningDidMutate || autoReclaimDidMutateThisPass || m0RematerializedThisPass || m0M1InjectedThisPass && historyWasConsumedThisPass || historyWasConsumedThisPass;
188370
+ return {
188371
+ explicitMaterializedSuccessfully,
188372
+ deferredMaterializedSuccessfully,
188373
+ materialized,
188374
+ materializeReason,
188375
+ droppedTokens,
188376
+ droppedCount,
188377
+ emergency,
188378
+ bustedThisPass
188379
+ };
187634
188380
  }
187635
188381
  function checkM0MutationDriftAndSignal(args) {
187636
188382
  const currentMaxMutationId = getMaxM0MutationId(args.db, args.sessionId) ?? 0;
@@ -187665,6 +188411,12 @@ function clearMessageTokensCache(sessionId, messageId) {
187665
188411
  cache.delete(messageId);
187666
188412
  }
187667
188413
  var recordedSessionProjectIdentity = new BoundedSessionMap(MESSAGE_TOKENS_CACHE_MAX);
188414
+ function deriveTaggerLoadFloor(messages, sessionId, db) {
188415
+ return deriveTagLoadFloor(db, sessionId, function* () {
188416
+ for (const message of messages)
188417
+ yield message.info?.id;
188418
+ }());
188419
+ }
187668
188420
  function findLastAssistantModel2(messages) {
187669
188421
  for (let i = messages.length - 1;i >= 0; i--) {
187670
188422
  const info = messages[i].info;
@@ -187687,6 +188439,7 @@ function createTransform(deps) {
187687
188439
  return;
187688
188440
  }
187689
188441
  const resolvedSessionId = sessionId;
188442
+ clearOpenCodePendingTransformDecision(sessionId);
187690
188443
  logTransformTiming(sessionId, "findSessionId", startTime, `messages=${messages.length}`);
187691
188444
  const db = deps.db;
187692
188445
  if (deps.client !== undefined) {
@@ -187989,12 +188742,16 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187989
188742
  recordSessionProjectIdentity(db, sessionId, sessionProjectIdentity);
187990
188743
  recordedSessionProjectIdentity.set(sessionId, sessionProjectIdentity);
187991
188744
  }
188745
+ const taggerFloor = deriveTaggerLoadFloor(messages, sessionId, db);
188746
+ if (taggerFloor === 0 && messages.length > 0) {
188747
+ sessionLog(sessionId, `tag floor: 0 (full-scan fallback) — no leading wire message resolved a tag across ${messages.length} msgs`);
188748
+ }
187992
188749
  let triggerBoundarySnapshot;
187993
188750
  if (fullFeatureMode && historianRunnable && !sessionMeta.compartmentInProgress) {
187994
188751
  const tTrigger = performance.now();
187995
188752
  try {
187996
188753
  const inMemoryTail = buildTriggerInMemoryTail(db, sessionId, extractInMemoryMessageViews(messages));
187997
- const triggerResult = checkCompartmentTrigger(db, sessionId, sessionMeta, boundaryUsageForProtectedTail, sessionMeta.lastContextPercentage, boundaryExecuteThreshold, deriveTriggerBudget(boundaryContextLimit, boundaryExecuteThreshold), deps.clearReasoningAge, deps.commitClusterTrigger, undefined, boundaryContextLimit, inMemoryTail);
188754
+ const triggerResult = checkCompartmentTrigger(db, sessionId, sessionMeta, boundaryUsageForProtectedTail, sessionMeta.lastContextPercentage, boundaryExecuteThreshold, deriveTriggerBudget(boundaryContextLimit, boundaryExecuteThreshold), deps.clearReasoningAge, deps.commitClusterTrigger, undefined, boundaryContextLimit, inMemoryTail, taggerFloor);
187998
188755
  if (triggerResult.shouldFire) {
187999
188756
  sessionLog(sessionId, `compartment trigger: firing (reason=${triggerResult.reason})`);
188000
188757
  updateSessionMeta(db, sessionId, { compartmentInProgress: true });
@@ -188036,7 +188793,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
188036
188793
  try {
188037
188794
  const t0 = performance.now();
188038
188795
  const tInitFromDb = performance.now();
188039
- deps.tagger.initFromDb(sessionId, db);
188796
+ deps.tagger.initFromDb(sessionId, db, taggerFloor);
188040
188797
  logTransformTiming(sessionId, "tag.initFromDb", tInitFromDb);
188041
188798
  const skipPrefixInjection = !ctxReduceEnabledEffective;
188042
188799
  const result = tagMessages(sessionId, messages, deps.tagger, db, {
@@ -188088,7 +188845,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
188088
188845
  const persistedReasoningWatermark = sessionMeta?.clearedReasoningThroughTag ?? 0;
188089
188846
  if (persistedReasoningWatermark > 0) {
188090
188847
  const tReplay = performance.now();
188091
- const replayed = replayClearedReasoning(messages, reasoningByMessage, messageTagNumbers, persistedReasoningWatermark);
188848
+ const replayed = canUseEmptySentinels ? replayClearedReasoning(messages, reasoningByMessage, messageTagNumbers, persistedReasoningWatermark) : 0;
188092
188849
  const replayedInline = replayStrippedInlineThinking(messages, messageTagNumbers, persistedReasoningWatermark);
188093
188850
  if (replayed > 0 || replayedInline > 0) {
188094
188851
  sessionLog(sessionId, `reasoning replay: cleared=${replayed} inlineStripped=${replayedInline} (watermark=${persistedReasoningWatermark})`);
@@ -188186,7 +188943,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
188186
188943
  const wasEmergencyBlock = contextUsageEarly.percentage >= FORCE_MATERIALIZE_PERCENTAGE && compartmentPhase.justAwaitedPublication;
188187
188944
  const historyRebuiltThisPass = wasEmergencyBlock ? compartmentPhase.rebuiltHistoryThisPass : rebuiltHistoryFromInitialPrepare || compartmentPhase.rebuiltHistoryThisPass;
188188
188945
  const tPostProcess = performance.now();
188189
- await runPostTransformPhase({
188946
+ const postTransformResult = await runPostTransformPhase({
188190
188947
  sessionId,
188191
188948
  db,
188192
188949
  messages,
@@ -188240,6 +188997,19 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
188240
188997
  hardSignals: m0HardSignals
188241
188998
  }
188242
188999
  });
189000
+ if (postTransformResult.bustedThisPass) {
189001
+ recordPendingTransformDecision(sessionId, {
189002
+ tsMs: Date.now(),
189003
+ decision: schedulerDecision,
189004
+ materialized: postTransformResult.materialized,
189005
+ materializeReason: normalizeMaterializeReason("opencode", postTransformResult.materializeReason, postTransformResult.materialized),
189006
+ emergency: postTransformResult.emergency,
189007
+ droppedTokens: postTransformResult.droppedTokens,
189008
+ droppedCount: postTransformResult.droppedCount,
189009
+ inputTokens: contextUsage.inputTokens,
189010
+ bustedThisPass: true
189011
+ });
189012
+ }
188243
189013
  logTransformTiming(sessionId, "postTransformPhase", tPostProcess);
188244
189014
  const msgTokens = getMessageTokensCache(sessionId);
188245
189015
  let storedByMessage;
@@ -188390,6 +189160,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
188390
189160
  const executeThresholdTokens = Math.round((resolvedContextLimit ?? 0) * resolvedExecuteThresholdPct / 100);
188391
189161
  const usableTokens = Math.max(0, executeThresholdTokens - contextUsage.inputTokens + liveTailTokens);
188392
189162
  resetLastNudgeCycleIfTailShrank(db, sessionId, tailToolTokens);
189163
+ const oldestReclaimableToolTags = getOldestActiveUnprotectedToolTags(db, sessionId, deps.protectedTags);
188393
189164
  deps.channel1StateBySession.set(sessionId, {
188394
189165
  tailToolTokens,
188395
189166
  historyBudgetTokens: historyBudgetTokens ?? 0,
@@ -188398,9 +189169,10 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
188398
189169
  lastInputTokens: contextUsage.inputTokens,
188399
189170
  turnToolTokens: 0,
188400
189171
  usableTokens,
188401
- reducedSinceRefresh: false
189172
+ reducedSinceRefresh: false,
189173
+ oldestReclaimableToolTags
188402
189174
  });
188403
- const channel2MetricsKnown = fullFeatureMode && resolvedContextLimit !== undefined && resolvedContextLimit > 0 && resolvedExecuteThresholdPct > 0;
189175
+ const channel2MetricsKnown = resolvedContextLimit !== undefined && resolvedContextLimit > 0 && resolvedExecuteThresholdPct > 0;
188404
189176
  if (channel2MetricsKnown) {
188405
189177
  const channel2ShouldTrigger = shouldTriggerChannel2({
188406
189178
  reclaimableTokens: tailToolTokens,
@@ -188424,6 +189196,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
188424
189196
  }
188425
189197
  const elapsed = (performance.now() - startTime).toFixed(1);
188426
189198
  sessionLog(sessionId, `transform completed in ${elapsed}ms (${messages.length} messages, ${targets.size} targets, watermark: ${watermark})`);
189199
+ deps.maybeAutoEmbedSession?.(sessionId);
188427
189200
  };
188428
189201
  }
188429
189202
  function resolveHistoryBudgetTokens(historyBudgetPercentage, contextUsage, executeThresholdPercentage, modelKey, executeThresholdTokens, resolvedContextLimit) {
@@ -188469,18 +189242,14 @@ function evictExpiredUsageEntries(contextUsageMap) {
188469
189242
  }
188470
189243
  async function deliverChannel2IfPending(deps, sessionId) {
188471
189244
  try {
188472
- try {
188473
- const meta3 = getOrCreateSessionMeta(deps.db, sessionId);
188474
- if (meta3.isSubagent)
188475
- return;
188476
- } catch {}
188477
189245
  const baseline = deps.channel1StateBySession?.get(sessionId);
188478
189246
  await maybeDeliverChannel2(sessionId, {
188479
189247
  db: deps.db,
188480
189248
  serverUrl: deps.serverUrl,
188481
189249
  directory: deps.directory ?? ".",
188482
189250
  reclaimableTokens: baseline ? baseline.tailToolTokens + baseline.turnToolTokens : undefined,
188483
- usableTokens: baseline?.usableTokens
189251
+ usableTokens: baseline?.usableTokens,
189252
+ oldestReclaimableToolTags: baseline?.oldestReclaimableToolTags
188484
189253
  });
188485
189254
  } catch (error51) {
188486
189255
  sessionLog(sessionId, "channel2 delivery wrapper failed (ignored):", error51);
@@ -188622,6 +189391,15 @@ function createEventHandler2(deps) {
188622
189391
  info.tokens?.cache?.write
188623
189392
  ];
188624
189393
  const hasUsageTokens = usageTokens.some((value) => typeof value === "number" && value > 0);
189394
+ const terminalAssistantUpdate = info.messageID !== undefined && hasUsageTokens && (typeof info.finish === "string" || typeof info.completedAt === "number");
189395
+ if (terminalAssistantUpdate && info.messageID) {
189396
+ scheduleOpenCodeTransformDecisionWrite({
189397
+ db: deps.db,
189398
+ sessionId: info.sessionID,
189399
+ messageId: info.messageID,
189400
+ inputTokens: (info.tokens?.input ?? 0) + (info.tokens?.cache?.read ?? 0) + (info.tokens?.cache?.write ?? 0)
189401
+ });
189402
+ }
188625
189403
  sessionLog(info.sessionID, `event message.updated: provider=${info.providerID} model=${info.modelID} hasUsageTokens=${hasUsageTokens} tokens.input=${info.tokens?.input} cache.read=${info.tokens?.cache?.read} cache.write=${info.tokens?.cache?.write}`);
188626
189404
  const hasKnownUsage = hasUsageTokens || deps.contextUsageMap.has(info.sessionID);
188627
189405
  if (!hasKnownUsage) {
@@ -188779,6 +189557,7 @@ function createEventHandler2(deps) {
188779
189557
  deps.onSessionDeleted?.(sessionId);
188780
189558
  deps.contextUsageMap.delete(sessionId);
188781
189559
  deps.tagger.cleanup(sessionId);
189560
+ clearTransformDecisionSession(sessionId);
188782
189561
  clearMessageTokensCache(sessionId);
188783
189562
  invalidateTrueRawTokenCache({ sessionId, reason: "session.deleted" });
188784
189563
  return;
@@ -188786,6 +189565,46 @@ function createEventHandler2(deps) {
188786
189565
  };
188787
189566
  }
188788
189567
 
189568
+ // src/hooks/magic-context/format-embed-status.ts
189569
+ function formatEmbedStatusText(coverage, drain) {
189570
+ if (!coverage.enabled) {
189571
+ return "Embedding is off (no provider configured).";
189572
+ }
189573
+ const lines = [];
189574
+ lines.push(`Embedding — model: ${coverage.model} (${coverage.provider})`);
189575
+ lines.push(`This session: ${coverage.session.embedded} / ${coverage.session.total} compartments embedded`);
189576
+ lines.push(`Project memories: ${coverage.memories.embedded} / ${coverage.memories.total} embedded`);
189577
+ if (coverage.commits.gitEnabled) {
189578
+ lines.push(`Git commits: ${coverage.commits.embedded} / ${coverage.commits.total}`);
189579
+ } else {
189580
+ lines.push("Git commits: 0 / 0 (git indexing off)");
189581
+ }
189582
+ let drainLine = "Drain: idle";
189583
+ switch (drain.status) {
189584
+ case "running": {
189585
+ const e = drain.embedded ?? coverage.session.embedded;
189586
+ const t = drain.total ?? coverage.session.total;
189587
+ const failedSuffix = drain.failed && drain.failed > 0 ? ` (${drain.failed} failed)` : "";
189588
+ drainLine = `Drain: running ${e}/${t}${failedSuffix}`;
189589
+ break;
189590
+ }
189591
+ case "paused": {
189592
+ const e = drain.embedded ?? coverage.session.embedded;
189593
+ const t = drain.total ?? coverage.session.total;
189594
+ drainLine = `Drain: paused ${e}/${t}`;
189595
+ break;
189596
+ }
189597
+ case "stopped":
189598
+ drainLine = "Drain: stopped (provider down)";
189599
+ break;
189600
+ default:
189601
+ drainLine = "Drain: idle";
189602
+ }
189603
+ lines.push(drainLine);
189604
+ return lines.join(`
189605
+ `);
189606
+ }
189607
+
188789
189608
  // src/hooks/magic-context/hook.ts
188790
189609
  await __promiseAll([
188791
189610
  init_inject_compartments(),
@@ -189003,7 +189822,7 @@ function maybeInjectChannel1Nudge(args, sessionId, tool, output) {
189003
189822
  setLastNudgeLevel(args.db, sessionId, decision.nextLastNudgeLevel);
189004
189823
  if (!decision.fire)
189005
189824
  return;
189006
- out.output += buildChannel1Reminder(decision.level, decision.undroppedTokens);
189825
+ out.output += buildChannel1Reminder(decision.level, decision.undroppedTokens, state.oldestReclaimableToolTags);
189007
189826
  sessionLog(sessionId, `channel1 nudge fired: level=${decision.level} undropped~${Math.round(decision.undroppedTokens / 1000)}k tool=${tool}`);
189008
189827
  }
189009
189828
  function createToolExecuteAfterHook(args) {
@@ -189075,9 +189894,7 @@ Context is managed for you entirely automatically — there's nothing to prune a
189075
189894
  var CTX_NOTE_GUIDANCE = `Use \`ctx_note\` ONLY for genuinely future concerns — something to revisit much later, not work coming up in the next few turns (that's already in your active context) and not active multi-step work (use todos for that). Magic Context preserves your full context across both compaction and restarts, so an upcoming restart or "let's come back to this later" is never a reason to take a note — nothing is lost either way. Notes you do take survive compression and resurface at natural work boundaries (after commits, historian runs, todo completion).`;
189076
189895
  var TOOL_HISTORY_GUIDANCE = `Compressed history intentionally omits tool calls and their outputs — summaries like "I edited file X" are historian records, not patterns to replicate. In the live conversation, older tool calls and their results are cleaned up to save context — you may see your own past messages referencing actions without the corresponding tool call or result visible. This is normal context management. ALWAYS use real tool calls; never simulate, fabricate, or inline tool outputs in your text. If there is no tool result message, the action did not happen. NEVER simulate, hallucinate or claim tool calls, command output, search results, file edits, or diffs in plain text as if they actually occurred.`;
189077
189896
  var BASE_INTRO = (protectedTags) => `Messages and tool outputs are tagged with §N§ identifiers (e.g., §1§, §42§).
189078
- Use \`ctx_reduce\` to manage context size. It supports one operation:
189079
- - \`drop\`: Remove entirely (best for tool outputs you already acted on).
189080
- Syntax: "3-5", "1,2,9", or "1-5,8,12-15". Last ${protectedTags} tags are protected.
189897
+ Use \`ctx_reduce\` to mark spent tagged content as discardable and reclaim space. Marking is NOT an immediate delete — it queues the content, which stays fully visible until space is actually needed (as soon as the next turn if you're already under pressure, much later if not), so mark a tool output as soon as you're done with it rather than hoarding the call for the end of the turn. The last ${protectedTags} tags are protected (marking one just queues it until it ages out). Syntax: "3-5", "1,2,9", or "1-5,8,12-15".
189081
189898
  Do not announce or narrate \`ctx_reduce\` drops — just call the tool silently. Saying "I'll drop these outputs" wastes tokens the user does not care about.
189082
189899
  ${CTX_NOTE_GUIDANCE}
189083
189900
  Use \`ctx_memory\` for durable project knowledge: write what future sessions must know, update/archive/merge the memories you see in \`<project-memory>\` when they drift. Memories persist across sessions and every new session starts with them.
@@ -189096,7 +189913,7 @@ Use \`ctx_expand\` to recover the raw conversation behind a \`<compartment>\` su
189096
189913
  \`ctx_search\` returns ranked results from memories, git commits, and raw message history. Use message ordinals from results with \`ctx_expand\` to retrieve surrounding conversation context.
189097
189914
  ${TOOL_HISTORY_GUIDANCE}
189098
189915
  NEVER drop large ranges blindly (e.g., "1-50"). Review each tag before deciding.
189099
- NEVER drop user messagesthey are short and will be summarized by compartmentalization automatically. Dropping them loses context the historian needs.
189916
+ Keep your user's instructions and intent never drop a user message for its directive, even an old one. But a large block of pasted content inside a user message (logs, data dumps, long code, attachments) is fair to mark discardable once you've extracted what you need — it stays searchable via \`ctx_search\`.
189100
189917
  NEVER drop assistant text messages unless they are exceptionally large. Your conversation messages are lightweight; only large tool outputs are worth dropping.
189101
189918
  Before your turn finishes, consider using \`ctx_reduce\` to drop large tool outputs you no longer need.`;
189102
189919
  var BASE_INTRO_NO_REDUCE = () => `${CTX_NOTE_GUIDANCE}
@@ -189444,7 +190261,7 @@ function createMagicContextHook(deps) {
189444
190261
  }
189445
190262
  let lastScheduleCheckMs = 0;
189446
190263
  const getHistorianChunkTokens = () => deriveHistorianChunkTokens(resolveHistorianContextLimit(deps.config.historian?.model));
189447
- const historianFallbackModels = resolveFallbackChain(HISTORIAN_AGENT, deps.config.historian?.fallback_models);
190264
+ const historianFallbackModels = resolveFallbackChain(deps.config.historian?.fallback_models);
189448
190265
  const historyRefreshSessions = deps.liveSessionState?.historyRefreshSessions ?? new Set;
189449
190266
  const deferredHistoryRefreshSessions = deps.liveSessionState?.deferredHistoryRefreshSessions ?? new Set;
189450
190267
  try {
@@ -189517,29 +190334,55 @@ function createMagicContextHook(deps) {
189517
190334
  ensureProjectRegistered: ensureProjectRegisteredFromOpenCodeDirectory,
189518
190335
  getNotificationParams: (sid) => getLiveNotificationParams(sid, liveModelBySession, variantBySession, agentBySession)
189519
190336
  });
189520
- const executeEmbedHistory = async (sessionId) => {
190337
+ const executeEmbedHistory = async (sessionId, options) => {
189521
190338
  if (deps.config.memory?.enabled === false) {
189522
190339
  return "Memory is disabled for this project, so there is no semantic embedding to backfill.";
189523
190340
  }
189524
190341
  const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
190342
+ const active = embedRunStateBySession.get(sessionId);
190343
+ if (active && !active.signal.aborted && !options?.signal) {
190344
+ return "Embedding is already running for this session.";
190345
+ }
189525
190346
  await ensureProjectRegisteredFromOpenCodeDirectory(directory, db);
189526
190347
  const sessionProjectIdentity = resolveProjectIdentity(directory);
189527
- setRecompStarting({ recompProgressBySession }, sessionId, "Embedding history…", "embed");
189528
- const outcome = await embedSessionCompartmentChunks(db, sessionProjectIdentity, sessionId, {
189529
- onProgress: ({ embedded, total }) => {
189530
- const cur = recompProgressBySession.get(sessionId);
189531
- if (!cur || cur.phase !== "recomp")
189532
- return;
189533
- recompProgressBySession.set(sessionId, {
189534
- ...cur,
189535
- processedMessages: embedded,
189536
- totalMessages: total,
189537
- updatedAt: Date.now()
189538
- });
190348
+ embedPauseBySession.delete(sessionId);
190349
+ const prior = embedRunStateBySession.get(sessionId);
190350
+ if (prior)
190351
+ prior.abort();
190352
+ const controller = new AbortController;
190353
+ embedRunStateBySession.set(sessionId, controller);
190354
+ const signal = options?.signal ?? controller.signal;
190355
+ if (!options?.silent) {
190356
+ setRecompStarting({ recompProgressBySession }, sessionId, "Embedding history…", "embed");
190357
+ }
190358
+ let runFailed = 0;
190359
+ let outcome;
190360
+ try {
190361
+ outcome = await embedSessionCompartmentChunks(db, sessionProjectIdentity, sessionId, {
190362
+ signal,
190363
+ onProgress: ({ embedded, total }) => {
190364
+ const cur = recompProgressBySession.get(sessionId);
190365
+ if (!cur || cur.phase !== "recomp")
190366
+ return;
190367
+ recompProgressBySession.set(sessionId, {
190368
+ ...cur,
190369
+ processedMessages: embedded,
190370
+ totalMessages: total,
190371
+ updatedAt: Date.now()
190372
+ });
190373
+ }
190374
+ });
190375
+ } finally {
190376
+ if (embedRunStateBySession.get(sessionId) === controller) {
190377
+ embedRunStateBySession.delete(sessionId);
189539
190378
  }
189540
- });
190379
+ }
190380
+ if ("failed" in outcome)
190381
+ runFailed = outcome.failed;
189541
190382
  const terminal = (phase, message) => {
189542
- setRecompTerminal({ recompProgressBySession }, sessionId, phase, message);
190383
+ if (!options?.silent) {
190384
+ setRecompTerminal({ recompProgressBySession }, sessionId, phase, message);
190385
+ }
189543
190386
  return message;
189544
190387
  };
189545
190388
  switch (outcome.status) {
@@ -189548,15 +190391,78 @@ function createMagicContextHook(deps) {
189548
190391
  case "disabled":
189549
190392
  return terminal("skipped", "No embedding provider is configured, so there is nothing to embed.");
189550
190393
  case "busy":
189551
- return terminal("skipped", `Embedding is already running for this project — ${outcome.total} compartment${outcome.total === 1 ? "" : "s"} still pending. Try again shortly.`);
189552
- case "aborted":
189553
- return terminal("done", `Embedded ${outcome.embedded} of ${outcome.total} compartments before stopping.`);
190394
+ return terminal("skipped", "Embedding is already running for this project. Try again shortly.");
190395
+ case "aborted": {
190396
+ const cov = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
190397
+ const msg = `Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`;
190398
+ return terminal("skipped", msg);
190399
+ }
189554
190400
  case "stalled":
189555
- return terminal("skipped", `Embedded ${outcome.embedded} compartments; ${outcome.remaining} could not be embedded (the provider returned no result). Run /ctx-embed-history again to retry them.`);
190401
+ return terminal("skipped", `Embedded ${outcome.embedded} compartments; ${outcome.remaining} could not be embedded (the provider returned no result). Run /ctx-embed start again to retry them.`);
189556
190402
  default:
189557
- return terminal("done", `Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search.`);
190403
+ return terminal("done", `Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search${runFailed > 0 ? ` (${runFailed} failed)` : ""}.`);
189558
190404
  }
189559
190405
  };
190406
+ const pauseEmbedDrain = (sessionId) => {
190407
+ embedPauseBySession.add(sessionId);
190408
+ const ctrl = embedRunStateBySession.get(sessionId);
190409
+ if (ctrl)
190410
+ ctrl.abort();
190411
+ const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
190412
+ const sessionProjectIdentity = resolveProjectIdentity(directory);
190413
+ const cov = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
190414
+ return `Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`;
190415
+ };
190416
+ const getEmbedStatusText = (sessionId) => {
190417
+ const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
190418
+ const sessionProjectIdentity = resolveProjectIdentity(directory);
190419
+ const coverage = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
190420
+ const progress = recompProgressBySession.get(sessionId);
190421
+ const drainUi = getEmbedDrainUiStatus(sessionId, progress);
190422
+ return formatEmbedStatusText(coverage, {
190423
+ status: drainUi.status,
190424
+ embedded: progress?.processedMessages,
190425
+ total: progress?.totalMessages
190426
+ });
190427
+ };
190428
+ const maybeAutoEmbedSession = (sessionId) => {
190429
+ if (autoEmbedAttemptedBySession.has(sessionId))
190430
+ return;
190431
+ if (embedPauseBySession.has(sessionId))
190432
+ return;
190433
+ if (deps.config.memory?.enabled === false)
190434
+ return;
190435
+ autoEmbedAttemptedBySession.add(sessionId);
190436
+ const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
190437
+ (async () => {
190438
+ try {
190439
+ await new Promise((resolve7) => setTimeout(resolve7, 0));
190440
+ await ensureProjectRegisteredFromOpenCodeDirectory(directory, db);
190441
+ const sessionProjectIdentity = resolveProjectIdentity(directory);
190442
+ const coverage = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
190443
+ if (!coverage.enabled)
190444
+ return;
190445
+ const remaining = coverage.session.total - coverage.session.embedded;
190446
+ if (remaining <= 0)
190447
+ return;
190448
+ const notifyParams = getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession);
190449
+ if (!isTuiConnected(sessionId)) {
190450
+ const startMsg = `Embedding ${remaining} compartment${remaining === 1 ? "" : "s"} of history in the background…`;
190451
+ await sendIgnoredMessage(deps.client, sessionId, startMsg, {
190452
+ ...notifyParams
190453
+ });
190454
+ }
190455
+ const summary = await executeEmbedHistory(sessionId);
190456
+ if (!isTuiConnected(sessionId)) {
190457
+ await sendIgnoredMessage(deps.client, sessionId, summary, {
190458
+ ...notifyParams
190459
+ });
190460
+ }
190461
+ } catch (error51) {
190462
+ log("[magic-context] auto-embed drain failed:", error51);
190463
+ }
190464
+ })();
190465
+ };
189560
190466
  const sidekickRunnable = isSidekickRunnable(deps.config);
189561
190467
  const sidekickConfig = sidekickRunnable ? deps.config.sidekick : undefined;
189562
190468
  const transform2 = createTransform({
@@ -189618,7 +190524,8 @@ function createMagicContextHook(deps) {
189618
190524
  cavemanTextCompression: ctxReduceEnabled === false && deps.config.caveman_text_compression?.enabled === true ? {
189619
190525
  enabled: true,
189620
190526
  minChars: deps.config.caveman_text_compression.min_chars ?? 500
189621
- } : undefined
190527
+ } : undefined,
190528
+ maybeAutoEmbedSession
189622
190529
  });
189623
190530
  const eventHandler = createEventHandler2({
189624
190531
  contextUsageMap,
@@ -189647,6 +190554,7 @@ function createMagicContextHook(deps) {
189647
190554
  recompProgressBySession.delete(sessionId);
189648
190555
  internalChildSessions.delete(sessionId);
189649
190556
  channel1StateBySession.delete(sessionId);
190557
+ clearEmbedSessionState(sessionId);
189650
190558
  }
189651
190559
  });
189652
190560
  const runDreamQueueInBackground = () => {
@@ -189681,7 +190589,7 @@ function createMagicContextHook(deps) {
189681
190589
  token_budget: dreaming.pin_key_files.token_budget,
189682
190590
  min_reads: dreaming.pin_key_files.min_reads
189683
190591
  } : undefined,
189684
- fallbackModels: resolveFallbackChain(DREAMER_AGENT, dreaming.fallback_models),
190592
+ fallbackModels: resolveFallbackChain(dreaming.fallback_models),
189685
190593
  projectIdentity: projectPath
189686
190594
  }).catch((error51) => {
189687
190595
  log("[dreamer] scheduled queue processing failed:", error51);
@@ -189712,6 +190620,8 @@ function createMagicContextHook(deps) {
189712
190620
  executeRecomp: historianRunnable ? async (sessionId, options) => runManagedRecomp(buildManagedRecompCtx(sessionId), sessionId, options) : undefined,
189713
190621
  runUpgrade: historianRunnable ? async (sessionId) => runManagedUpgrade(buildManagedRecompCtx(sessionId), sessionId) : undefined,
189714
190622
  executeEmbedHistory,
190623
+ pauseEmbedDrain,
190624
+ getEmbedStatusText,
189715
190625
  sendNotification: async (sessionId, text, params) => {
189716
190626
  await sendIgnoredMessage(deps.client, sessionId, text, {
189717
190627
  ...getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
@@ -189738,7 +190648,7 @@ function createMagicContextHook(deps) {
189738
190648
  token_budget: dreamerConfig.pin_key_files.token_budget,
189739
190649
  min_reads: dreamerConfig.pin_key_files.min_reads
189740
190650
  } : undefined,
189741
- fallbackModels: resolveFallbackChain(DREAMER_AGENT, dreamerConfig.fallback_models)
190651
+ fallbackModels: resolveFallbackChain(dreamerConfig.fallback_models)
189742
190652
  } : undefined
189743
190653
  });
189744
190654
  const systemPromptHash = createSystemPromptHashHandler({
@@ -189923,6 +190833,7 @@ function truncateError(name2, code, message, maxLen = 240) {
189923
190833
 
189924
190834
  // src/plugin/rpc-handlers.ts
189925
190835
  init_project_identity();
190836
+ init_project_embedding_registry();
189926
190837
  init_tool_definition_tokens();
189927
190838
  await init_storage();
189928
190839
 
@@ -190643,6 +191554,26 @@ function buildStatusDetail(db, sessionId, directory, modelKey, config2, liveSess
190643
191554
  }
190644
191555
  return detail;
190645
191556
  }
191557
+ function buildEmbedDetail(db, sessionId, dir, liveSessionState) {
191558
+ const projectIdentity = resolveProjectIdentity(dir);
191559
+ const coverage = getEmbeddingCoverageStatus(db, projectIdentity, sessionId);
191560
+ const progress = liveSessionState.recompProgressBySession.get(sessionId);
191561
+ const drainUi = getEmbedDrainUiStatus(sessionId, progress);
191562
+ const statusText = formatEmbedStatusText(coverage, {
191563
+ status: drainUi.status,
191564
+ embedded: progress?.processedMessages,
191565
+ total: progress?.totalMessages
191566
+ });
191567
+ return {
191568
+ enabled: coverage.enabled,
191569
+ model: coverage.model,
191570
+ provider: coverage.provider,
191571
+ session: coverage.session,
191572
+ memories: coverage.memories,
191573
+ commits: coverage.commits,
191574
+ statusText
191575
+ };
191576
+ }
190646
191577
  function registerRpcHandlers(rpcServer, args) {
190647
191578
  const { directory, config: config2, liveSessionState } = args;
190648
191579
  const rawConfig = config2;
@@ -190665,6 +191596,19 @@ function registerRpcHandlers(rpcServer, args) {
190665
191596
  return { error: "unavailable" };
190666
191597
  return buildStatusDetail(db, sessionId, dir, modelKey, rawConfig, liveSessionState, injectionBudgetTokens);
190667
191598
  });
191599
+ rpcServer.handle("embed-detail", async (params) => {
191600
+ const sessionId = String(params.sessionId ?? "");
191601
+ const dir = String(params.directory ?? directory);
191602
+ const db = getDb();
191603
+ if (!db || !sessionId)
191604
+ return { error: "unavailable" };
191605
+ try {
191606
+ return buildEmbedDetail(db, sessionId, dir, liveSessionState);
191607
+ } catch (err) {
191608
+ log("[rpc] embed-detail error:", err);
191609
+ return { error: "unavailable" };
191610
+ }
191611
+ });
190668
191612
  rpcServer.handle("compartment-count", async (params) => {
190669
191613
  const sessionId = String(params.sessionId ?? "");
190670
191614
  const db = getDb();
@@ -190679,8 +191623,7 @@ function registerRpcHandlers(rpcServer, args) {
190679
191623
  });
190680
191624
  const buildManagedCtx = async (db) => {
190681
191625
  const { deriveHistorianChunkTokens: deriveHistorianChunkTokens2, resolveHistorianContextLimit: resolveHistorianContextLimit2 } = await Promise.resolve().then(() => (init_derive_budgets(), exports_derive_budgets));
190682
- const { resolveFallbackChain: resolveFallbackChain2 } = await Promise.resolve().then(() => (init_resolve_fallbacks(), exports_resolve_fallbacks));
190683
- const { HISTORIAN_AGENT: HISTORIAN_AGENT2 } = await Promise.resolve().then(() => exports_historian);
191626
+ const { resolveFallbackChain: resolveFallbackChain2 } = await Promise.resolve().then(() => exports_resolve_fallbacks);
190684
191627
  const DEFAULT_HISTORIAN_TIMEOUT_MS2 = 600000;
190685
191628
  return {
190686
191629
  client: args.client,
@@ -190691,7 +191634,7 @@ function registerRpcHandlers(rpcServer, args) {
190691
191634
  historianTimeoutMs: config2.historian_timeout_ms ?? DEFAULT_HISTORIAN_TIMEOUT_MS2,
190692
191635
  memoryEnabled: config2.memory?.enabled ?? true,
190693
191636
  autoPromote: config2.memory?.auto_promote ?? true,
190694
- fallbackModels: resolveFallbackChain2(HISTORIAN_AGENT2, config2.historian?.fallback_models),
191637
+ fallbackModels: resolveFallbackChain2(config2.historian?.fallback_models),
190695
191638
  runMigration: config2.memory?.enabled !== false && !!config2.historian?.model,
190696
191639
  userMemoriesEnabled: config2.dreamer?.user_memories?.enabled === true,
190697
191640
  historianTwoPass: config2.historian?.two_pass === true,
@@ -190787,27 +191730,225 @@ Older parts of this session are summarized into <compartment> blocks inside <ses
190787
191730
 
190788
191731
  ctx_expand(start=120, end=245) ← the compartment's own start/end attributes
190789
191732
 
190790
- Returns the raw transcript as [N] U:/A: lines, capped at ~15K tokens; an oversized range returns the head and tells you where to continue. Also works with ordinals from ctx_search message results — expand a window around a hit (e.g. start=N-10, end=N+5). Ranges after the last compartment are your live tail — already visible in context, not expandable.`;
191733
+ Returns the raw transcript as [N] U:/A: lines, capped at ~15K tokens; an oversized range returns the head and tells you where to continue. Also works with ordinals from ctx_search message results — expand a window around a hit (e.g. start=N-10, end=N+5). Ranges after the last compartment are your live tail — already visible in context, not expandable.
191734
+
191735
+ Two recovery modes for finer detail:
191736
+ - ctx_expand(start=120, end=245, verbose=true) — lists each message SEPARATELY with its ordinal [N] and a per-part preview (each tool call shown with its output size). Use this to find the exact message or tool call you want, then recover it in full by ordinal.
191737
+ - ctx_expand(message=138) — returns the FULL untruncated content of the message at that ordinal: every text part, and every tool call's complete input + output, read from stored history. This is the cheap way to get back a tool output you dropped with ctx_reduce — the original is still in storage even though the wire shows [dropped §N§]. If the message was deleted from history (session prune/revert), it says so.`;
190791
191738
  var CTX_EXPAND_TOKEN_BUDGET = 15000;
190792
191739
 
191740
+ // src/tools/ctx-expand/render.ts
191741
+ init_read_session_formatting();
191742
+ await init_read_session_chunk();
191743
+ function isRecord3(value) {
191744
+ return value !== null && typeof value === "object" && !Array.isArray(value);
191745
+ }
191746
+ function roleLabel(role) {
191747
+ if (role === "assistant")
191748
+ return "A (assistant)";
191749
+ if (role === "user")
191750
+ return "U (user)";
191751
+ return role;
191752
+ }
191753
+ function truncate2(value, max) {
191754
+ const t = value.trim();
191755
+ return t.length <= max ? t : `${t.slice(0, max)}…`;
191756
+ }
191757
+ function keyArg(input) {
191758
+ if (!input)
191759
+ return "";
191760
+ for (const k of ["filePath", "path", "pattern", "query", "symbol", "module", "action"]) {
191761
+ const v = input[k];
191762
+ if (typeof v === "string" && v.length > 0)
191763
+ return truncate2(v, 60);
191764
+ }
191765
+ if (typeof input.description === "string")
191766
+ return truncate2(input.description, 60);
191767
+ return "";
191768
+ }
191769
+ function asToolPart(part) {
191770
+ const type = typeof part.type === "string" ? part.type : "";
191771
+ if (type === "tool") {
191772
+ const state = isRecord3(part.state) ? part.state : null;
191773
+ const output = state && typeof state.output === "string" ? state.output : state && state.output != null ? JSON.stringify(state.output) : null;
191774
+ const metadata = state && isRecord3(state.metadata) ? state.metadata : null;
191775
+ const title = state && typeof state.title === "string" && state.title || metadata && typeof metadata.title === "string" && metadata.title || null;
191776
+ return {
191777
+ name: typeof part.tool === "string" ? part.tool : "tool",
191778
+ callId: typeof part.callID === "string" ? part.callID : "",
191779
+ title,
191780
+ input: state && isRecord3(state.input) ? state.input : null,
191781
+ output
191782
+ };
191783
+ }
191784
+ if (type === "tool_use") {
191785
+ return {
191786
+ name: typeof part.name === "string" ? part.name : "tool",
191787
+ callId: typeof part.id === "string" ? part.id : "",
191788
+ title: null,
191789
+ input: isRecord3(part.input) ? part.input : null,
191790
+ output: null
191791
+ };
191792
+ }
191793
+ if (type === "tool_result") {
191794
+ const content = part.content;
191795
+ const output = typeof content === "string" ? content : content != null ? JSON.stringify(content) : null;
191796
+ return {
191797
+ name: "tool_result",
191798
+ callId: typeof part.tool_use_id === "string" ? part.tool_use_id : "",
191799
+ title: null,
191800
+ input: null,
191801
+ output
191802
+ };
191803
+ }
191804
+ return null;
191805
+ }
191806
+ function textOf(part) {
191807
+ if (part.type === "text" && typeof part.text === "string")
191808
+ return part.text;
191809
+ return null;
191810
+ }
191811
+ function reasoningOf(part) {
191812
+ if ((part.type === "reasoning" || part.type === "thinking") && typeof part.text === "string") {
191813
+ return part.text;
191814
+ }
191815
+ return null;
191816
+ }
191817
+ function renderPartPreview(part) {
191818
+ if (!isRecord3(part))
191819
+ return null;
191820
+ const text = textOf(part);
191821
+ if (text !== null) {
191822
+ const t = truncate2(text, 200);
191823
+ return t.length > 0 ? ` • ${t}` : null;
191824
+ }
191825
+ const tool = asToolPart(part);
191826
+ if (tool) {
191827
+ const arg = keyArg(tool.input);
191828
+ const head = arg ? `${tool.name}(${arg})` : tool.name;
191829
+ return tool.output !== null ? ` • tool ${head} → output ~${estimateTokens(tool.output)} tok` : ` • tool ${head}`;
191830
+ }
191831
+ const reasoning = reasoningOf(part);
191832
+ if (reasoning !== null)
191833
+ return ` • [reasoning] ${truncate2(reasoning, 120)}`;
191834
+ const type = typeof part.type === "string" ? part.type : "part";
191835
+ if (type === "file")
191836
+ return " • [file]";
191837
+ if (type === "step-start" || type === "step-finish")
191838
+ return null;
191839
+ return ` • [${type}]`;
191840
+ }
191841
+ function renderPartFull(part) {
191842
+ if (!isRecord3(part))
191843
+ return null;
191844
+ const text = textOf(part);
191845
+ if (text !== null) {
191846
+ return text.trim().length > 0 ? ` [text]
191847
+ ${text}` : null;
191848
+ }
191849
+ const tool = asToolPart(part);
191850
+ if (tool) {
191851
+ const lines = [];
191852
+ const idSuffix = tool.callId ? ` #${tool.callId}` : "";
191853
+ lines.push(` [tool: ${tool.name}${idSuffix}]`);
191854
+ if (tool.title && tool.title.trim().length > 0) {
191855
+ lines.push(` description: ${tool.title.trim()}`);
191856
+ }
191857
+ if (tool.input)
191858
+ lines.push(` input: ${JSON.stringify(tool.input)}`);
191859
+ if (tool.output !== null)
191860
+ lines.push(` output:
191861
+ ${tool.output}`);
191862
+ return lines.join(`
191863
+ `);
191864
+ }
191865
+ const type = typeof part.type === "string" ? part.type : "part";
191866
+ if (type === "file") {
191867
+ const name2 = typeof part.filename === "string" && part.filename || typeof part.url === "string" && part.url || "";
191868
+ return ` [file]${name2 ? ` ${name2}` : ""}`;
191869
+ }
191870
+ return null;
191871
+ }
191872
+ function renderMessageByOrdinal(sessionId, ordinal) {
191873
+ const msg = readRawSessionMessages(sessionId).find((m) => m.ordinal === ordinal);
191874
+ if (!msg) {
191875
+ return `No message at ordinal ${ordinal} in this session's stored history — it was deleted ` + `(session prune/revert) or the ordinal is wrong, so it can't be recovered. ` + `Re-run the tool if you still need the data.`;
191876
+ }
191877
+ const rendered = msg.parts.map(renderPartFull).filter((l) => l !== null);
191878
+ const lines = [`[${msg.ordinal}] ${roleLabel(msg.role)} — full recovery:`, ""];
191879
+ if (rendered.length === 0) {
191880
+ lines.push(" (no recoverable content — message had only structural/reasoning parts)");
191881
+ } else {
191882
+ lines.push(...rendered);
191883
+ }
191884
+ return lines.join(`
191885
+ `);
191886
+ }
191887
+ function renderVerboseRange(sessionId, start, end, tokenBudget) {
191888
+ const messages = readRawSessionMessages(sessionId).filter((m) => m.ordinal >= start && m.ordinal <= end);
191889
+ const out = [];
191890
+ let usedTokens = 0;
191891
+ let lastOrdinal = start - 1;
191892
+ let truncated = false;
191893
+ for (const msg of messages) {
191894
+ const header = `[${msg.ordinal}] ${roleLabel(msg.role)}`;
191895
+ const partLines = msg.parts.map(renderPartPreview).filter((l) => l !== null);
191896
+ const block = partLines.length > 0 ? `${header}
191897
+ ${partLines.join(`
191898
+ `)}` : header;
191899
+ const blockTokens = estimateTokens(block);
191900
+ if (usedTokens + blockTokens > tokenBudget && out.length > 0) {
191901
+ truncated = true;
191902
+ break;
191903
+ }
191904
+ out.push(block);
191905
+ usedTokens += blockTokens;
191906
+ lastOrdinal = msg.ordinal;
191907
+ }
191908
+ return { text: out.join(`
191909
+
191910
+ `), lastOrdinal, truncated };
191911
+ }
191912
+
190793
191913
  // src/tools/ctx-expand/tools.ts
190794
191914
  function createCtxExpandTool(deps) {
190795
191915
  return tool({
190796
191916
  description: CTX_EXPAND_DESCRIPTION,
190797
191917
  args: {
190798
- start: tool.schema.number().describe(`First message ordinal to expand — a compartment's start="N" attribute, or an ordinal from a ctx_search message hit`),
190799
- end: tool.schema.number().describe(`Last message ordinal to expand (inclusive) — a compartment's end="M" attribute`)
191918
+ start: tool.schema.number().optional().describe(`First message ordinal to expand — a compartment's start="N" attribute, or an ordinal from a ctx_search message hit`),
191919
+ end: tool.schema.number().optional().describe(`Last message ordinal to expand (inclusive) — a compartment's end="M" attribute`),
191920
+ verbose: tool.schema.boolean().optional().describe("With start/end: list each message separately with its ordinal [N] and per-part preview (each tool call shown with its output size), so you can pick one to recover in full by ordinal."),
191921
+ message: tool.schema.number().optional().describe("Full untruncated recovery of ONE message by its ordinal (every text part + every tool call's complete input/output). Use an ordinal from a compartment, ctx_search hit, or verbose range. Recovers a tool output you dropped with ctx_reduce.")
190800
191922
  },
190801
191923
  async execute(args, toolContext) {
190802
191924
  const sessionId = toolContext.sessionID;
191925
+ if (typeof args.message === "number" && args.message >= 1) {
191926
+ return renderMessageByOrdinal(sessionId, args.message);
191927
+ }
190803
191928
  if (!args.start || !args.end || args.start < 1 || args.end < args.start) {
190804
- return "Error: start and end must be positive integers with start <= end.";
191929
+ return "Error: provide either message=<ordinal>, or start and end (positive integers, start <= end).";
190805
191930
  }
190806
191931
  const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
190807
191932
  if (lastCompartmentEnd >= 0 && args.start > lastCompartmentEnd) {
190808
191933
  return `Range ${args.start}-${args.end} is entirely within the live tail (after the last compacted message ${lastCompartmentEnd}); those messages are already visible in context.`;
190809
191934
  }
190810
191935
  const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(args.end, lastCompartmentEnd) : args.end;
191936
+ if (args.verbose === true) {
191937
+ const v = renderVerboseRange(sessionId, args.start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET);
191938
+ if (!v.text) {
191939
+ return `No messages found in range ${args.start}-${effectiveEnd}. The range may be outside this session's history.`;
191940
+ }
191941
+ const out = [
191942
+ `Messages ${args.start}-${v.lastOrdinal} (verbose). Recover any one in full with ctx_expand(message=<ordinal>):`,
191943
+ "",
191944
+ v.text
191945
+ ];
191946
+ if (v.truncated) {
191947
+ out.push("", `Truncated at message ${v.lastOrdinal} (budget: ~${CTX_EXPAND_TOKEN_BUDGET} tokens). Call again with start=${v.lastOrdinal + 1} end=${effectiveEnd} verbose=true for more.`);
191948
+ }
191949
+ return out.join(`
191950
+ `);
191951
+ }
190811
191952
  const chunk = readSessionChunk(sessionId, CTX_EXPAND_TOKEN_BUDGET, args.start, effectiveEnd + 1);
190812
191953
  if (!chunk.text || chunk.messageCount === 0) {
190813
191954
  return `No messages found in range ${args.start}-${args.end}. The range may be outside this session's history.`;
@@ -190845,6 +191986,7 @@ Actions:
190845
191986
  Example: ctx_memory(action="write", category="CONSTRAINTS", content="Pi stores sessions as JSONL under ~/.pi/agent/sessions/, not SQLite")`;
190846
191987
  var DEFAULT_SEARCH_LIMIT2 = 10;
190847
191988
  // src/tools/ctx-memory/tools.ts
191989
+ import { tool as tool2 } from "@opencode-ai/plugin";
190848
191990
  init_memory();
190849
191991
  init_embedding();
190850
191992
  init_embedding_cache();
@@ -190852,7 +191994,6 @@ init_normalize_hash();
190852
191994
  init_workspaces();
190853
191995
  init_logger();
190854
191996
  await init_storage();
190855
- import { tool as tool2 } from "@opencode-ai/plugin";
190856
191997
 
190857
191998
  // src/tools/ctx-memory/types.ts
190858
191999
  var CTX_MEMORY_ACTIONS = ["write", "archive", "update", "merge"];
@@ -191501,15 +192642,16 @@ function createCtxNoteTools(deps) {
191501
192642
  };
191502
192643
  }
191503
192644
  // src/tools/ctx-reduce/constants.ts
191504
- var CTX_REDUCE_DESCRIPTION = `Reduce context by dropping tagged content you no longer need.
191505
- Use §N§ identifiers visible in conversation. The \`drop\` param accepts ranges: "3-5", "1,2,9", "1-5,8".
191506
-
191507
- CRITICAL RULES:
191508
- - NEVER blanket-drop large ranges (e.g., "1-50"). Always review what each tag contains first.
191509
- - Only drop tool outputs you have already processed and no longer need.
191510
- - Protected tags are accepted but deferred until they leave the last protected range.
191511
- - Keep recent context only reduce OLD content that is no longer relevant to current work.
191512
- - Dropped content is gone forever.`;
192645
+ var CTX_REDUCE_DESCRIPTION = `Mark spent tagged content as discardable to reclaim context space. This is NOT an immediate delete. Use §N§ identifiers visible in the conversation. The \`drop\` param accepts ranges: "3-5", "1,2,9", "1-5,8".
192646
+
192647
+ How it works:
192648
+ - Marking QUEUES content for release. It stays fully visible to you until context space is actually needed — which may be as soon as the next turn if you are already under pressure, or many turns later if not. So mark spent outputs as soon as you finish with them; don't hoard the call for the end of the turn.
192649
+ - The newest tags are protected: marking one just queues it until it ages out of the recent window, so marking recent output is harmless.
192650
+ - When content is finally released it becomes a short placeholder, and re-running the tool is the only way to get it back. So mark only what you are genuinely DONE with — the test is "have I extracted what I need from this?", not "is it safe / do I have time before it drops?".
192651
+
192652
+ Mark discardable once processed: large outputs you've summarized, repeated or redundant dumps, data written to disk, status/log output that only confirmed an expected state.
192653
+ Keep: user messages, unresolved errors, raw evidence you haven't extracted yet, and outputs whose exact wording may matter later.
192654
+ Never blanket-mark large ranges (e.g. "1-50") — review what each tag holds first.`;
191513
192655
  // src/tools/ctx-reduce/tools.ts
191514
192656
  import { tool as tool4 } from "@opencode-ai/plugin";
191515
192657
 
@@ -191776,7 +192918,7 @@ function createCtxSearchTool(deps) {
191776
192918
  memoryEnabled,
191777
192919
  embeddingEnabled,
191778
192920
  embedQuery: async (text, signal) => {
191779
- const result = await embedTextForProject(projectPath, text, signal);
192921
+ const result = await embedTextForProject(projectPath, text, signal, "query");
191780
192922
  return result?.vector ?? null;
191781
192923
  },
191782
192924
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
@@ -191879,7 +193021,6 @@ function createToolRegistry(args) {
191879
193021
  init_conflict_detector();
191880
193022
  init_data_path();
191881
193023
  init_logger();
191882
- init_model_requirements();
191883
193024
  init_models_dev_cache();
191884
193025
 
191885
193026
  // src/shared/rpc-server.ts
@@ -192092,6 +193233,9 @@ class MagicContextRpcServer {
192092
193233
  }
192093
193234
 
192094
193235
  // src/index.ts
193236
+ var HISTORIAN_MAX_STEPS = 40;
193237
+ var SIDEKICK_MAX_STEPS = 40;
193238
+ var DREAMER_MAX_STEPS = 150;
192095
193239
  var plugin = async (ctx) => {
192096
193240
  const pluginConfig = loadPluginConfig(ctx.directory);
192097
193241
  setSqlitePragmaConfig({
@@ -192308,12 +193452,13 @@ var plugin = async (ctx) => {
192308
193452
  await hooks.magicContext?.["experimental.text.complete"]?.(input, output);
192309
193453
  },
192310
193454
  config: async (config2) => {
192311
- const buildHiddenAgentConfig = (agentId, prompt, allowedTools, overrides) => {
193455
+ const buildHiddenAgentConfig = (prompt, allowedTools, maxSteps, overrides) => {
192312
193456
  const { permission: overridePermission, ...restOverrides } = overrides ?? {};
192313
193457
  const basePermission = buildAllowOnlyPermission(allowedTools);
192314
193458
  return {
192315
193459
  prompt,
192316
- ...getAgentFallbackModels(agentId) ? { fallback_models: getAgentFallbackModels(agentId) } : {},
193460
+ steps: maxSteps,
193461
+ maxSteps,
192317
193462
  ...restOverrides,
192318
193463
  permission: {
192319
193464
  ...basePermission,
@@ -192335,6 +193480,7 @@ var plugin = async (ctx) => {
192335
193480
  max_runtime_minutes: _max,
192336
193481
  tasks: _tasks,
192337
193482
  task_timeout_minutes: _tto,
193483
+ thinking_level: _thinkingLevel,
192338
193484
  ...agentOverrides
192339
193485
  } = pluginConfig.dreamer;
192340
193486
  return agentOverrides;
@@ -192343,20 +193489,28 @@ var plugin = async (ctx) => {
192343
193489
  const {
192344
193490
  timeout_ms: _timeoutMs,
192345
193491
  system_prompt: _systemPrompt,
193492
+ thinking_level: _thinkingLevel,
192346
193493
  ...agentOverrides
192347
193494
  } = pluginConfig.sidekick;
192348
193495
  return agentOverrides;
192349
193496
  })() : undefined;
192350
193497
  const historianAgentOverrides = pluginConfig.historian ? (() => {
192351
- const { two_pass: _twoPass, ...agentOverrides } = pluginConfig.historian;
193498
+ const {
193499
+ two_pass: _twoPass,
193500
+ disallowed_tools: _disallowedTools,
193501
+ thinking_level: _thinkingLevel,
193502
+ ...agentOverrides
193503
+ } = pluginConfig.historian;
192352
193504
  return agentOverrides;
192353
193505
  })() : undefined;
193506
+ const historianDisallowed = pluginConfig.historian?.disallowed_tools ?? [];
193507
+ const historianAllowedTools = applyDisallowedTools(HISTORIAN_ALLOWED_TOOLS, historianDisallowed);
192354
193508
  config2.agent = {
192355
193509
  ...config2.agent ?? {},
192356
- [DREAMER_AGENT]: buildHiddenAgentConfig(DREAMER_AGENT, DREAMER_SYSTEM_PROMPT, DREAMER_ALLOWED_TOOLS, dreamerAgentOverrides),
192357
- [HISTORIAN_AGENT]: buildHiddenAgentConfig(HISTORIAN_AGENT, COMPARTMENT_AGENT_SYSTEM_PROMPT, HISTORIAN_ALLOWED_TOOLS, historianAgentOverrides),
192358
- [HISTORIAN_EDITOR_AGENT]: buildHiddenAgentConfig(HISTORIAN_EDITOR_AGENT, HISTORIAN_EDITOR_SYSTEM_PROMPT, HISTORIAN_ALLOWED_TOOLS, historianAgentOverrides),
192359
- [SIDEKICK_AGENT]: buildHiddenAgentConfig(SIDEKICK_AGENT, SIDEKICK_SYSTEM_PROMPT, SIDEKICK_ALLOWED_TOOLS, sidekickAgentOverrides)
193510
+ [DREAMER_AGENT]: buildHiddenAgentConfig(DREAMER_SYSTEM_PROMPT, DREAMER_ALLOWED_TOOLS, DREAMER_MAX_STEPS, dreamerAgentOverrides),
193511
+ [HISTORIAN_AGENT]: buildHiddenAgentConfig(COMPARTMENT_AGENT_SYSTEM_PROMPT, historianAllowedTools, HISTORIAN_MAX_STEPS, historianAgentOverrides),
193512
+ [HISTORIAN_EDITOR_AGENT]: buildHiddenAgentConfig(HISTORIAN_EDITOR_SYSTEM_PROMPT, historianAllowedTools, HISTORIAN_MAX_STEPS, historianAgentOverrides),
193513
+ [SIDEKICK_AGENT]: buildHiddenAgentConfig(SIDEKICK_SYSTEM_PROMPT, SIDEKICK_ALLOWED_TOOLS, SIDEKICK_MAX_STEPS, sidekickAgentOverrides)
192360
193514
  };
192361
193515
  }
192362
193516
  };