@wolfx/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/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 +102 -11
package/dist/index.js
CHANGED
|
@@ -15565,9 +15565,11 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
|
|
|
15565
15565
|
});
|
|
15566
15566
|
} catch (error51) {
|
|
15567
15567
|
if (signal?.aborted) {
|
|
15568
|
+
await abortChildRun(client, args.path.id);
|
|
15568
15569
|
throw new Error("prompt aborted by external signal");
|
|
15569
15570
|
}
|
|
15570
15571
|
if (controller.signal.aborted) {
|
|
15572
|
+
await abortChildRun(client, args.path.id);
|
|
15571
15573
|
throw new Error(`prompt timed out after ${timeoutMs}ms`);
|
|
15572
15574
|
}
|
|
15573
15575
|
throw error51;
|
|
@@ -15576,6 +15578,13 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
|
|
|
15576
15578
|
signal?.removeEventListener("abort", onExternalAbort);
|
|
15577
15579
|
}
|
|
15578
15580
|
}
|
|
15581
|
+
async function abortChildRun(client, sessionId) {
|
|
15582
|
+
try {
|
|
15583
|
+
await client.session.abort({ path: { id: sessionId } });
|
|
15584
|
+
} catch (error51) {
|
|
15585
|
+
log(`[model-retry] child session abort failed for ${sessionId}: ${String(error51)}`);
|
|
15586
|
+
}
|
|
15587
|
+
}
|
|
15579
15588
|
function isNonRetryable(error51, externalSignal) {
|
|
15580
15589
|
if (externalSignal?.aborted)
|
|
15581
15590
|
return true;
|
|
@@ -15847,7 +15856,7 @@ function isSessionMetaRow(row) {
|
|
|
15847
15856
|
if (row === null || typeof row !== "object")
|
|
15848
15857
|
return false;
|
|
15849
15858
|
const r = row;
|
|
15850
|
-
return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.observed_safe_input_tokens) && isNumberOrNull(r.cache_alert_sent) && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state) && isBlobOrNull(r.cached_m0_bytes) && isBlobOrNull(r.cached_m1_bytes) && isNumberOrNull(r.cached_m0_project_memory_epoch) && isStringOrNull(r.cached_m0_workspace_fingerprint) && isNumberOrNull(r.cached_m0_project_user_profile_version) && isNumberOrNull(r.cached_m0_max_compartment_seq) && isNumberOrNull(r.cached_m0_max_memory_id) && isNumberOrNull(r.cached_m0_max_mutation_id) && isNumberOrNull(r.cached_m0_max_memory_mutation_id) && isStringOrNull(r.cached_m0_project_docs_hash) && isNumberOrNull(r.cached_m0_materialized_at) && isNumberOrNull(r.cached_m0_session_facts_version) && isStringOrNull(r.cached_m0_upgrade_state) && isStringOrNull(r.cached_m0_system_hash) && isStringOrNull(r.cached_m0_tool_set_hash) && isStringOrNull(r.cached_m0_model_key) && isStringOrNull(r.last_observed_model_key) && isNumberOrNull(r.last_usage_context_limit) && isNumberOrNull(r.prior_boundary_ordinal) && isNumberOrNull(r.protected_tail_policy_version) && isNumberOrNull(r.protected_tail_drain_window_started_at) && isNumberOrNull(r.protected_tail_drain_tokens) && isNumberOrNull(r.recovery_no_eligible_head_count) && isNumberOrNull(r.force_emergency_bypass_window_start) && isNumberOrNull(r.force_emergency_bypass_used) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme);
|
|
15859
|
+
return typeof r.session_id === "string" && typeof r.last_response_time === "number" && isStringOrNull(r.cache_ttl) && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && isStringOrNull(r.last_nudge_band) && isStringOrNull(r.last_transform_error) && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && isNumberOrNull(r.observed_safe_input_tokens) && isNumberOrNull(r.cache_alert_sent) && isNumberOrNull(r.times_execute_threshold_reached) && isNumberOrNull(r.compartment_in_progress) && (r.system_prompt_hash === null || typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && isNumberOrNull(r.system_prompt_tokens) && isNumberOrNull(r.conversation_tokens) && isNumberOrNull(r.tool_call_tokens) && isNumberOrNull(r.cleared_reasoning_through_tag) && isStringOrNull(r.last_todo_state) && isBlobOrNull(r.cached_m0_bytes) && isBlobOrNull(r.cached_m1_bytes) && isNumberOrNull(r.cached_m0_project_memory_epoch) && isStringOrNull(r.cached_m0_workspace_fingerprint) && isNumberOrNull(r.cached_m0_project_user_profile_version) && isNumberOrNull(r.cached_m0_max_compartment_seq) && isNumberOrNull(r.cached_m0_max_memory_id) && isNumberOrNull(r.cached_m0_max_mutation_id) && isNumberOrNull(r.cached_m0_max_memory_mutation_id) && isStringOrNull(r.cached_m0_project_docs_hash) && isNumberOrNull(r.cached_m0_materialized_at) && isNumberOrNull(r.cached_m0_session_facts_version) && isStringOrNull(r.cached_m0_upgrade_state) && isStringOrNull(r.cached_m0_system_hash) && isStringOrNull(r.cached_m0_tool_set_hash) && isStringOrNull(r.cached_m0_model_key) && isStringOrNull(r.last_observed_model_key) && isNumberOrNull(r.last_usage_context_limit) && isNumberOrNull(r.prior_boundary_ordinal) && isNumberOrNull(r.protected_tail_policy_version) && isNumberOrNull(r.protected_tail_drain_window_started_at) && isNumberOrNull(r.protected_tail_drain_tokens) && isNumberOrNull(r.recovery_no_eligible_head_count) && isNumberOrNull(r.force_emergency_bypass_window_start) && isNumberOrNull(r.force_emergency_bypass_used) && isNumberOrNull(r.upgrade_reminded_at) && isNumberOrNull(r.pi_stable_id_scheme) && isNumberOrNull(r.tool_reclaim_watermark);
|
|
15851
15860
|
}
|
|
15852
15861
|
function getDefaultSessionMeta(sessionId) {
|
|
15853
15862
|
return {
|
|
@@ -15870,6 +15879,7 @@ function getDefaultSessionMeta(sessionId) {
|
|
|
15870
15879
|
conversationTokens: 0,
|
|
15871
15880
|
toolCallTokens: 0,
|
|
15872
15881
|
clearedReasoningThroughTag: 0,
|
|
15882
|
+
toolReclaimWatermark: 0,
|
|
15873
15883
|
lastTodoState: "",
|
|
15874
15884
|
cachedM0Bytes: null,
|
|
15875
15885
|
cachedM1Bytes: null,
|
|
@@ -15933,6 +15943,7 @@ function toSessionMeta(row) {
|
|
|
15933
15943
|
conversationTokens: numOrZero(row.conversation_tokens),
|
|
15934
15944
|
toolCallTokens: numOrZero(row.tool_call_tokens),
|
|
15935
15945
|
clearedReasoningThroughTag: numOrZero(row.cleared_reasoning_through_tag),
|
|
15946
|
+
toolReclaimWatermark: numOrZero(row.tool_reclaim_watermark),
|
|
15936
15947
|
lastTodoState: lastTodoStateRaw,
|
|
15937
15948
|
cachedM0Bytes: toBufferOrNull(row.cached_m0_bytes),
|
|
15938
15949
|
cachedM1Bytes: toBufferOrNull(row.cached_m1_bytes),
|
|
@@ -16042,6 +16053,7 @@ var init_storage_meta_shared = __esm(() => {
|
|
|
16042
16053
|
"conversation_tokens",
|
|
16043
16054
|
"tool_call_tokens",
|
|
16044
16055
|
"cleared_reasoning_through_tag",
|
|
16056
|
+
"tool_reclaim_watermark",
|
|
16045
16057
|
"last_todo_state",
|
|
16046
16058
|
"cached_m0_bytes",
|
|
16047
16059
|
"cached_m1_bytes",
|
|
@@ -16090,6 +16102,7 @@ var init_storage_meta_shared = __esm(() => {
|
|
|
16090
16102
|
conversationTokens: "conversation_tokens",
|
|
16091
16103
|
toolCallTokens: "tool_call_tokens",
|
|
16092
16104
|
clearedReasoningThroughTag: "cleared_reasoning_through_tag",
|
|
16105
|
+
toolReclaimWatermark: "tool_reclaim_watermark",
|
|
16093
16106
|
lastTodoState: "last_todo_state",
|
|
16094
16107
|
cachedM0Bytes: "cached_m0_bytes",
|
|
16095
16108
|
cachedM1Bytes: "cached_m1_bytes",
|
|
@@ -16366,6 +16379,14 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
|
|
|
16366
16379
|
}
|
|
16367
16380
|
super(typeof filename === "string" ? filename : ":memory:", translated);
|
|
16368
16381
|
}
|
|
16382
|
+
prepare(sql) {
|
|
16383
|
+
const stmt = super.prepare(sql);
|
|
16384
|
+
for (const method of ["run", "get", "all"]) {
|
|
16385
|
+
const original = stmt[method].bind(stmt);
|
|
16386
|
+
stmt[method] = (...args) => args.length === 1 && Array.isArray(args[0]) ? original(...args[0]) : original(...args);
|
|
16387
|
+
}
|
|
16388
|
+
return stmt;
|
|
16389
|
+
}
|
|
16369
16390
|
transaction(fn) {
|
|
16370
16391
|
const self = this;
|
|
16371
16392
|
const wrapped = function(...args) {
|
|
@@ -149342,6 +149363,9 @@ function stripCompleteTagPairsGlobally(value) {
|
|
|
149342
149363
|
function stripMalformedTagNotationGlobally(value) {
|
|
149343
149364
|
return value.replace(MALFORMED_TAG_GLOBAL_REGEX, "");
|
|
149344
149365
|
}
|
|
149366
|
+
function stripDanglingTagNotationGlobally(value) {
|
|
149367
|
+
return value.replace(DANGLING_TAG_GLOBAL_REGEX, "");
|
|
149368
|
+
}
|
|
149345
149369
|
function stripTagSectionCharacters(value) {
|
|
149346
149370
|
return value.replace(STRAY_SECTION_CHAR_REGEX, "");
|
|
149347
149371
|
}
|
|
@@ -149349,6 +149373,7 @@ function stripPersistedAssistantText(value) {
|
|
|
149349
149373
|
let text = stripWellFormedLeadingTagPrefix(value);
|
|
149350
149374
|
text = stripCompleteTagPairsGlobally(text);
|
|
149351
149375
|
text = stripMalformedTagNotationGlobally(text);
|
|
149376
|
+
text = stripDanglingTagNotationGlobally(text);
|
|
149352
149377
|
text = stripTagSectionCharacters(text);
|
|
149353
149378
|
return text.trim();
|
|
149354
149379
|
}
|
|
@@ -149361,6 +149386,7 @@ function stripTagPrefix(value) {
|
|
|
149361
149386
|
const prev = stripped;
|
|
149362
149387
|
stripped = stripped.replace(MALFORMED_TAG_PREFIX_REGEX, "");
|
|
149363
149388
|
stripped = stripped.replace(TAG_PREFIX_REGEX, "");
|
|
149389
|
+
stripped = stripped.replace(DANGLING_TAG_PREFIX_REGEX, "");
|
|
149364
149390
|
if (stripped === prev)
|
|
149365
149391
|
break;
|
|
149366
149392
|
}
|
|
@@ -149382,11 +149408,13 @@ function isThinkingPart(part) {
|
|
|
149382
149408
|
const candidate = part;
|
|
149383
149409
|
return candidate.type === "thinking" || candidate.type === "reasoning";
|
|
149384
149410
|
}
|
|
149385
|
-
var encoder, TAG_PREFIX_REGEX, MALFORMED_TAG_PREFIX_REGEX, COMPLETE_TAG_PAIR_GLOBAL_REGEX, MALFORMED_TAG_GLOBAL_REGEX, STRAY_SECTION_CHAR_REGEX;
|
|
149411
|
+
var encoder, TAG_PREFIX_REGEX, MALFORMED_TAG_PREFIX_REGEX, DANGLING_TAG_GLOBAL_REGEX, DANGLING_TAG_PREFIX_REGEX, COMPLETE_TAG_PAIR_GLOBAL_REGEX, MALFORMED_TAG_GLOBAL_REGEX, STRAY_SECTION_CHAR_REGEX;
|
|
149386
149412
|
var init_tag_content_primitives = __esm(() => {
|
|
149387
149413
|
encoder = new TextEncoder;
|
|
149388
149414
|
TAG_PREFIX_REGEX = /^(?:§\d+§\s*)+/;
|
|
149389
149415
|
MALFORMED_TAG_PREFIX_REGEX = /^(?:§\d+">§(?:\d+§)?\s*)+/;
|
|
149416
|
+
DANGLING_TAG_GLOBAL_REGEX = /\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?/g;
|
|
149417
|
+
DANGLING_TAG_PREFIX_REGEX = /^(?:\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?\s*)+/;
|
|
149390
149418
|
COMPLETE_TAG_PAIR_GLOBAL_REGEX = /\u00a7\d+\u00a7/g;
|
|
149391
149419
|
MALFORMED_TAG_GLOBAL_REGEX = /\u00a7\d+">(?:\u00a7(?:\d+\u00a7)?)?/g;
|
|
149392
149420
|
STRAY_SECTION_CHAR_REGEX = /\u00a7/g;
|
|
@@ -149450,12 +149478,13 @@ function setToolContent(part, content) {
|
|
|
149450
149478
|
part.content = content;
|
|
149451
149479
|
}
|
|
149452
149480
|
}
|
|
149453
|
-
function truncateToolPart(part) {
|
|
149481
|
+
function truncateToolPart(part, tagId) {
|
|
149454
149482
|
if (!isRecord(part))
|
|
149455
149483
|
return;
|
|
149484
|
+
const sentinel = `[dropped §${tagId}§]`;
|
|
149456
149485
|
if (part.type === "tool" && isRecord(part.state)) {
|
|
149457
149486
|
const state = part.state;
|
|
149458
|
-
state.output =
|
|
149487
|
+
state.output = sentinel;
|
|
149459
149488
|
if (isRecord(state.input)) {
|
|
149460
149489
|
const inputSize = estimateInputSize(state.input);
|
|
149461
149490
|
if (inputSize > 500) {
|
|
@@ -149465,7 +149494,7 @@ function truncateToolPart(part) {
|
|
|
149465
149494
|
return;
|
|
149466
149495
|
}
|
|
149467
149496
|
if (part.type === "tool_result") {
|
|
149468
|
-
part.content =
|
|
149497
|
+
part.content = sentinel;
|
|
149469
149498
|
return;
|
|
149470
149499
|
}
|
|
149471
149500
|
if (part.type === "tool-invocation" && isRecord(part.args)) {
|
|
@@ -149582,7 +149611,7 @@ class ToolMutationBatch {
|
|
|
149582
149611
|
this.affectedMessages.clear();
|
|
149583
149612
|
}
|
|
149584
149613
|
}
|
|
149585
|
-
function createToolDropTarget(compositeKey, thinkingParts, index, batch) {
|
|
149614
|
+
function createToolDropTarget(compositeKey, thinkingParts, index, batch, tagId) {
|
|
149586
149615
|
const drop = () => {
|
|
149587
149616
|
const entry = index.get(compositeKey);
|
|
149588
149617
|
if (!entry || entry.occurrences.length === 0)
|
|
@@ -149603,7 +149632,7 @@ function createToolDropTarget(compositeKey, thinkingParts, index, batch) {
|
|
|
149603
149632
|
if (!entry.hasResult)
|
|
149604
149633
|
return "incomplete";
|
|
149605
149634
|
for (const occurrence of entry.occurrences) {
|
|
149606
|
-
truncateToolPart(occurrence.part);
|
|
149635
|
+
truncateToolPart(occurrence.part, tagId);
|
|
149607
149636
|
}
|
|
149608
149637
|
clearThinkingParts(thinkingParts);
|
|
149609
149638
|
return "truncated";
|
|
@@ -151337,6 +151366,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
151337
151366
|
ensureColumn(db, "session_meta", "historian_last_failure_at", "INTEGER DEFAULT NULL");
|
|
151338
151367
|
ensureColumn(db, "session_meta", "system_prompt_hash", "TEXT DEFAULT ''");
|
|
151339
151368
|
ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
|
|
151369
|
+
ensureColumn(db, "session_meta", "tool_reclaim_watermark", "INTEGER DEFAULT 0");
|
|
151340
151370
|
ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
|
|
151341
151371
|
ensureColumn(db, "session_meta", "stale_reduce_stripped_ids", "TEXT DEFAULT ''");
|
|
151342
151372
|
ensureColumn(db, "session_meta", "processed_image_stripped_ids", "TEXT DEFAULT ''");
|
|
@@ -154126,7 +154156,8 @@ var exports_storage_meta_session = {};
|
|
|
154126
154156
|
__export(exports_storage_meta_session, {
|
|
154127
154157
|
updateSessionMeta: () => updateSessionMeta,
|
|
154128
154158
|
getOrCreateSessionMeta: () => getOrCreateSessionMeta,
|
|
154129
|
-
clearSession: () => clearSession
|
|
154159
|
+
clearSession: () => clearSession,
|
|
154160
|
+
advanceToolReclaimWatermark: () => advanceToolReclaimWatermark
|
|
154130
154161
|
});
|
|
154131
154162
|
import { Buffer as Buffer3 } from "node:buffer";
|
|
154132
154163
|
function getSessionMetaSelectColumns(db) {
|
|
@@ -154187,6 +154218,14 @@ function updateSessionMeta(db, sessionId, updates) {
|
|
|
154187
154218
|
db.prepare(`UPDATE session_meta SET ${setClauses.join(", ")} WHERE session_id = ?`).run(...values, sessionId);
|
|
154188
154219
|
})();
|
|
154189
154220
|
}
|
|
154221
|
+
function advanceToolReclaimWatermark(db, sessionId, maxTagNumber) {
|
|
154222
|
+
if (maxTagNumber <= 0)
|
|
154223
|
+
return;
|
|
154224
|
+
db.transaction(() => {
|
|
154225
|
+
ensureSessionMetaRow(db, sessionId);
|
|
154226
|
+
db.prepare("UPDATE session_meta SET tool_reclaim_watermark = MAX(COALESCE(tool_reclaim_watermark, 0), ?) WHERE session_id = ?").run(maxTagNumber, sessionId);
|
|
154227
|
+
})();
|
|
154228
|
+
}
|
|
154190
154229
|
function clearSession(db, sessionId) {
|
|
154191
154230
|
db.transaction(() => {
|
|
154192
154231
|
db.prepare("DELETE FROM pending_ops WHERE session_id = ?").run(sessionId);
|
|
@@ -154225,6 +154264,7 @@ var init_storage_meta_session = __esm(async () => {
|
|
|
154225
154264
|
last_transform_error: "'' AS last_transform_error",
|
|
154226
154265
|
system_prompt_hash: "'' AS system_prompt_hash",
|
|
154227
154266
|
last_todo_state: "'' AS last_todo_state",
|
|
154267
|
+
tool_reclaim_watermark: "0 AS tool_reclaim_watermark",
|
|
154228
154268
|
cached_m0_bytes: "NULL AS cached_m0_bytes",
|
|
154229
154269
|
cached_m1_bytes: "NULL AS cached_m1_bytes",
|
|
154230
154270
|
cached_m0_project_memory_epoch: "NULL AS cached_m0_project_memory_epoch",
|
|
@@ -154609,15 +154649,22 @@ function ownerMessageIdForTagRow(row) {
|
|
|
154609
154649
|
}
|
|
154610
154650
|
return row.message_id.replace(CONTENT_ID_SUFFIX, "");
|
|
154611
154651
|
}
|
|
154612
|
-
function getActiveTagTokenAggregate(db, sessionId) {
|
|
154613
|
-
const
|
|
154652
|
+
function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
|
|
154653
|
+
const toolOutputExpr = protectedTags > 0 ? `COALESCE(SUM(CASE WHEN type = 'tool' AND tag_number < (
|
|
154654
|
+
SELECT tag_number FROM tags
|
|
154655
|
+
WHERE session_id = ? AND status = 'active'
|
|
154656
|
+
ORDER BY tag_number DESC LIMIT 1 OFFSET ?
|
|
154657
|
+
) THEN COALESCE(token_count, 0) ELSE 0 END), 0)` : `COALESCE(SUM(CASE WHEN type = 'tool' THEN COALESCE(token_count, 0) ELSE 0 END), 0)`;
|
|
154658
|
+
const sql = `SELECT
|
|
154614
154659
|
COALESCE(SUM(CASE WHEN type != 'tool' THEN COALESCE(token_count, 0) ELSE 0 END), 0)
|
|
154615
154660
|
+ COALESCE(SUM(COALESCE(reasoning_token_count, 0)), 0) AS conversation,
|
|
154616
154661
|
COALESCE(SUM(CASE WHEN type = 'tool' THEN COALESCE(token_count, 0) + COALESCE(input_token_count, 0) ELSE 0 END), 0) AS tool_call,
|
|
154617
|
-
|
|
154662
|
+
${toolOutputExpr} AS tool_output,
|
|
154618
154663
|
COALESCE(SUM(CASE WHEN token_count IS NULL THEN 1 ELSE 0 END), 0) AS null_count
|
|
154619
154664
|
FROM tags
|
|
154620
|
-
WHERE session_id = ? AND status = 'active'
|
|
154665
|
+
WHERE session_id = ? AND status = 'active'`;
|
|
154666
|
+
const params = protectedTags > 0 ? [sessionId, protectedTags - 1, sessionId] : [sessionId];
|
|
154667
|
+
const row = db.prepare(sql).get(...params);
|
|
154621
154668
|
return {
|
|
154622
154669
|
conversation: row?.conversation ?? 0,
|
|
154623
154670
|
toolCall: row?.tool_call ?? 0,
|
|
@@ -154625,6 +154672,26 @@ function getActiveTagTokenAggregate(db, sessionId) {
|
|
|
154625
154672
|
nullCount: row?.null_count ?? 0
|
|
154626
154673
|
};
|
|
154627
154674
|
}
|
|
154675
|
+
function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, limit = 4) {
|
|
154676
|
+
if (limit <= 0)
|
|
154677
|
+
return [];
|
|
154678
|
+
const boundedLimit = Math.max(1, Math.min(10, Math.floor(limit)));
|
|
154679
|
+
const whereProtected = protectedTags > 0 ? `AND tag_number < (
|
|
154680
|
+
SELECT tag_number FROM tags
|
|
154681
|
+
WHERE session_id = ? AND status = 'active'
|
|
154682
|
+
ORDER BY tag_number DESC LIMIT 1 OFFSET ?
|
|
154683
|
+
)` : "";
|
|
154684
|
+
const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
|
|
154685
|
+
const rows = db.prepare(`SELECT tag_number, tool_name
|
|
154686
|
+
FROM tags
|
|
154687
|
+
WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
|
|
154688
|
+
ORDER BY tag_number ASC, id ASC
|
|
154689
|
+
LIMIT ?`).all(...params);
|
|
154690
|
+
return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
|
|
154691
|
+
tagNumber: row.tag_number,
|
|
154692
|
+
toolName: typeof row.tool_name === "string" ? row.tool_name : null
|
|
154693
|
+
}));
|
|
154694
|
+
}
|
|
154628
154695
|
function getTriggerTagTokenUpperBound(db, sessionId) {
|
|
154629
154696
|
const row = db.prepare(`SELECT
|
|
154630
154697
|
COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
|
|
@@ -156211,6 +156278,58 @@ var init_safe_notification_target = __esm(() => {
|
|
|
156211
156278
|
DEFAULT_TITLE_RE = /^(New session - |Child session - )\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
|
|
156212
156279
|
});
|
|
156213
156280
|
|
|
156281
|
+
// src/shared/rpc-notifications.ts
|
|
156282
|
+
var exports_rpc_notifications = {};
|
|
156283
|
+
__export(exports_rpc_notifications, {
|
|
156284
|
+
pushNotification: () => pushNotification,
|
|
156285
|
+
isTuiConnected: () => isTuiConnected,
|
|
156286
|
+
drainNotifications: () => drainNotifications
|
|
156287
|
+
});
|
|
156288
|
+
function pushNotification(type, payload, sessionId) {
|
|
156289
|
+
queue.push({ id: nextNotificationId++, type, payload, sessionId });
|
|
156290
|
+
if (queue.length > 100) {
|
|
156291
|
+
const newestPerSession = new Map;
|
|
156292
|
+
for (const n of queue) {
|
|
156293
|
+
const prev = newestPerSession.get(n.sessionId);
|
|
156294
|
+
if (prev === undefined || n.id > prev) {
|
|
156295
|
+
newestPerSession.set(n.sessionId, n.id);
|
|
156296
|
+
}
|
|
156297
|
+
}
|
|
156298
|
+
const mustKeep = new Set(newestPerSession.values());
|
|
156299
|
+
const byNewest = [...queue].sort((a, b) => b.id - a.id);
|
|
156300
|
+
const kept = [];
|
|
156301
|
+
for (const n of byNewest) {
|
|
156302
|
+
if (kept.length < 50 || mustKeep.has(n.id))
|
|
156303
|
+
kept.push(n);
|
|
156304
|
+
}
|
|
156305
|
+
queue = kept.sort((a, b) => a.id - b.id);
|
|
156306
|
+
}
|
|
156307
|
+
}
|
|
156308
|
+
function drainNotifications(lastReceivedId = 0, sessionId) {
|
|
156309
|
+
const now = Date.now();
|
|
156310
|
+
lastDrainAtAny = now;
|
|
156311
|
+
if (sessionId !== undefined)
|
|
156312
|
+
lastDrainAtBySession.set(sessionId, now);
|
|
156313
|
+
const matchesClient = (notification) => sessionId === undefined || notification.sessionId === undefined || notification.sessionId === sessionId;
|
|
156314
|
+
if (lastReceivedId > 0) {
|
|
156315
|
+
queue = queue.filter((notification) => !(notification.id <= lastReceivedId && matchesClient(notification)));
|
|
156316
|
+
}
|
|
156317
|
+
return queue.filter((notification) => notification.id > lastReceivedId && matchesClient(notification));
|
|
156318
|
+
}
|
|
156319
|
+
function isTuiConnected(sessionId) {
|
|
156320
|
+
const now = Date.now();
|
|
156321
|
+
if (sessionId !== undefined) {
|
|
156322
|
+
const at = lastDrainAtBySession.get(sessionId) ?? 0;
|
|
156323
|
+
return at > 0 && now - at < TUI_CONNECTED_WINDOW_MS;
|
|
156324
|
+
}
|
|
156325
|
+
return lastDrainAtAny > 0 && now - lastDrainAtAny < TUI_CONNECTED_WINDOW_MS;
|
|
156326
|
+
}
|
|
156327
|
+
var queue, nextNotificationId = 1, lastDrainAtBySession, lastDrainAtAny = 0, TUI_CONNECTED_WINDOW_MS = 3000;
|
|
156328
|
+
var init_rpc_notifications = __esm(() => {
|
|
156329
|
+
queue = [];
|
|
156330
|
+
lastDrainAtBySession = new Map;
|
|
156331
|
+
});
|
|
156332
|
+
|
|
156214
156333
|
// src/plugin/conflict-warning-hook.ts
|
|
156215
156334
|
var exports_conflict_warning_hook = {};
|
|
156216
156335
|
__export(exports_conflict_warning_hook, {
|
|
@@ -156545,6 +156664,9 @@ async function sendStartupAnnouncement(client, directory, version2, features, fo
|
|
|
156545
156664
|
if (!sessionId) {
|
|
156546
156665
|
return;
|
|
156547
156666
|
}
|
|
156667
|
+
const { isTuiConnected: isTuiConnected2 } = await Promise.resolve().then(() => (init_rpc_notifications(), exports_rpc_notifications));
|
|
156668
|
+
if (isTuiConnected2(sessionId) || isTuiConnected2())
|
|
156669
|
+
return;
|
|
156548
156670
|
if (await waitForSafeNotificationTarget(client, sessionId) === "skip")
|
|
156549
156671
|
return;
|
|
156550
156672
|
const bullets = features.map((line) => ` • ${line}`).join(`
|
|
@@ -164524,6 +164646,20 @@ function getDistinctStoredModelIds(db, projectPath) {
|
|
|
164524
164646
|
const rows = getDistinctStoredModelIdsStatement(db).all(projectPath);
|
|
164525
164647
|
return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
|
|
164526
164648
|
}
|
|
164649
|
+
function getMemoryEmbedCoverage(db, projectPath, modelId) {
|
|
164650
|
+
const row = db.prepare(`SELECT
|
|
164651
|
+
COUNT(*) AS total,
|
|
164652
|
+
SUM(CASE WHEN EXISTS (
|
|
164653
|
+
SELECT 1 FROM memory_embeddings e
|
|
164654
|
+
WHERE e.memory_id = m.id AND e.model_id = ?
|
|
164655
|
+
) THEN 1 ELSE 0 END) AS embedded
|
|
164656
|
+
FROM memories m
|
|
164657
|
+
WHERE m.project_path = ? AND m.status = 'active'`).get(modelId, projectPath);
|
|
164658
|
+
return {
|
|
164659
|
+
total: typeof row?.total === "number" ? row.total : 0,
|
|
164660
|
+
embedded: typeof row?.embedded === "number" ? row.embedded : 0
|
|
164661
|
+
};
|
|
164662
|
+
}
|
|
164527
164663
|
var saveEmbeddingStatements, loadAllEmbeddingsStatements, deleteEmbeddingStatements, getStoredModelIdStatements, clearAllEmbeddingsStatements, getDistinctStoredModelIdsStatements;
|
|
164528
164664
|
var init_storage_memory_embeddings = __esm(() => {
|
|
164529
164665
|
saveEmbeddingStatements = new WeakMap;
|
|
@@ -165372,9 +165508,10 @@ function chunkCanonicalText(canonicalText, startOrdinal, endOrdinal, maxInputTok
|
|
|
165372
165508
|
if (lines.length === 0 || endOrdinal < startOrdinal)
|
|
165373
165509
|
return [];
|
|
165374
165510
|
const normalizedMax = normalizeCompartmentChunkMaxInputTokens(maxInputTokens);
|
|
165511
|
+
const effectiveMax = Math.max(1, Math.floor(normalizedMax * CHUNK_WINDOW_SAFETY_RATIO));
|
|
165375
165512
|
const fullText = lines.join(`
|
|
165376
165513
|
`);
|
|
165377
|
-
if (estimateTokens(fullText) <=
|
|
165514
|
+
if (estimateTokens(fullText) <= effectiveMax) {
|
|
165378
165515
|
return [
|
|
165379
165516
|
{
|
|
165380
165517
|
windowIndex: 0,
|
|
@@ -165412,7 +165549,7 @@ function chunkCanonicalText(canonicalText, startOrdinal, endOrdinal, maxInputTok
|
|
|
165412
165549
|
const lineStart = range?.start ?? startOrdinal;
|
|
165413
165550
|
const lineEnd = range?.end ?? lineStart;
|
|
165414
165551
|
const lineTokens = estimateTokens(line);
|
|
165415
|
-
if (currentLines.length > 0 && currentTokens + lineTokens >
|
|
165552
|
+
if (currentLines.length > 0 && currentTokens + lineTokens > effectiveMax) {
|
|
165416
165553
|
flush2();
|
|
165417
165554
|
}
|
|
165418
165555
|
if (currentLines.length === 0) {
|
|
@@ -165567,7 +165704,29 @@ function countUnembeddedSessionCompartments(db, projectPath, sessionId, modelId)
|
|
|
165567
165704
|
)`).get(projectPath, sessionId, projectPath, modelId);
|
|
165568
165705
|
return typeof row?.n === "number" ? row.n : 0;
|
|
165569
165706
|
}
|
|
165570
|
-
|
|
165707
|
+
function countSessionCompartmentEmbedCoverage(db, projectPath, sessionId, modelId) {
|
|
165708
|
+
const row = db.prepare(`SELECT
|
|
165709
|
+
COUNT(*) AS total,
|
|
165710
|
+
SUM(CASE WHEN EXISTS (
|
|
165711
|
+
SELECT 1 FROM compartment_chunk_embeddings e
|
|
165712
|
+
WHERE e.compartment_id = c.id
|
|
165713
|
+
AND e.project_path = ?
|
|
165714
|
+
AND e.model_id = ?
|
|
165715
|
+
) THEN 1 ELSE 0 END) AS embedded
|
|
165716
|
+
FROM compartments c
|
|
165717
|
+
JOIN session_projects sp
|
|
165718
|
+
ON sp.session_id = c.session_id
|
|
165719
|
+
AND sp.harness = c.harness
|
|
165720
|
+
AND sp.project_path = ?
|
|
165721
|
+
WHERE c.session_id = ?
|
|
165722
|
+
AND c.start_message IS NOT NULL
|
|
165723
|
+
AND c.end_message IS NOT NULL`).get(projectPath, modelId, projectPath, sessionId);
|
|
165724
|
+
return {
|
|
165725
|
+
total: typeof row?.total === "number" ? row.total : 0,
|
|
165726
|
+
embedded: typeof row?.embedded === "number" ? row.embedded : 0
|
|
165727
|
+
};
|
|
165728
|
+
}
|
|
165729
|
+
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;
|
|
165571
165730
|
var init_compartment_chunk_embedding = __esm(() => {
|
|
165572
165731
|
init_read_session_formatting();
|
|
165573
165732
|
loadFtsRowsStatements = new WeakMap;
|
|
@@ -165724,6 +165883,18 @@ async function withQuietConsole(fn) {
|
|
|
165724
165883
|
console.error = origError;
|
|
165725
165884
|
}
|
|
165726
165885
|
}
|
|
165886
|
+
function isNativeRuntimeMissingError(error51) {
|
|
165887
|
+
const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
|
|
165888
|
+
const lower = message.toLowerCase();
|
|
165889
|
+
const code = error51?.code;
|
|
165890
|
+
const name2 = error51?.name;
|
|
165891
|
+
if (code === "ERR_DLOPEN_FAILED" && lower.includes("onnxruntime")) {
|
|
165892
|
+
return true;
|
|
165893
|
+
}
|
|
165894
|
+
if (!lower.includes("onnxruntime-node"))
|
|
165895
|
+
return false;
|
|
165896
|
+
return code === "ERR_MODULE_NOT_FOUND" || name2 === "ResolveMessage" || lower.includes("cannot find package") || lower.includes("cannot find module") || lower.includes("err_module_not_found");
|
|
165897
|
+
}
|
|
165727
165898
|
function isTransientLoadError(error51) {
|
|
165728
165899
|
const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
|
|
165729
165900
|
if (!message)
|
|
@@ -165788,6 +165959,9 @@ class LocalEmbeddingProvider {
|
|
|
165788
165959
|
if (this.pipeline) {
|
|
165789
165960
|
return true;
|
|
165790
165961
|
}
|
|
165962
|
+
if (nativeRuntimeMissing) {
|
|
165963
|
+
return false;
|
|
165964
|
+
}
|
|
165791
165965
|
if (this.initPromise) {
|
|
165792
165966
|
await this.initPromise;
|
|
165793
165967
|
return this.pipeline !== null;
|
|
@@ -165854,7 +166028,12 @@ class LocalEmbeddingProvider {
|
|
|
165854
166028
|
await releaseLock();
|
|
165855
166029
|
}
|
|
165856
166030
|
} catch (error51) {
|
|
165857
|
-
|
|
166031
|
+
if (isNativeRuntimeMissingError(error51)) {
|
|
166032
|
+
nativeRuntimeMissing = true;
|
|
166033
|
+
log("[magic-context] local embedding runtime is not installed (onnxruntime-node missing from this install). Local embeddings are disabled. Fix: reinstall the plugin (run `npx @wolfx/magic-context@latest doctor --force`), or configure an `openai-compatible`/`ollama` embedding endpoint instead. Existing memories are unaffected.");
|
|
166034
|
+
} else {
|
|
166035
|
+
log("[magic-context] embedding model failed to load:", error51);
|
|
166036
|
+
}
|
|
165858
166037
|
this.pipeline = null;
|
|
165859
166038
|
} finally {
|
|
165860
166039
|
this.initPromise = null;
|
|
@@ -165965,7 +166144,7 @@ class LocalEmbeddingProvider {
|
|
|
165965
166144
|
return this.pipeline !== null;
|
|
165966
166145
|
}
|
|
165967
166146
|
}
|
|
165968
|
-
var LOCK_POLL_MS = 150, STALE_LOCK_MS, MAX_LOCK_WAIT_MS;
|
|
166147
|
+
var LOCK_POLL_MS = 150, STALE_LOCK_MS, MAX_LOCK_WAIT_MS, nativeRuntimeMissing = false;
|
|
165969
166148
|
var init_embedding_local = __esm(() => {
|
|
165970
166149
|
init_magic_context();
|
|
165971
166150
|
init_data_path();
|
|
@@ -166035,6 +166214,13 @@ var init_embedding_ssrf = __esm(() => {
|
|
|
166035
166214
|
function normalizeEndpoint3(endpoint) {
|
|
166036
166215
|
return endpoint?.trim().replace(/\/+$/, "") ?? "";
|
|
166037
166216
|
}
|
|
166217
|
+
function embeddingModelsMatch(served, requested) {
|
|
166218
|
+
const a = served.trim().toLowerCase();
|
|
166219
|
+
const b = requested.trim().toLowerCase();
|
|
166220
|
+
if (a.length === 0 || b.length === 0)
|
|
166221
|
+
return true;
|
|
166222
|
+
return a === b || a.includes(b) || b.includes(a);
|
|
166223
|
+
}
|
|
166038
166224
|
|
|
166039
166225
|
class OpenAICompatibleEmbeddingProvider {
|
|
166040
166226
|
modelId;
|
|
@@ -166048,6 +166234,7 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
166048
166234
|
failureTimes = [];
|
|
166049
166235
|
circuitOpenUntil = 0;
|
|
166050
166236
|
openLogged = false;
|
|
166237
|
+
modelMismatchLogged = false;
|
|
166051
166238
|
halfOpenProbeInFlight = false;
|
|
166052
166239
|
constructor(options) {
|
|
166053
166240
|
this.endpoint = normalizeEndpoint3(options.endpoint);
|
|
@@ -166146,6 +166333,15 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
166146
166333
|
this.recordFailure(isProbe);
|
|
166147
166334
|
return Array.from({ length: texts.length }, () => null);
|
|
166148
166335
|
}
|
|
166336
|
+
const servedModel = typeof body.model === "string" ? body.model : "";
|
|
166337
|
+
if (this.model && servedModel && !embeddingModelsMatch(servedModel, this.model)) {
|
|
166338
|
+
if (!this.modelMismatchLogged) {
|
|
166339
|
+
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.`);
|
|
166340
|
+
this.modelMismatchLogged = true;
|
|
166341
|
+
}
|
|
166342
|
+
this.recordFailure(isProbe);
|
|
166343
|
+
return Array.from({ length: texts.length }, () => null);
|
|
166344
|
+
}
|
|
166149
166345
|
const items = Array.isArray(body.data) ? body.data : [];
|
|
166150
166346
|
const results = Array.from({ length: texts.length }, (_, index) => {
|
|
166151
166347
|
const embedding = items[index]?.embedding;
|
|
@@ -166362,6 +166558,121 @@ var init_storage_git_commit_embeddings = __esm(() => {
|
|
|
166362
166558
|
distinctModelIdStatements = new WeakMap;
|
|
166363
166559
|
});
|
|
166364
166560
|
|
|
166561
|
+
// src/features/magic-context/git-commits/storage-git-commits.ts
|
|
166562
|
+
function getInsertStatement(db) {
|
|
166563
|
+
let stmt = insertStatements.get(db);
|
|
166564
|
+
if (!stmt) {
|
|
166565
|
+
stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
|
|
166566
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
166567
|
+
ON CONFLICT(sha) DO UPDATE SET
|
|
166568
|
+
project_path = excluded.project_path,
|
|
166569
|
+
short_sha = excluded.short_sha,
|
|
166570
|
+
message = excluded.message,
|
|
166571
|
+
author = excluded.author,
|
|
166572
|
+
committed_at = excluded.committed_at,
|
|
166573
|
+
indexed_at = excluded.indexed_at
|
|
166574
|
+
WHERE git_commits.message != excluded.message`);
|
|
166575
|
+
insertStatements.set(db, stmt);
|
|
166576
|
+
}
|
|
166577
|
+
return stmt;
|
|
166578
|
+
}
|
|
166579
|
+
function getExistingShasStatement(db) {
|
|
166580
|
+
let stmt = existingShasStatements.get(db);
|
|
166581
|
+
if (!stmt) {
|
|
166582
|
+
stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
|
|
166583
|
+
existingShasStatements.set(db, stmt);
|
|
166584
|
+
}
|
|
166585
|
+
return stmt;
|
|
166586
|
+
}
|
|
166587
|
+
function getProjectCountStatement(db) {
|
|
166588
|
+
let stmt = projectCountStatements.get(db);
|
|
166589
|
+
if (!stmt) {
|
|
166590
|
+
stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
|
|
166591
|
+
projectCountStatements.set(db, stmt);
|
|
166592
|
+
}
|
|
166593
|
+
return stmt;
|
|
166594
|
+
}
|
|
166595
|
+
function getLatestCommitTimeStatement(db) {
|
|
166596
|
+
let stmt = latestCommitTimeStatements.get(db);
|
|
166597
|
+
if (!stmt) {
|
|
166598
|
+
stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
|
|
166599
|
+
latestCommitTimeStatements.set(db, stmt);
|
|
166600
|
+
}
|
|
166601
|
+
return stmt;
|
|
166602
|
+
}
|
|
166603
|
+
function getEvictOverflowStatement(db) {
|
|
166604
|
+
let stmt = evictOverflowStatements.get(db);
|
|
166605
|
+
if (!stmt) {
|
|
166606
|
+
stmt = db.prepare(`DELETE FROM git_commits
|
|
166607
|
+
WHERE rowid IN (
|
|
166608
|
+
SELECT rowid FROM git_commits
|
|
166609
|
+
WHERE project_path = ?
|
|
166610
|
+
ORDER BY committed_at DESC, sha DESC
|
|
166611
|
+
LIMIT -1 OFFSET ?
|
|
166612
|
+
)`);
|
|
166613
|
+
evictOverflowStatements.set(db, stmt);
|
|
166614
|
+
}
|
|
166615
|
+
return stmt;
|
|
166616
|
+
}
|
|
166617
|
+
function upsertCommits(db, projectPath, commits) {
|
|
166618
|
+
if (commits.length === 0)
|
|
166619
|
+
return { inserted: 0, updated: 0 };
|
|
166620
|
+
const existing = new Set;
|
|
166621
|
+
for (const row of getExistingShasStatement(db).all(projectPath)) {
|
|
166622
|
+
existing.add(row.sha);
|
|
166623
|
+
}
|
|
166624
|
+
let inserted = 0;
|
|
166625
|
+
let updated = 0;
|
|
166626
|
+
const now = Date.now();
|
|
166627
|
+
const insertStmt = getInsertStatement(db);
|
|
166628
|
+
db.transaction(() => {
|
|
166629
|
+
for (const commit of commits) {
|
|
166630
|
+
const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
|
|
166631
|
+
if (result.changes > 0) {
|
|
166632
|
+
if (existing.has(commit.sha)) {
|
|
166633
|
+
updated++;
|
|
166634
|
+
} else {
|
|
166635
|
+
inserted++;
|
|
166636
|
+
existing.add(commit.sha);
|
|
166637
|
+
}
|
|
166638
|
+
}
|
|
166639
|
+
}
|
|
166640
|
+
})();
|
|
166641
|
+
return { inserted, updated };
|
|
166642
|
+
}
|
|
166643
|
+
function getCommitCount(db, projectPath) {
|
|
166644
|
+
const row = getProjectCountStatement(db).get(projectPath);
|
|
166645
|
+
return row?.count ?? 0;
|
|
166646
|
+
}
|
|
166647
|
+
function getLatestIndexedCommitTimeMs(db, projectPath) {
|
|
166648
|
+
const row = getLatestCommitTimeStatement(db).get(projectPath);
|
|
166649
|
+
return row?.latest ?? null;
|
|
166650
|
+
}
|
|
166651
|
+
function enforceProjectCap(db, projectPath, maxCommits) {
|
|
166652
|
+
if (maxCommits <= 0)
|
|
166653
|
+
return 0;
|
|
166654
|
+
const count = getCommitCount(db, projectPath);
|
|
166655
|
+
if (count <= maxCommits)
|
|
166656
|
+
return 0;
|
|
166657
|
+
getEvictOverflowStatement(db).run(projectPath, maxCommits);
|
|
166658
|
+
const after = getCommitCount(db, projectPath);
|
|
166659
|
+
const evicted = Math.max(0, count - after);
|
|
166660
|
+
if (evicted > 0) {
|
|
166661
|
+
log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
|
|
166662
|
+
}
|
|
166663
|
+
return evicted;
|
|
166664
|
+
}
|
|
166665
|
+
var insertStatements, existingShasStatements, projectCountStatements, evictStatements, evictOverflowStatements, latestCommitTimeStatements;
|
|
166666
|
+
var init_storage_git_commits = __esm(() => {
|
|
166667
|
+
init_logger();
|
|
166668
|
+
insertStatements = new WeakMap;
|
|
166669
|
+
existingShasStatements = new WeakMap;
|
|
166670
|
+
projectCountStatements = new WeakMap;
|
|
166671
|
+
evictStatements = new WeakMap;
|
|
166672
|
+
evictOverflowStatements = new WeakMap;
|
|
166673
|
+
latestCommitTimeStatements = new WeakMap;
|
|
166674
|
+
});
|
|
166675
|
+
|
|
166365
166676
|
// src/features/magic-context/git-commits/sweep-coordinator.ts
|
|
166366
166677
|
function runImmediate2(db, body) {
|
|
166367
166678
|
db.exec("BEGIN IMMEDIATE");
|
|
@@ -166632,7 +166943,7 @@ function getChunkEmbeddingModelId(config2, providerIdentity) {
|
|
|
166632
166943
|
}
|
|
166633
166944
|
const chunkIdentity = {
|
|
166634
166945
|
providerIdentity,
|
|
166635
|
-
chunkerVersion:
|
|
166946
|
+
chunkerVersion: 2,
|
|
166636
166947
|
maxInputTokens: normalizeCompartmentChunkMaxInputTokens("max_input_tokens" in config2 ? config2.max_input_tokens : undefined),
|
|
166637
166948
|
truncate: config2.provider === "openai-compatible" ? config2.truncate ?? "" : ""
|
|
166638
166949
|
};
|
|
@@ -166655,7 +166966,9 @@ function snapshotFor(registration) {
|
|
|
166655
166966
|
enabled,
|
|
166656
166967
|
gitCommitEnabled,
|
|
166657
166968
|
modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
|
|
166658
|
-
chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId
|
|
166969
|
+
chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId,
|
|
166970
|
+
model: registration.observationMode || !providerIsOn ? "off" : ("model" in registration.config) && registration.config.model.trim() ? registration.config.model.trim() : registration.modelId,
|
|
166971
|
+
provider: registration.observationMode || !providerIsOn ? "off" : registration.config.provider ?? "local"
|
|
166659
166972
|
};
|
|
166660
166973
|
}
|
|
166661
166974
|
function disposeProvider(provider) {
|
|
@@ -166866,8 +167179,9 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
166866
167179
|
}
|
|
166867
167180
|
async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates, signal) {
|
|
166868
167181
|
const noWork = [];
|
|
167182
|
+
const failed = [];
|
|
166869
167183
|
if (candidates.length === 0)
|
|
166870
|
-
return { embedded: 0, noWork };
|
|
167184
|
+
return { embedded: 0, noWork, failed };
|
|
166871
167185
|
const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
|
|
166872
167186
|
const prepared = [];
|
|
166873
167187
|
for (const candidate of candidates) {
|
|
@@ -166884,7 +167198,7 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
166884
167198
|
prepared.push({ candidate, windows });
|
|
166885
167199
|
}
|
|
166886
167200
|
if (prepared.length === 0)
|
|
166887
|
-
return { embedded: 0, noWork };
|
|
167201
|
+
return { embedded: 0, noWork, failed };
|
|
166888
167202
|
let embedded = 0;
|
|
166889
167203
|
let i = 0;
|
|
166890
167204
|
while (i < prepared.length) {
|
|
@@ -166901,35 +167215,60 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
166901
167215
|
const texts = [];
|
|
166902
167216
|
for (const item of slice)
|
|
166903
167217
|
texts.push(...item.windows.map((w) => w.text));
|
|
166904
|
-
|
|
166905
|
-
|
|
166906
|
-
if (!result)
|
|
166907
|
-
continue;
|
|
167218
|
+
const persistedIds = new Set;
|
|
167219
|
+
for (let attempt = 0;attempt < EMBED_SLICE_RETRY_ATTEMPTS; attempt++) {
|
|
166908
167220
|
if (signal?.aborted)
|
|
166909
167221
|
break;
|
|
166910
|
-
let
|
|
166911
|
-
|
|
166912
|
-
|
|
166913
|
-
|
|
166914
|
-
|
|
166915
|
-
|
|
167222
|
+
let result = null;
|
|
167223
|
+
const attemptStart = Date.now();
|
|
167224
|
+
try {
|
|
167225
|
+
result = await embedBatchForProject(projectIdentity, texts, signal);
|
|
167226
|
+
} catch (error51) {
|
|
167227
|
+
log("[magic-context] failed to proactively embed compartment chunks:", error51);
|
|
167228
|
+
}
|
|
167229
|
+
if (signal?.aborted)
|
|
167230
|
+
break;
|
|
167231
|
+
if (result) {
|
|
167232
|
+
let offset = 0;
|
|
167233
|
+
for (const item of slice) {
|
|
167234
|
+
const vectors = result.vectors.slice(offset, offset + item.windows.length);
|
|
167235
|
+
offset += item.windows.length;
|
|
167236
|
+
if (persistedIds.has(item.candidate.id))
|
|
167237
|
+
continue;
|
|
167238
|
+
if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
|
|
167239
|
+
continue;
|
|
167240
|
+
}
|
|
167241
|
+
const rows = item.windows.map((window, index) => ({
|
|
167242
|
+
compartmentId: item.candidate.id,
|
|
167243
|
+
sessionId: item.candidate.sessionId,
|
|
167244
|
+
projectPath: projectIdentity,
|
|
167245
|
+
window,
|
|
167246
|
+
modelId,
|
|
167247
|
+
vector: vectors[index]
|
|
167248
|
+
}));
|
|
167249
|
+
replaceCompartmentChunkEmbeddings(db, rows);
|
|
167250
|
+
persistedIds.add(item.candidate.id);
|
|
166916
167251
|
}
|
|
166917
|
-
const rows = item.windows.map((window, index) => ({
|
|
166918
|
-
compartmentId: item.candidate.id,
|
|
166919
|
-
sessionId: item.candidate.sessionId,
|
|
166920
|
-
projectPath: projectIdentity,
|
|
166921
|
-
window,
|
|
166922
|
-
modelId,
|
|
166923
|
-
vector: vectors[index]
|
|
166924
|
-
}));
|
|
166925
|
-
replaceCompartmentChunkEmbeddings(db, rows);
|
|
166926
|
-
embedded += 1;
|
|
166927
167252
|
}
|
|
166928
|
-
|
|
166929
|
-
|
|
167253
|
+
if (persistedIds.size === slice.length)
|
|
167254
|
+
break;
|
|
167255
|
+
if (persistedIds.size > 0)
|
|
167256
|
+
break;
|
|
167257
|
+
if (Date.now() - attemptStart >= EMBED_SLOW_FAILURE_NO_RETRY_MS)
|
|
167258
|
+
break;
|
|
167259
|
+
if (attempt < EMBED_SLICE_RETRY_ATTEMPTS - 1) {
|
|
167260
|
+
await new Promise((resolve6) => setTimeout(resolve6, EMBED_SLICE_RETRY_BASE_MS * 2 ** attempt));
|
|
167261
|
+
}
|
|
167262
|
+
}
|
|
167263
|
+
embedded += persistedIds.size;
|
|
167264
|
+
if (!signal?.aborted) {
|
|
167265
|
+
for (const item of slice) {
|
|
167266
|
+
if (!persistedIds.has(item.candidate.id))
|
|
167267
|
+
failed.push(item.candidate.id);
|
|
167268
|
+
}
|
|
166930
167269
|
}
|
|
166931
167270
|
}
|
|
166932
|
-
return { embedded, noWork };
|
|
167271
|
+
return { embedded, noWork, failed };
|
|
166933
167272
|
}
|
|
166934
167273
|
async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, options) {
|
|
166935
167274
|
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
@@ -166952,9 +167291,11 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
166952
167291
|
renewal.unref?.();
|
|
166953
167292
|
const batchSize = Math.max(1, options?.batchSize ?? CHUNK_DRAIN_BATCH_SIZE);
|
|
166954
167293
|
const skipIds = [];
|
|
167294
|
+
const failedIds = [];
|
|
166955
167295
|
let embedded = 0;
|
|
166956
167296
|
let aborted2 = false;
|
|
166957
|
-
let
|
|
167297
|
+
let providerDown = false;
|
|
167298
|
+
let consecutiveFailedBatches = 0;
|
|
166958
167299
|
try {
|
|
166959
167300
|
options?.onProgress?.({ embedded, total });
|
|
166960
167301
|
for (;; ) {
|
|
@@ -166962,15 +167303,26 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
166962
167303
|
aborted2 = true;
|
|
166963
167304
|
break;
|
|
166964
167305
|
}
|
|
166965
|
-
const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, skipIds);
|
|
167306
|
+
const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, [...skipIds, ...failedIds]);
|
|
166966
167307
|
if (candidates.length === 0)
|
|
166967
167308
|
break;
|
|
166968
|
-
const {
|
|
167309
|
+
const {
|
|
167310
|
+
embedded: n,
|
|
167311
|
+
noWork,
|
|
167312
|
+
failed
|
|
167313
|
+
} = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
|
|
166969
167314
|
for (const id of noWork)
|
|
166970
167315
|
skipIds.push(id);
|
|
167316
|
+
for (const id of failed)
|
|
167317
|
+
failedIds.push(id);
|
|
166971
167318
|
if (n === 0 && noWork.length === 0) {
|
|
166972
|
-
|
|
166973
|
-
|
|
167319
|
+
consecutiveFailedBatches += 1;
|
|
167320
|
+
if (consecutiveFailedBatches >= MAX_CONSECUTIVE_FAILED_BATCHES) {
|
|
167321
|
+
providerDown = true;
|
|
167322
|
+
break;
|
|
167323
|
+
}
|
|
167324
|
+
} else {
|
|
167325
|
+
consecutiveFailedBatches = 0;
|
|
166974
167326
|
}
|
|
166975
167327
|
embedded += n;
|
|
166976
167328
|
options?.onProgress?.({ embedded: Math.min(embedded, total), total });
|
|
@@ -166978,23 +167330,58 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
166978
167330
|
}
|
|
166979
167331
|
} finally {
|
|
166980
167332
|
clearInterval(renewal);
|
|
166981
|
-
|
|
167333
|
+
try {
|
|
167334
|
+
releaseGitSweepLease(db, projectIdentity, holderId);
|
|
167335
|
+
} catch (error51) {
|
|
167336
|
+
log("[magic-context] embed drain: lease release failed (will TTL-expire):", error51);
|
|
167337
|
+
}
|
|
166982
167338
|
}
|
|
166983
167339
|
if (aborted2)
|
|
166984
|
-
return { status: "aborted", embedded, total };
|
|
166985
|
-
if (
|
|
167340
|
+
return { status: "aborted", embedded, total, failed: failedIds.length };
|
|
167341
|
+
if (providerDown || failedIds.length > 0) {
|
|
166986
167342
|
const remaining = Math.max(0, countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId) - skipIds.length);
|
|
166987
|
-
if (remaining > 0)
|
|
166988
|
-
return { status: "stalled", embedded, total, remaining };
|
|
167343
|
+
if (remaining > 0) {
|
|
167344
|
+
return { status: "stalled", embedded, total, remaining, failed: failedIds.length };
|
|
167345
|
+
}
|
|
166989
167346
|
}
|
|
166990
|
-
return { status: "done", embedded, total };
|
|
167347
|
+
return { status: "done", embedded, total, failed: failedIds.length };
|
|
167348
|
+
}
|
|
167349
|
+
function getEmbeddingCoverageStatus(db, projectIdentity, sessionId) {
|
|
167350
|
+
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
167351
|
+
if (!snapshot?.enabled || snapshot.chunkModelId === "off") {
|
|
167352
|
+
return {
|
|
167353
|
+
enabled: false,
|
|
167354
|
+
model: snapshot?.model ?? "off",
|
|
167355
|
+
provider: snapshot?.provider ?? "off",
|
|
167356
|
+
session: { embedded: 0, total: 0 },
|
|
167357
|
+
memories: { embedded: 0, total: 0 },
|
|
167358
|
+
commits: { embedded: 0, total: 0, gitEnabled: false }
|
|
167359
|
+
};
|
|
167360
|
+
}
|
|
167361
|
+
const session = countSessionCompartmentEmbedCoverage(db, projectIdentity, sessionId, snapshot.chunkModelId);
|
|
167362
|
+
const memories = getMemoryEmbedCoverage(db, projectIdentity, snapshot.modelId);
|
|
167363
|
+
const gitEnabled = snapshot.gitCommitEnabled;
|
|
167364
|
+
const commits = gitEnabled ? {
|
|
167365
|
+
embedded: countEmbeddedCommits(db, projectIdentity),
|
|
167366
|
+
total: getCommitCount(db, projectIdentity),
|
|
167367
|
+
gitEnabled: true
|
|
167368
|
+
} : { embedded: 0, total: 0, gitEnabled: false };
|
|
167369
|
+
return {
|
|
167370
|
+
enabled: true,
|
|
167371
|
+
model: snapshot.model,
|
|
167372
|
+
provider: snapshot.provider,
|
|
167373
|
+
session,
|
|
167374
|
+
memories,
|
|
167375
|
+
commits
|
|
167376
|
+
};
|
|
166991
167377
|
}
|
|
166992
|
-
var OFF_PROVIDER_IDENTITY = "embedding-provider:off", SWEEP_MAX_WALL_CLOCK_MS, CHUNK_DRAIN_BATCH_SIZE = 8, MAX_WINDOWS_PER_EMBED_CALL =
|
|
167378
|
+
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;
|
|
166993
167379
|
var init_project_embedding_registry = __esm(() => {
|
|
166994
167380
|
init_magic_context();
|
|
166995
167381
|
init_logger();
|
|
166996
167382
|
init_compartment_chunk_embedding();
|
|
166997
167383
|
init_storage_git_commit_embeddings();
|
|
167384
|
+
init_storage_git_commits();
|
|
166998
167385
|
init_sweep_coordinator();
|
|
166999
167386
|
init_embedding_cache();
|
|
167000
167387
|
init_embedding_identity();
|
|
@@ -167275,58 +167662,6 @@ var init_models_dev_cache = __esm(() => {
|
|
|
167275
167662
|
init_logger();
|
|
167276
167663
|
});
|
|
167277
167664
|
|
|
167278
|
-
// src/shared/rpc-notifications.ts
|
|
167279
|
-
var exports_rpc_notifications = {};
|
|
167280
|
-
__export(exports_rpc_notifications, {
|
|
167281
|
-
pushNotification: () => pushNotification,
|
|
167282
|
-
isTuiConnected: () => isTuiConnected,
|
|
167283
|
-
drainNotifications: () => drainNotifications
|
|
167284
|
-
});
|
|
167285
|
-
function pushNotification(type, payload, sessionId) {
|
|
167286
|
-
queue2.push({ id: nextNotificationId++, type, payload, sessionId });
|
|
167287
|
-
if (queue2.length > 100) {
|
|
167288
|
-
const newestPerSession = new Map;
|
|
167289
|
-
for (const n of queue2) {
|
|
167290
|
-
const prev = newestPerSession.get(n.sessionId);
|
|
167291
|
-
if (prev === undefined || n.id > prev) {
|
|
167292
|
-
newestPerSession.set(n.sessionId, n.id);
|
|
167293
|
-
}
|
|
167294
|
-
}
|
|
167295
|
-
const mustKeep = new Set(newestPerSession.values());
|
|
167296
|
-
const byNewest = [...queue2].sort((a, b) => b.id - a.id);
|
|
167297
|
-
const kept = [];
|
|
167298
|
-
for (const n of byNewest) {
|
|
167299
|
-
if (kept.length < 50 || mustKeep.has(n.id))
|
|
167300
|
-
kept.push(n);
|
|
167301
|
-
}
|
|
167302
|
-
queue2 = kept.sort((a, b) => a.id - b.id);
|
|
167303
|
-
}
|
|
167304
|
-
}
|
|
167305
|
-
function drainNotifications(lastReceivedId = 0, sessionId) {
|
|
167306
|
-
const now = Date.now();
|
|
167307
|
-
lastDrainAtAny = now;
|
|
167308
|
-
if (sessionId !== undefined)
|
|
167309
|
-
lastDrainAtBySession.set(sessionId, now);
|
|
167310
|
-
const matchesClient = (notification) => sessionId === undefined || notification.sessionId === undefined || notification.sessionId === sessionId;
|
|
167311
|
-
if (lastReceivedId > 0) {
|
|
167312
|
-
queue2 = queue2.filter((notification) => !(notification.id <= lastReceivedId && matchesClient(notification)));
|
|
167313
|
-
}
|
|
167314
|
-
return queue2.filter((notification) => notification.id > lastReceivedId && matchesClient(notification));
|
|
167315
|
-
}
|
|
167316
|
-
function isTuiConnected(sessionId) {
|
|
167317
|
-
const now = Date.now();
|
|
167318
|
-
if (sessionId !== undefined) {
|
|
167319
|
-
const at = lastDrainAtBySession.get(sessionId) ?? 0;
|
|
167320
|
-
return at > 0 && now - at < TUI_CONNECTED_WINDOW_MS;
|
|
167321
|
-
}
|
|
167322
|
-
return lastDrainAtAny > 0 && now - lastDrainAtAny < TUI_CONNECTED_WINDOW_MS;
|
|
167323
|
-
}
|
|
167324
|
-
var queue2, nextNotificationId = 1, lastDrainAtBySession, lastDrainAtAny = 0, TUI_CONNECTED_WINDOW_MS = 3000;
|
|
167325
|
-
var init_rpc_notifications = __esm(() => {
|
|
167326
|
-
queue2 = [];
|
|
167327
|
-
lastDrainAtBySession = new Map;
|
|
167328
|
-
});
|
|
167329
|
-
|
|
167330
167665
|
// src/features/magic-context/compartment-embedding.ts
|
|
167331
167666
|
async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compartments) {
|
|
167332
167667
|
if (compartments.length === 0)
|
|
@@ -169271,7 +169606,7 @@ ${prepared.block}
|
|
|
169271
169606
|
if (!firstMessage || !textPart || isDroppedPlaceholder(textPart.text)) {
|
|
169272
169607
|
messages.unshift({
|
|
169273
169608
|
info: { role: "user", sessionID: sessionId },
|
|
169274
|
-
parts: [{ type: "text", text: historyBlock }]
|
|
169609
|
+
parts: [{ type: "text", text: historyBlock, synthetic: true }]
|
|
169275
169610
|
});
|
|
169276
169611
|
} else {
|
|
169277
169612
|
textPart.text = `${historyBlock}
|
|
@@ -170161,10 +170496,16 @@ function softRefreshCachedM1(options) {
|
|
|
170161
170496
|
function prependM0M1Messages(sessionId, messages, m0Text, m1Text) {
|
|
170162
170497
|
messages.unshift({
|
|
170163
170498
|
info: { role: "user", sessionID: sessionId },
|
|
170164
|
-
parts: [
|
|
170499
|
+
parts: [
|
|
170500
|
+
{
|
|
170501
|
+
type: "text",
|
|
170502
|
+
text: m0Text.length > 0 ? m0Text : M0_EMPTY_BODY,
|
|
170503
|
+
synthetic: true
|
|
170504
|
+
}
|
|
170505
|
+
]
|
|
170165
170506
|
}, {
|
|
170166
170507
|
info: { role: "user", sessionID: sessionId },
|
|
170167
|
-
parts: [{ type: "text", text: m1Text }]
|
|
170508
|
+
parts: [{ type: "text", text: m1Text, synthetic: true }]
|
|
170168
170509
|
});
|
|
170169
170510
|
}
|
|
170170
170511
|
function renderFreshM0NonPersisted(options) {
|
|
@@ -170872,7 +171213,7 @@ function buildToolArcs(messages) {
|
|
|
170872
171213
|
}
|
|
170873
171214
|
return arcs.sort((a, b) => a.invOrdinal - b.invOrdinal || (a.resOrdinal ?? Number.MAX_SAFE_INTEGER) - (b.resOrdinal ?? Number.MAX_SAFE_INTEGER));
|
|
170874
171215
|
}
|
|
170875
|
-
function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
|
|
171216
|
+
function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal, recentOpenArcCutoff) {
|
|
170876
171217
|
let boundary = candidate;
|
|
170877
171218
|
for (const arc of arcs) {
|
|
170878
171219
|
if (arc.resOrdinal !== null) {
|
|
@@ -170881,6 +171222,8 @@ function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
|
|
|
170881
171222
|
}
|
|
170882
171223
|
continue;
|
|
170883
171224
|
}
|
|
171225
|
+
if (arc.invOrdinal < recentOpenArcCutoff)
|
|
171226
|
+
continue;
|
|
170884
171227
|
if (arc.invOrdinal >= lastCompartmentEndOrdinal + 1 && arc.invOrdinal < boundary) {
|
|
170885
171228
|
return arc.invOrdinal;
|
|
170886
171229
|
}
|
|
@@ -171120,7 +171463,7 @@ function semanticSnapBoundary(args) {
|
|
|
171120
171463
|
return snapped;
|
|
171121
171464
|
}
|
|
171122
171465
|
function applyHeadCap(args) {
|
|
171123
|
-
const { index, protectedTailStart, offset, arcs, capTokens } = args;
|
|
171466
|
+
const { index, protectedTailStart, offset, arcs, capTokens, recentOpenArcCutoff } = args;
|
|
171124
171467
|
if (offset >= protectedTailStart)
|
|
171125
171468
|
return { eligibleEndOrdinal: offset, oversizeAtomicUnit: false };
|
|
171126
171469
|
let end = index.findHeadEndForCap(offset, protectedTailStart, capTokens);
|
|
@@ -171128,7 +171471,7 @@ function applyHeadCap(args) {
|
|
|
171128
171471
|
for (const arc of arcs) {
|
|
171129
171472
|
const resOrdinal = arc.resOrdinal;
|
|
171130
171473
|
if (resOrdinal === null) {
|
|
171131
|
-
if (arc.invOrdinal >= offset && arc.invOrdinal < end) {
|
|
171474
|
+
if (arc.invOrdinal >= recentOpenArcCutoff && arc.invOrdinal >= offset && arc.invOrdinal < end) {
|
|
171132
171475
|
end = Math.min(end, arc.invOrdinal);
|
|
171133
171476
|
}
|
|
171134
171477
|
continue;
|
|
@@ -171195,7 +171538,14 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
171195
171538
|
}
|
|
171196
171539
|
if (ctx.mode === "manual-full-recomp") {
|
|
171197
171540
|
const arcs2 = buildToolArcs(messages);
|
|
171198
|
-
const
|
|
171541
|
+
const recompTarget = deriveProtectedTailTokenTarget({
|
|
171542
|
+
contextLimit: ctx.contextLimit,
|
|
171543
|
+
executeThresholdPercentage: ctx.executeThresholdPercentage,
|
|
171544
|
+
usagePercentage: 0,
|
|
171545
|
+
triggerBudget: ctx.triggerBudget
|
|
171546
|
+
});
|
|
171547
|
+
const recentOpenArcCutoff2 = index.findSuffixStartForTokens(recompTarget.N);
|
|
171548
|
+
const firstOpenArc = arcs2.find((arc) => arc.resOrdinal === null && arc.invOrdinal >= offset && arc.invOrdinal >= recentOpenArcCutoff2);
|
|
171199
171549
|
const protectedTailStart2 = firstOpenArc?.invOrdinal ?? rawMessageCount + 1;
|
|
171200
171550
|
const rawRangeFingerprint2 = computeRawRangeFingerprint(messages, offset, protectedTailStart2);
|
|
171201
171551
|
return {
|
|
@@ -171237,13 +171587,14 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
171237
171587
|
const scaledN = ctx.emergencyTailScale ? Math.max(1, Math.floor(target.N * ctx.emergencyTailScale)) : target.N;
|
|
171238
171588
|
const arcs = buildToolArcs(messages);
|
|
171239
171589
|
let boundary = index.findSuffixStartForTokens(scaledN);
|
|
171590
|
+
const recentOpenArcCutoff = boundary;
|
|
171240
171591
|
let boundaryReason = boundary === 1 ? "whole-session-smaller-than-tail" : "size-walk";
|
|
171241
171592
|
const tokenAtBoundary = index.tokenForOrdinal(boundary);
|
|
171242
171593
|
if (boundary <= rawMessageCount && tokenAtBoundary > Math.max(2 * scaledN, 64000) && boundary < rawMessageCount) {
|
|
171243
171594
|
boundary += 1;
|
|
171244
171595
|
boundaryReason = "huge-message-exception";
|
|
171245
171596
|
}
|
|
171246
|
-
boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal);
|
|
171597
|
+
boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
|
|
171247
171598
|
const snapped = semanticSnapBoundary({
|
|
171248
171599
|
messages,
|
|
171249
171600
|
index,
|
|
@@ -171253,7 +171604,7 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
171253
171604
|
});
|
|
171254
171605
|
if (snapped !== boundary)
|
|
171255
171606
|
boundaryReason = "semantic-snap";
|
|
171256
|
-
boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal);
|
|
171607
|
+
boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
|
|
171257
171608
|
let runtimeFloor = offset;
|
|
171258
171609
|
if (ctx.migrationFloorActive)
|
|
171259
171610
|
runtimeFloor = Math.max(runtimeFloor, ctx.priorBoundaryOrdinal);
|
|
@@ -171289,7 +171640,8 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
171289
171640
|
offset,
|
|
171290
171641
|
arcs,
|
|
171291
171642
|
lastCompartmentEndOrdinal: ctx.lastCompartmentEndOrdinal,
|
|
171292
|
-
capTokens: perRunCap
|
|
171643
|
+
capTokens: perRunCap,
|
|
171644
|
+
recentOpenArcCutoff
|
|
171293
171645
|
});
|
|
171294
171646
|
const rawRangeFingerprint = computeRawRangeFingerprint(messages, offset, head.eligibleEndOrdinal);
|
|
171295
171647
|
return {
|
|
@@ -177054,6 +177406,11 @@ async function runManagedRecomp(ctx, sessionId, options) {
|
|
|
177054
177406
|
try {
|
|
177055
177407
|
const message = await executeContextRecomp(buildRecompDeps(ctx, sessionId), options);
|
|
177056
177408
|
const terminalPhase = isRecompSkip(message) ? "skipped" : isRecompFailure(message) ? "failed" : "done";
|
|
177409
|
+
if (terminalPhase === "done") {
|
|
177410
|
+
try {
|
|
177411
|
+
clearEmergencyRecovery(ctx.db, sessionId);
|
|
177412
|
+
} catch {}
|
|
177413
|
+
}
|
|
177057
177414
|
setRecompTerminal(ctx.liveSessionState, sessionId, terminalPhase, extractRecompReason(message));
|
|
177058
177415
|
return message;
|
|
177059
177416
|
} catch (error51) {
|
|
@@ -177152,6 +177509,7 @@ var RECOMP_DONE_GRACE_MS = 30000;
|
|
|
177152
177509
|
var init_recomp_orchestrator = __esm(async () => {
|
|
177153
177510
|
init_compartment_storage();
|
|
177154
177511
|
init_project_identity2();
|
|
177512
|
+
init_storage_meta_persisted();
|
|
177155
177513
|
await __promiseAll([
|
|
177156
177514
|
init_memory_migration(),
|
|
177157
177515
|
init_compartment_runner()
|
|
@@ -177212,15 +177570,15 @@ function shouldShowAnnouncement() {
|
|
|
177212
177570
|
}
|
|
177213
177571
|
return state.version !== ANNOUNCEMENT_VERSION;
|
|
177214
177572
|
}
|
|
177215
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
177573
|
+
var ANNOUNCEMENT_VERSION = "0.25.0", ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU", STATE_FILENAME = "last_announced_version";
|
|
177216
177574
|
var init_announcement = __esm(() => {
|
|
177217
177575
|
init_data_path();
|
|
177218
177576
|
ANNOUNCEMENT_FEATURES = [
|
|
177219
|
-
"
|
|
177220
|
-
"
|
|
177221
|
-
"
|
|
177222
|
-
"
|
|
177223
|
-
"
|
|
177577
|
+
"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.",
|
|
177578
|
+
"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.",
|
|
177579
|
+
"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.",
|
|
177580
|
+
"Pi: fixed /ctx-dream (was failing with 'Unknown named parameter') and local-embedding load failures on Windows/Desktop (#151, #128).",
|
|
177581
|
+
"Runaway background agents on weak/local models are now capped and force-stopped (#154, #152). Plus several prompt-cache busts removed."
|
|
177224
177582
|
];
|
|
177225
177583
|
});
|
|
177226
177584
|
// src/agents/permissions.ts
|
|
@@ -177949,9 +178307,9 @@ function getMagicContextBuiltinCommands() {
|
|
|
177949
178307
|
template: "ctx-dream",
|
|
177950
178308
|
description: "Run the hidden dreamer maintenance pass for this project now"
|
|
177951
178309
|
},
|
|
177952
|
-
"ctx-embed
|
|
177953
|
-
template: "ctx-embed
|
|
177954
|
-
description: "
|
|
178310
|
+
"ctx-embed": {
|
|
178311
|
+
template: "ctx-embed",
|
|
178312
|
+
description: "Embedding status, or start/pause history compartment embedding (start | pause)"
|
|
177955
178313
|
}
|
|
177956
178314
|
};
|
|
177957
178315
|
}
|
|
@@ -178822,7 +179180,7 @@ function enqueueDream(db, projectIdentity, reason, force = false) {
|
|
|
178822
179180
|
return db.transaction(() => {
|
|
178823
179181
|
if (!hasActiveDreamLease(db)) {
|
|
178824
179182
|
const staleThresholdMs = force ? 2 * 60 * 1000 : 120 * 60 * 1000;
|
|
178825
|
-
db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run(
|
|
179183
|
+
db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run(projectIdentity, now - staleThresholdMs);
|
|
178826
179184
|
}
|
|
178827
179185
|
const existing = db.prepare("SELECT id FROM dream_queue WHERE project_path = ?").get(projectIdentity);
|
|
178828
179186
|
if (existing) {
|
|
@@ -180517,120 +180875,7 @@ ${body}` : subject;
|
|
|
180517
180875
|
init_logger();
|
|
180518
180876
|
init_embedding();
|
|
180519
180877
|
init_storage_git_commit_embeddings();
|
|
180520
|
-
|
|
180521
|
-
// src/features/magic-context/git-commits/storage-git-commits.ts
|
|
180522
|
-
init_logger();
|
|
180523
|
-
var insertStatements = new WeakMap;
|
|
180524
|
-
var existingShasStatements = new WeakMap;
|
|
180525
|
-
var projectCountStatements = new WeakMap;
|
|
180526
|
-
var evictStatements = new WeakMap;
|
|
180527
|
-
var evictOverflowStatements = new WeakMap;
|
|
180528
|
-
var latestCommitTimeStatements = new WeakMap;
|
|
180529
|
-
function getInsertStatement(db) {
|
|
180530
|
-
let stmt = insertStatements.get(db);
|
|
180531
|
-
if (!stmt) {
|
|
180532
|
-
stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
|
|
180533
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
180534
|
-
ON CONFLICT(sha) DO UPDATE SET
|
|
180535
|
-
project_path = excluded.project_path,
|
|
180536
|
-
short_sha = excluded.short_sha,
|
|
180537
|
-
message = excluded.message,
|
|
180538
|
-
author = excluded.author,
|
|
180539
|
-
committed_at = excluded.committed_at,
|
|
180540
|
-
indexed_at = excluded.indexed_at
|
|
180541
|
-
WHERE git_commits.message != excluded.message`);
|
|
180542
|
-
insertStatements.set(db, stmt);
|
|
180543
|
-
}
|
|
180544
|
-
return stmt;
|
|
180545
|
-
}
|
|
180546
|
-
function getExistingShasStatement(db) {
|
|
180547
|
-
let stmt = existingShasStatements.get(db);
|
|
180548
|
-
if (!stmt) {
|
|
180549
|
-
stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
|
|
180550
|
-
existingShasStatements.set(db, stmt);
|
|
180551
|
-
}
|
|
180552
|
-
return stmt;
|
|
180553
|
-
}
|
|
180554
|
-
function getProjectCountStatement(db) {
|
|
180555
|
-
let stmt = projectCountStatements.get(db);
|
|
180556
|
-
if (!stmt) {
|
|
180557
|
-
stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
|
|
180558
|
-
projectCountStatements.set(db, stmt);
|
|
180559
|
-
}
|
|
180560
|
-
return stmt;
|
|
180561
|
-
}
|
|
180562
|
-
function getLatestCommitTimeStatement(db) {
|
|
180563
|
-
let stmt = latestCommitTimeStatements.get(db);
|
|
180564
|
-
if (!stmt) {
|
|
180565
|
-
stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
|
|
180566
|
-
latestCommitTimeStatements.set(db, stmt);
|
|
180567
|
-
}
|
|
180568
|
-
return stmt;
|
|
180569
|
-
}
|
|
180570
|
-
function getEvictOverflowStatement(db) {
|
|
180571
|
-
let stmt = evictOverflowStatements.get(db);
|
|
180572
|
-
if (!stmt) {
|
|
180573
|
-
stmt = db.prepare(`DELETE FROM git_commits
|
|
180574
|
-
WHERE rowid IN (
|
|
180575
|
-
SELECT rowid FROM git_commits
|
|
180576
|
-
WHERE project_path = ?
|
|
180577
|
-
ORDER BY committed_at DESC, sha DESC
|
|
180578
|
-
LIMIT -1 OFFSET ?
|
|
180579
|
-
)`);
|
|
180580
|
-
evictOverflowStatements.set(db, stmt);
|
|
180581
|
-
}
|
|
180582
|
-
return stmt;
|
|
180583
|
-
}
|
|
180584
|
-
function upsertCommits(db, projectPath, commits) {
|
|
180585
|
-
if (commits.length === 0)
|
|
180586
|
-
return { inserted: 0, updated: 0 };
|
|
180587
|
-
const existing = new Set;
|
|
180588
|
-
for (const row of getExistingShasStatement(db).all(projectPath)) {
|
|
180589
|
-
existing.add(row.sha);
|
|
180590
|
-
}
|
|
180591
|
-
let inserted = 0;
|
|
180592
|
-
let updated = 0;
|
|
180593
|
-
const now = Date.now();
|
|
180594
|
-
const insertStmt = getInsertStatement(db);
|
|
180595
|
-
db.transaction(() => {
|
|
180596
|
-
for (const commit of commits) {
|
|
180597
|
-
const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
|
|
180598
|
-
if (result.changes > 0) {
|
|
180599
|
-
if (existing.has(commit.sha)) {
|
|
180600
|
-
updated++;
|
|
180601
|
-
} else {
|
|
180602
|
-
inserted++;
|
|
180603
|
-
existing.add(commit.sha);
|
|
180604
|
-
}
|
|
180605
|
-
}
|
|
180606
|
-
}
|
|
180607
|
-
})();
|
|
180608
|
-
return { inserted, updated };
|
|
180609
|
-
}
|
|
180610
|
-
function getCommitCount(db, projectPath) {
|
|
180611
|
-
const row = getProjectCountStatement(db).get(projectPath);
|
|
180612
|
-
return row?.count ?? 0;
|
|
180613
|
-
}
|
|
180614
|
-
function getLatestIndexedCommitTimeMs(db, projectPath) {
|
|
180615
|
-
const row = getLatestCommitTimeStatement(db).get(projectPath);
|
|
180616
|
-
return row?.latest ?? null;
|
|
180617
|
-
}
|
|
180618
|
-
function enforceProjectCap(db, projectPath, maxCommits) {
|
|
180619
|
-
if (maxCommits <= 0)
|
|
180620
|
-
return 0;
|
|
180621
|
-
const count = getCommitCount(db, projectPath);
|
|
180622
|
-
if (count <= maxCommits)
|
|
180623
|
-
return 0;
|
|
180624
|
-
getEvictOverflowStatement(db).run(projectPath, maxCommits);
|
|
180625
|
-
const after = getCommitCount(db, projectPath);
|
|
180626
|
-
const evicted = Math.max(0, count - after);
|
|
180627
|
-
if (evicted > 0) {
|
|
180628
|
-
log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
|
|
180629
|
-
}
|
|
180630
|
-
return evicted;
|
|
180631
|
-
}
|
|
180632
|
-
|
|
180633
|
-
// src/features/magic-context/git-commits/indexer.ts
|
|
180878
|
+
init_storage_git_commits();
|
|
180634
180879
|
var MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
180635
180880
|
var EMBED_BATCH_SIZE = 16;
|
|
180636
180881
|
var EMBED_MAX_PER_SWEEP = 500;
|
|
@@ -180868,6 +181113,7 @@ function searchGitCommitsSync(db, projectPath, query, options) {
|
|
|
180868
181113
|
|
|
180869
181114
|
// src/features/magic-context/git-commits/index.ts
|
|
180870
181115
|
init_storage_git_commit_embeddings();
|
|
181116
|
+
init_storage_git_commits();
|
|
180871
181117
|
init_sweep_coordinator();
|
|
180872
181118
|
|
|
180873
181119
|
// src/plugin/dream-timer.ts
|
|
@@ -182291,7 +182537,7 @@ function createMagicContextCommandHandler(deps) {
|
|
|
182291
182537
|
const isAugCommand = (command) => command === "ctx-aug";
|
|
182292
182538
|
const isDreamCommand = (command) => command === "ctx-dream";
|
|
182293
182539
|
const isSessionUpgradeCommand = (command) => command === "ctx-session-upgrade";
|
|
182294
|
-
const
|
|
182540
|
+
const isEmbedCommand = (command) => command === "ctx-embed";
|
|
182295
182541
|
return {
|
|
182296
182542
|
"command.execute.before": async (input, _output, _params) => {
|
|
182297
182543
|
const isStatus = isStatusCommand(input.command);
|
|
@@ -182300,8 +182546,8 @@ function createMagicContextCommandHandler(deps) {
|
|
|
182300
182546
|
const isAug = isAugCommand(input.command);
|
|
182301
182547
|
const isDream = isDreamCommand(input.command);
|
|
182302
182548
|
const isSessionUpgrade = isSessionUpgradeCommand(input.command);
|
|
182303
|
-
const
|
|
182304
|
-
if (!isStatus && !isFlush && !isRecomp && !isAug && !isDream && !isSessionUpgrade && !
|
|
182549
|
+
const isEmbed = isEmbedCommand(input.command);
|
|
182550
|
+
if (!isStatus && !isFlush && !isRecomp && !isAug && !isDream && !isSessionUpgrade && !isEmbed) {
|
|
182305
182551
|
return;
|
|
182306
182552
|
}
|
|
182307
182553
|
const sessionId = input.sessionID;
|
|
@@ -182314,15 +182560,50 @@ function createMagicContextCommandHandler(deps) {
|
|
|
182314
182560
|
await executeDreaming(deps, sessionId);
|
|
182315
182561
|
return;
|
|
182316
182562
|
}
|
|
182317
|
-
if (
|
|
182318
|
-
const
|
|
182319
|
-
|
|
182320
|
-
|
|
182563
|
+
if (isEmbed) {
|
|
182564
|
+
const sub = input.arguments.trim().toLowerCase();
|
|
182565
|
+
if (sub === "pause") {
|
|
182566
|
+
const summary = deps.pauseEmbedDrain ? deps.pauseEmbedDrain(sessionId) : "Embedding pause is unavailable.";
|
|
182567
|
+
if (isTuiConnected(sessionId)) {
|
|
182568
|
+
pushNotification("action", { action: "show-result-dialog", title: "Embed", message: summary }, sessionId);
|
|
182569
|
+
} else {
|
|
182570
|
+
await deps.sendNotification(sessionId, summary, {});
|
|
182571
|
+
}
|
|
182572
|
+
throwSentinel(input.command);
|
|
182573
|
+
}
|
|
182574
|
+
if (sub === "start") {
|
|
182575
|
+
const summary = deps.executeEmbedHistory ? await deps.executeEmbedHistory(sessionId) : "Semantic embedding is not configured for this project, so there is nothing to embed.";
|
|
182576
|
+
if (isTuiConnected(sessionId)) {
|
|
182577
|
+
pushNotification("action", { action: "show-result-dialog", title: "Embed", message: summary }, sessionId);
|
|
182578
|
+
} else {
|
|
182579
|
+
await deps.sendNotification(sessionId, summary, {});
|
|
182580
|
+
}
|
|
182581
|
+
throwSentinel(input.command);
|
|
182582
|
+
}
|
|
182583
|
+
if (sub !== "") {
|
|
182584
|
+
await deps.sendNotification(sessionId, "Usage: `/ctx-embed` (status), `/ctx-embed start`, or `/ctx-embed pause`.", {});
|
|
182585
|
+
throwSentinel(input.command);
|
|
182586
|
+
}
|
|
182587
|
+
if (isTuiConnected(sessionId)) {
|
|
182588
|
+
pushNotification("action", { action: "show-embed-dialog" }, sessionId);
|
|
182589
|
+
sessionLog(sessionId, "command ctx-embed: pushed show-embed-dialog to TUI");
|
|
182590
|
+
throwSentinel(input.command);
|
|
182591
|
+
}
|
|
182592
|
+
result = deps.getEmbedStatusText ? `## Embedding Status
|
|
182593
|
+
|
|
182594
|
+
${deps.getEmbedStatusText(sessionId)}` : `## Embedding Status
|
|
182595
|
+
|
|
182596
|
+
Embedding status is unavailable.`;
|
|
182321
182597
|
}
|
|
182322
182598
|
if (isFlush) {
|
|
182323
182599
|
result = executeFlush(deps.db, sessionId);
|
|
182324
182600
|
clearCachedM0M1(deps.db, sessionId);
|
|
182325
182601
|
deps.onFlush?.(sessionId);
|
|
182602
|
+
if (isTuiConnected(sessionId)) {
|
|
182603
|
+
pushNotification("action", { action: "show-flush-dialog", message: result }, sessionId);
|
|
182604
|
+
sessionLog(sessionId, "command ctx-flush: pushed show-flush-dialog to TUI");
|
|
182605
|
+
throwSentinel(input.command);
|
|
182606
|
+
}
|
|
182326
182607
|
}
|
|
182327
182608
|
if (isStatus) {
|
|
182328
182609
|
if (isTuiConnected(sessionId)) {
|
|
@@ -182449,6 +182730,34 @@ ${snap.error}`;
|
|
|
182449
182730
|
// src/hooks/magic-context/hook.ts
|
|
182450
182731
|
init_derive_budgets();
|
|
182451
182732
|
|
|
182733
|
+
// src/hooks/magic-context/embed-session-state.ts
|
|
182734
|
+
var embedPauseBySession = new Set;
|
|
182735
|
+
var embedRunStateBySession = new Map;
|
|
182736
|
+
var autoEmbedAttemptedBySession = new Set;
|
|
182737
|
+
function getEmbedDrainUiStatus(sessionId, progress) {
|
|
182738
|
+
if (embedPauseBySession.has(sessionId)) {
|
|
182739
|
+
return { status: "paused" };
|
|
182740
|
+
}
|
|
182741
|
+
if (progress?.kind === "embed" && progress.phase === "recomp") {
|
|
182742
|
+
return { status: "running" };
|
|
182743
|
+
}
|
|
182744
|
+
if (progress?.kind === "embed" && (progress.phase === "failed" || progress.phase === "skipped") && progress.message) {
|
|
182745
|
+
if (/provider/i.test(progress.message)) {
|
|
182746
|
+
return { status: "stopped", detail: progress.message };
|
|
182747
|
+
}
|
|
182748
|
+
}
|
|
182749
|
+
return { status: "idle" };
|
|
182750
|
+
}
|
|
182751
|
+
function clearEmbedSessionState(sessionId) {
|
|
182752
|
+
embedPauseBySession.delete(sessionId);
|
|
182753
|
+
const ctrl = embedRunStateBySession.get(sessionId);
|
|
182754
|
+
if (ctrl) {
|
|
182755
|
+
ctrl.abort();
|
|
182756
|
+
embedRunStateBySession.delete(sessionId);
|
|
182757
|
+
}
|
|
182758
|
+
autoEmbedAttemptedBySession.delete(sessionId);
|
|
182759
|
+
}
|
|
182760
|
+
|
|
182452
182761
|
// src/features/magic-context/message-index-async.ts
|
|
182453
182762
|
init_logger();
|
|
182454
182763
|
await init_message_index();
|
|
@@ -182686,8 +182995,8 @@ var CHANNEL1_SENTINEL = "<system-reminder>";
|
|
|
182686
182995
|
var TOKENS_PER_BYTE = 0.25;
|
|
182687
182996
|
var CHANNEL1_FLOOR_TOKENS = 1e4;
|
|
182688
182997
|
var CHANNEL1_REFIRE_FLOOR_TOKENS = 1e4;
|
|
182689
|
-
function channel1RefireTokens(
|
|
182690
|
-
const scaled = Math.round(0.05 * Math.max(0,
|
|
182998
|
+
function channel1RefireTokens(workingWindowTokens) {
|
|
182999
|
+
const scaled = Math.round(0.05 * Math.max(0, workingWindowTokens));
|
|
182691
183000
|
return Math.max(CHANNEL1_REFIRE_FLOOR_TOKENS, scaled);
|
|
182692
183001
|
}
|
|
182693
183002
|
var S_GENTLE = 0.2;
|
|
@@ -182757,7 +183066,7 @@ function computeTailTokenEstimate(messages) {
|
|
|
182757
183066
|
};
|
|
182758
183067
|
}
|
|
182759
183068
|
function decideChannel1(input) {
|
|
182760
|
-
const { undroppedTokens, pressure,
|
|
183069
|
+
const { undroppedTokens, pressure, workingWindowTokens, hasRecentReduce } = input;
|
|
182761
183070
|
const resetCycle = hasRecentReduce || undroppedTokens < input.lastNudgeUndropped;
|
|
182762
183071
|
const lastNudge = resetCycle ? 0 : input.lastNudgeUndropped;
|
|
182763
183072
|
const lastLevel = resetCycle ? "" : input.lastNudgeLevel;
|
|
@@ -182772,7 +183081,7 @@ function decideChannel1(input) {
|
|
|
182772
183081
|
return quiet();
|
|
182773
183082
|
if (undroppedTokens < CHANNEL1_FLOOR_TOKENS)
|
|
182774
183083
|
return quiet();
|
|
182775
|
-
const budget =
|
|
183084
|
+
const budget = workingWindowTokens > 0 ? workingWindowTokens : undroppedTokens || 1;
|
|
182776
183085
|
const severity = undroppedTokens / budget * pressure;
|
|
182777
183086
|
if (severity < S_GENTLE)
|
|
182778
183087
|
return quiet();
|
|
@@ -182784,7 +183093,7 @@ function decideChannel1(input) {
|
|
|
182784
183093
|
else
|
|
182785
183094
|
level = "gentle";
|
|
182786
183095
|
if (lastLevel === "") {
|
|
182787
|
-
if (undroppedTokens < lastNudge + channel1RefireTokens(
|
|
183096
|
+
if (undroppedTokens < lastNudge + channel1RefireTokens(workingWindowTokens)) {
|
|
182788
183097
|
return quiet();
|
|
182789
183098
|
}
|
|
182790
183099
|
} else if (LEVEL_RANK[level] <= LEVEL_RANK[lastLevel]) {
|
|
@@ -182809,6 +183118,13 @@ function computePressure(input) {
|
|
|
182809
183118
|
function approxThousands(tokens) {
|
|
182810
183119
|
return `${Math.round(tokens / 1000)}k`;
|
|
182811
183120
|
}
|
|
183121
|
+
function formatOldestReclaimableHint(hint) {
|
|
183122
|
+
if (!hint || hint.length === 0)
|
|
183123
|
+
return "";
|
|
183124
|
+
const rendered = hint.slice(0, 4).map((tag) => `§${tag.tagNumber}§ ${tag.toolName ?? "tool"}`).join(" · ");
|
|
183125
|
+
return rendered.length > 0 ? `
|
|
183126
|
+
oldest reclaimable: ${rendered}.` : "";
|
|
183127
|
+
}
|
|
182812
183128
|
var CHANNEL2_USABLE_FRACTION = 1 / 3;
|
|
182813
183129
|
var CHANNEL2_MIN_RECLAIMABLE = 1e4;
|
|
182814
183130
|
function shouldTriggerChannel2(input) {
|
|
@@ -182818,30 +183134,32 @@ function shouldTriggerChannel2(input) {
|
|
|
182818
183134
|
return true;
|
|
182819
183135
|
return input.reclaimableTokens >= input.usableTokens * CHANNEL2_USABLE_FRACTION;
|
|
182820
183136
|
}
|
|
182821
|
-
function buildChannel2Reminder(undroppedTokens) {
|
|
183137
|
+
function buildChannel2Reminder(undroppedTokens, hint) {
|
|
182822
183138
|
const amount = approxThousands(undroppedTokens);
|
|
183139
|
+
const hintText = formatOldestReclaimableHint(hint);
|
|
182823
183140
|
return `<system-reminder>
|
|
182824
|
-
` + `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
|
|
183141
|
+
` + `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}
|
|
182825
183142
|
` + `</system-reminder>`;
|
|
182826
183143
|
}
|
|
182827
|
-
function buildChannel1Reminder(level, undroppedTokens) {
|
|
183144
|
+
function buildChannel1Reminder(level, undroppedTokens, hint) {
|
|
182828
183145
|
const amount = approxThousands(undroppedTokens);
|
|
183146
|
+
const hintText = formatOldestReclaimableHint(hint);
|
|
182829
183147
|
let body;
|
|
182830
183148
|
switch (level) {
|
|
182831
183149
|
case "gentle":
|
|
182832
|
-
body = `You have ~${amount} tokens of tool output you have not reduced. ` + `
|
|
183150
|
+
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.`;
|
|
182833
183151
|
break;
|
|
182834
183152
|
case "firm":
|
|
182835
|
-
body = `~${amount} tokens of unreduced tool output
|
|
183153
|
+
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.`;
|
|
182836
183154
|
break;
|
|
182837
183155
|
case "urgent":
|
|
182838
|
-
body = `~${amount} tokens of unreduced tool output remain
|
|
183156
|
+
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.`;
|
|
182839
183157
|
break;
|
|
182840
183158
|
}
|
|
182841
183159
|
return `
|
|
182842
183160
|
|
|
182843
183161
|
<system-reminder>
|
|
182844
|
-
${body}
|
|
183162
|
+
${body}${hintText}
|
|
182845
183163
|
</system-reminder>`;
|
|
182846
183164
|
}
|
|
182847
183165
|
|
|
@@ -182895,10 +183213,10 @@ async function maybeDeliverChannel2(sessionId, deps) {
|
|
|
182895
183213
|
try {
|
|
182896
183214
|
const client3 = getLiveServerClient(serverUrl, deps.directory);
|
|
182897
183215
|
const promptContext = await resolvePromptContext(client3, sessionId);
|
|
182898
|
-
const reminder = buildChannel2Reminder(deps.reclaimableTokens);
|
|
183216
|
+
const reminder = buildChannel2Reminder(deps.reclaimableTokens, deps.oldestReclaimableToolTags);
|
|
182899
183217
|
const body = {
|
|
182900
183218
|
noReply: false,
|
|
182901
|
-
parts: [{ type: "text", text: reminder }]
|
|
183219
|
+
parts: [{ type: "text", text: reminder, synthetic: true }]
|
|
182902
183220
|
};
|
|
182903
183221
|
if (promptContext?.agent)
|
|
182904
183222
|
body.agent = promptContext.agent;
|
|
@@ -183356,7 +183674,8 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
|
|
|
183356
183674
|
const result = {
|
|
183357
183675
|
compressedToLite: 0,
|
|
183358
183676
|
compressedToFull: 0,
|
|
183359
|
-
compressedToUltra: 0
|
|
183677
|
+
compressedToUltra: 0,
|
|
183678
|
+
mutatedTextTags: 0
|
|
183360
183679
|
};
|
|
183361
183680
|
if (!config2.enabled)
|
|
183362
183681
|
return result;
|
|
@@ -183397,7 +183716,9 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
|
|
|
183397
183716
|
const target = targets.get(tag.tagNumber);
|
|
183398
183717
|
if (!target)
|
|
183399
183718
|
continue;
|
|
183400
|
-
target.setContent(compressed);
|
|
183719
|
+
const didMutate = target.setContent(compressed);
|
|
183720
|
+
if (didMutate)
|
|
183721
|
+
result.mutatedTextTags += 1;
|
|
183401
183722
|
updateCavemanDepth(db, sessionId, tag.tagNumber, targetDepth);
|
|
183402
183723
|
if (targetDepth === DEPTH_LITE)
|
|
183403
183724
|
result.compressedToLite += 1;
|
|
@@ -183985,28 +184306,6 @@ function stripInlineThinking(messages, messageTagNumbers, clearReasoningAge) {
|
|
|
183985
184306
|
}
|
|
183986
184307
|
return stripped;
|
|
183987
184308
|
}
|
|
183988
|
-
function truncateErroredTools(messages, watermark, messageTagNumbers) {
|
|
183989
|
-
let truncated = 0;
|
|
183990
|
-
for (let i = 0;i < messages.length; i++) {
|
|
183991
|
-
const maxTag = messageTagNumbers.get(messages[i]) ?? 0;
|
|
183992
|
-
if (maxTag > watermark) {
|
|
183993
|
-
continue;
|
|
183994
|
-
}
|
|
183995
|
-
for (const part of messages[i].parts) {
|
|
183996
|
-
if (!isRecord(part) || part.type !== "tool" || !isRecord(part.state)) {
|
|
183997
|
-
continue;
|
|
183998
|
-
}
|
|
183999
|
-
if (part.state.status !== "error") {
|
|
184000
|
-
continue;
|
|
184001
|
-
}
|
|
184002
|
-
if (typeof part.state.error === "string" && part.state.error.length > 100) {
|
|
184003
|
-
part.state.error = `${part.state.error.slice(0, 100)}... [truncated]`;
|
|
184004
|
-
truncated++;
|
|
184005
|
-
}
|
|
184006
|
-
}
|
|
184007
|
-
}
|
|
184008
|
-
return truncated;
|
|
184009
|
-
}
|
|
184010
184309
|
var REASONING_IGNORED_PART_TYPES = new Set([
|
|
184011
184310
|
"step-start",
|
|
184012
184311
|
"step-finish",
|
|
@@ -184423,28 +184722,12 @@ function appendReminderToUserMessage(message, reminder) {
|
|
|
184423
184722
|
}
|
|
184424
184723
|
|
|
184425
184724
|
// src/hooks/magic-context/apply-operations.ts
|
|
184426
|
-
init_tag_part_guards();
|
|
184427
184725
|
await init_storage();
|
|
184428
|
-
var USER_DROP_PREVIEW_CHARS = 250;
|
|
184429
184726
|
var RECENT_TOOL_SKELETON_WINDOW = 20;
|
|
184430
|
-
function buildReplacementContent(tagId
|
|
184431
|
-
|
|
184432
|
-
|
|
184433
|
-
|
|
184434
|
-
}
|
|
184435
|
-
const currentContent = target.getContent?.() ?? "";
|
|
184436
|
-
const originalText = stripTagPrefix(currentContent);
|
|
184437
|
-
if (originalText.length <= USER_DROP_PREVIEW_CHARS) {
|
|
184438
|
-
return `[truncated §${tagId}§]
|
|
184439
|
-
${originalText}`;
|
|
184440
|
-
}
|
|
184441
|
-
const hardCut = originalText.slice(0, USER_DROP_PREVIEW_CHARS);
|
|
184442
|
-
const softCutIndex = hardCut.search(/\s\S*$/);
|
|
184443
|
-
const preview = softCutIndex > USER_DROP_PREVIEW_CHARS - 30 ? hardCut.slice(0, softCutIndex) : hardCut;
|
|
184444
|
-
return `[truncated §${tagId}§]
|
|
184445
|
-
${preview}…`;
|
|
184446
|
-
}
|
|
184447
|
-
function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps) {
|
|
184727
|
+
function buildReplacementContent(tagId) {
|
|
184728
|
+
return `[dropped §${tagId}§]`;
|
|
184729
|
+
}
|
|
184730
|
+
function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps, syntheticPendingOps = []) {
|
|
184448
184731
|
let didMutateMessage = false;
|
|
184449
184732
|
db.transaction(() => {
|
|
184450
184733
|
const tags = preloadedTags ?? getTagsBySession(db, sessionId);
|
|
@@ -184452,11 +184735,16 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
|
|
|
184452
184735
|
const tagTypeById = new Map(tags.map((tag) => [tag.tagNumber, tag.type]));
|
|
184453
184736
|
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;
|
|
184454
184737
|
const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
|
|
184738
|
+
const opsToApply = [
|
|
184739
|
+
...pendingOps.map((op) => ({ op, synthetic: false })),
|
|
184740
|
+
...syntheticPendingOps.map((op) => ({ op, synthetic: true }))
|
|
184741
|
+
];
|
|
184455
184742
|
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));
|
|
184456
|
-
for (const pendingOp of
|
|
184743
|
+
for (const { op: pendingOp, synthetic } of opsToApply) {
|
|
184457
184744
|
const tagStatus = tagStatusById.get(pendingOp.tagId);
|
|
184458
184745
|
if (tagStatus === "compacted" || tagStatus === "dropped") {
|
|
184459
|
-
|
|
184746
|
+
if (!synthetic)
|
|
184747
|
+
removePendingOp(db, sessionId, pendingOp.tagId);
|
|
184460
184748
|
continue;
|
|
184461
184749
|
}
|
|
184462
184750
|
if (protectedTagIds.has(pendingOp.tagId)) {
|
|
@@ -184464,33 +184752,46 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
|
|
|
184464
184752
|
}
|
|
184465
184753
|
const target = targets.get(pendingOp.tagId);
|
|
184466
184754
|
const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
|
|
184755
|
+
if (synthetic) {
|
|
184756
|
+
if (!isToolTag || target?.canDrop?.() !== true)
|
|
184757
|
+
continue;
|
|
184758
|
+
}
|
|
184759
|
+
let shouldPersistDrop = false;
|
|
184467
184760
|
if (isToolTag) {
|
|
184468
184761
|
if (skeletonWindow.has(pendingOp.tagId)) {
|
|
184469
184762
|
const truncResult = target?.truncate?.() ?? "absent";
|
|
184470
|
-
if (truncResult === "incomplete") {
|
|
184763
|
+
if (truncResult === "incomplete" || synthetic && truncResult !== "truncated") {
|
|
184471
184764
|
continue;
|
|
184472
184765
|
}
|
|
184473
184766
|
if (truncResult === "truncated") {
|
|
184474
184767
|
didMutateMessage = true;
|
|
184475
184768
|
}
|
|
184476
184769
|
updateTagDropMode(db, sessionId, pendingOp.tagId, "truncated");
|
|
184770
|
+
shouldPersistDrop = true;
|
|
184477
184771
|
} else {
|
|
184478
184772
|
const dropResult = target?.drop?.() ?? "absent";
|
|
184479
|
-
if (dropResult === "incomplete") {
|
|
184773
|
+
if (dropResult === "incomplete" || synthetic && dropResult !== "removed") {
|
|
184480
184774
|
continue;
|
|
184481
184775
|
}
|
|
184482
184776
|
if (dropResult === "removed") {
|
|
184483
184777
|
didMutateMessage = true;
|
|
184484
184778
|
}
|
|
184485
184779
|
updateTagDropMode(db, sessionId, pendingOp.tagId, "full");
|
|
184780
|
+
shouldPersistDrop = true;
|
|
184486
184781
|
}
|
|
184487
184782
|
} else if (target) {
|
|
184488
|
-
const changed = target.setContent(buildReplacementContent(pendingOp.tagId
|
|
184783
|
+
const changed = target.setContent(buildReplacementContent(pendingOp.tagId));
|
|
184489
184784
|
if (changed)
|
|
184490
184785
|
didMutateMessage = true;
|
|
184786
|
+
shouldPersistDrop = true;
|
|
184787
|
+
} else if (!synthetic) {
|
|
184788
|
+
shouldPersistDrop = true;
|
|
184491
184789
|
}
|
|
184790
|
+
if (!shouldPersistDrop)
|
|
184791
|
+
continue;
|
|
184492
184792
|
updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
|
|
184493
|
-
|
|
184793
|
+
if (!synthetic)
|
|
184794
|
+
removePendingOp(db, sessionId, pendingOp.tagId);
|
|
184494
184795
|
}
|
|
184495
184796
|
})();
|
|
184496
184797
|
return didMutateMessage;
|
|
@@ -184514,7 +184815,7 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
|
|
|
184514
184815
|
}
|
|
184515
184816
|
}
|
|
184516
184817
|
} else if (target) {
|
|
184517
|
-
const changed = target.setContent(buildReplacementContent(tag.tagNumber
|
|
184818
|
+
const changed = target.setContent(buildReplacementContent(tag.tagNumber));
|
|
184518
184819
|
if (changed)
|
|
184519
184820
|
didMutateMessage = true;
|
|
184520
184821
|
}
|
|
@@ -185031,7 +185332,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
|
|
|
185031
185332
|
logTransformTiming(sessionId, "tag.saveSource", performance.now() - accSaveSource);
|
|
185032
185333
|
for (const [compositeKey, tagId] of toolTagByCallId) {
|
|
185033
185334
|
const thinkingParts = toolThinkingByCallId.get(compositeKey) ?? [];
|
|
185034
|
-
targets.set(tagId, createToolDropTarget(compositeKey, thinkingParts, toolCallIndex, batch));
|
|
185335
|
+
targets.set(tagId, createToolDropTarget(compositeKey, thinkingParts, toolCallIndex, batch, tagId));
|
|
185035
185336
|
}
|
|
185036
185337
|
const hasRecentReduceCall = lastReduceMessageIndex >= 0 && messages.length - lastReduceMessageIndex <= RECENT_REDUCE_LOOKBACK;
|
|
185037
185338
|
return {
|
|
@@ -186311,7 +186612,9 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
|
|
|
186311
186612
|
continue;
|
|
186312
186613
|
updateTagDropMode(db, sessionId, tag.tagNumber, "full");
|
|
186313
186614
|
updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
|
|
186314
|
-
|
|
186615
|
+
if (result === "removed" || result === "truncated") {
|
|
186616
|
+
deduplicatedTools++;
|
|
186617
|
+
}
|
|
186315
186618
|
}
|
|
186316
186619
|
}
|
|
186317
186620
|
})();
|
|
@@ -186320,6 +186623,7 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
|
|
|
186320
186623
|
sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
|
|
186321
186624
|
}
|
|
186322
186625
|
let compressedTextTags = 0;
|
|
186626
|
+
let mutatedTextTags = 0;
|
|
186323
186627
|
if (config2.caveman?.enabled) {
|
|
186324
186628
|
const cavemanResult = applyCavemanCleanup(sessionId, db, targets, tags, {
|
|
186325
186629
|
enabled: true,
|
|
@@ -186327,8 +186631,15 @@ function applyHeuristicCleanup(sessionId, db, targets, messageTagNumbers, config
|
|
|
186327
186631
|
protectedTags: config2.protectedTags
|
|
186328
186632
|
});
|
|
186329
186633
|
compressedTextTags = cavemanResult.compressedToLite + cavemanResult.compressedToFull + cavemanResult.compressedToUltra;
|
|
186634
|
+
mutatedTextTags = cavemanResult.mutatedTextTags;
|
|
186330
186635
|
}
|
|
186331
|
-
return {
|
|
186636
|
+
return {
|
|
186637
|
+
droppedTools,
|
|
186638
|
+
deduplicatedTools,
|
|
186639
|
+
droppedInjections,
|
|
186640
|
+
compressedTextTags,
|
|
186641
|
+
mutatedTextTags
|
|
186642
|
+
};
|
|
186332
186643
|
}
|
|
186333
186644
|
function extractToolInfo(part) {
|
|
186334
186645
|
if (part.type === "tool" && typeof part.tool === "string" && DEDUP_SAFE_TOOLS.has(part.tool)) {
|
|
@@ -186502,6 +186813,42 @@ function isTodoItem(value) {
|
|
|
186502
186813
|
return typeof todo.content === "string" && typeof todo.status === "string" && (todo.priority === undefined || typeof todo.priority === "string");
|
|
186503
186814
|
}
|
|
186504
186815
|
|
|
186816
|
+
// src/hooks/magic-context/tool-reclaim.ts
|
|
186817
|
+
await init_storage();
|
|
186818
|
+
function buildSyntheticToolReclaimOps(input) {
|
|
186819
|
+
const watermark = Math.max(0, input.watermark);
|
|
186820
|
+
if (watermark <= 0)
|
|
186821
|
+
return [];
|
|
186822
|
+
const realPendingTagIds = new Set((input.pendingOps ?? []).map((op) => op.tagId));
|
|
186823
|
+
const tags = getActiveTagsBySession(input.db, input.sessionId);
|
|
186824
|
+
const synthetic = [];
|
|
186825
|
+
for (const tag of tags) {
|
|
186826
|
+
if (tag.type !== "tool")
|
|
186827
|
+
continue;
|
|
186828
|
+
if (tag.status !== "active")
|
|
186829
|
+
continue;
|
|
186830
|
+
if (tag.tagNumber > watermark)
|
|
186831
|
+
continue;
|
|
186832
|
+
if (realPendingTagIds.has(tag.tagNumber))
|
|
186833
|
+
continue;
|
|
186834
|
+
if (input.targets.get(tag.tagNumber)?.canDrop?.() !== true)
|
|
186835
|
+
continue;
|
|
186836
|
+
synthetic.push({
|
|
186837
|
+
id: 0,
|
|
186838
|
+
sessionId: input.sessionId,
|
|
186839
|
+
tagId: tag.tagNumber,
|
|
186840
|
+
operation: "drop",
|
|
186841
|
+
queuedAt: 0
|
|
186842
|
+
});
|
|
186843
|
+
}
|
|
186844
|
+
return synthetic;
|
|
186845
|
+
}
|
|
186846
|
+
function advanceToolReclaimWatermarkToCurrentMax(db, sessionId) {
|
|
186847
|
+
const maxTagNumber = getMaxTagNumberBySession(db, sessionId);
|
|
186848
|
+
advanceToolReclaimWatermark(db, sessionId, maxTagNumber);
|
|
186849
|
+
return maxTagNumber;
|
|
186850
|
+
}
|
|
186851
|
+
|
|
186505
186852
|
// src/hooks/magic-context/transform-postprocess-phase.ts
|
|
186506
186853
|
var DEGRADE_CACHE_WARNING_THRESHOLD = 10;
|
|
186507
186854
|
var degradedCacheCountBySession = new BoundedSessionMap(100);
|
|
@@ -186556,12 +186903,14 @@ async function runPostTransformPhase(args) {
|
|
|
186556
186903
|
let deferredMaterializedSuccessfully = false;
|
|
186557
186904
|
let heuristicsRanSuccessfully = false;
|
|
186558
186905
|
let pendingOpsRanSuccessfully = false;
|
|
186906
|
+
let pendingOpsDidMutate = false;
|
|
186907
|
+
let heuristicOrReasoningDidMutate = false;
|
|
186559
186908
|
try {
|
|
186560
186909
|
if (shouldApplyPendingOps) {
|
|
186561
186910
|
const applyReason = isExplicitFlush ? "explicit_flush" : deferredMaterialize ? "deferred_materialization" : `scheduler_execute (scheduler=${args.schedulerDecision})`;
|
|
186562
186911
|
sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOps.length}, context=${args.contextUsage.percentage.toFixed(1)}%`);
|
|
186563
186912
|
const tApply = performance.now();
|
|
186564
|
-
applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, pendingOps);
|
|
186913
|
+
pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, pendingOps);
|
|
186565
186914
|
logTransformTiming(args.sessionId, "applyPendingOperations", tApply);
|
|
186566
186915
|
}
|
|
186567
186916
|
if (shouldRunHeuristics) {
|
|
@@ -186579,7 +186928,8 @@ async function runPostTransformPhase(args) {
|
|
|
186579
186928
|
} : undefined,
|
|
186580
186929
|
caveman: cavemanConfig
|
|
186581
186930
|
}, heuristicTags);
|
|
186582
|
-
logTransformTiming(args.sessionId, "applyHeuristicCleanup", t5, `droppedTools=${cleanup.droppedTools} deduplicatedTools=${cleanup.deduplicatedTools} droppedInjections=${cleanup.droppedInjections} compressedTextTags=${cleanup.compressedTextTags}`);
|
|
186931
|
+
logTransformTiming(args.sessionId, "applyHeuristicCleanup", t5, `droppedTools=${cleanup.droppedTools} deduplicatedTools=${cleanup.deduplicatedTools} droppedInjections=${cleanup.droppedInjections} compressedTextTags=${cleanup.compressedTextTags} mutatedTextTags=${cleanup.mutatedTextTags}`);
|
|
186932
|
+
const heuristicMutationCount = cleanup.droppedTools + cleanup.deduplicatedTools + cleanup.droppedInjections + cleanup.mutatedTextTags;
|
|
186583
186933
|
const t7 = performance.now();
|
|
186584
186934
|
const clearedReasoning = clearOldReasoning(args.messages, args.reasoningByMessage, args.messageTagNumbers, args.clearReasoningAge);
|
|
186585
186935
|
if (canUseEmptySentinels) {
|
|
@@ -186605,6 +186955,7 @@ async function runPostTransformPhase(args) {
|
|
|
186605
186955
|
}
|
|
186606
186956
|
}
|
|
186607
186957
|
logTransformTiming(args.sessionId, "clearOldReasoning", t7);
|
|
186958
|
+
heuristicOrReasoningDidMutate = heuristicMutationCount + clearedReasoning + strippedInline > 0;
|
|
186608
186959
|
if (pendingMaterializationAtPassStart) {
|
|
186609
186960
|
args.pendingMaterializationSessions.delete(args.sessionId);
|
|
186610
186961
|
}
|
|
@@ -186615,7 +186966,31 @@ async function runPostTransformPhase(args) {
|
|
|
186615
186966
|
if (args.schedulerDecision === "execute" && !materializationRequested) {
|
|
186616
186967
|
updateSessionMeta(args.db, args.sessionId, { lastResponseTime: Date.now() });
|
|
186617
186968
|
}
|
|
186969
|
+
const toolReclaimExecutePass = args.schedulerDecision === "execute";
|
|
186970
|
+
const alreadyMutatingThisPass = pendingOpsDidMutate || heuristicOrReasoningDidMutate;
|
|
186971
|
+
let autoReclaimTargetCount = 0;
|
|
186972
|
+
let autoReclaimDidMutate = false;
|
|
186973
|
+
if (toolReclaimExecutePass && alreadyMutatingThisPass && !emergencyDropEligible) {
|
|
186974
|
+
const syntheticPendingOps = buildSyntheticToolReclaimOps({
|
|
186975
|
+
db: args.db,
|
|
186976
|
+
sessionId: args.sessionId,
|
|
186977
|
+
targets: args.targets,
|
|
186978
|
+
watermark: args.sessionMeta.toolReclaimWatermark ?? 0,
|
|
186979
|
+
pendingOps
|
|
186980
|
+
});
|
|
186981
|
+
autoReclaimTargetCount = syntheticPendingOps.length;
|
|
186982
|
+
if (syntheticPendingOps.length > 0) {
|
|
186983
|
+
autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, args.targets, args.protectedTags, undefined, [], syntheticPendingOps);
|
|
186984
|
+
}
|
|
186985
|
+
}
|
|
186618
186986
|
args.batch?.finalize();
|
|
186987
|
+
if (toolReclaimExecutePass) {
|
|
186988
|
+
const maxTagNumber = advanceToolReclaimWatermarkToCurrentMax(args.db, args.sessionId);
|
|
186989
|
+
args.sessionMeta.toolReclaimWatermark = Math.max(args.sessionMeta.toolReclaimWatermark ?? 0, maxTagNumber);
|
|
186990
|
+
}
|
|
186991
|
+
if (autoReclaimTargetCount > 0) {
|
|
186992
|
+
sessionLog(args.sessionId, `tool reclaim auto-drop: targets=${autoReclaimTargetCount} mutated=${autoReclaimDidMutate}`);
|
|
186993
|
+
}
|
|
186619
186994
|
logTransformTiming(args.sessionId, "batchFinalize:heuristics", performance.now());
|
|
186620
186995
|
if (args.sessionMeta.lastTransformError !== null) {
|
|
186621
186996
|
updateSessionMeta(args.db, args.sessionId, { lastTransformError: null });
|
|
@@ -186627,11 +187002,6 @@ async function runPostTransformPhase(args) {
|
|
|
186627
187002
|
deferredMaterializedSuccessfully = true;
|
|
186628
187003
|
heuristicsRanSuccessfully = true;
|
|
186629
187004
|
}
|
|
186630
|
-
if (args.watermark > 0) {
|
|
186631
|
-
const tWatermarkCleanup = performance.now();
|
|
186632
|
-
truncateErroredTools(args.messages, args.watermark, args.messageTagNumbers);
|
|
186633
|
-
logTransformTiming(args.sessionId, "watermarkCleanup", tWatermarkCleanup);
|
|
186634
|
-
}
|
|
186635
187005
|
if (shouldApplyPendingOps) {
|
|
186636
187006
|
pendingOpsRanSuccessfully = true;
|
|
186637
187007
|
}
|
|
@@ -187647,7 +188017,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
|
|
|
187647
188017
|
let tailToolTokens;
|
|
187648
188018
|
let liveTailTokens;
|
|
187649
188019
|
try {
|
|
187650
|
-
const agg = getActiveTagTokenAggregate(db, sessionId);
|
|
188020
|
+
const agg = getActiveTagTokenAggregate(db, sessionId, deps.protectedTags);
|
|
187651
188021
|
tailToolTokens = agg.toolOutput;
|
|
187652
188022
|
liveTailTokens = agg.conversation + agg.toolCall;
|
|
187653
188023
|
} catch {
|
|
@@ -187658,6 +188028,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
|
|
|
187658
188028
|
const executeThresholdTokens = Math.round((resolvedContextLimit ?? 0) * resolvedExecuteThresholdPct / 100);
|
|
187659
188029
|
const usableTokens = Math.max(0, executeThresholdTokens - contextUsage.inputTokens + liveTailTokens);
|
|
187660
188030
|
resetLastNudgeCycleIfTailShrank(db, sessionId, tailToolTokens);
|
|
188031
|
+
const oldestReclaimableToolTags = getOldestActiveUnprotectedToolTags(db, sessionId, deps.protectedTags);
|
|
187661
188032
|
deps.channel1StateBySession.set(sessionId, {
|
|
187662
188033
|
tailToolTokens,
|
|
187663
188034
|
historyBudgetTokens: historyBudgetTokens ?? 0,
|
|
@@ -187666,9 +188037,10 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
|
|
|
187666
188037
|
lastInputTokens: contextUsage.inputTokens,
|
|
187667
188038
|
turnToolTokens: 0,
|
|
187668
188039
|
usableTokens,
|
|
187669
|
-
reducedSinceRefresh: false
|
|
188040
|
+
reducedSinceRefresh: false,
|
|
188041
|
+
oldestReclaimableToolTags
|
|
187670
188042
|
});
|
|
187671
|
-
const channel2MetricsKnown =
|
|
188043
|
+
const channel2MetricsKnown = resolvedContextLimit !== undefined && resolvedContextLimit > 0 && resolvedExecuteThresholdPct > 0;
|
|
187672
188044
|
if (channel2MetricsKnown) {
|
|
187673
188045
|
const channel2ShouldTrigger = shouldTriggerChannel2({
|
|
187674
188046
|
reclaimableTokens: tailToolTokens,
|
|
@@ -187692,6 +188064,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
|
|
|
187692
188064
|
}
|
|
187693
188065
|
const elapsed = (performance.now() - startTime).toFixed(1);
|
|
187694
188066
|
sessionLog(sessionId, `transform completed in ${elapsed}ms (${messages.length} messages, ${targets.size} targets, watermark: ${watermark})`);
|
|
188067
|
+
deps.maybeAutoEmbedSession?.(sessionId);
|
|
187695
188068
|
};
|
|
187696
188069
|
}
|
|
187697
188070
|
function resolveHistoryBudgetTokens(historyBudgetPercentage, contextUsage, executeThresholdPercentage, modelKey, executeThresholdTokens, resolvedContextLimit) {
|
|
@@ -187737,18 +188110,14 @@ function evictExpiredUsageEntries(contextUsageMap) {
|
|
|
187737
188110
|
}
|
|
187738
188111
|
async function deliverChannel2IfPending(deps, sessionId) {
|
|
187739
188112
|
try {
|
|
187740
|
-
try {
|
|
187741
|
-
const meta3 = getOrCreateSessionMeta(deps.db, sessionId);
|
|
187742
|
-
if (meta3.isSubagent)
|
|
187743
|
-
return;
|
|
187744
|
-
} catch {}
|
|
187745
188113
|
const baseline = deps.channel1StateBySession?.get(sessionId);
|
|
187746
188114
|
await maybeDeliverChannel2(sessionId, {
|
|
187747
188115
|
db: deps.db,
|
|
187748
188116
|
serverUrl: deps.serverUrl,
|
|
187749
188117
|
directory: deps.directory ?? ".",
|
|
187750
188118
|
reclaimableTokens: baseline ? baseline.tailToolTokens + baseline.turnToolTokens : undefined,
|
|
187751
|
-
usableTokens: baseline?.usableTokens
|
|
188119
|
+
usableTokens: baseline?.usableTokens,
|
|
188120
|
+
oldestReclaimableToolTags: baseline?.oldestReclaimableToolTags
|
|
187752
188121
|
});
|
|
187753
188122
|
} catch (error51) {
|
|
187754
188123
|
sessionLog(sessionId, "channel2 delivery wrapper failed (ignored):", error51);
|
|
@@ -188054,6 +188423,46 @@ function createEventHandler2(deps) {
|
|
|
188054
188423
|
};
|
|
188055
188424
|
}
|
|
188056
188425
|
|
|
188426
|
+
// src/hooks/magic-context/format-embed-status.ts
|
|
188427
|
+
function formatEmbedStatusText(coverage, drain) {
|
|
188428
|
+
if (!coverage.enabled) {
|
|
188429
|
+
return "Embedding is off (no provider configured).";
|
|
188430
|
+
}
|
|
188431
|
+
const lines = [];
|
|
188432
|
+
lines.push(`Embedding — model: ${coverage.model} (${coverage.provider})`);
|
|
188433
|
+
lines.push(`This session: ${coverage.session.embedded} / ${coverage.session.total} compartments embedded`);
|
|
188434
|
+
lines.push(`Project memories: ${coverage.memories.embedded} / ${coverage.memories.total} embedded`);
|
|
188435
|
+
if (coverage.commits.gitEnabled) {
|
|
188436
|
+
lines.push(`Git commits: ${coverage.commits.embedded} / ${coverage.commits.total}`);
|
|
188437
|
+
} else {
|
|
188438
|
+
lines.push("Git commits: 0 / 0 (git indexing off)");
|
|
188439
|
+
}
|
|
188440
|
+
let drainLine = "Drain: idle";
|
|
188441
|
+
switch (drain.status) {
|
|
188442
|
+
case "running": {
|
|
188443
|
+
const e = drain.embedded ?? coverage.session.embedded;
|
|
188444
|
+
const t = drain.total ?? coverage.session.total;
|
|
188445
|
+
const failedSuffix = drain.failed && drain.failed > 0 ? ` (${drain.failed} failed)` : "";
|
|
188446
|
+
drainLine = `Drain: running ${e}/${t}${failedSuffix}`;
|
|
188447
|
+
break;
|
|
188448
|
+
}
|
|
188449
|
+
case "paused": {
|
|
188450
|
+
const e = drain.embedded ?? coverage.session.embedded;
|
|
188451
|
+
const t = drain.total ?? coverage.session.total;
|
|
188452
|
+
drainLine = `Drain: paused ${e}/${t}`;
|
|
188453
|
+
break;
|
|
188454
|
+
}
|
|
188455
|
+
case "stopped":
|
|
188456
|
+
drainLine = "Drain: stopped (provider down)";
|
|
188457
|
+
break;
|
|
188458
|
+
default:
|
|
188459
|
+
drainLine = "Drain: idle";
|
|
188460
|
+
}
|
|
188461
|
+
lines.push(drainLine);
|
|
188462
|
+
return lines.join(`
|
|
188463
|
+
`);
|
|
188464
|
+
}
|
|
188465
|
+
|
|
188057
188466
|
// src/hooks/magic-context/hook.ts
|
|
188058
188467
|
await __promiseAll([
|
|
188059
188468
|
init_inject_compartments(),
|
|
@@ -188282,10 +188691,11 @@ function maybeInjectChannel1Nudge(args, sessionId, tool, output) {
|
|
|
188282
188691
|
contextLimit: state.contextLimit,
|
|
188283
188692
|
executeThresholdPercentage: state.executeThresholdPercentage
|
|
188284
188693
|
});
|
|
188694
|
+
const workingWindowTokens = Math.round(state.contextLimit * state.executeThresholdPercentage / 100);
|
|
188285
188695
|
const decision = decideChannel1({
|
|
188286
188696
|
undroppedTokens,
|
|
188287
188697
|
pressure,
|
|
188288
|
-
|
|
188698
|
+
workingWindowTokens,
|
|
188289
188699
|
lastNudgeUndropped: getLastNudgeUndropped(args.db, sessionId),
|
|
188290
188700
|
lastNudgeLevel: getLastNudgeLevel(args.db, sessionId),
|
|
188291
188701
|
hasRecentReduce: false
|
|
@@ -188294,7 +188704,7 @@ function maybeInjectChannel1Nudge(args, sessionId, tool, output) {
|
|
|
188294
188704
|
setLastNudgeLevel(args.db, sessionId, decision.nextLastNudgeLevel);
|
|
188295
188705
|
if (!decision.fire)
|
|
188296
188706
|
return;
|
|
188297
|
-
out.output += buildChannel1Reminder(decision.level, decision.undroppedTokens);
|
|
188707
|
+
out.output += buildChannel1Reminder(decision.level, decision.undroppedTokens, state.oldestReclaimableToolTags);
|
|
188298
188708
|
sessionLog(sessionId, `channel1 nudge fired: level=${decision.level} undropped~${Math.round(decision.undroppedTokens / 1000)}k tool=${tool}`);
|
|
188299
188709
|
}
|
|
188300
188710
|
function createToolExecuteAfterHook(args) {
|
|
@@ -188366,9 +188776,7 @@ Context is managed for you entirely automatically — there's nothing to prune a
|
|
|
188366
188776
|
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).`;
|
|
188367
188777
|
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.`;
|
|
188368
188778
|
var BASE_INTRO = (protectedTags) => `Messages and tool outputs are tagged with §N§ identifiers (e.g., §1§, §42§).
|
|
188369
|
-
Use \`ctx_reduce\` to
|
|
188370
|
-
- \`drop\`: Remove entirely (best for tool outputs you already acted on).
|
|
188371
|
-
Syntax: "3-5", "1,2,9", or "1-5,8,12-15". Last ${protectedTags} tags are protected.
|
|
188779
|
+
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".
|
|
188372
188780
|
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.
|
|
188373
188781
|
${CTX_NOTE_GUIDANCE}
|
|
188374
188782
|
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.
|
|
@@ -188387,7 +188795,7 @@ Use \`ctx_expand\` to recover the raw conversation behind a \`<compartment>\` su
|
|
|
188387
188795
|
\`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.
|
|
188388
188796
|
${TOOL_HISTORY_GUIDANCE}
|
|
188389
188797
|
NEVER drop large ranges blindly (e.g., "1-50"). Review each tag before deciding.
|
|
188390
|
-
|
|
188798
|
+
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\`.
|
|
188391
188799
|
NEVER drop assistant text messages unless they are exceptionally large. Your conversation messages are lightweight; only large tool outputs are worth dropping.
|
|
188392
188800
|
Before your turn finishes, consider using \`ctx_reduce\` to drop large tool outputs you no longer need.`;
|
|
188393
188801
|
var BASE_INTRO_NO_REDUCE = () => `${CTX_NOTE_GUIDANCE}
|
|
@@ -188808,29 +189216,55 @@ function createMagicContextHook(deps) {
|
|
|
188808
189216
|
ensureProjectRegistered: ensureProjectRegisteredFromOpenCodeDirectory,
|
|
188809
189217
|
getNotificationParams: (sid) => getLiveNotificationParams(sid, liveModelBySession, variantBySession, agentBySession)
|
|
188810
189218
|
});
|
|
188811
|
-
const executeEmbedHistory = async (sessionId) => {
|
|
189219
|
+
const executeEmbedHistory = async (sessionId, options) => {
|
|
188812
189220
|
if (deps.config.memory?.enabled === false) {
|
|
188813
189221
|
return "Memory is disabled for this project, so there is no semantic embedding to backfill.";
|
|
188814
189222
|
}
|
|
188815
189223
|
const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
|
|
189224
|
+
const active = embedRunStateBySession.get(sessionId);
|
|
189225
|
+
if (active && !active.signal.aborted && !options?.signal) {
|
|
189226
|
+
return "Embedding is already running for this session.";
|
|
189227
|
+
}
|
|
188816
189228
|
await ensureProjectRegisteredFromOpenCodeDirectory(directory, db);
|
|
188817
189229
|
const sessionProjectIdentity = resolveProjectIdentity(directory);
|
|
188818
|
-
|
|
188819
|
-
const
|
|
188820
|
-
|
|
188821
|
-
|
|
188822
|
-
|
|
188823
|
-
|
|
188824
|
-
|
|
188825
|
-
|
|
188826
|
-
|
|
188827
|
-
|
|
188828
|
-
|
|
188829
|
-
|
|
189230
|
+
embedPauseBySession.delete(sessionId);
|
|
189231
|
+
const prior = embedRunStateBySession.get(sessionId);
|
|
189232
|
+
if (prior)
|
|
189233
|
+
prior.abort();
|
|
189234
|
+
const controller = new AbortController;
|
|
189235
|
+
embedRunStateBySession.set(sessionId, controller);
|
|
189236
|
+
const signal = options?.signal ?? controller.signal;
|
|
189237
|
+
if (!options?.silent) {
|
|
189238
|
+
setRecompStarting({ recompProgressBySession }, sessionId, "Embedding history…", "embed");
|
|
189239
|
+
}
|
|
189240
|
+
let runFailed = 0;
|
|
189241
|
+
let outcome;
|
|
189242
|
+
try {
|
|
189243
|
+
outcome = await embedSessionCompartmentChunks(db, sessionProjectIdentity, sessionId, {
|
|
189244
|
+
signal,
|
|
189245
|
+
onProgress: ({ embedded, total }) => {
|
|
189246
|
+
const cur = recompProgressBySession.get(sessionId);
|
|
189247
|
+
if (!cur || cur.phase !== "recomp")
|
|
189248
|
+
return;
|
|
189249
|
+
recompProgressBySession.set(sessionId, {
|
|
189250
|
+
...cur,
|
|
189251
|
+
processedMessages: embedded,
|
|
189252
|
+
totalMessages: total,
|
|
189253
|
+
updatedAt: Date.now()
|
|
189254
|
+
});
|
|
189255
|
+
}
|
|
189256
|
+
});
|
|
189257
|
+
} finally {
|
|
189258
|
+
if (embedRunStateBySession.get(sessionId) === controller) {
|
|
189259
|
+
embedRunStateBySession.delete(sessionId);
|
|
188830
189260
|
}
|
|
188831
|
-
}
|
|
189261
|
+
}
|
|
189262
|
+
if ("failed" in outcome)
|
|
189263
|
+
runFailed = outcome.failed;
|
|
188832
189264
|
const terminal = (phase, message) => {
|
|
188833
|
-
|
|
189265
|
+
if (!options?.silent) {
|
|
189266
|
+
setRecompTerminal({ recompProgressBySession }, sessionId, phase, message);
|
|
189267
|
+
}
|
|
188834
189268
|
return message;
|
|
188835
189269
|
};
|
|
188836
189270
|
switch (outcome.status) {
|
|
@@ -188839,15 +189273,78 @@ function createMagicContextHook(deps) {
|
|
|
188839
189273
|
case "disabled":
|
|
188840
189274
|
return terminal("skipped", "No embedding provider is configured, so there is nothing to embed.");
|
|
188841
189275
|
case "busy":
|
|
188842
|
-
return terminal("skipped",
|
|
188843
|
-
case "aborted":
|
|
188844
|
-
|
|
189276
|
+
return terminal("skipped", "Embedding is already running for this project. Try again shortly.");
|
|
189277
|
+
case "aborted": {
|
|
189278
|
+
const cov = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
|
|
189279
|
+
const msg = `Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`;
|
|
189280
|
+
return terminal("skipped", msg);
|
|
189281
|
+
}
|
|
188845
189282
|
case "stalled":
|
|
188846
|
-
return terminal("skipped", `Embedded ${outcome.embedded} compartments; ${outcome.remaining} could not be embedded (the provider returned no result). Run /ctx-embed
|
|
189283
|
+
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.`);
|
|
188847
189284
|
default:
|
|
188848
|
-
return terminal("done", `Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search.`);
|
|
189285
|
+
return terminal("done", `Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search${runFailed > 0 ? ` (${runFailed} failed)` : ""}.`);
|
|
188849
189286
|
}
|
|
188850
189287
|
};
|
|
189288
|
+
const pauseEmbedDrain = (sessionId) => {
|
|
189289
|
+
embedPauseBySession.add(sessionId);
|
|
189290
|
+
const ctrl = embedRunStateBySession.get(sessionId);
|
|
189291
|
+
if (ctrl)
|
|
189292
|
+
ctrl.abort();
|
|
189293
|
+
const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
|
|
189294
|
+
const sessionProjectIdentity = resolveProjectIdentity(directory);
|
|
189295
|
+
const cov = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
|
|
189296
|
+
return `Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`;
|
|
189297
|
+
};
|
|
189298
|
+
const getEmbedStatusText = (sessionId) => {
|
|
189299
|
+
const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
|
|
189300
|
+
const sessionProjectIdentity = resolveProjectIdentity(directory);
|
|
189301
|
+
const coverage = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
|
|
189302
|
+
const progress = recompProgressBySession.get(sessionId);
|
|
189303
|
+
const drainUi = getEmbedDrainUiStatus(sessionId, progress);
|
|
189304
|
+
return formatEmbedStatusText(coverage, {
|
|
189305
|
+
status: drainUi.status,
|
|
189306
|
+
embedded: progress?.processedMessages,
|
|
189307
|
+
total: progress?.totalMessages
|
|
189308
|
+
});
|
|
189309
|
+
};
|
|
189310
|
+
const maybeAutoEmbedSession = (sessionId) => {
|
|
189311
|
+
if (autoEmbedAttemptedBySession.has(sessionId))
|
|
189312
|
+
return;
|
|
189313
|
+
if (embedPauseBySession.has(sessionId))
|
|
189314
|
+
return;
|
|
189315
|
+
if (deps.config.memory?.enabled === false)
|
|
189316
|
+
return;
|
|
189317
|
+
autoEmbedAttemptedBySession.add(sessionId);
|
|
189318
|
+
const directory = sessionDirectoryBySession.get(sessionId) ?? deps.directory;
|
|
189319
|
+
(async () => {
|
|
189320
|
+
try {
|
|
189321
|
+
await new Promise((resolve6) => setTimeout(resolve6, 0));
|
|
189322
|
+
await ensureProjectRegisteredFromOpenCodeDirectory(directory, db);
|
|
189323
|
+
const sessionProjectIdentity = resolveProjectIdentity(directory);
|
|
189324
|
+
const coverage = getEmbeddingCoverageStatus(db, sessionProjectIdentity, sessionId);
|
|
189325
|
+
if (!coverage.enabled)
|
|
189326
|
+
return;
|
|
189327
|
+
const remaining = coverage.session.total - coverage.session.embedded;
|
|
189328
|
+
if (remaining <= 0)
|
|
189329
|
+
return;
|
|
189330
|
+
const notifyParams = getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession);
|
|
189331
|
+
if (!isTuiConnected(sessionId)) {
|
|
189332
|
+
const startMsg = `Embedding ${remaining} compartment${remaining === 1 ? "" : "s"} of history in the background…`;
|
|
189333
|
+
await sendIgnoredMessage(deps.client, sessionId, startMsg, {
|
|
189334
|
+
...notifyParams
|
|
189335
|
+
});
|
|
189336
|
+
}
|
|
189337
|
+
const summary = await executeEmbedHistory(sessionId);
|
|
189338
|
+
if (!isTuiConnected(sessionId)) {
|
|
189339
|
+
await sendIgnoredMessage(deps.client, sessionId, summary, {
|
|
189340
|
+
...notifyParams
|
|
189341
|
+
});
|
|
189342
|
+
}
|
|
189343
|
+
} catch (error51) {
|
|
189344
|
+
log("[magic-context] auto-embed drain failed:", error51);
|
|
189345
|
+
}
|
|
189346
|
+
})();
|
|
189347
|
+
};
|
|
188851
189348
|
const sidekickRunnable = isSidekickRunnable(deps.config);
|
|
188852
189349
|
const sidekickConfig = sidekickRunnable ? deps.config.sidekick : undefined;
|
|
188853
189350
|
const transform2 = createTransform({
|
|
@@ -188909,7 +189406,8 @@ function createMagicContextHook(deps) {
|
|
|
188909
189406
|
cavemanTextCompression: ctxReduceEnabled === false && deps.config.caveman_text_compression?.enabled === true ? {
|
|
188910
189407
|
enabled: true,
|
|
188911
189408
|
minChars: deps.config.caveman_text_compression.min_chars ?? 500
|
|
188912
|
-
} : undefined
|
|
189409
|
+
} : undefined,
|
|
189410
|
+
maybeAutoEmbedSession
|
|
188913
189411
|
});
|
|
188914
189412
|
const eventHandler = createEventHandler2({
|
|
188915
189413
|
contextUsageMap,
|
|
@@ -188938,6 +189436,7 @@ function createMagicContextHook(deps) {
|
|
|
188938
189436
|
recompProgressBySession.delete(sessionId);
|
|
188939
189437
|
internalChildSessions.delete(sessionId);
|
|
188940
189438
|
channel1StateBySession.delete(sessionId);
|
|
189439
|
+
clearEmbedSessionState(sessionId);
|
|
188941
189440
|
}
|
|
188942
189441
|
});
|
|
188943
189442
|
const runDreamQueueInBackground = () => {
|
|
@@ -189003,6 +189502,8 @@ function createMagicContextHook(deps) {
|
|
|
189003
189502
|
executeRecomp: historianRunnable ? async (sessionId, options) => runManagedRecomp(buildManagedRecompCtx(sessionId), sessionId, options) : undefined,
|
|
189004
189503
|
runUpgrade: historianRunnable ? async (sessionId) => runManagedUpgrade(buildManagedRecompCtx(sessionId), sessionId) : undefined,
|
|
189005
189504
|
executeEmbedHistory,
|
|
189505
|
+
pauseEmbedDrain,
|
|
189506
|
+
getEmbedStatusText,
|
|
189006
189507
|
sendNotification: async (sessionId, text, params) => {
|
|
189007
189508
|
await sendIgnoredMessage(deps.client, sessionId, text, {
|
|
189008
189509
|
...getLiveNotificationParams(sessionId, liveModelBySession, variantBySession, agentBySession),
|
|
@@ -189215,6 +189716,7 @@ function truncateError(name2, code, message, maxLen = 240) {
|
|
|
189215
189716
|
|
|
189216
189717
|
// src/plugin/rpc-handlers.ts
|
|
189217
189718
|
init_project_identity();
|
|
189719
|
+
init_project_embedding_registry();
|
|
189218
189720
|
init_tool_definition_tokens();
|
|
189219
189721
|
await init_storage();
|
|
189220
189722
|
|
|
@@ -189935,6 +190437,26 @@ function buildStatusDetail(db, sessionId, directory, modelKey, config2, liveSess
|
|
|
189935
190437
|
}
|
|
189936
190438
|
return detail;
|
|
189937
190439
|
}
|
|
190440
|
+
function buildEmbedDetail(db, sessionId, dir, liveSessionState) {
|
|
190441
|
+
const projectIdentity = resolveProjectIdentity(dir);
|
|
190442
|
+
const coverage = getEmbeddingCoverageStatus(db, projectIdentity, sessionId);
|
|
190443
|
+
const progress = liveSessionState.recompProgressBySession.get(sessionId);
|
|
190444
|
+
const drainUi = getEmbedDrainUiStatus(sessionId, progress);
|
|
190445
|
+
const statusText = formatEmbedStatusText(coverage, {
|
|
190446
|
+
status: drainUi.status,
|
|
190447
|
+
embedded: progress?.processedMessages,
|
|
190448
|
+
total: progress?.totalMessages
|
|
190449
|
+
});
|
|
190450
|
+
return {
|
|
190451
|
+
enabled: coverage.enabled,
|
|
190452
|
+
model: coverage.model,
|
|
190453
|
+
provider: coverage.provider,
|
|
190454
|
+
session: coverage.session,
|
|
190455
|
+
memories: coverage.memories,
|
|
190456
|
+
commits: coverage.commits,
|
|
190457
|
+
statusText
|
|
190458
|
+
};
|
|
190459
|
+
}
|
|
189938
190460
|
function registerRpcHandlers(rpcServer, args) {
|
|
189939
190461
|
const { directory, config: config2, liveSessionState } = args;
|
|
189940
190462
|
const rawConfig = config2;
|
|
@@ -189957,6 +190479,19 @@ function registerRpcHandlers(rpcServer, args) {
|
|
|
189957
190479
|
return { error: "unavailable" };
|
|
189958
190480
|
return buildStatusDetail(db, sessionId, dir, modelKey, rawConfig, liveSessionState, injectionBudgetTokens);
|
|
189959
190481
|
});
|
|
190482
|
+
rpcServer.handle("embed-detail", async (params) => {
|
|
190483
|
+
const sessionId = String(params.sessionId ?? "");
|
|
190484
|
+
const dir = String(params.directory ?? directory);
|
|
190485
|
+
const db = getDb();
|
|
190486
|
+
if (!db || !sessionId)
|
|
190487
|
+
return { error: "unavailable" };
|
|
190488
|
+
try {
|
|
190489
|
+
return buildEmbedDetail(db, sessionId, dir, liveSessionState);
|
|
190490
|
+
} catch (err) {
|
|
190491
|
+
log("[rpc] embed-detail error:", err);
|
|
190492
|
+
return { error: "unavailable" };
|
|
190493
|
+
}
|
|
190494
|
+
});
|
|
189960
190495
|
rpcServer.handle("compartment-count", async (params) => {
|
|
189961
190496
|
const sessionId = String(params.sessionId ?? "");
|
|
189962
190497
|
const db = getDb();
|
|
@@ -190079,27 +190614,225 @@ Older parts of this session are summarized into <compartment> blocks inside <ses
|
|
|
190079
190614
|
|
|
190080
190615
|
ctx_expand(start=120, end=245) ← the compartment's own start/end attributes
|
|
190081
190616
|
|
|
190082
|
-
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
|
|
190617
|
+
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.
|
|
190618
|
+
|
|
190619
|
+
Two recovery modes for finer detail:
|
|
190620
|
+
- 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.
|
|
190621
|
+
- 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.`;
|
|
190083
190622
|
var CTX_EXPAND_TOKEN_BUDGET = 15000;
|
|
190084
190623
|
|
|
190624
|
+
// src/tools/ctx-expand/render.ts
|
|
190625
|
+
init_read_session_formatting();
|
|
190626
|
+
await init_read_session_chunk();
|
|
190627
|
+
function isRecord3(value) {
|
|
190628
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
190629
|
+
}
|
|
190630
|
+
function roleLabel(role) {
|
|
190631
|
+
if (role === "assistant")
|
|
190632
|
+
return "A (assistant)";
|
|
190633
|
+
if (role === "user")
|
|
190634
|
+
return "U (user)";
|
|
190635
|
+
return role;
|
|
190636
|
+
}
|
|
190637
|
+
function truncate2(value, max) {
|
|
190638
|
+
const t = value.trim();
|
|
190639
|
+
return t.length <= max ? t : `${t.slice(0, max)}…`;
|
|
190640
|
+
}
|
|
190641
|
+
function keyArg(input) {
|
|
190642
|
+
if (!input)
|
|
190643
|
+
return "";
|
|
190644
|
+
for (const k of ["filePath", "path", "pattern", "query", "symbol", "module", "action"]) {
|
|
190645
|
+
const v = input[k];
|
|
190646
|
+
if (typeof v === "string" && v.length > 0)
|
|
190647
|
+
return truncate2(v, 60);
|
|
190648
|
+
}
|
|
190649
|
+
if (typeof input.description === "string")
|
|
190650
|
+
return truncate2(input.description, 60);
|
|
190651
|
+
return "";
|
|
190652
|
+
}
|
|
190653
|
+
function asToolPart(part) {
|
|
190654
|
+
const type = typeof part.type === "string" ? part.type : "";
|
|
190655
|
+
if (type === "tool") {
|
|
190656
|
+
const state = isRecord3(part.state) ? part.state : null;
|
|
190657
|
+
const output = state && typeof state.output === "string" ? state.output : state && state.output != null ? JSON.stringify(state.output) : null;
|
|
190658
|
+
const metadata = state && isRecord3(state.metadata) ? state.metadata : null;
|
|
190659
|
+
const title = state && typeof state.title === "string" && state.title || metadata && typeof metadata.title === "string" && metadata.title || null;
|
|
190660
|
+
return {
|
|
190661
|
+
name: typeof part.tool === "string" ? part.tool : "tool",
|
|
190662
|
+
callId: typeof part.callID === "string" ? part.callID : "",
|
|
190663
|
+
title,
|
|
190664
|
+
input: state && isRecord3(state.input) ? state.input : null,
|
|
190665
|
+
output
|
|
190666
|
+
};
|
|
190667
|
+
}
|
|
190668
|
+
if (type === "tool_use") {
|
|
190669
|
+
return {
|
|
190670
|
+
name: typeof part.name === "string" ? part.name : "tool",
|
|
190671
|
+
callId: typeof part.id === "string" ? part.id : "",
|
|
190672
|
+
title: null,
|
|
190673
|
+
input: isRecord3(part.input) ? part.input : null,
|
|
190674
|
+
output: null
|
|
190675
|
+
};
|
|
190676
|
+
}
|
|
190677
|
+
if (type === "tool_result") {
|
|
190678
|
+
const content = part.content;
|
|
190679
|
+
const output = typeof content === "string" ? content : content != null ? JSON.stringify(content) : null;
|
|
190680
|
+
return {
|
|
190681
|
+
name: "tool_result",
|
|
190682
|
+
callId: typeof part.tool_use_id === "string" ? part.tool_use_id : "",
|
|
190683
|
+
title: null,
|
|
190684
|
+
input: null,
|
|
190685
|
+
output
|
|
190686
|
+
};
|
|
190687
|
+
}
|
|
190688
|
+
return null;
|
|
190689
|
+
}
|
|
190690
|
+
function textOf(part) {
|
|
190691
|
+
if (part.type === "text" && typeof part.text === "string")
|
|
190692
|
+
return part.text;
|
|
190693
|
+
return null;
|
|
190694
|
+
}
|
|
190695
|
+
function reasoningOf(part) {
|
|
190696
|
+
if ((part.type === "reasoning" || part.type === "thinking") && typeof part.text === "string") {
|
|
190697
|
+
return part.text;
|
|
190698
|
+
}
|
|
190699
|
+
return null;
|
|
190700
|
+
}
|
|
190701
|
+
function renderPartPreview(part) {
|
|
190702
|
+
if (!isRecord3(part))
|
|
190703
|
+
return null;
|
|
190704
|
+
const text = textOf(part);
|
|
190705
|
+
if (text !== null) {
|
|
190706
|
+
const t = truncate2(text, 200);
|
|
190707
|
+
return t.length > 0 ? ` • ${t}` : null;
|
|
190708
|
+
}
|
|
190709
|
+
const tool = asToolPart(part);
|
|
190710
|
+
if (tool) {
|
|
190711
|
+
const arg = keyArg(tool.input);
|
|
190712
|
+
const head = arg ? `${tool.name}(${arg})` : tool.name;
|
|
190713
|
+
return tool.output !== null ? ` • tool ${head} → output ~${estimateTokens(tool.output)} tok` : ` • tool ${head}`;
|
|
190714
|
+
}
|
|
190715
|
+
const reasoning = reasoningOf(part);
|
|
190716
|
+
if (reasoning !== null)
|
|
190717
|
+
return ` • [reasoning] ${truncate2(reasoning, 120)}`;
|
|
190718
|
+
const type = typeof part.type === "string" ? part.type : "part";
|
|
190719
|
+
if (type === "file")
|
|
190720
|
+
return " • [file]";
|
|
190721
|
+
if (type === "step-start" || type === "step-finish")
|
|
190722
|
+
return null;
|
|
190723
|
+
return ` • [${type}]`;
|
|
190724
|
+
}
|
|
190725
|
+
function renderPartFull(part) {
|
|
190726
|
+
if (!isRecord3(part))
|
|
190727
|
+
return null;
|
|
190728
|
+
const text = textOf(part);
|
|
190729
|
+
if (text !== null) {
|
|
190730
|
+
return text.trim().length > 0 ? ` [text]
|
|
190731
|
+
${text}` : null;
|
|
190732
|
+
}
|
|
190733
|
+
const tool = asToolPart(part);
|
|
190734
|
+
if (tool) {
|
|
190735
|
+
const lines = [];
|
|
190736
|
+
const idSuffix = tool.callId ? ` #${tool.callId}` : "";
|
|
190737
|
+
lines.push(` [tool: ${tool.name}${idSuffix}]`);
|
|
190738
|
+
if (tool.title && tool.title.trim().length > 0) {
|
|
190739
|
+
lines.push(` description: ${tool.title.trim()}`);
|
|
190740
|
+
}
|
|
190741
|
+
if (tool.input)
|
|
190742
|
+
lines.push(` input: ${JSON.stringify(tool.input)}`);
|
|
190743
|
+
if (tool.output !== null)
|
|
190744
|
+
lines.push(` output:
|
|
190745
|
+
${tool.output}`);
|
|
190746
|
+
return lines.join(`
|
|
190747
|
+
`);
|
|
190748
|
+
}
|
|
190749
|
+
const type = typeof part.type === "string" ? part.type : "part";
|
|
190750
|
+
if (type === "file") {
|
|
190751
|
+
const name2 = typeof part.filename === "string" && part.filename || typeof part.url === "string" && part.url || "";
|
|
190752
|
+
return ` [file]${name2 ? ` ${name2}` : ""}`;
|
|
190753
|
+
}
|
|
190754
|
+
return null;
|
|
190755
|
+
}
|
|
190756
|
+
function renderMessageByOrdinal(sessionId, ordinal) {
|
|
190757
|
+
const msg = readRawSessionMessages(sessionId).find((m) => m.ordinal === ordinal);
|
|
190758
|
+
if (!msg) {
|
|
190759
|
+
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.`;
|
|
190760
|
+
}
|
|
190761
|
+
const rendered = msg.parts.map(renderPartFull).filter((l) => l !== null);
|
|
190762
|
+
const lines = [`[${msg.ordinal}] ${roleLabel(msg.role)} — full recovery:`, ""];
|
|
190763
|
+
if (rendered.length === 0) {
|
|
190764
|
+
lines.push(" (no recoverable content — message had only structural/reasoning parts)");
|
|
190765
|
+
} else {
|
|
190766
|
+
lines.push(...rendered);
|
|
190767
|
+
}
|
|
190768
|
+
return lines.join(`
|
|
190769
|
+
`);
|
|
190770
|
+
}
|
|
190771
|
+
function renderVerboseRange(sessionId, start, end, tokenBudget) {
|
|
190772
|
+
const messages = readRawSessionMessages(sessionId).filter((m) => m.ordinal >= start && m.ordinal <= end);
|
|
190773
|
+
const out = [];
|
|
190774
|
+
let usedTokens = 0;
|
|
190775
|
+
let lastOrdinal = start - 1;
|
|
190776
|
+
let truncated = false;
|
|
190777
|
+
for (const msg of messages) {
|
|
190778
|
+
const header = `[${msg.ordinal}] ${roleLabel(msg.role)}`;
|
|
190779
|
+
const partLines = msg.parts.map(renderPartPreview).filter((l) => l !== null);
|
|
190780
|
+
const block = partLines.length > 0 ? `${header}
|
|
190781
|
+
${partLines.join(`
|
|
190782
|
+
`)}` : header;
|
|
190783
|
+
const blockTokens = estimateTokens(block);
|
|
190784
|
+
if (usedTokens + blockTokens > tokenBudget && out.length > 0) {
|
|
190785
|
+
truncated = true;
|
|
190786
|
+
break;
|
|
190787
|
+
}
|
|
190788
|
+
out.push(block);
|
|
190789
|
+
usedTokens += blockTokens;
|
|
190790
|
+
lastOrdinal = msg.ordinal;
|
|
190791
|
+
}
|
|
190792
|
+
return { text: out.join(`
|
|
190793
|
+
|
|
190794
|
+
`), lastOrdinal, truncated };
|
|
190795
|
+
}
|
|
190796
|
+
|
|
190085
190797
|
// src/tools/ctx-expand/tools.ts
|
|
190086
190798
|
function createCtxExpandTool(deps) {
|
|
190087
190799
|
return tool({
|
|
190088
190800
|
description: CTX_EXPAND_DESCRIPTION,
|
|
190089
190801
|
args: {
|
|
190090
|
-
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`),
|
|
190091
|
-
end: tool.schema.number().describe(`Last message ordinal to expand (inclusive) — a compartment's end="M" attribute`)
|
|
190802
|
+
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`),
|
|
190803
|
+
end: tool.schema.number().optional().describe(`Last message ordinal to expand (inclusive) — a compartment's end="M" attribute`),
|
|
190804
|
+
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."),
|
|
190805
|
+
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.")
|
|
190092
190806
|
},
|
|
190093
190807
|
async execute(args, toolContext) {
|
|
190094
190808
|
const sessionId = toolContext.sessionID;
|
|
190809
|
+
if (typeof args.message === "number" && args.message >= 1) {
|
|
190810
|
+
return renderMessageByOrdinal(sessionId, args.message);
|
|
190811
|
+
}
|
|
190095
190812
|
if (!args.start || !args.end || args.start < 1 || args.end < args.start) {
|
|
190096
|
-
return "Error: start and end
|
|
190813
|
+
return "Error: provide either message=<ordinal>, or start and end (positive integers, start <= end).";
|
|
190097
190814
|
}
|
|
190098
190815
|
const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
|
|
190099
190816
|
if (lastCompartmentEnd >= 0 && args.start > lastCompartmentEnd) {
|
|
190100
190817
|
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.`;
|
|
190101
190818
|
}
|
|
190102
190819
|
const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(args.end, lastCompartmentEnd) : args.end;
|
|
190820
|
+
if (args.verbose === true) {
|
|
190821
|
+
const v = renderVerboseRange(sessionId, args.start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET);
|
|
190822
|
+
if (!v.text) {
|
|
190823
|
+
return `No messages found in range ${args.start}-${effectiveEnd}. The range may be outside this session's history.`;
|
|
190824
|
+
}
|
|
190825
|
+
const out = [
|
|
190826
|
+
`Messages ${args.start}-${v.lastOrdinal} (verbose). Recover any one in full with ctx_expand(message=<ordinal>):`,
|
|
190827
|
+
"",
|
|
190828
|
+
v.text
|
|
190829
|
+
];
|
|
190830
|
+
if (v.truncated) {
|
|
190831
|
+
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.`);
|
|
190832
|
+
}
|
|
190833
|
+
return out.join(`
|
|
190834
|
+
`);
|
|
190835
|
+
}
|
|
190103
190836
|
const chunk = readSessionChunk(sessionId, CTX_EXPAND_TOKEN_BUDGET, args.start, effectiveEnd + 1);
|
|
190104
190837
|
if (!chunk.text || chunk.messageCount === 0) {
|
|
190105
190838
|
return `No messages found in range ${args.start}-${args.end}. The range may be outside this session's history.`;
|
|
@@ -190793,15 +191526,16 @@ function createCtxNoteTools(deps) {
|
|
|
190793
191526
|
};
|
|
190794
191527
|
}
|
|
190795
191528
|
// src/tools/ctx-reduce/constants.ts
|
|
190796
|
-
var CTX_REDUCE_DESCRIPTION = `
|
|
190797
|
-
|
|
190798
|
-
|
|
190799
|
-
|
|
190800
|
-
-
|
|
190801
|
-
-
|
|
190802
|
-
|
|
190803
|
-
|
|
190804
|
-
|
|
191529
|
+
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".
|
|
191530
|
+
|
|
191531
|
+
How it works:
|
|
191532
|
+
- 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.
|
|
191533
|
+
- The newest tags are protected: marking one just queues it until it ages out of the recent window, so marking recent output is harmless.
|
|
191534
|
+
- 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?".
|
|
191535
|
+
|
|
191536
|
+
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.
|
|
191537
|
+
Keep: user messages, unresolved errors, raw evidence you haven't extracted yet, and outputs whose exact wording may matter later.
|
|
191538
|
+
Never blanket-mark large ranges (e.g. "1-50") — review what each tag holds first.`;
|
|
190805
191539
|
// src/tools/ctx-reduce/tools.ts
|
|
190806
191540
|
import { tool as tool4 } from "@opencode-ai/plugin";
|
|
190807
191541
|
|
|
@@ -191384,6 +192118,9 @@ class MagicContextRpcServer {
|
|
|
191384
192118
|
}
|
|
191385
192119
|
|
|
191386
192120
|
// src/index.ts
|
|
192121
|
+
var HISTORIAN_MAX_STEPS = 40;
|
|
192122
|
+
var SIDEKICK_MAX_STEPS = 40;
|
|
192123
|
+
var DREAMER_MAX_STEPS = 150;
|
|
191387
192124
|
var plugin = async (ctx) => {
|
|
191388
192125
|
const pluginConfig = loadPluginConfig(ctx.directory);
|
|
191389
192126
|
setSqlitePragmaConfig({
|
|
@@ -191576,11 +192313,13 @@ var plugin = async (ctx) => {
|
|
|
191576
192313
|
await hooks.magicContext?.["experimental.text.complete"]?.(input, output);
|
|
191577
192314
|
},
|
|
191578
192315
|
config: async (config2) => {
|
|
191579
|
-
const buildHiddenAgentConfig = (agentId, prompt, allowedTools, overrides) => {
|
|
192316
|
+
const buildHiddenAgentConfig = (agentId, prompt, allowedTools, maxSteps, overrides) => {
|
|
191580
192317
|
const { permission: overridePermission, ...restOverrides } = overrides ?? {};
|
|
191581
192318
|
const basePermission = buildAllowOnlyPermission(allowedTools);
|
|
191582
192319
|
return {
|
|
191583
192320
|
prompt,
|
|
192321
|
+
steps: maxSteps,
|
|
192322
|
+
maxSteps,
|
|
191584
192323
|
...getAgentFallbackModels(agentId) ? { fallback_models: getAgentFallbackModels(agentId) } : {},
|
|
191585
192324
|
...restOverrides,
|
|
191586
192325
|
permission: {
|
|
@@ -191621,10 +192360,10 @@ var plugin = async (ctx) => {
|
|
|
191621
192360
|
})() : undefined;
|
|
191622
192361
|
config2.agent = {
|
|
191623
192362
|
...config2.agent ?? {},
|
|
191624
|
-
[DREAMER_AGENT]: buildHiddenAgentConfig(DREAMER_AGENT, DREAMER_SYSTEM_PROMPT, DREAMER_ALLOWED_TOOLS, dreamerAgentOverrides),
|
|
191625
|
-
[HISTORIAN_AGENT]: buildHiddenAgentConfig(HISTORIAN_AGENT, COMPARTMENT_AGENT_SYSTEM_PROMPT, HISTORIAN_ALLOWED_TOOLS, historianAgentOverrides),
|
|
191626
|
-
[HISTORIAN_EDITOR_AGENT]: buildHiddenAgentConfig(HISTORIAN_EDITOR_AGENT, HISTORIAN_EDITOR_SYSTEM_PROMPT, HISTORIAN_ALLOWED_TOOLS, historianAgentOverrides),
|
|
191627
|
-
[SIDEKICK_AGENT]: buildHiddenAgentConfig(SIDEKICK_AGENT, SIDEKICK_SYSTEM_PROMPT, SIDEKICK_ALLOWED_TOOLS, sidekickAgentOverrides)
|
|
192363
|
+
[DREAMER_AGENT]: buildHiddenAgentConfig(DREAMER_AGENT, DREAMER_SYSTEM_PROMPT, DREAMER_ALLOWED_TOOLS, DREAMER_MAX_STEPS, dreamerAgentOverrides),
|
|
192364
|
+
[HISTORIAN_AGENT]: buildHiddenAgentConfig(HISTORIAN_AGENT, COMPARTMENT_AGENT_SYSTEM_PROMPT, HISTORIAN_ALLOWED_TOOLS, HISTORIAN_MAX_STEPS, historianAgentOverrides),
|
|
192365
|
+
[HISTORIAN_EDITOR_AGENT]: buildHiddenAgentConfig(HISTORIAN_EDITOR_AGENT, HISTORIAN_EDITOR_SYSTEM_PROMPT, HISTORIAN_ALLOWED_TOOLS, HISTORIAN_MAX_STEPS, historianAgentOverrides),
|
|
192366
|
+
[SIDEKICK_AGENT]: buildHiddenAgentConfig(SIDEKICK_AGENT, SIDEKICK_SYSTEM_PROMPT, SIDEKICK_ALLOWED_TOOLS, SIDEKICK_MAX_STEPS, sidekickAgentOverrides)
|
|
191628
192367
|
};
|
|
191629
192368
|
}
|
|
191630
192369
|
};
|