@cortexkit/pi-magic-context 0.24.1 → 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/index.js +1006 -350
- package/dist/subagent-entry.js +559 -175
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8275,6 +8275,14 @@ function buildNodeSqliteDatabaseClass(DatabaseSync) {
|
|
|
8275
8275
|
}
|
|
8276
8276
|
super(typeof filename === "string" ? filename : ":memory:", translated);
|
|
8277
8277
|
}
|
|
8278
|
+
prepare(sql) {
|
|
8279
|
+
const stmt = super.prepare(sql);
|
|
8280
|
+
for (const method of ["run", "get", "all"]) {
|
|
8281
|
+
const original = stmt[method].bind(stmt);
|
|
8282
|
+
stmt[method] = (...args) => args.length === 1 && Array.isArray(args[0]) ? original(...args[0]) : original(...args);
|
|
8283
|
+
}
|
|
8284
|
+
return stmt;
|
|
8285
|
+
}
|
|
8278
8286
|
transaction(fn) {
|
|
8279
8287
|
const self = this;
|
|
8280
8288
|
const wrapped = function(...args) {
|
|
@@ -141059,6 +141067,8 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
|
|
|
141059
141067
|
var encoder = new TextEncoder;
|
|
141060
141068
|
var TAG_PREFIX_REGEX = /^(?:§\d+§\s*)+/;
|
|
141061
141069
|
var MALFORMED_TAG_PREFIX_REGEX = /^(?:§\d+">§(?:\d+§)?\s*)+/;
|
|
141070
|
+
var DANGLING_TAG_GLOBAL_REGEX = /\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?/g;
|
|
141071
|
+
var DANGLING_TAG_PREFIX_REGEX = /^(?:\u00a7\d+(?!\.\d)[^\s\u00a7\w.]?\s*)+/;
|
|
141062
141072
|
var COMPLETE_TAG_PAIR_GLOBAL_REGEX = /\u00a7\d+\u00a7/g;
|
|
141063
141073
|
var MALFORMED_TAG_GLOBAL_REGEX = /\u00a7\d+">(?:\u00a7(?:\d+\u00a7)?)?/g;
|
|
141064
141074
|
var STRAY_SECTION_CHAR_REGEX = /\u00a7/g;
|
|
@@ -141071,6 +141081,9 @@ function stripCompleteTagPairsGlobally(value) {
|
|
|
141071
141081
|
function stripMalformedTagNotationGlobally(value) {
|
|
141072
141082
|
return value.replace(MALFORMED_TAG_GLOBAL_REGEX, "");
|
|
141073
141083
|
}
|
|
141084
|
+
function stripDanglingTagNotationGlobally(value) {
|
|
141085
|
+
return value.replace(DANGLING_TAG_GLOBAL_REGEX, "");
|
|
141086
|
+
}
|
|
141074
141087
|
function stripTagSectionCharacters(value) {
|
|
141075
141088
|
return value.replace(STRAY_SECTION_CHAR_REGEX, "");
|
|
141076
141089
|
}
|
|
@@ -141078,6 +141091,7 @@ function stripPersistedAssistantText(value) {
|
|
|
141078
141091
|
let text = stripWellFormedLeadingTagPrefix(value);
|
|
141079
141092
|
text = stripCompleteTagPairsGlobally(text);
|
|
141080
141093
|
text = stripMalformedTagNotationGlobally(text);
|
|
141094
|
+
text = stripDanglingTagNotationGlobally(text);
|
|
141081
141095
|
text = stripTagSectionCharacters(text);
|
|
141082
141096
|
return text.trim();
|
|
141083
141097
|
}
|
|
@@ -141090,6 +141104,7 @@ function stripTagPrefix(value) {
|
|
|
141090
141104
|
const prev = stripped;
|
|
141091
141105
|
stripped = stripped.replace(MALFORMED_TAG_PREFIX_REGEX, "");
|
|
141092
141106
|
stripped = stripped.replace(TAG_PREFIX_REGEX, "");
|
|
141107
|
+
stripped = stripped.replace(DANGLING_TAG_PREFIX_REGEX, "");
|
|
141093
141108
|
if (stripped === prev)
|
|
141094
141109
|
break;
|
|
141095
141110
|
}
|
|
@@ -141957,6 +141972,7 @@ var SESSION_META_SELECT_COLUMNS = [
|
|
|
141957
141972
|
"conversation_tokens",
|
|
141958
141973
|
"tool_call_tokens",
|
|
141959
141974
|
"cleared_reasoning_through_tag",
|
|
141975
|
+
"tool_reclaim_watermark",
|
|
141960
141976
|
"last_todo_state",
|
|
141961
141977
|
"cached_m0_bytes",
|
|
141962
141978
|
"cached_m1_bytes",
|
|
@@ -142005,6 +142021,7 @@ var META_COLUMNS = {
|
|
|
142005
142021
|
conversationTokens: "conversation_tokens",
|
|
142006
142022
|
toolCallTokens: "tool_call_tokens",
|
|
142007
142023
|
clearedReasoningThroughTag: "cleared_reasoning_through_tag",
|
|
142024
|
+
toolReclaimWatermark: "tool_reclaim_watermark",
|
|
142008
142025
|
lastTodoState: "last_todo_state",
|
|
142009
142026
|
cachedM0Bytes: "cached_m0_bytes",
|
|
142010
142027
|
cachedM1Bytes: "cached_m1_bytes",
|
|
@@ -142073,7 +142090,7 @@ function isSessionMetaRow(row) {
|
|
|
142073
142090
|
if (row === null || typeof row !== "object")
|
|
142074
142091
|
return false;
|
|
142075
142092
|
const r = row;
|
|
142076
|
-
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);
|
|
142093
|
+
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);
|
|
142077
142094
|
}
|
|
142078
142095
|
function getDefaultSessionMeta(sessionId) {
|
|
142079
142096
|
return {
|
|
@@ -142096,6 +142113,7 @@ function getDefaultSessionMeta(sessionId) {
|
|
|
142096
142113
|
conversationTokens: 0,
|
|
142097
142114
|
toolCallTokens: 0,
|
|
142098
142115
|
clearedReasoningThroughTag: 0,
|
|
142116
|
+
toolReclaimWatermark: 0,
|
|
142099
142117
|
lastTodoState: "",
|
|
142100
142118
|
cachedM0Bytes: null,
|
|
142101
142119
|
cachedM1Bytes: null,
|
|
@@ -142159,6 +142177,7 @@ function toSessionMeta(row) {
|
|
|
142159
142177
|
conversationTokens: numOrZero(row.conversation_tokens),
|
|
142160
142178
|
toolCallTokens: numOrZero(row.tool_call_tokens),
|
|
142161
142179
|
clearedReasoningThroughTag: numOrZero(row.cleared_reasoning_through_tag),
|
|
142180
|
+
toolReclaimWatermark: numOrZero(row.tool_reclaim_watermark),
|
|
142162
142181
|
lastTodoState: lastTodoStateRaw,
|
|
142163
142182
|
cachedM0Bytes: toBufferOrNull(row.cached_m0_bytes),
|
|
142164
142183
|
cachedM1Bytes: toBufferOrNull(row.cached_m1_bytes),
|
|
@@ -143598,6 +143617,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
143598
143617
|
ensureColumn(db, "session_meta", "historian_last_failure_at", "INTEGER DEFAULT NULL");
|
|
143599
143618
|
ensureColumn(db, "session_meta", "system_prompt_hash", "TEXT DEFAULT ''");
|
|
143600
143619
|
ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
|
|
143620
|
+
ensureColumn(db, "session_meta", "tool_reclaim_watermark", "INTEGER DEFAULT 0");
|
|
143601
143621
|
ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
|
|
143602
143622
|
ensureColumn(db, "session_meta", "stale_reduce_stripped_ids", "TEXT DEFAULT ''");
|
|
143603
143623
|
ensureColumn(db, "session_meta", "processed_image_stripped_ids", "TEXT DEFAULT ''");
|
|
@@ -146217,6 +146237,7 @@ var SESSION_META_FALLBACK_SELECTS = {
|
|
|
146217
146237
|
last_transform_error: "'' AS last_transform_error",
|
|
146218
146238
|
system_prompt_hash: "'' AS system_prompt_hash",
|
|
146219
146239
|
last_todo_state: "'' AS last_todo_state",
|
|
146240
|
+
tool_reclaim_watermark: "0 AS tool_reclaim_watermark",
|
|
146220
146241
|
cached_m0_bytes: "NULL AS cached_m0_bytes",
|
|
146221
146242
|
cached_m1_bytes: "NULL AS cached_m1_bytes",
|
|
146222
146243
|
cached_m0_project_memory_epoch: "NULL AS cached_m0_project_memory_epoch",
|
|
@@ -146294,6 +146315,14 @@ function updateSessionMeta(db, sessionId, updates) {
|
|
|
146294
146315
|
db.prepare(`UPDATE session_meta SET ${setClauses.join(", ")} WHERE session_id = ?`).run(...values, sessionId);
|
|
146295
146316
|
})();
|
|
146296
146317
|
}
|
|
146318
|
+
function advanceToolReclaimWatermark(db, sessionId, maxTagNumber) {
|
|
146319
|
+
if (maxTagNumber <= 0)
|
|
146320
|
+
return;
|
|
146321
|
+
db.transaction(() => {
|
|
146322
|
+
ensureSessionMetaRow(db, sessionId);
|
|
146323
|
+
db.prepare("UPDATE session_meta SET tool_reclaim_watermark = MAX(COALESCE(tool_reclaim_watermark, 0), ?) WHERE session_id = ?").run(maxTagNumber, sessionId);
|
|
146324
|
+
})();
|
|
146325
|
+
}
|
|
146297
146326
|
// ../plugin/src/features/magic-context/storage-notes.ts
|
|
146298
146327
|
var NOTE_TYPES = new Set(["session", "smart"]);
|
|
146299
146328
|
var NOTE_STATUSES = new Set(["active", "pending", "ready", "dismissed"]);
|
|
@@ -146691,6 +146720,26 @@ function getActiveTagTokenAggregate(db, sessionId, protectedTags = 0) {
|
|
|
146691
146720
|
nullCount: row?.null_count ?? 0
|
|
146692
146721
|
};
|
|
146693
146722
|
}
|
|
146723
|
+
function getOldestActiveUnprotectedToolTags(db, sessionId, protectedTags = 0, limit = 4) {
|
|
146724
|
+
if (limit <= 0)
|
|
146725
|
+
return [];
|
|
146726
|
+
const boundedLimit = Math.max(1, Math.min(10, Math.floor(limit)));
|
|
146727
|
+
const whereProtected = protectedTags > 0 ? `AND tag_number < (
|
|
146728
|
+
SELECT tag_number FROM tags
|
|
146729
|
+
WHERE session_id = ? AND status = 'active'
|
|
146730
|
+
ORDER BY tag_number DESC LIMIT 1 OFFSET ?
|
|
146731
|
+
)` : "";
|
|
146732
|
+
const params = protectedTags > 0 ? [sessionId, sessionId, protectedTags - 1, boundedLimit] : [sessionId, boundedLimit];
|
|
146733
|
+
const rows = db.prepare(`SELECT tag_number, tool_name
|
|
146734
|
+
FROM tags
|
|
146735
|
+
WHERE session_id = ? AND status = 'active' AND type = 'tool' ${whereProtected}
|
|
146736
|
+
ORDER BY tag_number ASC, id ASC
|
|
146737
|
+
LIMIT ?`).all(...params);
|
|
146738
|
+
return rows.filter((row) => typeof row.tag_number === "number").map((row) => ({
|
|
146739
|
+
tagNumber: row.tag_number,
|
|
146740
|
+
toolName: typeof row.tool_name === "string" ? row.tool_name : null
|
|
146741
|
+
}));
|
|
146742
|
+
}
|
|
146694
146743
|
function getTriggerTagTokenUpperBound(db, sessionId) {
|
|
146695
146744
|
const row = db.prepare(`SELECT
|
|
146696
146745
|
COALESCE(SUM(COALESCE(token_count, 0) + COALESCE(input_token_count, 0) + COALESCE(reasoning_token_count, 0)), 0) AS bound,
|
|
@@ -147743,13 +147792,13 @@ async function maybeSendUpgradeReminder(deps, sessionId) {
|
|
|
147743
147792
|
init_data_path();
|
|
147744
147793
|
import * as fs2 from "node:fs";
|
|
147745
147794
|
import * as path6 from "node:path";
|
|
147746
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
147795
|
+
var ANNOUNCEMENT_VERSION = "0.25.0";
|
|
147747
147796
|
var ANNOUNCEMENT_FEATURES = [
|
|
147748
|
-
"
|
|
147749
|
-
"
|
|
147750
|
-
"
|
|
147751
|
-
"
|
|
147752
|
-
"
|
|
147797
|
+
"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.",
|
|
147798
|
+
"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.",
|
|
147799
|
+
"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.",
|
|
147800
|
+
"Pi: fixed /ctx-dream (was failing with 'Unknown named parameter') and local-embedding load failures on Windows/Desktop (#151, #128).",
|
|
147801
|
+
"Runaway background agents on weak/local models are now capped and force-stopped (#154, #152). Plus several prompt-cache busts removed."
|
|
147753
147802
|
];
|
|
147754
147803
|
var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/F2uWxjGnU";
|
|
147755
147804
|
var STATE_FILENAME = "last_announced_version";
|
|
@@ -148693,7 +148742,7 @@ function enqueueDream(db, projectIdentity, reason, force = false) {
|
|
|
148693
148742
|
return db.transaction(() => {
|
|
148694
148743
|
if (!hasActiveDreamLease(db)) {
|
|
148695
148744
|
const staleThresholdMs = force ? 2 * 60 * 1000 : 120 * 60 * 1000;
|
|
148696
|
-
db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run(
|
|
148745
|
+
db.prepare("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?").run(projectIdentity, now - staleThresholdMs);
|
|
148697
148746
|
}
|
|
148698
148747
|
const existing = db.prepare("SELECT id FROM dream_queue WHERE project_path = ?").get(projectIdentity);
|
|
148699
148748
|
if (existing) {
|
|
@@ -148828,9 +148877,11 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
|
|
|
148828
148877
|
});
|
|
148829
148878
|
} catch (error) {
|
|
148830
148879
|
if (signal?.aborted) {
|
|
148880
|
+
await abortChildRun(client, args.path.id);
|
|
148831
148881
|
throw new Error("prompt aborted by external signal");
|
|
148832
148882
|
}
|
|
148833
148883
|
if (controller.signal.aborted) {
|
|
148884
|
+
await abortChildRun(client, args.path.id);
|
|
148834
148885
|
throw new Error(`prompt timed out after ${timeoutMs}ms`);
|
|
148835
148886
|
}
|
|
148836
148887
|
throw error;
|
|
@@ -148839,6 +148890,13 @@ async function promptWithTimeout(client, args, timeoutMs, signal) {
|
|
|
148839
148890
|
signal?.removeEventListener("abort", onExternalAbort);
|
|
148840
148891
|
}
|
|
148841
148892
|
}
|
|
148893
|
+
async function abortChildRun(client, sessionId) {
|
|
148894
|
+
try {
|
|
148895
|
+
await client.session.abort({ path: { id: sessionId } });
|
|
148896
|
+
} catch (error) {
|
|
148897
|
+
log(`[model-retry] child session abort failed for ${sessionId}: ${String(error)}`);
|
|
148898
|
+
}
|
|
148899
|
+
}
|
|
148842
148900
|
function isNonRetryable(error, externalSignal) {
|
|
148843
148901
|
if (externalSignal?.aborted)
|
|
148844
148902
|
return true;
|
|
@@ -149695,6 +149753,20 @@ function getDistinctStoredModelIds(db, projectPath) {
|
|
|
149695
149753
|
const rows = getDistinctStoredModelIdsStatement(db).all(projectPath);
|
|
149696
149754
|
return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
|
|
149697
149755
|
}
|
|
149756
|
+
function getMemoryEmbedCoverage(db, projectPath, modelId) {
|
|
149757
|
+
const row = db.prepare(`SELECT
|
|
149758
|
+
COUNT(*) AS total,
|
|
149759
|
+
SUM(CASE WHEN EXISTS (
|
|
149760
|
+
SELECT 1 FROM memory_embeddings e
|
|
149761
|
+
WHERE e.memory_id = m.id AND e.model_id = ?
|
|
149762
|
+
) THEN 1 ELSE 0 END) AS embedded
|
|
149763
|
+
FROM memories m
|
|
149764
|
+
WHERE m.project_path = ? AND m.status = 'active'`).get(modelId, projectPath);
|
|
149765
|
+
return {
|
|
149766
|
+
total: typeof row?.total === "number" ? row.total : 0,
|
|
149767
|
+
embedded: typeof row?.embedded === "number" ? row.embedded : 0
|
|
149768
|
+
};
|
|
149769
|
+
}
|
|
149698
149770
|
|
|
149699
149771
|
// ../plugin/src/features/magic-context/memory/embedding-cache.ts
|
|
149700
149772
|
var DEFAULT_EMBEDDING_CACHE_TTL_MS = 60000;
|
|
@@ -166733,6 +166805,28 @@ function countUnembeddedSessionCompartments(db, projectPath, sessionId, modelId)
|
|
|
166733
166805
|
)`).get(projectPath, sessionId, projectPath, modelId);
|
|
166734
166806
|
return typeof row?.n === "number" ? row.n : 0;
|
|
166735
166807
|
}
|
|
166808
|
+
function countSessionCompartmentEmbedCoverage(db, projectPath, sessionId, modelId) {
|
|
166809
|
+
const row = db.prepare(`SELECT
|
|
166810
|
+
COUNT(*) AS total,
|
|
166811
|
+
SUM(CASE WHEN EXISTS (
|
|
166812
|
+
SELECT 1 FROM compartment_chunk_embeddings e
|
|
166813
|
+
WHERE e.compartment_id = c.id
|
|
166814
|
+
AND e.project_path = ?
|
|
166815
|
+
AND e.model_id = ?
|
|
166816
|
+
) THEN 1 ELSE 0 END) AS embedded
|
|
166817
|
+
FROM compartments c
|
|
166818
|
+
JOIN session_projects sp
|
|
166819
|
+
ON sp.session_id = c.session_id
|
|
166820
|
+
AND sp.harness = c.harness
|
|
166821
|
+
AND sp.project_path = ?
|
|
166822
|
+
WHERE c.session_id = ?
|
|
166823
|
+
AND c.start_message IS NOT NULL
|
|
166824
|
+
AND c.end_message IS NOT NULL`).get(projectPath, modelId, projectPath, sessionId);
|
|
166825
|
+
return {
|
|
166826
|
+
total: typeof row?.total === "number" ? row.total : 0,
|
|
166827
|
+
embedded: typeof row?.embedded === "number" ? row.embedded : 0
|
|
166828
|
+
};
|
|
166829
|
+
}
|
|
166736
166830
|
|
|
166737
166831
|
// ../plugin/src/features/magic-context/memory/cosine-similarity.ts
|
|
166738
166832
|
function cosineSimilarity(a, b) {
|
|
@@ -166875,6 +166969,19 @@ async function withQuietConsole(fn) {
|
|
|
166875
166969
|
console.error = origError;
|
|
166876
166970
|
}
|
|
166877
166971
|
}
|
|
166972
|
+
var nativeRuntimeMissing = false;
|
|
166973
|
+
function isNativeRuntimeMissingError(error51) {
|
|
166974
|
+
const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
|
|
166975
|
+
const lower = message.toLowerCase();
|
|
166976
|
+
const code = error51?.code;
|
|
166977
|
+
const name2 = error51?.name;
|
|
166978
|
+
if (code === "ERR_DLOPEN_FAILED" && lower.includes("onnxruntime")) {
|
|
166979
|
+
return true;
|
|
166980
|
+
}
|
|
166981
|
+
if (!lower.includes("onnxruntime-node"))
|
|
166982
|
+
return false;
|
|
166983
|
+
return code === "ERR_MODULE_NOT_FOUND" || name2 === "ResolveMessage" || lower.includes("cannot find package") || lower.includes("cannot find module") || lower.includes("err_module_not_found");
|
|
166984
|
+
}
|
|
166878
166985
|
function isTransientLoadError(error51) {
|
|
166879
166986
|
const message = error51 instanceof Error ? error51.message : String(error51 ?? "");
|
|
166880
166987
|
if (!message)
|
|
@@ -166939,6 +167046,9 @@ class LocalEmbeddingProvider {
|
|
|
166939
167046
|
if (this.pipeline) {
|
|
166940
167047
|
return true;
|
|
166941
167048
|
}
|
|
167049
|
+
if (nativeRuntimeMissing) {
|
|
167050
|
+
return false;
|
|
167051
|
+
}
|
|
166942
167052
|
if (this.initPromise) {
|
|
166943
167053
|
await this.initPromise;
|
|
166944
167054
|
return this.pipeline !== null;
|
|
@@ -167005,7 +167115,12 @@ class LocalEmbeddingProvider {
|
|
|
167005
167115
|
await releaseLock();
|
|
167006
167116
|
}
|
|
167007
167117
|
} catch (error51) {
|
|
167008
|
-
|
|
167118
|
+
if (isNativeRuntimeMissingError(error51)) {
|
|
167119
|
+
nativeRuntimeMissing = true;
|
|
167120
|
+
log("[magic-context] local embedding runtime is not installed (onnxruntime-node missing from this install). Local embeddings are disabled. Fix: reinstall the plugin (run `npx @cortexkit/magic-context@latest doctor --force`), or configure an `openai-compatible`/`ollama` embedding endpoint instead. Existing memories are unaffected.");
|
|
167121
|
+
} else {
|
|
167122
|
+
log("[magic-context] embedding model failed to load:", error51);
|
|
167123
|
+
}
|
|
167009
167124
|
this.pipeline = null;
|
|
167010
167125
|
} finally {
|
|
167011
167126
|
this.initPromise = null;
|
|
@@ -167519,6 +167634,118 @@ function getDistinctCommitEmbeddingModelIds(db, projectPath) {
|
|
|
167519
167634
|
return new Set(rows.map((row) => typeof row.modelId === "string" ? row.modelId : null));
|
|
167520
167635
|
}
|
|
167521
167636
|
|
|
167637
|
+
// ../plugin/src/features/magic-context/git-commits/storage-git-commits.ts
|
|
167638
|
+
init_logger();
|
|
167639
|
+
var insertStatements = new WeakMap;
|
|
167640
|
+
var existingShasStatements = new WeakMap;
|
|
167641
|
+
var projectCountStatements = new WeakMap;
|
|
167642
|
+
var evictStatements = new WeakMap;
|
|
167643
|
+
var evictOverflowStatements = new WeakMap;
|
|
167644
|
+
var latestCommitTimeStatements = new WeakMap;
|
|
167645
|
+
function getInsertStatement(db) {
|
|
167646
|
+
let stmt = insertStatements.get(db);
|
|
167647
|
+
if (!stmt) {
|
|
167648
|
+
stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
|
|
167649
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
167650
|
+
ON CONFLICT(sha) DO UPDATE SET
|
|
167651
|
+
project_path = excluded.project_path,
|
|
167652
|
+
short_sha = excluded.short_sha,
|
|
167653
|
+
message = excluded.message,
|
|
167654
|
+
author = excluded.author,
|
|
167655
|
+
committed_at = excluded.committed_at,
|
|
167656
|
+
indexed_at = excluded.indexed_at
|
|
167657
|
+
WHERE git_commits.message != excluded.message`);
|
|
167658
|
+
insertStatements.set(db, stmt);
|
|
167659
|
+
}
|
|
167660
|
+
return stmt;
|
|
167661
|
+
}
|
|
167662
|
+
function getExistingShasStatement(db) {
|
|
167663
|
+
let stmt = existingShasStatements.get(db);
|
|
167664
|
+
if (!stmt) {
|
|
167665
|
+
stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
|
|
167666
|
+
existingShasStatements.set(db, stmt);
|
|
167667
|
+
}
|
|
167668
|
+
return stmt;
|
|
167669
|
+
}
|
|
167670
|
+
function getProjectCountStatement(db) {
|
|
167671
|
+
let stmt = projectCountStatements.get(db);
|
|
167672
|
+
if (!stmt) {
|
|
167673
|
+
stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
|
|
167674
|
+
projectCountStatements.set(db, stmt);
|
|
167675
|
+
}
|
|
167676
|
+
return stmt;
|
|
167677
|
+
}
|
|
167678
|
+
function getLatestCommitTimeStatement(db) {
|
|
167679
|
+
let stmt = latestCommitTimeStatements.get(db);
|
|
167680
|
+
if (!stmt) {
|
|
167681
|
+
stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
|
|
167682
|
+
latestCommitTimeStatements.set(db, stmt);
|
|
167683
|
+
}
|
|
167684
|
+
return stmt;
|
|
167685
|
+
}
|
|
167686
|
+
function getEvictOverflowStatement(db) {
|
|
167687
|
+
let stmt = evictOverflowStatements.get(db);
|
|
167688
|
+
if (!stmt) {
|
|
167689
|
+
stmt = db.prepare(`DELETE FROM git_commits
|
|
167690
|
+
WHERE rowid IN (
|
|
167691
|
+
SELECT rowid FROM git_commits
|
|
167692
|
+
WHERE project_path = ?
|
|
167693
|
+
ORDER BY committed_at DESC, sha DESC
|
|
167694
|
+
LIMIT -1 OFFSET ?
|
|
167695
|
+
)`);
|
|
167696
|
+
evictOverflowStatements.set(db, stmt);
|
|
167697
|
+
}
|
|
167698
|
+
return stmt;
|
|
167699
|
+
}
|
|
167700
|
+
function upsertCommits(db, projectPath, commits) {
|
|
167701
|
+
if (commits.length === 0)
|
|
167702
|
+
return { inserted: 0, updated: 0 };
|
|
167703
|
+
const existing = new Set;
|
|
167704
|
+
for (const row of getExistingShasStatement(db).all(projectPath)) {
|
|
167705
|
+
existing.add(row.sha);
|
|
167706
|
+
}
|
|
167707
|
+
let inserted = 0;
|
|
167708
|
+
let updated = 0;
|
|
167709
|
+
const now = Date.now();
|
|
167710
|
+
const insertStmt = getInsertStatement(db);
|
|
167711
|
+
db.transaction(() => {
|
|
167712
|
+
for (const commit of commits) {
|
|
167713
|
+
const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
|
|
167714
|
+
if (result.changes > 0) {
|
|
167715
|
+
if (existing.has(commit.sha)) {
|
|
167716
|
+
updated++;
|
|
167717
|
+
} else {
|
|
167718
|
+
inserted++;
|
|
167719
|
+
existing.add(commit.sha);
|
|
167720
|
+
}
|
|
167721
|
+
}
|
|
167722
|
+
}
|
|
167723
|
+
})();
|
|
167724
|
+
return { inserted, updated };
|
|
167725
|
+
}
|
|
167726
|
+
function getCommitCount(db, projectPath) {
|
|
167727
|
+
const row = getProjectCountStatement(db).get(projectPath);
|
|
167728
|
+
return row?.count ?? 0;
|
|
167729
|
+
}
|
|
167730
|
+
function getLatestIndexedCommitTimeMs(db, projectPath) {
|
|
167731
|
+
const row = getLatestCommitTimeStatement(db).get(projectPath);
|
|
167732
|
+
return row?.latest ?? null;
|
|
167733
|
+
}
|
|
167734
|
+
function enforceProjectCap(db, projectPath, maxCommits) {
|
|
167735
|
+
if (maxCommits <= 0)
|
|
167736
|
+
return 0;
|
|
167737
|
+
const count = getCommitCount(db, projectPath);
|
|
167738
|
+
if (count <= maxCommits)
|
|
167739
|
+
return 0;
|
|
167740
|
+
getEvictOverflowStatement(db).run(projectPath, maxCommits);
|
|
167741
|
+
const after = getCommitCount(db, projectPath);
|
|
167742
|
+
const evicted = Math.max(0, count - after);
|
|
167743
|
+
if (evicted > 0) {
|
|
167744
|
+
log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
|
|
167745
|
+
}
|
|
167746
|
+
return evicted;
|
|
167747
|
+
}
|
|
167748
|
+
|
|
167522
167749
|
// ../plugin/src/features/magic-context/git-commits/sweep-coordinator.ts
|
|
167523
167750
|
var GIT_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
|
|
167524
167751
|
var GIT_SWEEP_LEASE_TTL_MS = 5 * 60 * 1000;
|
|
@@ -167713,8 +167940,12 @@ function repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectPath) {
|
|
|
167713
167940
|
var OFF_PROVIDER_IDENTITY = "embedding-provider:off";
|
|
167714
167941
|
var SWEEP_MAX_WALL_CLOCK_MS = 10 * 60 * 1000;
|
|
167715
167942
|
var CHUNK_DRAIN_BATCH_SIZE = 8;
|
|
167716
|
-
var MAX_WINDOWS_PER_EMBED_CALL =
|
|
167943
|
+
var MAX_WINDOWS_PER_EMBED_CALL = 2;
|
|
167717
167944
|
var SESSION_EMBED_LEASE_RENEWAL_MS = 60 * 1000;
|
|
167945
|
+
var EMBED_SLICE_RETRY_ATTEMPTS = 3;
|
|
167946
|
+
var EMBED_SLICE_RETRY_BASE_MS = 250;
|
|
167947
|
+
var EMBED_SLOW_FAILURE_NO_RETRY_MS = 1e4;
|
|
167948
|
+
var MAX_CONSECUTIVE_FAILED_BATCHES = 3;
|
|
167718
167949
|
var projectRegistrations = new Map;
|
|
167719
167950
|
var loadUnembeddedMemoriesStatements = new WeakMap;
|
|
167720
167951
|
var globalRegistrationGeneration = 0;
|
|
@@ -167814,7 +168045,9 @@ function snapshotFor(registration) {
|
|
|
167814
168045
|
enabled,
|
|
167815
168046
|
gitCommitEnabled,
|
|
167816
168047
|
modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
|
|
167817
|
-
chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId
|
|
168048
|
+
chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId,
|
|
168049
|
+
model: registration.observationMode || !providerIsOn ? "off" : ("model" in registration.config) && registration.config.model.trim() ? registration.config.model.trim() : registration.modelId,
|
|
168050
|
+
provider: registration.observationMode || !providerIsOn ? "off" : registration.config.provider ?? "local"
|
|
167818
168051
|
};
|
|
167819
168052
|
}
|
|
167820
168053
|
function disposeProvider(provider) {
|
|
@@ -168025,8 +168258,9 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
168025
168258
|
}
|
|
168026
168259
|
async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates, signal) {
|
|
168027
168260
|
const noWork = [];
|
|
168261
|
+
const failed = [];
|
|
168028
168262
|
if (candidates.length === 0)
|
|
168029
|
-
return { embedded: 0, noWork };
|
|
168263
|
+
return { embedded: 0, noWork, failed };
|
|
168030
168264
|
const maxInputTokens = getProjectEmbeddingMaxInputTokens(projectIdentity);
|
|
168031
168265
|
const prepared = [];
|
|
168032
168266
|
for (const candidate of candidates) {
|
|
@@ -168043,7 +168277,7 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
168043
168277
|
prepared.push({ candidate, windows });
|
|
168044
168278
|
}
|
|
168045
168279
|
if (prepared.length === 0)
|
|
168046
|
-
return { embedded: 0, noWork };
|
|
168280
|
+
return { embedded: 0, noWork, failed };
|
|
168047
168281
|
let embedded = 0;
|
|
168048
168282
|
let i = 0;
|
|
168049
168283
|
while (i < prepared.length) {
|
|
@@ -168060,35 +168294,60 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
168060
168294
|
const texts = [];
|
|
168061
168295
|
for (const item of slice)
|
|
168062
168296
|
texts.push(...item.windows.map((w) => w.text));
|
|
168063
|
-
|
|
168064
|
-
|
|
168065
|
-
if (!result)
|
|
168066
|
-
continue;
|
|
168297
|
+
const persistedIds = new Set;
|
|
168298
|
+
for (let attempt = 0;attempt < EMBED_SLICE_RETRY_ATTEMPTS; attempt++) {
|
|
168067
168299
|
if (signal?.aborted)
|
|
168068
168300
|
break;
|
|
168069
|
-
let
|
|
168070
|
-
|
|
168071
|
-
|
|
168072
|
-
|
|
168073
|
-
|
|
168074
|
-
|
|
168301
|
+
let result = null;
|
|
168302
|
+
const attemptStart = Date.now();
|
|
168303
|
+
try {
|
|
168304
|
+
result = await embedBatchForProject(projectIdentity, texts, signal);
|
|
168305
|
+
} catch (error51) {
|
|
168306
|
+
log("[magic-context] failed to proactively embed compartment chunks:", error51);
|
|
168307
|
+
}
|
|
168308
|
+
if (signal?.aborted)
|
|
168309
|
+
break;
|
|
168310
|
+
if (result) {
|
|
168311
|
+
let offset = 0;
|
|
168312
|
+
for (const item of slice) {
|
|
168313
|
+
const vectors = result.vectors.slice(offset, offset + item.windows.length);
|
|
168314
|
+
offset += item.windows.length;
|
|
168315
|
+
if (persistedIds.has(item.candidate.id))
|
|
168316
|
+
continue;
|
|
168317
|
+
if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
|
|
168318
|
+
continue;
|
|
168319
|
+
}
|
|
168320
|
+
const rows = item.windows.map((window, index) => ({
|
|
168321
|
+
compartmentId: item.candidate.id,
|
|
168322
|
+
sessionId: item.candidate.sessionId,
|
|
168323
|
+
projectPath: projectIdentity,
|
|
168324
|
+
window,
|
|
168325
|
+
modelId,
|
|
168326
|
+
vector: vectors[index]
|
|
168327
|
+
}));
|
|
168328
|
+
replaceCompartmentChunkEmbeddings(db, rows);
|
|
168329
|
+
persistedIds.add(item.candidate.id);
|
|
168075
168330
|
}
|
|
168076
|
-
const rows = item.windows.map((window, index) => ({
|
|
168077
|
-
compartmentId: item.candidate.id,
|
|
168078
|
-
sessionId: item.candidate.sessionId,
|
|
168079
|
-
projectPath: projectIdentity,
|
|
168080
|
-
window,
|
|
168081
|
-
modelId,
|
|
168082
|
-
vector: vectors[index]
|
|
168083
|
-
}));
|
|
168084
|
-
replaceCompartmentChunkEmbeddings(db, rows);
|
|
168085
|
-
embedded += 1;
|
|
168086
168331
|
}
|
|
168087
|
-
|
|
168088
|
-
|
|
168332
|
+
if (persistedIds.size === slice.length)
|
|
168333
|
+
break;
|
|
168334
|
+
if (persistedIds.size > 0)
|
|
168335
|
+
break;
|
|
168336
|
+
if (Date.now() - attemptStart >= EMBED_SLOW_FAILURE_NO_RETRY_MS)
|
|
168337
|
+
break;
|
|
168338
|
+
if (attempt < EMBED_SLICE_RETRY_ATTEMPTS - 1) {
|
|
168339
|
+
await new Promise((resolve4) => setTimeout(resolve4, EMBED_SLICE_RETRY_BASE_MS * 2 ** attempt));
|
|
168340
|
+
}
|
|
168341
|
+
}
|
|
168342
|
+
embedded += persistedIds.size;
|
|
168343
|
+
if (!signal?.aborted) {
|
|
168344
|
+
for (const item of slice) {
|
|
168345
|
+
if (!persistedIds.has(item.candidate.id))
|
|
168346
|
+
failed.push(item.candidate.id);
|
|
168347
|
+
}
|
|
168089
168348
|
}
|
|
168090
168349
|
}
|
|
168091
|
-
return { embedded, noWork };
|
|
168350
|
+
return { embedded, noWork, failed };
|
|
168092
168351
|
}
|
|
168093
168352
|
async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, options) {
|
|
168094
168353
|
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
@@ -168111,9 +168370,11 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
168111
168370
|
renewal.unref?.();
|
|
168112
168371
|
const batchSize = Math.max(1, options?.batchSize ?? CHUNK_DRAIN_BATCH_SIZE);
|
|
168113
168372
|
const skipIds = [];
|
|
168373
|
+
const failedIds = [];
|
|
168114
168374
|
let embedded = 0;
|
|
168115
168375
|
let aborted2 = false;
|
|
168116
|
-
let
|
|
168376
|
+
let providerDown = false;
|
|
168377
|
+
let consecutiveFailedBatches = 0;
|
|
168117
168378
|
try {
|
|
168118
168379
|
options?.onProgress?.({ embedded, total });
|
|
168119
168380
|
for (;; ) {
|
|
@@ -168121,15 +168382,26 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
168121
168382
|
aborted2 = true;
|
|
168122
168383
|
break;
|
|
168123
168384
|
}
|
|
168124
|
-
const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, skipIds);
|
|
168385
|
+
const candidates = loadUnembeddedSessionChunkCandidates(db, projectIdentity, sessionId, snapshot.chunkModelId, batchSize, [...skipIds, ...failedIds]);
|
|
168125
168386
|
if (candidates.length === 0)
|
|
168126
168387
|
break;
|
|
168127
|
-
const {
|
|
168388
|
+
const {
|
|
168389
|
+
embedded: n,
|
|
168390
|
+
noWork,
|
|
168391
|
+
failed
|
|
168392
|
+
} = await embedCandidateChunkBatch(db, projectIdentity, snapshot.chunkModelId, candidates, options?.signal);
|
|
168128
168393
|
for (const id of noWork)
|
|
168129
168394
|
skipIds.push(id);
|
|
168395
|
+
for (const id of failed)
|
|
168396
|
+
failedIds.push(id);
|
|
168130
168397
|
if (n === 0 && noWork.length === 0) {
|
|
168131
|
-
|
|
168132
|
-
|
|
168398
|
+
consecutiveFailedBatches += 1;
|
|
168399
|
+
if (consecutiveFailedBatches >= MAX_CONSECUTIVE_FAILED_BATCHES) {
|
|
168400
|
+
providerDown = true;
|
|
168401
|
+
break;
|
|
168402
|
+
}
|
|
168403
|
+
} else {
|
|
168404
|
+
consecutiveFailedBatches = 0;
|
|
168133
168405
|
}
|
|
168134
168406
|
embedded += n;
|
|
168135
168407
|
options?.onProgress?.({ embedded: Math.min(embedded, total), total });
|
|
@@ -168137,16 +168409,50 @@ async function embedSessionCompartmentChunks(db, projectIdentity, sessionId, opt
|
|
|
168137
168409
|
}
|
|
168138
168410
|
} finally {
|
|
168139
168411
|
clearInterval(renewal);
|
|
168140
|
-
|
|
168412
|
+
try {
|
|
168413
|
+
releaseGitSweepLease(db, projectIdentity, holderId);
|
|
168414
|
+
} catch (error51) {
|
|
168415
|
+
log("[magic-context] embed drain: lease release failed (will TTL-expire):", error51);
|
|
168416
|
+
}
|
|
168141
168417
|
}
|
|
168142
168418
|
if (aborted2)
|
|
168143
|
-
return { status: "aborted", embedded, total };
|
|
168144
|
-
if (
|
|
168419
|
+
return { status: "aborted", embedded, total, failed: failedIds.length };
|
|
168420
|
+
if (providerDown || failedIds.length > 0) {
|
|
168145
168421
|
const remaining = Math.max(0, countUnembeddedSessionCompartments(db, projectIdentity, sessionId, snapshot.chunkModelId) - skipIds.length);
|
|
168146
|
-
if (remaining > 0)
|
|
168147
|
-
return { status: "stalled", embedded, total, remaining };
|
|
168422
|
+
if (remaining > 0) {
|
|
168423
|
+
return { status: "stalled", embedded, total, remaining, failed: failedIds.length };
|
|
168424
|
+
}
|
|
168148
168425
|
}
|
|
168149
|
-
return { status: "done", embedded, total };
|
|
168426
|
+
return { status: "done", embedded, total, failed: failedIds.length };
|
|
168427
|
+
}
|
|
168428
|
+
function getEmbeddingCoverageStatus(db, projectIdentity, sessionId) {
|
|
168429
|
+
const snapshot = getProjectEmbeddingSnapshot(projectIdentity);
|
|
168430
|
+
if (!snapshot?.enabled || snapshot.chunkModelId === "off") {
|
|
168431
|
+
return {
|
|
168432
|
+
enabled: false,
|
|
168433
|
+
model: snapshot?.model ?? "off",
|
|
168434
|
+
provider: snapshot?.provider ?? "off",
|
|
168435
|
+
session: { embedded: 0, total: 0 },
|
|
168436
|
+
memories: { embedded: 0, total: 0 },
|
|
168437
|
+
commits: { embedded: 0, total: 0, gitEnabled: false }
|
|
168438
|
+
};
|
|
168439
|
+
}
|
|
168440
|
+
const session = countSessionCompartmentEmbedCoverage(db, projectIdentity, sessionId, snapshot.chunkModelId);
|
|
168441
|
+
const memories = getMemoryEmbedCoverage(db, projectIdentity, snapshot.modelId);
|
|
168442
|
+
const gitEnabled = snapshot.gitCommitEnabled;
|
|
168443
|
+
const commits = gitEnabled ? {
|
|
168444
|
+
embedded: countEmbeddedCommits(db, projectIdentity),
|
|
168445
|
+
total: getCommitCount(db, projectIdentity),
|
|
168446
|
+
gitEnabled: true
|
|
168447
|
+
} : { embedded: 0, total: 0, gitEnabled: false };
|
|
168448
|
+
return {
|
|
168449
|
+
enabled: true,
|
|
168450
|
+
model: snapshot.model,
|
|
168451
|
+
provider: snapshot.provider,
|
|
168452
|
+
session,
|
|
168453
|
+
memories,
|
|
168454
|
+
commits
|
|
168455
|
+
};
|
|
168150
168456
|
}
|
|
168151
168457
|
|
|
168152
168458
|
// ../plugin/src/features/magic-context/memory/embedding.ts
|
|
@@ -168195,118 +168501,6 @@ async function embedText(text, signal) {
|
|
|
168195
168501
|
}
|
|
168196
168502
|
var SWEEP_MAX_WALL_CLOCK_MS2 = 10 * 60 * 1000;
|
|
168197
168503
|
|
|
168198
|
-
// ../plugin/src/features/magic-context/git-commits/storage-git-commits.ts
|
|
168199
|
-
init_logger();
|
|
168200
|
-
var insertStatements = new WeakMap;
|
|
168201
|
-
var existingShasStatements = new WeakMap;
|
|
168202
|
-
var projectCountStatements = new WeakMap;
|
|
168203
|
-
var evictStatements = new WeakMap;
|
|
168204
|
-
var evictOverflowStatements = new WeakMap;
|
|
168205
|
-
var latestCommitTimeStatements = new WeakMap;
|
|
168206
|
-
function getInsertStatement(db) {
|
|
168207
|
-
let stmt = insertStatements.get(db);
|
|
168208
|
-
if (!stmt) {
|
|
168209
|
-
stmt = db.prepare(`INSERT INTO git_commits (sha, project_path, short_sha, message, author, committed_at, indexed_at)
|
|
168210
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
168211
|
-
ON CONFLICT(sha) DO UPDATE SET
|
|
168212
|
-
project_path = excluded.project_path,
|
|
168213
|
-
short_sha = excluded.short_sha,
|
|
168214
|
-
message = excluded.message,
|
|
168215
|
-
author = excluded.author,
|
|
168216
|
-
committed_at = excluded.committed_at,
|
|
168217
|
-
indexed_at = excluded.indexed_at
|
|
168218
|
-
WHERE git_commits.message != excluded.message`);
|
|
168219
|
-
insertStatements.set(db, stmt);
|
|
168220
|
-
}
|
|
168221
|
-
return stmt;
|
|
168222
|
-
}
|
|
168223
|
-
function getExistingShasStatement(db) {
|
|
168224
|
-
let stmt = existingShasStatements.get(db);
|
|
168225
|
-
if (!stmt) {
|
|
168226
|
-
stmt = db.prepare("SELECT sha FROM git_commits WHERE project_path = ?");
|
|
168227
|
-
existingShasStatements.set(db, stmt);
|
|
168228
|
-
}
|
|
168229
|
-
return stmt;
|
|
168230
|
-
}
|
|
168231
|
-
function getProjectCountStatement(db) {
|
|
168232
|
-
let stmt = projectCountStatements.get(db);
|
|
168233
|
-
if (!stmt) {
|
|
168234
|
-
stmt = db.prepare("SELECT COUNT(*) AS count FROM git_commits WHERE project_path = ?");
|
|
168235
|
-
projectCountStatements.set(db, stmt);
|
|
168236
|
-
}
|
|
168237
|
-
return stmt;
|
|
168238
|
-
}
|
|
168239
|
-
function getLatestCommitTimeStatement(db) {
|
|
168240
|
-
let stmt = latestCommitTimeStatements.get(db);
|
|
168241
|
-
if (!stmt) {
|
|
168242
|
-
stmt = db.prepare("SELECT MAX(committed_at) AS latest FROM git_commits WHERE project_path = ?");
|
|
168243
|
-
latestCommitTimeStatements.set(db, stmt);
|
|
168244
|
-
}
|
|
168245
|
-
return stmt;
|
|
168246
|
-
}
|
|
168247
|
-
function getEvictOverflowStatement(db) {
|
|
168248
|
-
let stmt = evictOverflowStatements.get(db);
|
|
168249
|
-
if (!stmt) {
|
|
168250
|
-
stmt = db.prepare(`DELETE FROM git_commits
|
|
168251
|
-
WHERE rowid IN (
|
|
168252
|
-
SELECT rowid FROM git_commits
|
|
168253
|
-
WHERE project_path = ?
|
|
168254
|
-
ORDER BY committed_at DESC, sha DESC
|
|
168255
|
-
LIMIT -1 OFFSET ?
|
|
168256
|
-
)`);
|
|
168257
|
-
evictOverflowStatements.set(db, stmt);
|
|
168258
|
-
}
|
|
168259
|
-
return stmt;
|
|
168260
|
-
}
|
|
168261
|
-
function upsertCommits(db, projectPath, commits) {
|
|
168262
|
-
if (commits.length === 0)
|
|
168263
|
-
return { inserted: 0, updated: 0 };
|
|
168264
|
-
const existing = new Set;
|
|
168265
|
-
for (const row of getExistingShasStatement(db).all(projectPath)) {
|
|
168266
|
-
existing.add(row.sha);
|
|
168267
|
-
}
|
|
168268
|
-
let inserted = 0;
|
|
168269
|
-
let updated = 0;
|
|
168270
|
-
const now = Date.now();
|
|
168271
|
-
const insertStmt = getInsertStatement(db);
|
|
168272
|
-
db.transaction(() => {
|
|
168273
|
-
for (const commit of commits) {
|
|
168274
|
-
const result = insertStmt.run(commit.sha, projectPath, commit.shortSha, commit.message, commit.author, commit.committedAtMs, now);
|
|
168275
|
-
if (result.changes > 0) {
|
|
168276
|
-
if (existing.has(commit.sha)) {
|
|
168277
|
-
updated++;
|
|
168278
|
-
} else {
|
|
168279
|
-
inserted++;
|
|
168280
|
-
existing.add(commit.sha);
|
|
168281
|
-
}
|
|
168282
|
-
}
|
|
168283
|
-
}
|
|
168284
|
-
})();
|
|
168285
|
-
return { inserted, updated };
|
|
168286
|
-
}
|
|
168287
|
-
function getCommitCount(db, projectPath) {
|
|
168288
|
-
const row = getProjectCountStatement(db).get(projectPath);
|
|
168289
|
-
return row?.count ?? 0;
|
|
168290
|
-
}
|
|
168291
|
-
function getLatestIndexedCommitTimeMs(db, projectPath) {
|
|
168292
|
-
const row = getLatestCommitTimeStatement(db).get(projectPath);
|
|
168293
|
-
return row?.latest ?? null;
|
|
168294
|
-
}
|
|
168295
|
-
function enforceProjectCap(db, projectPath, maxCommits) {
|
|
168296
|
-
if (maxCommits <= 0)
|
|
168297
|
-
return 0;
|
|
168298
|
-
const count = getCommitCount(db, projectPath);
|
|
168299
|
-
if (count <= maxCommits)
|
|
168300
|
-
return 0;
|
|
168301
|
-
getEvictOverflowStatement(db).run(projectPath, maxCommits);
|
|
168302
|
-
const after = getCommitCount(db, projectPath);
|
|
168303
|
-
const evicted = Math.max(0, count - after);
|
|
168304
|
-
if (evicted > 0) {
|
|
168305
|
-
log(`[git-commits] evicted ${evicted} oldest commits for project ${projectPath} (cap=${maxCommits}, was=${count})`);
|
|
168306
|
-
}
|
|
168307
|
-
return evicted;
|
|
168308
|
-
}
|
|
168309
|
-
|
|
168310
168504
|
// ../plugin/src/features/magic-context/git-commits/indexer.ts
|
|
168311
168505
|
var MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
168312
168506
|
var EMBED_BATCH_SIZE = 16;
|
|
@@ -169836,87 +170030,229 @@ function registerCtxDreamCommand(pi, deps) {
|
|
|
169836
170030
|
});
|
|
169837
170031
|
}
|
|
169838
170032
|
|
|
169839
|
-
// src/
|
|
169840
|
-
|
|
169841
|
-
|
|
169842
|
-
|
|
169843
|
-
|
|
170033
|
+
// ../plugin/src/hooks/magic-context/embed-session-state.ts
|
|
170034
|
+
var embedPauseBySession = new Set;
|
|
170035
|
+
var embedRunStateBySession = new Map;
|
|
170036
|
+
var autoEmbedAttemptedBySession = new Set;
|
|
170037
|
+
|
|
170038
|
+
// ../plugin/src/hooks/magic-context/format-embed-status.ts
|
|
170039
|
+
function formatEmbedStatusText(coverage, drain) {
|
|
170040
|
+
if (!coverage.enabled) {
|
|
170041
|
+
return "Embedding is off (no provider configured).";
|
|
170042
|
+
}
|
|
170043
|
+
const lines = [];
|
|
170044
|
+
lines.push(`Embedding — model: ${coverage.model} (${coverage.provider})`);
|
|
170045
|
+
lines.push(`This session: ${coverage.session.embedded} / ${coverage.session.total} compartments embedded`);
|
|
170046
|
+
lines.push(`Project memories: ${coverage.memories.embedded} / ${coverage.memories.total} embedded`);
|
|
170047
|
+
if (coverage.commits.gitEnabled) {
|
|
170048
|
+
lines.push(`Git commits: ${coverage.commits.embedded} / ${coverage.commits.total}`);
|
|
170049
|
+
} else {
|
|
170050
|
+
lines.push("Git commits: 0 / 0 (git indexing off)");
|
|
170051
|
+
}
|
|
170052
|
+
let drainLine = "Drain: idle";
|
|
170053
|
+
switch (drain.status) {
|
|
170054
|
+
case "running": {
|
|
170055
|
+
const e = drain.embedded ?? coverage.session.embedded;
|
|
170056
|
+
const t = drain.total ?? coverage.session.total;
|
|
170057
|
+
const failedSuffix = drain.failed && drain.failed > 0 ? ` (${drain.failed} failed)` : "";
|
|
170058
|
+
drainLine = `Drain: running ${e}/${t}${failedSuffix}`;
|
|
170059
|
+
break;
|
|
170060
|
+
}
|
|
170061
|
+
case "paused": {
|
|
170062
|
+
const e = drain.embedded ?? coverage.session.embedded;
|
|
170063
|
+
const t = drain.total ?? coverage.session.total;
|
|
170064
|
+
drainLine = `Drain: paused ${e}/${t}`;
|
|
170065
|
+
break;
|
|
170066
|
+
}
|
|
170067
|
+
case "stopped":
|
|
170068
|
+
drainLine = "Drain: stopped (provider down)";
|
|
170069
|
+
break;
|
|
170070
|
+
default:
|
|
170071
|
+
drainLine = "Drain: idle";
|
|
170072
|
+
}
|
|
170073
|
+
lines.push(drainLine);
|
|
170074
|
+
return lines.join(`
|
|
170075
|
+
`);
|
|
170076
|
+
}
|
|
170077
|
+
|
|
170078
|
+
// src/commands/ctx-embed.ts
|
|
170079
|
+
function clearPiEmbedSessionState(sessionId) {
|
|
170080
|
+
embedPauseBySession.delete(sessionId);
|
|
170081
|
+
const ctrl = embedRunStateBySession.get(sessionId);
|
|
170082
|
+
if (ctrl) {
|
|
170083
|
+
ctrl.abort();
|
|
170084
|
+
embedRunStateBySession.delete(sessionId);
|
|
170085
|
+
}
|
|
170086
|
+
autoEmbedAttemptedBySession.delete(sessionId);
|
|
170087
|
+
}
|
|
170088
|
+
async function runEmbedDrain(db, projectIdentity, sessionId) {
|
|
170089
|
+
const activeCtrl = embedRunStateBySession.get(sessionId);
|
|
170090
|
+
if (activeCtrl && !activeCtrl.signal.aborted) {
|
|
170091
|
+
return {
|
|
170092
|
+
text: `## /ctx-embed
|
|
170093
|
+
|
|
170094
|
+
Embedding is already running for this session.`,
|
|
170095
|
+
level: "info"
|
|
170096
|
+
};
|
|
170097
|
+
}
|
|
170098
|
+
embedPauseBySession.delete(sessionId);
|
|
170099
|
+
const prior = embedRunStateBySession.get(sessionId);
|
|
170100
|
+
if (prior)
|
|
170101
|
+
prior.abort();
|
|
170102
|
+
const controller = new AbortController;
|
|
170103
|
+
embedRunStateBySession.set(sessionId, controller);
|
|
170104
|
+
let outcome;
|
|
170105
|
+
try {
|
|
170106
|
+
outcome = await embedSessionCompartmentChunks(db, projectIdentity, sessionId, {
|
|
170107
|
+
signal: controller.signal
|
|
170108
|
+
});
|
|
170109
|
+
} finally {
|
|
170110
|
+
if (embedRunStateBySession.get(sessionId) === controller) {
|
|
170111
|
+
embedRunStateBySession.delete(sessionId);
|
|
170112
|
+
}
|
|
170113
|
+
}
|
|
170114
|
+
switch (outcome.status) {
|
|
170115
|
+
case "nothing":
|
|
170116
|
+
return {
|
|
170117
|
+
text: `## /ctx-embed
|
|
170118
|
+
|
|
170119
|
+
All of this session's history is already embedded.`,
|
|
170120
|
+
level: "info"
|
|
170121
|
+
};
|
|
170122
|
+
case "disabled":
|
|
170123
|
+
return {
|
|
170124
|
+
text: `## /ctx-embed
|
|
170125
|
+
|
|
170126
|
+
No embedding provider is configured, so there is nothing to embed.`,
|
|
170127
|
+
level: "info"
|
|
170128
|
+
};
|
|
170129
|
+
case "busy":
|
|
170130
|
+
return {
|
|
170131
|
+
text: `## /ctx-embed
|
|
170132
|
+
|
|
170133
|
+
Embedding is already running for this project. Try again shortly.`,
|
|
170134
|
+
level: "info"
|
|
170135
|
+
};
|
|
170136
|
+
case "aborted": {
|
|
170137
|
+
const cov = getEmbeddingCoverageStatus(db, projectIdentity, sessionId);
|
|
170138
|
+
return {
|
|
170139
|
+
text: `## /ctx-embed
|
|
170140
|
+
|
|
170141
|
+
Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`,
|
|
170142
|
+
level: "info"
|
|
170143
|
+
};
|
|
170144
|
+
}
|
|
170145
|
+
case "stalled":
|
|
170146
|
+
return {
|
|
170147
|
+
text: `## /ctx-embed
|
|
170148
|
+
|
|
170149
|
+
Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"}; ${outcome.remaining} could not be embedded (the provider returned no result). Run /ctx-embed start again to retry them.`,
|
|
170150
|
+
level: "info"
|
|
170151
|
+
};
|
|
170152
|
+
default:
|
|
170153
|
+
return {
|
|
170154
|
+
text: `## /ctx-embed
|
|
170155
|
+
|
|
170156
|
+
Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"} of history for semantic search.`,
|
|
170157
|
+
level: "success"
|
|
170158
|
+
};
|
|
170159
|
+
}
|
|
170160
|
+
}
|
|
170161
|
+
function registerCtxEmbedCommand(pi, deps) {
|
|
170162
|
+
pi.registerCommand("ctx-embed", {
|
|
170163
|
+
description: "Embedding status, or start/pause history compartment embedding (start | pause)",
|
|
170164
|
+
handler: async (args, ctx) => {
|
|
169844
170165
|
const sessionId = resolveSessionId(ctx);
|
|
169845
170166
|
if (!sessionId) {
|
|
169846
170167
|
sendCtxStatusMessage(pi, {
|
|
169847
|
-
title: "/ctx-embed
|
|
169848
|
-
text: `## /ctx-embed
|
|
170168
|
+
title: "/ctx-embed",
|
|
170169
|
+
text: `## /ctx-embed
|
|
169849
170170
|
|
|
169850
170171
|
No active Pi session is available.`,
|
|
169851
170172
|
level: "error"
|
|
169852
170173
|
});
|
|
169853
170174
|
return;
|
|
169854
170175
|
}
|
|
170176
|
+
const project = deps.resolveProject?.(ctx) ?? {
|
|
170177
|
+
projectDir: deps.projectDir,
|
|
170178
|
+
projectIdentity: deps.projectIdentity
|
|
170179
|
+
};
|
|
170180
|
+
const sub = args.trim().toLowerCase();
|
|
170181
|
+
if (sub === "pause") {
|
|
170182
|
+
embedPauseBySession.add(sessionId);
|
|
170183
|
+
const ctrl = embedRunStateBySession.get(sessionId);
|
|
170184
|
+
if (ctrl)
|
|
170185
|
+
ctrl.abort();
|
|
170186
|
+
const cov = getEmbeddingCoverageStatus(deps.db, project.projectIdentity, sessionId);
|
|
170187
|
+
sendCtxStatusMessage(pi, {
|
|
170188
|
+
title: "/ctx-embed",
|
|
170189
|
+
text: `## /ctx-embed
|
|
170190
|
+
|
|
170191
|
+
Paused at ${cov.session.embedded}/${cov.session.total} compartments embedded.`,
|
|
170192
|
+
level: "info"
|
|
170193
|
+
});
|
|
170194
|
+
return;
|
|
170195
|
+
}
|
|
169855
170196
|
if (deps.memoryEnabled === false) {
|
|
169856
170197
|
sendCtxStatusMessage(pi, {
|
|
169857
|
-
title: "/ctx-embed
|
|
169858
|
-
text: `## /ctx-embed
|
|
170198
|
+
title: "/ctx-embed",
|
|
170199
|
+
text: `## /ctx-embed
|
|
169859
170200
|
|
|
169860
170201
|
Memory is disabled for this project, so there is no semantic embedding to backfill.`,
|
|
169861
170202
|
level: "info"
|
|
169862
170203
|
});
|
|
169863
170204
|
return;
|
|
169864
170205
|
}
|
|
169865
|
-
const project = deps.resolveProject?.(ctx) ?? {
|
|
169866
|
-
projectDir: deps.projectDir,
|
|
169867
|
-
projectIdentity: deps.projectIdentity
|
|
169868
|
-
};
|
|
169869
170206
|
await ensureProjectRegisteredFromPiDirectory(project.projectDir, deps.db);
|
|
169870
|
-
|
|
169871
|
-
|
|
169872
|
-
|
|
169873
|
-
|
|
169874
|
-
|
|
169875
|
-
|
|
169876
|
-
|
|
169877
|
-
|
|
169878
|
-
|
|
169879
|
-
|
|
169880
|
-
|
|
169881
|
-
|
|
169882
|
-
|
|
169883
|
-
|
|
169884
|
-
|
|
169885
|
-
|
|
169886
|
-
|
|
169887
|
-
|
|
169888
|
-
return {
|
|
169889
|
-
text: `## /ctx-embed-history
|
|
169890
|
-
|
|
169891
|
-
Embedding is already running for this project — ${outcome.total} compartment${outcome.total === 1 ? "" : "s"} still pending. Try again shortly.`,
|
|
169892
|
-
level: "info"
|
|
169893
|
-
};
|
|
169894
|
-
case "stalled":
|
|
169895
|
-
return {
|
|
169896
|
-
text: `## /ctx-embed-history
|
|
169897
|
-
|
|
169898
|
-
Embedded ${outcome.embedded} compartment${outcome.embedded === 1 ? "" : "s"}; ${outcome.remaining} could not be embedded (the provider returned no result). Run /ctx-embed-history again to retry them.`,
|
|
169899
|
-
level: "info"
|
|
169900
|
-
};
|
|
169901
|
-
default:
|
|
169902
|
-
return {
|
|
169903
|
-
text: `## /ctx-embed-history
|
|
170207
|
+
if (sub === "start") {
|
|
170208
|
+
const { text, level } = await runEmbedDrain(deps.db, project.projectIdentity, sessionId);
|
|
170209
|
+
sendCtxStatusMessage(pi, { title: "/ctx-embed", text, level });
|
|
170210
|
+
return;
|
|
170211
|
+
}
|
|
170212
|
+
if (sub !== "") {
|
|
170213
|
+
sendCtxStatusMessage(pi, {
|
|
170214
|
+
title: "/ctx-embed",
|
|
170215
|
+
text: "## /ctx-embed\n\nUsage: `/ctx-embed` (status), `/ctx-embed start`, or `/ctx-embed pause`.",
|
|
170216
|
+
level: "info"
|
|
170217
|
+
});
|
|
170218
|
+
return;
|
|
170219
|
+
}
|
|
170220
|
+
const coverage = getEmbeddingCoverageStatus(deps.db, project.projectIdentity, sessionId);
|
|
170221
|
+
const statusText = formatEmbedStatusText(coverage, { status: "idle" });
|
|
170222
|
+
sendCtxStatusMessage(pi, {
|
|
170223
|
+
title: "/ctx-embed",
|
|
170224
|
+
text: `## Embedding Status
|
|
169904
170225
|
|
|
169905
|
-
|
|
169906
|
-
|
|
169907
|
-
};
|
|
169908
|
-
}
|
|
169909
|
-
})();
|
|
169910
|
-
sendCtxStatusMessage(pi, { title: "/ctx-embed-history", text, level }, {
|
|
169911
|
-
sessionId,
|
|
169912
|
-
projectIdentity: project.projectIdentity,
|
|
169913
|
-
status: outcome.status,
|
|
169914
|
-
embedded: outcome.embedded,
|
|
169915
|
-
total: outcome.total
|
|
170226
|
+
${statusText}`,
|
|
170227
|
+
level: "info"
|
|
169916
170228
|
});
|
|
169917
170229
|
}
|
|
169918
170230
|
});
|
|
169919
170231
|
}
|
|
170232
|
+
function maybeAutoEmbedPiSession(deps, sessionId, projectDir, projectIdentity, notify) {
|
|
170233
|
+
if (autoEmbedAttemptedBySession.has(sessionId))
|
|
170234
|
+
return;
|
|
170235
|
+
if (embedPauseBySession.has(sessionId))
|
|
170236
|
+
return;
|
|
170237
|
+
if (deps.memoryEnabled === false)
|
|
170238
|
+
return;
|
|
170239
|
+
autoEmbedAttemptedBySession.add(sessionId);
|
|
170240
|
+
(async () => {
|
|
170241
|
+
try {
|
|
170242
|
+
await new Promise((resolve5) => setTimeout(resolve5, 0));
|
|
170243
|
+
await ensureProjectRegisteredFromPiDirectory(projectDir, deps.db);
|
|
170244
|
+
const coverage = getEmbeddingCoverageStatus(deps.db, projectIdentity, sessionId);
|
|
170245
|
+
if (!coverage.enabled)
|
|
170246
|
+
return;
|
|
170247
|
+
const remaining = coverage.session.total - coverage.session.embedded;
|
|
170248
|
+
if (remaining <= 0)
|
|
170249
|
+
return;
|
|
170250
|
+
notify(`Embedding ${remaining} compartment${remaining === 1 ? "" : "s"} of history in the background…`);
|
|
170251
|
+
const { text } = await runEmbedDrain(deps.db, projectIdentity, sessionId);
|
|
170252
|
+
notify(text.replace(/^## \/ctx-embed\n\n/, ""));
|
|
170253
|
+
} catch {}
|
|
170254
|
+
})();
|
|
170255
|
+
}
|
|
169920
170256
|
|
|
169921
170257
|
// ../plugin/src/hooks/magic-context/execute-flush.ts
|
|
169922
170258
|
init_logger();
|
|
@@ -170331,76 +170667,12 @@ function computePiWorkMetrics(sessionEntries) {
|
|
|
170331
170667
|
return { newWorkTokens, totalInputTokens };
|
|
170332
170668
|
}
|
|
170333
170669
|
|
|
170334
|
-
// ../plugin/src/hooks/magic-context/system-injection-stripper.ts
|
|
170335
|
-
var SYSTEM_INJECTION_MARKERS = [
|
|
170336
|
-
"<!-- OMO_INTERNAL_INITIATOR -->",
|
|
170337
|
-
"[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
|
|
170338
|
-
"[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
|
|
170339
|
-
"[Category+Skill Reminder]",
|
|
170340
|
-
"[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
|
|
170341
|
-
"[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
|
|
170342
|
-
"[EMERGENCY CONTEXT WINDOW WARNING]",
|
|
170343
|
-
"Unstable background agent appears idle",
|
|
170344
|
-
"**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
|
|
170345
|
-
];
|
|
170346
|
-
var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
|
|
170347
|
-
var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
|
|
170348
|
-
function stripSystemInjection(text) {
|
|
170349
|
-
let hasInjection = false;
|
|
170350
|
-
for (const marker of SYSTEM_INJECTION_MARKERS) {
|
|
170351
|
-
if (text.includes(marker)) {
|
|
170352
|
-
hasInjection = true;
|
|
170353
|
-
break;
|
|
170354
|
-
}
|
|
170355
|
-
}
|
|
170356
|
-
if (SYSTEM_REMINDER_REGEX.test(text))
|
|
170357
|
-
hasInjection = true;
|
|
170358
|
-
SYSTEM_REMINDER_REGEX.lastIndex = 0;
|
|
170359
|
-
if (!hasInjection)
|
|
170360
|
-
return null;
|
|
170361
|
-
let cleaned = text;
|
|
170362
|
-
cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
|
|
170363
|
-
cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
|
|
170364
|
-
cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
|
|
170365
|
-
for (const marker of SYSTEM_INJECTION_MARKERS) {
|
|
170366
|
-
if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
|
|
170367
|
-
continue;
|
|
170368
|
-
const idx = cleaned.indexOf(marker);
|
|
170369
|
-
if (idx === -1)
|
|
170370
|
-
continue;
|
|
170371
|
-
const blockEnd = cleaned.indexOf(`
|
|
170372
|
-
|
|
170373
|
-
`, idx + marker.length);
|
|
170374
|
-
cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
|
|
170375
|
-
}
|
|
170376
|
-
return cleaned.trim();
|
|
170377
|
-
}
|
|
170378
|
-
|
|
170379
170670
|
// ../plugin/src/hooks/magic-context/apply-operations.ts
|
|
170380
|
-
var USER_DROP_PREVIEW_CHARS = 250;
|
|
170381
170671
|
var RECENT_TOOL_SKELETON_WINDOW = 20;
|
|
170382
|
-
function buildReplacementContent(tagId
|
|
170383
|
-
|
|
170384
|
-
|
|
170385
|
-
|
|
170386
|
-
}
|
|
170387
|
-
const currentContent = target.getContent?.() ?? "";
|
|
170388
|
-
const strippedInjection = stripSystemInjection(currentContent);
|
|
170389
|
-
if (strippedInjection !== null && stripTagPrefix(strippedInjection).trim().length === 0) {
|
|
170390
|
-
return `[dropped §${tagId}§]`;
|
|
170391
|
-
}
|
|
170392
|
-
const originalText = stripTagPrefix(currentContent);
|
|
170393
|
-
if (originalText.length <= USER_DROP_PREVIEW_CHARS) {
|
|
170394
|
-
return `[truncated §${tagId}§]
|
|
170395
|
-
${originalText}`;
|
|
170396
|
-
}
|
|
170397
|
-
const hardCut = originalText.slice(0, USER_DROP_PREVIEW_CHARS);
|
|
170398
|
-
const softCutIndex = hardCut.search(/\s\S*$/);
|
|
170399
|
-
const preview = softCutIndex > USER_DROP_PREVIEW_CHARS - 30 ? hardCut.slice(0, softCutIndex) : hardCut;
|
|
170400
|
-
return `[truncated §${tagId}§]
|
|
170401
|
-
${preview}…`;
|
|
170402
|
-
}
|
|
170403
|
-
function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps) {
|
|
170672
|
+
function buildReplacementContent(tagId) {
|
|
170673
|
+
return `[dropped §${tagId}§]`;
|
|
170674
|
+
}
|
|
170675
|
+
function applyPendingOperations(sessionId, db, targets, protectedTags = 0, preloadedTags, preloadedPendingOps, syntheticPendingOps = []) {
|
|
170404
170676
|
let didMutateMessage = false;
|
|
170405
170677
|
db.transaction(() => {
|
|
170406
170678
|
const tags = preloadedTags ?? getTagsBySession(db, sessionId);
|
|
@@ -170408,11 +170680,16 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
|
|
|
170408
170680
|
const tagTypeById = new Map(tags.map((tag) => [tag.tagNumber, tag.type]));
|
|
170409
170681
|
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;
|
|
170410
170682
|
const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
|
|
170683
|
+
const opsToApply = [
|
|
170684
|
+
...pendingOps.map((op) => ({ op, synthetic: false })),
|
|
170685
|
+
...syntheticPendingOps.map((op) => ({ op, synthetic: true }))
|
|
170686
|
+
];
|
|
170411
170687
|
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));
|
|
170412
|
-
for (const pendingOp of
|
|
170688
|
+
for (const { op: pendingOp, synthetic } of opsToApply) {
|
|
170413
170689
|
const tagStatus = tagStatusById.get(pendingOp.tagId);
|
|
170414
170690
|
if (tagStatus === "compacted" || tagStatus === "dropped") {
|
|
170415
|
-
|
|
170691
|
+
if (!synthetic)
|
|
170692
|
+
removePendingOp(db, sessionId, pendingOp.tagId);
|
|
170416
170693
|
continue;
|
|
170417
170694
|
}
|
|
170418
170695
|
if (protectedTagIds.has(pendingOp.tagId)) {
|
|
@@ -170420,33 +170697,46 @@ function applyPendingOperations(sessionId, db, targets, protectedTags = 0, prelo
|
|
|
170420
170697
|
}
|
|
170421
170698
|
const target = targets.get(pendingOp.tagId);
|
|
170422
170699
|
const isToolTag = tagTypeById.get(pendingOp.tagId) === "tool";
|
|
170700
|
+
if (synthetic) {
|
|
170701
|
+
if (!isToolTag || target?.canDrop?.() !== true)
|
|
170702
|
+
continue;
|
|
170703
|
+
}
|
|
170704
|
+
let shouldPersistDrop = false;
|
|
170423
170705
|
if (isToolTag) {
|
|
170424
170706
|
if (skeletonWindow.has(pendingOp.tagId)) {
|
|
170425
170707
|
const truncResult = target?.truncate?.() ?? "absent";
|
|
170426
|
-
if (truncResult === "incomplete") {
|
|
170708
|
+
if (truncResult === "incomplete" || synthetic && truncResult !== "truncated") {
|
|
170427
170709
|
continue;
|
|
170428
170710
|
}
|
|
170429
170711
|
if (truncResult === "truncated") {
|
|
170430
170712
|
didMutateMessage = true;
|
|
170431
170713
|
}
|
|
170432
170714
|
updateTagDropMode(db, sessionId, pendingOp.tagId, "truncated");
|
|
170715
|
+
shouldPersistDrop = true;
|
|
170433
170716
|
} else {
|
|
170434
170717
|
const dropResult = target?.drop?.() ?? "absent";
|
|
170435
|
-
if (dropResult === "incomplete") {
|
|
170718
|
+
if (dropResult === "incomplete" || synthetic && dropResult !== "removed") {
|
|
170436
170719
|
continue;
|
|
170437
170720
|
}
|
|
170438
170721
|
if (dropResult === "removed") {
|
|
170439
170722
|
didMutateMessage = true;
|
|
170440
170723
|
}
|
|
170441
170724
|
updateTagDropMode(db, sessionId, pendingOp.tagId, "full");
|
|
170725
|
+
shouldPersistDrop = true;
|
|
170442
170726
|
}
|
|
170443
170727
|
} else if (target) {
|
|
170444
|
-
const changed = target.setContent(buildReplacementContent(pendingOp.tagId
|
|
170728
|
+
const changed = target.setContent(buildReplacementContent(pendingOp.tagId));
|
|
170445
170729
|
if (changed)
|
|
170446
170730
|
didMutateMessage = true;
|
|
170731
|
+
shouldPersistDrop = true;
|
|
170732
|
+
} else if (!synthetic) {
|
|
170733
|
+
shouldPersistDrop = true;
|
|
170447
170734
|
}
|
|
170735
|
+
if (!shouldPersistDrop)
|
|
170736
|
+
continue;
|
|
170448
170737
|
updateTagStatus(db, sessionId, pendingOp.tagId, "dropped");
|
|
170449
|
-
|
|
170738
|
+
if (!synthetic)
|
|
170739
|
+
removePendingOp(db, sessionId, pendingOp.tagId);
|
|
170450
170740
|
}
|
|
170451
170741
|
})();
|
|
170452
170742
|
return didMutateMessage;
|
|
@@ -170470,7 +170760,7 @@ function applyFlushedStatuses(sessionId, db, targets, preloadedTags) {
|
|
|
170470
170760
|
}
|
|
170471
170761
|
}
|
|
170472
170762
|
} else if (target) {
|
|
170473
|
-
const changed = target.setContent(buildReplacementContent(tag.tagNumber
|
|
170763
|
+
const changed = target.setContent(buildReplacementContent(tag.tagNumber));
|
|
170474
170764
|
if (changed)
|
|
170475
170765
|
didMutateMessage = true;
|
|
170476
170766
|
}
|
|
@@ -170758,7 +171048,8 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
|
|
|
170758
171048
|
const result = {
|
|
170759
171049
|
compressedToLite: 0,
|
|
170760
171050
|
compressedToFull: 0,
|
|
170761
|
-
compressedToUltra: 0
|
|
171051
|
+
compressedToUltra: 0,
|
|
171052
|
+
mutatedTextTags: 0
|
|
170762
171053
|
};
|
|
170763
171054
|
if (!config2.enabled)
|
|
170764
171055
|
return result;
|
|
@@ -170799,7 +171090,9 @@ function applyCavemanCleanup(sessionId, db, targets, tags, config2) {
|
|
|
170799
171090
|
const target = targets.get(tag.tagNumber);
|
|
170800
171091
|
if (!target)
|
|
170801
171092
|
continue;
|
|
170802
|
-
target.setContent(compressed);
|
|
171093
|
+
const didMutate = target.setContent(compressed);
|
|
171094
|
+
if (didMutate)
|
|
171095
|
+
result.mutatedTextTags += 1;
|
|
170803
171096
|
updateCavemanDepth(db, sessionId, tag.tagNumber, targetDepth);
|
|
170804
171097
|
if (targetDepth === DEPTH_LITE)
|
|
170805
171098
|
result.compressedToLite += 1;
|
|
@@ -171177,7 +171470,7 @@ function buildToolArcs(messages) {
|
|
|
171177
171470
|
}
|
|
171178
171471
|
return arcs.sort((a, b) => a.invOrdinal - b.invOrdinal || (a.resOrdinal ?? Number.MAX_SAFE_INTEGER) - (b.resOrdinal ?? Number.MAX_SAFE_INTEGER));
|
|
171179
171472
|
}
|
|
171180
|
-
function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
|
|
171473
|
+
function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal, recentOpenArcCutoff) {
|
|
171181
171474
|
let boundary = candidate;
|
|
171182
171475
|
for (const arc of arcs) {
|
|
171183
171476
|
if (arc.resOrdinal !== null) {
|
|
@@ -171186,6 +171479,8 @@ function fenceBoundaryForToolArcs(candidate, arcs, lastCompartmentEndOrdinal) {
|
|
|
171186
171479
|
}
|
|
171187
171480
|
continue;
|
|
171188
171481
|
}
|
|
171482
|
+
if (arc.invOrdinal < recentOpenArcCutoff)
|
|
171483
|
+
continue;
|
|
171189
171484
|
if (arc.invOrdinal >= lastCompartmentEndOrdinal + 1 && arc.invOrdinal < boundary) {
|
|
171190
171485
|
return arc.invOrdinal;
|
|
171191
171486
|
}
|
|
@@ -171423,7 +171718,7 @@ function semanticSnapBoundary(args) {
|
|
|
171423
171718
|
return snapped;
|
|
171424
171719
|
}
|
|
171425
171720
|
function applyHeadCap(args) {
|
|
171426
|
-
const { index, protectedTailStart, offset, arcs, capTokens } = args;
|
|
171721
|
+
const { index, protectedTailStart, offset, arcs, capTokens, recentOpenArcCutoff } = args;
|
|
171427
171722
|
if (offset >= protectedTailStart)
|
|
171428
171723
|
return { eligibleEndOrdinal: offset, oversizeAtomicUnit: false };
|
|
171429
171724
|
let end = index.findHeadEndForCap(offset, protectedTailStart, capTokens);
|
|
@@ -171431,7 +171726,7 @@ function applyHeadCap(args) {
|
|
|
171431
171726
|
for (const arc of arcs) {
|
|
171432
171727
|
const resOrdinal = arc.resOrdinal;
|
|
171433
171728
|
if (resOrdinal === null) {
|
|
171434
|
-
if (arc.invOrdinal >= offset && arc.invOrdinal < end) {
|
|
171729
|
+
if (arc.invOrdinal >= recentOpenArcCutoff && arc.invOrdinal >= offset && arc.invOrdinal < end) {
|
|
171435
171730
|
end = Math.min(end, arc.invOrdinal);
|
|
171436
171731
|
}
|
|
171437
171732
|
continue;
|
|
@@ -171498,7 +171793,14 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
171498
171793
|
}
|
|
171499
171794
|
if (ctx.mode === "manual-full-recomp") {
|
|
171500
171795
|
const arcs2 = buildToolArcs(messages);
|
|
171501
|
-
const
|
|
171796
|
+
const recompTarget = deriveProtectedTailTokenTarget({
|
|
171797
|
+
contextLimit: ctx.contextLimit,
|
|
171798
|
+
executeThresholdPercentage: ctx.executeThresholdPercentage,
|
|
171799
|
+
usagePercentage: 0,
|
|
171800
|
+
triggerBudget: ctx.triggerBudget
|
|
171801
|
+
});
|
|
171802
|
+
const recentOpenArcCutoff2 = index.findSuffixStartForTokens(recompTarget.N);
|
|
171803
|
+
const firstOpenArc = arcs2.find((arc) => arc.resOrdinal === null && arc.invOrdinal >= offset && arc.invOrdinal >= recentOpenArcCutoff2);
|
|
171502
171804
|
const protectedTailStart2 = firstOpenArc?.invOrdinal ?? rawMessageCount + 1;
|
|
171503
171805
|
const rawRangeFingerprint2 = computeRawRangeFingerprint(messages, offset, protectedTailStart2);
|
|
171504
171806
|
return {
|
|
@@ -171540,13 +171842,14 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
171540
171842
|
const scaledN = ctx.emergencyTailScale ? Math.max(1, Math.floor(target.N * ctx.emergencyTailScale)) : target.N;
|
|
171541
171843
|
const arcs = buildToolArcs(messages);
|
|
171542
171844
|
let boundary = index.findSuffixStartForTokens(scaledN);
|
|
171845
|
+
const recentOpenArcCutoff = boundary;
|
|
171543
171846
|
let boundaryReason = boundary === 1 ? "whole-session-smaller-than-tail" : "size-walk";
|
|
171544
171847
|
const tokenAtBoundary = index.tokenForOrdinal(boundary);
|
|
171545
171848
|
if (boundary <= rawMessageCount && tokenAtBoundary > Math.max(2 * scaledN, 64000) && boundary < rawMessageCount) {
|
|
171546
171849
|
boundary += 1;
|
|
171547
171850
|
boundaryReason = "huge-message-exception";
|
|
171548
171851
|
}
|
|
171549
|
-
boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal);
|
|
171852
|
+
boundary = fenceBoundaryForToolArcs(boundary, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
|
|
171550
171853
|
const snapped = semanticSnapBoundary({
|
|
171551
171854
|
messages,
|
|
171552
171855
|
index,
|
|
@@ -171556,7 +171859,7 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
171556
171859
|
});
|
|
171557
171860
|
if (snapped !== boundary)
|
|
171558
171861
|
boundaryReason = "semantic-snap";
|
|
171559
|
-
boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal);
|
|
171862
|
+
boundary = fenceBoundaryForToolArcs(snapped, arcs, ctx.lastCompartmentEndOrdinal, recentOpenArcCutoff);
|
|
171560
171863
|
let runtimeFloor = offset;
|
|
171561
171864
|
if (ctx.migrationFloorActive)
|
|
171562
171865
|
runtimeFloor = Math.max(runtimeFloor, ctx.priorBoundaryOrdinal);
|
|
@@ -171592,7 +171895,8 @@ function resolveProtectedTailBoundary(ctx) {
|
|
|
171592
171895
|
offset,
|
|
171593
171896
|
arcs,
|
|
171594
171897
|
lastCompartmentEndOrdinal: ctx.lastCompartmentEndOrdinal,
|
|
171595
|
-
capTokens: perRunCap
|
|
171898
|
+
capTokens: perRunCap,
|
|
171899
|
+
recentOpenArcCutoff
|
|
171596
171900
|
});
|
|
171597
171901
|
const rawRangeFingerprint = computeRawRangeFingerprint(messages, offset, head.eligibleEndOrdinal);
|
|
171598
171902
|
return {
|
|
@@ -172066,6 +172370,13 @@ function computePressure(input) {
|
|
|
172066
172370
|
function approxThousands(tokens) {
|
|
172067
172371
|
return `${Math.round(tokens / 1000)}k`;
|
|
172068
172372
|
}
|
|
172373
|
+
function formatOldestReclaimableHint(hint) {
|
|
172374
|
+
if (!hint || hint.length === 0)
|
|
172375
|
+
return "";
|
|
172376
|
+
const rendered = hint.slice(0, 4).map((tag) => `§${tag.tagNumber}§ ${tag.toolName ?? "tool"}`).join(" · ");
|
|
172377
|
+
return rendered.length > 0 ? `
|
|
172378
|
+
oldest reclaimable: ${rendered}.` : "";
|
|
172379
|
+
}
|
|
172069
172380
|
var CHANNEL2_USABLE_FRACTION = 1 / 3;
|
|
172070
172381
|
var CHANNEL2_MIN_RECLAIMABLE = 1e4;
|
|
172071
172382
|
function shouldTriggerChannel2(input) {
|
|
@@ -172075,14 +172386,16 @@ function shouldTriggerChannel2(input) {
|
|
|
172075
172386
|
return true;
|
|
172076
172387
|
return input.reclaimableTokens >= input.usableTokens * CHANNEL2_USABLE_FRACTION;
|
|
172077
172388
|
}
|
|
172078
|
-
function buildChannel2Reminder(undroppedTokens) {
|
|
172389
|
+
function buildChannel2Reminder(undroppedTokens, hint) {
|
|
172079
172390
|
const amount = approxThousands(undroppedTokens);
|
|
172391
|
+
const hintText = formatOldestReclaimableHint(hint);
|
|
172080
172392
|
return `<system-reminder>
|
|
172081
|
-
` + `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
|
|
172393
|
+
` + `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}
|
|
172082
172394
|
` + `</system-reminder>`;
|
|
172083
172395
|
}
|
|
172084
|
-
function buildChannel1Reminder(level, undroppedTokens) {
|
|
172396
|
+
function buildChannel1Reminder(level, undroppedTokens, hint) {
|
|
172085
172397
|
const amount = approxThousands(undroppedTokens);
|
|
172398
|
+
const hintText = formatOldestReclaimableHint(hint);
|
|
172086
172399
|
let body;
|
|
172087
172400
|
switch (level) {
|
|
172088
172401
|
case "gentle":
|
|
@@ -172098,7 +172411,7 @@ function buildChannel1Reminder(level, undroppedTokens) {
|
|
|
172098
172411
|
return `
|
|
172099
172412
|
|
|
172100
172413
|
<system-reminder>
|
|
172101
|
-
${body}
|
|
172414
|
+
${body}${hintText}
|
|
172102
172415
|
</system-reminder>`;
|
|
172103
172416
|
}
|
|
172104
172417
|
|
|
@@ -172646,6 +172959,41 @@ function renderMemoryBlockV2(memories, wrapper = "project-memory", renderOptions
|
|
|
172646
172959
|
`);
|
|
172647
172960
|
}
|
|
172648
172961
|
|
|
172962
|
+
// ../plugin/src/hooks/magic-context/tool-reclaim.ts
|
|
172963
|
+
function buildSyntheticToolReclaimOps(input) {
|
|
172964
|
+
const watermark = Math.max(0, input.watermark);
|
|
172965
|
+
if (watermark <= 0)
|
|
172966
|
+
return [];
|
|
172967
|
+
const realPendingTagIds = new Set((input.pendingOps ?? []).map((op) => op.tagId));
|
|
172968
|
+
const tags = getActiveTagsBySession(input.db, input.sessionId);
|
|
172969
|
+
const synthetic = [];
|
|
172970
|
+
for (const tag of tags) {
|
|
172971
|
+
if (tag.type !== "tool")
|
|
172972
|
+
continue;
|
|
172973
|
+
if (tag.status !== "active")
|
|
172974
|
+
continue;
|
|
172975
|
+
if (tag.tagNumber > watermark)
|
|
172976
|
+
continue;
|
|
172977
|
+
if (realPendingTagIds.has(tag.tagNumber))
|
|
172978
|
+
continue;
|
|
172979
|
+
if (input.targets.get(tag.tagNumber)?.canDrop?.() !== true)
|
|
172980
|
+
continue;
|
|
172981
|
+
synthetic.push({
|
|
172982
|
+
id: 0,
|
|
172983
|
+
sessionId: input.sessionId,
|
|
172984
|
+
tagId: tag.tagNumber,
|
|
172985
|
+
operation: "drop",
|
|
172986
|
+
queuedAt: 0
|
|
172987
|
+
});
|
|
172988
|
+
}
|
|
172989
|
+
return synthetic;
|
|
172990
|
+
}
|
|
172991
|
+
function advanceToolReclaimWatermarkToCurrentMax(db, sessionId) {
|
|
172992
|
+
const maxTagNumber = getMaxTagNumberBySession(db, sessionId);
|
|
172993
|
+
advanceToolReclaimWatermark(db, sessionId, maxTagNumber);
|
|
172994
|
+
return maxTagNumber;
|
|
172995
|
+
}
|
|
172996
|
+
|
|
172649
172997
|
// src/context-handler.ts
|
|
172650
172998
|
init_logger();
|
|
172651
172999
|
|
|
@@ -173056,7 +173404,7 @@ function tagToolPart(args) {
|
|
|
173056
173404
|
const tagged = prependTag(tagId, text);
|
|
173057
173405
|
args.part.setText(tagged);
|
|
173058
173406
|
}
|
|
173059
|
-
args.targets.set(tagId, buildToolTarget(args.part, args.message));
|
|
173407
|
+
args.targets.set(tagId, buildToolTarget(args.part, args.message, tagId));
|
|
173060
173408
|
}
|
|
173061
173409
|
function setToolContentOrText(part, content) {
|
|
173062
173410
|
try {
|
|
@@ -173096,7 +173444,7 @@ function buildAggregateTarget(tagId, occurrences) {
|
|
|
173096
173444
|
return any2 ? "removed" : "absent";
|
|
173097
173445
|
},
|
|
173098
173446
|
truncate() {
|
|
173099
|
-
const sentinel =
|
|
173447
|
+
const sentinel = `[dropped §${tagId}§]`;
|
|
173100
173448
|
let any2 = false;
|
|
173101
173449
|
for (const occ of occurrences) {
|
|
173102
173450
|
if (setToolContentOrText(occ.part, sentinel)) {
|
|
@@ -173128,7 +173476,7 @@ function buildTextTarget(part, message) {
|
|
|
173128
173476
|
}
|
|
173129
173477
|
};
|
|
173130
173478
|
}
|
|
173131
|
-
function buildToolTarget(part, message) {
|
|
173479
|
+
function buildToolTarget(part, message, tagId) {
|
|
173132
173480
|
return {
|
|
173133
173481
|
setContent(content) {
|
|
173134
173482
|
return setToolContentOrText(part, content);
|
|
@@ -173137,11 +173485,11 @@ function buildToolTarget(part, message) {
|
|
|
173137
173485
|
return part.getText() ?? null;
|
|
173138
173486
|
},
|
|
173139
173487
|
drop() {
|
|
173140
|
-
const replaced = part.replaceWithSentinel(`[dropped §${
|
|
173488
|
+
const replaced = part.replaceWithSentinel(`[dropped §${tagId}§]`);
|
|
173141
173489
|
return replaced ? "removed" : "absent";
|
|
173142
173490
|
},
|
|
173143
173491
|
truncate() {
|
|
173144
|
-
const ok = setToolContentOrText(part,
|
|
173492
|
+
const ok = setToolContentOrText(part, `[dropped §${tagId}§]`);
|
|
173145
173493
|
return ok ? "truncated" : "absent";
|
|
173146
173494
|
},
|
|
173147
173495
|
message: {
|
|
@@ -174426,9 +174774,10 @@ function maybeChannel1ReminderForToolResult(args) {
|
|
|
174426
174774
|
return null;
|
|
174427
174775
|
return {
|
|
174428
174776
|
type: "text",
|
|
174429
|
-
text: buildChannel1Reminder(decision.level, decision.undroppedTokens)
|
|
174777
|
+
text: buildChannel1Reminder(decision.level, decision.undroppedTokens, state.oldestReclaimableToolTags)
|
|
174430
174778
|
};
|
|
174431
174779
|
}
|
|
174780
|
+
var CHANNEL2_NUDGE_CUSTOM_TYPE = "magic-context:ceiling-nudge";
|
|
174432
174781
|
function maybeDeliverChannel2Pi(pi, db, sessionId, deliverAs = "followUp") {
|
|
174433
174782
|
let state;
|
|
174434
174783
|
try {
|
|
@@ -174456,9 +174805,12 @@ function maybeDeliverChannel2Pi(pi, db, sessionId, deliverAs = "followUp") {
|
|
|
174456
174805
|
if (!casChannel2NudgeState(db, sessionId, "pending", "claimed"))
|
|
174457
174806
|
return false;
|
|
174458
174807
|
try {
|
|
174459
|
-
pi.
|
|
174460
|
-
|
|
174461
|
-
|
|
174808
|
+
pi.sendMessage({
|
|
174809
|
+
customType: CHANNEL2_NUDGE_CUSTOM_TYPE,
|
|
174810
|
+
content: buildChannel2Reminder(undropped, baseline.oldestReclaimableToolTags),
|
|
174811
|
+
display: false,
|
|
174812
|
+
details: { kind: "channel-2-ceiling-nudge" }
|
|
174813
|
+
}, { deliverAs });
|
|
174462
174814
|
} catch (error51) {
|
|
174463
174815
|
try {
|
|
174464
174816
|
casChannel2NudgeState(db, sessionId, "claimed", "pending");
|
|
@@ -174650,6 +175002,51 @@ function planEmergencyDrop(input) {
|
|
|
174650
175002
|
};
|
|
174651
175003
|
}
|
|
174652
175004
|
|
|
175005
|
+
// ../plugin/src/hooks/magic-context/system-injection-stripper.ts
|
|
175006
|
+
var SYSTEM_INJECTION_MARKERS = [
|
|
175007
|
+
"<!-- OMO_INTERNAL_INITIATOR -->",
|
|
175008
|
+
"[SYSTEM DIRECTIVE: MAGIC-CONTEXT",
|
|
175009
|
+
"[SYSTEM DIRECTIVE: OH-MY-OPENCODE",
|
|
175010
|
+
"[Category+Skill Reminder]",
|
|
175011
|
+
"[EDIT ERROR - IMMEDIATE ACTION REQUIRED]",
|
|
175012
|
+
"[task CALL FAILED - IMMEDIATE RETRY REQUIRED]",
|
|
175013
|
+
"[EMERGENCY CONTEXT WINDOW WARNING]",
|
|
175014
|
+
"Unstable background agent appears idle",
|
|
175015
|
+
"**THE SUBAGENT JUST CLAIMED THIS TASK IS DONE."
|
|
175016
|
+
];
|
|
175017
|
+
var SYSTEM_REMINDER_REGEX = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
|
|
175018
|
+
var OMO_MARKER_REGEX = /<!-- OMO_INTERNAL_INITIATOR -->/g;
|
|
175019
|
+
function stripSystemInjection(text) {
|
|
175020
|
+
let hasInjection = false;
|
|
175021
|
+
for (const marker of SYSTEM_INJECTION_MARKERS) {
|
|
175022
|
+
if (text.includes(marker)) {
|
|
175023
|
+
hasInjection = true;
|
|
175024
|
+
break;
|
|
175025
|
+
}
|
|
175026
|
+
}
|
|
175027
|
+
if (SYSTEM_REMINDER_REGEX.test(text))
|
|
175028
|
+
hasInjection = true;
|
|
175029
|
+
SYSTEM_REMINDER_REGEX.lastIndex = 0;
|
|
175030
|
+
if (!hasInjection)
|
|
175031
|
+
return null;
|
|
175032
|
+
let cleaned = text;
|
|
175033
|
+
cleaned = cleaned.replace(SYSTEM_REMINDER_REGEX, "");
|
|
175034
|
+
cleaned = cleaned.replace(OMO_MARKER_REGEX, "");
|
|
175035
|
+
cleaned = cleaned.replace(/\[SYSTEM DIRECTIVE: OH-MY-(?:OPENCODE|CLAUDE)[^\]]*\][\s\S]*?(?=\n\n(?!\s*[-*])|$)/g, "");
|
|
175036
|
+
for (const marker of SYSTEM_INJECTION_MARKERS) {
|
|
175037
|
+
if (marker.startsWith("<!-- ") || marker.startsWith("[SYSTEM DIRECTIVE"))
|
|
175038
|
+
continue;
|
|
175039
|
+
const idx = cleaned.indexOf(marker);
|
|
175040
|
+
if (idx === -1)
|
|
175041
|
+
continue;
|
|
175042
|
+
const blockEnd = cleaned.indexOf(`
|
|
175043
|
+
|
|
175044
|
+
`, idx + marker.length);
|
|
175045
|
+
cleaned = blockEnd !== -1 ? cleaned.slice(0, idx) + cleaned.slice(blockEnd) : cleaned.slice(0, idx);
|
|
175046
|
+
}
|
|
175047
|
+
return cleaned.trim();
|
|
175048
|
+
}
|
|
175049
|
+
|
|
174653
175050
|
// src/heuristic-cleanup-pi.ts
|
|
174654
175051
|
init_logger();
|
|
174655
175052
|
var DEDUP_SAFE_TOOLS = new Set([
|
|
@@ -174808,10 +175205,14 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
|
|
|
174808
175205
|
if (!matched)
|
|
174809
175206
|
continue;
|
|
174810
175207
|
const target = targets.get(tag.tagNumber);
|
|
174811
|
-
target?.drop?.();
|
|
175208
|
+
const result = target?.drop?.() ?? "absent";
|
|
175209
|
+
if (result === "incomplete")
|
|
175210
|
+
continue;
|
|
174812
175211
|
updateTagDropMode(db, sessionId, tag.tagNumber, "full");
|
|
174813
175212
|
updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
|
|
174814
|
-
|
|
175213
|
+
if (result === "removed" || result === "truncated") {
|
|
175214
|
+
droppedStaleReduceCalls++;
|
|
175215
|
+
}
|
|
174815
175216
|
}
|
|
174816
175217
|
})();
|
|
174817
175218
|
}
|
|
@@ -174883,7 +175284,9 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
|
|
|
174883
175284
|
continue;
|
|
174884
175285
|
updateTagDropMode(db, sessionId, tag.tagNumber, "full");
|
|
174885
175286
|
updateTagStatus(db, sessionId, tag.tagNumber, "dropped");
|
|
174886
|
-
|
|
175287
|
+
if (result === "removed" || result === "truncated") {
|
|
175288
|
+
deduplicatedTools++;
|
|
175289
|
+
}
|
|
174887
175290
|
}
|
|
174888
175291
|
}
|
|
174889
175292
|
})();
|
|
@@ -174892,6 +175295,7 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
|
|
|
174892
175295
|
sessionLog(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, stale ctx_reduce=${droppedStaleReduceCalls}, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
|
|
174893
175296
|
}
|
|
174894
175297
|
let compressedTextTags = 0;
|
|
175298
|
+
let mutatedTextTags = 0;
|
|
174895
175299
|
if (config2.caveman?.enabled) {
|
|
174896
175300
|
const cavemanResult = applyCavemanCleanup(sessionId, db, targets, tags, {
|
|
174897
175301
|
enabled: true,
|
|
@@ -174899,13 +175303,15 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config2, pr
|
|
|
174899
175303
|
protectedTags: config2.protectedTags
|
|
174900
175304
|
});
|
|
174901
175305
|
compressedTextTags = cavemanResult.compressedToLite + cavemanResult.compressedToFull + cavemanResult.compressedToUltra;
|
|
175306
|
+
mutatedTextTags = cavemanResult.mutatedTextTags;
|
|
174902
175307
|
}
|
|
174903
175308
|
return {
|
|
174904
175309
|
droppedTools,
|
|
174905
175310
|
deduplicatedTools,
|
|
174906
175311
|
droppedInjections,
|
|
174907
175312
|
droppedStaleReduceCalls,
|
|
174908
|
-
compressedTextTags
|
|
175313
|
+
compressedTextTags,
|
|
175314
|
+
mutatedTextTags
|
|
174909
175315
|
};
|
|
174910
175316
|
}
|
|
174911
175317
|
function buildMessageIdToMaxTagFromTargets(targets) {
|
|
@@ -180282,9 +180688,7 @@ Context is managed for you entirely automatically — there's nothing to prune a
|
|
|
180282
180688
|
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).`;
|
|
180283
180689
|
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.`;
|
|
180284
180690
|
var BASE_INTRO = (protectedTags) => `Messages and tool outputs are tagged with §N§ identifiers (e.g., §1§, §42§).
|
|
180285
|
-
Use \`ctx_reduce\` to
|
|
180286
|
-
- \`drop\`: Remove entirely (best for tool outputs you already acted on).
|
|
180287
|
-
Syntax: "3-5", "1,2,9", or "1-5,8,12-15". Last ${protectedTags} tags are protected.
|
|
180691
|
+
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".
|
|
180288
180692
|
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.
|
|
180289
180693
|
${CTX_NOTE_GUIDANCE}
|
|
180290
180694
|
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.
|
|
@@ -180303,7 +180707,7 @@ Use \`ctx_expand\` to recover the raw conversation behind a \`<compartment>\` su
|
|
|
180303
180707
|
\`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.
|
|
180304
180708
|
${TOOL_HISTORY_GUIDANCE}
|
|
180305
180709
|
NEVER drop large ranges blindly (e.g., "1-50"). Review each tag before deciding.
|
|
180306
|
-
|
|
180710
|
+
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\`.
|
|
180307
180711
|
NEVER drop assistant text messages unless they are exceptionally large. Your conversation messages are lightweight; only large tool outputs are worth dropping.
|
|
180308
180712
|
Before your turn finishes, consider using \`ctx_reduce\` to drop large tool outputs you no longer need.`;
|
|
180309
180713
|
var BASE_INTRO_NO_REDUCE = () => `${CTX_NOTE_GUIDANCE}
|
|
@@ -181780,6 +182184,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
181780
182184
|
const executeThresholdTokensPi = Math.round((usageContextLimit ?? 0) * resolvedExecuteThresholdPct / 100);
|
|
181781
182185
|
const usableTokensPi = Math.max(0, executeThresholdTokensPi - usageInputTokens + liveTailTokens);
|
|
181782
182186
|
resetLastNudgeCycleIfTailShrank(options.db, sessionId, tailToolTokens);
|
|
182187
|
+
const oldestReclaimableToolTags = getOldestActiveUnprotectedToolTags(options.db, sessionId, options.protectedTags ?? 20);
|
|
181783
182188
|
setPiChannel1Baseline(sessionId, {
|
|
181784
182189
|
tailToolTokens,
|
|
181785
182190
|
historyBudgetTokens: historyBudgetTokens ?? 0,
|
|
@@ -181788,7 +182193,8 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
181788
182193
|
lastInputTokens: usageInputTokens,
|
|
181789
182194
|
turnToolTokens: 0,
|
|
181790
182195
|
usableTokens: usableTokensPi,
|
|
181791
|
-
reducedSinceRefresh: false
|
|
182196
|
+
reducedSinceRefresh: false,
|
|
182197
|
+
oldestReclaimableToolTags
|
|
181792
182198
|
});
|
|
181793
182199
|
if (usageContextLimit && usageContextLimit > 0 && resolvedExecuteThresholdPct > 0) {
|
|
181794
182200
|
const channel2ShouldTrigger = shouldTriggerChannel2({
|
|
@@ -181816,6 +182222,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
181816
182222
|
logTransformTiming(sessionId, "postTransformPhase", tPostTransform);
|
|
181817
182223
|
sessionLog(sessionId, `transform completed in ${(performance.now() - transformStartTime).toFixed(1)}ms (${outputMessages.length} messages, ${result.targetCount} targets, watermark: ${result.reasoningWatermark})`);
|
|
181818
182224
|
clearLastTransformErrorIfSet(options.db, sessionId);
|
|
182225
|
+
options.maybeAutoEmbedSession?.(sessionId, projectDirectory, projectIdentity);
|
|
181819
182226
|
return { messages: outputMessages };
|
|
181820
182227
|
} catch (err) {
|
|
181821
182228
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -182162,6 +182569,8 @@ async function runPipeline(args) {
|
|
|
182162
182569
|
let historyWasConsumedThisPass = false;
|
|
182163
182570
|
let materializationSatisfiedThisPass = false;
|
|
182164
182571
|
let pendingOpsAppliedThisPass = false;
|
|
182572
|
+
let pendingOpsDidMutate = false;
|
|
182573
|
+
let heuristicOrReasoningDidMutate = false;
|
|
182165
182574
|
let suppressDeferredHistoryDrain = false;
|
|
182166
182575
|
let casLost = false;
|
|
182167
182576
|
const deferredHistoryWasPendingAtPassStart = deferredHistoryRefreshSessions.has(args.sessionId);
|
|
@@ -182247,7 +182656,7 @@ async function runPipeline(args) {
|
|
|
182247
182656
|
sessionLog(args.sessionId, `pending ops WILL APPLY — reason=${applyReason}, pendingOps=${pendingOps.length}, context=${args.contextUsage.percentage.toFixed(1)}%`);
|
|
182248
182657
|
try {
|
|
182249
182658
|
const tApplyPending = performance.now();
|
|
182250
|
-
applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, undefined, pendingOps);
|
|
182659
|
+
pendingOpsDidMutate = applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, undefined, pendingOps);
|
|
182251
182660
|
logTransformTiming(args.sessionId, "applyPendingOperations", tApplyPending);
|
|
182252
182661
|
executedWorkThisPass = true;
|
|
182253
182662
|
materializationSatisfiedThisPass = true;
|
|
@@ -182331,6 +182740,9 @@ async function runPipeline(args) {
|
|
|
182331
182740
|
} : undefined,
|
|
182332
182741
|
caveman: args.heuristics.caveman
|
|
182333
182742
|
}, activeTags, stableIdResolver);
|
|
182743
|
+
const heuristicMutationCount = heuristicsResult.droppedTools + heuristicsResult.deduplicatedTools + heuristicsResult.droppedInjections + heuristicsResult.droppedStaleReduceCalls + heuristicsResult.mutatedTextTags;
|
|
182744
|
+
if (heuristicMutationCount > 0)
|
|
182745
|
+
heuristicOrReasoningDidMutate = true;
|
|
182334
182746
|
heuristicsExecuted = true;
|
|
182335
182747
|
executedWorkThisPass = true;
|
|
182336
182748
|
if (hasPendingMaterializeSignal) {
|
|
@@ -182339,7 +182751,7 @@ async function runPipeline(args) {
|
|
|
182339
182751
|
if (currentTurnId !== null) {
|
|
182340
182752
|
lastHeuristicsTurnIdBySession.set(args.sessionId, currentTurnId);
|
|
182341
182753
|
}
|
|
182342
|
-
logTransformTiming(args.sessionId, "applyHeuristicCleanup", tHeuristic, `droppedTools=${heuristicsResult.droppedTools} deduplicatedTools=${heuristicsResult.deduplicatedTools} droppedInjections=${heuristicsResult.droppedInjections} compressedTextTags=${heuristicsResult.compressedTextTags}`);
|
|
182754
|
+
logTransformTiming(args.sessionId, "applyHeuristicCleanup", tHeuristic, `droppedTools=${heuristicsResult.droppedTools} deduplicatedTools=${heuristicsResult.deduplicatedTools} droppedInjections=${heuristicsResult.droppedInjections} staleReduce=${heuristicsResult.droppedStaleReduceCalls} compressedTextTags=${heuristicsResult.compressedTextTags} mutatedTextTags=${heuristicsResult.mutatedTextTags}`);
|
|
182343
182755
|
} catch (err) {
|
|
182344
182756
|
sessionLog(args.sessionId, `heuristic cleanup failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
|
|
182345
182757
|
}
|
|
@@ -182376,6 +182788,9 @@ async function runPipeline(args) {
|
|
|
182376
182788
|
}
|
|
182377
182789
|
logTransformTiming(args.sessionId, "clearOldReasoning", tClearReasoning);
|
|
182378
182790
|
logTransformTiming(args.sessionId, "watermarkCleanup", tClearReasoning);
|
|
182791
|
+
if (clearOutcome.cleared > 0 || stripOutcome.stripped > 0) {
|
|
182792
|
+
heuristicOrReasoningDidMutate = true;
|
|
182793
|
+
}
|
|
182379
182794
|
if (combinedWatermark > prevWatermark || clearOutcome.cleared > 0 || stripOutcome.stripped > 0) {
|
|
182380
182795
|
executedWorkThisPass = true;
|
|
182381
182796
|
}
|
|
@@ -182383,7 +182798,32 @@ async function runPipeline(args) {
|
|
|
182383
182798
|
sessionLog(args.sessionId, `reasoning clearing failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
|
|
182384
182799
|
}
|
|
182385
182800
|
}
|
|
182801
|
+
const toolReclaimExecutePass = args.schedulerDecision === "execute";
|
|
182802
|
+
const alreadyMutatingThisPass = pendingOpsDidMutate || heuristicOrReasoningDidMutate;
|
|
182803
|
+
const emergencyDropEligible = args.forceMaterialization === true || args.contextUsage.percentage >= FORCE_MATERIALIZATION_PERCENTAGE;
|
|
182804
|
+
let autoReclaimTargetCount = 0;
|
|
182805
|
+
let autoReclaimDidMutate = false;
|
|
182806
|
+
if (toolReclaimExecutePass && alreadyMutatingThisPass && !emergencyDropEligible) {
|
|
182807
|
+
const reclaimMeta = getOrCreateSessionMeta(args.db, args.sessionId);
|
|
182808
|
+
const syntheticPendingOps = buildSyntheticToolReclaimOps({
|
|
182809
|
+
db: args.db,
|
|
182810
|
+
sessionId: args.sessionId,
|
|
182811
|
+
targets,
|
|
182812
|
+
watermark: reclaimMeta.toolReclaimWatermark ?? 0,
|
|
182813
|
+
pendingOps
|
|
182814
|
+
});
|
|
182815
|
+
autoReclaimTargetCount = syntheticPendingOps.length;
|
|
182816
|
+
if (syntheticPendingOps.length > 0) {
|
|
182817
|
+
autoReclaimDidMutate = applyPendingOperations(args.sessionId, args.db, targets, args.protectedTags, undefined, [], syntheticPendingOps);
|
|
182818
|
+
}
|
|
182819
|
+
}
|
|
182386
182820
|
transcript.commit();
|
|
182821
|
+
if (toolReclaimExecutePass) {
|
|
182822
|
+
advanceToolReclaimWatermarkToCurrentMax(args.db, args.sessionId);
|
|
182823
|
+
}
|
|
182824
|
+
if (autoReclaimTargetCount > 0) {
|
|
182825
|
+
sessionLog(args.sessionId, `tool reclaim auto-drop: targets=${autoReclaimTargetCount} mutated=${autoReclaimDidMutate}`);
|
|
182826
|
+
}
|
|
182387
182827
|
const postCommitStableIdByRef = new Map;
|
|
182388
182828
|
const postCommitEntryIdByRef = new Map;
|
|
182389
182829
|
for (let i = 0;i < args.messages.length; i++) {
|
|
@@ -182667,6 +183107,7 @@ function clearContextHandlerSession(sessionId) {
|
|
|
182667
183107
|
rawMessageProviderUnregistersBySession.delete(sessionId);
|
|
182668
183108
|
}
|
|
182669
183109
|
clearSessionTracking(sessionId);
|
|
183110
|
+
clearPiEmbedSessionState(sessionId);
|
|
182670
183111
|
}
|
|
182671
183112
|
|
|
182672
183113
|
// src/commands/ctx-flush.ts
|
|
@@ -185169,7 +185610,7 @@ function formatThresholdPercent(value) {
|
|
|
185169
185610
|
// package.json
|
|
185170
185611
|
var package_default = {
|
|
185171
185612
|
name: "@cortexkit/pi-magic-context",
|
|
185172
|
-
version: "0.
|
|
185613
|
+
version: "0.25.0",
|
|
185173
185614
|
type: "module",
|
|
185174
185615
|
description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
|
|
185175
185616
|
main: "dist/index.js",
|
|
@@ -185782,9 +186223,184 @@ Older parts of this session are summarized into <compartment> blocks inside <ses
|
|
|
185782
186223
|
|
|
185783
186224
|
ctx_expand(start=120, end=245) ← the compartment's own start/end attributes
|
|
185784
186225
|
|
|
185785
|
-
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
|
|
186226
|
+
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.
|
|
186227
|
+
|
|
186228
|
+
Two recovery modes for finer detail:
|
|
186229
|
+
- 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.
|
|
186230
|
+
- 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.`;
|
|
185786
186231
|
var CTX_EXPAND_TOKEN_BUDGET = 15000;
|
|
185787
186232
|
|
|
186233
|
+
// ../plugin/src/tools/ctx-expand/render.ts
|
|
186234
|
+
function isRecord3(value) {
|
|
186235
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
186236
|
+
}
|
|
186237
|
+
function roleLabel(role) {
|
|
186238
|
+
if (role === "assistant")
|
|
186239
|
+
return "A (assistant)";
|
|
186240
|
+
if (role === "user")
|
|
186241
|
+
return "U (user)";
|
|
186242
|
+
return role;
|
|
186243
|
+
}
|
|
186244
|
+
function truncate2(value, max) {
|
|
186245
|
+
const t = value.trim();
|
|
186246
|
+
return t.length <= max ? t : `${t.slice(0, max)}…`;
|
|
186247
|
+
}
|
|
186248
|
+
function keyArg(input) {
|
|
186249
|
+
if (!input)
|
|
186250
|
+
return "";
|
|
186251
|
+
for (const k of ["filePath", "path", "pattern", "query", "symbol", "module", "action"]) {
|
|
186252
|
+
const v = input[k];
|
|
186253
|
+
if (typeof v === "string" && v.length > 0)
|
|
186254
|
+
return truncate2(v, 60);
|
|
186255
|
+
}
|
|
186256
|
+
if (typeof input.description === "string")
|
|
186257
|
+
return truncate2(input.description, 60);
|
|
186258
|
+
return "";
|
|
186259
|
+
}
|
|
186260
|
+
function asToolPart(part) {
|
|
186261
|
+
const type = typeof part.type === "string" ? part.type : "";
|
|
186262
|
+
if (type === "tool") {
|
|
186263
|
+
const state = isRecord3(part.state) ? part.state : null;
|
|
186264
|
+
const output = state && typeof state.output === "string" ? state.output : state && state.output != null ? JSON.stringify(state.output) : null;
|
|
186265
|
+
const metadata = state && isRecord3(state.metadata) ? state.metadata : null;
|
|
186266
|
+
const title = state && typeof state.title === "string" && state.title || metadata && typeof metadata.title === "string" && metadata.title || null;
|
|
186267
|
+
return {
|
|
186268
|
+
name: typeof part.tool === "string" ? part.tool : "tool",
|
|
186269
|
+
callId: typeof part.callID === "string" ? part.callID : "",
|
|
186270
|
+
title,
|
|
186271
|
+
input: state && isRecord3(state.input) ? state.input : null,
|
|
186272
|
+
output
|
|
186273
|
+
};
|
|
186274
|
+
}
|
|
186275
|
+
if (type === "tool_use") {
|
|
186276
|
+
return {
|
|
186277
|
+
name: typeof part.name === "string" ? part.name : "tool",
|
|
186278
|
+
callId: typeof part.id === "string" ? part.id : "",
|
|
186279
|
+
title: null,
|
|
186280
|
+
input: isRecord3(part.input) ? part.input : null,
|
|
186281
|
+
output: null
|
|
186282
|
+
};
|
|
186283
|
+
}
|
|
186284
|
+
if (type === "tool_result") {
|
|
186285
|
+
const content = part.content;
|
|
186286
|
+
const output = typeof content === "string" ? content : content != null ? JSON.stringify(content) : null;
|
|
186287
|
+
return {
|
|
186288
|
+
name: "tool_result",
|
|
186289
|
+
callId: typeof part.tool_use_id === "string" ? part.tool_use_id : "",
|
|
186290
|
+
title: null,
|
|
186291
|
+
input: null,
|
|
186292
|
+
output
|
|
186293
|
+
};
|
|
186294
|
+
}
|
|
186295
|
+
return null;
|
|
186296
|
+
}
|
|
186297
|
+
function textOf(part) {
|
|
186298
|
+
if (part.type === "text" && typeof part.text === "string")
|
|
186299
|
+
return part.text;
|
|
186300
|
+
return null;
|
|
186301
|
+
}
|
|
186302
|
+
function reasoningOf(part) {
|
|
186303
|
+
if ((part.type === "reasoning" || part.type === "thinking") && typeof part.text === "string") {
|
|
186304
|
+
return part.text;
|
|
186305
|
+
}
|
|
186306
|
+
return null;
|
|
186307
|
+
}
|
|
186308
|
+
function renderPartPreview(part) {
|
|
186309
|
+
if (!isRecord3(part))
|
|
186310
|
+
return null;
|
|
186311
|
+
const text = textOf(part);
|
|
186312
|
+
if (text !== null) {
|
|
186313
|
+
const t = truncate2(text, 200);
|
|
186314
|
+
return t.length > 0 ? ` • ${t}` : null;
|
|
186315
|
+
}
|
|
186316
|
+
const tool = asToolPart(part);
|
|
186317
|
+
if (tool) {
|
|
186318
|
+
const arg = keyArg(tool.input);
|
|
186319
|
+
const head = arg ? `${tool.name}(${arg})` : tool.name;
|
|
186320
|
+
return tool.output !== null ? ` • tool ${head} → output ~${estimateTokens(tool.output)} tok` : ` • tool ${head}`;
|
|
186321
|
+
}
|
|
186322
|
+
const reasoning = reasoningOf(part);
|
|
186323
|
+
if (reasoning !== null)
|
|
186324
|
+
return ` • [reasoning] ${truncate2(reasoning, 120)}`;
|
|
186325
|
+
const type = typeof part.type === "string" ? part.type : "part";
|
|
186326
|
+
if (type === "file")
|
|
186327
|
+
return " • [file]";
|
|
186328
|
+
if (type === "step-start" || type === "step-finish")
|
|
186329
|
+
return null;
|
|
186330
|
+
return ` • [${type}]`;
|
|
186331
|
+
}
|
|
186332
|
+
function renderPartFull(part) {
|
|
186333
|
+
if (!isRecord3(part))
|
|
186334
|
+
return null;
|
|
186335
|
+
const text = textOf(part);
|
|
186336
|
+
if (text !== null) {
|
|
186337
|
+
return text.trim().length > 0 ? ` [text]
|
|
186338
|
+
${text}` : null;
|
|
186339
|
+
}
|
|
186340
|
+
const tool = asToolPart(part);
|
|
186341
|
+
if (tool) {
|
|
186342
|
+
const lines = [];
|
|
186343
|
+
const idSuffix = tool.callId ? ` #${tool.callId}` : "";
|
|
186344
|
+
lines.push(` [tool: ${tool.name}${idSuffix}]`);
|
|
186345
|
+
if (tool.title && tool.title.trim().length > 0) {
|
|
186346
|
+
lines.push(` description: ${tool.title.trim()}`);
|
|
186347
|
+
}
|
|
186348
|
+
if (tool.input)
|
|
186349
|
+
lines.push(` input: ${JSON.stringify(tool.input)}`);
|
|
186350
|
+
if (tool.output !== null)
|
|
186351
|
+
lines.push(` output:
|
|
186352
|
+
${tool.output}`);
|
|
186353
|
+
return lines.join(`
|
|
186354
|
+
`);
|
|
186355
|
+
}
|
|
186356
|
+
const type = typeof part.type === "string" ? part.type : "part";
|
|
186357
|
+
if (type === "file") {
|
|
186358
|
+
const name2 = typeof part.filename === "string" && part.filename || typeof part.url === "string" && part.url || "";
|
|
186359
|
+
return ` [file]${name2 ? ` ${name2}` : ""}`;
|
|
186360
|
+
}
|
|
186361
|
+
return null;
|
|
186362
|
+
}
|
|
186363
|
+
function renderMessageByOrdinal(sessionId, ordinal) {
|
|
186364
|
+
const msg = readRawSessionMessages(sessionId).find((m) => m.ordinal === ordinal);
|
|
186365
|
+
if (!msg) {
|
|
186366
|
+
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.`;
|
|
186367
|
+
}
|
|
186368
|
+
const rendered = msg.parts.map(renderPartFull).filter((l) => l !== null);
|
|
186369
|
+
const lines = [`[${msg.ordinal}] ${roleLabel(msg.role)} — full recovery:`, ""];
|
|
186370
|
+
if (rendered.length === 0) {
|
|
186371
|
+
lines.push(" (no recoverable content — message had only structural/reasoning parts)");
|
|
186372
|
+
} else {
|
|
186373
|
+
lines.push(...rendered);
|
|
186374
|
+
}
|
|
186375
|
+
return lines.join(`
|
|
186376
|
+
`);
|
|
186377
|
+
}
|
|
186378
|
+
function renderVerboseRange(sessionId, start, end, tokenBudget) {
|
|
186379
|
+
const messages = readRawSessionMessages(sessionId).filter((m) => m.ordinal >= start && m.ordinal <= end);
|
|
186380
|
+
const out = [];
|
|
186381
|
+
let usedTokens = 0;
|
|
186382
|
+
let lastOrdinal = start - 1;
|
|
186383
|
+
let truncated = false;
|
|
186384
|
+
for (const msg of messages) {
|
|
186385
|
+
const header = `[${msg.ordinal}] ${roleLabel(msg.role)}`;
|
|
186386
|
+
const partLines = msg.parts.map(renderPartPreview).filter((l) => l !== null);
|
|
186387
|
+
const block = partLines.length > 0 ? `${header}
|
|
186388
|
+
${partLines.join(`
|
|
186389
|
+
`)}` : header;
|
|
186390
|
+
const blockTokens = estimateTokens(block);
|
|
186391
|
+
if (usedTokens + blockTokens > tokenBudget && out.length > 0) {
|
|
186392
|
+
truncated = true;
|
|
186393
|
+
break;
|
|
186394
|
+
}
|
|
186395
|
+
out.push(block);
|
|
186396
|
+
usedTokens += blockTokens;
|
|
186397
|
+
lastOrdinal = msg.ordinal;
|
|
186398
|
+
}
|
|
186399
|
+
return { text: out.join(`
|
|
186400
|
+
|
|
186401
|
+
`), lastOrdinal, truncated };
|
|
186402
|
+
}
|
|
186403
|
+
|
|
185788
186404
|
// ../../node_modules/.bun/typebox@1.1.38/node_modules/typebox/build/system/memory/memory.mjs
|
|
185789
186405
|
var exports_memory = {};
|
|
185790
186406
|
__export(exports_memory, {
|
|
@@ -189850,12 +190466,18 @@ __export(exports_typebox, {
|
|
|
189850
190466
|
});
|
|
189851
190467
|
// src/tools/ctx-expand.ts
|
|
189852
190468
|
var ParamsSchema = exports_typebox.Object({
|
|
189853
|
-
start: exports_typebox.Number({
|
|
190469
|
+
start: exports_typebox.Optional(exports_typebox.Number({
|
|
189854
190470
|
description: "Start message ordinal (from compartment start attribute)"
|
|
189855
|
-
}),
|
|
189856
|
-
end: exports_typebox.Number({
|
|
190471
|
+
})),
|
|
190472
|
+
end: exports_typebox.Optional(exports_typebox.Number({
|
|
189857
190473
|
description: "End message ordinal (from compartment end attribute)"
|
|
189858
|
-
})
|
|
190474
|
+
})),
|
|
190475
|
+
verbose: exports_typebox.Optional(exports_typebox.Boolean({
|
|
190476
|
+
description: "With start/end: list each message separately with its ordinal [N] and per-part preview, so you can recover one in full by ordinal."
|
|
190477
|
+
})),
|
|
190478
|
+
message: exports_typebox.Optional(exports_typebox.Number({
|
|
190479
|
+
description: "Full untruncated recovery of ONE message by its ordinal (text + every tool call's full input/output). Recovers a tool output you dropped with ctx_reduce."
|
|
190480
|
+
}))
|
|
189859
190481
|
});
|
|
189860
190482
|
function ok(text) {
|
|
189861
190483
|
return { content: [{ type: "text", text }], details: undefined };
|
|
@@ -189874,22 +190496,41 @@ function createCtxExpandTool(deps) {
|
|
|
189874
190496
|
description: CTX_EXPAND_DESCRIPTION,
|
|
189875
190497
|
parameters: ParamsSchema,
|
|
189876
190498
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
189877
|
-
if (typeof params.start !== "number" || typeof params.end !== "number" || params.start < 1 || params.end < params.start) {
|
|
189878
|
-
return err("Error: start and end must be positive integers with start <= end.");
|
|
189879
|
-
}
|
|
189880
190499
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
189881
190500
|
if (!sessionId) {
|
|
189882
190501
|
return err("Error: no active Pi session.");
|
|
189883
190502
|
}
|
|
189884
|
-
const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
|
|
189885
|
-
if (lastCompartmentEnd >= 0 && params.start > lastCompartmentEnd) {
|
|
189886
|
-
return ok(`Range ${params.start}-${params.end} is entirely within the live tail (after the last compacted message ${lastCompartmentEnd}); those messages are already visible in context.`);
|
|
189887
|
-
}
|
|
189888
|
-
const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(params.end, lastCompartmentEnd) : params.end;
|
|
189889
190503
|
const unregister = setRawMessageProvider(sessionId, {
|
|
189890
190504
|
readMessages: () => readPiSessionMessages(ctx)
|
|
189891
190505
|
});
|
|
189892
190506
|
try {
|
|
190507
|
+
if (typeof params.message === "number" && params.message >= 1) {
|
|
190508
|
+
return ok(renderMessageByOrdinal(sessionId, params.message));
|
|
190509
|
+
}
|
|
190510
|
+
if (typeof params.start !== "number" || typeof params.end !== "number" || params.start < 1 || params.end < params.start) {
|
|
190511
|
+
return err("Error: provide either message=<ordinal>, or start and end (positive integers, start <= end).");
|
|
190512
|
+
}
|
|
190513
|
+
const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, sessionId);
|
|
190514
|
+
if (lastCompartmentEnd >= 0 && params.start > lastCompartmentEnd) {
|
|
190515
|
+
return ok(`Range ${params.start}-${params.end} is entirely within the live tail (after the last compacted message ${lastCompartmentEnd}); those messages are already visible in context.`);
|
|
190516
|
+
}
|
|
190517
|
+
const effectiveEnd = lastCompartmentEnd >= 0 ? Math.min(params.end, lastCompartmentEnd) : params.end;
|
|
190518
|
+
if (params.verbose === true) {
|
|
190519
|
+
const v = renderVerboseRange(sessionId, params.start, effectiveEnd, CTX_EXPAND_TOKEN_BUDGET);
|
|
190520
|
+
if (!v.text) {
|
|
190521
|
+
return ok(`No messages found in range ${params.start}-${effectiveEnd}. The range may be outside this session's history.`);
|
|
190522
|
+
}
|
|
190523
|
+
const out = [
|
|
190524
|
+
`Messages ${params.start}-${v.lastOrdinal} (verbose). Recover any one in full with ctx_expand(message=<ordinal>):`,
|
|
190525
|
+
"",
|
|
190526
|
+
v.text
|
|
190527
|
+
];
|
|
190528
|
+
if (v.truncated) {
|
|
190529
|
+
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.`);
|
|
190530
|
+
}
|
|
190531
|
+
return ok(out.join(`
|
|
190532
|
+
`));
|
|
190533
|
+
}
|
|
189893
190534
|
const chunk = readSessionChunk(sessionId, CTX_EXPAND_TOKEN_BUDGET, params.start, effectiveEnd + 1);
|
|
189894
190535
|
if (!chunk.text || chunk.messageCount === 0) {
|
|
189895
190536
|
return ok(`No messages found in range ${params.start}-${params.end}. The range may be outside this session's history.`);
|
|
@@ -190608,15 +191249,16 @@ function parseInteger(str) {
|
|
|
190608
191249
|
}
|
|
190609
191250
|
|
|
190610
191251
|
// ../plugin/src/tools/ctx-reduce/constants.ts
|
|
190611
|
-
var CTX_REDUCE_DESCRIPTION = `
|
|
190612
|
-
|
|
191252
|
+
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".
|
|
191253
|
+
|
|
191254
|
+
How it works:
|
|
191255
|
+
- 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.
|
|
191256
|
+
- The newest tags are protected: marking one just queues it until it ages out of the recent window, so marking recent output is harmless.
|
|
191257
|
+
- 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?".
|
|
190613
191258
|
|
|
190614
|
-
|
|
190615
|
-
|
|
190616
|
-
-
|
|
190617
|
-
- Protected tags are accepted but deferred until they leave the last protected range.
|
|
190618
|
-
- Keep recent context — only reduce OLD content that is no longer relevant to current work.
|
|
190619
|
-
- Dropped content is gone forever.`;
|
|
191259
|
+
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.
|
|
191260
|
+
Keep: user messages, unresolved errors, raw evidence you haven't extracted yet, and outputs whose exact wording may matter later.
|
|
191261
|
+
Never blanket-mark large ranges (e.g. "1-50") — review what each tag holds first.`;
|
|
190620
191262
|
|
|
190621
191263
|
// src/tools/ctx-reduce.ts
|
|
190622
191264
|
var ParamsSchema4 = exports_typebox.Object({
|
|
@@ -191217,7 +191859,21 @@ async function src_default2(pi) {
|
|
|
191217
191859
|
},
|
|
191218
191860
|
historian: hist,
|
|
191219
191861
|
autoSearch: auto,
|
|
191220
|
-
resolveForProject: resolveContextOptionsForProject
|
|
191862
|
+
resolveForProject: resolveContextOptionsForProject,
|
|
191863
|
+
maybeAutoEmbedSession: (sessionId, dir, identity) => {
|
|
191864
|
+
maybeAutoEmbedPiSession({
|
|
191865
|
+
db: database,
|
|
191866
|
+
projectDir: dir,
|
|
191867
|
+
projectIdentity: identity,
|
|
191868
|
+
memoryEnabled: cfg.memory.enabled
|
|
191869
|
+
}, sessionId, dir, identity, (text) => {
|
|
191870
|
+
pi.sendMessage({
|
|
191871
|
+
customType: "ctx-status",
|
|
191872
|
+
content: text,
|
|
191873
|
+
display: true
|
|
191874
|
+
}, { triggerTurn: false });
|
|
191875
|
+
});
|
|
191876
|
+
}
|
|
191221
191877
|
});
|
|
191222
191878
|
function resolveContextOptionsForProject(dir) {
|
|
191223
191879
|
const cached2 = contextOptionsByDir.get(dir);
|
|
@@ -191299,14 +191955,14 @@ async function src_default2(pi) {
|
|
|
191299
191955
|
onProjectSeen: (identity) => seenDreamerProjectIdentities.add(identity)
|
|
191300
191956
|
});
|
|
191301
191957
|
info("registered /ctx-dream");
|
|
191302
|
-
|
|
191958
|
+
registerCtxEmbedCommand(pi, {
|
|
191303
191959
|
db,
|
|
191304
191960
|
projectDir,
|
|
191305
191961
|
projectIdentity,
|
|
191306
191962
|
memoryEnabled: config2.memory.enabled,
|
|
191307
191963
|
resolveProject: resolveCurrentProject
|
|
191308
191964
|
});
|
|
191309
|
-
info("registered /ctx-embed
|
|
191965
|
+
info("registered /ctx-embed");
|
|
191310
191966
|
const dreamerConfig = resolveDreamerFromConfig(config2);
|
|
191311
191967
|
if (dreamerConfig) {
|
|
191312
191968
|
registerPiDreamerProject({
|