@cortexkit/opencode-magic-context 0.24.0 → 0.25.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 +4 -2
- package/dist/agents/magic-context-prompt.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts +18 -0
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-local.d.ts +4 -0
- package/dist/features/magic-context/memory/embedding-local.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-openai.d.ts +14 -0
- package/dist/features/magic-context/memory/embedding-openai.d.ts.map +1 -1
- package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts +6 -0
- package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts +38 -0
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-session.d.ts +1 -0
- package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-shared.d.ts +2 -1
- package/dist/features/magic-context/storage-meta-shared.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta.d.ts +1 -1
- package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
- package/dist/features/magic-context/storage-tags.d.ts +20 -1
- package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +2 -2
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/types.d.ts +1 -0
- package/dist/features/magic-context/types.d.ts.map +1 -1
- package/dist/hooks/magic-context/apply-operations.d.ts +3 -2
- package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
- package/dist/hooks/magic-context/caveman-cleanup.d.ts +1 -0
- package/dist/hooks/magic-context/caveman-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/channel2-delivery.d.ts +2 -0
- package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
- package/dist/hooks/magic-context/command-handler.d.ts +7 -5
- package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts +14 -4
- package/dist/hooks/magic-context/ctx-reduce-nudge.d.ts.map +1 -1
- package/dist/hooks/magic-context/embed-session-state.d.ts +14 -0
- package/dist/hooks/magic-context/embed-session-state.d.ts.map +1 -0
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/format-embed-status.d.ts +9 -0
- package/dist/hooks/magic-context/format-embed-status.d.ts.map +1 -0
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts +1 -0
- package/dist/hooks/magic-context/heuristic-cleanup.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/protected-tail-boundary.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts +1 -1
- package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
- package/dist/hooks/magic-context/recomp-orchestrator.d.ts.map +1 -1
- package/dist/hooks/magic-context/strip-content.d.ts +0 -1
- package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-content-primitives.d.ts +2 -0
- package/dist/hooks/magic-context/tag-content-primitives.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/hooks/magic-context/tool-drop-target.d.ts +1 -1
- package/dist/hooks/magic-context/tool-drop-target.d.ts.map +1 -1
- package/dist/hooks/magic-context/tool-reclaim.d.ts +12 -0
- package/dist/hooks/magic-context/tool-reclaim.d.ts.map +1 -0
- package/dist/hooks/magic-context/transform-operations.d.ts +1 -1
- package/dist/hooks/magic-context/transform-operations.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +2 -0
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1117 -378
- package/dist/plugin/conflict-warning-hook.d.ts.map +1 -1
- 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/announcement.d.ts +1 -1
- package/dist/shared/model-suggestion-retry.d.ts.map +1 -1
- package/dist/shared/rpc-types.d.ts +20 -0
- package/dist/shared/rpc-types.d.ts.map +1 -1
- package/dist/shared/sqlite.d.ts +5 -1
- package/dist/shared/sqlite.d.ts.map +1 -1
- package/dist/shared/tui-preferences.d.ts +32 -0
- package/dist/shared/tui-preferences.d.ts.map +1 -0
- package/dist/tools/ctx-expand/constants.d.ts +1 -1
- package/dist/tools/ctx-expand/constants.d.ts.map +1 -1
- package/dist/tools/ctx-expand/render.d.ts +43 -0
- package/dist/tools/ctx-expand/render.d.ts.map +1 -0
- package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
- package/dist/tools/ctx-expand/types.d.ts +6 -2
- package/dist/tools/ctx-expand/types.d.ts.map +1 -1
- package/dist/tools/ctx-reduce/constants.d.ts +1 -1
- package/dist/tools/ctx-reduce/constants.d.ts.map +1 -1
- package/dist/tui/data/context-db.d.ts +4 -2
- package/dist/tui/data/context-db.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/shared/announcement.ts +6 -6
- package/src/shared/model-suggestion-retry.test.ts +61 -1
- package/src/shared/model-suggestion-retry.ts +22 -0
- package/src/shared/rpc-types.ts +11 -0
- package/src/shared/sqlite-bind-style.test.ts +82 -0
- package/src/shared/sqlite.ts +30 -1
- package/src/shared/tag-transcript.test.ts +3 -1
- package/src/shared/tag-transcript.ts +19 -17
- package/src/shared/tui-preferences.test.ts +210 -0
- package/src/shared/tui-preferences.ts +303 -0
- package/src/tui/data/context-db.ts +34 -2
- package/src/tui/index.tsx +58 -4
- package/src/tui/slots/sidebar-content.tsx +106 -12
package/dist/index.js
CHANGED
|
@@ -15562,9 +15562,11 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
|
|
|
15562
15562
|
});
|
|
15563
15563
|
} catch (error51) {
|
|
15564
15564
|
if (signal?.aborted) {
|
|
15565
|
+
await abortChildRun(client, args.path.id);
|
|
15565
15566
|
throw new Error("prompt aborted by external signal");
|
|
15566
15567
|
}
|
|
15567
15568
|
if (controller.signal.aborted) {
|
|
15569
|
+
await abortChildRun(client, args.path.id);
|
|
15568
15570
|
throw new Error(`prompt timed out after ${timeoutMs}ms`);
|
|
15569
15571
|
}
|
|
15570
15572
|
throw error51;
|
|
@@ -15573,6 +15575,13 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
|
|
|
15573
15575
|
signal?.removeEventListener("abort", onExternalAbort);
|
|
15574
15576
|
}
|
|
15575
15577
|
}
|
|
15578
|
+
async function abortChildRun(client, sessionId) {
|
|
15579
|
+
try {
|
|
15580
|
+
await client.session.abort({ path: { id: sessionId } });
|
|
15581
|
+
} catch (error51) {
|
|
15582
|
+
log(`[model-retry] child session abort failed for ${sessionId}: ${String(error51)}`);
|
|
15583
|
+
}
|
|
15584
|
+
}
|
|
15576
15585
|
function isNonRetryable(error51, externalSignal) {
|
|
15577
15586
|
if (externalSignal?.aborted)
|
|
15578
15587
|
return true;
|
|
@@ -15844,7 +15853,7 @@ function isSessionMetaRow(row) {
|
|
|
15844
15853
|
if (row === null || typeof row !== "object")
|
|
15845
15854
|
return false;
|
|
15846
15855
|
const r = row;
|
|
15847
|
-
return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.observed_safe_input_tokens) && isNumberOrNull(r.cache_alert_sent) && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state) && isBlobOrNull(r.cached_m0_bytes) && isBlobOrNull(r.cached_m1_bytes) && isNumberOrNull(r.cached_m0_project_memory_epoch) && isStringOrNull(r.cached_m0_workspace_fingerprint) && isNumberOrNull(r.cached_m0_project_user_profile_version) && isNumberOrNull(r.cached_m0_max_compartment_seq) && isNumberOrNull(r.cached_m0_max_memory_id) && isNumberOrNull(r.cached_m0_max_mutation_id) && isNumberOrNull(r.cached_m0_max_memory_mutation_id) && isStringOrNull(r.cached_m0_project_docs_hash) && isNumberOrNull(r.cached_m0_materialized_at) && isNumberOrNull(r.cached_m0_session_facts_version) && isStringOrNull(r.cached_m0_upgrade_state) && isStringOrNull(r.cached_m0_system_hash) && isStringOrNull(r.cached_m0_tool_set_hash) && isStringOrNull(r.cached_m0_model_key) && isStringOrNull(r.last_observed_model_key) && isNumberOrNull(r.last_usage_context_limit) && isNumberOrNull(r.prior_boundary_ordinal) && isNumberOrNull(r.protected_tail_policy_version) && isNumberOrNull(r.protected_tail_drain_window_started_at) && isNumberOrNull(r.protected_tail_drain_tokens) && isNumberOrNull(r.recovery_no_eligible_head_count) && isNumberOrNull(r.force_emergency_bypass_window_start) && isNumberOrNull(r.force_emergency_bypass_used) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme);
|
|
15856
|
+
return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.observed_safe_input_tokens) && isNumberOrNull(r.cache_alert_sent) && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state) && isBlobOrNull(r.cached_m0_bytes) && isBlobOrNull(r.cached_m1_bytes) && isNumberOrNull(r.cached_m0_project_memory_epoch) && isStringOrNull(r.cached_m0_workspace_fingerprint) && isNumberOrNull(r.cached_m0_project_user_profile_version) && isNumberOrNull(r.cached_m0_max_compartment_seq) && isNumberOrNull(r.cached_m0_max_memory_id) && isNumberOrNull(r.cached_m0_max_mutation_id) && isNumberOrNull(r.cached_m0_max_memory_mutation_id) && isStringOrNull(r.cached_m0_project_docs_hash) && isNumberOrNull(r.cached_m0_materialized_at) && isNumberOrNull(r.cached_m0_session_facts_version) && isStringOrNull(r.cached_m0_upgrade_state) && isStringOrNull(r.cached_m0_system_hash) && isStringOrNull(r.cached_m0_tool_set_hash) && isStringOrNull(r.cached_m0_model_key) && isStringOrNull(r.last_observed_model_key) && isNumberOrNull(r.last_usage_context_limit) && isNumberOrNull(r.prior_boundary_ordinal) && isNumberOrNull(r.protected_tail_policy_version) && isNumberOrNull(r.protected_tail_drain_window_started_at) && isNumberOrNull(r.protected_tail_drain_tokens) && isNumberOrNull(r.recovery_no_eligible_head_count) && isNumberOrNull(r.force_emergency_bypass_window_start) && isNumberOrNull(r.force_emergency_bypass_used) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme) && isNumberOrNull(r.tool_reclaim_watermark);
|
|
15848
15857
|
}
|
|
15849
15858
|
function getDefaultSessionMeta(sessionId) {
|
|
15850
15859
|
return {
|
|
@@ -15867,6 +15876,7 @@ function getDefaultSessionMeta(sessionId) {
|
|
|
15867
15876
|
conversationTokens: 0,
|
|
15868
15877
|
toolCallTokens: 0,
|
|
15869
15878
|
clearedReasoningThroughTag: 0,
|
|
15879
|
+
toolReclaimWatermark: 0,
|
|
15870
15880
|
lastTodoState: "",
|
|
15871
15881
|
cachedM0Bytes: null,
|
|
15872
15882
|
cachedM1Bytes: null,
|
|
@@ -15930,6 +15940,7 @@ function toSessionMeta(row) {
|
|
|
15930
15940
|
conversationTokens: numOrZero(row.conversation_tokens),
|
|
15931
15941
|
toolCallTokens: numOrZero(row.tool_call_tokens),
|
|
15932
15942
|
clearedReasoningThroughTag: numOrZero(row.cleared_reasoning_through_tag),
|
|
15943
|
+
toolReclaimWatermark: numOrZero(row.tool_reclaim_watermark),
|
|
15933
15944
|
lastTodoState: lastTodoStateRaw,
|
|
15934
15945
|
cachedM0Bytes: toBufferOrNull(row.cached_m0_bytes),
|
|
15935
15946
|
cachedM1Bytes: toBufferOrNull(row.cached_m1_bytes),
|
|
@@ -16039,6 +16050,7 @@ var init_storage_meta_shared = __esm(() => {
|
|
|
16039
16050
|
"conversation_tokens",
|
|
16040
16051
|
"tool_call_tokens",
|
|
16041
16052
|
"cleared_reasoning_through_tag",
|
|
16053
|
+
"tool_reclaim_watermark",
|
|
16042
16054
|
"last_todo_state",
|
|
16043
16055
|
"cached_m0_bytes",
|
|
16044
16056
|
"cached_m1_bytes",
|
|
@@ -16087,6 +16099,7 @@ var init_storage_meta_shared = __esm(() => {
|
|
|
16087
16099
|
conversationTokens: "conversation_tokens",
|
|
16088
16100
|
toolCallTokens: "tool_call_tokens",
|
|
16089
16101
|
clearedReasoningThroughTag: "cleared_reasoning_through_tag",
|
|
16102
|
+
toolReclaimWatermark: "tool_reclaim_watermark",
|
|
16090
16103
|
lastTodoState: "last_todo_state",
|
|
16091
16104
|
cachedM0Bytes: "cached_m0_bytes",
|
|
16092
16105
|
cachedM1Bytes: "cached_m1_bytes",
|
|
@@ -16363,6 +16376,14 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
|
|
|
16363
16376
|
}
|
|
16364
16377
|
super(typeof filename === "string" ? filename : ":memory:", translated);
|
|
16365
16378
|
}
|
|
16379
|
+
prepare(sql) {
|
|
16380
|
+
const stmt = super.prepare(sql);
|
|
16381
|
+
for (const method of ["run", "get", "all"]) {
|
|
16382
|
+
const original = stmt[method].bind(stmt);
|
|
16383
|
+
stmt[method] = (...args) => args.length === 1 && Array.isArray(args[0]) ? original(...args[0]) : original(...args);
|
|
16384
|
+
}
|
|
16385
|
+
return stmt;
|
|
16386
|
+
}
|
|
16366
16387
|
transaction(fn) {
|
|
16367
16388
|
const self = this;
|
|
16368
16389
|
const wrapped = function(...args) {
|
|
@@ -149339,6 +149360,9 @@ function stripCompleteTagPairsGlobally(value) {
|
|
|
149339
149360
|
function stripMalformedTagNotationGlobally(value) {
|
|
149340
149361
|
return value.replace(MALFORMED_TAG_GLOBAL_REGEX, "");
|
|
149341
149362
|
}
|
|
149363
|
+
function stripDanglingTagNotationGlobally(value) {
|
|
149364
|
+
return value.replace(DANGLING_TAG_GLOBAL_REGEX, "");
|
|
149365
|
+
}
|
|
149342
149366
|
function stripTagSectionCharacters(value) {
|
|
149343
149367
|
return value.replace(STRAY_SECTION_CHAR_REGEX, "");
|
|
149344
149368
|
}
|
|
@@ -149346,6 +149370,7 @@ function stripPersistedAssistantText(value) {
|
|
|
149346
149370
|
let text = stripWellFormedLeadingTagPrefix(value);
|
|
149347
149371
|
text = stripCompleteTagPairsGlobally(text);
|
|
149348
149372
|
text = stripMalformedTagNotationGlobally(text);
|
|
149373
|
+
text = stripDanglingTagNotationGlobally(text);
|
|
149349
149374
|
text = stripTagSectionCharacters(text);
|
|
149350
149375
|
return text.trim();
|
|
149351
149376
|
}
|
|
@@ -149358,6 +149383,7 @@ function stripTagPrefix(value) {
|
|
|
149358
149383
|
const prev = stripped;
|
|
149359
149384
|
stripped = stripped.replace(MALFORMED_TAG_PREFIX_REGEX, "");
|
|
149360
149385
|
stripped = stripped.replace(TAG_PREFIX_REGEX, "");
|
|
149386
|
+
stripped = stripped.replace(DANGLING_TAG_PREFIX_REGEX, "");
|
|
149361
149387
|
if (stripped === prev)
|
|
149362
149388
|
break;
|
|
149363
149389
|
}
|
|
@@ -149379,11 +149405,13 @@ function isThinkingPart(part) {
|
|
|
149379
149405
|
const candidate = part;
|
|
149380
149406
|
return candidate.type === "thinking" || candidate.type === "reasoning";
|
|
149381
149407
|
}
|
|
149382
|
-
var encoder, TAG_PREFIX_REGEX, MALFORMED_TAG_PREFIX_REGEX, COMPLETE_TAG_PAIR_GLOBAL_REGEX, MALFORMED_TAG_GLOBAL_REGEX, STRAY_SECTION_CHAR_REGEX;
|
|
149408
|
+
var encoder, TAG_PREFIX_REGEX, MALFORMED_TAG_PREFIX_REGEX, DANGLING_TAG_GLOBAL_REGEX, DANGLING_TAG_PREFIX_REGEX, COMPLETE_TAG_PAIR_GLOBAL_REGEX, MALFORMED_TAG_GLOBAL_REGEX, STRAY_SECTION_CHAR_REGEX;
|
|
149383
149409
|
var init_tag_content_primitives = __esm(() => {
|
|
149384
149410
|
encoder = new TextEncoder;
|
|
149385
149411
|
TAG_PREFIX_REGEX = /^(?:§\d+§\s*)+/;
|
|
149386
149412
|
MALFORMED_TAG_PREFIX_REGEX = /^(?:§\d+">§(?:\d+§)?\s*)+/;
|
|
149413
|
+
DANGLING_TAG_GLOBAL_REGEX = /\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?/g;
|
|
149414
|
+
DANGLING_TAG_PREFIX_REGEX = /^(?:\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?\s*)+/;
|
|
149387
149415
|
COMPLETE_TAG_PAIR_GLOBAL_REGEX = /\u00a7\d+\u00a7/g;
|
|
149388
149416
|
MALFORMED_TAG_GLOBAL_REGEX = /\u00a7\d+">(?:\u00a7(?:\d+\u00a7)?)?/g;
|
|
149389
149417
|
STRAY_SECTION_CHAR_REGEX = /\u00a7/g;
|
|
@@ -149447,12 +149475,13 @@ function setToolContent(part, content) {
|
|
|
149447
149475
|
part.content = content;
|
|
149448
149476
|
}
|
|
149449
149477
|
}
|
|
149450
|
-
function truncateToolPart(part) {
|
|
149478
|
+
function truncateToolPart(part, tagId) {
|
|
149451
149479
|
if (!isRecord(part))
|
|
149452
149480
|
return;
|
|
149481
|
+
const sentinel = `[dropped §${tagId}§]`;
|
|
149453
149482
|
if (part.type === "tool" && isRecord(part.state)) {
|
|
149454
149483
|
const state = part.state;
|
|
149455
|
-
state.output =
|
|
149484
|
+
state.output = sentinel;
|
|
149456
149485
|
if (isRecord(state.input)) {
|
|
149457
149486
|
const inputSize = estimateInputSize(state.input);
|
|
149458
149487
|
if (inputSize > 500) {
|
|
@@ -149462,7 +149491,7 @@ function truncateToolPart(part) {
|
|
|
149462
149491
|
return;
|
|
149463
149492
|
}
|
|
149464
149493
|
if (part.type === "tool_result") {
|
|
149465
|
-
part.content =
|
|
149494
|
+
part.content = sentinel;
|
|
149466
149495
|
return;
|
|
149467
149496
|
}
|
|
149468
149497
|
if (part.type === "tool-invocation" && isRecord(part.args)) {
|
|
@@ -149579,7 +149608,7 @@ class ToolMutationBatch {
|
|
|
149579
149608
|
this.affectedMessages.clear();
|
|
149580
149609
|
}
|
|
149581
149610
|
}
|
|
149582
|
-
function createToolDropTarget(compositeKey, thinkingParts, index, batch) {
|
|
149611
|
+
function createToolDropTarget(compositeKey, thinkingParts, index, batch, tagId) {
|
|
149583
149612
|
const drop = () => {
|
|
149584
149613
|
const entry = index.get(compositeKey);
|
|
149585
149614
|
if (!entry || entry.occurrences.length === 0)
|
|
@@ -149600,7 +149629,7 @@ function createToolDropTarget(compositeKey, thinkingParts, index, batch) {
|
|
|
149600
149629
|
if (!entry.hasResult)
|
|
149601
149630
|
return "incomplete";
|
|
149602
149631
|
for (const occurrence of entry.occurrences) {
|
|
149603
|
-
truncateToolPart(occurrence.part);
|
|
149632
|
+
truncateToolPart(occurrence.part, tagId);
|
|
149604
149633
|
}
|
|
149605
149634
|
clearThinkingParts(thinkingParts);
|
|
149606
149635
|
return "truncated";
|
|
@@ -151334,6 +151363,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
151334
151363
|
ensureColumn(db, "session_meta", "historian_last_failure_at", "INTEGER DEFAULT NULL");
|
|
151335
151364
|
ensureColumn(db, "session_meta", "system_prompt_hash", "TEXT DEFAULT ''");
|
|
151336
151365
|
ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
|
|
151366
|
+
ensureColumn(db, "session_meta", "tool_reclaim_watermark", "INTEGER DEFAULT 0");
|
|
151337
151367
|
ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
|
|
151338
151368
|
ensureColumn(db, "session_meta", "stale_reduce_stripped_ids", "TEXT DEFAULT ''");
|
|
151339
151369
|
ensureColumn(db, "session_meta", "processed_image_stripped_ids", "TEXT DEFAULT ''");
|
|
@@ -154123,7 +154153,8 @@ var exports_storage_meta_session = {};
|
|
|
154123
154153
|
__export(exports_storage_meta_session, {
|
|
154124
154154
|
updateSessionMeta: () => updateSessionMeta,
|
|
154125
154155
|
getOrCreateSessionMeta: () => getOrCreateSessionMeta,
|
|
154126
|
-
clearSession: () => clearSession
|
|
154156
|
+
clearSession: () => clearSession,
|
|
154157
|
+
advanceToolReclaimWatermark: () => advanceToolReclaimWatermark
|
|
154127
154158
|
});
|
|
154128
154159
|
import { Buffer as Buffer3 } from "node:buffer";
|
|
154129
154160
|
function getSessionMetaSelectColumns(db) {
|
|
@@ -154184,6 +154215,14 @@ function updateSessionMeta(db, sessionId, updates) {
|
|
|
154184
154215
|
db.prepare(`UPDATE session_meta SET ${setClauses.join(", ")} WHERE session_id = ?`).run(...values, sessionId);
|
|
154185
154216
|
})();
|
|
154186
154217
|
}
|
|
154218
|
+
function advanceToolReclaimWatermark(db, sessionId, maxTagNumber) {
|
|
154219
|
+
if (maxTagNumber <= 0)
|
|
154220
|
+
return;
|
|
154221
|
+
db.transaction(() => {
|
|
154222
|
+
ensureSessionMetaRow(db, sessionId);
|
|
154223
|
+
db.prepare("UPDATE session_meta SET tool_reclaim_watermark = MAX(COALESCE(tool_reclaim_watermark, 0), ?) WHERE session_id = ?").run(maxTagNumber, sessionId);
|
|
154224
|
+
})();
|
|
154225
|
+
}
|
|
154187
154226
|
function clearSession(db, sessionId) {
|
|
154188
154227
|
db.transaction(() => {
|
|
154189
154228
|
db.prepare("DELETE FROM pending_ops WHERE session_id = ?").run(sessionId);
|
|
@@ -154222,6 +154261,7 @@ var init_storage_meta_session = __esm(async () => {
|
|
|
154222
154261
|
last_transform_error: "'' AS last_transform_error",
|
|
154223
154262
|
system_prompt_hash: "'' AS system_prompt_hash",
|
|
154224
154263
|
last_todo_state: "'' AS last_todo_state",
|
|
154264
|
+
tool_reclaim_watermark: "0 AS tool_reclaim_watermark",
|
|
154225
154265
|
cached_m0_bytes: "NULL AS cached_m0_bytes",
|
|
154226
154266
|
cached_m1_bytes: "NULL AS cached_m1_bytes",
|
|
154227
154267
|
cached_m0_project_memory_epoch: "NULL AS cached_m0_project_memory_epoch",
|
|
@@ -154606,15 +154646,22 @@ function ownerMessageIdForTagRow(row) {
|
|
|
154606
154646
|
}
|
|
154607
154647
|
return row.message_id.replace(CONTENT_ID_SUFFIX, "");
|
|
154608
154648
|
}
|
|
154609
|
-
function getActiveTagTokenAggregate(db, sessionId) {
|
|
154610
|
-
const
|
|
154649
|
+
function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
|
|
154650
|
+
const toolOutputExpr = protectedTags > 0 ? `COALESCE(SUM(CASE WHEN type = 'tool' AND tag_number < (
|
|
154651
|
+
SELECT tag_number FROM tags
|
|
154652
|
+
WHERE session_id = ? AND status = 'active'
|
|
154653
|
+
ORDER BY tag_number DESC LIMIT 1 OFFSET ?
|
|
154654
|
+
) THEN COALESCE(token_count, 0) ELSE 0 END), 0)` : `COALESCE(SUM(CASE WHEN type = 'tool' THEN COALESCE(token_count, 0) ELSE 0 END), 0)`;
|
|
154655
|
+
const sql = `SELECT
|
|
154611
154656
|
COALESCE(SUM(CASE WHEN type != 'tool' THEN COALESCE(token_count, 0) ELSE 0 END), 0)
|
|
154612
154657
|
+ COALESCE(SUM(COALESCE(reasoning_token_count, 0)), 0) AS conversation,
|
|
154613
154658
|
COALESCE(SUM(CASE WHEN type = 'tool' THEN COALESCE(token_count, 0) + COALESCE(input_token_count, 0) ELSE 0 END), 0) AS tool_call,
|
|
154614
|
-
|
|
154659
|
+
${toolOutputExpr} AS tool_output,
|
|
154615
154660
|
COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
|
|
154616
154661
|
FROM tags
|
|
154617
|
-
WHERE session_id = ? AND status = 'active'
|
|
154662
|
+
WHERE session_id = ? AND status = 'active'`;
|
|
154663
|
+
const params = protectedTags > 0 ? [sessionId, protectedTags - 1, sessionId] : [sessionId];
|
|
154664
|
+
const row = db.prepare(sql).get(...params);
|
|
154618
154665
|
return {
|
|
154619
154666
|
conversation: row?.conversation ?? 0,
|
|
154620
154667
|
toolCall: row?.tool_call ?? 0,
|
|
@@ -154622,6 +154669,26 @@ function getActiveTagTokenAggregate(db, sessionId) {
|
|
|
154622
154669
|
nullCount: row?.null_count ?? 0
|
|
154623
154670
|
};
|
|
154624
154671
|
}
|
|
154672
|
+
function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, limit = 4) {
|
|
154673
|
+
if (limit <= 0)
|
|
154674
|
+
return [];
|
|
154675
|
+
const boundedLimit = Math.max(1, Math.min(10, Math.floor(limit)));
|
|
154676
|
+
const whereProtected = protectedTags > 0 ? `AND tag_number < (
|
|
154677
|
+
SELECT tag_number FROM tags
|
|
154678
|
+
WHERE session_id = ? AND status = 'active'
|
|
154679
|
+
ORDER BY tag_number DESC LIMIT 1 OFFSET ?
|
|
154680
|
+
)` : "";
|
|
154681
|
+
const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
|
|
154682
|
+
const rows = db.prepare(`SELECT tag_number, tool_name
|
|
154683
|
+
FROM tags
|
|
154684
|
+
WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
|
|
154685
|
+
ORDER BY tag_number ASC, id ASC
|
|
154686
|
+
LIMIT ?`).all(...params);
|
|
154687
|
+
return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
|
|
154688
|
+
tagNumber: row.tag_number,
|
|
154689
|
+
toolName: typeof row.tool_name === "string" ? row.tool_name : null
|
|
154690
|
+
}));
|
|
154691
|
+
}
|
|
154625
154692
|
function getTriggerTagTokenUpperBound(db, sessionId) {
|
|
154626
154693
|
const row = db.prepare(`SELECT
|
|
154627
154694
|
COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
|
|
@@ -163965,6 +164032,58 @@ var init_safe_notification_target = __esm(() => {
|
|
|
163965
164032
|
DEFAULT_TITLE_RE = /^(New session - |Child session - )\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
|
|
163966
164033
|
});
|
|
163967
164034
|
|
|
164035
|
+
// src/shared/rpc-notifications.ts
|
|
164036
|
+
var exports_rpc_notifications = {};
|
|
164037
|
+
__export(exports_rpc_notifications, {
|
|
164038
|
+
pushNotification: () => pushNotification,
|
|
164039
|
+
isTuiConnected: () => isTuiConnected,
|
|
164040
|
+
drainNotifications: () => drainNotifications
|
|
164041
|
+
});
|
|
164042
|
+
function pushNotification(type, payload, sessionId) {
|
|
164043
|
+
queue.push({ id: nextNotificationId++, type, payload, sessionId });
|
|
164044
|
+
if (queue.length > 100) {
|
|
164045
|
+
const newestPerSession = new Map;
|
|
164046
|
+
for (const n of queue) {
|
|
164047
|
+
const prev = newestPerSession.get(n.sessionId);
|
|
164048
|
+
if (prev === undefined || n.id > prev) {
|
|
164049
|
+
newestPerSession.set(n.sessionId, n.id);
|
|
164050
|
+
}
|
|
164051
|
+
}
|
|
164052
|
+
const mustKeep = new Set(newestPerSession.values());
|
|
164053
|
+
const byNewest = [...queue].sort((a, b) => b.id - a.id);
|
|
164054
|
+
const kept = [];
|
|
164055
|
+
for (const n of byNewest) {
|
|
164056
|
+
if (kept.length < 50 || mustKeep.has(n.id))
|
|
164057
|
+
kept.push(n);
|
|
164058
|
+
}
|
|
164059
|
+
queue = kept.sort((a, b) => a.id - b.id);
|
|
164060
|
+
}
|
|
164061
|
+
}
|
|
164062
|
+
function drainNotifications(lastReceivedId = 0, sessionId) {
|
|
164063
|
+
const now = Date.now();
|
|
164064
|
+
lastDrainAtAny = now;
|
|
164065
|
+
if (sessionId !== undefined)
|
|
164066
|
+
lastDrainAtBySession.set(sessionId, now);
|
|
164067
|
+
const matchesClient = (notification) => sessionId === undefined || notification.sessionId === undefined || notification.sessionId === sessionId;
|
|
164068
|
+
if (lastReceivedId > 0) {
|
|
164069
|
+
queue = queue.filter((notification) => !(notification.id <= lastReceivedId && matchesClient(notification)));
|
|
164070
|
+
}
|
|
164071
|
+
return queue.filter((notification) => notification.id > lastReceivedId && matchesClient(notification));
|
|
164072
|
+
}
|
|
164073
|
+
function isTuiConnected(sessionId) {
|
|
164074
|
+
const now = Date.now();
|
|
164075
|
+
if (sessionId !== undefined) {
|
|
164076
|
+
const at = lastDrainAtBySession.get(sessionId) ?? 0;
|
|
164077
|
+
return at > 0 && now - at < TUI_CONNECTED_WINDOW_MS;
|
|
164078
|
+
}
|
|
164079
|
+
return lastDrainAtAny > 0 && now - lastDrainAtAny < TUI_CONNECTED_WINDOW_MS;
|
|
164080
|
+
}
|
|
164081
|
+
var queue, nextNotificationId = 1, lastDrainAtBySession, lastDrainAtAny = 0, TUI_CONNECTED_WINDOW_MS = 3000;
|
|
164082
|
+
var init_rpc_notifications = __esm(() => {
|
|
164083
|
+
queue = [];
|
|
164084
|
+
lastDrainAtBySession = new Map;
|
|
164085
|
+
});
|
|
164086
|
+
|
|
163968
164087
|
// src/plugin/conflict-warning-hook.ts
|
|
163969
164088
|
var exports_conflict_warning_hook = {};
|
|
163970
164089
|
__export(exports_conflict_warning_hook, {
|
|
@@ -164299,6 +164418,9 @@ async function sendStartupAnnouncement(client, directory, version2, features, fo
|
|
|
164299
164418
|
if (!sessionId) {
|
|
164300
164419
|
return;
|
|
164301
164420
|
}
|
|
164421
|
+
const { isTuiConnected: isTuiConnected2 } = await Promise.resolve().then(() => (init_rpc_notifications(), exports_rpc_notifications));
|
|
164422
|
+
if (isTuiConnected2(sessionId) || isTuiConnected2())
|
|
164423
|
+
return;
|
|
164302
164424
|
if (await waitForSafeNotificationTarget(client, sessionId) === "skip")
|
|
164303
164425
|
return;
|
|
164304
164426
|
const bullets = features.map((line) => ` • ${line}`).join(`
|
|
@@ -164521,6 +164643,20 @@ function getDistinctStoredModelIds(db, projectPath) {
|
|
|
164521
164643
|
const rows = getDistinctStoredModelIdsStatement(db).all(projectPath);
|
|
164522
164644
|
return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
|
|
164523
164645
|
}
|
|
164646
|
+
function getMemoryEmbedCoverage(db, projectPath, modelId) {
|
|
164647
|
+
const row = db.prepare(`SELECT
|
|
164648
|
+
COUNT(*) AS total,
|
|
164649
|
+
SUM(CASE WHEN EXISTS (
|
|
164650
|
+
SELECT 1 FROM memory_embeddings e
|
|
164651
|
+
WHERE e.memory_id = m.id AND e.model_id = ?
|
|
164652
|
+
) THEN 1 ELSE 0 END) AS embedded
|
|
164653
|
+
FROM memories m
|
|
164654
|
+
WHERE m.project_path = ? AND m.status = 'active'`).get(modelId, projectPath);
|
|
164655
|
+
return {
|
|
164656
|
+
total: typeof row?.total === "number" ? row.total : 0,
|
|
164657
|
+
embedded: typeof row?.embedded === "number" ? row.embedded : 0
|
|
164658
|
+
};
|
|
164659
|
+
}
|
|
164524
164660
|
var saveEmbeddingStatements, loadAllEmbeddingsStatements, deleteEmbeddingStatements, getStoredModelIdStatements, clearAllEmbeddingsStatements, getDistinctStoredModelIdsStatements;
|
|
164525
164661
|
var init_storage_memory_embeddings = __esm(() => {
|
|
164526
164662
|
saveEmbeddingStatements = new WeakMap;
|
|
@@ -165369,9 +165505,10 @@ function chunkCanonicalText(canonicalText, startOrdinal, endOrdinal, maxInputTok
|
|
|
165369
165505
|
if (lines.length === 0 || endOrdinal < startOrdinal)
|
|
165370
165506
|
return [];
|
|
165371
165507
|
const normalizedMax = normalizeCompartmentChunkMaxInputTokens(maxInputTokens);
|
|
165508
|
+
const effectiveMax = Math.max(1, Math.floor(normalizedMax * CHUNK_WINDOW_SAFETY_RATIO));
|
|
165372
165509
|
const fullText = lines.join(`
|
|
165373
165510
|
`);
|
|
165374
|
-
if (estimateTokens(fullText) <=
|
|
165511
|
+
if (estimateTokens(fullText) <= effectiveMax) {
|
|
165375
165512
|
return [
|
|
165376
165513
|
{
|
|
165377
165514
|
windowIndex: 0,
|
|
@@ -165409,7 +165546,7 @@ function chunkCanonicalText(canonicalText, startOrdinal, endOrdinal, maxInputTok
|
|
|
165409
165546
|
const lineStart = range?.start ?? startOrdinal;
|
|
165410
165547
|
const lineEnd = range?.end ?? lineStart;
|
|
165411
165548
|
const lineTokens = estimateTokens(line);
|
|
165412
|
-
if (currentLines.length > 0 && currentTokens + lineTokens >
|
|
165549
|
+
if (currentLines.length > 0 && currentTokens + lineTokens > effectiveMax) {
|
|
165413
165550
|
flush2();
|
|
165414
165551
|
}
|
|
165415
165552
|
if (currentLines.length === 0) {
|
|
@@ -165564,7 +165701,29 @@ function countUnembeddedSessionCompartments(db, projectPath, sessionId, modelId)
|
|
|
165564
165701
|
)`).get(projectPath, sessionId, projectPath, modelId);
|
|
165565
165702
|
return typeof row?.n === "number" ? row.n : 0;
|
|
165566
165703
|
}
|
|
165567
|
-
|
|
165704
|
+
function countSessionCompartmentEmbedCoverage(db, projectPath, sessionId, modelId) {
|
|
165705
|
+
const row = db.prepare(`SELECT
|
|
165706
|
+
COUNT(*) AS total,
|
|
165707
|
+
SUM(CASE WHEN EXISTS (
|
|
165708
|
+
SELECT 1 FROM compartment_chunk_embeddings e
|
|
165709
|
+
WHERE e.compartment_id = c.id
|
|
165710
|
+
AND e.project_path = ?
|
|
165711
|
+
AND e.model_id = ?
|
|
165712
|
+
) THEN 1 ELSE 0 END) AS embedded
|
|
165713
|
+
FROM compartments c
|
|
165714
|
+
JOIN session_projects sp
|
|
165715
|
+
ON sp.session_id = c.session_id
|
|
165716
|
+
AND sp.harness = c.harness
|
|
165717
|
+
AND sp.project_path = ?
|
|
165718
|
+
WHERE c.session_id = ?
|
|
165719
|
+
AND c.start_message IS NOT NULL
|
|
165720
|
+
AND c.end_message IS NOT NULL`).get(projectPath, modelId, projectPath, sessionId);
|
|
165721
|
+
return {
|
|
165722
|
+
total: typeof row?.total === "number" ? row.total : 0,
|
|
165723
|
+
embedded: typeof row?.embedded === "number" ? row.embedded : 0
|
|
165724
|
+
};
|
|
165725
|
+
}
|
|
165726
|
+
var DEFAULT_COMPARTMENT_CHUNK_MAX_INPUT_TOKENS = 512, CHUNK_WINDOW_SAFETY_RATIO = 0.9, loadFtsRowsStatements, existingHashStatements, existingHashByProjectStatements, deleteByCompartmentStatements, insertEmbeddingStatements, distinctModelStatements, clearProjectStatements, clearProjectModelStatements, searchRowsStatements, searchRowsByModelStatements, backfillCandidateStatements, sessionBackfillCandidateStatements;
|
|
165568
165727
|
var init_compartment_chunk_embedding = __esm(() => {
|
|
165569
165728
|
init_read_session_formatting();
|
|
165570
165729
|
loadFtsRowsStatements = new WeakMap;
|
|
@@ -165721,6 +165880,18 @@ async function withQuietConsole(fn) {
|
|
|
165721
165880
|
console.error = origError;
|
|
165722
165881
|
}
|
|
165723
165882
|
}
|
|
165883
|
+
function isNativeRuntimeMissingError(error51) {
|
|
165884
|
+
const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
|
|
165885
|
+
const lower = message.toLowerCase();
|
|
165886
|
+
const code = error51?.code;
|
|
165887
|
+
const name2 = error51?.name;
|
|
165888
|
+
if (code === "ERR_DLOPEN_FAILED" && lower.includes("onnxruntime")) {
|
|
165889
|
+
return true;
|
|
165890
|
+
}
|
|
165891
|
+
if (!lower.includes("onnxruntime-node"))
|
|
165892
|
+
return false;
|
|
165893
|
+
return code === "ERR_MODULE_NOT_FOUND" || name2 === "ResolveMessage" || lower.includes("cannot find package") || lower.includes("cannot find module") || lower.includes("err_module_not_found");
|
|
165894
|
+
}
|
|
165724
165895
|
function isTransientLoadError(error51) {
|
|
165725
165896
|
const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
|
|
165726
165897
|
if (!message)
|
|
@@ -165785,6 +165956,9 @@ class LocalEmbeddingProvider {
|
|
|
165785
165956
|
if (this.pipeline) {
|
|
165786
165957
|
return true;
|
|
165787
165958
|
}
|
|
165959
|
+
if (nativeRuntimeMissing) {
|
|
165960
|
+
return false;
|
|
165961
|
+
}
|
|
165788
165962
|
if (this.initPromise) {
|
|
165789
165963
|
await this.initPromise;
|
|
165790
165964
|
return this.pipeline !== null;
|
|
@@ -165851,7 +166025,12 @@ class LocalEmbeddingProvider {
|
|
|
165851
166025
|
await releaseLock();
|
|
165852
166026
|
}
|
|
165853
166027
|
} catch (error51) {
|
|
165854
|
-
|
|
166028
|
+
if (isNativeRuntimeMissingError(error51)) {
|
|
166029
|
+
nativeRuntimeMissing = true;
|
|
166030
|
+
log("[magic-context] local embedding runtime is not installed (onnxruntime-node missing from this install). Local embeddings are disabled. Fix: reinstall the plugin (run `npx @cortexkit/magic-context@latest doctor --force`), or configure an `openai-compatible`/`ollama` embedding endpoint instead. Existing memories are unaffected.");
|
|
166031
|
+
} else {
|
|
166032
|
+
log("[magic-context] embedding model failed to load:", error51);
|
|
166033
|
+
}
|
|
165855
166034
|
this.pipeline = null;
|
|
165856
166035
|
} finally {
|
|
165857
166036
|
this.initPromise = null;
|
|
@@ -165962,7 +166141,7 @@ class LocalEmbeddingProvider {
|
|
|
165962
166141
|
return this.pipeline !== null;
|
|
165963
166142
|
}
|
|
165964
166143
|
}
|
|
165965
|
-
var LOCK_POLL_MS = 150, STALE_LOCK_MS, MAX_LOCK_WAIT_MS;
|
|
166144
|
+
var LOCK_POLL_MS = 150, STALE_LOCK_MS, MAX_LOCK_WAIT_MS, nativeRuntimeMissing = false;
|
|
165966
166145
|
var init_embedding_local = __esm(() => {
|
|
165967
166146
|
init_magic_context();
|
|
165968
166147
|
init_data_path();
|
|
@@ -166032,6 +166211,13 @@ var init_embedding_ssrf = __esm(() => {
|
|
|
166032
166211
|
function normalizeEndpoint3(endpoint) {
|
|
166033
166212
|
return endpoint?.trim().replace(/\/+$/, "") ?? "";
|
|
166034
166213
|
}
|
|
166214
|
+
function embeddingModelsMatch(served, requested) {
|
|
166215
|
+
const a = served.trim().toLowerCase();
|
|
166216
|
+
const b = requested.trim().toLowerCase();
|
|
166217
|
+
if (a.length === 0 || b.length === 0)
|
|
166218
|
+
return true;
|
|
166219
|
+
return a === b || a.includes(b) || b.includes(a);
|
|
166220
|
+
}
|
|
166035
166221
|
|
|
166036
166222
|
class OpenAICompatibleEmbeddingProvider {
|
|
166037
166223
|
modelId;
|
|
@@ -166045,6 +166231,7 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
166045
166231
|
failureTimes = [];
|
|
166046
166232
|
circuitOpenUntil = 0;
|
|
166047
166233
|
openLogged = false;
|
|
166234
|
+
modelMismatchLogged = false;
|
|
166048
166235
|
halfOpenProbeInFlight = false;
|
|
166049
166236
|
constructor(options) {
|
|
166050
166237
|
this.endpoint = normalizeEndpoint3(options.endpoint);
|
|
@@ -166143,6 +166330,15 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
166143
166330
|
this.recordFailure(isProbe);
|
|
166144
166331
|
return Array.from({ length: texts.length }, () => null);
|
|
166145
166332
|
}
|
|
166333
|
+
const servedModel = typeof body.model === "string" ? body.model : "";
|
|
166334
|
+
if (this.model && servedModel && !embeddingModelsMatch(servedModel, this.model)) {
|
|
166335
|
+
if (!this.modelMismatchLogged) {
|
|
166336
|
+
log(`[magic-context] embedding endpoint served a DIFFERENT model than requested — refusing the substituted vectors (they have the wrong dimensions/space). requested="${this.model}" served="${servedModel}". The endpoint likely substituted a loaded model; load/select "${this.model}" on the endpoint, or set embedding.model to the served model.`);
|
|
166337
|
+
this.modelMismatchLogged = true;
|
|
166338
|
+
}
|
|
166339
|
+
this.recordFailure(isProbe);
|
|
166340
|
+
return Array.from({ length: texts.length }, () => null);
|
|
166341
|
+
}
|
|
166146
166342
|
const items = Array.isArray(body.data) ? body.data : [];
|
|
166147
166343
|
const results = Array.from({ length: texts.length }, (_, index) => {
|
|
166148
166344
|
const embedding = items[index]?.embedding;
|
|
@@ -166359,6 +166555,121 @@ var init_storage_git_commit_embeddings = __esm(() => {
|
|
|
166359
166555
|
distinctModelIdStatements = new WeakMap;
|
|
166360
166556
|
});
|
|
166361
166557
|
|
|
166558
|
+
// src/features/magic-context/git-commits/storage-git-commits.ts
|
|
166559
|
+
function getInsertStatement(db) {
|
|
166560
|
+
let stmt = insertStatements.get(db);
|
|
166561
|
+
if (!stmt) {
|
|
166562
|
+
stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
|
|
166563
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
166564
|
+
ON CONFLICT(sha) DO UPDATE SET
|
|
166565
|
+
project_path = excluded.project_path,
|
|
166566
|
+
short_sha = excluded.short_sha,
|
|
166567
|
+
message = excluded.message,
|
|
166568
|
+
author = excluded.author,
|
|
166569
|
+
committed_at = excluded.committed_at,
|
|
166570
|
+
indexed_at = excluded.indexed_at
|
|
166571
|
+
WHERE git_commits.message != excluded.message`);
|
|
166572
|
+
insertStatements.set(db, stmt);
|
|
166573
|
+
}
|
|
166574
|
+
return stmt;
|
|
166575
|
+
}
|
|
166576
|
+
function getExistingShasStatement(db) {
|
|
166577
|
+
let stmt = existingShasStatements.get(db);
|
|
166578
|
+
if (!stmt) {
|
|
166579
|
+
stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
|
|
166580
|
+
existingShasStatements.set(db, stmt);
|
|
166581
|
+
}
|
|
166582
|
+
return stmt;
|
|
166583
|
+
}
|
|
166584
|
+
function getProjectCountStatement(db) {
|
|
166585
|
+
let stmt = projectCountStatements.get(db);
|
|
166586
|
+
if (!stmt) {
|
|
166587
|
+
stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
|
|
166588
|
+
projectCountStatements.set(db, stmt);
|
|
166589
|
+
}
|
|
166590
|
+
return stmt;
|
|
166591
|
+
}
|
|
166592
|
+
function getLatestCommitTimeStatement(db) {
|
|
166593
|
+
let stmt = latestCommitTimeStatements.get(db);
|
|
166594
|
+
if (!stmt) {
|
|
166595
|
+
stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
|
|
166596
|
+
latestCommitTimeStatements.set(db, stmt);
|
|
166597
|
+
}
|
|
166598
|
+
return stmt;
|
|
166599
|
+
}
|
|
166600
|
+
function getEvictOverflowStatement(db) {
|
|
166601
|
+
let stmt = evictOverflowStatements.get(db);
|
|
166602
|
+
if (!stmt) {
|
|
166603
|
+
stmt = db.prepare(`DELETE FROM git_commits
|
|
166604
|
+
WHERE rowid IN (
|
|
166605
|
+
SELECT rowid FROM git_commits
|
|
166606
|
+
WHERE project_path = ?
|
|
166607
|
+
ORDER BY committed_at DESC, sha DESC
|
|
166608
|
+
LIMIT -1 OFFSET ?
|
|
166609
|
+
)`);
|
|
166610
|
+
evictOverflowStatements.set(db, stmt);
|
|
166611
|
+
}
|
|
166612
|
+
return stmt;
|
|
166613
|
+
}
|
|
166614
|
+
function upsertCommits(db, projectPath, commits) {
|
|
166615
|
+
if (commits.length === 0)
|
|
166616
|
+
return { inserted: 0, updated: 0 };
|
|
166617
|
+
const existing = new Set;
|
|
166618
|
+
for (const row of getExistingShasStatement(db).all(projectPath)) {
|
|
166619
|
+
existing.add(row.sha);
|
|
166620
|
+
}
|
|
166621
|
+
let inserted = 0;
|
|
166622
|
+
let updated = 0;
|
|
166623
|
+
const now = Date.now();
|
|
166624
|
+
const insertStmt = getInsertStatement(db);
|
|
166625
|
+
db.transaction(() => {
|
|
166626
|
+
for (const commit of commits) {
|
|
166627
|
+
const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
|
|
166628
|
+
if (result.changes > 0) {
|
|
166629
|
+
if (existing.has(commit.sha)) {
|
|
166630
|
+
updated++;
|
|
166631
|
+
} else {
|
|
166632
|
+
inserted++;
|
|
166633
|
+
existing.add(commit.sha);
|
|
166634
|
+
}
|
|
166635
|
+
}
|
|
166636
|
+
}
|
|
166637
|
+
})();
|
|
166638
|
+
return { inserted, updated };
|
|
166639
|
+
}
|
|
166640
|
+
function getCommitCount(db, projectPath) {
|
|
166641
|
+
const row = getProjectCountStatement(db).get(projectPath);
|
|
166642
|
+
return row?.count ?? 0;
|
|
166643
|
+
}
|
|
166644
|
+
function getLatestIndexedCommitTimeMs(db, projectPath) {
|
|
166645
|
+
const row = getLatestCommitTimeStatement(db).get(projectPath);
|
|
166646
|
+
return row?.latest ?? null;
|
|
166647
|
+
}
|
|
166648
|
+
function enforceProjectCap(db, projectPath, maxCommits) {
|
|
166649
|
+
if (maxCommits <= 0)
|
|
166650
|
+
return 0;
|
|
166651
|
+
const count = getCommitCount(db, projectPath);
|
|
166652
|
+
if (count <= maxCommits)
|
|
166653
|
+
return 0;
|
|
166654
|
+
getEvictOverflowStatement(db).run(projectPath, maxCommits);
|
|
166655
|
+
const after = getCommitCount(db, projectPath);
|
|
166656
|
+
const evicted = Math.max(0, count - after);
|
|
166657
|
+
if (evicted > 0) {
|
|
166658
|
+
log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
|
|
166659
|
+
}
|
|
166660
|
+
return evicted;
|
|
166661
|
+
}
|
|
166662
|
+
var insertStatements, existingShasStatements, projectCountStatements, evictStatements, evictOverflowStatements, latestCommitTimeStatements;
|
|
166663
|
+
var init_storage_git_commits = __esm(() => {
|
|
166664
|
+
init_logger();
|
|
166665
|
+
insertStatements = new WeakMap;
|
|
166666
|
+
existingShasStatements = new WeakMap;
|
|
166667
|
+
projectCountStatements = new WeakMap;
|
|
166668
|
+
evictStatements = new WeakMap;
|
|
166669
|
+
evictOverflowStatements = new WeakMap;
|
|
166670
|
+
latestCommitTimeStatements = new WeakMap;
|
|
166671
|
+
});
|
|
166672
|
+
|
|
166362
166673
|
// src/features/magic-context/git-commits/sweep-coordinator.ts
|
|
166363
166674
|
function runImmediate2(db, body) {
|
|
166364
166675
|
db.exec("BEGIN IMMEDIATE");
|
|
@@ -166629,7 +166940,7 @@ function getChunkEmbeddingModelId(config2, providerIdentity) {
|
|
|
166629
166940
|
}
|
|
166630
166941
|
const chunkIdentity = {
|
|
166631
166942
|
providerIdentity,
|
|
166632
|
-
chunkerVersion:
|
|
166943
|
+
chunkerVersion: 2,
|
|
166633
166944
|
maxInputTokens: normalizeCompartmentChunkMaxInputTokens("max_input_tokens" in config2 ? config2.max_input_tokens : undefined),
|
|
166634
166945
|
truncate: config2.provider === "openai-compatible" ? config2.truncate ?? "" : ""
|
|
166635
166946
|
};
|
|
@@ -166652,7 +166963,9 @@ function snapshotFor(registration) {
|
|
|
166652
166963
|
enabled,
|
|
166653
166964
|
gitCommitEnabled,
|
|
166654
166965
|
modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
|
|
166655
|
-
chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId
|
|
166966
|
+
chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId,
|
|
166967
|
+
model: registration.observationMode || !providerIsOn ? "off" : ("model" in registration.config) && registration.config.model.trim() ? registration.config.model.trim() : registration.modelId,
|
|
166968
|
+
provider: registration.observationMode || !providerIsOn ? "off" : registration.config.provider ?? "local"
|
|
166656
166969
|
};
|
|
166657
166970
|
}
|
|
166658
166971
|
function disposeProvider(provider) {
|
|
@@ -166863,8 +167176,9 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
166863
167176
|
}
|
|
166864
167177
|
async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates, signal) {
|
|
166865
167178
|
const noWork = [];
|
|
167179
|
+
const failed = [];
|
|
166866
167180
|
if (candidates.length === 0)
|
|
166867
|
-
return { embedded: 0, noWork };
|
|
167181
|
+
return { embedded: 0, noWork, failed };
|
|
166868
167182
|
const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
|
|
166869
167183
|
const prepared = [];
|
|
166870
167184
|
for (const candidate of candidates) {
|
|
@@ -166881,7 +167195,7 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
166881
167195
|
prepared.push({ candidate, windows });
|
|
166882
167196
|
}
|
|
166883
167197
|
if (prepared.length === 0)
|
|
166884
|
-
return { embedded: 0, noWork };
|
|
167198
|
+
return { embedded: 0, noWork, failed };
|
|
166885
167199
|
let embedded = 0;
|
|
166886
167200
|
let i = 0;
|
|
166887
167201
|
while (i < prepared.length) {
|
|
@@ -166898,35 +167212,60 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
166898
167212
|
const texts = [];
|
|
166899
167213
|
for (const item of slice)
|
|
166900
167214
|
texts.push(...item.windows.map((w) => w.text));
|
|
166901
|
-
|
|
166902
|
-
|
|
166903
|
-
if (!result)
|
|
166904
|
-
continue;
|
|
167215
|
+
const persistedIds = new Set;
|
|
167216
|
+
for (let attempt = 0;attempt < EMBED_SLICE_RETRY_ATTEMPTS; attempt++) {
|
|
166905
167217
|
if (signal?.aborted)
|
|
166906
167218
|
break;
|
|
166907
|
-
let
|
|
166908
|
-
|
|
166909
|
-
|
|
166910
|
-
|
|
166911
|
-
|
|
166912
|
-
|
|
167219
|
+
let result = null;
|
|
167220
|
+
const attemptStart = Date.now();
|
|
167221
|
+
try {
|
|
167222
|
+
result = await embedBatchForProject(projectIdentity, texts, signal);
|
|
167223
|
+
} catch (error51) {
|
|
167224
|
+
log("[magic-context] failed to proactively embed compartment chunks:", error51);
|
|
167225
|
+
}
|
|
167226
|
+
if (signal?.aborted)
|
|
167227
|
+
break;
|
|
167228
|
+
if (result) {
|
|
167229
|
+
let offset = 0;
|
|
167230
|
+
for (const item of slice) {
|
|
167231
|
+
const vectors = result.vectors.slice(offset, offset + item.windows.length);
|
|
167232
|
+
offset += item.windows.length;
|
|
167233
|
+
if (persistedIds.has(item.candidate.id))
|
|
167234
|
+
continue;
|
|
167235
|
+
if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
|
|
167236
|
+
continue;
|
|
167237
|
+
}
|
|
167238
|
+
const rows = item.windows.map((window, index) => ({
|
|
167239
|
+
compartmentId: item.candidate.id,
|
|
167240
|
+
sessionId: item.candidate.sessionId,
|
|
167241
|
+
projectPath: projectIdentity,
|
|
167242
|
+
window,
|
|
167243
|
+
modelId,
|
|
167244
|
+
vector: vectors[index]
|
|
167245
|
+
}));
|
|
167246
|
+
replaceCompartmentChunkEmbeddings(db, rows);
|
|
167247
|
+
persistedIds.add(item.candidate.id);
|
|
166913
167248
|
}
|
|
166914
|
-
const rows = item.windows.map((window, index) => ({
|
|
166915
|
-
compartmentId: item.candidate.id,
|
|
166916
|
-
sessionId: item.candidate.sessionId,
|
|
166917
|
-
projectPath: projectIdentity,
|
|
166918
|
-
window,
|
|
166919
|
-
modelId,
|
|
166920
|
-
vector: vectors[index]
|
|
166921
|
-
}));
|
|
166922
|
-
replaceCompartmentChunkEmbeddings(db, rows);
|
|
166923
|
-
embedded += 1;
|
|
166924
167249
|
}
|
|
166925
|
-
|
|
166926
|
-
|
|
167250
|
+
if (persistedIds.size === slice.length)
|
|
167251
|
+
break;
|
|
167252
|
+
if (persistedIds.size > 0)
|
|
167253
|
+
break;
|
|
167254
|
+
if (Date.now() - attemptStart >= EMBED_SLOW_FAILURE_NO_RETRY_MS)
|
|
167255
|
+
break;
|
|
167256
|
+
if (attempt < EMBED_SLICE_RETRY_ATTEMPTS - 1) {
|
|
167257
|
+
await new Promise((resolve7) => setTimeout(resolve7, EMBED_SLICE_RETRY_BASE_MS * 2 ** attempt));
|
|
167258
|
+
}
|
|
167259
|
+
}
|
|
167260
|
+
embedded += persistedIds.size;
|
|
167261
|
+
if (!signal?.aborted) {
|
|
167262
|
+
for (const item of slice) {
|
|
167263
|
+
if (!persistedIds.has(item.candidate.id))
|
|
167264
|
+
failed.push(item.candidate.id);
|
|
167265
|
+
}
|
|
166927
167266
|
}
|
|
166928
167267
|
}
|
|
166929
|
-
return { embedded, noWork };
|
|
167268
|
+
return { embedded, noWork, failed };
|
|
166930
167269
|
}
|
|
166931
167270
|
async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, options) {
|
|
166932
167271
|
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
@@ -166949,9 +167288,11 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
166949
167288
|
renewal.unref?.();
|
|
166950
167289
|
const batchSize = Math.max(1, options?.batchSize ?? CHUNK_DRAIN_BATCH_SIZE);
|
|
166951
167290
|
const skipIds = [];
|
|
167291
|
+
const failedIds = [];
|
|
166952
167292
|
let embedded = 0;
|
|
166953
167293
|
let aborted2 = false;
|
|
166954
|
-
let
|
|
167294
|
+
let providerDown = false;
|
|
167295
|
+
let consecutiveFailedBatches = 0;
|
|
166955
167296
|
try {
|
|
166956
167297
|
options?.onProgress?.({ embedded, total });
|
|
166957
167298
|
for (;; ) {
|
|
@@ -166959,15 +167300,26 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
166959
167300
|
aborted2 = true;
|
|
166960
167301
|
break;
|
|
166961
167302
|
}
|
|
166962
|
-
const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, skipIds);
|
|
167303
|
+
const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, [...skipIds, ...failedIds]);
|
|
166963
167304
|
if (candidates.length === 0)
|
|
166964
167305
|
break;
|
|
166965
|
-
const {
|
|
167306
|
+
const {
|
|
167307
|
+
embedded: n,
|
|
167308
|
+
noWork,
|
|
167309
|
+
failed
|
|
167310
|
+
} = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
|
|
166966
167311
|
for (const id of noWork)
|
|
166967
167312
|
skipIds.push(id);
|
|
167313
|
+
for (const id of failed)
|
|
167314
|
+
failedIds.push(id);
|
|
166968
167315
|
if (n === 0 && noWork.length === 0) {
|
|
166969
|
-
|
|
166970
|
-
|
|
167316
|
+
consecutiveFailedBatches += 1;
|
|
167317
|
+
if (consecutiveFailedBatches >= MAX_CONSECUTIVE_FAILED_BATCHES) {
|
|
167318
|
+
providerDown = true;
|
|
167319
|
+
break;
|
|
167320
|
+
}
|
|
167321
|
+
} else {
|
|
167322
|
+
consecutiveFailedBatches = 0;
|
|
166971
167323
|
}
|
|
166972
167324
|
embedded += n;
|
|
166973
167325
|
options?.onProgress?.({ embedded: Math.min(embedded, total), total });
|
|
@@ -166975,23 +167327,58 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
166975
167327
|
}
|
|
166976
167328
|
} finally {
|
|
166977
167329
|
clearInterval(renewal);
|
|
166978
|
-
|
|
167330
|
+
try {
|
|
167331
|
+
releaseGitSweepLease(db, projectIdentity, holderId);
|
|
167332
|
+
} catch (error51) {
|
|
167333
|
+
log("[magic-context] embed drain: lease release failed (will TTL-expire):", error51);
|
|
167334
|
+
}
|
|
166979
167335
|
}
|
|
166980
167336
|
if (aborted2)
|
|
166981
|
-
return { status: "aborted", embedded, total };
|
|
166982
|
-
if (
|
|
167337
|
+
return { status: "aborted", embedded, total, failed: failedIds.length };
|
|
167338
|
+
if (providerDown || failedIds.length > 0) {
|
|
166983
167339
|
const remaining = Math.max(0, countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId) - skipIds.length);
|
|
166984
|
-
if (remaining > 0)
|
|
166985
|
-
return { status: "stalled", embedded, total, remaining };
|
|
167340
|
+
if (remaining > 0) {
|
|
167341
|
+
return { status: "stalled", embedded, total, remaining, failed: failedIds.length };
|
|
167342
|
+
}
|
|
166986
167343
|
}
|
|
166987
|
-
return { status: "done", embedded, total };
|
|
167344
|
+
return { status: "done", embedded, total, failed: failedIds.length };
|
|
167345
|
+
}
|
|
167346
|
+
function getEmbeddingCoverageStatus(db, projectIdentity, sessionId) {
|
|
167347
|
+
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
167348
|
+
if (!snapshot?.enabled || snapshot.chunkModelId === "off") {
|
|
167349
|
+
return {
|
|
167350
|
+
enabled: false,
|
|
167351
|
+
model: snapshot?.model ?? "off",
|
|
167352
|
+
provider: snapshot?.provider ?? "off",
|
|
167353
|
+
session: { embedded: 0, total: 0 },
|
|
167354
|
+
memories: { embedded: 0, total: 0 },
|
|
167355
|
+
commits: { embedded: 0, total: 0, gitEnabled: false }
|
|
167356
|
+
};
|
|
167357
|
+
}
|
|
167358
|
+
const session = countSessionCompartmentEmbedCoverage(db, projectIdentity, sessionId, snapshot.chunkModelId);
|
|
167359
|
+
const memories = getMemoryEmbedCoverage(db, projectIdentity, snapshot.modelId);
|
|
167360
|
+
const gitEnabled = snapshot.gitCommitEnabled;
|
|
167361
|
+
const commits = gitEnabled ? {
|
|
167362
|
+
embedded: countEmbeddedCommits(db, projectIdentity),
|
|
167363
|
+
total: getCommitCount(db, projectIdentity),
|
|
167364
|
+
gitEnabled: true
|
|
167365
|
+
} : { embedded: 0, total: 0, gitEnabled: false };
|
|
167366
|
+
return {
|
|
167367
|
+
enabled: true,
|
|
167368
|
+
model: snapshot.model,
|
|
167369
|
+
provider: snapshot.provider,
|
|
167370
|
+
session,
|
|
167371
|
+
memories,
|
|
167372
|
+
commits
|
|
167373
|
+
};
|
|
166988
167374
|
}
|
|
166989
|
-
var OFF_PROVIDER_IDENTITY = "embedding-provider:off", SWEEP_MAX_WALL_CLOCK_MS, CHUNK_DRAIN_BATCH_SIZE = 8, MAX_WINDOWS_PER_EMBED_CALL =
|
|
167375
|
+
var OFF_PROVIDER_IDENTITY = "embedding-provider:off", SWEEP_MAX_WALL_CLOCK_MS, CHUNK_DRAIN_BATCH_SIZE = 8, MAX_WINDOWS_PER_EMBED_CALL = 2, SESSION_EMBED_LEASE_RENEWAL_MS, EMBED_SLICE_RETRY_ATTEMPTS = 3, EMBED_SLICE_RETRY_BASE_MS = 250, EMBED_SLOW_FAILURE_NO_RETRY_MS = 1e4, MAX_CONSECUTIVE_FAILED_BATCHES = 3, projectRegistrations, loadUnembeddedMemoriesStatements, globalRegistrationGeneration = 0, testProviderFactory = null;
|
|
166990
167376
|
var init_project_embedding_registry = __esm(() => {
|
|
166991
167377
|
init_magic_context();
|
|
166992
167378
|
init_logger();
|
|
166993
167379
|
init_compartment_chunk_embedding();
|
|
166994
167380
|
init_storage_git_commit_embeddings();
|
|
167381
|
+
init_storage_git_commits();
|
|
166995
167382
|
init_sweep_coordinator();
|
|
166996
167383
|
init_embedding_cache();
|
|
166997
167384
|
init_embedding_identity();
|
|
@@ -167272,58 +167659,6 @@ var init_models_dev_cache = __esm(() => {
|
|
|
167272
167659
|
init_logger();
|
|
167273
167660
|
});
|
|
167274
167661
|
|
|
167275
|
-
// src/shared/rpc-notifications.ts
|
|
167276
|
-
var exports_rpc_notifications = {};
|
|
167277
|
-
__export(exports_rpc_notifications, {
|
|
167278
|
-
pushNotification: () => pushNotification,
|
|
167279
|
-
isTuiConnected: () => isTuiConnected,
|
|
167280
|
-
drainNotifications: () => drainNotifications
|
|
167281
|
-
});
|
|
167282
|
-
function pushNotification(type, payload, sessionId) {
|
|
167283
|
-
queue2.push({ id: nextNotificationId++, type, payload, sessionId });
|
|
167284
|
-
if (queue2.length > 100) {
|
|
167285
|
-
const newestPerSession = new Map;
|
|
167286
|
-
for (const n of queue2) {
|
|
167287
|
-
const prev = newestPerSession.get(n.sessionId);
|
|
167288
|
-
if (prev === undefined || n.id > prev) {
|
|
167289
|
-
newestPerSession.set(n.sessionId, n.id);
|
|
167290
|
-
}
|
|
167291
|
-
}
|
|
167292
|
-
const mustKeep = new Set(newestPerSession.values());
|
|
167293
|
-
const byNewest = [...queue2].sort((a, b) => b.id - a.id);
|
|
167294
|
-
const kept = [];
|
|
167295
|
-
for (const n of byNewest) {
|
|
167296
|
-
if (kept.length < 50 || mustKeep.has(n.id))
|
|
167297
|
-
kept.push(n);
|
|
167298
|
-
}
|
|
167299
|
-
queue2 = kept.sort((a, b) => a.id - b.id);
|
|
167300
|
-
}
|
|
167301
|
-
}
|
|
167302
|
-
function drainNotifications(lastReceivedId = 0, sessionId) {
|
|
167303
|
-
const now = Date.now();
|
|
167304
|
-
lastDrainAtAny = now;
|
|
167305
|
-
if (sessionId !== undefined)
|
|
167306
|
-
lastDrainAtBySession.set(sessionId, now);
|
|
167307
|
-
const matchesClient = (notification) => sessionId === undefined || notification.sessionId === undefined || notification.sessionId === sessionId;
|
|
167308
|
-
if (lastReceivedId > 0) {
|
|
167309
|
-
queue2 = queue2.filter((notification) => !(notification.id <= lastReceivedId && matchesClient(notification)));
|
|
167310
|
-
}
|
|
167311
|
-
return queue2.filter((notification) => notification.id > lastReceivedId && matchesClient(notification));
|
|
167312
|
-
}
|
|
167313
|
-
function isTuiConnected(sessionId) {
|
|
167314
|
-
const now = Date.now();
|
|
167315
|
-
if (sessionId !== undefined) {
|
|
167316
|
-
const at = lastDrainAtBySession.get(sessionId) ?? 0;
|
|
167317
|
-
return at > 0 && now - at < TUI_CONNECTED_WINDOW_MS;
|
|
167318
|
-
}
|
|
167319
|
-
return lastDrainAtAny > 0 && now - lastDrainAtAny < TUI_CONNECTED_WINDOW_MS;
|
|
167320
|
-
}
|
|
167321
|
-
var queue2, nextNotificationId = 1, lastDrainAtBySession, lastDrainAtAny = 0, TUI_CONNECTED_WINDOW_MS = 3000;
|
|
167322
|
-
var init_rpc_notifications = __esm(() => {
|
|
167323
|
-
queue2 = [];
|
|
167324
|
-
lastDrainAtBySession = new Map;
|
|
167325
|
-
});
|
|
167326
|
-
|
|
167327
167662
|
// src/features/magic-context/compartment-embedding.ts
|
|
167328
167663
|
async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compartments) {
|
|
167329
167664
|
if (compartments.length === 0)
|
|
@@ -169268,7 +169603,7 @@ ${prepared.block}
|
|
|
169268
169603
|
if (!firstMessage || !textPart || isDroppedPlaceholder(textPart.text)) {
|
|
169269
169604
|
messages.unshift({
|
|
169270
169605
|
info: { role: "user", sessionID: sessionId },
|
|
169271
|
-
parts: [{ type: "text", text: historyBlock }]
|
|
169606
|
+
parts: [{ type: "text", text: historyBlock, synthetic: true }]
|
|
169272
169607
|
});
|
|
169273
169608
|
} else {
|
|
169274
169609
|
textPart.text = `${historyBlock}
|
|
@@ -170158,10 +170493,16 @@ function softRefreshCachedM1(options) {
|
|
|
170158
170493
|
function prependM0M1Messages(sessionId, messages, m0Text, m1Text) {
|
|
170159
170494
|
messages.unshift({
|
|
170160
170495
|
info: { role: "user", sessionID: sessionId },
|
|
170161
|
-
parts: [
|
|
170496
|
+
parts: [
|
|
170497
|
+
{
|
|
170498
|
+
type: "text",
|
|
170499
|
+
text: m0Text.length > 0 ? m0Text : M0_EMPTY_BODY,
|
|
170500
|
+
synthetic: true
|
|
170501
|
+
}
|
|
170502
|
+
]
|
|
170162
170503
|
}, {
|
|
170163
170504
|
info: { role: "user", sessionID: sessionId },
|
|
170164
|
-
parts: [{ type: "text", text: m1Text }]
|
|
170505
|
+
parts: [{ type: "text", text: m1Text, synthetic: true }]
|
|
170165
170506
|
});
|
|
170166
170507
|
}
|
|
170167
170508
|
function renderFreshM0NonPersisted(options) {
|
|
@@ -170869,7 +171210,7 @@ function buildToolArcs(messages) {
|
|
|
170869
171210
|
}
|
|
170870
171211
|
return arcs.sort((a, b) => a.invOrdinal - b.invOrdinal || (a.resOrdinal ?? Number.MAX_SAFE_INTEGER) - (b.resOrdinal ?? Number.MAX_SAFE_INTEGER));
|
|
170871
171212
|
}
|
|
170872
|
-
function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
|
|
171213
|
+
function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal, recentOpenArcCutoff) {
|
|
170873
171214
|
let boundary = candidate;
|
|
170874
171215
|
for (const arc of arcs) {
|
|
170875
171216
|
if (arc.resOrdinal !== null) {
|
|
@@ -170878,6 +171219,8 @@ function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
|
|
|
170878
171219
|
}
|
|
170879
171220
|
continue;
|
|
170880
171221
|
}
|
|
171222
|
+
if (arc.invOrdinal < recentOpenArcCutoff)
|
|
171223
|
+
continue;
|
|
170881
171224
|
if (arc.invOrdinal >= lastCompartmentEndOrdinal + 1 && arc.invOrdinal < boundary) {
|
|
170882
171225
|
return arc.invOrdinal;
|
|
170883
171226
|
}
|
|
@@ -171117,7 +171460,7 @@ function semanticSnapBoundary(args) {
|
|
|
171117
171460
|
return snapped;
|
|
171118
171461
|
}
|
|
171119
171462
|
function applyHeadCap(args) {
|
|
171120
|
-
const { index, protectedTailStart, offset, arcs, capTokens } = args;
|
|
171463
|
+
const { index, protectedTailStart, offset, arcs, capTokens, recentOpenArcCutoff } = args;
|
|
171121
171464
|
if (offset >= protectedTailStart)
|
|
171122
171465
|
return { eligibleEndOrdinal: offset, oversizeAtomicUnit: false };
|
|
171123
171466
|
let end = index.findHeadEndForCap(offset, protectedTailStart, capTokens);
|
|
@@ -171125,7 +171468,7 @@ function applyHeadCap(args) {
|
|
|
171125
171468
|
for (const arc of arcs) {
|
|
171126
171469
|
const resOrdinal = arc.resOrdinal;
|
|
171127
171470
|
if (resOrdinal === null) {
|
|
171128
|
-
if (arc.invOrdinal >= offset && arc.invOrdinal < end) {
|
|
171471
|
+
if (arc.invOrdinal >= recentOpenArcCutoff && arc.invOrdinal >= offset && arc.invOrdinal < end) {
|
|
171129
171472
|
end = Math.min(end, arc.invOrdinal);
|
|
171130
171473
|
}
|
|
171131
171474
|
continue;
|
|
@@ -171192,7 +171535,14 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
171192
171535
|
}
|
|
171193
171536
|
if (ctx.mode === "manual-full-recomp") {
|
|
171194
171537
|
const arcs2 = buildToolArcs(messages);
|
|
171195
|
-
const
|
|
171538
|
+
const recompTarget = deriveProtectedTailTokenTarget({
|
|
171539
|
+
contextLimit: ctx.contextLimit,
|
|
171540
|
+
executeThresholdPercentage: ctx.executeThresholdPercentage,
|
|
171541
|
+
usagePercentage: 0,
|
|
171542
|
+
triggerBudget: ctx.triggerBudget
|
|
171543
|
+
});
|
|
171544
|
+
const recentOpenArcCutoff2 = index.findSuffixStartForTokens(recompTarget.N);
|
|
171545
|
+
const firstOpenArc = arcs2.find((arc) => arc.resOrdinal === null && arc.invOrdinal >= offset && arc.invOrdinal >= recentOpenArcCutoff2);
|
|
171196
171546
|
const protectedTailStart2 = firstOpenArc?.invOrdinal ?? rawMessageCount + 1;
|
|
171197
171547
|
const rawRangeFingerprint2 = computeRawRangeFingerprint(messages, offset, protectedTailStart2);
|
|
171198
171548
|
return {
|
|
@@ -171234,13 +171584,14 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
171234
171584
|
const scaledN = ctx.emergencyTailScale ? Math.max(1, Math.floor(target.N * ctx.emergencyTailScale)) : target.N;
|
|
171235
171585
|
const arcs = buildToolArcs(messages);
|
|
171236
171586
|
let boundary = index.findSuffixStartForTokens(scaledN);
|
|
171587
|
+
const recentOpenArcCutoff = boundary;
|
|
171237
171588
|
let boundaryReason = boundary === 1 ? "whole-session-smaller-than-tail" : "size-walk";
|
|
171238
171589
|
const tokenAtBoundary = index.tokenForOrdinal(boundary);
|
|
171239
171590
|
if (boundary <= rawMessageCount && tokenAtBoundary > Math.max(2 * scaledN, 64000) && boundary < rawMessageCount) {
|
|
171240
171591
|
boundary += 1;
|
|
171241
171592
|
boundaryReason = "huge-message-exception";
|
|
171242
171593
|
}
|
|
171243
|
-
boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal);
|
|
171594
|
+
boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
|
|
171244
171595
|
const snapped = semanticSnapBoundary({
|
|
171245
171596
|
messages,
|
|
171246
171597
|
index,
|
|
@@ -171250,7 +171601,7 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
171250
171601
|
});
|
|
171251
171602
|
if (snapped !== boundary)
|
|
171252
171603
|
boundaryReason = "semantic-snap";
|
|
171253
|
-
boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal);
|
|
171604
|
+
boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
|
|
171254
171605
|
let runtimeFloor = offset;
|
|
171255
171606
|
if (ctx.migrationFloorActive)
|
|
171256
171607
|
runtimeFloor = Math.max(runtimeFloor, ctx.priorBoundaryOrdinal);
|
|
@@ -171286,7 +171637,8 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
171286
171637
|
offset,
|
|
171287
171638
|
arcs,
|
|
171288
171639
|
lastCompartmentEndOrdinal: ctx.lastCompartmentEndOrdinal,
|
|
171289
|
-
capTokens: perRunCap
|
|
171640
|
+
capTokens: perRunCap,
|
|
171641
|
+
recentOpenArcCutoff
|
|
171290
171642
|
});
|
|
171291
171643
|
const rawRangeFingerprint = computeRawRangeFingerprint(messages, offset, head.eligibleEndOrdinal);
|
|
171292
171644
|
return {
|
|
@@ -177079,6 +177431,11 @@ async function runManagedRecomp(ctx, sessionId, options) {
|
|
|
177079
177431
|
try {
|
|
177080
177432
|
const message = await executeContextRecomp(buildRecompDeps(ctx, sessionId), options);
|
|
177081
177433
|
const terminalPhase = isRecompSkip(message) ? "skipped" : isRecompFailure(message) ? "failed" : "done";
|
|
177434
|
+
if (terminalPhase === "done") {
|
|
177435
|
+
try {
|
|
177436
|
+
clearEmergencyRecovery(ctx.db, sessionId);
|
|
177437
|
+
} catch {}
|
|
177438
|
+
}
|
|
177082
177439
|
setRecompTerminal(ctx.liveSessionState, sessionId, terminalPhase, extractRecompReason(message));
|
|
177083
177440
|
return message;
|
|
177084
177441
|
} catch (error51) {
|
|
@@ -177177,6 +177534,7 @@ var RECOMP_DONE_GRACE_MS = 30000;
|
|
|
177177
177534
|
var init_recomp_orchestrator = __esm(async () => {
|
|
177178
177535
|
init_compartment_storage();
|
|
177179
177536
|
init_project_identity2();
|
|
177537
|
+
init_storage_meta_persisted();
|
|
177180
177538
|
await __promiseAll([
|
|
177181
177539
|
init_memory_migration(),
|
|
177182
177540
|
init_compartment_runner()
|
|
@@ -177237,15 +177595,15 @@ function shouldShowAnnouncement() {
|
|
|
177237
177595
|
}
|
|
177238
177596
|
return state.version !== ANNOUNCEMENT_VERSION;
|
|
177239
177597
|
}
|
|
177240
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
177598
|
+
var ANNOUNCEMENT_VERSION = "0.25.0", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
|
|
177241
177599
|
var init_announcement = __esm(() => {
|
|
177242
177600
|
init_data_path();
|
|
177243
177601
|
ANNOUNCEMENT_FEATURES = [
|
|
177244
|
-
"
|
|
177245
|
-
"
|
|
177246
|
-
"
|
|
177247
|
-
"
|
|
177248
|
-
"
|
|
177602
|
+
"Old tool output is now reclaimed automatically: once a file read / search / command output has gone a full execute cycle unused, it's dropped on the next one — no need to call ctx_reduce for stale results.",
|
|
177603
|
+
"Recover anything that was dropped: ctx_expand({ message: N }) returns a dropped message's full content (every tool call's input + output) from storage. ctx_expand({ start, end, verbose: true }) lists a range message-by-message to find it.",
|
|
177604
|
+
"Searchable history made reliable: /ctx-embed shows embedding coverage and runs a resilient backfill (retries transient failures, no longer bails on the first hiccup); the active session now auto-embeds in the background. ctx_reduce guidance also reframed as deferred + recoverable so models trim spent output earlier.",
|
|
177605
|
+
"Pi: fixed /ctx-dream (was failing with 'Unknown named parameter') and local-embedding load failures on Windows/Desktop (#151, #128).",
|
|
177606
|
+
"Runaway background agents on weak/local models are now capped and force-stopped (#154, #152). Plus several prompt-cache busts removed."
|
|
177249
177607
|
];
|
|
177250
177608
|
});
|
|
177251
177609
|
|
|
@@ -178040,9 +178398,9 @@ function getMagicContextBuiltinCommands() {
|
|
|
178040
178398
|
template: "ctx-dream",
|
|
178041
178399
|
description: "Run the hidden dreamer maintenance pass for this project now"
|
|
178042
178400
|
},
|
|
178043
|
-
"ctx-embed
|
|
178044
|
-
template: "ctx-embed
|
|
178045
|
-
description: "
|
|
178401
|
+
"ctx-embed": {
|
|
178402
|
+
template: "ctx-embed",
|
|
178403
|
+
description: "Embedding status, or start/pause history compartment embedding (start | pause)"
|
|
178046
178404
|
}
|
|
178047
178405
|
};
|
|
178048
178406
|
}
|
|
@@ -179510,7 +179868,7 @@ function enqueueDream(db, projectIdentity, reason, force = false) {
|
|
|
179510
179868
|
return db.transaction(() => {
|
|
179511
179869
|
if (!hasActiveDreamLease(db)) {
|
|
179512
179870
|
const staleThresholdMs = force ? 2 * 60 * 1000 : 120 * 60 * 1000;
|
|
179513
|
-
db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run(
|
|
179871
|
+
db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run(projectIdentity, now - staleThresholdMs);
|
|
179514
179872
|
}
|
|
179515
179873
|
const existing = db.prepare("SELECT id FROM dream_queue WHERE project_path = ?").get(projectIdentity);
|
|
179516
179874
|
if (existing) {
|
|
@@ -181205,120 +181563,7 @@ ${body}` : subject;
|
|
|
181205
181563
|
init_logger();
|
|
181206
181564
|
init_embedding();
|
|
181207
181565
|
init_storage_git_commit_embeddings();
|
|
181208
|
-
|
|
181209
|
-
// src/features/magic-context/git-commits/storage-git-commits.ts
|
|
181210
|
-
init_logger();
|
|
181211
|
-
var insertStatements = new WeakMap;
|
|
181212
|
-
var existingShasStatements = new WeakMap;
|
|
181213
|
-
var projectCountStatements = new WeakMap;
|
|
181214
|
-
var evictStatements = new WeakMap;
|
|
181215
|
-
var evictOverflowStatements = new WeakMap;
|
|
181216
|
-
var latestCommitTimeStatements = new WeakMap;
|
|
181217
|
-
function getInsertStatement(db) {
|
|
181218
|
-
let stmt = insertStatements.get(db);
|
|
181219
|
-
if (!stmt) {
|
|
181220
|
-
stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
|
|
181221
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
181222
|
-
ON CONFLICT(sha) DO UPDATE SET
|
|
181223
|
-
project_path = excluded.project_path,
|
|
181224
|
-
short_sha = excluded.short_sha,
|
|
181225
|
-
message = excluded.message,
|
|
181226
|
-
author = excluded.author,
|
|
181227
|
-
committed_at = excluded.committed_at,
|
|
181228
|
-
indexed_at = excluded.indexed_at
|
|
181229
|
-
WHERE git_commits.message != excluded.message`);
|
|
181230
|
-
insertStatements.set(db, stmt);
|
|
181231
|
-
}
|
|
181232
|
-
return stmt;
|
|
181233
|
-
}
|
|
181234
|
-
function getExistingShasStatement(db) {
|
|
181235
|
-
let stmt = existingShasStatements.get(db);
|
|
181236
|
-
if (!stmt) {
|
|
181237
|
-
stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
|
|
181238
|
-
existingShasStatements.set(db, stmt);
|
|
181239
|
-
}
|
|
181240
|
-
return stmt;
|
|
181241
|
-
}
|
|
181242
|
-
function getProjectCountStatement(db) {
|
|
181243
|
-
let stmt = projectCountStatements.get(db);
|
|
181244
|
-
if (!stmt) {
|
|
181245
|
-
stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
|
|
181246
|
-
projectCountStatements.set(db, stmt);
|
|
181247
|
-
}
|
|
181248
|
-
return stmt;
|
|
181249
|
-
}
|
|
181250
|
-
function getLatestCommitTimeStatement(db) {
|
|
181251
|
-
let stmt = latestCommitTimeStatements.get(db);
|
|
181252
|
-
if (!stmt) {
|
|
181253
|
-
stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
|
|
181254
|
-
latestCommitTimeStatements.set(db, stmt);
|
|
181255
|
-
}
|
|
181256
|
-
return stmt;
|
|
181257
|
-
}
|
|
181258
|
-
function getEvictOverflowStatement(db) {
|
|
181259
|
-
let stmt = evictOverflowStatements.get(db);
|
|
181260
|
-
if (!stmt) {
|
|
181261
|
-
stmt = db.prepare(`DELETE FROM git_commits
|
|
181262
|
-
WHERE rowid IN (
|
|
181263
|
-
SELECT rowid FROM git_commits
|
|
181264
|
-
WHERE project_path = ?
|
|
181265
|
-
ORDER BY committed_at DESC, sha DESC
|
|
181266
|
-
LIMIT -1 OFFSET ?
|
|
181267
|
-
)`);
|
|
181268
|
-
evictOverflowStatements.set(db, stmt);
|
|
181269
|
-
}
|
|
181270
|
-
return stmt;
|
|
181271
|
-
}
|
|
181272
|
-
function upsertCommits(db, projectPath, commits) {
|
|
181273
|
-
if (commits.length === 0)
|
|
181274
|
-
return { inserted: 0, updated: 0 };
|
|
181275
|
-
const existing = new Set;
|
|
181276
|
-
for (const row of getExistingShasStatement(db).all(projectPath)) {
|
|
181277
|
-
existing.add(row.sha);
|
|
181278
|
-
}
|
|
181279
|
-
let inserted = 0;
|
|
181280
|
-
let updated = 0;
|
|
181281
|
-
const now = Date.now();
|
|
181282
|
-
const insertStmt = getInsertStatement(db);
|
|
181283
|
-
db.transaction(() => {
|
|
181284
|
-
for (const commit of commits) {
|
|
181285
|
-
const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
|
|
181286
|
-
if (result.changes > 0) {
|
|
181287
|
-
if (existing.has(commit.sha)) {
|
|
181288
|
-
updated++;
|
|
181289
|
-
} else {
|
|
181290
|
-
inserted++;
|
|
181291
|
-
existing.add(commit.sha);
|
|
181292
|
-
}
|
|
181293
|
-
}
|
|
181294
|
-
}
|
|
181295
|
-
})();
|
|
181296
|
-
return { inserted, updated };
|
|
181297
|
-
}
|
|
181298
|
-
function getCommitCount(db, projectPath) {
|
|
181299
|
-
const row = getProjectCountStatement(db).get(projectPath);
|
|
181300
|
-
return row?.count ?? 0;
|
|
181301
|
-
}
|
|
181302
|
-
function getLatestIndexedCommitTimeMs(db, projectPath) {
|
|
181303
|
-
const row = getLatestCommitTimeStatement(db).get(projectPath);
|
|
181304
|
-
return row?.latest ?? null;
|
|
181305
|
-
}
|
|
181306
|
-
function enforceProjectCap(db, projectPath, maxCommits) {
|
|
181307
|
-
if (maxCommits <= 0)
|
|
181308
|
-
return 0;
|
|
181309
|
-
const count = getCommitCount(db, projectPath);
|
|
181310
|
-
if (count <= maxCommits)
|
|
181311
|
-
return 0;
|
|
181312
|
-
getEvictOverflowStatement(db).run(projectPath, maxCommits);
|
|
181313
|
-
const after = getCommitCount(db, projectPath);
|
|
181314
|
-
const evicted = Math.max(0, count - after);
|
|
181315
|
-
if (evicted > 0) {
|
|
181316
|
-
log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
|
|
181317
|
-
}
|
|
181318
|
-
return evicted;
|
|
181319
|
-
}
|
|
181320
|
-
|
|
181321
|
-
// src/features/magic-context/git-commits/indexer.ts
|
|
181566
|
+
init_storage_git_commits();
|
|
181322
181567
|
var MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
181323
181568
|
var EMBED_BATCH_SIZE = 16;
|
|
181324
181569
|
var EMBED_MAX_PER_SWEEP = 500;
|
|
@@ -181556,6 +181801,7 @@ function searchGitCommitsSync(db, projectPath, query, options) {
|
|
|
181556
181801
|
|
|
181557
181802
|
// src/features/magic-context/git-commits/index.ts
|
|
181558
181803
|
init_storage_git_commit_embeddings();
|
|
181804
|
+
init_storage_git_commits();
|
|
181559
181805
|
init_sweep_coordinator();
|
|
181560
181806
|
|
|
181561
181807
|
// src/plugin/dream-timer.ts
|
|
@@ -182980,7 +183226,7 @@ function createMagicContextCommandHandler(deps) {
|
|
|
182980
183226
|
const isAugCommand = (command) => command === "ctx-aug";
|
|
182981
183227
|
const isDreamCommand = (command) => command === "ctx-dream";
|
|
182982
183228
|
const isSessionUpgradeCommand = (command) => command === "ctx-session-upgrade";
|
|
182983
|
-
const
|
|
183229
|
+
const isEmbedCommand = (command) => command === "ctx-embed";
|
|
182984
183230
|
return {
|
|
182985
183231
|
"command.execute.before": async (input, _output, _params) => {
|
|
182986
183232
|
const isStatus = isStatusCommand(input.command);
|
|
@@ -182989,8 +183235,8 @@ function createMagicContextCommandHandler(deps) {
|
|
|
182989
183235
|
const isAug = isAugCommand(input.command);
|
|
182990
183236
|
const isDream = isDreamCommand(input.command);
|
|
182991
183237
|
const isSessionUpgrade = isSessionUpgradeCommand(input.command);
|
|
182992
|
-
const
|
|
182993
|
-
if (!isStatus && !isFlush && !isRecomp && !isAug && !isDream && !isSessionUpgrade && !
|
|
183238
|
+
const isEmbed = isEmbedCommand(input.command);
|
|
183239
|
+
if (!isStatus && !isFlush && !isRecomp && !isAug && !isDream && !isSessionUpgrade && !isEmbed) {
|
|
182994
183240
|
return;
|
|
182995
183241
|
}
|
|
182996
183242
|
const sessionId = input.sessionID;
|
|
@@ -183003,15 +183249,50 @@ function createMagicContextCommandHandler(deps) {
|
|
|
183003
183249
|
await executeDreaming(deps, sessionId);
|
|
183004
183250
|
return;
|
|
183005
183251
|
}
|
|
183006
|
-
if (
|
|
183007
|
-
const
|
|
183008
|
-
|
|
183009
|
-
|
|
183252
|
+
if (isEmbed) {
|
|
183253
|
+
const sub = input.arguments.trim().toLowerCase();
|
|
183254
|
+
if (sub === "pause") {
|
|
183255
|
+
const summary = deps.pauseEmbedDrain ? deps.pauseEmbedDrain(sessionId) : "Embedding pause is unavailable.";
|
|
183256
|
+
if (isTuiConnected(sessionId)) {
|
|
183257
|
+
pushNotification("action", { action: "show-result-dialog", title: "Embed", message: summary }, sessionId);
|
|
183258
|
+
} else {
|
|
183259
|
+
await deps.sendNotification(sessionId, summary, {});
|
|
183260
|
+
}
|
|
183261
|
+
throwSentinel(input.command);
|
|
183262
|
+
}
|
|
183263
|
+
if (sub === "start") {
|
|
183264
|
+
const summary = deps.executeEmbedHistory ? await deps.executeEmbedHistory(sessionId) : "Semantic embedding is not configured for this project, so there is nothing to embed.";
|
|
183265
|
+
if (isTuiConnected(sessionId)) {
|
|
183266
|
+
pushNotification("action", { action: "show-result-dialog", title: "Embed", message: summary }, sessionId);
|
|
183267
|
+
} else {
|
|
183268
|
+
await deps.sendNotification(sessionId, summary, {});
|
|
183269
|
+
}
|
|
183270
|
+
throwSentinel(input.command);
|
|
183271
|
+
}
|
|
183272
|
+
if (sub !== "") {
|
|
183273
|
+
await deps.sendNotification(sessionId, "Usage: `/ctx-embed` (status), `/ctx-embed start`, or `/ctx-embed pause`.", {});
|
|
183274
|
+
throwSentinel(input.command);
|
|
183275
|
+
}
|
|
183276
|
+
if (isTuiConnected(sessionId)) {
|
|
183277
|
+
pushNotification("action", { action: "show-embed-dialog" }, sessionId);
|
|
183278
|
+
sessionLog(sessionId, "command ctx-embed: pushed show-embed-dialog to TUI");
|
|
183279
|
+
throwSentinel(input.command);
|
|
183280
|
+
}
|
|
183281
|
+
result = deps.getEmbedStatusText ? `## Embedding Status
|
|
183282
|
+
|
|
183283
|
+
${deps.getEmbedStatusText(sessionId)}` : `## Embedding Status
|
|
183284
|
+
|
|
183285
|
+
Embedding status is unavailable.`;
|
|
183010
183286
|
}
|
|
183011
183287
|
if (isFlush) {
|
|
183012
183288
|
result = executeFlush(deps.db, sessionId);
|
|
183013
183289
|
clearCachedM0M1(deps.db, sessionId);
|
|
183014
183290
|
deps.onFlush?.(sessionId);
|
|
183291
|
+
if (isTuiConnected(sessionId)) {
|
|
183292
|
+
pushNotification("action", { action: "show-flush-dialog", message: result }, sessionId);
|
|
183293
|
+
sessionLog(sessionId, "command ctx-flush: pushed show-flush-dialog to TUI");
|
|
183294
|
+
throwSentinel(input.command);
|
|
183295
|
+
}
|
|
183015
183296
|
}
|
|
183016
183297
|
if (isStatus) {
|
|
183017
183298
|
if (isTuiConnected(sessionId)) {
|
|
@@ -183138,6 +183419,34 @@ ${snap.error}`;
|
|
|
183138
183419
|
// src/hooks/magic-context/hook.ts
|
|
183139
183420
|
init_derive_budgets();
|
|
183140
183421
|
|
|
183422
|
+
// src/hooks/magic-context/embed-session-state.ts
|
|
183423
|
+
var embedPauseBySession = new Set;
|
|
183424
|
+
var embedRunStateBySession = new Map;
|
|
183425
|
+
var autoEmbedAttemptedBySession = new Set;
|
|
183426
|
+
function getEmbedDrainUiStatus(sessionId, progress) {
|
|
183427
|
+
if (embedPauseBySession.has(sessionId)) {
|
|
183428
|
+
return { status: "paused" };
|
|
183429
|
+
}
|
|
183430
|
+
if (progress?.kind === "embed" && progress.phase === "recomp") {
|
|
183431
|
+
return { status: "running" };
|
|
183432
|
+
}
|
|
183433
|
+
if (progress?.kind === "embed" && (progress.phase === "failed" || progress.phase === "skipped") && progress.message) {
|
|
183434
|
+
if (/provider/i.test(progress.message)) {
|
|
183435
|
+
return { status: "stopped", detail: progress.message };
|
|
183436
|
+
}
|
|
183437
|
+
}
|
|
183438
|
+
return { status: "idle" };
|
|
183439
|
+
}
|
|
183440
|
+
function clearEmbedSessionState(sessionId) {
|
|
183441
|
+
embedPauseBySession.delete(sessionId);
|
|
183442
|
+
const ctrl = embedRunStateBySession.get(sessionId);
|
|
183443
|
+
if (ctrl) {
|
|
183444
|
+
ctrl.abort();
|
|
183445
|
+
embedRunStateBySession.delete(sessionId);
|
|
183446
|
+
}
|
|
183447
|
+
autoEmbedAttemptedBySession.delete(sessionId);
|
|
183448
|
+
}
|
|
183449
|
+
|
|
183141
183450
|
// src/features/magic-context/message-index-async.ts
|
|
183142
183451
|
init_logger();
|
|
183143
183452
|
await init_message_index();
|
|
@@ -183375,8 +183684,8 @@ var CHANNEL1_SENTINEL = "<system-reminder>";
|
|
|
183375
183684
|
var TOKENS_PER_BYTE = 0.25;
|
|
183376
183685
|
var CHANNEL1_FLOOR_TOKENS = 1e4;
|
|
183377
183686
|
var CHANNEL1_REFIRE_FLOOR_TOKENS = 1e4;
|
|
183378
|
-
function channel1RefireTokens(
|
|
183379
|
-
const scaled = Math.round(0.05 * Math.max(0,
|
|
183687
|
+
function channel1RefireTokens(workingWindowTokens) {
|
|
183688
|
+
const scaled = Math.round(0.05 * Math.max(0, workingWindowTokens));
|
|
183380
183689
|
return Math.max(CHANNEL1_REFIRE_FLOOR_TOKENS, scaled);
|
|
183381
183690
|
}
|
|
183382
183691
|
var S_GENTLE = 0.2;
|
|
@@ -183446,7 +183755,7 @@ function computeTailTokenEstimate(messages) {
|
|
|
183446
183755
|
};
|
|
183447
183756
|
}
|
|
183448
183757
|
function decideChannel1(input) {
|
|
183449
|
-
const { undroppedTokens, pressure,
|
|
183758
|
+
const { undroppedTokens, pressure, workingWindowTokens, hasRecentReduce } = input;
|
|
183450
183759
|
const resetCycle = hasRecentReduce || undroppedTokens < input.lastNudgeUndropped;
|
|
183451
183760
|
const lastNudge = resetCycle ? 0 : input.lastNudgeUndropped;
|
|
183452
183761
|
const lastLevel = resetCycle ? "" : input.lastNudgeLevel;
|
|
@@ -183461,7 +183770,7 @@ function decideChannel1(input) {
|
|
|
183461
183770
|
return quiet();
|
|
183462
183771
|
if (undroppedTokens < CHANNEL1_FLOOR_TOKENS)
|
|
183463
183772
|
return quiet();
|
|
183464
|
-
const budget =
|
|
183773
|
+
const budget = workingWindowTokens > 0 ? workingWindowTokens : undroppedTokens || 1;
|
|
183465
183774
|
const severity = undroppedTokens / budget * pressure;
|
|
183466
183775
|
if (severity < S_GENTLE)
|
|
183467
183776
|
return quiet();
|
|
@@ -183473,7 +183782,7 @@ function decideChannel1(input) {
|
|
|
183473
183782
|
else
|
|
183474
183783
|
level = "gentle";
|
|
183475
183784
|
if (lastLevel === "") {
|
|
183476
|
-
if (undroppedTokens < lastNudge + channel1RefireTokens(
|
|
183785
|
+
if (undroppedTokens < lastNudge + channel1RefireTokens(workingWindowTokens)) {
|
|
183477
183786
|
return quiet();
|
|
183478
183787
|
}
|
|
183479
183788
|
} else if (LEVEL_RANK[level] <= LEVEL_RANK[lastLevel]) {
|
|
@@ -183498,6 +183807,13 @@ function computePressure(input) {
|
|
|
183498
183807
|
function approxThousands(tokens) {
|
|
183499
183808
|
return `${Math.round(tokens / 1000)}k`;
|
|
183500
183809
|
}
|
|
183810
|
+
function formatOldestReclaimableHint(hint) {
|
|
183811
|
+
if (!hint || hint.length === 0)
|
|
183812
|
+
return "";
|
|
183813
|
+
const rendered = hint.slice(0, 4).map((tag) => `§${tag.tagNumber}§ ${tag.toolName ?? "tool"}`).join(" · ");
|
|
183814
|
+
return rendered.length > 0 ? `
|
|
183815
|
+
oldest reclaimable: ${rendered}.` : "";
|
|
183816
|
+
}
|
|
183501
183817
|
var CHANNEL2_USABLE_FRACTION = 1 / 3;
|
|
183502
183818
|
var CHANNEL2_MIN_RECLAIMABLE = 1e4;
|
|
183503
183819
|
function shouldTriggerChannel2(input) {
|
|
@@ -183507,30 +183823,32 @@ function shouldTriggerChannel2(input) {
|
|
|
183507
183823
|
return true;
|
|
183508
183824
|
return input.reclaimableTokens >= input.usableTokens * CHANNEL2_USABLE_FRACTION;
|
|
183509
183825
|
}
|
|
183510
|
-
function buildChannel2Reminder(undroppedTokens) {
|
|
183826
|
+
function buildChannel2Reminder(undroppedTokens, hint) {
|
|
183511
183827
|
const amount = approxThousands(undroppedTokens);
|
|
183828
|
+
const hintText = formatOldestReclaimableHint(hint);
|
|
183512
183829
|
return `<system-reminder>
|
|
183513
|
-
` + `Routine context housekeeping is near: a large span of this session will be comparted soon, ` + `and ~${amount} tokens of tool output remain unreduced. Drop spent outputs with ctx_reduce ` + `first so the archived span is the part that matters
|
|
183830
|
+
` + `Routine context housekeeping is near: a large span of this session will be comparted soon, ` + `and ~${amount} tokens of tool output remain unreduced. Drop spent outputs with ctx_reduce ` + `first so the archived span is the part that matters.${hintText}
|
|
183514
183831
|
` + `</system-reminder>`;
|
|
183515
183832
|
}
|
|
183516
|
-
function buildChannel1Reminder(level, undroppedTokens) {
|
|
183833
|
+
function buildChannel1Reminder(level, undroppedTokens, hint) {
|
|
183517
183834
|
const amount = approxThousands(undroppedTokens);
|
|
183835
|
+
const hintText = formatOldestReclaimableHint(hint);
|
|
183518
183836
|
let body;
|
|
183519
183837
|
switch (level) {
|
|
183520
183838
|
case "gentle":
|
|
183521
|
-
body = `You have ~${amount} tokens of tool output you have not reduced. ` + `
|
|
183839
|
+
body = `You have ~${amount} tokens of tool output you have not reduced. ` + `When you are done with earlier outputs, dropping them with ctx_reduce keeps context lean.`;
|
|
183522
183840
|
break;
|
|
183523
183841
|
case "firm":
|
|
183524
|
-
body = `~${amount} tokens of unreduced tool output
|
|
183842
|
+
body = `~${amount} tokens of unreduced tool output has built up. ` + `At your next natural stopping point, consider dropping what you have already processed with ctx_reduce.`;
|
|
183525
183843
|
break;
|
|
183526
183844
|
case "urgent":
|
|
183527
|
-
body = `~${amount} tokens of unreduced tool output remain
|
|
183845
|
+
body = `~${amount} tokens of unreduced tool output remain, and a large span of this session will be comparted before long. ` + `Consider dropping spent outputs with ctx_reduce so the archived span is the part that matters.`;
|
|
183528
183846
|
break;
|
|
183529
183847
|
}
|
|
183530
183848
|
return `
|
|
183531
183849
|
|
|
183532
183850
|
<system-reminder>
|
|
183533
|
-
${body}
|
|
183851
|
+
${body}${hintText}
|
|
183534
183852
|
</system-reminder>`;
|
|
183535
183853
|
}
|
|
183536
183854
|
|
|
@@ -183584,10 +183902,10 @@ async function maybeDeliverChannel2(sessionId, deps) {
|
|
|
183584
183902
|
try {
|
|
183585
183903
|
const client3 = getLiveServerClient(serverUrl, deps.directory);
|
|
183586
183904
|
const promptContext = await resolvePromptContext(client3, sessionId);
|
|
183587
|
-
const reminder = buildChannel2Reminder(deps.reclaimableTokens);
|
|
183905
|
+
const reminder = buildChannel2Reminder(deps.reclaimableTokens, deps.oldestReclaimableToolTags);
|
|
183588
183906
|
const body = {
|
|
183589
183907
|
noReply: false,
|
|
183590
|
-
parts: [{ type: "text", text: reminder }]
|
|
183908
|
+
parts: [{ type: "text", text: reminder, synthetic: true }]
|
|
183591
183909
|
};
|
|
183592
183910
|
if (promptContext?.agent)
|
|
183593
183911
|
body.agent = promptContext.agent;
|
|
@@ -184045,7 +184363,8 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
|
|
|
184045
184363
|
const result = {
|
|
184046
184364
|
compressedToLite: 0,
|
|
184047
184365
|
compressedToFull: 0,
|
|
184048
|
-
compressedToUltra: 0
|
|
184366
|
+
compressedToUltra: 0,
|
|
184367
|
+
mutatedTextTags: 0
|
|
184049
184368
|
};
|
|
184050
184369
|
if (!config2.enabled)
|
|
184051
184370
|
return result;
|
|
@@ -184086,7 +184405,9 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
|
|
|
184086
184405
|
const target = targets.get(tag.tagNumber);
|
|
184087
184406
|
if (!target)
|
|
184088
184407
|
continue;
|
|
184089
|
-
target.setContent(compressed);
|
|
184408
|
+
const didMutate = target.setContent(compressed);
|
|
184409
|
+
if (didMutate)
|
|
184410
|
+
result.mutatedTextTags += 1;
|
|
184090
184411
|
updateCavemanDepth(db, sessionId, tag.tagNumber, targetDepth);
|
|
184091
184412
|
if (targetDepth === DEPTH_LITE)
|
|
184092
184413
|
result.compressedToLite += 1;
|
|
@@ -184674,28 +184995,6 @@ function stripInlineThinking(messages, messageTagNumbers, clearReasoningAge) {
|
|
|
184674
184995
|
}
|
|
184675
184996
|
return stripped;
|
|
184676
184997
|
}
|
|
184677
|
-
function truncateErroredTools(messages, watermark, messageTagNumbers) {
|
|
184678
|
-
let truncated = 0;
|
|
184679
|
-
for (let i = 0;i < messages.length; i++) {
|
|
184680
|
-
const maxTag = messageTagNumbers.get(messages[i]) ?? 0;
|
|
184681
|
-
if (maxTag > watermark) {
|
|
184682
|
-
continue;
|
|
184683
|
-
}
|
|
184684
|
-
for (const part of messages[i].parts) {
|
|
184685
|
-
if (!isRecord(part) || part.type !== "tool" || !isRecord(part.state)) {
|
|
184686
|
-
continue;
|
|
184687
|
-
}
|
|
184688
|
-
if (part.state.status !== "error") {
|
|
184689
|
-
continue;
|
|
184690
|
-
}
|
|
184691
|
-
if (typeof part.state.error === "string" && part.state.error.length > 100) {
|
|
184692
|
-
part.state.error = `${part.state.error.slice(0, 100)}... [truncated]`;
|
|
184693
|
-
truncated++;
|
|
184694
|
-
}
|
|
184695
|
-
}
|
|
184696
|
-
}
|
|
184697
|
-
return truncated;
|
|
184698
|
-
}
|
|
184699
184998
|
var REASONING_IGNORED_PART_TYPES = new Set([
|
|
184700
184999
|
"step-start",
|
|
184701
185000
|
"step-finish",
|
|
@@ -185112,28 +185411,12 @@ function appendReminderToUserMessage(message, reminder) {
|
|
|
185112
185411
|
}
|
|
185113
185412
|
|
|
185114
185413
|
// src/hooks/magic-context/apply-operations.ts
|
|
185115
|
-
init_tag_part_guards();
|
|
185116
185414
|
await init_storage();
|
|
185117
|
-
var USER_DROP_PREVIEW_CHARS = 250;
|
|
185118
185415
|
var RECENT_TOOL_SKELETON_WINDOW = 20;
|
|
185119
|
-
function buildReplacementContent(tagId
|
|
185120
|
-
|
|
185121
|
-
|
|
185122
|
-
|
|
185123
|
-
}
|
|
185124
|
-
const currentContent = target.getContent?.() ?? "";
|
|
185125
|
-
const originalText = stripTagPrefix(currentContent);
|
|
185126
|
-
if (originalText.length <= USER_DROP_PREVIEW_CHARS) {
|
|
185127
|
-
return `[truncated §${tagId}§]
|
|
185128
|
-
${originalText}`;
|
|
185129
|
-
}
|
|
185130
|
-
const hardCut = originalText.slice(0, USER_DROP_PREVIEW_CHARS);
|
|
185131
|
-
const softCutIndex = hardCut.search(/\s\S*$/);
|
|
185132
|
-
const preview = softCutIndex > USER_DROP_PREVIEW_CHARS - 30 ? hardCut.slice(0, softCutIndex) : hardCut;
|
|
185133
|
-
return `[truncated §${tagId}§]
|
|
185134
|
-
${preview}…`;
|
|
185135
|
-
}
|
|
185136
|
-
function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps) {
|
|
185416
|
+
function buildReplacementContent(tagId) {
|
|
185417
|
+
return `[dropped §${tagId}§]`;
|
|
185418
|
+
}
|
|
185419
|
+
function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps, syntheticPendingOps = []) {
|
|
185137
185420
|
let didMutateMessage = false;
|
|
185138
185421
|
db.transaction(() => {
|
|
185139
185422
|
const tags = preloadedTags ?? getTagsBySession(db, sessionId);
|
|
@@ -185141,11 +185424,16 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
|
|
|
185141
185424
|
const tagTypeById = new Map(tags.map((tag) => [tag.tagNumber, tag.type]));
|
|
185142
185425
|
const protectedTagIds = protectedTags > 0 ? new Set(tags.filter((tag) => tag.status === "active").map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0, protectedTags)) : new Set;
|
|
185143
185426
|
const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
|
|
185427
|
+
const opsToApply = [
|
|
185428
|
+
...pendingOps.map((op) => ({ op, synthetic: false })),
|
|
185429
|
+
...syntheticPendingOps.map((op) => ({ op, synthetic: true }))
|
|
185430
|
+
];
|
|
185144
185431
|
const skeletonWindow = new Set(tags.filter((tag) => tag.type === "tool").map((tag) => tag.tagNumber).sort((left, right) => right - left).slice(0, RECENT_TOOL_SKELETON_WINDOW));
|
|
185145
|
-
for (const pendingOp of
|
|
185432
|
+
for (const { op: pendingOp, synthetic } of opsToApply) {
|
|
185146
185433
|
const tagStatus = tagStatusById.get(pendingOp.tagId);
|
|
185147
185434
|
if (tagStatus === "compacted" || tagStatus === "dropped") {
|
|
185148
|
-
|
|
185435
|
+
if (!synthetic)
|
|
185436
|
+
removePendingOp(db, sessionId, pendingOp.tagId);
|
|
185149
185437
|
continue;
|
|
185150
185438
|
}
|
|
185151
185439
|
if (protectedTagIds.has(pendingOp.tagId)) {
|
|
@@ -185153,33 +185441,46 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
|
|
|
185153
185441
|
}
|
|
185154
185442
|
const target = targets.get(pendingOp.tagId);
|
|
185155
185443
|
const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
|
|
185444
|
+
if (synthetic) {
|
|
185445
|
+
if (!isToolTag || target?.canDrop?.() !== true)
|
|
185446
|
+
continue;
|
|
185447
|
+
}
|
|
185448
|
+
let shouldPersistDrop = false;
|
|
185156
185449
|
if (isToolTag) {
|
|
185157
185450
|
if (skeletonWindow.has(pendingOp.tagId)) {
|
|
185158
185451
|
const truncResult = target?.truncate?.() ?? "absent";
|
|
185159
|
-
if (truncResult === "incomplete") {
|
|
185452
|
+
if (truncResult === "incomplete" || synthetic && truncResult !== "truncated") {
|
|
185160
185453
|
continue;
|
|
185161
185454
|
}
|
|
185162
185455
|
if (truncResult === "truncated") {
|
|
185163
185456
|
didMutateMessage = true;
|
|
185164
185457
|
}
|
|
185165
185458
|
updateTagDropMode(db, sessionId, pendingOp.tagId, "truncated");
|
|
185459
|
+
shouldPersistDrop = true;
|
|
185166
185460
|
} else {
|
|
185167
185461
|
const dropResult = target?.drop?.() ?? "absent";
|
|
185168
|
-
if (dropResult === "incomplete") {
|
|
185462
|
+
if (dropResult === "incomplete" || synthetic && dropResult !== "removed") {
|
|
185169
185463
|
continue;
|
|
185170
185464
|
}
|
|
185171
185465
|
if (dropResult === "removed") {
|
|
185172
185466
|
didMutateMessage = true;
|
|
185173
185467
|
}
|
|
185174
185468
|
updateTagDropMode(db, sessionId, pendingOp.tagId, "full");
|
|
185469
|
+
shouldPersistDrop = true;
|
|
185175
185470
|
}
|
|
185176
185471
|
} else if (target) {
|
|
185177
|
-
const changed = target.setContent(buildReplacementContent(pendingOp.tagId
|
|
185472
|
+
const changed = target.setContent(buildReplacementContent(pendingOp.tagId));
|
|
185178
185473
|
if (changed)
|
|
185179
185474
|
didMutateMessage = true;
|
|
185475
|
+
shouldPersistDrop = true;
|
|
185476
|
+
} else if (!synthetic) {
|
|
185477
|
+
shouldPersistDrop = true;
|
|
185180
185478
|
}
|
|
185479
|
+
if (!shouldPersistDrop)
|
|
185480
|
+
continue;
|
|
185181
185481
|
updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
|
|
185182
|
-
|
|
185482
|
+
if (!synthetic)
|
|
185483
|
+
removePendingOp(db, sessionId, pendingOp.tagId);
|
|
185183
185484
|
}
|
|
185184
185485
|
})();
|
|
185185
185486
|
return didMutateMessage;
|
|
@@ -185203,7 +185504,7 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
|
|
|
185203
185504
|
}
|
|
185204
185505
|
}
|
|
185205
185506
|
} else if (target) {
|
|
185206
|
-
const changed = target.setContent(buildReplacementContent(tag.tagNumber
|
|
185507
|
+
const changed = target.setContent(buildReplacementContent(tag.tagNumber));
|
|
185207
185508
|
if (changed)
|
|
185208
185509
|
didMutateMessage = true;
|
|
185209
185510
|
}
|
|
@@ -185720,7 +186021,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
|
|
|
185720
186021
|
logTransformTiming(sessionId, "tag.saveSource", performance.now() - accSaveSource);
|
|
185721
186022
|
for (const [compositeKey, tagId] of toolTagByCallId) {
|
|
185722
186023
|
const thinkingParts = toolThinkingByCallId.get(compositeKey) ?? [];
|
|
185723
|
-
targets.set(tagId, createToolDropTarget(compositeKey, thinkingParts, toolCallIndex, batch));
|
|
186024
|
+
targets.set(tagId, createToolDropTarget(compositeKey, thinkingParts, toolCallIndex, batch, tagId));
|
|
185724
186025
|
}
|
|
185725
186026
|
const hasRecentReduceCall = lastReduceMessageIndex >= 0 && messages.length - lastReduceMessageIndex <= RECENT_REDUCE_LOOKBACK;
|
|
185726
186027
|
return {
|
|
@@ -187000,7 +187301,9 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
|
|
|
187000
187301
|
continue;
|
|
187001
187302
|
updateTagDropMode(db, sessionId, tag.tagNumber, "full");
|
|
187002
187303
|
updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
|
|
187003
|
-
|
|
187304
|
+
if (result === "removed" || result === "truncated") {
|
|
187305
|
+
deduplicatedTools++;
|
|
187306
|
+
}
|
|
187004
187307
|
}
|
|
187005
187308
|
}
|
|
187006
187309
|
})();
|
|
@@ -187009,6 +187312,7 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
|
|
|
187009
187312
|
sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
|
|
187010
187313
|
}
|
|
187011
187314
|
let compressedTextTags = 0;
|
|
187315
|
+
let mutatedTextTags = 0;
|
|
187012
187316
|
if (config2.caveman?.enabled) {
|
|
187013
187317
|
const cavemanResult = applyCavemanCleanup(sessionId, db, targets, tags, {
|
|
187014
187318
|
enabled: true,
|
|
@@ -187016,8 +187320,15 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
|
|
|
187016
187320
|
protectedTags: config2.protectedTags
|
|
187017
187321
|
});
|
|
187018
187322
|
compressedTextTags = cavemanResult.compressedToLite + cavemanResult.compressedToFull + cavemanResult.compressedToUltra;
|
|
187323
|
+
mutatedTextTags = cavemanResult.mutatedTextTags;
|
|
187019
187324
|
}
|
|
187020
|
-
return {
|
|
187325
|
+
return {
|
|
187326
|
+
droppedTools,
|
|
187327
|
+
deduplicatedTools,
|
|
187328
|
+
droppedInjections,
|
|
187329
|
+
compressedTextTags,
|
|
187330
|
+
mutatedTextTags
|
|
187331
|
+
};
|
|
187021
187332
|
}
|
|
187022
187333
|
function extractToolInfo(part) {
|
|
187023
187334
|
if (part.type === "tool" && typeof part.tool === "string" && DEDUP_SAFE_TOOLS.has(part.tool)) {
|
|
@@ -187191,6 +187502,42 @@ function isTodoItem(value) {
|
|
|
187191
187502
|
return typeof todo.content === "string" && typeof todo.status === "string" && (todo.priority === undefined || typeof todo.priority === "string");
|
|
187192
187503
|
}
|
|
187193
187504
|
|
|
187505
|
+
// src/hooks/magic-context/tool-reclaim.ts
|
|
187506
|
+
await init_storage();
|
|
187507
|
+
function buildSyntheticToolReclaimOps(input) {
|
|
187508
|
+
const watermark = Math.max(0, input.watermark);
|
|
187509
|
+
if (watermark <= 0)
|
|
187510
|
+
return [];
|
|
187511
|
+
const realPendingTagIds = new Set((input.pendingOps ?? []).map((op) => op.tagId));
|
|
187512
|
+
const tags = getActiveTagsBySession(input.db, input.sessionId);
|
|
187513
|
+
const synthetic = [];
|
|
187514
|
+
for (const tag of tags) {
|
|
187515
|
+
if (tag.type !== "tool")
|
|
187516
|
+
continue;
|
|
187517
|
+
if (tag.status !== "active")
|
|
187518
|
+
continue;
|
|
187519
|
+
if (tag.tagNumber > watermark)
|
|
187520
|
+
continue;
|
|
187521
|
+
if (realPendingTagIds.has(tag.tagNumber))
|
|
187522
|
+
continue;
|
|
187523
|
+
if (input.targets.get(tag.tagNumber)?.canDrop?.() !== true)
|
|
187524
|
+
continue;
|
|
187525
|
+
synthetic.push({
|
|
187526
|
+
id: 0,
|
|
187527
|
+
sessionId: input.sessionId,
|
|
187528
|
+
tagId: tag.tagNumber,
|
|
187529
|
+
operation: "drop",
|
|
187530
|
+
queuedAt: 0
|
|
187531
|
+
});
|
|
187532
|
+
}
|
|
187533
|
+
return synthetic;
|
|
187534
|
+
}
|
|
187535
|
+
function advanceToolReclaimWatermarkToCurrentMax(db, sessionId) {
|
|
187536
|
+
const maxTagNumber = getMaxTagNumberBySession(db, sessionId);
|
|
187537
|
+
advanceToolReclaimWatermark(db, sessionId, maxTagNumber);
|
|
187538
|
+
return maxTagNumber;
|
|
187539
|
+
}
|
|
187540
|
+
|
|
187194
187541
|
// src/hooks/magic-context/transform-postprocess-phase.ts
|
|
187195
187542
|
var DEGRADE_CACHE_WARNING_THRESHOLD = 10;
|
|
187196
187543
|
var degradedCacheCountBySession = new BoundedSessionMap(100);
|
|
@@ -187245,12 +187592,14 @@ async function runPostTransformPhase(args) {
|
|
|
187245
187592
|
let deferredMaterializedSuccessfully = false;
|
|
187246
187593
|
let heuristicsRanSuccessfully = false;
|
|
187247
187594
|
let pendingOpsRanSuccessfully = false;
|
|
187595
|
+
let pendingOpsDidMutate = false;
|
|
187596
|
+
let heuristicOrReasoningDidMutate = false;
|
|
187248
187597
|
try {
|
|
187249
187598
|
if (shouldApplyPendingOps) {
|
|
187250
187599
|
const applyReason = isExplicitFlush ? "explicit_flush" : deferredMaterialize ? "deferred_materialization" : `scheduler_execute (scheduler=${args.schedulerDecision})`;
|
|
187251
187600
|
sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOps.length}, context=${args.contextUsage.percentage.toFixed(1)}%`);
|
|
187252
187601
|
const tApply = performance.now();
|
|
187253
|
-
applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, pendingOps);
|
|
187602
|
+
pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, pendingOps);
|
|
187254
187603
|
logTransformTiming(args.sessionId, "applyPendingOperations", tApply);
|
|
187255
187604
|
}
|
|
187256
187605
|
if (shouldRunHeuristics) {
|
|
@@ -187268,7 +187617,8 @@ async function runPostTransformPhase(args) {
|
|
|
187268
187617
|
} : undefined,
|
|
187269
187618
|
caveman: cavemanConfig
|
|
187270
187619
|
}, heuristicTags);
|
|
187271
|
-
logTransformTiming(args.sessionId, "applyHeuristicCleanup", t5, `droppedTools=${cleanup.droppedTools} deduplicatedTools=${cleanup.deduplicatedTools} droppedInjections=${cleanup.droppedInjections} compressedTextTags=${cleanup.compressedTextTags}`);
|
|
187620
|
+
logTransformTiming(args.sessionId, "applyHeuristicCleanup", t5, `droppedTools=${cleanup.droppedTools} deduplicatedTools=${cleanup.deduplicatedTools} droppedInjections=${cleanup.droppedInjections} compressedTextTags=${cleanup.compressedTextTags} mutatedTextTags=${cleanup.mutatedTextTags}`);
|
|
187621
|
+
const heuristicMutationCount = cleanup.droppedTools + cleanup.deduplicatedTools + cleanup.droppedInjections + cleanup.mutatedTextTags;
|
|
187272
187622
|
const t7 = performance.now();
|
|
187273
187623
|
const clearedReasoning = clearOldReasoning(args.messages, args.reasoningByMessage, args.messageTagNumbers, args.clearReasoningAge);
|
|
187274
187624
|
if (canUseEmptySentinels) {
|
|
@@ -187294,6 +187644,7 @@ async function runPostTransformPhase(args) {
|
|
|
187294
187644
|
}
|
|
187295
187645
|
}
|
|
187296
187646
|
logTransformTiming(args.sessionId, "clearOldReasoning", t7);
|
|
187647
|
+
heuristicOrReasoningDidMutate = heuristicMutationCount + clearedReasoning + strippedInline > 0;
|
|
187297
187648
|
if (pendingMaterializationAtPassStart) {
|
|
187298
187649
|
args.pendingMaterializationSessions.delete(args.sessionId);
|
|
187299
187650
|
}
|
|
@@ -187304,7 +187655,31 @@ async function runPostTransformPhase(args) {
|
|
|
187304
187655
|
if (args.schedulerDecision === "execute" && !materializationRequested) {
|
|
187305
187656
|
updateSessionMeta(args.db, args.sessionId, { lastResponseTime: Date.now() });
|
|
187306
187657
|
}
|
|
187658
|
+
const toolReclaimExecutePass = args.schedulerDecision === "execute";
|
|
187659
|
+
const alreadyMutatingThisPass = pendingOpsDidMutate || heuristicOrReasoningDidMutate;
|
|
187660
|
+
let autoReclaimTargetCount = 0;
|
|
187661
|
+
let autoReclaimDidMutate = false;
|
|
187662
|
+
if (toolReclaimExecutePass && alreadyMutatingThisPass && !emergencyDropEligible) {
|
|
187663
|
+
const syntheticPendingOps = buildSyntheticToolReclaimOps({
|
|
187664
|
+
db: args.db,
|
|
187665
|
+
sessionId: args.sessionId,
|
|
187666
|
+
targets: args.targets,
|
|
187667
|
+
watermark: args.sessionMeta.toolReclaimWatermark ?? 0,
|
|
187668
|
+
pendingOps
|
|
187669
|
+
});
|
|
187670
|
+
autoReclaimTargetCount = syntheticPendingOps.length;
|
|
187671
|
+
if (syntheticPendingOps.length > 0) {
|
|
187672
|
+
autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, [], syntheticPendingOps);
|
|
187673
|
+
}
|
|
187674
|
+
}
|
|
187307
187675
|
args.batch?.finalize();
|
|
187676
|
+
if (toolReclaimExecutePass) {
|
|
187677
|
+
const maxTagNumber = advanceToolReclaimWatermarkToCurrentMax(args.db, args.sessionId);
|
|
187678
|
+
args.sessionMeta.toolReclaimWatermark = Math.max(args.sessionMeta.toolReclaimWatermark ?? 0, maxTagNumber);
|
|
187679
|
+
}
|
|
187680
|
+
if (autoReclaimTargetCount > 0) {
|
|
187681
|
+
sessionLog(args.sessionId, `tool reclaim auto-drop: targets=${autoReclaimTargetCount} mutated=${autoReclaimDidMutate}`);
|
|
187682
|
+
}
|
|
187308
187683
|
logTransformTiming(args.sessionId, "batchFinalize:heuristics", performance.now());
|
|
187309
187684
|
if (args.sessionMeta.lastTransformError !== null) {
|
|
187310
187685
|
updateSessionMeta(args.db, args.sessionId, { lastTransformError: null });
|
|
@@ -187316,11 +187691,6 @@ async function runPostTransformPhase(args) {
|
|
|
187316
187691
|
deferredMaterializedSuccessfully = true;
|
|
187317
187692
|
heuristicsRanSuccessfully = true;
|
|
187318
187693
|
}
|
|
187319
|
-
if (args.watermark > 0) {
|
|
187320
|
-
const tWatermarkCleanup = performance.now();
|
|
187321
|
-
truncateErroredTools(args.messages, args.watermark, args.messageTagNumbers);
|
|
187322
|
-
logTransformTiming(args.sessionId, "watermarkCleanup", tWatermarkCleanup);
|
|
187323
|
-
}
|
|
187324
187694
|
if (shouldApplyPendingOps) {
|
|
187325
187695
|
pendingOpsRanSuccessfully = true;
|
|
187326
187696
|
}
|
|
@@ -188336,7 +188706,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
|
|
|
188336
188706
|
let tailToolTokens;
|
|
188337
188707
|
let liveTailTokens;
|
|
188338
188708
|
try {
|
|
188339
|
-
const agg = getActiveTagTokenAggregate(db, sessionId);
|
|
188709
|
+
const agg = getActiveTagTokenAggregate(db, sessionId, deps.protectedTags);
|
|
188340
188710
|
tailToolTokens = agg.toolOutput;
|
|
188341
188711
|
liveTailTokens = agg.conversation + agg.toolCall;
|
|
188342
188712
|
} catch {
|
|
@@ -188347,6 +188717,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
|
|
|
188347
188717
|
const executeThresholdTokens = Math.round((resolvedContextLimit ?? 0) * resolvedExecuteThresholdPct / 100);
|
|
188348
188718
|
const usableTokens = Math.max(0, executeThresholdTokens - contextUsage.inputTokens + liveTailTokens);
|
|
188349
188719
|
resetLastNudgeCycleIfTailShrank(db, sessionId, tailToolTokens);
|
|
188720
|
+
const oldestReclaimableToolTags = getOldestActiveUnprotectedToolTags(db, sessionId, deps.protectedTags);
|
|
188350
188721
|
deps.channel1StateBySession.set(sessionId, {
|
|
188351
188722
|
tailToolTokens,
|
|
188352
188723
|
historyBudgetTokens: historyBudgetTokens ?? 0,
|
|
@@ -188355,9 +188726,10 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
|
|
|
188355
188726
|
lastInputTokens: contextUsage.inputTokens,
|
|
188356
188727
|
turnToolTokens: 0,
|
|
188357
188728
|
usableTokens,
|
|
188358
|
-
reducedSinceRefresh: false
|
|
188729
|
+
reducedSinceRefresh: false,
|
|
188730
|
+
oldestReclaimableToolTags
|
|
188359
188731
|
});
|
|
188360
|
-
const channel2MetricsKnown =
|
|
188732
|
+
const channel2MetricsKnown = resolvedContextLimit !== undefined && resolvedContextLimit > 0 && resolvedExecuteThresholdPct > 0;
|
|
188361
188733
|
if (channel2MetricsKnown) {
|
|
188362
188734
|
const channel2ShouldTrigger = shouldTriggerChannel2({
|
|
188363
188735
|
reclaimableTokens: tailToolTokens,
|
|
@@ -188381,6 +188753,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
|
|
|
188381
188753
|
}
|
|
188382
188754
|
const elapsed = (performance.now() - startTime).toFixed(1);
|
|
188383
188755
|
sessionLog(sessionId, `transform completed in ${elapsed}ms (${messages.length} messages, ${targets.size} targets, watermark: ${watermark})`);
|
|
188756
|
+
deps.maybeAutoEmbedSession?.(sessionId);
|
|
188384
188757
|
};
|
|
188385
188758
|
}
|
|
188386
188759
|
function resolveHistoryBudgetTokens(historyBudgetPercentage, contextUsage, executeThresholdPercentage, modelKey, executeThresholdTokens, resolvedContextLimit) {
|
|
@@ -188426,18 +188799,14 @@ function evictExpiredUsageEntries(contextUsageMap) {
|
|
|
188426
188799
|
}
|
|
188427
188800
|
async function deliverChannel2IfPending(deps, sessionId) {
|
|
188428
188801
|
try {
|
|
188429
|
-
try {
|
|
188430
|
-
const meta3 = getOrCreateSessionMeta(deps.db, sessionId);
|
|
188431
|
-
if (meta3.isSubagent)
|
|
188432
|
-
return;
|
|
188433
|
-
} catch {}
|
|
188434
188802
|
const baseline = deps.channel1StateBySession?.get(sessionId);
|
|
188435
188803
|
await maybeDeliverChannel2(sessionId, {
|
|
188436
188804
|
db: deps.db,
|
|
188437
188805
|
serverUrl: deps.serverUrl,
|
|
188438
188806
|
directory: deps.directory ?? ".",
|
|
188439
188807
|
reclaimableTokens: baseline ? baseline.tailToolTokens + baseline.turnToolTokens : undefined,
|
|
188440
|
-
usableTokens: baseline?.usableTokens
|
|
188808
|
+
usableTokens: baseline?.usableTokens,
|
|
188809
|
+
oldestReclaimableToolTags: baseline?.oldestReclaimableToolTags
|
|
188441
188810
|
});
|
|
188442
188811
|
} catch (error51) {
|
|
188443
188812
|
sessionLog(sessionId, "channel2 delivery wrapper failed (ignored):", error51);
|
|
@@ -188743,6 +189112,46 @@ function createEventHandler2(deps) {
|
|
|
188743
189112
|
};
|
|
188744
189113
|
}
|
|
188745
189114
|
|
|
189115
|
+
// src/hooks/magic-context/format-embed-status.ts
|
|
189116
|
+
function formatEmbedStatusText(coverage, drain) {
|
|
189117
|
+
if (!coverage.enabled) {
|
|
189118
|
+
return "Embedding is off (no provider configured).";
|
|
189119
|
+
}
|
|
189120
|
+
const lines = [];
|
|
189121
|
+
lines.push(`Embedding — model: ${coverage.model} (${coverage.provider})`);
|
|
189122
|
+
lines.push(`This session: ${coverage.session.embedded} / ${coverage.session.total} compartments embedded`);
|
|
189123
|
+
lines.push(`Project memories: ${coverage.memories.embedded} / ${coverage.memories.total} embedded`);
|
|
189124
|
+
if (coverage.commits.gitEnabled) {
|
|
189125
|
+
lines.push(`Git commits: ${coverage.commits.embedded} / ${coverage.commits.total}`);
|
|
189126
|
+
} else {
|
|
189127
|
+
lines.push("Git commits: 0 / 0 (git indexing off)");
|
|
189128
|
+
}
|
|
189129
|
+
let drainLine = "Drain: idle";
|
|
189130
|
+
switch (drain.status) {
|
|
189131
|
+
case "running": {
|
|
189132
|
+
const e = drain.embedded ?? coverage.session.embedded;
|
|
189133
|
+
const t = drain.total ?? coverage.session.total;
|
|
189134
|
+
const failedSuffix = drain.failed && drain.failed > 0 ? ` (${drain.failed} failed)` : "";
|
|
189135
|
+
drainLine = `Drain: running ${e}/${t}${failedSuffix}`;
|
|
189136
|
+
break;
|
|
189137
|
+
}
|
|
189138
|
+
case "paused": {
|
|
189139
|
+
const e = drain.embedded ?? coverage.session.embedded;
|
|
189140
|
+
const t = drain.total ?? coverage.session.total;
|
|
189141
|
+
drainLine = `Drain: paused ${e}/${t}`;
|
|
189142
|
+
break;
|
|
189143
|
+
}
|
|
189144
|
+
case "stopped":
|
|
189145
|
+
drainLine = "Drain: stopped (provider down)";
|
|
189146
|
+
break;
|
|
189147
|
+
default:
|
|
189148
|
+
drainLine = "Drain: idle";
|
|
189149
|
+
}
|
|
189150
|
+
lines.push(drainLine);
|
|
189151
|
+
return lines.join(`
|
|
189152
|
+
`);
|
|
189153
|
+
}
|
|
189154
|
+
|
|
188746
189155
|
// src/hooks/magic-context/hook.ts
|
|
188747
189156
|
await __promiseAll([
|
|
188748
189157
|
init_inject_compartments(),
|
|
@@ -188947,10 +189356,11 @@ function maybeInjectChannel1Nudge(args, sessionId, tool, output) {
|
|
|
188947
189356
|
contextLimit: state.contextLimit,
|
|
188948
189357
|
executeThresholdPercentage: state.executeThresholdPercentage
|
|
188949
189358
|
});
|
|
189359
|
+
const workingWindowTokens = Math.round(state.contextLimit * state.executeThresholdPercentage / 100);
|
|
188950
189360
|
const decision = decideChannel1({
|
|
188951
189361
|
undroppedTokens,
|
|
188952
189362
|
pressure,
|
|
188953
|
-
|
|
189363
|
+
workingWindowTokens,
|
|
188954
189364
|
lastNudgeUndropped: getLastNudgeUndropped(args.db, sessionId),
|
|
188955
189365
|
lastNudgeLevel: getLastNudgeLevel(args.db, sessionId),
|
|
188956
189366
|
hasRecentReduce: false
|
|
@@ -188959,7 +189369,7 @@ function maybeInjectChannel1Nudge(args, sessionId, tool, output) {
|
|
|
188959
189369
|
setLastNudgeLevel(args.db, sessionId, decision.nextLastNudgeLevel);
|
|
188960
189370
|
if (!decision.fire)
|
|
188961
189371
|
return;
|
|
188962
|
-
out.output += buildChannel1Reminder(decision.level, decision.undroppedTokens);
|
|
189372
|
+
out.output += buildChannel1Reminder(decision.level, decision.undroppedTokens, state.oldestReclaimableToolTags);
|
|
188963
189373
|
sessionLog(sessionId, `channel1 nudge fired: level=${decision.level} undropped~${Math.round(decision.undroppedTokens / 1000)}k tool=${tool}`);
|
|
188964
189374
|
}
|
|
188965
189375
|
function createToolExecuteAfterHook(args) {
|
|
@@ -189031,9 +189441,7 @@ Context is managed for you entirely automatically — there's nothing to prune a
|
|
|
189031
189441
|
var CTX_NOTE_GUIDANCE = `Use \`ctx_note\` ONLY for genuinely future concerns — something to revisit much later, not work coming up in the next few turns (that's already in your active context) and not active multi-step work (use todos for that). Magic Context preserves your full context across both compaction and restarts, so an upcoming restart or "let's come back to this later" is never a reason to take a note — nothing is lost either way. Notes you do take survive compression and resurface at natural work boundaries (after commits, historian runs, todo completion).`;
|
|
189032
189442
|
var TOOL_HISTORY_GUIDANCE = `Compressed history intentionally omits tool calls and their outputs — summaries like "I edited file X" are historian records, not patterns to replicate. In the live conversation, older tool calls and their results are cleaned up to save context — you may see your own past messages referencing actions without the corresponding tool call or result visible. This is normal context management. ALWAYS use real tool calls; never simulate, fabricate, or inline tool outputs in your text. If there is no tool result message, the action did not happen. NEVER simulate, hallucinate or claim tool calls, command output, search results, file edits, or diffs in plain text as if they actually occurred.`;
|
|
189033
189443
|
var BASE_INTRO = (protectedTags) => `Messages and tool outputs are tagged with §N§ identifiers (e.g., §1§, §42§).
|
|
189034
|
-
Use \`ctx_reduce\` to
|
|
189035
|
-
- \`drop\`: Remove entirely (best for tool outputs you already acted on).
|
|
189036
|
-
Syntax: "3-5", "1,2,9", or "1-5,8,12-15". Last ${protectedTags} tags are protected.
|
|
189444
|
+
Use \`ctx_reduce\` to mark spent tagged content as discardable and reclaim space. Marking is NOT an immediate delete — it queues the content, which stays fully visible until space is actually needed (as soon as the next turn if you're already under pressure, much later if not), so mark a tool output as soon as you're done with it rather than hoarding the call for the end of the turn. The last ${protectedTags} tags are protected (marking one just queues it until it ages out). Syntax: "3-5", "1,2,9", or "1-5,8,12-15".
|
|
189037
189445
|
Do not announce or narrate \`ctx_reduce\` drops — just call the tool silently. Saying "I'll drop these outputs" wastes tokens the user does not care about.
|
|
189038
189446
|
${CTX_NOTE_GUIDANCE}
|
|
189039
189447
|
Use \`ctx_memory\` for durable project knowledge: write what future sessions must know, update/archive/merge the memories you see in \`<project-memory>\` when they drift. Memories persist across sessions and every new session starts with them.
|
|
@@ -189052,7 +189460,7 @@ Use \`ctx_expand\` to recover the raw conversation behind a \`<compartment>\` su
|
|
|
189052
189460
|
\`ctx_search\` returns ranked results from memories, git commits, and raw message history. Use message ordinals from results with \`ctx_expand\` to retrieve surrounding conversation context.
|
|
189053
189461
|
${TOOL_HISTORY_GUIDANCE}
|
|
189054
189462
|
NEVER drop large ranges blindly (e.g., "1-50"). Review each tag before deciding.
|
|
189055
|
-
|
|
189463
|
+
Keep your user's instructions and intent — never drop a user message for its directive, even an old one. But a large block of pasted content inside a user message (logs, data dumps, long code, attachments) is fair to mark discardable once you've extracted what you need — it stays searchable via \`ctx_search\`.
|
|
189056
189464
|
NEVER drop assistant text messages unless they are exceptionally large. Your conversation messages are lightweight; only large tool outputs are worth dropping.
|
|
189057
189465
|
Before your turn finishes, consider using \`ctx_reduce\` to drop large tool outputs you no longer need.`;
|
|
189058
189466
|
var BASE_INTRO_NO_REDUCE = () => `${CTX_NOTE_GUIDANCE}
|
|
@@ -189473,29 +189881,55 @@ function createMagicContextHook(deps) {
|
|
|
189473
189881
|
ensureProjectRegistered: ensureProjectRegisteredFromOpenCodeDirectory,
|
|
189474
189882
|
getNotificationParams: (sid) => getLiveNotificationParams(sid, liveModelBySession, variantBySession, agentBySession)
|
|
189475
189883
|
});
|
|
189476
|
-
const executeEmbedHistory = async (sessionId) => {
|
|
189884
|
+
const executeEmbedHistory = async (sessionId, options) => {
|
|
189477
189885
|
if (deps.config.memory?.enabled === false) {
|
|
189478
189886
|
return "Memory is disabled for this project, so there is no semantic embedding to backfill.";
|
|
189479
189887
|
}
|
|
189480
189888
|
const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
|
|
189889
|
+
const active = embedRunStateBySession.get(sessionId);
|
|
189890
|
+
if (active && !active.signal.aborted && !options?.signal) {
|
|
189891
|
+
return "Embedding is already running for this session.";
|
|
189892
|
+
}
|
|
189481
189893
|
await ensureProjectRegisteredFromOpenCodeDirectory(directory, db);
|
|
189482
189894
|
const sessionProjectIdentity = resolveProjectIdentity(directory);
|
|
189483
|
-
|
|
189484
|
-
const
|
|
189485
|
-
|
|
189486
|
-
|
|
189487
|
-
|
|
189488
|
-
|
|
189489
|
-
|
|
189490
|
-
|
|
189491
|
-
|
|
189492
|
-
|
|
189493
|
-
|
|
189494
|
-
|
|
189895
|
+
embedPauseBySession.delete(sessionId);
|
|
189896
|
+
const prior = embedRunStateBySession.get(sessionId);
|
|
189897
|
+
if (prior)
|
|
189898
|
+
prior.abort();
|
|
189899
|
+
const controller = new AbortController;
|
|
189900
|
+
embedRunStateBySession.set(sessionId, controller);
|
|
189901
|
+
const signal = options?.signal ?? controller.signal;
|
|
189902
|
+
if (!options?.silent) {
|
|
189903
|
+
setRecompStarting({ recompProgressBySession }, sessionId, "Embedding history…", "embed");
|
|
189904
|
+
}
|
|
189905
|
+
let runFailed = 0;
|
|
189906
|
+
let outcome;
|
|
189907
|
+
try {
|
|
189908
|
+
outcome = await embedSessionCompartmentChunks(db, sessionProjectIdentity, sessionId, {
|
|
189909
|
+
signal,
|
|
189910
|
+
onProgress: ({ embedded, total }) => {
|
|
189911
|
+
const cur = recompProgressBySession.get(sessionId);
|
|
189912
|
+
if (!cur || cur.phase !== "recomp")
|
|
189913
|
+
return;
|
|
189914
|
+
recompProgressBySession.set(sessionId, {
|
|
189915
|
+
...cur,
|
|
189916
|
+
processedMessages: embedded,
|
|
189917
|
+
totalMessages: total,
|
|
189918
|
+
updatedAt: Date.now()
|
|
189919
|
+
});
|
|
189920
|
+
}
|
|
189921
|
+
});
|
|
189922
|
+
} finally {
|
|
189923
|
+
if (embedRunStateBySession.get(sessionId) === controller) {
|
|
189924
|
+
embedRunStateBySession.delete(sessionId);
|
|
189495
189925
|
}
|
|
189496
|
-
}
|
|
189926
|
+
}
|
|
189927
|
+
if ("failed" in outcome)
|
|
189928
|
+
runFailed = outcome.failed;
|
|
189497
189929
|
const terminal = (phase, message) => {
|
|
189498
|
-
|
|
189930
|
+
if (!options?.silent) {
|
|
189931
|
+
setRecompTerminal({ recompProgressBySession }, sessionId, phase, message);
|
|
189932
|
+
}
|
|
189499
189933
|
return message;
|
|
189500
189934
|
};
|
|
189501
189935
|
switch (outcome.status) {
|
|
@@ -189504,15 +189938,78 @@ function createMagicContextHook(deps) {
|
|
|
189504
189938
|
case "disabled":
|
|
189505
189939
|
return terminal("skipped", "No embedding provider is configured, so there is nothing to embed.");
|
|
189506
189940
|
case "busy":
|
|
189507
|
-
return terminal("skipped",
|
|
189508
|
-
case "aborted":
|
|
189509
|
-
|
|
189941
|
+
return terminal("skipped", "Embedding is already running for this project. Try again shortly.");
|
|
189942
|
+
case "aborted": {
|
|
189943
|
+
const cov = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
|
|
189944
|
+
const msg = `Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`;
|
|
189945
|
+
return terminal("skipped", msg);
|
|
189946
|
+
}
|
|
189510
189947
|
case "stalled":
|
|
189511
|
-
return terminal("skipped", `Embedded ${outcome.embedded} compartments; ${outcome.remaining} could not be embedded (the provider returned no result). Run /ctx-embed
|
|
189948
|
+
return terminal("skipped", `Embedded ${outcome.embedded} compartments; ${outcome.remaining} could not be embedded (the provider returned no result). Run /ctx-embed start again to retry them.`);
|
|
189512
189949
|
default:
|
|
189513
|
-
return terminal("done", `Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search.`);
|
|
189950
|
+
return terminal("done", `Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search${runFailed > 0 ? ` (${runFailed} failed)` : ""}.`);
|
|
189514
189951
|
}
|
|
189515
189952
|
};
|
|
189953
|
+
const pauseEmbedDrain = (sessionId) => {
|
|
189954
|
+
embedPauseBySession.add(sessionId);
|
|
189955
|
+
const ctrl = embedRunStateBySession.get(sessionId);
|
|
189956
|
+
if (ctrl)
|
|
189957
|
+
ctrl.abort();
|
|
189958
|
+
const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
|
|
189959
|
+
const sessionProjectIdentity = resolveProjectIdentity(directory);
|
|
189960
|
+
const cov = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
|
|
189961
|
+
return `Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`;
|
|
189962
|
+
};
|
|
189963
|
+
const getEmbedStatusText = (sessionId) => {
|
|
189964
|
+
const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
|
|
189965
|
+
const sessionProjectIdentity = resolveProjectIdentity(directory);
|
|
189966
|
+
const coverage = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
|
|
189967
|
+
const progress = recompProgressBySession.get(sessionId);
|
|
189968
|
+
const drainUi = getEmbedDrainUiStatus(sessionId, progress);
|
|
189969
|
+
return formatEmbedStatusText(coverage, {
|
|
189970
|
+
status: drainUi.status,
|
|
189971
|
+
embedded: progress?.processedMessages,
|
|
189972
|
+
total: progress?.totalMessages
|
|
189973
|
+
});
|
|
189974
|
+
};
|
|
189975
|
+
const maybeAutoEmbedSession = (sessionId) => {
|
|
189976
|
+
if (autoEmbedAttemptedBySession.has(sessionId))
|
|
189977
|
+
return;
|
|
189978
|
+
if (embedPauseBySession.has(sessionId))
|
|
189979
|
+
return;
|
|
189980
|
+
if (deps.config.memory?.enabled === false)
|
|
189981
|
+
return;
|
|
189982
|
+
autoEmbedAttemptedBySession.add(sessionId);
|
|
189983
|
+
const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
|
|
189984
|
+
(async () => {
|
|
189985
|
+
try {
|
|
189986
|
+
await new Promise((resolve7) => setTimeout(resolve7, 0));
|
|
189987
|
+
await ensureProjectRegisteredFromOpenCodeDirectory(directory, db);
|
|
189988
|
+
const sessionProjectIdentity = resolveProjectIdentity(directory);
|
|
189989
|
+
const coverage = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
|
|
189990
|
+
if (!coverage.enabled)
|
|
189991
|
+
return;
|
|
189992
|
+
const remaining = coverage.session.total - coverage.session.embedded;
|
|
189993
|
+
if (remaining <= 0)
|
|
189994
|
+
return;
|
|
189995
|
+
const notifyParams = getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession);
|
|
189996
|
+
if (!isTuiConnected(sessionId)) {
|
|
189997
|
+
const startMsg = `Embedding ${remaining} compartment${remaining === 1 ? "" : "s"} of history in the background…`;
|
|
189998
|
+
await sendIgnoredMessage(deps.client, sessionId, startMsg, {
|
|
189999
|
+
...notifyParams
|
|
190000
|
+
});
|
|
190001
|
+
}
|
|
190002
|
+
const summary = await executeEmbedHistory(sessionId);
|
|
190003
|
+
if (!isTuiConnected(sessionId)) {
|
|
190004
|
+
await sendIgnoredMessage(deps.client, sessionId, summary, {
|
|
190005
|
+
...notifyParams
|
|
190006
|
+
});
|
|
190007
|
+
}
|
|
190008
|
+
} catch (error51) {
|
|
190009
|
+
log("[magic-context] auto-embed drain failed:", error51);
|
|
190010
|
+
}
|
|
190011
|
+
})();
|
|
190012
|
+
};
|
|
189516
190013
|
const sidekickRunnable = isSidekickRunnable(deps.config);
|
|
189517
190014
|
const sidekickConfig = sidekickRunnable ? deps.config.sidekick : undefined;
|
|
189518
190015
|
const transform2 = createTransform({
|
|
@@ -189574,7 +190071,8 @@ function createMagicContextHook(deps) {
|
|
|
189574
190071
|
cavemanTextCompression: ctxReduceEnabled === false && deps.config.caveman_text_compression?.enabled === true ? {
|
|
189575
190072
|
enabled: true,
|
|
189576
190073
|
minChars: deps.config.caveman_text_compression.min_chars ?? 500
|
|
189577
|
-
} : undefined
|
|
190074
|
+
} : undefined,
|
|
190075
|
+
maybeAutoEmbedSession
|
|
189578
190076
|
});
|
|
189579
190077
|
const eventHandler = createEventHandler2({
|
|
189580
190078
|
contextUsageMap,
|
|
@@ -189603,6 +190101,7 @@ function createMagicContextHook(deps) {
|
|
|
189603
190101
|
recompProgressBySession.delete(sessionId);
|
|
189604
190102
|
internalChildSessions.delete(sessionId);
|
|
189605
190103
|
channel1StateBySession.delete(sessionId);
|
|
190104
|
+
clearEmbedSessionState(sessionId);
|
|
189606
190105
|
}
|
|
189607
190106
|
});
|
|
189608
190107
|
const runDreamQueueInBackground = () => {
|
|
@@ -189668,6 +190167,8 @@ function createMagicContextHook(deps) {
|
|
|
189668
190167
|
executeRecomp: historianRunnable ? async (sessionId, options) => runManagedRecomp(buildManagedRecompCtx(sessionId), sessionId, options) : undefined,
|
|
189669
190168
|
runUpgrade: historianRunnable ? async (sessionId) => runManagedUpgrade(buildManagedRecompCtx(sessionId), sessionId) : undefined,
|
|
189670
190169
|
executeEmbedHistory,
|
|
190170
|
+
pauseEmbedDrain,
|
|
190171
|
+
getEmbedStatusText,
|
|
189671
190172
|
sendNotification: async (sessionId, text, params) => {
|
|
189672
190173
|
await sendIgnoredMessage(deps.client, sessionId, text, {
|
|
189673
190174
|
...getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
|
|
@@ -189879,6 +190380,7 @@ function truncateError(name2, code, message, maxLen = 240) {
|
|
|
189879
190380
|
|
|
189880
190381
|
// src/plugin/rpc-handlers.ts
|
|
189881
190382
|
init_project_identity();
|
|
190383
|
+
init_project_embedding_registry();
|
|
189882
190384
|
init_tool_definition_tokens();
|
|
189883
190385
|
await init_storage();
|
|
189884
190386
|
|
|
@@ -190599,6 +191101,26 @@ function buildStatusDetail(db, sessionId, directory, modelKey, config2, liveSess
|
|
|
190599
191101
|
}
|
|
190600
191102
|
return detail;
|
|
190601
191103
|
}
|
|
191104
|
+
function buildEmbedDetail(db, sessionId, dir, liveSessionState) {
|
|
191105
|
+
const projectIdentity = resolveProjectIdentity(dir);
|
|
191106
|
+
const coverage = getEmbeddingCoverageStatus(db, projectIdentity, sessionId);
|
|
191107
|
+
const progress = liveSessionState.recompProgressBySession.get(sessionId);
|
|
191108
|
+
const drainUi = getEmbedDrainUiStatus(sessionId, progress);
|
|
191109
|
+
const statusText = formatEmbedStatusText(coverage, {
|
|
191110
|
+
status: drainUi.status,
|
|
191111
|
+
embedded: progress?.processedMessages,
|
|
191112
|
+
total: progress?.totalMessages
|
|
191113
|
+
});
|
|
191114
|
+
return {
|
|
191115
|
+
enabled: coverage.enabled,
|
|
191116
|
+
model: coverage.model,
|
|
191117
|
+
provider: coverage.provider,
|
|
191118
|
+
session: coverage.session,
|
|
191119
|
+
memories: coverage.memories,
|
|
191120
|
+
commits: coverage.commits,
|
|
191121
|
+
statusText
|
|
191122
|
+
};
|
|
191123
|
+
}
|
|
190602
191124
|
function registerRpcHandlers(rpcServer, args) {
|
|
190603
191125
|
const { directory, config: config2, liveSessionState } = args;
|
|
190604
191126
|
const rawConfig = config2;
|
|
@@ -190621,6 +191143,19 @@ function registerRpcHandlers(rpcServer, args) {
|
|
|
190621
191143
|
return { error: "unavailable" };
|
|
190622
191144
|
return buildStatusDetail(db, sessionId, dir, modelKey, rawConfig, liveSessionState, injectionBudgetTokens);
|
|
190623
191145
|
});
|
|
191146
|
+
rpcServer.handle("embed-detail", async (params) => {
|
|
191147
|
+
const sessionId = String(params.sessionId ?? "");
|
|
191148
|
+
const dir = String(params.directory ?? directory);
|
|
191149
|
+
const db = getDb();
|
|
191150
|
+
if (!db || !sessionId)
|
|
191151
|
+
return { error: "unavailable" };
|
|
191152
|
+
try {
|
|
191153
|
+
return buildEmbedDetail(db, sessionId, dir, liveSessionState);
|
|
191154
|
+
} catch (err) {
|
|
191155
|
+
log("[rpc] embed-detail error:", err);
|
|
191156
|
+
return { error: "unavailable" };
|
|
191157
|
+
}
|
|
191158
|
+
});
|
|
190624
191159
|
rpcServer.handle("compartment-count", async (params) => {
|
|
190625
191160
|
const sessionId = String(params.sessionId ?? "");
|
|
190626
191161
|
const db = getDb();
|
|
@@ -190743,27 +191278,225 @@ Older parts of this session are summarized into <compartment> blocks inside <ses
|
|
|
190743
191278
|
|
|
190744
191279
|
ctx_expand(start=120, end=245) ← the compartment's own start/end attributes
|
|
190745
191280
|
|
|
190746
|
-
Returns the raw transcript as [N] U:/A: lines, capped at ~15K tokens; an oversized range returns the head and tells you where to continue. Also works with ordinals from ctx_search message results — expand a window around a hit (e.g. start=N-10, end=N+5). Ranges after the last compartment are your live tail — already visible in context, not expandable
|
|
191281
|
+
Returns the raw transcript as [N] U:/A: lines, capped at ~15K tokens; an oversized range returns the head and tells you where to continue. Also works with ordinals from ctx_search message results — expand a window around a hit (e.g. start=N-10, end=N+5). Ranges after the last compartment are your live tail — already visible in context, not expandable.
|
|
191282
|
+
|
|
191283
|
+
Two recovery modes for finer detail:
|
|
191284
|
+
- ctx_expand(start=120, end=245, verbose=true) — lists each message SEPARATELY with its ordinal [N] and a per-part preview (each tool call shown with its output size). Use this to find the exact message or tool call you want, then recover it in full by ordinal.
|
|
191285
|
+
- ctx_expand(message=138) — returns the FULL untruncated content of the message at that ordinal: every text part, and every tool call's complete input + output, read from stored history. This is the cheap way to get back a tool output you dropped with ctx_reduce — the original is still in storage even though the wire shows [dropped §N§]. If the message was deleted from history (session prune/revert), it says so.`;
|
|
190747
191286
|
var CTX_EXPAND_TOKEN_BUDGET = 15000;
|
|
190748
191287
|
|
|
191288
|
+
// src/tools/ctx-expand/render.ts
|
|
191289
|
+
init_read_session_formatting();
|
|
191290
|
+
await init_read_session_chunk();
|
|
191291
|
+
function isRecord3(value) {
|
|
191292
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
191293
|
+
}
|
|
191294
|
+
function roleLabel(role) {
|
|
191295
|
+
if (role === "assistant")
|
|
191296
|
+
return "A (assistant)";
|
|
191297
|
+
if (role === "user")
|
|
191298
|
+
return "U (user)";
|
|
191299
|
+
return role;
|
|
191300
|
+
}
|
|
191301
|
+
function truncate2(value, max) {
|
|
191302
|
+
const t = value.trim();
|
|
191303
|
+
return t.length <= max ? t : `${t.slice(0, max)}…`;
|
|
191304
|
+
}
|
|
191305
|
+
function keyArg(input) {
|
|
191306
|
+
if (!input)
|
|
191307
|
+
return "";
|
|
191308
|
+
for (const k of ["filePath", "path", "pattern", "query", "symbol", "module", "action"]) {
|
|
191309
|
+
const v = input[k];
|
|
191310
|
+
if (typeof v === "string" && v.length > 0)
|
|
191311
|
+
return truncate2(v, 60);
|
|
191312
|
+
}
|
|
191313
|
+
if (typeof input.description === "string")
|
|
191314
|
+
return truncate2(input.description, 60);
|
|
191315
|
+
return "";
|
|
191316
|
+
}
|
|
191317
|
+
function asToolPart(part) {
|
|
191318
|
+
const type = typeof part.type === "string" ? part.type : "";
|
|
191319
|
+
if (type === "tool") {
|
|
191320
|
+
const state = isRecord3(part.state) ? part.state : null;
|
|
191321
|
+
const output = state && typeof state.output === "string" ? state.output : state && state.output != null ? JSON.stringify(state.output) : null;
|
|
191322
|
+
const metadata = state && isRecord3(state.metadata) ? state.metadata : null;
|
|
191323
|
+
const title = state && typeof state.title === "string" && state.title || metadata && typeof metadata.title === "string" && metadata.title || null;
|
|
191324
|
+
return {
|
|
191325
|
+
name: typeof part.tool === "string" ? part.tool : "tool",
|
|
191326
|
+
callId: typeof part.callID === "string" ? part.callID : "",
|
|
191327
|
+
title,
|
|
191328
|
+
input: state && isRecord3(state.input) ? state.input : null,
|
|
191329
|
+
output
|
|
191330
|
+
};
|
|
191331
|
+
}
|
|
191332
|
+
if (type === "tool_use") {
|
|
191333
|
+
return {
|
|
191334
|
+
name: typeof part.name === "string" ? part.name : "tool",
|
|
191335
|
+
callId: typeof part.id === "string" ? part.id : "",
|
|
191336
|
+
title: null,
|
|
191337
|
+
input: isRecord3(part.input) ? part.input : null,
|
|
191338
|
+
output: null
|
|
191339
|
+
};
|
|
191340
|
+
}
|
|
191341
|
+
if (type === "tool_result") {
|
|
191342
|
+
const content = part.content;
|
|
191343
|
+
const output = typeof content === "string" ? content : content != null ? JSON.stringify(content) : null;
|
|
191344
|
+
return {
|
|
191345
|
+
name: "tool_result",
|
|
191346
|
+
callId: typeof part.tool_use_id === "string" ? part.tool_use_id : "",
|
|
191347
|
+
title: null,
|
|
191348
|
+
input: null,
|
|
191349
|
+
output
|
|
191350
|
+
};
|
|
191351
|
+
}
|
|
191352
|
+
return null;
|
|
191353
|
+
}
|
|
191354
|
+
function textOf(part) {
|
|
191355
|
+
if (part.type === "text" && typeof part.text === "string")
|
|
191356
|
+
return part.text;
|
|
191357
|
+
return null;
|
|
191358
|
+
}
|
|
191359
|
+
function reasoningOf(part) {
|
|
191360
|
+
if ((part.type === "reasoning" || part.type === "thinking") && typeof part.text === "string") {
|
|
191361
|
+
return part.text;
|
|
191362
|
+
}
|
|
191363
|
+
return null;
|
|
191364
|
+
}
|
|
191365
|
+
function renderPartPreview(part) {
|
|
191366
|
+
if (!isRecord3(part))
|
|
191367
|
+
return null;
|
|
191368
|
+
const text = textOf(part);
|
|
191369
|
+
if (text !== null) {
|
|
191370
|
+
const t = truncate2(text, 200);
|
|
191371
|
+
return t.length > 0 ? ` • ${t}` : null;
|
|
191372
|
+
}
|
|
191373
|
+
const tool = asToolPart(part);
|
|
191374
|
+
if (tool) {
|
|
191375
|
+
const arg = keyArg(tool.input);
|
|
191376
|
+
const head = arg ? `${tool.name}(${arg})` : tool.name;
|
|
191377
|
+
return tool.output !== null ? ` • tool ${head} → output ~${estimateTokens(tool.output)} tok` : ` • tool ${head}`;
|
|
191378
|
+
}
|
|
191379
|
+
const reasoning = reasoningOf(part);
|
|
191380
|
+
if (reasoning !== null)
|
|
191381
|
+
return ` • [reasoning] ${truncate2(reasoning, 120)}`;
|
|
191382
|
+
const type = typeof part.type === "string" ? part.type : "part";
|
|
191383
|
+
if (type === "file")
|
|
191384
|
+
return " • [file]";
|
|
191385
|
+
if (type === "step-start" || type === "step-finish")
|
|
191386
|
+
return null;
|
|
191387
|
+
return ` • [${type}]`;
|
|
191388
|
+
}
|
|
191389
|
+
function renderPartFull(part) {
|
|
191390
|
+
if (!isRecord3(part))
|
|
191391
|
+
return null;
|
|
191392
|
+
const text = textOf(part);
|
|
191393
|
+
if (text !== null) {
|
|
191394
|
+
return text.trim().length > 0 ? ` [text]
|
|
191395
|
+
${text}` : null;
|
|
191396
|
+
}
|
|
191397
|
+
const tool = asToolPart(part);
|
|
191398
|
+
if (tool) {
|
|
191399
|
+
const lines = [];
|
|
191400
|
+
const idSuffix = tool.callId ? ` #${tool.callId}` : "";
|
|
191401
|
+
lines.push(` [tool: ${tool.name}${idSuffix}]`);
|
|
191402
|
+
if (tool.title && tool.title.trim().length > 0) {
|
|
191403
|
+
lines.push(` description: ${tool.title.trim()}`);
|
|
191404
|
+
}
|
|
191405
|
+
if (tool.input)
|
|
191406
|
+
lines.push(` input: ${JSON.stringify(tool.input)}`);
|
|
191407
|
+
if (tool.output !== null)
|
|
191408
|
+
lines.push(` output:
|
|
191409
|
+
${tool.output}`);
|
|
191410
|
+
return lines.join(`
|
|
191411
|
+
`);
|
|
191412
|
+
}
|
|
191413
|
+
const type = typeof part.type === "string" ? part.type : "part";
|
|
191414
|
+
if (type === "file") {
|
|
191415
|
+
const name2 = typeof part.filename === "string" && part.filename || typeof part.url === "string" && part.url || "";
|
|
191416
|
+
return ` [file]${name2 ? ` ${name2}` : ""}`;
|
|
191417
|
+
}
|
|
191418
|
+
return null;
|
|
191419
|
+
}
|
|
191420
|
+
function renderMessageByOrdinal(sessionId, ordinal) {
|
|
191421
|
+
const msg = readRawSessionMessages(sessionId).find((m) => m.ordinal === ordinal);
|
|
191422
|
+
if (!msg) {
|
|
191423
|
+
return `No message at ordinal ${ordinal} in this session's stored history — it was deleted ` + `(session prune/revert) or the ordinal is wrong, so it can't be recovered. ` + `Re-run the tool if you still need the data.`;
|
|
191424
|
+
}
|
|
191425
|
+
const rendered = msg.parts.map(renderPartFull).filter((l) => l !== null);
|
|
191426
|
+
const lines = [`[${msg.ordinal}] ${roleLabel(msg.role)} — full recovery:`, ""];
|
|
191427
|
+
if (rendered.length === 0) {
|
|
191428
|
+
lines.push(" (no recoverable content — message had only structural/reasoning parts)");
|
|
191429
|
+
} else {
|
|
191430
|
+
lines.push(...rendered);
|
|
191431
|
+
}
|
|
191432
|
+
return lines.join(`
|
|
191433
|
+
`);
|
|
191434
|
+
}
|
|
191435
|
+
function renderVerboseRange(sessionId, start, end, tokenBudget) {
|
|
191436
|
+
const messages = readRawSessionMessages(sessionId).filter((m) => m.ordinal >= start && m.ordinal <= end);
|
|
191437
|
+
const out = [];
|
|
191438
|
+
let usedTokens = 0;
|
|
191439
|
+
let lastOrdinal = start - 1;
|
|
191440
|
+
let truncated = false;
|
|
191441
|
+
for (const msg of messages) {
|
|
191442
|
+
const header = `[${msg.ordinal}] ${roleLabel(msg.role)}`;
|
|
191443
|
+
const partLines = msg.parts.map(renderPartPreview).filter((l) => l !== null);
|
|
191444
|
+
const block = partLines.length > 0 ? `${header}
|
|
191445
|
+
${partLines.join(`
|
|
191446
|
+
`)}` : header;
|
|
191447
|
+
const blockTokens = estimateTokens(block);
|
|
191448
|
+
if (usedTokens + blockTokens > tokenBudget && out.length > 0) {
|
|
191449
|
+
truncated = true;
|
|
191450
|
+
break;
|
|
191451
|
+
}
|
|
191452
|
+
out.push(block);
|
|
191453
|
+
usedTokens += blockTokens;
|
|
191454
|
+
lastOrdinal = msg.ordinal;
|
|
191455
|
+
}
|
|
191456
|
+
return { text: out.join(`
|
|
191457
|
+
|
|
191458
|
+
`), lastOrdinal, truncated };
|
|
191459
|
+
}
|
|
191460
|
+
|
|
190749
191461
|
// src/tools/ctx-expand/tools.ts
|
|
190750
191462
|
function createCtxExpandTool(deps) {
|
|
190751
191463
|
return tool({
|
|
190752
191464
|
description: CTX_EXPAND_DESCRIPTION,
|
|
190753
191465
|
args: {
|
|
190754
|
-
start: tool.schema.number().describe(`First message ordinal to expand — a compartment's start="N" attribute, or an ordinal from a ctx_search message hit`),
|
|
190755
|
-
end: tool.schema.number().describe(`Last message ordinal to expand (inclusive) — a compartment's end="M" attribute`)
|
|
191466
|
+
start: tool.schema.number().optional().describe(`First message ordinal to expand — a compartment's start="N" attribute, or an ordinal from a ctx_search message hit`),
|
|
191467
|
+
end: tool.schema.number().optional().describe(`Last message ordinal to expand (inclusive) — a compartment's end="M" attribute`),
|
|
191468
|
+
verbose: tool.schema.boolean().optional().describe("With start/end: list each message separately with its ordinal [N] and per-part preview (each tool call shown with its output size), so you can pick one to recover in full by ordinal."),
|
|
191469
|
+
message: tool.schema.number().optional().describe("Full untruncated recovery of ONE message by its ordinal (every text part + every tool call's complete input/output). Use an ordinal from a compartment, ctx_search hit, or verbose range. Recovers a tool output you dropped with ctx_reduce.")
|
|
190756
191470
|
},
|
|
190757
191471
|
async execute(args, toolContext) {
|
|
190758
191472
|
const sessionId = toolContext.sessionID;
|
|
191473
|
+
if (typeof args.message === "number" && args.message >= 1) {
|
|
191474
|
+
return renderMessageByOrdinal(sessionId, args.message);
|
|
191475
|
+
}
|
|
190759
191476
|
if (!args.start || !args.end || args.start < 1 || args.end < args.start) {
|
|
190760
|
-
return "Error: start and end
|
|
191477
|
+
return "Error: provide either message=<ordinal>, or start and end (positive integers, start <= end).";
|
|
190761
191478
|
}
|
|
190762
191479
|
const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
|
|
190763
191480
|
if (lastCompartmentEnd >= 0 && args.start > lastCompartmentEnd) {
|
|
190764
191481
|
return `Range ${args.start}-${args.end} is entirely within the live tail (after the last compacted message ${lastCompartmentEnd}); those messages are already visible in context.`;
|
|
190765
191482
|
}
|
|
190766
191483
|
const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(args.end, lastCompartmentEnd) : args.end;
|
|
191484
|
+
if (args.verbose === true) {
|
|
191485
|
+
const v = renderVerboseRange(sessionId, args.start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET);
|
|
191486
|
+
if (!v.text) {
|
|
191487
|
+
return `No messages found in range ${args.start}-${effectiveEnd}. The range may be outside this session's history.`;
|
|
191488
|
+
}
|
|
191489
|
+
const out = [
|
|
191490
|
+
`Messages ${args.start}-${v.lastOrdinal} (verbose). Recover any one in full with ctx_expand(message=<ordinal>):`,
|
|
191491
|
+
"",
|
|
191492
|
+
v.text
|
|
191493
|
+
];
|
|
191494
|
+
if (v.truncated) {
|
|
191495
|
+
out.push("", `Truncated at message ${v.lastOrdinal} (budget: ~${CTX_EXPAND_TOKEN_BUDGET} tokens). Call again with start=${v.lastOrdinal + 1} end=${effectiveEnd} verbose=true for more.`);
|
|
191496
|
+
}
|
|
191497
|
+
return out.join(`
|
|
191498
|
+
`);
|
|
191499
|
+
}
|
|
190767
191500
|
const chunk = readSessionChunk(sessionId, CTX_EXPAND_TOKEN_BUDGET, args.start, effectiveEnd + 1);
|
|
190768
191501
|
if (!chunk.text || chunk.messageCount === 0) {
|
|
190769
191502
|
return `No messages found in range ${args.start}-${args.end}. The range may be outside this session's history.`;
|
|
@@ -191457,15 +192190,16 @@ function createCtxNoteTools(deps) {
|
|
|
191457
192190
|
};
|
|
191458
192191
|
}
|
|
191459
192192
|
// src/tools/ctx-reduce/constants.ts
|
|
191460
|
-
var CTX_REDUCE_DESCRIPTION = `
|
|
191461
|
-
|
|
191462
|
-
|
|
191463
|
-
|
|
191464
|
-
-
|
|
191465
|
-
-
|
|
191466
|
-
|
|
191467
|
-
|
|
191468
|
-
|
|
192193
|
+
var CTX_REDUCE_DESCRIPTION = `Mark spent tagged content as discardable to reclaim context space. This is NOT an immediate delete. Use §N§ identifiers visible in the conversation. The \`drop\` param accepts ranges: "3-5", "1,2,9", "1-5,8".
|
|
192194
|
+
|
|
192195
|
+
How it works:
|
|
192196
|
+
- Marking QUEUES content for release. It stays fully visible to you until context space is actually needed — which may be as soon as the next turn if you are already under pressure, or many turns later if not. So mark spent outputs as soon as you finish with them; don't hoard the call for the end of the turn.
|
|
192197
|
+
- The newest tags are protected: marking one just queues it until it ages out of the recent window, so marking recent output is harmless.
|
|
192198
|
+
- When content is finally released it becomes a short placeholder, and re-running the tool is the only way to get it back. So mark only what you are genuinely DONE with — the test is "have I extracted what I need from this?", not "is it safe / do I have time before it drops?".
|
|
192199
|
+
|
|
192200
|
+
Mark discardable once processed: large outputs you've summarized, repeated or redundant dumps, data written to disk, status/log output that only confirmed an expected state.
|
|
192201
|
+
Keep: user messages, unresolved errors, raw evidence you haven't extracted yet, and outputs whose exact wording may matter later.
|
|
192202
|
+
Never blanket-mark large ranges (e.g. "1-50") — review what each tag holds first.`;
|
|
191469
192203
|
// src/tools/ctx-reduce/tools.ts
|
|
191470
192204
|
import { tool as tool4 } from "@opencode-ai/plugin";
|
|
191471
192205
|
|
|
@@ -192048,6 +192782,9 @@ class MagicContextRpcServer {
|
|
|
192048
192782
|
}
|
|
192049
192783
|
|
|
192050
192784
|
// src/index.ts
|
|
192785
|
+
var HISTORIAN_MAX_STEPS = 40;
|
|
192786
|
+
var SIDEKICK_MAX_STEPS = 40;
|
|
192787
|
+
var DREAMER_MAX_STEPS = 150;
|
|
192051
192788
|
var plugin = async (ctx) => {
|
|
192052
192789
|
const pluginConfig = loadPluginConfig(ctx.directory);
|
|
192053
192790
|
setSqlitePragmaConfig({
|
|
@@ -192264,11 +193001,13 @@ var plugin = async (ctx) => {
|
|
|
192264
193001
|
await hooks.magicContext?.["experimental.text.complete"]?.(input, output);
|
|
192265
193002
|
},
|
|
192266
193003
|
config: async (config2) => {
|
|
192267
|
-
const buildHiddenAgentConfig = (agentId, prompt, allowedTools, overrides) => {
|
|
193004
|
+
const buildHiddenAgentConfig = (agentId, prompt, allowedTools, maxSteps, overrides) => {
|
|
192268
193005
|
const { permission: overridePermission, ...restOverrides } = overrides ?? {};
|
|
192269
193006
|
const basePermission = buildAllowOnlyPermission(allowedTools);
|
|
192270
193007
|
return {
|
|
192271
193008
|
prompt,
|
|
193009
|
+
steps: maxSteps,
|
|
193010
|
+
maxSteps,
|
|
192272
193011
|
...getAgentFallbackModels(agentId) ? { fallback_models: getAgentFallbackModels(agentId) } : {},
|
|
192273
193012
|
...restOverrides,
|
|
192274
193013
|
permission: {
|
|
@@ -192309,10 +193048,10 @@ var plugin = async (ctx) => {
|
|
|
192309
193048
|
})() : undefined;
|
|
192310
193049
|
config2.agent = {
|
|
192311
193050
|
...config2.agent ?? {},
|
|
192312
|
-
[DREAMER_AGENT]: buildHiddenAgentConfig(DREAMER_AGENT, DREAMER_SYSTEM_PROMPT, DREAMER_ALLOWED_TOOLS, dreamerAgentOverrides),
|
|
192313
|
-
[HISTORIAN_AGENT]: buildHiddenAgentConfig(HISTORIAN_AGENT, COMPARTMENT_AGENT_SYSTEM_PROMPT, HISTORIAN_ALLOWED_TOOLS, historianAgentOverrides),
|
|
192314
|
-
[HISTORIAN_EDITOR_AGENT]: buildHiddenAgentConfig(HISTORIAN_EDITOR_AGENT, HISTORIAN_EDITOR_SYSTEM_PROMPT, HISTORIAN_ALLOWED_TOOLS, historianAgentOverrides),
|
|
192315
|
-
[SIDEKICK_AGENT]: buildHiddenAgentConfig(SIDEKICK_AGENT, SIDEKICK_SYSTEM_PROMPT, SIDEKICK_ALLOWED_TOOLS, sidekickAgentOverrides)
|
|
193051
|
+
[DREAMER_AGENT]: buildHiddenAgentConfig(DREAMER_AGENT, DREAMER_SYSTEM_PROMPT, DREAMER_ALLOWED_TOOLS, DREAMER_MAX_STEPS, dreamerAgentOverrides),
|
|
193052
|
+
[HISTORIAN_AGENT]: buildHiddenAgentConfig(HISTORIAN_AGENT, COMPARTMENT_AGENT_SYSTEM_PROMPT, HISTORIAN_ALLOWED_TOOLS, HISTORIAN_MAX_STEPS, historianAgentOverrides),
|
|
193053
|
+
[HISTORIAN_EDITOR_AGENT]: buildHiddenAgentConfig(HISTORIAN_EDITOR_AGENT, HISTORIAN_EDITOR_SYSTEM_PROMPT, HISTORIAN_ALLOWED_TOOLS, HISTORIAN_MAX_STEPS, historianAgentOverrides),
|
|
193054
|
+
[SIDEKICK_AGENT]: buildHiddenAgentConfig(SIDEKICK_AGENT, SIDEKICK_SYSTEM_PROMPT, SIDEKICK_ALLOWED_TOOLS, SIDEKICK_MAX_STEPS, sidekickAgentOverrides)
|
|
192316
193055
|
};
|
|
192317
193056
|
}
|
|
192318
193057
|
};
|