@wolfx/opencode-magic-context 0.24.1 → 0.26.0-patch.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) 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 +24 -17
  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-identity.d.ts.map +1 -1
  11. package/dist/features/magic-context/memory/embedding-openai.d.ts +8 -4
  12. package/dist/features/magic-context/memory/embedding-openai.d.ts.map +1 -1
  13. package/dist/features/magic-context/memory/embedding-provider.d.ts +8 -4
  14. package/dist/features/magic-context/memory/embedding-provider.d.ts.map +1 -1
  15. package/dist/features/magic-context/memory/embedding.d.ts.map +1 -1
  16. package/dist/features/magic-context/memory/relocate-memory.d.ts +58 -0
  17. package/dist/features/magic-context/memory/relocate-memory.d.ts.map +1 -0
  18. package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts +6 -0
  19. package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts.map +1 -1
  20. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  21. package/dist/features/magic-context/project-embedding-registry.d.ts +41 -3
  22. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  23. package/dist/features/magic-context/storage-db.d.ts +2 -1
  24. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  25. package/dist/features/magic-context/storage-meta-persisted.d.ts +37 -0
  26. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  27. package/dist/features/magic-context/storage-meta-session.d.ts +1 -0
  28. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  29. package/dist/features/magic-context/storage-meta-shared.d.ts +4 -1
  30. package/dist/features/magic-context/storage-meta-shared.d.ts.map +1 -1
  31. package/dist/features/magic-context/storage-meta.d.ts +2 -2
  32. package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
  33. package/dist/features/magic-context/storage-tags.d.ts +58 -2
  34. package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
  35. package/dist/features/magic-context/storage.d.ts +3 -3
  36. package/dist/features/magic-context/storage.d.ts.map +1 -1
  37. package/dist/features/magic-context/tagger.d.ts +1 -1
  38. package/dist/features/magic-context/tagger.d.ts.map +1 -1
  39. package/dist/features/magic-context/transform-decision-log.d.ts +49 -0
  40. package/dist/features/magic-context/transform-decision-log.d.ts.map +1 -0
  41. package/dist/features/magic-context/types.d.ts +1 -0
  42. package/dist/features/magic-context/types.d.ts.map +1 -1
  43. package/dist/features/magic-context/v22-deferred-backfill.d.ts.map +1 -1
  44. package/dist/hooks/magic-context/apply-operations.d.ts +3 -25
  45. package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
  46. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  47. package/dist/hooks/magic-context/caveman-cleanup.d.ts +1 -0
  48. package/dist/hooks/magic-context/caveman-cleanup.d.ts.map +1 -1
  49. package/dist/hooks/magic-context/channel2-delivery.d.ts +2 -0
  50. package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
  51. package/dist/hooks/magic-context/command-handler.d.ts +7 -5
  52. package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
  53. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  54. package/dist/hooks/magic-context/compartment-trigger.d.ts +1 -1
  55. package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
  56. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts +7 -2
  57. package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts.map +1 -1
  58. package/dist/hooks/magic-context/derive-budgets.d.ts +5 -9
  59. package/dist/hooks/magic-context/derive-budgets.d.ts.map +1 -1
  60. package/dist/hooks/magic-context/embed-session-state.d.ts +14 -0
  61. package/dist/hooks/magic-context/embed-session-state.d.ts.map +1 -0
  62. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  63. package/dist/hooks/magic-context/event-payloads.d.ts +1 -0
  64. package/dist/hooks/magic-context/event-payloads.d.ts.map +1 -1
  65. package/dist/hooks/magic-context/format-embed-status.d.ts +9 -0
  66. package/dist/hooks/magic-context/format-embed-status.d.ts.map +1 -0
  67. package/dist/hooks/magic-context/heuristic-cleanup.d.ts +2 -0
  68. package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
  69. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  70. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  71. package/dist/hooks/magic-context/protected-tail-boundary.d.ts +10 -0
  72. package/dist/hooks/magic-context/protected-tail-boundary.d.ts.map +1 -1
  73. package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
  74. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts +1 -1
  75. package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
  76. package/dist/hooks/magic-context/strip-content.d.ts +0 -1
  77. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  78. package/dist/hooks/magic-context/tag-content-primitives.d.ts +2 -0
  79. package/dist/hooks/magic-context/tag-content-primitives.d.ts.map +1 -1
  80. package/dist/hooks/magic-context/tag-id-fallback.d.ts.map +1 -1
  81. package/dist/hooks/magic-context/tag-messages.d.ts +10 -0
  82. package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
  83. package/dist/hooks/magic-context/tool-drop-target.d.ts +1 -1
  84. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  85. package/dist/hooks/magic-context/tool-reclaim.d.ts +12 -0
  86. package/dist/hooks/magic-context/tool-reclaim.d.ts.map +1 -0
  87. package/dist/hooks/magic-context/transform-compartment-phase.d.ts +32 -1
  88. package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
  89. package/dist/hooks/magic-context/transform-operations.d.ts +1 -1
  90. package/dist/hooks/magic-context/transform-operations.d.ts.map +1 -1
  91. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +6 -0
  92. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  93. package/dist/hooks/magic-context/transform.d.ts +2 -0
  94. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +1790 -1025
  97. package/dist/plugin/conflict-warning-hook.d.ts.map +1 -1
  98. package/dist/plugin/dream-timer.d.ts.map +1 -1
  99. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  100. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  101. package/dist/shared/announcement.d.ts +1 -1
  102. package/dist/shared/index.d.ts +0 -1
  103. package/dist/shared/index.d.ts.map +1 -1
  104. package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
  105. package/dist/shared/resolve-fallbacks.d.ts +16 -16
  106. package/dist/shared/resolve-fallbacks.d.ts.map +1 -1
  107. package/dist/shared/rpc-types.d.ts +20 -0
  108. package/dist/shared/rpc-types.d.ts.map +1 -1
  109. package/dist/shared/sqlite.d.ts +5 -1
  110. package/dist/shared/sqlite.d.ts.map +1 -1
  111. package/dist/tools/ctx-expand/constants.d.ts +1 -1
  112. package/dist/tools/ctx-expand/constants.d.ts.map +1 -1
  113. package/dist/tools/ctx-expand/render.d.ts +43 -0
  114. package/dist/tools/ctx-expand/render.d.ts.map +1 -0
  115. package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
  116. package/dist/tools/ctx-expand/types.d.ts +6 -2
  117. package/dist/tools/ctx-expand/types.d.ts.map +1 -1
  118. package/dist/tools/ctx-reduce/constants.d.ts +1 -1
  119. package/dist/tools/ctx-reduce/constants.d.ts.map +1 -1
  120. package/dist/tools/ctx-search/tools.d.ts.map +1 -1
  121. package/dist/tui/data/context-db.d.ts +4 -2
  122. package/dist/tui/data/context-db.d.ts.map +1 -1
  123. package/package.json +5 -12
  124. package/src/shared/announcement.ts +6 -6
  125. package/src/shared/index.ts +0 -1
  126. package/src/shared/model-suggestion-retry.test.ts +61 -1
  127. package/src/shared/model-suggestion-retry.ts +22 -0
  128. package/src/shared/resolve-fallbacks.test.ts +37 -71
  129. package/src/shared/resolve-fallbacks.ts +16 -26
  130. package/src/shared/rpc-types.ts +11 -0
  131. package/src/shared/sqlite-bind-style.test.ts +82 -0
  132. package/src/shared/sqlite.ts +30 -1
  133. package/src/shared/tag-transcript.test.ts +3 -1
  134. package/src/shared/tag-transcript.ts +19 -17
  135. package/src/tui/data/context-db.ts +34 -2
  136. package/src/tui/index.tsx +58 -4
  137. package/src/tui/slots/sidebar-content.tsx +18 -9
  138. package/dist/features/magic-context/memory/embedding-local.d.ts +0 -21
  139. package/dist/features/magic-context/memory/embedding-local.d.ts.map +0 -1
  140. package/dist/shared/model-requirements.d.ts +0 -26
  141. package/dist/shared/model-requirements.d.ts.map +0 -1
  142. package/src/shared/model-requirements.ts +0 -86
package/dist/index.js CHANGED
@@ -47,20 +47,9 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
47
47
  var __promiseAll = (args) => Promise.all(args);
48
48
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
49
49
 
50
- // src/agents/dreamer.ts
51
- var DREAMER_AGENT = "dreamer";
52
-
53
50
  // src/agents/historian.ts
54
- var exports_historian = {};
55
- __export(exports_historian, {
56
- HISTORIAN_EDITOR_AGENT: () => HISTORIAN_EDITOR_AGENT,
57
- HISTORIAN_AGENT: () => HISTORIAN_AGENT
58
- });
59
51
  var HISTORIAN_AGENT = "historian", HISTORIAN_EDITOR_AGENT = "historian-editor";
60
52
 
61
- // src/agents/sidekick.ts
62
- var SIDEKICK_AGENT = "sidekick";
63
-
64
53
  // src/shared/jsonc-parser.ts
65
54
  import { existsSync, readFileSync } from "node:fs";
66
55
  function stripJsonComments(content) {
@@ -14836,7 +14825,7 @@ var init_agent_overrides = __esm(() => {
14836
14825
  });
14837
14826
 
14838
14827
  // src/config/schema/magic-context.ts
14839
- var DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65, EXECUTE_THRESHOLD_CAP_MESSAGE = "execute_threshold is capped at 80% for cache safety: a single large agent step can overflow the context window before Magic Context can compact between turns, forcing OpenCode's native compaction (hard to recover from). 80% also leaves headroom below the 85%/95% emergency bands. Use a value between 20 and 80.", DEFAULT_HISTORIAN_TIMEOUT_MS = 300000, DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15, DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2", DREAMER_TASKS, DreamingTaskSchema, DEFAULT_DREAMER_TASKS, PiThinkingLevelSchema, DreamerConfigSchema, SidekickConfigSchema, HistorianConfigSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema;
14828
+ var DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65, EXECUTE_THRESHOLD_CAP_MESSAGE = "execute_threshold is capped at 80% for cache safety: a single large agent step can overflow the context window before Magic Context can compact between turns, forcing OpenCode's native compaction (hard to recover from). 80% also leaves headroom below the 85%/95% emergency bands. Use a value between 20 and 80.", DEFAULT_HISTORIAN_TIMEOUT_MS = 300000, DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15, DREAMER_TASKS, DreamingTaskSchema, DEFAULT_DREAMER_TASKS, PiThinkingLevelSchema, DreamerConfigSchema, SidekickConfigSchema, HistorianConfigSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema;
14840
14829
  var init_magic_context = __esm(() => {
14841
14830
  init_zod();
14842
14831
  init_agent_overrides();
@@ -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
- 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
- model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible, ignored for local."),
14875
+ provider: exports_external.enum(["openai-compatible", "off"]).default("off").describe("Embedding provider. 'openai-compatible' requires endpoint and model, 'off' disables embeddings (default)."),
14876
+ model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible."),
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) => {
@@ -14906,16 +14897,10 @@ var init_magic_context = __esm(() => {
14906
14897
  }
14907
14898
  });
14908
14899
  EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
14909
- if (data.provider === "local") {
14910
- return {
14911
- provider: "local",
14912
- model: data.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
14913
- ...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
14914
- };
14915
- }
14916
14900
  if (data.provider === "openai-compatible") {
14917
14901
  const apiKey = data.api_key?.trim();
14918
14902
  const inputType = data.input_type?.trim();
14903
+ const queryInputType = data.query_input_type?.trim();
14919
14904
  const truncate = data.truncate?.trim();
14920
14905
  return {
14921
14906
  provider: "openai-compatible",
@@ -14923,6 +14908,7 @@ var init_magic_context = __esm(() => {
14923
14908
  endpoint: data.endpoint?.trim() ?? "",
14924
14909
  ...apiKey ? { api_key: apiKey } : {},
14925
14910
  ...inputType ? { input_type: inputType } : {},
14911
+ ...queryInputType ? { query_input_type: queryInputType } : {},
14926
14912
  ...truncate ? { truncate } : {},
14927
14913
  ...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
14928
14914
  };
@@ -14962,8 +14948,7 @@ var init_magic_context = __esm(() => {
14962
14948
  mmap_size_mb: exports_external.number().min(0).max(8192).default(0).describe("Memory-mapped I/O size in MiB (PRAGMA mmap_size). 0 disables mmap (SQLite default). Raising it can cut read overhead on large DBs at the cost of address space. (min 0, max 8192, default 0)")
14963
14949
  }).default({ cache_size_mb: 64, mmap_size_mb: 0 }).describe("SQLite connection tuning for Magic Context's own context.db. These are per-connection PRAGMAs applied at open; they do not change the schema or what is stored."),
14964
14950
  embedding: EmbeddingConfigSchema.default({
14965
- provider: "local",
14966
- model: DEFAULT_LOCAL_EMBEDDING_MODEL
14951
+ provider: "off"
14967
14952
  }).describe("Embedding provider configuration"),
14968
14953
  temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add start/end date attributes on compartments. Gives the agent a sense of session pacing and "how long ago" across multi-day sessions. Graduated from experimental.temporal_awareness; default: true (set false to opt out).'),
14969
14954
  keep_subagents: exports_external.boolean().default(false).describe("Debug: keep the child sessions Magic Context spawns for its own subagents (historian, dreamer, sidekick, memory-migration) instead of deleting them on success. Useful for short-term inspection/data collection — their full transcript (prompt, tool calls, token usage, output) stays in the host session store. Kept sessions accumulate until manually cleared; leave false for normal use. Requires a restart to take effect."),
@@ -15284,58 +15269,6 @@ var init_logger = __esm(() => {
15284
15269
  }
15285
15270
  });
15286
15271
 
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
15272
  // src/features/magic-context/overflow-detection.ts
15340
15273
  function extractErrorMessage(error51) {
15341
15274
  if (!error51)
@@ -15449,15 +15382,9 @@ __export(exports_resolve_fallbacks, {
15449
15382
  resolveFallbackChain: () => resolveFallbackChain,
15450
15383
  parseProviderModel: () => parseProviderModel
15451
15384
  });
15452
- function resolveFallbackChain(agentName, userFallbacks) {
15385
+ function resolveFallbackChain(userFallbacks) {
15453
15386
  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));
15387
+ return dedupe(userList.filter(isValidModelSpec));
15461
15388
  }
15462
15389
  function normalizeUserFallbacks(userFallbacks) {
15463
15390
  if (!userFallbacks)
@@ -15492,9 +15419,6 @@ function parseProviderModel(spec) {
15492
15419
  modelID: spec.slice(slash + 1).trim()
15493
15420
  };
15494
15421
  }
15495
- var init_resolve_fallbacks = __esm(() => {
15496
- init_model_requirements();
15497
- });
15498
15422
 
15499
15423
  // src/shared/model-suggestion-retry.ts
15500
15424
  function extractMessage(error51) {
@@ -15565,9 +15489,11 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
15565
15489
  });
15566
15490
  } catch (error51) {
15567
15491
  if (signal?.aborted) {
15492
+ await abortChildRun(client, args.path.id);
15568
15493
  throw new Error("prompt aborted by external signal");
15569
15494
  }
15570
15495
  if (controller.signal.aborted) {
15496
+ await abortChildRun(client, args.path.id);
15571
15497
  throw new Error(`prompt timed out after ${timeoutMs}ms`);
15572
15498
  }
15573
15499
  throw error51;
@@ -15576,6 +15502,13 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
15576
15502
  signal?.removeEventListener("abort", onExternalAbort);
15577
15503
  }
15578
15504
  }
15505
+ async function abortChildRun(client, sessionId) {
15506
+ try {
15507
+ await client.session.abort({ path: { id: sessionId } });
15508
+ } catch (error51) {
15509
+ log(`[model-retry] child session abort failed for ${sessionId}: ${String(error51)}`);
15510
+ }
15511
+ }
15579
15512
  function isNonRetryable(error51, externalSignal) {
15580
15513
  if (externalSignal?.aborted)
15581
15514
  return true;
@@ -15673,7 +15606,6 @@ async function promptSyncWithModelSuggestionRetry(client, args, options = {}) {
15673
15606
  var init_model_suggestion_retry = __esm(() => {
15674
15607
  init_overflow_detection();
15675
15608
  init_logger();
15676
- init_resolve_fallbacks();
15677
15609
  });
15678
15610
 
15679
15611
  // src/shared/normalize-sdk-response.ts
@@ -15703,9 +15635,7 @@ function normalizeSDKResponse(response, fallback, options) {
15703
15635
  // src/shared/index.ts
15704
15636
  var init_shared = __esm(() => {
15705
15637
  init_logger();
15706
- init_model_requirements();
15707
15638
  init_model_suggestion_retry();
15708
- init_resolve_fallbacks();
15709
15639
  });
15710
15640
 
15711
15641
  // src/shared/record-type-guard.ts
@@ -15847,7 +15777,7 @@ function isSessionMetaRow(row) {
15847
15777
  if (row === null || typeof row !== "object")
15848
15778
  return false;
15849
15779
  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);
15780
+ 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
15781
  }
15852
15782
  function getDefaultSessionMeta(sessionId) {
15853
15783
  return {
@@ -15870,6 +15800,7 @@ function getDefaultSessionMeta(sessionId) {
15870
15800
  conversationTokens: 0,
15871
15801
  toolCallTokens: 0,
15872
15802
  clearedReasoningThroughTag: 0,
15803
+ toolReclaimWatermark: 0,
15873
15804
  lastTodoState: "",
15874
15805
  cachedM0Bytes: null,
15875
15806
  cachedM1Bytes: null,
@@ -15933,6 +15864,7 @@ function toSessionMeta(row) {
15933
15864
  conversationTokens: numOrZero(row.conversation_tokens),
15934
15865
  toolCallTokens: numOrZero(row.tool_call_tokens),
15935
15866
  clearedReasoningThroughTag: numOrZero(row.cleared_reasoning_through_tag),
15867
+ toolReclaimWatermark: numOrZero(row.tool_reclaim_watermark),
15936
15868
  lastTodoState: lastTodoStateRaw,
15937
15869
  cachedM0Bytes: toBufferOrNull(row.cached_m0_bytes),
15938
15870
  cachedM1Bytes: toBufferOrNull(row.cached_m1_bytes),
@@ -16042,6 +15974,7 @@ var init_storage_meta_shared = __esm(() => {
16042
15974
  "conversation_tokens",
16043
15975
  "tool_call_tokens",
16044
15976
  "cleared_reasoning_through_tag",
15977
+ "tool_reclaim_watermark",
16045
15978
  "last_todo_state",
16046
15979
  "cached_m0_bytes",
16047
15980
  "cached_m1_bytes",
@@ -16068,6 +16001,8 @@ var init_storage_meta_shared = __esm(() => {
16068
16001
  "recovery_no_eligible_head_count",
16069
16002
  "force_emergency_bypass_window_start",
16070
16003
  "force_emergency_bypass_used",
16004
+ "emergency_drain_active",
16005
+ "historian_drain_failure_at",
16071
16006
  "upgrade_reminded_at",
16072
16007
  "pi_stable_id_scheme"
16073
16008
  ];
@@ -16090,6 +16025,7 @@ var init_storage_meta_shared = __esm(() => {
16090
16025
  conversationTokens: "conversation_tokens",
16091
16026
  toolCallTokens: "tool_call_tokens",
16092
16027
  clearedReasoningThroughTag: "cleared_reasoning_through_tag",
16028
+ toolReclaimWatermark: "tool_reclaim_watermark",
16093
16029
  lastTodoState: "last_todo_state",
16094
16030
  cachedM0Bytes: "cached_m0_bytes",
16095
16031
  cachedM1Bytes: "cached_m1_bytes",
@@ -16116,6 +16052,8 @@ var init_storage_meta_shared = __esm(() => {
16116
16052
  recoveryNoEligibleHeadCount: "recovery_no_eligible_head_count",
16117
16053
  forceEmergencyBypassWindowStart: "force_emergency_bypass_window_start",
16118
16054
  forceEmergencyBypassUsed: "force_emergency_bypass_used",
16055
+ emergencyDrainActive: "emergency_drain_active",
16056
+ historianDrainFailureAt: "historian_drain_failure_at",
16119
16057
  upgradeRemindedAt: "upgrade_reminded_at",
16120
16058
  piStableIdScheme: "pi_stable_id_scheme"
16121
16059
  };
@@ -16366,6 +16304,14 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
16366
16304
  }
16367
16305
  super(typeof filename === "string" ? filename : ":memory:", translated);
16368
16306
  }
16307
+ prepare(sql) {
16308
+ const stmt = super.prepare(sql);
16309
+ for (const method of ["run", "get", "all"]) {
16310
+ const original = stmt[method].bind(stmt);
16311
+ stmt[method] = (...args) => args.length === 1 && Array.isArray(args[0]) ? original(...args[0]) : original(...args);
16312
+ }
16313
+ return stmt;
16314
+ }
16369
16315
  transaction(fn) {
16370
16316
  const self = this;
16371
16317
  const wrapped = function(...args) {
@@ -149342,6 +149288,9 @@ function stripCompleteTagPairsGlobally(value) {
149342
149288
  function stripMalformedTagNotationGlobally(value) {
149343
149289
  return value.replace(MALFORMED_TAG_GLOBAL_REGEX, "");
149344
149290
  }
149291
+ function stripDanglingTagNotationGlobally(value) {
149292
+ return value.replace(DANGLING_TAG_GLOBAL_REGEX, "");
149293
+ }
149345
149294
  function stripTagSectionCharacters(value) {
149346
149295
  return value.replace(STRAY_SECTION_CHAR_REGEX, "");
149347
149296
  }
@@ -149349,6 +149298,7 @@ function stripPersistedAssistantText(value) {
149349
149298
  let text = stripWellFormedLeadingTagPrefix(value);
149350
149299
  text = stripCompleteTagPairsGlobally(text);
149351
149300
  text = stripMalformedTagNotationGlobally(text);
149301
+ text = stripDanglingTagNotationGlobally(text);
149352
149302
  text = stripTagSectionCharacters(text);
149353
149303
  return text.trim();
149354
149304
  }
@@ -149361,6 +149311,7 @@ function stripTagPrefix(value) {
149361
149311
  const prev = stripped;
149362
149312
  stripped = stripped.replace(MALFORMED_TAG_PREFIX_REGEX, "");
149363
149313
  stripped = stripped.replace(TAG_PREFIX_REGEX, "");
149314
+ stripped = stripped.replace(DANGLING_TAG_PREFIX_REGEX, "");
149364
149315
  if (stripped === prev)
149365
149316
  break;
149366
149317
  }
@@ -149382,11 +149333,13 @@ function isThinkingPart(part) {
149382
149333
  const candidate = part;
149383
149334
  return candidate.type === "thinking" || candidate.type === "reasoning";
149384
149335
  }
149385
- var encoder, TAG_PREFIX_REGEX, MALFORMED_TAG_PREFIX_REGEX, COMPLETE_TAG_PAIR_GLOBAL_REGEX, MALFORMED_TAG_GLOBAL_REGEX, STRAY_SECTION_CHAR_REGEX;
149336
+ 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
149337
  var init_tag_content_primitives = __esm(() => {
149387
149338
  encoder = new TextEncoder;
149388
149339
  TAG_PREFIX_REGEX = /^(?:§\d+§\s*)+/;
149389
149340
  MALFORMED_TAG_PREFIX_REGEX = /^(?:§\d+">§(?:\d+§)?\s*)+/;
149341
+ DANGLING_TAG_GLOBAL_REGEX = /\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?/g;
149342
+ DANGLING_TAG_PREFIX_REGEX = /^(?:\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?\s*)+/;
149390
149343
  COMPLETE_TAG_PAIR_GLOBAL_REGEX = /\u00a7\d+\u00a7/g;
149391
149344
  MALFORMED_TAG_GLOBAL_REGEX = /\u00a7\d+">(?:\u00a7(?:\d+\u00a7)?)?/g;
149392
149345
  STRAY_SECTION_CHAR_REGEX = /\u00a7/g;
@@ -149450,12 +149403,13 @@ function setToolContent(part, content) {
149450
149403
  part.content = content;
149451
149404
  }
149452
149405
  }
149453
- function truncateToolPart(part) {
149406
+ function truncateToolPart(part, tagId) {
149454
149407
  if (!isRecord(part))
149455
149408
  return;
149409
+ const sentinel = `[dropped §${tagId}§]`;
149456
149410
  if (part.type === "tool" && isRecord(part.state)) {
149457
149411
  const state = part.state;
149458
- state.output = "[truncated]";
149412
+ state.output = sentinel;
149459
149413
  if (isRecord(state.input)) {
149460
149414
  const inputSize = estimateInputSize(state.input);
149461
149415
  if (inputSize > 500) {
@@ -149465,7 +149419,7 @@ function truncateToolPart(part) {
149465
149419
  return;
149466
149420
  }
149467
149421
  if (part.type === "tool_result") {
149468
- part.content = "[truncated]";
149422
+ part.content = sentinel;
149469
149423
  return;
149470
149424
  }
149471
149425
  if (part.type === "tool-invocation" && isRecord(part.args)) {
@@ -149582,7 +149536,7 @@ class ToolMutationBatch {
149582
149536
  this.affectedMessages.clear();
149583
149537
  }
149584
149538
  }
149585
- function createToolDropTarget(compositeKey, thinkingParts, index, batch) {
149539
+ function createToolDropTarget(compositeKey, thinkingParts, index, batch, tagId) {
149586
149540
  const drop = () => {
149587
149541
  const entry = index.get(compositeKey);
149588
149542
  if (!entry || entry.occurrences.length === 0)
@@ -149603,7 +149557,7 @@ function createToolDropTarget(compositeKey, thinkingParts, index, batch) {
149603
149557
  if (!entry.hasResult)
149604
149558
  return "incomplete";
149605
149559
  for (const occurrence of entry.occurrences) {
149606
- truncateToolPart(occurrence.part);
149560
+ truncateToolPart(occurrence.part, tagId);
149607
149561
  }
149608
149562
  clearThinkingParts(thinkingParts);
149609
149563
  return "truncated";
@@ -149651,6 +149605,22 @@ var init_tool_drop_target = __esm(() => {
149651
149605
  });
149652
149606
 
149653
149607
  // src/hooks/magic-context/read-session-chunk.ts
149608
+ function estimateBlockTokens(blockText) {
149609
+ const cached2 = blockTokenMemo.get(blockText);
149610
+ if (cached2 !== undefined) {
149611
+ blockTokenMemo.delete(blockText);
149612
+ blockTokenMemo.set(blockText, cached2);
149613
+ return cached2;
149614
+ }
149615
+ const count = estimateTokens(blockText);
149616
+ if (blockTokenMemo.size >= BLOCK_TOKEN_MEMO_MAX) {
149617
+ const oldest = blockTokenMemo.keys().next().value;
149618
+ if (oldest !== undefined)
149619
+ blockTokenMemo.delete(oldest);
149620
+ }
149621
+ blockTokenMemo.set(blockText, count);
149622
+ return count;
149623
+ }
149654
149624
  function cleanUserText(text) {
149655
149625
  return removeSystemReminders(text).replace(OMO_INTERNAL_INITIATOR_MARKER, "").trim();
149656
149626
  }
@@ -149818,7 +149788,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
149818
149788
  if (!currentBlock)
149819
149789
  return true;
149820
149790
  const blockText = formatBlock(currentBlock);
149821
- const blockTokens = estimateTokens(blockText);
149791
+ const blockTokens = estimateBlockTokens(blockText);
149822
149792
  if (totalTokens + blockTokens > tokenBudget && totalTokens > 0) {
149823
149793
  return false;
149824
149794
  }
@@ -149936,13 +149906,14 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
149936
149906
  toolOnlyRanges
149937
149907
  };
149938
149908
  }
149939
- var activeRawMessageCache = null, activeAbsoluteCountCache = null, sessionProviders, PROTECTED_TAIL_USER_TURNS = 5;
149909
+ var BLOCK_TOKEN_MEMO_MAX = 2048, blockTokenMemo, activeRawMessageCache = null, activeAbsoluteCountCache = null, sessionProviders, PROTECTED_TAIL_USER_TURNS = 5;
149940
149910
  var init_read_session_chunk = __esm(async () => {
149941
149911
  init_read_session_formatting();
149942
149912
  init_tag_part_guards();
149943
149913
  init_tool_drop_target();
149944
149914
  init_read_session_formatting();
149945
149915
  await init_read_session_db();
149916
+ blockTokenMemo = new Map;
149946
149917
  sessionProviders = new Map;
149947
149918
  });
149948
149919
 
@@ -150687,6 +150658,9 @@ function resolveDatabasePath(dbPathOverride) {
150687
150658
  const dbDir = getMagicContextStorageDir();
150688
150659
  return { dbDir, dbPath: join6(dbDir, "context.db") };
150689
150660
  }
150661
+ function getDatabasePath(db) {
150662
+ return pathByDatabase.get(db) ?? null;
150663
+ }
150690
150664
  function migrateLegacyStorageIfNeeded(targetDbPath, targetDbDir) {
150691
150665
  if (existsSync7(targetDbPath))
150692
150666
  return;
@@ -151197,6 +151171,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151197
151171
  recovery_no_eligible_head_count INTEGER NOT NULL DEFAULT 0,
151198
151172
  force_emergency_bypass_window_start INTEGER NOT NULL DEFAULT 0,
151199
151173
  force_emergency_bypass_used INTEGER NOT NULL DEFAULT 0,
151174
+ emergency_drain_active INTEGER NOT NULL DEFAULT 0,
151175
+ historian_drain_failure_at INTEGER NOT NULL DEFAULT 0,
151200
151176
  cached_m0_materialized_at INTEGER,
151201
151177
  cached_m0_session_facts_version INTEGER,
151202
151178
  cached_m0_upgrade_state TEXT,
@@ -151260,6 +151236,23 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151260
151236
  CREATE INDEX IF NOT EXISTS idx_historian_runs_status
151261
151237
  ON historian_runs(status, created_at DESC);
151262
151238
 
151239
+ CREATE TABLE IF NOT EXISTS transform_decisions (
151240
+ session_id TEXT NOT NULL,
151241
+ harness TEXT NOT NULL DEFAULT 'opencode',
151242
+ message_id TEXT NOT NULL,
151243
+ ts_ms INTEGER NOT NULL,
151244
+ decision TEXT NOT NULL,
151245
+ materialized INTEGER NOT NULL DEFAULT 0,
151246
+ materialize_reason TEXT,
151247
+ emergency INTEGER NOT NULL DEFAULT 0,
151248
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
151249
+ dropped_count INTEGER NOT NULL DEFAULT 0,
151250
+ input_tokens INTEGER NOT NULL DEFAULT 0,
151251
+ PRIMARY KEY (session_id, harness, message_id)
151252
+ );
151253
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
151254
+ ON transform_decisions(session_id, harness);
151255
+
151263
151256
  CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
151264
151257
  CREATE INDEX IF NOT EXISTS idx_tags_session_message_id ON tags(session_id, message_id);
151265
151258
  CREATE INDEX IF NOT EXISTS idx_pending_ops_session ON pending_ops(session_id);
@@ -151337,6 +151330,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151337
151330
  ensureColumn(db, "session_meta", "historian_last_failure_at", "INTEGER DEFAULT NULL");
151338
151331
  ensureColumn(db, "session_meta", "system_prompt_hash", "TEXT DEFAULT ''");
151339
151332
  ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
151333
+ ensureColumn(db, "session_meta", "tool_reclaim_watermark", "INTEGER DEFAULT 0");
151340
151334
  ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
151341
151335
  ensureColumn(db, "session_meta", "stale_reduce_stripped_ids", "TEXT DEFAULT ''");
151342
151336
  ensureColumn(db, "session_meta", "processed_image_stripped_ids", "TEXT DEFAULT ''");
@@ -151410,6 +151404,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151410
151404
  ensureColumn(db, "session_meta", "recovery_no_eligible_head_count", "INTEGER NOT NULL DEFAULT 0");
151411
151405
  ensureColumn(db, "session_meta", "force_emergency_bypass_window_start", "INTEGER NOT NULL DEFAULT 0");
151412
151406
  ensureColumn(db, "session_meta", "force_emergency_bypass_used", "INTEGER NOT NULL DEFAULT 0");
151407
+ ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
151408
+ ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
151413
151409
  ensureColumn(db, "session_meta", "cached_m0_materialized_at", "INTEGER");
151414
151410
  ensureColumn(db, "session_meta", "cached_m0_session_facts_version", "INTEGER");
151415
151411
  ensureColumn(db, "session_meta", "cached_m0_upgrade_state", "TEXT");
@@ -151488,6 +151484,22 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151488
151484
  failed_at INTEGER NOT NULL,
151489
151485
  UNIQUE(table_name, row_id)
151490
151486
  );
151487
+ CREATE TABLE IF NOT EXISTS transform_decisions (
151488
+ session_id TEXT NOT NULL,
151489
+ harness TEXT NOT NULL DEFAULT 'opencode',
151490
+ message_id TEXT NOT NULL,
151491
+ ts_ms INTEGER NOT NULL,
151492
+ decision TEXT NOT NULL,
151493
+ materialized INTEGER NOT NULL DEFAULT 0,
151494
+ materialize_reason TEXT,
151495
+ emergency INTEGER NOT NULL DEFAULT 0,
151496
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
151497
+ dropped_count INTEGER NOT NULL DEFAULT 0,
151498
+ input_tokens INTEGER NOT NULL DEFAULT 0,
151499
+ PRIMARY KEY (session_id, harness, message_id)
151500
+ );
151501
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
151502
+ ON transform_decisions(session_id, harness);
151491
151503
  `);
151492
151504
  ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
151493
151505
  ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
@@ -151573,7 +151585,9 @@ function healNullIntegerColumns(db) {
151573
151585
  ["protected_tail_drain_tokens", 0],
151574
151586
  ["recovery_no_eligible_head_count", 0],
151575
151587
  ["force_emergency_bypass_window_start", 0],
151576
- ["force_emergency_bypass_used", 0]
151588
+ ["force_emergency_bypass_used", 0],
151589
+ ["emergency_drain_active", 0],
151590
+ ["historian_drain_failure_at", 0]
151577
151591
  ];
151578
151592
  for (const [column, fallback] of columns) {
151579
151593
  try {
@@ -151649,6 +151663,7 @@ function openDatabase(dbPathOrOptions) {
151649
151663
  setDatabase(db);
151650
151664
  loadToolDefinitionMeasurements(db);
151651
151665
  databases.set(dbPath, db);
151666
+ pathByDatabase.set(db, dbPath);
151652
151667
  persistenceByDatabase.set(db, true);
151653
151668
  persistenceErrorByDatabase.delete(db);
151654
151669
  return db;
@@ -151668,7 +151683,7 @@ function getDatabasePersistenceError(db) {
151668
151683
  return null;
151669
151684
  return persistenceErrorByDatabase.get(db) ?? null;
151670
151685
  }
151671
- var databases, persistenceByDatabase, persistenceErrorByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 36, sqlitePragmaConfig, CHANNEL2_CLAIM_TTL_MS = 120000;
151686
+ var databases, persistenceByDatabase, persistenceErrorByDatabase, pathByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 38, sqlitePragmaConfig, CHANNEL2_CLAIM_TTL_MS = 120000;
151672
151687
  var init_storage_db = __esm(async () => {
151673
151688
  init_data_path();
151674
151689
  init_logger();
@@ -151682,6 +151697,7 @@ var init_storage_db = __esm(async () => {
151682
151697
  databases = new Map;
151683
151698
  persistenceByDatabase = new WeakMap;
151684
151699
  persistenceErrorByDatabase = new WeakMap;
151700
+ pathByDatabase = new WeakMap;
151685
151701
  sqlitePragmaConfig = {
151686
151702
  cacheSizeMb: 64,
151687
151703
  mmapSizeMb: 0
@@ -153000,6 +153016,41 @@ var init_migrations = __esm(async () => {
153000
153016
  `);
153001
153017
  }
153002
153018
  }
153019
+ },
153020
+ {
153021
+ version: 37,
153022
+ description: "emergency drain catch-up latch + historian drain failure backoff",
153023
+ up: (db) => {
153024
+ const hasSessionMeta = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='session_meta'").get();
153025
+ if (!hasSessionMeta)
153026
+ return;
153027
+ ensureColumn(db, "session_meta", "emergency_drain_active", "INTEGER NOT NULL DEFAULT 0");
153028
+ ensureColumn(db, "session_meta", "historian_drain_failure_at", "INTEGER NOT NULL DEFAULT 0");
153029
+ }
153030
+ },
153031
+ {
153032
+ version: 38,
153033
+ description: "durable transform decisions for cache-event cause attribution",
153034
+ up: (db) => {
153035
+ db.exec(`
153036
+ CREATE TABLE IF NOT EXISTS transform_decisions (
153037
+ session_id TEXT NOT NULL,
153038
+ harness TEXT NOT NULL DEFAULT 'opencode',
153039
+ message_id TEXT NOT NULL,
153040
+ ts_ms INTEGER NOT NULL,
153041
+ decision TEXT NOT NULL,
153042
+ materialized INTEGER NOT NULL DEFAULT 0,
153043
+ materialize_reason TEXT,
153044
+ emergency INTEGER NOT NULL DEFAULT 0,
153045
+ dropped_tokens INTEGER NOT NULL DEFAULT 0,
153046
+ dropped_count INTEGER NOT NULL DEFAULT 0,
153047
+ input_tokens INTEGER NOT NULL DEFAULT 0,
153048
+ PRIMARY KEY (session_id, harness, message_id)
153049
+ );
153050
+ CREATE INDEX IF NOT EXISTS idx_transform_decisions_session_harness
153051
+ ON transform_decisions(session_id, harness);
153052
+ `);
153053
+ }
153003
153054
  }
153004
153055
  ];
153005
153056
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -153404,7 +153455,9 @@ function toProtectedTailMeta(row) {
153404
153455
  protectedTailDrainTokens: numberOr(r.protected_tail_drain_tokens, 0),
153405
153456
  recoveryNoEligibleHeadCount: numberOr(r.recovery_no_eligible_head_count, 0),
153406
153457
  forceEmergencyBypassWindowStart: numberOr(r.force_emergency_bypass_window_start, 0),
153407
- forceEmergencyBypassUsed: numberOr(r.force_emergency_bypass_used, 0)
153458
+ forceEmergencyBypassUsed: numberOr(r.force_emergency_bypass_used, 0),
153459
+ emergencyDrainActive: numberOr(r.emergency_drain_active, 0),
153460
+ historianDrainFailureAt: numberOr(r.historian_drain_failure_at, 0)
153408
153461
  };
153409
153462
  }
153410
153463
  function loadProtectedTailMeta(db, sessionId) {
@@ -153412,7 +153465,7 @@ function loadProtectedTailMeta(db, sessionId) {
153412
153465
  const row = db.prepare(`SELECT prior_boundary_ordinal, protected_tail_policy_version,
153413
153466
  protected_tail_drain_window_started_at, protected_tail_drain_tokens,
153414
153467
  recovery_no_eligible_head_count, force_emergency_bypass_window_start,
153415
- force_emergency_bypass_used
153468
+ force_emergency_bypass_used, emergency_drain_active, historian_drain_failure_at
153416
153469
  FROM session_meta WHERE session_id = ?`).get(sessionId);
153417
153470
  return toProtectedTailMeta(row);
153418
153471
  }
@@ -153461,6 +153514,12 @@ function protectedTailWindowBudget(usagePercentage, usable, perRunCap) {
153461
153514
  return Math.min(750000, Math.max(3 * perRunCap, Math.round(0.35 * usable)));
153462
153515
  return Math.min(500000, Math.max(perRunCap, Math.round(0.2 * usable)));
153463
153516
  }
153517
+ function emergencyDrainExitThreshold(executeThresholdPercentage) {
153518
+ if (!Number.isFinite(executeThresholdPercentage) || executeThresholdPercentage <= 0) {
153519
+ return EMERGENCY_DRAIN_FALLBACK_EXIT_PERCENTAGE;
153520
+ }
153521
+ return Math.max(0, executeThresholdPercentage - EMERGENCY_DRAIN_EXIT_MARGIN);
153522
+ }
153464
153523
  function reserveProtectedTailDrainTokens(args) {
153465
153524
  const now = args.now ?? Date.now();
153466
153525
  const requested = Math.max(0, Math.floor(args.trueRawTokens));
@@ -153479,18 +153538,30 @@ function reserveProtectedTailDrainTokens(args) {
153479
153538
  let meta3 = loadProtectedTailMeta(args.db, args.sessionId);
153480
153539
  if (now - meta3.protectedTailDrainWindowStartedAt > DRAIN_WINDOW_MS) {
153481
153540
  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);
153541
+ SET protected_tail_drain_window_started_at = ?, protected_tail_drain_tokens = 0
153542
+ WHERE session_id = ?`).run(now, args.sessionId);
153485
153543
  meta3 = loadProtectedTailMeta(args.db, args.sessionId);
153486
153544
  }
153545
+ const exitThreshold = emergencyDrainExitThreshold(args.executeThresholdPercentage);
153546
+ let latchActiveSince = meta3.emergencyDrainActive;
153547
+ if (args.usagePercentage >= EMERGENCY_DRAIN_ENTER_PERCENTAGE) {
153548
+ if (latchActiveSince <= 0)
153549
+ latchActiveSince = now;
153550
+ } else if (latchActiveSince > 0) {
153551
+ const expired = now - latchActiveSince > EMERGENCY_DRAIN_MAX_LATCH_MS;
153552
+ if (args.usagePercentage < exitThreshold || expired)
153553
+ latchActiveSince = 0;
153554
+ }
153555
+ if (latchActiveSince !== meta3.emergencyDrainActive) {
153556
+ args.db.prepare("UPDATE session_meta SET emergency_drain_active = ? WHERE session_id = ?").run(latchActiveSince, args.sessionId);
153557
+ }
153558
+ const latchActive = latchActiveSince > 0;
153487
153559
  const budget = protectedTailWindowBudget(args.usagePercentage, args.usable, args.perRunCap);
153488
153560
  const remaining = Math.max(0, budget - meta3.protectedTailDrainTokens);
153489
153561
  let reserved = Math.min(requested, args.perRunCap, remaining);
153490
153562
  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) {
153563
+ const inFailureBackoff = meta3.historianDrainFailureAt > 0 && now - meta3.historianDrainFailureAt < EMERGENCY_DRAIN_FAILURE_BACKOFF_MS;
153564
+ if (reserved <= 0 && latchActive && !inFailureBackoff) {
153494
153565
  reserved = Math.min(requested, args.perRunCap);
153495
153566
  bypass = true;
153496
153567
  }
@@ -153498,10 +153569,8 @@ function reserveProtectedTailDrainTokens(args) {
153498
153569
  return;
153499
153570
  args.db.prepare(`UPDATE session_meta
153500
153571
  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);
153572
+ protected_tail_drain_tokens = COALESCE(protected_tail_drain_tokens, 0) + ?
153573
+ WHERE session_id = ?`).run(now, reserved, args.sessionId);
153505
153574
  result = {
153506
153575
  ok: true,
153507
153576
  reservedTokens: reserved,
@@ -153511,6 +153580,25 @@ function reserveProtectedTailDrainTokens(args) {
153511
153580
  })();
153512
153581
  return result;
153513
153582
  }
153583
+ function clearEmergencyDrainLatch(db, sessionId) {
153584
+ db.transaction(() => {
153585
+ ensureSessionMetaRow(db, sessionId);
153586
+ db.prepare("UPDATE session_meta SET emergency_drain_active = 0 WHERE session_id = ?").run(sessionId);
153587
+ })();
153588
+ }
153589
+ function recordHistorianDrainFailure(db, sessionId, now) {
153590
+ const ts = now ?? Date.now();
153591
+ db.transaction(() => {
153592
+ ensureSessionMetaRow(db, sessionId);
153593
+ db.prepare("UPDATE session_meta SET historian_drain_failure_at = ? WHERE session_id = ?").run(ts, sessionId);
153594
+ })();
153595
+ }
153596
+ function clearHistorianDrainFailure(db, sessionId) {
153597
+ db.transaction(() => {
153598
+ ensureSessionMetaRow(db, sessionId);
153599
+ db.prepare("UPDATE session_meta SET historian_drain_failure_at = 0 WHERE session_id = ?").run(sessionId);
153600
+ })();
153601
+ }
153514
153602
  function rollbackProtectedTailDrainReservation(db, reservation) {
153515
153603
  if (!reservation || reservation.tokens <= 0)
153516
153604
  return;
@@ -154078,7 +154166,7 @@ function setSessionWorkMetrics(db, sessionId, newWorkTokens, totalInputTokens) {
154078
154166
  SET new_work_tokens = ?, total_input_tokens = ?
154079
154167
  WHERE session_id = ?`).run(Math.max(0, Math.floor(newWorkTokens)), Math.max(0, Math.floor(totalInputTokens)), sessionId);
154080
154168
  }
154081
- var CAS_RETRY_LIMIT = 5, AUTO_SEARCH_NO_HINT_REASONS, DEFAULT_PROTECTED_TAIL_META, DRAIN_WINDOW_MS;
154169
+ 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
154170
  var init_storage_meta_persisted = __esm(() => {
154083
154171
  init_logger();
154084
154172
  init_storage_meta_shared();
@@ -154097,9 +154185,12 @@ var init_storage_meta_persisted = __esm(() => {
154097
154185
  protectedTailDrainTokens: 0,
154098
154186
  recoveryNoEligibleHeadCount: 0,
154099
154187
  forceEmergencyBypassWindowStart: 0,
154100
- forceEmergencyBypassUsed: 0
154188
+ forceEmergencyBypassUsed: 0,
154189
+ emergencyDrainActive: 0,
154190
+ historianDrainFailureAt: 0
154101
154191
  };
154102
154192
  DRAIN_WINDOW_MS = 10 * 60 * 1000;
154193
+ EMERGENCY_DRAIN_MAX_LATCH_MS = 30 * 60 * 1000;
154103
154194
  });
154104
154195
 
154105
154196
  // src/features/magic-context/resolve-subagent-fallback.ts
@@ -154126,7 +154217,8 @@ var exports_storage_meta_session = {};
154126
154217
  __export(exports_storage_meta_session, {
154127
154218
  updateSessionMeta: () => updateSessionMeta,
154128
154219
  getOrCreateSessionMeta: () => getOrCreateSessionMeta,
154129
- clearSession: () => clearSession
154220
+ clearSession: () => clearSession,
154221
+ advanceToolReclaimWatermark: () => advanceToolReclaimWatermark
154130
154222
  });
154131
154223
  import { Buffer as Buffer3 } from "node:buffer";
154132
154224
  function getSessionMetaSelectColumns(db) {
@@ -154187,6 +154279,14 @@ function updateSessionMeta(db, sessionId, updates) {
154187
154279
  db.prepare(`UPDATE session_meta SET ${setClauses.join(", ")} WHERE session_id = ?`).run(...values, sessionId);
154188
154280
  })();
154189
154281
  }
154282
+ function advanceToolReclaimWatermark(db, sessionId, maxTagNumber) {
154283
+ if (maxTagNumber <= 0)
154284
+ return;
154285
+ db.transaction(() => {
154286
+ ensureSessionMetaRow(db, sessionId);
154287
+ db.prepare("UPDATE session_meta SET tool_reclaim_watermark = MAX(COALESCE(tool_reclaim_watermark, 0), ?) WHERE session_id = ?").run(maxTagNumber, sessionId);
154288
+ })();
154289
+ }
154190
154290
  function clearSession(db, sessionId) {
154191
154291
  db.transaction(() => {
154192
154292
  db.prepare("DELETE FROM pending_ops WHERE session_id = ?").run(sessionId);
@@ -154208,6 +154308,7 @@ function clearSession(db, sessionId) {
154208
154308
  db.prepare("DELETE FROM subagent_invocations WHERE session_id = ?").run(sessionId);
154209
154309
  db.prepare("DELETE FROM historian_runs WHERE session_id = ?").run(sessionId);
154210
154310
  db.prepare("DELETE FROM plugin_messages WHERE session_id = ?").run(sessionId);
154311
+ db.prepare("DELETE FROM transform_decisions WHERE session_id = ?").run(sessionId);
154211
154312
  clearIndexedMessages(db, sessionId);
154212
154313
  })();
154213
154314
  }
@@ -154225,6 +154326,7 @@ var init_storage_meta_session = __esm(async () => {
154225
154326
  last_transform_error: "'' AS last_transform_error",
154226
154327
  system_prompt_hash: "'' AS system_prompt_hash",
154227
154328
  last_todo_state: "'' AS last_todo_state",
154329
+ tool_reclaim_watermark: "0 AS tool_reclaim_watermark",
154228
154330
  cached_m0_bytes: "NULL AS cached_m0_bytes",
154229
154331
  cached_m1_bytes: "NULL AS cached_m1_bytes",
154230
154332
  cached_m0_project_memory_epoch: "NULL AS cached_m0_project_memory_epoch",
@@ -154632,12 +154734,37 @@ function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
154632
154734
  nullCount: row?.null_count ?? 0
154633
154735
  };
154634
154736
  }
154635
- function getTriggerTagTokenUpperBound(db, sessionId) {
154636
- const row = db.prepare(`SELECT
154737
+ function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, limit = 4) {
154738
+ if (limit <= 0)
154739
+ return [];
154740
+ const boundedLimit = Math.max(1, Math.min(10, Math.floor(limit)));
154741
+ const whereProtected = protectedTags > 0 ? `AND tag_number < (
154742
+ SELECT tag_number FROM tags
154743
+ WHERE session_id = ? AND status = 'active'
154744
+ ORDER BY tag_number DESC LIMIT 1 OFFSET ?
154745
+ )` : "";
154746
+ const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
154747
+ const rows = db.prepare(`SELECT tag_number, tool_name
154748
+ FROM tags
154749
+ WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
154750
+ ORDER BY tag_number ASC, id ASC
154751
+ LIMIT ?`).all(...params);
154752
+ return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
154753
+ tagNumber: row.tag_number,
154754
+ toolName: typeof row.tool_name === "string" ? row.tool_name : null
154755
+ }));
154756
+ }
154757
+ function getTriggerTagTokenUpperBound(db, sessionId, floor = 0) {
154758
+ const sql = floor > 0 ? `SELECT
154637
154759
  COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
154638
154760
  COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
154639
154761
  FROM tags
154640
- WHERE session_id = ? AND status IN ('active', 'dropped')`).get(sessionId);
154762
+ WHERE session_id = ? AND status IN ('active', 'dropped') AND tag_number >= ?` : `SELECT
154763
+ COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
154764
+ COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
154765
+ FROM tags
154766
+ WHERE session_id = ? AND status IN ('active', 'dropped')`;
154767
+ const row = floor > 0 ? db.prepare(sql).get(sessionId, floor) : db.prepare(sql).get(sessionId);
154641
154768
  return { bound: row?.bound ?? 0, nullCount: row?.null_count ?? 0 };
154642
154769
  }
154643
154770
  function getActiveTagTokenTotalsByMessage(db, sessionId) {
@@ -154666,10 +154793,12 @@ function getActiveTagTokenTotalsByMessage(db, sessionId) {
154666
154793
  }
154667
154794
  return out;
154668
154795
  }
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);
154796
+ function getAllStatusTagTokenTotalsFlat(db, sessionId, floor = 0) {
154797
+ const rows = floor > 0 ? db.prepare(`SELECT type, message_id, tool_owner_message_id, token_count, input_token_count, reasoning_token_count
154798
+ FROM tags
154799
+ 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
154800
+ FROM tags
154801
+ WHERE session_id = ?`).all(sessionId);
154673
154802
  const totals = new Map;
154674
154803
  const nullMessageIds = new Set;
154675
154804
  for (const row of rows) {
@@ -154828,6 +154957,47 @@ function getTagNumberByMessageId(db, sessionId, messageId) {
154828
154957
  const row = getTagNumberByMessageIdStatement(db).get(sessionId, messageId);
154829
154958
  return isTagNumberRow(row) ? row.tag_number : null;
154830
154959
  }
154960
+ function isMinTagNumberRow(row) {
154961
+ return row !== null && typeof row === "object" && "m" in row;
154962
+ }
154963
+ function getMinMessageTagNumberForRawId(db, sessionId, rawId) {
154964
+ if (rawId.includes(":"))
154965
+ return null;
154966
+ let stmt = getMinMessageTagNumberForRawIdStatements.get(db);
154967
+ if (!stmt) {
154968
+ stmt = db.prepare("SELECT MIN(tag_number) AS m FROM tags WHERE session_id = ? AND message_id >= ? AND message_id < ?");
154969
+ getMinMessageTagNumberForRawIdStatements.set(db, stmt);
154970
+ }
154971
+ const row = stmt.get(sessionId, `${rawId}:`, `${rawId};`);
154972
+ return isMinTagNumberRow(row) && typeof row.m === "number" ? row.m : null;
154973
+ }
154974
+ function deriveTagLoadFloor(db, sessionId, rawIds) {
154975
+ let min = Number.POSITIVE_INFINITY;
154976
+ let probes = 0;
154977
+ let hits = 0;
154978
+ let skippedBeforeFirstHit = 0;
154979
+ for (const rawId of rawIds) {
154980
+ if (typeof rawId !== "string" || rawId.length === 0)
154981
+ continue;
154982
+ if (probes >= TAGGER_FLOOR_MAX_PROBES)
154983
+ break;
154984
+ probes++;
154985
+ const m = getMinMessageTagNumberForRawId(db, sessionId, rawId);
154986
+ if (m === null) {
154987
+ if (hits === 0)
154988
+ skippedBeforeFirstHit++;
154989
+ continue;
154990
+ }
154991
+ if (m < min)
154992
+ min = m;
154993
+ if (++hits >= TAGGER_FLOOR_SCAN_MESSAGES)
154994
+ break;
154995
+ }
154996
+ if (!Number.isFinite(min))
154997
+ return 0;
154998
+ const margin = TAGGER_FLOOR_SAFETY_MARGIN + skippedBeforeFirstHit * TAGGER_FLOOR_PER_SKIP_MARGIN;
154999
+ return Math.max(0, min - margin);
155000
+ }
154831
155001
  function getTagsBySession(db, sessionId) {
154832
155002
  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
155003
  return rows.map(toTagEntry);
@@ -154966,7 +155136,7 @@ function deleteToolTagsByOwner(db, sessionId, ownerMsgId) {
154966
155136
  const result = getDeleteToolTagsByOwnerStatement(db).run(sessionId, ownerMsgId);
154967
155137
  return result.changes ?? 0;
154968
155138
  }
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;
155139
+ 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
155140
  var init_storage_tags = __esm(() => {
154971
155141
  insertTagStatements = new WeakMap;
154972
155142
  updateTagStatusStatements = new WeakMap;
@@ -154982,6 +155152,7 @@ var init_storage_tags = __esm(() => {
154982
155152
  updateTagTokenCountStatements = new WeakMap;
154983
155153
  updateTagInputTokenCountStatements = new WeakMap;
154984
155154
  getOwnerScopedToolTagNumbersStatements = new WeakMap;
155155
+ getMinMessageTagNumberForRawIdStatements = new WeakMap;
154985
155156
  getActiveTagsBySessionStatements = new WeakMap;
154986
155157
  getMaxDroppedTagNumberStatements = new WeakMap;
154987
155158
  getToolTagNumberByOwnerStatements = new WeakMap;
@@ -156218,6 +156389,58 @@ var init_safe_notification_target = __esm(() => {
156218
156389
  DEFAULT_TITLE_RE = /^(New session - |Child session - )\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
156219
156390
  });
156220
156391
 
156392
+ // src/shared/rpc-notifications.ts
156393
+ var exports_rpc_notifications = {};
156394
+ __export(exports_rpc_notifications, {
156395
+ pushNotification: () => pushNotification,
156396
+ isTuiConnected: () => isTuiConnected,
156397
+ drainNotifications: () => drainNotifications
156398
+ });
156399
+ function pushNotification(type, payload, sessionId) {
156400
+ queue.push({ id: nextNotificationId++, type, payload, sessionId });
156401
+ if (queue.length > 100) {
156402
+ const newestPerSession = new Map;
156403
+ for (const n of queue) {
156404
+ const prev = newestPerSession.get(n.sessionId);
156405
+ if (prev === undefined || n.id > prev) {
156406
+ newestPerSession.set(n.sessionId, n.id);
156407
+ }
156408
+ }
156409
+ const mustKeep = new Set(newestPerSession.values());
156410
+ const byNewest = [...queue].sort((a, b) => b.id - a.id);
156411
+ const kept = [];
156412
+ for (const n of byNewest) {
156413
+ if (kept.length < 50 || mustKeep.has(n.id))
156414
+ kept.push(n);
156415
+ }
156416
+ queue = kept.sort((a, b) => a.id - b.id);
156417
+ }
156418
+ }
156419
+ function drainNotifications(lastReceivedId = 0, sessionId) {
156420
+ const now = Date.now();
156421
+ lastDrainAtAny = now;
156422
+ if (sessionId !== undefined)
156423
+ lastDrainAtBySession.set(sessionId, now);
156424
+ const matchesClient = (notification) => sessionId === undefined || notification.sessionId === undefined || notification.sessionId === sessionId;
156425
+ if (lastReceivedId > 0) {
156426
+ queue = queue.filter((notification) => !(notification.id <= lastReceivedId && matchesClient(notification)));
156427
+ }
156428
+ return queue.filter((notification) => notification.id > lastReceivedId && matchesClient(notification));
156429
+ }
156430
+ function isTuiConnected(sessionId) {
156431
+ const now = Date.now();
156432
+ if (sessionId !== undefined) {
156433
+ const at = lastDrainAtBySession.get(sessionId) ?? 0;
156434
+ return at > 0 && now - at < TUI_CONNECTED_WINDOW_MS;
156435
+ }
156436
+ return lastDrainAtAny > 0 && now - lastDrainAtAny < TUI_CONNECTED_WINDOW_MS;
156437
+ }
156438
+ var queue, nextNotificationId = 1, lastDrainAtBySession, lastDrainAtAny = 0, TUI_CONNECTED_WINDOW_MS = 3000;
156439
+ var init_rpc_notifications = __esm(() => {
156440
+ queue = [];
156441
+ lastDrainAtBySession = new Map;
156442
+ });
156443
+
156221
156444
  // src/plugin/conflict-warning-hook.ts
156222
156445
  var exports_conflict_warning_hook = {};
156223
156446
  __export(exports_conflict_warning_hook, {
@@ -156552,6 +156775,9 @@ async function sendStartupAnnouncement(client, directory, version2, features, fo
156552
156775
  if (!sessionId) {
156553
156776
  return;
156554
156777
  }
156778
+ const { isTuiConnected: isTuiConnected2 } = await Promise.resolve().then(() => (init_rpc_notifications(), exports_rpc_notifications));
156779
+ if (isTuiConnected2(sessionId) || isTuiConnected2())
156780
+ return;
156555
156781
  if (await waitForSafeNotificationTarget(client, sessionId) === "skip")
156556
156782
  return;
156557
156783
  const bullets = features.map((line) => ` • ${line}`).join(`
@@ -164531,6 +164757,20 @@ function getDistinctStoredModelIds(db, projectPath) {
164531
164757
  const rows = getDistinctStoredModelIdsStatement(db).all(projectPath);
164532
164758
  return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
164533
164759
  }
164760
+ function getMemoryEmbedCoverage(db, projectPath, modelId) {
164761
+ const row = db.prepare(`SELECT
164762
+ COUNT(*) AS total,
164763
+ SUM(CASE WHEN EXISTS (
164764
+ SELECT 1 FROM memory_embeddings e
164765
+ WHERE e.memory_id = m.id AND e.model_id = ?
164766
+ ) THEN 1 ELSE 0 END) AS embedded
164767
+ FROM memories m
164768
+ WHERE m.project_path = ? AND m.status = 'active'`).get(modelId, projectPath);
164769
+ return {
164770
+ total: typeof row?.total === "number" ? row.total : 0,
164771
+ embedded: typeof row?.embedded === "number" ? row.embedded : 0
164772
+ };
164773
+ }
164534
164774
  var saveEmbeddingStatements, loadAllEmbeddingsStatements, deleteEmbeddingStatements, getStoredModelIdStatements, clearAllEmbeddingsStatements, getDistinctStoredModelIdsStatements;
164535
164775
  var init_storage_memory_embeddings = __esm(() => {
164536
164776
  saveEmbeddingStatements = new WeakMap;
@@ -165333,6 +165573,16 @@ function buildCanonicalChunkTextFromFts(db, sessionId, startOrdinal, endOrdinal)
165333
165573
  return lines.join(`
165334
165574
  `);
165335
165575
  }
165576
+ function buildCompartmentSummaryFallbackText(db, compartmentId) {
165577
+ const row = db.prepare("SELECT title, p1, content FROM compartments WHERE id = ?").get(compartmentId);
165578
+ if (!row)
165579
+ return "";
165580
+ const title = typeof row.title === "string" ? row.title.trim() : "";
165581
+ const p1 = typeof row.p1 === "string" ? row.p1.trim() : "";
165582
+ const body = p1.length > 0 ? p1 : typeof row.content === "string" ? row.content.trim() : "";
165583
+ return [title, body].filter((s) => s.length > 0).join(`
165584
+ `);
165585
+ }
165336
165586
  function canonicalizeInMemoryChunkTextForEmbedding(chunkText, startOrdinal, endOrdinal) {
165337
165587
  const lines = [];
165338
165588
  for (const rawLine of chunkText.split(/\r?\n/)) {
@@ -165575,6 +165825,28 @@ function countUnembeddedSessionCompartments(db, projectPath, sessionId, modelId)
165575
165825
  )`).get(projectPath, sessionId, projectPath, modelId);
165576
165826
  return typeof row?.n === "number" ? row.n : 0;
165577
165827
  }
165828
+ function countSessionCompartmentEmbedCoverage(db, projectPath, sessionId, modelId) {
165829
+ const row = db.prepare(`SELECT
165830
+ COUNT(*) AS total,
165831
+ SUM(CASE WHEN EXISTS (
165832
+ SELECT 1 FROM compartment_chunk_embeddings e
165833
+ WHERE e.compartment_id = c.id
165834
+ AND e.project_path = ?
165835
+ AND e.model_id = ?
165836
+ ) THEN 1 ELSE 0 END) AS embedded
165837
+ FROM compartments c
165838
+ JOIN session_projects sp
165839
+ ON sp.session_id = c.session_id
165840
+ AND sp.harness = c.harness
165841
+ AND sp.project_path = ?
165842
+ WHERE c.session_id = ?
165843
+ AND c.start_message IS NOT NULL
165844
+ AND c.end_message IS NOT NULL`).get(projectPath, modelId, projectPath, sessionId);
165845
+ return {
165846
+ total: typeof row?.total === "number" ? row.total : 0,
165847
+ embedded: typeof row?.embedded === "number" ? row.embedded : 0
165848
+ };
165849
+ }
165578
165850
  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
165851
  var init_compartment_chunk_embedding = __esm(() => {
165580
165852
  init_read_session_formatting();
@@ -165623,366 +165895,16 @@ function getEmbeddingProviderIdentity(config2) {
165623
165895
  endpoint: normalizeEndpoint2(config2.endpoint),
165624
165896
  apiKeyPresent: Boolean(config2.api_key?.trim()),
165625
165897
  inputType: config2.input_type?.trim() || ""
165626
- } : {
165627
- provider: "local",
165628
- model: config2.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
165629
- endpoint: "",
165630
- apiKeyPresent: false
165631
- };
165898
+ } : null;
165899
+ if (!identityInput) {
165900
+ return "embedding-provider:off";
165901
+ }
165632
165902
  return `embedding-provider:${computeNormalizedHash(JSON.stringify(identityInput))}`;
165633
165903
  }
165634
165904
  var init_embedding_identity = __esm(() => {
165635
- init_magic_context();
165636
165905
  init_normalize_hash();
165637
165906
  });
165638
165907
 
165639
- // src/features/magic-context/memory/embedding-local.ts
165640
- import { mkdirSync as mkdirSync3 } from "node:fs";
165641
- import { open, stat, unlink, writeFile } from "node:fs/promises";
165642
- import { dirname as dirname4, join as join13 } from "node:path";
165643
- import { pathToFileURL } from "node:url";
165644
- async function acquireModelLoadLock(lockPath) {
165645
- const waitStart = Date.now();
165646
- while (true) {
165647
- try {
165648
- const handle = await open(lockPath, "wx");
165649
- try {
165650
- await handle.writeFile(`pid=${process.pid} started=${Date.now()}
165651
- `);
165652
- } catch {}
165653
- await handle.close();
165654
- return async () => {
165655
- try {
165656
- await unlink(lockPath);
165657
- } catch {}
165658
- };
165659
- } catch (error51) {
165660
- const code = error51.code;
165661
- if (code !== "EEXIST" && code !== "EPERM") {
165662
- throw error51;
165663
- }
165664
- try {
165665
- const info = await stat(lockPath);
165666
- if (Date.now() - info.mtimeMs > STALE_LOCK_MS) {
165667
- log(`[magic-context] embedding-load lock stale (>${STALE_LOCK_MS}ms), taking over`);
165668
- try {
165669
- await unlink(lockPath);
165670
- } catch {}
165671
- continue;
165672
- }
165673
- } catch {
165674
- continue;
165675
- }
165676
- if (Date.now() - waitStart > MAX_LOCK_WAIT_MS) {
165677
- throw new Error(`[magic-context] embedding-load lock wait exceeded ${MAX_LOCK_WAIT_MS}ms; another process is still loading the model. Skipping this init attempt to avoid an unsynchronized native load.`);
165678
- }
165679
- await new Promise((resolve6) => setTimeout(resolve6, LOCK_POLL_MS));
165680
- }
165681
- }
165682
- }
165683
- function startLockHeartbeat(lockPath) {
165684
- const HEARTBEAT_MS = Math.floor(STALE_LOCK_MS / 3);
165685
- const timer = setInterval(() => {
165686
- writeFile(lockPath, `pid=${process.pid} alive=${Date.now()}
165687
- `).catch(() => {});
165688
- }, HEARTBEAT_MS);
165689
- timer.unref?.();
165690
- return () => clearInterval(timer);
165691
- }
165692
- async function injectWasmOrtForElectron() {
165693
- if (typeof process === "undefined" || !process.versions?.electron) {
165694
- return false;
165695
- }
165696
- try {
165697
- const ortWebSpec = `onnxruntime-${"web"}`;
165698
- const ortWeb = await import(ortWebSpec);
165699
- try {
165700
- const { createRequire: createRequireFn } = await import("node:module");
165701
- const requireFn = createRequireFn(import.meta.url);
165702
- const pkgPath = requireFn.resolve("onnxruntime-web/package.json");
165703
- const distDir = join13(dirname4(pkgPath), "dist");
165704
- const wasmPathsPrefix = `${pathToFileURL(distDir).href}/`;
165705
- if (ortWeb.env?.wasm) {
165706
- ortWeb.env.wasm.wasmPaths = wasmPathsPrefix;
165707
- }
165708
- } catch (pathError) {
165709
- log("[magic-context] could not resolve local onnxruntime-web/dist, falling back to default WASM paths:", pathError instanceof Error ? pathError.message : String(pathError));
165710
- }
165711
- globalThis[Symbol.for("onnxruntime")] = ortWeb;
165712
- log("[magic-context] Electron detected — using onnxruntime-web (WASM) for embeddings (bypasses onnxruntime-node native load)");
165713
- return true;
165714
- } catch (error51) {
165715
- log("[magic-context] failed to inject onnxruntime-web for Electron — letting transformers fall back to native:", error51 instanceof Error ? error51.message : String(error51));
165716
- return false;
165717
- }
165718
- }
165719
- async function withQuietConsole(fn) {
165720
- const origWarn = console.warn;
165721
- const origError = console.error;
165722
- const redirect = (...args) => {
165723
- const message = args.map((a) => typeof a === "string" ? a : String(a)).join(" ");
165724
- log(`[transformers] ${message}`);
165725
- };
165726
- console.warn = redirect;
165727
- console.error = redirect;
165728
- try {
165729
- return await fn();
165730
- } finally {
165731
- console.warn = origWarn;
165732
- console.error = origError;
165733
- }
165734
- }
165735
- function isTransientLoadError(error51) {
165736
- const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
165737
- if (!message)
165738
- return false;
165739
- const lower = message.toLowerCase();
165740
- return lower.includes("protobuf parsing failed") || lower.includes("unable to get model file path or buffer") || lower.includes("ebusy") || lower.includes("resource busy") || lower.includes("resource temporarily unavailable");
165741
- }
165742
- function isArrayLikeNumber(value) {
165743
- if (typeof value !== "object" || value === null || !("length" in value)) {
165744
- return false;
165745
- }
165746
- const arr = value;
165747
- if (typeof arr.length !== "number") {
165748
- return false;
165749
- }
165750
- return arr.length === 0 || typeof arr[0] === "number";
165751
- }
165752
- function toFloat32Array3(values) {
165753
- return values instanceof Float32Array ? new Float32Array(values) : Float32Array.from(Array.from(values));
165754
- }
165755
- function extractBatchEmbeddings(result, expectedCount) {
165756
- const { data } = result;
165757
- if (Array.isArray(data) && data.length === expectedCount && data.every((entry) => typeof entry !== "number" && isArrayLikeNumber(entry))) {
165758
- return data.map((entry) => toFloat32Array3(entry));
165759
- }
165760
- if (!isArrayLikeNumber(data)) {
165761
- log("[magic-context] embedding batch returned unexpected data shape");
165762
- return Array.from({ length: expectedCount }, () => null);
165763
- }
165764
- const flatData = toFloat32Array3(data);
165765
- const dimension = result.dims?.at(-1) ?? flatData.length / expectedCount;
165766
- if (!Number.isInteger(dimension) || dimension <= 0 || flatData.length !== expectedCount * dimension) {
165767
- log("[magic-context] embedding batch returned invalid dimensions");
165768
- return Array.from({ length: expectedCount }, () => null);
165769
- }
165770
- const embeddings = [];
165771
- for (let index = 0;index < expectedCount; index++) {
165772
- embeddings.push(flatData.slice(index * dimension, (index + 1) * dimension));
165773
- }
165774
- return embeddings;
165775
- }
165776
-
165777
- class LocalEmbeddingProvider {
165778
- modelId;
165779
- maxInputTokens;
165780
- model;
165781
- pipeline = null;
165782
- initPromise = null;
165783
- inFlight = 0;
165784
- disposing = false;
165785
- disposePromise = null;
165786
- inFlightWaiters = [];
165787
- constructor(model = DEFAULT_LOCAL_EMBEDDING_MODEL, maxInputTokens = 512) {
165788
- this.model = model;
165789
- this.maxInputTokens = maxInputTokens;
165790
- this.modelId = getEmbeddingProviderIdentity({ provider: "local", model });
165791
- }
165792
- async initialize() {
165793
- if (this.disposing) {
165794
- return false;
165795
- }
165796
- if (this.pipeline) {
165797
- return true;
165798
- }
165799
- if (this.initPromise) {
165800
- await this.initPromise;
165801
- return this.pipeline !== null;
165802
- }
165803
- this.initPromise = (async () => {
165804
- try {
165805
- if (this.disposing) {
165806
- return;
165807
- }
165808
- await injectWasmOrtForElectron();
165809
- const transformersSpec = "@huggingface/transformers";
165810
- const transformersModule = await import(transformersSpec);
165811
- const env = transformersModule.env;
165812
- const LogLevel = transformersModule.LogLevel;
165813
- if (LogLevel && "ERROR" in LogLevel) {
165814
- env.logLevel = LogLevel.ERROR;
165815
- }
165816
- const modelCacheDir = join13(getMagicContextStorageDir(), "models");
165817
- try {
165818
- mkdirSync3(modelCacheDir, { recursive: true });
165819
- env.cacheDir = modelCacheDir;
165820
- } catch {
165821
- log("[magic-context] could not create model cache dir, using library default");
165822
- }
165823
- const createPipeline = transformersModule.pipeline;
165824
- const lockPath = join13(modelCacheDir, ".load.lock");
165825
- const releaseLock = await acquireModelLoadLock(lockPath);
165826
- const stopHeartbeat = startLockHeartbeat(lockPath);
165827
- try {
165828
- const MAX_ATTEMPTS = 3;
165829
- let lastError;
165830
- for (let attempt = 1;attempt <= MAX_ATTEMPTS; attempt++) {
165831
- try {
165832
- const pipeline = await withQuietConsole(() => createPipeline("feature-extraction", this.model, {
165833
- dtype: "fp32"
165834
- }));
165835
- if (this.disposing) {
165836
- await pipeline.dispose?.();
165837
- this.pipeline = null;
165838
- } else {
165839
- this.pipeline = pipeline;
165840
- }
165841
- lastError = undefined;
165842
- break;
165843
- } catch (error51) {
165844
- lastError = error51;
165845
- if (!isTransientLoadError(error51) || attempt === MAX_ATTEMPTS) {
165846
- break;
165847
- }
165848
- const delayMs = 300 * attempt + Math.floor(Math.random() * 200);
165849
- log(`[magic-context] embedding model load attempt ${attempt}/${MAX_ATTEMPTS} failed transiently, retrying in ${delayMs}ms`);
165850
- await new Promise((resolve6) => setTimeout(resolve6, delayMs));
165851
- }
165852
- }
165853
- if (this.pipeline) {
165854
- log(`[magic-context] embedding model loaded: ${this.model}`);
165855
- } else if (this.disposing) {
165856
- return;
165857
- } else {
165858
- throw lastError ?? new Error("unknown embedding load failure");
165859
- }
165860
- } finally {
165861
- stopHeartbeat();
165862
- await releaseLock();
165863
- }
165864
- } catch (error51) {
165865
- log("[magic-context] embedding model failed to load:", error51);
165866
- this.pipeline = null;
165867
- } finally {
165868
- this.initPromise = null;
165869
- }
165870
- })();
165871
- await this.initPromise;
165872
- return this.pipeline !== null;
165873
- }
165874
- waitForInFlightToDrain() {
165875
- if (this.inFlight === 0) {
165876
- return Promise.resolve();
165877
- }
165878
- return new Promise((resolve6) => {
165879
- this.inFlightWaiters.push(resolve6);
165880
- });
165881
- }
165882
- finishInFlight() {
165883
- this.inFlight = Math.max(0, this.inFlight - 1);
165884
- if (this.inFlight !== 0)
165885
- return;
165886
- const waiters = this.inFlightWaiters.splice(0);
165887
- for (const waiter of waiters) {
165888
- waiter();
165889
- }
165890
- }
165891
- async embed(text, signal) {
165892
- if (signal?.aborted)
165893
- return null;
165894
- if (this.disposing)
165895
- return null;
165896
- this.inFlight += 1;
165897
- try {
165898
- if (!await this.initialize()) {
165899
- return null;
165900
- }
165901
- const pipeline = this.pipeline;
165902
- if (!pipeline) {
165903
- return null;
165904
- }
165905
- const result = await withQuietConsole(() => pipeline(text, {
165906
- pooling: "mean",
165907
- normalize: true
165908
- }));
165909
- return extractBatchEmbeddings(result, 1)[0] ?? null;
165910
- } catch (error51) {
165911
- log("[magic-context] embedding failed:", error51);
165912
- return null;
165913
- } finally {
165914
- this.finishInFlight();
165915
- }
165916
- }
165917
- async embedBatch(texts, signal) {
165918
- if (texts.length === 0) {
165919
- return [];
165920
- }
165921
- if (signal?.aborted) {
165922
- return Array.from({ length: texts.length }, () => null);
165923
- }
165924
- if (this.disposing) {
165925
- return Array.from({ length: texts.length }, () => null);
165926
- }
165927
- this.inFlight += 1;
165928
- try {
165929
- if (!await this.initialize()) {
165930
- return Array.from({ length: texts.length }, () => null);
165931
- }
165932
- const pipeline = this.pipeline;
165933
- if (!pipeline) {
165934
- return Array.from({ length: texts.length }, () => null);
165935
- }
165936
- const result = await withQuietConsole(() => pipeline(texts, {
165937
- pooling: "mean",
165938
- normalize: true
165939
- }));
165940
- return extractBatchEmbeddings(result, texts.length);
165941
- } catch (error51) {
165942
- log("[magic-context] embedding batch failed:", error51);
165943
- return Array.from({ length: texts.length }, () => null);
165944
- } finally {
165945
- this.finishInFlight();
165946
- }
165947
- }
165948
- async dispose() {
165949
- if (this.disposePromise) {
165950
- return this.disposePromise;
165951
- }
165952
- this.disposing = true;
165953
- this.disposePromise = (async () => {
165954
- if (this.initPromise) {
165955
- await this.initPromise;
165956
- }
165957
- await this.waitForInFlightToDrain();
165958
- const pipelineToDispose = this.pipeline;
165959
- this.pipeline = null;
165960
- this.initPromise = null;
165961
- if (!pipelineToDispose) {
165962
- return;
165963
- }
165964
- try {
165965
- await pipelineToDispose.dispose?.();
165966
- } catch (error51) {
165967
- log("[magic-context] embedding model dispose failed:", error51);
165968
- }
165969
- })();
165970
- return this.disposePromise;
165971
- }
165972
- isLoaded() {
165973
- return this.pipeline !== null;
165974
- }
165975
- }
165976
- var LOCK_POLL_MS = 150, STALE_LOCK_MS, MAX_LOCK_WAIT_MS;
165977
- var init_embedding_local = __esm(() => {
165978
- init_magic_context();
165979
- init_data_path();
165980
- init_logger();
165981
- init_embedding_identity();
165982
- STALE_LOCK_MS = 3 * 60000;
165983
- MAX_LOCK_WAIT_MS = 5 * 60000;
165984
- });
165985
-
165986
165908
  // src/features/magic-context/memory/embedding-ssrf.ts
165987
165909
  function isLinkLocalIpv4(host) {
165988
165910
  return /^169\.254\.\d{1,3}\.\d{1,3}$/.test(host);
@@ -166058,6 +165980,7 @@ class OpenAICompatibleEmbeddingProvider {
166058
165980
  model;
166059
165981
  apiKey;
166060
165982
  inputType;
165983
+ queryInputType;
166061
165984
  truncate;
166062
165985
  initialized = false;
166063
165986
  failureTimes = [];
@@ -166070,6 +165993,7 @@ class OpenAICompatibleEmbeddingProvider {
166070
165993
  this.model = options.model?.trim() ?? "";
166071
165994
  this.apiKey = options.apiKey?.trim() ?? "";
166072
165995
  this.inputType = options.inputType?.trim() ?? "";
165996
+ this.queryInputType = options.queryInputType?.trim() ?? "";
166073
165997
  this.truncate = options.truncate?.trim() ?? "";
166074
165998
  this.maxInputTokens = typeof options.maxInputTokens === "number" && Number.isFinite(options.maxInputTokens) ? Math.max(1, Math.floor(options.maxInputTokens)) : 512;
166075
165999
  this.modelId = getEmbeddingProviderIdentity({
@@ -166097,11 +166021,17 @@ class OpenAICompatibleEmbeddingProvider {
166097
166021
  this.initialized = true;
166098
166022
  return true;
166099
166023
  }
166100
- async embed(text, signal) {
166101
- const [embedding] = await this.embedBatch([text], signal);
166024
+ resolveInputTypeForPurpose(purpose = "passage") {
166025
+ if (purpose === "query") {
166026
+ return this.queryInputType || this.inputType;
166027
+ }
166028
+ return this.inputType;
166029
+ }
166030
+ async embed(text, signal, purpose) {
166031
+ const [embedding] = await this.embedBatch([text], signal, purpose);
166102
166032
  return embedding ?? null;
166103
166033
  }
166104
- async embedBatch(texts, signal) {
166034
+ async embedBatch(texts, signal, purpose) {
166105
166035
  if (texts.length === 0) {
166106
166036
  return [];
166107
166037
  }
@@ -166127,6 +166057,7 @@ class OpenAICompatibleEmbeddingProvider {
166127
166057
  if (signal) {
166128
166058
  signal.addEventListener("abort", onOuterAbort, { once: true });
166129
166059
  }
166060
+ const inputTypeForRequest = this.resolveInputTypeForPurpose(purpose);
166130
166061
  const response = await fetch(`${this.endpoint}/embeddings`, {
166131
166062
  method: "POST",
166132
166063
  headers: {
@@ -166136,7 +166067,7 @@ class OpenAICompatibleEmbeddingProvider {
166136
166067
  body: JSON.stringify({
166137
166068
  model: this.model,
166138
166069
  input: texts,
166139
- ...this.inputType ? { input_type: this.inputType } : {},
166070
+ ...inputTypeForRequest ? { input_type: inputTypeForRequest } : {},
166140
166071
  ...this.truncate ? { truncate: this.truncate } : {}
166141
166072
  }),
166142
166073
  redirect: "error",
@@ -166387,6 +166318,121 @@ var init_storage_git_commit_embeddings = __esm(() => {
166387
166318
  distinctModelIdStatements = new WeakMap;
166388
166319
  });
166389
166320
 
166321
+ // src/features/magic-context/git-commits/storage-git-commits.ts
166322
+ function getInsertStatement(db) {
166323
+ let stmt = insertStatements.get(db);
166324
+ if (!stmt) {
166325
+ stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
166326
+ VALUES (?, ?, ?, ?, ?, ?, ?)
166327
+ ON CONFLICT(sha) DO UPDATE SET
166328
+ project_path = excluded.project_path,
166329
+ short_sha = excluded.short_sha,
166330
+ message = excluded.message,
166331
+ author = excluded.author,
166332
+ committed_at = excluded.committed_at,
166333
+ indexed_at = excluded.indexed_at
166334
+ WHERE git_commits.message != excluded.message`);
166335
+ insertStatements.set(db, stmt);
166336
+ }
166337
+ return stmt;
166338
+ }
166339
+ function getExistingShasStatement(db) {
166340
+ let stmt = existingShasStatements.get(db);
166341
+ if (!stmt) {
166342
+ stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
166343
+ existingShasStatements.set(db, stmt);
166344
+ }
166345
+ return stmt;
166346
+ }
166347
+ function getProjectCountStatement(db) {
166348
+ let stmt = projectCountStatements.get(db);
166349
+ if (!stmt) {
166350
+ stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
166351
+ projectCountStatements.set(db, stmt);
166352
+ }
166353
+ return stmt;
166354
+ }
166355
+ function getLatestCommitTimeStatement(db) {
166356
+ let stmt = latestCommitTimeStatements.get(db);
166357
+ if (!stmt) {
166358
+ stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
166359
+ latestCommitTimeStatements.set(db, stmt);
166360
+ }
166361
+ return stmt;
166362
+ }
166363
+ function getEvictOverflowStatement(db) {
166364
+ let stmt = evictOverflowStatements.get(db);
166365
+ if (!stmt) {
166366
+ stmt = db.prepare(`DELETE FROM git_commits
166367
+ WHERE rowid IN (
166368
+ SELECT rowid FROM git_commits
166369
+ WHERE project_path = ?
166370
+ ORDER BY committed_at DESC, sha DESC
166371
+ LIMIT -1 OFFSET ?
166372
+ )`);
166373
+ evictOverflowStatements.set(db, stmt);
166374
+ }
166375
+ return stmt;
166376
+ }
166377
+ function upsertCommits(db, projectPath, commits) {
166378
+ if (commits.length === 0)
166379
+ return { inserted: 0, updated: 0 };
166380
+ const existing = new Set;
166381
+ for (const row of getExistingShasStatement(db).all(projectPath)) {
166382
+ existing.add(row.sha);
166383
+ }
166384
+ let inserted = 0;
166385
+ let updated = 0;
166386
+ const now = Date.now();
166387
+ const insertStmt = getInsertStatement(db);
166388
+ db.transaction(() => {
166389
+ for (const commit of commits) {
166390
+ const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
166391
+ if (result.changes > 0) {
166392
+ if (existing.has(commit.sha)) {
166393
+ updated++;
166394
+ } else {
166395
+ inserted++;
166396
+ existing.add(commit.sha);
166397
+ }
166398
+ }
166399
+ }
166400
+ })();
166401
+ return { inserted, updated };
166402
+ }
166403
+ function getCommitCount(db, projectPath) {
166404
+ const row = getProjectCountStatement(db).get(projectPath);
166405
+ return row?.count ?? 0;
166406
+ }
166407
+ function getLatestIndexedCommitTimeMs(db, projectPath) {
166408
+ const row = getLatestCommitTimeStatement(db).get(projectPath);
166409
+ return row?.latest ?? null;
166410
+ }
166411
+ function enforceProjectCap(db, projectPath, maxCommits) {
166412
+ if (maxCommits <= 0)
166413
+ return 0;
166414
+ const count = getCommitCount(db, projectPath);
166415
+ if (count <= maxCommits)
166416
+ return 0;
166417
+ getEvictOverflowStatement(db).run(projectPath, maxCommits);
166418
+ const after = getCommitCount(db, projectPath);
166419
+ const evicted = Math.max(0, count - after);
166420
+ if (evicted > 0) {
166421
+ log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
166422
+ }
166423
+ return evicted;
166424
+ }
166425
+ var insertStatements, existingShasStatements, projectCountStatements, evictStatements, evictOverflowStatements, latestCommitTimeStatements;
166426
+ var init_storage_git_commits = __esm(() => {
166427
+ init_logger();
166428
+ insertStatements = new WeakMap;
166429
+ existingShasStatements = new WeakMap;
166430
+ projectCountStatements = new WeakMap;
166431
+ evictStatements = new WeakMap;
166432
+ evictOverflowStatements = new WeakMap;
166433
+ latestCommitTimeStatements = new WeakMap;
166434
+ });
166435
+
166390
166436
  // src/features/magic-context/git-commits/sweep-coordinator.ts
166391
166437
  function runImmediate2(db, body) {
166392
166438
  db.exec("BEGIN IMMEDIATE");
@@ -166586,18 +166632,13 @@ var init_session_project_storage = __esm(() => {
166586
166632
  // src/features/magic-context/project-embedding-registry.ts
166587
166633
  import { createHash as createHash9, randomUUID } from "node:crypto";
166588
166634
  function resolveEmbeddingConfig(config2) {
166589
- if (!config2 || config2.provider === "local") {
166590
- return {
166591
- provider: "local",
166592
- model: config2?.model?.trim() || DEFAULT_LOCAL_EMBEDDING_MODEL,
166593
- ...config2?.max_input_tokens ? {
166594
- max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
166595
- } : {}
166596
- };
166635
+ if (!config2 || config2.provider === "off") {
166636
+ return { provider: "off" };
166597
166637
  }
166598
166638
  if (config2.provider === "openai-compatible") {
166599
166639
  const apiKey = config2.api_key?.trim();
166600
166640
  const inputType = config2.input_type?.trim();
166641
+ const queryInputType = config2.query_input_type?.trim();
166601
166642
  const truncate = config2.truncate?.trim();
166602
166643
  return {
166603
166644
  provider: "openai-compatible",
@@ -166605,6 +166646,7 @@ function resolveEmbeddingConfig(config2) {
166605
166646
  endpoint: config2.endpoint.trim(),
166606
166647
  ...apiKey ? { api_key: apiKey } : {},
166607
166648
  ...inputType ? { input_type: inputType } : {},
166649
+ ...queryInputType ? { query_input_type: queryInputType } : {},
166608
166650
  ...truncate ? { truncate } : {},
166609
166651
  ...config2.max_input_tokens ? {
166610
166652
  max_input_tokens: normalizeCompartmentChunkMaxInputTokens(config2.max_input_tokens)
@@ -166626,11 +166668,12 @@ function createProvider(config2) {
166626
166668
  model: config2.model,
166627
166669
  apiKey: config2.api_key,
166628
166670
  inputType: config2.input_type,
166671
+ queryInputType: config2.query_input_type,
166629
166672
  truncate: config2.truncate,
166630
166673
  maxInputTokens: config2.max_input_tokens
166631
166674
  });
166632
166675
  }
166633
- return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
166676
+ return null;
166634
166677
  }
166635
166678
  function stableStringify2(value) {
166636
166679
  if (Array.isArray(value)) {
@@ -166680,7 +166723,9 @@ function snapshotFor(registration) {
166680
166723
  enabled,
166681
166724
  gitCommitEnabled,
166682
166725
  modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
166683
- chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId
166726
+ chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId,
166727
+ model: registration.observationMode || !providerIsOn ? "off" : ("model" in registration.config) && registration.config.model.trim() ? registration.config.model.trim() : registration.modelId,
166728
+ provider: registration.observationMode || !providerIsOn ? "off" : registration.config.provider ?? "off"
166684
166729
  };
166685
166730
  }
166686
166731
  function disposeProvider(provider) {
@@ -166806,7 +166851,7 @@ function getOrCreateProjectProvider(registration) {
166806
166851
  registration.provider = provider;
166807
166852
  return provider;
166808
166853
  }
166809
- async function embedTextForProject(projectIdentity, text, signal) {
166854
+ async function embedTextForProject(projectIdentity, text, signal, purpose = "passage") {
166810
166855
  const registration = projectRegistrations.get(projectIdentity);
166811
166856
  if (!registration)
166812
166857
  return null;
@@ -166815,7 +166860,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
166815
166860
  const provider = getOrCreateProjectProvider(registration);
166816
166861
  if (!provider)
166817
166862
  return null;
166818
- const vector = await provider.embed(text, signal);
166863
+ const vector = await provider.embed(text, signal, purpose);
166819
166864
  if (!vector)
166820
166865
  return null;
166821
166866
  const current = projectRegistrations.get(projectIdentity);
@@ -166824,7 +166869,7 @@ async function embedTextForProject(projectIdentity, text, signal) {
166824
166869
  }
166825
166870
  return { vector, modelId, generation };
166826
166871
  }
166827
- async function embedBatchForProject(projectIdentity, texts, signal) {
166872
+ async function embedBatchForProject(projectIdentity, texts, signal, purpose = "passage") {
166828
166873
  if (texts.length === 0) {
166829
166874
  const registration2 = projectRegistrations.get(projectIdentity);
166830
166875
  if (!registration2 || registration2.observationMode)
@@ -166840,7 +166885,7 @@ async function embedBatchForProject(projectIdentity, texts, signal) {
166840
166885
  const provider = getOrCreateProjectProvider(registration);
166841
166886
  if (!provider)
166842
166887
  return null;
166843
- const vectors = await provider.embedBatch(texts, signal);
166888
+ const vectors = await provider.embedBatch(texts, signal, purpose);
166844
166889
  const current = projectRegistrations.get(projectIdentity);
166845
166890
  if (!current || current.generation !== generation || current.runtimeFingerprint !== runtimeFingerprint) {
166846
166891
  return null;
@@ -166891,12 +166936,13 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
166891
166936
  }
166892
166937
  async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates, signal) {
166893
166938
  const noWork = [];
166939
+ const failed = [];
166894
166940
  if (candidates.length === 0)
166895
- return { embedded: 0, noWork };
166941
+ return { embedded: 0, noWork, failed };
166896
166942
  const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
166897
166943
  const prepared = [];
166898
166944
  for (const candidate of candidates) {
166899
- const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage);
166945
+ const canonicalText = buildCanonicalChunkTextFromFts(db, candidate.sessionId, candidate.startMessage, candidate.endMessage) || buildCompartmentSummaryFallbackText(db, candidate.id);
166900
166946
  if (canonicalText.length === 0) {
166901
166947
  noWork.push(candidate.id);
166902
166948
  continue;
@@ -166909,7 +166955,7 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
166909
166955
  prepared.push({ candidate, windows });
166910
166956
  }
166911
166957
  if (prepared.length === 0)
166912
- return { embedded: 0, noWork };
166958
+ return { embedded: 0, noWork, failed };
166913
166959
  let embedded = 0;
166914
166960
  let i = 0;
166915
166961
  while (i < prepared.length) {
@@ -166926,35 +166972,60 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
166926
166972
  const texts = [];
166927
166973
  for (const item of slice)
166928
166974
  texts.push(...item.windows.map((w) => w.text));
166929
- try {
166930
- const result = await embedBatchForProject(projectIdentity, texts, signal);
166931
- if (!result)
166932
- continue;
166975
+ const persistedIds = new Set;
166976
+ for (let attempt = 0;attempt < EMBED_SLICE_RETRY_ATTEMPTS; attempt++) {
166933
166977
  if (signal?.aborted)
166934
166978
  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;
166979
+ let result = null;
166980
+ const attemptStart = Date.now();
166981
+ try {
166982
+ result = await embedBatchForProject(projectIdentity, texts, signal);
166983
+ } catch (error51) {
166984
+ log("[magic-context] failed to proactively embed compartment chunks:", error51);
166985
+ }
166986
+ if (signal?.aborted)
166987
+ break;
166988
+ if (result) {
166989
+ let offset = 0;
166990
+ for (const item of slice) {
166991
+ const vectors = result.vectors.slice(offset, offset + item.windows.length);
166992
+ offset += item.windows.length;
166993
+ if (persistedIds.has(item.candidate.id))
166994
+ continue;
166995
+ if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
166996
+ continue;
166997
+ }
166998
+ const rows = item.windows.map((window, index) => ({
166999
+ compartmentId: item.candidate.id,
167000
+ sessionId: item.candidate.sessionId,
167001
+ projectPath: projectIdentity,
167002
+ window,
167003
+ modelId,
167004
+ vector: vectors[index]
167005
+ }));
167006
+ replaceCompartmentChunkEmbeddings(db, rows);
167007
+ persistedIds.add(item.candidate.id);
166941
167008
  }
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
167009
  }
166953
- } catch (error51) {
166954
- log("[magic-context] failed to proactively embed compartment chunks:", error51);
167010
+ if (persistedIds.size === slice.length)
167011
+ break;
167012
+ if (persistedIds.size > 0)
167013
+ break;
167014
+ if (Date.now() - attemptStart >= EMBED_SLOW_FAILURE_NO_RETRY_MS)
167015
+ break;
167016
+ if (attempt < EMBED_SLICE_RETRY_ATTEMPTS - 1) {
167017
+ await new Promise((resolve6) => setTimeout(resolve6, EMBED_SLICE_RETRY_BASE_MS * 2 ** attempt));
167018
+ }
167019
+ }
167020
+ embedded += persistedIds.size;
167021
+ if (!signal?.aborted) {
167022
+ for (const item of slice) {
167023
+ if (!persistedIds.has(item.candidate.id))
167024
+ failed.push(item.candidate.id);
167025
+ }
166955
167026
  }
166956
167027
  }
166957
- return { embedded, noWork };
167028
+ return { embedded, noWork, failed };
166958
167029
  }
166959
167030
  async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, options) {
166960
167031
  const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
@@ -166977,9 +167048,11 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
166977
167048
  renewal.unref?.();
166978
167049
  const batchSize = Math.max(1, options?.batchSize ?? CHUNK_DRAIN_BATCH_SIZE);
166979
167050
  const skipIds = [];
167051
+ const failedIds = [];
166980
167052
  let embedded = 0;
166981
167053
  let aborted2 = false;
166982
- let providerStalled = false;
167054
+ let providerDown = false;
167055
+ let consecutiveFailedBatches = 0;
166983
167056
  try {
166984
167057
  options?.onProgress?.({ embedded, total });
166985
167058
  for (;; ) {
@@ -166987,15 +167060,26 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
166987
167060
  aborted2 = true;
166988
167061
  break;
166989
167062
  }
166990
- const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, skipIds);
167063
+ const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, [...skipIds, ...failedIds]);
166991
167064
  if (candidates.length === 0)
166992
167065
  break;
166993
- const { embedded: n, noWork } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
167066
+ const {
167067
+ embedded: n,
167068
+ noWork,
167069
+ failed
167070
+ } = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
166994
167071
  for (const id of noWork)
166995
167072
  skipIds.push(id);
167073
+ for (const id of failed)
167074
+ failedIds.push(id);
166996
167075
  if (n === 0 && noWork.length === 0) {
166997
- providerStalled = true;
166998
- break;
167076
+ consecutiveFailedBatches += 1;
167077
+ if (consecutiveFailedBatches >= MAX_CONSECUTIVE_FAILED_BATCHES) {
167078
+ providerDown = true;
167079
+ break;
167080
+ }
167081
+ } else {
167082
+ consecutiveFailedBatches = 0;
166999
167083
  }
167000
167084
  embedded += n;
167001
167085
  options?.onProgress?.({ embedded: Math.min(embedded, total), total });
@@ -167003,27 +167087,60 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
167003
167087
  }
167004
167088
  } finally {
167005
167089
  clearInterval(renewal);
167006
- releaseGitSweepLease(db, projectIdentity, holderId);
167090
+ try {
167091
+ releaseGitSweepLease(db, projectIdentity, holderId);
167092
+ } catch (error51) {
167093
+ log("[magic-context] embed drain: lease release failed (will TTL-expire):", error51);
167094
+ }
167007
167095
  }
167008
167096
  if (aborted2)
167009
- return { status: "aborted", embedded, total };
167010
- if (providerStalled) {
167097
+ return { status: "aborted", embedded, total, failed: failedIds.length };
167098
+ if (providerDown || failedIds.length > 0) {
167011
167099
  const remaining = Math.max(0, countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId) - skipIds.length);
167012
- if (remaining > 0)
167013
- return { status: "stalled", embedded, total, remaining };
167100
+ if (remaining > 0) {
167101
+ return { status: "stalled", embedded, total, remaining, failed: failedIds.length };
167102
+ }
167014
167103
  }
167015
- return { status: "done", embedded, total };
167104
+ return { status: "done", embedded, total, failed: failedIds.length };
167016
167105
  }
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;
167106
+ function getEmbeddingCoverageStatus(db, projectIdentity, sessionId) {
167107
+ const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
167108
+ if (!snapshot?.enabled || snapshot.chunkModelId === "off") {
167109
+ return {
167110
+ enabled: false,
167111
+ model: snapshot?.model ?? "off",
167112
+ provider: snapshot?.provider ?? "off",
167113
+ session: { embedded: 0, total: 0 },
167114
+ memories: { embedded: 0, total: 0 },
167115
+ commits: { embedded: 0, total: 0, gitEnabled: false }
167116
+ };
167117
+ }
167118
+ const session = countSessionCompartmentEmbedCoverage(db, projectIdentity, sessionId, snapshot.chunkModelId);
167119
+ const memories = getMemoryEmbedCoverage(db, projectIdentity, snapshot.modelId);
167120
+ const gitEnabled = snapshot.gitCommitEnabled;
167121
+ const commits = gitEnabled ? {
167122
+ embedded: countEmbeddedCommits(db, projectIdentity),
167123
+ total: getCommitCount(db, projectIdentity),
167124
+ gitEnabled: true
167125
+ } : { embedded: 0, total: 0, gitEnabled: false };
167126
+ return {
167127
+ enabled: true,
167128
+ model: snapshot.model,
167129
+ provider: snapshot.provider,
167130
+ session,
167131
+ memories,
167132
+ commits
167133
+ };
167134
+ }
167135
+ 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
167136
  var init_project_embedding_registry = __esm(() => {
167019
- init_magic_context();
167020
167137
  init_logger();
167021
167138
  init_compartment_chunk_embedding();
167022
167139
  init_storage_git_commit_embeddings();
167140
+ init_storage_git_commits();
167023
167141
  init_sweep_coordinator();
167024
167142
  init_embedding_cache();
167025
167143
  init_embedding_identity();
167026
- init_embedding_local();
167027
167144
  init_embedding_openai();
167028
167145
  init_storage_memory_embeddings();
167029
167146
  init_session_project_storage();
@@ -167044,11 +167161,12 @@ function createProvider2(config2) {
167044
167161
  model: config2.model,
167045
167162
  apiKey: config2.api_key,
167046
167163
  inputType: config2.input_type,
167164
+ queryInputType: config2.query_input_type,
167047
167165
  truncate: config2.truncate,
167048
167166
  maxInputTokens: config2.max_input_tokens
167049
167167
  });
167050
167168
  }
167051
- return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
167169
+ return null;
167052
167170
  }
167053
167171
  function getOrCreateProvider() {
167054
167172
  if (provider) {
@@ -167072,17 +167190,14 @@ async function embedText(text, signal) {
167072
167190
  }
167073
167191
  var DEFAULT_EMBEDDING_CONFIG, embeddingConfig, provider = null, loadUnembeddedMemoriesStatements2, SWEEP_MAX_WALL_CLOCK_MS2;
167074
167192
  var init_embedding = __esm(() => {
167075
- init_magic_context();
167076
167193
  init_logger();
167077
167194
  init_compartment_chunk_embedding();
167078
167195
  init_embedding_identity();
167079
- init_embedding_local();
167080
167196
  init_embedding_openai();
167081
167197
  init_storage_memory_embeddings();
167082
167198
  init_project_embedding_registry();
167083
167199
  DEFAULT_EMBEDDING_CONFIG = {
167084
- provider: "local",
167085
- model: DEFAULT_LOCAL_EMBEDDING_MODEL
167200
+ provider: "off"
167086
167201
  };
167087
167202
  embeddingConfig = DEFAULT_EMBEDDING_CONFIG;
167088
167203
  loadUnembeddedMemoriesStatements2 = new WeakMap;
@@ -167163,13 +167278,13 @@ var init_storage_memory_fts = __esm(() => {
167163
167278
  });
167164
167279
 
167165
167280
  // src/shared/models-dev-cache.ts
167166
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync9, renameSync, writeFileSync } from "node:fs";
167167
- import { join as join14 } from "node:path";
167281
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync9, renameSync, writeFileSync } from "node:fs";
167282
+ import { join as join13 } from "node:path";
167168
167283
  function isSaneLimit(limit) {
167169
167284
  return typeof limit === "number" && limit >= MIN_SANE_LIMIT && limit <= MAX_SANE_LIMIT;
167170
167285
  }
167171
167286
  function persistFilePath() {
167172
- return join14(getMagicContextStorageDir(), `model-context-limits-${getHarness()}.json`);
167287
+ return join13(getMagicContextStorageDir(), `model-context-limits-${getHarness()}.json`);
167173
167288
  }
167174
167289
  function loadPersistedApiCacheOnce() {
167175
167290
  if (persistSeedLoaded || apiCache !== null)
@@ -167199,7 +167314,7 @@ function persistApiCache() {
167199
167314
  }
167200
167315
  try {
167201
167316
  const dir = getMagicContextStorageDir();
167202
- mkdirSync4(dir, { recursive: true });
167317
+ mkdirSync3(dir, { recursive: true });
167203
167318
  const target = persistFilePath();
167204
167319
  const tmp = `${target}.${process.pid}.tmp`;
167205
167320
  writeFileSync(tmp, JSON.stringify(obj), { encoding: "utf-8", mode: 384 });
@@ -167300,58 +167415,6 @@ var init_models_dev_cache = __esm(() => {
167300
167415
  init_logger();
167301
167416
  });
167302
167417
 
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
167418
  // src/features/magic-context/compartment-embedding.ts
167356
167419
  async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compartments) {
167357
167420
  if (compartments.length === 0)
@@ -167360,7 +167423,7 @@ async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compar
167360
167423
  for (const compartment of compartments) {
167361
167424
  try {
167362
167425
  const fromMemory = compartment.sourceChunkText ? canonicalizeInMemoryChunkTextForEmbedding(compartment.sourceChunkText, compartment.startMessage, compartment.endMessage) : "";
167363
- const canonicalText = fromMemory || buildCanonicalChunkTextFromFts(db, sessionId, compartment.startMessage, compartment.endMessage);
167426
+ const canonicalText = fromMemory || buildCanonicalChunkTextFromFts(db, sessionId, compartment.startMessage, compartment.endMessage) || buildCompartmentSummaryFallbackText(db, compartment.id);
167364
167427
  if (canonicalText.length === 0)
167365
167428
  continue;
167366
167429
  const windows = chunkCanonicalText(canonicalText, compartment.startMessage, compartment.endMessage, maxInputTokens);
@@ -167405,7 +167468,7 @@ var init_compartment_embedding = __esm(() => {
167405
167468
  });
167406
167469
 
167407
167470
  // src/features/magic-context/compaction-marker.ts
167408
- import { join as join15 } from "node:path";
167471
+ import { join as join14 } from "node:path";
167409
167472
  function randomBase62(length) {
167410
167473
  const chars = [];
167411
167474
  for (let i = 0;i < length; i++) {
@@ -167425,7 +167488,7 @@ function generatePartId(timestampMs, counter = 0n) {
167425
167488
  return generateId("prt", timestampMs, counter);
167426
167489
  }
167427
167490
  function getOpenCodeDbPath3() {
167428
- return join15(getDataDir(), "opencode", "opencode.db");
167491
+ return join14(getDataDir(), "opencode", "opencode.db");
167429
167492
  }
167430
167493
  function isOpenCodeSchemaCompatible(db, dbPath) {
167431
167494
  if (cachedSchemaCompatible?.path === dbPath) {
@@ -167567,7 +167630,7 @@ var init_compaction_marker = __esm(async () => {
167567
167630
  });
167568
167631
 
167569
167632
  // src/hooks/magic-context/compaction-marker-manager.ts
167570
- import { join as join16 } from "node:path";
167633
+ import { join as join15 } from "node:path";
167571
167634
  function validatePendingTarget(db, sessionId, pending) {
167572
167635
  const ocMessage = getOpenCodeMessageById(sessionId, pending.endMessageId);
167573
167636
  if (!ocMessage) {
@@ -167676,7 +167739,7 @@ function removeCompactionMarkerForSession(db, sessionId) {
167676
167739
  }
167677
167740
  }
167678
167741
  function checkCompactionMarkerConsistency(db) {
167679
- const opencodeDbPath = join16(getDataDir(), "opencode", "opencode.db");
167742
+ const opencodeDbPath = join15(getDataDir(), "opencode", "opencode.db");
167680
167743
  let opencodeDb;
167681
167744
  try {
167682
167745
  opencodeDb = new Database(opencodeDbPath, { readonly: true });
@@ -168073,8 +168136,8 @@ var init_compartment_runner_validation = __esm(async () => {
168073
168136
  });
168074
168137
 
168075
168138
  // src/hooks/magic-context/compartment-runner-historian.ts
168076
- import { mkdirSync as mkdirSync5, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
168077
- import { join as join17 } from "node:path";
168139
+ import { mkdirSync as mkdirSync4, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
168140
+ import { join as join16 } from "node:path";
168078
168141
  function historianResponseDumpDir(directory) {
168079
168142
  return getProjectMagicContextHistorianDir(directory);
168080
168143
  }
@@ -168376,10 +168439,10 @@ function cleanupHistorianDump(sessionId, dumpPath) {
168376
168439
  function dumpHistorianResponse(sessionId, directory, label, text) {
168377
168440
  try {
168378
168441
  const dumpDir = historianResponseDumpDir(directory);
168379
- mkdirSync5(dumpDir, { recursive: true });
168442
+ mkdirSync4(dumpDir, { recursive: true });
168380
168443
  const safeSessionId = sanitizeDumpName(sessionId);
168381
168444
  const safeLabel = sanitizeDumpName(label);
168382
- const dumpPath = join17(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
168445
+ const dumpPath = join16(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
168383
168446
  writeFileSync2(dumpPath, text, "utf8");
168384
168447
  sessionLog(sessionId, "compartment agent: historian response dumped", {
168385
168448
  label,
@@ -168502,7 +168565,7 @@ function insertCompartmentEvents(db, sessionId, events, compartmentIds) {
168502
168565
  var init_compartment_events = () => {};
168503
168566
 
168504
168567
  // src/hooks/magic-context/historian-state-file.ts
168505
- import { mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
168568
+ import { mkdirSync as mkdirSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
168506
168569
  function cleanupHistorianStateFile(path6) {
168507
168570
  if (!path6)
168508
168571
  return;
@@ -168840,7 +168903,7 @@ var init_decay_render = __esm(() => {
168840
168903
 
168841
168904
  // src/hooks/magic-context/key-files-block.ts
168842
168905
  import { readFileSync as readFileSync10, realpathSync as realpathSync4 } from "node:fs";
168843
- import { join as join18, sep as sep2 } from "node:path";
168906
+ import { join as join17, sep as sep2 } from "node:path";
168844
168907
  function staleKey(update) {
168845
168908
  return `${update.projectPath}\x00${update.path}\x00${update.generatedAtWitness}\x00${update.staleReason}`;
168846
168909
  }
@@ -168895,7 +168958,7 @@ function buildKeyFilesBlock(db, projectPath, config2 = { enabled: true, tokenBud
168895
168958
  let nextStale = null;
168896
168959
  let observed = false;
168897
168960
  try {
168898
- const absPath = join18(projectPath, row.path);
168961
+ const absPath = join17(projectPath, row.path);
168899
168962
  const real = realpathSync4(absPath);
168900
168963
  if (!isUnderResolvedRoot(projectRoot, real)) {
168901
168964
  nextStale = "missing";
@@ -170560,29 +170623,12 @@ function resolveHistorianContextLimit(historianModelOverride) {
170560
170623
  return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
170561
170624
  }
170562
170625
  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.`);
170626
+ console.warn(`[magic-context] historian.model "${historianModelOverride}" lacks provider prefix ("provider/model-id"); using the default context limit for chunk-budget derivation.`);
170564
170627
  }
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;
170580
- }
170581
- return minLimit ?? DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
170628
+ return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
170582
170629
  }
170583
170630
  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
170631
  var init_derive_budgets = __esm(() => {
170585
- init_model_requirements();
170586
170632
  init_models_dev_cache();
170587
170633
  });
170588
170634
 
@@ -170903,7 +170949,7 @@ function buildToolArcs(messages) {
170903
170949
  }
170904
170950
  return arcs.sort((a, b) => a.invOrdinal - b.invOrdinal || (a.resOrdinal ?? Number.MAX_SAFE_INTEGER) - (b.resOrdinal ?? Number.MAX_SAFE_INTEGER));
170905
170951
  }
170906
- function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
170952
+ function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal, recentOpenArcCutoff) {
170907
170953
  let boundary = candidate;
170908
170954
  for (const arc of arcs) {
170909
170955
  if (arc.resOrdinal !== null) {
@@ -170912,6 +170958,8 @@ function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
170912
170958
  }
170913
170959
  continue;
170914
170960
  }
170961
+ if (arc.invOrdinal < recentOpenArcCutoff)
170962
+ continue;
170915
170963
  if (arc.invOrdinal >= lastCompartmentEndOrdinal + 1 && arc.invOrdinal < boundary) {
170916
170964
  return arc.invOrdinal;
170917
170965
  }
@@ -171151,7 +171199,7 @@ function semanticSnapBoundary(args) {
171151
171199
  return snapped;
171152
171200
  }
171153
171201
  function applyHeadCap(args) {
171154
- const { index, protectedTailStart, offset, arcs, capTokens } = args;
171202
+ const { index, protectedTailStart, offset, arcs, capTokens, recentOpenArcCutoff } = args;
171155
171203
  if (offset >= protectedTailStart)
171156
171204
  return { eligibleEndOrdinal: offset, oversizeAtomicUnit: false };
171157
171205
  let end = index.findHeadEndForCap(offset, protectedTailStart, capTokens);
@@ -171159,7 +171207,7 @@ function applyHeadCap(args) {
171159
171207
  for (const arc of arcs) {
171160
171208
  const resOrdinal = arc.resOrdinal;
171161
171209
  if (resOrdinal === null) {
171162
- if (arc.invOrdinal >= offset && arc.invOrdinal < end) {
171210
+ if (arc.invOrdinal >= recentOpenArcCutoff && arc.invOrdinal >= offset && arc.invOrdinal < end) {
171163
171211
  end = Math.min(end, arc.invOrdinal);
171164
171212
  }
171165
171213
  continue;
@@ -171226,7 +171274,14 @@ function resolveProtectedTailBoundary(ctx) {
171226
171274
  }
171227
171275
  if (ctx.mode === "manual-full-recomp") {
171228
171276
  const arcs2 = buildToolArcs(messages);
171229
- const firstOpenArc = arcs2.find((arc) => arc.resOrdinal === null && arc.invOrdinal >= offset);
171277
+ const recompTarget = deriveProtectedTailTokenTarget({
171278
+ contextLimit: ctx.contextLimit,
171279
+ executeThresholdPercentage: ctx.executeThresholdPercentage,
171280
+ usagePercentage: 0,
171281
+ triggerBudget: ctx.triggerBudget
171282
+ });
171283
+ const recentOpenArcCutoff2 = index.findSuffixStartForTokens(recompTarget.N);
171284
+ const firstOpenArc = arcs2.find((arc) => arc.resOrdinal === null && arc.invOrdinal >= offset && arc.invOrdinal >= recentOpenArcCutoff2);
171230
171285
  const protectedTailStart2 = firstOpenArc?.invOrdinal ?? rawMessageCount + 1;
171231
171286
  const rawRangeFingerprint2 = computeRawRangeFingerprint(messages, offset, protectedTailStart2);
171232
171287
  return {
@@ -171268,13 +171323,14 @@ function resolveProtectedTailBoundary(ctx) {
171268
171323
  const scaledN = ctx.emergencyTailScale ? Math.max(1, Math.floor(target.N * ctx.emergencyTailScale)) : target.N;
171269
171324
  const arcs = buildToolArcs(messages);
171270
171325
  let boundary = index.findSuffixStartForTokens(scaledN);
171326
+ const recentOpenArcCutoff = boundary;
171271
171327
  let boundaryReason = boundary === 1 ? "whole-session-smaller-than-tail" : "size-walk";
171272
171328
  const tokenAtBoundary = index.tokenForOrdinal(boundary);
171273
171329
  if (boundary <= rawMessageCount && tokenAtBoundary > Math.max(2 * scaledN, 64000) && boundary < rawMessageCount) {
171274
171330
  boundary += 1;
171275
171331
  boundaryReason = "huge-message-exception";
171276
171332
  }
171277
- boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal);
171333
+ boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
171278
171334
  const snapped = semanticSnapBoundary({
171279
171335
  messages,
171280
171336
  index,
@@ -171284,7 +171340,7 @@ function resolveProtectedTailBoundary(ctx) {
171284
171340
  });
171285
171341
  if (snapped !== boundary)
171286
171342
  boundaryReason = "semantic-snap";
171287
- boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal);
171343
+ boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
171288
171344
  let runtimeFloor = offset;
171289
171345
  if (ctx.migrationFloorActive)
171290
171346
  runtimeFloor = Math.max(runtimeFloor, ctx.priorBoundaryOrdinal);
@@ -171320,7 +171376,8 @@ function resolveProtectedTailBoundary(ctx) {
171320
171376
  offset,
171321
171377
  arcs,
171322
171378
  lastCompartmentEndOrdinal: ctx.lastCompartmentEndOrdinal,
171323
- capTokens: perRunCap
171379
+ capTokens: perRunCap,
171380
+ recentOpenArcCutoff
171324
171381
  });
171325
171382
  const rawRangeFingerprint = computeRawRangeFingerprint(messages, offset, head.eligibleEndOrdinal);
171326
171383
  return {
@@ -171371,7 +171428,7 @@ function resolveBoundaryContext(args) {
171371
171428
  }
171372
171429
  let storedTokenTotals;
171373
171430
  try {
171374
- storedTokenTotals = getAllStatusTagTokenTotalsFlat(args.db, args.sessionId).totals;
171431
+ storedTokenTotals = getAllStatusTagTokenTotalsFlat(args.db, args.sessionId, args.taggerFloor ?? 0).totals;
171375
171432
  } catch (error51) {
171376
171433
  sessionLog(args.sessionId, "protected-tail stored-token map unavailable (live fallback):", error51);
171377
171434
  }
@@ -173575,6 +173632,7 @@ async function runCompartmentAgent(deps) {
173575
173632
  const count = recordHighPressureNoEligibleHead(db, boundarySnapshot);
173576
173633
  sessionLog(sessionId, `historian high-pressure no-op: recovery remains armed (noEligibleHeadCount=${count})`);
173577
173634
  }
173635
+ clearEmergencyDrainLatch(db, sessionId);
173578
173636
  telemetry.status = "noop";
173579
173637
  telemetry.failureReason = "nothing to compact before protected tail";
173580
173638
  rollbackDrainReservation();
@@ -173589,7 +173647,8 @@ async function runCompartmentAgent(deps) {
173589
173647
  trueRawTokens: boundarySnapshot.trueRawEligibleTokens,
173590
173648
  usagePercentage: boundarySnapshot.usagePercentage,
173591
173649
  usable,
173592
- perRunCap
173650
+ perRunCap,
173651
+ executeThresholdPercentage: boundarySnapshot.executeThresholdPercentage
173593
173652
  });
173594
173653
  if (!reserve.ok) {
173595
173654
  sessionLog(sessionId, `historian rate-limit skip: ${reserve.skippedReason ?? "quota exhausted"}`);
@@ -173608,6 +173667,7 @@ async function runCompartmentAgent(deps) {
173608
173667
  } else {
173609
173668
  recordHighPressureNoEligibleHead(db, boundarySnapshot);
173610
173669
  }
173670
+ clearEmergencyDrainLatch(db, sessionId);
173611
173671
  telemetry.status = "noop";
173612
173672
  telemetry.failureReason = "chunk empty after filtering";
173613
173673
  rollbackDrainReservation();
@@ -173715,6 +173775,7 @@ ${chunkText}`,
173715
173775
  }
173716
173776
  appendCompartments(db, sessionId, persistedCompartments);
173717
173777
  clearHistorianFailureState(db, sessionId);
173778
+ clearHistorianDrainFailure(db, sessionId);
173718
173779
  recordProtectedTailPublicationFloor(db, sessionId, lastCompartmentEnd + 1);
173719
173780
  clearEmergencyRecovery(db, sessionId);
173720
173781
  drainReservation = null;
@@ -173818,8 +173879,11 @@ ${chunkText}`,
173818
173879
  }
173819
173880
  } finally {
173820
173881
  if (!completedSuccessfully) {
173821
- if (!retainDrainReservationForRetryThrottle)
173882
+ if (!retainDrainReservationForRetryThrottle) {
173822
173883
  rollbackDrainReservation();
173884
+ } else {
173885
+ recordHistorianDrainFailure(db, sessionId);
173886
+ }
173823
173887
  updateSessionMeta(db, sessionId, { compartmentInProgress: false });
173824
173888
  }
173825
173889
  recordTelemetry();
@@ -176033,15 +176097,15 @@ var require_windows = __commonJS((exports, module) => {
176033
176097
  }
176034
176098
  return false;
176035
176099
  }
176036
- function checkStat(stat2, path6, options) {
176037
- if (!stat2.isSymbolicLink() && !stat2.isFile()) {
176100
+ function checkStat(stat, path6, options) {
176101
+ if (!stat.isSymbolicLink() && !stat.isFile()) {
176038
176102
  return false;
176039
176103
  }
176040
176104
  return checkPathExt(path6, options);
176041
176105
  }
176042
176106
  function isexe(path6, options, cb) {
176043
- fs2.stat(path6, function(er, stat2) {
176044
- cb(er, er ? false : checkStat(stat2, path6, options));
176107
+ fs2.stat(path6, function(er, stat) {
176108
+ cb(er, er ? false : checkStat(stat, path6, options));
176045
176109
  });
176046
176110
  }
176047
176111
  function sync(path6, options) {
@@ -176055,20 +176119,20 @@ var require_mode = __commonJS((exports, module) => {
176055
176119
  isexe.sync = sync;
176056
176120
  var fs2 = __require("fs");
176057
176121
  function isexe(path6, options, cb) {
176058
- fs2.stat(path6, function(er, stat2) {
176059
- cb(er, er ? false : checkStat(stat2, options));
176122
+ fs2.stat(path6, function(er, stat) {
176123
+ cb(er, er ? false : checkStat(stat, options));
176060
176124
  });
176061
176125
  }
176062
176126
  function sync(path6, options) {
176063
176127
  return checkStat(fs2.statSync(path6), options);
176064
176128
  }
176065
- function checkStat(stat2, options) {
176066
- return stat2.isFile() && checkMode(stat2, options);
176129
+ function checkStat(stat, options) {
176130
+ return stat.isFile() && checkMode(stat, options);
176067
176131
  }
176068
- function checkMode(stat2, options) {
176069
- var mod = stat2.mode;
176070
- var uid = stat2.uid;
176071
- var gid = stat2.gid;
176132
+ function checkMode(stat, options) {
176133
+ var mod = stat.mode;
176134
+ var uid = stat.uid;
176135
+ var gid = stat.gid;
176072
176136
  var myUid = options.uid !== undefined ? options.uid : process.getuid && process.getuid();
176073
176137
  var myGid = options.gid !== undefined ? options.gid : process.getgid && process.getgid();
176074
176138
  var u = parseInt("100", 8);
@@ -176933,7 +176997,6 @@ var init_memory_migration = __esm(async () => {
176933
176997
  init_shared();
176934
176998
  init_assistant_message_extractor();
176935
176999
  init_logger();
176936
- init_resolve_fallbacks();
176937
177000
  init_project_identity();
176938
177001
  init_storage_memory();
176939
177002
  await init_storage();
@@ -177249,17 +177312,20 @@ function shouldShowAnnouncement() {
177249
177312
  }
177250
177313
  return state.version !== ANNOUNCEMENT_VERSION;
177251
177314
  }
177252
- var ANNOUNCEMENT_VERSION = "0.24.0", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
177315
+ var ANNOUNCEMENT_VERSION = "0.26.0", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
177253
177316
  var init_announcement = __esm(() => {
177254
177317
  init_data_path();
177255
177318
  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)."
177319
+ "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).",
177320
+ "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.",
177321
+ "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.",
177322
+ "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).",
177323
+ "New: doctor migrate-session re-homes a session (and optionally its memories) to another project, with a dry-run preview."
177261
177324
  ];
177262
177325
  });
177326
+
177327
+ // src/agents/dreamer.ts
177328
+ var DREAMER_AGENT = "dreamer";
177263
177329
  // src/agents/permissions.ts
177264
177330
  function buildAllowOnlyPermission(allowedTools) {
177265
177331
  const permission = { "*": "deny" };
@@ -177269,6 +177335,11 @@ function buildAllowOnlyPermission(allowedTools) {
177269
177335
  return permission;
177270
177336
  }
177271
177337
  var HISTORIAN_ALLOWED_TOOLS = ["read", "aft_outline", "aft_zoom", "aft_search"];
177338
+ function applyDisallowedTools(defaults, disallowed) {
177339
+ if (disallowed.includes("*"))
177340
+ return [];
177341
+ return defaults.filter((t) => !disallowed.includes(t));
177342
+ }
177272
177343
  var DREAMER_ALLOWED_TOOLS = [
177273
177344
  "read",
177274
177345
  "grep",
@@ -177284,6 +177355,10 @@ var DREAMER_ALLOWED_TOOLS = [
177284
177355
  "ctx_note"
177285
177356
  ];
177286
177357
  var SIDEKICK_ALLOWED_TOOLS = ["ctx_search", "aft_outline", "aft_zoom"];
177358
+
177359
+ // src/agents/sidekick.ts
177360
+ var SIDEKICK_AGENT = "sidekick";
177361
+
177287
177362
  // src/config/index.ts
177288
177363
  init_jsonc_parser();
177289
177364
  import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
@@ -177986,9 +178061,9 @@ function getMagicContextBuiltinCommands() {
177986
178061
  template: "ctx-dream",
177987
178062
  description: "Run the hidden dreamer maintenance pass for this project now"
177988
178063
  },
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"
178064
+ "ctx-embed": {
178065
+ template: "ctx-embed",
178066
+ description: "Embedding status, or start/pause history compartment embedding (start | pause)"
177992
178067
  }
177993
178068
  };
177994
178069
  }
@@ -178237,6 +178312,7 @@ ${modeIntro}
178237
178312
  4. **Write or update** using the Write tool. Always write to project root, NOT to .planning/.
178238
178313
 
178239
178314
  ### Rules
178315
+ - **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
178316
  - **Be prescriptive**: "Use X pattern" not "X pattern is used"
178241
178317
  - **Always include file paths** in backticks
178242
178318
  - **Write current state only**: no temporal language, no history
@@ -178373,7 +178449,6 @@ init_project_identity();
178373
178449
  init_shared();
178374
178450
  init_assistant_message_extractor();
178375
178451
  init_logger();
178376
- init_resolve_fallbacks();
178377
178452
  init_subagent_token_capture();
178378
178453
  await init_storage();
178379
178454
 
@@ -178395,7 +178470,7 @@ function stripThinkingBlocks(text) {
178395
178470
 
178396
178471
  // src/features/magic-context/sidekick/agent.ts
178397
178472
  async function runSidekick(deps) {
178398
- const fallbackModels = resolveFallbackChain(SIDEKICK_AGENT, deps.config.fallback_models);
178473
+ const fallbackModels = resolveFallbackChain(deps.config.fallback_models);
178399
178474
  let agentSessionId = null;
178400
178475
  const startedAt = Date.now();
178401
178476
  let invocationRecorded = false;
@@ -178485,6 +178560,11 @@ init_project_identity();
178485
178560
  import { createHash as createHash6 } from "node:crypto";
178486
178561
  import { realpathSync as realpathSync2 } from "node:fs";
178487
178562
  import path5 from "node:path";
178563
+
178564
+ // src/features/magic-context/memory/relocate-memory.ts
178565
+ var memoryCopyColumnsCache = new WeakMap;
178566
+
178567
+ // src/features/magic-context/v22-deferred-backfill.ts
178488
178568
  var BATCH_SIZE = 25;
178489
178569
  var YIELD_EVERY_N_ROWS = 5;
178490
178570
  var BACKFILL_META_KEY = "v22_legacy_memory_backfill";
@@ -178722,6 +178802,7 @@ function createLiveSessionState() {
178722
178802
 
178723
178803
  // src/index.ts
178724
178804
  init_conflict_warning_hook();
178805
+
178725
178806
  // src/features/magic-context/dreamer/storage-dream-state.ts
178726
178807
  var getDreamStateStatements = new WeakMap;
178727
178808
  var setDreamStateStatements = new WeakMap;
@@ -178859,7 +178940,7 @@ function enqueueDream(db, projectIdentity, reason, force = false) {
178859
178940
  return db.transaction(() => {
178860
178941
  if (!hasActiveDreamLease(db)) {
178861
178942
  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]);
178943
+ db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run(projectIdentity, now - staleThresholdMs);
178863
178944
  }
178864
178945
  const existing = db.prepare("SELECT id FROM dream_queue WHERE project_path = ?").get(projectIdentity);
178865
178946
  if (existing) {
@@ -178915,21 +178996,21 @@ function clearStaleEntries(db, maxAgeMs, projectIdentity) {
178915
178996
  return result.changes;
178916
178997
  }
178917
178998
  // src/features/magic-context/dreamer/runner.ts
178999
+ import { existsSync as existsSync10 } from "node:fs";
179000
+ import { join as join12 } from "node:path";
178918
179001
  init_shared();
178919
179002
  init_assistant_message_extractor();
178920
179003
  init_data_path();
178921
179004
  init_logger();
178922
179005
  await init_sqlite();
178923
- import { existsSync as existsSync10 } from "node:fs";
178924
- import { join as join12 } from "node:path";
178925
179006
 
178926
179007
  // src/features/magic-context/key-files/identify-key-files.ts
179008
+ import { readFileSync as readFileSync8 } from "node:fs";
179009
+ import { isAbsolute as isAbsolute3, join as join11, relative as relative2 } from "node:path";
178927
179010
  init_read_session_formatting();
178928
179011
  init_shared();
178929
179012
  init_assistant_message_extractor();
178930
179013
  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
179014
  init_subagent_token_capture();
178934
179015
  init_aft_availability();
178935
179016
  init_project_key_files();
@@ -180554,120 +180635,7 @@ ${body}` : subject;
180554
180635
  init_logger();
180555
180636
  init_embedding();
180556
180637
  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
180638
+ init_storage_git_commits();
180671
180639
  var MS_PER_DAY = 24 * 60 * 60 * 1000;
180672
180640
  var EMBED_BATCH_SIZE = 16;
180673
180641
  var EMBED_MAX_PER_SWEEP = 500;
@@ -180905,12 +180873,12 @@ function searchGitCommitsSync(db, projectPath, query, options) {
180905
180873
 
180906
180874
  // src/features/magic-context/git-commits/index.ts
180907
180875
  init_storage_git_commit_embeddings();
180876
+ init_storage_git_commits();
180908
180877
  init_sweep_coordinator();
180909
180878
 
180910
180879
  // src/plugin/dream-timer.ts
180911
180880
  init_embedding();
180912
180881
  init_logger();
180913
- init_resolve_fallbacks();
180914
180882
  await init_storage();
180915
180883
  var DREAM_TIMER_INTERVAL_MS = 15 * 60 * 1000;
180916
180884
  var activeTimer = null;
@@ -181013,7 +180981,7 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
181013
180981
  experimentalPinKeyFiles: reg.experimentalPinKeyFiles,
181014
180982
  projectIdentity: reg.projectIdentity,
181015
180983
  sessionDirectoryOverride: reg.directory,
181016
- fallbackModels: resolveFallbackChain(DREAMER_AGENT, reg.dreamerConfig.fallback_models)
180984
+ fallbackModels: resolveFallbackChain(reg.dreamerConfig.fallback_models)
181017
180985
  });
181018
180986
  } catch (error51) {
181019
180987
  log(`[dreamer] timer-triggered queue processing failed for ${reg.projectIdentity}:`, error51);
@@ -181462,10 +181430,9 @@ function makeToolCompositeKey(ownerMsgId, callId) {
181462
181430
  }
181463
181431
  var GET_COUNTER_SQL = `SELECT counter FROM session_meta WHERE session_id = ?`;
181464
181432
  var GET_ASSIGNMENTS_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id FROM tags WHERE session_id = ? ORDER BY tag_number ASC";
181433
+ 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
181434
  var PROBE_DATA_VERSION_SQL = "PRAGMA main.data_version";
181466
- var PROBE_TOTAL_CHANGES_SQL = "SELECT total_changes() AS tc";
181467
181435
  var probeDataVersionStatements = new WeakMap;
181468
- var probeTotalChangesStatements = new WeakMap;
181469
181436
  function getProbeDataVersionStatement(db) {
181470
181437
  let stmt = probeDataVersionStatements.get(db);
181471
181438
  if (!stmt) {
@@ -181474,14 +181441,6 @@ function getProbeDataVersionStatement(db) {
181474
181441
  }
181475
181442
  return stmt;
181476
181443
  }
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
181444
  function isAssignmentRow(row) {
181486
181445
  if (row === null || typeof row !== "object") {
181487
181446
  return false;
@@ -181674,20 +181633,18 @@ function createTagger() {
181674
181633
  }
181675
181634
  function probeSignature(db) {
181676
181635
  const dvRow = getProbeDataVersionStatement(db).get();
181677
- const tcRow = getProbeTotalChangesStatement(db).get();
181678
181636
  return {
181679
- dataVersion: dvRow?.data_version ?? 0,
181680
- totalChanges: tcRow?.tc ?? 0
181637
+ dataVersion: dvRow?.data_version ?? 0
181681
181638
  };
181682
181639
  }
181683
- function initFromDb(sessionId, db) {
181640
+ function initFromDb(sessionId, db, floor = 0) {
181684
181641
  const probe = probeSignature(db);
181685
181642
  const cached2 = loadSignatures.get(sessionId);
181686
- if (cached2 !== undefined && cached2.db === db && cached2.dataVersion === probe.dataVersion && cached2.totalChanges === probe.totalChanges) {
181643
+ if (cached2 !== undefined && cached2.db === db && cached2.dataVersion === probe.dataVersion && cached2.floor === floor) {
181687
181644
  return;
181688
181645
  }
181689
181646
  const row = db.prepare(GET_COUNTER_SQL).get(sessionId);
181690
- const assignmentRows = db.prepare(GET_ASSIGNMENTS_SQL).all(sessionId).filter(isAssignmentRow);
181647
+ const assignmentRows = (floor > 0 ? db.prepare(GET_ASSIGNMENTS_SCOPED_SQL).all(sessionId, floor) : db.prepare(GET_ASSIGNMENTS_SQL).all(sessionId)).filter(isAssignmentRow);
181691
181648
  const sessionAssignments = getSessionAssignments(sessionId);
181692
181649
  sessionAssignments.clear();
181693
181650
  let maxTagNumber = 0;
@@ -181708,7 +181665,7 @@ function createTagger() {
181708
181665
  loadSignatures.set(sessionId, {
181709
181666
  db,
181710
181667
  dataVersion: probe.dataVersion,
181711
- totalChanges: probe.totalChanges
181668
+ floor
181712
181669
  });
181713
181670
  }
181714
181671
  function cleanup(sessionId) {
@@ -181731,13 +181688,13 @@ function createTagger() {
181731
181688
  cleanup
181732
181689
  };
181733
181690
  }
181691
+
181734
181692
  // src/hooks/magic-context/hook.ts
181735
181693
  init_magic_context();
181736
181694
  init_project_identity();
181737
181695
  init_project_embedding_registry();
181738
181696
  await init_storage();
181739
181697
  init_logger();
181740
- init_resolve_fallbacks();
181741
181698
  init_rpc_notifications();
181742
181699
 
181743
181700
  // src/hooks/magic-context/command-handler.ts
@@ -181788,6 +181745,7 @@ await __promiseAll([
181788
181745
  // src/hooks/magic-context/compartment-trigger.ts
181789
181746
  init_compartment_storage();
181790
181747
  init_logger();
181748
+ init_read_session_true_raw_tokens();
181791
181749
  await __promiseAll([
181792
181750
  init_storage(),
181793
181751
  init_protected_tail_boundary(),
@@ -181802,6 +181760,38 @@ var TAIL_SIZE_TRIGGER_MULTIPLIER = 3;
181802
181760
  var FORCE_COMPARTMENT_PERCENTAGE = 80;
181803
181761
  var BLOCK_UNTIL_DONE_PERCENTAGE = 95;
181804
181762
  var FORCE_MATERIALIZE_PERCENTAGE = 85;
181763
+ var CONTENT_TAG_OWNER_SUFFIX = /:(?:p|file)\d+$/;
181764
+ function tagOwnerMessageId(row) {
181765
+ if (row.type === "tool")
181766
+ return row.tool_owner_message_id ?? row.message_id;
181767
+ return row.message_id.replace(CONTENT_TAG_OWNER_SUFFIX, "");
181768
+ }
181769
+ function getActiveOrDroppedTagOwnerMessageIds(db, sessionId, floor = 0) {
181770
+ const rows = floor > 0 ? db.prepare(`SELECT type, message_id, tool_owner_message_id
181771
+ FROM tags
181772
+ WHERE session_id = ? AND status IN ('active', 'dropped') AND tag_number >= ?`).all(sessionId, floor) : db.prepare(`SELECT type, message_id, tool_owner_message_id
181773
+ FROM tags
181774
+ WHERE session_id = ? AND status IN ('active', 'dropped')`).all(sessionId);
181775
+ const owners = new Set;
181776
+ for (const row of rows)
181777
+ owners.add(tagOwnerMessageId(row));
181778
+ return owners;
181779
+ }
181780
+ function estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, taggerFloor = 0) {
181781
+ const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
181782
+ const coveredOwnerMessageIds = getActiveOrDroppedTagOwnerMessageIds(db, sessionId, taggerFloor);
181783
+ let total = 0;
181784
+ for (const message of inMemoryTail.messages) {
181785
+ if (message.ordinal <= lastCompartmentEnd)
181786
+ continue;
181787
+ if (coveredOwnerMessageIds.has(message.id))
181788
+ continue;
181789
+ total += estimateTrueRawMessageTokens(message, {
181790
+ providerShapeVersion: "opencode-v1"
181791
+ }).total;
181792
+ }
181793
+ return total;
181794
+ }
181805
181795
  function buildTriggerInMemoryTail(db, sessionId, messages) {
181806
181796
  if (messages.length === 0)
181807
181797
  return;
@@ -181873,7 +181863,7 @@ function resolveBoundaryContextLimit(usage, fallbackContextLimit) {
181873
181863
  }
181874
181864
  return 128000;
181875
181865
  }
181876
- function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail) {
181866
+ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail, taggerFloor = 0) {
181877
181867
  return withRawSessionMessageCache(() => {
181878
181868
  try {
181879
181869
  const memoryPrimed = inMemoryTail ? primeInMemoryTailRawMessageCache({
@@ -181902,7 +181892,8 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
181902
181892
  contextLimit: resolveBoundaryContextLimit(usage, contextLimit),
181903
181893
  executeThresholdPercentage,
181904
181894
  usage,
181905
- usageSource: "live"
181895
+ usageSource: "live",
181896
+ taggerFloor
181906
181897
  });
181907
181898
  const hasProtectedEligibleHead = boundary.offset < boundary.protectedTailStart;
181908
181899
  if (!hasProtectedEligibleHead) {
@@ -181933,7 +181924,7 @@ function getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThr
181933
181924
  }
181934
181925
  });
181935
181926
  }
181936
- function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail) {
181927
+ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, triggerBudget, clearReasoningAge, commitClusterTrigger, preloadedActiveTags, contextLimit, inMemoryTail, taggerFloorOverride) {
181937
181928
  if (sessionMeta.compartmentInProgress) {
181938
181929
  sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
181939
181930
  return { shouldFire: false };
@@ -181947,14 +181938,17 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
181947
181938
  inMemoryTail = undefined;
181948
181939
  }
181949
181940
  }
181941
+ const taggerFloor = taggerFloorOverride !== undefined && taggerFloorOverride > 0 ? taggerFloorOverride : inMemoryTail ? deriveTagLoadFloor(db, sessionId, inMemoryTail.messages.map((m) => m.id)) : 0;
181950
181942
  const proactiveFloorForGate = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
181951
- if (!inMemoryTail && usage.percentage < proactiveFloorForGate) {
181943
+ if (usage.percentage < proactiveFloorForGate) {
181952
181944
  try {
181953
- const { bound, nullCount } = getTriggerTagTokenUpperBound(db, sessionId);
181945
+ const { bound: persistedBound, nullCount } = getTriggerTagTokenUpperBound(db, sessionId, taggerFloor);
181954
181946
  if (nullCount === 0) {
181955
- const eligibleUpperBound = bound;
181947
+ const untaggedUpperBound = inMemoryTail ? estimateUntaggedInMemoryTailUpperBound(db, sessionId, inMemoryTail, taggerFloor) : 0;
181948
+ const eligibleUpperBound = persistedBound + untaggedUpperBound;
181956
181949
  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`);
181950
+ const memorySuffix = inMemoryTail ? ` (persisted=${persistedBound}, untagged-memory≤${untaggedUpperBound})` : "";
181951
+ 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
181952
  return { shouldFire: false };
181959
181953
  }
181960
181954
  }
@@ -181962,7 +181956,7 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
181962
181956
  sessionLog(sessionId, `compartment trigger: cheap-gate skipped (falling through to full read): ${error51 instanceof Error ? error51.message : String(error51)}`);
181963
181957
  }
181964
181958
  }
181965
- const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail);
181959
+ const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, inMemoryTail, taggerFloor);
181966
181960
  if (!tailInfo.hasNewRawHistory) {
181967
181961
  try {
181968
181962
  const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
@@ -182328,7 +182322,7 @@ function createMagicContextCommandHandler(deps) {
182328
182322
  const isAugCommand = (command) => command === "ctx-aug";
182329
182323
  const isDreamCommand = (command) => command === "ctx-dream";
182330
182324
  const isSessionUpgradeCommand = (command) => command === "ctx-session-upgrade";
182331
- const isEmbedHistoryCommand = (command) => command === "ctx-embed-history";
182325
+ const isEmbedCommand = (command) => command === "ctx-embed";
182332
182326
  return {
182333
182327
  "command.execute.before": async (input, _output, _params) => {
182334
182328
  const isStatus = isStatusCommand(input.command);
@@ -182337,8 +182331,8 @@ function createMagicContextCommandHandler(deps) {
182337
182331
  const isAug = isAugCommand(input.command);
182338
182332
  const isDream = isDreamCommand(input.command);
182339
182333
  const isSessionUpgrade = isSessionUpgradeCommand(input.command);
182340
- const isEmbedHistory = isEmbedHistoryCommand(input.command);
182341
- if (!isStatus && !isFlush && !isRecomp && !isAug && !isDream && !isSessionUpgrade && !isEmbedHistory) {
182334
+ const isEmbed = isEmbedCommand(input.command);
182335
+ if (!isStatus && !isFlush && !isRecomp && !isAug && !isDream && !isSessionUpgrade && !isEmbed) {
182342
182336
  return;
182343
182337
  }
182344
182338
  const sessionId = input.sessionID;
@@ -182351,15 +182345,50 @@ function createMagicContextCommandHandler(deps) {
182351
182345
  await executeDreaming(deps, sessionId);
182352
182346
  return;
182353
182347
  }
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);
182348
+ if (isEmbed) {
182349
+ const sub = input.arguments.trim().toLowerCase();
182350
+ if (sub === "pause") {
182351
+ const summary = deps.pauseEmbedDrain ? deps.pauseEmbedDrain(sessionId) : "Embedding pause is unavailable.";
182352
+ if (isTuiConnected(sessionId)) {
182353
+ pushNotification("action", { action: "show-result-dialog", title: "Embed", message: summary }, sessionId);
182354
+ } else {
182355
+ await deps.sendNotification(sessionId, summary, {});
182356
+ }
182357
+ throwSentinel(input.command);
182358
+ }
182359
+ if (sub === "start") {
182360
+ const summary = deps.executeEmbedHistory ? await deps.executeEmbedHistory(sessionId) : "Semantic embedding is not configured for this project, so there is nothing to embed.";
182361
+ if (isTuiConnected(sessionId)) {
182362
+ pushNotification("action", { action: "show-result-dialog", title: "Embed", message: summary }, sessionId);
182363
+ } else {
182364
+ await deps.sendNotification(sessionId, summary, {});
182365
+ }
182366
+ throwSentinel(input.command);
182367
+ }
182368
+ if (sub !== "") {
182369
+ await deps.sendNotification(sessionId, "Usage: `/ctx-embed` (status), `/ctx-embed start`, or `/ctx-embed pause`.", {});
182370
+ throwSentinel(input.command);
182371
+ }
182372
+ if (isTuiConnected(sessionId)) {
182373
+ pushNotification("action", { action: "show-embed-dialog" }, sessionId);
182374
+ sessionLog(sessionId, "command ctx-embed: pushed show-embed-dialog to TUI");
182375
+ throwSentinel(input.command);
182376
+ }
182377
+ result = deps.getEmbedStatusText ? `## Embedding Status
182378
+
182379
+ ${deps.getEmbedStatusText(sessionId)}` : `## Embedding Status
182380
+
182381
+ Embedding status is unavailable.`;
182358
182382
  }
182359
182383
  if (isFlush) {
182360
182384
  result = executeFlush(deps.db, sessionId);
182361
182385
  clearCachedM0M1(deps.db, sessionId);
182362
182386
  deps.onFlush?.(sessionId);
182387
+ if (isTuiConnected(sessionId)) {
182388
+ pushNotification("action", { action: "show-flush-dialog", message: result }, sessionId);
182389
+ sessionLog(sessionId, "command ctx-flush: pushed show-flush-dialog to TUI");
182390
+ throwSentinel(input.command);
182391
+ }
182363
182392
  }
182364
182393
  if (isStatus) {
182365
182394
  if (isTuiConnected(sessionId)) {
@@ -182486,6 +182515,34 @@ ${snap.error}`;
182486
182515
  // src/hooks/magic-context/hook.ts
182487
182516
  init_derive_budgets();
182488
182517
 
182518
+ // src/hooks/magic-context/embed-session-state.ts
182519
+ var embedPauseBySession = new Set;
182520
+ var embedRunStateBySession = new Map;
182521
+ var autoEmbedAttemptedBySession = new Set;
182522
+ function getEmbedDrainUiStatus(sessionId, progress) {
182523
+ if (embedPauseBySession.has(sessionId)) {
182524
+ return { status: "paused" };
182525
+ }
182526
+ if (progress?.kind === "embed" && progress.phase === "recomp") {
182527
+ return { status: "running" };
182528
+ }
182529
+ if (progress?.kind === "embed" && (progress.phase === "failed" || progress.phase === "skipped") && progress.message) {
182530
+ if (/provider/i.test(progress.message)) {
182531
+ return { status: "stopped", detail: progress.message };
182532
+ }
182533
+ }
182534
+ return { status: "idle" };
182535
+ }
182536
+ function clearEmbedSessionState(sessionId) {
182537
+ embedPauseBySession.delete(sessionId);
182538
+ const ctrl = embedRunStateBySession.get(sessionId);
182539
+ if (ctrl) {
182540
+ ctrl.abort();
182541
+ embedRunStateBySession.delete(sessionId);
182542
+ }
182543
+ autoEmbedAttemptedBySession.delete(sessionId);
182544
+ }
182545
+
182489
182546
  // src/features/magic-context/message-index-async.ts
182490
182547
  init_logger();
182491
182548
  await init_message_index();
@@ -182634,9 +182691,142 @@ function clearSessionTracking(sessionId) {
182634
182691
  // src/hooks/magic-context/event-handler.ts
182635
182692
  init_overflow_detection();
182636
182693
  init_storage_meta_persisted();
182694
+ await init_storage();
182695
+
182696
+ // src/features/magic-context/transform-decision-log.ts
182697
+ await __promiseAll([
182698
+ init_sqlite(),
182699
+ init_storage_db()
182700
+ ]);
182701
+ var canonicalReasons = new Set([
182702
+ "system_hash",
182703
+ "model_change",
182704
+ "project_memory_epoch",
182705
+ "ttl_idle",
182706
+ "explicit_flush",
182707
+ "max_mutation_id",
182708
+ "first_render",
182709
+ "pressure_refold",
182710
+ "upgrade_state",
182711
+ "cached_m1_missing"
182712
+ ]);
182713
+ var piReasonAliases = {
182714
+ project_memory_change: "project_memory_epoch",
182715
+ pending_mutations: "max_mutation_id",
182716
+ renderer_upgrade: "upgrade_state",
182717
+ cache_invalid: "cached_m1_missing",
182718
+ drift: "pressure_refold"
182719
+ };
182720
+ var sharedReasonAliases = {
182721
+ model_key: "model_change",
182722
+ pressure: "pressure_refold"
182723
+ };
182724
+ var pendingDecisionBySession = new Map;
182725
+ var pendingPiDecisionBySession = new Map;
182726
+ var lastBoundMessageIdBySession = new Map;
182727
+ var scheduledWriteTokensBySession = new Map;
182728
+ var writerOverrideForTests = null;
182729
+ function normalizeMaterializeReason(harness, reason, rematerialized) {
182730
+ const raw = typeof reason === "string" ? reason.trim() : "";
182731
+ if (raw.length > 0) {
182732
+ const alias = sharedReasonAliases[raw] ?? (harness === "pi" ? piReasonAliases[raw] : undefined) ?? undefined;
182733
+ if (alias)
182734
+ return alias;
182735
+ if (canonicalReasons.has(raw))
182736
+ return raw;
182737
+ return null;
182738
+ }
182739
+ return rematerialized ? "pressure_refold" : null;
182740
+ }
182741
+ function clearOpenCodePendingTransformDecision(sessionId) {
182742
+ pendingDecisionBySession.delete(sessionId);
182743
+ }
182744
+ function clearTransformDecisionSession(sessionId) {
182745
+ pendingDecisionBySession.delete(sessionId);
182746
+ pendingPiDecisionBySession.delete(sessionId);
182747
+ lastBoundMessageIdBySession.delete(sessionId);
182748
+ scheduledWriteTokensBySession.delete(sessionId);
182749
+ }
182750
+ function recordPendingTransformDecision(sessionId, decision) {
182751
+ if (!decision.bustedThisPass) {
182752
+ pendingDecisionBySession.delete(sessionId);
182753
+ return;
182754
+ }
182755
+ pendingDecisionBySession.set(sessionId, decision);
182756
+ }
182757
+ function scheduleOpenCodeTransformDecisionWrite(args) {
182758
+ const pending = pendingDecisionBySession.get(args.sessionId);
182759
+ if (!pending)
182760
+ return false;
182761
+ if (lastBoundMessageIdBySession.get(args.sessionId) === args.messageId) {
182762
+ return false;
182763
+ }
182764
+ const dbPath = getDatabasePath(args.db);
182765
+ if (!dbPath)
182766
+ return false;
182767
+ lastBoundMessageIdBySession.set(args.sessionId, args.messageId);
182768
+ pendingDecisionBySession.delete(args.sessionId);
182769
+ const token = addScheduledWriteToken(args.sessionId);
182770
+ setTimeout(() => {
182771
+ try {
182772
+ if (!hasScheduledWriteToken(args.sessionId, token))
182773
+ return;
182774
+ writeTransformDecisionBestEffort(dbPath, {
182775
+ ...pending,
182776
+ sessionId: args.sessionId,
182777
+ harness: "opencode",
182778
+ messageId: args.messageId,
182779
+ inputTokens: args.inputTokens
182780
+ });
182781
+ } finally {
182782
+ deleteScheduledWriteToken(args.sessionId, token);
182783
+ }
182784
+ }, 0);
182785
+ return true;
182786
+ }
182787
+ function addScheduledWriteToken(sessionId) {
182788
+ const token = Symbol(sessionId);
182789
+ let tokens = scheduledWriteTokensBySession.get(sessionId);
182790
+ if (!tokens) {
182791
+ tokens = new Set;
182792
+ scheduledWriteTokensBySession.set(sessionId, tokens);
182793
+ }
182794
+ tokens.add(token);
182795
+ return token;
182796
+ }
182797
+ function hasScheduledWriteToken(sessionId, token) {
182798
+ return scheduledWriteTokensBySession.get(sessionId)?.has(token) === true;
182799
+ }
182800
+ function deleteScheduledWriteToken(sessionId, token) {
182801
+ const tokens = scheduledWriteTokensBySession.get(sessionId);
182802
+ if (!tokens)
182803
+ return;
182804
+ tokens.delete(token);
182805
+ if (tokens.size === 0)
182806
+ scheduledWriteTokensBySession.delete(sessionId);
182807
+ }
182808
+ function writeTransformDecisionBestEffort(dbPath, row) {
182809
+ try {
182810
+ const writer = writerOverrideForTests ?? writeTransformDecisionRow;
182811
+ writer(dbPath, row);
182812
+ } catch {}
182813
+ }
182814
+ function writeTransformDecisionRow(dbPath, row) {
182815
+ const db = new Database(dbPath);
182816
+ try {
182817
+ db.exec("PRAGMA busy_timeout=0");
182818
+ db.prepare(`INSERT OR REPLACE INTO transform_decisions (
182819
+ session_id, harness, message_id, ts_ms, decision, materialized,
182820
+ materialize_reason, emergency, dropped_tokens, dropped_count, input_tokens
182821
+ ) 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)));
182822
+ } finally {
182823
+ closeQuietly(db);
182824
+ }
182825
+ }
182826
+
182827
+ // src/hooks/magic-context/event-handler.ts
182637
182828
  init_logger();
182638
182829
  init_models_dev_cache();
182639
- await init_storage();
182640
182830
 
182641
182831
  // src/hooks/magic-context/channel2-delivery.ts
182642
182832
  init_storage_meta_persisted();
@@ -182846,6 +183036,13 @@ function computePressure(input) {
182846
183036
  function approxThousands(tokens) {
182847
183037
  return `${Math.round(tokens / 1000)}k`;
182848
183038
  }
183039
+ function formatOldestReclaimableHint(hint) {
183040
+ if (!hint || hint.length === 0)
183041
+ return "";
183042
+ const rendered = hint.slice(0, 4).map((tag) => `§${tag.tagNumber}§ ${tag.toolName ?? "tool"}`).join(" · ");
183043
+ return rendered.length > 0 ? `
183044
+ oldest reclaimable: ${rendered}.` : "";
183045
+ }
182849
183046
  var CHANNEL2_USABLE_FRACTION = 1 / 3;
182850
183047
  var CHANNEL2_MIN_RECLAIMABLE = 1e4;
182851
183048
  function shouldTriggerChannel2(input) {
@@ -182855,14 +183052,16 @@ function shouldTriggerChannel2(input) {
182855
183052
  return true;
182856
183053
  return input.reclaimableTokens >= input.usableTokens * CHANNEL2_USABLE_FRACTION;
182857
183054
  }
182858
- function buildChannel2Reminder(undroppedTokens) {
183055
+ function buildChannel2Reminder(undroppedTokens, hint) {
182859
183056
  const amount = approxThousands(undroppedTokens);
183057
+ const hintText = formatOldestReclaimableHint(hint);
182860
183058
  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.
183059
+ ` + `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
183060
  ` + `</system-reminder>`;
182863
183061
  }
182864
- function buildChannel1Reminder(level, undroppedTokens) {
183062
+ function buildChannel1Reminder(level, undroppedTokens, hint) {
182865
183063
  const amount = approxThousands(undroppedTokens);
183064
+ const hintText = formatOldestReclaimableHint(hint);
182866
183065
  let body;
182867
183066
  switch (level) {
182868
183067
  case "gentle":
@@ -182878,7 +183077,7 @@ function buildChannel1Reminder(level, undroppedTokens) {
182878
183077
  return `
182879
183078
 
182880
183079
  <system-reminder>
182881
- ${body}
183080
+ ${body}${hintText}
182882
183081
  </system-reminder>`;
182883
183082
  }
182884
183083
 
@@ -182932,10 +183131,10 @@ async function maybeDeliverChannel2(sessionId, deps) {
182932
183131
  try {
182933
183132
  const client3 = getLiveServerClient(serverUrl, deps.directory);
182934
183133
  const promptContext = await resolvePromptContext(client3, sessionId);
182935
- const reminder = buildChannel2Reminder(deps.reclaimableTokens);
183134
+ const reminder = buildChannel2Reminder(deps.reclaimableTokens, deps.oldestReclaimableToolTags);
182936
183135
  const body = {
182937
183136
  noReply: false,
182938
- parts: [{ type: "text", text: reminder }]
183137
+ parts: [{ type: "text", text: reminder, synthetic: true }]
182939
183138
  };
182940
183139
  if (promptContext?.agent)
182941
183140
  body.agent = promptContext.agent;
@@ -183031,11 +183230,13 @@ function getMessageUpdatedAssistantInfo(properties) {
183031
183230
  }
183032
183231
  const tokens = isRecord(info.tokens) ? info.tokens : undefined;
183033
183232
  const cache = tokens && isRecord(tokens.cache) ? tokens.cache : undefined;
183233
+ const time3 = isRecord(info.time) ? info.time : undefined;
183034
183234
  return {
183035
183235
  role: "assistant",
183036
183236
  finish: typeof info.finish === "string" ? info.finish : undefined,
183037
183237
  sessionID: info.sessionID,
183038
183238
  messageID: typeof info.id === "string" ? info.id : undefined,
183239
+ completedAt: typeof time3?.completed === "number" ? time3.completed : undefined,
183039
183240
  providerID: typeof info.providerID === "string" ? info.providerID : undefined,
183040
183241
  modelID: typeof info.modelID === "string" ? info.modelID : undefined,
183041
183242
  tokens: {
@@ -183098,8 +183299,8 @@ init_project_identity();
183098
183299
  import * as crypto2 from "node:crypto";
183099
183300
  init_session_project_storage();
183100
183301
  init_storage_meta_persisted();
183101
- init_logger();
183102
183302
  await init_storage();
183303
+ init_logger();
183103
183304
 
183104
183305
  // src/hooks/magic-context/boundary-execution.ts
183105
183306
  var FORCE_MATERIALIZE_PERCENTAGE2 = 85;
@@ -183393,7 +183594,8 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
183393
183594
  const result = {
183394
183595
  compressedToLite: 0,
183395
183596
  compressedToFull: 0,
183396
- compressedToUltra: 0
183597
+ compressedToUltra: 0,
183598
+ mutatedTextTags: 0
183397
183599
  };
183398
183600
  if (!config2.enabled)
183399
183601
  return result;
@@ -183434,7 +183636,9 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
183434
183636
  const target = targets.get(tag.tagNumber);
183435
183637
  if (!target)
183436
183638
  continue;
183437
- target.setContent(compressed);
183639
+ const didMutate = target.setContent(compressed);
183640
+ if (didMutate)
183641
+ result.mutatedTextTags += 1;
183438
183642
  updateCavemanDepth(db, sessionId, tag.tagNumber, targetDepth);
183439
183643
  if (targetDepth === DEPTH_LITE)
183440
183644
  result.compressedToLite += 1;
@@ -184022,28 +184226,6 @@ function stripInlineThinking(messages, messageTagNumbers, clearReasoningAge) {
184022
184226
  }
184023
184227
  return stripped;
184024
184228
  }
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
184229
  var REASONING_IGNORED_PART_TYPES = new Set([
184048
184230
  "step-start",
184049
184231
  "step-finish",
@@ -184154,6 +184336,7 @@ function stripProcessedImages(messages, frozenIds, options) {
184154
184336
  init_temporal_awareness();
184155
184337
 
184156
184338
  // src/hooks/magic-context/transform-compartment-phase.ts
184339
+ init_compartment_storage();
184157
184340
  init_logger();
184158
184341
  await __promiseAll([
184159
184342
  init_storage(),
@@ -184162,9 +184345,29 @@ await __promiseAll([
184162
184345
  init_send_session_notification();
184163
184346
  await __promiseAll([
184164
184347
  init_inject_compartments(),
184165
- init_protected_tail_boundary()
184348
+ init_protected_tail_boundary(),
184349
+ init_read_session_chunk()
184166
184350
  ]);
184167
- async function runCompartmentPhase(args) {
184351
+ function runCompartmentPhase(args) {
184352
+ const historianRunnable = args.historianRunnable !== false;
184353
+ const willReadRawHistory = historianRunnable && args.canRunCompartments && getActiveCompartmentRun(args.sessionId) === undefined && (args.sessionMeta.compartmentInProgress || !args.skipAwaitForThisPass && args.contextUsage.percentage >= BLOCK_UNTIL_DONE_PERCENTAGE);
184354
+ if (!willReadRawHistory) {
184355
+ return runCompartmentPhaseImpl(args);
184356
+ }
184357
+ return withRawSessionMessageCache(() => {
184358
+ try {
184359
+ primeTailRawMessageCache({
184360
+ sessionId: args.resolvedSessionId,
184361
+ lastCompartmentEnd: getLastCompartmentEndMessage(args.db, args.resolvedSessionId),
184362
+ anchorMessageId: getLastCompartmentEndMessageId(args.db, args.resolvedSessionId)
184363
+ });
184364
+ } catch (error51) {
184365
+ sessionLog(args.sessionId, "compartment phase: tail prime failed (non-fatal):", error51);
184366
+ }
184367
+ return runCompartmentPhaseImpl(args);
184368
+ });
184369
+ }
184370
+ async function runCompartmentPhaseImpl(args) {
184168
184371
  let pendingCompartmentInjection = args.pendingCompartmentInjection;
184169
184372
  let compartmentInProgress = args.sessionMeta.compartmentInProgress;
184170
184373
  let published = false;
@@ -184461,78 +184664,11 @@ function appendReminderToUserMessage(message, reminder) {
184461
184664
 
184462
184665
  // src/hooks/magic-context/apply-operations.ts
184463
184666
  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
184667
  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) {
184668
+ function buildReplacementContent(tagId) {
184669
+ return `[dropped §${tagId}§]`;
184670
+ }
184671
+ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps, syntheticPendingOps = []) {
184536
184672
  let didMutateMessage = false;
184537
184673
  db.transaction(() => {
184538
184674
  const tags = preloadedTags ?? getTagsBySession(db, sessionId);
@@ -184540,11 +184676,16 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
184540
184676
  const tagTypeById = new Map(tags.map((tag) => [tag.tagNumber, tag.type]));
184541
184677
  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
184678
  const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
184679
+ const opsToApply = [
184680
+ ...pendingOps.map((op) => ({ op, synthetic: false })),
184681
+ ...syntheticPendingOps.map((op) => ({ op, synthetic: true }))
184682
+ ];
184543
184683
  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) {
184684
+ for (const { op: pendingOp, synthetic } of opsToApply) {
184545
184685
  const tagStatus = tagStatusById.get(pendingOp.tagId);
184546
184686
  if (tagStatus === "compacted" || tagStatus === "dropped") {
184547
- removePendingOp(db, sessionId, pendingOp.tagId);
184687
+ if (!synthetic)
184688
+ removePendingOp(db, sessionId, pendingOp.tagId);
184548
184689
  continue;
184549
184690
  }
184550
184691
  if (protectedTagIds.has(pendingOp.tagId)) {
@@ -184552,33 +184693,46 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
184552
184693
  }
184553
184694
  const target = targets.get(pendingOp.tagId);
184554
184695
  const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
184696
+ if (synthetic) {
184697
+ if (!isToolTag || target?.canDrop?.() !== true)
184698
+ continue;
184699
+ }
184700
+ let shouldPersistDrop = false;
184555
184701
  if (isToolTag) {
184556
184702
  if (skeletonWindow.has(pendingOp.tagId)) {
184557
184703
  const truncResult = target?.truncate?.() ?? "absent";
184558
- if (truncResult === "incomplete") {
184704
+ if (truncResult === "incomplete" || synthetic && truncResult !== "truncated") {
184559
184705
  continue;
184560
184706
  }
184561
184707
  if (truncResult === "truncated") {
184562
184708
  didMutateMessage = true;
184563
184709
  }
184564
184710
  updateTagDropMode(db, sessionId, pendingOp.tagId, "truncated");
184711
+ shouldPersistDrop = true;
184565
184712
  } else {
184566
184713
  const dropResult = target?.drop?.() ?? "absent";
184567
- if (dropResult === "incomplete") {
184714
+ if (dropResult === "incomplete" || synthetic && dropResult !== "removed") {
184568
184715
  continue;
184569
184716
  }
184570
184717
  if (dropResult === "removed") {
184571
184718
  didMutateMessage = true;
184572
184719
  }
184573
184720
  updateTagDropMode(db, sessionId, pendingOp.tagId, "full");
184721
+ shouldPersistDrop = true;
184574
184722
  }
184575
184723
  } else if (target) {
184576
- const changed = target.setContent(buildReplacementContent(pendingOp.tagId, target));
184724
+ const changed = target.setContent(buildReplacementContent(pendingOp.tagId));
184577
184725
  if (changed)
184578
184726
  didMutateMessage = true;
184727
+ shouldPersistDrop = true;
184728
+ } else if (!synthetic) {
184729
+ shouldPersistDrop = true;
184579
184730
  }
184731
+ if (!shouldPersistDrop)
184732
+ continue;
184580
184733
  updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
184581
- removePendingOp(db, sessionId, pendingOp.tagId);
184734
+ if (!synthetic)
184735
+ removePendingOp(db, sessionId, pendingOp.tagId);
184582
184736
  }
184583
184737
  })();
184584
184738
  return didMutateMessage;
@@ -184602,7 +184756,7 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
184602
184756
  }
184603
184757
  }
184604
184758
  } else if (target) {
184605
- const changed = target.setContent(buildReplacementContent(tag.tagNumber, target));
184759
+ const changed = target.setContent(buildReplacementContent(tag.tagNumber));
184606
184760
  if (changed)
184607
184761
  didMutateMessage = true;
184608
184762
  }
@@ -184779,6 +184933,7 @@ function createExistingTagResolver(sessionId, tagger, db) {
184779
184933
  return;
184780
184934
  }
184781
184935
  updateTagMessageId(db, sessionId, fallback.tagNumber, currentContentId);
184936
+ tagger.unbindTag(sessionId, fallback.contentId);
184782
184937
  tagger.bindTag(sessionId, currentContentId, fallback.tagNumber);
184783
184938
  usedTagNumbers.add(fallback.tagNumber);
184784
184939
  return fallback.tagNumber;
@@ -184799,7 +184954,45 @@ function logTransformTiming(sessionId, stage, startMs, extra) {
184799
184954
  }
184800
184955
 
184801
184956
  // src/hooks/magic-context/tag-messages.ts
184802
- function deriveToolOwnerMessageId(sessionId, db, message, obs, unpaired) {
184957
+ var TOOL_OWNER_CACHE_KEY_SEP = "\x00";
184958
+ function makeToolOwnerCacheKey(sessionId, callId) {
184959
+ return `${sessionId}${TOOL_OWNER_CACHE_KEY_SEP}${callId}`;
184960
+ }
184961
+ function getCachedCandidateToolOwners(db, sessionId, callId, cache, onLookup) {
184962
+ const key = makeToolOwnerCacheKey(sessionId, callId);
184963
+ const cached2 = cache.candidateOwnersByCallId.get(key);
184964
+ if (cached2 !== undefined)
184965
+ return cached2;
184966
+ onLookup?.({ kind: "candidates", callId });
184967
+ const candidates = getCandidateToolOwners(db, sessionId, callId);
184968
+ cache.candidateOwnersByCallId.set(key, candidates);
184969
+ return candidates;
184970
+ }
184971
+ function getCachedMessageTimesFromOpenCodeDb(sessionId, messageIds, cache, onLookup) {
184972
+ const uncached = [...new Set(messageIds)].filter((id) => !cache.messageTimesById.has(id));
184973
+ if (uncached.length > 0) {
184974
+ onLookup?.({ kind: "messageTimes", messageIds: uncached });
184975
+ const resolved = getMessageTimesFromOpenCodeDb(sessionId, uncached);
184976
+ for (const id of uncached) {
184977
+ cache.messageTimesById.set(id, resolved.get(id) ?? null);
184978
+ }
184979
+ }
184980
+ const times = new Map;
184981
+ for (const id of messageIds) {
184982
+ const time3 = cache.messageTimesById.get(id);
184983
+ if (typeof time3 === "number")
184984
+ times.set(id, time3);
184985
+ }
184986
+ return times;
184987
+ }
184988
+ function invalidateCachedCandidateToolOwnersIfNewOwner(cache, sessionId, callId, ownerMsgId) {
184989
+ const key = makeToolOwnerCacheKey(sessionId, callId);
184990
+ const cached2 = cache.candidateOwnersByCallId.get(key);
184991
+ if (cached2 !== undefined && !cached2.includes(ownerMsgId)) {
184992
+ cache.candidateOwnersByCallId.delete(key);
184993
+ }
184994
+ }
184995
+ function deriveToolOwnerMessageId(sessionId, db, message, obs, unpaired, cache, onFallbackLookup) {
184803
184996
  const messageId = typeof message.info.id === "string" ? message.info.id : "";
184804
184997
  if (obs.kind === "invocation") {
184805
184998
  if (messageId) {
@@ -184819,10 +185012,10 @@ function deriveToolOwnerMessageId(sessionId, db, message, obs, unpaired) {
184819
185012
  return popped;
184820
185013
  }
184821
185014
  if (messageId) {
184822
- const candidates = getCandidateToolOwners(db, sessionId, obs.callId);
185015
+ const candidates = getCachedCandidateToolOwners(db, sessionId, obs.callId, cache, onFallbackLookup);
184823
185016
  if (candidates.length > 0) {
184824
185017
  const ids = [...candidates, messageId];
184825
- const times = getMessageTimesFromOpenCodeDb(sessionId, ids);
185018
+ const times = getCachedMessageTimesFromOpenCodeDb(sessionId, ids, cache, onFallbackLookup);
184826
185019
  const persisted = pickNearestPriorOwner(candidates, messageId, times);
184827
185020
  if (persisted !== null)
184828
185021
  return persisted;
@@ -184903,6 +185096,7 @@ function extractToolTagMetadata(part) {
184903
185096
  }
184904
185097
  function tagMessages(sessionId, messages, tagger, db, options = {}) {
184905
185098
  const skipPrefixInjection = options.skipPrefixInjection === true;
185099
+ const onToolOwnerFallbackLookup = options.onToolOwnerFallbackLookup;
184906
185100
  const targets = new Map;
184907
185101
  const reasoningByMessage = new Map;
184908
185102
  const messageTagNumbers = new Map;
@@ -184910,6 +185104,10 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
184910
185104
  const toolThinkingByCallId = new Map;
184911
185105
  const toolCallIndex = new Map;
184912
185106
  const unpairedInvocations = new Map;
185107
+ const ownerDerivationCache = {
185108
+ candidateOwnersByCallId: new Map,
185109
+ messageTimesById: new Map
185110
+ };
184913
185111
  const ownerByPartKey = new Map;
184914
185112
  const batch = new ToolMutationBatch(messages);
184915
185113
  const assignments = tagger.getAssignments(sessionId);
@@ -184950,7 +185148,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
184950
185148
  const toolObservation = extractToolCallObservation(part);
184951
185149
  if (toolObservation) {
184952
185150
  const _tDerive = performance.now();
184953
- const ownerMsgId = deriveToolOwnerMessageId(sessionId, db, message, toolObservation, unpairedInvocations);
185151
+ const ownerMsgId = deriveToolOwnerMessageId(sessionId, db, message, toolObservation, unpairedInvocations, ownerDerivationCache, onToolOwnerFallbackLookup);
184954
185152
  accDerive += performance.now() - _tDerive;
184955
185153
  const compositeKey = makeToolCompositeKey(ownerMsgId, toolObservation.callId);
184956
185154
  const entry = toolCallIndex.get(compositeKey) ?? {
@@ -184969,6 +185167,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
184969
185167
  if (orphan !== null) {
184970
185168
  const claimed = adoptNullOwnerToolTag(db, orphan.id, ownerMsgId);
184971
185169
  if (claimed) {
185170
+ invalidateCachedCandidateToolOwnersIfNewOwner(ownerDerivationCache, sessionId, toolObservation.callId, ownerMsgId);
184972
185171
  tagger.bindToolTag(sessionId, toolObservation.callId, ownerMsgId, orphan.tagNumber);
184973
185172
  existingTagId = orphan.tagNumber;
184974
185173
  } else {
@@ -184976,6 +185175,13 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
184976
185175
  }
184977
185176
  }
184978
185177
  }
185178
+ if (existingTagId === undefined) {
185179
+ const persisted = getToolTagNumberByOwner(db, sessionId, toolObservation.callId, ownerMsgId);
185180
+ if (persisted !== null) {
185181
+ tagger.bindToolTag(sessionId, toolObservation.callId, ownerMsgId, persisted);
185182
+ existingTagId = persisted;
185183
+ }
185184
+ }
184979
185185
  if (existingTagId !== undefined) {
184980
185186
  toolTagByCallId.set(compositeKey, existingTagId);
184981
185187
  messageTagNumbers.set(message, Math.max(messageTagNumbers.get(message) ?? 0, existingTagId));
@@ -185048,6 +185254,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
185048
185254
  inputTokenCount,
185049
185255
  reasoningTokenCount: reasoningTokens
185050
185256
  }));
185257
+ invalidateCachedCandidateToolOwnersIfNewOwner(ownerDerivationCache, sessionId, toolPart.callID, ownerMsgId);
185051
185258
  accAssignToolTag += performance.now() - _tAssignTool;
185052
185259
  messageTagNumbers.set(message, Math.max(messageTagNumbers.get(message) ?? 0, tagId));
185053
185260
  if (!skipPrefixInjection) {
@@ -185119,7 +185326,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
185119
185326
  logTransformTiming(sessionId, "tag.saveSource", performance.now() - accSaveSource);
185120
185327
  for (const [compositeKey, tagId] of toolTagByCallId) {
185121
185328
  const thinkingParts = toolThinkingByCallId.get(compositeKey) ?? [];
185122
- targets.set(tagId, createToolDropTarget(compositeKey, thinkingParts, toolCallIndex, batch));
185329
+ targets.set(tagId, createToolDropTarget(compositeKey, thinkingParts, toolCallIndex, batch, tagId));
185123
185330
  }
185124
185331
  const hasRecentReduceCall = lastReduceMessageIndex >= 0 && messages.length - lastReduceMessageIndex <= RECENT_REDUCE_LOOKBACK;
185125
185332
  return {
@@ -186046,7 +186253,7 @@ async function runAutoSearchHint(args) {
186046
186253
  embeddingEnabled,
186047
186254
  gitCommitsEnabled,
186048
186255
  embedQuery: async (text, signal) => {
186049
- const result = await embedTextForProject(options.projectPath, text, signal);
186256
+ const result = await embedTextForProject(options.projectPath, text, signal, "query");
186050
186257
  return result?.vector ?? null;
186051
186258
  },
186052
186259
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
@@ -186227,6 +186434,51 @@ function planEmergencyDrop(input) {
186227
186434
  };
186228
186435
  }
186229
186436
 
186437
+ // src/hooks/magic-context/system-injection-stripper.ts
186438
+ var SYSTEM_INJECTION_MARKERS = [
186439
+ "<!-- OMO_INTERNAL_INITIATOR -->",
186440
+ "[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
186441
+ "[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
186442
+ "[Category+Skill Reminder]",
186443
+ "[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
186444
+ "[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
186445
+ "[EMERGENCY CONTEXT WINDOW WARNING]",
186446
+ "Unstable background agent appears idle",
186447
+ "**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
186448
+ ];
186449
+ var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
186450
+ var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
186451
+ function stripSystemInjection(text) {
186452
+ let hasInjection = false;
186453
+ for (const marker of SYSTEM_INJECTION_MARKERS) {
186454
+ if (text.includes(marker)) {
186455
+ hasInjection = true;
186456
+ break;
186457
+ }
186458
+ }
186459
+ if (SYSTEM_REMINDER_REGEX.test(text))
186460
+ hasInjection = true;
186461
+ SYSTEM_REMINDER_REGEX.lastIndex = 0;
186462
+ if (!hasInjection)
186463
+ return null;
186464
+ let cleaned = text;
186465
+ cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
186466
+ cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
186467
+ cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
186468
+ for (const marker of SYSTEM_INJECTION_MARKERS) {
186469
+ if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
186470
+ continue;
186471
+ const idx = cleaned.indexOf(marker);
186472
+ if (idx === -1)
186473
+ continue;
186474
+ const blockEnd = cleaned.indexOf(`
186475
+
186476
+ `, idx + marker.length);
186477
+ cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
186478
+ }
186479
+ return cleaned.trim();
186480
+ }
186481
+
186230
186482
  // src/hooks/magic-context/heuristic-cleanup.ts
186231
186483
  init_tag_part_guards();
186232
186484
  var DEDUP_SAFE_TOOLS = new Set([
@@ -186245,6 +186497,7 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
186245
186497
  const maxTag = getMaxTagNumberBySession(db, sessionId);
186246
186498
  const protectedCutoff = maxTag - config2.protectedTags;
186247
186499
  let droppedTools = 0;
186500
+ let emergencyDroppedTools = 0;
186248
186501
  let deduplicatedTools = 0;
186249
186502
  let droppedInjections = 0;
186250
186503
  if (config2.emergency) {
@@ -186276,6 +186529,7 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
186276
186529
  updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
186277
186530
  updateTagDropMode(db, sessionId, tag.tagNumber, "full");
186278
186531
  droppedTools++;
186532
+ emergencyDroppedTools++;
186279
186533
  }
186280
186534
  }
186281
186535
  setEmergencyDropSample(db, sessionId, emergency.currentTotalInputTokens);
@@ -186354,7 +186608,9 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
186354
186608
  continue;
186355
186609
  updateTagDropMode(db, sessionId, tag.tagNumber, "full");
186356
186610
  updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
186357
- deduplicatedTools++;
186611
+ if (result === "removed" || result === "truncated") {
186612
+ deduplicatedTools++;
186613
+ }
186358
186614
  }
186359
186615
  }
186360
186616
  })();
@@ -186363,6 +186619,7 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
186363
186619
  sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
186364
186620
  }
186365
186621
  let compressedTextTags = 0;
186622
+ let mutatedTextTags = 0;
186366
186623
  if (config2.caveman?.enabled) {
186367
186624
  const cavemanResult = applyCavemanCleanup(sessionId, db, targets, tags, {
186368
186625
  enabled: true,
@@ -186370,8 +186627,16 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
186370
186627
  protectedTags: config2.protectedTags
186371
186628
  });
186372
186629
  compressedTextTags = cavemanResult.compressedToLite + cavemanResult.compressedToFull + cavemanResult.compressedToUltra;
186630
+ mutatedTextTags = cavemanResult.mutatedTextTags;
186373
186631
  }
186374
- return { droppedTools, deduplicatedTools, droppedInjections, compressedTextTags };
186632
+ return {
186633
+ droppedTools,
186634
+ deduplicatedTools,
186635
+ droppedInjections,
186636
+ emergencyDroppedTools,
186637
+ compressedTextTags,
186638
+ mutatedTextTags
186639
+ };
186375
186640
  }
186376
186641
  function extractToolInfo(part) {
186377
186642
  if (part.type === "tool" && typeof part.tool === "string" && DEDUP_SAFE_TOOLS.has(part.tool)) {
@@ -186545,6 +186810,42 @@ function isTodoItem(value) {
186545
186810
  return typeof todo.content === "string" && typeof todo.status === "string" && (todo.priority === undefined || typeof todo.priority === "string");
186546
186811
  }
186547
186812
 
186813
+ // src/hooks/magic-context/tool-reclaim.ts
186814
+ await init_storage();
186815
+ function buildSyntheticToolReclaimOps(input) {
186816
+ const watermark = Math.max(0, input.watermark);
186817
+ if (watermark <= 0)
186818
+ return [];
186819
+ const realPendingTagIds = new Set((input.pendingOps ?? []).map((op) => op.tagId));
186820
+ const tags = getActiveTagsBySession(input.db, input.sessionId);
186821
+ const synthetic = [];
186822
+ for (const tag of tags) {
186823
+ if (tag.type !== "tool")
186824
+ continue;
186825
+ if (tag.status !== "active")
186826
+ continue;
186827
+ if (tag.tagNumber > watermark)
186828
+ continue;
186829
+ if (realPendingTagIds.has(tag.tagNumber))
186830
+ continue;
186831
+ if (input.targets.get(tag.tagNumber)?.canDrop?.() !== true)
186832
+ continue;
186833
+ synthetic.push({
186834
+ id: 0,
186835
+ sessionId: input.sessionId,
186836
+ tagId: tag.tagNumber,
186837
+ operation: "drop",
186838
+ queuedAt: 0
186839
+ });
186840
+ }
186841
+ return synthetic;
186842
+ }
186843
+ function advanceToolReclaimWatermarkToCurrentMax(db, sessionId) {
186844
+ const maxTagNumber = getMaxTagNumberBySession(db, sessionId);
186845
+ advanceToolReclaimWatermark(db, sessionId, maxTagNumber);
186846
+ return maxTagNumber;
186847
+ }
186848
+
186548
186849
  // src/hooks/magic-context/transform-postprocess-phase.ts
186549
186850
  var DEGRADE_CACHE_WARNING_THRESHOLD = 10;
186550
186851
  var degradedCacheCountBySession = new BoundedSessionMap(100);
@@ -186561,7 +186862,6 @@ async function runPostTransformPhase(args) {
186561
186862
  const emergencyDropEligible = args.contextUsage.percentage >= args.forceMaterializationPercentage;
186562
186863
  const activeCompartmentRun = args.canRunCompartments ? getActiveCompartmentRun(args.sessionId) : undefined;
186563
186864
  const compartmentRunning = args.canRunCompartments && !args.awaitedCompartmentRun && activeCompartmentRun !== undefined;
186564
- const emergencyBypassCompartmentGate = forceMaterialization;
186565
186865
  const deferredMaterialize = args.canConsumeDeferredLate && deferredMaterializationWasPending;
186566
186866
  const materializationRequested = isExplicitFlush || deferredMaterialize;
186567
186867
  const m0M1EnabledForFold = args.fullFeatureMode && args.m0M1 !== undefined && (!!args.m0M1.projectPath || !!args.m0M1.projectDirectory);
@@ -186573,11 +186873,12 @@ async function runPostTransformPhase(args) {
186573
186873
  projectDirectory: args.m0M1.projectDirectory,
186574
186874
  hardSignals: args.m0M1.hardSignals
186575
186875
  }).value : false;
186876
+ const bypassCompartmentGate = forceMaterialization || m0HardFoldThisPass;
186576
186877
  const shouldReadPendingOps = materializationRequested || args.schedulerDecision === "execute" || forceMaterialization || m0HardFoldThisPass || compartmentRunning;
186577
186878
  const pendingOps = shouldReadPendingOps ? getPendingOps(args.db, args.sessionId) : [];
186578
186879
  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));
186880
+ const shouldApplyPendingOps = (args.schedulerDecision === "execute" || materializationRequested || forceMaterialization || m0HardFoldThisPass) && (!compartmentRunning || bypassCompartmentGate);
186881
+ const shouldRunHeuristics = (!compartmentRunning || bypassCompartmentGate) && (materializationRequested || forceMaterialization || m0HardFoldThisPass || emergencyDropEligible || args.schedulerDecision === "execute" && (!alreadyRanThisTurn || !args.fullFeatureMode));
186581
186882
  const isCacheBustingPass = shouldApplyPendingOps || shouldRunHeuristics;
186582
186883
  const canUseEmptySentinels = modelAcceptsEmptyContent(args.resolvedProviderID);
186583
186884
  if (shouldRunHeuristics) {
@@ -186589,8 +186890,9 @@ async function runPostTransformPhase(args) {
186589
186890
  sessionLog(args.sessionId, `transform: skipping heuristics (already ran for turn ${args.currentTurnId})`);
186590
186891
  }
186591
186892
  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)}%)`);
186893
+ if (bypassCompartmentGate) {
186894
+ const bypassReason = forceMaterialization ? "emergency >=85%" : "m0 hard fold";
186895
+ sessionLog(args.sessionId, `transform: compartment-gate bypass (${bypassReason}) — applying ${pendingOps.length} pending ops while compartment agent runs (${args.contextUsage.percentage.toFixed(1)}%)`);
186594
186896
  } else {
186595
186897
  sessionLog(args.sessionId, "transform: deferring pending ops — compartment agent in progress");
186596
186898
  }
@@ -186599,12 +186901,24 @@ async function runPostTransformPhase(args) {
186599
186901
  let deferredMaterializedSuccessfully = false;
186600
186902
  let heuristicsRanSuccessfully = false;
186601
186903
  let pendingOpsRanSuccessfully = false;
186904
+ let pendingOpsDidMutate = false;
186905
+ let heuristicOrReasoningDidMutate = false;
186906
+ let droppedCount = 0;
186907
+ const droppedTokens = 0;
186908
+ let emergency = false;
186909
+ let m0RematerializedThisPass = false;
186910
+ let m0MaterializeReason = null;
186911
+ let m0M1InjectedThisPass = false;
186912
+ let autoReclaimDidMutateThisPass = false;
186602
186913
  try {
186603
186914
  if (shouldApplyPendingOps) {
186604
186915
  const applyReason = isExplicitFlush ? "explicit_flush" : deferredMaterialize ? "deferred_materialization" : `scheduler_execute (scheduler=${args.schedulerDecision})`;
186605
186916
  sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOps.length}, context=${args.contextUsage.percentage.toFixed(1)}%`);
186606
186917
  const tApply = performance.now();
186607
- applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, pendingOps);
186918
+ pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, pendingOps);
186919
+ if (pendingOpsDidMutate) {
186920
+ droppedCount += pendingOps.length;
186921
+ }
186608
186922
  logTransformTiming(args.sessionId, "applyPendingOperations", tApply);
186609
186923
  }
186610
186924
  if (shouldRunHeuristics) {
@@ -186622,9 +186936,12 @@ async function runPostTransformPhase(args) {
186622
186936
  } : undefined,
186623
186937
  caveman: cavemanConfig
186624
186938
  }, heuristicTags);
186625
- logTransformTiming(args.sessionId, "applyHeuristicCleanup", t5, `droppedTools=${cleanup.droppedTools} deduplicatedTools=${cleanup.deduplicatedTools} droppedInjections=${cleanup.droppedInjections} compressedTextTags=${cleanup.compressedTextTags}`);
186939
+ logTransformTiming(args.sessionId, "applyHeuristicCleanup", t5, `droppedTools=${cleanup.droppedTools} deduplicatedTools=${cleanup.deduplicatedTools} droppedInjections=${cleanup.droppedInjections} compressedTextTags=${cleanup.compressedTextTags} mutatedTextTags=${cleanup.mutatedTextTags}`);
186940
+ const heuristicMutationCount = cleanup.droppedTools + cleanup.deduplicatedTools + cleanup.droppedInjections + cleanup.mutatedTextTags;
186941
+ droppedCount += cleanup.droppedTools + cleanup.deduplicatedTools + cleanup.droppedInjections + cleanup.mutatedTextTags;
186942
+ emergency ||= cleanup.emergencyDroppedTools > 0;
186626
186943
  const t7 = performance.now();
186627
- const clearedReasoning = clearOldReasoning(args.messages, args.reasoningByMessage, args.messageTagNumbers, args.clearReasoningAge);
186944
+ const clearedReasoning = canUseEmptySentinels ? clearOldReasoning(args.messages, args.reasoningByMessage, args.messageTagNumbers, args.clearReasoningAge) : 0;
186628
186945
  if (canUseEmptySentinels) {
186629
186946
  stripClearedReasoning(args.messages);
186630
186947
  }
@@ -186648,6 +186965,8 @@ async function runPostTransformPhase(args) {
186648
186965
  }
186649
186966
  }
186650
186967
  logTransformTiming(args.sessionId, "clearOldReasoning", t7);
186968
+ heuristicOrReasoningDidMutate = heuristicMutationCount + clearedReasoning + strippedInline > 0;
186969
+ droppedCount += clearedReasoning + strippedInline;
186651
186970
  if (pendingMaterializationAtPassStart) {
186652
186971
  args.pendingMaterializationSessions.delete(args.sessionId);
186653
186972
  }
@@ -186658,7 +186977,35 @@ async function runPostTransformPhase(args) {
186658
186977
  if (args.schedulerDecision === "execute" && !materializationRequested) {
186659
186978
  updateSessionMeta(args.db, args.sessionId, { lastResponseTime: Date.now() });
186660
186979
  }
186980
+ const toolReclaimExecutePass = args.schedulerDecision === "execute";
186981
+ const alreadyMutatingThisPass = pendingOpsDidMutate || heuristicOrReasoningDidMutate;
186982
+ let autoReclaimTargetCount = 0;
186983
+ let autoReclaimDidMutate = false;
186984
+ if (toolReclaimExecutePass && alreadyMutatingThisPass && !emergencyDropEligible) {
186985
+ const syntheticPendingOps = buildSyntheticToolReclaimOps({
186986
+ db: args.db,
186987
+ sessionId: args.sessionId,
186988
+ targets: args.targets,
186989
+ watermark: args.sessionMeta.toolReclaimWatermark ?? 0,
186990
+ pendingOps
186991
+ });
186992
+ autoReclaimTargetCount = syntheticPendingOps.length;
186993
+ if (syntheticPendingOps.length > 0) {
186994
+ autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, [], syntheticPendingOps);
186995
+ if (autoReclaimDidMutate) {
186996
+ droppedCount += syntheticPendingOps.length;
186997
+ autoReclaimDidMutateThisPass = true;
186998
+ }
186999
+ }
187000
+ }
186661
187001
  args.batch?.finalize();
187002
+ if (toolReclaimExecutePass) {
187003
+ const maxTagNumber = advanceToolReclaimWatermarkToCurrentMax(args.db, args.sessionId);
187004
+ args.sessionMeta.toolReclaimWatermark = Math.max(args.sessionMeta.toolReclaimWatermark ?? 0, maxTagNumber);
187005
+ }
187006
+ if (autoReclaimTargetCount > 0) {
187007
+ sessionLog(args.sessionId, `tool reclaim auto-drop: targets=${autoReclaimTargetCount} mutated=${autoReclaimDidMutate}`);
187008
+ }
186662
187009
  logTransformTiming(args.sessionId, "batchFinalize:heuristics", performance.now());
186663
187010
  if (args.sessionMeta.lastTransformError !== null) {
186664
187011
  updateSessionMeta(args.db, args.sessionId, { lastTransformError: null });
@@ -186670,11 +187017,6 @@ async function runPostTransformPhase(args) {
186670
187017
  deferredMaterializedSuccessfully = true;
186671
187018
  heuristicsRanSuccessfully = true;
186672
187019
  }
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
187020
  if (shouldApplyPendingOps) {
186679
187021
  pendingOpsRanSuccessfully = true;
186680
187022
  }
@@ -186733,6 +187075,9 @@ async function runPostTransformPhase(args) {
186733
187075
  hardSignals: args.m0M1.hardSignals
186734
187076
  });
186735
187077
  if (result.injected) {
187078
+ m0M1InjectedThisPass = true;
187079
+ m0RematerializedThisPass ||= result.m0RematerializedThisPass;
187080
+ m0MaterializeReason = result.decision.reason ?? m0MaterializeReason;
186736
187081
  sessionLog(args.sessionId, `transform: injected m[0]/m[1] (rematerialized=${result.m0RematerializedThisPass}, reason=${result.decision.reason ?? "cache_hit"})`);
186737
187082
  }
186738
187083
  } catch (error51) {
@@ -186941,7 +187286,19 @@ async function runPostTransformPhase(args) {
186941
187286
  sessionLog(args.sessionId, `sticky-injection GC: pruned ${prunedAnchors} note-nudge anchor(s), ${prunedDecisions} auto-search decision(s)`);
186942
187287
  }
186943
187288
  }
186944
- return { explicitMaterializedSuccessfully, deferredMaterializedSuccessfully };
187289
+ const materializeReason = m0MaterializeReason ?? (explicitMaterializedSuccessfully ? "explicit_flush" : null);
187290
+ const materialized = m0RematerializedThisPass || explicitMaterializedSuccessfully || deferredMaterializedSuccessfully;
187291
+ const bustedThisPass = args.didMutateFromFlushedStatuses || pendingOpsDidMutate || heuristicOrReasoningDidMutate || autoReclaimDidMutateThisPass || m0RematerializedThisPass || m0M1InjectedThisPass && historyWasConsumedThisPass || historyWasConsumedThisPass;
187292
+ return {
187293
+ explicitMaterializedSuccessfully,
187294
+ deferredMaterializedSuccessfully,
187295
+ materialized,
187296
+ materializeReason,
187297
+ droppedTokens,
187298
+ droppedCount,
187299
+ emergency,
187300
+ bustedThisPass
187301
+ };
186945
187302
  }
186946
187303
  function checkM0MutationDriftAndSignal(args) {
186947
187304
  const currentMaxMutationId = getMaxM0MutationId(args.db, args.sessionId) ?? 0;
@@ -186976,6 +187333,12 @@ function clearMessageTokensCache(sessionId, messageId) {
186976
187333
  cache.delete(messageId);
186977
187334
  }
186978
187335
  var recordedSessionProjectIdentity = new BoundedSessionMap(MESSAGE_TOKENS_CACHE_MAX);
187336
+ function deriveTaggerLoadFloor(messages, sessionId, db) {
187337
+ return deriveTagLoadFloor(db, sessionId, function* () {
187338
+ for (const message of messages)
187339
+ yield message.info?.id;
187340
+ }());
187341
+ }
186979
187342
  function findLastAssistantModel2(messages) {
186980
187343
  for (let i = messages.length - 1;i >= 0; i--) {
186981
187344
  const info = messages[i].info;
@@ -186998,6 +187361,7 @@ function createTransform(deps) {
186998
187361
  return;
186999
187362
  }
187000
187363
  const resolvedSessionId = sessionId;
187364
+ clearOpenCodePendingTransformDecision(sessionId);
187001
187365
  logTransformTiming(sessionId, "findSessionId", startTime, `messages=${messages.length}`);
187002
187366
  const db = deps.db;
187003
187367
  if (deps.client !== undefined) {
@@ -187300,12 +187664,16 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187300
187664
  recordSessionProjectIdentity(db, sessionId, sessionProjectIdentity);
187301
187665
  recordedSessionProjectIdentity.set(sessionId, sessionProjectIdentity);
187302
187666
  }
187667
+ const taggerFloor = deriveTaggerLoadFloor(messages, sessionId, db);
187668
+ if (taggerFloor === 0 && messages.length > 0) {
187669
+ sessionLog(sessionId, `tag floor: 0 (full-scan fallback) — no leading wire message resolved a tag across ${messages.length} msgs`);
187670
+ }
187303
187671
  let triggerBoundarySnapshot;
187304
187672
  if (fullFeatureMode && historianRunnable && !sessionMeta.compartmentInProgress) {
187305
187673
  const tTrigger = performance.now();
187306
187674
  try {
187307
187675
  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);
187676
+ const triggerResult = checkCompartmentTrigger(db, sessionId, sessionMeta, boundaryUsageForProtectedTail, sessionMeta.lastContextPercentage, boundaryExecuteThreshold, deriveTriggerBudget(boundaryContextLimit, boundaryExecuteThreshold), deps.clearReasoningAge, deps.commitClusterTrigger, undefined, boundaryContextLimit, inMemoryTail, taggerFloor);
187309
187677
  if (triggerResult.shouldFire) {
187310
187678
  sessionLog(sessionId, `compartment trigger: firing (reason=${triggerResult.reason})`);
187311
187679
  updateSessionMeta(db, sessionId, { compartmentInProgress: true });
@@ -187347,7 +187715,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187347
187715
  try {
187348
187716
  const t0 = performance.now();
187349
187717
  const tInitFromDb = performance.now();
187350
- deps.tagger.initFromDb(sessionId, db);
187718
+ deps.tagger.initFromDb(sessionId, db, taggerFloor);
187351
187719
  logTransformTiming(sessionId, "tag.initFromDb", tInitFromDb);
187352
187720
  const skipPrefixInjection = !ctxReduceEnabledEffective;
187353
187721
  const result = tagMessages(sessionId, messages, deps.tagger, db, {
@@ -187399,7 +187767,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187399
187767
  const persistedReasoningWatermark = sessionMeta?.clearedReasoningThroughTag ?? 0;
187400
187768
  if (persistedReasoningWatermark > 0) {
187401
187769
  const tReplay = performance.now();
187402
- const replayed = replayClearedReasoning(messages, reasoningByMessage, messageTagNumbers, persistedReasoningWatermark);
187770
+ const replayed = canUseEmptySentinels ? replayClearedReasoning(messages, reasoningByMessage, messageTagNumbers, persistedReasoningWatermark) : 0;
187403
187771
  const replayedInline = replayStrippedInlineThinking(messages, messageTagNumbers, persistedReasoningWatermark);
187404
187772
  if (replayed > 0 || replayedInline > 0) {
187405
187773
  sessionLog(sessionId, `reasoning replay: cleared=${replayed} inlineStripped=${replayedInline} (watermark=${persistedReasoningWatermark})`);
@@ -187497,7 +187865,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187497
187865
  const wasEmergencyBlock = contextUsageEarly.percentage >= FORCE_MATERIALIZE_PERCENTAGE && compartmentPhase.justAwaitedPublication;
187498
187866
  const historyRebuiltThisPass = wasEmergencyBlock ? compartmentPhase.rebuiltHistoryThisPass : rebuiltHistoryFromInitialPrepare || compartmentPhase.rebuiltHistoryThisPass;
187499
187867
  const tPostProcess = performance.now();
187500
- await runPostTransformPhase({
187868
+ const postTransformResult = await runPostTransformPhase({
187501
187869
  sessionId,
187502
187870
  db,
187503
187871
  messages,
@@ -187551,6 +187919,19 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187551
187919
  hardSignals: m0HardSignals
187552
187920
  }
187553
187921
  });
187922
+ if (postTransformResult.bustedThisPass) {
187923
+ recordPendingTransformDecision(sessionId, {
187924
+ tsMs: Date.now(),
187925
+ decision: schedulerDecision,
187926
+ materialized: postTransformResult.materialized,
187927
+ materializeReason: normalizeMaterializeReason("opencode", postTransformResult.materializeReason, postTransformResult.materialized),
187928
+ emergency: postTransformResult.emergency,
187929
+ droppedTokens: postTransformResult.droppedTokens,
187930
+ droppedCount: postTransformResult.droppedCount,
187931
+ inputTokens: contextUsage.inputTokens,
187932
+ bustedThisPass: true
187933
+ });
187934
+ }
187554
187935
  logTransformTiming(sessionId, "postTransformPhase", tPostProcess);
187555
187936
  const msgTokens = getMessageTokensCache(sessionId);
187556
187937
  let storedByMessage;
@@ -187701,6 +188082,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187701
188082
  const executeThresholdTokens = Math.round((resolvedContextLimit ?? 0) * resolvedExecuteThresholdPct / 100);
187702
188083
  const usableTokens = Math.max(0, executeThresholdTokens - contextUsage.inputTokens + liveTailTokens);
187703
188084
  resetLastNudgeCycleIfTailShrank(db, sessionId, tailToolTokens);
188085
+ const oldestReclaimableToolTags = getOldestActiveUnprotectedToolTags(db, sessionId, deps.protectedTags);
187704
188086
  deps.channel1StateBySession.set(sessionId, {
187705
188087
  tailToolTokens,
187706
188088
  historyBudgetTokens: historyBudgetTokens ?? 0,
@@ -187709,9 +188091,10 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187709
188091
  lastInputTokens: contextUsage.inputTokens,
187710
188092
  turnToolTokens: 0,
187711
188093
  usableTokens,
187712
- reducedSinceRefresh: false
188094
+ reducedSinceRefresh: false,
188095
+ oldestReclaimableToolTags
187713
188096
  });
187714
- const channel2MetricsKnown = fullFeatureMode && resolvedContextLimit !== undefined && resolvedContextLimit > 0 && resolvedExecuteThresholdPct > 0;
188097
+ const channel2MetricsKnown = resolvedContextLimit !== undefined && resolvedContextLimit > 0 && resolvedExecuteThresholdPct > 0;
187715
188098
  if (channel2MetricsKnown) {
187716
188099
  const channel2ShouldTrigger = shouldTriggerChannel2({
187717
188100
  reclaimableTokens: tailToolTokens,
@@ -187735,6 +188118,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
187735
188118
  }
187736
188119
  const elapsed = (performance.now() - startTime).toFixed(1);
187737
188120
  sessionLog(sessionId, `transform completed in ${elapsed}ms (${messages.length} messages, ${targets.size} targets, watermark: ${watermark})`);
188121
+ deps.maybeAutoEmbedSession?.(sessionId);
187738
188122
  };
187739
188123
  }
187740
188124
  function resolveHistoryBudgetTokens(historyBudgetPercentage, contextUsage, executeThresholdPercentage, modelKey, executeThresholdTokens, resolvedContextLimit) {
@@ -187780,18 +188164,14 @@ function evictExpiredUsageEntries(contextUsageMap) {
187780
188164
  }
187781
188165
  async function deliverChannel2IfPending(deps, sessionId) {
187782
188166
  try {
187783
- try {
187784
- const meta3 = getOrCreateSessionMeta(deps.db, sessionId);
187785
- if (meta3.isSubagent)
187786
- return;
187787
- } catch {}
187788
188167
  const baseline = deps.channel1StateBySession?.get(sessionId);
187789
188168
  await maybeDeliverChannel2(sessionId, {
187790
188169
  db: deps.db,
187791
188170
  serverUrl: deps.serverUrl,
187792
188171
  directory: deps.directory ?? ".",
187793
188172
  reclaimableTokens: baseline ? baseline.tailToolTokens + baseline.turnToolTokens : undefined,
187794
- usableTokens: baseline?.usableTokens
188173
+ usableTokens: baseline?.usableTokens,
188174
+ oldestReclaimableToolTags: baseline?.oldestReclaimableToolTags
187795
188175
  });
187796
188176
  } catch (error51) {
187797
188177
  sessionLog(sessionId, "channel2 delivery wrapper failed (ignored):", error51);
@@ -187933,6 +188313,15 @@ function createEventHandler2(deps) {
187933
188313
  info.tokens?.cache?.write
187934
188314
  ];
187935
188315
  const hasUsageTokens = usageTokens.some((value) => typeof value === "number" && value > 0);
188316
+ const terminalAssistantUpdate = info.messageID !== undefined && hasUsageTokens && (typeof info.finish === "string" || typeof info.completedAt === "number");
188317
+ if (terminalAssistantUpdate && info.messageID) {
188318
+ scheduleOpenCodeTransformDecisionWrite({
188319
+ db: deps.db,
188320
+ sessionId: info.sessionID,
188321
+ messageId: info.messageID,
188322
+ inputTokens: (info.tokens?.input ?? 0) + (info.tokens?.cache?.read ?? 0) + (info.tokens?.cache?.write ?? 0)
188323
+ });
188324
+ }
187936
188325
  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
188326
  const hasKnownUsage = hasUsageTokens || deps.contextUsageMap.has(info.sessionID);
187938
188327
  if (!hasKnownUsage) {
@@ -188090,6 +188479,7 @@ function createEventHandler2(deps) {
188090
188479
  deps.onSessionDeleted?.(sessionId);
188091
188480
  deps.contextUsageMap.delete(sessionId);
188092
188481
  deps.tagger.cleanup(sessionId);
188482
+ clearTransformDecisionSession(sessionId);
188093
188483
  clearMessageTokensCache(sessionId);
188094
188484
  invalidateTrueRawTokenCache({ sessionId, reason: "session.deleted" });
188095
188485
  return;
@@ -188097,6 +188487,46 @@ function createEventHandler2(deps) {
188097
188487
  };
188098
188488
  }
188099
188489
 
188490
+ // src/hooks/magic-context/format-embed-status.ts
188491
+ function formatEmbedStatusText(coverage, drain) {
188492
+ if (!coverage.enabled) {
188493
+ return "Embedding is off (no provider configured).";
188494
+ }
188495
+ const lines = [];
188496
+ lines.push(`Embedding — model: ${coverage.model} (${coverage.provider})`);
188497
+ lines.push(`This session: ${coverage.session.embedded} / ${coverage.session.total} compartments embedded`);
188498
+ lines.push(`Project memories: ${coverage.memories.embedded} / ${coverage.memories.total} embedded`);
188499
+ if (coverage.commits.gitEnabled) {
188500
+ lines.push(`Git commits: ${coverage.commits.embedded} / ${coverage.commits.total}`);
188501
+ } else {
188502
+ lines.push("Git commits: 0 / 0 (git indexing off)");
188503
+ }
188504
+ let drainLine = "Drain: idle";
188505
+ switch (drain.status) {
188506
+ case "running": {
188507
+ const e = drain.embedded ?? coverage.session.embedded;
188508
+ const t = drain.total ?? coverage.session.total;
188509
+ const failedSuffix = drain.failed && drain.failed > 0 ? ` (${drain.failed} failed)` : "";
188510
+ drainLine = `Drain: running ${e}/${t}${failedSuffix}`;
188511
+ break;
188512
+ }
188513
+ case "paused": {
188514
+ const e = drain.embedded ?? coverage.session.embedded;
188515
+ const t = drain.total ?? coverage.session.total;
188516
+ drainLine = `Drain: paused ${e}/${t}`;
188517
+ break;
188518
+ }
188519
+ case "stopped":
188520
+ drainLine = "Drain: stopped (provider down)";
188521
+ break;
188522
+ default:
188523
+ drainLine = "Drain: idle";
188524
+ }
188525
+ lines.push(drainLine);
188526
+ return lines.join(`
188527
+ `);
188528
+ }
188529
+
188100
188530
  // src/hooks/magic-context/hook.ts
188101
188531
  await __promiseAll([
188102
188532
  init_inject_compartments(),
@@ -188338,7 +188768,7 @@ function maybeInjectChannel1Nudge(args, sessionId, tool, output) {
188338
188768
  setLastNudgeLevel(args.db, sessionId, decision.nextLastNudgeLevel);
188339
188769
  if (!decision.fire)
188340
188770
  return;
188341
- out.output += buildChannel1Reminder(decision.level, decision.undroppedTokens);
188771
+ out.output += buildChannel1Reminder(decision.level, decision.undroppedTokens, state.oldestReclaimableToolTags);
188342
188772
  sessionLog(sessionId, `channel1 nudge fired: level=${decision.level} undropped~${Math.round(decision.undroppedTokens / 1000)}k tool=${tool}`);
188343
188773
  }
188344
188774
  function createToolExecuteAfterHook(args) {
@@ -188410,9 +188840,7 @@ Context is managed for you entirely automatically — there's nothing to prune a
188410
188840
  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
188841
  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
188842
  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.
188843
+ 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
188844
  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
188845
  ${CTX_NOTE_GUIDANCE}
188418
188846
  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 +188859,7 @@ Use \`ctx_expand\` to recover the raw conversation behind a \`<compartment>\` su
188431
188859
  \`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
188860
  ${TOOL_HISTORY_GUIDANCE}
188433
188861
  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.
188862
+ 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
188863
  NEVER drop assistant text messages unless they are exceptionally large. Your conversation messages are lightweight; only large tool outputs are worth dropping.
188436
188864
  Before your turn finishes, consider using \`ctx_reduce\` to drop large tool outputs you no longer need.`;
188437
188865
  var BASE_INTRO_NO_REDUCE = () => `${CTX_NOTE_GUIDANCE}
@@ -188779,7 +189207,7 @@ function createMagicContextHook(deps) {
188779
189207
  }
188780
189208
  let lastScheduleCheckMs = 0;
188781
189209
  const getHistorianChunkTokens = () => deriveHistorianChunkTokens(resolveHistorianContextLimit(deps.config.historian?.model));
188782
- const historianFallbackModels = resolveFallbackChain(HISTORIAN_AGENT, deps.config.historian?.fallback_models);
189210
+ const historianFallbackModels = resolveFallbackChain(deps.config.historian?.fallback_models);
188783
189211
  const historyRefreshSessions = deps.liveSessionState?.historyRefreshSessions ?? new Set;
188784
189212
  const deferredHistoryRefreshSessions = deps.liveSessionState?.deferredHistoryRefreshSessions ?? new Set;
188785
189213
  try {
@@ -188852,29 +189280,55 @@ function createMagicContextHook(deps) {
188852
189280
  ensureProjectRegistered: ensureProjectRegisteredFromOpenCodeDirectory,
188853
189281
  getNotificationParams: (sid) => getLiveNotificationParams(sid, liveModelBySession, variantBySession, agentBySession)
188854
189282
  });
188855
- const executeEmbedHistory = async (sessionId) => {
189283
+ const executeEmbedHistory = async (sessionId, options) => {
188856
189284
  if (deps.config.memory?.enabled === false) {
188857
189285
  return "Memory is disabled for this project, so there is no semantic embedding to backfill.";
188858
189286
  }
188859
189287
  const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
189288
+ const active = embedRunStateBySession.get(sessionId);
189289
+ if (active && !active.signal.aborted && !options?.signal) {
189290
+ return "Embedding is already running for this session.";
189291
+ }
188860
189292
  await ensureProjectRegisteredFromOpenCodeDirectory(directory, db);
188861
189293
  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
- });
189294
+ embedPauseBySession.delete(sessionId);
189295
+ const prior = embedRunStateBySession.get(sessionId);
189296
+ if (prior)
189297
+ prior.abort();
189298
+ const controller = new AbortController;
189299
+ embedRunStateBySession.set(sessionId, controller);
189300
+ const signal = options?.signal ?? controller.signal;
189301
+ if (!options?.silent) {
189302
+ setRecompStarting({ recompProgressBySession }, sessionId, "Embedding history…", "embed");
189303
+ }
189304
+ let runFailed = 0;
189305
+ let outcome;
189306
+ try {
189307
+ outcome = await embedSessionCompartmentChunks(db, sessionProjectIdentity, sessionId, {
189308
+ signal,
189309
+ onProgress: ({ embedded, total }) => {
189310
+ const cur = recompProgressBySession.get(sessionId);
189311
+ if (!cur || cur.phase !== "recomp")
189312
+ return;
189313
+ recompProgressBySession.set(sessionId, {
189314
+ ...cur,
189315
+ processedMessages: embedded,
189316
+ totalMessages: total,
189317
+ updatedAt: Date.now()
189318
+ });
189319
+ }
189320
+ });
189321
+ } finally {
189322
+ if (embedRunStateBySession.get(sessionId) === controller) {
189323
+ embedRunStateBySession.delete(sessionId);
188874
189324
  }
188875
- });
189325
+ }
189326
+ if ("failed" in outcome)
189327
+ runFailed = outcome.failed;
188876
189328
  const terminal = (phase, message) => {
188877
- setRecompTerminal({ recompProgressBySession }, sessionId, phase, message);
189329
+ if (!options?.silent) {
189330
+ setRecompTerminal({ recompProgressBySession }, sessionId, phase, message);
189331
+ }
188878
189332
  return message;
188879
189333
  };
188880
189334
  switch (outcome.status) {
@@ -188883,15 +189337,78 @@ function createMagicContextHook(deps) {
188883
189337
  case "disabled":
188884
189338
  return terminal("skipped", "No embedding provider is configured, so there is nothing to embed.");
188885
189339
  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.`);
189340
+ return terminal("skipped", "Embedding is already running for this project. Try again shortly.");
189341
+ case "aborted": {
189342
+ const cov = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
189343
+ const msg = `Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`;
189344
+ return terminal("skipped", msg);
189345
+ }
188889
189346
  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.`);
189347
+ 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
189348
  default:
188892
- return terminal("done", `Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search.`);
189349
+ return terminal("done", `Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search${runFailed > 0 ? ` (${runFailed} failed)` : ""}.`);
188893
189350
  }
188894
189351
  };
189352
+ const pauseEmbedDrain = (sessionId) => {
189353
+ embedPauseBySession.add(sessionId);
189354
+ const ctrl = embedRunStateBySession.get(sessionId);
189355
+ if (ctrl)
189356
+ ctrl.abort();
189357
+ const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
189358
+ const sessionProjectIdentity = resolveProjectIdentity(directory);
189359
+ const cov = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
189360
+ return `Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`;
189361
+ };
189362
+ const getEmbedStatusText = (sessionId) => {
189363
+ const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
189364
+ const sessionProjectIdentity = resolveProjectIdentity(directory);
189365
+ const coverage = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
189366
+ const progress = recompProgressBySession.get(sessionId);
189367
+ const drainUi = getEmbedDrainUiStatus(sessionId, progress);
189368
+ return formatEmbedStatusText(coverage, {
189369
+ status: drainUi.status,
189370
+ embedded: progress?.processedMessages,
189371
+ total: progress?.totalMessages
189372
+ });
189373
+ };
189374
+ const maybeAutoEmbedSession = (sessionId) => {
189375
+ if (autoEmbedAttemptedBySession.has(sessionId))
189376
+ return;
189377
+ if (embedPauseBySession.has(sessionId))
189378
+ return;
189379
+ if (deps.config.memory?.enabled === false)
189380
+ return;
189381
+ autoEmbedAttemptedBySession.add(sessionId);
189382
+ const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
189383
+ (async () => {
189384
+ try {
189385
+ await new Promise((resolve6) => setTimeout(resolve6, 0));
189386
+ await ensureProjectRegisteredFromOpenCodeDirectory(directory, db);
189387
+ const sessionProjectIdentity = resolveProjectIdentity(directory);
189388
+ const coverage = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
189389
+ if (!coverage.enabled)
189390
+ return;
189391
+ const remaining = coverage.session.total - coverage.session.embedded;
189392
+ if (remaining <= 0)
189393
+ return;
189394
+ const notifyParams = getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession);
189395
+ if (!isTuiConnected(sessionId)) {
189396
+ const startMsg = `Embedding ${remaining} compartment${remaining === 1 ? "" : "s"} of history in the background…`;
189397
+ await sendIgnoredMessage(deps.client, sessionId, startMsg, {
189398
+ ...notifyParams
189399
+ });
189400
+ }
189401
+ const summary = await executeEmbedHistory(sessionId);
189402
+ if (!isTuiConnected(sessionId)) {
189403
+ await sendIgnoredMessage(deps.client, sessionId, summary, {
189404
+ ...notifyParams
189405
+ });
189406
+ }
189407
+ } catch (error51) {
189408
+ log("[magic-context] auto-embed drain failed:", error51);
189409
+ }
189410
+ })();
189411
+ };
188895
189412
  const sidekickRunnable = isSidekickRunnable(deps.config);
188896
189413
  const sidekickConfig = sidekickRunnable ? deps.config.sidekick : undefined;
188897
189414
  const transform2 = createTransform({
@@ -188953,7 +189470,8 @@ function createMagicContextHook(deps) {
188953
189470
  cavemanTextCompression: ctxReduceEnabled === false && deps.config.caveman_text_compression?.enabled === true ? {
188954
189471
  enabled: true,
188955
189472
  minChars: deps.config.caveman_text_compression.min_chars ?? 500
188956
- } : undefined
189473
+ } : undefined,
189474
+ maybeAutoEmbedSession
188957
189475
  });
188958
189476
  const eventHandler = createEventHandler2({
188959
189477
  contextUsageMap,
@@ -188982,6 +189500,7 @@ function createMagicContextHook(deps) {
188982
189500
  recompProgressBySession.delete(sessionId);
188983
189501
  internalChildSessions.delete(sessionId);
188984
189502
  channel1StateBySession.delete(sessionId);
189503
+ clearEmbedSessionState(sessionId);
188985
189504
  }
188986
189505
  });
188987
189506
  const runDreamQueueInBackground = () => {
@@ -189016,7 +189535,7 @@ function createMagicContextHook(deps) {
189016
189535
  token_budget: dreaming.pin_key_files.token_budget,
189017
189536
  min_reads: dreaming.pin_key_files.min_reads
189018
189537
  } : undefined,
189019
- fallbackModels: resolveFallbackChain(DREAMER_AGENT, dreaming.fallback_models),
189538
+ fallbackModels: resolveFallbackChain(dreaming.fallback_models),
189020
189539
  projectIdentity: projectPath
189021
189540
  }).catch((error51) => {
189022
189541
  log("[dreamer] scheduled queue processing failed:", error51);
@@ -189047,6 +189566,8 @@ function createMagicContextHook(deps) {
189047
189566
  executeRecomp: historianRunnable ? async (sessionId, options) => runManagedRecomp(buildManagedRecompCtx(sessionId), sessionId, options) : undefined,
189048
189567
  runUpgrade: historianRunnable ? async (sessionId) => runManagedUpgrade(buildManagedRecompCtx(sessionId), sessionId) : undefined,
189049
189568
  executeEmbedHistory,
189569
+ pauseEmbedDrain,
189570
+ getEmbedStatusText,
189050
189571
  sendNotification: async (sessionId, text, params) => {
189051
189572
  await sendIgnoredMessage(deps.client, sessionId, text, {
189052
189573
  ...getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
@@ -189073,7 +189594,7 @@ function createMagicContextHook(deps) {
189073
189594
  token_budget: dreamerConfig.pin_key_files.token_budget,
189074
189595
  min_reads: dreamerConfig.pin_key_files.min_reads
189075
189596
  } : undefined,
189076
- fallbackModels: resolveFallbackChain(DREAMER_AGENT, dreamerConfig.fallback_models)
189597
+ fallbackModels: resolveFallbackChain(dreamerConfig.fallback_models)
189077
189598
  } : undefined
189078
189599
  });
189079
189600
  const systemPromptHash = createSystemPromptHashHandler({
@@ -189259,6 +189780,7 @@ function truncateError(name2, code, message, maxLen = 240) {
189259
189780
 
189260
189781
  // src/plugin/rpc-handlers.ts
189261
189782
  init_project_identity();
189783
+ init_project_embedding_registry();
189262
189784
  init_tool_definition_tokens();
189263
189785
  await init_storage();
189264
189786
 
@@ -189979,6 +190501,26 @@ function buildStatusDetail(db, sessionId, directory, modelKey, config2, liveSess
189979
190501
  }
189980
190502
  return detail;
189981
190503
  }
190504
+ function buildEmbedDetail(db, sessionId, dir, liveSessionState) {
190505
+ const projectIdentity = resolveProjectIdentity(dir);
190506
+ const coverage = getEmbeddingCoverageStatus(db, projectIdentity, sessionId);
190507
+ const progress = liveSessionState.recompProgressBySession.get(sessionId);
190508
+ const drainUi = getEmbedDrainUiStatus(sessionId, progress);
190509
+ const statusText = formatEmbedStatusText(coverage, {
190510
+ status: drainUi.status,
190511
+ embedded: progress?.processedMessages,
190512
+ total: progress?.totalMessages
190513
+ });
190514
+ return {
190515
+ enabled: coverage.enabled,
190516
+ model: coverage.model,
190517
+ provider: coverage.provider,
190518
+ session: coverage.session,
190519
+ memories: coverage.memories,
190520
+ commits: coverage.commits,
190521
+ statusText
190522
+ };
190523
+ }
189982
190524
  function registerRpcHandlers(rpcServer, args) {
189983
190525
  const { directory, config: config2, liveSessionState } = args;
189984
190526
  const rawConfig = config2;
@@ -190001,6 +190543,19 @@ function registerRpcHandlers(rpcServer, args) {
190001
190543
  return { error: "unavailable" };
190002
190544
  return buildStatusDetail(db, sessionId, dir, modelKey, rawConfig, liveSessionState, injectionBudgetTokens);
190003
190545
  });
190546
+ rpcServer.handle("embed-detail", async (params) => {
190547
+ const sessionId = String(params.sessionId ?? "");
190548
+ const dir = String(params.directory ?? directory);
190549
+ const db = getDb();
190550
+ if (!db || !sessionId)
190551
+ return { error: "unavailable" };
190552
+ try {
190553
+ return buildEmbedDetail(db, sessionId, dir, liveSessionState);
190554
+ } catch (err) {
190555
+ log("[rpc] embed-detail error:", err);
190556
+ return { error: "unavailable" };
190557
+ }
190558
+ });
190004
190559
  rpcServer.handle("compartment-count", async (params) => {
190005
190560
  const sessionId = String(params.sessionId ?? "");
190006
190561
  const db = getDb();
@@ -190015,8 +190570,7 @@ function registerRpcHandlers(rpcServer, args) {
190015
190570
  });
190016
190571
  const buildManagedCtx = async (db) => {
190017
190572
  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);
190573
+ const { resolveFallbackChain: resolveFallbackChain2 } = await Promise.resolve().then(() => exports_resolve_fallbacks);
190020
190574
  const DEFAULT_HISTORIAN_TIMEOUT_MS2 = 600000;
190021
190575
  return {
190022
190576
  client: args.client,
@@ -190027,7 +190581,7 @@ function registerRpcHandlers(rpcServer, args) {
190027
190581
  historianTimeoutMs: config2.historian_timeout_ms ?? DEFAULT_HISTORIAN_TIMEOUT_MS2,
190028
190582
  memoryEnabled: config2.memory?.enabled ?? true,
190029
190583
  autoPromote: config2.memory?.auto_promote ?? true,
190030
- fallbackModels: resolveFallbackChain2(HISTORIAN_AGENT2, config2.historian?.fallback_models),
190584
+ fallbackModels: resolveFallbackChain2(config2.historian?.fallback_models),
190031
190585
  runMigration: config2.memory?.enabled !== false && !!config2.historian?.model,
190032
190586
  userMemoriesEnabled: config2.dreamer?.user_memories?.enabled === true,
190033
190587
  historianTwoPass: config2.historian?.two_pass === true,
@@ -190123,27 +190677,225 @@ Older parts of this session are summarized into <compartment> blocks inside <ses
190123
190677
 
190124
190678
  ctx_expand(start=120, end=245) ← the compartment's own start/end attributes
190125
190679
 
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.`;
190680
+ 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.
190681
+
190682
+ Two recovery modes for finer detail:
190683
+ - 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.
190684
+ - 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
190685
  var CTX_EXPAND_TOKEN_BUDGET = 15000;
190128
190686
 
190687
+ // src/tools/ctx-expand/render.ts
190688
+ init_read_session_formatting();
190689
+ await init_read_session_chunk();
190690
+ function isRecord3(value) {
190691
+ return value !== null && typeof value === "object" && !Array.isArray(value);
190692
+ }
190693
+ function roleLabel(role) {
190694
+ if (role === "assistant")
190695
+ return "A (assistant)";
190696
+ if (role === "user")
190697
+ return "U (user)";
190698
+ return role;
190699
+ }
190700
+ function truncate2(value, max) {
190701
+ const t = value.trim();
190702
+ return t.length <= max ? t : `${t.slice(0, max)}…`;
190703
+ }
190704
+ function keyArg(input) {
190705
+ if (!input)
190706
+ return "";
190707
+ for (const k of ["filePath", "path", "pattern", "query", "symbol", "module", "action"]) {
190708
+ const v = input[k];
190709
+ if (typeof v === "string" && v.length > 0)
190710
+ return truncate2(v, 60);
190711
+ }
190712
+ if (typeof input.description === "string")
190713
+ return truncate2(input.description, 60);
190714
+ return "";
190715
+ }
190716
+ function asToolPart(part) {
190717
+ const type = typeof part.type === "string" ? part.type : "";
190718
+ if (type === "tool") {
190719
+ const state = isRecord3(part.state) ? part.state : null;
190720
+ const output = state && typeof state.output === "string" ? state.output : state && state.output != null ? JSON.stringify(state.output) : null;
190721
+ const metadata = state && isRecord3(state.metadata) ? state.metadata : null;
190722
+ const title = state && typeof state.title === "string" && state.title || metadata && typeof metadata.title === "string" && metadata.title || null;
190723
+ return {
190724
+ name: typeof part.tool === "string" ? part.tool : "tool",
190725
+ callId: typeof part.callID === "string" ? part.callID : "",
190726
+ title,
190727
+ input: state && isRecord3(state.input) ? state.input : null,
190728
+ output
190729
+ };
190730
+ }
190731
+ if (type === "tool_use") {
190732
+ return {
190733
+ name: typeof part.name === "string" ? part.name : "tool",
190734
+ callId: typeof part.id === "string" ? part.id : "",
190735
+ title: null,
190736
+ input: isRecord3(part.input) ? part.input : null,
190737
+ output: null
190738
+ };
190739
+ }
190740
+ if (type === "tool_result") {
190741
+ const content = part.content;
190742
+ const output = typeof content === "string" ? content : content != null ? JSON.stringify(content) : null;
190743
+ return {
190744
+ name: "tool_result",
190745
+ callId: typeof part.tool_use_id === "string" ? part.tool_use_id : "",
190746
+ title: null,
190747
+ input: null,
190748
+ output
190749
+ };
190750
+ }
190751
+ return null;
190752
+ }
190753
+ function textOf(part) {
190754
+ if (part.type === "text" && typeof part.text === "string")
190755
+ return part.text;
190756
+ return null;
190757
+ }
190758
+ function reasoningOf(part) {
190759
+ if ((part.type === "reasoning" || part.type === "thinking") && typeof part.text === "string") {
190760
+ return part.text;
190761
+ }
190762
+ return null;
190763
+ }
190764
+ function renderPartPreview(part) {
190765
+ if (!isRecord3(part))
190766
+ return null;
190767
+ const text = textOf(part);
190768
+ if (text !== null) {
190769
+ const t = truncate2(text, 200);
190770
+ return t.length > 0 ? ` • ${t}` : null;
190771
+ }
190772
+ const tool = asToolPart(part);
190773
+ if (tool) {
190774
+ const arg = keyArg(tool.input);
190775
+ const head = arg ? `${tool.name}(${arg})` : tool.name;
190776
+ return tool.output !== null ? ` • tool ${head} → output ~${estimateTokens(tool.output)} tok` : ` • tool ${head}`;
190777
+ }
190778
+ const reasoning = reasoningOf(part);
190779
+ if (reasoning !== null)
190780
+ return ` • [reasoning] ${truncate2(reasoning, 120)}`;
190781
+ const type = typeof part.type === "string" ? part.type : "part";
190782
+ if (type === "file")
190783
+ return " • [file]";
190784
+ if (type === "step-start" || type === "step-finish")
190785
+ return null;
190786
+ return ` • [${type}]`;
190787
+ }
190788
+ function renderPartFull(part) {
190789
+ if (!isRecord3(part))
190790
+ return null;
190791
+ const text = textOf(part);
190792
+ if (text !== null) {
190793
+ return text.trim().length > 0 ? ` [text]
190794
+ ${text}` : null;
190795
+ }
190796
+ const tool = asToolPart(part);
190797
+ if (tool) {
190798
+ const lines = [];
190799
+ const idSuffix = tool.callId ? ` #${tool.callId}` : "";
190800
+ lines.push(` [tool: ${tool.name}${idSuffix}]`);
190801
+ if (tool.title && tool.title.trim().length > 0) {
190802
+ lines.push(` description: ${tool.title.trim()}`);
190803
+ }
190804
+ if (tool.input)
190805
+ lines.push(` input: ${JSON.stringify(tool.input)}`);
190806
+ if (tool.output !== null)
190807
+ lines.push(` output:
190808
+ ${tool.output}`);
190809
+ return lines.join(`
190810
+ `);
190811
+ }
190812
+ const type = typeof part.type === "string" ? part.type : "part";
190813
+ if (type === "file") {
190814
+ const name2 = typeof part.filename === "string" && part.filename || typeof part.url === "string" && part.url || "";
190815
+ return ` [file]${name2 ? ` ${name2}` : ""}`;
190816
+ }
190817
+ return null;
190818
+ }
190819
+ function renderMessageByOrdinal(sessionId, ordinal) {
190820
+ const msg = readRawSessionMessages(sessionId).find((m) => m.ordinal === ordinal);
190821
+ if (!msg) {
190822
+ 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.`;
190823
+ }
190824
+ const rendered = msg.parts.map(renderPartFull).filter((l) => l !== null);
190825
+ const lines = [`[${msg.ordinal}] ${roleLabel(msg.role)} — full recovery:`, ""];
190826
+ if (rendered.length === 0) {
190827
+ lines.push(" (no recoverable content — message had only structural/reasoning parts)");
190828
+ } else {
190829
+ lines.push(...rendered);
190830
+ }
190831
+ return lines.join(`
190832
+ `);
190833
+ }
190834
+ function renderVerboseRange(sessionId, start, end, tokenBudget) {
190835
+ const messages = readRawSessionMessages(sessionId).filter((m) => m.ordinal >= start && m.ordinal <= end);
190836
+ const out = [];
190837
+ let usedTokens = 0;
190838
+ let lastOrdinal = start - 1;
190839
+ let truncated = false;
190840
+ for (const msg of messages) {
190841
+ const header = `[${msg.ordinal}] ${roleLabel(msg.role)}`;
190842
+ const partLines = msg.parts.map(renderPartPreview).filter((l) => l !== null);
190843
+ const block = partLines.length > 0 ? `${header}
190844
+ ${partLines.join(`
190845
+ `)}` : header;
190846
+ const blockTokens = estimateTokens(block);
190847
+ if (usedTokens + blockTokens > tokenBudget && out.length > 0) {
190848
+ truncated = true;
190849
+ break;
190850
+ }
190851
+ out.push(block);
190852
+ usedTokens += blockTokens;
190853
+ lastOrdinal = msg.ordinal;
190854
+ }
190855
+ return { text: out.join(`
190856
+
190857
+ `), lastOrdinal, truncated };
190858
+ }
190859
+
190129
190860
  // src/tools/ctx-expand/tools.ts
190130
190861
  function createCtxExpandTool(deps) {
190131
190862
  return tool({
190132
190863
  description: CTX_EXPAND_DESCRIPTION,
190133
190864
  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`)
190865
+ 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`),
190866
+ end: tool.schema.number().optional().describe(`Last message ordinal to expand (inclusive) — a compartment's end="M" attribute`),
190867
+ 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."),
190868
+ 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
190869
  },
190137
190870
  async execute(args, toolContext) {
190138
190871
  const sessionId = toolContext.sessionID;
190872
+ if (typeof args.message === "number" && args.message >= 1) {
190873
+ return renderMessageByOrdinal(sessionId, args.message);
190874
+ }
190139
190875
  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.";
190876
+ return "Error: provide either message=<ordinal>, or start and end (positive integers, start <= end).";
190141
190877
  }
190142
190878
  const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
190143
190879
  if (lastCompartmentEnd >= 0 && args.start > lastCompartmentEnd) {
190144
190880
  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
190881
  }
190146
190882
  const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(args.end, lastCompartmentEnd) : args.end;
190883
+ if (args.verbose === true) {
190884
+ const v = renderVerboseRange(sessionId, args.start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET);
190885
+ if (!v.text) {
190886
+ return `No messages found in range ${args.start}-${effectiveEnd}. The range may be outside this session's history.`;
190887
+ }
190888
+ const out = [
190889
+ `Messages ${args.start}-${v.lastOrdinal} (verbose). Recover any one in full with ctx_expand(message=<ordinal>):`,
190890
+ "",
190891
+ v.text
190892
+ ];
190893
+ if (v.truncated) {
190894
+ 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.`);
190895
+ }
190896
+ return out.join(`
190897
+ `);
190898
+ }
190147
190899
  const chunk = readSessionChunk(sessionId, CTX_EXPAND_TOKEN_BUDGET, args.start, effectiveEnd + 1);
190148
190900
  if (!chunk.text || chunk.messageCount === 0) {
190149
190901
  return `No messages found in range ${args.start}-${args.end}. The range may be outside this session's history.`;
@@ -190181,6 +190933,7 @@ Actions:
190181
190933
  Example: ctx_memory(action="write", category="CONSTRAINTS", content="Pi stores sessions as JSONL under ~/.pi/agent/sessions/, not SQLite")`;
190182
190934
  var DEFAULT_SEARCH_LIMIT2 = 10;
190183
190935
  // src/tools/ctx-memory/tools.ts
190936
+ import { tool as tool2 } from "@opencode-ai/plugin";
190184
190937
  init_memory();
190185
190938
  init_embedding();
190186
190939
  init_embedding_cache();
@@ -190188,7 +190941,6 @@ init_normalize_hash();
190188
190941
  init_workspaces();
190189
190942
  init_logger();
190190
190943
  await init_storage();
190191
- import { tool as tool2 } from "@opencode-ai/plugin";
190192
190944
 
190193
190945
  // src/tools/ctx-memory/types.ts
190194
190946
  var CTX_MEMORY_ACTIONS = ["write", "archive", "update", "merge"];
@@ -190837,15 +191589,16 @@ function createCtxNoteTools(deps) {
190837
191589
  };
190838
191590
  }
190839
191591
  // 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.`;
191592
+ 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".
191593
+
191594
+ How it works:
191595
+ - 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.
191596
+ - The newest tags are protected: marking one just queues it until it ages out of the recent window, so marking recent output is harmless.
191597
+ - 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?".
191598
+
191599
+ 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.
191600
+ Keep: user messages, unresolved errors, raw evidence you haven't extracted yet, and outputs whose exact wording may matter later.
191601
+ Never blanket-mark large ranges (e.g. "1-50") — review what each tag holds first.`;
190849
191602
  // src/tools/ctx-reduce/tools.ts
190850
191603
  import { tool as tool4 } from "@opencode-ai/plugin";
190851
191604
 
@@ -191112,7 +191865,7 @@ function createCtxSearchTool(deps) {
191112
191865
  memoryEnabled,
191113
191866
  embeddingEnabled,
191114
191867
  embedQuery: async (text, signal) => {
191115
- const result = await embedTextForProject(projectPath, text, signal);
191868
+ const result = await embedTextForProject(projectPath, text, signal, "query");
191116
191869
  return result?.vector ?? null;
191117
191870
  },
191118
191871
  isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
@@ -191215,14 +191968,13 @@ function createToolRegistry(args) {
191215
191968
  init_conflict_detector();
191216
191969
  init_data_path();
191217
191970
  init_logger();
191218
- init_model_requirements();
191219
191971
  init_models_dev_cache();
191220
191972
 
191221
191973
  // src/shared/rpc-server.ts
191222
191974
  init_logger();
191223
191975
  import { randomBytes, timingSafeEqual } from "node:crypto";
191224
191976
  import {
191225
- mkdirSync as mkdirSync8,
191977
+ mkdirSync as mkdirSync7,
191226
191978
  readdirSync,
191227
191979
  readFileSync as readFileSync12,
191228
191980
  renameSync as renameSync2,
@@ -191230,20 +191982,20 @@ import {
191230
191982
  writeFileSync as writeFileSync5
191231
191983
  } from "node:fs";
191232
191984
  import { createServer } from "node:http";
191233
- import { dirname as dirname5 } from "node:path";
191985
+ import { dirname as dirname4 } from "node:path";
191234
191986
 
191235
191987
  // src/shared/rpc-utils.ts
191236
191988
  import { createHash as createHash13 } from "node:crypto";
191237
- import { join as join20 } from "node:path";
191989
+ import { join as join19 } from "node:path";
191238
191990
  function projectHash(directory) {
191239
191991
  const normalized = directory.replace(/\/+$/, "");
191240
191992
  return createHash13("sha256").update(normalized).digest("hex").slice(0, 16);
191241
191993
  }
191242
191994
  function rpcPortDir(storageDir, directory) {
191243
- return join20(storageDir, "rpc", projectHash(directory));
191995
+ return join19(storageDir, "rpc", projectHash(directory));
191244
191996
  }
191245
191997
  function rpcPortFilePath(storageDir, directory, pid = process.pid) {
191246
- return join20(rpcPortDir(storageDir, directory), `port-${pid}.json`);
191998
+ return join19(rpcPortDir(storageDir, directory), `port-${pid}.json`);
191247
191999
  }
191248
192000
  function isPidAlive(pid) {
191249
192001
  if (!Number.isInteger(pid) || pid <= 0)
@@ -191327,8 +192079,8 @@ class MagicContextRpcServer {
191327
192079
  this.server = server2;
191328
192080
  try {
191329
192081
  this.warnIfOtherLiveInstance();
191330
- const dir = dirname5(this.portFilePath);
191331
- mkdirSync8(dir, { recursive: true, mode: 448 });
192082
+ const dir = dirname4(this.portFilePath);
192083
+ mkdirSync7(dir, { recursive: true, mode: 448 });
191332
192084
  const tmpPath = `${this.portFilePath}.tmp`;
191333
192085
  writeFileSync5(tmpPath, JSON.stringify({
191334
192086
  port: this.port,
@@ -191428,6 +192180,9 @@ class MagicContextRpcServer {
191428
192180
  }
191429
192181
 
191430
192182
  // src/index.ts
192183
+ var HISTORIAN_MAX_STEPS = 40;
192184
+ var SIDEKICK_MAX_STEPS = 40;
192185
+ var DREAMER_MAX_STEPS = 150;
191431
192186
  var plugin = async (ctx) => {
191432
192187
  const pluginConfig = loadPluginConfig(ctx.directory);
191433
192188
  setSqlitePragmaConfig({
@@ -191620,12 +192375,13 @@ var plugin = async (ctx) => {
191620
192375
  await hooks.magicContext?.["experimental.text.complete"]?.(input, output);
191621
192376
  },
191622
192377
  config: async (config2) => {
191623
- const buildHiddenAgentConfig = (agentId, prompt, allowedTools, overrides) => {
192378
+ const buildHiddenAgentConfig = (prompt, allowedTools, maxSteps, overrides) => {
191624
192379
  const { permission: overridePermission, ...restOverrides } = overrides ?? {};
191625
192380
  const basePermission = buildAllowOnlyPermission(allowedTools);
191626
192381
  return {
191627
192382
  prompt,
191628
- ...getAgentFallbackModels(agentId) ? { fallback_models: getAgentFallbackModels(agentId) } : {},
192383
+ steps: maxSteps,
192384
+ maxSteps,
191629
192385
  ...restOverrides,
191630
192386
  permission: {
191631
192387
  ...basePermission,
@@ -191647,6 +192403,7 @@ var plugin = async (ctx) => {
191647
192403
  max_runtime_minutes: _max,
191648
192404
  tasks: _tasks,
191649
192405
  task_timeout_minutes: _tto,
192406
+ thinking_level: _thinkingLevel,
191650
192407
  ...agentOverrides
191651
192408
  } = pluginConfig.dreamer;
191652
192409
  return agentOverrides;
@@ -191655,20 +192412,28 @@ var plugin = async (ctx) => {
191655
192412
  const {
191656
192413
  timeout_ms: _timeoutMs,
191657
192414
  system_prompt: _systemPrompt,
192415
+ thinking_level: _thinkingLevel,
191658
192416
  ...agentOverrides
191659
192417
  } = pluginConfig.sidekick;
191660
192418
  return agentOverrides;
191661
192419
  })() : undefined;
191662
192420
  const historianAgentOverrides = pluginConfig.historian ? (() => {
191663
- const { two_pass: _twoPass, ...agentOverrides } = pluginConfig.historian;
192421
+ const {
192422
+ two_pass: _twoPass,
192423
+ disallowed_tools: _disallowedTools,
192424
+ thinking_level: _thinkingLevel,
192425
+ ...agentOverrides
192426
+ } = pluginConfig.historian;
191664
192427
  return agentOverrides;
191665
192428
  })() : undefined;
192429
+ const historianDisallowed = pluginConfig.historian?.disallowed_tools ?? [];
192430
+ const historianAllowedTools = applyDisallowedTools(HISTORIAN_ALLOWED_TOOLS, historianDisallowed);
191666
192431
  config2.agent = {
191667
192432
  ...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)
192433
+ [DREAMER_AGENT]: buildHiddenAgentConfig(DREAMER_SYSTEM_PROMPT, DREAMER_ALLOWED_TOOLS, DREAMER_MAX_STEPS, dreamerAgentOverrides),
192434
+ [HISTORIAN_AGENT]: buildHiddenAgentConfig(COMPARTMENT_AGENT_SYSTEM_PROMPT, historianAllowedTools, HISTORIAN_MAX_STEPS, historianAgentOverrides),
192435
+ [HISTORIAN_EDITOR_AGENT]: buildHiddenAgentConfig(HISTORIAN_EDITOR_SYSTEM_PROMPT, historianAllowedTools, HISTORIAN_MAX_STEPS, historianAgentOverrides),
192436
+ [SIDEKICK_AGENT]: buildHiddenAgentConfig(SIDEKICK_SYSTEM_PROMPT, SIDEKICK_ALLOWED_TOOLS, SIDEKICK_MAX_STEPS, sidekickAgentOverrides)
191672
192437
  };
191673
192438
  }
191674
192439
  };