@wolfx/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 (141) hide show
  1. package/dist/agents/magic-context-prompt.d.ts.map +1 -1
  2. package/dist/agents/permissions.d.ts +6 -0
  3. package/dist/agents/permissions.d.ts.map +1 -1
  4. package/dist/config/schema/magic-context.d.ts +22 -0
  5. package/dist/config/schema/magic-context.d.ts.map +1 -1
  6. package/dist/features/magic-context/compartment-chunk-embedding.d.ts +25 -0
  7. package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
  8. package/dist/features/magic-context/compartment-embedding.d.ts.map +1 -1
  9. package/dist/features/magic-context/dreamer/task-prompts.d.ts.map +1 -1
  10. package/dist/features/magic-context/memory/embedding-local.d.ts +7 -3
  11. package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
  12. package/dist/features/magic-context/memory/embedding-openai.d.ts +8 -4
  13. package/dist/features/magic-context/memory/embedding-openai.d.ts.map +1 -1
  14. package/dist/features/magic-context/memory/embedding-provider.d.ts +8 -4
  15. package/dist/features/magic-context/memory/embedding-provider.d.ts.map +1 -1
  16. package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
  17. package/dist/features/magic-context/memory/relocate-memory.d.ts +58 -0
  18. package/dist/features/magic-context/memory/relocate-memory.d.ts.map +1 -0
  19. package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts +6 -0
  20. package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts.map +1 -1
  21. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  22. package/dist/features/magic-context/project-embedding-registry.d.ts +41 -3
  23. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  24. package/dist/features/magic-context/storage-db.d.ts +2 -1
  25. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  26. package/dist/features/magic-context/storage-meta-persisted.d.ts +37 -0
  27. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  28. package/dist/features/magic-context/storage-meta-session.d.ts +1 -0
  29. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  30. package/dist/features/magic-context/storage-meta-shared.d.ts +4 -1
  31. package/dist/features/magic-context/storage-meta-shared.d.ts.map +1 -1
  32. package/dist/features/magic-context/storage-meta.d.ts +2 -2
  33. package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
  34. package/dist/features/magic-context/storage-tags.d.ts +58 -2
  35. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  36. package/dist/features/magic-context/storage.d.ts +3 -3
  37. package/dist/features/magic-context/storage.d.ts.map +1 -1
  38. package/dist/features/magic-context/tagger.d.ts +1 -1
  39. package/dist/features/magic-context/tagger.d.ts.map +1 -1
  40. package/dist/features/magic-context/transform-decision-log.d.ts +49 -0
  41. package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -0
  42. package/dist/features/magic-context/types.d.ts +1 -0
  43. package/dist/features/magic-context/types.d.ts.map +1 -1
  44. package/dist/features/magic-context/v22-deferred-backfill.d.ts.map +1 -1
  45. package/dist/hooks/magic-context/apply-operations.d.ts +3 -25
  46. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  47. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  48. package/dist/hooks/magic-context/caveman-cleanup.d.ts +1 -0
  49. package/dist/hooks/magic-context/caveman-cleanup.d.ts.map +1 -1
  50. package/dist/hooks/magic-context/channel2-delivery.d.ts +2 -0
  51. package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
  52. package/dist/hooks/magic-context/command-handler.d.ts +7 -5
  53. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  54. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  55. package/dist/hooks/magic-context/compartment-trigger.d.ts +1 -1
  56. package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
  57. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts +7 -2
  58. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts.map +1 -1
  59. package/dist/hooks/magic-context/derive-budgets.d.ts +5 -9
  60. package/dist/hooks/magic-context/derive-budgets.d.ts.map +1 -1
  61. package/dist/hooks/magic-context/embed-session-state.d.ts +14 -0
  62. package/dist/hooks/magic-context/embed-session-state.d.ts.map +1 -0
  63. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  64. package/dist/hooks/magic-context/event-payloads.d.ts +1 -0
  65. package/dist/hooks/magic-context/event-payloads.d.ts.map +1 -1
  66. package/dist/hooks/magic-context/format-embed-status.d.ts +9 -0
  67. package/dist/hooks/magic-context/format-embed-status.d.ts.map +1 -0
  68. package/dist/hooks/magic-context/heuristic-cleanup.d.ts +2 -0
  69. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  70. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  71. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  72. package/dist/hooks/magic-context/protected-tail-boundary.d.ts +10 -0
  73. package/dist/hooks/magic-context/protected-tail-boundary.d.ts.map +1 -1
  74. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  75. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts +1 -1
  76. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
  77. package/dist/hooks/magic-context/strip-content.d.ts +0 -1
  78. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  79. package/dist/hooks/magic-context/tag-content-primitives.d.ts +2 -0
  80. package/dist/hooks/magic-context/tag-content-primitives.d.ts.map +1 -1
  81. package/dist/hooks/magic-context/tag-id-fallback.d.ts.map +1 -1
  82. package/dist/hooks/magic-context/tag-messages.d.ts +10 -0
  83. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  84. package/dist/hooks/magic-context/tool-drop-target.d.ts +1 -1
  85. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  86. package/dist/hooks/magic-context/tool-reclaim.d.ts +12 -0
  87. package/dist/hooks/magic-context/tool-reclaim.d.ts.map +1 -0
  88. package/dist/hooks/magic-context/transform-compartment-phase.d.ts +32 -1
  89. package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
  90. package/dist/hooks/magic-context/transform-operations.d.ts +1 -1
  91. package/dist/hooks/magic-context/transform-operations.d.ts.map +1 -1
  92. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +6 -0
  93. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  94. package/dist/hooks/magic-context/transform.d.ts +2 -0
  95. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +1767 -613
  98. package/dist/plugin/conflict-warning-hook.d.ts.map +1 -1
  99. package/dist/plugin/dream-timer.d.ts.map +1 -1
  100. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  101. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  102. package/dist/shared/announcement.d.ts +1 -1
  103. package/dist/shared/index.d.ts +0 -1
  104. package/dist/shared/index.d.ts.map +1 -1
  105. package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
  106. package/dist/shared/resolve-fallbacks.d.ts +16 -16
  107. package/dist/shared/resolve-fallbacks.d.ts.map +1 -1
  108. package/dist/shared/rpc-types.d.ts +20 -0
  109. package/dist/shared/rpc-types.d.ts.map +1 -1
  110. package/dist/shared/sqlite.d.ts +5 -1
  111. package/dist/shared/sqlite.d.ts.map +1 -1
  112. package/dist/tools/ctx-expand/constants.d.ts +1 -1
  113. package/dist/tools/ctx-expand/constants.d.ts.map +1 -1
  114. package/dist/tools/ctx-expand/render.d.ts +43 -0
  115. package/dist/tools/ctx-expand/render.d.ts.map +1 -0
  116. package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
  117. package/dist/tools/ctx-expand/types.d.ts +6 -2
  118. package/dist/tools/ctx-expand/types.d.ts.map +1 -1
  119. package/dist/tools/ctx-reduce/constants.d.ts +1 -1
  120. package/dist/tools/ctx-reduce/constants.d.ts.map +1 -1
  121. package/dist/tools/ctx-search/tools.d.ts.map +1 -1
  122. package/dist/tui/data/context-db.d.ts +4 -2
  123. package/dist/tui/data/context-db.d.ts.map +1 -1
  124. package/package.json +5 -11
  125. package/src/shared/announcement.ts +6 -6
  126. package/src/shared/index.ts +0 -1
  127. package/src/shared/model-suggestion-retry.test.ts +61 -1
  128. package/src/shared/model-suggestion-retry.ts +22 -0
  129. package/src/shared/resolve-fallbacks.test.ts +37 -71
  130. package/src/shared/resolve-fallbacks.ts +16 -26
  131. package/src/shared/rpc-types.ts +11 -0
  132. package/src/shared/sqlite-bind-style.test.ts +82 -0
  133. package/src/shared/sqlite.ts +30 -1
  134. package/src/shared/tag-transcript.test.ts +3 -1
  135. package/src/shared/tag-transcript.ts +19 -17
  136. package/src/tui/data/context-db.ts +34 -2
  137. package/src/tui/index.tsx +58 -4
  138. package/src/tui/slots/sidebar-content.tsx +18 -9
  139. package/dist/shared/model-requirements.d.ts +0 -26
  140. package/dist/shared/model-requirements.d.ts.map +0 -1
  141. 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
  };
@@ -15284,58 +15277,6 @@ var init_logger = __esm(() => {
15284
15277
  }
15285
15278
  });
15286
15279
 
15287
- // src/shared/model-requirements.ts
15288
- function expandFallbackChain(chain) {
15289
- const models = [];
15290
- for (const entry of chain) {
15291
- for (const provider of entry.providers) {
15292
- models.push(`${provider}/${entry.model}`);
15293
- }
15294
- }
15295
- return models;
15296
- }
15297
- function getAgentFallbackModels(agent) {
15298
- const requirement = AGENT_MODEL_REQUIREMENTS[agent];
15299
- if (!requirement)
15300
- return;
15301
- return expandFallbackChain(requirement.fallbackChain);
15302
- }
15303
- var HISTORIAN_FALLBACK_CHAIN, DREAMER_FALLBACK_CHAIN, SIDEKICK_FALLBACK_CHAIN, AGENT_MODEL_REQUIREMENTS;
15304
- var init_model_requirements = __esm(() => {
15305
- HISTORIAN_FALLBACK_CHAIN = [
15306
- { providers: ["github-copilot", "anthropic", "opencode"], model: "claude-sonnet-4-6" },
15307
- { providers: ["opencode-go"], model: "minimax-m2.7" },
15308
- {
15309
- providers: ["zai-coding-plan", "bailian-coding-plan", "opencode-go", "opencode"],
15310
- model: "glm-5"
15311
- },
15312
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.4" },
15313
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" }
15314
- ];
15315
- DREAMER_FALLBACK_CHAIN = [
15316
- { providers: ["github-copilot", "anthropic", "opencode"], model: "claude-sonnet-4-6" },
15317
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
15318
- {
15319
- providers: ["zai-coding-plan", "bailian-coding-plan", "opencode-go", "opencode"],
15320
- model: "glm-5"
15321
- },
15322
- { providers: ["opencode-go"], model: "minimax-m2.7" },
15323
- { providers: ["github-copilot", "openai", "opencode"], model: "gpt-5.4-mini" }
15324
- ];
15325
- SIDEKICK_FALLBACK_CHAIN = [
15326
- { providers: ["cerebras"], model: "qwen-3-235b-a22b-instruct-2507" },
15327
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
15328
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.4-mini" },
15329
- { providers: ["opencode"], model: "gpt-5-nano" }
15330
- ];
15331
- AGENT_MODEL_REQUIREMENTS = {
15332
- [HISTORIAN_AGENT]: { fallbackChain: HISTORIAN_FALLBACK_CHAIN },
15333
- [HISTORIAN_EDITOR_AGENT]: { fallbackChain: HISTORIAN_FALLBACK_CHAIN },
15334
- [DREAMER_AGENT]: { fallbackChain: DREAMER_FALLBACK_CHAIN },
15335
- [SIDEKICK_AGENT]: { fallbackChain: SIDEKICK_FALLBACK_CHAIN }
15336
- };
15337
- });
15338
-
15339
15280
  // src/features/magic-context/overflow-detection.ts
15340
15281
  function extractErrorMessage(error51) {
15341
15282
  if (!error51)
@@ -15449,15 +15390,9 @@ __export(exports_resolve_fallbacks, {
15449
15390
  resolveFallbackChain: () => resolveFallbackChain,
15450
15391
  parseProviderModel: () => parseProviderModel
15451
15392
  });
15452
- function resolveFallbackChain(agentName, userFallbacks) {
15393
+ function resolveFallbackChain(userFallbacks) {
15453
15394
  const userList = normalizeUserFallbacks(userFallbacks);
15454
- if (userList.length > 0) {
15455
- return dedupe(userList.filter(isValidModelSpec));
15456
- }
15457
- const builtin = getAgentFallbackModels(agentName);
15458
- if (!builtin || builtin.length === 0)
15459
- return [];
15460
- return dedupe(builtin.filter(isValidModelSpec));
15395
+ return dedupe(userList.filter(isValidModelSpec));
15461
15396
  }
15462
15397
  function normalizeUserFallbacks(userFallbacks) {
15463
15398
  if (!userFallbacks)
@@ -15492,9 +15427,6 @@ function parseProviderModel(spec) {
15492
15427
  modelID: spec.slice(slash + 1).trim()
15493
15428
  };
15494
15429
  }
15495
- var init_resolve_fallbacks = __esm(() => {
15496
- init_model_requirements();
15497
- });
15498
15430
 
15499
15431
  // src/shared/model-suggestion-retry.ts
15500
15432
  function extractMessage(error51) {
@@ -15565,9 +15497,11 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
15565
15497
  });
15566
15498
  } catch (error51) {
15567
15499
  if (signal?.aborted) {
15500
+ await abortChildRun(client, args.path.id);
15568
15501
  throw new Error("prompt aborted by external signal");
15569
15502
  }
15570
15503
  if (controller.signal.aborted) {
15504
+ await abortChildRun(client, args.path.id);
15571
15505
  throw new Error(`prompt timed out after ${timeoutMs}ms`);
15572
15506
  }
15573
15507
  throw error51;
@@ -15576,6 +15510,13 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
15576
15510
  signal?.removeEventListener("abort", onExternalAbort);
15577
15511
  }
15578
15512
  }
15513
+ async function abortChildRun(client, sessionId) {
15514
+ try {
15515
+ await client.session.abort({ path: { id: sessionId } });
15516
+ } catch (error51) {
15517
+ log(`[model-retry] child session abort failed for ${sessionId}: ${String(error51)}`);
15518
+ }
15519
+ }
15579
15520
  function isNonRetryable(error51, externalSignal) {
15580
15521
  if (externalSignal?.aborted)
15581
15522
  return true;
@@ -15673,7 +15614,6 @@ async function promptSyncWithModelSuggestionRetry(client, args, options = {}) {
15673
15614
  var init_model_suggestion_retry = __esm(() => {
15674
15615
  init_overflow_detection();
15675
15616
  init_logger();
15676
- init_resolve_fallbacks();
15677
15617
  });
15678
15618
 
15679
15619
  // src/shared/normalize-sdk-response.ts
@@ -15703,9 +15643,7 @@ function normalizeSDKResponse(response, fallback, options) {
15703
15643
  // src/shared/index.ts
15704
15644
  var init_shared = __esm(() => {
15705
15645
  init_logger();
15706
- init_model_requirements();
15707
15646
  init_model_suggestion_retry();
15708
- init_resolve_fallbacks();
15709
15647
  });
15710
15648
 
15711
15649
  // src/shared/record-type-guard.ts
@@ -15847,7 +15785,7 @@ function isSessionMetaRow(row) {
15847
15785
  if (row === null || typeof row !== "object")
15848
15786
  return false;
15849
15787
  const r = row;
15850
- 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);
15788
+ 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);
15851
15789
  }
15852
15790
  function getDefaultSessionMeta(sessionId) {
15853
15791
  return {
@@ -15870,6 +15808,7 @@ function getDefaultSessionMeta(sessionId) {
15870
15808
  conversationTokens: 0,
15871
15809
  toolCallTokens: 0,
15872
15810
  clearedReasoningThroughTag: 0,
15811
+ toolReclaimWatermark: 0,
15873
15812
  lastTodoState: "",
15874
15813
  cachedM0Bytes: null,
15875
15814
  cachedM1Bytes: null,
@@ -15933,6 +15872,7 @@ function toSessionMeta(row) {
15933
15872
  conversationTokens: numOrZero(row.conversation_tokens),
15934
15873
  toolCallTokens: numOrZero(row.tool_call_tokens),
15935
15874
  clearedReasoningThroughTag: numOrZero(row.cleared_reasoning_through_tag),
15875
+ toolReclaimWatermark: numOrZero(row.tool_reclaim_watermark),
15936
15876
  lastTodoState: lastTodoStateRaw,
15937
15877
  cachedM0Bytes: toBufferOrNull(row.cached_m0_bytes),
15938
15878
  cachedM1Bytes: toBufferOrNull(row.cached_m1_bytes),
@@ -16042,6 +15982,7 @@ var init_storage_meta_shared = __esm(() => {
16042
15982
  "conversation_tokens",
16043
15983
  "tool_call_tokens",
16044
15984
  "cleared_reasoning_through_tag",
15985
+ "tool_reclaim_watermark",
16045
15986
  "last_todo_state",
16046
15987
  "cached_m0_bytes",
16047
15988
  "cached_m1_bytes",
@@ -16068,6 +16009,8 @@ var init_storage_meta_shared = __esm(() => {
16068
16009
  "recovery_no_eligible_head_count",
16069
16010
  "force_emergency_bypass_window_start",
16070
16011
  "force_emergency_bypass_used",
16012
+ "emergency_drain_active",
16013
+ "historian_drain_failure_at",
16071
16014
  "upgrade_reminded_at",
16072
16015
  "pi_stable_id_scheme"
16073
16016
  ];
@@ -16090,6 +16033,7 @@ var init_storage_meta_shared = __esm(() => {
16090
16033
  conversationTokens: "conversation_tokens",
16091
16034
  toolCallTokens: "tool_call_tokens",
16092
16035
  clearedReasoningThroughTag: "cleared_reasoning_through_tag",
16036
+ toolReclaimWatermark: "tool_reclaim_watermark",
16093
16037
  lastTodoState: "last_todo_state",
16094
16038
  cachedM0Bytes: "cached_m0_bytes",
16095
16039
  cachedM1Bytes: "cached_m1_bytes",
@@ -16116,6 +16060,8 @@ var init_storage_meta_shared = __esm(() => {
16116
16060
  recoveryNoEligibleHeadCount: "recovery_no_eligible_head_count",
16117
16061
  forceEmergencyBypassWindowStart: "force_emergency_bypass_window_start",
16118
16062
  forceEmergencyBypassUsed: "force_emergency_bypass_used",
16063
+ emergencyDrainActive: "emergency_drain_active",
16064
+ historianDrainFailureAt: "historian_drain_failure_at",
16119
16065
  upgradeRemindedAt: "upgrade_reminded_at",
16120
16066
  piStableIdScheme: "pi_stable_id_scheme"
16121
16067
  };
@@ -16366,6 +16312,14 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
16366
16312
  }
16367
16313
  super(typeof filename === "string" ? filename : ":memory:", translated);
16368
16314
  }
16315
+ prepare(sql) {
16316
+ const stmt = super.prepare(sql);
16317
+ for (const method of ["run", "get", "all"]) {
16318
+ const original = stmt[method].bind(stmt);
16319
+ stmt[method] = (...args) => args.length === 1 && Array.isArray(args[0]) ? original(...args[0]) : original(...args);
16320
+ }
16321
+ return stmt;
16322
+ }
16369
16323
  transaction(fn) {
16370
16324
  const self = this;
16371
16325
  const wrapped = function(...args) {
@@ -149342,6 +149296,9 @@ function stripCompleteTagPairsGlobally(value) {
149342
149296
  function stripMalformedTagNotationGlobally(value) {
149343
149297
  return value.replace(MALFORMED_TAG_GLOBAL_REGEX, "");
149344
149298
  }
149299
+ function stripDanglingTagNotationGlobally(value) {
149300
+ return value.replace(DANGLING_TAG_GLOBAL_REGEX, "");
149301
+ }
149345
149302
  function stripTagSectionCharacters(value) {
149346
149303
  return value.replace(STRAY_SECTION_CHAR_REGEX, "");
149347
149304
  }
@@ -149349,6 +149306,7 @@ function stripPersistedAssistantText(value) {
149349
149306
  let text = stripWellFormedLeadingTagPrefix(value);
149350
149307
  text = stripCompleteTagPairsGlobally(text);
149351
149308
  text = stripMalformedTagNotationGlobally(text);
149309
+ text = stripDanglingTagNotationGlobally(text);
149352
149310
  text = stripTagSectionCharacters(text);
149353
149311
  return text.trim();
149354
149312
  }
@@ -149361,6 +149319,7 @@ function stripTagPrefix(value) {
149361
149319
  const prev = stripped;
149362
149320
  stripped = stripped.replace(MALFORMED_TAG_PREFIX_REGEX, "");
149363
149321
  stripped = stripped.replace(TAG_PREFIX_REGEX, "");
149322
+ stripped = stripped.replace(DANGLING_TAG_PREFIX_REGEX, "");
149364
149323
  if (stripped === prev)
149365
149324
  break;
149366
149325
  }
@@ -149382,11 +149341,13 @@ function isThinkingPart(part) {
149382
149341
  const candidate = part;
149383
149342
  return candidate.type === "thinking" || candidate.type === "reasoning";
149384
149343
  }
149385
- var encoder, TAG_PREFIX_REGEX, MALFORMED_TAG_PREFIX_REGEX, COMPLETE_TAG_PAIR_GLOBAL_REGEX, MALFORMED_TAG_GLOBAL_REGEX, STRAY_SECTION_CHAR_REGEX;
149344
+ 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;
149386
149345
  var init_tag_content_primitives = __esm(() => {
149387
149346
  encoder = new TextEncoder;
149388
149347
  TAG_PREFIX_REGEX = /^(?:§\d+§\s*)+/;
149389
149348
  MALFORMED_TAG_PREFIX_REGEX = /^(?:§\d+">§(?:\d+§)?\s*)+/;
149349
+ DANGLING_TAG_GLOBAL_REGEX = /\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?/g;
149350
+ DANGLING_TAG_PREFIX_REGEX = /^(?:\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?\s*)+/;
149390
149351
  COMPLETE_TAG_PAIR_GLOBAL_REGEX = /\u00a7\d+\u00a7/g;
149391
149352
  MALFORMED_TAG_GLOBAL_REGEX = /\u00a7\d+">(?:\u00a7(?:\d+\u00a7)?)?/g;
149392
149353
  STRAY_SECTION_CHAR_REGEX = /\u00a7/g;
@@ -149450,12 +149411,13 @@ function setToolContent(part, content) {
149450
149411
  part.content = content;
149451
149412
  }
149452
149413
  }
149453
- function truncateToolPart(part) {
149414
+ function truncateToolPart(part, tagId) {
149454
149415
  if (!isRecord(part))
149455
149416
  return;
149417
+ const sentinel = `[dropped §${tagId}§]`;
149456
149418
  if (part.type === "tool" && isRecord(part.state)) {
149457
149419
  const state = part.state;
149458
- state.output = "[truncated]";
149420
+ state.output = sentinel;
149459
149421
  if (isRecord(state.input)) {
149460
149422
  const inputSize = estimateInputSize(state.input);
149461
149423
  if (inputSize > 500) {
@@ -149465,7 +149427,7 @@ function truncateToolPart(part) {
149465
149427
  return;
149466
149428
  }
149467
149429
  if (part.type === "tool_result") {
149468
- part.content = "[truncated]";
149430
+ part.content = sentinel;
149469
149431
  return;
149470
149432
  }
149471
149433
  if (part.type === "tool-invocation" && isRecord(part.args)) {
@@ -149582,7 +149544,7 @@ class ToolMutationBatch {
149582
149544
  this.affectedMessages.clear();
149583
149545
  }
149584
149546
  }
149585
- function createToolDropTarget(compositeKey, thinkingParts, index, batch) {
149547
+ function createToolDropTarget(compositeKey, thinkingParts, index, batch, tagId) {
149586
149548
  const drop = () => {
149587
149549
  const entry = index.get(compositeKey);
149588
149550
  if (!entry || entry.occurrences.length === 0)
@@ -149603,7 +149565,7 @@ function createToolDropTarget(compositeKey, thinkingParts, index, batch) {
149603
149565
  if (!entry.hasResult)
149604
149566
  return "incomplete";
149605
149567
  for (const occurrence of entry.occurrences) {
149606
- truncateToolPart(occurrence.part);
149568
+ truncateToolPart(occurrence.part, tagId);
149607
149569
  }
149608
149570
  clearThinkingParts(thinkingParts);
149609
149571
  return "truncated";
@@ -149651,6 +149613,22 @@ var init_tool_drop_target = __esm(() => {
149651
149613
  });
149652
149614
 
149653
149615
  // src/hooks/magic-context/read-session-chunk.ts
149616
+ function estimateBlockTokens(blockText) {
149617
+ const cached2 = blockTokenMemo.get(blockText);
149618
+ if (cached2 !== undefined) {
149619
+ blockTokenMemo.delete(blockText);
149620
+ blockTokenMemo.set(blockText, cached2);
149621
+ return cached2;
149622
+ }
149623
+ const count = estimateTokens(blockText);
149624
+ if (blockTokenMemo.size >= BLOCK_TOKEN_MEMO_MAX) {
149625
+ const oldest = blockTokenMemo.keys().next().value;
149626
+ if (oldest !== undefined)
149627
+ blockTokenMemo.delete(oldest);
149628
+ }
149629
+ blockTokenMemo.set(blockText, count);
149630
+ return count;
149631
+ }
149654
149632
  function cleanUserText(text) {
149655
149633
  return removeSystemReminders(text).replace(OMO_INTERNAL_INITIATOR_MARKER, "").trim();
149656
149634
  }
@@ -149818,7 +149796,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
149818
149796
  if (!currentBlock)
149819
149797
  return true;
149820
149798
  const blockText = formatBlock(currentBlock);
149821
- const blockTokens = estimateTokens(blockText);
149799
+ const blockTokens = estimateBlockTokens(blockText);
149822
149800
  if (totalTokens + blockTokens > tokenBudget && totalTokens > 0) {
149823
149801
  return false;
149824
149802
  }
@@ -149936,13 +149914,14 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
149936
149914
  toolOnlyRanges
149937
149915
  };
149938
149916
  }
149939
- var activeRawMessageCache = null, activeAbsoluteCountCache = null, sessionProviders, PROTECTED_TAIL_USER_TURNS = 5;
149917
+ var BLOCK_TOKEN_MEMO_MAX = 2048, blockTokenMemo, activeRawMessageCache = null, activeAbsoluteCountCache = null, sessionProviders, PROTECTED_TAIL_USER_TURNS = 5;
149940
149918
  var init_read_session_chunk = __esm(async () => {
149941
149919
  init_read_session_formatting();
149942
149920
  init_tag_part_guards();
149943
149921
  init_tool_drop_target();
149944
149922
  init_read_session_formatting();
149945
149923
  await init_read_session_db();
149924
+ blockTokenMemo = new Map;
149946
149925
  sessionProviders = new Map;
149947
149926
  });
149948
149927
 
@@ -150687,6 +150666,9 @@ function resolveDatabasePath(dbPathOverride) {
150687
150666
  const dbDir = getMagicContextStorageDir();
150688
150667
  return { dbDir, dbPath: join6(dbDir, "context.db") };
150689
150668
  }
150669
+ function getDatabasePath(db) {
150670
+ return pathByDatabase.get(db) ?? null;
150671
+ }
150690
150672
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
150691
150673
  if (existsSync7(targetDbPath))
150692
150674
  return;
@@ -151197,6 +151179,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151197
151179
  recovery_no_eligible_head_count INTEGER NOT NULL DEFAULT 0,
151198
151180
  force_emergency_bypass_window_start INTEGER NOT NULL DEFAULT 0,
151199
151181
  force_emergency_bypass_used INTEGER NOT NULL DEFAULT 0,
151182
+ emergency_drain_active INTEGER NOT NULL DEFAULT 0,
151183
+ historian_drain_failure_at INTEGER NOT NULL DEFAULT 0,
151200
151184
  cached_m0_materialized_at INTEGER,
151201
151185
  cached_m0_session_facts_version INTEGER,
151202
151186
  cached_m0_upgrade_state TEXT,
@@ -151260,6 +151244,23 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151260
151244
  CREATE INDEX IF NOT EXISTS idx_historian_runs_status
151261
151245
  ON historian_runs(status, created_at DESC);
151262
151246
 
151247
+ CREATE TABLE IF NOT EXISTS transform_decisions (
151248
+ session_id TEXT NOT NULL,
151249
+ harness TEXT NOT NULL DEFAULT 'opencode',
151250
+ message_id TEXT NOT NULL,
151251
+ ts_ms INTEGER NOT NULL,
151252
+ decision TEXT NOT NULL,
151253
+ materialized INTEGER NOT NULL DEFAULT 0,
151254
+ materialize_reason TEXT,
151255
+ emergency INTEGER NOT NULL DEFAULT 0,
151256
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
151257
+ dropped_count INTEGER NOT NULL DEFAULT 0,
151258
+ input_tokens INTEGER NOT NULL DEFAULT 0,
151259
+ PRIMARY KEY (session_id, harness, message_id)
151260
+ );
151261
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
151262
+ ON transform_decisions(session_id, harness);
151263
+
151263
151264
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
151264
151265
  CREATE INDEX IF NOT EXISTS idx_tags_session_message_id ON tags(session_id, message_id);
151265
151266
  CREATE INDEX IF NOT EXISTS idx_pending_ops_session ON pending_ops(session_id);
@@ -151337,6 +151338,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151337
151338
  ensureColumn(db, "session_meta", "historian_last_failure_at", "INTEGER DEFAULT NULL");
151338
151339
  ensureColumn(db, "session_meta", "system_prompt_hash", "TEXT DEFAULT ''");
151339
151340
  ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
151341
+ ensureColumn(db, "session_meta", "tool_reclaim_watermark", "INTEGER DEFAULT 0");
151340
151342
  ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
151341
151343
  ensureColumn(db, "session_meta", "stale_reduce_stripped_ids", "TEXT DEFAULT ''");
151342
151344
  ensureColumn(db, "session_meta", "processed_image_stripped_ids", "TEXT DEFAULT ''");
@@ -151410,6 +151412,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151410
151412
  ensureColumn(db, "session_meta", "recovery_no_eligible_head_count", "INTEGER NOT NULL DEFAULT 0");
151411
151413
  ensureColumn(db, "session_meta", "force_emergency_bypass_window_start", "INTEGER NOT NULL DEFAULT 0");
151412
151414
  ensureColumn(db, "session_meta", "force_emergency_bypass_used", "INTEGER NOT NULL DEFAULT 0");
151415
+ ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
151416
+ ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
151413
151417
  ensureColumn(db, "session_meta", "cached_m0_materialized_at", "INTEGER");
151414
151418
  ensureColumn(db, "session_meta", "cached_m0_session_facts_version", "INTEGER");
151415
151419
  ensureColumn(db, "session_meta", "cached_m0_upgrade_state", "TEXT");
@@ -151488,6 +151492,22 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151488
151492
  failed_at INTEGER NOT NULL,
151489
151493
  UNIQUE(table_name, row_id)
151490
151494
  );
151495
+ CREATE TABLE IF NOT EXISTS transform_decisions (
151496
+ session_id TEXT NOT NULL,
151497
+ harness TEXT NOT NULL DEFAULT 'opencode',
151498
+ message_id TEXT NOT NULL,
151499
+ ts_ms INTEGER NOT NULL,
151500
+ decision TEXT NOT NULL,
151501
+ materialized INTEGER NOT NULL DEFAULT 0,
151502
+ materialize_reason TEXT,
151503
+ emergency INTEGER NOT NULL DEFAULT 0,
151504
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
151505
+ dropped_count INTEGER NOT NULL DEFAULT 0,
151506
+ input_tokens INTEGER NOT NULL DEFAULT 0,
151507
+ PRIMARY KEY (session_id, harness, message_id)
151508
+ );
151509
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
151510
+ ON transform_decisions(session_id, harness);
151491
151511
  `);
151492
151512
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
151493
151513
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -151573,7 +151593,9 @@ function healNullIntegerColumns(db) {
151573
151593
  ["protected_tail_drain_tokens", 0],
151574
151594
  ["recovery_no_eligible_head_count", 0],
151575
151595
  ["force_emergency_bypass_window_start", 0],
151576
- ["force_emergency_bypass_used", 0]
151596
+ ["force_emergency_bypass_used", 0],
151597
+ ["emergency_drain_active", 0],
151598
+ ["historian_drain_failure_at", 0]
151577
151599
  ];
151578
151600
  for (const [column, fallback] of columns) {
151579
151601
  try {
@@ -151649,6 +151671,7 @@ function openDatabase(dbPathOrOptions) {
151649
151671
  setDatabase(db);
151650
151672
  loadToolDefinitionMeasurements(db);
151651
151673
  databases.set(dbPath, db);
151674
+ pathByDatabase.set(db, dbPath);
151652
151675
  persistenceByDatabase.set(db, true);
151653
151676
  persistenceErrorByDatabase.delete(db);
151654
151677
  return db;
@@ -151668,7 +151691,7 @@ function getDatabasePersistenceError(db) {
151668
151691
  return null;
151669
151692
  return persistenceErrorByDatabase.get(db) ?? null;
151670
151693
  }
151671
- var databases, persistenceByDatabase, persistenceErrorByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 36, sqlitePragmaConfig, CHANNEL2_CLAIM_TTL_MS = 120000;
151694
+ var databases, persistenceByDatabase, persistenceErrorByDatabase, pathByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 38, sqlitePragmaConfig, CHANNEL2_CLAIM_TTL_MS = 120000;
151672
151695
  var init_storage_db = __esm(async () => {
151673
151696
  init_data_path();
151674
151697
  init_logger();
@@ -151682,6 +151705,7 @@ var init_storage_db = __esm(async () => {
151682
151705
  databases = new Map;
151683
151706
  persistenceByDatabase = new WeakMap;
151684
151707
  persistenceErrorByDatabase = new WeakMap;
151708
+ pathByDatabase = new WeakMap;
151685
151709
  sqlitePragmaConfig = {
151686
151710
  cacheSizeMb: 64,
151687
151711
  mmapSizeMb: 0
@@ -153000,6 +153024,41 @@ var init_migrations = __esm(async () => {
153000
153024
  `);
153001
153025
  }
153002
153026
  }
153027
+ },
153028
+ {
153029
+ version: 37,
153030
+ description: "emergency drain catch-up latch + historian drain failure backoff",
153031
+ up: (db) => {
153032
+ const hasSessionMeta = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='session_meta'").get();
153033
+ if (!hasSessionMeta)
153034
+ return;
153035
+ ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
153036
+ ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
153037
+ }
153038
+ },
153039
+ {
153040
+ version: 38,
153041
+ description: "durable transform decisions for cache-event cause attribution",
153042
+ up: (db) => {
153043
+ db.exec(`
153044
+ CREATE TABLE IF NOT EXISTS transform_decisions (
153045
+ session_id TEXT NOT NULL,
153046
+ harness TEXT NOT NULL DEFAULT 'opencode',
153047
+ message_id TEXT NOT NULL,
153048
+ ts_ms INTEGER NOT NULL,
153049
+ decision TEXT NOT NULL,
153050
+ materialized INTEGER NOT NULL DEFAULT 0,
153051
+ materialize_reason TEXT,
153052
+ emergency INTEGER NOT NULL DEFAULT 0,
153053
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
153054
+ dropped_count INTEGER NOT NULL DEFAULT 0,
153055
+ input_tokens INTEGER NOT NULL DEFAULT 0,
153056
+ PRIMARY KEY (session_id, harness, message_id)
153057
+ );
153058
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
153059
+ ON transform_decisions(session_id, harness);
153060
+ `);
153061
+ }
153003
153062
  }
153004
153063
  ];
153005
153064
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -153404,7 +153463,9 @@ function toProtectedTailMeta(row) {
153404
153463
  protectedTailDrainTokens: numberOr(r.protected_tail_drain_tokens, 0),
153405
153464
  recoveryNoEligibleHeadCount: numberOr(r.recovery_no_eligible_head_count, 0),
153406
153465
  forceEmergencyBypassWindowStart: numberOr(r.force_emergency_bypass_window_start, 0),
153407
- forceEmergencyBypassUsed: numberOr(r.force_emergency_bypass_used, 0)
153466
+ forceEmergencyBypassUsed: numberOr(r.force_emergency_bypass_used, 0),
153467
+ emergencyDrainActive: numberOr(r.emergency_drain_active, 0),
153468
+ historianDrainFailureAt: numberOr(r.historian_drain_failure_at, 0)
153408
153469
  };
153409
153470
  }
153410
153471
  function loadProtectedTailMeta(db, sessionId) {
@@ -153412,7 +153473,7 @@ function loadProtectedTailMeta(db, sessionId) {
153412
153473
  const row = db.prepare(`SELECT prior_boundary_ordinal, protected_tail_policy_version,
153413
153474
  protected_tail_drain_window_started_at, protected_tail_drain_tokens,
153414
153475
  recovery_no_eligible_head_count, force_emergency_bypass_window_start,
153415
- force_emergency_bypass_used
153476
+ force_emergency_bypass_used, emergency_drain_active, historian_drain_failure_at
153416
153477
  FROM session_meta WHERE session_id = ?`).get(sessionId);
153417
153478
  return toProtectedTailMeta(row);
153418
153479
  }
@@ -153461,6 +153522,12 @@ function protectedTailWindowBudget(usagePercentage, usable, perRunCap) {
153461
153522
  return Math.min(750000, Math.max(3 * perRunCap, Math.round(0.35 * usable)));
153462
153523
  return Math.min(500000, Math.max(perRunCap, Math.round(0.2 * usable)));
153463
153524
  }
153525
+ function emergencyDrainExitThreshold(executeThresholdPercentage) {
153526
+ if (!Number.isFinite(executeThresholdPercentage) || executeThresholdPercentage <= 0) {
153527
+ return EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE;
153528
+ }
153529
+ return Math.max(0, executeThresholdPercentage - EMERGENCY_DRAIN_EXIT_MARGIN);
153530
+ }
153464
153531
  function reserveProtectedTailDrainTokens(args) {
153465
153532
  const now = args.now ?? Date.now();
153466
153533
  const requested = Math.max(0, Math.floor(args.trueRawTokens));
@@ -153479,18 +153546,30 @@ function reserveProtectedTailDrainTokens(args) {
153479
153546
  let meta3 = loadProtectedTailMeta(args.db, args.sessionId);
153480
153547
  if (now - meta3.protectedTailDrainWindowStartedAt > DRAIN_WINDOW_MS) {
153481
153548
  args.db.prepare(`UPDATE session_meta
153482
- SET protected_tail_drain_window_started_at = ?, protected_tail_drain_tokens = 0,
153483
- force_emergency_bypass_window_start = ?, force_emergency_bypass_used = 0
153484
- WHERE session_id = ?`).run(now, now, args.sessionId);
153549
+ SET protected_tail_drain_window_started_at = ?, protected_tail_drain_tokens = 0
153550
+ WHERE session_id = ?`).run(now, args.sessionId);
153485
153551
  meta3 = loadProtectedTailMeta(args.db, args.sessionId);
153486
153552
  }
153553
+ const exitThreshold = emergencyDrainExitThreshold(args.executeThresholdPercentage);
153554
+ let latchActiveSince = meta3.emergencyDrainActive;
153555
+ if (args.usagePercentage >= EMERGENCY_DRAIN_ENTER_PERCENTAGE) {
153556
+ if (latchActiveSince <= 0)
153557
+ latchActiveSince = now;
153558
+ } else if (latchActiveSince > 0) {
153559
+ const expired = now - latchActiveSince > EMERGENCY_DRAIN_MAX_LATCH_MS;
153560
+ if (args.usagePercentage < exitThreshold || expired)
153561
+ latchActiveSince = 0;
153562
+ }
153563
+ if (latchActiveSince !== meta3.emergencyDrainActive) {
153564
+ args.db.prepare("UPDATE session_meta SET emergency_drain_active = ? WHERE session_id = ?").run(latchActiveSince, args.sessionId);
153565
+ }
153566
+ const latchActive = latchActiveSince > 0;
153487
153567
  const budget = protectedTailWindowBudget(args.usagePercentage, args.usable, args.perRunCap);
153488
153568
  const remaining = Math.max(0, budget - meta3.protectedTailDrainTokens);
153489
153569
  let reserved = Math.min(requested, args.perRunCap, remaining);
153490
153570
  let bypass = false;
153491
- const bypassWindowExpired = now - meta3.forceEmergencyBypassWindowStart > DRAIN_WINDOW_MS;
153492
- const bypassUsed = bypassWindowExpired ? 0 : meta3.forceEmergencyBypassUsed;
153493
- if (reserved <= 0 && args.usagePercentage >= 95 && bypassUsed === 0) {
153571
+ const inFailureBackoff = meta3.historianDrainFailureAt > 0 && now - meta3.historianDrainFailureAt < EMERGENCY_DRAIN_FAILURE_BACKOFF_MS;
153572
+ if (reserved <= 0 && latchActive && !inFailureBackoff) {
153494
153573
  reserved = Math.min(requested, args.perRunCap);
153495
153574
  bypass = true;
153496
153575
  }
@@ -153498,10 +153577,8 @@ function reserveProtectedTailDrainTokens(args) {
153498
153577
  return;
153499
153578
  args.db.prepare(`UPDATE session_meta
153500
153579
  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,
153501
- protected_tail_drain_tokens = COALESCE(protected_tail_drain_tokens, 0) + ?,
153502
- force_emergency_bypass_window_start = CASE WHEN ? THEN ? ELSE force_emergency_bypass_window_start END,
153503
- force_emergency_bypass_used = CASE WHEN ? THEN 1 ELSE force_emergency_bypass_used END
153504
- WHERE session_id = ?`).run(now, reserved, bypass ? 1 : 0, now, bypass ? 1 : 0, args.sessionId);
153580
+ protected_tail_drain_tokens = COALESCE(protected_tail_drain_tokens, 0) + ?
153581
+ WHERE session_id = ?`).run(now, reserved, args.sessionId);
153505
153582
  result = {
153506
153583
  ok: true,
153507
153584
  reservedTokens: reserved,
@@ -153511,6 +153588,25 @@ function reserveProtectedTailDrainTokens(args) {
153511
153588
  })();
153512
153589
  return result;
153513
153590
  }
153591
+ function clearEmergencyDrainLatch(db, sessionId) {
153592
+ db.transaction(() => {
153593
+ ensureSessionMetaRow(db, sessionId);
153594
+ db.prepare("UPDATE session_meta SET emergency_drain_active = 0 WHERE session_id = ?").run(sessionId);
153595
+ })();
153596
+ }
153597
+ function recordHistorianDrainFailure(db, sessionId, now) {
153598
+ const ts = now ?? Date.now();
153599
+ db.transaction(() => {
153600
+ ensureSessionMetaRow(db, sessionId);
153601
+ db.prepare("UPDATE session_meta SET historian_drain_failure_at = ? WHERE session_id = ?").run(ts, sessionId);
153602
+ })();
153603
+ }
153604
+ function clearHistorianDrainFailure(db, sessionId) {
153605
+ db.transaction(() => {
153606
+ ensureSessionMetaRow(db, sessionId);
153607
+ db.prepare("UPDATE session_meta SET historian_drain_failure_at = 0 WHERE session_id = ?").run(sessionId);
153608
+ })();
153609
+ }
153514
153610
  function rollbackProtectedTailDrainReservation(db, reservation) {
153515
153611
  if (!reservation || reservation.tokens <= 0)
153516
153612
  return;
@@ -154078,7 +154174,7 @@ function setSessionWorkMetrics(db, sessionId, newWorkTokens, totalInputTokens) {
154078
154174
  SET new_work_tokens = ?, total_input_tokens = ?
154079
154175
  WHERE session_id = ?`).run(Math.max(0, Math.floor(newWorkTokens)), Math.max(0, Math.floor(totalInputTokens)), sessionId);
154080
154176
  }
154081
- var CAS_RETRY_LIMIT = 5, AUTO_SEARCH_NO_HINT_REASONS, DEFAULT_PROTECTED_TAIL_META, DRAIN_WINDOW_MS;
154177
+ 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;
154082
154178
  var init_storage_meta_persisted = __esm(() => {
154083
154179
  init_logger();
154084
154180
  init_storage_meta_shared();
@@ -154097,9 +154193,12 @@ var init_storage_meta_persisted = __esm(() => {
154097
154193
  protectedTailDrainTokens: 0,
154098
154194
  recoveryNoEligibleHeadCount: 0,
154099
154195
  forceEmergencyBypassWindowStart: 0,
154100
- forceEmergencyBypassUsed: 0
154196
+ forceEmergencyBypassUsed: 0,
154197
+ emergencyDrainActive: 0,
154198
+ historianDrainFailureAt: 0
154101
154199
  };
154102
154200
  DRAIN_WINDOW_MS = 10 * 60 * 1000;
154201
+ EMERGENCY_DRAIN_MAX_LATCH_MS = 30 * 60 * 1000;
154103
154202
  });
154104
154203
 
154105
154204
  // src/features/magic-context/resolve-subagent-fallback.ts
@@ -154126,7 +154225,8 @@ var exports_storage_meta_session = {};
154126
154225
  __export(exports_storage_meta_session, {
154127
154226
  updateSessionMeta: () => updateSessionMeta,
154128
154227
  getOrCreateSessionMeta: () => getOrCreateSessionMeta,
154129
- clearSession: () => clearSession
154228
+ clearSession: () => clearSession,
154229
+ advanceToolReclaimWatermark: () => advanceToolReclaimWatermark
154130
154230
  });
154131
154231
  import { Buffer as Buffer3 } from "node:buffer";
154132
154232
  function getSessionMetaSelectColumns(db) {
@@ -154187,6 +154287,14 @@ function updateSessionMeta(db, sessionId, updates) {
154187
154287
  db.prepare(`UPDATE session_meta SET ${setClauses.join(", ")} WHERE session_id = ?`).run(...values, sessionId);
154188
154288
  })();
154189
154289
  }
154290
+ function advanceToolReclaimWatermark(db, sessionId, maxTagNumber) {
154291
+ if (maxTagNumber <= 0)
154292
+ return;
154293
+ db.transaction(() => {
154294
+ ensureSessionMetaRow(db, sessionId);
154295
+ db.prepare("UPDATE session_meta SET tool_reclaim_watermark = MAX(COALESCE(tool_reclaim_watermark, 0), ?) WHERE session_id = ?").run(maxTagNumber, sessionId);
154296
+ })();
154297
+ }
154190
154298
  function clearSession(db, sessionId) {
154191
154299
  db.transaction(() => {
154192
154300
  db.prepare("DELETE FROM pending_ops WHERE session_id = ?").run(sessionId);
@@ -154208,6 +154316,7 @@ function clearSession(db, sessionId) {
154208
154316
  db.prepare("DELETE FROM subagent_invocations WHERE session_id = ?").run(sessionId);
154209
154317
  db.prepare("DELETE FROM historian_runs WHERE session_id = ?").run(sessionId);
154210
154318
  db.prepare("DELETE FROM plugin_messages WHERE session_id = ?").run(sessionId);
154319
+ db.prepare("DELETE FROM transform_decisions WHERE session_id = ?").run(sessionId);
154211
154320
  clearIndexedMessages(db, sessionId);
154212
154321
  })();
154213
154322
  }
@@ -154225,6 +154334,7 @@ var init_storage_meta_session = __esm(async () => {
154225
154334
  last_transform_error: "'' AS last_transform_error",
154226
154335
  system_prompt_hash: "'' AS system_prompt_hash",
154227
154336
  last_todo_state: "'' AS last_todo_state",
154337
+ tool_reclaim_watermark: "0 AS tool_reclaim_watermark",
154228
154338
  cached_m0_bytes: "NULL AS cached_m0_bytes",
154229
154339
  cached_m1_bytes: "NULL AS cached_m1_bytes",
154230
154340
  cached_m0_project_memory_epoch: "NULL AS cached_m0_project_memory_epoch",
@@ -154632,12 +154742,37 @@ function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
154632
154742
  nullCount: row?.null_count ?? 0
154633
154743
  };
154634
154744
  }
154635
- function getTriggerTagTokenUpperBound(db, sessionId) {
154636
- const row = db.prepare(`SELECT
154745
+ function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, limit = 4) {
154746
+ if (limit <= 0)
154747
+ return [];
154748
+ const boundedLimit = Math.max(1, Math.min(10, Math.floor(limit)));
154749
+ const whereProtected = protectedTags > 0 ? `AND tag_number < (
154750
+ SELECT tag_number FROM tags
154751
+ WHERE session_id = ? AND status = 'active'
154752
+ ORDER BY tag_number DESC LIMIT 1 OFFSET ?
154753
+ )` : "";
154754
+ const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
154755
+ const rows = db.prepare(`SELECT tag_number, tool_name
154756
+ FROM tags
154757
+ WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
154758
+ ORDER BY tag_number ASC, id ASC
154759
+ LIMIT ?`).all(...params);
154760
+ return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
154761
+ tagNumber: row.tag_number,
154762
+ toolName: typeof row.tool_name === "string" ? row.tool_name : null
154763
+ }));
154764
+ }
154765
+ function getTriggerTagTokenUpperBound(db, sessionId, floor = 0) {
154766
+ const sql = floor > 0 ? `SELECT
154767
+ COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
154768
+ COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
154769
+ FROM tags
154770
+ WHERE session_id = ? AND status IN ('active', 'dropped') AND tag_number >= ?` : `SELECT
154637
154771
  COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
154638
154772
  COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
154639
154773
  FROM tags
154640
- WHERE session_id = ? AND status IN ('active', 'dropped')`).get(sessionId);
154774
+ WHERE session_id = ? AND status IN ('active', 'dropped')`;
154775
+ const row = floor > 0 ? db.prepare(sql).get(sessionId, floor) : db.prepare(sql).get(sessionId);
154641
154776
  return { bound: row?.bound ?? 0, nullCount: row?.null_count ?? 0 };
154642
154777
  }
154643
154778
  function getActiveTagTokenTotalsByMessage(db, sessionId) {
@@ -154666,10 +154801,12 @@ function getActiveTagTokenTotalsByMessage(db, sessionId) {
154666
154801
  }
154667
154802
  return out;
154668
154803
  }
154669
- function getAllStatusTagTokenTotalsFlat(db, sessionId) {
154670
- const rows = db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
154671
- FROM tags
154672
- WHERE session_id = ?`).all(sessionId);
154804
+ function getAllStatusTagTokenTotalsFlat(db, sessionId, floor = 0) {
154805
+ const rows = floor > 0 ? db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
154806
+ FROM tags
154807
+ 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
154808
+ FROM tags
154809
+ WHERE session_id = ?`).all(sessionId);
154673
154810
  const totals = new Map;
154674
154811
  const nullMessageIds = new Set;
154675
154812
  for (const row of rows) {
@@ -154828,6 +154965,47 @@ function getTagNumberByMessageId(db, sessionId, messageId) {
154828
154965
  const row = getTagNumberByMessageIdStatement(db).get(sessionId, messageId);
154829
154966
  return isTagNumberRow(row) ? row.tag_number : null;
154830
154967
  }
154968
+ function isMinTagNumberRow(row) {
154969
+ return row !== null && typeof row === "object" && "m" in row;
154970
+ }
154971
+ function getMinMessageTagNumberForRawId(db, sessionId, rawId) {
154972
+ if (rawId.includes(":"))
154973
+ return null;
154974
+ let stmt = getMinMessageTagNumberForRawIdStatements.get(db);
154975
+ if (!stmt) {
154976
+ stmt = db.prepare("SELECT MIN(tag_number) AS m FROM tags WHERE session_id = ? AND message_id >= ? AND message_id < ?");
154977
+ getMinMessageTagNumberForRawIdStatements.set(db, stmt);
154978
+ }
154979
+ const row = stmt.get(sessionId, `${rawId}:`, `${rawId};`);
154980
+ return isMinTagNumberRow(row) && typeof row.m === "number" ? row.m : null;
154981
+ }
154982
+ function deriveTagLoadFloor(db, sessionId, rawIds) {
154983
+ let min = Number.POSITIVE_INFINITY;
154984
+ let probes = 0;
154985
+ let hits = 0;
154986
+ let skippedBeforeFirstHit = 0;
154987
+ for (const rawId of rawIds) {
154988
+ if (typeof rawId !== "string" || rawId.length === 0)
154989
+ continue;
154990
+ if (probes >= TAGGER_FLOOR_MAX_PROBES)
154991
+ break;
154992
+ probes++;
154993
+ const m = getMinMessageTagNumberForRawId(db, sessionId, rawId);
154994
+ if (m === null) {
154995
+ if (hits === 0)
154996
+ skippedBeforeFirstHit++;
154997
+ continue;
154998
+ }
154999
+ if (m < min)
155000
+ min = m;
155001
+ if (++hits >= TAGGER_FLOOR_SCAN_MESSAGES)
155002
+ break;
155003
+ }
155004
+ if (!Number.isFinite(min))
155005
+ return 0;
155006
+ const margin = TAGGER_FLOOR_SAFETY_MARGIN + skippedBeforeFirstHit * TAGGER_FLOOR_PER_SKIP_MARGIN;
155007
+ return Math.max(0, min - margin);
155008
+ }
154831
155009
  function getTagsBySession(db, sessionId) {
154832
155010
  const rows = db.prepare(`SELECT ${TAG_SELECT_COLUMNS} FROM tags WHERE session_id = ? ORDER BY tag_number ASC, id ASC`).all(sessionId).filter(isTagRow);
154833
155011
  return rows.map(toTagEntry);
@@ -154966,7 +155144,7 @@ function deleteToolTagsByOwner(db, sessionId, ownerMsgId) {
154966
155144
  const result = getDeleteToolTagsByOwnerStatement(db).run(sessionId, ownerMsgId);
154967
155145
  return result.changes ?? 0;
154968
155146
  }
154969
- 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;
155147
+ 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;
154970
155148
  var init_storage_tags = __esm(() => {
154971
155149
  insertTagStatements = new WeakMap;
154972
155150
  updateTagStatusStatements = new WeakMap;
@@ -154982,6 +155160,7 @@ var init_storage_tags = __esm(() => {
154982
155160
  updateTagTokenCountStatements = new WeakMap;
154983
155161
  updateTagInputTokenCountStatements = new WeakMap;
154984
155162
  getOwnerScopedToolTagNumbersStatements = new WeakMap;
155163
+ getMinMessageTagNumberForRawIdStatements = new WeakMap;
154985
155164
  getActiveTagsBySessionStatements = new WeakMap;
154986
155165
  getMaxDroppedTagNumberStatements = new WeakMap;
154987
155166
  getToolTagNumberByOwnerStatements = new WeakMap;
@@ -156218,6 +156397,58 @@ var init_safe_notification_target = __esm(() => {
156218
156397
  DEFAULT_TITLE_RE = /^(New session - |Child session - )\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
156219
156398
  });
156220
156399
 
156400
+ // src/shared/rpc-notifications.ts
156401
+ var exports_rpc_notifications = {};
156402
+ __export(exports_rpc_notifications, {
156403
+ pushNotification: () => pushNotification,
156404
+ isTuiConnected: () => isTuiConnected,
156405
+ drainNotifications: () => drainNotifications
156406
+ });
156407
+ function pushNotification(type, payload, sessionId) {
156408
+ queue.push({ id: nextNotificationId++, type, payload, sessionId });
156409
+ if (queue.length > 100) {
156410
+ const newestPerSession = new Map;
156411
+ for (const n of queue) {
156412
+ const prev = newestPerSession.get(n.sessionId);
156413
+ if (prev === undefined || n.id > prev) {
156414
+ newestPerSession.set(n.sessionId, n.id);
156415
+ }
156416
+ }
156417
+ const mustKeep = new Set(newestPerSession.values());
156418
+ const byNewest = [...queue].sort((a, b) => b.id - a.id);
156419
+ const kept = [];
156420
+ for (const n of byNewest) {
156421
+ if (kept.length < 50 || mustKeep.has(n.id))
156422
+ kept.push(n);
156423
+ }
156424
+ queue = kept.sort((a, b) => a.id - b.id);
156425
+ }
156426
+ }
156427
+ function drainNotifications(lastReceivedId = 0, sessionId) {
156428
+ const now = Date.now();
156429
+ lastDrainAtAny = now;
156430
+ if (sessionId !== undefined)
156431
+ lastDrainAtBySession.set(sessionId, now);
156432
+ const matchesClient = (notification) => sessionId === undefined || notification.sessionId === undefined || notification.sessionId === sessionId;
156433
+ if (lastReceivedId > 0) {
156434
+ queue = queue.filter((notification) => !(notification.id <= lastReceivedId && matchesClient(notification)));
156435
+ }
156436
+ return queue.filter((notification) => notification.id > lastReceivedId && matchesClient(notification));
156437
+ }
156438
+ function isTuiConnected(sessionId) {
156439
+ const now = Date.now();
156440
+ if (sessionId !== undefined) {
156441
+ const at = lastDrainAtBySession.get(sessionId) ?? 0;
156442
+ return at > 0 && now - at < TUI_CONNECTED_WINDOW_MS;
156443
+ }
156444
+ return lastDrainAtAny > 0 && now - lastDrainAtAny < TUI_CONNECTED_WINDOW_MS;
156445
+ }
156446
+ var queue, nextNotificationId = 1, lastDrainAtBySession, lastDrainAtAny = 0, TUI_CONNECTED_WINDOW_MS = 3000;
156447
+ var init_rpc_notifications = __esm(() => {
156448
+ queue = [];
156449
+ lastDrainAtBySession = new Map;
156450
+ });
156451
+
156221
156452
  // src/plugin/conflict-warning-hook.ts
156222
156453
  var exports_conflict_warning_hook = {};
156223
156454
  __export(exports_conflict_warning_hook, {
@@ -156552,6 +156783,9 @@ async function sendStartupAnnouncement(client, directory, version2, features, fo
156552
156783
  if (!sessionId) {
156553
156784
  return;
156554
156785
  }
156786
+ const { isTuiConnected: isTuiConnected2 } = await Promise.resolve().then(() => (init_rpc_notifications(), exports_rpc_notifications));
156787
+ if (isTuiConnected2(sessionId) || isTuiConnected2())
156788
+ return;
156555
156789
  if (await waitForSafeNotificationTarget(client, sessionId) === "skip")
156556
156790
  return;
156557
156791
  const bullets = features.map((line) => ` • ${line}`).join(`
@@ -164531,6 +164765,20 @@ function getDistinctStoredModelIds(db, projectPath) {
164531
164765
  const rows = getDistinctStoredModelIdsStatement(db).all(projectPath);
164532
164766
  return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
164533
164767
  }
164768
+ function getMemoryEmbedCoverage(db, projectPath, modelId) {
164769
+ const row = db.prepare(`SELECT
164770
+ COUNT(*) AS total,
164771
+ SUM(CASE WHEN EXISTS (
164772
+ SELECT 1 FROM memory_embeddings e
164773
+ WHERE e.memory_id = m.id AND e.model_id = ?
164774
+ ) THEN 1 ELSE 0 END) AS embedded
164775
+ FROM memories m
164776
+ WHERE m.project_path = ? AND m.status = 'active'`).get(modelId, projectPath);
164777
+ return {
164778
+ total: typeof row?.total === "number" ? row.total : 0,
164779
+ embedded: typeof row?.embedded === "number" ? row.embedded : 0
164780
+ };
164781
+ }
164534
164782
  var saveEmbeddingStatements, loadAllEmbeddingsStatements, deleteEmbeddingStatements, getStoredModelIdStatements, clearAllEmbeddingsStatements, getDistinctStoredModelIdsStatements;
164535
164783
  var init_storage_memory_embeddings = __esm(() => {
164536
164784
  saveEmbeddingStatements = new WeakMap;
@@ -165333,6 +165581,16 @@ function buildCanonicalChunkTextFromFts(db, sessionId, startOrdinal, endOrdinal)
165333
165581
  return lines.join(`
165334
165582
  `);
165335
165583
  }
165584
+ function buildCompartmentSummaryFallbackText(db, compartmentId) {
165585
+ const row = db.prepare("SELECT title, p1, content FROM compartments WHERE id = ?").get(compartmentId);
165586
+ if (!row)
165587
+ return "";
165588
+ const title = typeof row.title === "string" ? row.title.trim() : "";
165589
+ const p1 = typeof row.p1 === "string" ? row.p1.trim() : "";
165590
+ const body = p1.length > 0 ? p1 : typeof row.content === "string" ? row.content.trim() : "";
165591
+ return [title, body].filter((s) => s.length > 0).join(`
165592
+ `);
165593
+ }
165336
165594
  function canonicalizeInMemoryChunkTextForEmbedding(chunkText, startOrdinal, endOrdinal) {
165337
165595
  const lines = [];
165338
165596
  for (const rawLine of chunkText.split(/\r?\n/)) {
@@ -165575,6 +165833,28 @@ function countUnembeddedSessionCompartments(db, projectPath, sessionId, modelId)
165575
165833
  )`).get(projectPath, sessionId, projectPath, modelId);
165576
165834
  return typeof row?.n === "number" ? row.n : 0;
165577
165835
  }
165836
+ function countSessionCompartmentEmbedCoverage(db, projectPath, sessionId, modelId) {
165837
+ const row = db.prepare(`SELECT
165838
+ COUNT(*) AS total,
165839
+ SUM(CASE WHEN EXISTS (
165840
+ SELECT 1 FROM compartment_chunk_embeddings e
165841
+ WHERE e.compartment_id = c.id
165842
+ AND e.project_path = ?
165843
+ AND e.model_id = ?
165844
+ ) THEN 1 ELSE 0 END) AS embedded
165845
+ FROM compartments c
165846
+ JOIN session_projects sp
165847
+ ON sp.session_id = c.session_id
165848
+ AND sp.harness = c.harness
165849
+ AND sp.project_path = ?
165850
+ WHERE c.session_id = ?
165851
+ AND c.start_message IS NOT NULL
165852
+ AND c.end_message IS NOT NULL`).get(projectPath, modelId, projectPath, sessionId);
165853
+ return {
165854
+ total: typeof row?.total === "number" ? row.total : 0,
165855
+ embedded: typeof row?.embedded === "number" ? row.embedded : 0
165856
+ };
165857
+ }
165578
165858
  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;
165579
165859
  var init_compartment_chunk_embedding = __esm(() => {
165580
165860
  init_read_session_formatting();
@@ -165732,6 +166012,18 @@ async function withQuietConsole(fn) {
165732
166012
  console.error = origError;
165733
166013
  }
165734
166014
  }
166015
+ function isNativeRuntimeMissingError(error51) {
166016
+ const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
166017
+ const lower = message.toLowerCase();
166018
+ const code = error51?.code;
166019
+ const name2 = error51?.name;
166020
+ if (code === "ERR_DLOPEN_FAILED" && lower.includes("onnxruntime")) {
166021
+ return true;
166022
+ }
166023
+ if (!lower.includes("onnxruntime-node"))
166024
+ return false;
166025
+ return code === "ERR_MODULE_NOT_FOUND" || name2 === "ResolveMessage" || lower.includes("cannot find package") || lower.includes("cannot find module") || lower.includes("err_module_not_found");
166026
+ }
165735
166027
  function isTransientLoadError(error51) {
165736
166028
  const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
165737
166029
  if (!message)
@@ -165796,6 +166088,9 @@ class LocalEmbeddingProvider {
165796
166088
  if (this.pipeline) {
165797
166089
  return true;
165798
166090
  }
166091
+ if (nativeRuntimeMissing) {
166092
+ return false;
166093
+ }
165799
166094
  if (this.initPromise) {
165800
166095
  await this.initPromise;
165801
166096
  return this.pipeline !== null;
@@ -165862,7 +166157,12 @@ class LocalEmbeddingProvider {
165862
166157
  await releaseLock();
165863
166158
  }
165864
166159
  } catch (error51) {
165865
- log("[magic-context] embedding model failed to load:", error51);
166160
+ if (isNativeRuntimeMissingError(error51)) {
166161
+ nativeRuntimeMissing = true;
166162
+ 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 @wolfx/magic-context@latest doctor --force`), or configure an `openai-compatible`/`ollama` embedding endpoint instead. Existing memories are unaffected.");
166163
+ } else {
166164
+ log("[magic-context] embedding model failed to load:", error51);
166165
+ }
165866
166166
  this.pipeline = null;
165867
166167
  } finally {
165868
166168
  this.initPromise = null;
@@ -165888,7 +166188,7 @@ class LocalEmbeddingProvider {
165888
166188
  waiter();
165889
166189
  }
165890
166190
  }
165891
- async embed(text, signal) {
166191
+ async embed(text, signal, _purpose) {
165892
166192
  if (signal?.aborted)
165893
166193
  return null;
165894
166194
  if (this.disposing)
@@ -165914,7 +166214,7 @@ class LocalEmbeddingProvider {
165914
166214
  this.finishInFlight();
165915
166215
  }
165916
166216
  }
165917
- async embedBatch(texts, signal) {
166217
+ async embedBatch(texts, signal, _purpose) {
165918
166218
  if (texts.length === 0) {
165919
166219
  return [];
165920
166220
  }
@@ -165973,7 +166273,7 @@ class LocalEmbeddingProvider {
165973
166273
  return this.pipeline !== null;
165974
166274
  }
165975
166275
  }
165976
- var LOCK_POLL_MS = 150, STALE_LOCK_MS, MAX_LOCK_WAIT_MS;
166276
+ var LOCK_POLL_MS = 150, STALE_LOCK_MS, MAX_LOCK_WAIT_MS, nativeRuntimeMissing = false;
165977
166277
  var init_embedding_local = __esm(() => {
165978
166278
  init_magic_context();
165979
166279
  init_data_path();
@@ -166058,6 +166358,7 @@ class OpenAICompatibleEmbeddingProvider {
166058
166358
  model;
166059
166359
  apiKey;
166060
166360
  inputType;
166361
+ queryInputType;
166061
166362
  truncate;
166062
166363
  initialized = false;
166063
166364
  failureTimes = [];
@@ -166070,6 +166371,7 @@ class OpenAICompatibleEmbeddingProvider {
166070
166371
  this.model = options.model?.trim() ?? "";
166071
166372
  this.apiKey = options.apiKey?.trim() ?? "";
166072
166373
  this.inputType = options.inputType?.trim() ?? "";
166374
+ this.queryInputType = options.queryInputType?.trim() ?? "";
166073
166375
  this.truncate = options.truncate?.trim() ?? "";
166074
166376
  this.maxInputTokens = typeof options.maxInputTokens === "number" && Number.isFinite(options.maxInputTokens) ? Math.max(1, Math.floor(options.maxInputTokens)) : 512;
166075
166377
  this.modelId = getEmbeddingProviderIdentity({
@@ -166097,11 +166399,17 @@ class OpenAICompatibleEmbeddingProvider {
166097
166399
  this.initialized = true;
166098
166400
  return true;
166099
166401
  }
166100
- async embed(text, signal) {
166101
- const [embedding] = await this.embedBatch([text], signal);
166402
+ resolveInputTypeForPurpose(purpose = "passage") {
166403
+ if (purpose === "query") {
166404
+ return this.queryInputType || this.inputType;
166405
+ }
166406
+ return this.inputType;
166407
+ }
166408
+ async embed(text, signal, purpose) {
166409
+ const [embedding] = await this.embedBatch([text], signal, purpose);
166102
166410
  return embedding ?? null;
166103
166411
  }
166104
- async embedBatch(texts, signal) {
166412
+ async embedBatch(texts, signal, purpose) {
166105
166413
  if (texts.length === 0) {
166106
166414
  return [];
166107
166415
  }
@@ -166127,6 +166435,7 @@ class OpenAICompatibleEmbeddingProvider {
166127
166435
  if (signal) {
166128
166436
  signal.addEventListener("abort", onOuterAbort, { once: true });
166129
166437
  }
166438
+ const inputTypeForRequest = this.resolveInputTypeForPurpose(purpose);
166130
166439
  const response = await fetch(`${this.endpoint}/embeddings`, {
166131
166440
  method: "POST",
166132
166441
  headers: {
@@ -166136,7 +166445,7 @@ class OpenAICompatibleEmbeddingProvider {
166136
166445
  body: JSON.stringify({
166137
166446
  model: this.model,
166138
166447
  input: texts,
166139
- ...this.inputType ? { input_type: this.inputType } : {},
166448
+ ...inputTypeForRequest ? { input_type: inputTypeForRequest } : {},
166140
166449
  ...this.truncate ? { truncate: this.truncate } : {}
166141
166450
  }),
166142
166451
  redirect: "error",
@@ -166387,6 +166696,121 @@ var init_storage_git_commit_embeddings = __esm(() => {
166387
166696
  distinctModelIdStatements = new WeakMap;
166388
166697
  });
166389
166698
 
166699
+ // src/features/magic-context/git-commits/storage-git-commits.ts
166700
+ function getInsertStatement(db) {
166701
+ let stmt = insertStatements.get(db);
166702
+ if (!stmt) {
166703
+ stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
166704
+ VALUES (?, ?, ?, ?, ?, ?, ?)
166705
+ ON CONFLICT(sha) DO UPDATE SET
166706
+ project_path = excluded.project_path,
166707
+ short_sha = excluded.short_sha,
166708
+ message = excluded.message,
166709
+ author = excluded.author,
166710
+ committed_at = excluded.committed_at,
166711
+ indexed_at = excluded.indexed_at
166712
+ WHERE git_commits.message != excluded.message`);
166713
+ insertStatements.set(db, stmt);
166714
+ }
166715
+ return stmt;
166716
+ }
166717
+ function getExistingShasStatement(db) {
166718
+ let stmt = existingShasStatements.get(db);
166719
+ if (!stmt) {
166720
+ stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
166721
+ existingShasStatements.set(db, stmt);
166722
+ }
166723
+ return stmt;
166724
+ }
166725
+ function getProjectCountStatement(db) {
166726
+ let stmt = projectCountStatements.get(db);
166727
+ if (!stmt) {
166728
+ stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
166729
+ projectCountStatements.set(db, stmt);
166730
+ }
166731
+ return stmt;
166732
+ }
166733
+ function getLatestCommitTimeStatement(db) {
166734
+ let stmt = latestCommitTimeStatements.get(db);
166735
+ if (!stmt) {
166736
+ stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
166737
+ latestCommitTimeStatements.set(db, stmt);
166738
+ }
166739
+ return stmt;
166740
+ }
166741
+ function getEvictOverflowStatement(db) {
166742
+ let stmt = evictOverflowStatements.get(db);
166743
+ if (!stmt) {
166744
+ stmt = db.prepare(`DELETE FROM git_commits
166745
+ WHERE rowid IN (
166746
+ SELECT rowid FROM git_commits
166747
+ WHERE project_path = ?
166748
+ ORDER BY committed_at DESC, sha DESC
166749
+ LIMIT -1 OFFSET ?
166750
+ )`);
166751
+ evictOverflowStatements.set(db, stmt);
166752
+ }
166753
+ return stmt;
166754
+ }
166755
+ function upsertCommits(db, projectPath, commits) {
166756
+ if (commits.length === 0)
166757
+ return { inserted: 0, updated: 0 };
166758
+ const existing = new Set;
166759
+ for (const row of getExistingShasStatement(db).all(projectPath)) {
166760
+ existing.add(row.sha);
166761
+ }
166762
+ let inserted = 0;
166763
+ let updated = 0;
166764
+ const now = Date.now();
166765
+ const insertStmt = getInsertStatement(db);
166766
+ db.transaction(() => {
166767
+ for (const commit of commits) {
166768
+ const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
166769
+ if (result.changes > 0) {
166770
+ if (existing.has(commit.sha)) {
166771
+ updated++;
166772
+ } else {
166773
+ inserted++;
166774
+ existing.add(commit.sha);
166775
+ }
166776
+ }
166777
+ }
166778
+ })();
166779
+ return { inserted, updated };
166780
+ }
166781
+ function getCommitCount(db, projectPath) {
166782
+ const row = getProjectCountStatement(db).get(projectPath);
166783
+ return row?.count ?? 0;
166784
+ }
166785
+ function getLatestIndexedCommitTimeMs(db, projectPath) {
166786
+ const row = getLatestCommitTimeStatement(db).get(projectPath);
166787
+ return row?.latest ?? null;
166788
+ }
166789
+ function enforceProjectCap(db, projectPath, maxCommits) {
166790
+ if (maxCommits <= 0)
166791
+ return 0;
166792
+ const count = getCommitCount(db, projectPath);
166793
+ if (count <= maxCommits)
166794
+ return 0;
166795
+ getEvictOverflowStatement(db).run(projectPath, maxCommits);
166796
+ const after = getCommitCount(db, projectPath);
166797
+ const evicted = Math.max(0, count - after);
166798
+ if (evicted > 0) {
166799
+ log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
166800
+ }
166801
+ return evicted;
166802
+ }
166803
+ var insertStatements, existingShasStatements, projectCountStatements, evictStatements, evictOverflowStatements, latestCommitTimeStatements;
166804
+ var init_storage_git_commits = __esm(() => {
166805
+ init_logger();
166806
+ insertStatements = new WeakMap;
166807
+ existingShasStatements = new WeakMap;
166808
+ projectCountStatements = new WeakMap;
166809
+ evictStatements = new WeakMap;
166810
+ evictOverflowStatements = new WeakMap;
166811
+ latestCommitTimeStatements = new WeakMap;
166812
+ });
166813
+
166390
166814
  // src/features/magic-context/git-commits/sweep-coordinator.ts
166391
166815
  function runImmediate2(db, body) {
166392
166816
  db.exec("BEGIN IMMEDIATE");
@@ -166598,6 +167022,7 @@ function resolveEmbeddingConfig(config2) {
166598
167022
  if (config2.provider === "openai-compatible") {
166599
167023
  const apiKey = config2.api_key?.trim();
166600
167024
  const inputType = config2.input_type?.trim();
167025
+ const queryInputType = config2.query_input_type?.trim();
166601
167026
  const truncate = config2.truncate?.trim();
166602
167027
  return {
166603
167028
  provider: "openai-compatible",
@@ -166605,6 +167030,7 @@ function resolveEmbeddingConfig(config2) {
166605
167030
  endpoint: config2.endpoint.trim(),
166606
167031
  ...apiKey ? { api_key: apiKey } : {},
166607
167032
  ...inputType ? { input_type: inputType } : {},
167033
+ ...queryInputType ? { query_input_type: queryInputType } : {},
166608
167034
  ...truncate ? { truncate } : {},
166609
167035
  ...config2.max_input_tokens ? {
166610
167036
  max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
@@ -166626,6 +167052,7 @@ function createProvider(config2) {
166626
167052
  model: config2.model,
166627
167053
  apiKey: config2.api_key,
166628
167054
  inputType: config2.input_type,
167055
+ queryInputType: config2.query_input_type,
166629
167056
  truncate: config2.truncate,
166630
167057
  maxInputTokens: config2.max_input_tokens
166631
167058
  });
@@ -166680,7 +167107,9 @@ function snapshotFor(registration) {
166680
167107
  enabled,
166681
167108
  gitCommitEnabled,
166682
167109
  modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
166683
- chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId
167110
+ chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId,
167111
+ model: registration.observationMode || !providerIsOn ? "off" : ("model" in registration.config) && registration.config.model.trim() ? registration.config.model.trim() : registration.modelId,
167112
+ provider: registration.observationMode || !providerIsOn ? "off" : registration.config.provider ?? "local"
166684
167113
  };
166685
167114
  }
166686
167115
  function disposeProvider(provider) {
@@ -166806,7 +167235,7 @@ function getOrCreateProjectProvider(registration) {
166806
167235
  registration.provider = provider;
166807
167236
  return provider;
166808
167237
  }
166809
- async function embedTextForProject(projectIdentity, text, signal) {
167238
+ async function embedTextForProject(projectIdentity, text, signal, purpose = "passage") {
166810
167239
  const registration = projectRegistrations.get(projectIdentity);
166811
167240
  if (!registration)
166812
167241
  return null;
@@ -166815,7 +167244,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
166815
167244
  const provider = getOrCreateProjectProvider(registration);
166816
167245
  if (!provider)
166817
167246
  return null;
166818
- const vector = await provider.embed(text, signal);
167247
+ const vector = await provider.embed(text, signal, purpose);
166819
167248
  if (!vector)
166820
167249
  return null;
166821
167250
  const current = projectRegistrations.get(projectIdentity);
@@ -166824,7 +167253,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
166824
167253
  }
166825
167254
  return { vector, modelId, generation };
166826
167255
  }
166827
- async function embedBatchForProject(projectIdentity, texts, signal) {
167256
+ async function embedBatchForProject(projectIdentity, texts, signal, purpose = "passage") {
166828
167257
  if (texts.length === 0) {
166829
167258
  const registration2 = projectRegistrations.get(projectIdentity);
166830
167259
  if (!registration2 || registration2.observationMode)
@@ -166840,7 +167269,7 @@ async function embedBatchForProject(projectIdentity, texts, signal) {
166840
167269
  const provider = getOrCreateProjectProvider(registration);
166841
167270
  if (!provider)
166842
167271
  return null;
166843
- const vectors = await provider.embedBatch(texts, signal);
167272
+ const vectors = await provider.embedBatch(texts, signal, purpose);
166844
167273
  const current = projectRegistrations.get(projectIdentity);
166845
167274
  if (!current || current.generation !== generation || current.runtimeFingerprint !== runtimeFingerprint) {
166846
167275
  return null;
@@ -166891,12 +167320,13 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
166891
167320
  }
166892
167321
  async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates, signal) {
166893
167322
  const noWork = [];
167323
+ const failed = [];
166894
167324
  if (candidates.length === 0)
166895
- return { embedded: 0, noWork };
167325
+ return { embedded: 0, noWork, failed };
166896
167326
  const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
166897
167327
  const prepared = [];
166898
167328
  for (const candidate of candidates) {
166899
- const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage);
167329
+ const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage) || buildCompartmentSummaryFallbackText(db, candidate.id);
166900
167330
  if (canonicalText.length === 0) {
166901
167331
  noWork.push(candidate.id);
166902
167332
  continue;
@@ -166909,7 +167339,7 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
166909
167339
  prepared.push({ candidate, windows });
166910
167340
  }
166911
167341
  if (prepared.length === 0)
166912
- return { embedded: 0, noWork };
167342
+ return { embedded: 0, noWork, failed };
166913
167343
  let embedded = 0;
166914
167344
  let i = 0;
166915
167345
  while (i < prepared.length) {
@@ -166926,35 +167356,60 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
166926
167356
  const texts = [];
166927
167357
  for (const item of slice)
166928
167358
  texts.push(...item.windows.map((w) => w.text));
166929
- try {
166930
- const result = await embedBatchForProject(projectIdentity, texts, signal);
166931
- if (!result)
166932
- continue;
167359
+ const persistedIds = new Set;
167360
+ for (let attempt = 0;attempt < EMBED_SLICE_RETRY_ATTEMPTS; attempt++) {
166933
167361
  if (signal?.aborted)
166934
167362
  break;
166935
- let offset = 0;
166936
- for (const item of slice) {
166937
- const vectors = result.vectors.slice(offset, offset + item.windows.length);
166938
- offset += item.windows.length;
166939
- if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
166940
- continue;
167363
+ let result = null;
167364
+ const attemptStart = Date.now();
167365
+ try {
167366
+ result = await embedBatchForProject(projectIdentity, texts, signal);
167367
+ } catch (error51) {
167368
+ log("[magic-context] failed to proactively embed compartment chunks:", error51);
167369
+ }
167370
+ if (signal?.aborted)
167371
+ break;
167372
+ if (result) {
167373
+ let offset = 0;
167374
+ for (const item of slice) {
167375
+ const vectors = result.vectors.slice(offset, offset + item.windows.length);
167376
+ offset += item.windows.length;
167377
+ if (persistedIds.has(item.candidate.id))
167378
+ continue;
167379
+ if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
167380
+ continue;
167381
+ }
167382
+ const rows = item.windows.map((window, index) => ({
167383
+ compartmentId: item.candidate.id,
167384
+ sessionId: item.candidate.sessionId,
167385
+ projectPath: projectIdentity,
167386
+ window,
167387
+ modelId,
167388
+ vector: vectors[index]
167389
+ }));
167390
+ replaceCompartmentChunkEmbeddings(db, rows);
167391
+ persistedIds.add(item.candidate.id);
166941
167392
  }
166942
- const rows = item.windows.map((window, index) => ({
166943
- compartmentId: item.candidate.id,
166944
- sessionId: item.candidate.sessionId,
166945
- projectPath: projectIdentity,
166946
- window,
166947
- modelId,
166948
- vector: vectors[index]
166949
- }));
166950
- replaceCompartmentChunkEmbeddings(db, rows);
166951
- embedded += 1;
166952
167393
  }
166953
- } catch (error51) {
166954
- log("[magic-context] failed to proactively embed compartment chunks:", error51);
167394
+ if (persistedIds.size === slice.length)
167395
+ break;
167396
+ if (persistedIds.size > 0)
167397
+ break;
167398
+ if (Date.now() - attemptStart >= EMBED_SLOW_FAILURE_NO_RETRY_MS)
167399
+ break;
167400
+ if (attempt < EMBED_SLICE_RETRY_ATTEMPTS - 1) {
167401
+ await new Promise((resolve6) => setTimeout(resolve6, EMBED_SLICE_RETRY_BASE_MS * 2 ** attempt));
167402
+ }
167403
+ }
167404
+ embedded += persistedIds.size;
167405
+ if (!signal?.aborted) {
167406
+ for (const item of slice) {
167407
+ if (!persistedIds.has(item.candidate.id))
167408
+ failed.push(item.candidate.id);
167409
+ }
166955
167410
  }
166956
167411
  }
166957
- return { embedded, noWork };
167412
+ return { embedded, noWork, failed };
166958
167413
  }
166959
167414
  async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, options) {
166960
167415
  const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
@@ -166977,9 +167432,11 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
166977
167432
  renewal.unref?.();
166978
167433
  const batchSize = Math.max(1, options?.batchSize ?? CHUNK_DRAIN_BATCH_SIZE);
166979
167434
  const skipIds = [];
167435
+ const failedIds = [];
166980
167436
  let embedded = 0;
166981
167437
  let aborted2 = false;
166982
- let providerStalled = false;
167438
+ let providerDown = false;
167439
+ let consecutiveFailedBatches = 0;
166983
167440
  try {
166984
167441
  options?.onProgress?.({ embedded, total });
166985
167442
  for (;; ) {
@@ -166987,15 +167444,26 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
166987
167444
  aborted2 = true;
166988
167445
  break;
166989
167446
  }
166990
- const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, skipIds);
167447
+ const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, [...skipIds, ...failedIds]);
166991
167448
  if (candidates.length === 0)
166992
167449
  break;
166993
- const { embedded: n, noWork } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
167450
+ const {
167451
+ embedded: n,
167452
+ noWork,
167453
+ failed
167454
+ } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
166994
167455
  for (const id of noWork)
166995
167456
  skipIds.push(id);
167457
+ for (const id of failed)
167458
+ failedIds.push(id);
166996
167459
  if (n === 0 && noWork.length === 0) {
166997
- providerStalled = true;
166998
- break;
167460
+ consecutiveFailedBatches += 1;
167461
+ if (consecutiveFailedBatches >= MAX_CONSECUTIVE_FAILED_BATCHES) {
167462
+ providerDown = true;
167463
+ break;
167464
+ }
167465
+ } else {
167466
+ consecutiveFailedBatches = 0;
166999
167467
  }
167000
167468
  embedded += n;
167001
167469
  options?.onProgress?.({ embedded: Math.min(embedded, total), total });
@@ -167003,23 +167471,58 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
167003
167471
  }
167004
167472
  } finally {
167005
167473
  clearInterval(renewal);
167006
- releaseGitSweepLease(db, projectIdentity, holderId);
167474
+ try {
167475
+ releaseGitSweepLease(db, projectIdentity, holderId);
167476
+ } catch (error51) {
167477
+ log("[magic-context] embed drain: lease release failed (will TTL-expire):", error51);
167478
+ }
167007
167479
  }
167008
167480
  if (aborted2)
167009
- return { status: "aborted", embedded, total };
167010
- if (providerStalled) {
167481
+ return { status: "aborted", embedded, total, failed: failedIds.length };
167482
+ if (providerDown || failedIds.length > 0) {
167011
167483
  const remaining = Math.max(0, countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId) - skipIds.length);
167012
- if (remaining > 0)
167013
- return { status: "stalled", embedded, total, remaining };
167484
+ if (remaining > 0) {
167485
+ return { status: "stalled", embedded, total, remaining, failed: failedIds.length };
167486
+ }
167014
167487
  }
167015
- return { status: "done", embedded, total };
167488
+ return { status: "done", embedded, total, failed: failedIds.length };
167016
167489
  }
167017
- 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;
167490
+ function getEmbeddingCoverageStatus(db, projectIdentity, sessionId) {
167491
+ const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
167492
+ if (!snapshot?.enabled || snapshot.chunkModelId === "off") {
167493
+ return {
167494
+ enabled: false,
167495
+ model: snapshot?.model ?? "off",
167496
+ provider: snapshot?.provider ?? "off",
167497
+ session: { embedded: 0, total: 0 },
167498
+ memories: { embedded: 0, total: 0 },
167499
+ commits: { embedded: 0, total: 0, gitEnabled: false }
167500
+ };
167501
+ }
167502
+ const session = countSessionCompartmentEmbedCoverage(db, projectIdentity, sessionId, snapshot.chunkModelId);
167503
+ const memories = getMemoryEmbedCoverage(db, projectIdentity, snapshot.modelId);
167504
+ const gitEnabled = snapshot.gitCommitEnabled;
167505
+ const commits = gitEnabled ? {
167506
+ embedded: countEmbeddedCommits(db, projectIdentity),
167507
+ total: getCommitCount(db, projectIdentity),
167508
+ gitEnabled: true
167509
+ } : { embedded: 0, total: 0, gitEnabled: false };
167510
+ return {
167511
+ enabled: true,
167512
+ model: snapshot.model,
167513
+ provider: snapshot.provider,
167514
+ session,
167515
+ memories,
167516
+ commits
167517
+ };
167518
+ }
167519
+ 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;
167018
167520
  var init_project_embedding_registry = __esm(() => {
167019
167521
  init_magic_context();
167020
167522
  init_logger();
167021
167523
  init_compartment_chunk_embedding();
167022
167524
  init_storage_git_commit_embeddings();
167525
+ init_storage_git_commits();
167023
167526
  init_sweep_coordinator();
167024
167527
  init_embedding_cache();
167025
167528
  init_embedding_identity();
@@ -167044,6 +167547,7 @@ function createProvider2(config2) {
167044
167547
  model: config2.model,
167045
167548
  apiKey: config2.api_key,
167046
167549
  inputType: config2.input_type,
167550
+ queryInputType: config2.query_input_type,
167047
167551
  truncate: config2.truncate,
167048
167552
  maxInputTokens: config2.max_input_tokens
167049
167553
  });
@@ -167300,58 +167804,6 @@ var init_models_dev_cache = __esm(() => {
167300
167804
  init_logger();
167301
167805
  });
167302
167806
 
167303
- // src/shared/rpc-notifications.ts
167304
- var exports_rpc_notifications = {};
167305
- __export(exports_rpc_notifications, {
167306
- pushNotification: () => pushNotification,
167307
- isTuiConnected: () => isTuiConnected,
167308
- drainNotifications: () => drainNotifications
167309
- });
167310
- function pushNotification(type, payload, sessionId) {
167311
- queue2.push({ id: nextNotificationId++, type, payload, sessionId });
167312
- if (queue2.length > 100) {
167313
- const newestPerSession = new Map;
167314
- for (const n of queue2) {
167315
- const prev = newestPerSession.get(n.sessionId);
167316
- if (prev === undefined || n.id > prev) {
167317
- newestPerSession.set(n.sessionId, n.id);
167318
- }
167319
- }
167320
- const mustKeep = new Set(newestPerSession.values());
167321
- const byNewest = [...queue2].sort((a, b) => b.id - a.id);
167322
- const kept = [];
167323
- for (const n of byNewest) {
167324
- if (kept.length < 50 || mustKeep.has(n.id))
167325
- kept.push(n);
167326
- }
167327
- queue2 = kept.sort((a, b) => a.id - b.id);
167328
- }
167329
- }
167330
- function drainNotifications(lastReceivedId = 0, sessionId) {
167331
- const now = Date.now();
167332
- lastDrainAtAny = now;
167333
- if (sessionId !== undefined)
167334
- lastDrainAtBySession.set(sessionId, now);
167335
- const matchesClient = (notification) => sessionId === undefined || notification.sessionId === undefined || notification.sessionId === sessionId;
167336
- if (lastReceivedId > 0) {
167337
- queue2 = queue2.filter((notification) => !(notification.id <= lastReceivedId && matchesClient(notification)));
167338
- }
167339
- return queue2.filter((notification) => notification.id > lastReceivedId && matchesClient(notification));
167340
- }
167341
- function isTuiConnected(sessionId) {
167342
- const now = Date.now();
167343
- if (sessionId !== undefined) {
167344
- const at = lastDrainAtBySession.get(sessionId) ?? 0;
167345
- return at > 0 && now - at < TUI_CONNECTED_WINDOW_MS;
167346
- }
167347
- return lastDrainAtAny > 0 && now - lastDrainAtAny < TUI_CONNECTED_WINDOW_MS;
167348
- }
167349
- var queue2, nextNotificationId = 1, lastDrainAtBySession, lastDrainAtAny = 0, TUI_CONNECTED_WINDOW_MS = 3000;
167350
- var init_rpc_notifications = __esm(() => {
167351
- queue2 = [];
167352
- lastDrainAtBySession = new Map;
167353
- });
167354
-
167355
167807
  // src/features/magic-context/compartment-embedding.ts
167356
167808
  async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compartments) {
167357
167809
  if (compartments.length === 0)
@@ -167360,7 +167812,7 @@ async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compar
167360
167812
  for (const compartment of compartments) {
167361
167813
  try {
167362
167814
  const fromMemory = compartment.sourceChunkText ? canonicalizeInMemoryChunkTextForEmbedding(compartment.sourceChunkText, compartment.startMessage, compartment.endMessage) : "";
167363
- const canonicalText = fromMemory || buildCanonicalChunkTextFromFts(db, sessionId, compartment.startMessage, compartment.endMessage);
167815
+ const canonicalText = fromMemory || buildCanonicalChunkTextFromFts(db, sessionId, compartment.startMessage, compartment.endMessage) || buildCompartmentSummaryFallbackText(db, compartment.id);
167364
167816
  if (canonicalText.length === 0)
167365
167817
  continue;
167366
167818
  const windows = chunkCanonicalText(canonicalText, compartment.startMessage, compartment.endMessage, maxInputTokens);
@@ -170560,29 +171012,12 @@ function resolveHistorianContextLimit(historianModelOverride) {
170560
171012
  return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
170561
171013
  }
170562
171014
  if (typeof historianModelOverride === "string" && historianModelOverride.trim() !== "") {
170563
- console.warn(`[magic-context] historian.model "${historianModelOverride}" lacks provider prefix ("provider/model-id"); using fallback chain for chunk-budget derivation.`);
170564
- }
170565
- const chain = AGENT_MODEL_REQUIREMENTS[HISTORIAN_AGENT]?.fallbackChain;
170566
- if (!chain || chain.length === 0)
170567
- return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
170568
- const expanded = expandFallbackChain(chain);
170569
- let minLimit;
170570
- for (const key of expanded) {
170571
- const [providerID, ...rest] = key.split("/");
170572
- const modelID = rest.join("/");
170573
- if (!providerID || !modelID)
170574
- continue;
170575
- const limit = getSdkContextLimit(providerID, modelID);
170576
- if (typeof limit !== "number" || limit <= 0)
170577
- continue;
170578
- if (minLimit === undefined || limit < minLimit)
170579
- minLimit = limit;
171015
+ console.warn(`[magic-context] historian.model "${historianModelOverride}" lacks provider prefix ("provider/model-id"); using the default context limit for chunk-budget derivation.`);
170580
171016
  }
170581
- return minLimit ?? DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
171017
+ return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
170582
171018
  }
170583
171019
  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;
170584
171020
  var init_derive_budgets = __esm(() => {
170585
- init_model_requirements();
170586
171021
  init_models_dev_cache();
170587
171022
  });
170588
171023
 
@@ -170903,7 +171338,7 @@ function buildToolArcs(messages) {
170903
171338
  }
170904
171339
  return arcs.sort((a, b) => a.invOrdinal - b.invOrdinal || (a.resOrdinal ?? Number.MAX_SAFE_INTEGER) - (b.resOrdinal ?? Number.MAX_SAFE_INTEGER));
170905
171340
  }
170906
- function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
171341
+ function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal, recentOpenArcCutoff) {
170907
171342
  let boundary = candidate;
170908
171343
  for (const arc of arcs) {
170909
171344
  if (arc.resOrdinal !== null) {
@@ -170912,6 +171347,8 @@ function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
170912
171347
  }
170913
171348
  continue;
170914
171349
  }
171350
+ if (arc.invOrdinal < recentOpenArcCutoff)
171351
+ continue;
170915
171352
  if (arc.invOrdinal >= lastCompartmentEndOrdinal + 1 && arc.invOrdinal < boundary) {
170916
171353
  return arc.invOrdinal;
170917
171354
  }
@@ -171151,7 +171588,7 @@ function semanticSnapBoundary(args) {
171151
171588
  return snapped;
171152
171589
  }
171153
171590
  function applyHeadCap(args) {
171154
- const { index, protectedTailStart, offset, arcs, capTokens } = args;
171591
+ const { index, protectedTailStart, offset, arcs, capTokens, recentOpenArcCutoff } = args;
171155
171592
  if (offset >= protectedTailStart)
171156
171593
  return { eligibleEndOrdinal: offset, oversizeAtomicUnit: false };
171157
171594
  let end = index.findHeadEndForCap(offset, protectedTailStart, capTokens);
@@ -171159,7 +171596,7 @@ function applyHeadCap(args) {
171159
171596
  for (const arc of arcs) {
171160
171597
  const resOrdinal = arc.resOrdinal;
171161
171598
  if (resOrdinal === null) {
171162
- if (arc.invOrdinal >= offset && arc.invOrdinal < end) {
171599
+ if (arc.invOrdinal >= recentOpenArcCutoff && arc.invOrdinal >= offset && arc.invOrdinal < end) {
171163
171600
  end = Math.min(end, arc.invOrdinal);
171164
171601
  }
171165
171602
  continue;
@@ -171226,7 +171663,14 @@ function resolveProtectedTailBoundary(ctx) {
171226
171663
  }
171227
171664
  if (ctx.mode === "manual-full-recomp") {
171228
171665
  const arcs2 = buildToolArcs(messages);
171229
- const firstOpenArc = arcs2.find((arc) => arc.resOrdinal === null && arc.invOrdinal >= offset);
171666
+ const recompTarget = deriveProtectedTailTokenTarget({
171667
+ contextLimit: ctx.contextLimit,
171668
+ executeThresholdPercentage: ctx.executeThresholdPercentage,
171669
+ usagePercentage: 0,
171670
+ triggerBudget: ctx.triggerBudget
171671
+ });
171672
+ const recentOpenArcCutoff2 = index.findSuffixStartForTokens(recompTarget.N);
171673
+ const firstOpenArc = arcs2.find((arc) => arc.resOrdinal === null && arc.invOrdinal >= offset && arc.invOrdinal >= recentOpenArcCutoff2);
171230
171674
  const protectedTailStart2 = firstOpenArc?.invOrdinal ?? rawMessageCount + 1;
171231
171675
  const rawRangeFingerprint2 = computeRawRangeFingerprint(messages, offset, protectedTailStart2);
171232
171676
  return {
@@ -171268,13 +171712,14 @@ function resolveProtectedTailBoundary(ctx) {
171268
171712
  const scaledN = ctx.emergencyTailScale ? Math.max(1, Math.floor(target.N * ctx.emergencyTailScale)) : target.N;
171269
171713
  const arcs = buildToolArcs(messages);
171270
171714
  let boundary = index.findSuffixStartForTokens(scaledN);
171715
+ const recentOpenArcCutoff = boundary;
171271
171716
  let boundaryReason = boundary === 1 ? "whole-session-smaller-than-tail" : "size-walk";
171272
171717
  const tokenAtBoundary = index.tokenForOrdinal(boundary);
171273
171718
  if (boundary <= rawMessageCount && tokenAtBoundary > Math.max(2 * scaledN, 64000) && boundary < rawMessageCount) {
171274
171719
  boundary += 1;
171275
171720
  boundaryReason = "huge-message-exception";
171276
171721
  }
171277
- boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal);
171722
+ boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
171278
171723
  const snapped = semanticSnapBoundary({
171279
171724
  messages,
171280
171725
  index,
@@ -171284,7 +171729,7 @@ function resolveProtectedTailBoundary(ctx) {
171284
171729
  });
171285
171730
  if (snapped !== boundary)
171286
171731
  boundaryReason = "semantic-snap";
171287
- boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal);
171732
+ boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
171288
171733
  let runtimeFloor = offset;
171289
171734
  if (ctx.migrationFloorActive)
171290
171735
  runtimeFloor = Math.max(runtimeFloor, ctx.priorBoundaryOrdinal);
@@ -171320,7 +171765,8 @@ function resolveProtectedTailBoundary(ctx) {
171320
171765
  offset,
171321
171766
  arcs,
171322
171767
  lastCompartmentEndOrdinal: ctx.lastCompartmentEndOrdinal,
171323
- capTokens: perRunCap
171768
+ capTokens: perRunCap,
171769
+ recentOpenArcCutoff
171324
171770
  });
171325
171771
  const rawRangeFingerprint = computeRawRangeFingerprint(messages, offset, head.eligibleEndOrdinal);
171326
171772
  return {
@@ -171371,7 +171817,7 @@ function resolveBoundaryContext(args) {
171371
171817
  }
171372
171818
  let storedTokenTotals;
171373
171819
  try {
171374
- storedTokenTotals = getAllStatusTagTokenTotalsFlat(args.db, args.sessionId).totals;
171820
+ storedTokenTotals = getAllStatusTagTokenTotalsFlat(args.db, args.sessionId, args.taggerFloor ?? 0).totals;
171375
171821
  } catch (error51) {
171376
171822
  sessionLog(args.sessionId, "protected-tail stored-token map unavailable (live fallback):", error51);
171377
171823
  }
@@ -173575,6 +174021,7 @@ async function runCompartmentAgent(deps) {
173575
174021
  const count = recordHighPressureNoEligibleHead(db, boundarySnapshot);
173576
174022
  sessionLog(sessionId, `historian high-pressure no-op: recovery remains armed (noEligibleHeadCount=${count})`);
173577
174023
  }
174024
+ clearEmergencyDrainLatch(db, sessionId);
173578
174025
  telemetry.status = "noop";
173579
174026
  telemetry.failureReason = "nothing to compact before protected tail";
173580
174027
  rollbackDrainReservation();
@@ -173589,7 +174036,8 @@ async function runCompartmentAgent(deps) {
173589
174036
  trueRawTokens: boundarySnapshot.trueRawEligibleTokens,
173590
174037
  usagePercentage: boundarySnapshot.usagePercentage,
173591
174038
  usable,
173592
- perRunCap
174039
+ perRunCap,
174040
+ executeThresholdPercentage: boundarySnapshot.executeThresholdPercentage
173593
174041
  });
173594
174042
  if (!reserve.ok) {
173595
174043
  sessionLog(sessionId, `historian rate-limit skip: ${reserve.skippedReason ?? "quota exhausted"}`);
@@ -173608,6 +174056,7 @@ async function runCompartmentAgent(deps) {
173608
174056
  } else {
173609
174057
  recordHighPressureNoEligibleHead(db, boundarySnapshot);
173610
174058
  }
174059
+ clearEmergencyDrainLatch(db, sessionId);
173611
174060
  telemetry.status = "noop";
173612
174061
  telemetry.failureReason = "chunk empty after filtering";
173613
174062
  rollbackDrainReservation();
@@ -173715,6 +174164,7 @@ ${chunkText}`,
173715
174164
  }
173716
174165
  appendCompartments(db, sessionId, persistedCompartments);
173717
174166
  clearHistorianFailureState(db, sessionId);
174167
+ clearHistorianDrainFailure(db, sessionId);
173718
174168
  recordProtectedTailPublicationFloor(db, sessionId, lastCompartmentEnd + 1);
173719
174169
  clearEmergencyRecovery(db, sessionId);
173720
174170
  drainReservation = null;
@@ -173818,8 +174268,11 @@ ${chunkText}`,
173818
174268
  }
173819
174269
  } finally {
173820
174270
  if (!completedSuccessfully) {
173821
- if (!retainDrainReservationForRetryThrottle)
174271
+ if (!retainDrainReservationForRetryThrottle) {
173822
174272
  rollbackDrainReservation();
174273
+ } else {
174274
+ recordHistorianDrainFailure(db, sessionId);
174275
+ }
173823
174276
  updateSessionMeta(db, sessionId, { compartmentInProgress: false });
173824
174277
  }
173825
174278
  recordTelemetry();
@@ -176933,7 +177386,6 @@ var init_memory_migration = __esm(async () => {
176933
177386
  init_shared();
176934
177387
  init_assistant_message_extractor();
176935
177388
  init_logger();
176936
- init_resolve_fallbacks();
176937
177389
  init_project_identity();
176938
177390
  init_storage_memory();
176939
177391
  await init_storage();
@@ -177249,17 +177701,20 @@ function shouldShowAnnouncement() {
177249
177701
  }
177250
177702
  return state.version !== ANNOUNCEMENT_VERSION;
177251
177703
  }
177252
- var ANNOUNCEMENT_VERSION = "0.24.0", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
177704
+ var ANNOUNCEMENT_VERSION = "0.26.0", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
177253
177705
  var init_announcement = __esm(() => {
177254
177706
  init_data_path();
177255
177707
  ANNOUNCEMENT_FEATURES = [
177256
- "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).",
177257
- "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.",
177258
- "Pi: fixed sessions overflowing the model context while still showing moderate usage Pi now sheds context before a tool-heavy turn overflows.",
177259
- "Fewer prompt-cache busts: doc edits, processed screenshots, and a rebuild-then-bust-again case no longer re-bill large prompt prefixes.",
177260
- "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)."
177708
+ "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).",
177709
+ "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.",
177710
+ "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.",
177711
+ "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).",
177712
+ "New: doctor migrate-session re-homes a session (and optionally its memories) to another project, with a dry-run preview."
177261
177713
  ];
177262
177714
  });
177715
+
177716
+ // src/agents/dreamer.ts
177717
+ var DREAMER_AGENT = "dreamer";
177263
177718
  // src/agents/permissions.ts
177264
177719
  function buildAllowOnlyPermission(allowedTools) {
177265
177720
  const permission = { "*": "deny" };
@@ -177269,6 +177724,11 @@ function buildAllowOnlyPermission(allowedTools) {
177269
177724
  return permission;
177270
177725
  }
177271
177726
  var HISTORIAN_ALLOWED_TOOLS = ["read", "aft_outline", "aft_zoom", "aft_search"];
177727
+ function applyDisallowedTools(defaults, disallowed) {
177728
+ if (disallowed.includes("*"))
177729
+ return [];
177730
+ return defaults.filter((t) => !disallowed.includes(t));
177731
+ }
177272
177732
  var DREAMER_ALLOWED_TOOLS = [
177273
177733
  "read",
177274
177734
  "grep",
@@ -177284,6 +177744,10 @@ var DREAMER_ALLOWED_TOOLS = [
177284
177744
  "ctx_note"
177285
177745
  ];
177286
177746
  var SIDEKICK_ALLOWED_TOOLS = ["ctx_search", "aft_outline", "aft_zoom"];
177747
+
177748
+ // src/agents/sidekick.ts
177749
+ var SIDEKICK_AGENT = "sidekick";
177750
+
177287
177751
  // src/config/index.ts
177288
177752
  init_jsonc_parser();
177289
177753
  import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
@@ -177986,9 +178450,9 @@ function getMagicContextBuiltinCommands() {
177986
178450
  template: "ctx-dream",
177987
178451
  description: "Run the hidden dreamer maintenance pass for this project now"
177988
178452
  },
177989
- "ctx-embed-history": {
177990
- template: "ctx-embed-history",
177991
- description: "Embed all of this session's history compartments for semantic search, in one pass"
178453
+ "ctx-embed": {
178454
+ template: "ctx-embed",
178455
+ description: "Embedding status, or start/pause history compartment embedding (start | pause)"
177992
178456
  }
177993
178457
  };
177994
178458
  }
@@ -178237,6 +178701,7 @@ ${modeIntro}
178237
178701
  4. **Write or update** using the Write tool. Always write to project root, NOT to .planning/.
178238
178702
 
178239
178703
  ### Rules
178704
+ - **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.
178240
178705
  - **Be prescriptive**: "Use X pattern" not "X pattern is used"
178241
178706
  - **Always include file paths** in backticks
178242
178707
  - **Write current state only**: no temporal language, no history
@@ -178373,7 +178838,6 @@ init_project_identity();
178373
178838
  init_shared();
178374
178839
  init_assistant_message_extractor();
178375
178840
  init_logger();
178376
- init_resolve_fallbacks();
178377
178841
  init_subagent_token_capture();
178378
178842
  await init_storage();
178379
178843
 
@@ -178395,7 +178859,7 @@ function stripThinkingBlocks(text) {
178395
178859
 
178396
178860
  // src/features/magic-context/sidekick/agent.ts
178397
178861
  async function runSidekick(deps) {
178398
- const fallbackModels = resolveFallbackChain(SIDEKICK_AGENT, deps.config.fallback_models);
178862
+ const fallbackModels = resolveFallbackChain(deps.config.fallback_models);
178399
178863
  let agentSessionId = null;
178400
178864
  const startedAt = Date.now();
178401
178865
  let invocationRecorded = false;
@@ -178485,6 +178949,11 @@ init_project_identity();
178485
178949
  import { createHash as createHash6 } from "node:crypto";
178486
178950
  import { realpathSync as realpathSync2 } from "node:fs";
178487
178951
  import path5 from "node:path";
178952
+
178953
+ // src/features/magic-context/memory/relocate-memory.ts
178954
+ var memoryCopyColumnsCache = new WeakMap;
178955
+
178956
+ // src/features/magic-context/v22-deferred-backfill.ts
178488
178957
  var BATCH_SIZE = 25;
178489
178958
  var YIELD_EVERY_N_ROWS = 5;
178490
178959
  var BACKFILL_META_KEY = "v22_legacy_memory_backfill";
@@ -178722,6 +179191,7 @@ function createLiveSessionState() {
178722
179191
 
178723
179192
  // src/index.ts
178724
179193
  init_conflict_warning_hook();
179194
+
178725
179195
  // src/features/magic-context/dreamer/storage-dream-state.ts
178726
179196
  var getDreamStateStatements = new WeakMap;
178727
179197
  var setDreamStateStatements = new WeakMap;
@@ -178859,7 +179329,7 @@ function enqueueDream(db, projectIdentity, reason, force = false) {
178859
179329
  return db.transaction(() => {
178860
179330
  if (!hasActiveDreamLease(db)) {
178861
179331
  const staleThresholdMs = force ? 2 * 60 * 1000 : 120 * 60 * 1000;
178862
- db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run([projectIdentity, now - staleThresholdMs]);
179332
+ db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run(projectIdentity, now - staleThresholdMs);
178863
179333
  }
178864
179334
  const existing = db.prepare("SELECT id FROM dream_queue WHERE project_path = ?").get(projectIdentity);
178865
179335
  if (existing) {
@@ -178915,21 +179385,21 @@ function clearStaleEntries(db, maxAgeMs, projectIdentity) {
178915
179385
  return result.changes;
178916
179386
  }
178917
179387
  // src/features/magic-context/dreamer/runner.ts
179388
+ import { existsSync as existsSync10 } from "node:fs";
179389
+ import { join as join12 } from "node:path";
178918
179390
  init_shared();
178919
179391
  init_assistant_message_extractor();
178920
179392
  init_data_path();
178921
179393
  init_logger();
178922
179394
  await init_sqlite();
178923
- import { existsSync as existsSync10 } from "node:fs";
178924
- import { join as join12 } from "node:path";
178925
179395
 
178926
179396
  // src/features/magic-context/key-files/identify-key-files.ts
179397
+ import { readFileSync as readFileSync8 } from "node:fs";
179398
+ import { isAbsolute as isAbsolute3, join as join11, relative as relative2 } from "node:path";
178927
179399
  init_read_session_formatting();
178928
179400
  init_shared();
178929
179401
  init_assistant_message_extractor();
178930
179402
  init_logger();
178931
- import { readFileSync as readFileSync8 } from "node:fs";
178932
- import { isAbsolute as isAbsolute3, join as join11, relative as relative2 } from "node:path";
178933
179403
  init_subagent_token_capture();
178934
179404
  init_aft_availability();
178935
179405
  init_project_key_files();
@@ -180554,120 +181024,7 @@ ${body}` : subject;
180554
181024
  init_logger();
180555
181025
  init_embedding();
180556
181026
  init_storage_git_commit_embeddings();
180557
-
180558
- // src/features/magic-context/git-commits/storage-git-commits.ts
180559
- init_logger();
180560
- var insertStatements = new WeakMap;
180561
- var existingShasStatements = new WeakMap;
180562
- var projectCountStatements = new WeakMap;
180563
- var evictStatements = new WeakMap;
180564
- var evictOverflowStatements = new WeakMap;
180565
- var latestCommitTimeStatements = new WeakMap;
180566
- function getInsertStatement(db) {
180567
- let stmt = insertStatements.get(db);
180568
- if (!stmt) {
180569
- stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
180570
- VALUES (?, ?, ?, ?, ?, ?, ?)
180571
- ON CONFLICT(sha) DO UPDATE SET
180572
- project_path = excluded.project_path,
180573
- short_sha = excluded.short_sha,
180574
- message = excluded.message,
180575
- author = excluded.author,
180576
- committed_at = excluded.committed_at,
180577
- indexed_at = excluded.indexed_at
180578
- WHERE git_commits.message != excluded.message`);
180579
- insertStatements.set(db, stmt);
180580
- }
180581
- return stmt;
180582
- }
180583
- function getExistingShasStatement(db) {
180584
- let stmt = existingShasStatements.get(db);
180585
- if (!stmt) {
180586
- stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
180587
- existingShasStatements.set(db, stmt);
180588
- }
180589
- return stmt;
180590
- }
180591
- function getProjectCountStatement(db) {
180592
- let stmt = projectCountStatements.get(db);
180593
- if (!stmt) {
180594
- stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
180595
- projectCountStatements.set(db, stmt);
180596
- }
180597
- return stmt;
180598
- }
180599
- function getLatestCommitTimeStatement(db) {
180600
- let stmt = latestCommitTimeStatements.get(db);
180601
- if (!stmt) {
180602
- stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
180603
- latestCommitTimeStatements.set(db, stmt);
180604
- }
180605
- return stmt;
180606
- }
180607
- function getEvictOverflowStatement(db) {
180608
- let stmt = evictOverflowStatements.get(db);
180609
- if (!stmt) {
180610
- stmt = db.prepare(`DELETE FROM git_commits
180611
- WHERE rowid IN (
180612
- SELECT rowid FROM git_commits
180613
- WHERE project_path = ?
180614
- ORDER BY committed_at DESC, sha DESC
180615
- LIMIT -1 OFFSET ?
180616
- )`);
180617
- evictOverflowStatements.set(db, stmt);
180618
- }
180619
- return stmt;
180620
- }
180621
- function upsertCommits(db, projectPath, commits) {
180622
- if (commits.length === 0)
180623
- return { inserted: 0, updated: 0 };
180624
- const existing = new Set;
180625
- for (const row of getExistingShasStatement(db).all(projectPath)) {
180626
- existing.add(row.sha);
180627
- }
180628
- let inserted = 0;
180629
- let updated = 0;
180630
- const now = Date.now();
180631
- const insertStmt = getInsertStatement(db);
180632
- db.transaction(() => {
180633
- for (const commit of commits) {
180634
- const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
180635
- if (result.changes > 0) {
180636
- if (existing.has(commit.sha)) {
180637
- updated++;
180638
- } else {
180639
- inserted++;
180640
- existing.add(commit.sha);
180641
- }
180642
- }
180643
- }
180644
- })();
180645
- return { inserted, updated };
180646
- }
180647
- function getCommitCount(db, projectPath) {
180648
- const row = getProjectCountStatement(db).get(projectPath);
180649
- return row?.count ?? 0;
180650
- }
180651
- function getLatestIndexedCommitTimeMs(db, projectPath) {
180652
- const row = getLatestCommitTimeStatement(db).get(projectPath);
180653
- return row?.latest ?? null;
180654
- }
180655
- function enforceProjectCap(db, projectPath, maxCommits) {
180656
- if (maxCommits <= 0)
180657
- return 0;
180658
- const count = getCommitCount(db, projectPath);
180659
- if (count <= maxCommits)
180660
- return 0;
180661
- getEvictOverflowStatement(db).run(projectPath, maxCommits);
180662
- const after = getCommitCount(db, projectPath);
180663
- const evicted = Math.max(0, count - after);
180664
- if (evicted > 0) {
180665
- log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
180666
- }
180667
- return evicted;
180668
- }
180669
-
180670
- // src/features/magic-context/git-commits/indexer.ts
181027
+ init_storage_git_commits();
180671
181028
  var MS_PER_DAY = 24 * 60 * 60 * 1000;
180672
181029
  var EMBED_BATCH_SIZE = 16;
180673
181030
  var EMBED_MAX_PER_SWEEP = 500;
@@ -180905,12 +181262,12 @@ function searchGitCommitsSync(db, projectPath, query, options) {
180905
181262
 
180906
181263
  // src/features/magic-context/git-commits/index.ts
180907
181264
  init_storage_git_commit_embeddings();
181265
+ init_storage_git_commits();
180908
181266
  init_sweep_coordinator();
180909
181267
 
180910
181268
  // src/plugin/dream-timer.ts
180911
181269
  init_embedding();
180912
181270
  init_logger();
180913
- init_resolve_fallbacks();
180914
181271
  await init_storage();
180915
181272
  var DREAM_TIMER_INTERVAL_MS = 15 * 60 * 1000;
180916
181273
  var activeTimer = null;
@@ -181013,7 +181370,7 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
181013
181370
  experimentalPinKeyFiles: reg.experimentalPinKeyFiles,
181014
181371
  projectIdentity: reg.projectIdentity,
181015
181372
  sessionDirectoryOverride: reg.directory,
181016
- fallbackModels: resolveFallbackChain(DREAMER_AGENT, reg.dreamerConfig.fallback_models)
181373
+ fallbackModels: resolveFallbackChain(reg.dreamerConfig.fallback_models)
181017
181374
  });
181018
181375
  } catch (error51) {
181019
181376
  log(`[dreamer] timer-triggered queue processing failed for ${reg.projectIdentity}:`, error51);
@@ -181462,10 +181819,9 @@ function makeToolCompositeKey(ownerMsgId, callId) {
181462
181819
  }
181463
181820
  var GET_COUNTER_SQL = `SELECT counter FROM session_meta WHERE session_id = ?`;
181464
181821
  var GET_ASSIGNMENTS_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id FROM tags WHERE session_id = ? ORDER BY tag_number ASC";
181822
+ 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";
181465
181823
  var PROBE_DATA_VERSION_SQL = "PRAGMA main.data_version";
181466
- var PROBE_TOTAL_CHANGES_SQL = "SELECT total_changes() AS tc";
181467
181824
  var probeDataVersionStatements = new WeakMap;
181468
- var probeTotalChangesStatements = new WeakMap;
181469
181825
  function getProbeDataVersionStatement(db) {
181470
181826
  let stmt = probeDataVersionStatements.get(db);
181471
181827
  if (!stmt) {
@@ -181474,14 +181830,6 @@ function getProbeDataVersionStatement(db) {
181474
181830
  }
181475
181831
  return stmt;
181476
181832
  }
181477
- function getProbeTotalChangesStatement(db) {
181478
- let stmt = probeTotalChangesStatements.get(db);
181479
- if (!stmt) {
181480
- stmt = db.prepare(PROBE_TOTAL_CHANGES_SQL);
181481
- probeTotalChangesStatements.set(db, stmt);
181482
- }
181483
- return stmt;
181484
- }
181485
181833
  function isAssignmentRow(row) {
181486
181834
  if (row === null || typeof row !== "object") {
181487
181835
  return false;
@@ -181674,20 +182022,18 @@ function createTagger() {
181674
182022
  }
181675
182023
  function probeSignature(db) {
181676
182024
  const dvRow = getProbeDataVersionStatement(db).get();
181677
- const tcRow = getProbeTotalChangesStatement(db).get();
181678
182025
  return {
181679
- dataVersion: dvRow?.data_version ?? 0,
181680
- totalChanges: tcRow?.tc ?? 0
182026
+ dataVersion: dvRow?.data_version ?? 0
181681
182027
  };
181682
182028
  }
181683
- function initFromDb(sessionId, db) {
182029
+ function initFromDb(sessionId, db, floor = 0) {
181684
182030
  const probe = probeSignature(db);
181685
182031
  const cached2 = loadSignatures.get(sessionId);
181686
- if (cached2 !== undefined && cached2.db === db && cached2.dataVersion === probe.dataVersion && cached2.totalChanges === probe.totalChanges) {
182032
+ if (cached2 !== undefined && cached2.db === db && cached2.dataVersion === probe.dataVersion && cached2.floor === floor) {
181687
182033
  return;
181688
182034
  }
181689
182035
  const row = db.prepare(GET_COUNTER_SQL).get(sessionId);
181690
- const assignmentRows = db.prepare(GET_ASSIGNMENTS_SQL).all(sessionId).filter(isAssignmentRow);
182036
+ const assignmentRows = (floor > 0 ? db.prepare(GET_ASSIGNMENTS_SCOPED_SQL).all(sessionId, floor) : db.prepare(GET_ASSIGNMENTS_SQL).all(sessionId)).filter(isAssignmentRow);
181691
182037
  const sessionAssignments = getSessionAssignments(sessionId);
181692
182038
  sessionAssignments.clear();
181693
182039
  let maxTagNumber = 0;
@@ -181708,7 +182054,7 @@ function createTagger() {
181708
182054
  loadSignatures.set(sessionId, {
181709
182055
  db,
181710
182056
  dataVersion: probe.dataVersion,
181711
- totalChanges: probe.totalChanges
182057
+ floor
181712
182058
  });
181713
182059
  }
181714
182060
  function cleanup(sessionId) {
@@ -181731,13 +182077,13 @@ function createTagger() {
181731
182077
  cleanup
181732
182078
  };
181733
182079
  }
182080
+
181734
182081
  // src/hooks/magic-context/hook.ts
181735
182082
  init_magic_context();
181736
182083
  init_project_identity();
181737
182084
  init_project_embedding_registry();
181738
182085
  await init_storage();
181739
182086
  init_logger();
181740
- init_resolve_fallbacks();
181741
182087
  init_rpc_notifications();
181742
182088
 
181743
182089
  // src/hooks/magic-context/command-handler.ts
@@ -181788,6 +182134,7 @@ await __promiseAll([
181788
182134
  // src/hooks/magic-context/compartment-trigger.ts
181789
182135
  init_compartment_storage();
181790
182136
  init_logger();
182137
+ init_read_session_true_raw_tokens();
181791
182138
  await __promiseAll([
181792
182139
  init_storage(),
181793
182140
  init_protected_tail_boundary(),
@@ -181802,6 +182149,38 @@ var TAIL_SIZE_TRIGGER_MULTIPLIER = 3;
181802
182149
  var FORCE_COMPARTMENT_PERCENTAGE = 80;
181803
182150
  var BLOCK_UNTIL_DONE_PERCENTAGE = 95;
181804
182151
  var FORCE_MATERIALIZE_PERCENTAGE = 85;
182152
+ var CONTENT_TAG_OWNER_SUFFIX = /:(?:p|file)\d+$/;
182153
+ function tagOwnerMessageId(row) {
182154
+ if (row.type === "tool")
182155
+ return row.tool_owner_message_id ?? row.message_id;
182156
+ return row.message_id.replace(CONTENT_TAG_OWNER_SUFFIX, "");
182157
+ }
182158
+ function getActiveOrDroppedTagOwnerMessageIds(db, sessionId, floor = 0) {
182159
+ const rows = floor > 0 ? db.prepare(`SELECT type, message_id, tool_owner_message_id
182160
+ FROM tags
182161
+ WHERE session_id = ? AND status IN ('active', 'dropped') AND tag_number >= ?`).all(sessionId, floor) : db.prepare(`SELECT type, message_id, tool_owner_message_id
182162
+ FROM tags
182163
+ WHERE session_id = ? AND status IN ('active', 'dropped')`).all(sessionId);
182164
+ const owners = new Set;
182165
+ for (const row of rows)
182166
+ owners.add(tagOwnerMessageId(row));
182167
+ return owners;
182168
+ }
182169
+ function estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, taggerFloor = 0) {
182170
+ const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
182171
+ const coveredOwnerMessageIds = getActiveOrDroppedTagOwnerMessageIds(db, sessionId, taggerFloor);
182172
+ let total = 0;
182173
+ for (const message of inMemoryTail.messages) {
182174
+ if (message.ordinal <= lastCompartmentEnd)
182175
+ continue;
182176
+ if (coveredOwnerMessageIds.has(message.id))
182177
+ continue;
182178
+ total += estimateTrueRawMessageTokens(message, {
182179
+ providerShapeVersion: "opencode-v1"
182180
+ }).total;
182181
+ }
182182
+ return total;
182183
+ }
181805
182184
  function buildTriggerInMemoryTail(db, sessionId, messages) {
181806
182185
  if (messages.length === 0)
181807
182186
  return;
@@ -181873,7 +182252,7 @@ function resolveBoundaryContextLimit(usage, fallbackContextLimit) {
181873
182252
  }
181874
182253
  return 128000;
181875
182254
  }
181876
- function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail) {
182255
+ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail, taggerFloor = 0) {
181877
182256
  return withRawSessionMessageCache(() => {
181878
182257
  try {
181879
182258
  const memoryPrimed = inMemoryTail ? primeInMemoryTailRawMessageCache({
@@ -181902,7 +182281,8 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
181902
182281
  contextLimit: resolveBoundaryContextLimit(usage, contextLimit),
181903
182282
  executeThresholdPercentage,
181904
182283
  usage,
181905
- usageSource: "live"
182284
+ usageSource: "live",
182285
+ taggerFloor
181906
182286
  });
181907
182287
  const hasProtectedEligibleHead = boundary.offset < boundary.protectedTailStart;
181908
182288
  if (!hasProtectedEligibleHead) {
@@ -181933,7 +182313,7 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
181933
182313
  }
181934
182314
  });
181935
182315
  }
181936
- function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail) {
182316
+ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail, taggerFloorOverride) {
181937
182317
  if (sessionMeta.compartmentInProgress) {
181938
182318
  sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
181939
182319
  return { shouldFire: false };
@@ -181947,14 +182327,17 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
181947
182327
  inMemoryTail = undefined;
181948
182328
  }
181949
182329
  }
182330
+ const taggerFloor = taggerFloorOverride !== undefined && taggerFloorOverride > 0 ? taggerFloorOverride : inMemoryTail ? deriveTagLoadFloor(db, sessionId, inMemoryTail.messages.map((m) => m.id)) : 0;
181950
182331
  const proactiveFloorForGate = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
181951
- if (!inMemoryTail && usage.percentage < proactiveFloorForGate) {
182332
+ if (usage.percentage < proactiveFloorForGate) {
181952
182333
  try {
181953
- const { bound, nullCount } = getTriggerTagTokenUpperBound(db, sessionId);
182334
+ const { bound: persistedBound, nullCount } = getTriggerTagTokenUpperBound(db, sessionId, taggerFloor);
181954
182335
  if (nullCount === 0) {
181955
- const eligibleUpperBound = bound;
182336
+ const untaggedUpperBound = inMemoryTail ? estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, taggerFloor) : 0;
182337
+ const eligibleUpperBound = persistedBound + untaggedUpperBound;
181956
182338
  if (eligibleUpperBound < triggerBudget) {
181957
- 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`);
182339
+ const memorySuffix = inMemoryTail ? ` (persisted=${persistedBound}, untagged-memory≤${untaggedUpperBound})` : "";
182340
+ 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`);
181958
182341
  return { shouldFire: false };
181959
182342
  }
181960
182343
  }
@@ -181962,7 +182345,7 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
181962
182345
  sessionLog(sessionId, `compartment trigger: cheap-gate skipped (falling through to full read): ${error51 instanceof Error ? error51.message : String(error51)}`);
181963
182346
  }
181964
182347
  }
181965
- const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail);
182348
+ const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail, taggerFloor);
181966
182349
  if (!tailInfo.hasNewRawHistory) {
181967
182350
  try {
181968
182351
  const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
@@ -182328,7 +182711,7 @@ function createMagicContextCommandHandler(deps) {
182328
182711
  const isAugCommand = (command) => command === "ctx-aug";
182329
182712
  const isDreamCommand = (command) => command === "ctx-dream";
182330
182713
  const isSessionUpgradeCommand = (command) => command === "ctx-session-upgrade";
182331
- const isEmbedHistoryCommand = (command) => command === "ctx-embed-history";
182714
+ const isEmbedCommand = (command) => command === "ctx-embed";
182332
182715
  return {
182333
182716
  "command.execute.before": async (input, _output, _params) => {
182334
182717
  const isStatus = isStatusCommand(input.command);
@@ -182337,8 +182720,8 @@ function createMagicContextCommandHandler(deps) {
182337
182720
  const isAug = isAugCommand(input.command);
182338
182721
  const isDream = isDreamCommand(input.command);
182339
182722
  const isSessionUpgrade = isSessionUpgradeCommand(input.command);
182340
- const isEmbedHistory = isEmbedHistoryCommand(input.command);
182341
- if (!isStatus && !isFlush && !isRecomp && !isAug && !isDream && !isSessionUpgrade && !isEmbedHistory) {
182723
+ const isEmbed = isEmbedCommand(input.command);
182724
+ if (!isStatus && !isFlush && !isRecomp && !isAug && !isDream && !isSessionUpgrade && !isEmbed) {
182342
182725
  return;
182343
182726
  }
182344
182727
  const sessionId = input.sessionID;
@@ -182351,15 +182734,50 @@ function createMagicContextCommandHandler(deps) {
182351
182734
  await executeDreaming(deps, sessionId);
182352
182735
  return;
182353
182736
  }
182354
- if (isEmbedHistory) {
182355
- const summary = deps.executeEmbedHistory ? await deps.executeEmbedHistory(sessionId) : "Semantic embedding is not configured for this project, so there is nothing to embed.";
182356
- await deps.sendNotification(sessionId, summary, {});
182357
- throwSentinel(input.command);
182737
+ if (isEmbed) {
182738
+ const sub = input.arguments.trim().toLowerCase();
182739
+ if (sub === "pause") {
182740
+ const summary = deps.pauseEmbedDrain ? deps.pauseEmbedDrain(sessionId) : "Embedding pause is unavailable.";
182741
+ if (isTuiConnected(sessionId)) {
182742
+ pushNotification("action", { action: "show-result-dialog", title: "Embed", message: summary }, sessionId);
182743
+ } else {
182744
+ await deps.sendNotification(sessionId, summary, {});
182745
+ }
182746
+ throwSentinel(input.command);
182747
+ }
182748
+ if (sub === "start") {
182749
+ const summary = deps.executeEmbedHistory ? await deps.executeEmbedHistory(sessionId) : "Semantic embedding is not configured for this project, so there is nothing to embed.";
182750
+ if (isTuiConnected(sessionId)) {
182751
+ pushNotification("action", { action: "show-result-dialog", title: "Embed", message: summary }, sessionId);
182752
+ } else {
182753
+ await deps.sendNotification(sessionId, summary, {});
182754
+ }
182755
+ throwSentinel(input.command);
182756
+ }
182757
+ if (sub !== "") {
182758
+ await deps.sendNotification(sessionId, "Usage: `/ctx-embed` (status), `/ctx-embed start`, or `/ctx-embed pause`.", {});
182759
+ throwSentinel(input.command);
182760
+ }
182761
+ if (isTuiConnected(sessionId)) {
182762
+ pushNotification("action", { action: "show-embed-dialog" }, sessionId);
182763
+ sessionLog(sessionId, "command ctx-embed: pushed show-embed-dialog to TUI");
182764
+ throwSentinel(input.command);
182765
+ }
182766
+ result = deps.getEmbedStatusText ? `## Embedding Status
182767
+
182768
+ ${deps.getEmbedStatusText(sessionId)}` : `## Embedding Status
182769
+
182770
+ Embedding status is unavailable.`;
182358
182771
  }
182359
182772
  if (isFlush) {
182360
182773
  result = executeFlush(deps.db, sessionId);
182361
182774
  clearCachedM0M1(deps.db, sessionId);
182362
182775
  deps.onFlush?.(sessionId);
182776
+ if (isTuiConnected(sessionId)) {
182777
+ pushNotification("action", { action: "show-flush-dialog", message: result }, sessionId);
182778
+ sessionLog(sessionId, "command ctx-flush: pushed show-flush-dialog to TUI");
182779
+ throwSentinel(input.command);
182780
+ }
182363
182781
  }
182364
182782
  if (isStatus) {
182365
182783
  if (isTuiConnected(sessionId)) {
@@ -182486,6 +182904,34 @@ ${snap.error}`;
182486
182904
  // src/hooks/magic-context/hook.ts
182487
182905
  init_derive_budgets();
182488
182906
 
182907
+ // src/hooks/magic-context/embed-session-state.ts
182908
+ var embedPauseBySession = new Set;
182909
+ var embedRunStateBySession = new Map;
182910
+ var autoEmbedAttemptedBySession = new Set;
182911
+ function getEmbedDrainUiStatus(sessionId, progress) {
182912
+ if (embedPauseBySession.has(sessionId)) {
182913
+ return { status: "paused" };
182914
+ }
182915
+ if (progress?.kind === "embed" && progress.phase === "recomp") {
182916
+ return { status: "running" };
182917
+ }
182918
+ if (progress?.kind === "embed" && (progress.phase === "failed" || progress.phase === "skipped") && progress.message) {
182919
+ if (/provider/i.test(progress.message)) {
182920
+ return { status: "stopped", detail: progress.message };
182921
+ }
182922
+ }
182923
+ return { status: "idle" };
182924
+ }
182925
+ function clearEmbedSessionState(sessionId) {
182926
+ embedPauseBySession.delete(sessionId);
182927
+ const ctrl = embedRunStateBySession.get(sessionId);
182928
+ if (ctrl) {
182929
+ ctrl.abort();
182930
+ embedRunStateBySession.delete(sessionId);
182931
+ }
182932
+ autoEmbedAttemptedBySession.delete(sessionId);
182933
+ }
182934
+
182489
182935
  // src/features/magic-context/message-index-async.ts
182490
182936
  init_logger();
182491
182937
  await init_message_index();
@@ -182634,9 +183080,142 @@ function clearSessionTracking(sessionId) {
182634
183080
  // src/hooks/magic-context/event-handler.ts
182635
183081
  init_overflow_detection();
182636
183082
  init_storage_meta_persisted();
183083
+ await init_storage();
183084
+
183085
+ // src/features/magic-context/transform-decision-log.ts
183086
+ await __promiseAll([
183087
+ init_sqlite(),
183088
+ init_storage_db()
183089
+ ]);
183090
+ var canonicalReasons = new Set([
183091
+ "system_hash",
183092
+ "model_change",
183093
+ "project_memory_epoch",
183094
+ "ttl_idle",
183095
+ "explicit_flush",
183096
+ "max_mutation_id",
183097
+ "first_render",
183098
+ "pressure_refold",
183099
+ "upgrade_state",
183100
+ "cached_m1_missing"
183101
+ ]);
183102
+ var piReasonAliases = {
183103
+ project_memory_change: "project_memory_epoch",
183104
+ pending_mutations: "max_mutation_id",
183105
+ renderer_upgrade: "upgrade_state",
183106
+ cache_invalid: "cached_m1_missing",
183107
+ drift: "pressure_refold"
183108
+ };
183109
+ var sharedReasonAliases = {
183110
+ model_key: "model_change",
183111
+ pressure: "pressure_refold"
183112
+ };
183113
+ var pendingDecisionBySession = new Map;
183114
+ var pendingPiDecisionBySession = new Map;
183115
+ var lastBoundMessageIdBySession = new Map;
183116
+ var scheduledWriteTokensBySession = new Map;
183117
+ var writerOverrideForTests = null;
183118
+ function normalizeMaterializeReason(harness, reason, rematerialized) {
183119
+ const raw = typeof reason === "string" ? reason.trim() : "";
183120
+ if (raw.length > 0) {
183121
+ const alias = sharedReasonAliases[raw] ?? (harness === "pi" ? piReasonAliases[raw] : undefined) ?? undefined;
183122
+ if (alias)
183123
+ return alias;
183124
+ if (canonicalReasons.has(raw))
183125
+ return raw;
183126
+ return null;
183127
+ }
183128
+ return rematerialized ? "pressure_refold" : null;
183129
+ }
183130
+ function clearOpenCodePendingTransformDecision(sessionId) {
183131
+ pendingDecisionBySession.delete(sessionId);
183132
+ }
183133
+ function clearTransformDecisionSession(sessionId) {
183134
+ pendingDecisionBySession.delete(sessionId);
183135
+ pendingPiDecisionBySession.delete(sessionId);
183136
+ lastBoundMessageIdBySession.delete(sessionId);
183137
+ scheduledWriteTokensBySession.delete(sessionId);
183138
+ }
183139
+ function recordPendingTransformDecision(sessionId, decision) {
183140
+ if (!decision.bustedThisPass) {
183141
+ pendingDecisionBySession.delete(sessionId);
183142
+ return;
183143
+ }
183144
+ pendingDecisionBySession.set(sessionId, decision);
183145
+ }
183146
+ function scheduleOpenCodeTransformDecisionWrite(args) {
183147
+ const pending = pendingDecisionBySession.get(args.sessionId);
183148
+ if (!pending)
183149
+ return false;
183150
+ if (lastBoundMessageIdBySession.get(args.sessionId) === args.messageId) {
183151
+ return false;
183152
+ }
183153
+ const dbPath = getDatabasePath(args.db);
183154
+ if (!dbPath)
183155
+ return false;
183156
+ lastBoundMessageIdBySession.set(args.sessionId, args.messageId);
183157
+ pendingDecisionBySession.delete(args.sessionId);
183158
+ const token = addScheduledWriteToken(args.sessionId);
183159
+ setTimeout(() => {
183160
+ try {
183161
+ if (!hasScheduledWriteToken(args.sessionId, token))
183162
+ return;
183163
+ writeTransformDecisionBestEffort(dbPath, {
183164
+ ...pending,
183165
+ sessionId: args.sessionId,
183166
+ harness: "opencode",
183167
+ messageId: args.messageId,
183168
+ inputTokens: args.inputTokens
183169
+ });
183170
+ } finally {
183171
+ deleteScheduledWriteToken(args.sessionId, token);
183172
+ }
183173
+ }, 0);
183174
+ return true;
183175
+ }
183176
+ function addScheduledWriteToken(sessionId) {
183177
+ const token = Symbol(sessionId);
183178
+ let tokens = scheduledWriteTokensBySession.get(sessionId);
183179
+ if (!tokens) {
183180
+ tokens = new Set;
183181
+ scheduledWriteTokensBySession.set(sessionId, tokens);
183182
+ }
183183
+ tokens.add(token);
183184
+ return token;
183185
+ }
183186
+ function hasScheduledWriteToken(sessionId, token) {
183187
+ return scheduledWriteTokensBySession.get(sessionId)?.has(token) === true;
183188
+ }
183189
+ function deleteScheduledWriteToken(sessionId, token) {
183190
+ const tokens = scheduledWriteTokensBySession.get(sessionId);
183191
+ if (!tokens)
183192
+ return;
183193
+ tokens.delete(token);
183194
+ if (tokens.size === 0)
183195
+ scheduledWriteTokensBySession.delete(sessionId);
183196
+ }
183197
+ function writeTransformDecisionBestEffort(dbPath, row) {
183198
+ try {
183199
+ const writer = writerOverrideForTests ?? writeTransformDecisionRow;
183200
+ writer(dbPath, row);
183201
+ } catch {}
183202
+ }
183203
+ function writeTransformDecisionRow(dbPath, row) {
183204
+ const db = new Database(dbPath);
183205
+ try {
183206
+ db.exec("PRAGMA busy_timeout=0");
183207
+ db.prepare(`INSERT OR REPLACE INTO transform_decisions (
183208
+ session_id, harness, message_id, ts_ms, decision, materialized,
183209
+ materialize_reason, emergency, dropped_tokens, dropped_count, input_tokens
183210
+ ) 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)));
183211
+ } finally {
183212
+ closeQuietly(db);
183213
+ }
183214
+ }
183215
+
183216
+ // src/hooks/magic-context/event-handler.ts
182637
183217
  init_logger();
182638
183218
  init_models_dev_cache();
182639
- await init_storage();
182640
183219
 
182641
183220
  // src/hooks/magic-context/channel2-delivery.ts
182642
183221
  init_storage_meta_persisted();
@@ -182846,6 +183425,13 @@ function computePressure(input) {
182846
183425
  function approxThousands(tokens) {
182847
183426
  return `${Math.round(tokens / 1000)}k`;
182848
183427
  }
183428
+ function formatOldestReclaimableHint(hint) {
183429
+ if (!hint || hint.length === 0)
183430
+ return "";
183431
+ const rendered = hint.slice(0, 4).map((tag) => `§${tag.tagNumber}§ ${tag.toolName ?? "tool"}`).join(" · ");
183432
+ return rendered.length > 0 ? `
183433
+ oldest reclaimable: ${rendered}.` : "";
183434
+ }
182849
183435
  var CHANNEL2_USABLE_FRACTION = 1 / 3;
182850
183436
  var CHANNEL2_MIN_RECLAIMABLE = 1e4;
182851
183437
  function shouldTriggerChannel2(input) {
@@ -182855,14 +183441,16 @@ function shouldTriggerChannel2(input) {
182855
183441
  return true;
182856
183442
  return input.reclaimableTokens >= input.usableTokens * CHANNEL2_USABLE_FRACTION;
182857
183443
  }
182858
- function buildChannel2Reminder(undroppedTokens) {
183444
+ function buildChannel2Reminder(undroppedTokens, hint) {
182859
183445
  const amount = approxThousands(undroppedTokens);
183446
+ const hintText = formatOldestReclaimableHint(hint);
182860
183447
  return `<system-reminder>
182861
- ` + `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.
183448
+ ` + `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}
182862
183449
  ` + `</system-reminder>`;
182863
183450
  }
182864
- function buildChannel1Reminder(level, undroppedTokens) {
183451
+ function buildChannel1Reminder(level, undroppedTokens, hint) {
182865
183452
  const amount = approxThousands(undroppedTokens);
183453
+ const hintText = formatOldestReclaimableHint(hint);
182866
183454
  let body;
182867
183455
  switch (level) {
182868
183456
  case "gentle":
@@ -182878,7 +183466,7 @@ function buildChannel1Reminder(level, undroppedTokens) {
182878
183466
  return `
182879
183467
 
182880
183468
  <system-reminder>
182881
- ${body}
183469
+ ${body}${hintText}
182882
183470
  </system-reminder>`;
182883
183471
  }
182884
183472
 
@@ -182932,10 +183520,10 @@ async function maybeDeliverChannel2(sessionId, deps) {
182932
183520
  try {
182933
183521
  const client3 = getLiveServerClient(serverUrl, deps.directory);
182934
183522
  const promptContext = await resolvePromptContext(client3, sessionId);
182935
- const reminder = buildChannel2Reminder(deps.reclaimableTokens);
183523
+ const reminder = buildChannel2Reminder(deps.reclaimableTokens, deps.oldestReclaimableToolTags);
182936
183524
  const body = {
182937
183525
  noReply: false,
182938
- parts: [{ type: "text", text: reminder }]
183526
+ parts: [{ type: "text", text: reminder, synthetic: true }]
182939
183527
  };
182940
183528
  if (promptContext?.agent)
182941
183529
  body.agent = promptContext.agent;
@@ -183031,11 +183619,13 @@ function getMessageUpdatedAssistantInfo(properties) {
183031
183619
  }
183032
183620
  const tokens = isRecord(info.tokens) ? info.tokens : undefined;
183033
183621
  const cache = tokens && isRecord(tokens.cache) ? tokens.cache : undefined;
183622
+ const time3 = isRecord(info.time) ? info.time : undefined;
183034
183623
  return {
183035
183624
  role: "assistant",
183036
183625
  finish: typeof info.finish === "string" ? info.finish : undefined,
183037
183626
  sessionID: info.sessionID,
183038
183627
  messageID: typeof info.id === "string" ? info.id : undefined,
183628
+ completedAt: typeof time3?.completed === "number" ? time3.completed : undefined,
183039
183629
  providerID: typeof info.providerID === "string" ? info.providerID : undefined,
183040
183630
  modelID: typeof info.modelID === "string" ? info.modelID : undefined,
183041
183631
  tokens: {
@@ -183098,8 +183688,8 @@ init_project_identity();
183098
183688
  import * as crypto2 from "node:crypto";
183099
183689
  init_session_project_storage();
183100
183690
  init_storage_meta_persisted();
183101
- init_logger();
183102
183691
  await init_storage();
183692
+ init_logger();
183103
183693
 
183104
183694
  // src/hooks/magic-context/boundary-execution.ts
183105
183695
  var FORCE_MATERIALIZE_PERCENTAGE2 = 85;
@@ -183393,7 +183983,8 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
183393
183983
  const result = {
183394
183984
  compressedToLite: 0,
183395
183985
  compressedToFull: 0,
183396
- compressedToUltra: 0
183986
+ compressedToUltra: 0,
183987
+ mutatedTextTags: 0
183397
183988
  };
183398
183989
  if (!config2.enabled)
183399
183990
  return result;
@@ -183434,7 +184025,9 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
183434
184025
  const target = targets.get(tag.tagNumber);
183435
184026
  if (!target)
183436
184027
  continue;
183437
- target.setContent(compressed);
184028
+ const didMutate = target.setContent(compressed);
184029
+ if (didMutate)
184030
+ result.mutatedTextTags += 1;
183438
184031
  updateCavemanDepth(db, sessionId, tag.tagNumber, targetDepth);
183439
184032
  if (targetDepth === DEPTH_LITE)
183440
184033
  result.compressedToLite += 1;
@@ -184022,28 +184615,6 @@ function stripInlineThinking(messages, messageTagNumbers, clearReasoningAge) {
184022
184615
  }
184023
184616
  return stripped;
184024
184617
  }
184025
- function truncateErroredTools(messages, watermark, messageTagNumbers) {
184026
- let truncated = 0;
184027
- for (let i = 0;i < messages.length; i++) {
184028
- const maxTag = messageTagNumbers.get(messages[i]) ?? 0;
184029
- if (maxTag > watermark) {
184030
- continue;
184031
- }
184032
- for (const part of messages[i].parts) {
184033
- if (!isRecord(part) || part.type !== "tool" || !isRecord(part.state)) {
184034
- continue;
184035
- }
184036
- if (part.state.status !== "error") {
184037
- continue;
184038
- }
184039
- if (typeof part.state.error === "string" && part.state.error.length > 100) {
184040
- part.state.error = `${part.state.error.slice(0, 100)}... [truncated]`;
184041
- truncated++;
184042
- }
184043
- }
184044
- }
184045
- return truncated;
184046
- }
184047
184618
  var REASONING_IGNORED_PART_TYPES = new Set([
184048
184619
  "step-start",
184049
184620
  "step-finish",
@@ -184154,6 +184725,7 @@ function stripProcessedImages(messages, frozenIds, options) {
184154
184725
  init_temporal_awareness();
184155
184726
 
184156
184727
  // src/hooks/magic-context/transform-compartment-phase.ts
184728
+ init_compartment_storage();
184157
184729
  init_logger();
184158
184730
  await __promiseAll([
184159
184731
  init_storage(),
@@ -184162,9 +184734,29 @@ await __promiseAll([
184162
184734
  init_send_session_notification();
184163
184735
  await __promiseAll([
184164
184736
  init_inject_compartments(),
184165
- init_protected_tail_boundary()
184737
+ init_protected_tail_boundary(),
184738
+ init_read_session_chunk()
184166
184739
  ]);
184167
- async function runCompartmentPhase(args) {
184740
+ function runCompartmentPhase(args) {
184741
+ const historianRunnable = args.historianRunnable !== false;
184742
+ const willReadRawHistory = historianRunnable && args.canRunCompartments && getActiveCompartmentRun(args.sessionId) === undefined && (args.sessionMeta.compartmentInProgress || !args.skipAwaitForThisPass && args.contextUsage.percentage >= BLOCK_UNTIL_DONE_PERCENTAGE);
184743
+ if (!willReadRawHistory) {
184744
+ return runCompartmentPhaseImpl(args);
184745
+ }
184746
+ return withRawSessionMessageCache(() => {
184747
+ try {
184748
+ primeTailRawMessageCache({
184749
+ sessionId: args.resolvedSessionId,
184750
+ lastCompartmentEnd: getLastCompartmentEndMessage(args.db, args.resolvedSessionId),
184751
+ anchorMessageId: getLastCompartmentEndMessageId(args.db, args.resolvedSessionId)
184752
+ });
184753
+ } catch (error51) {
184754
+ sessionLog(args.sessionId, "compartment phase: tail prime failed (non-fatal):", error51);
184755
+ }
184756
+ return runCompartmentPhaseImpl(args);
184757
+ });
184758
+ }
184759
+ async function runCompartmentPhaseImpl(args) {
184168
184760
  let pendingCompartmentInjection = args.pendingCompartmentInjection;
184169
184761
  let compartmentInProgress = args.sessionMeta.compartmentInProgress;
184170
184762
  let published = false;
@@ -184461,78 +185053,11 @@ function appendReminderToUserMessage(message, reminder) {
184461
185053
 
184462
185054
  // src/hooks/magic-context/apply-operations.ts
184463
185055
  await init_storage();
184464
-
184465
- // src/hooks/magic-context/system-injection-stripper.ts
184466
- var SYSTEM_INJECTION_MARKERS = [
184467
- "<!-- OMO_INTERNAL_INITIATOR -->",
184468
- "[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
184469
- "[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
184470
- "[Category+Skill Reminder]",
184471
- "[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
184472
- "[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
184473
- "[EMERGENCY CONTEXT WINDOW WARNING]",
184474
- "Unstable background agent appears idle",
184475
- "**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
184476
- ];
184477
- var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
184478
- var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
184479
- function stripSystemInjection(text) {
184480
- let hasInjection = false;
184481
- for (const marker of SYSTEM_INJECTION_MARKERS) {
184482
- if (text.includes(marker)) {
184483
- hasInjection = true;
184484
- break;
184485
- }
184486
- }
184487
- if (SYSTEM_REMINDER_REGEX.test(text))
184488
- hasInjection = true;
184489
- SYSTEM_REMINDER_REGEX.lastIndex = 0;
184490
- if (!hasInjection)
184491
- return null;
184492
- let cleaned = text;
184493
- cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
184494
- cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
184495
- cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
184496
- for (const marker of SYSTEM_INJECTION_MARKERS) {
184497
- if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
184498
- continue;
184499
- const idx = cleaned.indexOf(marker);
184500
- if (idx === -1)
184501
- continue;
184502
- const blockEnd = cleaned.indexOf(`
184503
-
184504
- `, idx + marker.length);
184505
- cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
184506
- }
184507
- return cleaned.trim();
184508
- }
184509
-
184510
- // src/hooks/magic-context/apply-operations.ts
184511
- init_tag_part_guards();
184512
- var USER_DROP_PREVIEW_CHARS = 250;
184513
185056
  var RECENT_TOOL_SKELETON_WINDOW = 20;
184514
- function buildReplacementContent(tagId, target) {
184515
- const role = target.message?.info.role;
184516
- if (role !== "user") {
184517
- return `[dropped §${tagId}§]`;
184518
- }
184519
- const currentContent = target.getContent?.() ?? "";
184520
- const strippedInjection = stripSystemInjection(currentContent);
184521
- if (strippedInjection !== null && stripTagPrefix(strippedInjection).trim().length === 0) {
184522
- return `[dropped §${tagId}§]`;
184523
- }
184524
- const originalText = stripTagPrefix(currentContent);
184525
- if (originalText.length <= USER_DROP_PREVIEW_CHARS) {
184526
- return `[truncated §${tagId}§]
184527
- ${originalText}`;
184528
- }
184529
- const hardCut = originalText.slice(0, USER_DROP_PREVIEW_CHARS);
184530
- const softCutIndex = hardCut.search(/\s\S*$/);
184531
- const preview = softCutIndex > USER_DROP_PREVIEW_CHARS - 30 ? hardCut.slice(0, softCutIndex) : hardCut;
184532
- return `[truncated §${tagId}§]
184533
- ${preview}…`;
184534
- }
184535
- function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps) {
185057
+ function buildReplacementContent(tagId) {
185058
+ return `[dropped §${tagId}§]`;
185059
+ }
185060
+ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps, syntheticPendingOps = []) {
184536
185061
  let didMutateMessage = false;
184537
185062
  db.transaction(() => {
184538
185063
  const tags = preloadedTags ?? getTagsBySession(db, sessionId);
@@ -184540,11 +185065,16 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
184540
185065
  const tagTypeById = new Map(tags.map((tag) => [tag.tagNumber, tag.type]));
184541
185066
  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;
184542
185067
  const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
185068
+ const opsToApply = [
185069
+ ...pendingOps.map((op) => ({ op, synthetic: false })),
185070
+ ...syntheticPendingOps.map((op) => ({ op, synthetic: true }))
185071
+ ];
184543
185072
  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));
184544
- for (const pendingOp of pendingOps) {
185073
+ for (const { op: pendingOp, synthetic } of opsToApply) {
184545
185074
  const tagStatus = tagStatusById.get(pendingOp.tagId);
184546
185075
  if (tagStatus === "compacted" || tagStatus === "dropped") {
184547
- removePendingOp(db, sessionId, pendingOp.tagId);
185076
+ if (!synthetic)
185077
+ removePendingOp(db, sessionId, pendingOp.tagId);
184548
185078
  continue;
184549
185079
  }
184550
185080
  if (protectedTagIds.has(pendingOp.tagId)) {
@@ -184552,33 +185082,46 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
184552
185082
  }
184553
185083
  const target = targets.get(pendingOp.tagId);
184554
185084
  const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
185085
+ if (synthetic) {
185086
+ if (!isToolTag || target?.canDrop?.() !== true)
185087
+ continue;
185088
+ }
185089
+ let shouldPersistDrop = false;
184555
185090
  if (isToolTag) {
184556
185091
  if (skeletonWindow.has(pendingOp.tagId)) {
184557
185092
  const truncResult = target?.truncate?.() ?? "absent";
184558
- if (truncResult === "incomplete") {
185093
+ if (truncResult === "incomplete" || synthetic && truncResult !== "truncated") {
184559
185094
  continue;
184560
185095
  }
184561
185096
  if (truncResult === "truncated") {
184562
185097
  didMutateMessage = true;
184563
185098
  }
184564
185099
  updateTagDropMode(db, sessionId, pendingOp.tagId, "truncated");
185100
+ shouldPersistDrop = true;
184565
185101
  } else {
184566
185102
  const dropResult = target?.drop?.() ?? "absent";
184567
- if (dropResult === "incomplete") {
185103
+ if (dropResult === "incomplete" || synthetic && dropResult !== "removed") {
184568
185104
  continue;
184569
185105
  }
184570
185106
  if (dropResult === "removed") {
184571
185107
  didMutateMessage = true;
184572
185108
  }
184573
185109
  updateTagDropMode(db, sessionId, pendingOp.tagId, "full");
185110
+ shouldPersistDrop = true;
184574
185111
  }
184575
185112
  } else if (target) {
184576
- const changed = target.setContent(buildReplacementContent(pendingOp.tagId, target));
185113
+ const changed = target.setContent(buildReplacementContent(pendingOp.tagId));
184577
185114
  if (changed)
184578
185115
  didMutateMessage = true;
185116
+ shouldPersistDrop = true;
185117
+ } else if (!synthetic) {
185118
+ shouldPersistDrop = true;
184579
185119
  }
185120
+ if (!shouldPersistDrop)
185121
+ continue;
184580
185122
  updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
184581
- removePendingOp(db, sessionId, pendingOp.tagId);
185123
+ if (!synthetic)
185124
+ removePendingOp(db, sessionId, pendingOp.tagId);
184582
185125
  }
184583
185126
  })();
184584
185127
  return didMutateMessage;
@@ -184602,7 +185145,7 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
184602
185145
  }
184603
185146
  }
184604
185147
  } else if (target) {
184605
- const changed = target.setContent(buildReplacementContent(tag.tagNumber, target));
185148
+ const changed = target.setContent(buildReplacementContent(tag.tagNumber));
184606
185149
  if (changed)
184607
185150
  didMutateMessage = true;
184608
185151
  }
@@ -184779,6 +185322,7 @@ function createExistingTagResolver(sessionId, tagger, db) {
184779
185322
  return;
184780
185323
  }
184781
185324
  updateTagMessageId(db, sessionId, fallback.tagNumber, currentContentId);
185325
+ tagger.unbindTag(sessionId, fallback.contentId);
184782
185326
  tagger.bindTag(sessionId, currentContentId, fallback.tagNumber);
184783
185327
  usedTagNumbers.add(fallback.tagNumber);
184784
185328
  return fallback.tagNumber;
@@ -184799,7 +185343,45 @@ function logTransformTiming(sessionId, stage, startMs, extra) {
184799
185343
  }
184800
185344
 
184801
185345
  // src/hooks/magic-context/tag-messages.ts
184802
- function deriveToolOwnerMessageId(sessionId, db, message, obs, unpaired) {
185346
+ var TOOL_OWNER_CACHE_KEY_SEP = "\x00";
185347
+ function makeToolOwnerCacheKey(sessionId, callId) {
185348
+ return `${sessionId}${TOOL_OWNER_CACHE_KEY_SEP}${callId}`;
185349
+ }
185350
+ function getCachedCandidateToolOwners(db, sessionId, callId, cache, onLookup) {
185351
+ const key = makeToolOwnerCacheKey(sessionId, callId);
185352
+ const cached2 = cache.candidateOwnersByCallId.get(key);
185353
+ if (cached2 !== undefined)
185354
+ return cached2;
185355
+ onLookup?.({ kind: "candidates", callId });
185356
+ const candidates = getCandidateToolOwners(db, sessionId, callId);
185357
+ cache.candidateOwnersByCallId.set(key, candidates);
185358
+ return candidates;
185359
+ }
185360
+ function getCachedMessageTimesFromOpenCodeDb(sessionId, messageIds, cache, onLookup) {
185361
+ const uncached = [...new Set(messageIds)].filter((id) => !cache.messageTimesById.has(id));
185362
+ if (uncached.length > 0) {
185363
+ onLookup?.({ kind: "messageTimes", messageIds: uncached });
185364
+ const resolved = getMessageTimesFromOpenCodeDb(sessionId, uncached);
185365
+ for (const id of uncached) {
185366
+ cache.messageTimesById.set(id, resolved.get(id) ?? null);
185367
+ }
185368
+ }
185369
+ const times = new Map;
185370
+ for (const id of messageIds) {
185371
+ const time3 = cache.messageTimesById.get(id);
185372
+ if (typeof time3 === "number")
185373
+ times.set(id, time3);
185374
+ }
185375
+ return times;
185376
+ }
185377
+ function invalidateCachedCandidateToolOwnersIfNewOwner(cache, sessionId, callId, ownerMsgId) {
185378
+ const key = makeToolOwnerCacheKey(sessionId, callId);
185379
+ const cached2 = cache.candidateOwnersByCallId.get(key);
185380
+ if (cached2 !== undefined && !cached2.includes(ownerMsgId)) {
185381
+ cache.candidateOwnersByCallId.delete(key);
185382
+ }
185383
+ }
185384
+ function deriveToolOwnerMessageId(sessionId, db, message, obs, unpaired, cache, onFallbackLookup) {
184803
185385
  const messageId = typeof message.info.id === "string" ? message.info.id : "";
184804
185386
  if (obs.kind === "invocation") {
184805
185387
  if (messageId) {
@@ -184819,10 +185401,10 @@ function deriveToolOwnerMessageId(sessionId, db, message, obs, unpaired) {
184819
185401
  return popped;
184820
185402
  }
184821
185403
  if (messageId) {
184822
- const candidates = getCandidateToolOwners(db, sessionId, obs.callId);
185404
+ const candidates = getCachedCandidateToolOwners(db, sessionId, obs.callId, cache, onFallbackLookup);
184823
185405
  if (candidates.length > 0) {
184824
185406
  const ids = [...candidates, messageId];
184825
- const times = getMessageTimesFromOpenCodeDb(sessionId, ids);
185407
+ const times = getCachedMessageTimesFromOpenCodeDb(sessionId, ids, cache, onFallbackLookup);
184826
185408
  const persisted = pickNearestPriorOwner(candidates, messageId, times);
184827
185409
  if (persisted !== null)
184828
185410
  return persisted;
@@ -184903,6 +185485,7 @@ function extractToolTagMetadata(part) {
184903
185485
  }
184904
185486
  function tagMessages(sessionId, messages, tagger, db, options = {}) {
184905
185487
  const skipPrefixInjection = options.skipPrefixInjection === true;
185488
+ const onToolOwnerFallbackLookup = options.onToolOwnerFallbackLookup;
184906
185489
  const targets = new Map;
184907
185490
  const reasoningByMessage = new Map;
184908
185491
  const messageTagNumbers = new Map;
@@ -184910,6 +185493,10 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
184910
185493
  const toolThinkingByCallId = new Map;
184911
185494
  const toolCallIndex = new Map;
184912
185495
  const unpairedInvocations = new Map;
185496
+ const ownerDerivationCache = {
185497
+ candidateOwnersByCallId: new Map,
185498
+ messageTimesById: new Map
185499
+ };
184913
185500
  const ownerByPartKey = new Map;
184914
185501
  const batch = new ToolMutationBatch(messages);
184915
185502
  const assignments = tagger.getAssignments(sessionId);
@@ -184950,7 +185537,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
184950
185537
  const toolObservation = extractToolCallObservation(part);
184951
185538
  if (toolObservation) {
184952
185539
  const _tDerive = performance.now();
184953
- const ownerMsgId = deriveToolOwnerMessageId(sessionId, db, message, toolObservation, unpairedInvocations);
185540
+ const ownerMsgId = deriveToolOwnerMessageId(sessionId, db, message, toolObservation, unpairedInvocations, ownerDerivationCache, onToolOwnerFallbackLookup);
184954
185541
  accDerive += performance.now() - _tDerive;
184955
185542
  const compositeKey = makeToolCompositeKey(ownerMsgId, toolObservation.callId);
184956
185543
  const entry = toolCallIndex.get(compositeKey) ?? {
@@ -184969,6 +185556,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
184969
185556
  if (orphan !== null) {
184970
185557
  const claimed = adoptNullOwnerToolTag(db, orphan.id, ownerMsgId);
184971
185558
  if (claimed) {
185559
+ invalidateCachedCandidateToolOwnersIfNewOwner(ownerDerivationCache, sessionId, toolObservation.callId, ownerMsgId);
184972
185560
  tagger.bindToolTag(sessionId, toolObservation.callId, ownerMsgId, orphan.tagNumber);
184973
185561
  existingTagId = orphan.tagNumber;
184974
185562
  } else {
@@ -184976,6 +185564,13 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
184976
185564
  }
184977
185565
  }
184978
185566
  }
185567
+ if (existingTagId === undefined) {
185568
+ const persisted = getToolTagNumberByOwner(db, sessionId, toolObservation.callId, ownerMsgId);
185569
+ if (persisted !== null) {
185570
+ tagger.bindToolTag(sessionId, toolObservation.callId, ownerMsgId, persisted);
185571
+ existingTagId = persisted;
185572
+ }
185573
+ }
184979
185574
  if (existingTagId !== undefined) {
184980
185575
  toolTagByCallId.set(compositeKey, existingTagId);
184981
185576
  messageTagNumbers.set(message, Math.max(messageTagNumbers.get(message) ?? 0, existingTagId));
@@ -185048,6 +185643,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
185048
185643
  inputTokenCount,
185049
185644
  reasoningTokenCount: reasoningTokens
185050
185645
  }));
185646
+ invalidateCachedCandidateToolOwnersIfNewOwner(ownerDerivationCache, sessionId, toolPart.callID, ownerMsgId);
185051
185647
  accAssignToolTag += performance.now() - _tAssignTool;
185052
185648
  messageTagNumbers.set(message, Math.max(messageTagNumbers.get(message) ?? 0, tagId));
185053
185649
  if (!skipPrefixInjection) {
@@ -185119,7 +185715,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
185119
185715
  logTransformTiming(sessionId, "tag.saveSource", performance.now() - accSaveSource);
185120
185716
  for (const [compositeKey, tagId] of toolTagByCallId) {
185121
185717
  const thinkingParts = toolThinkingByCallId.get(compositeKey) ?? [];
185122
- targets.set(tagId, createToolDropTarget(compositeKey, thinkingParts, toolCallIndex, batch));
185718
+ targets.set(tagId, createToolDropTarget(compositeKey, thinkingParts, toolCallIndex, batch, tagId));
185123
185719
  }
185124
185720
  const hasRecentReduceCall = lastReduceMessageIndex >= 0 && messages.length - lastReduceMessageIndex <= RECENT_REDUCE_LOOKBACK;
185125
185721
  return {
@@ -186046,7 +186642,7 @@ async function runAutoSearchHint(args) {
186046
186642
  embeddingEnabled,
186047
186643
  gitCommitsEnabled,
186048
186644
  embedQuery: async (text, signal) => {
186049
- const result = await embedTextForProject(options.projectPath, text, signal);
186645
+ const result = await embedTextForProject(options.projectPath, text, signal, "query");
186050
186646
  return result?.vector ?? null;
186051
186647
  },
186052
186648
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
@@ -186227,6 +186823,51 @@ function planEmergencyDrop(input) {
186227
186823
  };
186228
186824
  }
186229
186825
 
186826
+ // src/hooks/magic-context/system-injection-stripper.ts
186827
+ var SYSTEM_INJECTION_MARKERS = [
186828
+ "<!-- OMO_INTERNAL_INITIATOR -->",
186829
+ "[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
186830
+ "[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
186831
+ "[Category+Skill Reminder]",
186832
+ "[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
186833
+ "[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
186834
+ "[EMERGENCY CONTEXT WINDOW WARNING]",
186835
+ "Unstable background agent appears idle",
186836
+ "**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
186837
+ ];
186838
+ var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
186839
+ var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
186840
+ function stripSystemInjection(text) {
186841
+ let hasInjection = false;
186842
+ for (const marker of SYSTEM_INJECTION_MARKERS) {
186843
+ if (text.includes(marker)) {
186844
+ hasInjection = true;
186845
+ break;
186846
+ }
186847
+ }
186848
+ if (SYSTEM_REMINDER_REGEX.test(text))
186849
+ hasInjection = true;
186850
+ SYSTEM_REMINDER_REGEX.lastIndex = 0;
186851
+ if (!hasInjection)
186852
+ return null;
186853
+ let cleaned = text;
186854
+ cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
186855
+ cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
186856
+ cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
186857
+ for (const marker of SYSTEM_INJECTION_MARKERS) {
186858
+ if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
186859
+ continue;
186860
+ const idx = cleaned.indexOf(marker);
186861
+ if (idx === -1)
186862
+ continue;
186863
+ const blockEnd = cleaned.indexOf(`
186864
+
186865
+ `, idx + marker.length);
186866
+ cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
186867
+ }
186868
+ return cleaned.trim();
186869
+ }
186870
+
186230
186871
  // src/hooks/magic-context/heuristic-cleanup.ts
186231
186872
  init_tag_part_guards();
186232
186873
  var DEDUP_SAFE_TOOLS = new Set([
@@ -186245,6 +186886,7 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
186245
186886
  const maxTag = getMaxTagNumberBySession(db, sessionId);
186246
186887
  const protectedCutoff = maxTag - config2.protectedTags;
186247
186888
  let droppedTools = 0;
186889
+ let emergencyDroppedTools = 0;
186248
186890
  let deduplicatedTools = 0;
186249
186891
  let droppedInjections = 0;
186250
186892
  if (config2.emergency) {
@@ -186276,6 +186918,7 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
186276
186918
  updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
186277
186919
  updateTagDropMode(db, sessionId, tag.tagNumber, "full");
186278
186920
  droppedTools++;
186921
+ emergencyDroppedTools++;
186279
186922
  }
186280
186923
  }
186281
186924
  setEmergencyDropSample(db, sessionId, emergency.currentTotalInputTokens);
@@ -186354,7 +186997,9 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
186354
186997
  continue;
186355
186998
  updateTagDropMode(db, sessionId, tag.tagNumber, "full");
186356
186999
  updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
186357
- deduplicatedTools++;
187000
+ if (result === "removed" || result === "truncated") {
187001
+ deduplicatedTools++;
187002
+ }
186358
187003
  }
186359
187004
  }
186360
187005
  })();
@@ -186363,6 +187008,7 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
186363
187008
  sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
186364
187009
  }
186365
187010
  let compressedTextTags = 0;
187011
+ let mutatedTextTags = 0;
186366
187012
  if (config2.caveman?.enabled) {
186367
187013
  const cavemanResult = applyCavemanCleanup(sessionId, db, targets, tags, {
186368
187014
  enabled: true,
@@ -186370,8 +187016,16 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
186370
187016
  protectedTags: config2.protectedTags
186371
187017
  });
186372
187018
  compressedTextTags = cavemanResult.compressedToLite + cavemanResult.compressedToFull + cavemanResult.compressedToUltra;
187019
+ mutatedTextTags = cavemanResult.mutatedTextTags;
186373
187020
  }
186374
- return { droppedTools, deduplicatedTools, droppedInjections, compressedTextTags };
187021
+ return {
187022
+ droppedTools,
187023
+ deduplicatedTools,
187024
+ droppedInjections,
187025
+ emergencyDroppedTools,
187026
+ compressedTextTags,
187027
+ mutatedTextTags
187028
+ };
186375
187029
  }
186376
187030
  function extractToolInfo(part) {
186377
187031
  if (part.type === "tool" && typeof part.tool === "string" && DEDUP_SAFE_TOOLS.has(part.tool)) {
@@ -186545,6 +187199,42 @@ function isTodoItem(value) {
186545
187199
  return typeof todo.content === "string" && typeof todo.status === "string" && (todo.priority === undefined || typeof todo.priority === "string");
186546
187200
  }
186547
187201
 
187202
+ // src/hooks/magic-context/tool-reclaim.ts
187203
+ await init_storage();
187204
+ function buildSyntheticToolReclaimOps(input) {
187205
+ const watermark = Math.max(0, input.watermark);
187206
+ if (watermark <= 0)
187207
+ return [];
187208
+ const realPendingTagIds = new Set((input.pendingOps ?? []).map((op) => op.tagId));
187209
+ const tags = getActiveTagsBySession(input.db, input.sessionId);
187210
+ const synthetic = [];
187211
+ for (const tag of tags) {
187212
+ if (tag.type !== "tool")
187213
+ continue;
187214
+ if (tag.status !== "active")
187215
+ continue;
187216
+ if (tag.tagNumber > watermark)
187217
+ continue;
187218
+ if (realPendingTagIds.has(tag.tagNumber))
187219
+ continue;
187220
+ if (input.targets.get(tag.tagNumber)?.canDrop?.() !== true)
187221
+ continue;
187222
+ synthetic.push({
187223
+ id: 0,
187224
+ sessionId: input.sessionId,
187225
+ tagId: tag.tagNumber,
187226
+ operation: "drop",
187227
+ queuedAt: 0
187228
+ });
187229
+ }
187230
+ return synthetic;
187231
+ }
187232
+ function advanceToolReclaimWatermarkToCurrentMax(db, sessionId) {
187233
+ const maxTagNumber = getMaxTagNumberBySession(db, sessionId);
187234
+ advanceToolReclaimWatermark(db, sessionId, maxTagNumber);
187235
+ return maxTagNumber;
187236
+ }
187237
+
186548
187238
  // src/hooks/magic-context/transform-postprocess-phase.ts
186549
187239
  var DEGRADE_CACHE_WARNING_THRESHOLD = 10;
186550
187240
  var degradedCacheCountBySession = new BoundedSessionMap(100);
@@ -186561,7 +187251,6 @@ async function runPostTransformPhase(args) {
186561
187251
  const emergencyDropEligible = args.contextUsage.percentage >= args.forceMaterializationPercentage;
186562
187252
  const activeCompartmentRun = args.canRunCompartments ? getActiveCompartmentRun(args.sessionId) : undefined;
186563
187253
  const compartmentRunning = args.canRunCompartments && !args.awaitedCompartmentRun && activeCompartmentRun !== undefined;
186564
- const emergencyBypassCompartmentGate = forceMaterialization;
186565
187254
  const deferredMaterialize = args.canConsumeDeferredLate && deferredMaterializationWasPending;
186566
187255
  const materializationRequested = isExplicitFlush || deferredMaterialize;
186567
187256
  const m0M1EnabledForFold = args.fullFeatureMode && args.m0M1 !== undefined && (!!args.m0M1.projectPath || !!args.m0M1.projectDirectory);
@@ -186573,11 +187262,12 @@ async function runPostTransformPhase(args) {
186573
187262
  projectDirectory: args.m0M1.projectDirectory,
186574
187263
  hardSignals: args.m0M1.hardSignals
186575
187264
  }).value : false;
187265
+ const bypassCompartmentGate = forceMaterialization || m0HardFoldThisPass;
186576
187266
  const shouldReadPendingOps = materializationRequested || args.schedulerDecision === "execute" || forceMaterialization || m0HardFoldThisPass || compartmentRunning;
186577
187267
  const pendingOps = shouldReadPendingOps ? getPendingOps(args.db, args.sessionId) : [];
186578
187268
  const hasPendingUserOps = pendingOps.length > 0;
186579
- const shouldApplyPendingOps = (args.schedulerDecision === "execute" || materializationRequested || forceMaterialization || m0HardFoldThisPass) && (!compartmentRunning || emergencyBypassCompartmentGate);
186580
- const shouldRunHeuristics = (!compartmentRunning || emergencyBypassCompartmentGate) && (materializationRequested || forceMaterialization || m0HardFoldThisPass || emergencyDropEligible || args.schedulerDecision === "execute" && (!alreadyRanThisTurn || !args.fullFeatureMode));
187269
+ const shouldApplyPendingOps = (args.schedulerDecision === "execute" || materializationRequested || forceMaterialization || m0HardFoldThisPass) && (!compartmentRunning || bypassCompartmentGate);
187270
+ const shouldRunHeuristics = (!compartmentRunning || bypassCompartmentGate) && (materializationRequested || forceMaterialization || m0HardFoldThisPass || emergencyDropEligible || args.schedulerDecision === "execute" && (!alreadyRanThisTurn || !args.fullFeatureMode));
186581
187271
  const isCacheBustingPass = shouldApplyPendingOps || shouldRunHeuristics;
186582
187272
  const canUseEmptySentinels = modelAcceptsEmptyContent(args.resolvedProviderID);
186583
187273
  if (shouldRunHeuristics) {
@@ -186589,8 +187279,9 @@ async function runPostTransformPhase(args) {
186589
187279
  sessionLog(args.sessionId, `transform: skipping heuristics (already ran for turn ${args.currentTurnId})`);
186590
187280
  }
186591
187281
  if (compartmentRunning && hasPendingUserOps) {
186592
- if (emergencyBypassCompartmentGate) {
186593
- sessionLog(args.sessionId, `transform: emergency bypass applying ${pendingOps.length} pending ops while compartment agent runs (${args.contextUsage.percentage.toFixed(1)}%)`);
187282
+ if (bypassCompartmentGate) {
187283
+ const bypassReason = forceMaterialization ? "emergency >=85%" : "m0 hard fold";
187284
+ sessionLog(args.sessionId, `transform: compartment-gate bypass (${bypassReason}) — applying ${pendingOps.length} pending ops while compartment agent runs (${args.contextUsage.percentage.toFixed(1)}%)`);
186594
187285
  } else {
186595
187286
  sessionLog(args.sessionId, "transform: deferring pending ops — compartment agent in progress");
186596
187287
  }
@@ -186599,12 +187290,24 @@ async function runPostTransformPhase(args) {
186599
187290
  let deferredMaterializedSuccessfully = false;
186600
187291
  let heuristicsRanSuccessfully = false;
186601
187292
  let pendingOpsRanSuccessfully = false;
187293
+ let pendingOpsDidMutate = false;
187294
+ let heuristicOrReasoningDidMutate = false;
187295
+ let droppedCount = 0;
187296
+ const droppedTokens = 0;
187297
+ let emergency = false;
187298
+ let m0RematerializedThisPass = false;
187299
+ let m0MaterializeReason = null;
187300
+ let m0M1InjectedThisPass = false;
187301
+ let autoReclaimDidMutateThisPass = false;
186602
187302
  try {
186603
187303
  if (shouldApplyPendingOps) {
186604
187304
  const applyReason = isExplicitFlush ? "explicit_flush" : deferredMaterialize ? "deferred_materialization" : `scheduler_execute (scheduler=${args.schedulerDecision})`;
186605
187305
  sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOps.length}, context=${args.contextUsage.percentage.toFixed(1)}%`);
186606
187306
  const tApply = performance.now();
186607
- applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, pendingOps);
187307
+ pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, pendingOps);
187308
+ if (pendingOpsDidMutate) {
187309
+ droppedCount += pendingOps.length;
187310
+ }
186608
187311
  logTransformTiming(args.sessionId, "applyPendingOperations", tApply);
186609
187312
  }
186610
187313
  if (shouldRunHeuristics) {
@@ -186622,9 +187325,12 @@ async function runPostTransformPhase(args) {
186622
187325
  } : undefined,
186623
187326
  caveman: cavemanConfig
186624
187327
  }, heuristicTags);
186625
- logTransformTiming(args.sessionId, "applyHeuristicCleanup", t5, `droppedTools=${cleanup.droppedTools} deduplicatedTools=${cleanup.deduplicatedTools} droppedInjections=${cleanup.droppedInjections} compressedTextTags=${cleanup.compressedTextTags}`);
187328
+ logTransformTiming(args.sessionId, "applyHeuristicCleanup", t5, `droppedTools=${cleanup.droppedTools} deduplicatedTools=${cleanup.deduplicatedTools} droppedInjections=${cleanup.droppedInjections} compressedTextTags=${cleanup.compressedTextTags} mutatedTextTags=${cleanup.mutatedTextTags}`);
187329
+ const heuristicMutationCount = cleanup.droppedTools + cleanup.deduplicatedTools + cleanup.droppedInjections + cleanup.mutatedTextTags;
187330
+ droppedCount += cleanup.droppedTools + cleanup.deduplicatedTools + cleanup.droppedInjections + cleanup.mutatedTextTags;
187331
+ emergency ||= cleanup.emergencyDroppedTools > 0;
186626
187332
  const t7 = performance.now();
186627
- const clearedReasoning = clearOldReasoning(args.messages, args.reasoningByMessage, args.messageTagNumbers, args.clearReasoningAge);
187333
+ const clearedReasoning = canUseEmptySentinels ? clearOldReasoning(args.messages, args.reasoningByMessage, args.messageTagNumbers, args.clearReasoningAge) : 0;
186628
187334
  if (canUseEmptySentinels) {
186629
187335
  stripClearedReasoning(args.messages);
186630
187336
  }
@@ -186648,6 +187354,8 @@ async function runPostTransformPhase(args) {
186648
187354
  }
186649
187355
  }
186650
187356
  logTransformTiming(args.sessionId, "clearOldReasoning", t7);
187357
+ heuristicOrReasoningDidMutate = heuristicMutationCount + clearedReasoning + strippedInline > 0;
187358
+ droppedCount += clearedReasoning + strippedInline;
186651
187359
  if (pendingMaterializationAtPassStart) {
186652
187360
  args.pendingMaterializationSessions.delete(args.sessionId);
186653
187361
  }
@@ -186658,7 +187366,35 @@ async function runPostTransformPhase(args) {
186658
187366
  if (args.schedulerDecision === "execute" && !materializationRequested) {
186659
187367
  updateSessionMeta(args.db, args.sessionId, { lastResponseTime: Date.now() });
186660
187368
  }
187369
+ const toolReclaimExecutePass = args.schedulerDecision === "execute";
187370
+ const alreadyMutatingThisPass = pendingOpsDidMutate || heuristicOrReasoningDidMutate;
187371
+ let autoReclaimTargetCount = 0;
187372
+ let autoReclaimDidMutate = false;
187373
+ if (toolReclaimExecutePass && alreadyMutatingThisPass && !emergencyDropEligible) {
187374
+ const syntheticPendingOps = buildSyntheticToolReclaimOps({
187375
+ db: args.db,
187376
+ sessionId: args.sessionId,
187377
+ targets: args.targets,
187378
+ watermark: args.sessionMeta.toolReclaimWatermark ?? 0,
187379
+ pendingOps
187380
+ });
187381
+ autoReclaimTargetCount = syntheticPendingOps.length;
187382
+ if (syntheticPendingOps.length > 0) {
187383
+ autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, [], syntheticPendingOps);
187384
+ if (autoReclaimDidMutate) {
187385
+ droppedCount += syntheticPendingOps.length;
187386
+ autoReclaimDidMutateThisPass = true;
187387
+ }
187388
+ }
187389
+ }
186661
187390
  args.batch?.finalize();
187391
+ if (toolReclaimExecutePass) {
187392
+ const maxTagNumber = advanceToolReclaimWatermarkToCurrentMax(args.db, args.sessionId);
187393
+ args.sessionMeta.toolReclaimWatermark = Math.max(args.sessionMeta.toolReclaimWatermark ?? 0, maxTagNumber);
187394
+ }
187395
+ if (autoReclaimTargetCount > 0) {
187396
+ sessionLog(args.sessionId, `tool reclaim auto-drop: targets=${autoReclaimTargetCount} mutated=${autoReclaimDidMutate}`);
187397
+ }
186662
187398
  logTransformTiming(args.sessionId, "batchFinalize:heuristics", performance.now());
186663
187399
  if (args.sessionMeta.lastTransformError !== null) {
186664
187400
  updateSessionMeta(args.db, args.sessionId, { lastTransformError: null });
@@ -186670,11 +187406,6 @@ async function runPostTransformPhase(args) {
186670
187406
  deferredMaterializedSuccessfully = true;
186671
187407
  heuristicsRanSuccessfully = true;
186672
187408
  }
186673
- if (args.watermark > 0) {
186674
- const tWatermarkCleanup = performance.now();
186675
- truncateErroredTools(args.messages, args.watermark, args.messageTagNumbers);
186676
- logTransformTiming(args.sessionId, "watermarkCleanup", tWatermarkCleanup);
186677
- }
186678
187409
  if (shouldApplyPendingOps) {
186679
187410
  pendingOpsRanSuccessfully = true;
186680
187411
  }
@@ -186733,6 +187464,9 @@ async function runPostTransformPhase(args) {
186733
187464
  hardSignals: args.m0M1.hardSignals
186734
187465
  });
186735
187466
  if (result.injected) {
187467
+ m0M1InjectedThisPass = true;
187468
+ m0RematerializedThisPass ||= result.m0RematerializedThisPass;
187469
+ m0MaterializeReason = result.decision.reason ?? m0MaterializeReason;
186736
187470
  sessionLog(args.sessionId, `transform: injected m[0]/m[1] (rematerialized=${result.m0RematerializedThisPass}, reason=${result.decision.reason ?? "cache_hit"})`);
186737
187471
  }
186738
187472
  } catch (error51) {
@@ -186941,7 +187675,19 @@ async function runPostTransformPhase(args) {
186941
187675
  sessionLog(args.sessionId, `sticky-injection GC: pruned ${prunedAnchors} note-nudge anchor(s), ${prunedDecisions} auto-search decision(s)`);
186942
187676
  }
186943
187677
  }
186944
- return { explicitMaterializedSuccessfully, deferredMaterializedSuccessfully };
187678
+ const materializeReason = m0MaterializeReason ?? (explicitMaterializedSuccessfully ? "explicit_flush" : null);
187679
+ const materialized = m0RematerializedThisPass || explicitMaterializedSuccessfully || deferredMaterializedSuccessfully;
187680
+ const bustedThisPass = args.didMutateFromFlushedStatuses || pendingOpsDidMutate || heuristicOrReasoningDidMutate || autoReclaimDidMutateThisPass || m0RematerializedThisPass || m0M1InjectedThisPass && historyWasConsumedThisPass || historyWasConsumedThisPass;
187681
+ return {
187682
+ explicitMaterializedSuccessfully,
187683
+ deferredMaterializedSuccessfully,
187684
+ materialized,
187685
+ materializeReason,
187686
+ droppedTokens,
187687
+ droppedCount,
187688
+ emergency,
187689
+ bustedThisPass
187690
+ };
186945
187691
  }
186946
187692
  function checkM0MutationDriftAndSignal(args) {
186947
187693
  const currentMaxMutationId = getMaxM0MutationId(args.db, args.sessionId) ?? 0;
@@ -186976,6 +187722,12 @@ function clearMessageTokensCache(sessionId, messageId) {
186976
187722
  cache.delete(messageId);
186977
187723
  }
186978
187724
  var recordedSessionProjectIdentity = new BoundedSessionMap(MESSAGE_TOKENS_CACHE_MAX);
187725
+ function deriveTaggerLoadFloor(messages, sessionId, db) {
187726
+ return deriveTagLoadFloor(db, sessionId, function* () {
187727
+ for (const message of messages)
187728
+ yield message.info?.id;
187729
+ }());
187730
+ }
186979
187731
  function findLastAssistantModel2(messages) {
186980
187732
  for (let i = messages.length - 1;i >= 0; i--) {
186981
187733
  const info = messages[i].info;
@@ -186998,6 +187750,7 @@ function createTransform(deps) {
186998
187750
  return;
186999
187751
  }
187000
187752
  const resolvedSessionId = sessionId;
187753
+ clearOpenCodePendingTransformDecision(sessionId);
187001
187754
  logTransformTiming(sessionId, "findSessionId", startTime, `messages=${messages.length}`);
187002
187755
  const db = deps.db;
187003
187756
  if (deps.client !== undefined) {
@@ -187300,12 +188053,16 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187300
188053
  recordSessionProjectIdentity(db, sessionId, sessionProjectIdentity);
187301
188054
  recordedSessionProjectIdentity.set(sessionId, sessionProjectIdentity);
187302
188055
  }
188056
+ const taggerFloor = deriveTaggerLoadFloor(messages, sessionId, db);
188057
+ if (taggerFloor === 0 && messages.length > 0) {
188058
+ sessionLog(sessionId, `tag floor: 0 (full-scan fallback) — no leading wire message resolved a tag across ${messages.length} msgs`);
188059
+ }
187303
188060
  let triggerBoundarySnapshot;
187304
188061
  if (fullFeatureMode && historianRunnable && !sessionMeta.compartmentInProgress) {
187305
188062
  const tTrigger = performance.now();
187306
188063
  try {
187307
188064
  const inMemoryTail = buildTriggerInMemoryTail(db, sessionId, extractInMemoryMessageViews(messages));
187308
- const triggerResult = checkCompartmentTrigger(db, sessionId, sessionMeta, boundaryUsageForProtectedTail, sessionMeta.lastContextPercentage, boundaryExecuteThreshold, deriveTriggerBudget(boundaryContextLimit, boundaryExecuteThreshold), deps.clearReasoningAge, deps.commitClusterTrigger, undefined, boundaryContextLimit, inMemoryTail);
188065
+ const triggerResult = checkCompartmentTrigger(db, sessionId, sessionMeta, boundaryUsageForProtectedTail, sessionMeta.lastContextPercentage, boundaryExecuteThreshold, deriveTriggerBudget(boundaryContextLimit, boundaryExecuteThreshold), deps.clearReasoningAge, deps.commitClusterTrigger, undefined, boundaryContextLimit, inMemoryTail, taggerFloor);
187309
188066
  if (triggerResult.shouldFire) {
187310
188067
  sessionLog(sessionId, `compartment trigger: firing (reason=${triggerResult.reason})`);
187311
188068
  updateSessionMeta(db, sessionId, { compartmentInProgress: true });
@@ -187347,7 +188104,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187347
188104
  try {
187348
188105
  const t0 = performance.now();
187349
188106
  const tInitFromDb = performance.now();
187350
- deps.tagger.initFromDb(sessionId, db);
188107
+ deps.tagger.initFromDb(sessionId, db, taggerFloor);
187351
188108
  logTransformTiming(sessionId, "tag.initFromDb", tInitFromDb);
187352
188109
  const skipPrefixInjection = !ctxReduceEnabledEffective;
187353
188110
  const result = tagMessages(sessionId, messages, deps.tagger, db, {
@@ -187399,7 +188156,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187399
188156
  const persistedReasoningWatermark = sessionMeta?.clearedReasoningThroughTag ?? 0;
187400
188157
  if (persistedReasoningWatermark > 0) {
187401
188158
  const tReplay = performance.now();
187402
- const replayed = replayClearedReasoning(messages, reasoningByMessage, messageTagNumbers, persistedReasoningWatermark);
188159
+ const replayed = canUseEmptySentinels ? replayClearedReasoning(messages, reasoningByMessage, messageTagNumbers, persistedReasoningWatermark) : 0;
187403
188160
  const replayedInline = replayStrippedInlineThinking(messages, messageTagNumbers, persistedReasoningWatermark);
187404
188161
  if (replayed > 0 || replayedInline > 0) {
187405
188162
  sessionLog(sessionId, `reasoning replay: cleared=${replayed} inlineStripped=${replayedInline} (watermark=${persistedReasoningWatermark})`);
@@ -187497,7 +188254,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187497
188254
  const wasEmergencyBlock = contextUsageEarly.percentage >= FORCE_MATERIALIZE_PERCENTAGE && compartmentPhase.justAwaitedPublication;
187498
188255
  const historyRebuiltThisPass = wasEmergencyBlock ? compartmentPhase.rebuiltHistoryThisPass : rebuiltHistoryFromInitialPrepare || compartmentPhase.rebuiltHistoryThisPass;
187499
188256
  const tPostProcess = performance.now();
187500
- await runPostTransformPhase({
188257
+ const postTransformResult = await runPostTransformPhase({
187501
188258
  sessionId,
187502
188259
  db,
187503
188260
  messages,
@@ -187551,6 +188308,19 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187551
188308
  hardSignals: m0HardSignals
187552
188309
  }
187553
188310
  });
188311
+ if (postTransformResult.bustedThisPass) {
188312
+ recordPendingTransformDecision(sessionId, {
188313
+ tsMs: Date.now(),
188314
+ decision: schedulerDecision,
188315
+ materialized: postTransformResult.materialized,
188316
+ materializeReason: normalizeMaterializeReason("opencode", postTransformResult.materializeReason, postTransformResult.materialized),
188317
+ emergency: postTransformResult.emergency,
188318
+ droppedTokens: postTransformResult.droppedTokens,
188319
+ droppedCount: postTransformResult.droppedCount,
188320
+ inputTokens: contextUsage.inputTokens,
188321
+ bustedThisPass: true
188322
+ });
188323
+ }
187554
188324
  logTransformTiming(sessionId, "postTransformPhase", tPostProcess);
187555
188325
  const msgTokens = getMessageTokensCache(sessionId);
187556
188326
  let storedByMessage;
@@ -187701,6 +188471,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187701
188471
  const executeThresholdTokens = Math.round((resolvedContextLimit ?? 0) * resolvedExecuteThresholdPct / 100);
187702
188472
  const usableTokens = Math.max(0, executeThresholdTokens - contextUsage.inputTokens + liveTailTokens);
187703
188473
  resetLastNudgeCycleIfTailShrank(db, sessionId, tailToolTokens);
188474
+ const oldestReclaimableToolTags = getOldestActiveUnprotectedToolTags(db, sessionId, deps.protectedTags);
187704
188475
  deps.channel1StateBySession.set(sessionId, {
187705
188476
  tailToolTokens,
187706
188477
  historyBudgetTokens: historyBudgetTokens ?? 0,
@@ -187709,9 +188480,10 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187709
188480
  lastInputTokens: contextUsage.inputTokens,
187710
188481
  turnToolTokens: 0,
187711
188482
  usableTokens,
187712
- reducedSinceRefresh: false
188483
+ reducedSinceRefresh: false,
188484
+ oldestReclaimableToolTags
187713
188485
  });
187714
- const channel2MetricsKnown = fullFeatureMode && resolvedContextLimit !== undefined && resolvedContextLimit > 0 && resolvedExecuteThresholdPct > 0;
188486
+ const channel2MetricsKnown = resolvedContextLimit !== undefined && resolvedContextLimit > 0 && resolvedExecuteThresholdPct > 0;
187715
188487
  if (channel2MetricsKnown) {
187716
188488
  const channel2ShouldTrigger = shouldTriggerChannel2({
187717
188489
  reclaimableTokens: tailToolTokens,
@@ -187735,6 +188507,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187735
188507
  }
187736
188508
  const elapsed = (performance.now() - startTime).toFixed(1);
187737
188509
  sessionLog(sessionId, `transform completed in ${elapsed}ms (${messages.length} messages, ${targets.size} targets, watermark: ${watermark})`);
188510
+ deps.maybeAutoEmbedSession?.(sessionId);
187738
188511
  };
187739
188512
  }
187740
188513
  function resolveHistoryBudgetTokens(historyBudgetPercentage, contextUsage, executeThresholdPercentage, modelKey, executeThresholdTokens, resolvedContextLimit) {
@@ -187780,18 +188553,14 @@ function evictExpiredUsageEntries(contextUsageMap) {
187780
188553
  }
187781
188554
  async function deliverChannel2IfPending(deps, sessionId) {
187782
188555
  try {
187783
- try {
187784
- const meta3 = getOrCreateSessionMeta(deps.db, sessionId);
187785
- if (meta3.isSubagent)
187786
- return;
187787
- } catch {}
187788
188556
  const baseline = deps.channel1StateBySession?.get(sessionId);
187789
188557
  await maybeDeliverChannel2(sessionId, {
187790
188558
  db: deps.db,
187791
188559
  serverUrl: deps.serverUrl,
187792
188560
  directory: deps.directory ?? ".",
187793
188561
  reclaimableTokens: baseline ? baseline.tailToolTokens + baseline.turnToolTokens : undefined,
187794
- usableTokens: baseline?.usableTokens
188562
+ usableTokens: baseline?.usableTokens,
188563
+ oldestReclaimableToolTags: baseline?.oldestReclaimableToolTags
187795
188564
  });
187796
188565
  } catch (error51) {
187797
188566
  sessionLog(sessionId, "channel2 delivery wrapper failed (ignored):", error51);
@@ -187933,6 +188702,15 @@ function createEventHandler2(deps) {
187933
188702
  info.tokens?.cache?.write
187934
188703
  ];
187935
188704
  const hasUsageTokens = usageTokens.some((value) => typeof value === "number" && value > 0);
188705
+ const terminalAssistantUpdate = info.messageID !== undefined && hasUsageTokens && (typeof info.finish === "string" || typeof info.completedAt === "number");
188706
+ if (terminalAssistantUpdate && info.messageID) {
188707
+ scheduleOpenCodeTransformDecisionWrite({
188708
+ db: deps.db,
188709
+ sessionId: info.sessionID,
188710
+ messageId: info.messageID,
188711
+ inputTokens: (info.tokens?.input ?? 0) + (info.tokens?.cache?.read ?? 0) + (info.tokens?.cache?.write ?? 0)
188712
+ });
188713
+ }
187936
188714
  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}`);
187937
188715
  const hasKnownUsage = hasUsageTokens || deps.contextUsageMap.has(info.sessionID);
187938
188716
  if (!hasKnownUsage) {
@@ -188090,6 +188868,7 @@ function createEventHandler2(deps) {
188090
188868
  deps.onSessionDeleted?.(sessionId);
188091
188869
  deps.contextUsageMap.delete(sessionId);
188092
188870
  deps.tagger.cleanup(sessionId);
188871
+ clearTransformDecisionSession(sessionId);
188093
188872
  clearMessageTokensCache(sessionId);
188094
188873
  invalidateTrueRawTokenCache({ sessionId, reason: "session.deleted" });
188095
188874
  return;
@@ -188097,6 +188876,46 @@ function createEventHandler2(deps) {
188097
188876
  };
188098
188877
  }
188099
188878
 
188879
+ // src/hooks/magic-context/format-embed-status.ts
188880
+ function formatEmbedStatusText(coverage, drain) {
188881
+ if (!coverage.enabled) {
188882
+ return "Embedding is off (no provider configured).";
188883
+ }
188884
+ const lines = [];
188885
+ lines.push(`Embedding — model: ${coverage.model} (${coverage.provider})`);
188886
+ lines.push(`This session: ${coverage.session.embedded} / ${coverage.session.total} compartments embedded`);
188887
+ lines.push(`Project memories: ${coverage.memories.embedded} / ${coverage.memories.total} embedded`);
188888
+ if (coverage.commits.gitEnabled) {
188889
+ lines.push(`Git commits: ${coverage.commits.embedded} / ${coverage.commits.total}`);
188890
+ } else {
188891
+ lines.push("Git commits: 0 / 0 (git indexing off)");
188892
+ }
188893
+ let drainLine = "Drain: idle";
188894
+ switch (drain.status) {
188895
+ case "running": {
188896
+ const e = drain.embedded ?? coverage.session.embedded;
188897
+ const t = drain.total ?? coverage.session.total;
188898
+ const failedSuffix = drain.failed && drain.failed > 0 ? ` (${drain.failed} failed)` : "";
188899
+ drainLine = `Drain: running ${e}/${t}${failedSuffix}`;
188900
+ break;
188901
+ }
188902
+ case "paused": {
188903
+ const e = drain.embedded ?? coverage.session.embedded;
188904
+ const t = drain.total ?? coverage.session.total;
188905
+ drainLine = `Drain: paused ${e}/${t}`;
188906
+ break;
188907
+ }
188908
+ case "stopped":
188909
+ drainLine = "Drain: stopped (provider down)";
188910
+ break;
188911
+ default:
188912
+ drainLine = "Drain: idle";
188913
+ }
188914
+ lines.push(drainLine);
188915
+ return lines.join(`
188916
+ `);
188917
+ }
188918
+
188100
188919
  // src/hooks/magic-context/hook.ts
188101
188920
  await __promiseAll([
188102
188921
  init_inject_compartments(),
@@ -188338,7 +189157,7 @@ function maybeInjectChannel1Nudge(args, sessionId, tool, output) {
188338
189157
  setLastNudgeLevel(args.db, sessionId, decision.nextLastNudgeLevel);
188339
189158
  if (!decision.fire)
188340
189159
  return;
188341
- out.output += buildChannel1Reminder(decision.level, decision.undroppedTokens);
189160
+ out.output += buildChannel1Reminder(decision.level, decision.undroppedTokens, state.oldestReclaimableToolTags);
188342
189161
  sessionLog(sessionId, `channel1 nudge fired: level=${decision.level} undropped~${Math.round(decision.undroppedTokens / 1000)}k tool=${tool}`);
188343
189162
  }
188344
189163
  function createToolExecuteAfterHook(args) {
@@ -188410,9 +189229,7 @@ Context is managed for you entirely automatically — there's nothing to prune a
188410
189229
  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).`;
188411
189230
  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.`;
188412
189231
  var BASE_INTRO = (protectedTags) => `Messages and tool outputs are tagged with §N§ identifiers (e.g., §1§, §42§).
188413
- Use \`ctx_reduce\` to manage context size. It supports one operation:
188414
- - \`drop\`: Remove entirely (best for tool outputs you already acted on).
188415
- Syntax: "3-5", "1,2,9", or "1-5,8,12-15". Last ${protectedTags} tags are protected.
189232
+ 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".
188416
189233
  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.
188417
189234
  ${CTX_NOTE_GUIDANCE}
188418
189235
  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.
@@ -188431,7 +189248,7 @@ Use \`ctx_expand\` to recover the raw conversation behind a \`<compartment>\` su
188431
189248
  \`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.
188432
189249
  ${TOOL_HISTORY_GUIDANCE}
188433
189250
  NEVER drop large ranges blindly (e.g., "1-50"). Review each tag before deciding.
188434
- NEVER drop user messagesthey are short and will be summarized by compartmentalization automatically. Dropping them loses context the historian needs.
189251
+ 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\`.
188435
189252
  NEVER drop assistant text messages unless they are exceptionally large. Your conversation messages are lightweight; only large tool outputs are worth dropping.
188436
189253
  Before your turn finishes, consider using \`ctx_reduce\` to drop large tool outputs you no longer need.`;
188437
189254
  var BASE_INTRO_NO_REDUCE = () => `${CTX_NOTE_GUIDANCE}
@@ -188779,7 +189596,7 @@ function createMagicContextHook(deps) {
188779
189596
  }
188780
189597
  let lastScheduleCheckMs = 0;
188781
189598
  const getHistorianChunkTokens = () => deriveHistorianChunkTokens(resolveHistorianContextLimit(deps.config.historian?.model));
188782
- const historianFallbackModels = resolveFallbackChain(HISTORIAN_AGENT, deps.config.historian?.fallback_models);
189599
+ const historianFallbackModels = resolveFallbackChain(deps.config.historian?.fallback_models);
188783
189600
  const historyRefreshSessions = deps.liveSessionState?.historyRefreshSessions ?? new Set;
188784
189601
  const deferredHistoryRefreshSessions = deps.liveSessionState?.deferredHistoryRefreshSessions ?? new Set;
188785
189602
  try {
@@ -188852,29 +189669,55 @@ function createMagicContextHook(deps) {
188852
189669
  ensureProjectRegistered: ensureProjectRegisteredFromOpenCodeDirectory,
188853
189670
  getNotificationParams: (sid) => getLiveNotificationParams(sid, liveModelBySession, variantBySession, agentBySession)
188854
189671
  });
188855
- const executeEmbedHistory = async (sessionId) => {
189672
+ const executeEmbedHistory = async (sessionId, options) => {
188856
189673
  if (deps.config.memory?.enabled === false) {
188857
189674
  return "Memory is disabled for this project, so there is no semantic embedding to backfill.";
188858
189675
  }
188859
189676
  const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
189677
+ const active = embedRunStateBySession.get(sessionId);
189678
+ if (active && !active.signal.aborted && !options?.signal) {
189679
+ return "Embedding is already running for this session.";
189680
+ }
188860
189681
  await ensureProjectRegisteredFromOpenCodeDirectory(directory, db);
188861
189682
  const sessionProjectIdentity = resolveProjectIdentity(directory);
188862
- setRecompStarting({ recompProgressBySession }, sessionId, "Embedding history…", "embed");
188863
- const outcome = await embedSessionCompartmentChunks(db, sessionProjectIdentity, sessionId, {
188864
- onProgress: ({ embedded, total }) => {
188865
- const cur = recompProgressBySession.get(sessionId);
188866
- if (!cur || cur.phase !== "recomp")
188867
- return;
188868
- recompProgressBySession.set(sessionId, {
188869
- ...cur,
188870
- processedMessages: embedded,
188871
- totalMessages: total,
188872
- updatedAt: Date.now()
188873
- });
189683
+ embedPauseBySession.delete(sessionId);
189684
+ const prior = embedRunStateBySession.get(sessionId);
189685
+ if (prior)
189686
+ prior.abort();
189687
+ const controller = new AbortController;
189688
+ embedRunStateBySession.set(sessionId, controller);
189689
+ const signal = options?.signal ?? controller.signal;
189690
+ if (!options?.silent) {
189691
+ setRecompStarting({ recompProgressBySession }, sessionId, "Embedding history…", "embed");
189692
+ }
189693
+ let runFailed = 0;
189694
+ let outcome;
189695
+ try {
189696
+ outcome = await embedSessionCompartmentChunks(db, sessionProjectIdentity, sessionId, {
189697
+ signal,
189698
+ onProgress: ({ embedded, total }) => {
189699
+ const cur = recompProgressBySession.get(sessionId);
189700
+ if (!cur || cur.phase !== "recomp")
189701
+ return;
189702
+ recompProgressBySession.set(sessionId, {
189703
+ ...cur,
189704
+ processedMessages: embedded,
189705
+ totalMessages: total,
189706
+ updatedAt: Date.now()
189707
+ });
189708
+ }
189709
+ });
189710
+ } finally {
189711
+ if (embedRunStateBySession.get(sessionId) === controller) {
189712
+ embedRunStateBySession.delete(sessionId);
188874
189713
  }
188875
- });
189714
+ }
189715
+ if ("failed" in outcome)
189716
+ runFailed = outcome.failed;
188876
189717
  const terminal = (phase, message) => {
188877
- setRecompTerminal({ recompProgressBySession }, sessionId, phase, message);
189718
+ if (!options?.silent) {
189719
+ setRecompTerminal({ recompProgressBySession }, sessionId, phase, message);
189720
+ }
188878
189721
  return message;
188879
189722
  };
188880
189723
  switch (outcome.status) {
@@ -188883,15 +189726,78 @@ function createMagicContextHook(deps) {
188883
189726
  case "disabled":
188884
189727
  return terminal("skipped", "No embedding provider is configured, so there is nothing to embed.");
188885
189728
  case "busy":
188886
- return terminal("skipped", `Embedding is already running for this project — ${outcome.total} compartment${outcome.total === 1 ? "" : "s"} still pending. Try again shortly.`);
188887
- case "aborted":
188888
- return terminal("done", `Embedded ${outcome.embedded} of ${outcome.total} compartments before stopping.`);
189729
+ return terminal("skipped", "Embedding is already running for this project. Try again shortly.");
189730
+ case "aborted": {
189731
+ const cov = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
189732
+ const msg = `Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`;
189733
+ return terminal("skipped", msg);
189734
+ }
188889
189735
  case "stalled":
188890
- 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.`);
189736
+ 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.`);
188891
189737
  default:
188892
- return terminal("done", `Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search.`);
189738
+ return terminal("done", `Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search${runFailed > 0 ? ` (${runFailed} failed)` : ""}.`);
188893
189739
  }
188894
189740
  };
189741
+ const pauseEmbedDrain = (sessionId) => {
189742
+ embedPauseBySession.add(sessionId);
189743
+ const ctrl = embedRunStateBySession.get(sessionId);
189744
+ if (ctrl)
189745
+ ctrl.abort();
189746
+ const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
189747
+ const sessionProjectIdentity = resolveProjectIdentity(directory);
189748
+ const cov = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
189749
+ return `Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`;
189750
+ };
189751
+ const getEmbedStatusText = (sessionId) => {
189752
+ const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
189753
+ const sessionProjectIdentity = resolveProjectIdentity(directory);
189754
+ const coverage = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
189755
+ const progress = recompProgressBySession.get(sessionId);
189756
+ const drainUi = getEmbedDrainUiStatus(sessionId, progress);
189757
+ return formatEmbedStatusText(coverage, {
189758
+ status: drainUi.status,
189759
+ embedded: progress?.processedMessages,
189760
+ total: progress?.totalMessages
189761
+ });
189762
+ };
189763
+ const maybeAutoEmbedSession = (sessionId) => {
189764
+ if (autoEmbedAttemptedBySession.has(sessionId))
189765
+ return;
189766
+ if (embedPauseBySession.has(sessionId))
189767
+ return;
189768
+ if (deps.config.memory?.enabled === false)
189769
+ return;
189770
+ autoEmbedAttemptedBySession.add(sessionId);
189771
+ const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
189772
+ (async () => {
189773
+ try {
189774
+ await new Promise((resolve6) => setTimeout(resolve6, 0));
189775
+ await ensureProjectRegisteredFromOpenCodeDirectory(directory, db);
189776
+ const sessionProjectIdentity = resolveProjectIdentity(directory);
189777
+ const coverage = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
189778
+ if (!coverage.enabled)
189779
+ return;
189780
+ const remaining = coverage.session.total - coverage.session.embedded;
189781
+ if (remaining <= 0)
189782
+ return;
189783
+ const notifyParams = getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession);
189784
+ if (!isTuiConnected(sessionId)) {
189785
+ const startMsg = `Embedding ${remaining} compartment${remaining === 1 ? "" : "s"} of history in the background…`;
189786
+ await sendIgnoredMessage(deps.client, sessionId, startMsg, {
189787
+ ...notifyParams
189788
+ });
189789
+ }
189790
+ const summary = await executeEmbedHistory(sessionId);
189791
+ if (!isTuiConnected(sessionId)) {
189792
+ await sendIgnoredMessage(deps.client, sessionId, summary, {
189793
+ ...notifyParams
189794
+ });
189795
+ }
189796
+ } catch (error51) {
189797
+ log("[magic-context] auto-embed drain failed:", error51);
189798
+ }
189799
+ })();
189800
+ };
188895
189801
  const sidekickRunnable = isSidekickRunnable(deps.config);
188896
189802
  const sidekickConfig = sidekickRunnable ? deps.config.sidekick : undefined;
188897
189803
  const transform2 = createTransform({
@@ -188953,7 +189859,8 @@ function createMagicContextHook(deps) {
188953
189859
  cavemanTextCompression: ctxReduceEnabled === false && deps.config.caveman_text_compression?.enabled === true ? {
188954
189860
  enabled: true,
188955
189861
  minChars: deps.config.caveman_text_compression.min_chars ?? 500
188956
- } : undefined
189862
+ } : undefined,
189863
+ maybeAutoEmbedSession
188957
189864
  });
188958
189865
  const eventHandler = createEventHandler2({
188959
189866
  contextUsageMap,
@@ -188982,6 +189889,7 @@ function createMagicContextHook(deps) {
188982
189889
  recompProgressBySession.delete(sessionId);
188983
189890
  internalChildSessions.delete(sessionId);
188984
189891
  channel1StateBySession.delete(sessionId);
189892
+ clearEmbedSessionState(sessionId);
188985
189893
  }
188986
189894
  });
188987
189895
  const runDreamQueueInBackground = () => {
@@ -189016,7 +189924,7 @@ function createMagicContextHook(deps) {
189016
189924
  token_budget: dreaming.pin_key_files.token_budget,
189017
189925
  min_reads: dreaming.pin_key_files.min_reads
189018
189926
  } : undefined,
189019
- fallbackModels: resolveFallbackChain(DREAMER_AGENT, dreaming.fallback_models),
189927
+ fallbackModels: resolveFallbackChain(dreaming.fallback_models),
189020
189928
  projectIdentity: projectPath
189021
189929
  }).catch((error51) => {
189022
189930
  log("[dreamer] scheduled queue processing failed:", error51);
@@ -189047,6 +189955,8 @@ function createMagicContextHook(deps) {
189047
189955
  executeRecomp: historianRunnable ? async (sessionId, options) => runManagedRecomp(buildManagedRecompCtx(sessionId), sessionId, options) : undefined,
189048
189956
  runUpgrade: historianRunnable ? async (sessionId) => runManagedUpgrade(buildManagedRecompCtx(sessionId), sessionId) : undefined,
189049
189957
  executeEmbedHistory,
189958
+ pauseEmbedDrain,
189959
+ getEmbedStatusText,
189050
189960
  sendNotification: async (sessionId, text, params) => {
189051
189961
  await sendIgnoredMessage(deps.client, sessionId, text, {
189052
189962
  ...getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
@@ -189073,7 +189983,7 @@ function createMagicContextHook(deps) {
189073
189983
  token_budget: dreamerConfig.pin_key_files.token_budget,
189074
189984
  min_reads: dreamerConfig.pin_key_files.min_reads
189075
189985
  } : undefined,
189076
- fallbackModels: resolveFallbackChain(DREAMER_AGENT, dreamerConfig.fallback_models)
189986
+ fallbackModels: resolveFallbackChain(dreamerConfig.fallback_models)
189077
189987
  } : undefined
189078
189988
  });
189079
189989
  const systemPromptHash = createSystemPromptHashHandler({
@@ -189259,6 +190169,7 @@ function truncateError(name2, code, message, maxLen = 240) {
189259
190169
 
189260
190170
  // src/plugin/rpc-handlers.ts
189261
190171
  init_project_identity();
190172
+ init_project_embedding_registry();
189262
190173
  init_tool_definition_tokens();
189263
190174
  await init_storage();
189264
190175
 
@@ -189979,6 +190890,26 @@ function buildStatusDetail(db, sessionId, directory, modelKey, config2, liveSess
189979
190890
  }
189980
190891
  return detail;
189981
190892
  }
190893
+ function buildEmbedDetail(db, sessionId, dir, liveSessionState) {
190894
+ const projectIdentity = resolveProjectIdentity(dir);
190895
+ const coverage = getEmbeddingCoverageStatus(db, projectIdentity, sessionId);
190896
+ const progress = liveSessionState.recompProgressBySession.get(sessionId);
190897
+ const drainUi = getEmbedDrainUiStatus(sessionId, progress);
190898
+ const statusText = formatEmbedStatusText(coverage, {
190899
+ status: drainUi.status,
190900
+ embedded: progress?.processedMessages,
190901
+ total: progress?.totalMessages
190902
+ });
190903
+ return {
190904
+ enabled: coverage.enabled,
190905
+ model: coverage.model,
190906
+ provider: coverage.provider,
190907
+ session: coverage.session,
190908
+ memories: coverage.memories,
190909
+ commits: coverage.commits,
190910
+ statusText
190911
+ };
190912
+ }
189982
190913
  function registerRpcHandlers(rpcServer, args) {
189983
190914
  const { directory, config: config2, liveSessionState } = args;
189984
190915
  const rawConfig = config2;
@@ -190001,6 +190932,19 @@ function registerRpcHandlers(rpcServer, args) {
190001
190932
  return { error: "unavailable" };
190002
190933
  return buildStatusDetail(db, sessionId, dir, modelKey, rawConfig, liveSessionState, injectionBudgetTokens);
190003
190934
  });
190935
+ rpcServer.handle("embed-detail", async (params) => {
190936
+ const sessionId = String(params.sessionId ?? "");
190937
+ const dir = String(params.directory ?? directory);
190938
+ const db = getDb();
190939
+ if (!db || !sessionId)
190940
+ return { error: "unavailable" };
190941
+ try {
190942
+ return buildEmbedDetail(db, sessionId, dir, liveSessionState);
190943
+ } catch (err) {
190944
+ log("[rpc] embed-detail error:", err);
190945
+ return { error: "unavailable" };
190946
+ }
190947
+ });
190004
190948
  rpcServer.handle("compartment-count", async (params) => {
190005
190949
  const sessionId = String(params.sessionId ?? "");
190006
190950
  const db = getDb();
@@ -190015,8 +190959,7 @@ function registerRpcHandlers(rpcServer, args) {
190015
190959
  });
190016
190960
  const buildManagedCtx = async (db) => {
190017
190961
  const { deriveHistorianChunkTokens: deriveHistorianChunkTokens2, resolveHistorianContextLimit: resolveHistorianContextLimit2 } = await Promise.resolve().then(() => (init_derive_budgets(), exports_derive_budgets));
190018
- const { resolveFallbackChain: resolveFallbackChain2 } = await Promise.resolve().then(() => (init_resolve_fallbacks(), exports_resolve_fallbacks));
190019
- const { HISTORIAN_AGENT: HISTORIAN_AGENT2 } = await Promise.resolve().then(() => exports_historian);
190962
+ const { resolveFallbackChain: resolveFallbackChain2 } = await Promise.resolve().then(() => exports_resolve_fallbacks);
190020
190963
  const DEFAULT_HISTORIAN_TIMEOUT_MS2 = 600000;
190021
190964
  return {
190022
190965
  client: args.client,
@@ -190027,7 +190970,7 @@ function registerRpcHandlers(rpcServer, args) {
190027
190970
  historianTimeoutMs: config2.historian_timeout_ms ?? DEFAULT_HISTORIAN_TIMEOUT_MS2,
190028
190971
  memoryEnabled: config2.memory?.enabled ?? true,
190029
190972
  autoPromote: config2.memory?.auto_promote ?? true,
190030
- fallbackModels: resolveFallbackChain2(HISTORIAN_AGENT2, config2.historian?.fallback_models),
190973
+ fallbackModels: resolveFallbackChain2(config2.historian?.fallback_models),
190031
190974
  runMigration: config2.memory?.enabled !== false && !!config2.historian?.model,
190032
190975
  userMemoriesEnabled: config2.dreamer?.user_memories?.enabled === true,
190033
190976
  historianTwoPass: config2.historian?.two_pass === true,
@@ -190123,27 +191066,225 @@ Older parts of this session are summarized into <compartment> blocks inside <ses
190123
191066
 
190124
191067
  ctx_expand(start=120, end=245) ← the compartment's own start/end attributes
190125
191068
 
190126
- 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.`;
191069
+ 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.
191070
+
191071
+ Two recovery modes for finer detail:
191072
+ - 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.
191073
+ - 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.`;
190127
191074
  var CTX_EXPAND_TOKEN_BUDGET = 15000;
190128
191075
 
191076
+ // src/tools/ctx-expand/render.ts
191077
+ init_read_session_formatting();
191078
+ await init_read_session_chunk();
191079
+ function isRecord3(value) {
191080
+ return value !== null && typeof value === "object" && !Array.isArray(value);
191081
+ }
191082
+ function roleLabel(role) {
191083
+ if (role === "assistant")
191084
+ return "A (assistant)";
191085
+ if (role === "user")
191086
+ return "U (user)";
191087
+ return role;
191088
+ }
191089
+ function truncate2(value, max) {
191090
+ const t = value.trim();
191091
+ return t.length <= max ? t : `${t.slice(0, max)}…`;
191092
+ }
191093
+ function keyArg(input) {
191094
+ if (!input)
191095
+ return "";
191096
+ for (const k of ["filePath", "path", "pattern", "query", "symbol", "module", "action"]) {
191097
+ const v = input[k];
191098
+ if (typeof v === "string" && v.length > 0)
191099
+ return truncate2(v, 60);
191100
+ }
191101
+ if (typeof input.description === "string")
191102
+ return truncate2(input.description, 60);
191103
+ return "";
191104
+ }
191105
+ function asToolPart(part) {
191106
+ const type = typeof part.type === "string" ? part.type : "";
191107
+ if (type === "tool") {
191108
+ const state = isRecord3(part.state) ? part.state : null;
191109
+ const output = state && typeof state.output === "string" ? state.output : state && state.output != null ? JSON.stringify(state.output) : null;
191110
+ const metadata = state && isRecord3(state.metadata) ? state.metadata : null;
191111
+ const title = state && typeof state.title === "string" && state.title || metadata && typeof metadata.title === "string" && metadata.title || null;
191112
+ return {
191113
+ name: typeof part.tool === "string" ? part.tool : "tool",
191114
+ callId: typeof part.callID === "string" ? part.callID : "",
191115
+ title,
191116
+ input: state && isRecord3(state.input) ? state.input : null,
191117
+ output
191118
+ };
191119
+ }
191120
+ if (type === "tool_use") {
191121
+ return {
191122
+ name: typeof part.name === "string" ? part.name : "tool",
191123
+ callId: typeof part.id === "string" ? part.id : "",
191124
+ title: null,
191125
+ input: isRecord3(part.input) ? part.input : null,
191126
+ output: null
191127
+ };
191128
+ }
191129
+ if (type === "tool_result") {
191130
+ const content = part.content;
191131
+ const output = typeof content === "string" ? content : content != null ? JSON.stringify(content) : null;
191132
+ return {
191133
+ name: "tool_result",
191134
+ callId: typeof part.tool_use_id === "string" ? part.tool_use_id : "",
191135
+ title: null,
191136
+ input: null,
191137
+ output
191138
+ };
191139
+ }
191140
+ return null;
191141
+ }
191142
+ function textOf(part) {
191143
+ if (part.type === "text" && typeof part.text === "string")
191144
+ return part.text;
191145
+ return null;
191146
+ }
191147
+ function reasoningOf(part) {
191148
+ if ((part.type === "reasoning" || part.type === "thinking") && typeof part.text === "string") {
191149
+ return part.text;
191150
+ }
191151
+ return null;
191152
+ }
191153
+ function renderPartPreview(part) {
191154
+ if (!isRecord3(part))
191155
+ return null;
191156
+ const text = textOf(part);
191157
+ if (text !== null) {
191158
+ const t = truncate2(text, 200);
191159
+ return t.length > 0 ? ` • ${t}` : null;
191160
+ }
191161
+ const tool = asToolPart(part);
191162
+ if (tool) {
191163
+ const arg = keyArg(tool.input);
191164
+ const head = arg ? `${tool.name}(${arg})` : tool.name;
191165
+ return tool.output !== null ? ` • tool ${head} → output ~${estimateTokens(tool.output)} tok` : ` • tool ${head}`;
191166
+ }
191167
+ const reasoning = reasoningOf(part);
191168
+ if (reasoning !== null)
191169
+ return ` • [reasoning] ${truncate2(reasoning, 120)}`;
191170
+ const type = typeof part.type === "string" ? part.type : "part";
191171
+ if (type === "file")
191172
+ return " • [file]";
191173
+ if (type === "step-start" || type === "step-finish")
191174
+ return null;
191175
+ return ` • [${type}]`;
191176
+ }
191177
+ function renderPartFull(part) {
191178
+ if (!isRecord3(part))
191179
+ return null;
191180
+ const text = textOf(part);
191181
+ if (text !== null) {
191182
+ return text.trim().length > 0 ? ` [text]
191183
+ ${text}` : null;
191184
+ }
191185
+ const tool = asToolPart(part);
191186
+ if (tool) {
191187
+ const lines = [];
191188
+ const idSuffix = tool.callId ? ` #${tool.callId}` : "";
191189
+ lines.push(` [tool: ${tool.name}${idSuffix}]`);
191190
+ if (tool.title && tool.title.trim().length > 0) {
191191
+ lines.push(` description: ${tool.title.trim()}`);
191192
+ }
191193
+ if (tool.input)
191194
+ lines.push(` input: ${JSON.stringify(tool.input)}`);
191195
+ if (tool.output !== null)
191196
+ lines.push(` output:
191197
+ ${tool.output}`);
191198
+ return lines.join(`
191199
+ `);
191200
+ }
191201
+ const type = typeof part.type === "string" ? part.type : "part";
191202
+ if (type === "file") {
191203
+ const name2 = typeof part.filename === "string" && part.filename || typeof part.url === "string" && part.url || "";
191204
+ return ` [file]${name2 ? ` ${name2}` : ""}`;
191205
+ }
191206
+ return null;
191207
+ }
191208
+ function renderMessageByOrdinal(sessionId, ordinal) {
191209
+ const msg = readRawSessionMessages(sessionId).find((m) => m.ordinal === ordinal);
191210
+ if (!msg) {
191211
+ 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.`;
191212
+ }
191213
+ const rendered = msg.parts.map(renderPartFull).filter((l) => l !== null);
191214
+ const lines = [`[${msg.ordinal}] ${roleLabel(msg.role)} — full recovery:`, ""];
191215
+ if (rendered.length === 0) {
191216
+ lines.push(" (no recoverable content — message had only structural/reasoning parts)");
191217
+ } else {
191218
+ lines.push(...rendered);
191219
+ }
191220
+ return lines.join(`
191221
+ `);
191222
+ }
191223
+ function renderVerboseRange(sessionId, start, end, tokenBudget) {
191224
+ const messages = readRawSessionMessages(sessionId).filter((m) => m.ordinal >= start && m.ordinal <= end);
191225
+ const out = [];
191226
+ let usedTokens = 0;
191227
+ let lastOrdinal = start - 1;
191228
+ let truncated = false;
191229
+ for (const msg of messages) {
191230
+ const header = `[${msg.ordinal}] ${roleLabel(msg.role)}`;
191231
+ const partLines = msg.parts.map(renderPartPreview).filter((l) => l !== null);
191232
+ const block = partLines.length > 0 ? `${header}
191233
+ ${partLines.join(`
191234
+ `)}` : header;
191235
+ const blockTokens = estimateTokens(block);
191236
+ if (usedTokens + blockTokens > tokenBudget && out.length > 0) {
191237
+ truncated = true;
191238
+ break;
191239
+ }
191240
+ out.push(block);
191241
+ usedTokens += blockTokens;
191242
+ lastOrdinal = msg.ordinal;
191243
+ }
191244
+ return { text: out.join(`
191245
+
191246
+ `), lastOrdinal, truncated };
191247
+ }
191248
+
190129
191249
  // src/tools/ctx-expand/tools.ts
190130
191250
  function createCtxExpandTool(deps) {
190131
191251
  return tool({
190132
191252
  description: CTX_EXPAND_DESCRIPTION,
190133
191253
  args: {
190134
- 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`),
190135
- end: tool.schema.number().describe(`Last message ordinal to expand (inclusive) — a compartment's end="M" attribute`)
191254
+ 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`),
191255
+ end: tool.schema.number().optional().describe(`Last message ordinal to expand (inclusive) — a compartment's end="M" attribute`),
191256
+ 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."),
191257
+ 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.")
190136
191258
  },
190137
191259
  async execute(args, toolContext) {
190138
191260
  const sessionId = toolContext.sessionID;
191261
+ if (typeof args.message === "number" && args.message >= 1) {
191262
+ return renderMessageByOrdinal(sessionId, args.message);
191263
+ }
190139
191264
  if (!args.start || !args.end || args.start < 1 || args.end < args.start) {
190140
- return "Error: start and end must be positive integers with start <= end.";
191265
+ return "Error: provide either message=<ordinal>, or start and end (positive integers, start <= end).";
190141
191266
  }
190142
191267
  const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
190143
191268
  if (lastCompartmentEnd >= 0 && args.start > lastCompartmentEnd) {
190144
191269
  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.`;
190145
191270
  }
190146
191271
  const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(args.end, lastCompartmentEnd) : args.end;
191272
+ if (args.verbose === true) {
191273
+ const v = renderVerboseRange(sessionId, args.start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET);
191274
+ if (!v.text) {
191275
+ return `No messages found in range ${args.start}-${effectiveEnd}. The range may be outside this session's history.`;
191276
+ }
191277
+ const out = [
191278
+ `Messages ${args.start}-${v.lastOrdinal} (verbose). Recover any one in full with ctx_expand(message=<ordinal>):`,
191279
+ "",
191280
+ v.text
191281
+ ];
191282
+ if (v.truncated) {
191283
+ 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.`);
191284
+ }
191285
+ return out.join(`
191286
+ `);
191287
+ }
190147
191288
  const chunk = readSessionChunk(sessionId, CTX_EXPAND_TOKEN_BUDGET, args.start, effectiveEnd + 1);
190148
191289
  if (!chunk.text || chunk.messageCount === 0) {
190149
191290
  return `No messages found in range ${args.start}-${args.end}. The range may be outside this session's history.`;
@@ -190181,6 +191322,7 @@ Actions:
190181
191322
  Example: ctx_memory(action="write", category="CONSTRAINTS", content="Pi stores sessions as JSONL under ~/.pi/agent/sessions/, not SQLite")`;
190182
191323
  var DEFAULT_SEARCH_LIMIT2 = 10;
190183
191324
  // src/tools/ctx-memory/tools.ts
191325
+ import { tool as tool2 } from "@opencode-ai/plugin";
190184
191326
  init_memory();
190185
191327
  init_embedding();
190186
191328
  init_embedding_cache();
@@ -190188,7 +191330,6 @@ init_normalize_hash();
190188
191330
  init_workspaces();
190189
191331
  init_logger();
190190
191332
  await init_storage();
190191
- import { tool as tool2 } from "@opencode-ai/plugin";
190192
191333
 
190193
191334
  // src/tools/ctx-memory/types.ts
190194
191335
  var CTX_MEMORY_ACTIONS = ["write", "archive", "update", "merge"];
@@ -190837,15 +191978,16 @@ function createCtxNoteTools(deps) {
190837
191978
  };
190838
191979
  }
190839
191980
  // src/tools/ctx-reduce/constants.ts
190840
- var CTX_REDUCE_DESCRIPTION = `Reduce context by dropping tagged content you no longer need.
190841
- Use §N§ identifiers visible in conversation. The \`drop\` param accepts ranges: "3-5", "1,2,9", "1-5,8".
190842
-
190843
- CRITICAL RULES:
190844
- - NEVER blanket-drop large ranges (e.g., "1-50"). Always review what each tag contains first.
190845
- - Only drop tool outputs you have already processed and no longer need.
190846
- - Protected tags are accepted but deferred until they leave the last protected range.
190847
- - Keep recent context only reduce OLD content that is no longer relevant to current work.
190848
- - Dropped content is gone forever.`;
191981
+ 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".
191982
+
191983
+ How it works:
191984
+ - 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.
191985
+ - The newest tags are protected: marking one just queues it until it ages out of the recent window, so marking recent output is harmless.
191986
+ - 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?".
191987
+
191988
+ 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.
191989
+ Keep: user messages, unresolved errors, raw evidence you haven't extracted yet, and outputs whose exact wording may matter later.
191990
+ Never blanket-mark large ranges (e.g. "1-50") — review what each tag holds first.`;
190849
191991
  // src/tools/ctx-reduce/tools.ts
190850
191992
  import { tool as tool4 } from "@opencode-ai/plugin";
190851
191993
 
@@ -191112,7 +192254,7 @@ function createCtxSearchTool(deps) {
191112
192254
  memoryEnabled,
191113
192255
  embeddingEnabled,
191114
192256
  embedQuery: async (text, signal) => {
191115
- const result = await embedTextForProject(projectPath, text, signal);
192257
+ const result = await embedTextForProject(projectPath, text, signal, "query");
191116
192258
  return result?.vector ?? null;
191117
192259
  },
191118
192260
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
@@ -191215,7 +192357,6 @@ function createToolRegistry(args) {
191215
192357
  init_conflict_detector();
191216
192358
  init_data_path();
191217
192359
  init_logger();
191218
- init_model_requirements();
191219
192360
  init_models_dev_cache();
191220
192361
 
191221
192362
  // src/shared/rpc-server.ts
@@ -191428,6 +192569,9 @@ class MagicContextRpcServer {
191428
192569
  }
191429
192570
 
191430
192571
  // src/index.ts
192572
+ var HISTORIAN_MAX_STEPS = 40;
192573
+ var SIDEKICK_MAX_STEPS = 40;
192574
+ var DREAMER_MAX_STEPS = 150;
191431
192575
  var plugin = async (ctx) => {
191432
192576
  const pluginConfig = loadPluginConfig(ctx.directory);
191433
192577
  setSqlitePragmaConfig({
@@ -191620,12 +192764,13 @@ var plugin = async (ctx) => {
191620
192764
  await hooks.magicContext?.["experimental.text.complete"]?.(input, output);
191621
192765
  },
191622
192766
  config: async (config2) => {
191623
- const buildHiddenAgentConfig = (agentId, prompt, allowedTools, overrides) => {
192767
+ const buildHiddenAgentConfig = (prompt, allowedTools, maxSteps, overrides) => {
191624
192768
  const { permission: overridePermission, ...restOverrides } = overrides ?? {};
191625
192769
  const basePermission = buildAllowOnlyPermission(allowedTools);
191626
192770
  return {
191627
192771
  prompt,
191628
- ...getAgentFallbackModels(agentId) ? { fallback_models: getAgentFallbackModels(agentId) } : {},
192772
+ steps: maxSteps,
192773
+ maxSteps,
191629
192774
  ...restOverrides,
191630
192775
  permission: {
191631
192776
  ...basePermission,
@@ -191647,6 +192792,7 @@ var plugin = async (ctx) => {
191647
192792
  max_runtime_minutes: _max,
191648
192793
  tasks: _tasks,
191649
192794
  task_timeout_minutes: _tto,
192795
+ thinking_level: _thinkingLevel,
191650
192796
  ...agentOverrides
191651
192797
  } = pluginConfig.dreamer;
191652
192798
  return agentOverrides;
@@ -191655,20 +192801,28 @@ var plugin = async (ctx) => {
191655
192801
  const {
191656
192802
  timeout_ms: _timeoutMs,
191657
192803
  system_prompt: _systemPrompt,
192804
+ thinking_level: _thinkingLevel,
191658
192805
  ...agentOverrides
191659
192806
  } = pluginConfig.sidekick;
191660
192807
  return agentOverrides;
191661
192808
  })() : undefined;
191662
192809
  const historianAgentOverrides = pluginConfig.historian ? (() => {
191663
- const { two_pass: _twoPass, ...agentOverrides } = pluginConfig.historian;
192810
+ const {
192811
+ two_pass: _twoPass,
192812
+ disallowed_tools: _disallowedTools,
192813
+ thinking_level: _thinkingLevel,
192814
+ ...agentOverrides
192815
+ } = pluginConfig.historian;
191664
192816
  return agentOverrides;
191665
192817
  })() : undefined;
192818
+ const historianDisallowed = pluginConfig.historian?.disallowed_tools ?? [];
192819
+ const historianAllowedTools = applyDisallowedTools(HISTORIAN_ALLOWED_TOOLS, historianDisallowed);
191666
192820
  config2.agent = {
191667
192821
  ...config2.agent ?? {},
191668
- [DREAMER_AGENT]: buildHiddenAgentConfig(DREAMER_AGENT, DREAMER_SYSTEM_PROMPT, DREAMER_ALLOWED_TOOLS, dreamerAgentOverrides),
191669
- [HISTORIAN_AGENT]: buildHiddenAgentConfig(HISTORIAN_AGENT, COMPARTMENT_AGENT_SYSTEM_PROMPT, HISTORIAN_ALLOWED_TOOLS, historianAgentOverrides),
191670
- [HISTORIAN_EDITOR_AGENT]: buildHiddenAgentConfig(HISTORIAN_EDITOR_AGENT, HISTORIAN_EDITOR_SYSTEM_PROMPT, HISTORIAN_ALLOWED_TOOLS, historianAgentOverrides),
191671
- [SIDEKICK_AGENT]: buildHiddenAgentConfig(SIDEKICK_AGENT, SIDEKICK_SYSTEM_PROMPT, SIDEKICK_ALLOWED_TOOLS, sidekickAgentOverrides)
192822
+ [DREAMER_AGENT]: buildHiddenAgentConfig(DREAMER_SYSTEM_PROMPT, DREAMER_ALLOWED_TOOLS, DREAMER_MAX_STEPS, dreamerAgentOverrides),
192823
+ [HISTORIAN_AGENT]: buildHiddenAgentConfig(COMPARTMENT_AGENT_SYSTEM_PROMPT, historianAllowedTools, HISTORIAN_MAX_STEPS, historianAgentOverrides),
192824
+ [HISTORIAN_EDITOR_AGENT]: buildHiddenAgentConfig(HISTORIAN_EDITOR_SYSTEM_PROMPT, historianAllowedTools, HISTORIAN_MAX_STEPS, historianAgentOverrides),
192825
+ [SIDEKICK_AGENT]: buildHiddenAgentConfig(SIDEKICK_SYSTEM_PROMPT, SIDEKICK_ALLOWED_TOOLS, SIDEKICK_MAX_STEPS, sidekickAgentOverrides)
191672
192826
  };
191673
192827
  }
191674
192828
  };