@wolfx/opencode-magic-context 0.22.1-patch.1 → 0.22.4

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 (93) hide show
  1. package/dist/config/agent-disable.d.ts +0 -9
  2. package/dist/config/agent-disable.d.ts.map +1 -1
  3. package/dist/config/schema/agent-overrides.d.ts +0 -3
  4. package/dist/config/schema/agent-overrides.d.ts.map +1 -1
  5. package/dist/config/schema/magic-context.d.ts +7 -0
  6. package/dist/config/schema/magic-context.d.ts.map +1 -1
  7. package/dist/features/builtin-commands/types.d.ts +0 -2
  8. package/dist/features/builtin-commands/types.d.ts.map +1 -1
  9. package/dist/features/magic-context/dreamer/runner.d.ts.map +1 -1
  10. package/dist/features/magic-context/dreamer/scheduler.d.ts +0 -4
  11. package/dist/features/magic-context/dreamer/scheduler.d.ts.map +1 -1
  12. package/dist/features/magic-context/git-commits/git-log-reader.d.ts +8 -0
  13. package/dist/features/magic-context/git-commits/git-log-reader.d.ts.map +1 -1
  14. package/dist/features/magic-context/git-commits/index.d.ts +1 -0
  15. package/dist/features/magic-context/git-commits/index.d.ts.map +1 -1
  16. package/dist/features/magic-context/git-commits/indexer.d.ts.map +1 -1
  17. package/dist/features/magic-context/git-commits/storage-git-commits.d.ts.map +1 -1
  18. package/dist/features/magic-context/git-commits/sweep-coordinator.d.ts +48 -0
  19. package/dist/features/magic-context/git-commits/sweep-coordinator.d.ts.map +1 -0
  20. package/dist/features/magic-context/key-files/aft-availability.d.ts.map +1 -1
  21. package/dist/features/magic-context/key-files/identify-key-files.d.ts.map +1 -1
  22. package/dist/features/magic-context/key-files/storage-key-files.d.ts +0 -5
  23. package/dist/features/magic-context/key-files/storage-key-files.d.ts.map +1 -1
  24. package/dist/features/magic-context/literal-probes.d.ts +24 -0
  25. package/dist/features/magic-context/literal-probes.d.ts.map +1 -0
  26. package/dist/features/magic-context/memory/memory-migration.d.ts.map +1 -1
  27. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  28. package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
  29. package/dist/features/magic-context/search.d.ts +7 -0
  30. package/dist/features/magic-context/search.d.ts.map +1 -1
  31. package/dist/features/magic-context/sidekick/agent.d.ts.map +1 -1
  32. package/dist/features/magic-context/storage-db.d.ts +1 -1
  33. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  34. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  35. package/dist/features/magic-context/storage-meta-shared.d.ts +7 -1
  36. package/dist/features/magic-context/storage-meta-shared.d.ts.map +1 -1
  37. package/dist/features/magic-context/storage-notes.d.ts +8 -0
  38. package/dist/features/magic-context/storage-notes.d.ts.map +1 -1
  39. package/dist/features/magic-context/tool-definition-tokens.d.ts +21 -0
  40. package/dist/features/magic-context/tool-definition-tokens.d.ts.map +1 -1
  41. package/dist/features/magic-context/types.d.ts +4 -0
  42. package/dist/features/magic-context/types.d.ts.map +1 -1
  43. package/dist/features/magic-context/user-memory/review-user-memories.d.ts.map +1 -1
  44. package/dist/hooks/magic-context/compartment-runner-historian.d.ts.map +1 -1
  45. package/dist/hooks/magic-context/compartment-runner-types.d.ts +14 -1
  46. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  47. package/dist/hooks/magic-context/derive-budgets.d.ts +3 -3
  48. package/dist/hooks/magic-context/event-handler.d.ts +7 -0
  49. package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
  50. package/dist/hooks/magic-context/event-payloads.d.ts +7 -0
  51. package/dist/hooks/magic-context/event-payloads.d.ts.map +1 -1
  52. package/dist/hooks/magic-context/event-resolvers.d.ts +1 -0
  53. package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
  54. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  55. package/dist/hooks/magic-context/inject-compartments.d.ts +41 -0
  56. package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
  57. package/dist/hooks/magic-context/live-session-state.d.ts +12 -0
  58. package/dist/hooks/magic-context/live-session-state.d.ts.map +1 -1
  59. package/dist/hooks/magic-context/recomp-orchestrator.d.ts +7 -2
  60. package/dist/hooks/magic-context/recomp-orchestrator.d.ts.map +1 -1
  61. package/dist/hooks/magic-context/system-prompt-hash.d.ts +9 -0
  62. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  63. package/dist/hooks/magic-context/tag-content-primitives.d.ts +23 -0
  64. package/dist/hooks/magic-context/tag-content-primitives.d.ts.map +1 -1
  65. package/dist/hooks/magic-context/temporal-awareness.d.ts.map +1 -1
  66. package/dist/hooks/magic-context/text-complete.d.ts +11 -26
  67. package/dist/hooks/magic-context/text-complete.d.ts.map +1 -1
  68. package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
  69. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +2 -1
  70. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  71. package/dist/hooks/magic-context/transform.d.ts +17 -0
  72. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +830 -263
  75. package/dist/plugin/dream-timer.d.ts.map +1 -1
  76. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  77. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  78. package/dist/shared/keep-subagents.d.ts +7 -0
  79. package/dist/shared/keep-subagents.d.ts.map +1 -0
  80. package/dist/shared/models-dev-cache.d.ts +54 -27
  81. package/dist/shared/models-dev-cache.d.ts.map +1 -1
  82. package/dist/shared/rpc-types.d.ts +3 -1
  83. package/dist/shared/rpc-types.d.ts.map +1 -1
  84. package/dist/tools/ctx-note/tools.d.ts.map +1 -1
  85. package/dist/tools/ctx-search/tools.d.ts.map +1 -1
  86. package/package.json +1 -1
  87. package/src/shared/keep-subagents.test.ts +39 -0
  88. package/src/shared/keep-subagents.ts +33 -0
  89. package/src/shared/models-dev-cache.test.ts +192 -360
  90. package/src/shared/models-dev-cache.ts +162 -193
  91. package/src/shared/rpc-types.ts +3 -1
  92. package/src/tui/index.tsx +17 -8
  93. package/src/tui/slots/sidebar-content.tsx +20 -10
package/dist/index.js CHANGED
@@ -14967,6 +14967,7 @@ var init_magic_context = __esm(() => {
14967
14967
  model: DEFAULT_LOCAL_EMBEDDING_MODEL
14968
14968
  }).describe("Embedding provider configuration"),
14969
14969
  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).'),
14970
+ 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."),
14970
14971
  caveman_text_compression: exports_external.object({
14971
14972
  enabled: exports_external.boolean().default(false).describe("Apply deterministic caveman-style text compression to old conversation text. Only active when ctx_reduce_enabled=false. Compresses user/assistant text in oldest-first tiers: ultra (oldest 20%), full, lite, untouched (newest 40%)."),
14972
14973
  min_chars: exports_external.number().min(100).max(1e4).default(500).describe("Text parts shorter than this (characters) stay untouched. Min 100, max 10000. Default: 500.")
@@ -15216,9 +15217,6 @@ function getMagicContextStorageDir() {
15216
15217
  function getLegacyOpenCodeMagicContextStorageDir() {
15217
15218
  return path2.join(getOpenCodeStorageDir(), "plugin", "magic-context");
15218
15219
  }
15219
- function getCacheDir() {
15220
- return process.env.XDG_CACHE_HOME ?? path2.join(os.homedir(), ".cache");
15221
- }
15222
15220
  var init_data_path = () => {};
15223
15221
 
15224
15222
  // src/shared/logger.ts
@@ -15752,6 +15750,15 @@ function extractLatestAssistantText(messages) {
15752
15750
  }
15753
15751
  var init_assistant_message_extractor = () => {};
15754
15752
 
15753
+ // src/shared/keep-subagents.ts
15754
+ function setKeepSubagents(value) {
15755
+ keepSubagents = value === true;
15756
+ }
15757
+ function shouldKeepSubagents() {
15758
+ return keepSubagents;
15759
+ }
15760
+ var keepSubagents = false;
15761
+
15755
15762
  // src/features/magic-context/compartment-lease.ts
15756
15763
  function acquireCompartmentLease(db, sessionId, holderId) {
15757
15764
  const acquiredAt = Date.now();
@@ -15838,7 +15845,7 @@ function isSessionMetaRow(row) {
15838
15845
  if (row === null || typeof row !== "object")
15839
15846
  return false;
15840
15847
  const r = row;
15841
- 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) && 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.last_observed_model_key) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme);
15848
+ 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) && 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.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme);
15842
15849
  }
15843
15850
  function getDefaultSessionMeta(sessionId) {
15844
15851
  return {
@@ -15874,6 +15881,9 @@ function getDefaultSessionMeta(sessionId) {
15874
15881
  cachedM0MaterializedAt: null,
15875
15882
  cachedM0SessionFactsVersion: null,
15876
15883
  cachedM0UpgradeState: null,
15884
+ cachedM0SystemHash: null,
15885
+ cachedM0ToolSetHash: null,
15886
+ cachedM0ModelKey: null,
15877
15887
  lastObservedModelKey: null,
15878
15888
  upgradeRemindedAt: null,
15879
15889
  piStableIdScheme: null
@@ -15925,6 +15935,9 @@ function toSessionMeta(row) {
15925
15935
  cachedM0MaterializedAt: numOrNull(row.cached_m0_materialized_at),
15926
15936
  cachedM0SessionFactsVersion: numOrNull(row.cached_m0_session_facts_version),
15927
15937
  cachedM0UpgradeState: stringOrNull(row.cached_m0_upgrade_state),
15938
+ cachedM0SystemHash: stringOrNull(row.cached_m0_system_hash),
15939
+ cachedM0ToolSetHash: stringOrNull(row.cached_m0_tool_set_hash),
15940
+ cachedM0ModelKey: stringOrNull(row.cached_m0_model_key),
15928
15941
  lastObservedModelKey: stringOrNull(row.last_observed_model_key),
15929
15942
  upgradeRemindedAt: numOrNull(row.upgrade_reminded_at),
15930
15943
  piStableIdScheme: numOrNull(row.pi_stable_id_scheme)
@@ -15944,8 +15957,11 @@ function persistCachedM0(db, sessionId, payload) {
15944
15957
  cached_m0_project_docs_hash = ?,
15945
15958
  cached_m0_materialized_at = ?,
15946
15959
  cached_m0_session_facts_version = ?,
15947
- cached_m0_upgrade_state = ?
15948
- WHERE session_id = ?`).run(Buffer2.from(payload.m0Bytes), payload.projectMemoryEpoch, payload.projectUserProfileVersion, payload.maxCompartmentSeq, payload.maxMemoryId, payload.maxMutationId, payload.maxMemoryMutationId ?? null, payload.m1Bytes ? Buffer2.from(payload.m1Bytes) : null, payload.projectDocsHash, payload.materializedAt, payload.sessionFactsVersion, payload.upgradeState, sessionId);
15960
+ cached_m0_upgrade_state = ?,
15961
+ cached_m0_system_hash = ?,
15962
+ cached_m0_tool_set_hash = ?,
15963
+ cached_m0_model_key = ?
15964
+ WHERE session_id = ?`).run(Buffer2.from(payload.m0Bytes), payload.projectMemoryEpoch, payload.projectUserProfileVersion, payload.maxCompartmentSeq, payload.maxMemoryId, payload.maxMutationId, payload.maxMemoryMutationId ?? null, payload.m1Bytes ? Buffer2.from(payload.m1Bytes) : null, payload.projectDocsHash, payload.materializedAt, payload.sessionFactsVersion, payload.upgradeState, payload.systemHash ?? "", payload.toolSetHash ?? "", payload.modelKey ?? "", sessionId);
15949
15965
  }
15950
15966
  function clearCachedM0M1(db, sessionId) {
15951
15967
  ensureSessionMetaRow(db, sessionId);
@@ -15963,6 +15979,9 @@ function clearCachedM0M1(db, sessionId) {
15963
15979
  ["cached_m0_materialized_at", null],
15964
15980
  ["cached_m0_session_facts_version", null],
15965
15981
  ["cached_m0_upgrade_state", null],
15982
+ ["cached_m0_system_hash", null],
15983
+ ["cached_m0_tool_set_hash", null],
15984
+ ["cached_m0_model_key", null],
15966
15985
  ["cached_m0_last_baseline_end_message_id", null],
15967
15986
  ["memory_block_cache", ""],
15968
15987
  ["memory_block_count", 0],
@@ -16015,6 +16034,9 @@ var init_storage_meta_shared = __esm(() => {
16015
16034
  "cached_m0_materialized_at",
16016
16035
  "cached_m0_session_facts_version",
16017
16036
  "cached_m0_upgrade_state",
16037
+ "cached_m0_system_hash",
16038
+ "cached_m0_tool_set_hash",
16039
+ "cached_m0_model_key",
16018
16040
  "last_observed_model_key",
16019
16041
  "upgrade_reminded_at",
16020
16042
  "pi_stable_id_scheme"
@@ -16051,6 +16073,9 @@ var init_storage_meta_shared = __esm(() => {
16051
16073
  cachedM0MaterializedAt: "cached_m0_materialized_at",
16052
16074
  cachedM0SessionFactsVersion: "cached_m0_session_facts_version",
16053
16075
  cachedM0UpgradeState: "cached_m0_upgrade_state",
16076
+ cachedM0SystemHash: "cached_m0_system_hash",
16077
+ cachedM0ToolSetHash: "cached_m0_tool_set_hash",
16078
+ cachedM0ModelKey: "cached_m0_model_key",
16054
16079
  lastObservedModelKey: "last_observed_model_key",
16055
16080
  upgradeRemindedAt: "upgrade_reminded_at",
16056
16081
  piStableIdScheme: "pi_stable_id_scheme"
@@ -149164,14 +149189,45 @@ function readRawSessionMessageByIdFromDb(db, sessionId, messageId) {
149164
149189
  }
149165
149190
 
149166
149191
  // src/hooks/magic-context/tag-content-primitives.ts
149192
+ function stripWellFormedLeadingTagPrefix(value) {
149193
+ return value.replace(/^(\u00a7\d+\u00a7\s*)+/, "");
149194
+ }
149195
+ function stripCompleteTagPairsGlobally(value) {
149196
+ return value.replace(COMPLETE_TAG_PAIR_GLOBAL_REGEX, "");
149197
+ }
149198
+ function stripMalformedTagNotationGlobally(value) {
149199
+ return value.replace(MALFORMED_TAG_GLOBAL_REGEX, "");
149200
+ }
149201
+ function stripTagSectionCharacters(value) {
149202
+ return value.replace(STRAY_SECTION_CHAR_REGEX, "");
149203
+ }
149204
+ function stripPersistedAssistantText(value) {
149205
+ let text = stripWellFormedLeadingTagPrefix(value);
149206
+ text = stripCompleteTagPairsGlobally(text);
149207
+ text = stripMalformedTagNotationGlobally(text);
149208
+ text = stripTagSectionCharacters(text);
149209
+ return text.trim();
149210
+ }
149167
149211
  function byteSize(value) {
149168
149212
  return encoder.encode(value).length;
149169
149213
  }
149170
149214
  function stripTagPrefix(value) {
149171
- let stripped = value.replace(MALFORMED_TAG_PREFIX_REGEX, "");
149172
- stripped = stripped.replace(TAG_PREFIX_REGEX, "");
149215
+ let stripped = value;
149216
+ for (let pass = 0;pass < 8; pass++) {
149217
+ const prev = stripped;
149218
+ stripped = stripped.replace(MALFORMED_TAG_PREFIX_REGEX, "");
149219
+ stripped = stripped.replace(TAG_PREFIX_REGEX, "");
149220
+ if (stripped === prev)
149221
+ break;
149222
+ }
149173
149223
  return stripped;
149174
149224
  }
149225
+ function peelLeadingMcTagNotation(value) {
149226
+ const body = stripTagPrefix(value);
149227
+ if (body === value)
149228
+ return { tagPrefix: "", body };
149229
+ return { tagPrefix: value.slice(0, value.length - body.length), body };
149230
+ }
149175
149231
  function prependTag(tagId, value) {
149176
149232
  const stripped = stripTagPrefix(value);
149177
149233
  return `§${tagId}§ ${stripped}`;
@@ -149182,11 +149238,14 @@ function isThinkingPart(part) {
149182
149238
  const candidate = part;
149183
149239
  return candidate.type === "thinking" || candidate.type === "reasoning";
149184
149240
  }
149185
- var encoder, TAG_PREFIX_REGEX, MALFORMED_TAG_PREFIX_REGEX;
149241
+ var encoder, TAG_PREFIX_REGEX, MALFORMED_TAG_PREFIX_REGEX, COMPLETE_TAG_PAIR_GLOBAL_REGEX, MALFORMED_TAG_GLOBAL_REGEX, STRAY_SECTION_CHAR_REGEX;
149186
149242
  var init_tag_content_primitives = __esm(() => {
149187
149243
  encoder = new TextEncoder;
149188
149244
  TAG_PREFIX_REGEX = /^(?:§\d+§\s*)+/;
149189
149245
  MALFORMED_TAG_PREFIX_REGEX = /^(?:§\d+">§(?:\d+§)?\s*)+/;
149246
+ COMPLETE_TAG_PAIR_GLOBAL_REGEX = /\u00a7\d+\u00a7/g;
149247
+ MALFORMED_TAG_GLOBAL_REGEX = /\u00a7\d+">(?:\u00a7(?:\d+\u00a7)?)?/g;
149248
+ STRAY_SECTION_CHAR_REGEX = /\u00a7/g;
149190
149249
  });
149191
149250
 
149192
149251
  // src/hooks/magic-context/tag-part-guards.ts
@@ -149314,7 +149373,9 @@ function hasMeaningfulPart(part) {
149314
149373
  return false;
149315
149374
  const type = part.type;
149316
149375
  if (type === "text") {
149317
- return typeof part.text === "string" && part.text.trim().length > 0;
149376
+ if (typeof part.text !== "string")
149377
+ return false;
149378
+ return stripTagPrefix(part.text).trim().length > 0;
149318
149379
  }
149319
149380
  if (typeof type !== "string")
149320
149381
  return false;
@@ -149430,6 +149491,7 @@ function createToolDropTarget(compositeKey, thinkingParts, index, batch) {
149430
149491
  }
149431
149492
  var DROP_PREFIX = "[dropped", IGNORE_PART_TYPES, TRUNCATION_SENTINEL = "...[truncated]";
149432
149493
  var init_tool_drop_target = __esm(() => {
149494
+ init_tag_content_primitives();
149433
149495
  IGNORE_PART_TYPES = new Set([
149434
149496
  "thinking",
149435
149497
  "reasoning",
@@ -150082,6 +150144,18 @@ function keyFor(providerID, modelID, agentName) {
150082
150144
  function fingerprintFor(description, parameters) {
150083
150145
  return createHash3("sha256").update(description).update("\x00").update(stableStringify(parameters)).digest("hex");
150084
150146
  }
150147
+ function getCurrentToolSetHash(providerID, modelID, agentName) {
150148
+ const key = keyFor(providerID, modelID, agentName);
150149
+ const inner = fingerprints.get(key);
150150
+ if (!inner || inner.size === 0)
150151
+ return "";
150152
+ const parts = [];
150153
+ for (const [toolID, fp] of inner)
150154
+ parts.push(`${toolID}\x00${fp}`);
150155
+ parts.sort();
150156
+ return createHash3("sha256").update(parts.join(`
150157
+ `)).digest("hex");
150158
+ }
150085
150159
  function setDatabase(db) {
150086
150160
  persistenceDb = db;
150087
150161
  cachedInsertStmt = null;
@@ -150730,6 +150804,17 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
150730
150804
  updated_at INTEGER NOT NULL DEFAULT 0
150731
150805
  );
150732
150806
 
150807
+ CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
150808
+ project_path TEXT PRIMARY KEY,
150809
+ lease_holder TEXT,
150810
+ lease_expires_at INTEGER,
150811
+ last_swept_at INTEGER
150812
+ );
150813
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
150814
+ ON git_sweep_coordinator(lease_expires_at);
150815
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
150816
+ ON git_sweep_coordinator(last_swept_at);
150817
+
150733
150818
  CREATE TABLE IF NOT EXISTS m0_mutation_log (
150734
150819
  id INTEGER PRIMARY KEY AUTOINCREMENT,
150735
150820
  session_id TEXT NOT NULL,
@@ -150864,6 +150949,9 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
150864
150949
  cached_m0_materialized_at INTEGER,
150865
150950
  cached_m0_session_facts_version INTEGER,
150866
150951
  cached_m0_upgrade_state TEXT,
150952
+ cached_m0_system_hash TEXT,
150953
+ cached_m0_tool_set_hash TEXT,
150954
+ cached_m0_model_key TEXT,
150867
150955
  cached_m0_last_baseline_end_message_id TEXT,
150868
150956
  upgrade_reminded_at INTEGER,
150869
150957
  pi_stable_id_scheme INTEGER
@@ -151055,6 +151143,9 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
151055
151143
  ensureColumn(db, "session_meta", "cached_m0_materialized_at", "INTEGER");
151056
151144
  ensureColumn(db, "session_meta", "cached_m0_session_facts_version", "INTEGER");
151057
151145
  ensureColumn(db, "session_meta", "cached_m0_upgrade_state", "TEXT");
151146
+ ensureColumn(db, "session_meta", "cached_m0_system_hash", "TEXT");
151147
+ ensureColumn(db, "session_meta", "cached_m0_tool_set_hash", "TEXT");
151148
+ ensureColumn(db, "session_meta", "cached_m0_model_key", "TEXT");
151058
151149
  ensureColumn(db, "session_meta", "cached_m0_last_baseline_end_message_id", "TEXT");
151059
151150
  ensureColumn(db, "session_meta", "upgrade_reminded_at", "INTEGER");
151060
151151
  db.exec(`
@@ -151259,7 +151350,7 @@ function getDatabasePersistenceError(db) {
151259
151350
  return null;
151260
151351
  return persistenceErrorByDatabase.get(db) ?? null;
151261
151352
  }
151262
- var databases, persistenceByDatabase, persistenceErrorByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 27, sqlitePragmaConfig;
151353
+ var databases, persistenceByDatabase, persistenceErrorByDatabase, lastSchemaFenceRejection = null, LATEST_SUPPORTED_VERSION = 30, sqlitePragmaConfig;
151263
151354
  var init_storage_db = __esm(async () => {
151264
151355
  init_data_path();
151265
151356
  init_logger();
@@ -152050,6 +152141,60 @@ var init_migrations = __esm(async () => {
152050
152141
  ON tags(session_id, entry_fingerprint)
152051
152142
  WHERE type='message' AND entry_fingerprint IS NOT NULL`);
152052
152143
  }
152144
+ },
152145
+ {
152146
+ version: 28,
152147
+ description: "Add git commit sweep coordinator lease/cooldown table",
152148
+ up: (db) => {
152149
+ db.exec(`
152150
+ CREATE TABLE IF NOT EXISTS git_sweep_coordinator (
152151
+ project_path TEXT PRIMARY KEY,
152152
+ lease_holder TEXT,
152153
+ lease_expires_at INTEGER,
152154
+ last_swept_at INTEGER
152155
+ );
152156
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_lease_expires
152157
+ ON git_sweep_coordinator(lease_expires_at);
152158
+ CREATE INDEX IF NOT EXISTS idx_git_sweep_coordinator_last_swept
152159
+ ON git_sweep_coordinator(last_swept_at);
152160
+ `);
152161
+ }
152162
+ },
152163
+ {
152164
+ version: 29,
152165
+ description: "Add anchor_ordinal to notes (traceback to the conversation tail)",
152166
+ up: (db) => {
152167
+ const notesExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='notes'").get();
152168
+ if (!notesExists) {
152169
+ return;
152170
+ }
152171
+ const columns = db.prepare("PRAGMA table_info(notes)").all();
152172
+ if (!columns.some((column) => column.name === "anchor_ordinal")) {
152173
+ db.exec("ALTER TABLE notes ADD COLUMN anchor_ordinal INTEGER");
152174
+ }
152175
+ }
152176
+ },
152177
+ {
152178
+ version: 30,
152179
+ description: "HARD-bust m[0] markers: cached system/tool-set/model identity",
152180
+ up: (db) => {
152181
+ const hasSessionMeta = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='session_meta' LIMIT 1").get();
152182
+ if (!hasSessionMeta)
152183
+ return;
152184
+ ensureColumn(db, "session_meta", "cached_m0_system_hash", "TEXT");
152185
+ ensureColumn(db, "session_meta", "cached_m0_tool_set_hash", "TEXT");
152186
+ ensureColumn(db, "session_meta", "cached_m0_model_key", "TEXT");
152187
+ const columns = new Set(db.prepare("PRAGMA table_info(session_meta)").all().map((column) => column.name));
152188
+ if (columns.has("cached_m0_bytes")) {
152189
+ db.prepare(`UPDATE session_meta SET
152190
+ cached_m0_bytes = NULL,
152191
+ cached_m1_bytes = NULL,
152192
+ cached_m0_materialized_at = NULL,
152193
+ cached_m0_system_hash = NULL,
152194
+ cached_m0_tool_set_hash = NULL,
152195
+ cached_m0_model_key = NULL`).run();
152196
+ }
152197
+ }
152053
152198
  }
152054
152199
  ];
152055
152200
  LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
@@ -152968,6 +153113,9 @@ var init_storage_meta_session = __esm(async () => {
152968
153113
  cached_m0_materialized_at: "NULL AS cached_m0_materialized_at",
152969
153114
  cached_m0_session_facts_version: "NULL AS cached_m0_session_facts_version",
152970
153115
  cached_m0_upgrade_state: "NULL AS cached_m0_upgrade_state",
153116
+ cached_m0_system_hash: "NULL AS cached_m0_system_hash",
153117
+ cached_m0_tool_set_hash: "NULL AS cached_m0_tool_set_hash",
153118
+ cached_m0_model_key: "NULL AS cached_m0_model_key",
152971
153119
  last_observed_model_key: "NULL AS last_observed_model_key",
152972
153120
  upgrade_reminded_at: "NULL AS upgrade_reminded_at"
152973
153121
  };
@@ -153007,7 +153155,8 @@ function toNote(row) {
153007
153155
  updatedAt: row.updated_at,
153008
153156
  lastCheckedAt: toNullableNumber(row.last_checked_at),
153009
153157
  readyAt: toNullableNumber(row.ready_at),
153010
- readyReason: toNullableString(row.ready_reason)
153158
+ readyReason: toNullableString(row.ready_reason),
153159
+ anchorOrdinal: toNullableNumber(row.anchor_ordinal)
153011
153160
  };
153012
153161
  }
153013
153162
  function getNoteById(db, noteId) {
@@ -153059,7 +153208,7 @@ function getNotes(db, options = {}) {
153059
153208
  }
153060
153209
  function addNote(db, type, options) {
153061
153210
  const now = Date.now();
153062
- const result = type === "session" ? db.prepare("INSERT INTO notes (type, status, content, session_id, created_at, updated_at, harness) VALUES ('session', 'active', ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId, now, now, getHarness()) : db.prepare("INSERT INTO notes (type, status, content, session_id, project_path, surface_condition, created_at, updated_at, harness) VALUES ('smart', 'pending', ?, ?, ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId ?? null, options.projectPath, options.surfaceCondition, now, now, getHarness());
153211
+ const result = type === "session" ? db.prepare("INSERT INTO notes (type, status, content, session_id, created_at, updated_at, harness, anchor_ordinal) VALUES ('session', 'active', ?, ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId, now, now, getHarness(), options.anchorOrdinal ?? null) : db.prepare("INSERT INTO notes (type, status, content, session_id, project_path, surface_condition, created_at, updated_at, harness, anchor_ordinal) VALUES ('smart', 'pending', ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *").get(options.content, options.sessionId ?? null, options.projectPath, options.surfaceCondition, now, now, getHarness(), options.anchorOrdinal ?? null);
153063
153212
  if (!isNoteRow(result)) {
153064
153213
  throw new Error("[notes] failed to insert note");
153065
153214
  }
@@ -162943,25 +163092,56 @@ var require_src2 = __commonJS((exports, module) => {
162943
163092
  // src/features/magic-context/key-files/aft-availability.ts
162944
163093
  import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
162945
163094
  import { homedir as homedir6 } from "node:os";
162946
- import { join as join10 } from "node:path";
163095
+ import { isAbsolute as isAbsolute2, join as join10, resolve as resolve4 } from "node:path";
163096
+ import { fileURLToPath } from "node:url";
162947
163097
  function parseConfig(path6) {
162948
163098
  if (!existsSync9(path6))
162949
163099
  return null;
162950
163100
  return import_comment_json.parse(readFileSync7(path6, "utf-8"));
162951
163101
  }
162952
- function entryMatchesAft(entry) {
163102
+ function stringMentionsAft(value) {
163103
+ return AFT_NAME_NEEDLES.some((needle) => value.includes(needle));
163104
+ }
163105
+ function resolveLocalEntryPackageName(value, configDir) {
163106
+ let dir = null;
163107
+ if (value.startsWith("file://")) {
163108
+ try {
163109
+ dir = fileURLToPath(value);
163110
+ } catch {
163111
+ return null;
163112
+ }
163113
+ } else if (value.startsWith("~/")) {
163114
+ dir = join10(homedir6(), value.slice(2));
163115
+ } else if (value.startsWith("/") || value.startsWith("./") || value.startsWith("../")) {
163116
+ dir = isAbsolute2(value) ? value : resolve4(configDir, value);
163117
+ } else {
163118
+ return null;
163119
+ }
163120
+ try {
163121
+ const pkg = JSON.parse(readFileSync7(join10(dir, "package.json"), "utf-8"));
163122
+ return typeof pkg.name === "string" ? pkg.name : null;
163123
+ } catch {
163124
+ return null;
163125
+ }
163126
+ }
163127
+ function entryMatchesAft(entry, configDir) {
162953
163128
  const value = Array.isArray(entry) ? entry[0] : entry;
162954
- return typeof value === "string" && (value.includes("@cortexkit/aft") || value.includes("aft-opencode") || value.includes("aft-pi"));
163129
+ if (typeof value !== "string")
163130
+ return false;
163131
+ if (stringMentionsAft(value))
163132
+ return true;
163133
+ const name2 = resolveLocalEntryPackageName(value, configDir);
163134
+ return name2 != null && stringMentionsAft(name2);
162955
163135
  }
162956
- function hasAftInArray(value) {
162957
- return Array.isArray(value) && value.some(entryMatchesAft);
163136
+ function hasAftInArray(value, configDir) {
163137
+ return Array.isArray(value) && value.some((entry) => entryMatchesAft(entry, configDir));
162958
163138
  }
162959
- function hasAftAtKeys(value, keys) {
163139
+ function hasAftAtKeys(value, keys, configDir) {
162960
163140
  if (!value || typeof value !== "object")
162961
163141
  return false;
162962
163142
  const record2 = value;
162963
163143
  for (const key of keys) {
162964
- if (hasAftInArray(record2[key]))
163144
+ if (hasAftInArray(record2[key], configDir))
162965
163145
  return true;
162966
163146
  }
162967
163147
  return false;
@@ -162978,7 +163158,8 @@ function getAftAvailability() {
162978
163158
  for (const path6 of opencodePaths) {
162979
163159
  try {
162980
163160
  const config2 = parseConfig(path6);
162981
- if (hasAftAtKeys(config2, ["plugin", "plugins", "mcp", "mcp_servers"])) {
163161
+ const configDir = join10(path6, "..");
163162
+ if (hasAftAtKeys(config2, ["plugin", "plugins", "mcp", "mcp_servers"], configDir)) {
162982
163163
  opencode = true;
162983
163164
  break;
162984
163165
  }
@@ -162988,12 +163169,13 @@ function getAftAvailability() {
162988
163169
  for (const path6 of piPaths) {
162989
163170
  try {
162990
163171
  const config2 = parseConfig(path6);
162991
- if (hasAftAtKeys(config2, ["packages", "extensions"])) {
163172
+ const configDir = join10(path6, "..");
163173
+ if (hasAftAtKeys(config2, ["packages", "extensions"], configDir)) {
162992
163174
  pi = true;
162993
163175
  break;
162994
163176
  }
162995
163177
  const agent = config2?.agent;
162996
- if (hasAftAtKeys(agent, ["packages", "extensions"])) {
163178
+ if (hasAftAtKeys(agent, ["packages", "extensions"], configDir)) {
162997
163179
  pi = true;
162998
163180
  break;
162999
163181
  }
@@ -163010,9 +163192,10 @@ function getAftAvailability() {
163010
163192
  function isAftAvailable() {
163011
163193
  return getAftAvailability().available;
163012
163194
  }
163013
- var import_comment_json, overrideAvailability = null;
163195
+ var import_comment_json, overrideAvailability = null, AFT_NAME_NEEDLES;
163014
163196
  var init_aft_availability = __esm(() => {
163015
163197
  import_comment_json = __toESM(require_src2(), 1);
163198
+ AFT_NAME_NEEDLES = ["@cortexkit/aft", "aft-opencode", "aft-pi"];
163016
163199
  });
163017
163200
 
163018
163201
  // src/features/magic-context/memory/storage-memory-embeddings.ts
@@ -163697,7 +163880,7 @@ async function acquireModelLoadLock(lockPath) {
163697
163880
  if (Date.now() - waitStart > MAX_LOCK_WAIT_MS) {
163698
163881
  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.`);
163699
163882
  }
163700
- await new Promise((resolve5) => setTimeout(resolve5, LOCK_POLL_MS));
163883
+ await new Promise((resolve6) => setTimeout(resolve6, LOCK_POLL_MS));
163701
163884
  }
163702
163885
  }
163703
163886
  }
@@ -163866,7 +164049,7 @@ class LocalEmbeddingProvider {
163866
164049
  }
163867
164050
  const delayMs = 300 * attempt + Math.floor(Math.random() * 200);
163868
164051
  log(`[magic-context] embedding model load attempt ${attempt}/${MAX_ATTEMPTS} failed transiently, retrying in ${delayMs}ms`);
163869
- await new Promise((resolve5) => setTimeout(resolve5, delayMs));
164052
+ await new Promise((resolve6) => setTimeout(resolve6, delayMs));
163870
164053
  }
163871
164054
  }
163872
164055
  if (this.pipeline) {
@@ -163894,8 +164077,8 @@ class LocalEmbeddingProvider {
163894
164077
  if (this.inFlight === 0) {
163895
164078
  return Promise.resolve();
163896
164079
  }
163897
- return new Promise((resolve5) => {
163898
- this.inFlightWaiters.push(resolve5);
164080
+ return new Promise((resolve6) => {
164081
+ this.inFlightWaiters.push(resolve6);
163899
164082
  });
163900
164083
  }
163901
164084
  finishInFlight() {
@@ -164322,8 +164505,130 @@ var init_storage_git_commit_embeddings = __esm(() => {
164322
164505
  distinctModelIdStatements = new WeakMap;
164323
164506
  });
164324
164507
 
164508
+ // src/features/magic-context/git-commits/sweep-coordinator.ts
164509
+ function runImmediate2(db, body) {
164510
+ db.exec("BEGIN IMMEDIATE");
164511
+ let committed = false;
164512
+ try {
164513
+ const result = body();
164514
+ db.exec("COMMIT");
164515
+ committed = true;
164516
+ return result;
164517
+ } finally {
164518
+ if (!committed) {
164519
+ try {
164520
+ db.exec("ROLLBACK");
164521
+ } catch {}
164522
+ }
164523
+ }
164524
+ }
164525
+ function rowToState(row) {
164526
+ return {
164527
+ projectPath: row.project_path,
164528
+ leaseHolder: row.lease_holder,
164529
+ leaseExpiresAt: row.lease_expires_at,
164530
+ lastSweptAt: row.last_swept_at
164531
+ };
164532
+ }
164533
+ function getGitSweepCoordinatorState(db, projectPath) {
164534
+ const row = db.prepare(`SELECT project_path, lease_holder, lease_expires_at, last_swept_at
164535
+ FROM git_sweep_coordinator
164536
+ WHERE project_path = ?`).get(projectPath);
164537
+ return row ? rowToState(row) : null;
164538
+ }
164539
+ function acquireGitSweepLease(db, projectPath, holderId, options = {}) {
164540
+ const cooldownMs = options.cooldownMs ?? GIT_SWEEP_COOLDOWN_MS;
164541
+ const leaseTtlMs = options.leaseTtlMs ?? GIT_SWEEP_LEASE_TTL_MS;
164542
+ return runImmediate2(db, () => {
164543
+ const now = Date.now();
164544
+ const row = getGitSweepCoordinatorState(db, projectPath);
164545
+ if (row?.leaseHolder && row.leaseExpiresAt !== null && row.leaseExpiresAt > now) {
164546
+ return {
164547
+ acquired: false,
164548
+ projectPath,
164549
+ reason: "lease_active",
164550
+ leaseHolder: row.leaseHolder,
164551
+ leaseExpiresAt: row.leaseExpiresAt,
164552
+ lastSweptAt: row.lastSweptAt,
164553
+ nextAllowedAt: null
164554
+ };
164555
+ }
164556
+ if (!options.ignoreCooldown && row?.lastSweptAt !== null && row?.lastSweptAt !== undefined) {
164557
+ const nextAllowedAt = row.lastSweptAt + cooldownMs;
164558
+ if (nextAllowedAt > now) {
164559
+ return {
164560
+ acquired: false,
164561
+ projectPath,
164562
+ reason: "cooldown_active",
164563
+ leaseHolder: row.leaseHolder,
164564
+ leaseExpiresAt: row.leaseExpiresAt,
164565
+ lastSweptAt: row.lastSweptAt,
164566
+ nextAllowedAt
164567
+ };
164568
+ }
164569
+ }
164570
+ const leaseExpiresAt = now + leaseTtlMs;
164571
+ db.prepare(`INSERT INTO git_sweep_coordinator (
164572
+ project_path,
164573
+ lease_holder,
164574
+ lease_expires_at,
164575
+ last_swept_at
164576
+ ) VALUES (?, ?, ?, NULL)
164577
+ ON CONFLICT(project_path) DO UPDATE SET
164578
+ lease_holder = excluded.lease_holder,
164579
+ lease_expires_at = excluded.lease_expires_at`).run(projectPath, holderId, leaseExpiresAt);
164580
+ return {
164581
+ acquired: true,
164582
+ projectPath,
164583
+ holderId,
164584
+ acquiredAt: now,
164585
+ leaseExpiresAt
164586
+ };
164587
+ });
164588
+ }
164589
+ function renewGitSweepLease(db, projectPath, holderId, leaseTtlMs = GIT_SWEEP_LEASE_TTL_MS) {
164590
+ return runImmediate2(db, () => {
164591
+ const now = Date.now();
164592
+ const leaseExpiresAt = now + leaseTtlMs;
164593
+ const result = db.prepare(`UPDATE git_sweep_coordinator
164594
+ SET lease_expires_at = ?
164595
+ WHERE project_path = ?
164596
+ AND lease_holder = ?
164597
+ AND lease_expires_at > ?`).run(leaseExpiresAt, projectPath, holderId, now);
164598
+ return result.changes === 1;
164599
+ });
164600
+ }
164601
+ function markGitSweepSuccessAndRelease(db, projectPath, holderId) {
164602
+ return runImmediate2(db, () => {
164603
+ const now = Date.now();
164604
+ const result = db.prepare(`UPDATE git_sweep_coordinator
164605
+ SET lease_holder = NULL,
164606
+ lease_expires_at = NULL,
164607
+ last_swept_at = ?
164608
+ WHERE project_path = ?
164609
+ AND lease_holder = ?
164610
+ AND lease_expires_at > ?`).run(now, projectPath, holderId, now);
164611
+ return result.changes === 1;
164612
+ });
164613
+ }
164614
+ function releaseGitSweepLease(db, projectPath, holderId) {
164615
+ runImmediate2(db, () => {
164616
+ db.prepare(`UPDATE git_sweep_coordinator
164617
+ SET lease_holder = NULL,
164618
+ lease_expires_at = NULL
164619
+ WHERE project_path = ?
164620
+ AND lease_holder = ?`).run(projectPath, holderId);
164621
+ });
164622
+ }
164623
+ var GIT_SWEEP_COOLDOWN_MS, GIT_SWEEP_LEASE_TTL_MS, GIT_SWEEP_LEASE_RENEWAL_MS;
164624
+ var init_sweep_coordinator = __esm(() => {
164625
+ GIT_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
164626
+ GIT_SWEEP_LEASE_TTL_MS = 5 * 60 * 1000;
164627
+ GIT_SWEEP_LEASE_RENEWAL_MS = 60 * 1000;
164628
+ });
164629
+
164325
164630
  // src/features/magic-context/project-embedding-registry.ts
164326
- import { createHash as createHash7 } from "node:crypto";
164631
+ import { createHash as createHash7, randomUUID } from "node:crypto";
164327
164632
  function resolveEmbeddingConfig(config2) {
164328
164633
  if (!config2 || config2.provider === "local") {
164329
164634
  return {
@@ -164587,6 +164892,7 @@ var init_project_embedding_registry = __esm(() => {
164587
164892
  init_magic_context();
164588
164893
  init_logger();
164589
164894
  init_storage_git_commit_embeddings();
164895
+ init_sweep_coordinator();
164590
164896
  init_embedding_cache();
164591
164897
  init_embedding_identity();
164592
164898
  init_embedding_local();
@@ -164685,32 +164991,48 @@ var init_storage_memory_fts = __esm(() => {
164685
164991
  });
164686
164992
 
164687
164993
  // src/shared/models-dev-cache.ts
164688
- import { createHash as createHash9 } from "node:crypto";
164689
- import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
164690
- import { homedir as homedir7, platform as platform2 } from "node:os";
164994
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync9, renameSync, writeFileSync } from "node:fs";
164691
164995
  import { join as join14 } from "node:path";
164692
- function hashFast(input) {
164693
- return createHash9("sha1").update(input).digest("hex");
164694
- }
164695
- function getModelsJsonPath() {
164696
- const explicit = process.env.OPENCODE_MODELS_PATH?.trim();
164697
- if (explicit)
164698
- return explicit;
164699
- const cacheBase = getCacheDir();
164700
- const source = process.env.OPENCODE_MODELS_URL?.trim();
164701
- const filename = source && source !== "https://models.dev" ? `models-${hashFast(source)}.json` : "models.json";
164702
- return join14(cacheBase, "opencode", filename);
164703
- }
164704
- function getOpencodeConfigPath() {
164705
- const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
164706
- const configDir = envDir ? envDir : platform2() === "win32" ? join14(homedir7(), ".config", "opencode") : join14(process.env.XDG_CONFIG_HOME || join14(homedir7(), ".config"), "opencode");
164707
- const jsonc = join14(configDir, "opencode.jsonc");
164708
- if (existsSync11(jsonc))
164709
- return jsonc;
164710
- const json2 = join14(configDir, "opencode.json");
164711
- if (existsSync11(json2))
164712
- return json2;
164713
- return null;
164996
+ function isSaneLimit(limit) {
164997
+ return typeof limit === "number" && limit >= MIN_SANE_LIMIT && limit <= MAX_SANE_LIMIT;
164998
+ }
164999
+ function persistFilePath() {
165000
+ return join14(getMagicContextStorageDir(), `model-context-limits-${getHarness()}.json`);
165001
+ }
165002
+ function loadPersistedApiCacheOnce() {
165003
+ if (persistSeedLoaded || apiCache !== null)
165004
+ return;
165005
+ persistSeedLoaded = true;
165006
+ try {
165007
+ const raw = readFileSync9(persistFilePath(), "utf-8");
165008
+ const obj = JSON.parse(raw);
165009
+ const map2 = new Map;
165010
+ for (const [key, limit] of Object.entries(obj)) {
165011
+ if (isSaneLimit(limit))
165012
+ map2.set(key, { limit });
165013
+ }
165014
+ if (map2.size > 0) {
165015
+ apiCache = map2;
165016
+ sessionLog("global", `models-dev-cache: seeded ${map2.size} entries from persisted cache (cold start)`);
165017
+ }
165018
+ } catch {}
165019
+ }
165020
+ function persistApiCache() {
165021
+ if (!apiCache)
165022
+ return;
165023
+ const obj = {};
165024
+ for (const [key, value] of apiCache) {
165025
+ if (isSaneLimit(value.limit))
165026
+ obj[key] = value.limit;
165027
+ }
165028
+ try {
165029
+ const dir = getMagicContextStorageDir();
165030
+ mkdirSync4(dir, { recursive: true });
165031
+ const target = persistFilePath();
165032
+ const tmp = `${target}.${process.pid}.tmp`;
165033
+ writeFileSync(tmp, JSON.stringify(obj), { encoding: "utf-8", mode: 384 });
165034
+ renameSync(tmp, target);
165035
+ } catch {}
164714
165036
  }
164715
165037
  function resolveLimit(limit) {
164716
165038
  if (!limit)
@@ -164723,7 +165045,7 @@ function resolveLimit(limit) {
164723
165045
  }
164724
165046
  function setCachedModelMetadata(cache, key, model) {
164725
165047
  const limit = resolveLimit(model?.limit);
164726
- if (limit === undefined) {
165048
+ if (!isSaneLimit(limit)) {
164727
165049
  return;
164728
165050
  }
164729
165051
  const value = { limit };
@@ -164735,54 +165057,26 @@ function setCachedModelMetadata(cache, key, model) {
164735
165057
  }
164736
165058
  }
164737
165059
  }
164738
- function loadModelsDevMetadataFromFile() {
164739
- const metadata = new Map;
164740
- const modelsJsonPath = getModelsJsonPath();
164741
- let fileFound = false;
164742
- try {
164743
- if (existsSync11(modelsJsonPath)) {
164744
- fileFound = true;
164745
- const raw = readFileSync9(modelsJsonPath, "utf-8");
164746
- const data = JSON.parse(raw);
164747
- for (const [providerId, provider2] of Object.entries(data)) {
164748
- if (!provider2?.models || typeof provider2.models !== "object")
164749
- continue;
164750
- for (const [modelId, model] of Object.entries(provider2.models)) {
164751
- setCachedModelMetadata(metadata, `${providerId}/${modelId}`, model);
164752
- }
164753
- }
164754
- }
164755
- } catch (error51) {
164756
- sessionLog("global", `models-dev-cache: failed to read models.json at ${modelsJsonPath}:`, error51 instanceof Error ? error51.message : String(error51));
164757
- }
164758
- try {
164759
- const configPath = getOpencodeConfigPath();
164760
- if (configPath && existsSync11(configPath)) {
164761
- const config2 = parseJsonc(readFileSync9(configPath, "utf-8"));
164762
- if (config2.provider && typeof config2.provider === "object") {
164763
- for (const [providerId, provider2] of Object.entries(config2.provider)) {
164764
- if (!provider2?.models || typeof provider2.models !== "object")
164765
- continue;
164766
- for (const [modelId, model] of Object.entries(provider2.models)) {
164767
- setCachedModelMetadata(metadata, `${providerId}/${modelId}`, model);
164768
- }
164769
- }
164770
- }
165060
+ async function refreshModelLimitsFromApi(client, options) {
165061
+ const attempts = Math.max(1, (options?.retries ?? 0) + 1);
165062
+ const delayMs = options?.retryDelayMs ?? 1000;
165063
+ for (let attempt = 1;attempt <= attempts; attempt++) {
165064
+ const ok = await refreshModelLimitsOnce(client);
165065
+ if (ok)
165066
+ return;
165067
+ if (attempt < attempts) {
165068
+ await new Promise((resolve6) => setTimeout(resolve6, delayMs));
164771
165069
  }
164772
- } catch (error51) {
164773
- sessionLog("global", "models-dev-cache: failed to read opencode config for custom models:", error51 instanceof Error ? error51.message : String(error51));
164774
165070
  }
164775
- sessionLog("global", `models-dev-cache: file-layer loaded ${metadata.size} model metadata entries (modelsJsonPath=${modelsJsonPath}, found=${fileFound})`);
164776
- return metadata;
164777
165071
  }
164778
- async function refreshModelLimitsFromApi(client) {
165072
+ async function refreshModelLimitsOnce(client) {
164779
165073
  try {
164780
165074
  const result = await client.config.providers();
164781
165075
  const data = result.data;
164782
165076
  const providers = data?.providers;
164783
- if (!Array.isArray(providers)) {
164784
- sessionLog("global", "models-dev-cache: API refresh returned no providers payload");
164785
- return;
165077
+ if (!Array.isArray(providers) || providers.length === 0) {
165078
+ sessionLog("global", "models-dev-cache: API refresh returned no providers payload (will retry if attempts remain)");
165079
+ return false;
164786
165080
  }
164787
165081
  const map2 = new Map;
164788
165082
  for (const entry of providers) {
@@ -164796,27 +165090,22 @@ async function refreshModelLimitsFromApi(client) {
164796
165090
  const previousSize = apiCache?.size ?? null;
164797
165091
  apiCache = map2;
164798
165092
  apiLoadedAt = Date.now();
165093
+ persistApiCache();
164799
165094
  if (previousSize === null) {
164800
165095
  sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model metadata entries`);
164801
165096
  } else if (previousSize !== map2.size) {
164802
165097
  sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model metadata entries (was ${previousSize})`);
164803
165098
  }
165099
+ return true;
164804
165100
  } catch (error51) {
164805
165101
  sessionLog("global", "models-dev-cache: API refresh failed:", error51 instanceof Error ? error51.message : String(error51));
165102
+ return false;
164806
165103
  }
164807
165104
  }
164808
- function getModelsDevContextLimit(providerID, modelID) {
164809
- const now = Date.now();
164810
- if (!fileCache || now - fileLastAttempt > RELOAD_INTERVAL_MS) {
164811
- fileLastAttempt = now;
164812
- fileCache = loadModelsDevMetadataFromFile();
164813
- }
165105
+ function getSdkContextLimit(providerID, modelID) {
165106
+ loadPersistedApiCacheOnce();
164814
165107
  const fromApi = lookupLimitWithTagFallback(apiCache, providerID, modelID);
164815
- const fromFile = lookupLimitWithTagFallback(fileCache, providerID, modelID);
164816
- if (typeof fromApi === "number" && typeof fromFile === "number") {
164817
- return Math.max(fromApi, fromFile);
164818
- }
164819
- return fromApi ?? fromFile;
165108
+ return isSaneLimit(fromApi) ? fromApi : undefined;
164820
165109
  }
164821
165110
  function lookupLimitWithTagFallback(cache, providerID, modelID) {
164822
165111
  if (!cache)
@@ -164833,12 +165122,10 @@ function lookupLimitWithTagFallback(cache, providerID, modelID) {
164833
165122
  }
164834
165123
  return;
164835
165124
  }
164836
- var RELOAD_INTERVAL_MS, apiCache = null, apiLoadedAt = 0, fileCache = null, fileLastAttempt = 0;
165125
+ var MIN_SANE_LIMIT = 20000, MAX_SANE_LIMIT = 3000000, apiCache = null, apiLoadedAt = 0, persistSeedLoaded = false;
164837
165126
  var init_models_dev_cache = __esm(() => {
164838
165127
  init_data_path();
164839
- init_jsonc_parser();
164840
165128
  init_logger();
164841
- RELOAD_INTERVAL_MS = 5 * 60 * 1000;
164842
165129
  });
164843
165130
 
164844
165131
  // src/shared/rpc-notifications.ts
@@ -165564,7 +165851,7 @@ var init_compartment_runner_validation = __esm(async () => {
165564
165851
  });
165565
165852
 
165566
165853
  // src/hooks/magic-context/compartment-runner-historian.ts
165567
- import { mkdirSync as mkdirSync4, unlinkSync, writeFileSync } from "node:fs";
165854
+ import { mkdirSync as mkdirSync5, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
165568
165855
  import { join as join17 } from "node:path";
165569
165856
  function historianResponseDumpDir(directory) {
165570
165857
  return getProjectMagicContextHistorianDir(directory);
@@ -165764,12 +166051,12 @@ async function runHistorianPrompt(args) {
165764
166051
  error: `Historian failed while processing this session: ${desc.brief}`
165765
166052
  };
165766
166053
  } finally {
165767
- if (agentSessionId && outcomeOk) {
166054
+ if (agentSessionId && outcomeOk && !shouldKeepSubagents()) {
165768
166055
  await client.session.delete({ path: { id: agentSessionId } }).catch((e) => {
165769
166056
  sessionLog(parentSessionId, "compartment agent: session cleanup failed", getErrorMessage(e));
165770
166057
  });
165771
- } else if (agentSessionId && !outcomeOk) {
165772
- sessionLog(parentSessionId, `historian: KEEPING failed child session ${agentSessionId} for debugging (not deleted)`);
166058
+ } else if (agentSessionId && (!outcomeOk || shouldKeepSubagents())) {
166059
+ sessionLog(parentSessionId, `historian: KEEPING child session ${agentSessionId} (${outcomeOk ? "keep_subagents" : "failed"}) — not deleted`);
165773
166060
  }
165774
166061
  }
165775
166062
  }
@@ -165848,8 +166135,8 @@ function isTransientHistorianPromptError(message) {
165848
166135
  ].some((token) => normalized.includes(token));
165849
166136
  }
165850
166137
  function sleep(ms) {
165851
- return new Promise((resolve5) => {
165852
- setTimeout(resolve5, ms);
166138
+ return new Promise((resolve6) => {
166139
+ setTimeout(resolve6, ms);
165853
166140
  });
165854
166141
  }
165855
166142
  function cleanupHistorianDump(sessionId, dumpPath) {
@@ -165867,11 +166154,11 @@ function cleanupHistorianDump(sessionId, dumpPath) {
165867
166154
  function dumpHistorianResponse(sessionId, directory, label, text) {
165868
166155
  try {
165869
166156
  const dumpDir = historianResponseDumpDir(directory);
165870
- mkdirSync4(dumpDir, { recursive: true });
166157
+ mkdirSync5(dumpDir, { recursive: true });
165871
166158
  const safeSessionId = sanitizeDumpName(sessionId);
165872
166159
  const safeLabel = sanitizeDumpName(label);
165873
166160
  const dumpPath = join17(dumpDir, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
165874
- writeFileSync(dumpPath, text, "utf8");
166161
+ writeFileSync2(dumpPath, text, "utf8");
165875
166162
  sessionLog(sessionId, "compartment agent: historian response dumped", {
165876
166163
  label,
165877
166164
  dumpPath
@@ -165993,7 +166280,7 @@ function insertCompartmentEvents(db, sessionId, events, compartmentIds) {
165993
166280
  var init_compartment_events = () => {};
165994
166281
 
165995
166282
  // src/hooks/magic-context/historian-state-file.ts
165996
- import { mkdirSync as mkdirSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "node:fs";
166283
+ import { mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
165997
166284
  function cleanupHistorianStateFile(path6) {
165998
166285
  if (!path6)
165999
166286
  return;
@@ -166548,9 +166835,7 @@ function injectTemporalMarkers(messages) {
166548
166835
  if (prefix && Array.isArray(msg.parts)) {
166549
166836
  const target = findFirstVisibleTextPart(msg.parts);
166550
166837
  if (target && typeof target.text === "string") {
166551
- const tagMatch = target.text.match(/^(?:§\d+§\s*)+/);
166552
- const tagPrefix = tagMatch ? tagMatch[0] : "";
166553
- const body = target.text.slice(tagPrefix.length);
166838
+ const { tagPrefix, body } = peelLeadingMcTagNotation(target.text);
166554
166839
  if (!TEMPORAL_MARKER_PATTERN.test(body)) {
166555
166840
  target.text = tagPrefix + prefix + body;
166556
166841
  injected++;
@@ -166565,6 +166850,7 @@ function injectTemporalMarkers(messages) {
166565
166850
  }
166566
166851
  var TEMPORAL_AWARENESS_THRESHOLD_SECONDS = 300, SECONDS_PER_MINUTE = 60, SECONDS_PER_HOUR, SECONDS_PER_DAY, SECONDS_PER_WEEK, TEMPORAL_MARKER_PATTERN;
166567
166852
  var init_temporal_awareness = __esm(() => {
166853
+ init_tag_content_primitives();
166568
166854
  SECONDS_PER_HOUR = 60 * 60;
166569
166855
  SECONDS_PER_DAY = 24 * 60 * 60;
166570
166856
  SECONDS_PER_WEEK = 7 * 24 * 60 * 60;
@@ -166859,12 +167145,6 @@ function getMaxCompartmentSeq(db, sessionId) {
166859
167145
  const row = cachedStatement(maxCompartmentSeqStatements, db, "SELECT COALESCE(MAX(sequence), -1) AS s FROM compartments WHERE session_id = ?").get(sessionId);
166860
167146
  return numberFromRow(row, "s");
166861
167147
  }
166862
- function normalizeCachedMaxCompartmentSeq(db, sessionId, stored) {
166863
- if (stored === 0 && getMaxCompartmentSeq(db, sessionId) === EMPTY_MAX_COMPARTMENT_SEQ) {
166864
- return EMPTY_MAX_COMPARTMENT_SEQ;
166865
- }
166866
- return stored;
166867
- }
166868
167148
  function getMaxMemoryId(db, projectPath) {
166869
167149
  if (!projectPath)
166870
167150
  return 0;
@@ -166888,6 +167168,7 @@ function getGlobalUserProfileVersion(db) {
166888
167168
  }
166889
167169
  function readCurrentM0SnapshotMarkers(args) {
166890
167170
  const projectDirectory = args.projectDirectory ?? args.projectPath ?? "";
167171
+ const hard = args.hardSignals ?? EMPTY_HARD_SIGNALS;
166891
167172
  return {
166892
167173
  projectMemoryEpoch: getProjectMemoryEpoch(args.db, args.projectPath),
166893
167174
  projectUserProfileVersion: getGlobalUserProfileVersion(args.db),
@@ -166898,7 +167179,10 @@ function readCurrentM0SnapshotMarkers(args) {
166898
167179
  projectDocsHash: projectDirectory ? computeProjectDocsHash(projectDirectory) : "",
166899
167180
  materializedAt: Date.now(),
166900
167181
  sessionFactsVersion: getSessionFactsVersion(args.db, args.sessionId),
166901
- upgradeState: getUpgradeState(args.db, args.sessionId)
167182
+ upgradeState: getUpgradeState(args.db, args.sessionId),
167183
+ systemHash: hard.systemHash,
167184
+ toolSetHash: hard.toolSetHash,
167185
+ modelKey: hard.modelKey
166902
167186
  };
166903
167187
  }
166904
167188
  function snapshotMarkersFromCachedM0(state) {
@@ -166928,7 +167212,10 @@ function snapshotMarkersFromCachedM0(state) {
166928
167212
  projectDocsHash: state.cachedM0ProjectDocsHash ?? "",
166929
167213
  materializedAt: state.cachedM0MaterializedAt ?? 0,
166930
167214
  sessionFactsVersion: state.cachedM0SessionFactsVersion,
166931
- upgradeState: state.cachedM0UpgradeState
167215
+ upgradeState: state.cachedM0UpgradeState,
167216
+ systemHash: state.cachedM0SystemHash ?? "",
167217
+ toolSetHash: state.cachedM0ToolSetHash ?? "",
167218
+ modelKey: state.cachedM0ModelKey ?? ""
166932
167219
  };
166933
167220
  }
166934
167221
  function mustMaterialize(args) {
@@ -166936,16 +167223,22 @@ function mustMaterialize(args) {
166936
167223
  return { value: true, reason: "first_render" };
166937
167224
  if (!args.state.cachedM1Bytes)
166938
167225
  return { value: true, reason: "cached_m1_missing" };
167226
+ const hard = args.hardSignals ?? EMPTY_HARD_SIGNALS;
166939
167227
  const current = readCurrentM0SnapshotMarkers(args);
166940
- if (args.state.cachedM0ProjectMemoryEpoch !== current.projectMemoryEpoch) {
166941
- return { value: true, reason: "project_memory_epoch" };
167228
+ if (hard.modelKey !== "" && hard.modelKey !== (args.state.cachedM0ModelKey ?? "")) {
167229
+ return { value: true, reason: "model_change" };
167230
+ }
167231
+ if (hard.systemHash !== "" && hard.systemHash !== (args.state.cachedM0SystemHash ?? "")) {
167232
+ return { value: true, reason: "system_hash" };
166942
167233
  }
166943
- if (args.state.cachedM0ProjectUserProfileVersion !== current.projectUserProfileVersion) {
166944
- return { value: true, reason: "project_user_profile_version" };
167234
+ if (hard.toolSetHash !== "" && hard.toolSetHash !== (args.state.cachedM0ToolSetHash ?? "")) {
167235
+ return { value: true, reason: "tool_set_hash" };
166945
167236
  }
166946
- const cachedMaxSeq = args.state.cachedM0MaxCompartmentSeq === null ? null : normalizeCachedMaxCompartmentSeq(args.db, args.sessionId, args.state.cachedM0MaxCompartmentSeq);
166947
- if (cachedMaxSeq !== current.maxCompartmentSeq) {
166948
- return { value: true, reason: "max_compartment_seq" };
167237
+ if (hard.cacheExpired && hard.lastResponseTime > 0 && hard.lastResponseTime > (args.state.cachedM0MaterializedAt ?? 0)) {
167238
+ return { value: true, reason: "ttl_idle" };
167239
+ }
167240
+ if (args.state.cachedM0ProjectMemoryEpoch !== current.projectMemoryEpoch) {
167241
+ return { value: true, reason: "project_memory_epoch" };
166949
167242
  }
166950
167243
  if (args.state.cachedM0MaxMutationId !== current.maxMutationId) {
166951
167244
  return { value: true, reason: "max_mutation_id" };
@@ -167134,6 +167427,9 @@ function applyMarkersToState(state, m0Bytes, markers, m1Bytes) {
167134
167427
  state.cachedM0MaterializedAt = markers.materializedAt;
167135
167428
  state.cachedM0SessionFactsVersion = markers.sessionFactsVersion;
167136
167429
  state.cachedM0UpgradeState = markers.upgradeState;
167430
+ state.cachedM0SystemHash = markers.systemHash;
167431
+ state.cachedM0ToolSetHash = markers.toolSetHash;
167432
+ state.cachedM0ModelKey = markers.modelKey;
167137
167433
  state.snapshotMarkers = markers;
167138
167434
  }
167139
167435
  function historySliceTokens(m0Text) {
@@ -167158,7 +167454,8 @@ function materializeM0(options) {
167158
167454
  db: options.db,
167159
167455
  sessionId: options.sessionId,
167160
167456
  projectPath,
167161
- projectDirectory
167457
+ projectDirectory,
167458
+ hardSignals: options.hardSignals
167162
167459
  });
167163
167460
  docs = projectDirectory ? readProjectDocsCanonical(projectDirectory) : { renderedBlock: "", canonicalHash: "" };
167164
167461
  snapshotMarkers.projectDocsHash = docs.canonicalHash;
@@ -167224,7 +167521,10 @@ function materializeM0(options) {
167224
167521
  projectDocsHash: phase3ProjectDocsHash,
167225
167522
  materializedAt: Date.now(),
167226
167523
  sessionFactsVersion: getSessionFactsVersion(options.db, options.sessionId),
167227
- upgradeState: getUpgradeState(options.db, options.sessionId)
167524
+ upgradeState: getUpgradeState(options.db, options.sessionId),
167525
+ systemHash: snapshotMarkers.systemHash,
167526
+ toolSetHash: snapshotMarkers.toolSetHash,
167527
+ modelKey: snapshotMarkers.modelKey
167228
167528
  };
167229
167529
  const stale = current.projectMemoryEpoch !== snapshotMarkers.projectMemoryEpoch || current.projectUserProfileVersion !== snapshotMarkers.projectUserProfileVersion || current.maxCompartmentSeq !== snapshotMarkers.maxCompartmentSeq || current.maxMutationId !== snapshotMarkers.maxMutationId || current.maxMemoryMutationId !== snapshotMarkers.maxMemoryMutationId || current.projectDocsHash !== snapshotMarkers.projectDocsHash || current.sessionFactsVersion !== snapshotMarkers.sessionFactsVersion || current.upgradeState !== snapshotMarkers.upgradeState;
167230
167530
  if (stale) {
@@ -167246,7 +167546,10 @@ function materializeM0(options) {
167246
167546
  projectDocsHash: snapshotMarkers.projectDocsHash,
167247
167547
  materializedAt: snapshotMarkers.materializedAt,
167248
167548
  sessionFactsVersion: snapshotMarkers.sessionFactsVersion,
167249
- upgradeState: snapshotMarkers.upgradeState
167549
+ upgradeState: snapshotMarkers.upgradeState,
167550
+ systemHash: snapshotMarkers.systemHash,
167551
+ toolSetHash: snapshotMarkers.toolSetHash,
167552
+ modelKey: snapshotMarkers.modelKey
167250
167553
  });
167251
167554
  options.db.prepare("UPDATE session_meta SET memory_block_count = ?, memory_block_ids = ? WHERE session_id = ?").run(renderedMemoryIds.length, JSON.stringify(renderedMemoryIds), options.sessionId);
167252
167555
  options.db.exec("COMMIT");
@@ -167405,6 +167708,9 @@ function readCachedM0M1Row(db, sessionId) {
167405
167708
  cached_m0_materialized_at,
167406
167709
  cached_m0_session_facts_version,
167407
167710
  cached_m0_upgrade_state,
167711
+ cached_m0_system_hash,
167712
+ cached_m0_tool_set_hash,
167713
+ cached_m0_model_key,
167408
167714
  memory_block_ids
167409
167715
  FROM session_meta
167410
167716
  WHERE session_id = ?`).get(sessionId);
@@ -167436,11 +167742,14 @@ function markersFromCachedRow(row) {
167436
167742
  projectDocsHash: row.cached_m0_project_docs_hash ?? "",
167437
167743
  materializedAt: row.cached_m0_materialized_at ?? 0,
167438
167744
  sessionFactsVersion: row.cached_m0_session_facts_version,
167439
- upgradeState: row.cached_m0_upgrade_state
167745
+ upgradeState: row.cached_m0_upgrade_state,
167746
+ systemHash: row.cached_m0_system_hash ?? "",
167747
+ toolSetHash: row.cached_m0_tool_set_hash ?? "",
167748
+ modelKey: row.cached_m0_model_key ?? ""
167440
167749
  };
167441
167750
  }
167442
167751
  function cachedRowMatchesState(row, state) {
167443
- return bufferEqualsNullable(row.cached_m0_bytes, state.cachedM0Bytes) && row.cached_m0_project_memory_epoch === state.cachedM0ProjectMemoryEpoch && row.cached_m0_project_user_profile_version === state.cachedM0ProjectUserProfileVersion && row.cached_m0_max_compartment_seq === state.cachedM0MaxCompartmentSeq && row.cached_m0_max_memory_id === state.cachedM0MaxMemoryId && row.cached_m0_max_mutation_id === state.cachedM0MaxMutationId && row.cached_m0_max_memory_mutation_id === state.cachedM0MaxMemoryMutationId && (row.cached_m0_project_docs_hash ?? "") === (state.cachedM0ProjectDocsHash ?? "") && row.cached_m0_materialized_at === state.cachedM0MaterializedAt && row.cached_m0_session_facts_version === state.cachedM0SessionFactsVersion && (row.cached_m0_upgrade_state ?? null) === (state.cachedM0UpgradeState ?? null);
167752
+ return bufferEqualsNullable(row.cached_m0_bytes, state.cachedM0Bytes) && row.cached_m0_project_memory_epoch === state.cachedM0ProjectMemoryEpoch && row.cached_m0_project_user_profile_version === state.cachedM0ProjectUserProfileVersion && row.cached_m0_max_compartment_seq === state.cachedM0MaxCompartmentSeq && row.cached_m0_max_memory_id === state.cachedM0MaxMemoryId && row.cached_m0_max_mutation_id === state.cachedM0MaxMutationId && row.cached_m0_max_memory_mutation_id === state.cachedM0MaxMemoryMutationId && (row.cached_m0_project_docs_hash ?? "") === (state.cachedM0ProjectDocsHash ?? "") && row.cached_m0_materialized_at === state.cachedM0MaterializedAt && row.cached_m0_session_facts_version === state.cachedM0SessionFactsVersion && (row.cached_m0_upgrade_state ?? null) === (state.cachedM0UpgradeState ?? null) && (row.cached_m0_system_hash ?? "") === (state.cachedM0SystemHash ?? "") && (row.cached_m0_tool_set_hash ?? "") === (state.cachedM0ToolSetHash ?? "") && (row.cached_m0_model_key ?? "") === (state.cachedM0ModelKey ?? "");
167444
167753
  }
167445
167754
  function applyCachedRowToState(state, row) {
167446
167755
  const markers = markersFromCachedRow(row);
@@ -167459,6 +167768,9 @@ function applyCachedRowToState(state, row) {
167459
167768
  state.cachedM0MaterializedAt = markers.materializedAt;
167460
167769
  state.cachedM0SessionFactsVersion = markers.sessionFactsVersion;
167461
167770
  state.cachedM0UpgradeState = markers.upgradeState;
167771
+ state.cachedM0SystemHash = markers.systemHash;
167772
+ state.cachedM0ToolSetHash = markers.toolSetHash;
167773
+ state.cachedM0ModelKey = markers.modelKey;
167462
167774
  state.snapshotMarkers = markers;
167463
167775
  }
167464
167776
  function replayCachedM1(state) {
@@ -167575,7 +167887,8 @@ function injectM0M1(options) {
167575
167887
  sessionId: options.sessionId,
167576
167888
  state: options.state,
167577
167889
  projectPath: options.projectPath,
167578
- projectDirectory: options.projectDirectory
167890
+ projectDirectory: options.projectDirectory,
167891
+ hardSignals: options.hardSignals
167579
167892
  });
167580
167893
  let rematerialized = false;
167581
167894
  let contentionExhausted = false;
@@ -167633,7 +167946,10 @@ function injectM0M1(options) {
167633
167946
  m1Text = replayCachedM1(options.state);
167634
167947
  }
167635
167948
  const M0_DRIFT_RATIO_FLOOR = 2000;
167636
- if (!rematerialized && !contentionExhausted && m1Recomputed && options.isCacheBustingPass && (memoryUpdateCount > 40 || m1Text !== M1_EMPTY_PLACEHOLDER && m0Text.length >= M0_DRIFT_RATIO_FLOOR && m1Text.length > m0Text.length * 0.15)) {
167949
+ const M1_ABSOLUTE_CAP_RATIO = 0.2;
167950
+ const m1AbsoluteBudget = (options.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS) * M1_ABSOLUTE_CAP_RATIO;
167951
+ const m1OverAbsoluteCap = m1Text !== M1_EMPTY_PLACEHOLDER && estimateTokens(m1Text) > m1AbsoluteBudget;
167952
+ if (!rematerialized && !contentionExhausted && m1Recomputed && options.isCacheBustingPass && (memoryUpdateCount > 40 || m1OverAbsoluteCap || m1Text !== M1_EMPTY_PLACEHOLDER && m0Text.length >= M0_DRIFT_RATIO_FLOOR && m1Text.length > m0Text.length * 0.15)) {
167637
167953
  try {
167638
167954
  const refolded = materializeWithRetry(options);
167639
167955
  applyMarkersToState(options.state, refolded.m0Bytes, refolded.snapshotMarkers, refolded.m1Bytes);
@@ -167657,7 +167973,7 @@ function injectM0M1(options) {
167657
167973
  m1Text
167658
167974
  };
167659
167975
  }
167660
- var INJECTION_CACHE_MAX = 100, injectionCache, CONSTRAINT_KEYWORDS, MaterializeContentionError, RenderM1InvalidMarkersError, DEFAULT_HISTORY_BUDGET_TOKENS = 60000, DEFAULT_MEMORY_BUDGET_TOKENS = 8000, MEMORY_BLOCK_WRAPPER_TOKENS = 6, DEFAULT_USER_PROFILE_BUDGET_TOKENS = 4000, M0_EMPTY_BODY = "<session-history></session-history>", M1_EMPTY_PLACEHOLDER = "<session-history-since>(no new content since last materialization)</session-history-since>", maxCompartmentSeqStatements, maxMemoryIdStatements, legacyCompartmentCountStatements, m0CompartmentStatements, newCompartmentStatements, EMPTY_MAX_COMPARTMENT_SEQ = -1;
167976
+ var INJECTION_CACHE_MAX = 100, injectionCache, CONSTRAINT_KEYWORDS, EMPTY_HARD_SIGNALS, MaterializeContentionError, RenderM1InvalidMarkersError, DEFAULT_HISTORY_BUDGET_TOKENS = 60000, DEFAULT_MEMORY_BUDGET_TOKENS = 8000, MEMORY_BLOCK_WRAPPER_TOKENS = 6, DEFAULT_USER_PROFILE_BUDGET_TOKENS = 4000, M0_EMPTY_BODY = "<session-history></session-history>", M1_EMPTY_PLACEHOLDER = "<session-history-since>(no new content since last materialization)</session-history-since>", maxCompartmentSeqStatements, maxMemoryIdStatements, legacyCompartmentCountStatements, m0CompartmentStatements, newCompartmentStatements;
167661
167977
  var init_inject_compartments = __esm(async () => {
167662
167978
  init_compartment_storage();
167663
167979
  init_constants();
@@ -167673,6 +167989,13 @@ var init_inject_compartments = __esm(async () => {
167673
167989
  ]);
167674
167990
  injectionCache = new BoundedSessionMap(INJECTION_CACHE_MAX);
167675
167991
  CONSTRAINT_KEYWORDS = /\b(must|never|always|cannot|should not|must not)\b/i;
167992
+ EMPTY_HARD_SIGNALS = {
167993
+ systemHash: "",
167994
+ toolSetHash: "",
167995
+ modelKey: "",
167996
+ cacheExpired: false,
167997
+ lastResponseTime: 0
167998
+ };
167676
167999
  MaterializeContentionError = class MaterializeContentionError extends Error {
167677
168000
  retries;
167678
168001
  reason;
@@ -170829,7 +171152,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
170829
171152
  const [providerID, ...rest] = historianModelOverride.split("/");
170830
171153
  const modelID = rest.join("/");
170831
171154
  if (providerID && modelID) {
170832
- const limit = getModelsDevContextLimit(providerID, modelID);
171155
+ const limit = getSdkContextLimit(providerID, modelID);
170833
171156
  if (typeof limit === "number" && limit > 0)
170834
171157
  return limit;
170835
171158
  }
@@ -170848,7 +171171,7 @@ function resolveHistorianContextLimit(historianModelOverride) {
170848
171171
  const modelID = rest.join("/");
170849
171172
  if (!providerID || !modelID)
170850
171173
  continue;
170851
- const limit = getModelsDevContextLimit(providerID, modelID);
171174
+ const limit = getSdkContextLimit(providerID, modelID);
170852
171175
  if (typeof limit !== "number" || limit <= 0)
170853
171176
  continue;
170854
171177
  if (minLimit === undefined || limit < minLimit)
@@ -171078,6 +171401,10 @@ async function runMemoryMigration(deps) {
171078
171401
  const cleanupChildSession = async (sid) => {
171079
171402
  if (!sid)
171080
171403
  return;
171404
+ if (shouldKeepSubagents()) {
171405
+ sessionLog(parentSessionId, `memory-migration: KEEPING child session ${sid} (keep_subagents)`);
171406
+ return;
171407
+ }
171081
171408
  await client.session.delete({ path: { id: sid } }).catch((e) => {
171082
171409
  sessionLog(parentSessionId, `memory-migration: child cleanup failed: ${String(e)}`);
171083
171410
  });
@@ -171215,6 +171542,7 @@ __export(exports_recomp_orchestrator, {
171215
171542
  setRecompNote: () => setRecompNote,
171216
171543
  runManagedUpgrade: () => runManagedUpgrade,
171217
171544
  runManagedRecomp: () => runManagedRecomp,
171545
+ isRecompSkip: () => isRecompSkip,
171218
171546
  isRecompFailure: () => isRecompFailure,
171219
171547
  isRecompComplete: () => isRecompComplete,
171220
171548
  extractRecompReason: () => extractRecompReason,
@@ -171227,6 +171555,9 @@ function resolveLiveModelKey(liveSessionState, sessionId) {
171227
171555
  function isRecompFailure(message) {
171228
171556
  return /—\s*(Failed|Skipped)/.test(message);
171229
171557
  }
171558
+ function isRecompSkip(message) {
171559
+ return /—\s*Skipped|already mutating compartment state|already running/i.test(message);
171560
+ }
171230
171561
  function isRecompComplete(message) {
171231
171562
  return /—\s*Complete/.test(message);
171232
171563
  }
@@ -171242,9 +171573,10 @@ function contextualizeUpgradeReason(reason) {
171242
171573
  }
171243
171574
  return rewritten;
171244
171575
  }
171245
- function setRecompStarting(liveSessionState, sessionId, note) {
171576
+ function setRecompStarting(liveSessionState, sessionId, note, kind = "recomp") {
171246
171577
  liveSessionState.recompProgressBySession.set(sessionId, {
171247
171578
  sessionId,
171579
+ kind,
171248
171580
  phase: "recomp",
171249
171581
  processedMessages: 0,
171250
171582
  totalMessages: 0,
@@ -171269,6 +171601,7 @@ function setRecompTerminal(liveSessionState, sessionId, phase, message) {
171269
171601
  const existing = liveSessionState.recompProgressBySession.get(sessionId);
171270
171602
  liveSessionState.recompProgressBySession.set(sessionId, {
171271
171603
  sessionId,
171604
+ kind: existing?.kind ?? "recomp",
171272
171605
  phase,
171273
171606
  processedMessages: existing?.processedMessages ?? 0,
171274
171607
  totalMessages: existing?.totalMessages ?? 0,
@@ -171278,10 +171611,10 @@ function setRecompTerminal(liveSessionState, sessionId, phase, message) {
171278
171611
  updatedAt: Date.now(),
171279
171612
  message
171280
171613
  });
171281
- if (phase === "done") {
171614
+ if (phase === "done" || phase === "skipped") {
171282
171615
  const t = setTimeout(() => {
171283
171616
  const cur = liveSessionState.recompProgressBySession.get(sessionId);
171284
- if (cur?.phase === "done")
171617
+ if (cur?.phase === phase)
171285
171618
  liveSessionState.recompProgressBySession.delete(sessionId);
171286
171619
  }, RECOMP_DONE_GRACE_MS);
171287
171620
  t.unref?.();
@@ -171310,7 +171643,11 @@ function buildRecompDeps(ctx, sessionId) {
171310
171643
  ctx.liveSessionState.deferredHistoryRefreshSessions.add(sid);
171311
171644
  },
171312
171645
  onRecompProgress: (p) => {
171313
- ctx.liveSessionState.recompProgressBySession.set(sessionId, p);
171646
+ const prevKind = ctx.liveSessionState.recompProgressBySession.get(sessionId)?.kind ?? "recomp";
171647
+ ctx.liveSessionState.recompProgressBySession.set(sessionId, {
171648
+ ...p,
171649
+ kind: p.kind ?? prevKind
171650
+ });
171314
171651
  }
171315
171652
  };
171316
171653
  }
@@ -171329,10 +171666,11 @@ async function resolveSessionDirectory(ctx, sessionId) {
171329
171666
  return ctx.directory;
171330
171667
  }
171331
171668
  async function runManagedRecomp(ctx, sessionId, options) {
171332
- setRecompStarting(ctx.liveSessionState, sessionId, "Starting recomp…");
171669
+ setRecompStarting(ctx.liveSessionState, sessionId, "Starting recomp…", "recomp");
171333
171670
  try {
171334
171671
  const message = await executeContextRecomp(buildRecompDeps(ctx, sessionId), options);
171335
- setRecompTerminal(ctx.liveSessionState, sessionId, isRecompFailure(message) ? "failed" : "done", extractRecompReason(message));
171672
+ const terminalPhase = isRecompSkip(message) ? "skipped" : isRecompFailure(message) ? "failed" : "done";
171673
+ setRecompTerminal(ctx.liveSessionState, sessionId, terminalPhase, extractRecompReason(message));
171336
171674
  return message;
171337
171675
  } catch (error51) {
171338
171676
  setRecompTerminal(ctx.liveSessionState, sessionId, "failed", `Recomp crashed: ${String(error51)}`);
@@ -171342,7 +171680,7 @@ Recomp crashed: ${String(error51)}`;
171342
171680
  }
171343
171681
  }
171344
171682
  async function runManagedUpgrade(ctx, sessionId) {
171345
- setRecompStarting(ctx.liveSessionState, sessionId, "Starting upgrade…");
171683
+ setRecompStarting(ctx.liveSessionState, sessionId, "Starting upgrade…", "upgrade");
171346
171684
  try {
171347
171685
  const compartments = getCompartments(ctx.db, sessionId);
171348
171686
  const legacyCount = compartments.filter((c) => c.legacy === 1 || !c.p1 || c.p1.trim() === "").length;
@@ -171400,6 +171738,7 @@ async function runUpgradeMemoryMigration(ctx, sessionId, migrationDirectory) {
171400
171738
  const prev = ctx.liveSessionState.recompProgressBySession.get(sessionId);
171401
171739
  ctx.liveSessionState.recompProgressBySession.set(sessionId, {
171402
171740
  sessionId,
171741
+ kind: prev?.kind ?? "upgrade",
171403
171742
  phase: "migration",
171404
171743
  processedMessages: prev?.processedMessages ?? 0,
171405
171744
  totalMessages: prev?.totalMessages ?? 0,
@@ -172546,7 +172885,7 @@ async function runSidekick(deps) {
172546
172885
  }
172547
172886
  return null;
172548
172887
  } finally {
172549
- if (agentSessionId) {
172888
+ if (agentSessionId && !shouldKeepSubagents()) {
172550
172889
  await deps.client.session.delete({
172551
172890
  path: { id: agentSessionId }
172552
172891
  }).catch((error51) => {
@@ -172796,7 +173135,8 @@ function createLiveSessionState() {
172796
173135
  pendingMaterializationSessions: new Set,
172797
173136
  deferredMaterializationSessions: new Set,
172798
173137
  sessionDirectoryBySession: new Map,
172799
- recompProgressBySession: new Map
173138
+ recompProgressBySession: new Map,
173139
+ internalChildSessions: new Set
172800
173140
  };
172801
173141
  }
172802
173142
 
@@ -173009,13 +173349,14 @@ init_shared();
173009
173349
  init_assistant_message_extractor();
173010
173350
  init_logger();
173011
173351
  import { readFileSync as readFileSync8 } from "node:fs";
173012
- import { isAbsolute as isAbsolute2, join as join11, relative as relative2 } from "node:path";
173352
+ import { isAbsolute as isAbsolute3, join as join11, relative as relative2 } from "node:path";
173353
+ init_subagent_token_capture();
173013
173354
  init_aft_availability();
173014
173355
  init_project_key_files();
173015
173356
 
173016
173357
  // src/features/magic-context/key-files/read-history.ts
173017
173358
  import { realpathSync as realpathSync3 } from "node:fs";
173018
- import { relative, resolve as resolve4 } from "node:path";
173359
+ import { relative, resolve as resolve5 } from "node:path";
173019
173360
  function toMs(value) {
173020
173361
  if (typeof value === "number" && Number.isFinite(value))
173021
173362
  return value;
@@ -173052,7 +173393,7 @@ function coalesceRanges(ranges) {
173052
173393
  }
173053
173394
  function normalizeProjectRelativePath(projectPath, filePath) {
173054
173395
  const root = realpathSync3(projectPath);
173055
- const abs = filePath.startsWith("/") ? resolve4(filePath) : resolve4(root, filePath);
173396
+ const abs = filePath.startsWith("/") ? resolve5(filePath) : resolve5(root, filePath);
173056
173397
  let real = abs;
173057
173398
  try {
173058
173399
  real = realpathSync3(abs);
@@ -173420,11 +173761,13 @@ async function runKeyFilesLlm(args) {
173420
173761
  const text = extractLatestAssistantText(messages);
173421
173762
  if (!text)
173422
173763
  throw new Error("Dreamer returned no key-files output.");
173423
- return text;
173764
+ return { text, messages };
173424
173765
  } finally {
173425
- await args.client.session.delete({ path: { id: agentSessionId } }).catch(() => {
173426
- return;
173427
- });
173766
+ if (!shouldKeepSubagents()) {
173767
+ await args.client.session.delete({ path: { id: agentSessionId } }).catch(() => {
173768
+ return;
173769
+ });
173770
+ }
173428
173771
  }
173429
173772
  }
173430
173773
  async function runKeyFilesTask(args) {
@@ -173470,9 +173813,27 @@ async function runKeyFilesTask(args) {
173470
173813
  log(`[key-files] lease renewal threw: ${getErrorMessage(error51)}`);
173471
173814
  }
173472
173815
  }, 60000);
173816
+ let invocationRecorded = false;
173817
+ const llmStartedAt = Date.now();
173818
+ const recordKeyFilesInvocation = (params) => {
173819
+ if (!args.parentSessionId || invocationRecorded)
173820
+ return;
173821
+ invocationRecorded = true;
173822
+ recordChildInvocation({
173823
+ db: args.db,
173824
+ parentSessionId: args.parentSessionId,
173825
+ harness: getHarness(),
173826
+ subagent: "dreamer",
173827
+ task: "key files",
173828
+ startedAt: llmStartedAt,
173829
+ status: params.status,
173830
+ messages: params.messages,
173831
+ error: params.error
173832
+ });
173833
+ };
173473
173834
  try {
173474
173835
  try {
173475
- const raw = await runKeyFilesLlm({
173836
+ const { text: raw, messages: llmMessages } = await runKeyFilesLlm({
173476
173837
  client: args.client,
173477
173838
  parentSessionId: args.parentSessionId,
173478
173839
  projectPath,
@@ -173480,8 +173841,10 @@ async function runKeyFilesTask(args) {
173480
173841
  deadline: args.deadline,
173481
173842
  fallbackModels: args.fallbackModels
173482
173843
  });
173844
+ recordKeyFilesInvocation({ status: "completed", messages: llmMessages });
173483
173845
  validated = validateLlmOutput(raw, args.config, projectPath, new Set(candidates.map((candidate) => candidate.path)));
173484
173846
  } catch (error51) {
173847
+ recordKeyFilesInvocation({ status: "failed", error: error51 });
173485
173848
  log(`[key-files] LLM validation failed: ${getErrorMessage(error51)}`);
173486
173849
  throw error51;
173487
173850
  }
@@ -173580,7 +173943,8 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
173580
173943
  db: args.db,
173581
173944
  parentSessionId: args.parentSessionId,
173582
173945
  harness: "opencode",
173583
- subagent: "user_memory_review",
173946
+ subagent: "dreamer",
173947
+ task: "user memories",
173584
173948
  startedAt,
173585
173949
  status: params.status,
173586
173950
  messages: params.messages,
@@ -173704,7 +174068,7 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
173704
174068
  return result;
173705
174069
  } finally {
173706
174070
  clearInterval(leaseInterval);
173707
- if (agentSessionId) {
174071
+ if (agentSessionId && !shouldKeepSubagents()) {
173708
174072
  await args.client.session.delete({
173709
174073
  path: { id: agentSessionId },
173710
174074
  query: { directory: args.sessionDirectory }
@@ -174027,14 +174391,14 @@ async function runDream(args) {
174027
174391
  }
174028
174392
  } finally {
174029
174393
  clearInterval(leaseRenewalInterval);
174030
- if (agentSessionId && !taskFailed) {
174394
+ if (agentSessionId && !taskFailed && !shouldKeepSubagents()) {
174031
174395
  await args.client.session.delete({
174032
174396
  path: { id: agentSessionId }
174033
174397
  }).catch((error51) => {
174034
174398
  log("[dreamer] failed to delete child session:", error51);
174035
174399
  });
174036
- } else if (agentSessionId && taskFailed) {
174037
- log(`[dreamer] KEEPING failed child session ${agentSessionId} for task ${taskName} (debugging)`);
174400
+ } else if (agentSessionId && (taskFailed || shouldKeepSubagents())) {
174401
+ log(`[dreamer] KEEPING child session ${agentSessionId} for task ${taskName} (${taskFailed ? "failed" : "keep_subagents"})`);
174038
174402
  }
174039
174403
  }
174040
174404
  if (lostLease) {
@@ -174319,7 +174683,7 @@ Only include notes whose conditions you could definitively evaluate against exte
174319
174683
  parts: [{ type: "text", text: evaluationPrompt, synthetic: true }]
174320
174684
  }
174321
174685
  }, {
174322
- timeoutMs: Math.min(remainingMs, 300000),
174686
+ timeoutMs: Math.min(remainingMs, 5 * 60 * 1000),
174323
174687
  signal: abortController.signal,
174324
174688
  fallbackModels: args.fallbackModels,
174325
174689
  callContext: "dreamer:smart-notes"
@@ -174395,7 +174759,7 @@ Only include notes whose conditions you could definitively evaluate against exte
174395
174759
  });
174396
174760
  } finally {
174397
174761
  clearInterval(leaseInterval);
174398
- if (agentSessionId) {
174762
+ if (agentSessionId && !shouldKeepSubagents()) {
174399
174763
  await args.client.session.delete({
174400
174764
  path: { id: agentSessionId }
174401
174765
  }).catch(() => {});
@@ -174406,14 +174770,14 @@ var MAX_LEASE_RETRIES = 3;
174406
174770
  async function processDreamQueue(args) {
174407
174771
  const maxRuntimeMs = args.maxRuntimeMinutes * 60 * 1000;
174408
174772
  if (!hasActiveDreamLease(args.db)) {
174409
- clearStaleEntries(args.db, maxRuntimeMs + 1800000, args.projectIdentity);
174773
+ clearStaleEntries(args.db, maxRuntimeMs + 30 * 60 * 1000, args.projectIdentity);
174410
174774
  }
174411
174775
  const entry = dequeueNext(args.db, args.projectIdentity);
174412
174776
  if (!entry) {
174413
174777
  return null;
174414
174778
  }
174415
174779
  const projectDirectory = args.sessionDirectoryOverride ?? resolveDreamSessionDirectory(entry.projectIdentity);
174416
- log(`[dreamer] dequeued project ${entry.projectIdentity} (dir=${projectDirectory}), starting dream run`);
174780
+ log(`[dreamer] dequeued project ${entry.projectIdentity}, starting dream run`);
174417
174781
  let result;
174418
174782
  try {
174419
174783
  result = await runDream({
@@ -174537,6 +174901,7 @@ async function readGitCommits(directory, options = {}) {
174537
174901
  if (revision.startsWith("-")) {
174538
174902
  throw new Error(`readGitCommits: refusing revision that looks like an option: "${revision}"`);
174539
174903
  }
174904
+ const projectLabel = options.projectIdentity ?? "<project>";
174540
174905
  const args = [
174541
174906
  "log",
174542
174907
  revision,
@@ -174559,11 +174924,11 @@ async function readGitCommits(directory, options = {}) {
174559
174924
  stdout = result.stdout;
174560
174925
  } catch (error51) {
174561
174926
  const message = error51 instanceof Error ? error51.message : String(error51);
174562
- log(`[git-commits] readGitCommits failed at cwd=${directory}: ${message.slice(0, 500)}`);
174927
+ log(`[git-commits] readGitCommits failed for ${projectLabel}: ${message.slice(0, 500)}`);
174563
174928
  return [];
174564
174929
  }
174565
174930
  if (stdout.trim().length === 0) {
174566
- log(`[git-commits] readGitCommits returned empty stdout at cwd=${directory} (sinceMs=${options.sinceMs ?? "none"} args=${args.slice(0, 4).join(" ")})`);
174931
+ log(`[git-commits] readGitCommits returned empty stdout for ${projectLabel} (sinceMs=${options.sinceMs ?? "none"} args=${args.slice(0, 4).join(" ")})`);
174567
174932
  }
174568
174933
  return parseGitLogOutput(stdout);
174569
174934
  }
@@ -174618,6 +174983,7 @@ var insertStatements = new WeakMap;
174618
174983
  var existingShasStatements = new WeakMap;
174619
174984
  var projectCountStatements = new WeakMap;
174620
174985
  var evictStatements = new WeakMap;
174986
+ var evictOverflowStatements = new WeakMap;
174621
174987
  var latestCommitTimeStatements = new WeakMap;
174622
174988
  function getInsertStatement(db) {
174623
174989
  let stmt = insertStatements.get(db);
@@ -174660,17 +175026,17 @@ function getLatestCommitTimeStatement(db) {
174660
175026
  }
174661
175027
  return stmt;
174662
175028
  }
174663
- function getEvictStatement(db) {
174664
- let stmt = evictStatements.get(db);
175029
+ function getEvictOverflowStatement(db) {
175030
+ let stmt = evictOverflowStatements.get(db);
174665
175031
  if (!stmt) {
174666
175032
  stmt = db.prepare(`DELETE FROM git_commits
174667
- WHERE sha IN (
174668
- SELECT sha FROM git_commits
175033
+ WHERE rowid IN (
175034
+ SELECT rowid FROM git_commits
174669
175035
  WHERE project_path = ?
174670
- ORDER BY committed_at ASC
174671
- LIMIT ?
175036
+ ORDER BY committed_at DESC, sha DESC
175037
+ LIMIT -1 OFFSET ?
174672
175038
  )`);
174673
- evictStatements.set(db, stmt);
175039
+ evictOverflowStatements.set(db, stmt);
174674
175040
  }
174675
175041
  return stmt;
174676
175042
  }
@@ -174708,22 +175074,15 @@ function getLatestIndexedCommitTimeMs(db, projectPath) {
174708
175074
  const row = getLatestCommitTimeStatement(db).get(projectPath);
174709
175075
  return row?.latest ?? null;
174710
175076
  }
174711
- function evictOldestCommits(db, projectPath, excess) {
174712
- if (excess <= 0)
174713
- return 0;
174714
- const before = getCommitCount(db, projectPath);
174715
- getEvictStatement(db).run(projectPath, excess);
174716
- const after = getCommitCount(db, projectPath);
174717
- return Math.max(0, before - after);
174718
- }
174719
175077
  function enforceProjectCap(db, projectPath, maxCommits) {
174720
175078
  if (maxCommits <= 0)
174721
175079
  return 0;
174722
175080
  const count = getCommitCount(db, projectPath);
174723
175081
  if (count <= maxCommits)
174724
175082
  return 0;
174725
- const excess = count - maxCommits;
174726
- const evicted = evictOldestCommits(db, projectPath, excess);
175083
+ getEvictOverflowStatement(db).run(projectPath, maxCommits);
175084
+ const after = getCommitCount(db, projectPath);
175085
+ const evicted = Math.max(0, count - after);
174727
175086
  if (evicted > 0) {
174728
175087
  log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
174729
175088
  }
@@ -174755,7 +175114,8 @@ async function indexCommitsForProject(db, projectPath, directory, options) {
174755
175114
  const sinceMs = latestIndexed !== null ? Math.max(latestIndexed - 60000, Date.now() - options.sinceDays * MS_PER_DAY) : Date.now() - options.sinceDays * MS_PER_DAY;
174756
175115
  const commits = await readGitCommits(directory, {
174757
175116
  sinceMs,
174758
- maxCommits: options.maxCommits
175117
+ maxCommits: options.maxCommits,
175118
+ projectIdentity: projectPath
174759
175119
  });
174760
175120
  result.scanned = commits.length;
174761
175121
  if (commits.length === 0) {
@@ -174967,6 +175327,7 @@ function searchGitCommitsSync(db, projectPath, query, options) {
174967
175327
 
174968
175328
  // src/features/magic-context/git-commits/index.ts
174969
175329
  init_storage_git_commit_embeddings();
175330
+ init_sweep_coordinator();
174970
175331
 
174971
175332
  // src/plugin/dream-timer.ts
174972
175333
  init_embedding();
@@ -174999,7 +175360,7 @@ async function startDreamScheduleTimer(args) {
174999
175360
  const isNewRegistration = !registeredProjects.has(args.directory);
175000
175361
  registeredProjects.set(args.directory, args);
175001
175362
  if (isNewRegistration) {
175002
- log(`[dreamer] registered project ${args.directory} (dreaming=${dreamingEnabled} embeddings=${embeddingSweepEnabled} commits=${commitIndexingEnabled}; total=${registeredProjects.size})`);
175363
+ log(`[dreamer] registered project ${args.projectIdentity} (dreaming=${dreamingEnabled} embeddings=${embeddingSweepEnabled} commits=${commitIndexingEnabled}; total=${registeredProjects.size})`);
175003
175364
  }
175004
175365
  if (!activeTimer) {
175005
175366
  log(`[dreamer] started independent schedule timer (every ${DREAM_TIMER_INTERVAL_MS / 60000}m)`);
@@ -175014,7 +175375,7 @@ async function startDreamScheduleTimer(args) {
175014
175375
  }
175015
175376
  return () => {
175016
175377
  registeredProjects.delete(args.directory);
175017
- log(`[dreamer] unregistered project ${args.directory} (remaining=${registeredProjects.size})`);
175378
+ log(`[dreamer] unregistered project ${args.projectIdentity} (remaining=${registeredProjects.size})`);
175018
175379
  if (registeredProjects.size === 0 && activeTimer) {
175019
175380
  clearInterval(activeTimer);
175020
175381
  activeTimer = null;
@@ -175062,7 +175423,7 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
175062
175423
  return;
175063
175424
  }
175064
175425
  try {
175065
- log(`[dreamer] timer tick (${origin}) ${reg.directory} — checking schedule window "${reg.dreamerConfig.schedule}"`);
175426
+ log(`[dreamer] timer tick (${origin}) ${reg.projectIdentity} — checking schedule window "${reg.dreamerConfig.schedule}"`);
175066
175427
  checkScheduleAndEnqueue(db, reg.dreamerConfig.schedule, reg.projectIdentity);
175067
175428
  await processDreamQueue({
175068
175429
  db,
@@ -175077,13 +175438,34 @@ async function sweepProject(reg, origin, db, gitCommitEnabled = getProjectEmbedd
175077
175438
  fallbackModels: resolveFallbackChain(DREAMER_AGENT, reg.dreamerConfig.fallback_models)
175078
175439
  });
175079
175440
  } catch (error51) {
175080
- log(`[dreamer] timer-triggered queue processing failed for ${reg.directory}:`, error51);
175441
+ log(`[dreamer] timer-triggered queue processing failed for ${reg.projectIdentity}:`, error51);
175081
175442
  }
175082
175443
  }
175444
+ function startGitSweepLeaseRenewal(db, projectIdentity, holderId) {
175445
+ const timer = setInterval(() => {
175446
+ try {
175447
+ if (!renewGitSweepLease(db, projectIdentity, holderId)) {
175448
+ log(`[git-commits] sweep lease renewal failed for ${projectIdentity}`);
175449
+ }
175450
+ } catch (error51) {
175451
+ log(`[git-commits] sweep lease renewal errored for ${projectIdentity}: ${error51 instanceof Error ? error51.message : String(error51)}`);
175452
+ }
175453
+ }, GIT_SWEEP_LEASE_RENEWAL_MS);
175454
+ timer.unref?.();
175455
+ return () => clearInterval(timer);
175456
+ }
175083
175457
  async function sweepGitCommits(args) {
175084
175458
  const { directory, projectIdentity, db, gitCommitIndexing } = args;
175459
+ const holderId = crypto.randomUUID();
175460
+ const lease2 = acquireGitSweepLease(db, projectIdentity, holderId);
175461
+ if (!lease2.acquired) {
175462
+ const reason = lease2.reason === "cooldown_active" ? `cooldown active until ${lease2.nextAllowedAt}` : `lease held by ${lease2.leaseHolder ?? "another holder"} until ${lease2.leaseExpiresAt ?? "unknown"}`;
175463
+ log(`[git-commits] sweep skipped for ${projectIdentity}: ${reason}`);
175464
+ return;
175465
+ }
175085
175466
  const startedAt = Date.now();
175086
- log(`[git-commits] sweep starting for ${directory} (sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`);
175467
+ const stopRenewal = startGitSweepLeaseRenewal(db, projectIdentity, holderId);
175468
+ log(`[git-commits] sweep starting for ${projectIdentity} (sinceDays=${gitCommitIndexing.since_days} maxCommits=${gitCommitIndexing.max_commits})`);
175087
175469
  try {
175088
175470
  const result = await indexCommitsForProject(db, projectIdentity, directory, {
175089
175471
  sinceDays: gitCommitIndexing.since_days,
@@ -175093,11 +175475,19 @@ async function sweepGitCommits(args) {
175093
175475
  if (result.embedded > 0) {
175094
175476
  drainedEmbeddings = await embedUnembeddedCommits(db, projectIdentity);
175095
175477
  }
175478
+ const cooldownMarked = markGitSweepSuccessAndRelease(db, projectIdentity, holderId);
175479
+ if (!cooldownMarked) {
175480
+ releaseGitSweepLease(db, projectIdentity, holderId);
175481
+ log(`[git-commits] sweep finished for ${projectIdentity}, but lease was no longer active; cooldown not advanced`);
175482
+ }
175096
175483
  const elapsedMs = Date.now() - startedAt;
175097
175484
  log(`[git-commits] sweep finished for ${projectIdentity} in ${elapsedMs}ms: scanned=${result.scanned} inserted=${result.inserted} updated=${result.updated} evicted=${result.evicted} embedded=${result.embedded} drained=${drainedEmbeddings}`);
175098
175485
  } catch (error51) {
175486
+ releaseGitSweepLease(db, projectIdentity, holderId);
175099
175487
  const elapsedMs = Date.now() - startedAt;
175100
- log(`[git-commits] sweep failed for ${directory} after ${elapsedMs}ms: ${error51 instanceof Error ? error51.message : String(error51)}`);
175488
+ log(`[git-commits] sweep failed for ${projectIdentity} after ${elapsedMs}ms: ${error51 instanceof Error ? error51.message : String(error51)}`);
175489
+ } finally {
175490
+ stopRenewal();
175101
175491
  }
175102
175492
  }
175103
175493
 
@@ -175237,7 +175627,7 @@ init_models_dev_cache();
175237
175627
  var DEFAULT_CONTEXT_LIMIT = 128000;
175238
175628
  var MAX_EXECUTE_THRESHOLD = 80;
175239
175629
  function resolveContextLimit(providerID, modelID, ctx) {
175240
- const fromModelsDev = providerID && modelID ? getModelsDevContextLimit(providerID, modelID) : undefined;
175630
+ const fromModelsDev = providerID && modelID ? getSdkContextLimit(providerID, modelID) : undefined;
175241
175631
  const baseline = fromModelsDev ?? DEFAULT_CONTEXT_LIMIT;
175242
175632
  if (ctx?.db && ctx.sessionID) {
175243
175633
  try {
@@ -175250,7 +175640,7 @@ function resolveContextLimit(providerID, modelID, ctx) {
175250
175640
  return baseline;
175251
175641
  }
175252
175642
  function resolveTrustedContextLimit(providerID, modelID, ctx) {
175253
- const fromModelsDev = providerID && modelID ? getModelsDevContextLimit(providerID, modelID) : undefined;
175643
+ const fromModelsDev = providerID && modelID ? getSdkContextLimit(providerID, modelID) : undefined;
175254
175644
  let detected;
175255
175645
  if (ctx?.db && ctx.sessionID) {
175256
175646
  try {
@@ -175738,6 +176128,7 @@ function createTagger() {
175738
176128
  // src/hooks/magic-context/hook.ts
175739
176129
  init_magic_context();
175740
176130
  init_project_identity();
176131
+ init_tool_definition_tokens();
175741
176132
  await init_storage();
175742
176133
  init_logger();
175743
176134
  init_resolve_fallbacks();
@@ -176608,7 +176999,8 @@ function getSessionCreatedInfo(properties) {
176608
176999
  id: info.id,
176609
177000
  parentID: info.parentID,
176610
177001
  providerID: typeof info.providerID === "string" ? info.providerID : undefined,
176611
- modelID: typeof info.modelID === "string" ? info.modelID : undefined
177002
+ modelID: typeof info.modelID === "string" ? info.modelID : undefined,
177003
+ title: typeof info.title === "string" ? info.title : undefined
176612
177004
  };
176613
177005
  }
176614
177006
  function getMessageUpdatedAssistantInfo(properties) {
@@ -177718,7 +178110,7 @@ async function runCompartmentPhase(args) {
177718
178110
  async function awaitCompartmentRun(activeRun, reason) {
177719
178111
  sessionLog(args.sessionId, reason);
177720
178112
  const timeoutMs = args.historianTimeoutMs ?? 120000;
177721
- const timeout = new Promise((resolve5) => setTimeout(() => resolve5("timeout"), timeoutMs));
178113
+ const timeout = new Promise((resolve6) => setTimeout(() => resolve6("timeout"), timeoutMs));
177722
178114
  const result = await Promise.race([activeRun.promise.then(() => "done"), timeout]);
177723
178115
  if (result === "timeout") {
177724
178116
  sessionLog(args.sessionId, `transform: compartment await timed out after ${timeoutMs}ms — proceeding without waiting`);
@@ -178675,6 +179067,64 @@ init_embedding();
178675
179067
 
178676
179068
  // src/features/magic-context/search.ts
178677
179069
  init_logger();
179070
+
179071
+ // src/features/magic-context/literal-probes.ts
179072
+ var MAX_PROBES = 5;
179073
+ var MIN_PROBE_LENGTH = 3;
179074
+ var SLASH_COMMAND_RE = /\/[a-z][a-z0-9]*(?:-[a-z0-9]+)+/gi;
179075
+ var KEBAB_SNAKE_RE = /[a-z][a-z0-9]*(?:[-_][a-z0-9]+)+/gi;
179076
+ var DOTTED_RE = /[a-z0-9][a-z0-9_-]*(?:\.[a-z0-9_-]+)+/gi;
179077
+ var CAMEL_RE = /\b[a-zA-Z][a-z0-9]*(?:[A-Z][a-z0-9]*)+\b/g;
179078
+ var SHA_RE = /\b[0-9a-f]{7,40}\b/gi;
179079
+ var ERROR_CODE_RE = /\b(?:TS\d{4,}|ERR_[A-Z][A-Z0-9_]*)\b/g;
179080
+ var QUOTED_RE = /["`]([^"`]{3,80})["`]/g;
179081
+ function looksLikeSha(token) {
179082
+ return /[0-9]/.test(token) && /^[0-9a-f]{7,40}$/i.test(token);
179083
+ }
179084
+ function extractLiteralProbes(query) {
179085
+ const trimmed = query.trim();
179086
+ if (trimmed.length === 0)
179087
+ return [];
179088
+ const ordered = [];
179089
+ const seen = new Set;
179090
+ const add = (raw) => {
179091
+ if (!raw)
179092
+ return;
179093
+ const probe = raw.trim();
179094
+ if (probe.length < MIN_PROBE_LENGTH)
179095
+ return;
179096
+ const key = probe.toLowerCase();
179097
+ if (seen.has(key))
179098
+ return;
179099
+ seen.add(key);
179100
+ ordered.push(probe);
179101
+ };
179102
+ for (const m of trimmed.matchAll(QUOTED_RE))
179103
+ add(m[1]);
179104
+ for (const m of trimmed.matchAll(SLASH_COMMAND_RE))
179105
+ add(m[0]);
179106
+ for (const m of trimmed.matchAll(ERROR_CODE_RE))
179107
+ add(m[0]);
179108
+ for (const m of trimmed.matchAll(DOTTED_RE))
179109
+ add(m[0]);
179110
+ for (const m of trimmed.matchAll(KEBAB_SNAKE_RE))
179111
+ add(m[0]);
179112
+ for (const m of trimmed.matchAll(CAMEL_RE))
179113
+ add(m[0]);
179114
+ for (const m of trimmed.matchAll(SHA_RE)) {
179115
+ if (looksLikeSha(m[0]))
179116
+ add(m[0]);
179117
+ }
179118
+ return ordered.slice(0, MAX_PROBES);
179119
+ }
179120
+ function containsProbeVerbatim(text, probes) {
179121
+ if (probes.length === 0)
179122
+ return false;
179123
+ const haystack = text.toLowerCase();
179124
+ return probes.some((probe) => haystack.includes(probe.toLowerCase()));
179125
+ }
179126
+
179127
+ // src/features/magic-context/search.ts
178678
179128
  init_memory();
178679
179129
  init_embedding();
178680
179130
  init_storage_memory_fts();
@@ -178854,36 +179304,82 @@ function linearDecayScore(rank, total) {
178854
179304
  return 0;
178855
179305
  return Math.max(0, 1 - rank / total);
178856
179306
  }
178857
- function searchMessages(args) {
178858
- const sanitizedQuery = sanitizeFtsQuery(args.query.trim());
178859
- if (sanitizedQuery.length === 0) {
179307
+ function runMessageFtsQuery(db, sessionId, ftsQuery, fetchLimit, cutoff) {
179308
+ if (ftsQuery.length === 0)
178860
179309
  return [];
178861
- }
178862
- const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
178863
- const rows = getMessageSearchStatement(args.db).all(args.sessionId, sanitizedQuery, fetchLimit).map((row) => row);
178864
- const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
178865
- const filtered = rows.map((row) => {
179310
+ const rows = getMessageSearchStatement(db).all(sessionId, ftsQuery, fetchLimit).map((row) => row);
179311
+ const result = [];
179312
+ for (const row of rows) {
178866
179313
  const messageOrdinal = getMessageOrdinal(row.messageOrdinal);
178867
179314
  if (messageOrdinal === null || typeof row.messageId !== "string" || typeof row.role !== "string" || typeof row.content !== "string") {
178868
- return null;
179315
+ continue;
178869
179316
  }
178870
179317
  if (cutoff !== null && messageOrdinal > cutoff) {
178871
- return null;
179318
+ continue;
178872
179319
  }
178873
- return {
179320
+ result.push({
178874
179321
  messageOrdinal,
178875
179322
  messageId: row.messageId,
178876
179323
  role: row.role,
178877
179324
  content: row.content
178878
- };
178879
- }).filter((result) => result !== null).slice(0, args.limit);
178880
- return filtered.map((row, rank) => ({
179325
+ });
179326
+ }
179327
+ return result;
179328
+ }
179329
+ var RRF_K = 60;
179330
+ var VERBATIM_PROBE_BONUS = 0.5;
179331
+ function searchMessages(args) {
179332
+ const cutoff = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.maxOrdinal : null;
179333
+ const fetchLimit = args.maxOrdinal != null && args.maxOrdinal >= 0 ? args.limit * 3 : args.limit;
179334
+ const baseQuery = sanitizeFtsQuery(args.query.trim());
179335
+ const probes = args.probes ?? [];
179336
+ if (probes.length === 0) {
179337
+ const filtered = runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff).slice(0, args.limit);
179338
+ return filtered.map((row, rank) => ({
179339
+ source: "message",
179340
+ content: previewText(row.content),
179341
+ score: linearDecayScore(rank, filtered.length),
179342
+ messageOrdinal: row.messageOrdinal,
179343
+ messageId: row.messageId,
179344
+ role: row.role
179345
+ }));
179346
+ }
179347
+ const queryLists = [];
179348
+ if (baseQuery.length > 0) {
179349
+ queryLists.push(runMessageFtsQuery(args.db, args.sessionId, baseQuery, fetchLimit, cutoff));
179350
+ }
179351
+ for (const probe of probes) {
179352
+ const probeQuery = sanitizeFtsQuery(probe);
179353
+ if (probeQuery.length === 0)
179354
+ continue;
179355
+ queryLists.push(runMessageFtsQuery(args.db, args.sessionId, probeQuery, fetchLimit, cutoff));
179356
+ }
179357
+ const fused = new Map;
179358
+ for (const list of queryLists) {
179359
+ list.forEach((row, rank) => {
179360
+ const rrf = 1 / (RRF_K + rank);
179361
+ const existing = fused.get(row.messageId);
179362
+ if (existing) {
179363
+ existing.score += rrf;
179364
+ } else {
179365
+ fused.set(row.messageId, { row, score: rrf });
179366
+ }
179367
+ });
179368
+ }
179369
+ for (const entry of fused.values()) {
179370
+ if (containsProbeVerbatim(entry.row.content, probes)) {
179371
+ entry.score += VERBATIM_PROBE_BONUS;
179372
+ }
179373
+ }
179374
+ const ranked = [...fused.values()].sort((a, b) => b.score !== a.score ? b.score - a.score : a.row.messageOrdinal - b.row.messageOrdinal).slice(0, args.limit);
179375
+ const maxScore = ranked.length > 0 ? ranked[0].score : 1;
179376
+ return ranked.map((entry) => ({
178881
179377
  source: "message",
178882
- content: previewText(row.content),
178883
- score: linearDecayScore(rank, filtered.length),
178884
- messageOrdinal: row.messageOrdinal,
178885
- messageId: row.messageId,
178886
- role: row.role
179378
+ content: previewText(entry.row.content),
179379
+ score: maxScore > 0 ? entry.score / maxScore : 0,
179380
+ messageOrdinal: entry.row.messageOrdinal,
179381
+ messageId: entry.row.messageId,
179382
+ role: entry.row.role
178887
179383
  }));
178888
179384
  }
178889
179385
  function getSourceBoost(result) {
@@ -178967,12 +179463,14 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
178967
179463
  return null;
178968
179464
  }) : Promise.resolve(null);
178969
179465
  await Promise.resolve();
179466
+ const messageProbes = options.explicitSearch ? extractLiteralProbes(trimmedQuery) : [];
178970
179467
  const messageResults = runMessages ? searchMessages({
178971
179468
  db,
178972
179469
  sessionId,
178973
179470
  query: trimmedQuery,
178974
179471
  limit: tierLimit,
178975
- maxOrdinal: options.maxMessageOrdinal
179472
+ maxOrdinal: options.maxMessageOrdinal,
179473
+ probes: messageProbes
178976
179474
  }) : [];
178977
179475
  const queryEmbedding = await queryEmbeddingPromise;
178978
179476
  const [memoryResults, gitCommitResults] = await Promise.all([
@@ -179095,10 +179593,10 @@ var AUTO_SEARCH_TIMEOUT_MS = 3000;
179095
179593
  async function unifiedSearchWithTimeout(db, sessionId, projectPath, prompt, options, timeoutMs) {
179096
179594
  const controller = new AbortController;
179097
179595
  let timer;
179098
- const timeoutPromise = new Promise((resolve5) => {
179596
+ const timeoutPromise = new Promise((resolve6) => {
179099
179597
  timer = setTimeout(() => {
179100
179598
  controller.abort();
179101
- resolve5(null);
179599
+ resolve6(null);
179102
179600
  }, timeoutMs);
179103
179601
  });
179104
179602
  try {
@@ -179555,7 +180053,7 @@ function isVisibleNoteReadPart(part) {
179555
180053
  }
179556
180054
 
179557
180055
  // src/hooks/magic-context/todo-view.ts
179558
- import { createHash as createHash10 } from "node:crypto";
180056
+ import { createHash as createHash9 } from "node:crypto";
179559
180057
  var TERMINAL_STATUSES = new Set(["completed", "cancelled"]);
179560
180058
  var TITLE_DONE_STATUSES = new Set(["completed"]);
179561
180059
  var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
@@ -179600,7 +180098,7 @@ function buildSyntheticTodoPart(stateJson) {
179600
180098
  };
179601
180099
  }
179602
180100
  function computeSyntheticCallId(stateJson) {
179603
- const hash2 = createHash10("sha256").update(stateJson).digest("hex").slice(0, 16);
180101
+ const hash2 = createHash9("sha256").update(stateJson).digest("hex").slice(0, 16);
179604
180102
  return `${SYNTHETIC_CALL_ID_PREFIX}${hash2}`;
179605
180103
  }
179606
180104
  function parseTodoState(stateJson) {
@@ -179792,7 +180290,8 @@ async function runPostTransformPhase(args) {
179792
180290
  memoryInjectionBudgetTokens: args.m0M1.memoryInjectionBudgetTokens,
179793
180291
  historyBudgetTokens: args.m0M1.historyBudgetTokens,
179794
180292
  keyFiles: args.m0M1.keyFiles,
179795
- isCacheBustingPass
180293
+ isCacheBustingPass,
180294
+ hardSignals: args.m0M1.hardSignals
179796
180295
  });
179797
180296
  if (result.injected) {
179798
180297
  sessionLog(args.sessionId, `transform: injected m[0]/m[1] (rematerialized=${result.m0RematerializedThisPass}, reason=${result.decision.reason ?? "cache_hit"})`);
@@ -180166,6 +180665,10 @@ function createTransform(deps) {
180166
180665
  return;
180167
180666
  }
180168
180667
  logTransformTiming(sessionId, "getOrCreateSessionMeta", tMeta);
180668
+ if (deps.internalChildSessions?.has(sessionId)) {
180669
+ sessionLog(sessionId, "transform skipped (internal magic-context child session)");
180670
+ return;
180671
+ }
180169
180672
  const reducedMode = sessionMeta.isSubagent;
180170
180673
  const fullFeatureMode = !reducedMode;
180171
180674
  let sessionDirectory = deps.directory ?? "";
@@ -180536,6 +181039,22 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
180536
181039
  const compartmentInProgress = compartmentPhase.compartmentInProgress;
180537
181040
  sessionMeta = { ...sessionMeta, compartmentInProgress };
180538
181041
  logTransformTiming(sessionId, "compartmentPhase", tCompartmentPhase);
181042
+ const hardModel = deps.liveModelBySession?.get(sessionId);
181043
+ const hardModelKey = hardModel ? `${hardModel.providerID}/${hardModel.modelID}` : "";
181044
+ const hardToolSetHash = deps.getToolSetHash?.(sessionId) ?? "";
181045
+ const hardSystemHash = typeof sessionMeta.systemPromptHash === "string" ? sessionMeta.systemPromptHash : "";
181046
+ let hardTtlMs = 5 * 60 * 1000;
181047
+ try {
181048
+ hardTtlMs = parseCacheTtl(sessionMeta.cacheTtl);
181049
+ } catch {}
181050
+ const hardCacheExpired = sessionMeta.lastResponseTime > 0 && Date.now() - sessionMeta.lastResponseTime >= hardTtlMs;
181051
+ const m0HardSignals = {
181052
+ systemHash: hardSystemHash,
181053
+ toolSetHash: hardToolSetHash,
181054
+ modelKey: hardModelKey,
181055
+ cacheExpired: hardCacheExpired,
181056
+ lastResponseTime: sessionMeta.lastResponseTime
181057
+ };
180539
181058
  const lateActiveRunBlocksMaterialization = getActiveCompartmentRun(sessionId) !== undefined && contextUsageEarly.percentage < FORCE_MATERIALIZE_PERCENTAGE;
180540
181059
  const canConsumeDeferredLate = canConsumeDeferredOnThisPass({
180541
181060
  schedulerDecision: midTurnAdjustedSchedulerDecision,
@@ -180599,7 +181118,8 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
180599
181118
  keyFiles: {
180600
181119
  enabled: deps.experimentalPinKeyFiles === true,
180601
181120
  tokenBudget: deps.experimentalPinKeyFilesTokenBudget ?? 1e4
180602
- }
181121
+ },
181122
+ hardSignals: m0HardSignals
180603
181123
  }
180604
181124
  });
180605
181125
  logTransformTiming(sessionId, "postTransformPhase", tPostProcess);
@@ -180761,6 +181281,7 @@ function truncateHistorianEmergencyError(error51) {
180761
181281
 
180762
181282
  // src/hooks/magic-context/event-handler.ts
180763
181283
  var CONTEXT_USAGE_TTL_MS = 60 * 60 * 1000;
181284
+ var INTERNAL_CHILD_TITLE_PREFIX = "magic-context-";
180764
181285
  function formatTokens(value) {
180765
181286
  return value.toLocaleString();
180766
181287
  }
@@ -180825,6 +181346,10 @@ function createEventHandler2(deps) {
180825
181346
  if (!info) {
180826
181347
  return;
180827
181348
  }
181349
+ if (deps.internalChildSessions && info.parentID.length > 0 && typeof info.title === "string" && info.title.startsWith(INTERNAL_CHILD_TITLE_PREFIX)) {
181350
+ deps.internalChildSessions.add(info.id);
181351
+ sessionLog(info.id, `marked internal magic-context child (title="${info.title}") — exempt from transform + injection`);
181352
+ }
180828
181353
  try {
180829
181354
  const modelKey = resolveModelKey(info.providerID, info.modelID);
180830
181355
  updateSessionMeta(deps.db, info.id, {
@@ -181241,10 +181766,7 @@ await __promiseAll([
181241
181766
  ]);
181242
181767
 
181243
181768
  // src/hooks/magic-context/text-complete.ts
181244
- var DEFAULT_PATTERNS = [
181245
- /^(\u00a7\d+\u00a7\s*)+/,
181246
- /\u00a7/g
181247
- ];
181769
+ init_tag_content_primitives();
181248
181770
  function parseRegex(src) {
181249
181771
  if (src.startsWith("/")) {
181250
181772
  const lastSlash = src.lastIndexOf("/");
@@ -181258,17 +181780,19 @@ function parseRegex(src) {
181258
181780
  }
181259
181781
  function createTextCompleteHandler(stripConfig) {
181260
181782
  const isEnabled = stripConfig?.enabled ?? true;
181261
- let patterns;
181262
- if (!isEnabled) {
181263
- patterns = [];
181264
- } else if (stripConfig?.patterns && stripConfig.patterns.length > 0) {
181783
+ let patterns = null;
181784
+ if (!isEnabled) {} else if (stripConfig?.patterns && stripConfig.patterns.length > 0) {
181265
181785
  patterns = stripConfig.patterns.map(parseRegex);
181266
- } else {
181267
- patterns = DEFAULT_PATTERNS;
181268
181786
  }
181269
181787
  return async (_input, output) => {
181270
- for (const regex of patterns) {
181271
- output.text = output.text.replace(regex, "");
181788
+ if (!isEnabled)
181789
+ return;
181790
+ if (patterns) {
181791
+ for (const regex of patterns) {
181792
+ output.text = output.text.replace(regex, "");
181793
+ }
181794
+ } else {
181795
+ output.text = stripPersistedAssistantText(output.text);
181272
181796
  }
181273
181797
  };
181274
181798
  }
@@ -181491,7 +182015,7 @@ function createToolExecuteAfterHook(args) {
181491
182015
  init_send_session_notification();
181492
182016
 
181493
182017
  // src/hooks/magic-context/system-prompt-hash.ts
181494
- import { createHash as createHash11 } from "node:crypto";
182018
+ import { createHash as createHash10 } from "node:crypto";
181495
182019
 
181496
182020
  // src/agents/magic-context-prompt.ts
181497
182021
  var LONG_TERM_PARTNER_FRAME = `### You are the user's long-term partner on this project — not a one-off hire
@@ -181613,6 +182137,9 @@ function clearSystemPromptHashSession(sessionId, handleMaps) {
181613
182137
  function isInternalOpenCodeAgent(systemPromptContent) {
181614
182138
  return systemPromptContent.includes("You are a title generator. You output ONLY a thread title.") || systemPromptContent.includes("Summarize what was done in this conversation. Write like a pull request description.") || systemPromptContent.includes("You are an anchored context summarization assistant for coding sessions.");
181615
182139
  }
182140
+ function isMagicContextInternalAgent(systemPromptContent) {
182141
+ return systemPromptContent.includes("You are Historian — the hippocampus of a long-running coding agent.") || systemPromptContent.includes("You are a memory maintenance agent for the magic-context system.") || systemPromptContent.includes("You are Sidekick, a focused memory-retrieval subagent for an AI coding assistant.") || systemPromptContent.includes("You are a file importance evaluator. Given read statistics about files");
182142
+ }
181616
182143
  function createSystemPromptHashHandler(deps) {
181617
182144
  const stickyDateBySession = new Map;
181618
182145
  const handler = async (input, output) => {
@@ -181625,6 +182152,10 @@ function createSystemPromptHashHandler(deps) {
181625
182152
  sessionLog(sessionId, "system-prompt-hash skipped (OpenCode internal agent: title/summary/compaction)");
181626
182153
  return;
181627
182154
  }
182155
+ if (deps.internalChildSessions?.has(sessionId) || isMagicContextInternalAgent(fullPromptForDetection)) {
182156
+ sessionLog(sessionId, "system-prompt-hash skipped (Magic Context internal child: historian/dreamer/sidekick/migration)");
182157
+ return;
182158
+ }
181628
182159
  const injectionEnabled = deps.injectionEnabled !== false;
181629
182160
  const skipSignatures = deps.injectionSkipSignatures ?? [];
181630
182161
  if (!injectionEnabled) {
@@ -181675,7 +182206,7 @@ function createSystemPromptHashHandler(deps) {
181675
182206
  `);
181676
182207
  if (systemContent.length === 0)
181677
182208
  return;
181678
- const currentHash = createHash11("md5").update(systemContent).digest("hex");
182209
+ const currentHash = createHash10("md5").update(systemContent).digest("hex");
181679
182210
  if (!sessionMetaEarly) {
181680
182211
  return;
181681
182212
  }
@@ -181883,6 +182414,7 @@ function createMagicContextHook(deps) {
181883
182414
  const liveModelBySession = deps.liveSessionState?.liveModelBySession ?? new Map;
181884
182415
  const agentBySession = deps.liveSessionState?.agentBySession ?? new Map;
181885
182416
  const sessionDirectoryBySession = deps.liveSessionState?.sessionDirectoryBySession ?? new Map;
182417
+ const internalChildSessions = deps.liveSessionState?.internalChildSessions ?? new Set;
181886
182418
  const recompProgressBySession = deps.liveSessionState?.recompProgressBySession ?? new Map;
181887
182419
  const recentReduceBySession = new Map;
181888
182420
  const toolUsageSinceUserTurn = new Map;
@@ -181914,7 +182446,8 @@ function createMagicContextHook(deps) {
181914
182446
  pendingMaterializationSessions,
181915
182447
  deferredMaterializationSessions,
181916
182448
  sessionDirectoryBySession,
181917
- recompProgressBySession
182449
+ recompProgressBySession,
182450
+ internalChildSessions
181918
182451
  },
181919
182452
  directory: deps.directory,
181920
182453
  historianChunkTokens: getHistorianChunkTokens(),
@@ -181959,6 +182492,7 @@ function createMagicContextHook(deps) {
181959
182492
  deferredMaterializationSessions,
181960
182493
  lastHeuristicsTurnId,
181961
182494
  commitSeenLastPass,
182495
+ internalChildSessions,
181962
182496
  client: deps.client,
181963
182497
  directory: deps.directory,
181964
182498
  memoryConfig: deps.config.memory ? {
@@ -181978,6 +182512,12 @@ function createMagicContextHook(deps) {
181978
182512
  const model = liveModelBySession.get(sessionId);
181979
182513
  return resolveModelKey(model?.providerID, model?.modelID);
181980
182514
  },
182515
+ getToolSetHash: (sessionId) => {
182516
+ const model = liveModelBySession.get(sessionId);
182517
+ if (!model)
182518
+ return "";
182519
+ return getCurrentToolSetHash(model.providerID, model.modelID, agentBySession.get(sessionId));
182520
+ },
181981
182521
  getFallbackModelId: (sessionId) => {
181982
182522
  const model = liveModelBySession.get(sessionId);
181983
182523
  return model ? `${model.providerID}/${model.modelID}` : undefined;
@@ -182010,6 +182550,7 @@ function createMagicContextHook(deps) {
182010
182550
  tagger: deps.tagger,
182011
182551
  db,
182012
182552
  client: deps.client,
182553
+ internalChildSessions,
182013
182554
  getNotificationParams: (sessionId) => getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
182014
182555
  nudgePlacements,
182015
182556
  onSessionCacheInvalidated: (sessionId) => {
@@ -182027,6 +182568,7 @@ function createMagicContextHook(deps) {
182027
182568
  recompProgressBySession.delete(sessionId);
182028
182569
  recentReduceBySession.delete(sessionId);
182029
182570
  toolUsageSinceUserTurn.delete(sessionId);
182571
+ internalChildSessions.delete(sessionId);
182030
182572
  }
182031
182573
  });
182032
182574
  const runDreamQueueInBackground = () => {
@@ -182137,6 +182679,7 @@ function createMagicContextHook(deps) {
182137
182679
  injectionSkipSignatures: deps.config.system_prompt_injection?.skip_signatures ?? [
182138
182680
  "<!-- magic-context: skip -->"
182139
182681
  ],
182682
+ internalChildSessions,
182140
182683
  experimentalUserMemories: deps.config.dreamer?.user_memories?.enabled,
182141
182684
  experimentalPinKeyFiles: deps.config.dreamer?.pin_key_files?.enabled ?? false,
182142
182685
  experimentalPinKeyFilesTokenBudget: deps.config.dreamer?.pin_key_files?.token_budget,
@@ -182905,6 +183448,7 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
182905
183448
  if (!p)
182906
183449
  return null;
182907
183450
  return {
183451
+ kind: p.kind ?? "recomp",
182908
183452
  phase: p.phase,
182909
183453
  processedMessages: p.processedMessages,
182910
183454
  totalMessages: p.totalMessages,
@@ -182924,6 +183468,7 @@ function buildSidebarSnapshot(db, sessionId, directory, liveSessionState, inject
182924
183468
  return {
182925
183469
  ...empty,
182926
183470
  recompProgress: {
183471
+ kind: p.kind ?? "recomp",
182927
183472
  phase: p.phase,
182928
183473
  processedMessages: p.processedMessages,
182929
183474
  totalMessages: p.totalMessages,
@@ -183640,16 +184185,30 @@ Example: \`ctx_note(action="write", content="Implement X because Y", surface_con
183640
184185
 
183641
184186
  Historian reads these notes, deduplicates them, and rewrites the remaining useful notes over time.`;
183642
184187
  // src/tools/ctx-note/tools.ts
183643
- await init_storage();
184188
+ await __promiseAll([
184189
+ init_message_index(),
184190
+ init_storage()
184191
+ ]);
183644
184192
  import { tool as tool3 } from "@opencode-ai/plugin";
184193
+ function captureAnchorOrdinal(db, sessionId) {
184194
+ try {
184195
+ const ordinal = getLastIndexedOrdinal(db, sessionId);
184196
+ return ordinal > 0 ? ordinal : null;
184197
+ } catch {
184198
+ return null;
184199
+ }
184200
+ }
184201
+ function anchorSuffix(note) {
184202
+ return note.anchorOrdinal !== null ? ` ↳ @msg ${note.anchorOrdinal}` : "";
184203
+ }
183645
184204
  function formatNoteLine(note) {
183646
184205
  const statusSuffix = note.status === "active" ? "" : ` (${note.status})`;
183647
184206
  if (note.type === "session") {
183648
- return `- **#${note.id}**${statusSuffix}: ${note.content}`;
184207
+ return `- **#${note.id}**${statusSuffix}: ${note.content}${anchorSuffix(note)}`;
183649
184208
  }
183650
184209
  const conditionText = note.status === "ready" ? note.readyReason ?? note.surfaceCondition ?? "Condition satisfied" : note.surfaceCondition ?? "No condition recorded";
183651
184210
  const conditionLabel = note.status === "ready" ? "Condition met" : "Condition";
183652
- return `- **#${note.id}**${statusSuffix}: ${note.content}
184211
+ return `- **#${note.id}**${statusSuffix}: ${note.content}${anchorSuffix(note)}
183653
184212
  ${conditionLabel}: ${conditionText}`;
183654
184213
  }
183655
184214
  var DISMISS_FOOTER = `
@@ -183727,6 +184286,7 @@ function createCtxNoteTool(deps) {
183727
184286
  if (!content) {
183728
184287
  return "Error: 'content' is required when action is 'write'.";
183729
184288
  }
184289
+ const anchorOrdinal = captureAnchorOrdinal(deps.db, sessionId);
183730
184290
  if (args.surface_condition?.trim()) {
183731
184291
  if (!deps.dreamerEnabled) {
183732
184292
  return "Error: Smart notes require dreamer to be enabled. Enable dreamer in magic-context.jsonc to use surface_condition.";
@@ -183738,13 +184298,14 @@ function createCtxNoteTool(deps) {
183738
184298
  content,
183739
184299
  projectPath: projectIdentity,
183740
184300
  sessionId,
183741
- surfaceCondition: args.surface_condition.trim()
184301
+ surfaceCondition: args.surface_condition.trim(),
184302
+ anchorOrdinal
183742
184303
  });
183743
184304
  return `Created smart note #${note2.id}. Dreamer will evaluate the condition during nightly runs:
183744
184305
  - Content: ${content}
183745
184306
  - Condition: ${args.surface_condition.trim()}`;
183746
184307
  }
183747
- const note = addNote(deps.db, "session", { sessionId, content });
184308
+ const note = addNote(deps.db, "session", { sessionId, content, anchorOrdinal });
183748
184309
  return `Saved session note #${note.id}. Historian will rewrite or deduplicate notes as needed.`;
183749
184310
  }
183750
184311
  if (action === "dismiss") {
@@ -183807,9 +184368,13 @@ ${parts.join(`
183807
184368
 
183808
184369
  No session notes or smart notes.`;
183809
184370
  }
183810
- return sections.join(`
184371
+ const body = sections.join(`
184372
+
184373
+ `);
184374
+ const anchorHint = body.includes("↳ @msg ") ? `
183811
184375
 
183812
- `) + DISMISS_FOOTER;
184376
+ ↳ @msg N marks the conversation tail when a note was written. To see what led to it: ctx_expand(start=N-x, end=N) (pick x for how far back to look).` : "";
184377
+ return body + anchorHint + DISMISS_FOOTER;
183813
184378
  }
183814
184379
  });
183815
184380
  }
@@ -184100,7 +184665,8 @@ function createCtxSearchTool(deps) {
184100
184665
  maxMessageOrdinal: lastCompartmentEnd >= 0 ? lastCompartmentEnd : undefined,
184101
184666
  gitCommitsEnabled,
184102
184667
  sources: normalizeSources(args.sources),
184103
- visibleMemoryIds
184668
+ visibleMemoryIds,
184669
+ explicitSearch: true
184104
184670
  });
184105
184671
  return formatSearchResults(query, results);
184106
184672
  }
@@ -184201,22 +184767,22 @@ init_models_dev_cache();
184201
184767
  init_logger();
184202
184768
  import { randomBytes } from "node:crypto";
184203
184769
  import {
184204
- mkdirSync as mkdirSync7,
184770
+ mkdirSync as mkdirSync8,
184205
184771
  readdirSync,
184206
184772
  readFileSync as readFileSync12,
184207
- renameSync,
184773
+ renameSync as renameSync2,
184208
184774
  unlinkSync as unlinkSync3,
184209
- writeFileSync as writeFileSync4
184775
+ writeFileSync as writeFileSync5
184210
184776
  } from "node:fs";
184211
184777
  import { createServer } from "node:http";
184212
184778
  import { dirname as dirname5 } from "node:path";
184213
184779
 
184214
184780
  // src/shared/rpc-utils.ts
184215
- import { createHash as createHash12 } from "node:crypto";
184781
+ import { createHash as createHash11 } from "node:crypto";
184216
184782
  import { join as join20 } from "node:path";
184217
184783
  function projectHash(directory) {
184218
184784
  const normalized = directory.replace(/\/+$/, "");
184219
- return createHash12("sha256").update(normalized).digest("hex").slice(0, 16);
184785
+ return createHash11("sha256").update(normalized).digest("hex").slice(0, 16);
184220
184786
  }
184221
184787
  function rpcPortDir(storageDir, directory) {
184222
184788
  return join20(storageDir, "rpc", projectHash(directory));
@@ -184282,7 +184848,7 @@ class MagicContextRpcServer {
184282
184848
  this.handlers.set(method, handler);
184283
184849
  }
184284
184850
  async start() {
184285
- return new Promise((resolve5, reject) => {
184851
+ return new Promise((resolve6, reject) => {
184286
184852
  const server = createServer((req, res) => this.dispatch(req, res));
184287
184853
  server.on("error", (err) => {
184288
184854
  log(`[rpc] server error: ${err.message}`);
@@ -184299,20 +184865,20 @@ class MagicContextRpcServer {
184299
184865
  try {
184300
184866
  this.warnIfOtherLiveInstance();
184301
184867
  const dir = dirname5(this.portFilePath);
184302
- mkdirSync7(dir, { recursive: true, mode: 448 });
184868
+ mkdirSync8(dir, { recursive: true, mode: 448 });
184303
184869
  const tmpPath = `${this.portFilePath}.tmp`;
184304
- writeFileSync4(tmpPath, JSON.stringify({
184870
+ writeFileSync5(tmpPath, JSON.stringify({
184305
184871
  port: this.port,
184306
184872
  pid: process.pid,
184307
184873
  started_at: this.startedAt,
184308
184874
  token: this.token
184309
184875
  }), { encoding: "utf-8", mode: 384 });
184310
- renameSync(tmpPath, this.portFilePath);
184876
+ renameSync2(tmpPath, this.portFilePath);
184311
184877
  log(`[rpc] server listening on 127.0.0.1:${this.port}`);
184312
184878
  } catch (err) {
184313
184879
  log(`[rpc] failed to write port file: ${err}`);
184314
184880
  }
184315
- resolve5(this.port);
184881
+ resolve6(this.port);
184316
184882
  });
184317
184883
  server.unref();
184318
184884
  });
@@ -184405,6 +184971,7 @@ var plugin = async (ctx) => {
184405
184971
  cacheSizeMb: pluginConfig.sqlite.cache_size_mb,
184406
184972
  mmapSizeMb: pluginConfig.sqlite.mmap_size_mb
184407
184973
  });
184974
+ setKeepSubagents(pluginConfig.keep_subagents === true);
184408
184975
  if (pluginConfig.configWarnings?.length) {
184409
184976
  for (const w of pluginConfig.configWarnings) {
184410
184977
  log(`[magic-context] config warning: ${w}`);
@@ -184503,7 +185070,7 @@ var plugin = async (ctx) => {
184503
185070
  rpcServer.start().catch((err) => {
184504
185071
  log(`[magic-context] RPC server failed to start: ${err}`);
184505
185072
  });
184506
- refreshModelLimitsFromApi(ctx.client);
185073
+ refreshModelLimitsFromApi(ctx.client, { retries: 3, retryDelayMs: 1000 });
184507
185074
  }
184508
185075
  {
184509
185076
  const fence = getSchemaFenceRejection();