@cortexkit/opencode-magic-context 0.11.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/agents/historian.d.ts +1 -0
- package/dist/agents/historian.d.ts.map +1 -1
- package/dist/agents/magic-context-prompt.d.ts +1 -1
- package/dist/agents/magic-context-prompt.d.ts.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli.js +362 -83
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +117 -2
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/config/variable.d.ts +46 -0
- package/dist/config/variable.d.ts.map +1 -0
- package/dist/features/magic-context/compartment-storage.d.ts +27 -1
- package/dist/features/magic-context/compartment-storage.d.ts.map +1 -1
- package/dist/features/magic-context/compression-depth-storage.d.ts +7 -0
- package/dist/features/magic-context/compression-depth-storage.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-probe.d.ts +69 -0
- package/dist/features/magic-context/memory/embedding-probe.d.ts.map +1 -0
- package/dist/features/magic-context/scheduler.d.ts +5 -1
- package/dist/features/magic-context/scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/hooks/magic-context/caveman.d.ts +35 -0
- package/dist/hooks/magic-context/caveman.d.ts.map +1 -0
- package/dist/hooks/magic-context/command-handler.d.ts +29 -1
- package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-prompt.d.ts +8 -3
- package/dist/hooks/magic-context/compartment-prompt.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-compressor.d.ts +46 -0
- package/dist/hooks/magic-context/compartment-runner-compressor.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts +10 -0
- package/dist/hooks/magic-context/compartment-runner-historian.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts +29 -0
- package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -0
- package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-types.d.ts +9 -0
- package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-validation.d.ts +5 -0
- package/dist/hooks/magic-context/compartment-runner-validation.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner.d.ts +31 -1
- package/dist/hooks/magic-context/compartment-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-handler.d.ts +4 -0
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-resolvers.d.ts +39 -1
- package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
- package/dist/hooks/magic-context/execute-status.d.ts +4 -1
- package/dist/hooks/magic-context/execute-status.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts +13 -4
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/nudger.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-chunk.d.ts +11 -0
- package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-db.d.ts +13 -0
- package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-formatting.d.ts +7 -0
- package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
- package/dist/hooks/magic-context/system-prompt-hash.d.ts +2 -0
- package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
- package/dist/hooks/magic-context/temporal-awareness.d.ts +73 -0
- package/dist/hooks/magic-context/temporal-awareness.d.ts.map +1 -0
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts +10 -0
- package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +18 -0
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2562 -1086
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/plugin/rpc-handlers.d.ts.map +1 -1
- package/dist/shared/model-requirements.d.ts.map +1 -1
- package/dist/shared/models-dev-cache.d.ts.map +1 -1
- package/dist/shared/rpc-types.d.ts +11 -0
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/tui/data/context-db.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/shared/model-requirements.ts +3 -1
- package/src/shared/models-dev-cache.ts +11 -1
- package/src/shared/rpc-types.ts +11 -0
- package/src/tui/data/context-db.ts +1 -0
package/dist/index.js
CHANGED
|
@@ -49,7 +49,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
49
49
|
var DREAMER_AGENT = "dreamer";
|
|
50
50
|
|
|
51
51
|
// src/agents/historian.ts
|
|
52
|
-
var HISTORIAN_AGENT = "historian";
|
|
52
|
+
var HISTORIAN_AGENT = "historian", HISTORIAN_EDITOR_AGENT = "historian-editor";
|
|
53
53
|
|
|
54
54
|
// src/agents/sidekick.ts
|
|
55
55
|
var SIDEKICK_AGENT = "sidekick";
|
|
@@ -14070,10 +14070,17 @@ var init_agent_overrides = __esm(() => {
|
|
|
14070
14070
|
});
|
|
14071
14071
|
|
|
14072
14072
|
// src/config/schema/magic-context.ts
|
|
14073
|
-
var DEFAULT_NUDGE_INTERVAL_TOKENS = 1e4, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65, DEFAULT_HISTORIAN_TIMEOUT_MS = 300000, DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15, DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2", DREAMER_TASKS, DreamingTaskSchema, DEFAULT_DREAMER_TASKS, DreamerConfigSchema, SidekickConfigSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema;
|
|
14073
|
+
var DEFAULT_NUDGE_INTERVAL_TOKENS = 1e4, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65, DEFAULT_HISTORIAN_TIMEOUT_MS = 300000, DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15, DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2", DEFAULT_COMPRESSOR_MIN_COMPARTMENT_RATIO = 1000, DEFAULT_COMPRESSOR_MAX_MERGE_DEPTH = 5, DEFAULT_COMPRESSOR_COOLDOWN_MS = 600000, DEFAULT_COMPRESSOR_MAX_COMPARTMENTS_PER_PASS = 15, DEFAULT_COMPRESSOR_GRACE_COMPARTMENTS = 10, COMPRESSOR_MERGE_RATIO_BY_DEPTH, DREAMER_TASKS, DreamingTaskSchema, DEFAULT_DREAMER_TASKS, DreamerConfigSchema, SidekickConfigSchema, HistorianConfigSchema, BaseEmbeddingConfigSchema, EmbeddingConfigSchema, MagicContextConfigSchema;
|
|
14074
14074
|
var init_magic_context = __esm(() => {
|
|
14075
14075
|
init_zod();
|
|
14076
14076
|
init_agent_overrides();
|
|
14077
|
+
COMPRESSOR_MERGE_RATIO_BY_DEPTH = {
|
|
14078
|
+
1: 1.33,
|
|
14079
|
+
2: 1.5,
|
|
14080
|
+
3: 2,
|
|
14081
|
+
4: 2,
|
|
14082
|
+
5: 0
|
|
14083
|
+
};
|
|
14077
14084
|
DREAMER_TASKS = [
|
|
14078
14085
|
"consolidate",
|
|
14079
14086
|
"verify",
|
|
@@ -14101,6 +14108,9 @@ var init_magic_context = __esm(() => {
|
|
|
14101
14108
|
timeout_ms: exports_external.number().default(30000),
|
|
14102
14109
|
system_prompt: exports_external.string().optional()
|
|
14103
14110
|
}).optional();
|
|
14111
|
+
HistorianConfigSchema = AgentOverrideConfigSchema.extend({
|
|
14112
|
+
two_pass: exports_external.boolean().default(false)
|
|
14113
|
+
}).optional();
|
|
14104
14114
|
BaseEmbeddingConfigSchema = exports_external.object({
|
|
14105
14115
|
provider: exports_external.enum(["local", "openai-compatible", "off"]).default("local"),
|
|
14106
14116
|
model: exports_external.string().optional(),
|
|
@@ -14143,14 +14153,17 @@ var init_magic_context = __esm(() => {
|
|
|
14143
14153
|
MagicContextConfigSchema = exports_external.object({
|
|
14144
14154
|
enabled: exports_external.boolean().default(true),
|
|
14145
14155
|
ctx_reduce_enabled: exports_external.boolean().default(true),
|
|
14146
|
-
historian:
|
|
14156
|
+
historian: HistorianConfigSchema,
|
|
14147
14157
|
dreamer: DreamerConfigSchema.optional(),
|
|
14148
14158
|
cache_ttl: exports_external.union([exports_external.string(), exports_external.object({ default: exports_external.string() }).catchall(exports_external.string())]).default("5m"),
|
|
14149
14159
|
nudge_interval_tokens: exports_external.number().min(1000).default(DEFAULT_NUDGE_INTERVAL_TOKENS),
|
|
14150
14160
|
execute_threshold_percentage: exports_external.union([
|
|
14151
|
-
exports_external.number().min(20).max(
|
|
14152
|
-
exports_external.object({ default: exports_external.number().min(20).max(
|
|
14161
|
+
exports_external.number().min(20).max(80),
|
|
14162
|
+
exports_external.object({ default: exports_external.number().min(20).max(80) }).catchall(exports_external.number().min(20).max(80))
|
|
14153
14163
|
]).default(DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE),
|
|
14164
|
+
execute_threshold_tokens: exports_external.object({
|
|
14165
|
+
default: exports_external.number().min(5000).max(2000000).optional()
|
|
14166
|
+
}).catchall(exports_external.number().min(5000).max(2000000)).optional(),
|
|
14154
14167
|
protected_tags: exports_external.number().min(1).max(100).optional(),
|
|
14155
14168
|
auto_drop_tool_age: exports_external.number().min(10).default(100),
|
|
14156
14169
|
drop_tool_structure: exports_external.boolean().default(true),
|
|
@@ -14163,6 +14176,21 @@ var init_magic_context = __esm(() => {
|
|
|
14163
14176
|
min_clusters: exports_external.number().min(1).default(3)
|
|
14164
14177
|
}).default({ enabled: true, min_clusters: 3 }),
|
|
14165
14178
|
compaction_markers: exports_external.boolean().default(true),
|
|
14179
|
+
compressor: exports_external.object({
|
|
14180
|
+
enabled: exports_external.boolean().default(true),
|
|
14181
|
+
min_compartment_ratio: exports_external.number().min(100).max(1e4).default(DEFAULT_COMPRESSOR_MIN_COMPARTMENT_RATIO),
|
|
14182
|
+
max_merge_depth: exports_external.number().min(1).max(5).default(DEFAULT_COMPRESSOR_MAX_MERGE_DEPTH),
|
|
14183
|
+
cooldown_ms: exports_external.number().min(60000).default(DEFAULT_COMPRESSOR_COOLDOWN_MS),
|
|
14184
|
+
max_compartments_per_pass: exports_external.number().min(3).max(50).default(DEFAULT_COMPRESSOR_MAX_COMPARTMENTS_PER_PASS),
|
|
14185
|
+
grace_compartments: exports_external.number().min(0).max(100).default(DEFAULT_COMPRESSOR_GRACE_COMPARTMENTS)
|
|
14186
|
+
}).default({
|
|
14187
|
+
enabled: true,
|
|
14188
|
+
min_compartment_ratio: DEFAULT_COMPRESSOR_MIN_COMPARTMENT_RATIO,
|
|
14189
|
+
max_merge_depth: DEFAULT_COMPRESSOR_MAX_MERGE_DEPTH,
|
|
14190
|
+
cooldown_ms: DEFAULT_COMPRESSOR_COOLDOWN_MS,
|
|
14191
|
+
max_compartments_per_pass: DEFAULT_COMPRESSOR_MAX_COMPARTMENTS_PER_PASS,
|
|
14192
|
+
grace_compartments: DEFAULT_COMPRESSOR_GRACE_COMPARTMENTS
|
|
14193
|
+
}),
|
|
14166
14194
|
embedding: EmbeddingConfigSchema.default({
|
|
14167
14195
|
provider: "local",
|
|
14168
14196
|
model: DEFAULT_LOCAL_EMBEDDING_MODEL
|
|
@@ -14176,10 +14204,12 @@ var init_magic_context = __esm(() => {
|
|
|
14176
14204
|
enabled: exports_external.boolean().default(false),
|
|
14177
14205
|
token_budget: exports_external.number().min(2000).max(30000).default(1e4),
|
|
14178
14206
|
min_reads: exports_external.number().min(2).max(20).default(4)
|
|
14179
|
-
}).default({ enabled: false, token_budget: 1e4, min_reads: 4 })
|
|
14207
|
+
}).default({ enabled: false, token_budget: 1e4, min_reads: 4 }),
|
|
14208
|
+
temporal_awareness: exports_external.boolean().default(false)
|
|
14180
14209
|
}).default({
|
|
14181
14210
|
user_memories: { enabled: false, promotion_threshold: 3 },
|
|
14182
|
-
pin_key_files: { enabled: false, token_budget: 1e4, min_reads: 4 }
|
|
14211
|
+
pin_key_files: { enabled: false, token_budget: 1e4, min_reads: 4 },
|
|
14212
|
+
temporal_awareness: false
|
|
14183
14213
|
}),
|
|
14184
14214
|
memory: exports_external.object({
|
|
14185
14215
|
enabled: exports_external.boolean().default(true),
|
|
@@ -14300,6 +14330,7 @@ var init_model_requirements = __esm(() => {
|
|
|
14300
14330
|
];
|
|
14301
14331
|
AGENT_MODEL_REQUIREMENTS = {
|
|
14302
14332
|
[HISTORIAN_AGENT]: { fallbackChain: HISTORIAN_FALLBACK_CHAIN },
|
|
14333
|
+
[HISTORIAN_EDITOR_AGENT]: { fallbackChain: HISTORIAN_FALLBACK_CHAIN },
|
|
14303
14334
|
[DREAMER_AGENT]: { fallbackChain: DREAMER_FALLBACK_CHAIN },
|
|
14304
14335
|
[SIDEKICK_AGENT]: { fallbackChain: SIDEKICK_FALLBACK_CHAIN }
|
|
14305
14336
|
};
|
|
@@ -14589,14 +14620,16 @@ function replaceAllCompartmentState(db, sessionId, compartments, facts) {
|
|
|
14589
14620
|
db.prepare("UPDATE session_meta SET memory_block_cache = '' WHERE session_id = ?").run(sessionId);
|
|
14590
14621
|
})();
|
|
14591
14622
|
}
|
|
14592
|
-
function buildCompartmentBlock(compartments, facts, memoryBlock) {
|
|
14623
|
+
function buildCompartmentBlock(compartments, facts, memoryBlock, dateRanges) {
|
|
14593
14624
|
const lines = [];
|
|
14594
14625
|
if (memoryBlock) {
|
|
14595
14626
|
lines.push(memoryBlock);
|
|
14596
14627
|
lines.push("");
|
|
14597
14628
|
}
|
|
14598
14629
|
for (const c of compartments) {
|
|
14599
|
-
|
|
14630
|
+
const dates = dateRanges?.byId.get(c.id);
|
|
14631
|
+
const dateAttr = dates ? ` start-date="${dates.start}" end-date="${dates.end}"` : "";
|
|
14632
|
+
lines.push(`<compartment start="${c.startMessage}" end="${c.endMessage}"${dateAttr} title="${escapeXmlAttr(c.title)}">`);
|
|
14600
14633
|
lines.push(escapeXmlContent(c.content));
|
|
14601
14634
|
lines.push("</compartment>");
|
|
14602
14635
|
lines.push("");
|
|
@@ -14679,8 +14712,29 @@ function clearRecompStaging(db, sessionId) {
|
|
|
14679
14712
|
db.transaction(() => {
|
|
14680
14713
|
db.prepare("DELETE FROM recomp_compartments WHERE session_id = ?").run(sessionId);
|
|
14681
14714
|
db.prepare("DELETE FROM recomp_facts WHERE session_id = ?").run(sessionId);
|
|
14715
|
+
try {
|
|
14716
|
+
db.prepare("UPDATE session_meta SET recomp_partial_range_start = 0, recomp_partial_range_end = 0 WHERE session_id = ?").run(sessionId);
|
|
14717
|
+
} catch {}
|
|
14682
14718
|
})();
|
|
14683
14719
|
}
|
|
14720
|
+
function getRecompPartialRange(db, sessionId) {
|
|
14721
|
+
try {
|
|
14722
|
+
const row = db.prepare("SELECT recomp_partial_range_start AS start, recomp_partial_range_end AS end FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
14723
|
+
const start = typeof row?.start === "number" ? row.start : 0;
|
|
14724
|
+
const end = typeof row?.end === "number" ? row.end : 0;
|
|
14725
|
+
if (start <= 0 || end <= 0)
|
|
14726
|
+
return null;
|
|
14727
|
+
return { start, end };
|
|
14728
|
+
} catch {
|
|
14729
|
+
return null;
|
|
14730
|
+
}
|
|
14731
|
+
}
|
|
14732
|
+
function setRecompPartialRange(db, sessionId, range) {
|
|
14733
|
+
const start = range ? range.start : 0;
|
|
14734
|
+
const end = range ? range.end : 0;
|
|
14735
|
+
db.prepare("INSERT OR IGNORE INTO session_meta (session_id) VALUES (?)").run(sessionId);
|
|
14736
|
+
db.prepare("UPDATE session_meta SET recomp_partial_range_start = ?, recomp_partial_range_end = ? WHERE session_id = ?").run(start, end, sessionId);
|
|
14737
|
+
}
|
|
14684
14738
|
function isRecompCompartmentRow(row) {
|
|
14685
14739
|
if (row === null || typeof row !== "object")
|
|
14686
14740
|
return false;
|
|
@@ -14706,23 +14760,36 @@ var init_compartment_storage = __esm(() => {
|
|
|
14706
14760
|
});
|
|
14707
14761
|
|
|
14708
14762
|
// src/hooks/magic-context/compartment-prompt.ts
|
|
14709
|
-
function
|
|
14763
|
+
function buildHistorianEditorPrompt(draft) {
|
|
14764
|
+
return [
|
|
14765
|
+
"This is a historian draft. Clean it up following the rules in your system prompt.",
|
|
14766
|
+
"",
|
|
14767
|
+
"<draft>",
|
|
14768
|
+
draft,
|
|
14769
|
+
"</draft>",
|
|
14770
|
+
"",
|
|
14771
|
+
"Return the cleaned draft as valid XML matching the original structure."
|
|
14772
|
+
].join(`
|
|
14773
|
+
`);
|
|
14774
|
+
}
|
|
14775
|
+
function buildCompressorPrompt(compartments, currentTokens, targetTokens, outputDepth, outputCount) {
|
|
14710
14776
|
const lines = [];
|
|
14711
|
-
|
|
14777
|
+
const densityLabel = outputDepth === 1 ? "MERGE ONLY" : outputDepth === 2 ? "LITE TIGHTEN" : outputDepth === 3 ? "FULL CONDENSE" : "ULTRA TELEGRAPH";
|
|
14778
|
+
const resolvedOutputCount = outputCount ?? Math.max(1, Math.ceil(compartments.length / 2));
|
|
14779
|
+
lines.push(`Density target: LEVEL ${outputDepth} (${densityLabel}). See system prompt for level rules.`);
|
|
14780
|
+
lines.push(`Input: ${compartments.length} compartments, ~${currentTokens} tokens. Target output: exactly ${resolvedOutputCount} compartments, ~${targetTokens} tokens total.`);
|
|
14712
14781
|
lines.push("");
|
|
14713
|
-
if (
|
|
14714
|
-
lines.push("
|
|
14715
|
-
} else if (
|
|
14716
|
-
lines.push("
|
|
14782
|
+
if (outputDepth === 1) {
|
|
14783
|
+
lines.push("Merge only. Preserve narrative and all U: lines. Drop only genuine duplicate sentences spanning compartments.");
|
|
14784
|
+
} else if (outputDepth === 2) {
|
|
14785
|
+
lines.push("Merge + drop filler words and hedging. Keep grammar, keep U: lines verbatim.");
|
|
14786
|
+
} else if (outputDepth === 3) {
|
|
14787
|
+
lines.push("Merge into single-paragraph compartments. Drop articles and weak auxiliaries. Keep only IRREPLACEABLE U: lines.");
|
|
14717
14788
|
} else {
|
|
14718
|
-
lines.push("
|
|
14789
|
+
lines.push("Merge into telegraphic fragments with symbol connectives (\u2192 + // |). U: lines only if truly irreplaceable.");
|
|
14719
14790
|
}
|
|
14720
14791
|
lines.push("");
|
|
14721
|
-
lines.push("
|
|
14722
|
-
lines.push("- Merge adjacent compartments when they cover related work.");
|
|
14723
|
-
lines.push("- Each output compartment must use the exact start/end ordinals from the input compartments it covers.");
|
|
14724
|
-
lines.push("- Do not invent new ordinal boundaries that don't exist in the input.");
|
|
14725
|
-
lines.push("- Preserve commit hashes and key technical details where possible.");
|
|
14792
|
+
lines.push("Preserved literally at all levels: commit hashes, file paths, URLs, code spans.");
|
|
14726
14793
|
lines.push("");
|
|
14727
14794
|
for (const c of compartments) {
|
|
14728
14795
|
lines.push(`<compartment start="${c.startMessage}" end="${c.endMessage}" title="${escapeXmlAttr(c.title)}">`);
|
|
@@ -14730,7 +14797,7 @@ function buildCompressorPrompt(compartments, currentTokens, targetTokens, averag
|
|
|
14730
14797
|
lines.push("</compartment>");
|
|
14731
14798
|
lines.push("");
|
|
14732
14799
|
}
|
|
14733
|
-
lines.push("Return
|
|
14800
|
+
lines.push("Return merged compartments as XML.");
|
|
14734
14801
|
return lines.join(`
|
|
14735
14802
|
`);
|
|
14736
14803
|
}
|
|
@@ -14764,29 +14831,144 @@ var COMPARTMENT_AGENT_SYSTEM_PROMPT = `You condense long AI coding sessions into
|
|
|
14764
14831
|
Compartment rules:
|
|
14765
14832
|
- A compartment is one contiguous completed work unit: investigation, fix, refactor, docs update, feature, or decision.
|
|
14766
14833
|
- Start a new compartment only when the work clearly pivots to a different objective.
|
|
14834
|
+
- If one broad effort contains multiple completed sub-pivots with distinct outcomes, prefer multiple smaller compartments over one umbrella compartment with many U: lines.
|
|
14767
14835
|
- Do not create compartments for magic-context commands or tool-only noise.
|
|
14768
14836
|
- If the input ends mid-topic, leave it out and report its first message index in <unprocessed_from>.
|
|
14769
14837
|
- All compartment start/end ordinals and <unprocessed_from> must use the absolute raw message numbers shown in the input. Never renumber relative to this chunk.
|
|
14838
|
+
- Every displayed raw message ordinal in the input MUST appear in exactly one compartment. Gaps between compartments are invalid. When a displayed block is pure tool noise (e.g. a long "TC: ..." run with no narrative text), do NOT skip it \u2014 extend the preceding compartment's \`end\` to absorb the range, or include it inside the current compartment if the block falls within an ongoing work unit. Never create a dedicated compartment just to cover a tool-only run.
|
|
14770
14839
|
- Only emit NEW compartments for the new messages. Do not re-emit existing compartments from the existing state.
|
|
14771
14840
|
- Write comprehensive, detailed compartments. Include file paths, function names, commit hashes, config keys, and values when they matter.
|
|
14772
14841
|
- Do not list every changed file. Do not narrate tool calls. Do not preserve dead-end exploration beyond a brief clause when needed.
|
|
14773
14842
|
|
|
14774
|
-
|
|
14775
|
-
|
|
14776
|
-
|
|
14777
|
-
|
|
14778
|
-
|
|
14779
|
-
-
|
|
14780
|
-
|
|
14781
|
-
|
|
14782
|
-
|
|
14783
|
-
|
|
14784
|
-
U:
|
|
14785
|
-
|
|
14786
|
-
|
|
14787
|
-
|
|
14843
|
+
# Construction order (MANDATORY)
|
|
14844
|
+
|
|
14845
|
+
For each compartment, build in this exact order:
|
|
14846
|
+
|
|
14847
|
+
1. Write the narrative summary first \u2014 what was done, why, and the outcome. This is 1\u20134 sentences covering the work unit completely.
|
|
14848
|
+
2. Re-read your narrative. Ask: does the summary already convey all important decisions and constraints from this work unit?
|
|
14849
|
+
3. If yes, the compartment is DONE with zero U: lines. Move on.
|
|
14850
|
+
4. If no, identify the specific signal the narrative cannot capture. Add U: lines ONLY for those signals.
|
|
14851
|
+
5. Before writing each U: line, run the CROSS-COMPARTMENT CHECK (see below).
|
|
14852
|
+
|
|
14853
|
+
Zero U: lines in a compartment is normal and expected. Most compartments should have 0\u20132 U: lines. Compartments with 3\u20135 are rare and must be justified by genuinely distinct durable signals.
|
|
14854
|
+
|
|
14855
|
+
# DROP rules (check these first \u2014 if any match, drop without exception)
|
|
14856
|
+
|
|
14857
|
+
- Questions in ANY form: "should I X?", "what about Y?", "do you think Z?", "isn't it better to A?", "why don't we B?", "any ideas?" \u2014 the resolved answer belongs in narrative only. If it feels important to keep the question, you are wrong: keep the answer in narrative.
|
|
14858
|
+
- Agreements and acknowledgments: "yes", "okay", "sure", "thanks", "go ahead", "looks good", "perfect", "I agree", "sounds good", "great".
|
|
14859
|
+
- Pure pacing and sequencing: "let's start", "continue", "let's do all", "now we can X", "let's commit", "first do A then B", "before that", "in the meantime".
|
|
14860
|
+
- Tactical observations: "I just noticed X", "we recently did Y", "I'm seeing Z right now", "this seems wrong".
|
|
14861
|
+
- Debugging status: "context is at 78%", "I'm restarting", "the last build failed".
|
|
14862
|
+
- Dogfooding/restart loops: "I restarted, can you check?", "okay we should have updated versions now", "let me try again".
|
|
14863
|
+
- Pasted error output or logs as U: line \u2014 capture the underlying problem in narrative, not the raw paste.
|
|
14864
|
+
- Examples and illustrations: "mine was when an agent wants to see X" \u2014 convert the underlying intent into a directive or drop.
|
|
14865
|
+
- Hype with embedded directive: ALL-CAPS pleas, "PLEASE PLEASE PLEASE just do X" \u2014 extract only the underlying directive into narrative; drop the hype.
|
|
14866
|
+
- Social signals, banter, emoji-only, enthusiasm.
|
|
14867
|
+
- Deferred ideas: "for later", "we can do X later", "another idea for the future".
|
|
14868
|
+
- Mid-process status: "running Y", "checking Z".
|
|
14869
|
+
- Superseded drafts once a later message gives the final decision.
|
|
14870
|
+
- Standing workflow rules ("always run lint before push") \u2014 these belong in WORKFLOW_RULES facts, not U: lines.
|
|
14871
|
+
|
|
14872
|
+
# Wording rule (default: verbatim)
|
|
14873
|
+
|
|
14874
|
+
By default, U: lines use the user's actual wording. The user's exact phrasing often carries negotiation context, emphasis, or technical specificity that paraphrase loses.
|
|
14875
|
+
|
|
14876
|
+
Paraphrase ONLY in these cases:
|
|
14877
|
+
- **Strip agreement prefixes**: "Yes X", "Okay X", "Sure X" \u2192 keep only the substantive part of X, in the user's original wording.
|
|
14878
|
+
- **Split compound directives**: If one message contains two distinct durable directives, split into two U: lines \u2014 each preserving the user's wording for its part.
|
|
14879
|
+
- **Drop conversational noise, keep core**: If a message wraps a directive in exploratory phrasing ("so I was thinking, maybe... but actually..."), drop the exploration and keep the core directive in the user's remaining words. Don't invent new phrasing.
|
|
14880
|
+
|
|
14881
|
+
NEVER:
|
|
14882
|
+
- Rewrite a clear user directive into a formal constraint statement. ("We need tool count at ~8" stays as-is; do NOT convert to "Tool count must be capped at 8.")
|
|
14883
|
+
- Synthesize a directive from multiple messages into one canonical statement. If the signal needs synthesis, it belongs in narrative, not a U: line.
|
|
14884
|
+
- Add technical specificity the user didn't state (file paths, function names, constant names). Canonical technical specificity belongs in narrative or facts, not in U: lines attributed to the user.
|
|
14885
|
+
|
|
14886
|
+
Good example:
|
|
14887
|
+
Original user message: "Yes let's do this. But we need to also make sure that we limit by message count as some sessions have quite a lot of messages."
|
|
14888
|
+
Correct U: line: "We need to also make sure that we limit by message count as some sessions have quite a lot of messages."
|
|
14889
|
+
(Stripped agreement prefix; kept the user's actual wording.)
|
|
14890
|
+
|
|
14891
|
+
Bad example (do not do this):
|
|
14892
|
+
Incorrect U: line: "Cap session history retrieval at a maximum message count to prevent memory issues on large sessions."
|
|
14893
|
+
(Rewrote the user directive into formal language and invented specificity.)
|
|
14894
|
+
|
|
14895
|
+
# KEEP rules (U: line survives only if ALL pass)
|
|
14896
|
+
|
|
14897
|
+
1. DURABLE: The signal matters after the immediate turn.
|
|
14898
|
+
2. SPECIFIC: Concrete goal, hard constraint, design decision, rejection, rationale, threshold, source-of-truth correction, or future-work directive.
|
|
14899
|
+
3. OUTCOME-BACKED: This compartment's narrative clearly states what was done, decided, or changed because of the message.
|
|
14900
|
+
4. NON-REDUNDANT: Not captured by another U: line (see CROSS-COMPARTMENT CHECK), by a fact, or by the narrative.
|
|
14901
|
+
5. IRREPLACEABLE: The user's wording adds signal that narrative paraphrase cannot preserve. If the same information could appear as narrative without losing meaning, it should.
|
|
14902
|
+
|
|
14903
|
+
Categories of KEEP:
|
|
14904
|
+
- Hard gates, thresholds, config defaults, percentages, byte sizes with concrete values.
|
|
14905
|
+
- Accepted designs and explicit decisions.
|
|
14906
|
+
- Rejections and negative constraints: "X is wrong because Y", "we should NOT do Z".
|
|
14907
|
+
- Source-of-truth corrections: "follow the code, not the README".
|
|
14908
|
+
- Implementation pivots stated in future tense: "instead of X let's do Y", "switch to Z".
|
|
14909
|
+
- Durable rationale that explains WHY an approach was chosen.
|
|
14910
|
+
- Specific feature requirements stated as durable goals.
|
|
14911
|
+
|
|
14912
|
+
# PIVOT vs OBSERVATION test
|
|
14913
|
+
|
|
14914
|
+
A pivot is FUTURE-TENSE and changes the plan: "instead of X, let's do Y", "switch this to Z", "actually, let's not do A".
|
|
14915
|
+
An observation is PAST-TENSE or PRESENT-TENSE and reports state: "we recently did X", "I just noticed Y", "this is broken right now".
|
|
14916
|
+
Observations may frame narrative context but are NOT pivots and NOT durable. Drop them as U: lines.
|
|
14917
|
+
|
|
14918
|
+
# CROSS-COMPARTMENT CHECK (forward-looking)
|
|
14919
|
+
|
|
14920
|
+
Before writing ANY U: line in the current compartment:
|
|
14921
|
+
1. Scan U: lines you have ALREADY written in previous compartments in this response.
|
|
14922
|
+
2. If any prior U: line expresses the same intent, decision, constraint, or rationale \u2014 even in different words \u2014 do NOT write the new U: line.
|
|
14923
|
+
3. Let the narrative in the current compartment carry the signal instead.
|
|
14924
|
+
|
|
14925
|
+
This is a forward operation: you only need to check what you already wrote, not revisit past compartments.
|
|
14926
|
+
|
|
14927
|
+
Examples of same-intent pairs to collapse:
|
|
14928
|
+
- "X shouldn't cause cache bust" + "X must not bust cache by itself" \u2192 keep only the first, in its original compartment.
|
|
14929
|
+
- "Let's use monorepo" + "Yes, monorepo is the right call" \u2192 keep only the first.
|
|
14930
|
+
- "Add logging" + "We need logs here too" \u2192 keep only the first.
|
|
14931
|
+
|
|
14932
|
+
Never keep two U: lines for the same underlying directive across compartments.
|
|
14933
|
+
|
|
14934
|
+
# Budget
|
|
14935
|
+
|
|
14936
|
+
- HARD LIMIT: 3\u20135 U: lines per compartment. 0\u20132 is typical.
|
|
14937
|
+
- If you have more than 5 candidate U: lines in one compartment, that is a signal to split into two compartments at a natural pivot, not to stuff more.
|
|
14938
|
+
- Every U: line must be immediately followed by 1\u20133 sentences describing the outcome, decision, or effect. Never stack two U: lines without intervening outcome text.
|
|
14939
|
+
|
|
14940
|
+
# Example: CORRECT preservation (narrative-first, verbatim U: line)
|
|
14941
|
+
|
|
14942
|
+
<compartment start="50" end="120" title="Built the auth layer">
|
|
14943
|
+
Implemented JWT auth with hard 60-minute exp claim and refresh-token rotation. Chose Bearer tokens over cookies after finding cookie-based auth broke the SPA flow. Added session_expiry config (read-only at runtime). Commits: a3f891, b22c4e.
|
|
14944
|
+
U: We need session expiry capped at 1 hour, no exceptions
|
|
14945
|
+
Hardcoded the 60-minute cap at the JWT-issuer layer so runtime overrides cannot extend it.
|
|
14946
|
+
</compartment>
|
|
14947
|
+
|
|
14948
|
+
Notice: only one U: line, kept verbatim from the user's actual message. The cookie-to-Bearer pivot is narrative because paraphrase captures the signal fully.
|
|
14949
|
+
|
|
14950
|
+
# Example: OVER-PRESERVATION (avoid)
|
|
14951
|
+
|
|
14952
|
+
<compartment start="200" end="350" title="Refactored data layer">
|
|
14953
|
+
U: Okay let's start on the data layer
|
|
14954
|
+
U: What about transactions?
|
|
14955
|
+
U: Yes that approach looks good
|
|
14956
|
+
U: Actually wait, maybe we need write-ahead logging
|
|
14957
|
+
U: I just noticed the previous commit broke a test
|
|
14958
|
+
U: Let's commit and ship it
|
|
14959
|
+
Refactored data layer with WAL mode and connection pooling.
|
|
14960
|
+
</compartment>
|
|
14961
|
+
|
|
14962
|
+
Problems: pacing, question, agreement, observation, pacing again. Only one message carries signal, and even that is narrative-capturable.
|
|
14963
|
+
|
|
14964
|
+
# CORRECT version of the above
|
|
14965
|
+
|
|
14966
|
+
<compartment start="200" end="350" title="Refactored data layer">
|
|
14967
|
+
Refactored data layer to use WAL mode plus connection pooling. Chose WAL over plain connections for concurrent read performance under sustained write load.
|
|
14788
14968
|
</compartment>
|
|
14789
14969
|
|
|
14970
|
+
Zero U: lines. The pivot to WAL is clear in narrative.
|
|
14971
|
+
|
|
14790
14972
|
Fact rules:
|
|
14791
14973
|
- Facts are editable state, not append-only notes. Rewrite, normalize, deduplicate, or drop existing facts whenever needed.
|
|
14792
14974
|
- Before emitting any fact, check all existing facts in the same category for semantic duplicates. If two facts describe the same decision, constraint, or default with different wording, merge them into one canonical statement. Never emit two facts that could be answered by the same question.
|
|
@@ -14849,7 +15031,44 @@ More summary text.
|
|
|
14849
15031
|
</meta>
|
|
14850
15032
|
</output>
|
|
14851
15033
|
|
|
14852
|
-
Omit empty fact categories. Compartments must be ordered, contiguous for the ranges they cover, and non-overlapping.`,
|
|
15034
|
+
Omit empty fact categories. Compartments must be ordered, contiguous for the ranges they cover, and non-overlapping.`, HISTORIAN_EDITOR_SYSTEM_PROMPT = `You are an editor refining a historian draft. The draft was produced by a first-pass historian and may contain noise \u2014 low-signal U: lines, redundant quotes across compartments, and weak preservation decisions.
|
|
15035
|
+
|
|
15036
|
+
Your job is to clean the draft without changing its structure:
|
|
15037
|
+
|
|
15038
|
+
1. DROP low-signal U: lines:
|
|
15039
|
+
- Questions in any form \u2014 resolved decision goes in narrative only.
|
|
15040
|
+
- Pacing/agreement: "let's go", "yes", "okay", "sounds good", "I agree".
|
|
15041
|
+
- Pasted error output, debugging status, mid-process observations.
|
|
15042
|
+
- Tactical micro-direction: "now look at X", "first check Y".
|
|
15043
|
+
|
|
15044
|
+
2. DROP cross-compartment duplicates:
|
|
15045
|
+
- Scan U: lines across ALL compartments in the draft.
|
|
15046
|
+
- If two U: lines express the same intent/decision, keep only ONE \u2014 in the compartment where the outcome is actually described.
|
|
15047
|
+
|
|
15048
|
+
3. STRIP agreement prefixes:
|
|
15049
|
+
- "Yes we should X" \u2192 keep only the directive content, or drop entirely if nothing substantive remains after "Yes".
|
|
15050
|
+
|
|
15051
|
+
4. PREFER verbatim over paraphrase:
|
|
15052
|
+
- If the draft rephrased a user directive into formal constraint language, restore the user's wording if available.
|
|
15053
|
+
- Do not invent technical specificity (file paths, function names, constants) the user did not state.
|
|
15054
|
+
|
|
15055
|
+
5. FOLD into narrative when possible:
|
|
15056
|
+
- If a U: line's signal is already captured in the surrounding narrative, drop the U: line.
|
|
15057
|
+
- Narrative should not need the U: line to be understood.
|
|
15058
|
+
|
|
15059
|
+
6. KEEP as U: lines ONLY:
|
|
15060
|
+
- Hard constraints with concrete values (thresholds, byte sizes, timeouts).
|
|
15061
|
+
- Explicit rejections ("X is wrong because Y", "NOT Z").
|
|
15062
|
+
- Implementation pivots in future-tense ("instead of A, do B").
|
|
15063
|
+
- Source-of-truth corrections.
|
|
15064
|
+
|
|
15065
|
+
Do NOT change:
|
|
15066
|
+
- Compartment titles, ranges, or ordering.
|
|
15067
|
+
- Narrative summary text unless it directly references a U: line you dropped (in which case integrate the signal into the narrative).
|
|
15068
|
+
- Facts \u2014 leave the facts section untouched.
|
|
15069
|
+
- <meta> section \u2014 leave messages_processed and unprocessed_from exactly as the draft has them.
|
|
15070
|
+
|
|
15071
|
+
Output the cleaned version as valid XML matching the original structure. Preserve all XML tags, compartment ranges, meta, and facts.`, USER_OBSERVATIONS_APPENDIX = `
|
|
14853
15072
|
|
|
14854
15073
|
User observation rules (EXPERIMENTAL):
|
|
14855
15074
|
- After outputting compartments and facts, also output a <user_observations> section.
|
|
@@ -14870,17 +15089,17 @@ var init_compartment_prompt = __esm(() => {
|
|
|
14870
15089
|
});
|
|
14871
15090
|
|
|
14872
15091
|
// src/shared/opencode-config-dir.ts
|
|
14873
|
-
import { homedir as
|
|
14874
|
-
import { join as join3, resolve } from "path";
|
|
15092
|
+
import { homedir as homedir3 } from "os";
|
|
15093
|
+
import { join as join3, resolve as resolve2 } from "path";
|
|
14875
15094
|
function getCliConfigDir() {
|
|
14876
15095
|
const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
14877
15096
|
if (envConfigDir) {
|
|
14878
|
-
return
|
|
15097
|
+
return resolve2(envConfigDir);
|
|
14879
15098
|
}
|
|
14880
15099
|
if (process.platform === "win32") {
|
|
14881
|
-
return join3(
|
|
15100
|
+
return join3(homedir3(), ".config", "opencode");
|
|
14882
15101
|
}
|
|
14883
|
-
return join3(process.env.XDG_CONFIG_HOME || join3(
|
|
15102
|
+
return join3(process.env.XDG_CONFIG_HOME || join3(homedir3(), ".config"), "opencode");
|
|
14884
15103
|
}
|
|
14885
15104
|
function getOpenCodeConfigDir(_options) {
|
|
14886
15105
|
return getCliConfigDir();
|
|
@@ -15087,12 +15306,12 @@ __export(exports_conflict_warning_hook, {
|
|
|
15087
15306
|
sendConflictWarning: () => sendConflictWarning,
|
|
15088
15307
|
cleanupConflictWarnings: () => cleanupConflictWarnings
|
|
15089
15308
|
});
|
|
15090
|
-
import { existsSync as
|
|
15091
|
-
import { homedir as
|
|
15309
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
15310
|
+
import { homedir as homedir4, platform } from "os";
|
|
15092
15311
|
import { join as join5 } from "path";
|
|
15093
15312
|
function getDesktopStatePath() {
|
|
15094
15313
|
const os2 = platform();
|
|
15095
|
-
const home =
|
|
15314
|
+
const home = homedir4();
|
|
15096
15315
|
if (os2 === "darwin") {
|
|
15097
15316
|
return join5(home, "Library", "Application Support", "ai.opencode.desktop", "opencode.global.dat");
|
|
15098
15317
|
}
|
|
@@ -15108,12 +15327,12 @@ function getDesktopStatePath() {
|
|
|
15108
15327
|
}
|
|
15109
15328
|
function readDesktopState(directory) {
|
|
15110
15329
|
const statePath = getDesktopStatePath();
|
|
15111
|
-
if (!statePath || !
|
|
15330
|
+
if (!statePath || !existsSync4(statePath)) {
|
|
15112
15331
|
log(`[magic-context] conflict-warning: Desktop state file not found at ${statePath}`);
|
|
15113
15332
|
return { sessionId: null, sidecarUrl: null };
|
|
15114
15333
|
}
|
|
15115
15334
|
try {
|
|
15116
|
-
const raw =
|
|
15335
|
+
const raw = readFileSync4(statePath, "utf-8");
|
|
15117
15336
|
const state = JSON.parse(raw);
|
|
15118
15337
|
let sidecarUrl = null;
|
|
15119
15338
|
const serverStr = state.server;
|
|
@@ -16099,6 +16318,8 @@ var exports_read_session_db = {};
|
|
|
16099
16318
|
__export(exports_read_session_db, {
|
|
16100
16319
|
withReadOnlySessionDb: () => withReadOnlySessionDb,
|
|
16101
16320
|
getRawSessionMessageCountFromDb: () => getRawSessionMessageCountFromDb,
|
|
16321
|
+
getMessageTimesFromOpenCodeDb: () => getMessageTimesFromOpenCodeDb,
|
|
16322
|
+
findLastAssistantModelFromOpenCodeDb: () => findLastAssistantModelFromOpenCodeDb,
|
|
16102
16323
|
closeReadOnlySessionDb: () => closeReadOnlySessionDb
|
|
16103
16324
|
});
|
|
16104
16325
|
import { Database } from "bun:sqlite";
|
|
@@ -16140,6 +16361,47 @@ function getRawSessionMessageCountFromDb(db, sessionId) {
|
|
|
16140
16361
|
AND COALESCE(json_extract(data, '$.finish'), '') = 'stop')`).get(sessionId);
|
|
16141
16362
|
return typeof row?.count === "number" ? row.count : 0;
|
|
16142
16363
|
}
|
|
16364
|
+
function getMessageTimesFromOpenCodeDb(sessionId, messageIds) {
|
|
16365
|
+
const result = new Map;
|
|
16366
|
+
if (messageIds.length === 0)
|
|
16367
|
+
return result;
|
|
16368
|
+
try {
|
|
16369
|
+
withReadOnlySessionDb((db) => {
|
|
16370
|
+
const placeholders = messageIds.map(() => "?").join(",");
|
|
16371
|
+
const rows = db.prepare(`SELECT id, time_created FROM message WHERE session_id = ? AND id IN (${placeholders})`).all(sessionId, ...messageIds);
|
|
16372
|
+
for (const row of rows) {
|
|
16373
|
+
if (typeof row.id === "string" && typeof row.time_created === "number") {
|
|
16374
|
+
result.set(row.id, row.time_created);
|
|
16375
|
+
}
|
|
16376
|
+
}
|
|
16377
|
+
});
|
|
16378
|
+
} catch (error48) {
|
|
16379
|
+
log("[magic-context] failed to resolve message times from OpenCode DB:", error48);
|
|
16380
|
+
}
|
|
16381
|
+
return result;
|
|
16382
|
+
}
|
|
16383
|
+
function findLastAssistantModelFromOpenCodeDb(sessionId) {
|
|
16384
|
+
try {
|
|
16385
|
+
return withReadOnlySessionDb((db) => {
|
|
16386
|
+
const row = db.prepare(`SELECT json_extract(data, '$.providerID') as providerID,
|
|
16387
|
+
json_extract(data, '$.modelID') as modelID
|
|
16388
|
+
FROM message
|
|
16389
|
+
WHERE session_id = ?
|
|
16390
|
+
AND json_extract(data, '$.role') = 'assistant'
|
|
16391
|
+
AND json_extract(data, '$.providerID') IS NOT NULL
|
|
16392
|
+
AND json_extract(data, '$.modelID') IS NOT NULL
|
|
16393
|
+
ORDER BY time_created DESC
|
|
16394
|
+
LIMIT 1`).get(sessionId);
|
|
16395
|
+
if (!row || typeof row.providerID !== "string" || typeof row.modelID !== "string") {
|
|
16396
|
+
return null;
|
|
16397
|
+
}
|
|
16398
|
+
return { providerID: row.providerID, modelID: row.modelID };
|
|
16399
|
+
});
|
|
16400
|
+
} catch (error48) {
|
|
16401
|
+
log("[magic-context] failed to recover live model from OpenCode DB:", error48);
|
|
16402
|
+
return null;
|
|
16403
|
+
}
|
|
16404
|
+
}
|
|
16143
16405
|
var cachedReadOnlyDb = null;
|
|
16144
16406
|
var init_read_session_db = __esm(() => {
|
|
16145
16407
|
init_data_path();
|
|
@@ -16203,7 +16465,7 @@ async function acquireModelLoadLock(lockPath) {
|
|
|
16203
16465
|
log("[magic-context] embedding-load lock wait exceeded, proceeding without lock");
|
|
16204
16466
|
return async () => {};
|
|
16205
16467
|
}
|
|
16206
|
-
await new Promise((
|
|
16468
|
+
await new Promise((resolve3) => setTimeout(resolve3, LOCK_POLL_MS));
|
|
16207
16469
|
}
|
|
16208
16470
|
}
|
|
16209
16471
|
}
|
|
@@ -16328,7 +16590,7 @@ class LocalEmbeddingProvider {
|
|
|
16328
16590
|
}
|
|
16329
16591
|
const delayMs = 300 * attempt + Math.floor(Math.random() * 200);
|
|
16330
16592
|
log(`[magic-context] embedding model load attempt ${attempt}/${MAX_ATTEMPTS} failed transiently, retrying in ${delayMs}ms`);
|
|
16331
|
-
await new Promise((
|
|
16593
|
+
await new Promise((resolve3) => setTimeout(resolve3, delayMs));
|
|
16332
16594
|
}
|
|
16333
16595
|
}
|
|
16334
16596
|
if (this.pipeline) {
|
|
@@ -16692,14 +16954,6 @@ function getTotalDepthStatement(db) {
|
|
|
16692
16954
|
}
|
|
16693
16955
|
return stmt;
|
|
16694
16956
|
}
|
|
16695
|
-
function getMaxDepthStatement(db) {
|
|
16696
|
-
let stmt = maxDepthStatements.get(db);
|
|
16697
|
-
if (!stmt) {
|
|
16698
|
-
stmt = db.prepare("SELECT COALESCE(MAX(depth), 0) AS max_depth FROM compression_depth WHERE session_id = ?");
|
|
16699
|
-
maxDepthStatements.set(db, stmt);
|
|
16700
|
-
}
|
|
16701
|
-
return stmt;
|
|
16702
|
-
}
|
|
16703
16957
|
function getClearDepthStatement(db) {
|
|
16704
16958
|
let stmt = clearDepthStatements.get(db);
|
|
16705
16959
|
if (!stmt) {
|
|
@@ -16728,13 +16982,15 @@ function getAverageCompressionDepth(db, sessionId, startOrdinal, endOrdinal) {
|
|
|
16728
16982
|
const messageCount = endOrdinal - startOrdinal + 1;
|
|
16729
16983
|
return totalDepth / messageCount;
|
|
16730
16984
|
}
|
|
16731
|
-
function getMaxCompressionDepth(db, sessionId) {
|
|
16732
|
-
const row = getMaxDepthStatement(db).get(sessionId);
|
|
16733
|
-
return typeof row?.max_depth === "number" ? row.max_depth : 0;
|
|
16734
|
-
}
|
|
16735
16985
|
function clearCompressionDepth(db, sessionId) {
|
|
16736
16986
|
getClearDepthStatement(db).run(sessionId);
|
|
16737
16987
|
}
|
|
16988
|
+
function clearCompressionDepthRange(db, sessionId, startOrdinal, endOrdinal) {
|
|
16989
|
+
if (endOrdinal < startOrdinal) {
|
|
16990
|
+
return;
|
|
16991
|
+
}
|
|
16992
|
+
db.prepare("DELETE FROM compression_depth WHERE session_id = ? AND message_ordinal BETWEEN ? AND ?").run(sessionId, startOrdinal, endOrdinal);
|
|
16993
|
+
}
|
|
16738
16994
|
var incrementDepthStatements, totalDepthStatements, maxDepthStatements, clearDepthStatements;
|
|
16739
16995
|
var init_compression_depth_storage = __esm(() => {
|
|
16740
16996
|
incrementDepthStatements = new WeakMap;
|
|
@@ -149526,6 +149782,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
149526
149782
|
const startOrdinal = Math.max(1, offset);
|
|
149527
149783
|
const lines = [];
|
|
149528
149784
|
const lineMeta = [];
|
|
149785
|
+
const flushedToolOnlyBlocks = [];
|
|
149529
149786
|
let totalTokens = 0;
|
|
149530
149787
|
let messagesProcessed = 0;
|
|
149531
149788
|
let lastOrdinal = startOrdinal - 1;
|
|
@@ -149555,6 +149812,12 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
149555
149812
|
lines.push(blockText);
|
|
149556
149813
|
lineMeta.push(...currentBlock.meta);
|
|
149557
149814
|
totalTokens += blockTokens;
|
|
149815
|
+
if (currentBlock.isToolOnly) {
|
|
149816
|
+
flushedToolOnlyBlocks.push({
|
|
149817
|
+
start: currentBlock.startOrdinal,
|
|
149818
|
+
end: currentBlock.endOrdinal
|
|
149819
|
+
});
|
|
149820
|
+
}
|
|
149558
149821
|
currentBlock = null;
|
|
149559
149822
|
return true;
|
|
149560
149823
|
}
|
|
@@ -149585,7 +149848,8 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
149585
149848
|
endOrdinal: msg.ordinal,
|
|
149586
149849
|
parts: [tcText],
|
|
149587
149850
|
meta: [...pendingNoiseMeta, meta3],
|
|
149588
|
-
commitHashes: []
|
|
149851
|
+
commitHashes: [],
|
|
149852
|
+
isToolOnly: true
|
|
149589
149853
|
};
|
|
149590
149854
|
pendingNoiseMeta = [];
|
|
149591
149855
|
}
|
|
@@ -149601,11 +149865,14 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
149601
149865
|
pendingNoiseMeta.push(meta3);
|
|
149602
149866
|
continue;
|
|
149603
149867
|
}
|
|
149868
|
+
const msgHasNarrative = textParts.length > 0;
|
|
149604
149869
|
if (currentBlock && currentBlock.role === role) {
|
|
149605
149870
|
currentBlock.endOrdinal = msg.ordinal;
|
|
149606
149871
|
currentBlock.parts.push(text);
|
|
149607
149872
|
currentBlock.meta.push(...pendingNoiseMeta, meta3);
|
|
149608
149873
|
currentBlock.commitHashes = mergeCommitHashes(currentBlock.commitHashes, compacted.commitHashes);
|
|
149874
|
+
if (msgHasNarrative)
|
|
149875
|
+
currentBlock.isToolOnly = false;
|
|
149609
149876
|
pendingNoiseMeta = [];
|
|
149610
149877
|
continue;
|
|
149611
149878
|
}
|
|
@@ -149617,11 +149884,21 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
149617
149884
|
endOrdinal: msg.ordinal,
|
|
149618
149885
|
parts: [text],
|
|
149619
149886
|
meta: [...pendingNoiseMeta, meta3],
|
|
149620
|
-
commitHashes: [...compacted.commitHashes]
|
|
149887
|
+
commitHashes: [...compacted.commitHashes],
|
|
149888
|
+
isToolOnly: !msgHasNarrative
|
|
149621
149889
|
};
|
|
149622
149890
|
pendingNoiseMeta = [];
|
|
149623
149891
|
}
|
|
149624
149892
|
flushCurrentBlock();
|
|
149893
|
+
const toolOnlyRanges = [];
|
|
149894
|
+
for (const range of flushedToolOnlyBlocks) {
|
|
149895
|
+
const last = toolOnlyRanges[toolOnlyRanges.length - 1];
|
|
149896
|
+
if (last && range.start === last.end + 1) {
|
|
149897
|
+
last.end = range.end;
|
|
149898
|
+
} else {
|
|
149899
|
+
toolOnlyRanges.push({ start: range.start, end: range.end });
|
|
149900
|
+
}
|
|
149901
|
+
}
|
|
149625
149902
|
return {
|
|
149626
149903
|
startIndex: startOrdinal,
|
|
149627
149904
|
endIndex: lastOrdinal,
|
|
@@ -149633,7 +149910,8 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
149633
149910
|
text: lines.join(`
|
|
149634
149911
|
`),
|
|
149635
149912
|
lines: lineMeta,
|
|
149636
|
-
commitClusterCount: commitClusters
|
|
149913
|
+
commitClusterCount: commitClusters,
|
|
149914
|
+
toolOnlyRanges
|
|
149637
149915
|
};
|
|
149638
149916
|
}
|
|
149639
149917
|
var activeRawMessageCache = null, PROTECTED_TAIL_USER_TURNS = 5;
|
|
@@ -150211,6 +150489,8 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
150211
150489
|
ensureColumn(db, "session_meta", "key_files", "TEXT DEFAULT ''");
|
|
150212
150490
|
ensureColumn(db, "session_meta", "conversation_tokens", "INTEGER DEFAULT 0");
|
|
150213
150491
|
ensureColumn(db, "session_meta", "tool_call_tokens", "INTEGER DEFAULT 0");
|
|
150492
|
+
ensureColumn(db, "session_meta", "recomp_partial_range_start", "INTEGER DEFAULT 0");
|
|
150493
|
+
ensureColumn(db, "session_meta", "recomp_partial_range_end", "INTEGER DEFAULT 0");
|
|
150214
150494
|
healNullTextColumns(db);
|
|
150215
150495
|
healNullIntegerColumns(db);
|
|
150216
150496
|
}
|
|
@@ -150950,8 +151230,8 @@ var init_storage = __esm(() => {
|
|
|
150950
151230
|
|
|
150951
151231
|
// src/shared/models-dev-cache.ts
|
|
150952
151232
|
import { createHash } from "crypto";
|
|
150953
|
-
import { existsSync as
|
|
150954
|
-
import { homedir as
|
|
151233
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
151234
|
+
import { homedir as homedir6, platform as platform2 } from "os";
|
|
150955
151235
|
import { join as join11 } from "path";
|
|
150956
151236
|
function hashFast(input) {
|
|
150957
151237
|
return createHash("sha1").update(input).digest("hex");
|
|
@@ -150966,9 +151246,9 @@ function getModelsJsonPath() {
|
|
|
150966
151246
|
if (xdgCache) {
|
|
150967
151247
|
cacheBase = xdgCache;
|
|
150968
151248
|
} else if (os3 === "win32") {
|
|
150969
|
-
cacheBase = process.env.LOCALAPPDATA ?? join11(
|
|
151249
|
+
cacheBase = process.env.LOCALAPPDATA ?? join11(homedir6(), "AppData", "Local");
|
|
150970
151250
|
} else {
|
|
150971
|
-
cacheBase = join11(
|
|
151251
|
+
cacheBase = join11(homedir6(), ".cache");
|
|
150972
151252
|
}
|
|
150973
151253
|
const source = process.env.OPENCODE_MODELS_URL?.trim();
|
|
150974
151254
|
const filename = source && source !== "https://models.dev" ? `models-${hashFast(source)}.json` : "models.json";
|
|
@@ -150976,12 +151256,12 @@ function getModelsJsonPath() {
|
|
|
150976
151256
|
}
|
|
150977
151257
|
function getOpencodeConfigPath() {
|
|
150978
151258
|
const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
150979
|
-
const configDir = envDir ? envDir : platform2() === "win32" ? join11(
|
|
151259
|
+
const configDir = envDir ? envDir : platform2() === "win32" ? join11(homedir6(), ".config", "opencode") : join11(process.env.XDG_CONFIG_HOME || join11(homedir6(), ".config"), "opencode");
|
|
150980
151260
|
const jsonc = join11(configDir, "opencode.jsonc");
|
|
150981
|
-
if (
|
|
151261
|
+
if (existsSync6(jsonc))
|
|
150982
151262
|
return jsonc;
|
|
150983
151263
|
const json2 = join11(configDir, "opencode.json");
|
|
150984
|
-
if (
|
|
151264
|
+
if (existsSync6(json2))
|
|
150985
151265
|
return json2;
|
|
150986
151266
|
return null;
|
|
150987
151267
|
}
|
|
@@ -150999,9 +151279,9 @@ function loadModelsDevLimitsFromFile() {
|
|
|
150999
151279
|
const modelsJsonPath = getModelsJsonPath();
|
|
151000
151280
|
let fileFound = false;
|
|
151001
151281
|
try {
|
|
151002
|
-
if (
|
|
151282
|
+
if (existsSync6(modelsJsonPath)) {
|
|
151003
151283
|
fileFound = true;
|
|
151004
|
-
const raw =
|
|
151284
|
+
const raw = readFileSync5(modelsJsonPath, "utf-8");
|
|
151005
151285
|
const data = JSON.parse(raw);
|
|
151006
151286
|
for (const [providerId, provider2] of Object.entries(data)) {
|
|
151007
151287
|
if (!provider2?.models || typeof provider2.models !== "object")
|
|
@@ -151025,8 +151305,8 @@ function loadModelsDevLimitsFromFile() {
|
|
|
151025
151305
|
}
|
|
151026
151306
|
try {
|
|
151027
151307
|
const configPath = getOpencodeConfigPath();
|
|
151028
|
-
if (configPath &&
|
|
151029
|
-
let raw =
|
|
151308
|
+
if (configPath && existsSync6(configPath)) {
|
|
151309
|
+
let raw = readFileSync5(configPath, "utf-8");
|
|
151030
151310
|
raw = raw.replace(/"(?:[^"\\]|\\.)*"|\/\/.*$/gm, (match) => match.startsWith('"') ? match : "");
|
|
151031
151311
|
const config2 = JSON.parse(raw);
|
|
151032
151312
|
if (config2.provider && typeof config2.provider === "object") {
|
|
@@ -151069,9 +151349,12 @@ async function refreshModelLimitsFromApi(client) {
|
|
|
151069
151349
|
}
|
|
151070
151350
|
}
|
|
151071
151351
|
}
|
|
151352
|
+
const previousSize = apiCache?.size ?? null;
|
|
151072
151353
|
apiCache = map2;
|
|
151073
151354
|
apiLoadedAt = Date.now();
|
|
151074
|
-
|
|
151355
|
+
if (previousSize === null || previousSize !== map2.size) {
|
|
151356
|
+
sessionLog("global", `models-dev-cache: API layer loaded ${map2.size} model limits${previousSize !== null ? ` (was ${previousSize})` : ""}`);
|
|
151357
|
+
}
|
|
151075
151358
|
} catch (error48) {
|
|
151076
151359
|
sessionLog("global", "models-dev-cache: API refresh failed:", error48 instanceof Error ? error48.message : String(error48));
|
|
151077
151360
|
}
|
|
@@ -151162,608 +151445,6 @@ var init_rpc_notifications = __esm(() => {
|
|
|
151162
151445
|
queue2 = [];
|
|
151163
151446
|
});
|
|
151164
151447
|
|
|
151165
|
-
// src/hooks/magic-context/send-session-notification.ts
|
|
151166
|
-
var exports_send_session_notification = {};
|
|
151167
|
-
__export(exports_send_session_notification, {
|
|
151168
|
-
sendUserPrompt: () => sendUserPrompt,
|
|
151169
|
-
sendIgnoredMessage: () => sendIgnoredMessage
|
|
151170
|
-
});
|
|
151171
|
-
function hasNotificationSessionClient(client) {
|
|
151172
|
-
if (client === null || typeof client !== "object")
|
|
151173
|
-
return false;
|
|
151174
|
-
const candidate = client;
|
|
151175
|
-
if (candidate.session === undefined)
|
|
151176
|
-
return true;
|
|
151177
|
-
if (candidate.session === null || typeof candidate.session !== "object")
|
|
151178
|
-
return false;
|
|
151179
|
-
const session = candidate.session;
|
|
151180
|
-
return (session.prompt === undefined || typeof session.prompt === "function") && (session.promptAsync === undefined || typeof session.promptAsync === "function");
|
|
151181
|
-
}
|
|
151182
|
-
function inferToastVariant(text) {
|
|
151183
|
-
const lower = text.toLowerCase();
|
|
151184
|
-
if (lower.includes("error") || lower.includes("failed") || lower.includes("alert"))
|
|
151185
|
-
return "error";
|
|
151186
|
-
if (lower.includes("warning") || lower.includes("\u26A0"))
|
|
151187
|
-
return "warning";
|
|
151188
|
-
if (lower.includes("complete") || lower.includes("success") || lower.includes("\u2713") || lower.includes("finished"))
|
|
151189
|
-
return "success";
|
|
151190
|
-
return "info";
|
|
151191
|
-
}
|
|
151192
|
-
function extractToastTitle(text) {
|
|
151193
|
-
const headingMatch = text.match(/^#+\s+(.+)/m);
|
|
151194
|
-
if (headingMatch)
|
|
151195
|
-
return headingMatch[1].trim();
|
|
151196
|
-
const firstLine = text.split(`
|
|
151197
|
-
`)[0].trim();
|
|
151198
|
-
if (firstLine.length <= 80)
|
|
151199
|
-
return firstLine;
|
|
151200
|
-
return "Magic Context";
|
|
151201
|
-
}
|
|
151202
|
-
async function sendIgnoredMessage(client, sessionId, text, params) {
|
|
151203
|
-
const { isTuiConnected: checkTui } = await Promise.resolve().then(() => (init_rpc_notifications(), exports_rpc_notifications));
|
|
151204
|
-
if (checkTui()) {
|
|
151205
|
-
try {
|
|
151206
|
-
const c2 = client;
|
|
151207
|
-
const tui = c2?.tui;
|
|
151208
|
-
if (typeof tui?.showToast === "function") {
|
|
151209
|
-
const tuiClient = tui;
|
|
151210
|
-
await tuiClient.showToast({
|
|
151211
|
-
body: {
|
|
151212
|
-
title: extractToastTitle(text),
|
|
151213
|
-
message: text.length > 200 ? `${text.slice(0, 200)}\u2026` : text,
|
|
151214
|
-
variant: inferToastVariant(text),
|
|
151215
|
-
duration: 5000
|
|
151216
|
-
}
|
|
151217
|
-
}).catch(() => {});
|
|
151218
|
-
return;
|
|
151219
|
-
}
|
|
151220
|
-
} catch {}
|
|
151221
|
-
}
|
|
151222
|
-
const agent = params.agent || undefined;
|
|
151223
|
-
const variant = params.variant || undefined;
|
|
151224
|
-
const model = params.providerId && params.modelId ? {
|
|
151225
|
-
providerID: params.providerId,
|
|
151226
|
-
modelID: params.modelId
|
|
151227
|
-
} : undefined;
|
|
151228
|
-
if (!hasNotificationSessionClient(client)) {
|
|
151229
|
-
sessionLog(sessionId, "session prompt API unavailable for notification");
|
|
151230
|
-
return;
|
|
151231
|
-
}
|
|
151232
|
-
const c = client;
|
|
151233
|
-
const input = {
|
|
151234
|
-
path: { id: sessionId },
|
|
151235
|
-
body: {
|
|
151236
|
-
noReply: true,
|
|
151237
|
-
agent,
|
|
151238
|
-
model,
|
|
151239
|
-
variant,
|
|
151240
|
-
parts: [
|
|
151241
|
-
{
|
|
151242
|
-
type: "text",
|
|
151243
|
-
text,
|
|
151244
|
-
ignored: true
|
|
151245
|
-
}
|
|
151246
|
-
]
|
|
151247
|
-
}
|
|
151248
|
-
};
|
|
151249
|
-
try {
|
|
151250
|
-
if (typeof c.session?.prompt === "function") {
|
|
151251
|
-
await Promise.resolve(c.session.prompt(input));
|
|
151252
|
-
} else if (typeof c.session?.promptAsync === "function") {
|
|
151253
|
-
await c.session.promptAsync(input);
|
|
151254
|
-
} else {
|
|
151255
|
-
sessionLog(sessionId, "session prompt API unavailable for notification");
|
|
151256
|
-
}
|
|
151257
|
-
} catch (error48) {
|
|
151258
|
-
const msg = getErrorMessage(error48);
|
|
151259
|
-
sessionLog(sessionId, "failed to send notification:", msg);
|
|
151260
|
-
}
|
|
151261
|
-
}
|
|
151262
|
-
async function sendUserPrompt(client, sessionId, text) {
|
|
151263
|
-
if (!hasNotificationSessionClient(client)) {
|
|
151264
|
-
sessionLog(sessionId, "session prompt API unavailable for user prompt");
|
|
151265
|
-
return;
|
|
151266
|
-
}
|
|
151267
|
-
const c = client;
|
|
151268
|
-
const input = {
|
|
151269
|
-
path: { id: sessionId },
|
|
151270
|
-
body: {
|
|
151271
|
-
parts: [{ type: "text", text }]
|
|
151272
|
-
}
|
|
151273
|
-
};
|
|
151274
|
-
try {
|
|
151275
|
-
if (typeof c.session?.promptAsync === "function") {
|
|
151276
|
-
await c.session.promptAsync(input);
|
|
151277
|
-
} else if (typeof c.session?.prompt === "function") {
|
|
151278
|
-
await Promise.resolve(c.session.prompt(input));
|
|
151279
|
-
} else {
|
|
151280
|
-
sessionLog(sessionId, "session prompt API unavailable for user prompt");
|
|
151281
|
-
}
|
|
151282
|
-
} catch (error48) {
|
|
151283
|
-
const msg = getErrorMessage(error48);
|
|
151284
|
-
sessionLog(sessionId, "failed to send user prompt:", msg);
|
|
151285
|
-
}
|
|
151286
|
-
}
|
|
151287
|
-
var init_send_session_notification = __esm(() => {
|
|
151288
|
-
init_logger();
|
|
151289
|
-
});
|
|
151290
|
-
|
|
151291
|
-
// src/hooks/magic-context/derive-budgets.ts
|
|
151292
|
-
var exports_derive_budgets = {};
|
|
151293
|
-
__export(exports_derive_budgets, {
|
|
151294
|
-
resolveHistorianContextLimit: () => resolveHistorianContextLimit,
|
|
151295
|
-
deriveTriggerBudget: () => deriveTriggerBudget,
|
|
151296
|
-
deriveHistorianChunkTokens: () => deriveHistorianChunkTokens
|
|
151297
|
-
});
|
|
151298
|
-
function deriveTriggerBudget(mainContextLimit, executeThresholdPercentage) {
|
|
151299
|
-
if (!Number.isFinite(mainContextLimit) || mainContextLimit <= 0) {
|
|
151300
|
-
return TRIGGER_BUDGET_MIN;
|
|
151301
|
-
}
|
|
151302
|
-
const thresholdFraction = Math.max(0, executeThresholdPercentage) / 100;
|
|
151303
|
-
const usable = mainContextLimit * thresholdFraction;
|
|
151304
|
-
const derived = Math.round(usable * TRIGGER_BUDGET_PERCENTAGE);
|
|
151305
|
-
return Math.max(TRIGGER_BUDGET_MIN, Math.min(TRIGGER_BUDGET_MAX, derived));
|
|
151306
|
-
}
|
|
151307
|
-
function deriveHistorianChunkTokens(historianContextLimit) {
|
|
151308
|
-
if (!Number.isFinite(historianContextLimit) || historianContextLimit <= 0) {
|
|
151309
|
-
return HISTORIAN_CHUNK_MIN;
|
|
151310
|
-
}
|
|
151311
|
-
const derived = Math.round(historianContextLimit * HISTORIAN_CHUNK_PERCENTAGE);
|
|
151312
|
-
return Math.max(HISTORIAN_CHUNK_MIN, Math.min(HISTORIAN_CHUNK_MAX, derived));
|
|
151313
|
-
}
|
|
151314
|
-
function resolveHistorianContextLimit(historianModelOverride) {
|
|
151315
|
-
if (typeof historianModelOverride === "string" && historianModelOverride.includes("/")) {
|
|
151316
|
-
const [providerID, ...rest] = historianModelOverride.split("/");
|
|
151317
|
-
const modelID = rest.join("/");
|
|
151318
|
-
if (providerID && modelID) {
|
|
151319
|
-
const limit = getModelsDevContextLimit(providerID, modelID);
|
|
151320
|
-
if (typeof limit === "number" && limit > 0)
|
|
151321
|
-
return limit;
|
|
151322
|
-
}
|
|
151323
|
-
return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
|
|
151324
|
-
}
|
|
151325
|
-
if (typeof historianModelOverride === "string" && historianModelOverride.trim() !== "") {
|
|
151326
|
-
console.warn(`[magic-context] historian.model "${historianModelOverride}" lacks provider prefix ("provider/model-id"); using fallback chain for chunk-budget derivation.`);
|
|
151327
|
-
}
|
|
151328
|
-
const chain = AGENT_MODEL_REQUIREMENTS[HISTORIAN_AGENT]?.fallbackChain;
|
|
151329
|
-
if (!chain || chain.length === 0)
|
|
151330
|
-
return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
|
|
151331
|
-
const expanded = expandFallbackChain(chain);
|
|
151332
|
-
let minLimit;
|
|
151333
|
-
for (const key of expanded) {
|
|
151334
|
-
const [providerID, ...rest] = key.split("/");
|
|
151335
|
-
const modelID = rest.join("/");
|
|
151336
|
-
if (!providerID || !modelID)
|
|
151337
|
-
continue;
|
|
151338
|
-
const limit = getModelsDevContextLimit(providerID, modelID);
|
|
151339
|
-
if (typeof limit !== "number" || limit <= 0)
|
|
151340
|
-
continue;
|
|
151341
|
-
if (minLimit === undefined || limit < minLimit)
|
|
151342
|
-
minLimit = limit;
|
|
151343
|
-
}
|
|
151344
|
-
return minLimit ?? DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
|
|
151345
|
-
}
|
|
151346
|
-
var TRIGGER_BUDGET_PERCENTAGE = 0.05, TRIGGER_BUDGET_MIN = 5000, TRIGGER_BUDGET_MAX = 50000, HISTORIAN_CHUNK_PERCENTAGE = 0.25, HISTORIAN_CHUNK_MIN = 8000, HISTORIAN_CHUNK_MAX = 50000, DEFAULT_HISTORIAN_CONTEXT_FALLBACK = 128000;
|
|
151347
|
-
var init_derive_budgets = __esm(() => {
|
|
151348
|
-
init_model_requirements();
|
|
151349
|
-
init_models_dev_cache();
|
|
151350
|
-
});
|
|
151351
|
-
|
|
151352
|
-
// src/features/magic-context/compaction-marker.ts
|
|
151353
|
-
import { Database as Database4 } from "bun:sqlite";
|
|
151354
|
-
import { join as join12 } from "path";
|
|
151355
|
-
function randomBase62(length) {
|
|
151356
|
-
const chars = [];
|
|
151357
|
-
for (let i = 0;i < length; i++) {
|
|
151358
|
-
chars.push(BASE62_CHARS[Math.floor(Math.random() * BASE62_CHARS.length)]);
|
|
151359
|
-
}
|
|
151360
|
-
return chars.join("");
|
|
151361
|
-
}
|
|
151362
|
-
function generateId(prefix, timestampMs, counter = 0n) {
|
|
151363
|
-
const encoded = BigInt(timestampMs) * 0x1000n + counter;
|
|
151364
|
-
const hex3 = encoded.toString(16).padStart(14, "0");
|
|
151365
|
-
return `${prefix}_${hex3}${randomBase62(14)}`;
|
|
151366
|
-
}
|
|
151367
|
-
function generateMessageId(timestampMs, counter = 0n) {
|
|
151368
|
-
return generateId("msg", timestampMs, counter);
|
|
151369
|
-
}
|
|
151370
|
-
function generatePartId(timestampMs, counter = 0n) {
|
|
151371
|
-
return generateId("prt", timestampMs, counter);
|
|
151372
|
-
}
|
|
151373
|
-
function getOpenCodeDbPath3() {
|
|
151374
|
-
return join12(getDataDir(), "opencode", "opencode.db");
|
|
151375
|
-
}
|
|
151376
|
-
function getWritableOpenCodeDb() {
|
|
151377
|
-
const dbPath = getOpenCodeDbPath3();
|
|
151378
|
-
if (cachedWriteDb?.path === dbPath) {
|
|
151379
|
-
return cachedWriteDb.db;
|
|
151380
|
-
}
|
|
151381
|
-
if (cachedWriteDb) {
|
|
151382
|
-
try {
|
|
151383
|
-
cachedWriteDb.db.close(false);
|
|
151384
|
-
} catch {}
|
|
151385
|
-
}
|
|
151386
|
-
const db = new Database4(dbPath);
|
|
151387
|
-
db.exec("PRAGMA journal_mode=WAL");
|
|
151388
|
-
db.exec("PRAGMA busy_timeout=5000");
|
|
151389
|
-
cachedWriteDb = { path: dbPath, db };
|
|
151390
|
-
return db;
|
|
151391
|
-
}
|
|
151392
|
-
function findBoundaryUserMessage(sessionId, endOrdinal) {
|
|
151393
|
-
const db = getWritableOpenCodeDb();
|
|
151394
|
-
const rows = db.prepare("SELECT id, time_created, data FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId);
|
|
151395
|
-
const filtered = rows.filter((row) => {
|
|
151396
|
-
try {
|
|
151397
|
-
const info = JSON.parse(row.data);
|
|
151398
|
-
return !(info.summary === true && info.finish === "stop");
|
|
151399
|
-
} catch {
|
|
151400
|
-
return true;
|
|
151401
|
-
}
|
|
151402
|
-
});
|
|
151403
|
-
let bestMatch = null;
|
|
151404
|
-
for (let i = 0;i < filtered.length && i < endOrdinal; i++) {
|
|
151405
|
-
const row = filtered[i];
|
|
151406
|
-
try {
|
|
151407
|
-
const info = JSON.parse(row.data);
|
|
151408
|
-
if (info.role === "user") {
|
|
151409
|
-
bestMatch = { id: row.id, timeCreated: row.time_created };
|
|
151410
|
-
}
|
|
151411
|
-
} catch {}
|
|
151412
|
-
}
|
|
151413
|
-
return bestMatch;
|
|
151414
|
-
}
|
|
151415
|
-
function injectCompactionMarker(args) {
|
|
151416
|
-
const boundary = findBoundaryUserMessage(args.sessionId, args.endOrdinal);
|
|
151417
|
-
if (!boundary) {
|
|
151418
|
-
log(`[magic-context] compaction-marker: no user message found at or before ordinal ${args.endOrdinal}`);
|
|
151419
|
-
return null;
|
|
151420
|
-
}
|
|
151421
|
-
const db = getWritableOpenCodeDb();
|
|
151422
|
-
const boundaryTime = boundary.timeCreated;
|
|
151423
|
-
const summaryMsgId = generateMessageId(boundaryTime + 1, 1n);
|
|
151424
|
-
const compactionPartId = generatePartId(boundaryTime, 1n);
|
|
151425
|
-
const summaryPartId = generatePartId(boundaryTime + 1, 2n);
|
|
151426
|
-
const summaryMsgData = JSON.stringify({
|
|
151427
|
-
role: "assistant",
|
|
151428
|
-
parentID: boundary.id,
|
|
151429
|
-
summary: true,
|
|
151430
|
-
finish: "stop",
|
|
151431
|
-
mode: "compaction",
|
|
151432
|
-
agent: "compaction",
|
|
151433
|
-
path: { cwd: args.directory, root: args.directory },
|
|
151434
|
-
cost: 0,
|
|
151435
|
-
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
151436
|
-
modelID: "magic-context",
|
|
151437
|
-
providerID: "magic-context",
|
|
151438
|
-
time: { created: boundaryTime + 1 }
|
|
151439
|
-
});
|
|
151440
|
-
try {
|
|
151441
|
-
db.transaction(() => {
|
|
151442
|
-
db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(compactionPartId, boundary.id, args.sessionId, boundaryTime, boundaryTime, '{"type":"compaction","auto":true}');
|
|
151443
|
-
db.prepare("INSERT INTO message (id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?)").run(summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, summaryMsgData);
|
|
151444
|
-
db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(summaryPartId, summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, JSON.stringify({ type: "text", text: args.summaryText }));
|
|
151445
|
-
})();
|
|
151446
|
-
log(`[magic-context] compaction-marker: injected boundary at user msg ${boundary.id} (ordinal ~${args.endOrdinal}), summary msg ${summaryMsgId}`);
|
|
151447
|
-
return {
|
|
151448
|
-
boundaryMessageId: boundary.id,
|
|
151449
|
-
summaryMessageId: summaryMsgId,
|
|
151450
|
-
compactionPartId,
|
|
151451
|
-
summaryPartId
|
|
151452
|
-
};
|
|
151453
|
-
} catch (error48) {
|
|
151454
|
-
log(`[magic-context] compaction-marker: injection failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
|
|
151455
|
-
return null;
|
|
151456
|
-
}
|
|
151457
|
-
}
|
|
151458
|
-
function removeCompactionMarker(state) {
|
|
151459
|
-
try {
|
|
151460
|
-
const db = getWritableOpenCodeDb();
|
|
151461
|
-
db.transaction(() => {
|
|
151462
|
-
db.prepare("DELETE FROM part WHERE id = ?").run(state.summaryPartId);
|
|
151463
|
-
db.prepare("DELETE FROM message WHERE id = ?").run(state.summaryMessageId);
|
|
151464
|
-
db.prepare("DELETE FROM part WHERE id = ?").run(state.compactionPartId);
|
|
151465
|
-
})();
|
|
151466
|
-
return true;
|
|
151467
|
-
} catch (error48) {
|
|
151468
|
-
log(`[magic-context] compaction-marker: removal failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
|
|
151469
|
-
return false;
|
|
151470
|
-
}
|
|
151471
|
-
}
|
|
151472
|
-
var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", cachedWriteDb = null;
|
|
151473
|
-
var init_compaction_marker = __esm(() => {
|
|
151474
|
-
init_data_path();
|
|
151475
|
-
init_logger();
|
|
151476
|
-
});
|
|
151477
|
-
|
|
151478
|
-
// src/hooks/magic-context/compaction-marker-manager.ts
|
|
151479
|
-
function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, directory) {
|
|
151480
|
-
const existing = getPersistedCompactionMarkerState(db, sessionId);
|
|
151481
|
-
if (existing) {
|
|
151482
|
-
if (existing.boundaryOrdinal === lastCompartmentEnd) {
|
|
151483
|
-
return;
|
|
151484
|
-
}
|
|
151485
|
-
try {
|
|
151486
|
-
removeCompactionMarker(existing);
|
|
151487
|
-
setPersistedCompactionMarkerState(db, sessionId, null);
|
|
151488
|
-
sessionLog(sessionId, `compaction-marker: removed old boundary at ordinal ${existing.boundaryOrdinal}, moving to ${lastCompartmentEnd}`);
|
|
151489
|
-
} catch (error48) {
|
|
151490
|
-
sessionLog(sessionId, `compaction-marker: failed to remove old boundary at ordinal ${existing.boundaryOrdinal}, proceeding with new injection:`, error48);
|
|
151491
|
-
}
|
|
151492
|
-
}
|
|
151493
|
-
const result = injectCompactionMarker({
|
|
151494
|
-
sessionId,
|
|
151495
|
-
endOrdinal: lastCompartmentEnd,
|
|
151496
|
-
summaryText: MARKER_SUMMARY_TEXT,
|
|
151497
|
-
directory: directory ?? process.cwd()
|
|
151498
|
-
});
|
|
151499
|
-
if (result) {
|
|
151500
|
-
setPersistedCompactionMarkerState(db, sessionId, {
|
|
151501
|
-
...result,
|
|
151502
|
-
boundaryOrdinal: lastCompartmentEnd
|
|
151503
|
-
});
|
|
151504
|
-
sessionLog(sessionId, `compaction-marker: injected at ordinal ${lastCompartmentEnd}, boundary user msg ${result.boundaryMessageId}`);
|
|
151505
|
-
}
|
|
151506
|
-
}
|
|
151507
|
-
function removeCompactionMarkerForSession(db, sessionId) {
|
|
151508
|
-
const existing = getPersistedCompactionMarkerState(db, sessionId);
|
|
151509
|
-
if (existing) {
|
|
151510
|
-
try {
|
|
151511
|
-
removeCompactionMarker(existing);
|
|
151512
|
-
setPersistedCompactionMarkerState(db, sessionId, null);
|
|
151513
|
-
sessionLog(sessionId, "compaction-marker: removed on session cleanup");
|
|
151514
|
-
} catch (error48) {
|
|
151515
|
-
setPersistedCompactionMarkerState(db, sessionId, null);
|
|
151516
|
-
sessionLog(sessionId, "compaction-marker: removal failed during session cleanup, cleared persisted state:", error48);
|
|
151517
|
-
}
|
|
151518
|
-
}
|
|
151519
|
-
}
|
|
151520
|
-
var MARKER_SUMMARY_TEXT = "[Compacted by magic-context \u2014 session history is managed by the plugin]";
|
|
151521
|
-
var init_compaction_marker_manager = __esm(() => {
|
|
151522
|
-
init_compaction_marker();
|
|
151523
|
-
init_storage_meta_persisted();
|
|
151524
|
-
init_logger();
|
|
151525
|
-
});
|
|
151526
|
-
|
|
151527
|
-
// src/hooks/magic-context/note-nudger.ts
|
|
151528
|
-
function getPersistedNoteNudgeDeliveredAt(_db, sessionId) {
|
|
151529
|
-
return lastDeliveredAt.get(sessionId) ?? 0;
|
|
151530
|
-
}
|
|
151531
|
-
function recordNoteNudgeDeliveryTime(sessionId) {
|
|
151532
|
-
lastDeliveredAt.set(sessionId, Date.now());
|
|
151533
|
-
}
|
|
151534
|
-
function onNoteTrigger(db, sessionId, trigger) {
|
|
151535
|
-
setPersistedNoteNudgeTrigger(db, sessionId);
|
|
151536
|
-
sessionLog(sessionId, `note-nudge: trigger fired (${trigger}), triggerPending=true`);
|
|
151537
|
-
}
|
|
151538
|
-
function peekNoteNudgeText(db, sessionId, currentUserMessageId, projectIdentity) {
|
|
151539
|
-
const state = getPersistedNoteNudge(db, sessionId);
|
|
151540
|
-
if (!state.triggerPending)
|
|
151541
|
-
return null;
|
|
151542
|
-
if (!state.triggerMessageId && currentUserMessageId) {
|
|
151543
|
-
setPersistedNoteNudgeTriggerMessageId(db, sessionId, currentUserMessageId);
|
|
151544
|
-
state.triggerMessageId = currentUserMessageId;
|
|
151545
|
-
}
|
|
151546
|
-
if (state.triggerMessageId && currentUserMessageId && state.triggerMessageId === currentUserMessageId) {
|
|
151547
|
-
sessionLog(sessionId, `note-nudge: deferring \u2014 current user message ${currentUserMessageId} is same as trigger-time message`);
|
|
151548
|
-
return null;
|
|
151549
|
-
}
|
|
151550
|
-
const deliveredAt = getPersistedNoteNudgeDeliveredAt(db, sessionId);
|
|
151551
|
-
if (deliveredAt > 0 && Date.now() - deliveredAt < NOTE_NUDGE_COOLDOWN_MS) {
|
|
151552
|
-
sessionLog(sessionId, `note-nudge: suppressing \u2014 last delivered ${Math.round((Date.now() - deliveredAt) / 1000)}s ago (cooldown ${NOTE_NUDGE_COOLDOWN_MS / 60000}m)`);
|
|
151553
|
-
clearPersistedNoteNudge(db, sessionId);
|
|
151554
|
-
return null;
|
|
151555
|
-
}
|
|
151556
|
-
const notes = getSessionNotes(db, sessionId);
|
|
151557
|
-
const readySmartCount = projectIdentity ? getReadySmartNotes(db, projectIdentity).length : 0;
|
|
151558
|
-
const totalCount = notes.length + readySmartCount;
|
|
151559
|
-
if (totalCount === 0) {
|
|
151560
|
-
sessionLog(sessionId, "note-nudge: triggerPending but no notes found, skipping");
|
|
151561
|
-
clearPersistedNoteNudge(db, sessionId);
|
|
151562
|
-
return null;
|
|
151563
|
-
}
|
|
151564
|
-
const parts = [];
|
|
151565
|
-
if (notes.length > 0) {
|
|
151566
|
-
parts.push(`${notes.length} deferred note${notes.length === 1 ? "" : "s"}`);
|
|
151567
|
-
}
|
|
151568
|
-
if (readySmartCount > 0) {
|
|
151569
|
-
parts.push(`${readySmartCount} ready smart note${readySmartCount === 1 ? "" : "s"}`);
|
|
151570
|
-
}
|
|
151571
|
-
sessionLog(sessionId, `note-nudge: delivering nudge for ${parts.join(" and ")}`);
|
|
151572
|
-
return `You have ${parts.join(" and ")}. Review with ctx_note read \u2014 some may be actionable now.`;
|
|
151573
|
-
}
|
|
151574
|
-
function markNoteNudgeDelivered(db, sessionId, text, messageId) {
|
|
151575
|
-
setPersistedDeliveredNoteNudge(db, sessionId, messageId ? text : "", messageId ?? "");
|
|
151576
|
-
recordNoteNudgeDeliveryTime(sessionId);
|
|
151577
|
-
sessionLog(sessionId, messageId ? `note-nudge: marked delivered, sticky anchor=${messageId}` : "note-nudge: marked delivered without anchor");
|
|
151578
|
-
}
|
|
151579
|
-
function getStickyNoteNudge(db, sessionId) {
|
|
151580
|
-
const state = getPersistedNoteNudge(db, sessionId);
|
|
151581
|
-
if (!state.stickyText || !state.stickyMessageId)
|
|
151582
|
-
return null;
|
|
151583
|
-
return { text: state.stickyText, messageId: state.stickyMessageId };
|
|
151584
|
-
}
|
|
151585
|
-
function clearNoteNudgeState(db, sessionId, options) {
|
|
151586
|
-
if (options?.persist !== false) {
|
|
151587
|
-
clearPersistedNoteNudge(db, sessionId);
|
|
151588
|
-
}
|
|
151589
|
-
lastDeliveredAt.delete(sessionId);
|
|
151590
|
-
}
|
|
151591
|
-
var NOTE_NUDGE_COOLDOWN_MS, lastDeliveredAt;
|
|
151592
|
-
var init_note_nudger = __esm(() => {
|
|
151593
|
-
init_storage_meta_persisted();
|
|
151594
|
-
init_storage_notes();
|
|
151595
|
-
init_logger();
|
|
151596
|
-
NOTE_NUDGE_COOLDOWN_MS = 15 * 60 * 1000;
|
|
151597
|
-
lastDeliveredAt = new Map;
|
|
151598
|
-
});
|
|
151599
|
-
|
|
151600
|
-
// src/features/magic-context/memory/constants.ts
|
|
151601
|
-
var PROMOTABLE_CATEGORIES, CATEGORY_PRIORITY, CATEGORY_DEFAULT_TTL;
|
|
151602
|
-
var init_constants = __esm(() => {
|
|
151603
|
-
PROMOTABLE_CATEGORIES = [
|
|
151604
|
-
"ARCHITECTURE_DECISIONS",
|
|
151605
|
-
"CONSTRAINTS",
|
|
151606
|
-
"CONFIG_DEFAULTS",
|
|
151607
|
-
"NAMING",
|
|
151608
|
-
"USER_PREFERENCES",
|
|
151609
|
-
"USER_DIRECTIVES",
|
|
151610
|
-
"ENVIRONMENT",
|
|
151611
|
-
"WORKFLOW_RULES",
|
|
151612
|
-
"KNOWN_ISSUES"
|
|
151613
|
-
];
|
|
151614
|
-
CATEGORY_PRIORITY = [
|
|
151615
|
-
"USER_DIRECTIVES",
|
|
151616
|
-
"USER_PREFERENCES",
|
|
151617
|
-
"NAMING",
|
|
151618
|
-
"CONFIG_DEFAULTS",
|
|
151619
|
-
"CONSTRAINTS",
|
|
151620
|
-
"ARCHITECTURE_DECISIONS",
|
|
151621
|
-
"ENVIRONMENT",
|
|
151622
|
-
"WORKFLOW_RULES",
|
|
151623
|
-
"KNOWN_ISSUES"
|
|
151624
|
-
];
|
|
151625
|
-
CATEGORY_DEFAULT_TTL = {
|
|
151626
|
-
WORKFLOW_RULES: 90 * 24 * 60 * 60 * 1000,
|
|
151627
|
-
KNOWN_ISSUES: 30 * 24 * 60 * 60 * 1000
|
|
151628
|
-
};
|
|
151629
|
-
});
|
|
151630
|
-
|
|
151631
|
-
// src/features/magic-context/memory/embedding-backfill.ts
|
|
151632
|
-
async function ensureMemoryEmbeddings(args) {
|
|
151633
|
-
if (!isEmbeddingEnabled()) {
|
|
151634
|
-
return args.existingEmbeddings;
|
|
151635
|
-
}
|
|
151636
|
-
const missingMemories = args.memories.filter((memory) => !args.existingEmbeddings.has(memory.id));
|
|
151637
|
-
if (missingMemories.length === 0) {
|
|
151638
|
-
return args.existingEmbeddings;
|
|
151639
|
-
}
|
|
151640
|
-
try {
|
|
151641
|
-
const embeddings = await embedBatch(missingMemories.map((memory) => memory.content));
|
|
151642
|
-
const modelId = getEmbeddingModelId();
|
|
151643
|
-
const staged = new Map;
|
|
151644
|
-
args.db.transaction(() => {
|
|
151645
|
-
for (const [index, memory] of missingMemories.entries()) {
|
|
151646
|
-
const embedding = embeddings[index];
|
|
151647
|
-
if (!embedding) {
|
|
151648
|
-
continue;
|
|
151649
|
-
}
|
|
151650
|
-
saveEmbedding(args.db, memory.id, embedding, modelId);
|
|
151651
|
-
staged.set(memory.id, embedding);
|
|
151652
|
-
}
|
|
151653
|
-
})();
|
|
151654
|
-
for (const [id, embedding] of staged) {
|
|
151655
|
-
args.existingEmbeddings.set(id, embedding);
|
|
151656
|
-
}
|
|
151657
|
-
} catch (error48) {
|
|
151658
|
-
log("[magic-context] failed to backfill memory embeddings:", error48);
|
|
151659
|
-
}
|
|
151660
|
-
return args.existingEmbeddings;
|
|
151661
|
-
}
|
|
151662
|
-
var init_embedding_backfill = __esm(() => {
|
|
151663
|
-
init_logger();
|
|
151664
|
-
init_embedding();
|
|
151665
|
-
init_storage_memory_embeddings();
|
|
151666
|
-
});
|
|
151667
|
-
|
|
151668
|
-
// src/features/magic-context/memory/promotion.ts
|
|
151669
|
-
function isPromotableCategory(category) {
|
|
151670
|
-
return PROMOTABLE_CATEGORIES.some((promotableCategory) => promotableCategory === category);
|
|
151671
|
-
}
|
|
151672
|
-
function resolveExpiresAt(category) {
|
|
151673
|
-
const ttl = CATEGORY_DEFAULT_TTL[category];
|
|
151674
|
-
return ttl === undefined ? null : Date.now() + ttl;
|
|
151675
|
-
}
|
|
151676
|
-
function promoteSessionFactsToMemory(db, sessionId, projectPath, facts) {
|
|
151677
|
-
for (const fact of facts) {
|
|
151678
|
-
if (!isPromotableCategory(fact.category)) {
|
|
151679
|
-
continue;
|
|
151680
|
-
}
|
|
151681
|
-
try {
|
|
151682
|
-
const normalizedHash = computeNormalizedHash(fact.content);
|
|
151683
|
-
const existingMemory = getMemoryByHash(db, projectPath, fact.category, normalizedHash);
|
|
151684
|
-
if (existingMemory) {
|
|
151685
|
-
updateMemorySeenCount(db, existingMemory.id);
|
|
151686
|
-
continue;
|
|
151687
|
-
}
|
|
151688
|
-
const memoryInput = {
|
|
151689
|
-
projectPath,
|
|
151690
|
-
category: fact.category,
|
|
151691
|
-
content: fact.content,
|
|
151692
|
-
sourceSessionId: sessionId,
|
|
151693
|
-
sourceType: "historian",
|
|
151694
|
-
expiresAt: resolveExpiresAt(fact.category)
|
|
151695
|
-
};
|
|
151696
|
-
const memory = insertMemory(db, memoryInput);
|
|
151697
|
-
embedAndStoreMemory(db, sessionId, memory.id, memory.content);
|
|
151698
|
-
} catch (error48) {
|
|
151699
|
-
sessionLog(sessionId, `memory promotion failed for fact "${fact.content.slice(0, 60)}":`, error48);
|
|
151700
|
-
}
|
|
151701
|
-
}
|
|
151702
|
-
}
|
|
151703
|
-
async function embedAndStoreMemory(db, sessionId, memoryId, content) {
|
|
151704
|
-
try {
|
|
151705
|
-
const embedding = await embedText(content);
|
|
151706
|
-
if (embedding) {
|
|
151707
|
-
saveEmbedding(db, memoryId, embedding, getEmbeddingModelId());
|
|
151708
|
-
}
|
|
151709
|
-
} catch (error48) {
|
|
151710
|
-
sessionLog(sessionId, `memory embedding failed for memory ${memoryId}:`, error48);
|
|
151711
|
-
}
|
|
151712
|
-
}
|
|
151713
|
-
var init_promotion = __esm(() => {
|
|
151714
|
-
init_logger();
|
|
151715
|
-
init_constants();
|
|
151716
|
-
init_embedding();
|
|
151717
|
-
init_storage_memory();
|
|
151718
|
-
init_storage_memory_embeddings();
|
|
151719
|
-
});
|
|
151720
|
-
|
|
151721
|
-
// src/features/magic-context/memory/storage-memory-fts.ts
|
|
151722
|
-
function getSearchStatement(db) {
|
|
151723
|
-
let stmt = searchStatements.get(db);
|
|
151724
|
-
if (!stmt) {
|
|
151725
|
-
const selectColumns = Object.entries(COLUMN_MAP).map(([property, column]) => `memories.${column} AS ${property}`).join(", ");
|
|
151726
|
-
stmt = db.prepare(`SELECT ${selectColumns} FROM memories_fts INNER JOIN memories ON memories.id = memories_fts.rowid WHERE memories.project_path = ? AND memories.status IN ('active', 'permanent') AND (memories.expires_at IS NULL OR memories.expires_at > ?) AND memories_fts MATCH ? ORDER BY bm25(memories_fts), memories.updated_at DESC, memories.id ASC LIMIT ?`);
|
|
151727
|
-
searchStatements.set(db, stmt);
|
|
151728
|
-
}
|
|
151729
|
-
return stmt;
|
|
151730
|
-
}
|
|
151731
|
-
function sanitizeFtsQuery(query) {
|
|
151732
|
-
const tokens = query.split(/\s+/).filter((token) => token.length > 0);
|
|
151733
|
-
if (tokens.length === 0)
|
|
151734
|
-
return "";
|
|
151735
|
-
return tokens.map((token) => `"${token.replace(/"/g, '""')}"`).join(" ");
|
|
151736
|
-
}
|
|
151737
|
-
function searchMemoriesFTS(db, projectPath, query, limit = DEFAULT_SEARCH_LIMIT) {
|
|
151738
|
-
const trimmedQuery = query.trim();
|
|
151739
|
-
if (trimmedQuery.length === 0 || limit <= 0) {
|
|
151740
|
-
return [];
|
|
151741
|
-
}
|
|
151742
|
-
const sanitized = sanitizeFtsQuery(trimmedQuery);
|
|
151743
|
-
if (sanitized.length === 0) {
|
|
151744
|
-
return [];
|
|
151745
|
-
}
|
|
151746
|
-
const rows = getSearchStatement(db).all(projectPath, Date.now(), sanitized, limit).filter(isMemoryRow);
|
|
151747
|
-
return rows.map(toMemory);
|
|
151748
|
-
}
|
|
151749
|
-
var DEFAULT_SEARCH_LIMIT = 10, searchStatements;
|
|
151750
|
-
var init_storage_memory_fts = __esm(() => {
|
|
151751
|
-
init_storage_memory();
|
|
151752
|
-
searchStatements = new WeakMap;
|
|
151753
|
-
});
|
|
151754
|
-
// src/features/magic-context/memory/index.ts
|
|
151755
|
-
var init_memory = __esm(() => {
|
|
151756
|
-
init_project_identity();
|
|
151757
|
-
init_promotion();
|
|
151758
|
-
init_constants();
|
|
151759
|
-
init_embedding();
|
|
151760
|
-
init_embedding_backfill();
|
|
151761
|
-
init_embedding_cache();
|
|
151762
|
-
init_storage_memory();
|
|
151763
|
-
init_storage_memory_embeddings();
|
|
151764
|
-
init_storage_memory_fts();
|
|
151765
|
-
});
|
|
151766
|
-
|
|
151767
151448
|
// src/hooks/magic-context/compartment-parser.ts
|
|
151768
151449
|
function parseCompartmentOutput(text) {
|
|
151769
151450
|
const compartments = [];
|
|
@@ -151814,290 +151495,6 @@ var init_compartment_parser = __esm(() => {
|
|
|
151814
151495
|
USER_OBS_ITEM_REGEX = /^\s*\*\s*(.+)$/gm;
|
|
151815
151496
|
});
|
|
151816
151497
|
|
|
151817
|
-
// src/hooks/magic-context/compartment-runner-compressor.ts
|
|
151818
|
-
async function runCompressionPassIfNeeded(deps) {
|
|
151819
|
-
const { db, sessionId, historyBudgetTokens } = deps;
|
|
151820
|
-
const compartments = getCompartments(db, sessionId);
|
|
151821
|
-
if (compartments.length <= 1)
|
|
151822
|
-
return false;
|
|
151823
|
-
const facts = getSessionFacts(db, sessionId);
|
|
151824
|
-
let totalTokens = 0;
|
|
151825
|
-
for (const c of compartments) {
|
|
151826
|
-
totalTokens += estimateTokens(`<compartment start="${c.startMessage}" end="${c.endMessage}" title="${c.title}">
|
|
151827
|
-
${c.content}
|
|
151828
|
-
</compartment>
|
|
151829
|
-
`);
|
|
151830
|
-
}
|
|
151831
|
-
for (const f of facts) {
|
|
151832
|
-
totalTokens += estimateTokens(`* ${f.content}
|
|
151833
|
-
`);
|
|
151834
|
-
}
|
|
151835
|
-
if (totalTokens <= historyBudgetTokens) {
|
|
151836
|
-
sessionLog(sessionId, `compressor: history block ~${totalTokens} tokens within budget ${historyBudgetTokens}, skipping`);
|
|
151837
|
-
return false;
|
|
151838
|
-
}
|
|
151839
|
-
const overage = totalTokens - historyBudgetTokens;
|
|
151840
|
-
sessionLog(sessionId, `compressor: history block ~${totalTokens} tokens exceeds budget ${historyBudgetTokens} by ~${overage} tokens`);
|
|
151841
|
-
const maxDepth = getMaxCompressionDepth(db, sessionId);
|
|
151842
|
-
const scoredCompartments = compartments.map((compartment, index) => {
|
|
151843
|
-
const tokenEstimate = estimateTokens(`<compartment start="${compartment.startMessage}" end="${compartment.endMessage}" title="${compartment.title}">
|
|
151844
|
-
${compartment.content}
|
|
151845
|
-
</compartment>
|
|
151846
|
-
`);
|
|
151847
|
-
const averageDepth = getAverageCompressionDepth(db, sessionId, compartment.startMessage, compartment.endMessage);
|
|
151848
|
-
const normalizedAge = compartments.length > 1 ? 1 - index / (compartments.length - 1) : 1;
|
|
151849
|
-
const normalizedDepth = maxDepth > 0 ? 1 - averageDepth / maxDepth : 1;
|
|
151850
|
-
const score = 0.7 * normalizedAge + 0.3 * normalizedDepth;
|
|
151851
|
-
return {
|
|
151852
|
-
compartment,
|
|
151853
|
-
index,
|
|
151854
|
-
tokenEstimate,
|
|
151855
|
-
averageDepth,
|
|
151856
|
-
score
|
|
151857
|
-
};
|
|
151858
|
-
});
|
|
151859
|
-
const sortedByScore = [...scoredCompartments].sort((left, right) => right.score - left.score || left.index - right.index);
|
|
151860
|
-
const targetSelectionTokens = overage * 2;
|
|
151861
|
-
let selectedCandidateTokens = 0;
|
|
151862
|
-
const selectedCandidates = [];
|
|
151863
|
-
for (const compartment of sortedByScore) {
|
|
151864
|
-
if (selectedCandidateTokens >= targetSelectionTokens)
|
|
151865
|
-
break;
|
|
151866
|
-
selectedCandidates.push(compartment);
|
|
151867
|
-
selectedCandidateTokens += compartment.tokenEstimate;
|
|
151868
|
-
}
|
|
151869
|
-
if (selectedCandidates.length < 2) {
|
|
151870
|
-
sessionLog(sessionId, "compressor: not enough compartments to compress, skipping");
|
|
151871
|
-
return false;
|
|
151872
|
-
}
|
|
151873
|
-
const selectedStartIndex = Math.min(...selectedCandidates.map((compartment) => compartment.index));
|
|
151874
|
-
const selectedEndIndex = Math.max(...selectedCandidates.map((compartment) => compartment.index));
|
|
151875
|
-
let selectedScoredCompartments = scoredCompartments.slice(selectedStartIndex, selectedEndIndex + 1);
|
|
151876
|
-
const expandedTokens = selectedScoredCompartments.reduce((t, c) => t + c.tokenEstimate, 0);
|
|
151877
|
-
if (expandedTokens > selectedCandidateTokens * 3) {
|
|
151878
|
-
const sortedByIndex = [...selectedCandidates].sort((a, b) => a.index - b.index);
|
|
151879
|
-
let runEnd = sortedByIndex[0].index;
|
|
151880
|
-
for (let i = 1;i < sortedByIndex.length; i++) {
|
|
151881
|
-
if (sortedByIndex[i].index === runEnd + 1) {
|
|
151882
|
-
runEnd = sortedByIndex[i].index;
|
|
151883
|
-
} else {
|
|
151884
|
-
break;
|
|
151885
|
-
}
|
|
151886
|
-
}
|
|
151887
|
-
selectedScoredCompartments = scoredCompartments.slice(sortedByIndex[0].index, runEnd + 1);
|
|
151888
|
-
sessionLog(sessionId, `compressor: contiguous expansion was ${expandedTokens} tokens (>${selectedCandidateTokens * 3}), fell back to contiguous run of ${selectedScoredCompartments.length}`);
|
|
151889
|
-
}
|
|
151890
|
-
if (selectedScoredCompartments.length < 2) {
|
|
151891
|
-
sessionLog(sessionId, "compressor: not enough adjacent compartments to compress, skipping");
|
|
151892
|
-
return false;
|
|
151893
|
-
}
|
|
151894
|
-
const selectedCompartments = selectedScoredCompartments.map((scoredCompartment) => scoredCompartment.compartment);
|
|
151895
|
-
const selectedTokens = selectedScoredCompartments.reduce((total, scoredCompartment) => total + scoredCompartment.tokenEstimate, 0);
|
|
151896
|
-
const targetTokens = Math.floor(selectedTokens / 2);
|
|
151897
|
-
const minAverageDepth = Math.min(...selectedScoredCompartments.map((compartment) => compartment.averageDepth));
|
|
151898
|
-
const maxAverageDepth = Math.max(...selectedScoredCompartments.map((compartment) => compartment.averageDepth));
|
|
151899
|
-
const minScore = Math.min(...selectedScoredCompartments.map((compartment) => compartment.score));
|
|
151900
|
-
const maxScore = Math.max(...selectedScoredCompartments.map((compartment) => compartment.score));
|
|
151901
|
-
sessionLog(sessionId, `compressor: scored ${compartments.length} compartments, selected ${selectedCompartments.length} (avg_depth range: ${minAverageDepth.toFixed(1)}-${maxAverageDepth.toFixed(1)}, score range: ${minScore.toFixed(1)}-${maxScore.toFixed(1)})`);
|
|
151902
|
-
const overallAverageDepth = selectedScoredCompartments.reduce((sum, c) => sum + c.averageDepth, 0) / selectedScoredCompartments.length;
|
|
151903
|
-
const depthTier = overallAverageDepth < 2 ? "preserve U: lines" : overallAverageDepth < 3 ? "condense U: lines" : "fold U: into prose";
|
|
151904
|
-
sessionLog(sessionId, `compressor: selected contiguous range ${selectedCompartments[0].startMessage}-${selectedCompartments[selectedCompartments.length - 1].endMessage} (~${selectedTokens} tokens), target ~${targetTokens} tokens, avg_depth=${overallAverageDepth.toFixed(1)} (${depthTier})`);
|
|
151905
|
-
try {
|
|
151906
|
-
const compressed = await runCompressorPass({
|
|
151907
|
-
...deps,
|
|
151908
|
-
compartments: selectedCompartments,
|
|
151909
|
-
currentTokens: selectedTokens,
|
|
151910
|
-
targetTokens,
|
|
151911
|
-
averageDepth: overallAverageDepth
|
|
151912
|
-
});
|
|
151913
|
-
if (!compressed) {
|
|
151914
|
-
sessionLog(sessionId, "compressor: compression pass failed, keeping existing compartments");
|
|
151915
|
-
return false;
|
|
151916
|
-
}
|
|
151917
|
-
const leadingCompartments = compartments.slice(0, selectedStartIndex);
|
|
151918
|
-
const trailingCompartments = compartments.slice(selectedEndIndex + 1);
|
|
151919
|
-
const allCompartments = [
|
|
151920
|
-
...leadingCompartments.map((c, i) => ({
|
|
151921
|
-
sequence: i,
|
|
151922
|
-
startMessage: c.startMessage,
|
|
151923
|
-
endMessage: c.endMessage,
|
|
151924
|
-
startMessageId: c.startMessageId,
|
|
151925
|
-
endMessageId: c.endMessageId,
|
|
151926
|
-
title: c.title,
|
|
151927
|
-
content: c.content
|
|
151928
|
-
})),
|
|
151929
|
-
...compressed.map((c, i) => ({
|
|
151930
|
-
sequence: leadingCompartments.length + i,
|
|
151931
|
-
startMessage: c.startMessage,
|
|
151932
|
-
endMessage: c.endMessage,
|
|
151933
|
-
startMessageId: c.startMessageId,
|
|
151934
|
-
endMessageId: c.endMessageId,
|
|
151935
|
-
title: c.title,
|
|
151936
|
-
content: c.content
|
|
151937
|
-
})),
|
|
151938
|
-
...trailingCompartments.map((c, i) => ({
|
|
151939
|
-
sequence: leadingCompartments.length + compressed.length + i,
|
|
151940
|
-
startMessage: c.startMessage,
|
|
151941
|
-
endMessage: c.endMessage,
|
|
151942
|
-
startMessageId: c.startMessageId,
|
|
151943
|
-
endMessageId: c.endMessageId,
|
|
151944
|
-
title: c.title,
|
|
151945
|
-
content: c.content
|
|
151946
|
-
}))
|
|
151947
|
-
];
|
|
151948
|
-
const originalStart = selectedCompartments[0].startMessage;
|
|
151949
|
-
const originalEnd = selectedCompartments[selectedCompartments.length - 1].endMessage;
|
|
151950
|
-
const compressedStart = compressed[0].startMessage;
|
|
151951
|
-
const compressedEnd = compressed[compressed.length - 1].endMessage;
|
|
151952
|
-
if (compressedStart !== originalStart || compressedEnd !== originalEnd) {
|
|
151953
|
-
sessionLog(sessionId, `compressor: compressed range ${compressedStart}-${compressedEnd} doesn't match original ${originalStart}-${originalEnd}, aborting`);
|
|
151954
|
-
return false;
|
|
151955
|
-
}
|
|
151956
|
-
for (let i = 1;i < compressed.length; i++) {
|
|
151957
|
-
const prev = compressed[i - 1];
|
|
151958
|
-
const curr = compressed[i];
|
|
151959
|
-
if (curr.startMessage <= prev.endMessage) {
|
|
151960
|
-
sessionLog(sessionId, `compressor: overlap at compartment ${i}: prev ends ${prev.endMessage}, curr starts ${curr.startMessage}, aborting`);
|
|
151961
|
-
return false;
|
|
151962
|
-
}
|
|
151963
|
-
if (curr.startMessage > prev.endMessage + 1) {
|
|
151964
|
-
sessionLog(sessionId, `compressor: gap at compartment ${i}: prev ends ${prev.endMessage}, curr starts ${curr.startMessage}, aborting`);
|
|
151965
|
-
return false;
|
|
151966
|
-
}
|
|
151967
|
-
}
|
|
151968
|
-
replaceAllCompartmentState(db, sessionId, allCompartments, facts.map((f) => ({ category: f.category, content: f.content })));
|
|
151969
|
-
incrementCompressionDepth(db, sessionId, originalStart, originalEnd);
|
|
151970
|
-
sessionLog(sessionId, `compressor: replaced ${selectedCompartments.length} compartments with ${compressed.length} compressed compartments`);
|
|
151971
|
-
sessionLog(sessionId, `compressor: incremented compression depth for messages ${originalStart}-${originalEnd}`);
|
|
151972
|
-
return true;
|
|
151973
|
-
} catch (error48) {
|
|
151974
|
-
sessionLog(sessionId, "compressor: unexpected error:", getErrorMessage(error48));
|
|
151975
|
-
return false;
|
|
151976
|
-
}
|
|
151977
|
-
}
|
|
151978
|
-
async function runCompressorPass(args) {
|
|
151979
|
-
const {
|
|
151980
|
-
client,
|
|
151981
|
-
sessionId,
|
|
151982
|
-
directory,
|
|
151983
|
-
compartments,
|
|
151984
|
-
currentTokens,
|
|
151985
|
-
targetTokens,
|
|
151986
|
-
averageDepth,
|
|
151987
|
-
historianTimeoutMs
|
|
151988
|
-
} = args;
|
|
151989
|
-
const prompt = buildCompressorPrompt(compartments, currentTokens, targetTokens, averageDepth);
|
|
151990
|
-
let agentSessionId = null;
|
|
151991
|
-
try {
|
|
151992
|
-
const createResponse = await client.session.create({
|
|
151993
|
-
body: {
|
|
151994
|
-
parentID: sessionId,
|
|
151995
|
-
title: "magic-context-compressor"
|
|
151996
|
-
},
|
|
151997
|
-
query: { directory }
|
|
151998
|
-
});
|
|
151999
|
-
const createdSession = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
|
|
152000
|
-
agentSessionId = typeof createdSession?.id === "string" ? createdSession.id : null;
|
|
152001
|
-
if (!agentSessionId) {
|
|
152002
|
-
sessionLog(sessionId, "compressor: could not create child session");
|
|
152003
|
-
return null;
|
|
152004
|
-
}
|
|
152005
|
-
await promptSyncWithModelSuggestionRetry(client, {
|
|
152006
|
-
path: { id: agentSessionId },
|
|
152007
|
-
query: { directory },
|
|
152008
|
-
body: {
|
|
152009
|
-
agent: HISTORIAN_AGENT2,
|
|
152010
|
-
parts: [{ type: "text", text: prompt }]
|
|
152011
|
-
}
|
|
152012
|
-
}, { timeoutMs: historianTimeoutMs ?? DEFAULT_HISTORIAN_TIMEOUT_MS });
|
|
152013
|
-
const messagesResponse = await client.session.messages({
|
|
152014
|
-
path: { id: agentSessionId },
|
|
152015
|
-
query: { directory }
|
|
152016
|
-
});
|
|
152017
|
-
const messages = normalizeSDKResponse(messagesResponse, [], {
|
|
152018
|
-
preferResponseOnMissingData: true
|
|
152019
|
-
});
|
|
152020
|
-
const result = extractLatestAssistantText(messages);
|
|
152021
|
-
if (!result) {
|
|
152022
|
-
sessionLog(sessionId, "compressor: historian returned no output");
|
|
152023
|
-
return null;
|
|
152024
|
-
}
|
|
152025
|
-
const parsed = parseCompartmentOutput(result);
|
|
152026
|
-
if (parsed.compartments.length === 0) {
|
|
152027
|
-
sessionLog(sessionId, "compressor: historian returned no compartments");
|
|
152028
|
-
return null;
|
|
152029
|
-
}
|
|
152030
|
-
const messageIdMap = new Map;
|
|
152031
|
-
for (const c of compartments) {
|
|
152032
|
-
messageIdMap.set(c.startMessage, c.startMessageId);
|
|
152033
|
-
messageIdMap.set(c.endMessage, c.endMessageId);
|
|
152034
|
-
}
|
|
152035
|
-
const mapped = parsed.compartments.map((pc) => {
|
|
152036
|
-
const startId = messageIdMap.get(pc.startMessage) ?? "";
|
|
152037
|
-
const endId = messageIdMap.get(pc.endMessage) ?? "";
|
|
152038
|
-
if (!startId || !endId) {
|
|
152039
|
-
sessionLog(sessionId, `compressor: messageId miss for ordinals ${pc.startMessage}\u2192${pc.endMessage} (startId=${startId || "MISSING"}, endId=${endId || "MISSING"})`);
|
|
152040
|
-
}
|
|
152041
|
-
return {
|
|
152042
|
-
startMessage: pc.startMessage,
|
|
152043
|
-
endMessage: pc.endMessage,
|
|
152044
|
-
startMessageId: startId,
|
|
152045
|
-
endMessageId: endId,
|
|
152046
|
-
title: pc.title,
|
|
152047
|
-
content: pc.content
|
|
152048
|
-
};
|
|
152049
|
-
});
|
|
152050
|
-
const hasEmptyIds = mapped.some((c) => !c.startMessageId || !c.endMessageId);
|
|
152051
|
-
if (hasEmptyIds) {
|
|
152052
|
-
sessionLog(sessionId, "compressor: rejecting \u2014 one or more compartments have empty messageIds");
|
|
152053
|
-
return null;
|
|
152054
|
-
}
|
|
152055
|
-
return mapped;
|
|
152056
|
-
} catch (error48) {
|
|
152057
|
-
sessionLog(sessionId, "compressor: historian call failed:", getErrorMessage(error48));
|
|
152058
|
-
return null;
|
|
152059
|
-
} finally {
|
|
152060
|
-
if (agentSessionId) {
|
|
152061
|
-
await client.session.delete({ path: { id: agentSessionId }, query: { directory } }).catch((e) => {
|
|
152062
|
-
sessionLog(sessionId, "compressor: session cleanup failed:", getErrorMessage(e));
|
|
152063
|
-
});
|
|
152064
|
-
}
|
|
152065
|
-
}
|
|
152066
|
-
}
|
|
152067
|
-
var HISTORIAN_AGENT2 = "historian";
|
|
152068
|
-
var init_compartment_runner_compressor = __esm(() => {
|
|
152069
|
-
init_magic_context();
|
|
152070
|
-
init_storage();
|
|
152071
|
-
init_shared();
|
|
152072
|
-
init_assistant_message_extractor();
|
|
152073
|
-
init_logger();
|
|
152074
|
-
init_compartment_parser();
|
|
152075
|
-
init_compartment_prompt();
|
|
152076
|
-
init_read_session_formatting();
|
|
152077
|
-
});
|
|
152078
|
-
|
|
152079
|
-
// src/hooks/magic-context/compartment-runner-drop-queue.ts
|
|
152080
|
-
function queueDropsForCompartmentalizedMessages(db, sessionId, upToMessageIndex) {
|
|
152081
|
-
const tags = getTagsBySession(db, sessionId);
|
|
152082
|
-
const rawTagKeys = new Set(getRawSessionTagKeysThrough(sessionId, upToMessageIndex));
|
|
152083
|
-
let dropsQueued = 0;
|
|
152084
|
-
for (const tag of tags) {
|
|
152085
|
-
if (tag.status !== "active")
|
|
152086
|
-
continue;
|
|
152087
|
-
if (rawTagKeys.has(tag.messageId)) {
|
|
152088
|
-
queuePendingOp(db, sessionId, tag.tagNumber, "drop");
|
|
152089
|
-
dropsQueued += 1;
|
|
152090
|
-
}
|
|
152091
|
-
}
|
|
152092
|
-
sessionLog(sessionId, `compartment agent: queued ${dropsQueued} drops for messages 0-${upToMessageIndex}`);
|
|
152093
|
-
}
|
|
152094
|
-
var init_compartment_runner_drop_queue = __esm(() => {
|
|
152095
|
-
init_storage_ops();
|
|
152096
|
-
init_storage_tags();
|
|
152097
|
-
init_logger();
|
|
152098
|
-
init_read_session_chunk();
|
|
152099
|
-
});
|
|
152100
|
-
|
|
152101
151498
|
// src/hooks/magic-context/compartment-runner-mapping.ts
|
|
152102
151499
|
function mapParsedCompartmentsToChunk(compartments, chunk, sequenceOffset) {
|
|
152103
151500
|
const mapped = [];
|
|
@@ -152127,15 +151524,19 @@ var init_compartment_runner_mapping = __esm(() => {
|
|
|
152127
151524
|
});
|
|
152128
151525
|
|
|
152129
151526
|
// src/hooks/magic-context/compartment-runner-validation.ts
|
|
152130
|
-
function healCompartmentGaps(compartments) {
|
|
152131
|
-
const
|
|
151527
|
+
function healCompartmentGaps(compartments, toolOnlyRanges = []) {
|
|
151528
|
+
const SAFETY_HEAL_GAP = 15;
|
|
152132
151529
|
for (let i = 1;i < compartments.length; i++) {
|
|
152133
151530
|
const prev = compartments[i - 1];
|
|
152134
151531
|
const curr = compartments[i];
|
|
152135
|
-
const
|
|
152136
|
-
const
|
|
152137
|
-
|
|
152138
|
-
|
|
151532
|
+
const gapStart = prev.endMessage + 1;
|
|
151533
|
+
const gapEnd = curr.startMessage - 1;
|
|
151534
|
+
const gapSize = gapEnd - gapStart + 1;
|
|
151535
|
+
if (gapSize <= 0)
|
|
151536
|
+
continue;
|
|
151537
|
+
const fullyInsideToolOnly = toolOnlyRanges.some((range) => range.start <= gapStart && range.end >= gapEnd);
|
|
151538
|
+
if (fullyInsideToolOnly || gapSize <= SAFETY_HEAL_GAP) {
|
|
151539
|
+
prev.endMessage = gapEnd;
|
|
152139
151540
|
}
|
|
152140
151541
|
}
|
|
152141
151542
|
}
|
|
@@ -152147,7 +151548,7 @@ function validateHistorianOutput(text, _sessionId, chunk, _priorCompartments, se
|
|
|
152147
151548
|
error: "Historian returned no usable compartments."
|
|
152148
151549
|
};
|
|
152149
151550
|
}
|
|
152150
|
-
healCompartmentGaps(parsed.compartments);
|
|
151551
|
+
healCompartmentGaps(parsed.compartments, chunk.toolOnlyRanges);
|
|
152151
151552
|
const mapped = mapParsedCompartmentsToChunk(parsed.compartments, chunk, sequenceOffset);
|
|
152152
151553
|
if (!mapped.ok) {
|
|
152153
151554
|
return {
|
|
@@ -152266,7 +151667,7 @@ var init_compartment_runner_validation = __esm(() => {
|
|
|
152266
151667
|
// src/hooks/magic-context/compartment-runner-historian.ts
|
|
152267
151668
|
import { mkdirSync as mkdirSync3, unlinkSync, writeFileSync } from "fs";
|
|
152268
151669
|
import { tmpdir as tmpdir2 } from "os";
|
|
152269
|
-
import { join as
|
|
151670
|
+
import { join as join12 } from "path";
|
|
152270
151671
|
async function runValidatedHistorianPass(args) {
|
|
152271
151672
|
const firstRun = await runHistorianPrompt({
|
|
152272
151673
|
...args,
|
|
@@ -152282,8 +151683,14 @@ async function runValidatedHistorianPass(args) {
|
|
|
152282
151683
|
}
|
|
152283
151684
|
const firstValidation = validateHistorianOutput(firstRun.result, args.parentSessionId, args.chunk, args.priorCompartments, args.sequenceOffset);
|
|
152284
151685
|
if (firstValidation.ok) {
|
|
151686
|
+
const finalResult = args.twoPass ? await runEditorPassOrFallback({
|
|
151687
|
+
...args,
|
|
151688
|
+
draftXml: firstRun.result,
|
|
151689
|
+
draftValidation: firstValidation,
|
|
151690
|
+
draftDumpPath: firstRun.dumpPath
|
|
151691
|
+
}) : firstValidation;
|
|
152285
151692
|
cleanupHistorianDump(args.parentSessionId, firstRun.dumpPath);
|
|
152286
|
-
return
|
|
151693
|
+
return finalResult;
|
|
152287
151694
|
}
|
|
152288
151695
|
await args.callbacks?.onRepairRetry?.(firstValidation.error ?? "invalid compartment output");
|
|
152289
151696
|
const repairPrompt = buildHistorianRepairPrompt(args.prompt, firstRun.result, firstValidation.error ?? "invalid compartment output");
|
|
@@ -152302,8 +151709,14 @@ async function runValidatedHistorianPass(args) {
|
|
|
152302
151709
|
}
|
|
152303
151710
|
const repairValidation = validateHistorianOutput(repairRun.result, args.parentSessionId, args.chunk, args.priorCompartments, args.sequenceOffset);
|
|
152304
151711
|
if (repairValidation.ok) {
|
|
151712
|
+
const finalResult = args.twoPass ? await runEditorPassOrFallback({
|
|
151713
|
+
...args,
|
|
151714
|
+
draftXml: repairRun.result,
|
|
151715
|
+
draftValidation: repairValidation,
|
|
151716
|
+
draftDumpPath: repairRun.dumpPath
|
|
151717
|
+
}) : repairValidation;
|
|
152305
151718
|
cleanupHistorianDump(args.parentSessionId, repairRun.dumpPath);
|
|
152306
|
-
return
|
|
151719
|
+
return finalResult;
|
|
152307
151720
|
}
|
|
152308
151721
|
return runFallbackHistorianPass({
|
|
152309
151722
|
...args,
|
|
@@ -152312,6 +151725,32 @@ async function runValidatedHistorianPass(args) {
|
|
|
152312
151725
|
dumpPaths: [firstRun.dumpPath, repairRun.dumpPath]
|
|
152313
151726
|
});
|
|
152314
151727
|
}
|
|
151728
|
+
async function runEditorPassOrFallback(args) {
|
|
151729
|
+
sessionLog(args.parentSessionId, "historian two-pass: running editor on draft");
|
|
151730
|
+
const editorRun = await runHistorianPrompt({
|
|
151731
|
+
client: args.client,
|
|
151732
|
+
parentSessionId: args.parentSessionId,
|
|
151733
|
+
sessionDirectory: args.sessionDirectory,
|
|
151734
|
+
prompt: buildHistorianEditorPrompt(args.draftXml),
|
|
151735
|
+
timeoutMs: args.timeoutMs,
|
|
151736
|
+
dumpLabel: `${args.dumpLabelBase}-editor`,
|
|
151737
|
+
agentId: HISTORIAN_EDITOR_AGENT
|
|
151738
|
+
});
|
|
151739
|
+
if (!editorRun.ok || !editorRun.result) {
|
|
151740
|
+
sessionLog(args.parentSessionId, "historian two-pass: editor call failed", {
|
|
151741
|
+
error: editorRun.error
|
|
151742
|
+
});
|
|
151743
|
+
return args.draftValidation;
|
|
151744
|
+
}
|
|
151745
|
+
const editorValidation = validateHistorianOutput(editorRun.result, args.parentSessionId, args.chunk, args.priorCompartments, args.sequenceOffset);
|
|
151746
|
+
if (!editorValidation.ok) {
|
|
151747
|
+
sessionLog(args.parentSessionId, "historian two-pass: editor validation failed, falling back to draft", { error: editorValidation.error });
|
|
151748
|
+
return args.draftValidation;
|
|
151749
|
+
}
|
|
151750
|
+
cleanupHistorianDump(args.parentSessionId, editorRun.dumpPath);
|
|
151751
|
+
sessionLog(args.parentSessionId, "historian two-pass: editor accepted");
|
|
151752
|
+
return editorValidation;
|
|
151753
|
+
}
|
|
152315
151754
|
async function runHistorianPrompt(args) {
|
|
152316
151755
|
const {
|
|
152317
151756
|
client,
|
|
@@ -152320,11 +151759,12 @@ async function runHistorianPrompt(args) {
|
|
|
152320
151759
|
prompt,
|
|
152321
151760
|
timeoutMs,
|
|
152322
151761
|
dumpLabel,
|
|
152323
|
-
modelOverride
|
|
151762
|
+
modelOverride,
|
|
151763
|
+
agentId = HISTORIAN_AGENT
|
|
152324
151764
|
} = args;
|
|
152325
151765
|
let agentSessionId = null;
|
|
152326
151766
|
try {
|
|
152327
|
-
sessionLog(parentSessionId, `historian: creating child session (model=${modelOverride ? `${modelOverride.providerID}/${modelOverride.modelID}` : `agent:${
|
|
151767
|
+
sessionLog(parentSessionId, `historian: creating child session (agent=${agentId}, model=${modelOverride ? `${modelOverride.providerID}/${modelOverride.modelID}` : `agent:${agentId}`})`);
|
|
152328
151768
|
const createResponse = await client.session.create({
|
|
152329
151769
|
body: {
|
|
152330
151770
|
parentID: parentSessionId,
|
|
@@ -152343,7 +151783,7 @@ async function runHistorianPrompt(args) {
|
|
|
152343
151783
|
path: { id: agentSessionId },
|
|
152344
151784
|
query: { directory: sessionDirectory },
|
|
152345
151785
|
body: {
|
|
152346
|
-
agent:
|
|
151786
|
+
agent: agentId,
|
|
152347
151787
|
...modelOverride ? { model: modelOverride } : {},
|
|
152348
151788
|
parts: [{ type: "text", text: prompt }]
|
|
152349
151789
|
}
|
|
@@ -152451,8 +151891,8 @@ function isTransientHistorianPromptError(message) {
|
|
|
152451
151891
|
].some((token) => normalized.includes(token));
|
|
152452
151892
|
}
|
|
152453
151893
|
function sleep(ms) {
|
|
152454
|
-
return new Promise((
|
|
152455
|
-
setTimeout(
|
|
151894
|
+
return new Promise((resolve3) => {
|
|
151895
|
+
setTimeout(resolve3, ms);
|
|
152456
151896
|
});
|
|
152457
151897
|
}
|
|
152458
151898
|
function cleanupHistorianDump(sessionId, dumpPath) {
|
|
@@ -152472,7 +151912,7 @@ function dumpHistorianResponse(sessionId, label, text) {
|
|
|
152472
151912
|
mkdirSync3(HISTORIAN_RESPONSE_DUMP_DIR, { recursive: true });
|
|
152473
151913
|
const safeSessionId = sanitizeDumpName(sessionId);
|
|
152474
151914
|
const safeLabel = sanitizeDumpName(label);
|
|
152475
|
-
const dumpPath =
|
|
151915
|
+
const dumpPath = join12(HISTORIAN_RESPONSE_DUMP_DIR, `${safeSessionId}-${safeLabel}-${Date.now()}.xml`);
|
|
152476
151916
|
writeFileSync(dumpPath, text, "utf8");
|
|
152477
151917
|
sessionLog(sessionId, "compartment agent: historian response dumped", {
|
|
152478
151918
|
label,
|
|
@@ -152495,8 +151935,9 @@ var init_compartment_runner_historian = __esm(() => {
|
|
|
152495
151935
|
init_magic_context();
|
|
152496
151936
|
init_shared();
|
|
152497
151937
|
init_assistant_message_extractor();
|
|
151938
|
+
init_compartment_prompt();
|
|
152498
151939
|
init_compartment_runner_validation();
|
|
152499
|
-
HISTORIAN_RESPONSE_DUMP_DIR =
|
|
151940
|
+
HISTORIAN_RESPONSE_DUMP_DIR = join12(tmpdir2(), "magic-context-historian");
|
|
152500
151941
|
});
|
|
152501
151942
|
|
|
152502
151943
|
// src/hooks/magic-context/compartment-runner-state-xml.ts
|
|
@@ -152537,6 +151978,131 @@ var init_compartment_runner_state_xml = __esm(() => {
|
|
|
152537
151978
|
init_compartment_storage();
|
|
152538
151979
|
});
|
|
152539
151980
|
|
|
151981
|
+
// src/features/magic-context/memory/constants.ts
|
|
151982
|
+
var PROMOTABLE_CATEGORIES, CATEGORY_PRIORITY, CATEGORY_DEFAULT_TTL;
|
|
151983
|
+
var init_constants = __esm(() => {
|
|
151984
|
+
PROMOTABLE_CATEGORIES = [
|
|
151985
|
+
"ARCHITECTURE_DECISIONS",
|
|
151986
|
+
"CONSTRAINTS",
|
|
151987
|
+
"CONFIG_DEFAULTS",
|
|
151988
|
+
"NAMING",
|
|
151989
|
+
"USER_PREFERENCES",
|
|
151990
|
+
"USER_DIRECTIVES",
|
|
151991
|
+
"ENVIRONMENT",
|
|
151992
|
+
"WORKFLOW_RULES",
|
|
151993
|
+
"KNOWN_ISSUES"
|
|
151994
|
+
];
|
|
151995
|
+
CATEGORY_PRIORITY = [
|
|
151996
|
+
"USER_DIRECTIVES",
|
|
151997
|
+
"USER_PREFERENCES",
|
|
151998
|
+
"NAMING",
|
|
151999
|
+
"CONFIG_DEFAULTS",
|
|
152000
|
+
"CONSTRAINTS",
|
|
152001
|
+
"ARCHITECTURE_DECISIONS",
|
|
152002
|
+
"ENVIRONMENT",
|
|
152003
|
+
"WORKFLOW_RULES",
|
|
152004
|
+
"KNOWN_ISSUES"
|
|
152005
|
+
];
|
|
152006
|
+
CATEGORY_DEFAULT_TTL = {
|
|
152007
|
+
WORKFLOW_RULES: 90 * 24 * 60 * 60 * 1000,
|
|
152008
|
+
KNOWN_ISSUES: 30 * 24 * 60 * 60 * 1000
|
|
152009
|
+
};
|
|
152010
|
+
});
|
|
152011
|
+
|
|
152012
|
+
// src/hooks/magic-context/temporal-awareness.ts
|
|
152013
|
+
function formatGap(seconds) {
|
|
152014
|
+
if (!Number.isFinite(seconds) || seconds < TEMPORAL_AWARENESS_THRESHOLD_SECONDS) {
|
|
152015
|
+
return null;
|
|
152016
|
+
}
|
|
152017
|
+
if (seconds < SECONDS_PER_HOUR) {
|
|
152018
|
+
const minutes = Math.floor(seconds / SECONDS_PER_MINUTE);
|
|
152019
|
+
return `+${minutes}m`;
|
|
152020
|
+
}
|
|
152021
|
+
if (seconds < SECONDS_PER_DAY) {
|
|
152022
|
+
const hours = Math.floor(seconds / SECONDS_PER_HOUR);
|
|
152023
|
+
const minutes = Math.floor((seconds - hours * SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
|
|
152024
|
+
return minutes === 0 ? `+${hours}h` : `+${hours}h ${minutes}m`;
|
|
152025
|
+
}
|
|
152026
|
+
if (seconds < SECONDS_PER_WEEK) {
|
|
152027
|
+
const days2 = Math.floor(seconds / SECONDS_PER_DAY);
|
|
152028
|
+
const hours = Math.floor((seconds - days2 * SECONDS_PER_DAY) / SECONDS_PER_HOUR);
|
|
152029
|
+
return hours === 0 ? `+${days2}d` : `+${days2}d ${hours}h`;
|
|
152030
|
+
}
|
|
152031
|
+
const weeks = Math.floor(seconds / SECONDS_PER_WEEK);
|
|
152032
|
+
const days = Math.floor((seconds - weeks * SECONDS_PER_WEEK) / SECONDS_PER_DAY);
|
|
152033
|
+
return days === 0 ? `+${weeks}w` : `+${weeks}w ${days}d`;
|
|
152034
|
+
}
|
|
152035
|
+
function formatDate(ms) {
|
|
152036
|
+
const d = new Date(ms);
|
|
152037
|
+
const yyyy = d.getFullYear().toString().padStart(4, "0");
|
|
152038
|
+
const mm = (d.getMonth() + 1).toString().padStart(2, "0");
|
|
152039
|
+
const dd = d.getDate().toString().padStart(2, "0");
|
|
152040
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
152041
|
+
}
|
|
152042
|
+
function temporalMarkerPrefix(seconds) {
|
|
152043
|
+
const marker = formatGap(seconds);
|
|
152044
|
+
if (!marker)
|
|
152045
|
+
return null;
|
|
152046
|
+
return `<!-- ${marker} -->
|
|
152047
|
+
`;
|
|
152048
|
+
}
|
|
152049
|
+
function isMutableTextPart(part) {
|
|
152050
|
+
if (part === null || typeof part !== "object")
|
|
152051
|
+
return false;
|
|
152052
|
+
const p = part;
|
|
152053
|
+
return p.type === "text" && typeof p.text === "string";
|
|
152054
|
+
}
|
|
152055
|
+
function findFirstVisibleTextPart(parts) {
|
|
152056
|
+
for (const p of parts) {
|
|
152057
|
+
if (!isMutableTextPart(p))
|
|
152058
|
+
continue;
|
|
152059
|
+
if (p.ignored === true)
|
|
152060
|
+
continue;
|
|
152061
|
+
return p;
|
|
152062
|
+
}
|
|
152063
|
+
return null;
|
|
152064
|
+
}
|
|
152065
|
+
function injectTemporalMarkers(messages) {
|
|
152066
|
+
let injected = 0;
|
|
152067
|
+
let prev = null;
|
|
152068
|
+
for (const raw of messages) {
|
|
152069
|
+
if (!raw || typeof raw !== "object")
|
|
152070
|
+
continue;
|
|
152071
|
+
const msg = raw;
|
|
152072
|
+
const role = msg.info?.role;
|
|
152073
|
+
if (prev !== null && role === "user") {
|
|
152074
|
+
const prevTime = prev.info?.time;
|
|
152075
|
+
const currTime = msg.info?.time;
|
|
152076
|
+
if (prevTime?.created !== undefined && currTime?.created !== undefined) {
|
|
152077
|
+
const prevEnd = prevTime.completed ?? prevTime.created;
|
|
152078
|
+
const gapSec = (currTime.created - prevEnd) / 1000;
|
|
152079
|
+
const prefix = temporalMarkerPrefix(gapSec);
|
|
152080
|
+
if (prefix && Array.isArray(msg.parts)) {
|
|
152081
|
+
const target = findFirstVisibleTextPart(msg.parts);
|
|
152082
|
+
if (target && typeof target.text === "string") {
|
|
152083
|
+
const tagMatch = target.text.match(/^(?:\u00A7\d+\u00A7\s*)+/);
|
|
152084
|
+
const tagPrefix = tagMatch ? tagMatch[0] : "";
|
|
152085
|
+
const body = target.text.slice(tagPrefix.length);
|
|
152086
|
+
if (!TEMPORAL_MARKER_PATTERN.test(body)) {
|
|
152087
|
+
target.text = tagPrefix + prefix + body;
|
|
152088
|
+
injected++;
|
|
152089
|
+
}
|
|
152090
|
+
}
|
|
152091
|
+
}
|
|
152092
|
+
}
|
|
152093
|
+
}
|
|
152094
|
+
prev = msg;
|
|
152095
|
+
}
|
|
152096
|
+
return injected;
|
|
152097
|
+
}
|
|
152098
|
+
var TEMPORAL_AWARENESS_THRESHOLD_SECONDS = 300, SECONDS_PER_MINUTE = 60, SECONDS_PER_HOUR, SECONDS_PER_DAY, SECONDS_PER_WEEK, TEMPORAL_MARKER_PATTERN;
|
|
152099
|
+
var init_temporal_awareness = __esm(() => {
|
|
152100
|
+
SECONDS_PER_HOUR = 60 * 60;
|
|
152101
|
+
SECONDS_PER_DAY = 24 * 60 * 60;
|
|
152102
|
+
SECONDS_PER_WEEK = 7 * 24 * 60 * 60;
|
|
152103
|
+
TEMPORAL_MARKER_PATTERN = /^<!-- \+[\d]+[mhdw](?: [\d]+[mhdw])? -->\n/;
|
|
152104
|
+
});
|
|
152105
|
+
|
|
152540
152106
|
// src/hooks/magic-context/inject-compartments.ts
|
|
152541
152107
|
function clearInjectionCache(sessionId) {
|
|
152542
152108
|
injectionCache.delete(sessionId);
|
|
@@ -152606,7 +152172,7 @@ function trimMemoriesToBudget(sessionId, memories, budgetTokens) {
|
|
|
152606
152172
|
}
|
|
152607
152173
|
return result;
|
|
152608
152174
|
}
|
|
152609
|
-
function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, injectionBudgetTokens) {
|
|
152175
|
+
function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, injectionBudgetTokens, temporalAwareness) {
|
|
152610
152176
|
const cached2 = injectionCache.get(sessionId);
|
|
152611
152177
|
if (!isCacheBusting && cached2) {
|
|
152612
152178
|
if (cached2.compartmentEndMessageId.length > 0) {
|
|
@@ -152652,7 +152218,28 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
|
|
|
152652
152218
|
injectionCache.delete(sessionId);
|
|
152653
152219
|
return null;
|
|
152654
152220
|
}
|
|
152655
|
-
|
|
152221
|
+
let dateRanges;
|
|
152222
|
+
if (temporalAwareness && compartments.length > 0) {
|
|
152223
|
+
const ids = new Set;
|
|
152224
|
+
for (const c of compartments) {
|
|
152225
|
+
if (c.startMessageId)
|
|
152226
|
+
ids.add(c.startMessageId);
|
|
152227
|
+
if (c.endMessageId)
|
|
152228
|
+
ids.add(c.endMessageId);
|
|
152229
|
+
}
|
|
152230
|
+
const times = getMessageTimesFromOpenCodeDb(sessionId, Array.from(ids));
|
|
152231
|
+
const byId = new Map;
|
|
152232
|
+
for (const c of compartments) {
|
|
152233
|
+
const startMs = times.get(c.startMessageId);
|
|
152234
|
+
const endMs = times.get(c.endMessageId);
|
|
152235
|
+
if (startMs !== undefined && endMs !== undefined) {
|
|
152236
|
+
byId.set(c.id, { start: formatDate(startMs), end: formatDate(endMs) });
|
|
152237
|
+
}
|
|
152238
|
+
}
|
|
152239
|
+
if (byId.size > 0)
|
|
152240
|
+
dateRanges = { byId };
|
|
152241
|
+
}
|
|
152242
|
+
const block = buildCompartmentBlock(compartments, facts, memoryBlock, dateRanges);
|
|
152656
152243
|
if (compartments.length === 0) {
|
|
152657
152244
|
const result2 = {
|
|
152658
152245
|
block,
|
|
@@ -152754,11 +152341,1523 @@ var init_inject_compartments = __esm(() => {
|
|
|
152754
152341
|
init_constants();
|
|
152755
152342
|
init_storage_memory();
|
|
152756
152343
|
init_logger();
|
|
152344
|
+
init_read_session_db();
|
|
152757
152345
|
init_read_session_formatting();
|
|
152346
|
+
init_temporal_awareness();
|
|
152758
152347
|
injectionCache = new Map;
|
|
152759
152348
|
CONSTRAINT_KEYWORDS = /\b(must|never|always|cannot|should not|must not)\b/i;
|
|
152760
152349
|
});
|
|
152761
152350
|
|
|
152351
|
+
// src/hooks/magic-context/send-session-notification.ts
|
|
152352
|
+
var exports_send_session_notification = {};
|
|
152353
|
+
__export(exports_send_session_notification, {
|
|
152354
|
+
sendUserPrompt: () => sendUserPrompt,
|
|
152355
|
+
sendIgnoredMessage: () => sendIgnoredMessage
|
|
152356
|
+
});
|
|
152357
|
+
function hasNotificationSessionClient(client) {
|
|
152358
|
+
if (client === null || typeof client !== "object")
|
|
152359
|
+
return false;
|
|
152360
|
+
const candidate = client;
|
|
152361
|
+
if (candidate.session === undefined)
|
|
152362
|
+
return true;
|
|
152363
|
+
if (candidate.session === null || typeof candidate.session !== "object")
|
|
152364
|
+
return false;
|
|
152365
|
+
const session = candidate.session;
|
|
152366
|
+
return (session.prompt === undefined || typeof session.prompt === "function") && (session.promptAsync === undefined || typeof session.promptAsync === "function");
|
|
152367
|
+
}
|
|
152368
|
+
function inferToastVariant(text) {
|
|
152369
|
+
const lower = text.toLowerCase();
|
|
152370
|
+
if (lower.includes("error") || lower.includes("failed") || lower.includes("alert"))
|
|
152371
|
+
return "error";
|
|
152372
|
+
if (lower.includes("warning") || lower.includes("\u26A0"))
|
|
152373
|
+
return "warning";
|
|
152374
|
+
if (lower.includes("complete") || lower.includes("success") || lower.includes("\u2713") || lower.includes("finished"))
|
|
152375
|
+
return "success";
|
|
152376
|
+
return "info";
|
|
152377
|
+
}
|
|
152378
|
+
function extractToastTitle(text) {
|
|
152379
|
+
const headingMatch = text.match(/^#+\s+(.+)/m);
|
|
152380
|
+
if (headingMatch)
|
|
152381
|
+
return headingMatch[1].trim();
|
|
152382
|
+
const firstLine = text.split(`
|
|
152383
|
+
`)[0].trim();
|
|
152384
|
+
if (firstLine.length <= 80)
|
|
152385
|
+
return firstLine;
|
|
152386
|
+
return "Magic Context";
|
|
152387
|
+
}
|
|
152388
|
+
async function sendIgnoredMessage(client, sessionId, text, params) {
|
|
152389
|
+
const { isTuiConnected: checkTui } = await Promise.resolve().then(() => (init_rpc_notifications(), exports_rpc_notifications));
|
|
152390
|
+
if (checkTui()) {
|
|
152391
|
+
try {
|
|
152392
|
+
const c2 = client;
|
|
152393
|
+
const tui = c2?.tui;
|
|
152394
|
+
if (typeof tui?.showToast === "function") {
|
|
152395
|
+
const tuiClient = tui;
|
|
152396
|
+
await tuiClient.showToast({
|
|
152397
|
+
body: {
|
|
152398
|
+
title: extractToastTitle(text),
|
|
152399
|
+
message: text.length > 200 ? `${text.slice(0, 200)}\u2026` : text,
|
|
152400
|
+
variant: inferToastVariant(text),
|
|
152401
|
+
duration: 5000
|
|
152402
|
+
}
|
|
152403
|
+
}).catch(() => {});
|
|
152404
|
+
return;
|
|
152405
|
+
}
|
|
152406
|
+
} catch {}
|
|
152407
|
+
}
|
|
152408
|
+
const agent = params.agent || undefined;
|
|
152409
|
+
const variant = params.variant || undefined;
|
|
152410
|
+
const model = params.providerId && params.modelId ? {
|
|
152411
|
+
providerID: params.providerId,
|
|
152412
|
+
modelID: params.modelId
|
|
152413
|
+
} : undefined;
|
|
152414
|
+
if (!hasNotificationSessionClient(client)) {
|
|
152415
|
+
sessionLog(sessionId, "session prompt API unavailable for notification");
|
|
152416
|
+
return;
|
|
152417
|
+
}
|
|
152418
|
+
const c = client;
|
|
152419
|
+
const input = {
|
|
152420
|
+
path: { id: sessionId },
|
|
152421
|
+
body: {
|
|
152422
|
+
noReply: true,
|
|
152423
|
+
agent,
|
|
152424
|
+
model,
|
|
152425
|
+
variant,
|
|
152426
|
+
parts: [
|
|
152427
|
+
{
|
|
152428
|
+
type: "text",
|
|
152429
|
+
text,
|
|
152430
|
+
ignored: true
|
|
152431
|
+
}
|
|
152432
|
+
]
|
|
152433
|
+
}
|
|
152434
|
+
};
|
|
152435
|
+
try {
|
|
152436
|
+
if (typeof c.session?.prompt === "function") {
|
|
152437
|
+
await Promise.resolve(c.session.prompt(input));
|
|
152438
|
+
} else if (typeof c.session?.promptAsync === "function") {
|
|
152439
|
+
await c.session.promptAsync(input);
|
|
152440
|
+
} else {
|
|
152441
|
+
sessionLog(sessionId, "session prompt API unavailable for notification");
|
|
152442
|
+
}
|
|
152443
|
+
} catch (error48) {
|
|
152444
|
+
const msg = getErrorMessage(error48);
|
|
152445
|
+
sessionLog(sessionId, "failed to send notification:", msg);
|
|
152446
|
+
}
|
|
152447
|
+
}
|
|
152448
|
+
async function sendUserPrompt(client, sessionId, text) {
|
|
152449
|
+
if (!hasNotificationSessionClient(client)) {
|
|
152450
|
+
sessionLog(sessionId, "session prompt API unavailable for user prompt");
|
|
152451
|
+
return;
|
|
152452
|
+
}
|
|
152453
|
+
const c = client;
|
|
152454
|
+
const input = {
|
|
152455
|
+
path: { id: sessionId },
|
|
152456
|
+
body: {
|
|
152457
|
+
parts: [{ type: "text", text }]
|
|
152458
|
+
}
|
|
152459
|
+
};
|
|
152460
|
+
try {
|
|
152461
|
+
if (typeof c.session?.promptAsync === "function") {
|
|
152462
|
+
await c.session.promptAsync(input);
|
|
152463
|
+
} else if (typeof c.session?.prompt === "function") {
|
|
152464
|
+
await Promise.resolve(c.session.prompt(input));
|
|
152465
|
+
} else {
|
|
152466
|
+
sessionLog(sessionId, "session prompt API unavailable for user prompt");
|
|
152467
|
+
}
|
|
152468
|
+
} catch (error48) {
|
|
152469
|
+
const msg = getErrorMessage(error48);
|
|
152470
|
+
sessionLog(sessionId, "failed to send user prompt:", msg);
|
|
152471
|
+
}
|
|
152472
|
+
}
|
|
152473
|
+
var init_send_session_notification = __esm(() => {
|
|
152474
|
+
init_logger();
|
|
152475
|
+
});
|
|
152476
|
+
|
|
152477
|
+
// src/hooks/magic-context/compartment-runner-partial-recomp.ts
|
|
152478
|
+
function snapRangeToCompartments(compartments, range) {
|
|
152479
|
+
if (compartments.length === 0) {
|
|
152480
|
+
return {
|
|
152481
|
+
error: "No compartments exist yet for this session. Run `/ctx-recomp` (full) first, then use partial recomp to refine specific ranges."
|
|
152482
|
+
};
|
|
152483
|
+
}
|
|
152484
|
+
const sorted = compartments.slice().sort((a, b) => a.sequence - b.sequence);
|
|
152485
|
+
const { start, end } = range;
|
|
152486
|
+
if (start < 1)
|
|
152487
|
+
return { error: `Start must be >= 1 (got ${start}).` };
|
|
152488
|
+
if (end < start)
|
|
152489
|
+
return { error: `End must be >= start (got ${start}-${end}).` };
|
|
152490
|
+
const firstEnclosingIdx = sorted.findIndex((c) => c.endMessage >= start);
|
|
152491
|
+
if (firstEnclosingIdx === -1) {
|
|
152492
|
+
const last = sorted[sorted.length - 1];
|
|
152493
|
+
return {
|
|
152494
|
+
error: `Range ${start}-${end} starts after the last compartment (which ends at message ${last.endMessage}). Nothing to rebuild.`
|
|
152495
|
+
};
|
|
152496
|
+
}
|
|
152497
|
+
let lastEnclosingIdx = -1;
|
|
152498
|
+
for (let i = sorted.length - 1;i >= 0; i--) {
|
|
152499
|
+
if (sorted[i].startMessage <= end) {
|
|
152500
|
+
lastEnclosingIdx = i;
|
|
152501
|
+
break;
|
|
152502
|
+
}
|
|
152503
|
+
}
|
|
152504
|
+
if (lastEnclosingIdx === -1 || lastEnclosingIdx < firstEnclosingIdx) {
|
|
152505
|
+
return {
|
|
152506
|
+
error: `Range ${start}-${end} does not overlap any compartment.`
|
|
152507
|
+
};
|
|
152508
|
+
}
|
|
152509
|
+
return {
|
|
152510
|
+
snapStart: sorted[firstEnclosingIdx].startMessage,
|
|
152511
|
+
snapEnd: sorted[lastEnclosingIdx].endMessage,
|
|
152512
|
+
priorCompartments: sorted.slice(0, firstEnclosingIdx),
|
|
152513
|
+
rangeCompartments: sorted.slice(firstEnclosingIdx, lastEnclosingIdx + 1),
|
|
152514
|
+
tailCompartments: sorted.slice(lastEnclosingIdx + 1)
|
|
152515
|
+
};
|
|
152516
|
+
}
|
|
152517
|
+
function compartmentToInput(c, newSequence) {
|
|
152518
|
+
return {
|
|
152519
|
+
sequence: newSequence,
|
|
152520
|
+
startMessage: c.startMessage,
|
|
152521
|
+
endMessage: c.endMessage,
|
|
152522
|
+
startMessageId: c.startMessageId,
|
|
152523
|
+
endMessageId: c.endMessageId,
|
|
152524
|
+
title: c.title,
|
|
152525
|
+
content: c.content
|
|
152526
|
+
};
|
|
152527
|
+
}
|
|
152528
|
+
async function executePartialRecompInternal(deps, range) {
|
|
152529
|
+
const {
|
|
152530
|
+
client,
|
|
152531
|
+
db,
|
|
152532
|
+
sessionId,
|
|
152533
|
+
historianChunkTokens,
|
|
152534
|
+
directory,
|
|
152535
|
+
historianTimeoutMs,
|
|
152536
|
+
getNotificationParams
|
|
152537
|
+
} = deps;
|
|
152538
|
+
const notifParams = () => getNotificationParams?.() ?? {};
|
|
152539
|
+
updateSessionMeta(db, sessionId, { compartmentInProgress: true });
|
|
152540
|
+
try {
|
|
152541
|
+
let promoteFinal = function() {
|
|
152542
|
+
const newBuilt = candidateCompartments.slice(priorCompartments.length);
|
|
152543
|
+
if (newBuilt.length === 0)
|
|
152544
|
+
return null;
|
|
152545
|
+
const newBuiltError = (() => {
|
|
152546
|
+
let expected = snapStart;
|
|
152547
|
+
for (const c of newBuilt) {
|
|
152548
|
+
if (c.startMessage !== expected) {
|
|
152549
|
+
return c.startMessage < expected ? `overlap in rebuilt range near ${expected}` : `gap in rebuilt range before ${c.startMessage} (expected ${expected})`;
|
|
152550
|
+
}
|
|
152551
|
+
if (c.endMessage < c.startMessage) {
|
|
152552
|
+
return `invalid range ${c.startMessage}-${c.endMessage}`;
|
|
152553
|
+
}
|
|
152554
|
+
expected = c.endMessage + 1;
|
|
152555
|
+
}
|
|
152556
|
+
if (expected - 1 !== snapEnd) {
|
|
152557
|
+
return `rebuilt range ends at ${expected - 1} but snapped end is ${snapEnd}`;
|
|
152558
|
+
}
|
|
152559
|
+
return null;
|
|
152560
|
+
})();
|
|
152561
|
+
if (newBuiltError) {
|
|
152562
|
+
log(`[magic-context] partial recomp validation failed: ${newBuiltError}`);
|
|
152563
|
+
return null;
|
|
152564
|
+
}
|
|
152565
|
+
const merged = [
|
|
152566
|
+
...candidateCompartments,
|
|
152567
|
+
...tailCompartments.map((c, idx) => compartmentToInput(c, candidateCompartments.length + idx))
|
|
152568
|
+
];
|
|
152569
|
+
const mergedError = validateStoredCompartments(merged);
|
|
152570
|
+
if (mergedError) {
|
|
152571
|
+
log(`[magic-context] partial recomp merged validation failed: ${mergedError}`);
|
|
152572
|
+
return null;
|
|
152573
|
+
}
|
|
152574
|
+
saveRecompStagingPass(db, sessionId, passCount + 1, merged, currentFacts);
|
|
152575
|
+
const promoted = promoteRecompStaging(db, sessionId);
|
|
152576
|
+
if (!promoted) {
|
|
152577
|
+
log("[magic-context] partial recomp promote returned null");
|
|
152578
|
+
return null;
|
|
152579
|
+
}
|
|
152580
|
+
setRecompPartialRange(db, sessionId, null);
|
|
152581
|
+
clearCompressionDepthRange(db, sessionId, snapStart, snapEnd);
|
|
152582
|
+
clearInjectionCache(sessionId);
|
|
152583
|
+
const lastEnd = merged[merged.length - 1]?.endMessage ?? snapEnd;
|
|
152584
|
+
return { compartmentCount: merged.length, lastEndMessage: lastEnd };
|
|
152585
|
+
};
|
|
152586
|
+
const protectedTailStart = getProtectedTailStartOrdinal(sessionId);
|
|
152587
|
+
const existingCompartments = getCompartments(db, sessionId);
|
|
152588
|
+
const snapResult = snapRangeToCompartments(existingCompartments, range);
|
|
152589
|
+
if ("error" in snapResult) {
|
|
152590
|
+
return `## Magic Recomp \u2014 Failed
|
|
152591
|
+
|
|
152592
|
+
${snapResult.error}`;
|
|
152593
|
+
}
|
|
152594
|
+
const { snapStart, snapEnd, priorCompartments, tailCompartments } = snapResult;
|
|
152595
|
+
if (snapEnd >= protectedTailStart) {
|
|
152596
|
+
return `## Magic Recomp \u2014 Failed
|
|
152597
|
+
|
|
152598
|
+
Snapped range ${snapStart}-${snapEnd} would cross into the protected tail (starting at ${protectedTailStart}). Partial recomp cannot rebuild recent messages. Try an earlier range.`;
|
|
152599
|
+
}
|
|
152600
|
+
const storedRange = getRecompPartialRange(db, sessionId);
|
|
152601
|
+
const existingStaging = getRecompStaging(db, sessionId);
|
|
152602
|
+
if (existingStaging && storedRange && (storedRange.start !== snapStart || storedRange.end !== snapEnd)) {
|
|
152603
|
+
return [
|
|
152604
|
+
"## Magic Recomp \u2014 Failed",
|
|
152605
|
+
"",
|
|
152606
|
+
`An unfinished partial recomp is already staged for range ${storedRange.start}-${storedRange.end}, which does not match the requested range ${snapStart}-${snapEnd}.`,
|
|
152607
|
+
"",
|
|
152608
|
+
"Resume that range by running `/ctx-recomp` with the same original arguments,",
|
|
152609
|
+
"or cancel it by running `/ctx-flush` before starting a new partial recomp."
|
|
152610
|
+
].join(`
|
|
152611
|
+
`);
|
|
152612
|
+
}
|
|
152613
|
+
if (existingStaging && !storedRange) {
|
|
152614
|
+
return [
|
|
152615
|
+
"## Magic Recomp \u2014 Failed",
|
|
152616
|
+
"",
|
|
152617
|
+
"An unfinished full recomp is already staged for this session.",
|
|
152618
|
+
"Resume it by running `/ctx-recomp` without arguments,",
|
|
152619
|
+
"or cancel it before starting a partial recomp."
|
|
152620
|
+
].join(`
|
|
152621
|
+
`);
|
|
152622
|
+
}
|
|
152623
|
+
const currentFacts = getSessionFacts(db, sessionId).map((f) => ({
|
|
152624
|
+
category: f.category,
|
|
152625
|
+
content: f.content
|
|
152626
|
+
}));
|
|
152627
|
+
const parentSessionResponse = await client.session.get({ path: { id: sessionId } }).catch(() => null);
|
|
152628
|
+
const parentSession = normalizeSDKResponse(parentSessionResponse, null, { preferResponseOnMissingData: true });
|
|
152629
|
+
const sessionDirectory = parentSession?.directory ?? directory;
|
|
152630
|
+
const projectPath = directory ? resolveProjectIdentity(directory) : undefined;
|
|
152631
|
+
const memories = projectPath ? getMemoriesByProject(db, projectPath, ["active", "permanent"]) : [];
|
|
152632
|
+
const memoryBlockForExistingState = memories.length > 0 ? undefined : undefined;
|
|
152633
|
+
let candidateCompartments;
|
|
152634
|
+
let passCount;
|
|
152635
|
+
let offset;
|
|
152636
|
+
const resumed = existingStaging !== null && storedRange !== null;
|
|
152637
|
+
if (resumed && existingStaging) {
|
|
152638
|
+
candidateCompartments = existingStaging.compartments;
|
|
152639
|
+
passCount = existingStaging.passCount;
|
|
152640
|
+
const lastInStaging = existingStaging.lastEndMessage;
|
|
152641
|
+
offset = lastInStaging >= snapStart ? lastInStaging + 1 : snapStart;
|
|
152642
|
+
} else {
|
|
152643
|
+
candidateCompartments = priorCompartments.map((c, idx) => compartmentToInput(c, idx));
|
|
152644
|
+
passCount = 0;
|
|
152645
|
+
offset = snapStart;
|
|
152646
|
+
saveRecompStagingPass(db, sessionId, 0, candidateCompartments, currentFacts);
|
|
152647
|
+
setRecompPartialRange(db, sessionId, { start: snapStart, end: snapEnd });
|
|
152648
|
+
}
|
|
152649
|
+
let currentTokenBudget = historianChunkTokens;
|
|
152650
|
+
let passAttempt = 1;
|
|
152651
|
+
await sendIgnoredMessage(client, sessionId, resumed ? `## Magic Recomp \u2014 Resumed (Partial)
|
|
152652
|
+
|
|
152653
|
+
Found ${candidateCompartments.length - priorCompartments.length} newly built compartment(s) from ${passCount} previous pass(es), covering messages ${snapStart}-${offset - 1}. Resuming from message ${offset} toward ${snapEnd}.` : `## Magic Recomp \u2014 Partial
|
|
152654
|
+
|
|
152655
|
+
Snapped to compartment boundaries: rebuilding messages ${snapStart}-${snapEnd} (${tailCompartments.length} tail compartment(s) preserved).`, notifParams());
|
|
152656
|
+
while (offset <= snapEnd) {
|
|
152657
|
+
const chunk = readSessionChunk(sessionId, currentTokenBudget, offset, snapEnd + 1);
|
|
152658
|
+
if (!chunk.text || chunk.messageCount === 0 || chunk.endIndex < offset) {
|
|
152659
|
+
return `## Magic Recomp \u2014 Failed
|
|
152660
|
+
|
|
152661
|
+
Recomp stopped because raw history ${offset}-${snapEnd} could not be turned into a valid historian chunk. Partial recomp preserved original state (staging kept for retry).`;
|
|
152662
|
+
}
|
|
152663
|
+
const chunkCoverageError = validateChunkCoverage(chunk);
|
|
152664
|
+
if (chunkCoverageError) {
|
|
152665
|
+
return `## Magic Recomp \u2014 Failed
|
|
152666
|
+
|
|
152667
|
+
Partial recomp stopped because the raw chunk could not be represented safely: ${chunkCoverageError}
|
|
152668
|
+
|
|
152669
|
+
Original state preserved (staging kept for retry).`;
|
|
152670
|
+
}
|
|
152671
|
+
const existingState = buildExistingStateXml(candidateCompartments, currentFacts, undefined);
|
|
152672
|
+
const prompt = buildCompartmentAgentPrompt(existingState, `Messages ${chunk.startIndex}-${chunk.endIndex}:
|
|
152673
|
+
|
|
152674
|
+
${chunk.text}`);
|
|
152675
|
+
await sendIgnoredMessage(client, sessionId, `## Magic Recomp \u2014 Partial
|
|
152676
|
+
|
|
152677
|
+
Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${chunk.startIndex}-${chunk.endIndex}.`, notifParams());
|
|
152678
|
+
const validatedPass = await runValidatedHistorianPass({
|
|
152679
|
+
client,
|
|
152680
|
+
parentSessionId: sessionId,
|
|
152681
|
+
sessionDirectory,
|
|
152682
|
+
prompt,
|
|
152683
|
+
chunk,
|
|
152684
|
+
priorCompartments: candidateCompartments,
|
|
152685
|
+
sequenceOffset: candidateCompartments.length,
|
|
152686
|
+
dumpLabelBase: `partial-recomp-${sessionId}-${chunk.startIndex}-${chunk.endIndex}-pass-${passCount + 1}`,
|
|
152687
|
+
timeoutMs: historianTimeoutMs,
|
|
152688
|
+
fallbackModelId: deps.fallbackModelId,
|
|
152689
|
+
twoPass: deps.historianTwoPass,
|
|
152690
|
+
callbacks: {
|
|
152691
|
+
onRepairRetry: async (error48) => {
|
|
152692
|
+
await sendIgnoredMessage(client, sessionId, `## Magic Recomp \u2014 Partial
|
|
152693
|
+
|
|
152694
|
+
Historian pass ${passCount + 1}, attempt ${passAttempt} is continuing with a repair retry for messages ${chunk.startIndex}-${chunk.endIndex}.
|
|
152695
|
+
|
|
152696
|
+
The previous output did not validate: ${error48}`, notifParams());
|
|
152697
|
+
}
|
|
152698
|
+
}
|
|
152699
|
+
});
|
|
152700
|
+
if (!validatedPass.ok) {
|
|
152701
|
+
const reducedBudget = getReducedRecompTokenBudget(currentTokenBudget);
|
|
152702
|
+
if (reducedBudget !== null) {
|
|
152703
|
+
const smallerChunk = readSessionChunk(sessionId, reducedBudget, offset, snapEnd + 1);
|
|
152704
|
+
if (smallerChunk.messageCount > 0 && smallerChunk.endIndex < chunk.endIndex) {
|
|
152705
|
+
await sendIgnoredMessage(client, sessionId, `## Magic Recomp \u2014 Partial
|
|
152706
|
+
|
|
152707
|
+
Historian pass ${passCount + 1}, attempt ${passAttempt} is continuing with a smaller chunk ending at ${smallerChunk.endIndex} because messages ${chunk.startIndex}-${chunk.endIndex} could not be validated.
|
|
152708
|
+
|
|
152709
|
+
Validator result: ${validatedPass.error}`, notifParams());
|
|
152710
|
+
currentTokenBudget = reducedBudget;
|
|
152711
|
+
passAttempt += 1;
|
|
152712
|
+
continue;
|
|
152713
|
+
}
|
|
152714
|
+
}
|
|
152715
|
+
return `## Magic Recomp \u2014 Failed
|
|
152716
|
+
|
|
152717
|
+
Partial recomp failed while rebuilding messages ${chunk.startIndex}-${chunk.endIndex}: ${validatedPass.error}
|
|
152718
|
+
|
|
152719
|
+
Original state preserved (staging kept for retry).`;
|
|
152720
|
+
}
|
|
152721
|
+
candidateCompartments = [
|
|
152722
|
+
...candidateCompartments,
|
|
152723
|
+
...validatedPass.compartments ?? []
|
|
152724
|
+
];
|
|
152725
|
+
passCount += 1;
|
|
152726
|
+
currentTokenBudget = historianChunkTokens;
|
|
152727
|
+
passAttempt = 1;
|
|
152728
|
+
saveRecompStagingPass(db, sessionId, passCount, candidateCompartments, currentFacts);
|
|
152729
|
+
const nextOffset = (validatedPass.compartments?.[validatedPass.compartments.length - 1]?.endMessage ?? chunk.endIndex) + 1;
|
|
152730
|
+
if (nextOffset <= offset) {
|
|
152731
|
+
return `## Magic Recomp \u2014 Failed
|
|
152732
|
+
|
|
152733
|
+
Partial recomp made no forward progress after messages ${chunk.startIndex}-${chunk.endIndex}. Staging kept for retry.`;
|
|
152734
|
+
}
|
|
152735
|
+
offset = nextOffset;
|
|
152736
|
+
}
|
|
152737
|
+
const finalResult = promoteFinal();
|
|
152738
|
+
if (!finalResult) {
|
|
152739
|
+
return `## Magic Recomp \u2014 Failed
|
|
152740
|
+
|
|
152741
|
+
Partial recomp completed historian passes but the final compartment set failed validation. Original state preserved (staging kept for inspection).`;
|
|
152742
|
+
}
|
|
152743
|
+
return [
|
|
152744
|
+
"## Magic Recomp \u2014 Partial Complete",
|
|
152745
|
+
"",
|
|
152746
|
+
...resumed ? ["Resumed from previous interrupted partial run."] : [],
|
|
152747
|
+
`Rebuilt compartments covering messages ${snapStart}-${snapEnd} using ${passCount} historian pass${passCount === 1 ? "" : "es"}.`,
|
|
152748
|
+
`Preserved ${priorCompartments.length} prior compartment(s) and ${tailCompartments.length} tail compartment(s) unchanged.`,
|
|
152749
|
+
`Facts unchanged (${currentFacts.length} entr${currentFacts.length === 1 ? "y" : "ies"}).`,
|
|
152750
|
+
`Total compartments: ${finalResult.compartmentCount}.`
|
|
152751
|
+
].join(`
|
|
152752
|
+
`);
|
|
152753
|
+
} catch (error48) {
|
|
152754
|
+
const message = getErrorMessage(error48);
|
|
152755
|
+
return `## Magic Recomp \u2014 Failed
|
|
152756
|
+
|
|
152757
|
+
Partial recomp failed unexpectedly: ${message}
|
|
152758
|
+
|
|
152759
|
+
Staging preserved for resume on next attempt.`;
|
|
152760
|
+
} finally {
|
|
152761
|
+
updateSessionMeta(db, sessionId, { compartmentInProgress: false });
|
|
152762
|
+
const leftoverStaging = getRecompStaging(db, sessionId);
|
|
152763
|
+
const leftoverRange = getRecompPartialRange(db, sessionId);
|
|
152764
|
+
if (leftoverStaging && leftoverRange) {} else if (leftoverStaging && !leftoverRange) {
|
|
152765
|
+
log(`[magic-context] partial recomp cleanup: clearing orphaned staging without range marker for session ${sessionId}`);
|
|
152766
|
+
clearRecompStaging(db, sessionId);
|
|
152767
|
+
}
|
|
152768
|
+
}
|
|
152769
|
+
}
|
|
152770
|
+
var init_compartment_runner_partial_recomp = __esm(() => {
|
|
152771
|
+
init_compartment_storage();
|
|
152772
|
+
init_compression_depth_storage();
|
|
152773
|
+
init_project_identity();
|
|
152774
|
+
init_storage_memory();
|
|
152775
|
+
init_storage_meta();
|
|
152776
|
+
init_shared();
|
|
152777
|
+
init_logger();
|
|
152778
|
+
init_compartment_prompt();
|
|
152779
|
+
init_compartment_runner_historian();
|
|
152780
|
+
init_compartment_runner_state_xml();
|
|
152781
|
+
init_compartment_runner_validation();
|
|
152782
|
+
init_inject_compartments();
|
|
152783
|
+
init_read_session_chunk();
|
|
152784
|
+
init_send_session_notification();
|
|
152785
|
+
});
|
|
152786
|
+
|
|
152787
|
+
// src/hooks/magic-context/derive-budgets.ts
|
|
152788
|
+
var exports_derive_budgets = {};
|
|
152789
|
+
__export(exports_derive_budgets, {
|
|
152790
|
+
resolveHistorianContextLimit: () => resolveHistorianContextLimit,
|
|
152791
|
+
deriveTriggerBudget: () => deriveTriggerBudget,
|
|
152792
|
+
deriveHistorianChunkTokens: () => deriveHistorianChunkTokens
|
|
152793
|
+
});
|
|
152794
|
+
function deriveTriggerBudget(mainContextLimit, executeThresholdPercentage) {
|
|
152795
|
+
if (!Number.isFinite(mainContextLimit) || mainContextLimit <= 0) {
|
|
152796
|
+
return TRIGGER_BUDGET_MIN;
|
|
152797
|
+
}
|
|
152798
|
+
const thresholdFraction = Math.max(0, executeThresholdPercentage) / 100;
|
|
152799
|
+
const usable = mainContextLimit * thresholdFraction;
|
|
152800
|
+
const derived = Math.round(usable * TRIGGER_BUDGET_PERCENTAGE);
|
|
152801
|
+
return Math.max(TRIGGER_BUDGET_MIN, Math.min(TRIGGER_BUDGET_MAX, derived));
|
|
152802
|
+
}
|
|
152803
|
+
function deriveHistorianChunkTokens(historianContextLimit) {
|
|
152804
|
+
if (!Number.isFinite(historianContextLimit) || historianContextLimit <= 0) {
|
|
152805
|
+
return HISTORIAN_CHUNK_MIN;
|
|
152806
|
+
}
|
|
152807
|
+
const derived = Math.round(historianContextLimit * HISTORIAN_CHUNK_PERCENTAGE);
|
|
152808
|
+
return Math.max(HISTORIAN_CHUNK_MIN, Math.min(HISTORIAN_CHUNK_MAX, derived));
|
|
152809
|
+
}
|
|
152810
|
+
function resolveHistorianContextLimit(historianModelOverride) {
|
|
152811
|
+
if (typeof historianModelOverride === "string" && historianModelOverride.includes("/")) {
|
|
152812
|
+
const [providerID, ...rest] = historianModelOverride.split("/");
|
|
152813
|
+
const modelID = rest.join("/");
|
|
152814
|
+
if (providerID && modelID) {
|
|
152815
|
+
const limit = getModelsDevContextLimit(providerID, modelID);
|
|
152816
|
+
if (typeof limit === "number" && limit > 0)
|
|
152817
|
+
return limit;
|
|
152818
|
+
}
|
|
152819
|
+
return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
|
|
152820
|
+
}
|
|
152821
|
+
if (typeof historianModelOverride === "string" && historianModelOverride.trim() !== "") {
|
|
152822
|
+
console.warn(`[magic-context] historian.model "${historianModelOverride}" lacks provider prefix ("provider/model-id"); using fallback chain for chunk-budget derivation.`);
|
|
152823
|
+
}
|
|
152824
|
+
const chain = AGENT_MODEL_REQUIREMENTS[HISTORIAN_AGENT]?.fallbackChain;
|
|
152825
|
+
if (!chain || chain.length === 0)
|
|
152826
|
+
return DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
|
|
152827
|
+
const expanded = expandFallbackChain(chain);
|
|
152828
|
+
let minLimit;
|
|
152829
|
+
for (const key of expanded) {
|
|
152830
|
+
const [providerID, ...rest] = key.split("/");
|
|
152831
|
+
const modelID = rest.join("/");
|
|
152832
|
+
if (!providerID || !modelID)
|
|
152833
|
+
continue;
|
|
152834
|
+
const limit = getModelsDevContextLimit(providerID, modelID);
|
|
152835
|
+
if (typeof limit !== "number" || limit <= 0)
|
|
152836
|
+
continue;
|
|
152837
|
+
if (minLimit === undefined || limit < minLimit)
|
|
152838
|
+
minLimit = limit;
|
|
152839
|
+
}
|
|
152840
|
+
return minLimit ?? DEFAULT_HISTORIAN_CONTEXT_FALLBACK;
|
|
152841
|
+
}
|
|
152842
|
+
var TRIGGER_BUDGET_PERCENTAGE = 0.05, TRIGGER_BUDGET_MIN = 5000, TRIGGER_BUDGET_MAX = 50000, HISTORIAN_CHUNK_PERCENTAGE = 0.25, HISTORIAN_CHUNK_MIN = 8000, HISTORIAN_CHUNK_MAX = 50000, DEFAULT_HISTORIAN_CONTEXT_FALLBACK = 128000;
|
|
152843
|
+
var init_derive_budgets = __esm(() => {
|
|
152844
|
+
init_model_requirements();
|
|
152845
|
+
init_models_dev_cache();
|
|
152846
|
+
});
|
|
152847
|
+
|
|
152848
|
+
// src/features/magic-context/compaction-marker.ts
|
|
152849
|
+
import { Database as Database4 } from "bun:sqlite";
|
|
152850
|
+
import { join as join13 } from "path";
|
|
152851
|
+
function randomBase62(length) {
|
|
152852
|
+
const chars = [];
|
|
152853
|
+
for (let i = 0;i < length; i++) {
|
|
152854
|
+
chars.push(BASE62_CHARS[Math.floor(Math.random() * BASE62_CHARS.length)]);
|
|
152855
|
+
}
|
|
152856
|
+
return chars.join("");
|
|
152857
|
+
}
|
|
152858
|
+
function generateId(prefix, timestampMs, counter = 0n) {
|
|
152859
|
+
const encoded = BigInt(timestampMs) * 0x1000n + counter;
|
|
152860
|
+
const hex3 = encoded.toString(16).padStart(14, "0");
|
|
152861
|
+
return `${prefix}_${hex3}${randomBase62(14)}`;
|
|
152862
|
+
}
|
|
152863
|
+
function generateMessageId(timestampMs, counter = 0n) {
|
|
152864
|
+
return generateId("msg", timestampMs, counter);
|
|
152865
|
+
}
|
|
152866
|
+
function generatePartId(timestampMs, counter = 0n) {
|
|
152867
|
+
return generateId("prt", timestampMs, counter);
|
|
152868
|
+
}
|
|
152869
|
+
function getOpenCodeDbPath3() {
|
|
152870
|
+
return join13(getDataDir(), "opencode", "opencode.db");
|
|
152871
|
+
}
|
|
152872
|
+
function getWritableOpenCodeDb() {
|
|
152873
|
+
const dbPath = getOpenCodeDbPath3();
|
|
152874
|
+
if (cachedWriteDb?.path === dbPath) {
|
|
152875
|
+
return cachedWriteDb.db;
|
|
152876
|
+
}
|
|
152877
|
+
if (cachedWriteDb) {
|
|
152878
|
+
try {
|
|
152879
|
+
cachedWriteDb.db.close(false);
|
|
152880
|
+
} catch {}
|
|
152881
|
+
}
|
|
152882
|
+
const db = new Database4(dbPath);
|
|
152883
|
+
db.exec("PRAGMA journal_mode=WAL");
|
|
152884
|
+
db.exec("PRAGMA busy_timeout=5000");
|
|
152885
|
+
cachedWriteDb = { path: dbPath, db };
|
|
152886
|
+
return db;
|
|
152887
|
+
}
|
|
152888
|
+
function findBoundaryUserMessage(sessionId, endOrdinal) {
|
|
152889
|
+
const db = getWritableOpenCodeDb();
|
|
152890
|
+
const rows = db.prepare("SELECT id, time_created, data FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId);
|
|
152891
|
+
const filtered = rows.filter((row) => {
|
|
152892
|
+
try {
|
|
152893
|
+
const info = JSON.parse(row.data);
|
|
152894
|
+
return !(info.summary === true && info.finish === "stop");
|
|
152895
|
+
} catch {
|
|
152896
|
+
return true;
|
|
152897
|
+
}
|
|
152898
|
+
});
|
|
152899
|
+
let bestMatch = null;
|
|
152900
|
+
for (let i = 0;i < filtered.length && i < endOrdinal; i++) {
|
|
152901
|
+
const row = filtered[i];
|
|
152902
|
+
try {
|
|
152903
|
+
const info = JSON.parse(row.data);
|
|
152904
|
+
if (info.role === "user") {
|
|
152905
|
+
bestMatch = { id: row.id, timeCreated: row.time_created };
|
|
152906
|
+
}
|
|
152907
|
+
} catch {}
|
|
152908
|
+
}
|
|
152909
|
+
return bestMatch;
|
|
152910
|
+
}
|
|
152911
|
+
function injectCompactionMarker(args) {
|
|
152912
|
+
const boundary = findBoundaryUserMessage(args.sessionId, args.endOrdinal);
|
|
152913
|
+
if (!boundary) {
|
|
152914
|
+
log(`[magic-context] compaction-marker: no user message found at or before ordinal ${args.endOrdinal}`);
|
|
152915
|
+
return null;
|
|
152916
|
+
}
|
|
152917
|
+
const db = getWritableOpenCodeDb();
|
|
152918
|
+
const boundaryTime = boundary.timeCreated;
|
|
152919
|
+
const summaryMsgId = generateMessageId(boundaryTime + 1, 1n);
|
|
152920
|
+
const compactionPartId = generatePartId(boundaryTime, 1n);
|
|
152921
|
+
const summaryPartId = generatePartId(boundaryTime + 1, 2n);
|
|
152922
|
+
const summaryMsgData = JSON.stringify({
|
|
152923
|
+
role: "assistant",
|
|
152924
|
+
parentID: boundary.id,
|
|
152925
|
+
summary: true,
|
|
152926
|
+
finish: "stop",
|
|
152927
|
+
mode: "compaction",
|
|
152928
|
+
agent: "compaction",
|
|
152929
|
+
path: { cwd: args.directory, root: args.directory },
|
|
152930
|
+
cost: 0,
|
|
152931
|
+
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
152932
|
+
modelID: "magic-context",
|
|
152933
|
+
providerID: "magic-context",
|
|
152934
|
+
time: { created: boundaryTime + 1 }
|
|
152935
|
+
});
|
|
152936
|
+
try {
|
|
152937
|
+
db.transaction(() => {
|
|
152938
|
+
db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(compactionPartId, boundary.id, args.sessionId, boundaryTime, boundaryTime, '{"type":"compaction","auto":true}');
|
|
152939
|
+
db.prepare("INSERT INTO message (id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?)").run(summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, summaryMsgData);
|
|
152940
|
+
db.prepare("INSERT INTO part (id, message_id, session_id, time_created, time_updated, data) VALUES (?, ?, ?, ?, ?, ?)").run(summaryPartId, summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, JSON.stringify({ type: "text", text: args.summaryText }));
|
|
152941
|
+
})();
|
|
152942
|
+
log(`[magic-context] compaction-marker: injected boundary at user msg ${boundary.id} (ordinal ~${args.endOrdinal}), summary msg ${summaryMsgId}`);
|
|
152943
|
+
return {
|
|
152944
|
+
boundaryMessageId: boundary.id,
|
|
152945
|
+
summaryMessageId: summaryMsgId,
|
|
152946
|
+
compactionPartId,
|
|
152947
|
+
summaryPartId
|
|
152948
|
+
};
|
|
152949
|
+
} catch (error48) {
|
|
152950
|
+
log(`[magic-context] compaction-marker: injection failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
|
|
152951
|
+
return null;
|
|
152952
|
+
}
|
|
152953
|
+
}
|
|
152954
|
+
function removeCompactionMarker(state) {
|
|
152955
|
+
try {
|
|
152956
|
+
const db = getWritableOpenCodeDb();
|
|
152957
|
+
db.transaction(() => {
|
|
152958
|
+
db.prepare("DELETE FROM part WHERE id = ?").run(state.summaryPartId);
|
|
152959
|
+
db.prepare("DELETE FROM message WHERE id = ?").run(state.summaryMessageId);
|
|
152960
|
+
db.prepare("DELETE FROM part WHERE id = ?").run(state.compactionPartId);
|
|
152961
|
+
})();
|
|
152962
|
+
return true;
|
|
152963
|
+
} catch (error48) {
|
|
152964
|
+
log(`[magic-context] compaction-marker: removal failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
|
|
152965
|
+
return false;
|
|
152966
|
+
}
|
|
152967
|
+
}
|
|
152968
|
+
var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", cachedWriteDb = null;
|
|
152969
|
+
var init_compaction_marker = __esm(() => {
|
|
152970
|
+
init_data_path();
|
|
152971
|
+
init_logger();
|
|
152972
|
+
});
|
|
152973
|
+
|
|
152974
|
+
// src/hooks/magic-context/compaction-marker-manager.ts
|
|
152975
|
+
function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, directory) {
|
|
152976
|
+
const existing = getPersistedCompactionMarkerState(db, sessionId);
|
|
152977
|
+
if (existing) {
|
|
152978
|
+
if (existing.boundaryOrdinal === lastCompartmentEnd) {
|
|
152979
|
+
return;
|
|
152980
|
+
}
|
|
152981
|
+
try {
|
|
152982
|
+
removeCompactionMarker(existing);
|
|
152983
|
+
setPersistedCompactionMarkerState(db, sessionId, null);
|
|
152984
|
+
sessionLog(sessionId, `compaction-marker: removed old boundary at ordinal ${existing.boundaryOrdinal}, moving to ${lastCompartmentEnd}`);
|
|
152985
|
+
} catch (error48) {
|
|
152986
|
+
sessionLog(sessionId, `compaction-marker: failed to remove old boundary at ordinal ${existing.boundaryOrdinal}, proceeding with new injection:`, error48);
|
|
152987
|
+
}
|
|
152988
|
+
}
|
|
152989
|
+
const result = injectCompactionMarker({
|
|
152990
|
+
sessionId,
|
|
152991
|
+
endOrdinal: lastCompartmentEnd,
|
|
152992
|
+
summaryText: MARKER_SUMMARY_TEXT,
|
|
152993
|
+
directory: directory ?? process.cwd()
|
|
152994
|
+
});
|
|
152995
|
+
if (result) {
|
|
152996
|
+
setPersistedCompactionMarkerState(db, sessionId, {
|
|
152997
|
+
...result,
|
|
152998
|
+
boundaryOrdinal: lastCompartmentEnd
|
|
152999
|
+
});
|
|
153000
|
+
sessionLog(sessionId, `compaction-marker: injected at ordinal ${lastCompartmentEnd}, boundary user msg ${result.boundaryMessageId}`);
|
|
153001
|
+
}
|
|
153002
|
+
}
|
|
153003
|
+
function removeCompactionMarkerForSession(db, sessionId) {
|
|
153004
|
+
const existing = getPersistedCompactionMarkerState(db, sessionId);
|
|
153005
|
+
if (existing) {
|
|
153006
|
+
try {
|
|
153007
|
+
removeCompactionMarker(existing);
|
|
153008
|
+
setPersistedCompactionMarkerState(db, sessionId, null);
|
|
153009
|
+
sessionLog(sessionId, "compaction-marker: removed on session cleanup");
|
|
153010
|
+
} catch (error48) {
|
|
153011
|
+
setPersistedCompactionMarkerState(db, sessionId, null);
|
|
153012
|
+
sessionLog(sessionId, "compaction-marker: removal failed during session cleanup, cleared persisted state:", error48);
|
|
153013
|
+
}
|
|
153014
|
+
}
|
|
153015
|
+
}
|
|
153016
|
+
var MARKER_SUMMARY_TEXT = "[Compacted by magic-context \u2014 session history is managed by the plugin]";
|
|
153017
|
+
var init_compaction_marker_manager = __esm(() => {
|
|
153018
|
+
init_compaction_marker();
|
|
153019
|
+
init_storage_meta_persisted();
|
|
153020
|
+
init_logger();
|
|
153021
|
+
});
|
|
153022
|
+
|
|
153023
|
+
// src/hooks/magic-context/note-nudger.ts
|
|
153024
|
+
function getPersistedNoteNudgeDeliveredAt(_db, sessionId) {
|
|
153025
|
+
return lastDeliveredAt.get(sessionId) ?? 0;
|
|
153026
|
+
}
|
|
153027
|
+
function recordNoteNudgeDeliveryTime(sessionId) {
|
|
153028
|
+
lastDeliveredAt.set(sessionId, Date.now());
|
|
153029
|
+
}
|
|
153030
|
+
function onNoteTrigger(db, sessionId, trigger) {
|
|
153031
|
+
setPersistedNoteNudgeTrigger(db, sessionId);
|
|
153032
|
+
sessionLog(sessionId, `note-nudge: trigger fired (${trigger}), triggerPending=true`);
|
|
153033
|
+
}
|
|
153034
|
+
function peekNoteNudgeText(db, sessionId, currentUserMessageId, projectIdentity) {
|
|
153035
|
+
const state = getPersistedNoteNudge(db, sessionId);
|
|
153036
|
+
if (!state.triggerPending)
|
|
153037
|
+
return null;
|
|
153038
|
+
if (!state.triggerMessageId && currentUserMessageId) {
|
|
153039
|
+
setPersistedNoteNudgeTriggerMessageId(db, sessionId, currentUserMessageId);
|
|
153040
|
+
state.triggerMessageId = currentUserMessageId;
|
|
153041
|
+
}
|
|
153042
|
+
if (state.triggerMessageId && currentUserMessageId && state.triggerMessageId === currentUserMessageId) {
|
|
153043
|
+
sessionLog(sessionId, `note-nudge: deferring \u2014 current user message ${currentUserMessageId} is same as trigger-time message`);
|
|
153044
|
+
return null;
|
|
153045
|
+
}
|
|
153046
|
+
const deliveredAt = getPersistedNoteNudgeDeliveredAt(db, sessionId);
|
|
153047
|
+
if (deliveredAt > 0 && Date.now() - deliveredAt < NOTE_NUDGE_COOLDOWN_MS) {
|
|
153048
|
+
sessionLog(sessionId, `note-nudge: suppressing \u2014 last delivered ${Math.round((Date.now() - deliveredAt) / 1000)}s ago (cooldown ${NOTE_NUDGE_COOLDOWN_MS / 60000}m)`);
|
|
153049
|
+
clearPersistedNoteNudge(db, sessionId);
|
|
153050
|
+
return null;
|
|
153051
|
+
}
|
|
153052
|
+
const notes = getSessionNotes(db, sessionId);
|
|
153053
|
+
const readySmartCount = projectIdentity ? getReadySmartNotes(db, projectIdentity).length : 0;
|
|
153054
|
+
const totalCount = notes.length + readySmartCount;
|
|
153055
|
+
if (totalCount === 0) {
|
|
153056
|
+
sessionLog(sessionId, "note-nudge: triggerPending but no notes found, skipping");
|
|
153057
|
+
clearPersistedNoteNudge(db, sessionId);
|
|
153058
|
+
return null;
|
|
153059
|
+
}
|
|
153060
|
+
const parts = [];
|
|
153061
|
+
if (notes.length > 0) {
|
|
153062
|
+
parts.push(`${notes.length} deferred note${notes.length === 1 ? "" : "s"}`);
|
|
153063
|
+
}
|
|
153064
|
+
if (readySmartCount > 0) {
|
|
153065
|
+
parts.push(`${readySmartCount} ready smart note${readySmartCount === 1 ? "" : "s"}`);
|
|
153066
|
+
}
|
|
153067
|
+
sessionLog(sessionId, `note-nudge: delivering nudge for ${parts.join(" and ")}`);
|
|
153068
|
+
return `You have ${parts.join(" and ")}. Review with ctx_note read \u2014 some may be actionable now.`;
|
|
153069
|
+
}
|
|
153070
|
+
function markNoteNudgeDelivered(db, sessionId, text, messageId) {
|
|
153071
|
+
setPersistedDeliveredNoteNudge(db, sessionId, messageId ? text : "", messageId ?? "");
|
|
153072
|
+
recordNoteNudgeDeliveryTime(sessionId);
|
|
153073
|
+
sessionLog(sessionId, messageId ? `note-nudge: marked delivered, sticky anchor=${messageId}` : "note-nudge: marked delivered without anchor");
|
|
153074
|
+
}
|
|
153075
|
+
function getStickyNoteNudge(db, sessionId) {
|
|
153076
|
+
const state = getPersistedNoteNudge(db, sessionId);
|
|
153077
|
+
if (!state.stickyText || !state.stickyMessageId)
|
|
153078
|
+
return null;
|
|
153079
|
+
return { text: state.stickyText, messageId: state.stickyMessageId };
|
|
153080
|
+
}
|
|
153081
|
+
function clearNoteNudgeState(db, sessionId, options) {
|
|
153082
|
+
if (options?.persist !== false) {
|
|
153083
|
+
clearPersistedNoteNudge(db, sessionId);
|
|
153084
|
+
}
|
|
153085
|
+
lastDeliveredAt.delete(sessionId);
|
|
153086
|
+
}
|
|
153087
|
+
var NOTE_NUDGE_COOLDOWN_MS, lastDeliveredAt;
|
|
153088
|
+
var init_note_nudger = __esm(() => {
|
|
153089
|
+
init_storage_meta_persisted();
|
|
153090
|
+
init_storage_notes();
|
|
153091
|
+
init_logger();
|
|
153092
|
+
NOTE_NUDGE_COOLDOWN_MS = 15 * 60 * 1000;
|
|
153093
|
+
lastDeliveredAt = new Map;
|
|
153094
|
+
});
|
|
153095
|
+
|
|
153096
|
+
// src/features/magic-context/memory/embedding-backfill.ts
|
|
153097
|
+
async function ensureMemoryEmbeddings(args) {
|
|
153098
|
+
if (!isEmbeddingEnabled()) {
|
|
153099
|
+
return args.existingEmbeddings;
|
|
153100
|
+
}
|
|
153101
|
+
const missingMemories = args.memories.filter((memory) => !args.existingEmbeddings.has(memory.id));
|
|
153102
|
+
if (missingMemories.length === 0) {
|
|
153103
|
+
return args.existingEmbeddings;
|
|
153104
|
+
}
|
|
153105
|
+
try {
|
|
153106
|
+
const embeddings = await embedBatch(missingMemories.map((memory) => memory.content));
|
|
153107
|
+
const modelId = getEmbeddingModelId();
|
|
153108
|
+
const staged = new Map;
|
|
153109
|
+
args.db.transaction(() => {
|
|
153110
|
+
for (const [index, memory] of missingMemories.entries()) {
|
|
153111
|
+
const embedding = embeddings[index];
|
|
153112
|
+
if (!embedding) {
|
|
153113
|
+
continue;
|
|
153114
|
+
}
|
|
153115
|
+
saveEmbedding(args.db, memory.id, embedding, modelId);
|
|
153116
|
+
staged.set(memory.id, embedding);
|
|
153117
|
+
}
|
|
153118
|
+
})();
|
|
153119
|
+
for (const [id, embedding] of staged) {
|
|
153120
|
+
args.existingEmbeddings.set(id, embedding);
|
|
153121
|
+
}
|
|
153122
|
+
} catch (error48) {
|
|
153123
|
+
log("[magic-context] failed to backfill memory embeddings:", error48);
|
|
153124
|
+
}
|
|
153125
|
+
return args.existingEmbeddings;
|
|
153126
|
+
}
|
|
153127
|
+
var init_embedding_backfill = __esm(() => {
|
|
153128
|
+
init_logger();
|
|
153129
|
+
init_embedding();
|
|
153130
|
+
init_storage_memory_embeddings();
|
|
153131
|
+
});
|
|
153132
|
+
|
|
153133
|
+
// src/features/magic-context/memory/promotion.ts
|
|
153134
|
+
function isPromotableCategory(category) {
|
|
153135
|
+
return PROMOTABLE_CATEGORIES.some((promotableCategory) => promotableCategory === category);
|
|
153136
|
+
}
|
|
153137
|
+
function resolveExpiresAt(category) {
|
|
153138
|
+
const ttl = CATEGORY_DEFAULT_TTL[category];
|
|
153139
|
+
return ttl === undefined ? null : Date.now() + ttl;
|
|
153140
|
+
}
|
|
153141
|
+
function promoteSessionFactsToMemory(db, sessionId, projectPath, facts) {
|
|
153142
|
+
for (const fact of facts) {
|
|
153143
|
+
if (!isPromotableCategory(fact.category)) {
|
|
153144
|
+
continue;
|
|
153145
|
+
}
|
|
153146
|
+
try {
|
|
153147
|
+
const normalizedHash = computeNormalizedHash(fact.content);
|
|
153148
|
+
const existingMemory = getMemoryByHash(db, projectPath, fact.category, normalizedHash);
|
|
153149
|
+
if (existingMemory) {
|
|
153150
|
+
updateMemorySeenCount(db, existingMemory.id);
|
|
153151
|
+
continue;
|
|
153152
|
+
}
|
|
153153
|
+
const memoryInput = {
|
|
153154
|
+
projectPath,
|
|
153155
|
+
category: fact.category,
|
|
153156
|
+
content: fact.content,
|
|
153157
|
+
sourceSessionId: sessionId,
|
|
153158
|
+
sourceType: "historian",
|
|
153159
|
+
expiresAt: resolveExpiresAt(fact.category)
|
|
153160
|
+
};
|
|
153161
|
+
const memory = insertMemory(db, memoryInput);
|
|
153162
|
+
embedAndStoreMemory(db, sessionId, memory.id, memory.content);
|
|
153163
|
+
} catch (error48) {
|
|
153164
|
+
sessionLog(sessionId, `memory promotion failed for fact "${fact.content.slice(0, 60)}":`, error48);
|
|
153165
|
+
}
|
|
153166
|
+
}
|
|
153167
|
+
}
|
|
153168
|
+
async function embedAndStoreMemory(db, sessionId, memoryId, content) {
|
|
153169
|
+
try {
|
|
153170
|
+
const embedding = await embedText(content);
|
|
153171
|
+
if (embedding) {
|
|
153172
|
+
saveEmbedding(db, memoryId, embedding, getEmbeddingModelId());
|
|
153173
|
+
}
|
|
153174
|
+
} catch (error48) {
|
|
153175
|
+
sessionLog(sessionId, `memory embedding failed for memory ${memoryId}:`, error48);
|
|
153176
|
+
}
|
|
153177
|
+
}
|
|
153178
|
+
var init_promotion = __esm(() => {
|
|
153179
|
+
init_logger();
|
|
153180
|
+
init_constants();
|
|
153181
|
+
init_embedding();
|
|
153182
|
+
init_storage_memory();
|
|
153183
|
+
init_storage_memory_embeddings();
|
|
153184
|
+
});
|
|
153185
|
+
|
|
153186
|
+
// src/features/magic-context/memory/storage-memory-fts.ts
|
|
153187
|
+
function getSearchStatement(db) {
|
|
153188
|
+
let stmt = searchStatements.get(db);
|
|
153189
|
+
if (!stmt) {
|
|
153190
|
+
const selectColumns = Object.entries(COLUMN_MAP).map(([property, column]) => `memories.${column} AS ${property}`).join(", ");
|
|
153191
|
+
stmt = db.prepare(`SELECT ${selectColumns} FROM memories_fts INNER JOIN memories ON memories.id = memories_fts.rowid WHERE memories.project_path = ? AND memories.status IN ('active', 'permanent') AND (memories.expires_at IS NULL OR memories.expires_at > ?) AND memories_fts MATCH ? ORDER BY bm25(memories_fts), memories.updated_at DESC, memories.id ASC LIMIT ?`);
|
|
153192
|
+
searchStatements.set(db, stmt);
|
|
153193
|
+
}
|
|
153194
|
+
return stmt;
|
|
153195
|
+
}
|
|
153196
|
+
function sanitizeFtsQuery(query) {
|
|
153197
|
+
const tokens = query.split(/\s+/).filter((token) => token.length > 0);
|
|
153198
|
+
if (tokens.length === 0)
|
|
153199
|
+
return "";
|
|
153200
|
+
return tokens.map((token) => `"${token.replace(/"/g, '""')}"`).join(" ");
|
|
153201
|
+
}
|
|
153202
|
+
function searchMemoriesFTS(db, projectPath, query, limit = DEFAULT_SEARCH_LIMIT) {
|
|
153203
|
+
const trimmedQuery = query.trim();
|
|
153204
|
+
if (trimmedQuery.length === 0 || limit <= 0) {
|
|
153205
|
+
return [];
|
|
153206
|
+
}
|
|
153207
|
+
const sanitized = sanitizeFtsQuery(trimmedQuery);
|
|
153208
|
+
if (sanitized.length === 0) {
|
|
153209
|
+
return [];
|
|
153210
|
+
}
|
|
153211
|
+
const rows = getSearchStatement(db).all(projectPath, Date.now(), sanitized, limit).filter(isMemoryRow);
|
|
153212
|
+
return rows.map(toMemory);
|
|
153213
|
+
}
|
|
153214
|
+
var DEFAULT_SEARCH_LIMIT = 10, searchStatements;
|
|
153215
|
+
var init_storage_memory_fts = __esm(() => {
|
|
153216
|
+
init_storage_memory();
|
|
153217
|
+
searchStatements = new WeakMap;
|
|
153218
|
+
});
|
|
153219
|
+
// src/features/magic-context/memory/index.ts
|
|
153220
|
+
var init_memory = __esm(() => {
|
|
153221
|
+
init_project_identity();
|
|
153222
|
+
init_promotion();
|
|
153223
|
+
init_constants();
|
|
153224
|
+
init_embedding();
|
|
153225
|
+
init_embedding_backfill();
|
|
153226
|
+
init_embedding_cache();
|
|
153227
|
+
init_storage_memory();
|
|
153228
|
+
init_storage_memory_embeddings();
|
|
153229
|
+
init_storage_memory_fts();
|
|
153230
|
+
});
|
|
153231
|
+
|
|
153232
|
+
// src/hooks/magic-context/caveman.ts
|
|
153233
|
+
function protectRegions(text) {
|
|
153234
|
+
const preserved = [];
|
|
153235
|
+
let working = text;
|
|
153236
|
+
for (const pattern of PRESERVATION_PATTERNS) {
|
|
153237
|
+
working = working.replace(pattern, (match) => {
|
|
153238
|
+
const placeholder = `\x00MC_PRES_${preserved.length}\x00`;
|
|
153239
|
+
preserved.push({ placeholder, original: match });
|
|
153240
|
+
return placeholder;
|
|
153241
|
+
});
|
|
153242
|
+
}
|
|
153243
|
+
return { text: working, preserved };
|
|
153244
|
+
}
|
|
153245
|
+
function restoreRegions(text, preserved) {
|
|
153246
|
+
let working = text;
|
|
153247
|
+
for (let i = preserved.length - 1;i >= 0; i--) {
|
|
153248
|
+
working = working.split(preserved[i].placeholder).join(preserved[i].original);
|
|
153249
|
+
}
|
|
153250
|
+
return working;
|
|
153251
|
+
}
|
|
153252
|
+
function buildPhraseDropRegex(phrases) {
|
|
153253
|
+
const escaped = phrases.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
153254
|
+
return new RegExp(`(\\s+)?\\b(?:${escaped.join("|")})\\b`, "gi");
|
|
153255
|
+
}
|
|
153256
|
+
function dropPhrases(text, phrases) {
|
|
153257
|
+
return text.replace(buildPhraseDropRegex(phrases), "");
|
|
153258
|
+
}
|
|
153259
|
+
function dropArticles(text) {
|
|
153260
|
+
let working = text.replace(/\b(?:the|a|an)\b\s+/gi, "");
|
|
153261
|
+
working = working.replace(/ +/g, " ");
|
|
153262
|
+
return working;
|
|
153263
|
+
}
|
|
153264
|
+
function dropAuxiliaries(text) {
|
|
153265
|
+
const sorted = [...AUXILIARIES].sort((a, b) => b.length - a.length);
|
|
153266
|
+
const escaped = sorted.map((a) => a.replace(/\s+/g, "\\s+"));
|
|
153267
|
+
const pattern = new RegExp(`\\s+\\b(?:${escaped.join("|")})\\b\\s+(?=\\w+(?:ed|en|ing|ized|ised)\\b)`, "gi");
|
|
153268
|
+
let working = text.replace(pattern, " ");
|
|
153269
|
+
working = working.replace(/ +/g, " ");
|
|
153270
|
+
return working;
|
|
153271
|
+
}
|
|
153272
|
+
function applyPhraseShortenings(text) {
|
|
153273
|
+
let working = text;
|
|
153274
|
+
for (const [pattern, replacement] of PHRASE_SHORTENINGS) {
|
|
153275
|
+
working = working.replace(pattern, replacement);
|
|
153276
|
+
}
|
|
153277
|
+
return working;
|
|
153278
|
+
}
|
|
153279
|
+
function applyUltraConnectives(text) {
|
|
153280
|
+
let working = text;
|
|
153281
|
+
for (const [pattern, replacement] of ULTRA_CONNECTIVE_REPLACEMENTS) {
|
|
153282
|
+
working = working.replace(pattern, replacement);
|
|
153283
|
+
}
|
|
153284
|
+
return working;
|
|
153285
|
+
}
|
|
153286
|
+
function countWordOccurrences(text, term) {
|
|
153287
|
+
const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
153288
|
+
const matches = text.match(new RegExp(`\\b${escaped}\\b`, "gi"));
|
|
153289
|
+
return matches ? matches.length : 0;
|
|
153290
|
+
}
|
|
153291
|
+
function applyUltraAbbreviations(text) {
|
|
153292
|
+
let working = text;
|
|
153293
|
+
for (const [term, abbreviation] of Object.entries(ULTRA_ABBREVIATIONS)) {
|
|
153294
|
+
if (countWordOccurrences(working, term) < 3)
|
|
153295
|
+
continue;
|
|
153296
|
+
const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
153297
|
+
working = working.replace(new RegExp(`\\b${escaped}\\b`, "gi"), (match) => {
|
|
153298
|
+
return match[0] === match[0].toUpperCase() ? abbreviation[0].toUpperCase() + abbreviation.slice(1) : abbreviation;
|
|
153299
|
+
});
|
|
153300
|
+
}
|
|
153301
|
+
return working;
|
|
153302
|
+
}
|
|
153303
|
+
function transformPreservingUserLines(text, transform2) {
|
|
153304
|
+
const lines = text.split(`
|
|
153305
|
+
`);
|
|
153306
|
+
const output = [];
|
|
153307
|
+
let buffer2 = [];
|
|
153308
|
+
const flushBuffer = () => {
|
|
153309
|
+
if (buffer2.length === 0)
|
|
153310
|
+
return;
|
|
153311
|
+
const joined = buffer2.join(`
|
|
153312
|
+
`);
|
|
153313
|
+
output.push(transform2(joined));
|
|
153314
|
+
buffer2 = [];
|
|
153315
|
+
};
|
|
153316
|
+
for (const line of lines) {
|
|
153317
|
+
if (line.startsWith("U: ")) {
|
|
153318
|
+
flushBuffer();
|
|
153319
|
+
output.push(line);
|
|
153320
|
+
} else {
|
|
153321
|
+
buffer2.push(line);
|
|
153322
|
+
}
|
|
153323
|
+
}
|
|
153324
|
+
flushBuffer();
|
|
153325
|
+
return output.join(`
|
|
153326
|
+
`);
|
|
153327
|
+
}
|
|
153328
|
+
function normalizeWhitespace(text) {
|
|
153329
|
+
return text.split(`
|
|
153330
|
+
`).map((line) => line.replace(/[ \t]+/g, " ").replace(/[ \t]+$/, "")).join(`
|
|
153331
|
+
`).replace(/\n{3,}/g, `
|
|
153332
|
+
|
|
153333
|
+
`);
|
|
153334
|
+
}
|
|
153335
|
+
function cavemanCompress(text, level) {
|
|
153336
|
+
if (text.length === 0)
|
|
153337
|
+
return text;
|
|
153338
|
+
const { text: protectedText, preserved } = protectRegions(text);
|
|
153339
|
+
const transformed = transformPreservingUserLines(protectedText, (chunk) => {
|
|
153340
|
+
let working = chunk;
|
|
153341
|
+
working = dropPhrases(working, FILLER_WORDS);
|
|
153342
|
+
working = dropPhrases(working, HEDGING_PHRASES);
|
|
153343
|
+
working = dropPhrases(working, PLEASANTRIES);
|
|
153344
|
+
working = applyPhraseShortenings(working);
|
|
153345
|
+
if (level === "full" || level === "ultra") {
|
|
153346
|
+
working = dropAuxiliaries(working);
|
|
153347
|
+
working = dropArticles(working);
|
|
153348
|
+
}
|
|
153349
|
+
if (level === "ultra") {
|
|
153350
|
+
working = applyUltraConnectives(working);
|
|
153351
|
+
working = applyUltraAbbreviations(working);
|
|
153352
|
+
}
|
|
153353
|
+
return working;
|
|
153354
|
+
});
|
|
153355
|
+
const restored = restoreRegions(transformed, preserved);
|
|
153356
|
+
return normalizeWhitespace(restored).trim();
|
|
153357
|
+
}
|
|
153358
|
+
var PRESERVATION_PATTERNS, FILLER_WORDS, HEDGING_PHRASES, PLEASANTRIES, AUXILIARIES, PHRASE_SHORTENINGS, ULTRA_CONNECTIVE_REPLACEMENTS, ULTRA_ABBREVIATIONS;
|
|
153359
|
+
var init_caveman = __esm(() => {
|
|
153360
|
+
PRESERVATION_PATTERNS = [
|
|
153361
|
+
/```[\s\S]*?```/g,
|
|
153362
|
+
/`[^`\n]+`/g,
|
|
153363
|
+
/https?:\/\/\S+/g,
|
|
153364
|
+
/\u00A7\d+\u00A7/g,
|
|
153365
|
+
/\b(?:msg|ses|toolu)_[A-Za-z0-9]+/g,
|
|
153366
|
+
/(?:\.{1,2}\/)?(?:[\w.-]+\/)+[\w.-]+\.\w{1,6}/g,
|
|
153367
|
+
/(?<![a-z0-9])[0-9a-f]{7,40}(?![a-z0-9])/gi
|
|
153368
|
+
];
|
|
153369
|
+
FILLER_WORDS = [
|
|
153370
|
+
"just",
|
|
153371
|
+
"really",
|
|
153372
|
+
"basically",
|
|
153373
|
+
"actually",
|
|
153374
|
+
"essentially",
|
|
153375
|
+
"simply",
|
|
153376
|
+
"clearly",
|
|
153377
|
+
"obviously",
|
|
153378
|
+
"quite",
|
|
153379
|
+
"very",
|
|
153380
|
+
"somewhat",
|
|
153381
|
+
"rather",
|
|
153382
|
+
"fairly",
|
|
153383
|
+
"sort of",
|
|
153384
|
+
"kind of",
|
|
153385
|
+
"a bit"
|
|
153386
|
+
];
|
|
153387
|
+
HEDGING_PHRASES = [
|
|
153388
|
+
"i think",
|
|
153389
|
+
"i believe",
|
|
153390
|
+
"i feel",
|
|
153391
|
+
"probably",
|
|
153392
|
+
"perhaps",
|
|
153393
|
+
"maybe",
|
|
153394
|
+
"it seems",
|
|
153395
|
+
"it appears",
|
|
153396
|
+
"arguably",
|
|
153397
|
+
"i suppose",
|
|
153398
|
+
"i guess"
|
|
153399
|
+
];
|
|
153400
|
+
PLEASANTRIES = ["please", "thanks", "thank you", "kindly", "if possible"];
|
|
153401
|
+
AUXILIARIES = [
|
|
153402
|
+
"was",
|
|
153403
|
+
"were",
|
|
153404
|
+
"is",
|
|
153405
|
+
"are",
|
|
153406
|
+
"am",
|
|
153407
|
+
"be",
|
|
153408
|
+
"been",
|
|
153409
|
+
"being",
|
|
153410
|
+
"has been",
|
|
153411
|
+
"had been",
|
|
153412
|
+
"have been",
|
|
153413
|
+
"will be",
|
|
153414
|
+
"would be",
|
|
153415
|
+
"could be",
|
|
153416
|
+
"should be",
|
|
153417
|
+
"might be",
|
|
153418
|
+
"may be"
|
|
153419
|
+
];
|
|
153420
|
+
PHRASE_SHORTENINGS = [
|
|
153421
|
+
[/\bin order to\b/gi, "to"],
|
|
153422
|
+
[/\bdue to the fact that\b/gi, "because"],
|
|
153423
|
+
[/\bat this point in time\b/gi, "now"],
|
|
153424
|
+
[/\bat the moment\b/gi, "now"],
|
|
153425
|
+
[/\bin the event that\b/gi, "if"],
|
|
153426
|
+
[/\bfor the purpose of\b/gi, "for"],
|
|
153427
|
+
[/\bwith regard to\b/gi, "about"],
|
|
153428
|
+
[/\bin spite of the fact that\b/gi, "though"],
|
|
153429
|
+
[/\bon the grounds that\b/gi, "because"],
|
|
153430
|
+
[/\bfor the reason that\b/gi, "because"]
|
|
153431
|
+
];
|
|
153432
|
+
ULTRA_CONNECTIVE_REPLACEMENTS = [
|
|
153433
|
+
[/\b(?:and then|then after|afterwards)\b/gi, "\u2192"],
|
|
153434
|
+
[/\bbecause of\b/gi, "//"],
|
|
153435
|
+
[/\btherefore\b/gi, "\u2192"],
|
|
153436
|
+
[/\bbecause\b/gi, "//"],
|
|
153437
|
+
[/\bhowever\b/gi, "but"],
|
|
153438
|
+
[/\bfurthermore\b/gi, "+"],
|
|
153439
|
+
[/\badditionally\b/gi, "+"],
|
|
153440
|
+
[/\bas well as\b/gi, "+"],
|
|
153441
|
+
[/ and /gi, " + "],
|
|
153442
|
+
[/ or /gi, " | "]
|
|
153443
|
+
];
|
|
153444
|
+
ULTRA_ABBREVIATIONS = {
|
|
153445
|
+
historian: "hist",
|
|
153446
|
+
compartment: "cmpt",
|
|
153447
|
+
compartments: "cmpts",
|
|
153448
|
+
compressor: "cmp",
|
|
153449
|
+
compression: "cmp",
|
|
153450
|
+
context: "ctx",
|
|
153451
|
+
message: "msg",
|
|
153452
|
+
messages: "msgs",
|
|
153453
|
+
session: "ses",
|
|
153454
|
+
configuration: "cfg",
|
|
153455
|
+
config: "cfg",
|
|
153456
|
+
implementation: "impl",
|
|
153457
|
+
implemented: "impl",
|
|
153458
|
+
repository: "repo",
|
|
153459
|
+
database: "db",
|
|
153460
|
+
directory: "dir"
|
|
153461
|
+
};
|
|
153462
|
+
});
|
|
153463
|
+
|
|
153464
|
+
// src/hooks/magic-context/compartment-runner-compressor.ts
|
|
153465
|
+
function cavemanLevelForDepth(outputDepth) {
|
|
153466
|
+
if (outputDepth <= 1)
|
|
153467
|
+
return null;
|
|
153468
|
+
if (outputDepth === 2)
|
|
153469
|
+
return "lite";
|
|
153470
|
+
if (outputDepth === 3)
|
|
153471
|
+
return "full";
|
|
153472
|
+
if (outputDepth === 4)
|
|
153473
|
+
return "ultra";
|
|
153474
|
+
return null;
|
|
153475
|
+
}
|
|
153476
|
+
async function runCompressionPassIfNeeded(deps) {
|
|
153477
|
+
const { db, sessionId, historyBudgetTokens } = deps;
|
|
153478
|
+
const minCompartmentRatio = deps.minCompartmentRatio ?? DEFAULT_COMPRESSOR_MIN_COMPARTMENT_RATIO;
|
|
153479
|
+
const maxMergeDepth = deps.maxMergeDepth ?? DEFAULT_COMPRESSOR_MAX_MERGE_DEPTH;
|
|
153480
|
+
const compartments = getCompartments(db, sessionId);
|
|
153481
|
+
if (compartments.length <= 1)
|
|
153482
|
+
return false;
|
|
153483
|
+
const facts = getSessionFacts(db, sessionId);
|
|
153484
|
+
let totalTokens = 0;
|
|
153485
|
+
for (const c of compartments) {
|
|
153486
|
+
totalTokens += estimateTokens(`<compartment start="${c.startMessage}" end="${c.endMessage}" title="${c.title}">
|
|
153487
|
+
${c.content}
|
|
153488
|
+
</compartment>
|
|
153489
|
+
`);
|
|
153490
|
+
}
|
|
153491
|
+
for (const f of facts) {
|
|
153492
|
+
totalTokens += estimateTokens(`* ${f.content}
|
|
153493
|
+
`);
|
|
153494
|
+
}
|
|
153495
|
+
if (totalTokens <= historyBudgetTokens) {
|
|
153496
|
+
sessionLog(sessionId, `compressor: history block ~${totalTokens} tokens within budget ${historyBudgetTokens}, skipping`);
|
|
153497
|
+
return false;
|
|
153498
|
+
}
|
|
153499
|
+
const lastEndMessage = compartments[compartments.length - 1].endMessage;
|
|
153500
|
+
const floor = Math.max(1, Math.ceil(lastEndMessage / minCompartmentRatio));
|
|
153501
|
+
if (compartments.length <= floor) {
|
|
153502
|
+
sessionLog(sessionId, `compressor: at floor (${compartments.length} compartments, floor=${floor} from ${lastEndMessage} msgs), skipping`);
|
|
153503
|
+
return false;
|
|
153504
|
+
}
|
|
153505
|
+
const overage = totalTokens - historyBudgetTokens;
|
|
153506
|
+
sessionLog(sessionId, `compressor: history block ~${totalTokens} tokens exceeds budget ${historyBudgetTokens} by ~${overage} tokens`);
|
|
153507
|
+
const maxCompartmentsPerPass = deps.maxCompartmentsPerPass ?? DEFAULT_COMPRESSOR_MAX_COMPARTMENTS_PER_PASS;
|
|
153508
|
+
const graceCompartments = deps.graceCompartments ?? DEFAULT_COMPRESSOR_GRACE_COMPARTMENTS;
|
|
153509
|
+
const scored = scoreCompartments(db, sessionId, compartments);
|
|
153510
|
+
const floorHeadroom = compartments.length - floor;
|
|
153511
|
+
if (floorHeadroom < 1) {
|
|
153512
|
+
sessionLog(sessionId, `compressor: no floor headroom (${compartments.length} compartments, floor=${floor}), skipping`);
|
|
153513
|
+
return false;
|
|
153514
|
+
}
|
|
153515
|
+
const contiguous = findOldestContiguousSameDepthBand(scored, {
|
|
153516
|
+
maxPickable: maxCompartmentsPerPass,
|
|
153517
|
+
maxMergeDepth,
|
|
153518
|
+
graceCompartments,
|
|
153519
|
+
floorHeadroom
|
|
153520
|
+
});
|
|
153521
|
+
if (contiguous.length < 2) {
|
|
153522
|
+
sessionLog(sessionId, `compressor: no eligible same-depth band found (floor=${floor}, maxDepth=${maxMergeDepth}, grace=${graceCompartments}, maxPerPass=${maxCompartmentsPerPass}), skipping`);
|
|
153523
|
+
return false;
|
|
153524
|
+
}
|
|
153525
|
+
const firstIndex = contiguous[0].index;
|
|
153526
|
+
const lastIndex = contiguous[contiguous.length - 1].index;
|
|
153527
|
+
const selectedCompartments = contiguous.map((s) => s.compartment);
|
|
153528
|
+
const selectedTokens = contiguous.reduce((t, s) => t + s.tokenEstimate, 0);
|
|
153529
|
+
const overallAverageDepth = contiguous.reduce((sum, s) => sum + s.averageDepth, 0) / contiguous.length;
|
|
153530
|
+
const outputDepth = Math.min(5, Math.max(1, Math.round(overallAverageDepth) + 1));
|
|
153531
|
+
const mergeRatio = COMPRESSOR_MERGE_RATIO_BY_DEPTH[outputDepth] ?? 2;
|
|
153532
|
+
const outputCount = mergeRatio > 0 ? Math.max(1, Math.ceil(contiguous.length / mergeRatio)) : 1;
|
|
153533
|
+
sessionLog(sessionId, `compressor: scored ${compartments.length}, picked ${contiguous.length} contiguous (${selectedCompartments[0].startMessage}-${selectedCompartments[selectedCompartments.length - 1].endMessage}, ~${selectedTokens} tokens), avg_depth=${overallAverageDepth.toFixed(1)} \u2192 output_depth=${outputDepth} (ratio=${mergeRatio}, target=${outputCount} compartments)`);
|
|
153534
|
+
if (outputDepth === 5) {
|
|
153535
|
+
return finalizeCompression({
|
|
153536
|
+
db,
|
|
153537
|
+
sessionId,
|
|
153538
|
+
compartments,
|
|
153539
|
+
leadingCount: firstIndex,
|
|
153540
|
+
trailingIndex: lastIndex + 1,
|
|
153541
|
+
selectedCompartments,
|
|
153542
|
+
compressed: selectedCompartments.map((c) => ({
|
|
153543
|
+
startMessage: c.startMessage,
|
|
153544
|
+
endMessage: c.endMessage,
|
|
153545
|
+
startMessageId: c.startMessageId,
|
|
153546
|
+
endMessageId: c.endMessageId,
|
|
153547
|
+
title: c.title,
|
|
153548
|
+
content: ""
|
|
153549
|
+
})),
|
|
153550
|
+
originalStart: selectedCompartments[0].startMessage,
|
|
153551
|
+
originalEnd: selectedCompartments[selectedCompartments.length - 1].endMessage,
|
|
153552
|
+
facts,
|
|
153553
|
+
logLabel: `depth-5 title-only collapse (${selectedCompartments.length} \u2192 ${selectedCompartments.length})`
|
|
153554
|
+
});
|
|
153555
|
+
}
|
|
153556
|
+
try {
|
|
153557
|
+
const targetTokens = Math.max(200, Math.floor(selectedTokens / mergeRatio));
|
|
153558
|
+
const llmCompressed = await runCompressorPass({
|
|
153559
|
+
...deps,
|
|
153560
|
+
compartments: selectedCompartments,
|
|
153561
|
+
currentTokens: selectedTokens,
|
|
153562
|
+
targetTokens,
|
|
153563
|
+
outputCount,
|
|
153564
|
+
outputDepth
|
|
153565
|
+
});
|
|
153566
|
+
if (!llmCompressed) {
|
|
153567
|
+
sessionLog(sessionId, "compressor: LLM pass failed, keeping existing compartments");
|
|
153568
|
+
return false;
|
|
153569
|
+
}
|
|
153570
|
+
const level = cavemanLevelForDepth(outputDepth);
|
|
153571
|
+
const finalCompressed = level ? llmCompressed.map((c) => ({ ...c, content: cavemanCompress(c.content, level) })) : llmCompressed;
|
|
153572
|
+
return finalizeCompression({
|
|
153573
|
+
db,
|
|
153574
|
+
sessionId,
|
|
153575
|
+
compartments,
|
|
153576
|
+
leadingCount: firstIndex,
|
|
153577
|
+
trailingIndex: lastIndex + 1,
|
|
153578
|
+
selectedCompartments,
|
|
153579
|
+
compressed: finalCompressed,
|
|
153580
|
+
originalStart: selectedCompartments[0].startMessage,
|
|
153581
|
+
originalEnd: selectedCompartments[selectedCompartments.length - 1].endMessage,
|
|
153582
|
+
facts,
|
|
153583
|
+
logLabel: `depth-${outputDepth} (${selectedCompartments.length} \u2192 ${finalCompressed.length})`
|
|
153584
|
+
});
|
|
153585
|
+
} catch (error48) {
|
|
153586
|
+
sessionLog(sessionId, "compressor: unexpected error:", getErrorMessage(error48));
|
|
153587
|
+
return false;
|
|
153588
|
+
}
|
|
153589
|
+
}
|
|
153590
|
+
function scoreCompartments(db, sessionId, compartments) {
|
|
153591
|
+
let maxDepthAcrossSession = 0;
|
|
153592
|
+
for (const c of compartments) {
|
|
153593
|
+
const d = getAverageCompressionDepth(db, sessionId, c.startMessage, c.endMessage);
|
|
153594
|
+
if (d > maxDepthAcrossSession)
|
|
153595
|
+
maxDepthAcrossSession = d;
|
|
153596
|
+
}
|
|
153597
|
+
return compartments.map((compartment, index) => {
|
|
153598
|
+
const tokenEstimate = estimateTokens(`<compartment start="${compartment.startMessage}" end="${compartment.endMessage}" title="${compartment.title}">
|
|
153599
|
+
${compartment.content}
|
|
153600
|
+
</compartment>
|
|
153601
|
+
`);
|
|
153602
|
+
const averageDepth = getAverageCompressionDepth(db, sessionId, compartment.startMessage, compartment.endMessage);
|
|
153603
|
+
const normalizedAge = compartments.length > 1 ? 1 - index / (compartments.length - 1) : 1;
|
|
153604
|
+
const normalizedDepth = maxDepthAcrossSession > 0 ? 1 - averageDepth / maxDepthAcrossSession : 1;
|
|
153605
|
+
const score = 0.7 * normalizedAge + 0.3 * normalizedDepth;
|
|
153606
|
+
return { compartment, index, tokenEstimate, averageDepth, score };
|
|
153607
|
+
});
|
|
153608
|
+
}
|
|
153609
|
+
function findOldestContiguousSameDepthBand(scored, constraints) {
|
|
153610
|
+
const { maxPickable, maxMergeDepth, graceCompartments, floorHeadroom } = constraints;
|
|
153611
|
+
const hardMaxPick = Math.max(0, Math.min(maxPickable, floorHeadroom));
|
|
153612
|
+
if (hardMaxPick < 2)
|
|
153613
|
+
return [];
|
|
153614
|
+
const scanEnd = Math.max(0, scored.length - graceCompartments);
|
|
153615
|
+
if (scanEnd < 2)
|
|
153616
|
+
return [];
|
|
153617
|
+
let i = 0;
|
|
153618
|
+
while (i < scanEnd) {
|
|
153619
|
+
const c = scored[i];
|
|
153620
|
+
if (!c || c.averageDepth >= maxMergeDepth) {
|
|
153621
|
+
i++;
|
|
153622
|
+
continue;
|
|
153623
|
+
}
|
|
153624
|
+
const anchorDepth = Math.round(c.averageDepth);
|
|
153625
|
+
let j = i;
|
|
153626
|
+
while (j < scanEnd) {
|
|
153627
|
+
const entry = scored[j];
|
|
153628
|
+
if (!entry)
|
|
153629
|
+
break;
|
|
153630
|
+
if (entry.averageDepth >= maxMergeDepth)
|
|
153631
|
+
break;
|
|
153632
|
+
if (Math.round(entry.averageDepth) !== anchorDepth)
|
|
153633
|
+
break;
|
|
153634
|
+
if (j - i >= hardMaxPick)
|
|
153635
|
+
break;
|
|
153636
|
+
j++;
|
|
153637
|
+
}
|
|
153638
|
+
const runLen = j - i;
|
|
153639
|
+
if (runLen >= 2) {
|
|
153640
|
+
return scored.slice(i, j);
|
|
153641
|
+
}
|
|
153642
|
+
i = Math.max(j, i + 1);
|
|
153643
|
+
}
|
|
153644
|
+
return [];
|
|
153645
|
+
}
|
|
153646
|
+
function snapLLMOutputToInputBoundaries(llmOutput, inputCompartments) {
|
|
153647
|
+
const sorted = [...inputCompartments].sort((a, b) => a.startMessage - b.startMessage);
|
|
153648
|
+
const containing = (ord) => {
|
|
153649
|
+
let lo = 0;
|
|
153650
|
+
let hi = sorted.length - 1;
|
|
153651
|
+
while (lo <= hi) {
|
|
153652
|
+
const mid = lo + hi >> 1;
|
|
153653
|
+
const c = sorted[mid];
|
|
153654
|
+
if (!c)
|
|
153655
|
+
return null;
|
|
153656
|
+
if (ord < c.startMessage)
|
|
153657
|
+
hi = mid - 1;
|
|
153658
|
+
else if (ord > c.endMessage)
|
|
153659
|
+
lo = mid + 1;
|
|
153660
|
+
else
|
|
153661
|
+
return c;
|
|
153662
|
+
}
|
|
153663
|
+
return null;
|
|
153664
|
+
};
|
|
153665
|
+
const result = [];
|
|
153666
|
+
let snapCount = 0;
|
|
153667
|
+
for (const pc of llmOutput) {
|
|
153668
|
+
const startOwner = containing(pc.startMessage);
|
|
153669
|
+
const endOwner = containing(pc.endMessage);
|
|
153670
|
+
if (!startOwner || !endOwner) {
|
|
153671
|
+
return null;
|
|
153672
|
+
}
|
|
153673
|
+
if (startOwner.startMessage !== pc.startMessage)
|
|
153674
|
+
snapCount++;
|
|
153675
|
+
if (endOwner.endMessage !== pc.endMessage)
|
|
153676
|
+
snapCount++;
|
|
153677
|
+
result.push({
|
|
153678
|
+
startMessage: startOwner.startMessage,
|
|
153679
|
+
endMessage: endOwner.endMessage,
|
|
153680
|
+
startMessageId: startOwner.startMessageId,
|
|
153681
|
+
endMessageId: endOwner.endMessageId,
|
|
153682
|
+
title: pc.title,
|
|
153683
|
+
content: pc.content
|
|
153684
|
+
});
|
|
153685
|
+
}
|
|
153686
|
+
return { result, snapCount };
|
|
153687
|
+
}
|
|
153688
|
+
function finalizeCompression(args) {
|
|
153689
|
+
const {
|
|
153690
|
+
db,
|
|
153691
|
+
sessionId,
|
|
153692
|
+
compartments,
|
|
153693
|
+
leadingCount,
|
|
153694
|
+
trailingIndex,
|
|
153695
|
+
selectedCompartments: _selectedCompartments,
|
|
153696
|
+
compressed,
|
|
153697
|
+
originalStart,
|
|
153698
|
+
originalEnd,
|
|
153699
|
+
facts,
|
|
153700
|
+
logLabel
|
|
153701
|
+
} = args;
|
|
153702
|
+
const compressedStart = compressed[0].startMessage;
|
|
153703
|
+
const compressedEnd = compressed[compressed.length - 1].endMessage;
|
|
153704
|
+
if (compressedStart !== originalStart || compressedEnd !== originalEnd) {
|
|
153705
|
+
sessionLog(sessionId, `compressor: compressed range ${compressedStart}-${compressedEnd} doesn't match original ${originalStart}-${originalEnd}, aborting`);
|
|
153706
|
+
return false;
|
|
153707
|
+
}
|
|
153708
|
+
for (let i = 1;i < compressed.length; i++) {
|
|
153709
|
+
const prev = compressed[i - 1];
|
|
153710
|
+
const curr = compressed[i];
|
|
153711
|
+
if (curr.startMessage <= prev.endMessage) {
|
|
153712
|
+
sessionLog(sessionId, `compressor: overlap at compartment ${i}, aborting`);
|
|
153713
|
+
return false;
|
|
153714
|
+
}
|
|
153715
|
+
if (curr.startMessage > prev.endMessage + 1) {
|
|
153716
|
+
sessionLog(sessionId, `compressor: gap at compartment ${i}, aborting`);
|
|
153717
|
+
return false;
|
|
153718
|
+
}
|
|
153719
|
+
}
|
|
153720
|
+
const leading = compartments.slice(0, leadingCount);
|
|
153721
|
+
const trailing = compartments.slice(trailingIndex);
|
|
153722
|
+
const allCompartments = [
|
|
153723
|
+
...leading.map((c, i) => ({
|
|
153724
|
+
sequence: i,
|
|
153725
|
+
startMessage: c.startMessage,
|
|
153726
|
+
endMessage: c.endMessage,
|
|
153727
|
+
startMessageId: c.startMessageId,
|
|
153728
|
+
endMessageId: c.endMessageId,
|
|
153729
|
+
title: c.title,
|
|
153730
|
+
content: c.content
|
|
153731
|
+
})),
|
|
153732
|
+
...compressed.map((c, i) => ({
|
|
153733
|
+
sequence: leading.length + i,
|
|
153734
|
+
startMessage: c.startMessage,
|
|
153735
|
+
endMessage: c.endMessage,
|
|
153736
|
+
startMessageId: c.startMessageId,
|
|
153737
|
+
endMessageId: c.endMessageId,
|
|
153738
|
+
title: c.title,
|
|
153739
|
+
content: c.content
|
|
153740
|
+
})),
|
|
153741
|
+
...trailing.map((c, i) => ({
|
|
153742
|
+
sequence: leading.length + compressed.length + i,
|
|
153743
|
+
startMessage: c.startMessage,
|
|
153744
|
+
endMessage: c.endMessage,
|
|
153745
|
+
startMessageId: c.startMessageId,
|
|
153746
|
+
endMessageId: c.endMessageId,
|
|
153747
|
+
title: c.title,
|
|
153748
|
+
content: c.content
|
|
153749
|
+
}))
|
|
153750
|
+
];
|
|
153751
|
+
replaceAllCompartmentState(db, sessionId, allCompartments, facts.map((f) => ({ category: f.category, content: f.content })));
|
|
153752
|
+
incrementCompressionDepth(db, sessionId, originalStart, originalEnd);
|
|
153753
|
+
sessionLog(sessionId, `compressor: completed ${logLabel}`);
|
|
153754
|
+
return true;
|
|
153755
|
+
}
|
|
153756
|
+
async function runCompressorPass(args) {
|
|
153757
|
+
const {
|
|
153758
|
+
client,
|
|
153759
|
+
sessionId,
|
|
153760
|
+
directory,
|
|
153761
|
+
compartments,
|
|
153762
|
+
currentTokens,
|
|
153763
|
+
targetTokens,
|
|
153764
|
+
outputCount,
|
|
153765
|
+
outputDepth,
|
|
153766
|
+
historianTimeoutMs
|
|
153767
|
+
} = args;
|
|
153768
|
+
const prompt = buildCompressorPrompt(compartments, currentTokens, targetTokens, outputDepth, outputCount);
|
|
153769
|
+
let agentSessionId = null;
|
|
153770
|
+
try {
|
|
153771
|
+
const createResponse = await client.session.create({
|
|
153772
|
+
body: { parentID: sessionId, title: "magic-context-compressor" },
|
|
153773
|
+
query: { directory }
|
|
153774
|
+
});
|
|
153775
|
+
const createdSession = normalizeSDKResponse(createResponse, null, { preferResponseOnMissingData: true });
|
|
153776
|
+
agentSessionId = typeof createdSession?.id === "string" ? createdSession.id : null;
|
|
153777
|
+
if (!agentSessionId) {
|
|
153778
|
+
sessionLog(sessionId, "compressor: could not create child session");
|
|
153779
|
+
return null;
|
|
153780
|
+
}
|
|
153781
|
+
await promptSyncWithModelSuggestionRetry(client, {
|
|
153782
|
+
path: { id: agentSessionId },
|
|
153783
|
+
query: { directory },
|
|
153784
|
+
body: {
|
|
153785
|
+
agent: HISTORIAN_AGENT2,
|
|
153786
|
+
parts: [{ type: "text", text: prompt }]
|
|
153787
|
+
}
|
|
153788
|
+
}, { timeoutMs: historianTimeoutMs ?? DEFAULT_HISTORIAN_TIMEOUT_MS });
|
|
153789
|
+
const messagesResponse = await client.session.messages({
|
|
153790
|
+
path: { id: agentSessionId },
|
|
153791
|
+
query: { directory }
|
|
153792
|
+
});
|
|
153793
|
+
const messages = normalizeSDKResponse(messagesResponse, [], {
|
|
153794
|
+
preferResponseOnMissingData: true
|
|
153795
|
+
});
|
|
153796
|
+
const result = extractLatestAssistantText(messages);
|
|
153797
|
+
if (!result) {
|
|
153798
|
+
sessionLog(sessionId, "compressor: historian returned no output");
|
|
153799
|
+
return null;
|
|
153800
|
+
}
|
|
153801
|
+
const parsed = parseCompartmentOutput(result);
|
|
153802
|
+
if (parsed.compartments.length === 0) {
|
|
153803
|
+
sessionLog(sessionId, "compressor: historian returned no compartments");
|
|
153804
|
+
return null;
|
|
153805
|
+
}
|
|
153806
|
+
const snapped = snapLLMOutputToInputBoundaries(parsed.compartments, compartments);
|
|
153807
|
+
if (!snapped) {
|
|
153808
|
+
sessionLog(sessionId, "compressor: rejecting \u2014 LLM output contains ordinal(s) outside input range");
|
|
153809
|
+
return null;
|
|
153810
|
+
}
|
|
153811
|
+
if (snapped.snapCount > 0) {
|
|
153812
|
+
sessionLog(sessionId, `compressor: snapped ${snapped.snapCount} LLM boundary value(s) to input compartment boundaries`);
|
|
153813
|
+
}
|
|
153814
|
+
return snapped.result;
|
|
153815
|
+
} catch (error48) {
|
|
153816
|
+
sessionLog(sessionId, "compressor: historian call failed:", getErrorMessage(error48));
|
|
153817
|
+
return null;
|
|
153818
|
+
} finally {
|
|
153819
|
+
if (agentSessionId) {
|
|
153820
|
+
await client.session.delete({ path: { id: agentSessionId }, query: { directory } }).catch((e) => {
|
|
153821
|
+
sessionLog(sessionId, "compressor: session cleanup failed:", getErrorMessage(e));
|
|
153822
|
+
});
|
|
153823
|
+
}
|
|
153824
|
+
}
|
|
153825
|
+
}
|
|
153826
|
+
var HISTORIAN_AGENT2 = "historian";
|
|
153827
|
+
var init_compartment_runner_compressor = __esm(() => {
|
|
153828
|
+
init_magic_context();
|
|
153829
|
+
init_storage();
|
|
153830
|
+
init_shared();
|
|
153831
|
+
init_assistant_message_extractor();
|
|
153832
|
+
init_logger();
|
|
153833
|
+
init_caveman();
|
|
153834
|
+
init_compartment_parser();
|
|
153835
|
+
init_compartment_prompt();
|
|
153836
|
+
init_read_session_formatting();
|
|
153837
|
+
});
|
|
153838
|
+
|
|
153839
|
+
// src/hooks/magic-context/compartment-runner-drop-queue.ts
|
|
153840
|
+
function queueDropsForCompartmentalizedMessages(db, sessionId, upToMessageIndex) {
|
|
153841
|
+
const tags = getTagsBySession(db, sessionId);
|
|
153842
|
+
const rawTagKeys = new Set(getRawSessionTagKeysThrough(sessionId, upToMessageIndex));
|
|
153843
|
+
let dropsQueued = 0;
|
|
153844
|
+
for (const tag of tags) {
|
|
153845
|
+
if (tag.status !== "active")
|
|
153846
|
+
continue;
|
|
153847
|
+
if (rawTagKeys.has(tag.messageId)) {
|
|
153848
|
+
queuePendingOp(db, sessionId, tag.tagNumber, "drop");
|
|
153849
|
+
dropsQueued += 1;
|
|
153850
|
+
}
|
|
153851
|
+
}
|
|
153852
|
+
sessionLog(sessionId, `compartment agent: queued ${dropsQueued} drops for messages 0-${upToMessageIndex}`);
|
|
153853
|
+
}
|
|
153854
|
+
var init_compartment_runner_drop_queue = __esm(() => {
|
|
153855
|
+
init_storage_ops();
|
|
153856
|
+
init_storage_tags();
|
|
153857
|
+
init_logger();
|
|
153858
|
+
init_read_session_chunk();
|
|
153859
|
+
});
|
|
153860
|
+
|
|
152762
153861
|
// src/hooks/magic-context/compartment-runner-incremental.ts
|
|
152763
153862
|
function shouldSuppressHistorianAlert(sessionId) {
|
|
152764
153863
|
const lastAlert = lastHistorianAlertBySession.get(sessionId);
|
|
@@ -152833,6 +153932,8 @@ ${chunk.text}`);
|
|
|
152833
153932
|
const parentSessionResponse = await client.session.get({ path: { id: sessionId } }).catch(() => null);
|
|
152834
153933
|
const parentSession = normalizeSDKResponse(parentSessionResponse, null, { preferResponseOnMissingData: true });
|
|
152835
153934
|
const sessionDirectory = parentSession?.directory ?? directory;
|
|
153935
|
+
const maxExistingSequence = priorCompartments.reduce((max, c) => c.sequence > max ? c.sequence : max, -1);
|
|
153936
|
+
const sequenceOffset = priorCompartments.length === 0 ? 0 : maxExistingSequence + 1;
|
|
152836
153937
|
const validatedPass = await runValidatedHistorianPass({
|
|
152837
153938
|
client,
|
|
152838
153939
|
parentSessionId: sessionId,
|
|
@@ -152840,10 +153941,11 @@ ${chunk.text}`);
|
|
|
152840
153941
|
prompt,
|
|
152841
153942
|
chunk,
|
|
152842
153943
|
priorCompartments,
|
|
152843
|
-
sequenceOffset
|
|
153944
|
+
sequenceOffset,
|
|
152844
153945
|
dumpLabelBase: `incremental-${sessionId}-${chunk.startIndex}-${chunk.endIndex}`,
|
|
152845
153946
|
timeoutMs: historianTimeoutMs,
|
|
152846
|
-
fallbackModelId: deps.fallbackModelId
|
|
153947
|
+
fallbackModelId: deps.fallbackModelId,
|
|
153948
|
+
twoPass: deps.historianTwoPass
|
|
152847
153949
|
});
|
|
152848
153950
|
if (!validatedPass.ok) {
|
|
152849
153951
|
incrementHistorianFailure(db, sessionId, validatedPass.error);
|
|
@@ -152885,7 +153987,9 @@ No new compartments or facts were written. Check the historian model/output and
|
|
|
152885
153987
|
sessionId,
|
|
152886
153988
|
directory: sessionDirectory,
|
|
152887
153989
|
historyBudgetTokens: deps.historyBudgetTokens,
|
|
152888
|
-
historianTimeoutMs
|
|
153990
|
+
historianTimeoutMs,
|
|
153991
|
+
minCompartmentRatio: deps.compressorMinCompartmentRatio,
|
|
153992
|
+
maxMergeDepth: deps.compressorMaxMergeDepth
|
|
152889
153993
|
});
|
|
152890
153994
|
}
|
|
152891
153995
|
updateSessionMeta(db, sessionId, { compartmentInProgress: false });
|
|
@@ -152969,6 +154073,7 @@ async function executeContextRecompInternal(deps) {
|
|
|
152969
154073
|
const promoted2 = promoteRecompStaging(db, sessionId);
|
|
152970
154074
|
if (!promoted2)
|
|
152971
154075
|
return null;
|
|
154076
|
+
clearCompressionDepth(db, sessionId);
|
|
152972
154077
|
clearInjectionCache(sessionId);
|
|
152973
154078
|
if (deps.directory) {
|
|
152974
154079
|
promoteSessionFactsToMemory(db, sessionId, resolveProjectIdentity(deps.directory), promoted2.facts);
|
|
@@ -153060,6 +154165,7 @@ Historian pass ${passCount + 1}, attempt ${passAttempt} started for messages ${c
|
|
|
153060
154165
|
dumpLabelBase: `recomp-${sessionId}-${chunk.startIndex}-${chunk.endIndex}-pass-${passCount + 1}`,
|
|
153061
154166
|
timeoutMs: historianTimeoutMs,
|
|
153062
154167
|
fallbackModelId: deps.fallbackModelId,
|
|
154168
|
+
twoPass: deps.historianTwoPass,
|
|
153063
154169
|
callbacks: {
|
|
153064
154170
|
onRepairRetry: async (error48) => {
|
|
153065
154171
|
await sendIgnoredMessage(client, sessionId, `## Magic Recomp
|
|
@@ -153135,6 +154241,7 @@ Nothing was written.`;
|
|
|
153135
154241
|
replaceAllCompartmentState(db, sessionId, candidateCompartments, candidateFacts);
|
|
153136
154242
|
clearRecompStaging(db, sessionId);
|
|
153137
154243
|
}
|
|
154244
|
+
clearCompressionDepth(db, sessionId);
|
|
153138
154245
|
clearInjectionCache(sessionId);
|
|
153139
154246
|
const finalCompartments = promoted?.compartments ?? candidateCompartments;
|
|
153140
154247
|
const finalFacts = promoted?.facts ?? candidateFacts;
|
|
@@ -153153,7 +154260,9 @@ Nothing was written.`;
|
|
|
153153
154260
|
sessionId,
|
|
153154
154261
|
directory: sessionDirectory,
|
|
153155
154262
|
historyBudgetTokens: deps.historyBudgetTokens,
|
|
153156
|
-
historianTimeoutMs
|
|
154263
|
+
historianTimeoutMs,
|
|
154264
|
+
minCompartmentRatio: deps.compressorMinCompartmentRatio,
|
|
154265
|
+
maxMergeDepth: deps.compressorMaxMergeDepth
|
|
153157
154266
|
});
|
|
153158
154267
|
}
|
|
153159
154268
|
return [
|
|
@@ -153179,6 +154288,7 @@ Staging data preserved for resume on next attempt.`;
|
|
|
153179
154288
|
}
|
|
153180
154289
|
var init_compartment_runner_recomp = __esm(() => {
|
|
153181
154290
|
init_compartment_storage();
|
|
154291
|
+
init_compression_depth_storage();
|
|
153182
154292
|
init_memory();
|
|
153183
154293
|
init_project_identity();
|
|
153184
154294
|
init_storage_memory();
|
|
@@ -153201,12 +154311,21 @@ var exports_compartment_runner = {};
|
|
|
153201
154311
|
__export(exports_compartment_runner, {
|
|
153202
154312
|
startCompartmentAgent: () => startCompartmentAgent,
|
|
153203
154313
|
runCompartmentAgent: () => runCompartmentAgent,
|
|
154314
|
+
registerActiveCompartmentRun: () => registerActiveCompartmentRun,
|
|
153204
154315
|
getActiveCompartmentRun: () => getActiveCompartmentRun,
|
|
153205
154316
|
executeContextRecomp: () => executeContextRecomp
|
|
153206
154317
|
});
|
|
153207
154318
|
function getActiveCompartmentRun(sessionId) {
|
|
153208
154319
|
return activeRuns.get(sessionId);
|
|
153209
154320
|
}
|
|
154321
|
+
function registerActiveCompartmentRun(sessionId, promise2) {
|
|
154322
|
+
const wrapped = promise2.finally(() => {
|
|
154323
|
+
if (activeRuns.get(sessionId) === wrapped) {
|
|
154324
|
+
activeRuns.delete(sessionId);
|
|
154325
|
+
}
|
|
154326
|
+
});
|
|
154327
|
+
activeRuns.set(sessionId, wrapped);
|
|
154328
|
+
}
|
|
153210
154329
|
function startCompartmentAgent(deps) {
|
|
153211
154330
|
const existing = activeRuns.get(deps.sessionId);
|
|
153212
154331
|
if (existing) {
|
|
@@ -153222,12 +154341,12 @@ function startCompartmentAgent(deps) {
|
|
|
153222
154341
|
});
|
|
153223
154342
|
activeRuns.set(deps.sessionId, promise2);
|
|
153224
154343
|
}
|
|
153225
|
-
async function executeContextRecomp(deps) {
|
|
154344
|
+
async function executeContextRecomp(deps, options = {}) {
|
|
153226
154345
|
const { sessionId } = deps;
|
|
153227
154346
|
if (activeRuns.has(sessionId)) {
|
|
153228
154347
|
return "## Magic Recomp\n\nHistorian is already running for this session. Wait for it to finish, then try `/ctx-recomp` again.";
|
|
153229
154348
|
}
|
|
153230
|
-
const promise2 = executeContextRecompInternal(deps);
|
|
154349
|
+
const promise2 = options.range ? executePartialRecompInternal(deps, options.range) : executeContextRecompInternal(deps);
|
|
153231
154350
|
activeRuns.set(sessionId, promise2.then(() => {
|
|
153232
154351
|
return;
|
|
153233
154352
|
}).catch((err) => {
|
|
@@ -153244,6 +154363,7 @@ var init_compartment_runner = __esm(() => {
|
|
|
153244
154363
|
init_storage_meta();
|
|
153245
154364
|
init_logger();
|
|
153246
154365
|
init_compartment_runner_incremental();
|
|
154366
|
+
init_compartment_runner_partial_recomp();
|
|
153247
154367
|
init_compartment_runner_recomp();
|
|
153248
154368
|
init_compartment_runner_incremental();
|
|
153249
154369
|
activeRuns = new Map;
|
|
@@ -161011,15 +162131,15 @@ var exports_tui_config = {};
|
|
|
161011
162131
|
__export(exports_tui_config, {
|
|
161012
162132
|
ensureTuiPluginEntry: () => ensureTuiPluginEntry
|
|
161013
162133
|
});
|
|
161014
|
-
import { existsSync as
|
|
161015
|
-
import { dirname as
|
|
162134
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
|
|
162135
|
+
import { dirname as dirname3, join as join16 } from "path";
|
|
161016
162136
|
function resolveTuiConfigPath() {
|
|
161017
162137
|
const configDir = getOpenCodeConfigPaths({ binary: "opencode" }).configDir;
|
|
161018
162138
|
const jsoncPath = join16(configDir, "tui.jsonc");
|
|
161019
162139
|
const jsonPath = join16(configDir, "tui.json");
|
|
161020
|
-
if (
|
|
162140
|
+
if (existsSync8(jsoncPath))
|
|
161021
162141
|
return jsoncPath;
|
|
161022
|
-
if (
|
|
162142
|
+
if (existsSync8(jsonPath))
|
|
161023
162143
|
return jsonPath;
|
|
161024
162144
|
return jsonPath;
|
|
161025
162145
|
}
|
|
@@ -161027,8 +162147,8 @@ function ensureTuiPluginEntry() {
|
|
|
161027
162147
|
try {
|
|
161028
162148
|
const configPath = resolveTuiConfigPath();
|
|
161029
162149
|
let config2 = {};
|
|
161030
|
-
if (
|
|
161031
|
-
const raw =
|
|
162150
|
+
if (existsSync8(configPath)) {
|
|
162151
|
+
const raw = readFileSync7(configPath, "utf-8");
|
|
161032
162152
|
config2 = import_comment_json.parse(raw) ?? {};
|
|
161033
162153
|
}
|
|
161034
162154
|
const plugins = Array.isArray(config2.plugin) ? config2.plugin.filter((p) => typeof p === "string") : [];
|
|
@@ -161047,7 +162167,7 @@ function ensureTuiPluginEntry() {
|
|
|
161047
162167
|
plugins.push(PLUGIN_ENTRY);
|
|
161048
162168
|
}
|
|
161049
162169
|
config2.plugin = plugins;
|
|
161050
|
-
mkdirSync5(
|
|
162170
|
+
mkdirSync5(dirname3(configPath), { recursive: true });
|
|
161051
162171
|
writeFileSync3(configPath, `${import_comment_json.stringify(config2, null, 2)}
|
|
161052
162172
|
`);
|
|
161053
162173
|
log(`[magic-context] updated TUI plugin entry in ${configPath}`);
|
|
@@ -161067,12 +162187,76 @@ var init_tui_config = __esm(() => {
|
|
|
161067
162187
|
// src/config/index.ts
|
|
161068
162188
|
init_jsonc_parser();
|
|
161069
162189
|
init_magic_context();
|
|
162190
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
162191
|
+
import { homedir as homedir2 } from "os";
|
|
162192
|
+
import { join } from "path";
|
|
162193
|
+
|
|
162194
|
+
// src/config/variable.ts
|
|
161070
162195
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
161071
162196
|
import { homedir } from "os";
|
|
161072
|
-
import {
|
|
162197
|
+
import { dirname, isAbsolute, resolve } from "path";
|
|
162198
|
+
var ENV_PATTERN = /\{env:([^}]+)\}/g;
|
|
162199
|
+
var FILE_PATTERN = /\{file:([^}]+)\}/g;
|
|
162200
|
+
function substituteConfigVariables(input) {
|
|
162201
|
+
const warnings = [];
|
|
162202
|
+
let text = input.text;
|
|
162203
|
+
text = text.replace(ENV_PATTERN, (_, rawName) => {
|
|
162204
|
+
const varName = rawName.trim();
|
|
162205
|
+
const value = varName ? process.env[varName] : undefined;
|
|
162206
|
+
if (value === undefined || value === "") {
|
|
162207
|
+
warnings.push(`Environment variable ${varName} is not set (referenced via {env:${varName}}); using empty string`);
|
|
162208
|
+
return "";
|
|
162209
|
+
}
|
|
162210
|
+
return value;
|
|
162211
|
+
});
|
|
162212
|
+
const fileMatches = Array.from(text.matchAll(FILE_PATTERN));
|
|
162213
|
+
if (fileMatches.length === 0) {
|
|
162214
|
+
return { text, warnings };
|
|
162215
|
+
}
|
|
162216
|
+
const configDir = input.configPath ? dirname(input.configPath) : process.cwd();
|
|
162217
|
+
let output = "";
|
|
162218
|
+
let cursor = 0;
|
|
162219
|
+
for (const match of fileMatches) {
|
|
162220
|
+
const token = match[0];
|
|
162221
|
+
const rawPath = match[1] ?? "";
|
|
162222
|
+
const index = match.index ?? 0;
|
|
162223
|
+
output += text.slice(cursor, index);
|
|
162224
|
+
cursor = index + token.length;
|
|
162225
|
+
const lineStart = text.lastIndexOf(`
|
|
162226
|
+
`, index - 1) + 1;
|
|
162227
|
+
const prefix = text.slice(lineStart, index).trimStart();
|
|
162228
|
+
if (prefix.startsWith("//")) {
|
|
162229
|
+
output += token;
|
|
162230
|
+
continue;
|
|
162231
|
+
}
|
|
162232
|
+
let filePath = rawPath.trim();
|
|
162233
|
+
if (filePath.startsWith("~/")) {
|
|
162234
|
+
filePath = resolve(homedir(), filePath.slice(2));
|
|
162235
|
+
} else if (!isAbsolute(filePath)) {
|
|
162236
|
+
filePath = resolve(configDir, filePath);
|
|
162237
|
+
}
|
|
162238
|
+
if (!existsSync2(filePath)) {
|
|
162239
|
+
warnings.push(`File not found for ${token} (resolved to ${filePath}); using empty string`);
|
|
162240
|
+
continue;
|
|
162241
|
+
}
|
|
162242
|
+
let contents;
|
|
162243
|
+
try {
|
|
162244
|
+
contents = readFileSync2(filePath, "utf-8").trim();
|
|
162245
|
+
} catch (error48) {
|
|
162246
|
+
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
162247
|
+
warnings.push(`Failed to read file for ${token} (${filePath}): ${message}; using empty string`);
|
|
162248
|
+
continue;
|
|
162249
|
+
}
|
|
162250
|
+
output += JSON.stringify(contents).slice(1, -1);
|
|
162251
|
+
}
|
|
162252
|
+
output += text.slice(cursor);
|
|
162253
|
+
return { text: output, warnings };
|
|
162254
|
+
}
|
|
162255
|
+
|
|
162256
|
+
// src/config/index.ts
|
|
161073
162257
|
var CONFIG_FILE_BASENAME = "magic-context";
|
|
161074
162258
|
function getUserConfigBasePath() {
|
|
161075
|
-
const configRoot = process.env.XDG_CONFIG_HOME ?? join(
|
|
162259
|
+
const configRoot = process.env.XDG_CONFIG_HOME ?? join(homedir2(), ".config");
|
|
161076
162260
|
return join(configRoot, "opencode", CONFIG_FILE_BASENAME);
|
|
161077
162261
|
}
|
|
161078
162262
|
function getProjectConfigBasePath(directory) {
|
|
@@ -161080,10 +162264,15 @@ function getProjectConfigBasePath(directory) {
|
|
|
161080
162264
|
}
|
|
161081
162265
|
function loadConfigFile(configPath) {
|
|
161082
162266
|
try {
|
|
161083
|
-
if (!
|
|
162267
|
+
if (!existsSync3(configPath)) {
|
|
161084
162268
|
return null;
|
|
161085
162269
|
}
|
|
161086
|
-
|
|
162270
|
+
const rawText = readFileSync3(configPath, "utf-8");
|
|
162271
|
+
const substituted = substituteConfigVariables({ text: rawText, configPath });
|
|
162272
|
+
return {
|
|
162273
|
+
config: parseJsonc(substituted.text),
|
|
162274
|
+
warnings: substituted.warnings.map((w) => `${configPath}: ${w}`)
|
|
162275
|
+
};
|
|
161087
162276
|
} catch (error48) {
|
|
161088
162277
|
console.warn(`[magic-context] failed to load config from ${configPath}:`, error48 instanceof Error ? error48.message : String(error48));
|
|
161089
162278
|
return null;
|
|
@@ -161117,6 +162306,25 @@ function mergeConfigs(base, override) {
|
|
|
161117
162306
|
};
|
|
161118
162307
|
return config2;
|
|
161119
162308
|
}
|
|
162309
|
+
function redactConfigValue(value) {
|
|
162310
|
+
if (value === undefined)
|
|
162311
|
+
return "<missing>";
|
|
162312
|
+
if (value === null)
|
|
162313
|
+
return "null";
|
|
162314
|
+
if (typeof value === "string")
|
|
162315
|
+
return `string, ${value.length} char${value.length === 1 ? "" : "s"}`;
|
|
162316
|
+
if (typeof value === "number")
|
|
162317
|
+
return `number ${value}`;
|
|
162318
|
+
if (typeof value === "boolean")
|
|
162319
|
+
return `boolean ${value}`;
|
|
162320
|
+
if (Array.isArray(value))
|
|
162321
|
+
return `array, ${value.length} item${value.length === 1 ? "" : "s"}`;
|
|
162322
|
+
if (typeof value === "object") {
|
|
162323
|
+
const keys = Object.keys(value);
|
|
162324
|
+
return `object with keys [${keys.join(", ")}]`;
|
|
162325
|
+
}
|
|
162326
|
+
return typeof value;
|
|
162327
|
+
}
|
|
161120
162328
|
function parsePluginConfig(rawConfig) {
|
|
161121
162329
|
const parsed = MagicContextConfigSchema.safeParse(rawConfig);
|
|
161122
162330
|
const disabledHooks = Array.isArray(rawConfig.disabled_hooks) ? rawConfig.disabled_hooks.filter((value) => typeof value === "string") : undefined;
|
|
@@ -161146,7 +162354,7 @@ function parsePluginConfig(rawConfig) {
|
|
|
161146
162354
|
} else {
|
|
161147
162355
|
delete patched[key];
|
|
161148
162356
|
const defaultVal = defaults[key];
|
|
161149
|
-
warnings.push(`"${key}": invalid value ${
|
|
162357
|
+
warnings.push(`"${key}": invalid value (${redactConfigValue(rawConfig[key])}), using default ${JSON.stringify(defaultVal)}.`);
|
|
161150
162358
|
}
|
|
161151
162359
|
}
|
|
161152
162360
|
const retryParsed = MagicContextConfigSchema.safeParse(patched);
|
|
@@ -161166,19 +162374,21 @@ function loadPluginConfig(directory) {
|
|
|
161166
162374
|
const rootDetected = detectConfigFile(join(directory, CONFIG_FILE_BASENAME));
|
|
161167
162375
|
const dotOpenCodeDetected = detectConfigFile(getProjectConfigBasePath(directory));
|
|
161168
162376
|
const projectDetected = rootDetected.format !== "none" ? rootDetected : dotOpenCodeDetected;
|
|
161169
|
-
const
|
|
161170
|
-
const
|
|
162377
|
+
const userLoaded = userDetected.format === "none" ? null : loadConfigFile(userDetected.path);
|
|
162378
|
+
const projectLoaded = projectDetected.format === "none" ? null : loadConfigFile(projectDetected.path);
|
|
161171
162379
|
let config2 = parsePluginConfig({});
|
|
161172
162380
|
const allWarnings = [];
|
|
161173
|
-
if (
|
|
161174
|
-
|
|
162381
|
+
if (userLoaded) {
|
|
162382
|
+
allWarnings.push(...userLoaded.warnings.map((w) => `[user config] ${w}`));
|
|
162383
|
+
const parsed = parsePluginConfig(userLoaded.config);
|
|
161175
162384
|
if (parsed.configWarnings?.length) {
|
|
161176
162385
|
allWarnings.push(...parsed.configWarnings.map((w) => `[user config] ${w}`));
|
|
161177
162386
|
}
|
|
161178
162387
|
config2 = mergeConfigs(config2, parsed);
|
|
161179
162388
|
}
|
|
161180
|
-
if (
|
|
161181
|
-
|
|
162389
|
+
if (projectLoaded) {
|
|
162390
|
+
allWarnings.push(...projectLoaded.warnings.map((w) => `[project config] ${w}`));
|
|
162391
|
+
const parsed = parsePluginConfig(projectLoaded.config);
|
|
161182
162392
|
if (parsed.configWarnings?.length) {
|
|
161183
162393
|
allWarnings.push(...parsed.configWarnings.map((w) => `[project config] ${w}`));
|
|
161184
162394
|
}
|
|
@@ -161831,7 +163041,7 @@ init_assistant_message_extractor();
|
|
|
161831
163041
|
init_data_path();
|
|
161832
163042
|
init_logger();
|
|
161833
163043
|
import { Database as Database2 } from "bun:sqlite";
|
|
161834
|
-
import { existsSync as
|
|
163044
|
+
import { existsSync as existsSync5 } from "fs";
|
|
161835
163045
|
import { join as join8 } from "path";
|
|
161836
163046
|
|
|
161837
163047
|
// src/features/magic-context/key-files/identify-key-files.ts
|
|
@@ -162227,7 +163437,7 @@ function getOpenCodeDbPath2() {
|
|
|
162227
163437
|
}
|
|
162228
163438
|
function openOpenCodeDb() {
|
|
162229
163439
|
const dbPath = getOpenCodeDbPath2();
|
|
162230
|
-
if (!
|
|
163440
|
+
if (!existsSync5(dbPath)) {
|
|
162231
163441
|
log(`[key-files] OpenCode DB not found at ${dbPath} \u2014 skipping`);
|
|
162232
163442
|
return null;
|
|
162233
163443
|
}
|
|
@@ -162477,8 +163687,8 @@ async function runDream(args) {
|
|
|
162477
163687
|
try {
|
|
162478
163688
|
const docsDir = args.sessionDirectory ?? args.projectIdentity;
|
|
162479
163689
|
const existingDocs = taskName === "maintain-docs" ? {
|
|
162480
|
-
architecture:
|
|
162481
|
-
structure:
|
|
163690
|
+
architecture: existsSync5(join8(docsDir, "ARCHITECTURE.md")),
|
|
163691
|
+
structure: existsSync5(join8(docsDir, "STRUCTURE.md"))
|
|
162482
163692
|
} : undefined;
|
|
162483
163693
|
const userMemories = taskName === "archive-stale" ? getActiveUserMemories(args.db).map((um) => ({
|
|
162484
163694
|
id: um.id,
|
|
@@ -163033,8 +164243,10 @@ function createCompactionHandler() {
|
|
|
163033
164243
|
};
|
|
163034
164244
|
}
|
|
163035
164245
|
// src/hooks/magic-context/event-resolvers.ts
|
|
164246
|
+
init_logger();
|
|
163036
164247
|
init_models_dev_cache();
|
|
163037
164248
|
var DEFAULT_CONTEXT_LIMIT = 128000;
|
|
164249
|
+
var MAX_EXECUTE_THRESHOLD = 80;
|
|
163038
164250
|
function resolveContextLimit(providerID, modelID) {
|
|
163039
164251
|
if (!providerID) {
|
|
163040
164252
|
return DEFAULT_CONTEXT_LIMIT;
|
|
@@ -163062,6 +164274,10 @@ function resolveCacheTtl(cacheTtl, modelKey) {
|
|
|
163062
164274
|
}
|
|
163063
164275
|
return cacheTtl.default ?? "5m";
|
|
163064
164276
|
}
|
|
164277
|
+
var clampWarnSeen = new Set;
|
|
164278
|
+
function isFinitePositive(v) {
|
|
164279
|
+
return typeof v === "number" && Number.isFinite(v) && v > 0;
|
|
164280
|
+
}
|
|
163065
164281
|
function* modelKeyLookupOrder(modelKey) {
|
|
163066
164282
|
const slash = modelKey.indexOf("/");
|
|
163067
164283
|
const provider2 = slash >= 0 ? modelKey.slice(0, slash) : "";
|
|
@@ -163076,9 +164292,36 @@ function* modelKeyLookupOrder(modelKey) {
|
|
|
163076
164292
|
modelId = modelId.slice(0, lastDash);
|
|
163077
164293
|
}
|
|
163078
164294
|
}
|
|
163079
|
-
function
|
|
163080
|
-
|
|
164295
|
+
function resolveExecuteThresholdDetail(config2, modelKey, fallback, options) {
|
|
164296
|
+
if (options?.tokensConfig && isFinitePositive(options.contextLimit)) {
|
|
164297
|
+
const contextLimit = options.contextLimit;
|
|
164298
|
+
const tokenMatch = resolveTokensMatchWithKey(options.tokensConfig, modelKey);
|
|
164299
|
+
if (tokenMatch && isFinitePositive(tokenMatch.value)) {
|
|
164300
|
+
const cap = contextLimit * (MAX_EXECUTE_THRESHOLD / 100);
|
|
164301
|
+
const effectiveTokens = Math.min(tokenMatch.value, cap);
|
|
164302
|
+
if (effectiveTokens < tokenMatch.value) {
|
|
164303
|
+
const dedupeKey = `${options.sessionId ?? "__global__"}|${modelKey ?? "__default__"}|${tokenMatch.value}|${cap}`;
|
|
164304
|
+
if (!clampWarnSeen.has(dedupeKey)) {
|
|
164305
|
+
clampWarnSeen.add(dedupeKey);
|
|
164306
|
+
const msg = `execute_threshold_tokens clamped: ${tokenMatch.value} \u2192 ${effectiveTokens} (80% of ${contextLimit}) for ${modelKey ?? "default"}`;
|
|
164307
|
+
if (options.sessionId) {
|
|
164308
|
+
sessionLog(options.sessionId, `WARN: ${msg}`);
|
|
164309
|
+
} else {
|
|
164310
|
+
log(`[magic-context] WARN: ${msg}`);
|
|
164311
|
+
}
|
|
164312
|
+
}
|
|
164313
|
+
}
|
|
164314
|
+
const percentage = effectiveTokens / contextLimit * 100;
|
|
164315
|
+
return {
|
|
164316
|
+
percentage: Math.min(percentage, MAX_EXECUTE_THRESHOLD),
|
|
164317
|
+
mode: "tokens",
|
|
164318
|
+
absoluteTokens: Math.floor(effectiveTokens),
|
|
164319
|
+
matchedKey: tokenMatch.matchedKey
|
|
164320
|
+
};
|
|
164321
|
+
}
|
|
164322
|
+
}
|
|
163081
164323
|
let resolved;
|
|
164324
|
+
let matchedKey;
|
|
163082
164325
|
if (typeof config2 === "number") {
|
|
163083
164326
|
resolved = config2;
|
|
163084
164327
|
} else if (modelKey) {
|
|
@@ -163086,14 +164329,50 @@ function resolveExecuteThreshold(config2, modelKey, fallback) {
|
|
|
163086
164329
|
for (const candidate of modelKeyLookupOrder(modelKey)) {
|
|
163087
164330
|
if (typeof config2[candidate] === "number") {
|
|
163088
164331
|
matched = config2[candidate];
|
|
164332
|
+
matchedKey = candidate;
|
|
163089
164333
|
break;
|
|
163090
164334
|
}
|
|
163091
164335
|
}
|
|
163092
|
-
|
|
164336
|
+
if (matched === undefined && typeof config2.default === "number") {
|
|
164337
|
+
resolved = config2.default;
|
|
164338
|
+
matchedKey = "default";
|
|
164339
|
+
} else {
|
|
164340
|
+
resolved = matched ?? fallback;
|
|
164341
|
+
}
|
|
164342
|
+
} else if (typeof config2.default === "number") {
|
|
164343
|
+
resolved = config2.default;
|
|
164344
|
+
matchedKey = "default";
|
|
163093
164345
|
} else {
|
|
163094
|
-
resolved =
|
|
164346
|
+
resolved = fallback;
|
|
164347
|
+
}
|
|
164348
|
+
if (!Number.isFinite(resolved) || resolved < 0) {
|
|
164349
|
+
resolved = fallback;
|
|
163095
164350
|
}
|
|
163096
|
-
return
|
|
164351
|
+
return {
|
|
164352
|
+
percentage: Math.min(resolved, MAX_EXECUTE_THRESHOLD),
|
|
164353
|
+
mode: "percentage",
|
|
164354
|
+
matchedKey
|
|
164355
|
+
};
|
|
164356
|
+
}
|
|
164357
|
+
function resolveExecuteThreshold(config2, modelKey, fallback, options) {
|
|
164358
|
+
return resolveExecuteThresholdDetail(config2, modelKey, fallback, options).percentage;
|
|
164359
|
+
}
|
|
164360
|
+
function resolveTokensMatchWithKey(tokensConfig, modelKey) {
|
|
164361
|
+
if (!tokensConfig) {
|
|
164362
|
+
return;
|
|
164363
|
+
}
|
|
164364
|
+
if (modelKey) {
|
|
164365
|
+
for (const candidate of modelKeyLookupOrder(modelKey)) {
|
|
164366
|
+
const value = tokensConfig[candidate];
|
|
164367
|
+
if (typeof value === "number") {
|
|
164368
|
+
return { value, matchedKey: candidate };
|
|
164369
|
+
}
|
|
164370
|
+
}
|
|
164371
|
+
}
|
|
164372
|
+
if (typeof tokensConfig.default === "number") {
|
|
164373
|
+
return { value: tokensConfig.default, matchedKey: "default" };
|
|
164374
|
+
}
|
|
164375
|
+
return;
|
|
163097
164376
|
}
|
|
163098
164377
|
function resolveModelKey(providerID, modelID) {
|
|
163099
164378
|
if (!providerID || !modelID) {
|
|
@@ -163143,11 +164422,16 @@ function parseCacheTtl(ttl) {
|
|
|
163143
164422
|
}
|
|
163144
164423
|
function createScheduler(config2) {
|
|
163145
164424
|
return {
|
|
163146
|
-
shouldExecute(sessionMeta, contextUsage, currentTime = Date.now(), sessionId, modelKey) {
|
|
164425
|
+
shouldExecute(sessionMeta, contextUsage, currentTime = Date.now(), sessionId, modelKey, contextLimit) {
|
|
163147
164426
|
if (contextUsage.percentage === 0 && sessionMeta.lastResponseTime === 0) {
|
|
163148
164427
|
return "defer";
|
|
163149
164428
|
}
|
|
163150
|
-
const
|
|
164429
|
+
const effectiveContextLimit = contextLimit ?? (contextUsage.percentage > 0 && contextUsage.inputTokens > 0 ? contextUsage.inputTokens / (contextUsage.percentage / 100) : undefined);
|
|
164430
|
+
const threshold = resolveExecuteThreshold(config2.executeThresholdPercentage, modelKey, 65, {
|
|
164431
|
+
tokensConfig: config2.executeThresholdTokens,
|
|
164432
|
+
contextLimit: effectiveContextLimit,
|
|
164433
|
+
sessionId
|
|
164434
|
+
});
|
|
163151
164435
|
if (contextUsage.percentage >= threshold) {
|
|
163152
164436
|
return "execute";
|
|
163153
164437
|
}
|
|
@@ -163295,6 +164579,7 @@ init_logger();
|
|
|
163295
164579
|
init_storage();
|
|
163296
164580
|
init_shared();
|
|
163297
164581
|
init_rpc_notifications();
|
|
164582
|
+
init_compartment_runner_partial_recomp();
|
|
163298
164583
|
|
|
163299
164584
|
// src/hooks/magic-context/execute-flush.ts
|
|
163300
164585
|
init_storage();
|
|
@@ -163536,8 +164821,25 @@ function getRollingNudgeIntervalTokens(baseIntervalTokens, band) {
|
|
|
163536
164821
|
|
|
163537
164822
|
// src/hooks/magic-context/execute-status.ts
|
|
163538
164823
|
init_read_session_formatting();
|
|
163539
|
-
function
|
|
163540
|
-
|
|
164824
|
+
function formatExecuteThreshold(thresholdPercentage, mode, contextLimit) {
|
|
164825
|
+
if (mode === "tokens" && contextLimit > 0) {
|
|
164826
|
+
const tokens = Math.floor(thresholdPercentage / 100 * contextLimit);
|
|
164827
|
+
return `${tokens.toLocaleString()} tokens (${thresholdPercentage.toFixed(1)}% of ${contextLimit.toLocaleString()}) [token-mode]`;
|
|
164828
|
+
}
|
|
164829
|
+
if (contextLimit > 0) {
|
|
164830
|
+
const tokens = Math.floor(thresholdPercentage / 100 * contextLimit);
|
|
164831
|
+
return `${thresholdPercentage}% (${tokens.toLocaleString()} of ${contextLimit.toLocaleString()})`;
|
|
164832
|
+
}
|
|
164833
|
+
return `${thresholdPercentage}%`;
|
|
164834
|
+
}
|
|
164835
|
+
function executeStatus(db, sessionId, protectedTags, nudgeIntervalTokens = DEFAULT_NUDGE_INTERVAL_TOKENS, executeThresholdPercentageConfig = DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, liveModelKey, historyBudgetPercentage, commitClusterTrigger, executeThresholdTokens, contextLimit) {
|
|
164836
|
+
const thresholdDetail = resolveExecuteThresholdDetail(executeThresholdPercentageConfig, liveModelKey, DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, {
|
|
164837
|
+
tokensConfig: executeThresholdTokens,
|
|
164838
|
+
contextLimit,
|
|
164839
|
+
sessionId
|
|
164840
|
+
});
|
|
164841
|
+
const executeThresholdPercentage = thresholdDetail.percentage;
|
|
164842
|
+
const thresholdMode = thresholdDetail.mode;
|
|
163541
164843
|
try {
|
|
163542
164844
|
const meta3 = getOrCreateSessionMeta(db, sessionId);
|
|
163543
164845
|
const tags = getTagsBySession(db, sessionId);
|
|
@@ -163558,6 +164860,7 @@ function executeStatus(db, sessionId, protectedTags, nudgeIntervalTokens = DEFAU
|
|
|
163558
164860
|
const currentBand = getRollingNudgeBand(meta3.lastContextPercentage, executeThresholdPercentage);
|
|
163559
164861
|
const nudgeInterval = getRollingNudgeIntervalTokens(nudgeIntervalTokens, currentBand);
|
|
163560
164862
|
const proactiveCompartmentTrigger = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
|
|
164863
|
+
const displayContextLimit = contextLimit && contextLimit > 0 ? contextLimit : meta3.lastContextPercentage > 0 ? Math.round(meta3.lastInputTokens / (meta3.lastContextPercentage / 100)) : 0;
|
|
163561
164864
|
const lines = [
|
|
163562
164865
|
"## Magic Status",
|
|
163563
164866
|
"",
|
|
@@ -163581,7 +164884,7 @@ function executeStatus(db, sessionId, protectedTags, nudgeIntervalTokens = DEFAU
|
|
|
163581
164884
|
`- Queue will auto-execute: ${cacheExpired ? "yes (cache expired)" : `when TTL expires or context >= ${executeThresholdPercentage}%`}`,
|
|
163582
164885
|
"",
|
|
163583
164886
|
"### Rolling Nudges",
|
|
163584
|
-
`- Execute threshold: ${executeThresholdPercentage}
|
|
164887
|
+
`- Execute threshold: ${formatExecuteThreshold(executeThresholdPercentage, thresholdMode, displayContextLimit)}`,
|
|
163585
164888
|
`- Rolling anchor: ${meta3.lastNudgeTokens.toLocaleString()} tokens`,
|
|
163586
164889
|
`- Effective interval: ${nudgeInterval.toLocaleString()} tokens`,
|
|
163587
164890
|
`- Next rolling nudge after: ${(meta3.lastNudgeTokens + nudgeInterval).toLocaleString()} tokens`,
|
|
@@ -163592,9 +164895,8 @@ function executeStatus(db, sessionId, protectedTags, nudgeIntervalTokens = DEFAU
|
|
|
163592
164895
|
`**Protected tags:** ${protectedTags}`,
|
|
163593
164896
|
`**Subagent session:** ${meta3.isSubagent}`
|
|
163594
164897
|
];
|
|
163595
|
-
const contextLimit = meta3.lastContextPercentage > 0 ? Math.round(meta3.lastInputTokens / (meta3.lastContextPercentage / 100)) : 0;
|
|
163596
164898
|
if (meta3.lastContextPercentage > 0 || meta3.lastInputTokens > 0) {
|
|
163597
|
-
lines.push("", "### Context Usage", `- Last percentage: ${meta3.lastContextPercentage.toFixed(1)}%`, `- Last input tokens: ${meta3.lastInputTokens.toLocaleString()}`, `- Resolved context limit: ${
|
|
164899
|
+
lines.push("", "### Context Usage", `- Last percentage: ${meta3.lastContextPercentage.toFixed(1)}%`, `- Last input tokens: ${meta3.lastInputTokens.toLocaleString()}`, `- Resolved context limit: ${displayContextLimit > 0 ? displayContextLimit.toLocaleString() : "unknown"}`, `- Proactive compartment evaluation: ${proactiveCompartmentTrigger}%`, `- Post-drop target for historian: ${(executeThresholdPercentage * POST_DROP_TARGET_RATIO).toFixed(0)}% (${executeThresholdPercentage}% * ${POST_DROP_TARGET_RATIO})`, `- Commit cluster trigger: ${commitClusterTrigger?.enabled !== false ? `enabled (min ${commitClusterTrigger?.min_clusters ?? 3} clusters)` : "disabled"}, tail-size trigger: > 3x compartment budget`);
|
|
163598
164900
|
}
|
|
163599
164901
|
const compartments = getCompartments(db, sessionId);
|
|
163600
164902
|
const facts = getSessionFacts(db, sessionId);
|
|
@@ -163609,7 +164911,7 @@ ${c.content}
|
|
|
163609
164911
|
historyBlockTokens += estimateTokens(`* ${f.content}
|
|
163610
164912
|
`);
|
|
163611
164913
|
}
|
|
163612
|
-
const budgetTokens = historyBudgetPercentage &&
|
|
164914
|
+
const budgetTokens = historyBudgetPercentage && displayContextLimit > 0 ? Math.floor(displayContextLimit * (Math.min(executeThresholdPercentage, 80) / 100) * historyBudgetPercentage) : null;
|
|
163613
164915
|
const budgetUsage = budgetTokens ? (historyBlockTokens / budgetTokens * 100).toFixed(0) : null;
|
|
163614
164916
|
lines.push("", "### History Compression", `- Compartments: ${compartments.length}`, `- Facts: ${facts.length}`, `- History block: ~${historyBlockTokens.toLocaleString()} tokens`, ...budgetTokens ? [
|
|
163615
164917
|
`- Compression budget: ~${budgetTokens.toLocaleString()} tokens (${budgetUsage}% used)`,
|
|
@@ -163633,6 +164935,37 @@ ${c.content}
|
|
|
163633
164935
|
init_send_session_notification();
|
|
163634
164936
|
var recompConfirmationBySession = new Map;
|
|
163635
164937
|
var RECOMP_CONFIRMATION_WINDOW_MS = 60000;
|
|
164938
|
+
function parseRecompArgs(raw) {
|
|
164939
|
+
const trimmed = raw.trim();
|
|
164940
|
+
if (trimmed === "")
|
|
164941
|
+
return { kind: "full" };
|
|
164942
|
+
const match = trimmed.match(/^(\d+)\s*-\s*(\d+)$/);
|
|
164943
|
+
if (!match) {
|
|
164944
|
+
return {
|
|
164945
|
+
kind: "error",
|
|
164946
|
+
message: `Invalid /ctx-recomp arguments: \`${trimmed}\`.
|
|
164947
|
+
|
|
164948
|
+
Usage:
|
|
164949
|
+
- \`/ctx-recomp\` \u2014 full rebuild from message 1 to the protected tail
|
|
164950
|
+
- \`/ctx-recomp <start>-<end>\` \u2014 partial rebuild of a message range (e.g. \`/ctx-recomp 1-11322\`)`
|
|
164951
|
+
};
|
|
164952
|
+
}
|
|
164953
|
+
const start = Number.parseInt(match[1], 10);
|
|
164954
|
+
const end = Number.parseInt(match[2], 10);
|
|
164955
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) {
|
|
164956
|
+
return { kind: "error", message: "Range values must be finite integers." };
|
|
164957
|
+
}
|
|
164958
|
+
if (start < 1) {
|
|
164959
|
+
return { kind: "error", message: `Start must be >= 1 (got ${start}).` };
|
|
164960
|
+
}
|
|
164961
|
+
if (end < start) {
|
|
164962
|
+
return {
|
|
164963
|
+
kind: "error",
|
|
164964
|
+
message: `End must be >= start (got ${start}-${end}).`
|
|
164965
|
+
};
|
|
164966
|
+
}
|
|
164967
|
+
return { kind: "partial", range: { start, end } };
|
|
164968
|
+
}
|
|
163636
164969
|
var SENTINEL_PREFIX = "__CONTEXT_MANAGEMENT_";
|
|
163637
164970
|
function throwSentinel(command) {
|
|
163638
164971
|
throw new Error(`${SENTINEL_PREFIX}${command.toUpperCase()}_HANDLED__`);
|
|
@@ -163755,49 +165088,102 @@ function createMagicContextCommandHandler(deps) {
|
|
|
163755
165088
|
throwSentinel(input.command);
|
|
163756
165089
|
}
|
|
163757
165090
|
const liveModelKey = deps.getLiveModelKey?.(sessionId);
|
|
163758
|
-
const
|
|
165091
|
+
const liveContextLimit = deps.getContextLimit?.(sessionId);
|
|
165092
|
+
const statusOutput = executeStatus(deps.db, sessionId, deps.protectedTags, deps.nudgeIntervalTokens, deps.executeThresholdPercentage, liveModelKey, deps.historyBudgetPercentage, deps.commitClusterTrigger, deps.executeThresholdTokens, liveContextLimit);
|
|
163759
165093
|
result += result ? `
|
|
163760
165094
|
|
|
163761
165095
|
${statusOutput}` : statusOutput;
|
|
163762
165096
|
}
|
|
163763
165097
|
if (isRecomp) {
|
|
163764
|
-
|
|
165098
|
+
const parsedArgs = parseRecompArgs(input.arguments);
|
|
165099
|
+
if (parsedArgs.kind === "error") {
|
|
165100
|
+
result = `## Magic Recomp \u2014 Invalid Arguments
|
|
165101
|
+
|
|
165102
|
+
${parsedArgs.message}`;
|
|
165103
|
+
} else if (isTuiConnected()) {
|
|
163765
165104
|
pushNotification("action", { action: "show-recomp-dialog" }, sessionId);
|
|
163766
165105
|
sessionLog(sessionId, "command ctx-recomp: pushed show-recomp-dialog to TUI");
|
|
163767
165106
|
throwSentinel(input.command);
|
|
163768
|
-
}
|
|
163769
|
-
if (!deps.executeRecomp) {
|
|
165107
|
+
} else if (!deps.executeRecomp) {
|
|
163770
165108
|
result = `## Magic Recomp
|
|
163771
165109
|
|
|
163772
165110
|
/ctx-recomp is unavailable because the recomp handler is not configured.`;
|
|
163773
165111
|
} else {
|
|
165112
|
+
const argsKey = parsedArgs.kind === "partial" ? `${parsedArgs.range.start}-${parsedArgs.range.end}` : "";
|
|
163774
165113
|
const lastConfirmation = recompConfirmationBySession.get(sessionId);
|
|
163775
165114
|
const now = Date.now();
|
|
163776
|
-
|
|
165115
|
+
const confirmationValid = lastConfirmation && now - lastConfirmation.timestamp < RECOMP_CONFIRMATION_WINDOW_MS && lastConfirmation.argsKey === argsKey;
|
|
165116
|
+
if (confirmationValid) {
|
|
163777
165117
|
recompConfirmationBySession.delete(sessionId);
|
|
163778
|
-
|
|
165118
|
+
if (parsedArgs.kind === "partial") {
|
|
165119
|
+
await deps.sendNotification(sessionId, `## Magic Recomp
|
|
165120
|
+
|
|
165121
|
+
Partial recomp started for range ${parsedArgs.range.start}-${parsedArgs.range.end}. Rebuilding the matching compartments now (facts unchanged).`, {});
|
|
165122
|
+
result = await deps.executeRecomp(sessionId, {
|
|
165123
|
+
range: parsedArgs.range
|
|
165124
|
+
});
|
|
165125
|
+
} else {
|
|
165126
|
+
await deps.sendNotification(sessionId, `## Magic Recomp
|
|
163779
165127
|
|
|
163780
165128
|
Historian recomp started. Rebuilding compartments and facts from raw session history now.`, {});
|
|
163781
|
-
|
|
165129
|
+
result = await deps.executeRecomp(sessionId);
|
|
165130
|
+
}
|
|
163782
165131
|
} else {
|
|
163783
|
-
recompConfirmationBySession.set(sessionId,
|
|
165132
|
+
recompConfirmationBySession.set(sessionId, {
|
|
165133
|
+
timestamp: now,
|
|
165134
|
+
argsKey
|
|
165135
|
+
});
|
|
163784
165136
|
const compartments = getCompartments(deps.db, sessionId);
|
|
163785
165137
|
const compartmentCount = compartments.length;
|
|
163786
|
-
|
|
163787
|
-
|
|
163788
|
-
""
|
|
163789
|
-
|
|
163790
|
-
|
|
163791
|
-
|
|
163792
|
-
|
|
163793
|
-
|
|
163794
|
-
|
|
163795
|
-
|
|
163796
|
-
|
|
163797
|
-
|
|
163798
|
-
|
|
163799
|
-
|
|
165138
|
+
if (parsedArgs.kind === "partial") {
|
|
165139
|
+
const snap = snapRangeToCompartments(compartments, parsedArgs.range);
|
|
165140
|
+
if ("error" in snap) {
|
|
165141
|
+
recompConfirmationBySession.delete(sessionId);
|
|
165142
|
+
result = `## Magic Recomp \u2014 Failed
|
|
165143
|
+
|
|
165144
|
+
${snap.error}`;
|
|
165145
|
+
} else {
|
|
165146
|
+
const replaced = snap.rangeCompartments.length;
|
|
165147
|
+
const preserved = snap.priorCompartments.length + snap.tailCompartments.length;
|
|
165148
|
+
const warningLines = [
|
|
165149
|
+
"## \u26A0\uFE0F Partial Recomp Confirmation Required",
|
|
165150
|
+
"",
|
|
165151
|
+
`Requested range: \`${parsedArgs.range.start}-${parsedArgs.range.end}\``,
|
|
165152
|
+
`Snapped to compartment boundaries: **messages ${snap.snapStart}-${snap.snapEnd}**`,
|
|
165153
|
+
"",
|
|
165154
|
+
`This will **rebuild ${replaced} compartment(s)** in the snapped range.`,
|
|
165155
|
+
`**${preserved} compartment(s)** outside the range will be preserved unchanged.`,
|
|
165156
|
+
"Facts will not be re-extracted.",
|
|
165157
|
+
"",
|
|
165158
|
+
"This operation:",
|
|
165159
|
+
"- May take several minutes to tens of minutes depending on range size",
|
|
165160
|
+
"- Will consume historian-model tokens for each chunk",
|
|
165161
|
+
"- Is resumable if interrupted (staging preserved on failure)",
|
|
165162
|
+
"",
|
|
165163
|
+
`**To confirm, run \`/ctx-recomp ${parsedArgs.range.start}-${parsedArgs.range.end}\` again within 60 seconds.**`
|
|
165164
|
+
];
|
|
165165
|
+
result = warningLines.join(`
|
|
163800
165166
|
`);
|
|
165167
|
+
}
|
|
165168
|
+
} else {
|
|
165169
|
+
const warningLines = [
|
|
165170
|
+
"## \u26A0\uFE0F Recomp Confirmation Required",
|
|
165171
|
+
"",
|
|
165172
|
+
`You currently have **${compartmentCount}** compartments.`,
|
|
165173
|
+
"Running /ctx-recomp will **regenerate all compartments and facts** from raw session history.",
|
|
165174
|
+
"",
|
|
165175
|
+
"This operation:",
|
|
165176
|
+
"- May take a long time (minutes to hours for long sessions)",
|
|
165177
|
+
"- Will consume significant tokens on your historian model",
|
|
165178
|
+
"- Cannot be interrupted cleanly once started",
|
|
165179
|
+
"",
|
|
165180
|
+
"Tip: to rebuild only a specific message range, use `/ctx-recomp <start>-<end>` (e.g. `/ctx-recomp 1-11322`).",
|
|
165181
|
+
"",
|
|
165182
|
+
"**To confirm, run `/ctx-recomp` again within 60 seconds.**"
|
|
165183
|
+
];
|
|
165184
|
+
result = warningLines.join(`
|
|
165185
|
+
`);
|
|
165186
|
+
}
|
|
163801
165187
|
}
|
|
163802
165188
|
}
|
|
163803
165189
|
}
|
|
@@ -164420,7 +165806,11 @@ function stripProcessedImages(messages, watermark, messageTagNumbers) {
|
|
|
164420
165806
|
return stripped;
|
|
164421
165807
|
}
|
|
164422
165808
|
|
|
165809
|
+
// src/hooks/magic-context/transform.ts
|
|
165810
|
+
init_temporal_awareness();
|
|
165811
|
+
|
|
164423
165812
|
// src/hooks/magic-context/transform-compartment-phase.ts
|
|
165813
|
+
init_magic_context();
|
|
164424
165814
|
init_compartment_storage();
|
|
164425
165815
|
init_storage();
|
|
164426
165816
|
init_logger();
|
|
@@ -164430,11 +165820,11 @@ init_inject_compartments();
|
|
|
164430
165820
|
init_read_session_chunk();
|
|
164431
165821
|
init_send_session_notification();
|
|
164432
165822
|
var lastCompressorRunBySession = new Map;
|
|
164433
|
-
function isCompressorOnCooldown(sessionId) {
|
|
165823
|
+
function isCompressorOnCooldown(sessionId, cooldownMs) {
|
|
164434
165824
|
const lastRun = lastCompressorRunBySession.get(sessionId);
|
|
164435
165825
|
if (!lastRun)
|
|
164436
165826
|
return false;
|
|
164437
|
-
return Date.now() - lastRun <
|
|
165827
|
+
return Date.now() - lastRun < cooldownMs;
|
|
164438
165828
|
}
|
|
164439
165829
|
function markCompressorRun(sessionId) {
|
|
164440
165830
|
lastCompressorRunBySession.set(sessionId, Date.now());
|
|
@@ -164471,14 +165861,14 @@ async function runCompartmentPhase(args) {
|
|
|
164471
165861
|
async function awaitCompartmentRun(activeRun, reason) {
|
|
164472
165862
|
sessionLog(args.sessionId, reason);
|
|
164473
165863
|
const timeoutMs = args.historianTimeoutMs ?? 120000;
|
|
164474
|
-
const timeout = new Promise((
|
|
165864
|
+
const timeout = new Promise((resolve3) => setTimeout(() => resolve3("timeout"), timeoutMs));
|
|
164475
165865
|
const result = await Promise.race([activeRun.then(() => "done"), timeout]);
|
|
164476
165866
|
if (result === "timeout") {
|
|
164477
165867
|
sessionLog(args.sessionId, `transform: compartment await timed out after ${timeoutMs}ms \u2014 proceeding without waiting`);
|
|
164478
165868
|
return "timed_out";
|
|
164479
165869
|
}
|
|
164480
165870
|
sessionLog(args.sessionId, "transform: compartment agent completed, refreshing compartment coverage");
|
|
164481
|
-
pendingCompartmentInjection = prepareCompartmentInjection(args.db, args.resolvedSessionId, args.messages, args.cacheAlreadyBusting ?? false, args.projectPath, args.injectionBudgetTokens);
|
|
165871
|
+
pendingCompartmentInjection = prepareCompartmentInjection(args.db, args.resolvedSessionId, args.messages, args.cacheAlreadyBusting ?? false, args.projectPath, args.injectionBudgetTokens, args.experimentalTemporalAwareness);
|
|
164482
165872
|
return "completed";
|
|
164483
165873
|
}
|
|
164484
165874
|
if (args.canRunCompartments && args.sessionMeta.compartmentInProgress && !getActiveCompartmentRun(args.sessionId)) {
|
|
@@ -164503,7 +165893,10 @@ async function runCompartmentPhase(args) {
|
|
|
164503
165893
|
fallbackModelId: args.fallbackModelId,
|
|
164504
165894
|
getNotificationParams: args.getNotificationParams,
|
|
164505
165895
|
experimentalCompactionMarkers: args.experimentalCompactionMarkers,
|
|
164506
|
-
experimentalUserMemories: args.experimentalUserMemories
|
|
165896
|
+
experimentalUserMemories: args.experimentalUserMemories,
|
|
165897
|
+
historianTwoPass: args.historianTwoPass,
|
|
165898
|
+
compressorMinCompartmentRatio: args.compressorMinCompartmentRatio,
|
|
165899
|
+
compressorMaxMergeDepth: args.compressorMaxMergeDepth
|
|
164507
165900
|
});
|
|
164508
165901
|
compartmentInProgress = true;
|
|
164509
165902
|
}
|
|
@@ -164524,7 +165917,10 @@ async function runCompartmentPhase(args) {
|
|
|
164524
165917
|
fallbackModelId: args.fallbackModelId,
|
|
164525
165918
|
getNotificationParams: args.getNotificationParams,
|
|
164526
165919
|
experimentalCompactionMarkers: args.experimentalCompactionMarkers,
|
|
164527
|
-
experimentalUserMemories: args.experimentalUserMemories
|
|
165920
|
+
experimentalUserMemories: args.experimentalUserMemories,
|
|
165921
|
+
historianTwoPass: args.historianTwoPass,
|
|
165922
|
+
compressorMinCompartmentRatio: args.compressorMinCompartmentRatio,
|
|
165923
|
+
compressorMaxMergeDepth: args.compressorMaxMergeDepth
|
|
164528
165924
|
});
|
|
164529
165925
|
activeRun = getActiveCompartmentRun(args.sessionId);
|
|
164530
165926
|
} else if (!activeRun && hasEligibleHistoryForCompartment()) {
|
|
@@ -164544,15 +165940,17 @@ async function runCompartmentPhase(args) {
|
|
|
164544
165940
|
}
|
|
164545
165941
|
}
|
|
164546
165942
|
}
|
|
164547
|
-
if (args.cacheAlreadyBusting && args.historyBudgetTokens && args.historyBudgetTokens > 0 && args.client && !compartmentInProgress && !awaitedCompartmentRun && !isCompressorOnCooldown(args.sessionId)) {
|
|
165943
|
+
if (args.cacheAlreadyBusting && args.historyBudgetTokens && args.historyBudgetTokens > 0 && args.client && !compartmentInProgress && !awaitedCompartmentRun && !isCompressorOnCooldown(args.sessionId, args.compressorCooldownMs ?? DEFAULT_COMPRESSOR_COOLDOWN_MS)) {
|
|
164548
165944
|
markCompressorRun(args.sessionId);
|
|
164549
|
-
runCompressionPassIfNeeded({
|
|
165945
|
+
const compressorPromise = runCompressionPassIfNeeded({
|
|
164550
165946
|
client: args.client,
|
|
164551
165947
|
db: args.db,
|
|
164552
165948
|
sessionId: args.sessionId,
|
|
164553
165949
|
directory: args.compartmentDirectory,
|
|
164554
165950
|
historyBudgetTokens: args.historyBudgetTokens,
|
|
164555
|
-
historianTimeoutMs: args.historianTimeoutMs
|
|
165951
|
+
historianTimeoutMs: args.historianTimeoutMs,
|
|
165952
|
+
minCompartmentRatio: args.compressorMinCompartmentRatio,
|
|
165953
|
+
maxMergeDepth: args.compressorMaxMergeDepth
|
|
164556
165954
|
}).then((compressed) => {
|
|
164557
165955
|
if (compressed) {
|
|
164558
165956
|
sessionLog(args.sessionId, "independent compressor completed in background \u2014 compressed history will appear on next cache-busting pass");
|
|
@@ -164560,6 +165958,7 @@ async function runCompartmentPhase(args) {
|
|
|
164560
165958
|
}).catch((error48) => {
|
|
164561
165959
|
sessionLog(args.sessionId, "independent compressor failed in background:", getErrorMessage(error48));
|
|
164562
165960
|
});
|
|
165961
|
+
registerActiveCompartmentRun(args.sessionId, compressorPromise);
|
|
164563
165962
|
}
|
|
164564
165963
|
return { pendingCompartmentInjection, awaitedCompartmentRun, compartmentInProgress };
|
|
164565
165964
|
}
|
|
@@ -166076,7 +167475,7 @@ function createTransform(deps) {
|
|
|
166076
167475
|
}
|
|
166077
167476
|
}
|
|
166078
167477
|
const contextUsageEarly = loadContextUsage(deps.contextUsageMap, db, sessionId);
|
|
166079
|
-
const historyBudgetTokens = resolveHistoryBudgetTokens(deps.historyBudgetPercentage, contextUsageEarly, deps.executeThresholdPercentage, deps.getModelKey?.(sessionId));
|
|
167478
|
+
const historyBudgetTokens = resolveHistoryBudgetTokens(deps.historyBudgetPercentage, contextUsageEarly, deps.executeThresholdPercentage, deps.getModelKey?.(sessionId), deps.executeThresholdTokens);
|
|
166080
167479
|
const schedulerDecisionEarly = resolveSchedulerDecision(deps.scheduler, sessionMeta, contextUsageEarly, sessionId, deps.getModelKey?.(sessionId));
|
|
166081
167480
|
const isCacheBusting = deps.flushedSessions.has(sessionId);
|
|
166082
167481
|
if (historianFailureState.failureCount === 0) {
|
|
@@ -166110,7 +167509,11 @@ function createTransform(deps) {
|
|
|
166110
167509
|
fallbackModelId,
|
|
166111
167510
|
getNotificationParams: () => notificationParams,
|
|
166112
167511
|
experimentalCompactionMarkers: deps.experimentalCompactionMarkers,
|
|
166113
|
-
experimentalUserMemories: deps.experimentalUserMemories
|
|
167512
|
+
experimentalUserMemories: deps.experimentalUserMemories,
|
|
167513
|
+
experimentalTemporalAwareness: deps.experimentalTemporalAwareness,
|
|
167514
|
+
historianTwoPass: deps.historianTwoPass,
|
|
167515
|
+
compressorMinCompartmentRatio: deps.compressorMinCompartmentRatio,
|
|
167516
|
+
compressorMaxMergeDepth: deps.compressorMaxMergeDepth
|
|
166114
167517
|
});
|
|
166115
167518
|
skipCompartmentAwaitForThisPass = true;
|
|
166116
167519
|
return true;
|
|
@@ -166144,7 +167547,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
|
|
|
166144
167547
|
if (fullFeatureMode) {
|
|
166145
167548
|
const tInj = performance.now();
|
|
166146
167549
|
const projectPath = deps.memoryConfig?.enabled ? resolveProjectIdentity(deps.directory ?? process.cwd()) : undefined;
|
|
166147
|
-
pendingCompartmentInjection = prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, deps.memoryConfig?.injectionBudgetTokens);
|
|
167550
|
+
pendingCompartmentInjection = prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, projectPath, deps.memoryConfig?.injectionBudgetTokens, deps.experimentalTemporalAwareness);
|
|
166148
167551
|
logTransformTiming(sessionId, "prepareCompartmentInjection", tInj);
|
|
166149
167552
|
}
|
|
166150
167553
|
let targets = new Map;
|
|
@@ -166152,6 +167555,14 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
|
|
|
166152
167555
|
let messageTagNumbers = new Map;
|
|
166153
167556
|
let batch = null;
|
|
166154
167557
|
let hasRecentReduceCall = false;
|
|
167558
|
+
if (deps.experimentalTemporalAwareness) {
|
|
167559
|
+
const tTemporal = performance.now();
|
|
167560
|
+
const injected = injectTemporalMarkers(messages);
|
|
167561
|
+
if (injected > 0) {
|
|
167562
|
+
sessionLog(sessionId, `temporal: injected ${injected} gap markers`);
|
|
167563
|
+
}
|
|
167564
|
+
logTransformTiming(sessionId, "injectTemporalMarkers", tTemporal);
|
|
167565
|
+
}
|
|
166155
167566
|
try {
|
|
166156
167567
|
const t0 = performance.now();
|
|
166157
167568
|
deps.tagger.initFromDb(sessionId, db);
|
|
@@ -166243,7 +167654,12 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so ma
|
|
|
166243
167654
|
cacheAlreadyBusting: isCacheBusting || schedulerDecisionEarly === "execute",
|
|
166244
167655
|
skipAwaitForThisPass: skipCompartmentAwaitForThisPass,
|
|
166245
167656
|
experimentalCompactionMarkers: deps.experimentalCompactionMarkers,
|
|
166246
|
-
experimentalUserMemories: deps.experimentalUserMemories
|
|
167657
|
+
experimentalUserMemories: deps.experimentalUserMemories,
|
|
167658
|
+
experimentalTemporalAwareness: deps.experimentalTemporalAwareness,
|
|
167659
|
+
historianTwoPass: deps.historianTwoPass,
|
|
167660
|
+
compressorMinCompartmentRatio: deps.compressorMinCompartmentRatio,
|
|
167661
|
+
compressorMaxMergeDepth: deps.compressorMaxMergeDepth,
|
|
167662
|
+
compressorCooldownMs: deps.compressorCooldownMs
|
|
166247
167663
|
});
|
|
166248
167664
|
pendingCompartmentInjection = compartmentPhase.pendingCompartmentInjection;
|
|
166249
167665
|
const awaitedCompartmentRun = compartmentPhase.awaitedCompartmentRun;
|
|
@@ -166413,11 +167829,15 @@ function hasEligibleCompartmentHistory(db, sessionId) {
|
|
|
166413
167829
|
return false;
|
|
166414
167830
|
}
|
|
166415
167831
|
}
|
|
166416
|
-
function resolveHistoryBudgetTokens(historyBudgetPercentage, contextUsage, executeThresholdPercentage, modelKey) {
|
|
167832
|
+
function resolveHistoryBudgetTokens(historyBudgetPercentage, contextUsage, executeThresholdPercentage, modelKey, executeThresholdTokens) {
|
|
166417
167833
|
if (!historyBudgetPercentage || contextUsage.percentage <= 0) {
|
|
166418
167834
|
return;
|
|
166419
167835
|
}
|
|
166420
|
-
|
|
167836
|
+
const derivedContextLimit = contextUsage.inputTokens / (contextUsage.percentage / 100);
|
|
167837
|
+
return Math.floor(derivedContextLimit * (resolveExecuteThreshold(executeThresholdPercentage ?? 65, modelKey, 65, {
|
|
167838
|
+
tokensConfig: executeThresholdTokens,
|
|
167839
|
+
contextLimit: derivedContextLimit
|
|
167840
|
+
}) / 100) * historyBudgetPercentage);
|
|
166421
167841
|
}
|
|
166422
167842
|
function truncateHistorianEmergencyError(error48) {
|
|
166423
167843
|
const normalized = (error48 ?? "unknown error").replace(/\s+/g, " ").trim();
|
|
@@ -166558,7 +167978,11 @@ function createEventHandler2(deps) {
|
|
|
166558
167978
|
const sessionMeta = getOrCreateSessionMeta(deps.db, info.sessionID);
|
|
166559
167979
|
const previousPercentage = sessionMeta.lastContextPercentage;
|
|
166560
167980
|
if (!sessionMeta.isSubagent) {
|
|
166561
|
-
const effectiveExecuteThreshold = resolveExecuteThreshold(deps.config.execute_threshold_percentage ?? 65, modelKey, 65
|
|
167981
|
+
const effectiveExecuteThreshold = resolveExecuteThreshold(deps.config.execute_threshold_percentage ?? 65, modelKey, 65, {
|
|
167982
|
+
tokensConfig: deps.config.execute_threshold_tokens,
|
|
167983
|
+
contextLimit,
|
|
167984
|
+
sessionId: info.sessionID
|
|
167985
|
+
});
|
|
166562
167986
|
const triggerBudget = deriveTriggerBudget(contextLimit, effectiveExecuteThreshold);
|
|
166563
167987
|
const triggerResult = checkCompartmentTrigger(deps.db, info.sessionID, sessionMeta, { percentage, inputTokens: totalInputTokens }, previousPercentage, effectiveExecuteThreshold, triggerBudget, deps.config.auto_drop_tool_age ?? 100, deps.config.protected_tags, deps.config.clear_reasoning_age ?? 50, deps.config.drop_tool_structure ?? true, deps.config.commit_cluster_trigger);
|
|
166564
167988
|
if (triggerResult.shouldFire) {
|
|
@@ -166832,6 +168256,9 @@ function generateEmergencyNudgeText(db, sessionId, contextUsage, config2) {
|
|
|
166832
168256
|
`);
|
|
166833
168257
|
}
|
|
166834
168258
|
|
|
168259
|
+
// src/hooks/magic-context/hook.ts
|
|
168260
|
+
init_read_session_db();
|
|
168261
|
+
|
|
166835
168262
|
// src/hooks/magic-context/text-complete.ts
|
|
166836
168263
|
var TAG_PREFIX_REGEX2 = /^(\u00a7\d+\u00a7\s*)+/;
|
|
166837
168264
|
function createTextCompleteHandler() {
|
|
@@ -167010,8 +168437,8 @@ function createToolExecuteAfterHook(args) {
|
|
|
167010
168437
|
init_send_session_notification();
|
|
167011
168438
|
|
|
167012
168439
|
// src/hooks/magic-context/system-prompt-hash.ts
|
|
167013
|
-
import { existsSync as
|
|
167014
|
-
import { join as join14, resolve as
|
|
168440
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, realpathSync } from "fs";
|
|
168441
|
+
import { join as join14, resolve as resolve3, sep } from "path";
|
|
167015
168442
|
|
|
167016
168443
|
// src/agents/magic-context-prompt.ts
|
|
167017
168444
|
function getToolHistoryGuidance(dropToolStructure) {
|
|
@@ -167197,20 +168624,23 @@ function detectAgentFromSystemPrompt(systemPrompt) {
|
|
|
167197
168624
|
}
|
|
167198
168625
|
return null;
|
|
167199
168626
|
}
|
|
167200
|
-
|
|
168627
|
+
var TEMPORAL_AWARENESS_GUIDANCE = `
|
|
168628
|
+
**Temporal awareness**: User messages may be preceded by HTML comments like \`<!-- +12m -->\`, \`<!-- +2h 15m -->\`, or \`<!-- +3d 4h -->\` indicating time elapsed since the previous message's completion. Compartments in \`<session-history>\` carry \`start-date\` and \`end-date\` attributes (YYYY-MM-DD) showing real-time boundaries. Use these when reasoning about workflow pacing, log durations, build times, or how long ago something happened.`;
|
|
168629
|
+
function buildMagicContextSection(agent, protectedTags, ctxReduceEnabled = true, dreamerEnabled = false, dropToolStructure = true, temporalAwarenessEnabled = false) {
|
|
167201
168630
|
const smartNoteGuidance = dreamerEnabled ? `
|
|
167202
168631
|
When \`surface_condition\` is provided with \`write\`, the note becomes a project-scoped smart note.
|
|
167203
168632
|
The dreamer evaluates smart note conditions during nightly runs and surfaces them when conditions are met.
|
|
167204
168633
|
Example: \`ctx_note(action="write", content="Implement X because Y", surface_condition="When PR #42 is merged in this repo")\`` : "";
|
|
168634
|
+
const temporalGuidance = temporalAwarenessEnabled ? TEMPORAL_AWARENESS_GUIDANCE : "";
|
|
167205
168635
|
if (!ctxReduceEnabled) {
|
|
167206
168636
|
return `## Magic Context
|
|
167207
168637
|
|
|
167208
|
-
${BASE_INTRO_NO_REDUCE(dropToolStructure)}${smartNoteGuidance}`;
|
|
168638
|
+
${BASE_INTRO_NO_REDUCE(dropToolStructure)}${smartNoteGuidance}${temporalGuidance}`;
|
|
167209
168639
|
}
|
|
167210
168640
|
const section = agent ? AGENT_SECTIONS[agent] : GENERIC_SECTION;
|
|
167211
168641
|
return `## Magic Context
|
|
167212
168642
|
|
|
167213
|
-
${BASE_INTRO(protectedTags, dropToolStructure)}${smartNoteGuidance}
|
|
168643
|
+
${BASE_INTRO(protectedTags, dropToolStructure)}${smartNoteGuidance}${temporalGuidance}
|
|
167214
168644
|
${section}
|
|
167215
168645
|
|
|
167216
168646
|
Prefer many small targeted operations over one large blanket operation. Compress early and often \u2014 don't wait for warnings.`;
|
|
@@ -167233,8 +168663,8 @@ function readProjectDocs(directory) {
|
|
|
167233
168663
|
for (const filename of DOC_FILES) {
|
|
167234
168664
|
const filePath = join14(directory, filename);
|
|
167235
168665
|
try {
|
|
167236
|
-
if (
|
|
167237
|
-
const content =
|
|
168666
|
+
if (existsSync7(filePath)) {
|
|
168667
|
+
const content = readFileSync6(filePath, "utf-8").trim();
|
|
167238
168668
|
if (content.length > 0) {
|
|
167239
168669
|
sections.push(`<${filename}>
|
|
167240
168670
|
${content}
|
|
@@ -167265,7 +168695,7 @@ function createSystemPromptHashHandler(deps) {
|
|
|
167265
168695
|
`);
|
|
167266
168696
|
if (fullPrompt.length > 0 && !fullPrompt.includes(MAGIC_CONTEXT_MARKER)) {
|
|
167267
168697
|
const detectedAgent = detectAgentFromSystemPrompt(fullPrompt);
|
|
167268
|
-
const guidance = buildMagicContextSection(detectedAgent, deps.protectedTags, deps.ctxReduceEnabled, deps.dreamerEnabled, deps.dropToolStructure);
|
|
168698
|
+
const guidance = buildMagicContextSection(detectedAgent, deps.protectedTags, deps.ctxReduceEnabled, deps.dreamerEnabled, deps.dropToolStructure, deps.experimentalTemporalAwareness);
|
|
167269
168699
|
output.system.push(guidance);
|
|
167270
168700
|
sessionLog(sessionId, `injected ${detectedAgent ?? "generic"} guidance into system prompt`);
|
|
167271
168701
|
}
|
|
@@ -167314,16 +168744,16 @@ ${items}
|
|
|
167314
168744
|
const keyFileEntries = getKeyFiles(deps.db, sessionId);
|
|
167315
168745
|
if (keyFileEntries.length > 0) {
|
|
167316
168746
|
const sections = [];
|
|
167317
|
-
const projectRoot =
|
|
168747
|
+
const projectRoot = resolve3(deps.directory);
|
|
167318
168748
|
let remainingBudgetTokens = deps.experimentalPinKeyFilesTokenBudget ?? 1e4;
|
|
167319
168749
|
for (const entry of keyFileEntries) {
|
|
167320
168750
|
try {
|
|
167321
|
-
const absPath =
|
|
168751
|
+
const absPath = resolve3(deps.directory, entry.filePath);
|
|
167322
168752
|
if (!absPath.startsWith(projectRoot + sep) && absPath !== projectRoot) {
|
|
167323
168753
|
log(`[magic-context] key file path escapes project root, skipping: ${entry.filePath}`);
|
|
167324
168754
|
continue;
|
|
167325
168755
|
}
|
|
167326
|
-
if (!
|
|
168756
|
+
if (!existsSync7(absPath))
|
|
167327
168757
|
continue;
|
|
167328
168758
|
let realPath;
|
|
167329
168759
|
try {
|
|
@@ -167335,7 +168765,7 @@ ${items}
|
|
|
167335
168765
|
log(`[magic-context] key file symlink escapes project root, skipping: ${entry.filePath} \u2192 ${realPath}`);
|
|
167336
168766
|
continue;
|
|
167337
168767
|
}
|
|
167338
|
-
const content =
|
|
168768
|
+
const content = readFileSync6(realPath, "utf-8").trim();
|
|
167339
168769
|
if (content.length === 0)
|
|
167340
168770
|
continue;
|
|
167341
168771
|
const fileTokens = estimateTokens(content);
|
|
@@ -167473,6 +168903,17 @@ function createMagicContextHook(deps) {
|
|
|
167473
168903
|
const agentBySession = deps.liveSessionState?.agentBySession ?? new Map;
|
|
167474
168904
|
const recentReduceBySession = new Map;
|
|
167475
168905
|
const toolUsageSinceUserTurn = new Map;
|
|
168906
|
+
const resolveLiveModel = (sessionId) => {
|
|
168907
|
+
const cached2 = liveModelBySession.get(sessionId);
|
|
168908
|
+
if (cached2)
|
|
168909
|
+
return cached2;
|
|
168910
|
+
const recovered = findLastAssistantModelFromOpenCodeDb(sessionId);
|
|
168911
|
+
if (recovered) {
|
|
168912
|
+
liveModelBySession.set(sessionId, recovered);
|
|
168913
|
+
return recovered;
|
|
168914
|
+
}
|
|
168915
|
+
return;
|
|
168916
|
+
};
|
|
167476
168917
|
const ctxReduceEnabled = deps.config.ctx_reduce_enabled !== false;
|
|
167477
168918
|
const nudgerWithRecentReduce = ctxReduceEnabled ? createNudger({
|
|
167478
168919
|
protected_tags: deps.config.protected_tags,
|
|
@@ -167504,6 +168945,7 @@ function createMagicContextHook(deps) {
|
|
|
167504
168945
|
getHistorianChunkTokens,
|
|
167505
168946
|
historyBudgetPercentage: deps.config.history_budget_percentage,
|
|
167506
168947
|
executeThresholdPercentage: deps.config.execute_threshold_percentage,
|
|
168948
|
+
executeThresholdTokens: deps.config.execute_threshold_tokens,
|
|
167507
168949
|
historianTimeoutMs: deps.config.historian_timeout_ms ?? DEFAULT_HISTORIAN_TIMEOUT_MS,
|
|
167508
168950
|
getNotificationParams: (sessionId) => getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
|
|
167509
168951
|
getModelKey: (sessionId) => {
|
|
@@ -167517,6 +168959,11 @@ function createMagicContextHook(deps) {
|
|
|
167517
168959
|
projectPath,
|
|
167518
168960
|
experimentalCompactionMarkers: deps.config.compaction_markers,
|
|
167519
168961
|
experimentalUserMemories: deps.config.experimental?.user_memories?.enabled,
|
|
168962
|
+
experimentalTemporalAwareness: deps.config.experimental?.temporal_awareness === true,
|
|
168963
|
+
historianTwoPass: deps.config.historian?.two_pass === true,
|
|
168964
|
+
compressorMinCompartmentRatio: deps.config.compressor?.enabled === false ? undefined : deps.config.compressor?.min_compartment_ratio,
|
|
168965
|
+
compressorMaxMergeDepth: deps.config.compressor?.enabled === false ? undefined : deps.config.compressor?.max_merge_depth,
|
|
168966
|
+
compressorCooldownMs: deps.config.compressor?.enabled === false ? undefined : deps.config.compressor?.cooldown_ms,
|
|
167520
168967
|
liveModelBySession
|
|
167521
168968
|
});
|
|
167522
168969
|
const eventHandler = createEventHandler2({
|
|
@@ -167571,14 +169018,21 @@ function createMagicContextHook(deps) {
|
|
|
167571
169018
|
protectedTags: deps.config.protected_tags,
|
|
167572
169019
|
nudgeIntervalTokens: deps.config.nudge_interval_tokens ?? DEFAULT_NUDGE_INTERVAL_TOKENS,
|
|
167573
169020
|
executeThresholdPercentage: deps.config.execute_threshold_percentage ?? 65,
|
|
169021
|
+
executeThresholdTokens: deps.config.execute_threshold_tokens,
|
|
167574
169022
|
historyBudgetPercentage: deps.config.history_budget_percentage,
|
|
167575
169023
|
commitClusterTrigger: deps.config.commit_cluster_trigger,
|
|
167576
169024
|
getLiveModelKey: (sessionId) => {
|
|
167577
|
-
const model =
|
|
169025
|
+
const model = resolveLiveModel(sessionId);
|
|
167578
169026
|
return model ? `${model.providerID}/${model.modelID}` : undefined;
|
|
167579
169027
|
},
|
|
169028
|
+
getContextLimit: (sessionId) => {
|
|
169029
|
+
const model = resolveLiveModel(sessionId);
|
|
169030
|
+
if (!model)
|
|
169031
|
+
return;
|
|
169032
|
+
return resolveContextLimit(model.providerID, model.modelID);
|
|
169033
|
+
},
|
|
167580
169034
|
onFlush: (sessionId) => flushedSessions.add(sessionId),
|
|
167581
|
-
executeRecomp: async (sessionId) => executeContextRecomp({
|
|
169035
|
+
executeRecomp: async (sessionId, options) => executeContextRecomp({
|
|
167582
169036
|
client: deps.client,
|
|
167583
169037
|
db,
|
|
167584
169038
|
sessionId,
|
|
@@ -167586,11 +169040,12 @@ function createMagicContextHook(deps) {
|
|
|
167586
169040
|
historianTimeoutMs: deps.config.historian_timeout_ms ?? DEFAULT_HISTORIAN_TIMEOUT_MS,
|
|
167587
169041
|
directory: deps.directory,
|
|
167588
169042
|
fallbackModelId: (() => {
|
|
167589
|
-
const model =
|
|
169043
|
+
const model = resolveLiveModel(sessionId);
|
|
167590
169044
|
return model ? `${model.providerID}/${model.modelID}` : undefined;
|
|
167591
169045
|
})(),
|
|
167592
|
-
getNotificationParams: () => getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession)
|
|
167593
|
-
|
|
169046
|
+
getNotificationParams: () => getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
|
|
169047
|
+
historianTwoPass: deps.config.historian?.two_pass === true
|
|
169048
|
+
}, options),
|
|
167594
169049
|
sendNotification: async (sessionId, text, params) => {
|
|
167595
169050
|
await sendIgnoredMessage(deps.client, sessionId, text, {
|
|
167596
169051
|
...getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
|
|
@@ -167632,7 +169087,8 @@ function createMagicContextHook(deps) {
|
|
|
167632
169087
|
lastHeuristicsTurnId,
|
|
167633
169088
|
experimentalUserMemories: deps.config.experimental?.user_memories?.enabled,
|
|
167634
169089
|
experimentalPinKeyFiles: deps.config.experimental?.pin_key_files?.enabled ?? false,
|
|
167635
|
-
experimentalPinKeyFilesTokenBudget: deps.config.experimental?.pin_key_files?.token_budget
|
|
169090
|
+
experimentalPinKeyFilesTokenBudget: deps.config.experimental?.pin_key_files?.token_budget,
|
|
169091
|
+
experimentalTemporalAwareness: deps.config.experimental?.temporal_awareness === true
|
|
167636
169092
|
});
|
|
167637
169093
|
const eventHook = createEventHook({
|
|
167638
169094
|
eventHandler,
|
|
@@ -167687,7 +169143,8 @@ function createSessionHooks(args) {
|
|
|
167687
169143
|
}
|
|
167688
169144
|
const tagger = createTagger();
|
|
167689
169145
|
const scheduler2 = createScheduler({
|
|
167690
|
-
executeThresholdPercentage: pluginConfig.execute_threshold_percentage ?? DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE
|
|
169146
|
+
executeThresholdPercentage: pluginConfig.execute_threshold_percentage ?? DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE,
|
|
169147
|
+
executeThresholdTokens: pluginConfig.execute_threshold_tokens
|
|
167691
169148
|
});
|
|
167692
169149
|
const compactionHandler = createCompactionHandler();
|
|
167693
169150
|
return {
|
|
@@ -167708,12 +169165,14 @@ function createSessionHooks(args) {
|
|
|
167708
169165
|
clear_reasoning_age: pluginConfig.clear_reasoning_age,
|
|
167709
169166
|
iteration_nudge_threshold: pluginConfig.iteration_nudge_threshold,
|
|
167710
169167
|
execute_threshold_percentage: pluginConfig.execute_threshold_percentage ?? DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE,
|
|
169168
|
+
execute_threshold_tokens: pluginConfig.execute_threshold_tokens,
|
|
167711
169169
|
historian: pluginConfig.historian,
|
|
167712
169170
|
history_budget_percentage: pluginConfig.history_budget_percentage,
|
|
167713
169171
|
historian_timeout_ms: pluginConfig.historian_timeout_ms,
|
|
167714
169172
|
memory: pluginConfig.memory,
|
|
167715
169173
|
sidekick: pluginConfig.sidekick,
|
|
167716
169174
|
dreamer: pluginConfig.dreamer,
|
|
169175
|
+
compressor: pluginConfig.compressor,
|
|
167717
169176
|
experimental: pluginConfig.experimental
|
|
167718
169177
|
}
|
|
167719
169178
|
})
|
|
@@ -167969,6 +169428,7 @@ function buildStatusDetail(db, sessionId, directory, modelKey, config2) {
|
|
|
167969
169428
|
cacheRemainingMs: 0,
|
|
167970
169429
|
cacheExpired: false,
|
|
167971
169430
|
executeThreshold: 65,
|
|
169431
|
+
executeThresholdMode: "percentage",
|
|
167972
169432
|
protectedTagCount: 20,
|
|
167973
169433
|
nudgeInterval: 20000,
|
|
167974
169434
|
historyBudgetPercentage: 0.15,
|
|
@@ -167999,9 +169459,20 @@ function buildStatusDetail(db, sessionId, directory, modelKey, config2) {
|
|
|
167999
169459
|
const ops = db.query("SELECT tag_id, operation FROM pending_ops WHERE session_id = ?").all(sessionId);
|
|
168000
169460
|
detail.pendingOps = ops.map((o) => ({ tagId: o.tag_id, operation: o.operation }));
|
|
168001
169461
|
} catch {}
|
|
169462
|
+
const contextLimitForTokens = base.usagePercentage > 0 ? Math.round(base.inputTokens / (base.usagePercentage / 100)) : 0;
|
|
168002
169463
|
if (config2) {
|
|
168003
|
-
const
|
|
168004
|
-
|
|
169464
|
+
const pctCfg = config2.execute_threshold_percentage;
|
|
169465
|
+
const tokensCfg = config2.execute_threshold_tokens;
|
|
169466
|
+
const thresholdDetail = resolveExecuteThresholdDetail(pctCfg ?? 65, modelKey, 65, {
|
|
169467
|
+
tokensConfig: tokensCfg,
|
|
169468
|
+
contextLimit: contextLimitForTokens || undefined,
|
|
169469
|
+
sessionId
|
|
169470
|
+
});
|
|
169471
|
+
detail.executeThreshold = thresholdDetail.percentage;
|
|
169472
|
+
detail.executeThresholdMode = thresholdDetail.mode;
|
|
169473
|
+
if (thresholdDetail.absoluteTokens !== undefined) {
|
|
169474
|
+
detail.executeThresholdTokens = thresholdDetail.absoluteTokens;
|
|
169475
|
+
}
|
|
168005
169476
|
const ct = resolveConfigValue(config2, "cache_ttl", modelKey, "5m");
|
|
168006
169477
|
detail.cacheTtl = ct;
|
|
168007
169478
|
if (typeof config2.protected_tag_count === "number") {
|
|
@@ -169311,7 +170782,7 @@ init_models_dev_cache();
|
|
|
169311
170782
|
init_logger();
|
|
169312
170783
|
import { mkdirSync as mkdirSync4, renameSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
169313
170784
|
import { createServer } from "http";
|
|
169314
|
-
import { dirname } from "path";
|
|
170785
|
+
import { dirname as dirname2 } from "path";
|
|
169315
170786
|
|
|
169316
170787
|
// src/shared/rpc-utils.ts
|
|
169317
170788
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -169337,7 +170808,7 @@ class MagicContextRpcServer {
|
|
|
169337
170808
|
this.handlers.set(method, handler);
|
|
169338
170809
|
}
|
|
169339
170810
|
async start() {
|
|
169340
|
-
return new Promise((
|
|
170811
|
+
return new Promise((resolve4, reject) => {
|
|
169341
170812
|
const server = createServer((req, res) => this.dispatch(req, res));
|
|
169342
170813
|
server.on("error", (err) => {
|
|
169343
170814
|
log(`[rpc] server error: ${err.message}`);
|
|
@@ -169352,7 +170823,7 @@ class MagicContextRpcServer {
|
|
|
169352
170823
|
this.port = addr.port;
|
|
169353
170824
|
this.server = server;
|
|
169354
170825
|
try {
|
|
169355
|
-
const dir =
|
|
170826
|
+
const dir = dirname2(this.portFilePath);
|
|
169356
170827
|
mkdirSync4(dir, { recursive: true });
|
|
169357
170828
|
const tmpPath = `${this.portFilePath}.tmp`;
|
|
169358
170829
|
writeFileSync2(tmpPath, String(this.port), "utf-8");
|
|
@@ -169361,7 +170832,7 @@ class MagicContextRpcServer {
|
|
|
169361
170832
|
} catch (err) {
|
|
169362
170833
|
log(`[rpc] failed to write port file: ${err}`);
|
|
169363
170834
|
}
|
|
169364
|
-
|
|
170835
|
+
resolve4(this.port);
|
|
169365
170836
|
});
|
|
169366
170837
|
server.unref();
|
|
169367
170838
|
});
|
|
@@ -169582,10 +171053,15 @@ var plugin = async (ctx) => {
|
|
|
169582
171053
|
} = pluginConfig.sidekick;
|
|
169583
171054
|
return agentOverrides;
|
|
169584
171055
|
})() : undefined;
|
|
171056
|
+
const historianAgentOverrides = pluginConfig.historian ? (() => {
|
|
171057
|
+
const { two_pass: _twoPass, ...agentOverrides } = pluginConfig.historian;
|
|
171058
|
+
return agentOverrides;
|
|
171059
|
+
})() : undefined;
|
|
169585
171060
|
config2.agent = {
|
|
169586
171061
|
...config2.agent ?? {},
|
|
169587
171062
|
[DREAMER_AGENT]: buildHiddenAgentConfig(DREAMER_AGENT, DREAMER_SYSTEM_PROMPT, dreamerAgentOverrides),
|
|
169588
|
-
[HISTORIAN_AGENT]: buildHiddenAgentConfig(HISTORIAN_AGENT, pluginConfig.experimental?.user_memories?.enabled ? COMPARTMENT_AGENT_SYSTEM_PROMPT + USER_OBSERVATIONS_APPENDIX : COMPARTMENT_AGENT_SYSTEM_PROMPT,
|
|
171063
|
+
[HISTORIAN_AGENT]: buildHiddenAgentConfig(HISTORIAN_AGENT, pluginConfig.experimental?.user_memories?.enabled ? COMPARTMENT_AGENT_SYSTEM_PROMPT + USER_OBSERVATIONS_APPENDIX : COMPARTMENT_AGENT_SYSTEM_PROMPT, historianAgentOverrides),
|
|
171064
|
+
[HISTORIAN_EDITOR_AGENT]: buildHiddenAgentConfig(HISTORIAN_EDITOR_AGENT, HISTORIAN_EDITOR_SYSTEM_PROMPT, historianAgentOverrides),
|
|
169589
171065
|
[SIDEKICK_AGENT]: buildHiddenAgentConfig(SIDEKICK_AGENT, SIDEKICK_SYSTEM_PROMPT, sidekickAgentOverrides)
|
|
169590
171066
|
};
|
|
169591
171067
|
}
|