@cortexkit/opencode-magic-context 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/magic-context-prompt.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/queue.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/runner.d.ts.map +1 -1
- package/dist/features/magic-context/message-index.d.ts +1 -0
- package/dist/features/magic-context/message-index.d.ts.map +1 -1
- package/dist/features/magic-context/search.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +3 -0
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta.d.ts +1 -1
- package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
- package/dist/features/magic-context/storage-tags.d.ts +3 -1
- package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +3 -2
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/tagger.d.ts +1 -1
- package/dist/features/magic-context/tagger.d.ts.map +1 -1
- package/dist/features/magic-context/types.d.ts +1 -0
- package/dist/features/magic-context/types.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-trigger.d.ts +1 -1
- package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-handler.d.ts +2 -0
- package/dist/hooks/magic-context/event-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-payloads.d.ts +6 -1
- package/dist/hooks/magic-context/event-payloads.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
- package/dist/hooks/magic-context/note-nudger.d.ts +3 -1
- package/dist/hooks/magic-context/note-nudger.d.ts.map +1 -1
- package/dist/hooks/magic-context/nudge-placement-store.d.ts +3 -1
- package/dist/hooks/magic-context/nudge-placement-store.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/index.js +1264 -1008
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15037,6 +15037,8 @@ function releaseLease(db, holderId) {
|
|
|
15037
15037
|
function enqueueDream(db, projectIdentity, reason) {
|
|
15038
15038
|
const now = Date.now();
|
|
15039
15039
|
return db.transaction(() => {
|
|
15040
|
+
const staleThresholdMs = 10 * 60 * 1000;
|
|
15041
|
+
db.run("DELETE FROM dream_queue WHERE project_path = ? AND started_at IS NOT NULL AND started_at < ?", [projectIdentity, now - staleThresholdMs]);
|
|
15040
15042
|
const existing = db.query("SELECT id FROM dream_queue WHERE project_path = ?").get(projectIdentity);
|
|
15041
15043
|
if (existing) {
|
|
15042
15044
|
return null;
|
|
@@ -15323,7 +15325,7 @@ async function runDream(args) {
|
|
|
15323
15325
|
log(`[dreamer] lease released: ${holderId}`);
|
|
15324
15326
|
}
|
|
15325
15327
|
result.finishedAt = Date.now();
|
|
15326
|
-
const hasSuccessfulTask = result.tasks.some((t) => !t.error);
|
|
15328
|
+
const hasSuccessfulTask = result.tasks.some((t) => !t.error && t.name !== "smart-notes");
|
|
15327
15329
|
if (hasSuccessfulTask) {
|
|
15328
15330
|
setDreamState(args.db, `last_dream_at:${args.projectIdentity}`, String(result.finishedAt));
|
|
15329
15331
|
setDreamState(args.db, "last_dream_at", String(result.finishedAt));
|
|
@@ -15569,9 +15571,20 @@ function checkScheduleAndEnqueue(db, schedule) {
|
|
|
15569
15571
|
}
|
|
15570
15572
|
return enqueued;
|
|
15571
15573
|
}
|
|
15572
|
-
// src/
|
|
15574
|
+
// src/shared/internal-initiator-marker.ts
|
|
15575
|
+
var OMO_INTERNAL_INITIATOR_MARKER = "<!-- OMO_INTERNAL_INITIATOR -->";
|
|
15576
|
+
|
|
15577
|
+
// src/shared/system-directive.ts
|
|
15578
|
+
var SYSTEM_DIRECTIVE_PREFIX = "[SYSTEM DIRECTIVE: MAGIC-CONTEXT";
|
|
15579
|
+
function isSystemDirective(text) {
|
|
15580
|
+
return text.trimStart().startsWith(SYSTEM_DIRECTIVE_PREFIX);
|
|
15581
|
+
}
|
|
15582
|
+
function removeSystemReminders(text) {
|
|
15583
|
+
return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gi, "").trim();
|
|
15584
|
+
}
|
|
15585
|
+
|
|
15586
|
+
// src/hooks/magic-context/read-session-db.ts
|
|
15573
15587
|
import { Database } from "bun:sqlite";
|
|
15574
|
-
import { mkdirSync } from "fs";
|
|
15575
15588
|
import { join as join5 } from "path";
|
|
15576
15589
|
|
|
15577
15590
|
// src/shared/data-path.ts
|
|
@@ -15584,1012 +15597,1050 @@ function getOpenCodeStorageDir() {
|
|
|
15584
15597
|
return path2.join(getDataDir(), "opencode", "storage");
|
|
15585
15598
|
}
|
|
15586
15599
|
|
|
15587
|
-
// src/
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
var
|
|
15592
|
-
function
|
|
15593
|
-
|
|
15594
|
-
|
|
15600
|
+
// src/hooks/magic-context/read-session-db.ts
|
|
15601
|
+
function getOpenCodeDbPath() {
|
|
15602
|
+
return join5(getDataDir(), "opencode", "opencode.db");
|
|
15603
|
+
}
|
|
15604
|
+
var cachedReadOnlyDb = null;
|
|
15605
|
+
function closeCachedReadOnlyDb() {
|
|
15606
|
+
if (!cachedReadOnlyDb) {
|
|
15607
|
+
return;
|
|
15608
|
+
}
|
|
15609
|
+
try {
|
|
15610
|
+
cachedReadOnlyDb.db.close(false);
|
|
15611
|
+
} catch (error48) {
|
|
15612
|
+
log("[magic-context] failed to close cached OpenCode read-only DB:", error48);
|
|
15613
|
+
} finally {
|
|
15614
|
+
cachedReadOnlyDb = null;
|
|
15615
|
+
}
|
|
15616
|
+
}
|
|
15617
|
+
function getReadOnlySessionDb() {
|
|
15618
|
+
const dbPath = getOpenCodeDbPath();
|
|
15619
|
+
if (cachedReadOnlyDb?.path === dbPath) {
|
|
15620
|
+
return cachedReadOnlyDb.db;
|
|
15621
|
+
}
|
|
15622
|
+
closeCachedReadOnlyDb();
|
|
15623
|
+
const db = new Database(dbPath, { readonly: true });
|
|
15624
|
+
cachedReadOnlyDb = { path: dbPath, db };
|
|
15625
|
+
return db;
|
|
15626
|
+
}
|
|
15627
|
+
function withReadOnlySessionDb(fn) {
|
|
15628
|
+
return fn(getReadOnlySessionDb());
|
|
15629
|
+
}
|
|
15630
|
+
function getRawSessionMessageCountFromDb(db, sessionId) {
|
|
15631
|
+
const row = db.prepare("SELECT COUNT(*) as count FROM message WHERE session_id = ?").get(sessionId);
|
|
15632
|
+
return typeof row?.count === "number" ? row.count : 0;
|
|
15595
15633
|
}
|
|
15596
|
-
function initializeDatabase(db) {
|
|
15597
|
-
db.run("PRAGMA journal_mode=WAL");
|
|
15598
|
-
db.run("PRAGMA busy_timeout=5000");
|
|
15599
|
-
db.run("PRAGMA foreign_keys=ON");
|
|
15600
|
-
db.run(`
|
|
15601
|
-
CREATE TABLE IF NOT EXISTS tags (
|
|
15602
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
15603
|
-
session_id TEXT,
|
|
15604
|
-
message_id TEXT,
|
|
15605
|
-
type TEXT,
|
|
15606
|
-
status TEXT DEFAULT 'active',
|
|
15607
|
-
byte_size INTEGER,
|
|
15608
|
-
tag_number INTEGER,
|
|
15609
|
-
UNIQUE(session_id, tag_number)
|
|
15610
|
-
);
|
|
15611
|
-
|
|
15612
|
-
CREATE TABLE IF NOT EXISTS pending_ops (
|
|
15613
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
15614
|
-
session_id TEXT,
|
|
15615
|
-
tag_id INTEGER,
|
|
15616
|
-
operation TEXT,
|
|
15617
|
-
queued_at INTEGER
|
|
15618
|
-
);
|
|
15619
15634
|
|
|
15620
|
-
|
|
15621
|
-
|
|
15622
|
-
|
|
15623
|
-
|
|
15624
|
-
|
|
15625
|
-
|
|
15626
|
-
)
|
|
15635
|
+
// src/hooks/magic-context/read-session-formatting.ts
|
|
15636
|
+
var COMMIT_HASH_PATTERN = /`?\b([0-9a-f]{6,12})\b`?/gi;
|
|
15637
|
+
var COMMIT_HINT_PATTERN = /\b(commit(?:ted)?|cherry-?pick(?:ed)?|hash(?:es)?|sha)\b/i;
|
|
15638
|
+
var MAX_COMMITS_PER_BLOCK = 5;
|
|
15639
|
+
function hasMeaningfulUserText(parts) {
|
|
15640
|
+
for (const part of parts) {
|
|
15641
|
+
if (part === null || typeof part !== "object")
|
|
15642
|
+
continue;
|
|
15643
|
+
const candidate = part;
|
|
15644
|
+
if (candidate.type !== "text" || typeof candidate.text !== "string")
|
|
15645
|
+
continue;
|
|
15646
|
+
if (candidate.ignored === true)
|
|
15647
|
+
continue;
|
|
15648
|
+
const cleaned = removeSystemReminders(candidate.text).replace(OMO_INTERNAL_INITIATOR_MARKER, "").trim();
|
|
15649
|
+
if (!cleaned)
|
|
15650
|
+
continue;
|
|
15651
|
+
if (isSystemDirective(cleaned))
|
|
15652
|
+
continue;
|
|
15653
|
+
return true;
|
|
15654
|
+
}
|
|
15655
|
+
return false;
|
|
15656
|
+
}
|
|
15657
|
+
function extractTexts(parts) {
|
|
15658
|
+
const texts = [];
|
|
15659
|
+
for (const part of parts) {
|
|
15660
|
+
if (part === null || typeof part !== "object")
|
|
15661
|
+
continue;
|
|
15662
|
+
const p = part;
|
|
15663
|
+
if (p.type === "text" && typeof p.text === "string" && p.text.trim().length > 0) {
|
|
15664
|
+
texts.push(p.text.trim());
|
|
15665
|
+
}
|
|
15666
|
+
}
|
|
15667
|
+
return texts;
|
|
15668
|
+
}
|
|
15669
|
+
function estimateTokens(text) {
|
|
15670
|
+
return Math.ceil(text.length / 3.5);
|
|
15671
|
+
}
|
|
15672
|
+
function normalizeText(text) {
|
|
15673
|
+
return text.replace(/\s+/g, " ").trim();
|
|
15674
|
+
}
|
|
15675
|
+
function compactRole(role) {
|
|
15676
|
+
if (role === "assistant")
|
|
15677
|
+
return "A";
|
|
15678
|
+
if (role === "user")
|
|
15679
|
+
return "U";
|
|
15680
|
+
return role.slice(0, 1).toUpperCase() || "M";
|
|
15681
|
+
}
|
|
15682
|
+
function formatBlock(block) {
|
|
15683
|
+
const range = block.startOrdinal === block.endOrdinal ? `[${block.startOrdinal}]` : `[${block.startOrdinal}-${block.endOrdinal}]`;
|
|
15684
|
+
const commitSuffix = block.commitHashes.length > 0 ? ` commits: ${block.commitHashes.join(", ")}` : "";
|
|
15685
|
+
return `${range} ${block.role}:${commitSuffix} ${block.parts.join(" / ")}`;
|
|
15686
|
+
}
|
|
15687
|
+
function extractCommitHashes(text) {
|
|
15688
|
+
const hashes = [];
|
|
15689
|
+
const seen = new Set;
|
|
15690
|
+
for (const match of text.matchAll(COMMIT_HASH_PATTERN)) {
|
|
15691
|
+
const hash2 = match[1]?.toLowerCase();
|
|
15692
|
+
if (!hash2 || seen.has(hash2))
|
|
15693
|
+
continue;
|
|
15694
|
+
seen.add(hash2);
|
|
15695
|
+
hashes.push(hash2);
|
|
15696
|
+
if (hashes.length >= MAX_COMMITS_PER_BLOCK)
|
|
15697
|
+
break;
|
|
15698
|
+
}
|
|
15699
|
+
return hashes;
|
|
15700
|
+
}
|
|
15701
|
+
function compactTextForSummary(text, role) {
|
|
15702
|
+
const commitHashes = role === "assistant" ? extractCommitHashes(text) : [];
|
|
15703
|
+
if (commitHashes.length === 0 || !COMMIT_HINT_PATTERN.test(text)) {
|
|
15704
|
+
return { text, commitHashes };
|
|
15705
|
+
}
|
|
15706
|
+
const withoutHashes = text.replace(COMMIT_HASH_PATTERN, "").replace(/\(\s*\)/g, "").replace(/\s+,/g, ",").replace(/,\s*,+/g, ", ").replace(/\s{2,}/g, " ").replace(/\s+([,.;:])/g, "$1").trim();
|
|
15707
|
+
return {
|
|
15708
|
+
text: withoutHashes.length > 0 ? withoutHashes : text,
|
|
15709
|
+
commitHashes
|
|
15710
|
+
};
|
|
15711
|
+
}
|
|
15712
|
+
function mergeCommitHashes(existing, next) {
|
|
15713
|
+
if (next.length === 0)
|
|
15714
|
+
return existing;
|
|
15715
|
+
const merged = [...existing];
|
|
15716
|
+
for (const hash2 of next) {
|
|
15717
|
+
if (merged.includes(hash2))
|
|
15718
|
+
continue;
|
|
15719
|
+
merged.push(hash2);
|
|
15720
|
+
if (merged.length >= MAX_COMMITS_PER_BLOCK)
|
|
15721
|
+
break;
|
|
15722
|
+
}
|
|
15723
|
+
return merged;
|
|
15724
|
+
}
|
|
15627
15725
|
|
|
15628
|
-
|
|
15629
|
-
|
|
15630
|
-
|
|
15631
|
-
|
|
15632
|
-
|
|
15633
|
-
|
|
15634
|
-
|
|
15635
|
-
|
|
15636
|
-
|
|
15637
|
-
|
|
15638
|
-
|
|
15639
|
-
|
|
15640
|
-
|
|
15726
|
+
// src/hooks/magic-context/read-session-raw.ts
|
|
15727
|
+
function isRawMessageRow(row) {
|
|
15728
|
+
if (row === null || typeof row !== "object")
|
|
15729
|
+
return false;
|
|
15730
|
+
const candidate = row;
|
|
15731
|
+
return typeof candidate.id === "string" && typeof candidate.data === "string";
|
|
15732
|
+
}
|
|
15733
|
+
function isRawPartRow(row) {
|
|
15734
|
+
if (row === null || typeof row !== "object")
|
|
15735
|
+
return false;
|
|
15736
|
+
const candidate = row;
|
|
15737
|
+
return typeof candidate.message_id === "string" && typeof candidate.data === "string";
|
|
15738
|
+
}
|
|
15739
|
+
function parseJsonRecord(value) {
|
|
15740
|
+
const parsed = JSON.parse(value);
|
|
15741
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
15742
|
+
throw new Error("Expected JSON object");
|
|
15743
|
+
}
|
|
15744
|
+
return parsed;
|
|
15745
|
+
}
|
|
15746
|
+
function parseJsonUnknown(value) {
|
|
15747
|
+
return JSON.parse(value);
|
|
15748
|
+
}
|
|
15749
|
+
function readRawSessionMessagesFromDb(db, sessionId) {
|
|
15750
|
+
const messageRows = db.prepare("SELECT id, data FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId).filter(isRawMessageRow);
|
|
15751
|
+
const partRows = db.prepare("SELECT message_id, data FROM part WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId).filter(isRawPartRow);
|
|
15752
|
+
const partsByMessageId = new Map;
|
|
15753
|
+
for (const part of partRows) {
|
|
15754
|
+
const list = partsByMessageId.get(part.message_id) ?? [];
|
|
15755
|
+
list.push(parseJsonUnknown(part.data));
|
|
15756
|
+
partsByMessageId.set(part.message_id, list);
|
|
15757
|
+
}
|
|
15758
|
+
return messageRows.map((row, index) => {
|
|
15759
|
+
const info = parseJsonRecord(row.data);
|
|
15760
|
+
const role = typeof info.role === "string" ? info.role : "unknown";
|
|
15761
|
+
return {
|
|
15762
|
+
ordinal: index + 1,
|
|
15763
|
+
id: row.id,
|
|
15764
|
+
role,
|
|
15765
|
+
parts: partsByMessageId.get(row.id) ?? []
|
|
15766
|
+
};
|
|
15767
|
+
});
|
|
15768
|
+
}
|
|
15641
15769
|
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
15648
|
-
|
|
15649
|
-
|
|
15770
|
+
// src/hooks/magic-context/tag-content-primitives.ts
|
|
15771
|
+
var encoder = new TextEncoder;
|
|
15772
|
+
var TAG_PREFIX_REGEX = /^\u00A7\d+\u00A7\s*/;
|
|
15773
|
+
function byteSize(value) {
|
|
15774
|
+
return encoder.encode(value).length;
|
|
15775
|
+
}
|
|
15776
|
+
function stripTagPrefix(value) {
|
|
15777
|
+
return value.replace(TAG_PREFIX_REGEX, "");
|
|
15778
|
+
}
|
|
15779
|
+
function prependTag(tagId, value) {
|
|
15780
|
+
const stripped = stripTagPrefix(value);
|
|
15781
|
+
return `\xA7${tagId}\xA7 ${stripped}`;
|
|
15782
|
+
}
|
|
15783
|
+
function isThinkingPart(part) {
|
|
15784
|
+
if (part === null || typeof part !== "object")
|
|
15785
|
+
return false;
|
|
15786
|
+
const candidate = part;
|
|
15787
|
+
return candidate.type === "thinking" || candidate.type === "reasoning";
|
|
15788
|
+
}
|
|
15650
15789
|
|
|
15651
|
-
|
|
15652
|
-
|
|
15653
|
-
|
|
15654
|
-
|
|
15655
|
-
|
|
15656
|
-
|
|
15657
|
-
|
|
15658
|
-
|
|
15659
|
-
|
|
15660
|
-
|
|
15661
|
-
|
|
15662
|
-
|
|
15663
|
-
|
|
15664
|
-
|
|
15665
|
-
|
|
15666
|
-
|
|
15667
|
-
|
|
15668
|
-
|
|
15669
|
-
|
|
15670
|
-
|
|
15671
|
-
|
|
15672
|
-
|
|
15673
|
-
|
|
15674
|
-
|
|
15675
|
-
|
|
15676
|
-
|
|
15677
|
-
|
|
15678
|
-
|
|
15679
|
-
metadata_json TEXT,
|
|
15680
|
-
UNIQUE(project_path, category, normalized_hash)
|
|
15681
|
-
);
|
|
15682
|
-
|
|
15683
|
-
CREATE TABLE IF NOT EXISTS memory_embeddings (
|
|
15684
|
-
memory_id INTEGER PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
|
|
15685
|
-
embedding BLOB NOT NULL,
|
|
15686
|
-
model_id TEXT
|
|
15687
|
-
);
|
|
15688
|
-
|
|
15689
|
-
CREATE TABLE IF NOT EXISTS dream_state (
|
|
15690
|
-
key TEXT PRIMARY KEY,
|
|
15691
|
-
value TEXT NOT NULL
|
|
15692
|
-
);
|
|
15693
|
-
|
|
15694
|
-
CREATE TABLE IF NOT EXISTS dream_queue (
|
|
15695
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
15696
|
-
project_path TEXT NOT NULL,
|
|
15697
|
-
reason TEXT NOT NULL,
|
|
15698
|
-
enqueued_at INTEGER NOT NULL,
|
|
15699
|
-
started_at INTEGER,
|
|
15700
|
-
retry_count INTEGER DEFAULT 0
|
|
15701
|
-
);
|
|
15702
|
-
CREATE INDEX IF NOT EXISTS idx_dream_queue_project ON dream_queue(project_path);
|
|
15703
|
-
CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, enqueued_at);
|
|
15704
|
-
|
|
15705
|
-
CREATE TABLE IF NOT EXISTS smart_notes (
|
|
15706
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
15707
|
-
project_path TEXT NOT NULL,
|
|
15708
|
-
content TEXT NOT NULL,
|
|
15709
|
-
surface_condition TEXT NOT NULL,
|
|
15710
|
-
status TEXT NOT NULL DEFAULT 'pending',
|
|
15711
|
-
created_session_id TEXT,
|
|
15712
|
-
created_at INTEGER NOT NULL,
|
|
15713
|
-
updated_at INTEGER NOT NULL,
|
|
15714
|
-
last_checked_at INTEGER,
|
|
15715
|
-
ready_at INTEGER,
|
|
15716
|
-
ready_reason TEXT
|
|
15717
|
-
);
|
|
15718
|
-
CREATE INDEX IF NOT EXISTS idx_smart_notes_project_status ON smart_notes(project_path, status);
|
|
15719
|
-
|
|
15720
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
15721
|
-
content,
|
|
15722
|
-
category,
|
|
15723
|
-
content='memories',
|
|
15724
|
-
content_rowid='id',
|
|
15725
|
-
tokenize='porter unicode61'
|
|
15726
|
-
);
|
|
15727
|
-
|
|
15728
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS message_history_fts USING fts5(
|
|
15729
|
-
session_id UNINDEXED,
|
|
15730
|
-
message_ordinal UNINDEXED,
|
|
15731
|
-
message_id UNINDEXED,
|
|
15732
|
-
role,
|
|
15733
|
-
content,
|
|
15734
|
-
tokenize='porter unicode61'
|
|
15735
|
-
);
|
|
15736
|
-
|
|
15737
|
-
CREATE TABLE IF NOT EXISTS message_history_index (
|
|
15738
|
-
session_id TEXT PRIMARY KEY,
|
|
15739
|
-
last_indexed_ordinal INTEGER NOT NULL DEFAULT 0,
|
|
15740
|
-
updated_at INTEGER NOT NULL
|
|
15741
|
-
);
|
|
15742
|
-
|
|
15743
|
-
CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
|
|
15744
|
-
INSERT INTO memories_fts(rowid, content, category) VALUES (new.id, new.content, new.category);
|
|
15745
|
-
END;
|
|
15746
|
-
|
|
15747
|
-
CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
|
|
15748
|
-
INSERT INTO memories_fts(memories_fts, rowid, content, category) VALUES ('delete', old.id, old.content, old.category);
|
|
15749
|
-
END;
|
|
15750
|
-
|
|
15751
|
-
CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
|
|
15752
|
-
INSERT INTO memories_fts(memories_fts, rowid, content, category) VALUES ('delete', old.id, old.content, old.category);
|
|
15753
|
-
INSERT INTO memories_fts(rowid, content, category) VALUES (new.id, new.content, new.category);
|
|
15754
|
-
END;
|
|
15755
|
-
|
|
15756
|
-
CREATE TABLE IF NOT EXISTS session_meta (
|
|
15757
|
-
session_id TEXT PRIMARY KEY,
|
|
15758
|
-
last_response_time INTEGER,
|
|
15759
|
-
cache_ttl TEXT,
|
|
15760
|
-
counter INTEGER DEFAULT 0,
|
|
15761
|
-
last_nudge_tokens INTEGER DEFAULT 0,
|
|
15762
|
-
last_nudge_band TEXT DEFAULT '',
|
|
15763
|
-
last_transform_error TEXT DEFAULT '',
|
|
15764
|
-
nudge_anchor_message_id TEXT DEFAULT '',
|
|
15765
|
-
nudge_anchor_text TEXT DEFAULT '',
|
|
15766
|
-
sticky_turn_reminder_text TEXT DEFAULT '',
|
|
15767
|
-
sticky_turn_reminder_message_id TEXT DEFAULT '',
|
|
15768
|
-
note_nudge_trigger_pending INTEGER DEFAULT 0,
|
|
15769
|
-
note_nudge_trigger_message_id TEXT DEFAULT '',
|
|
15770
|
-
note_nudge_sticky_text TEXT DEFAULT '',
|
|
15771
|
-
note_nudge_sticky_message_id TEXT DEFAULT '',
|
|
15772
|
-
is_subagent INTEGER DEFAULT 0,
|
|
15773
|
-
last_context_percentage REAL DEFAULT 0,
|
|
15774
|
-
last_input_tokens INTEGER DEFAULT 0,
|
|
15775
|
-
times_execute_threshold_reached INTEGER DEFAULT 0,
|
|
15776
|
-
compartment_in_progress INTEGER DEFAULT 0,
|
|
15777
|
-
system_prompt_hash TEXT DEFAULT '',
|
|
15778
|
-
memory_block_cache TEXT DEFAULT '',
|
|
15779
|
-
memory_block_count INTEGER DEFAULT 0
|
|
15780
|
-
);
|
|
15781
|
-
|
|
15782
|
-
CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
|
|
15783
|
-
CREATE INDEX IF NOT EXISTS idx_pending_ops_session ON pending_ops(session_id);
|
|
15784
|
-
CREATE INDEX IF NOT EXISTS idx_pending_ops_session_tag_id ON pending_ops(session_id, tag_id);
|
|
15785
|
-
CREATE INDEX IF NOT EXISTS idx_source_contents_session ON source_contents(session_id);
|
|
15786
|
-
|
|
15787
|
-
CREATE TABLE IF NOT EXISTS recomp_compartments (
|
|
15788
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
15789
|
-
session_id TEXT NOT NULL,
|
|
15790
|
-
sequence INTEGER NOT NULL,
|
|
15791
|
-
start_message INTEGER NOT NULL,
|
|
15792
|
-
end_message INTEGER NOT NULL,
|
|
15793
|
-
start_message_id TEXT DEFAULT '',
|
|
15794
|
-
end_message_id TEXT DEFAULT '',
|
|
15795
|
-
title TEXT NOT NULL,
|
|
15796
|
-
content TEXT NOT NULL,
|
|
15797
|
-
pass_number INTEGER NOT NULL,
|
|
15798
|
-
created_at INTEGER NOT NULL,
|
|
15799
|
-
UNIQUE(session_id, sequence)
|
|
15800
|
-
);
|
|
15801
|
-
|
|
15802
|
-
CREATE TABLE IF NOT EXISTS recomp_facts (
|
|
15803
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
15804
|
-
session_id TEXT NOT NULL,
|
|
15805
|
-
category TEXT NOT NULL,
|
|
15806
|
-
content TEXT NOT NULL,
|
|
15807
|
-
pass_number INTEGER NOT NULL,
|
|
15808
|
-
created_at INTEGER NOT NULL
|
|
15809
|
-
);
|
|
15790
|
+
// src/hooks/magic-context/tag-part-guards.ts
|
|
15791
|
+
function isTextPart(part) {
|
|
15792
|
+
if (part === null || typeof part !== "object")
|
|
15793
|
+
return false;
|
|
15794
|
+
const p = part;
|
|
15795
|
+
return p.type === "text" && typeof p.text === "string";
|
|
15796
|
+
}
|
|
15797
|
+
function isToolPartWithOutput(part) {
|
|
15798
|
+
if (part === null || typeof part !== "object")
|
|
15799
|
+
return false;
|
|
15800
|
+
const p = part;
|
|
15801
|
+
if (p.type !== "tool" || typeof p.callID !== "string")
|
|
15802
|
+
return false;
|
|
15803
|
+
if (p.state === null || typeof p.state !== "object")
|
|
15804
|
+
return false;
|
|
15805
|
+
return typeof p.state.output === "string";
|
|
15806
|
+
}
|
|
15807
|
+
function isFilePart(part) {
|
|
15808
|
+
if (part === null || typeof part !== "object")
|
|
15809
|
+
return false;
|
|
15810
|
+
const p = part;
|
|
15811
|
+
return p.type === "file" && typeof p.url === "string";
|
|
15812
|
+
}
|
|
15813
|
+
function buildFileSourceContent(parts) {
|
|
15814
|
+
const content = parts.filter(isTextPart).map((part) => stripTagPrefix(part.text)).join(`
|
|
15815
|
+
`).trim();
|
|
15816
|
+
return content.length > 0 ? content : null;
|
|
15817
|
+
}
|
|
15810
15818
|
|
|
15811
|
-
|
|
15812
|
-
|
|
15813
|
-
|
|
15814
|
-
|
|
15815
|
-
CREATE INDEX IF NOT EXISTS idx_session_notes_session ON session_notes(session_id);
|
|
15816
|
-
CREATE INDEX IF NOT EXISTS idx_memories_project_status_category ON memories(project_path, status, category);
|
|
15817
|
-
CREATE INDEX IF NOT EXISTS idx_memories_project_status_expires ON memories(project_path, status, expires_at);
|
|
15818
|
-
CREATE INDEX IF NOT EXISTS idx_memories_project_category_hash ON memories(project_path, category, normalized_hash);
|
|
15819
|
-
CREATE INDEX IF NOT EXISTS idx_message_history_index_updated_at ON message_history_index(updated_at);
|
|
15820
|
-
`);
|
|
15821
|
-
ensureColumn(db, "session_meta", "last_nudge_band", "TEXT DEFAULT ''");
|
|
15822
|
-
ensureColumn(db, "session_meta", "last_transform_error", "TEXT DEFAULT ''");
|
|
15823
|
-
ensureColumn(db, "session_meta", "nudge_anchor_message_id", "TEXT DEFAULT ''");
|
|
15824
|
-
ensureColumn(db, "session_meta", "nudge_anchor_text", "TEXT DEFAULT ''");
|
|
15825
|
-
ensureColumn(db, "session_meta", "sticky_turn_reminder_text", "TEXT DEFAULT ''");
|
|
15826
|
-
ensureColumn(db, "session_meta", "sticky_turn_reminder_message_id", "TEXT DEFAULT ''");
|
|
15827
|
-
ensureColumn(db, "session_meta", "note_nudge_trigger_pending", "INTEGER DEFAULT 0");
|
|
15828
|
-
ensureColumn(db, "session_meta", "note_nudge_trigger_message_id", "TEXT DEFAULT ''");
|
|
15829
|
-
ensureColumn(db, "session_meta", "note_nudge_sticky_text", "TEXT DEFAULT ''");
|
|
15830
|
-
ensureColumn(db, "session_meta", "note_nudge_sticky_message_id", "TEXT DEFAULT ''");
|
|
15831
|
-
ensureColumn(db, "session_meta", "times_execute_threshold_reached", "INTEGER DEFAULT 0");
|
|
15832
|
-
ensureColumn(db, "session_meta", "compartment_in_progress", "INTEGER DEFAULT 0");
|
|
15833
|
-
ensureColumn(db, "session_meta", "system_prompt_hash", "TEXT DEFAULT ''");
|
|
15834
|
-
ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
|
|
15835
|
-
ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
|
|
15836
|
-
ensureColumn(db, "compartments", "start_message_id", "TEXT DEFAULT ''");
|
|
15837
|
-
ensureColumn(db, "compartments", "end_message_id", "TEXT DEFAULT ''");
|
|
15838
|
-
ensureColumn(db, "memory_embeddings", "model_id", "TEXT");
|
|
15839
|
-
ensureColumn(db, "session_meta", "memory_block_cache", "TEXT DEFAULT ''");
|
|
15840
|
-
ensureColumn(db, "session_meta", "memory_block_count", "INTEGER DEFAULT 0");
|
|
15841
|
-
ensureColumn(db, "dream_queue", "retry_count", "INTEGER DEFAULT 0");
|
|
15819
|
+
// src/hooks/magic-context/read-session-chunk.ts
|
|
15820
|
+
var activeRawMessageCache = null;
|
|
15821
|
+
function cleanUserText(text) {
|
|
15822
|
+
return removeSystemReminders(text).replace(OMO_INTERNAL_INITIATOR_MARKER, "").trim();
|
|
15842
15823
|
}
|
|
15843
|
-
function
|
|
15844
|
-
|
|
15845
|
-
|
|
15824
|
+
function withRawSessionMessageCache(fn) {
|
|
15825
|
+
const outerCache = activeRawMessageCache;
|
|
15826
|
+
if (!outerCache) {
|
|
15827
|
+
activeRawMessageCache = new Map;
|
|
15846
15828
|
}
|
|
15847
|
-
|
|
15848
|
-
|
|
15849
|
-
|
|
15829
|
+
try {
|
|
15830
|
+
return fn();
|
|
15831
|
+
} finally {
|
|
15832
|
+
if (!outerCache) {
|
|
15833
|
+
activeRawMessageCache = null;
|
|
15834
|
+
}
|
|
15850
15835
|
}
|
|
15851
|
-
db.run(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
|
15852
15836
|
}
|
|
15853
|
-
function
|
|
15854
|
-
|
|
15855
|
-
const
|
|
15856
|
-
|
|
15857
|
-
|
|
15858
|
-
|
|
15859
|
-
|
|
15837
|
+
function readRawSessionMessages(sessionId) {
|
|
15838
|
+
if (activeRawMessageCache) {
|
|
15839
|
+
const cached2 = activeRawMessageCache.get(sessionId);
|
|
15840
|
+
if (cached2) {
|
|
15841
|
+
return cached2;
|
|
15842
|
+
}
|
|
15843
|
+
const messages = withReadOnlySessionDb((db) => readRawSessionMessagesFromDb(db, sessionId));
|
|
15844
|
+
activeRawMessageCache.set(sessionId, messages);
|
|
15845
|
+
return messages;
|
|
15860
15846
|
}
|
|
15847
|
+
return withReadOnlySessionDb((db) => readRawSessionMessagesFromDb(db, sessionId));
|
|
15861
15848
|
}
|
|
15862
|
-
function
|
|
15863
|
-
|
|
15864
|
-
|
|
15865
|
-
|
|
15866
|
-
|
|
15867
|
-
|
|
15868
|
-
|
|
15849
|
+
function getRawSessionMessageCount(sessionId) {
|
|
15850
|
+
return withReadOnlySessionDb((db) => getRawSessionMessageCountFromDb(db, sessionId));
|
|
15851
|
+
}
|
|
15852
|
+
function getRawSessionTagKeysThrough(sessionId, upToMessageIndex) {
|
|
15853
|
+
const messages = readRawSessionMessages(sessionId);
|
|
15854
|
+
const keys = [];
|
|
15855
|
+
for (const message of messages) {
|
|
15856
|
+
if (message.ordinal > upToMessageIndex)
|
|
15857
|
+
break;
|
|
15858
|
+
for (const [partIndex, part] of message.parts.entries()) {
|
|
15859
|
+
if (isTextPart(part)) {
|
|
15860
|
+
keys.push(`${message.id}:p${partIndex}`);
|
|
15869
15861
|
}
|
|
15870
|
-
|
|
15871
|
-
|
|
15872
|
-
|
|
15873
|
-
|
|
15874
|
-
|
|
15875
|
-
databases.set(dbPath, db);
|
|
15876
|
-
persistenceByDatabase.set(db, true);
|
|
15877
|
-
persistenceErrorByDatabase.delete(db);
|
|
15878
|
-
return db;
|
|
15879
|
-
} catch (error48) {
|
|
15880
|
-
log("[magic-context] storage error:", error48);
|
|
15881
|
-
const errorMessage = getErrorMessage(error48);
|
|
15882
|
-
const existingFallback = databases.get(FALLBACK_DATABASE_KEY);
|
|
15883
|
-
if (existingFallback) {
|
|
15884
|
-
if (!persistenceByDatabase.has(existingFallback)) {
|
|
15885
|
-
persistenceByDatabase.set(existingFallback, false);
|
|
15886
|
-
persistenceErrorByDatabase.set(existingFallback, errorMessage);
|
|
15862
|
+
if (isFilePart(part)) {
|
|
15863
|
+
keys.push(`${message.id}:file${partIndex}`);
|
|
15864
|
+
}
|
|
15865
|
+
if (isToolPartWithOutput(part)) {
|
|
15866
|
+
keys.push(part.callID);
|
|
15887
15867
|
}
|
|
15888
|
-
return existingFallback;
|
|
15889
15868
|
}
|
|
15890
|
-
const fallback = createFallbackDatabase();
|
|
15891
|
-
databases.set(FALLBACK_DATABASE_KEY, fallback);
|
|
15892
|
-
persistenceByDatabase.set(fallback, false);
|
|
15893
|
-
persistenceErrorByDatabase.set(fallback, errorMessage);
|
|
15894
|
-
return fallback;
|
|
15895
15869
|
}
|
|
15870
|
+
return keys;
|
|
15896
15871
|
}
|
|
15897
|
-
|
|
15898
|
-
|
|
15899
|
-
|
|
15900
|
-
|
|
15901
|
-
|
|
15902
|
-
|
|
15903
|
-
|
|
15904
|
-
|
|
15905
|
-
lastResponseTime: "last_response_time",
|
|
15906
|
-
cacheTtl: "cache_ttl",
|
|
15907
|
-
counter: "counter",
|
|
15908
|
-
lastNudgeTokens: "last_nudge_tokens",
|
|
15909
|
-
lastNudgeBand: "last_nudge_band",
|
|
15910
|
-
lastTransformError: "last_transform_error",
|
|
15911
|
-
isSubagent: "is_subagent",
|
|
15912
|
-
lastContextPercentage: "last_context_percentage",
|
|
15913
|
-
lastInputTokens: "last_input_tokens",
|
|
15914
|
-
timesExecuteThresholdReached: "times_execute_threshold_reached",
|
|
15915
|
-
compartmentInProgress: "compartment_in_progress",
|
|
15916
|
-
systemPromptHash: "system_prompt_hash",
|
|
15917
|
-
clearedReasoningThroughTag: "cleared_reasoning_through_tag"
|
|
15918
|
-
};
|
|
15919
|
-
var BOOLEAN_META_KEYS = new Set(["isSubagent", "compartmentInProgress"]);
|
|
15920
|
-
function isSessionMetaRow(row) {
|
|
15921
|
-
if (row === null || typeof row !== "object")
|
|
15922
|
-
return false;
|
|
15923
|
-
const r = row;
|
|
15924
|
-
return typeof r.session_id === "string" && typeof r.last_response_time === "number" && typeof r.cache_ttl === "string" && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && typeof r.last_nudge_band === "string" && typeof r.last_transform_error === "string" && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && typeof r.times_execute_threshold_reached === "number" && typeof r.compartment_in_progress === "number" && (typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && typeof r.cleared_reasoning_through_tag === "number";
|
|
15925
|
-
}
|
|
15926
|
-
function getDefaultSessionMeta(sessionId) {
|
|
15927
|
-
return {
|
|
15928
|
-
sessionId,
|
|
15929
|
-
lastResponseTime: 0,
|
|
15930
|
-
cacheTtl: "5m",
|
|
15931
|
-
counter: 0,
|
|
15932
|
-
lastNudgeTokens: 0,
|
|
15933
|
-
lastNudgeBand: null,
|
|
15934
|
-
lastTransformError: null,
|
|
15935
|
-
isSubagent: false,
|
|
15936
|
-
lastContextPercentage: 0,
|
|
15937
|
-
lastInputTokens: 0,
|
|
15938
|
-
timesExecuteThresholdReached: 0,
|
|
15939
|
-
compartmentInProgress: false,
|
|
15940
|
-
systemPromptHash: "",
|
|
15941
|
-
clearedReasoningThroughTag: 0
|
|
15942
|
-
};
|
|
15943
|
-
}
|
|
15944
|
-
function ensureSessionMetaRow(db, sessionId) {
|
|
15945
|
-
const defaults = getDefaultSessionMeta(sessionId);
|
|
15946
|
-
db.prepare("INSERT OR IGNORE INTO session_meta (session_id, last_response_time, cache_ttl, counter, last_nudge_tokens, last_nudge_band, last_transform_error, is_subagent, last_context_percentage, last_input_tokens, times_execute_threshold_reached, compartment_in_progress, system_prompt_hash, cleared_reasoning_through_tag) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(sessionId, defaults.lastResponseTime, defaults.cacheTtl, defaults.counter, defaults.lastNudgeTokens, defaults.lastNudgeBand ?? "", defaults.lastTransformError ?? "", defaults.isSubagent ? 1 : 0, defaults.lastContextPercentage, defaults.lastInputTokens, defaults.timesExecuteThresholdReached, defaults.compartmentInProgress ? 1 : 0, defaults.systemPromptHash ?? "", defaults.clearedReasoningThroughTag);
|
|
15872
|
+
var PROTECTED_TAIL_USER_TURNS = 5;
|
|
15873
|
+
function getProtectedTailStartOrdinal(sessionId) {
|
|
15874
|
+
const messages = readRawSessionMessages(sessionId);
|
|
15875
|
+
const userOrdinals = messages.filter((m) => m.role === "user" && hasMeaningfulUserText(m.parts)).map((m) => m.ordinal);
|
|
15876
|
+
if (userOrdinals.length < PROTECTED_TAIL_USER_TURNS) {
|
|
15877
|
+
return 1;
|
|
15878
|
+
}
|
|
15879
|
+
return userOrdinals[userOrdinals.length - PROTECTED_TAIL_USER_TURNS];
|
|
15947
15880
|
}
|
|
15948
|
-
function
|
|
15881
|
+
function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal) {
|
|
15882
|
+
const messages = readRawSessionMessages(sessionId);
|
|
15883
|
+
const startOrdinal = Math.max(1, offset);
|
|
15884
|
+
const lines = [];
|
|
15885
|
+
const lineMeta = [];
|
|
15886
|
+
let totalTokens = 0;
|
|
15887
|
+
let messagesProcessed = 0;
|
|
15888
|
+
let lastOrdinal = startOrdinal - 1;
|
|
15889
|
+
let lastMessageId = "";
|
|
15890
|
+
let firstMessageId = "";
|
|
15891
|
+
let currentBlock = null;
|
|
15892
|
+
let pendingNoiseMeta = [];
|
|
15893
|
+
let commitClusters = 0;
|
|
15894
|
+
let lastFlushedRole = "";
|
|
15895
|
+
function flushCurrentBlock() {
|
|
15896
|
+
if (!currentBlock)
|
|
15897
|
+
return true;
|
|
15898
|
+
const blockText = formatBlock(currentBlock);
|
|
15899
|
+
const blockTokens = estimateTokens(blockText);
|
|
15900
|
+
if (totalTokens + blockTokens > tokenBudget && totalTokens > 0) {
|
|
15901
|
+
return false;
|
|
15902
|
+
}
|
|
15903
|
+
if (currentBlock.role === "A" && currentBlock.commitHashes.length > 0 && lastFlushedRole !== "A") {
|
|
15904
|
+
commitClusters++;
|
|
15905
|
+
}
|
|
15906
|
+
lastFlushedRole = currentBlock.role;
|
|
15907
|
+
if (!firstMessageId)
|
|
15908
|
+
firstMessageId = currentBlock.meta[0]?.messageId ?? "";
|
|
15909
|
+
lastOrdinal = currentBlock.meta[currentBlock.meta.length - 1]?.ordinal ?? currentBlock.endOrdinal;
|
|
15910
|
+
lastMessageId = currentBlock.meta[currentBlock.meta.length - 1]?.messageId ?? "";
|
|
15911
|
+
messagesProcessed += currentBlock.meta.length;
|
|
15912
|
+
lines.push(blockText);
|
|
15913
|
+
lineMeta.push(...currentBlock.meta);
|
|
15914
|
+
totalTokens += blockTokens;
|
|
15915
|
+
currentBlock = null;
|
|
15916
|
+
return true;
|
|
15917
|
+
}
|
|
15918
|
+
for (const msg of messages) {
|
|
15919
|
+
if (eligibleEndOrdinal !== undefined && msg.ordinal >= eligibleEndOrdinal)
|
|
15920
|
+
break;
|
|
15921
|
+
if (msg.ordinal < startOrdinal)
|
|
15922
|
+
continue;
|
|
15923
|
+
const meta3 = { ordinal: msg.ordinal, messageId: msg.id };
|
|
15924
|
+
if (msg.role === "user" && !hasMeaningfulUserText(msg.parts)) {
|
|
15925
|
+
pendingNoiseMeta.push(meta3);
|
|
15926
|
+
continue;
|
|
15927
|
+
}
|
|
15928
|
+
const role = compactRole(msg.role);
|
|
15929
|
+
const compacted = compactTextForSummary(extractTexts(msg.parts).map((t) => msg.role === "user" ? cleanUserText(t) : t).map(normalizeText).filter((value) => value.length > 0).join(" / "), msg.role);
|
|
15930
|
+
const text = compacted.text;
|
|
15931
|
+
if (!text) {
|
|
15932
|
+
pendingNoiseMeta.push(meta3);
|
|
15933
|
+
continue;
|
|
15934
|
+
}
|
|
15935
|
+
if (currentBlock && currentBlock.role === role) {
|
|
15936
|
+
currentBlock.endOrdinal = msg.ordinal;
|
|
15937
|
+
currentBlock.parts.push(text);
|
|
15938
|
+
currentBlock.meta.push(...pendingNoiseMeta, meta3);
|
|
15939
|
+
currentBlock.commitHashes = mergeCommitHashes(currentBlock.commitHashes, compacted.commitHashes);
|
|
15940
|
+
pendingNoiseMeta = [];
|
|
15941
|
+
continue;
|
|
15942
|
+
}
|
|
15943
|
+
if (!flushCurrentBlock())
|
|
15944
|
+
break;
|
|
15945
|
+
currentBlock = {
|
|
15946
|
+
role,
|
|
15947
|
+
startOrdinal: pendingNoiseMeta[0]?.ordinal ?? msg.ordinal,
|
|
15948
|
+
endOrdinal: msg.ordinal,
|
|
15949
|
+
parts: [text],
|
|
15950
|
+
meta: [...pendingNoiseMeta, meta3],
|
|
15951
|
+
commitHashes: [...compacted.commitHashes]
|
|
15952
|
+
};
|
|
15953
|
+
pendingNoiseMeta = [];
|
|
15954
|
+
}
|
|
15955
|
+
flushCurrentBlock();
|
|
15949
15956
|
return {
|
|
15950
|
-
|
|
15951
|
-
|
|
15952
|
-
|
|
15953
|
-
|
|
15954
|
-
|
|
15955
|
-
|
|
15956
|
-
|
|
15957
|
-
|
|
15958
|
-
|
|
15959
|
-
|
|
15960
|
-
|
|
15961
|
-
compartmentInProgress: row.compartment_in_progress === 1,
|
|
15962
|
-
systemPromptHash: String(row.system_prompt_hash),
|
|
15963
|
-
clearedReasoningThroughTag: row.cleared_reasoning_through_tag
|
|
15957
|
+
startIndex: startOrdinal,
|
|
15958
|
+
endIndex: lastOrdinal,
|
|
15959
|
+
startMessageId: firstMessageId,
|
|
15960
|
+
endMessageId: lastMessageId,
|
|
15961
|
+
messageCount: messagesProcessed,
|
|
15962
|
+
tokenEstimate: totalTokens,
|
|
15963
|
+
hasMore: lastOrdinal < (eligibleEndOrdinal !== undefined ? Math.min(eligibleEndOrdinal - 1, messages.length) : messages.length),
|
|
15964
|
+
text: lines.join(`
|
|
15965
|
+
`),
|
|
15966
|
+
lines: lineMeta,
|
|
15967
|
+
commitClusterCount: commitClusters
|
|
15964
15968
|
};
|
|
15965
15969
|
}
|
|
15966
15970
|
|
|
15967
|
-
// src/features/magic-context/
|
|
15968
|
-
|
|
15969
|
-
|
|
15970
|
-
|
|
15971
|
-
|
|
15972
|
-
|
|
15971
|
+
// src/features/magic-context/message-index.ts
|
|
15972
|
+
var lastIndexedStatements = new WeakMap;
|
|
15973
|
+
var insertMessageStatements = new WeakMap;
|
|
15974
|
+
var upsertIndexStatements = new WeakMap;
|
|
15975
|
+
var deleteFtsStatements = new WeakMap;
|
|
15976
|
+
var deleteIndexStatements = new WeakMap;
|
|
15977
|
+
var countIndexedMessageStatements = new WeakMap;
|
|
15978
|
+
var deleteIndexedMessageStatements = new WeakMap;
|
|
15979
|
+
function normalizeIndexText(text) {
|
|
15980
|
+
return text.replace(/\s+/g, " ").trim();
|
|
15973
15981
|
}
|
|
15974
|
-
function
|
|
15975
|
-
|
|
15976
|
-
|
|
15977
|
-
|
|
15978
|
-
|
|
15982
|
+
function getLastIndexedStatement(db) {
|
|
15983
|
+
let stmt = lastIndexedStatements.get(db);
|
|
15984
|
+
if (!stmt) {
|
|
15985
|
+
stmt = db.prepare("SELECT last_indexed_ordinal FROM message_history_index WHERE session_id = ?");
|
|
15986
|
+
lastIndexedStatements.set(db, stmt);
|
|
15987
|
+
}
|
|
15988
|
+
return stmt;
|
|
15979
15989
|
}
|
|
15980
|
-
function
|
|
15981
|
-
|
|
15982
|
-
|
|
15983
|
-
|
|
15984
|
-
|
|
15990
|
+
function getInsertMessageStatement(db) {
|
|
15991
|
+
let stmt = insertMessageStatements.get(db);
|
|
15992
|
+
if (!stmt) {
|
|
15993
|
+
stmt = db.prepare("INSERT INTO message_history_fts (session_id, message_ordinal, message_id, role, content) VALUES (?, ?, ?, ?, ?)");
|
|
15994
|
+
insertMessageStatements.set(db, stmt);
|
|
15995
|
+
}
|
|
15996
|
+
return stmt;
|
|
15985
15997
|
}
|
|
15986
|
-
function
|
|
15987
|
-
|
|
15988
|
-
|
|
15989
|
-
|
|
15990
|
-
|
|
15998
|
+
function getUpsertIndexStatement(db) {
|
|
15999
|
+
let stmt = upsertIndexStatements.get(db);
|
|
16000
|
+
if (!stmt) {
|
|
16001
|
+
stmt = db.prepare("INSERT INTO message_history_index (session_id, last_indexed_ordinal, updated_at) VALUES (?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET last_indexed_ordinal = excluded.last_indexed_ordinal, updated_at = excluded.updated_at");
|
|
16002
|
+
upsertIndexStatements.set(db, stmt);
|
|
16003
|
+
}
|
|
16004
|
+
return stmt;
|
|
15991
16005
|
}
|
|
15992
|
-
function
|
|
15993
|
-
|
|
15994
|
-
|
|
15995
|
-
|
|
15996
|
-
|
|
15997
|
-
stickyMessageId: null
|
|
15998
|
-
};
|
|
15999
|
-
}
|
|
16000
|
-
function loadPersistedUsage(db, sessionId) {
|
|
16001
|
-
const result = db.prepare("SELECT last_context_percentage, last_input_tokens, last_response_time FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
16002
|
-
if (!isPersistedUsageRow(result) || result.last_context_percentage === 0 && result.last_input_tokens === 0) {
|
|
16003
|
-
return null;
|
|
16006
|
+
function getDeleteFtsStatement(db) {
|
|
16007
|
+
let stmt = deleteFtsStatements.get(db);
|
|
16008
|
+
if (!stmt) {
|
|
16009
|
+
stmt = db.prepare("DELETE FROM message_history_fts WHERE session_id = ?");
|
|
16010
|
+
deleteFtsStatements.set(db, stmt);
|
|
16004
16011
|
}
|
|
16005
|
-
return
|
|
16006
|
-
usage: {
|
|
16007
|
-
percentage: result.last_context_percentage,
|
|
16008
|
-
inputTokens: result.last_input_tokens
|
|
16009
|
-
},
|
|
16010
|
-
updatedAt: result.last_response_time || Date.now()
|
|
16011
|
-
};
|
|
16012
|
+
return stmt;
|
|
16012
16013
|
}
|
|
16013
|
-
function
|
|
16014
|
-
|
|
16015
|
-
if (!
|
|
16016
|
-
|
|
16017
|
-
|
|
16018
|
-
if (result.nudge_anchor_message_id.length === 0 || result.nudge_anchor_text.length === 0) {
|
|
16019
|
-
return null;
|
|
16014
|
+
function getDeleteIndexStatement(db) {
|
|
16015
|
+
let stmt = deleteIndexStatements.get(db);
|
|
16016
|
+
if (!stmt) {
|
|
16017
|
+
stmt = db.prepare("DELETE FROM message_history_index WHERE session_id = ?");
|
|
16018
|
+
deleteIndexStatements.set(db, stmt);
|
|
16020
16019
|
}
|
|
16021
|
-
return
|
|
16022
|
-
messageId: result.nudge_anchor_message_id,
|
|
16023
|
-
nudgeText: result.nudge_anchor_text
|
|
16024
|
-
};
|
|
16025
|
-
}
|
|
16026
|
-
function setPersistedNudgePlacement(db, sessionId, messageId, nudgeText) {
|
|
16027
|
-
db.transaction(() => {
|
|
16028
|
-
ensureSessionMetaRow(db, sessionId);
|
|
16029
|
-
db.prepare("UPDATE session_meta SET nudge_anchor_message_id = ?, nudge_anchor_text = ? WHERE session_id = ?").run(messageId, nudgeText, sessionId);
|
|
16030
|
-
})();
|
|
16031
|
-
}
|
|
16032
|
-
function clearPersistedNudgePlacement(db, sessionId) {
|
|
16033
|
-
db.prepare("UPDATE session_meta SET nudge_anchor_message_id = '', nudge_anchor_text = '' WHERE session_id = ?").run(sessionId);
|
|
16020
|
+
return stmt;
|
|
16034
16021
|
}
|
|
16035
|
-
function
|
|
16036
|
-
|
|
16037
|
-
if (!
|
|
16038
|
-
|
|
16039
|
-
|
|
16040
|
-
if (result.sticky_turn_reminder_text.length === 0) {
|
|
16041
|
-
return null;
|
|
16022
|
+
function getCountIndexedMessageStatement(db) {
|
|
16023
|
+
let stmt = countIndexedMessageStatements.get(db);
|
|
16024
|
+
if (!stmt) {
|
|
16025
|
+
stmt = db.prepare("SELECT COUNT(*) AS count FROM message_history_fts WHERE session_id = ? AND message_id = ?");
|
|
16026
|
+
countIndexedMessageStatements.set(db, stmt);
|
|
16042
16027
|
}
|
|
16043
|
-
return
|
|
16044
|
-
text: result.sticky_turn_reminder_text,
|
|
16045
|
-
messageId: result.sticky_turn_reminder_message_id.length > 0 ? result.sticky_turn_reminder_message_id : null
|
|
16046
|
-
};
|
|
16047
|
-
}
|
|
16048
|
-
function setPersistedStickyTurnReminder(db, sessionId, text, messageId = "") {
|
|
16049
|
-
db.transaction(() => {
|
|
16050
|
-
ensureSessionMetaRow(db, sessionId);
|
|
16051
|
-
db.prepare("UPDATE session_meta SET sticky_turn_reminder_text = ?, sticky_turn_reminder_message_id = ? WHERE session_id = ?").run(text, messageId, sessionId);
|
|
16052
|
-
})();
|
|
16053
|
-
}
|
|
16054
|
-
function clearPersistedStickyTurnReminder(db, sessionId) {
|
|
16055
|
-
db.prepare("UPDATE session_meta SET sticky_turn_reminder_text = '', sticky_turn_reminder_message_id = '' WHERE session_id = ?").run(sessionId);
|
|
16028
|
+
return stmt;
|
|
16056
16029
|
}
|
|
16057
|
-
function
|
|
16058
|
-
|
|
16059
|
-
if (!
|
|
16060
|
-
|
|
16030
|
+
function getDeleteIndexedMessageStatement(db) {
|
|
16031
|
+
let stmt = deleteIndexedMessageStatements.get(db);
|
|
16032
|
+
if (!stmt) {
|
|
16033
|
+
stmt = db.prepare("DELETE FROM message_history_fts WHERE session_id = ? AND message_id = ?");
|
|
16034
|
+
deleteIndexedMessageStatements.set(db, stmt);
|
|
16061
16035
|
}
|
|
16062
|
-
return
|
|
16063
|
-
triggerPending: result.note_nudge_trigger_pending === 1,
|
|
16064
|
-
triggerMessageId: result.note_nudge_trigger_message_id.length > 0 ? result.note_nudge_trigger_message_id : null,
|
|
16065
|
-
stickyText: result.note_nudge_sticky_text.length > 0 ? result.note_nudge_sticky_text : null,
|
|
16066
|
-
stickyMessageId: result.note_nudge_sticky_message_id.length > 0 ? result.note_nudge_sticky_message_id : null
|
|
16067
|
-
};
|
|
16036
|
+
return stmt;
|
|
16068
16037
|
}
|
|
16069
|
-
function
|
|
16070
|
-
db.
|
|
16071
|
-
|
|
16072
|
-
db.prepare("UPDATE session_meta SET note_nudge_trigger_pending = 1, note_nudge_trigger_message_id = ?, note_nudge_sticky_text = '', note_nudge_sticky_message_id = '' WHERE session_id = ?").run(triggerMessageId, sessionId);
|
|
16073
|
-
})();
|
|
16038
|
+
function getLastIndexedOrdinal(db, sessionId) {
|
|
16039
|
+
const row = getLastIndexedStatement(db).get(sessionId);
|
|
16040
|
+
return typeof row?.last_indexed_ordinal === "number" ? row.last_indexed_ordinal : 0;
|
|
16074
16041
|
}
|
|
16075
|
-
function
|
|
16076
|
-
db.
|
|
16077
|
-
|
|
16078
|
-
|
|
16079
|
-
|
|
16042
|
+
function deleteIndexedMessage(db, sessionId, messageId) {
|
|
16043
|
+
const row = getCountIndexedMessageStatement(db).get(sessionId, messageId);
|
|
16044
|
+
const count = typeof row?.count === "number" ? row.count : 0;
|
|
16045
|
+
if (count > 0) {
|
|
16046
|
+
getDeleteIndexedMessageStatement(db).run(sessionId, messageId);
|
|
16047
|
+
}
|
|
16048
|
+
getDeleteIndexStatement(db).run(sessionId);
|
|
16049
|
+
return count;
|
|
16080
16050
|
}
|
|
16081
|
-
function
|
|
16051
|
+
function clearIndexedMessages(db, sessionId) {
|
|
16082
16052
|
db.transaction(() => {
|
|
16083
|
-
|
|
16084
|
-
db
|
|
16053
|
+
getDeleteFtsStatement(db).run(sessionId);
|
|
16054
|
+
getDeleteIndexStatement(db).run(sessionId);
|
|
16085
16055
|
})();
|
|
16086
16056
|
}
|
|
16087
|
-
function
|
|
16088
|
-
|
|
16089
|
-
|
|
16090
|
-
|
|
16091
|
-
|
|
16092
|
-
|
|
16093
|
-
if (!raw || raw.length === 0)
|
|
16094
|
-
return new Set;
|
|
16095
|
-
try {
|
|
16096
|
-
const parsed = JSON.parse(raw);
|
|
16097
|
-
if (Array.isArray(parsed))
|
|
16098
|
-
return new Set(parsed.filter((v) => typeof v === "string"));
|
|
16099
|
-
} catch {}
|
|
16100
|
-
return new Set;
|
|
16101
|
-
}
|
|
16102
|
-
function setStrippedPlaceholderIds(db, sessionId, ids) {
|
|
16103
|
-
ensureSessionMetaRow(db, sessionId);
|
|
16104
|
-
const json2 = ids.size > 0 ? JSON.stringify([...ids]) : "";
|
|
16105
|
-
db.prepare("UPDATE session_meta SET stripped_placeholder_ids = ? WHERE session_id = ?").run(json2, sessionId);
|
|
16106
|
-
}
|
|
16107
|
-
// src/shared/internal-initiator-marker.ts
|
|
16108
|
-
var OMO_INTERNAL_INITIATOR_MARKER = "<!-- OMO_INTERNAL_INITIATOR -->";
|
|
16109
|
-
|
|
16110
|
-
// src/shared/system-directive.ts
|
|
16111
|
-
var SYSTEM_DIRECTIVE_PREFIX = "[SYSTEM DIRECTIVE: MAGIC-CONTEXT";
|
|
16112
|
-
function isSystemDirective(text) {
|
|
16113
|
-
return text.trimStart().startsWith(SYSTEM_DIRECTIVE_PREFIX);
|
|
16114
|
-
}
|
|
16115
|
-
function removeSystemReminders(text) {
|
|
16116
|
-
return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gi, "").trim();
|
|
16117
|
-
}
|
|
16118
|
-
|
|
16119
|
-
// src/hooks/magic-context/read-session-db.ts
|
|
16120
|
-
import { Database as Database2 } from "bun:sqlite";
|
|
16121
|
-
import { join as join6 } from "path";
|
|
16122
|
-
function getOpenCodeDbPath() {
|
|
16123
|
-
return join6(getDataDir(), "opencode", "opencode.db");
|
|
16124
|
-
}
|
|
16125
|
-
var cachedReadOnlyDb = null;
|
|
16126
|
-
function closeCachedReadOnlyDb() {
|
|
16127
|
-
if (!cachedReadOnlyDb) {
|
|
16128
|
-
return;
|
|
16057
|
+
function getIndexableContent(role, parts) {
|
|
16058
|
+
if (role === "user") {
|
|
16059
|
+
if (!hasMeaningfulUserText(parts)) {
|
|
16060
|
+
return "";
|
|
16061
|
+
}
|
|
16062
|
+
return extractTexts(parts).map(cleanUserText).map(normalizeIndexText).filter((text) => text.length > 0).join(" / ");
|
|
16129
16063
|
}
|
|
16130
|
-
|
|
16131
|
-
|
|
16132
|
-
} catch (error48) {
|
|
16133
|
-
log("[magic-context] failed to close cached OpenCode read-only DB:", error48);
|
|
16134
|
-
} finally {
|
|
16135
|
-
cachedReadOnlyDb = null;
|
|
16064
|
+
if (role === "assistant") {
|
|
16065
|
+
return extractTexts(parts).map(removeSystemReminders).map(normalizeIndexText).filter((text) => text.length > 0).join(" / ");
|
|
16136
16066
|
}
|
|
16067
|
+
return "";
|
|
16137
16068
|
}
|
|
16138
|
-
function
|
|
16139
|
-
const
|
|
16140
|
-
if (
|
|
16141
|
-
|
|
16069
|
+
function ensureMessagesIndexed(db, sessionId, readMessages) {
|
|
16070
|
+
const messages = readMessages(sessionId);
|
|
16071
|
+
if (messages.length === 0) {
|
|
16072
|
+
db.transaction(() => clearIndexedMessages(db, sessionId))();
|
|
16073
|
+
return;
|
|
16142
16074
|
}
|
|
16143
|
-
|
|
16144
|
-
|
|
16145
|
-
|
|
16146
|
-
|
|
16147
|
-
}
|
|
16148
|
-
function withReadOnlySessionDb(fn) {
|
|
16149
|
-
return fn(getReadOnlySessionDb());
|
|
16150
|
-
}
|
|
16151
|
-
function getRawSessionMessageCountFromDb(db, sessionId) {
|
|
16152
|
-
const row = db.prepare("SELECT COUNT(*) as count FROM message WHERE session_id = ?").get(sessionId);
|
|
16153
|
-
return typeof row?.count === "number" ? row.count : 0;
|
|
16154
|
-
}
|
|
16155
|
-
|
|
16156
|
-
// src/hooks/magic-context/read-session-formatting.ts
|
|
16157
|
-
var COMMIT_HASH_PATTERN = /`?\b([0-9a-f]{6,12})\b`?/gi;
|
|
16158
|
-
var COMMIT_HINT_PATTERN = /\b(commit(?:ted)?|cherry-?pick(?:ed)?|hash(?:es)?|sha)\b/i;
|
|
16159
|
-
var MAX_COMMITS_PER_BLOCK = 5;
|
|
16160
|
-
function hasMeaningfulUserText(parts) {
|
|
16161
|
-
for (const part of parts) {
|
|
16162
|
-
if (part === null || typeof part !== "object")
|
|
16163
|
-
continue;
|
|
16164
|
-
const candidate = part;
|
|
16165
|
-
if (candidate.type !== "text" || typeof candidate.text !== "string")
|
|
16166
|
-
continue;
|
|
16167
|
-
if (candidate.ignored === true)
|
|
16168
|
-
continue;
|
|
16169
|
-
const cleaned = removeSystemReminders(candidate.text).replace(OMO_INTERNAL_INITIATOR_MARKER, "").trim();
|
|
16170
|
-
if (!cleaned)
|
|
16171
|
-
continue;
|
|
16172
|
-
if (isSystemDirective(cleaned))
|
|
16173
|
-
continue;
|
|
16174
|
-
return true;
|
|
16075
|
+
let lastIndexedOrdinal = getLastIndexedOrdinal(db, sessionId);
|
|
16076
|
+
if (lastIndexedOrdinal > messages.length) {
|
|
16077
|
+
db.transaction(() => clearIndexedMessages(db, sessionId))();
|
|
16078
|
+
lastIndexedOrdinal = 0;
|
|
16175
16079
|
}
|
|
16176
|
-
|
|
16177
|
-
|
|
16178
|
-
function extractTexts(parts) {
|
|
16179
|
-
const texts = [];
|
|
16180
|
-
for (const part of parts) {
|
|
16181
|
-
if (part === null || typeof part !== "object")
|
|
16182
|
-
continue;
|
|
16183
|
-
const p = part;
|
|
16184
|
-
if (p.type === "text" && typeof p.text === "string" && p.text.trim().length > 0) {
|
|
16185
|
-
texts.push(p.text.trim());
|
|
16186
|
-
}
|
|
16080
|
+
if (lastIndexedOrdinal >= messages.length) {
|
|
16081
|
+
return;
|
|
16187
16082
|
}
|
|
16188
|
-
|
|
16189
|
-
|
|
16190
|
-
|
|
16191
|
-
|
|
16192
|
-
|
|
16193
|
-
|
|
16194
|
-
|
|
16195
|
-
|
|
16196
|
-
|
|
16197
|
-
|
|
16198
|
-
|
|
16199
|
-
|
|
16200
|
-
|
|
16201
|
-
|
|
16083
|
+
const messagesToInsert = messages.filter((message) => message.ordinal > lastIndexedOrdinal).filter((message) => message.role === "user" || message.role === "assistant").map((message) => ({
|
|
16084
|
+
ordinal: message.ordinal,
|
|
16085
|
+
id: message.id,
|
|
16086
|
+
role: message.role,
|
|
16087
|
+
content: getIndexableContent(message.role, message.parts)
|
|
16088
|
+
})).filter((message) => message.content.length > 0);
|
|
16089
|
+
const now = Date.now();
|
|
16090
|
+
db.transaction(() => {
|
|
16091
|
+
const insertMessage = getInsertMessageStatement(db);
|
|
16092
|
+
for (const message of messagesToInsert) {
|
|
16093
|
+
insertMessage.run(sessionId, message.ordinal, message.id, message.role, message.content);
|
|
16094
|
+
}
|
|
16095
|
+
getUpsertIndexStatement(db).run(sessionId, messages.length, now);
|
|
16096
|
+
})();
|
|
16202
16097
|
}
|
|
16203
|
-
|
|
16204
|
-
|
|
16205
|
-
|
|
16206
|
-
|
|
16098
|
+
// src/features/magic-context/storage-db.ts
|
|
16099
|
+
import { Database as Database2 } from "bun:sqlite";
|
|
16100
|
+
import { mkdirSync } from "fs";
|
|
16101
|
+
import { join as join6 } from "path";
|
|
16102
|
+
var databases = new Map;
|
|
16103
|
+
var FALLBACK_DATABASE_KEY = "__fallback__:memory:";
|
|
16104
|
+
var persistenceByDatabase = new WeakMap;
|
|
16105
|
+
var persistenceErrorByDatabase = new WeakMap;
|
|
16106
|
+
function resolveDatabasePath() {
|
|
16107
|
+
const dbDir = join6(getOpenCodeStorageDir(), "plugin", "magic-context");
|
|
16108
|
+
return { dbDir, dbPath: join6(dbDir, "context.db") };
|
|
16207
16109
|
}
|
|
16208
|
-
function
|
|
16209
|
-
|
|
16210
|
-
|
|
16211
|
-
|
|
16212
|
-
|
|
16213
|
-
|
|
16214
|
-
|
|
16215
|
-
|
|
16216
|
-
|
|
16217
|
-
|
|
16218
|
-
|
|
16219
|
-
|
|
16220
|
-
|
|
16110
|
+
function initializeDatabase(db) {
|
|
16111
|
+
db.run("PRAGMA journal_mode=WAL");
|
|
16112
|
+
db.run("PRAGMA busy_timeout=5000");
|
|
16113
|
+
db.run("PRAGMA foreign_keys=ON");
|
|
16114
|
+
db.run(`
|
|
16115
|
+
CREATE TABLE IF NOT EXISTS tags (
|
|
16116
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16117
|
+
session_id TEXT,
|
|
16118
|
+
message_id TEXT,
|
|
16119
|
+
type TEXT,
|
|
16120
|
+
status TEXT DEFAULT 'active',
|
|
16121
|
+
byte_size INTEGER,
|
|
16122
|
+
tag_number INTEGER,
|
|
16123
|
+
UNIQUE(session_id, tag_number)
|
|
16124
|
+
);
|
|
16125
|
+
|
|
16126
|
+
CREATE TABLE IF NOT EXISTS pending_ops (
|
|
16127
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16128
|
+
session_id TEXT,
|
|
16129
|
+
tag_id INTEGER,
|
|
16130
|
+
operation TEXT,
|
|
16131
|
+
queued_at INTEGER
|
|
16132
|
+
);
|
|
16133
|
+
|
|
16134
|
+
CREATE TABLE IF NOT EXISTS source_contents (
|
|
16135
|
+
tag_id INTEGER,
|
|
16136
|
+
session_id TEXT,
|
|
16137
|
+
content TEXT,
|
|
16138
|
+
created_at INTEGER,
|
|
16139
|
+
PRIMARY KEY(session_id, tag_id)
|
|
16140
|
+
);
|
|
16141
|
+
|
|
16142
|
+
CREATE TABLE IF NOT EXISTS compartments (
|
|
16143
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16144
|
+
session_id TEXT NOT NULL,
|
|
16145
|
+
sequence INTEGER NOT NULL,
|
|
16146
|
+
start_message INTEGER NOT NULL,
|
|
16147
|
+
end_message INTEGER NOT NULL,
|
|
16148
|
+
start_message_id TEXT DEFAULT '',
|
|
16149
|
+
end_message_id TEXT DEFAULT '',
|
|
16150
|
+
title TEXT NOT NULL,
|
|
16151
|
+
content TEXT NOT NULL,
|
|
16152
|
+
created_at INTEGER NOT NULL,
|
|
16153
|
+
UNIQUE(session_id, sequence)
|
|
16154
|
+
);
|
|
16155
|
+
|
|
16156
|
+
CREATE TABLE IF NOT EXISTS session_facts (
|
|
16157
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16158
|
+
session_id TEXT NOT NULL,
|
|
16159
|
+
category TEXT NOT NULL,
|
|
16160
|
+
content TEXT NOT NULL,
|
|
16161
|
+
created_at INTEGER NOT NULL,
|
|
16162
|
+
updated_at INTEGER NOT NULL
|
|
16163
|
+
);
|
|
16164
|
+
|
|
16165
|
+
CREATE TABLE IF NOT EXISTS session_notes (
|
|
16166
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16167
|
+
session_id TEXT NOT NULL,
|
|
16168
|
+
content TEXT NOT NULL,
|
|
16169
|
+
created_at INTEGER NOT NULL
|
|
16170
|
+
);
|
|
16171
|
+
|
|
16172
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
16173
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16174
|
+
project_path TEXT NOT NULL,
|
|
16175
|
+
category TEXT NOT NULL,
|
|
16176
|
+
content TEXT NOT NULL,
|
|
16177
|
+
normalized_hash TEXT NOT NULL,
|
|
16178
|
+
source_session_id TEXT,
|
|
16179
|
+
source_type TEXT DEFAULT 'historian',
|
|
16180
|
+
seen_count INTEGER DEFAULT 1,
|
|
16181
|
+
retrieval_count INTEGER DEFAULT 0,
|
|
16182
|
+
first_seen_at INTEGER NOT NULL,
|
|
16183
|
+
created_at INTEGER NOT NULL,
|
|
16184
|
+
updated_at INTEGER NOT NULL,
|
|
16185
|
+
last_seen_at INTEGER NOT NULL,
|
|
16186
|
+
last_retrieved_at INTEGER,
|
|
16187
|
+
status TEXT DEFAULT 'active',
|
|
16188
|
+
expires_at INTEGER,
|
|
16189
|
+
verification_status TEXT DEFAULT 'unverified',
|
|
16190
|
+
verified_at INTEGER,
|
|
16191
|
+
superseded_by_memory_id INTEGER,
|
|
16192
|
+
merged_from TEXT,
|
|
16193
|
+
metadata_json TEXT,
|
|
16194
|
+
UNIQUE(project_path, category, normalized_hash)
|
|
16195
|
+
);
|
|
16196
|
+
|
|
16197
|
+
CREATE TABLE IF NOT EXISTS memory_embeddings (
|
|
16198
|
+
memory_id INTEGER PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
|
|
16199
|
+
embedding BLOB NOT NULL,
|
|
16200
|
+
model_id TEXT
|
|
16201
|
+
);
|
|
16202
|
+
|
|
16203
|
+
CREATE TABLE IF NOT EXISTS dream_state (
|
|
16204
|
+
key TEXT PRIMARY KEY,
|
|
16205
|
+
value TEXT NOT NULL
|
|
16206
|
+
);
|
|
16207
|
+
|
|
16208
|
+
CREATE TABLE IF NOT EXISTS dream_queue (
|
|
16209
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16210
|
+
project_path TEXT NOT NULL,
|
|
16211
|
+
reason TEXT NOT NULL,
|
|
16212
|
+
enqueued_at INTEGER NOT NULL,
|
|
16213
|
+
started_at INTEGER,
|
|
16214
|
+
retry_count INTEGER DEFAULT 0
|
|
16215
|
+
);
|
|
16216
|
+
CREATE INDEX IF NOT EXISTS idx_dream_queue_project ON dream_queue(project_path);
|
|
16217
|
+
CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, enqueued_at);
|
|
16218
|
+
|
|
16219
|
+
CREATE TABLE IF NOT EXISTS smart_notes (
|
|
16220
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16221
|
+
project_path TEXT NOT NULL,
|
|
16222
|
+
content TEXT NOT NULL,
|
|
16223
|
+
surface_condition TEXT NOT NULL,
|
|
16224
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
16225
|
+
created_session_id TEXT,
|
|
16226
|
+
created_at INTEGER NOT NULL,
|
|
16227
|
+
updated_at INTEGER NOT NULL,
|
|
16228
|
+
last_checked_at INTEGER,
|
|
16229
|
+
ready_at INTEGER,
|
|
16230
|
+
ready_reason TEXT
|
|
16231
|
+
);
|
|
16232
|
+
CREATE INDEX IF NOT EXISTS idx_smart_notes_project_status ON smart_notes(project_path, status);
|
|
16233
|
+
|
|
16234
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
|
|
16235
|
+
content,
|
|
16236
|
+
category,
|
|
16237
|
+
content='memories',
|
|
16238
|
+
content_rowid='id',
|
|
16239
|
+
tokenize='porter unicode61'
|
|
16240
|
+
);
|
|
16241
|
+
|
|
16242
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS message_history_fts USING fts5(
|
|
16243
|
+
session_id UNINDEXED,
|
|
16244
|
+
message_ordinal UNINDEXED,
|
|
16245
|
+
message_id UNINDEXED,
|
|
16246
|
+
role,
|
|
16247
|
+
content,
|
|
16248
|
+
tokenize='porter unicode61'
|
|
16249
|
+
);
|
|
16250
|
+
|
|
16251
|
+
CREATE TABLE IF NOT EXISTS message_history_index (
|
|
16252
|
+
session_id TEXT PRIMARY KEY,
|
|
16253
|
+
last_indexed_ordinal INTEGER NOT NULL DEFAULT 0,
|
|
16254
|
+
updated_at INTEGER NOT NULL
|
|
16255
|
+
);
|
|
16256
|
+
|
|
16257
|
+
CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
|
|
16258
|
+
INSERT INTO memories_fts(rowid, content, category) VALUES (new.id, new.content, new.category);
|
|
16259
|
+
END;
|
|
16260
|
+
|
|
16261
|
+
CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
|
|
16262
|
+
INSERT INTO memories_fts(memories_fts, rowid, content, category) VALUES ('delete', old.id, old.content, old.category);
|
|
16263
|
+
END;
|
|
16264
|
+
|
|
16265
|
+
CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
|
|
16266
|
+
INSERT INTO memories_fts(memories_fts, rowid, content, category) VALUES ('delete', old.id, old.content, old.category);
|
|
16267
|
+
INSERT INTO memories_fts(rowid, content, category) VALUES (new.id, new.content, new.category);
|
|
16268
|
+
END;
|
|
16269
|
+
|
|
16270
|
+
CREATE TABLE IF NOT EXISTS session_meta (
|
|
16271
|
+
session_id TEXT PRIMARY KEY,
|
|
16272
|
+
last_response_time INTEGER,
|
|
16273
|
+
cache_ttl TEXT,
|
|
16274
|
+
counter INTEGER DEFAULT 0,
|
|
16275
|
+
last_nudge_tokens INTEGER DEFAULT 0,
|
|
16276
|
+
last_nudge_band TEXT DEFAULT '',
|
|
16277
|
+
last_transform_error TEXT DEFAULT '',
|
|
16278
|
+
nudge_anchor_message_id TEXT DEFAULT '',
|
|
16279
|
+
nudge_anchor_text TEXT DEFAULT '',
|
|
16280
|
+
sticky_turn_reminder_text TEXT DEFAULT '',
|
|
16281
|
+
sticky_turn_reminder_message_id TEXT DEFAULT '',
|
|
16282
|
+
note_nudge_trigger_pending INTEGER DEFAULT 0,
|
|
16283
|
+
note_nudge_trigger_message_id TEXT DEFAULT '',
|
|
16284
|
+
note_nudge_sticky_text TEXT DEFAULT '',
|
|
16285
|
+
note_nudge_sticky_message_id TEXT DEFAULT '',
|
|
16286
|
+
is_subagent INTEGER DEFAULT 0,
|
|
16287
|
+
last_context_percentage REAL DEFAULT 0,
|
|
16288
|
+
last_input_tokens INTEGER DEFAULT 0,
|
|
16289
|
+
times_execute_threshold_reached INTEGER DEFAULT 0,
|
|
16290
|
+
compartment_in_progress INTEGER DEFAULT 0,
|
|
16291
|
+
system_prompt_hash TEXT DEFAULT '',
|
|
16292
|
+
memory_block_cache TEXT DEFAULT '',
|
|
16293
|
+
memory_block_count INTEGER DEFAULT 0
|
|
16294
|
+
);
|
|
16295
|
+
|
|
16296
|
+
CREATE INDEX IF NOT EXISTS idx_tags_session_tag_number ON tags(session_id, tag_number);
|
|
16297
|
+
CREATE INDEX IF NOT EXISTS idx_pending_ops_session ON pending_ops(session_id);
|
|
16298
|
+
CREATE INDEX IF NOT EXISTS idx_pending_ops_session_tag_id ON pending_ops(session_id, tag_id);
|
|
16299
|
+
CREATE INDEX IF NOT EXISTS idx_source_contents_session ON source_contents(session_id);
|
|
16300
|
+
|
|
16301
|
+
CREATE TABLE IF NOT EXISTS recomp_compartments (
|
|
16302
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16303
|
+
session_id TEXT NOT NULL,
|
|
16304
|
+
sequence INTEGER NOT NULL,
|
|
16305
|
+
start_message INTEGER NOT NULL,
|
|
16306
|
+
end_message INTEGER NOT NULL,
|
|
16307
|
+
start_message_id TEXT DEFAULT '',
|
|
16308
|
+
end_message_id TEXT DEFAULT '',
|
|
16309
|
+
title TEXT NOT NULL,
|
|
16310
|
+
content TEXT NOT NULL,
|
|
16311
|
+
pass_number INTEGER NOT NULL,
|
|
16312
|
+
created_at INTEGER NOT NULL,
|
|
16313
|
+
UNIQUE(session_id, sequence)
|
|
16314
|
+
);
|
|
16315
|
+
|
|
16316
|
+
CREATE TABLE IF NOT EXISTS recomp_facts (
|
|
16317
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
16318
|
+
session_id TEXT NOT NULL,
|
|
16319
|
+
category TEXT NOT NULL,
|
|
16320
|
+
content TEXT NOT NULL,
|
|
16321
|
+
pass_number INTEGER NOT NULL,
|
|
16322
|
+
created_at INTEGER NOT NULL
|
|
16323
|
+
);
|
|
16324
|
+
|
|
16325
|
+
CREATE INDEX IF NOT EXISTS idx_compartments_session ON compartments(session_id);
|
|
16326
|
+
CREATE INDEX IF NOT EXISTS idx_session_facts_session ON session_facts(session_id);
|
|
16327
|
+
CREATE INDEX IF NOT EXISTS idx_recomp_compartments_session ON recomp_compartments(session_id);
|
|
16328
|
+
CREATE INDEX IF NOT EXISTS idx_recomp_facts_session ON recomp_facts(session_id);
|
|
16329
|
+
CREATE INDEX IF NOT EXISTS idx_session_notes_session ON session_notes(session_id);
|
|
16330
|
+
CREATE INDEX IF NOT EXISTS idx_memories_project_status_category ON memories(project_path, status, category);
|
|
16331
|
+
CREATE INDEX IF NOT EXISTS idx_memories_project_status_expires ON memories(project_path, status, expires_at);
|
|
16332
|
+
CREATE INDEX IF NOT EXISTS idx_memories_project_category_hash ON memories(project_path, category, normalized_hash);
|
|
16333
|
+
CREATE INDEX IF NOT EXISTS idx_message_history_index_updated_at ON message_history_index(updated_at);
|
|
16334
|
+
`);
|
|
16335
|
+
ensureColumn(db, "session_meta", "last_nudge_band", "TEXT DEFAULT ''");
|
|
16336
|
+
ensureColumn(db, "session_meta", "last_transform_error", "TEXT DEFAULT ''");
|
|
16337
|
+
ensureColumn(db, "session_meta", "nudge_anchor_message_id", "TEXT DEFAULT ''");
|
|
16338
|
+
ensureColumn(db, "session_meta", "nudge_anchor_text", "TEXT DEFAULT ''");
|
|
16339
|
+
ensureColumn(db, "session_meta", "sticky_turn_reminder_text", "TEXT DEFAULT ''");
|
|
16340
|
+
ensureColumn(db, "session_meta", "sticky_turn_reminder_message_id", "TEXT DEFAULT ''");
|
|
16341
|
+
ensureColumn(db, "session_meta", "note_nudge_trigger_pending", "INTEGER DEFAULT 0");
|
|
16342
|
+
ensureColumn(db, "session_meta", "note_nudge_trigger_message_id", "TEXT DEFAULT ''");
|
|
16343
|
+
ensureColumn(db, "session_meta", "note_nudge_sticky_text", "TEXT DEFAULT ''");
|
|
16344
|
+
ensureColumn(db, "session_meta", "note_nudge_sticky_message_id", "TEXT DEFAULT ''");
|
|
16345
|
+
ensureColumn(db, "session_meta", "times_execute_threshold_reached", "INTEGER DEFAULT 0");
|
|
16346
|
+
ensureColumn(db, "session_meta", "compartment_in_progress", "INTEGER DEFAULT 0");
|
|
16347
|
+
ensureColumn(db, "session_meta", "system_prompt_hash", "TEXT DEFAULT ''");
|
|
16348
|
+
ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
|
|
16349
|
+
ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
|
|
16350
|
+
ensureColumn(db, "compartments", "start_message_id", "TEXT DEFAULT ''");
|
|
16351
|
+
ensureColumn(db, "compartments", "end_message_id", "TEXT DEFAULT ''");
|
|
16352
|
+
ensureColumn(db, "memory_embeddings", "model_id", "TEXT");
|
|
16353
|
+
ensureColumn(db, "session_meta", "memory_block_cache", "TEXT DEFAULT ''");
|
|
16354
|
+
ensureColumn(db, "session_meta", "memory_block_count", "INTEGER DEFAULT 0");
|
|
16355
|
+
ensureColumn(db, "dream_queue", "retry_count", "INTEGER DEFAULT 0");
|
|
16356
|
+
ensureColumn(db, "tags", "reasoning_byte_size", "INTEGER DEFAULT 0");
|
|
16221
16357
|
}
|
|
16222
|
-
function
|
|
16223
|
-
|
|
16224
|
-
|
|
16225
|
-
return { text, commitHashes };
|
|
16358
|
+
function ensureColumn(db, table, column, definition) {
|
|
16359
|
+
if (!/^[a-z_]+$/.test(table) || !/^[a-z_]+$/.test(column) || !/^[A-Z0-9_'(),\s]+$/i.test(definition)) {
|
|
16360
|
+
throw new Error(`Unsafe schema identifier: ${table}.${column} ${definition}`);
|
|
16226
16361
|
}
|
|
16227
|
-
const
|
|
16228
|
-
|
|
16229
|
-
|
|
16230
|
-
commitHashes
|
|
16231
|
-
};
|
|
16232
|
-
}
|
|
16233
|
-
function mergeCommitHashes(existing, next) {
|
|
16234
|
-
if (next.length === 0)
|
|
16235
|
-
return existing;
|
|
16236
|
-
const merged = [...existing];
|
|
16237
|
-
for (const hash2 of next) {
|
|
16238
|
-
if (merged.includes(hash2))
|
|
16239
|
-
continue;
|
|
16240
|
-
merged.push(hash2);
|
|
16241
|
-
if (merged.length >= MAX_COMMITS_PER_BLOCK)
|
|
16242
|
-
break;
|
|
16362
|
+
const rows = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
16363
|
+
if (rows.some((row) => row.name === column)) {
|
|
16364
|
+
return;
|
|
16243
16365
|
}
|
|
16244
|
-
|
|
16245
|
-
}
|
|
16246
|
-
|
|
16247
|
-
// src/hooks/magic-context/read-session-raw.ts
|
|
16248
|
-
function isRawMessageRow(row) {
|
|
16249
|
-
if (row === null || typeof row !== "object")
|
|
16250
|
-
return false;
|
|
16251
|
-
const candidate = row;
|
|
16252
|
-
return typeof candidate.id === "string" && typeof candidate.data === "string";
|
|
16253
|
-
}
|
|
16254
|
-
function isRawPartRow(row) {
|
|
16255
|
-
if (row === null || typeof row !== "object")
|
|
16256
|
-
return false;
|
|
16257
|
-
const candidate = row;
|
|
16258
|
-
return typeof candidate.message_id === "string" && typeof candidate.data === "string";
|
|
16366
|
+
db.run(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
|
|
16259
16367
|
}
|
|
16260
|
-
function
|
|
16261
|
-
|
|
16262
|
-
|
|
16263
|
-
|
|
16368
|
+
function createFallbackDatabase() {
|
|
16369
|
+
try {
|
|
16370
|
+
const fallback = new Database2(":memory:");
|
|
16371
|
+
initializeDatabase(fallback);
|
|
16372
|
+
return fallback;
|
|
16373
|
+
} catch (error48) {
|
|
16374
|
+
throw new Error(`[magic-context] storage fatal: failed to initialize fallback database: ${getErrorMessage(error48)}`);
|
|
16264
16375
|
}
|
|
16265
|
-
return parsed;
|
|
16266
|
-
}
|
|
16267
|
-
function parseJsonUnknown(value) {
|
|
16268
|
-
return JSON.parse(value);
|
|
16269
16376
|
}
|
|
16270
|
-
function
|
|
16271
|
-
|
|
16272
|
-
|
|
16273
|
-
|
|
16274
|
-
|
|
16275
|
-
|
|
16276
|
-
|
|
16277
|
-
|
|
16377
|
+
function openDatabase() {
|
|
16378
|
+
try {
|
|
16379
|
+
const { dbDir, dbPath } = resolveDatabasePath();
|
|
16380
|
+
const existing = databases.get(dbPath);
|
|
16381
|
+
if (existing) {
|
|
16382
|
+
if (!persistenceByDatabase.has(existing)) {
|
|
16383
|
+
persistenceByDatabase.set(existing, true);
|
|
16384
|
+
}
|
|
16385
|
+
return existing;
|
|
16386
|
+
}
|
|
16387
|
+
mkdirSync(dbDir, { recursive: true });
|
|
16388
|
+
const db = new Database2(dbPath);
|
|
16389
|
+
initializeDatabase(db);
|
|
16390
|
+
databases.set(dbPath, db);
|
|
16391
|
+
persistenceByDatabase.set(db, true);
|
|
16392
|
+
persistenceErrorByDatabase.delete(db);
|
|
16393
|
+
return db;
|
|
16394
|
+
} catch (error48) {
|
|
16395
|
+
log("[magic-context] storage error:", error48);
|
|
16396
|
+
const errorMessage = getErrorMessage(error48);
|
|
16397
|
+
const existingFallback = databases.get(FALLBACK_DATABASE_KEY);
|
|
16398
|
+
if (existingFallback) {
|
|
16399
|
+
if (!persistenceByDatabase.has(existingFallback)) {
|
|
16400
|
+
persistenceByDatabase.set(existingFallback, false);
|
|
16401
|
+
persistenceErrorByDatabase.set(existingFallback, errorMessage);
|
|
16402
|
+
}
|
|
16403
|
+
return existingFallback;
|
|
16404
|
+
}
|
|
16405
|
+
const fallback = createFallbackDatabase();
|
|
16406
|
+
databases.set(FALLBACK_DATABASE_KEY, fallback);
|
|
16407
|
+
persistenceByDatabase.set(fallback, false);
|
|
16408
|
+
persistenceErrorByDatabase.set(fallback, errorMessage);
|
|
16409
|
+
return fallback;
|
|
16278
16410
|
}
|
|
16279
|
-
return messageRows.map((row, index) => {
|
|
16280
|
-
const info = parseJsonRecord(row.data);
|
|
16281
|
-
const role = typeof info.role === "string" ? info.role : "unknown";
|
|
16282
|
-
return {
|
|
16283
|
-
ordinal: index + 1,
|
|
16284
|
-
id: row.id,
|
|
16285
|
-
role,
|
|
16286
|
-
parts: partsByMessageId.get(row.id) ?? []
|
|
16287
|
-
};
|
|
16288
|
-
});
|
|
16289
|
-
}
|
|
16290
|
-
|
|
16291
|
-
// src/hooks/magic-context/tag-content-primitives.ts
|
|
16292
|
-
var encoder = new TextEncoder;
|
|
16293
|
-
var TAG_PREFIX_REGEX = /^\u00A7\d+\u00A7\s*/;
|
|
16294
|
-
function byteSize(value) {
|
|
16295
|
-
return encoder.encode(value).length;
|
|
16296
|
-
}
|
|
16297
|
-
function stripTagPrefix(value) {
|
|
16298
|
-
return value.replace(TAG_PREFIX_REGEX, "");
|
|
16299
16411
|
}
|
|
16300
|
-
function
|
|
16301
|
-
|
|
16302
|
-
return `\xA7${tagId}\xA7 ${stripped}`;
|
|
16412
|
+
function isDatabasePersisted(db) {
|
|
16413
|
+
return persistenceByDatabase.get(db) ?? false;
|
|
16303
16414
|
}
|
|
16304
|
-
function
|
|
16305
|
-
|
|
16306
|
-
return false;
|
|
16307
|
-
const candidate = part;
|
|
16308
|
-
return candidate.type === "thinking" || candidate.type === "reasoning";
|
|
16415
|
+
function getDatabasePersistenceError(db) {
|
|
16416
|
+
return persistenceErrorByDatabase.get(db) ?? null;
|
|
16309
16417
|
}
|
|
16310
|
-
|
|
16311
|
-
|
|
16312
|
-
|
|
16313
|
-
|
|
16418
|
+
// src/features/magic-context/storage-meta-shared.ts
|
|
16419
|
+
var META_COLUMNS = {
|
|
16420
|
+
lastResponseTime: "last_response_time",
|
|
16421
|
+
cacheTtl: "cache_ttl",
|
|
16422
|
+
counter: "counter",
|
|
16423
|
+
lastNudgeTokens: "last_nudge_tokens",
|
|
16424
|
+
lastNudgeBand: "last_nudge_band",
|
|
16425
|
+
lastTransformError: "last_transform_error",
|
|
16426
|
+
isSubagent: "is_subagent",
|
|
16427
|
+
lastContextPercentage: "last_context_percentage",
|
|
16428
|
+
lastInputTokens: "last_input_tokens",
|
|
16429
|
+
timesExecuteThresholdReached: "times_execute_threshold_reached",
|
|
16430
|
+
compartmentInProgress: "compartment_in_progress",
|
|
16431
|
+
systemPromptHash: "system_prompt_hash",
|
|
16432
|
+
clearedReasoningThroughTag: "cleared_reasoning_through_tag"
|
|
16433
|
+
};
|
|
16434
|
+
var BOOLEAN_META_KEYS = new Set(["isSubagent", "compartmentInProgress"]);
|
|
16435
|
+
function isSessionMetaRow(row) {
|
|
16436
|
+
if (row === null || typeof row !== "object")
|
|
16314
16437
|
return false;
|
|
16315
|
-
const
|
|
16316
|
-
return
|
|
16438
|
+
const r = row;
|
|
16439
|
+
return typeof r.session_id === "string" && typeof r.last_response_time === "number" && typeof r.cache_ttl === "string" && typeof r.counter === "number" && typeof r.last_nudge_tokens === "number" && typeof r.last_nudge_band === "string" && typeof r.last_transform_error === "string" && typeof r.is_subagent === "number" && typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && typeof r.times_execute_threshold_reached === "number" && typeof r.compartment_in_progress === "number" && (typeof r.system_prompt_hash === "string" || typeof r.system_prompt_hash === "number") && typeof r.cleared_reasoning_through_tag === "number";
|
|
16317
16440
|
}
|
|
16318
|
-
function
|
|
16319
|
-
|
|
16320
|
-
|
|
16321
|
-
|
|
16322
|
-
|
|
16323
|
-
|
|
16324
|
-
|
|
16325
|
-
|
|
16326
|
-
|
|
16441
|
+
function getDefaultSessionMeta(sessionId) {
|
|
16442
|
+
return {
|
|
16443
|
+
sessionId,
|
|
16444
|
+
lastResponseTime: 0,
|
|
16445
|
+
cacheTtl: "5m",
|
|
16446
|
+
counter: 0,
|
|
16447
|
+
lastNudgeTokens: 0,
|
|
16448
|
+
lastNudgeBand: null,
|
|
16449
|
+
lastTransformError: null,
|
|
16450
|
+
isSubagent: false,
|
|
16451
|
+
lastContextPercentage: 0,
|
|
16452
|
+
lastInputTokens: 0,
|
|
16453
|
+
timesExecuteThresholdReached: 0,
|
|
16454
|
+
compartmentInProgress: false,
|
|
16455
|
+
systemPromptHash: "",
|
|
16456
|
+
clearedReasoningThroughTag: 0
|
|
16457
|
+
};
|
|
16327
16458
|
}
|
|
16328
|
-
function
|
|
16329
|
-
|
|
16330
|
-
|
|
16331
|
-
const p = part;
|
|
16332
|
-
return p.type === "file" && typeof p.url === "string";
|
|
16459
|
+
function ensureSessionMetaRow(db, sessionId) {
|
|
16460
|
+
const defaults = getDefaultSessionMeta(sessionId);
|
|
16461
|
+
db.prepare("INSERT OR IGNORE INTO session_meta (session_id, last_response_time, cache_ttl, counter, last_nudge_tokens, last_nudge_band, last_transform_error, is_subagent, last_context_percentage, last_input_tokens, times_execute_threshold_reached, compartment_in_progress, system_prompt_hash, cleared_reasoning_through_tag) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(sessionId, defaults.lastResponseTime, defaults.cacheTtl, defaults.counter, defaults.lastNudgeTokens, defaults.lastNudgeBand ?? "", defaults.lastTransformError ?? "", defaults.isSubagent ? 1 : 0, defaults.lastContextPercentage, defaults.lastInputTokens, defaults.timesExecuteThresholdReached, defaults.compartmentInProgress ? 1 : 0, defaults.systemPromptHash ?? "", defaults.clearedReasoningThroughTag);
|
|
16333
16462
|
}
|
|
16334
|
-
function
|
|
16335
|
-
|
|
16336
|
-
|
|
16337
|
-
|
|
16463
|
+
function toSessionMeta(row) {
|
|
16464
|
+
return {
|
|
16465
|
+
sessionId: row.session_id,
|
|
16466
|
+
lastResponseTime: row.last_response_time,
|
|
16467
|
+
cacheTtl: row.cache_ttl,
|
|
16468
|
+
counter: row.counter,
|
|
16469
|
+
lastNudgeTokens: row.last_nudge_tokens,
|
|
16470
|
+
lastNudgeBand: row.last_nudge_band.length > 0 ? row.last_nudge_band : null,
|
|
16471
|
+
lastTransformError: row.last_transform_error.length > 0 ? row.last_transform_error : null,
|
|
16472
|
+
isSubagent: row.is_subagent === 1,
|
|
16473
|
+
lastContextPercentage: row.last_context_percentage,
|
|
16474
|
+
lastInputTokens: row.last_input_tokens,
|
|
16475
|
+
timesExecuteThresholdReached: row.times_execute_threshold_reached,
|
|
16476
|
+
compartmentInProgress: row.compartment_in_progress === 1,
|
|
16477
|
+
systemPromptHash: String(row.system_prompt_hash),
|
|
16478
|
+
clearedReasoningThroughTag: row.cleared_reasoning_through_tag
|
|
16479
|
+
};
|
|
16338
16480
|
}
|
|
16339
16481
|
|
|
16340
|
-
// src/
|
|
16341
|
-
|
|
16342
|
-
|
|
16343
|
-
|
|
16482
|
+
// src/features/magic-context/storage-meta-persisted.ts
|
|
16483
|
+
function isPersistedUsageRow(row) {
|
|
16484
|
+
if (row === null || typeof row !== "object")
|
|
16485
|
+
return false;
|
|
16486
|
+
const r = row;
|
|
16487
|
+
return typeof r.last_context_percentage === "number" && typeof r.last_input_tokens === "number" && typeof r.last_response_time === "number";
|
|
16344
16488
|
}
|
|
16345
|
-
function
|
|
16346
|
-
|
|
16347
|
-
|
|
16348
|
-
|
|
16349
|
-
|
|
16350
|
-
try {
|
|
16351
|
-
return fn();
|
|
16352
|
-
} finally {
|
|
16353
|
-
if (!outerCache) {
|
|
16354
|
-
activeRawMessageCache = null;
|
|
16355
|
-
}
|
|
16356
|
-
}
|
|
16489
|
+
function isPersistedReasoningWatermarkRow(row) {
|
|
16490
|
+
if (row === null || typeof row !== "object")
|
|
16491
|
+
return false;
|
|
16492
|
+
const r = row;
|
|
16493
|
+
return typeof r.cleared_reasoning_through_tag === "number";
|
|
16357
16494
|
}
|
|
16358
|
-
function
|
|
16359
|
-
if (
|
|
16360
|
-
|
|
16361
|
-
|
|
16362
|
-
|
|
16363
|
-
}
|
|
16364
|
-
const messages = withReadOnlySessionDb((db) => readRawSessionMessagesFromDb(db, sessionId));
|
|
16365
|
-
activeRawMessageCache.set(sessionId, messages);
|
|
16366
|
-
return messages;
|
|
16367
|
-
}
|
|
16368
|
-
return withReadOnlySessionDb((db) => readRawSessionMessagesFromDb(db, sessionId));
|
|
16495
|
+
function isPersistedNudgePlacementRow(row) {
|
|
16496
|
+
if (row === null || typeof row !== "object")
|
|
16497
|
+
return false;
|
|
16498
|
+
const r = row;
|
|
16499
|
+
return typeof r.nudge_anchor_message_id === "string" && typeof r.nudge_anchor_text === "string";
|
|
16369
16500
|
}
|
|
16370
|
-
function
|
|
16371
|
-
|
|
16501
|
+
function isPersistedStickyTurnReminderRow(row) {
|
|
16502
|
+
if (row === null || typeof row !== "object")
|
|
16503
|
+
return false;
|
|
16504
|
+
const r = row;
|
|
16505
|
+
return typeof r.sticky_turn_reminder_text === "string" && typeof r.sticky_turn_reminder_message_id === "string";
|
|
16372
16506
|
}
|
|
16373
|
-
function
|
|
16374
|
-
|
|
16375
|
-
|
|
16376
|
-
|
|
16377
|
-
|
|
16378
|
-
break;
|
|
16379
|
-
for (const [partIndex, part] of message.parts.entries()) {
|
|
16380
|
-
if (isTextPart(part)) {
|
|
16381
|
-
keys.push(`${message.id}:p${partIndex}`);
|
|
16382
|
-
}
|
|
16383
|
-
if (isFilePart(part)) {
|
|
16384
|
-
keys.push(`${message.id}:file${partIndex}`);
|
|
16385
|
-
}
|
|
16386
|
-
if (isToolPartWithOutput(part)) {
|
|
16387
|
-
keys.push(part.callID);
|
|
16388
|
-
}
|
|
16389
|
-
}
|
|
16390
|
-
}
|
|
16391
|
-
return keys;
|
|
16507
|
+
function isPersistedNoteNudgeRow(row) {
|
|
16508
|
+
if (row === null || typeof row !== "object")
|
|
16509
|
+
return false;
|
|
16510
|
+
const r = row;
|
|
16511
|
+
return typeof r.note_nudge_trigger_pending === "number" && typeof r.note_nudge_trigger_message_id === "string" && typeof r.note_nudge_sticky_text === "string" && typeof r.note_nudge_sticky_message_id === "string";
|
|
16392
16512
|
}
|
|
16393
|
-
|
|
16394
|
-
|
|
16395
|
-
|
|
16396
|
-
|
|
16397
|
-
|
|
16398
|
-
|
|
16513
|
+
function getDefaultPersistedNoteNudge() {
|
|
16514
|
+
return {
|
|
16515
|
+
triggerPending: false,
|
|
16516
|
+
triggerMessageId: null,
|
|
16517
|
+
stickyText: null,
|
|
16518
|
+
stickyMessageId: null
|
|
16519
|
+
};
|
|
16520
|
+
}
|
|
16521
|
+
function loadPersistedUsage(db, sessionId) {
|
|
16522
|
+
const result = db.prepare("SELECT last_context_percentage, last_input_tokens, last_response_time FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
16523
|
+
if (!isPersistedUsageRow(result) || result.last_context_percentage === 0 && result.last_input_tokens === 0) {
|
|
16524
|
+
return null;
|
|
16399
16525
|
}
|
|
16400
|
-
return
|
|
16526
|
+
return {
|
|
16527
|
+
usage: {
|
|
16528
|
+
percentage: result.last_context_percentage,
|
|
16529
|
+
inputTokens: result.last_input_tokens
|
|
16530
|
+
},
|
|
16531
|
+
updatedAt: result.last_response_time || Date.now()
|
|
16532
|
+
};
|
|
16401
16533
|
}
|
|
16402
|
-
function
|
|
16403
|
-
const
|
|
16404
|
-
|
|
16405
|
-
|
|
16406
|
-
|
|
16407
|
-
|
|
16408
|
-
|
|
16409
|
-
|
|
16410
|
-
|
|
16411
|
-
|
|
16412
|
-
|
|
16413
|
-
|
|
16414
|
-
let commitClusters = 0;
|
|
16415
|
-
let lastFlushedRole = "";
|
|
16416
|
-
function flushCurrentBlock() {
|
|
16417
|
-
if (!currentBlock)
|
|
16418
|
-
return true;
|
|
16419
|
-
const blockText = formatBlock(currentBlock);
|
|
16420
|
-
const blockTokens = estimateTokens(blockText);
|
|
16421
|
-
if (totalTokens + blockTokens > tokenBudget && totalTokens > 0) {
|
|
16422
|
-
return false;
|
|
16423
|
-
}
|
|
16424
|
-
if (currentBlock.role === "A" && currentBlock.commitHashes.length > 0 && lastFlushedRole !== "A") {
|
|
16425
|
-
commitClusters++;
|
|
16426
|
-
}
|
|
16427
|
-
lastFlushedRole = currentBlock.role;
|
|
16428
|
-
if (!firstMessageId)
|
|
16429
|
-
firstMessageId = currentBlock.meta[0]?.messageId ?? "";
|
|
16430
|
-
lastOrdinal = currentBlock.meta[currentBlock.meta.length - 1]?.ordinal ?? currentBlock.endOrdinal;
|
|
16431
|
-
lastMessageId = currentBlock.meta[currentBlock.meta.length - 1]?.messageId ?? "";
|
|
16432
|
-
messagesProcessed += currentBlock.meta.length;
|
|
16433
|
-
lines.push(blockText);
|
|
16434
|
-
lineMeta.push(...currentBlock.meta);
|
|
16435
|
-
totalTokens += blockTokens;
|
|
16436
|
-
currentBlock = null;
|
|
16437
|
-
return true;
|
|
16534
|
+
function getPersistedReasoningWatermark(db, sessionId) {
|
|
16535
|
+
const result = db.prepare("SELECT cleared_reasoning_through_tag FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
16536
|
+
return isPersistedReasoningWatermarkRow(result) ? result.cleared_reasoning_through_tag : 0;
|
|
16537
|
+
}
|
|
16538
|
+
function setPersistedReasoningWatermark(db, sessionId, tagNumber) {
|
|
16539
|
+
ensureSessionMetaRow(db, sessionId);
|
|
16540
|
+
db.prepare("UPDATE session_meta SET cleared_reasoning_through_tag = ? WHERE session_id = ?").run(tagNumber, sessionId);
|
|
16541
|
+
}
|
|
16542
|
+
function getPersistedNudgePlacement(db, sessionId) {
|
|
16543
|
+
const result = db.prepare("SELECT nudge_anchor_message_id, nudge_anchor_text FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
16544
|
+
if (!isPersistedNudgePlacementRow(result)) {
|
|
16545
|
+
return null;
|
|
16438
16546
|
}
|
|
16439
|
-
|
|
16440
|
-
|
|
16441
|
-
break;
|
|
16442
|
-
if (msg.ordinal < startOrdinal)
|
|
16443
|
-
continue;
|
|
16444
|
-
const meta3 = { ordinal: msg.ordinal, messageId: msg.id };
|
|
16445
|
-
if (msg.role === "user" && !hasMeaningfulUserText(msg.parts)) {
|
|
16446
|
-
pendingNoiseMeta.push(meta3);
|
|
16447
|
-
continue;
|
|
16448
|
-
}
|
|
16449
|
-
const role = compactRole(msg.role);
|
|
16450
|
-
const compacted = compactTextForSummary(extractTexts(msg.parts).map((t) => msg.role === "user" ? cleanUserText(t) : t).map(normalizeText).filter((value) => value.length > 0).join(" / "), msg.role);
|
|
16451
|
-
const text = compacted.text;
|
|
16452
|
-
if (!text) {
|
|
16453
|
-
pendingNoiseMeta.push(meta3);
|
|
16454
|
-
continue;
|
|
16455
|
-
}
|
|
16456
|
-
if (currentBlock && currentBlock.role === role) {
|
|
16457
|
-
currentBlock.endOrdinal = msg.ordinal;
|
|
16458
|
-
currentBlock.parts.push(text);
|
|
16459
|
-
currentBlock.meta.push(...pendingNoiseMeta, meta3);
|
|
16460
|
-
currentBlock.commitHashes = mergeCommitHashes(currentBlock.commitHashes, compacted.commitHashes);
|
|
16461
|
-
pendingNoiseMeta = [];
|
|
16462
|
-
continue;
|
|
16463
|
-
}
|
|
16464
|
-
if (!flushCurrentBlock())
|
|
16465
|
-
break;
|
|
16466
|
-
currentBlock = {
|
|
16467
|
-
role,
|
|
16468
|
-
startOrdinal: pendingNoiseMeta[0]?.ordinal ?? msg.ordinal,
|
|
16469
|
-
endOrdinal: msg.ordinal,
|
|
16470
|
-
parts: [text],
|
|
16471
|
-
meta: [...pendingNoiseMeta, meta3],
|
|
16472
|
-
commitHashes: [...compacted.commitHashes]
|
|
16473
|
-
};
|
|
16474
|
-
pendingNoiseMeta = [];
|
|
16547
|
+
if (result.nudge_anchor_message_id.length === 0 || result.nudge_anchor_text.length === 0) {
|
|
16548
|
+
return null;
|
|
16475
16549
|
}
|
|
16476
|
-
flushCurrentBlock();
|
|
16477
16550
|
return {
|
|
16478
|
-
|
|
16479
|
-
|
|
16480
|
-
startMessageId: firstMessageId,
|
|
16481
|
-
endMessageId: lastMessageId,
|
|
16482
|
-
messageCount: messagesProcessed,
|
|
16483
|
-
tokenEstimate: totalTokens,
|
|
16484
|
-
hasMore: lastOrdinal < (eligibleEndOrdinal !== undefined ? Math.min(eligibleEndOrdinal - 1, messages.length) : messages.length),
|
|
16485
|
-
text: lines.join(`
|
|
16486
|
-
`),
|
|
16487
|
-
lines: lineMeta,
|
|
16488
|
-
commitClusterCount: commitClusters
|
|
16551
|
+
messageId: result.nudge_anchor_message_id,
|
|
16552
|
+
nudgeText: result.nudge_anchor_text
|
|
16489
16553
|
};
|
|
16490
16554
|
}
|
|
16491
|
-
|
|
16492
|
-
|
|
16493
|
-
|
|
16494
|
-
|
|
16495
|
-
|
|
16496
|
-
var deleteFtsStatements = new WeakMap;
|
|
16497
|
-
var deleteIndexStatements = new WeakMap;
|
|
16498
|
-
function normalizeIndexText(text) {
|
|
16499
|
-
return text.replace(/\s+/g, " ").trim();
|
|
16555
|
+
function setPersistedNudgePlacement(db, sessionId, messageId, nudgeText) {
|
|
16556
|
+
db.transaction(() => {
|
|
16557
|
+
ensureSessionMetaRow(db, sessionId);
|
|
16558
|
+
db.prepare("UPDATE session_meta SET nudge_anchor_message_id = ?, nudge_anchor_text = ? WHERE session_id = ?").run(messageId, nudgeText, sessionId);
|
|
16559
|
+
})();
|
|
16500
16560
|
}
|
|
16501
|
-
function
|
|
16502
|
-
|
|
16503
|
-
if (!stmt) {
|
|
16504
|
-
stmt = db.prepare("SELECT last_indexed_ordinal FROM message_history_index WHERE session_id = ?");
|
|
16505
|
-
lastIndexedStatements.set(db, stmt);
|
|
16506
|
-
}
|
|
16507
|
-
return stmt;
|
|
16561
|
+
function clearPersistedNudgePlacement(db, sessionId) {
|
|
16562
|
+
db.prepare("UPDATE session_meta SET nudge_anchor_message_id = '', nudge_anchor_text = '' WHERE session_id = ?").run(sessionId);
|
|
16508
16563
|
}
|
|
16509
|
-
function
|
|
16510
|
-
|
|
16511
|
-
if (!
|
|
16512
|
-
|
|
16513
|
-
insertMessageStatements.set(db, stmt);
|
|
16564
|
+
function getPersistedStickyTurnReminder(db, sessionId) {
|
|
16565
|
+
const result = db.prepare("SELECT sticky_turn_reminder_text, sticky_turn_reminder_message_id FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
16566
|
+
if (!isPersistedStickyTurnReminderRow(result)) {
|
|
16567
|
+
return null;
|
|
16514
16568
|
}
|
|
16515
|
-
|
|
16516
|
-
|
|
16517
|
-
function getUpsertIndexStatement(db) {
|
|
16518
|
-
let stmt = upsertIndexStatements.get(db);
|
|
16519
|
-
if (!stmt) {
|
|
16520
|
-
stmt = db.prepare("INSERT INTO message_history_index (session_id, last_indexed_ordinal, updated_at) VALUES (?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET last_indexed_ordinal = excluded.last_indexed_ordinal, updated_at = excluded.updated_at");
|
|
16521
|
-
upsertIndexStatements.set(db, stmt);
|
|
16569
|
+
if (result.sticky_turn_reminder_text.length === 0) {
|
|
16570
|
+
return null;
|
|
16522
16571
|
}
|
|
16523
|
-
return
|
|
16572
|
+
return {
|
|
16573
|
+
text: result.sticky_turn_reminder_text,
|
|
16574
|
+
messageId: result.sticky_turn_reminder_message_id.length > 0 ? result.sticky_turn_reminder_message_id : null
|
|
16575
|
+
};
|
|
16524
16576
|
}
|
|
16525
|
-
function
|
|
16526
|
-
|
|
16527
|
-
|
|
16528
|
-
|
|
16529
|
-
|
|
16530
|
-
}
|
|
16531
|
-
return stmt;
|
|
16577
|
+
function setPersistedStickyTurnReminder(db, sessionId, text, messageId = "") {
|
|
16578
|
+
db.transaction(() => {
|
|
16579
|
+
ensureSessionMetaRow(db, sessionId);
|
|
16580
|
+
db.prepare("UPDATE session_meta SET sticky_turn_reminder_text = ?, sticky_turn_reminder_message_id = ? WHERE session_id = ?").run(text, messageId, sessionId);
|
|
16581
|
+
})();
|
|
16532
16582
|
}
|
|
16533
|
-
function
|
|
16534
|
-
|
|
16535
|
-
if (!stmt) {
|
|
16536
|
-
stmt = db.prepare("DELETE FROM message_history_index WHERE session_id = ?");
|
|
16537
|
-
deleteIndexStatements.set(db, stmt);
|
|
16538
|
-
}
|
|
16539
|
-
return stmt;
|
|
16583
|
+
function clearPersistedStickyTurnReminder(db, sessionId) {
|
|
16584
|
+
db.prepare("UPDATE session_meta SET sticky_turn_reminder_text = '', sticky_turn_reminder_message_id = '' WHERE session_id = ?").run(sessionId);
|
|
16540
16585
|
}
|
|
16541
|
-
function
|
|
16542
|
-
const
|
|
16543
|
-
|
|
16586
|
+
function getPersistedNoteNudge(db, sessionId) {
|
|
16587
|
+
const result = db.prepare("SELECT note_nudge_trigger_pending, note_nudge_trigger_message_id, note_nudge_sticky_text, note_nudge_sticky_message_id FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
16588
|
+
if (!isPersistedNoteNudgeRow(result)) {
|
|
16589
|
+
return getDefaultPersistedNoteNudge();
|
|
16590
|
+
}
|
|
16591
|
+
return {
|
|
16592
|
+
triggerPending: result.note_nudge_trigger_pending === 1,
|
|
16593
|
+
triggerMessageId: result.note_nudge_trigger_message_id.length > 0 ? result.note_nudge_trigger_message_id : null,
|
|
16594
|
+
stickyText: result.note_nudge_sticky_text.length > 0 ? result.note_nudge_sticky_text : null,
|
|
16595
|
+
stickyMessageId: result.note_nudge_sticky_message_id.length > 0 ? result.note_nudge_sticky_message_id : null
|
|
16596
|
+
};
|
|
16544
16597
|
}
|
|
16545
|
-
function
|
|
16598
|
+
function setPersistedNoteNudgeTrigger(db, sessionId, triggerMessageId = "") {
|
|
16546
16599
|
db.transaction(() => {
|
|
16547
|
-
|
|
16548
|
-
|
|
16600
|
+
ensureSessionMetaRow(db, sessionId);
|
|
16601
|
+
db.prepare("UPDATE session_meta SET note_nudge_trigger_pending = 1, note_nudge_trigger_message_id = ?, note_nudge_sticky_text = '', note_nudge_sticky_message_id = '' WHERE session_id = ?").run(triggerMessageId, sessionId);
|
|
16549
16602
|
})();
|
|
16550
16603
|
}
|
|
16551
|
-
function
|
|
16552
|
-
|
|
16553
|
-
|
|
16554
|
-
|
|
16555
|
-
|
|
16556
|
-
return extractTexts(parts).map(cleanUserText).map(normalizeIndexText).filter((text) => text.length > 0).join(" / ");
|
|
16557
|
-
}
|
|
16558
|
-
if (role === "assistant") {
|
|
16559
|
-
return extractTexts(parts).map(removeSystemReminders).map(normalizeIndexText).filter((text) => text.length > 0).join(" / ");
|
|
16560
|
-
}
|
|
16561
|
-
return "";
|
|
16604
|
+
function setPersistedNoteNudgeTriggerMessageId(db, sessionId, triggerMessageId) {
|
|
16605
|
+
db.transaction(() => {
|
|
16606
|
+
ensureSessionMetaRow(db, sessionId);
|
|
16607
|
+
db.prepare("UPDATE session_meta SET note_nudge_trigger_message_id = ? WHERE session_id = ?").run(triggerMessageId, sessionId);
|
|
16608
|
+
})();
|
|
16562
16609
|
}
|
|
16563
|
-
function
|
|
16564
|
-
const messages = readMessages(sessionId);
|
|
16565
|
-
if (messages.length === 0) {
|
|
16566
|
-
db.transaction(() => clearIndexedMessages(db, sessionId))();
|
|
16567
|
-
return;
|
|
16568
|
-
}
|
|
16569
|
-
let lastIndexedOrdinal = getLastIndexedOrdinal(db, sessionId);
|
|
16570
|
-
if (lastIndexedOrdinal > messages.length) {
|
|
16571
|
-
db.transaction(() => clearIndexedMessages(db, sessionId))();
|
|
16572
|
-
lastIndexedOrdinal = 0;
|
|
16573
|
-
}
|
|
16574
|
-
if (lastIndexedOrdinal >= messages.length) {
|
|
16575
|
-
return;
|
|
16576
|
-
}
|
|
16577
|
-
const messagesToInsert = messages.filter((message) => message.ordinal > lastIndexedOrdinal).filter((message) => message.role === "user" || message.role === "assistant").map((message) => ({
|
|
16578
|
-
ordinal: message.ordinal,
|
|
16579
|
-
id: message.id,
|
|
16580
|
-
role: message.role,
|
|
16581
|
-
content: getIndexableContent(message.role, message.parts)
|
|
16582
|
-
})).filter((message) => message.content.length > 0);
|
|
16583
|
-
const now = Date.now();
|
|
16610
|
+
function setPersistedDeliveredNoteNudge(db, sessionId, text, messageId = "") {
|
|
16584
16611
|
db.transaction(() => {
|
|
16585
|
-
|
|
16586
|
-
|
|
16587
|
-
insertMessage.run(sessionId, message.ordinal, message.id, message.role, message.content);
|
|
16588
|
-
}
|
|
16589
|
-
getUpsertIndexStatement(db).run(sessionId, messages.length, now);
|
|
16612
|
+
ensureSessionMetaRow(db, sessionId);
|
|
16613
|
+
db.prepare("UPDATE session_meta SET note_nudge_trigger_pending = 0, note_nudge_trigger_message_id = '', note_nudge_sticky_text = ?, note_nudge_sticky_message_id = ? WHERE session_id = ?").run(text, messageId, sessionId);
|
|
16590
16614
|
})();
|
|
16591
16615
|
}
|
|
16592
|
-
|
|
16616
|
+
function clearPersistedNoteNudge(db, sessionId) {
|
|
16617
|
+
db.prepare("UPDATE session_meta SET note_nudge_trigger_pending = 0, note_nudge_trigger_message_id = '', note_nudge_sticky_text = '', note_nudge_sticky_message_id = '' WHERE session_id = ?").run(sessionId);
|
|
16618
|
+
}
|
|
16619
|
+
function getStrippedPlaceholderIds(db, sessionId) {
|
|
16620
|
+
const row = db.prepare("SELECT stripped_placeholder_ids FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
16621
|
+
const raw = row?.stripped_placeholder_ids;
|
|
16622
|
+
if (!raw || raw.length === 0)
|
|
16623
|
+
return new Set;
|
|
16624
|
+
try {
|
|
16625
|
+
const parsed = JSON.parse(raw);
|
|
16626
|
+
if (Array.isArray(parsed))
|
|
16627
|
+
return new Set(parsed.filter((v) => typeof v === "string"));
|
|
16628
|
+
} catch {}
|
|
16629
|
+
return new Set;
|
|
16630
|
+
}
|
|
16631
|
+
function setStrippedPlaceholderIds(db, sessionId, ids) {
|
|
16632
|
+
ensureSessionMetaRow(db, sessionId);
|
|
16633
|
+
const json2 = ids.size > 0 ? JSON.stringify([...ids]) : "";
|
|
16634
|
+
db.prepare("UPDATE session_meta SET stripped_placeholder_ids = ? WHERE session_id = ?").run(json2, sessionId);
|
|
16635
|
+
}
|
|
16636
|
+
function removeStrippedPlaceholderId(db, sessionId, messageId) {
|
|
16637
|
+
const ids = getStrippedPlaceholderIds(db, sessionId);
|
|
16638
|
+
if (!ids.delete(messageId)) {
|
|
16639
|
+
return false;
|
|
16640
|
+
}
|
|
16641
|
+
setStrippedPlaceholderIds(db, sessionId, ids);
|
|
16642
|
+
return true;
|
|
16643
|
+
}
|
|
16593
16644
|
// src/features/magic-context/storage-meta-session.ts
|
|
16594
16645
|
function getOrCreateSessionMeta(db, sessionId) {
|
|
16595
16646
|
const result = db.prepare("SELECT session_id, last_response_time, cache_ttl, counter, last_nudge_tokens, last_nudge_band, last_transform_error, is_subagent, last_context_percentage, last_input_tokens, times_execute_threshold_reached, compartment_in_progress, system_prompt_hash, cleared_reasoning_through_tag FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
@@ -16755,10 +16806,13 @@ function getSourceContents(db, sessionId, tagIds) {
|
|
|
16755
16806
|
var insertTagStatements = new WeakMap;
|
|
16756
16807
|
var updateTagStatusStatements = new WeakMap;
|
|
16757
16808
|
var updateTagMessageIdStatements = new WeakMap;
|
|
16809
|
+
var getTagNumbersByMessageIdStatements = new WeakMap;
|
|
16810
|
+
var deleteTagsByMessageIdStatements = new WeakMap;
|
|
16811
|
+
var getMaxTagNumberBySessionStatements = new WeakMap;
|
|
16758
16812
|
function getInsertTagStatement(db) {
|
|
16759
16813
|
let stmt = insertTagStatements.get(db);
|
|
16760
16814
|
if (!stmt) {
|
|
16761
|
-
stmt = db.prepare("INSERT INTO tags (session_id, message_id, type, byte_size, tag_number) VALUES (?, ?, ?, ?, ?)");
|
|
16815
|
+
stmt = db.prepare("INSERT INTO tags (session_id, message_id, type, byte_size, reasoning_byte_size, tag_number) VALUES (?, ?, ?, ?, ?, ?)");
|
|
16762
16816
|
insertTagStatements.set(db, stmt);
|
|
16763
16817
|
}
|
|
16764
16818
|
return stmt;
|
|
@@ -16779,6 +16833,30 @@ function getUpdateTagMessageIdStatement(db) {
|
|
|
16779
16833
|
}
|
|
16780
16834
|
return stmt;
|
|
16781
16835
|
}
|
|
16836
|
+
function getTagNumbersByMessageIdStatement(db) {
|
|
16837
|
+
let stmt = getTagNumbersByMessageIdStatements.get(db);
|
|
16838
|
+
if (!stmt) {
|
|
16839
|
+
stmt = db.prepare("SELECT tag_number FROM tags WHERE session_id = ? AND (message_id = ? OR message_id LIKE ? ESCAPE '\\' OR message_id LIKE ? ESCAPE '\\') ORDER BY tag_number ASC");
|
|
16840
|
+
getTagNumbersByMessageIdStatements.set(db, stmt);
|
|
16841
|
+
}
|
|
16842
|
+
return stmt;
|
|
16843
|
+
}
|
|
16844
|
+
function getDeleteTagsByMessageIdStatement(db) {
|
|
16845
|
+
let stmt = deleteTagsByMessageIdStatements.get(db);
|
|
16846
|
+
if (!stmt) {
|
|
16847
|
+
stmt = db.prepare("DELETE FROM tags WHERE session_id = ? AND (message_id = ? OR message_id LIKE ? ESCAPE '\\' OR message_id LIKE ? ESCAPE '\\')");
|
|
16848
|
+
deleteTagsByMessageIdStatements.set(db, stmt);
|
|
16849
|
+
}
|
|
16850
|
+
return stmt;
|
|
16851
|
+
}
|
|
16852
|
+
function getMaxTagNumberBySessionStatement(db) {
|
|
16853
|
+
let stmt = getMaxTagNumberBySessionStatements.get(db);
|
|
16854
|
+
if (!stmt) {
|
|
16855
|
+
stmt = db.prepare("SELECT COALESCE(MAX(tag_number), 0) AS max_tag_number FROM tags WHERE session_id = ?");
|
|
16856
|
+
getMaxTagNumberBySessionStatements.set(db, stmt);
|
|
16857
|
+
}
|
|
16858
|
+
return stmt;
|
|
16859
|
+
}
|
|
16782
16860
|
function isTagRow(row) {
|
|
16783
16861
|
if (row === null || typeof row !== "object")
|
|
16784
16862
|
return false;
|
|
@@ -16794,11 +16872,27 @@ function toTagEntry(row) {
|
|
|
16794
16872
|
type,
|
|
16795
16873
|
status,
|
|
16796
16874
|
byteSize: row.byte_size,
|
|
16875
|
+
reasoningByteSize: row.reasoning_byte_size ?? 0,
|
|
16797
16876
|
sessionId: row.session_id
|
|
16798
16877
|
};
|
|
16799
16878
|
}
|
|
16800
|
-
function
|
|
16801
|
-
|
|
16879
|
+
function isTagNumberRow(row) {
|
|
16880
|
+
if (row === null || typeof row !== "object")
|
|
16881
|
+
return false;
|
|
16882
|
+
const r = row;
|
|
16883
|
+
return typeof r.tag_number === "number";
|
|
16884
|
+
}
|
|
16885
|
+
function isMaxTagNumberRow(row) {
|
|
16886
|
+
if (row === null || typeof row !== "object")
|
|
16887
|
+
return false;
|
|
16888
|
+
const r = row;
|
|
16889
|
+
return typeof r.max_tag_number === "number";
|
|
16890
|
+
}
|
|
16891
|
+
function escapeLikePattern(value) {
|
|
16892
|
+
return value.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_");
|
|
16893
|
+
}
|
|
16894
|
+
function insertTag(db, sessionId, messageId, type, byteSize2, tagNumber, reasoningByteSize = 0) {
|
|
16895
|
+
getInsertTagStatement(db).run(sessionId, messageId, type, byteSize2, reasoningByteSize, tagNumber);
|
|
16802
16896
|
return tagNumber;
|
|
16803
16897
|
}
|
|
16804
16898
|
function updateTagStatus(db, sessionId, tagId, status) {
|
|
@@ -16807,15 +16901,30 @@ function updateTagStatus(db, sessionId, tagId, status) {
|
|
|
16807
16901
|
function updateTagMessageId(db, sessionId, tagId, messageId) {
|
|
16808
16902
|
getUpdateTagMessageIdStatement(db).run(messageId, sessionId, tagId);
|
|
16809
16903
|
}
|
|
16904
|
+
function deleteTagsByMessageId(db, sessionId, messageId) {
|
|
16905
|
+
const escapedMessageId = escapeLikePattern(messageId);
|
|
16906
|
+
const textPartPattern = `${escapedMessageId}:p%`;
|
|
16907
|
+
const filePartPattern = `${escapedMessageId}:file%`;
|
|
16908
|
+
const tagNumbers = getTagNumbersByMessageIdStatement(db).all(sessionId, messageId, textPartPattern, filePartPattern).filter(isTagNumberRow).map((row) => row.tag_number);
|
|
16909
|
+
if (tagNumbers.length === 0) {
|
|
16910
|
+
return [];
|
|
16911
|
+
}
|
|
16912
|
+
getDeleteTagsByMessageIdStatement(db).run(sessionId, messageId, textPartPattern, filePartPattern);
|
|
16913
|
+
return tagNumbers;
|
|
16914
|
+
}
|
|
16915
|
+
function getMaxTagNumberBySession(db, sessionId) {
|
|
16916
|
+
const row = getMaxTagNumberBySessionStatement(db).get(sessionId);
|
|
16917
|
+
return isMaxTagNumberRow(row) ? row.max_tag_number : 0;
|
|
16918
|
+
}
|
|
16810
16919
|
function getTagsBySession(db, sessionId) {
|
|
16811
|
-
const rows = db.prepare("SELECT id, message_id, type, status, byte_size, session_id, tag_number FROM tags WHERE session_id = ? ORDER BY tag_number ASC, id ASC").all(sessionId).filter(isTagRow);
|
|
16920
|
+
const rows = db.prepare("SELECT id, message_id, type, status, byte_size, reasoning_byte_size, session_id, tag_number FROM tags WHERE session_id = ? ORDER BY tag_number ASC, id ASC").all(sessionId).filter(isTagRow);
|
|
16812
16921
|
return rows.map(toTagEntry);
|
|
16813
16922
|
}
|
|
16814
16923
|
function getTopNBySize(db, sessionId, n) {
|
|
16815
16924
|
if (n <= 0) {
|
|
16816
16925
|
return [];
|
|
16817
16926
|
}
|
|
16818
|
-
const rows = db.prepare("SELECT id, message_id, type, status, byte_size, session_id, tag_number FROM tags WHERE session_id = ? AND status = 'active' ORDER BY byte_size DESC, tag_number ASC LIMIT ?").all(sessionId, n).filter(isTagRow);
|
|
16927
|
+
const rows = db.prepare("SELECT id, message_id, type, status, byte_size, reasoning_byte_size, session_id, tag_number FROM tags WHERE session_id = ? AND status = 'active' ORDER BY byte_size DESC, tag_number ASC LIMIT ?").all(sessionId, n).filter(isTagRow);
|
|
16819
16928
|
return rows.map(toTagEntry);
|
|
16820
16929
|
}
|
|
16821
16930
|
// src/plugin/dream-timer.ts
|
|
@@ -17035,7 +17144,7 @@ function createTagger() {
|
|
|
17035
17144
|
}
|
|
17036
17145
|
return map2;
|
|
17037
17146
|
}
|
|
17038
|
-
function assignTag(sessionId, messageId, type, byteSize2, db) {
|
|
17147
|
+
function assignTag(sessionId, messageId, type, byteSize2, db, reasoningByteSize = 0) {
|
|
17039
17148
|
const sessionAssignments = getSessionAssignments(sessionId);
|
|
17040
17149
|
const existing = sessionAssignments.get(messageId);
|
|
17041
17150
|
if (existing !== undefined) {
|
|
@@ -17044,7 +17153,7 @@ function createTagger() {
|
|
|
17044
17153
|
const current = counters.get(sessionId) ?? 0;
|
|
17045
17154
|
const next = current + 1;
|
|
17046
17155
|
db.transaction(() => {
|
|
17047
|
-
insertTag(db, sessionId, messageId, type, byteSize2, next);
|
|
17156
|
+
insertTag(db, sessionId, messageId, type, byteSize2, next, reasoningByteSize);
|
|
17048
17157
|
getUpsertCounterStatement(db).run(sessionId, next);
|
|
17049
17158
|
})();
|
|
17050
17159
|
counters.set(sessionId, next);
|
|
@@ -17180,17 +17289,50 @@ var FORCE_MATERIALIZE_PERCENTAGE = 85;
|
|
|
17180
17289
|
function getProactiveCompartmentTriggerPercentage(executeThresholdPercentage) {
|
|
17181
17290
|
return Math.max(0, executeThresholdPercentage - PROACTIVE_TRIGGER_OFFSET_PERCENTAGE);
|
|
17182
17291
|
}
|
|
17183
|
-
function estimateProjectedPostDropPercentage(db, sessionId, usage) {
|
|
17184
|
-
const pendingDrops = getPendingOps(db, sessionId).filter((op) => op.operation === "drop");
|
|
17185
|
-
if (pendingDrops.length === 0)
|
|
17186
|
-
return null;
|
|
17292
|
+
function estimateProjectedPostDropPercentage(db, sessionId, usage, autoDropToolAge, protectedTags, clearReasoningAge, clearedReasoningThroughTag) {
|
|
17187
17293
|
const activeTags = getTagsBySession(db, sessionId).filter((tag) => tag.status === "active");
|
|
17188
|
-
const totalActiveBytes = activeTags.reduce((sum, tag) => sum + tag.byteSize, 0);
|
|
17294
|
+
const totalActiveBytes = activeTags.reduce((sum, tag) => sum + tag.byteSize + tag.reasoningByteSize, 0);
|
|
17189
17295
|
if (totalActiveBytes === 0)
|
|
17190
17296
|
return null;
|
|
17297
|
+
let droppableBytes = 0;
|
|
17298
|
+
const pendingDrops = getPendingOps(db, sessionId).filter((op) => op.operation === "drop");
|
|
17191
17299
|
const pendingDropTagIds = new Set(pendingDrops.map((op) => op.tagId));
|
|
17192
|
-
|
|
17193
|
-
|
|
17300
|
+
if (pendingDrops.length > 0) {
|
|
17301
|
+
droppableBytes += activeTags.filter((tag) => pendingDropTagIds.has(tag.tagNumber)).reduce((sum, tag) => sum + tag.byteSize + tag.reasoningByteSize, 0);
|
|
17302
|
+
}
|
|
17303
|
+
const maxTag = activeTags.reduce((max, t) => Math.max(max, t.tagNumber), 0);
|
|
17304
|
+
if (autoDropToolAge !== undefined && protectedTags !== undefined) {
|
|
17305
|
+
const toolAgeCutoff = maxTag - autoDropToolAge;
|
|
17306
|
+
const protectedCutoff = maxTag - protectedTags;
|
|
17307
|
+
for (const tag of activeTags) {
|
|
17308
|
+
if (pendingDropTagIds.has(tag.tagNumber))
|
|
17309
|
+
continue;
|
|
17310
|
+
if (tag.tagNumber > protectedCutoff)
|
|
17311
|
+
continue;
|
|
17312
|
+
if (tag.type === "tool" && tag.tagNumber <= toolAgeCutoff) {
|
|
17313
|
+
droppableBytes += tag.byteSize + tag.reasoningByteSize;
|
|
17314
|
+
}
|
|
17315
|
+
}
|
|
17316
|
+
}
|
|
17317
|
+
if (clearReasoningAge !== undefined && clearedReasoningThroughTag !== undefined) {
|
|
17318
|
+
const reasoningAgeCutoff = maxTag - clearReasoningAge;
|
|
17319
|
+
for (const tag of activeTags) {
|
|
17320
|
+
if (tag.type !== "message")
|
|
17321
|
+
continue;
|
|
17322
|
+
if (pendingDropTagIds.has(tag.tagNumber))
|
|
17323
|
+
continue;
|
|
17324
|
+
if (tag.tagNumber <= clearedReasoningThroughTag)
|
|
17325
|
+
continue;
|
|
17326
|
+
if (tag.tagNumber > reasoningAgeCutoff)
|
|
17327
|
+
continue;
|
|
17328
|
+
if (tag.reasoningByteSize > 0) {
|
|
17329
|
+
droppableBytes += tag.reasoningByteSize;
|
|
17330
|
+
}
|
|
17331
|
+
}
|
|
17332
|
+
}
|
|
17333
|
+
if (droppableBytes === 0)
|
|
17334
|
+
return null;
|
|
17335
|
+
const dropRatio = Math.min(droppableBytes / totalActiveBytes, 1);
|
|
17194
17336
|
return usage.percentage * (1 - dropRatio);
|
|
17195
17337
|
}
|
|
17196
17338
|
var TAIL_INFO_DEFAULTS = {
|
|
@@ -17227,7 +17369,7 @@ function getUnsummarizedTailInfo(db, sessionId, compartmentTokenBudget) {
|
|
|
17227
17369
|
}
|
|
17228
17370
|
});
|
|
17229
17371
|
}
|
|
17230
|
-
function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, compartmentTokenBudget = DEFAULT_COMPARTMENT_TOKEN_BUDGET) {
|
|
17372
|
+
function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPercentage, executeThresholdPercentage, compartmentTokenBudget = DEFAULT_COMPARTMENT_TOKEN_BUDGET, autoDropToolAge, protectedTagCount, clearReasoningAge) {
|
|
17231
17373
|
if (sessionMeta.compartmentInProgress) {
|
|
17232
17374
|
return { shouldFire: false };
|
|
17233
17375
|
}
|
|
@@ -17235,7 +17377,7 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
|
|
|
17235
17377
|
if (!tailInfo.hasNewRawHistory) {
|
|
17236
17378
|
return { shouldFire: false };
|
|
17237
17379
|
}
|
|
17238
|
-
const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage);
|
|
17380
|
+
const projectedPostDropPercentage = estimateProjectedPostDropPercentage(db, sessionId, usage, autoDropToolAge, protectedTagCount, clearReasoningAge, sessionMeta.clearedReasoningThroughTag);
|
|
17239
17381
|
const relativePostDropTarget = executeThresholdPercentage * POST_DROP_TARGET_RATIO;
|
|
17240
17382
|
if (usage.percentage >= FORCE_COMPARTMENT_PERCENTAGE) {
|
|
17241
17383
|
if (projectedPostDropPercentage !== null && projectedPostDropPercentage <= relativePostDropTarget) {
|
|
@@ -17679,6 +17821,85 @@ function getMessageUpdatedAssistantInfo(properties) {
|
|
|
17679
17821
|
}
|
|
17680
17822
|
};
|
|
17681
17823
|
}
|
|
17824
|
+
function getMessageRemovedInfo(properties) {
|
|
17825
|
+
if (!isRecord(properties)) {
|
|
17826
|
+
return null;
|
|
17827
|
+
}
|
|
17828
|
+
if (typeof properties.sessionID !== "string" || typeof properties.messageID !== "string") {
|
|
17829
|
+
return null;
|
|
17830
|
+
}
|
|
17831
|
+
return {
|
|
17832
|
+
sessionID: properties.sessionID,
|
|
17833
|
+
messageID: properties.messageID
|
|
17834
|
+
};
|
|
17835
|
+
}
|
|
17836
|
+
|
|
17837
|
+
// src/hooks/magic-context/note-nudger.ts
|
|
17838
|
+
var NOTE_NUDGE_COOLDOWN_MS = 15 * 60 * 1000;
|
|
17839
|
+
var lastDeliveredAt = new Map;
|
|
17840
|
+
function getPersistedNoteNudgeDeliveredAt(_db, sessionId) {
|
|
17841
|
+
return lastDeliveredAt.get(sessionId) ?? 0;
|
|
17842
|
+
}
|
|
17843
|
+
function recordNoteNudgeDeliveryTime(sessionId) {
|
|
17844
|
+
lastDeliveredAt.set(sessionId, Date.now());
|
|
17845
|
+
}
|
|
17846
|
+
function onNoteTrigger(db, sessionId, trigger) {
|
|
17847
|
+
setPersistedNoteNudgeTrigger(db, sessionId);
|
|
17848
|
+
sessionLog(sessionId, `note-nudge: trigger fired (${trigger}), triggerPending=true`);
|
|
17849
|
+
}
|
|
17850
|
+
function peekNoteNudgeText(db, sessionId, currentUserMessageId, projectIdentity) {
|
|
17851
|
+
const state = getPersistedNoteNudge(db, sessionId);
|
|
17852
|
+
if (!state.triggerPending)
|
|
17853
|
+
return null;
|
|
17854
|
+
if (!state.triggerMessageId && currentUserMessageId) {
|
|
17855
|
+
setPersistedNoteNudgeTriggerMessageId(db, sessionId, currentUserMessageId);
|
|
17856
|
+
state.triggerMessageId = currentUserMessageId;
|
|
17857
|
+
}
|
|
17858
|
+
if (state.triggerMessageId && currentUserMessageId && state.triggerMessageId === currentUserMessageId) {
|
|
17859
|
+
sessionLog(sessionId, `note-nudge: deferring \u2014 current user message ${currentUserMessageId} is same as trigger-time message`);
|
|
17860
|
+
return null;
|
|
17861
|
+
}
|
|
17862
|
+
const deliveredAt = getPersistedNoteNudgeDeliveredAt(db, sessionId);
|
|
17863
|
+
if (deliveredAt > 0 && Date.now() - deliveredAt < NOTE_NUDGE_COOLDOWN_MS) {
|
|
17864
|
+
sessionLog(sessionId, `note-nudge: suppressing \u2014 last delivered ${Math.round((Date.now() - deliveredAt) / 1000)}s ago (cooldown ${NOTE_NUDGE_COOLDOWN_MS / 60000}m)`);
|
|
17865
|
+
clearPersistedNoteNudge(db, sessionId);
|
|
17866
|
+
return null;
|
|
17867
|
+
}
|
|
17868
|
+
const notes = getSessionNotes(db, sessionId);
|
|
17869
|
+
const readySmartCount = projectIdentity ? getReadySmartNotes(db, projectIdentity).length : 0;
|
|
17870
|
+
const totalCount = notes.length + readySmartCount;
|
|
17871
|
+
if (totalCount === 0) {
|
|
17872
|
+
sessionLog(sessionId, "note-nudge: triggerPending but no notes found, skipping");
|
|
17873
|
+
clearPersistedNoteNudge(db, sessionId);
|
|
17874
|
+
return null;
|
|
17875
|
+
}
|
|
17876
|
+
const parts = [];
|
|
17877
|
+
if (notes.length > 0) {
|
|
17878
|
+
parts.push(`${notes.length} deferred note${notes.length === 1 ? "" : "s"}`);
|
|
17879
|
+
}
|
|
17880
|
+
if (readySmartCount > 0) {
|
|
17881
|
+
parts.push(`${readySmartCount} ready smart note${readySmartCount === 1 ? "" : "s"}`);
|
|
17882
|
+
}
|
|
17883
|
+
sessionLog(sessionId, `note-nudge: delivering nudge for ${parts.join(" and ")}`);
|
|
17884
|
+
return `You have ${parts.join(" and ")}. Review with ctx_note read \u2014 some may be actionable now.`;
|
|
17885
|
+
}
|
|
17886
|
+
function markNoteNudgeDelivered(db, sessionId, text, messageId) {
|
|
17887
|
+
setPersistedDeliveredNoteNudge(db, sessionId, messageId ? text : "", messageId ?? "");
|
|
17888
|
+
recordNoteNudgeDeliveryTime(sessionId);
|
|
17889
|
+
sessionLog(sessionId, messageId ? `note-nudge: marked delivered, sticky anchor=${messageId}` : "note-nudge: marked delivered without anchor");
|
|
17890
|
+
}
|
|
17891
|
+
function getStickyNoteNudge(db, sessionId) {
|
|
17892
|
+
const state = getPersistedNoteNudge(db, sessionId);
|
|
17893
|
+
if (!state.stickyText || !state.stickyMessageId)
|
|
17894
|
+
return null;
|
|
17895
|
+
return { text: state.stickyText, messageId: state.stickyMessageId };
|
|
17896
|
+
}
|
|
17897
|
+
function clearNoteNudgeState(db, sessionId, options) {
|
|
17898
|
+
if (options?.persist !== false) {
|
|
17899
|
+
clearPersistedNoteNudge(db, sessionId);
|
|
17900
|
+
}
|
|
17901
|
+
lastDeliveredAt.delete(sessionId);
|
|
17902
|
+
}
|
|
17682
17903
|
|
|
17683
17904
|
// src/hooks/magic-context/event-handler.ts
|
|
17684
17905
|
var CONTEXT_USAGE_TTL_MS = 60 * 60 * 1000;
|
|
@@ -17690,6 +17911,46 @@ function evictExpiredUsageEntries(contextUsageMap) {
|
|
|
17690
17911
|
}
|
|
17691
17912
|
}
|
|
17692
17913
|
}
|
|
17914
|
+
function cleanupRemovedMessageState(deps, sessionId, messageId) {
|
|
17915
|
+
return deps.db.transaction(() => {
|
|
17916
|
+
const removedTagNumbers = deleteTagsByMessageId(deps.db, sessionId, messageId);
|
|
17917
|
+
sessionLog(sessionId, `event message.removed: deleted ${removedTagNumbers.length} tag(s) for message ${messageId}`);
|
|
17918
|
+
const strippedPlaceholderRemoved = removeStrippedPlaceholderId(deps.db, sessionId, messageId);
|
|
17919
|
+
sessionLog(sessionId, strippedPlaceholderRemoved ? `event message.removed: removed ${messageId} from stripped placeholder ids` : `event message.removed: stripped placeholder ids unchanged for ${messageId}`);
|
|
17920
|
+
const persistedNudgePlacement = getPersistedNudgePlacement(deps.db, sessionId);
|
|
17921
|
+
const clearedNudgePlacement = persistedNudgePlacement?.messageId === messageId;
|
|
17922
|
+
if (clearedNudgePlacement) {
|
|
17923
|
+
clearPersistedNudgePlacement(deps.db, sessionId);
|
|
17924
|
+
}
|
|
17925
|
+
sessionLog(sessionId, clearedNudgePlacement ? `event message.removed: cleared nudge anchor for ${messageId}` : `event message.removed: nudge anchor unchanged for ${messageId}`);
|
|
17926
|
+
const persistedNoteNudge = getPersistedNoteNudge(deps.db, sessionId);
|
|
17927
|
+
const clearedNoteNudge = persistedNoteNudge.triggerMessageId === messageId || persistedNoteNudge.stickyMessageId === messageId;
|
|
17928
|
+
if (clearedNoteNudge) {
|
|
17929
|
+
clearPersistedNoteNudge(deps.db, sessionId);
|
|
17930
|
+
}
|
|
17931
|
+
sessionLog(sessionId, clearedNoteNudge ? `event message.removed: cleared note nudge state for ${messageId}` : `event message.removed: note nudge state unchanged for ${messageId}`);
|
|
17932
|
+
const persistedStickyTurnReminder = getPersistedStickyTurnReminder(deps.db, sessionId);
|
|
17933
|
+
const clearedStickyTurnReminder = persistedStickyTurnReminder?.messageId === messageId;
|
|
17934
|
+
if (clearedStickyTurnReminder) {
|
|
17935
|
+
clearPersistedStickyTurnReminder(deps.db, sessionId);
|
|
17936
|
+
}
|
|
17937
|
+
sessionLog(sessionId, clearedStickyTurnReminder ? `event message.removed: cleared sticky turn reminder for ${messageId}` : `event message.removed: sticky turn reminder unchanged for ${messageId}`);
|
|
17938
|
+
const currentWatermark = getPersistedReasoningWatermark(deps.db, sessionId);
|
|
17939
|
+
const maxRemainingTag = getMaxTagNumberBySession(deps.db, sessionId);
|
|
17940
|
+
if (currentWatermark > maxRemainingTag) {
|
|
17941
|
+
setPersistedReasoningWatermark(deps.db, sessionId, maxRemainingTag);
|
|
17942
|
+
sessionLog(sessionId, `event message.removed: reset reasoning watermark ${currentWatermark}\u2192${maxRemainingTag}`);
|
|
17943
|
+
} else {
|
|
17944
|
+
sessionLog(sessionId, `event message.removed: reasoning watermark unchanged at ${currentWatermark} (max tag ${maxRemainingTag})`);
|
|
17945
|
+
}
|
|
17946
|
+
const removedIndexedMessages = deleteIndexedMessage(deps.db, sessionId, messageId);
|
|
17947
|
+
sessionLog(sessionId, `event message.removed: deleted ${removedIndexedMessages} indexed message row(s) for ${messageId}`);
|
|
17948
|
+
return {
|
|
17949
|
+
clearedNudgePlacement,
|
|
17950
|
+
clearedNoteNudge
|
|
17951
|
+
};
|
|
17952
|
+
})();
|
|
17953
|
+
}
|
|
17693
17954
|
function createEventHandler2(deps) {
|
|
17694
17955
|
return async (input) => {
|
|
17695
17956
|
evictExpiredUsageEntries(deps.contextUsageMap);
|
|
@@ -17763,7 +18024,7 @@ function createEventHandler2(deps) {
|
|
|
17763
18024
|
const sessionMeta = getOrCreateSessionMeta(deps.db, info.sessionID);
|
|
17764
18025
|
const previousPercentage = sessionMeta.lastContextPercentage;
|
|
17765
18026
|
if (!sessionMeta.isSubagent) {
|
|
17766
|
-
const triggerResult = checkCompartmentTrigger(deps.db, info.sessionID, sessionMeta, { percentage, inputTokens: totalInputTokens }, previousPercentage, resolveExecuteThreshold(deps.config.execute_threshold_percentage ?? 65, modelKey, 65));
|
|
18027
|
+
const triggerResult = checkCompartmentTrigger(deps.db, info.sessionID, sessionMeta, { percentage, inputTokens: totalInputTokens }, previousPercentage, resolveExecuteThreshold(deps.config.execute_threshold_percentage ?? 65, modelKey, 65), undefined, deps.config.auto_drop_tool_age ?? 100, deps.config.protected_tags, deps.config.clear_reasoning_age ?? 50);
|
|
17767
18028
|
if (triggerResult.shouldFire) {
|
|
17768
18029
|
sessionLog(info.sessionID, `compartment trigger: firing (reason=${triggerResult.reason})`);
|
|
17769
18030
|
updateSessionMeta(deps.db, info.sessionID, {
|
|
@@ -17778,6 +18039,37 @@ function createEventHandler2(deps) {
|
|
|
17778
18039
|
}
|
|
17779
18040
|
return;
|
|
17780
18041
|
}
|
|
18042
|
+
if (input.event.type === "message.removed") {
|
|
18043
|
+
const info = getMessageRemovedInfo(input.event.properties);
|
|
18044
|
+
if (!info) {
|
|
18045
|
+
const sessionId = properties ? resolveSessionId(properties) : null;
|
|
18046
|
+
if (sessionId) {
|
|
18047
|
+
sessionLog(sessionId, "event message.removed: no message removal info extracted from event");
|
|
18048
|
+
} else {
|
|
18049
|
+
log("[magic-context] event message.removed: no message removal info extracted from event");
|
|
18050
|
+
}
|
|
18051
|
+
return;
|
|
18052
|
+
}
|
|
18053
|
+
sessionLog(info.sessionID, `event message.removed: invalidating state for message ${info.messageID}`);
|
|
18054
|
+
try {
|
|
18055
|
+
const cleanup = cleanupRemovedMessageState(deps, info.sessionID, info.messageID);
|
|
18056
|
+
deps.tagger.cleanup(info.sessionID);
|
|
18057
|
+
sessionLog(info.sessionID, "event message.removed: invalidated tagger session cache");
|
|
18058
|
+
if (cleanup.clearedNudgePlacement) {
|
|
18059
|
+
deps.nudgePlacements.clear(info.sessionID, { persist: false });
|
|
18060
|
+
sessionLog(info.sessionID, "event message.removed: cleared in-memory nudge placement cache");
|
|
18061
|
+
}
|
|
18062
|
+
if (cleanup.clearedNoteNudge) {
|
|
18063
|
+
clearNoteNudgeState(deps.db, info.sessionID, { persist: false });
|
|
18064
|
+
sessionLog(info.sessionID, "event message.removed: cleared in-memory note nudge state");
|
|
18065
|
+
}
|
|
18066
|
+
deps.onSessionCacheInvalidated?.(info.sessionID);
|
|
18067
|
+
sessionLog(info.sessionID, "event message.removed: cleared session injection cache");
|
|
18068
|
+
} catch (error48) {
|
|
18069
|
+
sessionLog(info.sessionID, "event message.removed cleanup failed:", error48);
|
|
18070
|
+
}
|
|
18071
|
+
return;
|
|
18072
|
+
}
|
|
17781
18073
|
if (input.event.type === "session.compacted") {
|
|
17782
18074
|
const sessionId = resolveSessionId(properties);
|
|
17783
18075
|
if (!sessionId) {
|
|
@@ -18622,72 +18914,6 @@ function createTextCompleteHandler() {
|
|
|
18622
18914
|
};
|
|
18623
18915
|
}
|
|
18624
18916
|
|
|
18625
|
-
// src/hooks/magic-context/note-nudger.ts
|
|
18626
|
-
var NOTE_NUDGE_COOLDOWN_MS = 15 * 60 * 1000;
|
|
18627
|
-
var lastDeliveredAt = new Map;
|
|
18628
|
-
function getPersistedNoteNudgeDeliveredAt(_db, sessionId) {
|
|
18629
|
-
return lastDeliveredAt.get(sessionId) ?? 0;
|
|
18630
|
-
}
|
|
18631
|
-
function recordNoteNudgeDeliveryTime(sessionId) {
|
|
18632
|
-
lastDeliveredAt.set(sessionId, Date.now());
|
|
18633
|
-
}
|
|
18634
|
-
function onNoteTrigger(db, sessionId, trigger) {
|
|
18635
|
-
setPersistedNoteNudgeTrigger(db, sessionId);
|
|
18636
|
-
sessionLog(sessionId, `note-nudge: trigger fired (${trigger}), triggerPending=true`);
|
|
18637
|
-
}
|
|
18638
|
-
function peekNoteNudgeText(db, sessionId, currentUserMessageId, projectIdentity) {
|
|
18639
|
-
const state = getPersistedNoteNudge(db, sessionId);
|
|
18640
|
-
if (!state.triggerPending)
|
|
18641
|
-
return null;
|
|
18642
|
-
if (!state.triggerMessageId && currentUserMessageId) {
|
|
18643
|
-
setPersistedNoteNudgeTriggerMessageId(db, sessionId, currentUserMessageId);
|
|
18644
|
-
state.triggerMessageId = currentUserMessageId;
|
|
18645
|
-
}
|
|
18646
|
-
if (state.triggerMessageId && currentUserMessageId && state.triggerMessageId === currentUserMessageId) {
|
|
18647
|
-
sessionLog(sessionId, `note-nudge: deferring \u2014 current user message ${currentUserMessageId} is same as trigger-time message`);
|
|
18648
|
-
return null;
|
|
18649
|
-
}
|
|
18650
|
-
if (state.stickyText && state.stickyMessageId) {
|
|
18651
|
-
const deliveredAt = getPersistedNoteNudgeDeliveredAt(db, sessionId);
|
|
18652
|
-
if (deliveredAt > 0 && Date.now() - deliveredAt < NOTE_NUDGE_COOLDOWN_MS) {
|
|
18653
|
-
sessionLog(sessionId, `note-nudge: suppressing \u2014 last delivered ${Math.round((Date.now() - deliveredAt) / 1000)}s ago (cooldown ${NOTE_NUDGE_COOLDOWN_MS / 60000}m)`);
|
|
18654
|
-
clearPersistedNoteNudge(db, sessionId);
|
|
18655
|
-
return null;
|
|
18656
|
-
}
|
|
18657
|
-
}
|
|
18658
|
-
const notes = getSessionNotes(db, sessionId);
|
|
18659
|
-
const readySmartCount = projectIdentity ? getReadySmartNotes(db, projectIdentity).length : 0;
|
|
18660
|
-
const totalCount = notes.length + readySmartCount;
|
|
18661
|
-
if (totalCount === 0) {
|
|
18662
|
-
sessionLog(sessionId, "note-nudge: triggerPending but no notes found, skipping");
|
|
18663
|
-
clearPersistedNoteNudge(db, sessionId);
|
|
18664
|
-
return null;
|
|
18665
|
-
}
|
|
18666
|
-
const parts = [];
|
|
18667
|
-
if (notes.length > 0) {
|
|
18668
|
-
parts.push(`${notes.length} deferred note${notes.length === 1 ? "" : "s"}`);
|
|
18669
|
-
}
|
|
18670
|
-
if (readySmartCount > 0) {
|
|
18671
|
-
parts.push(`${readySmartCount} ready smart note${readySmartCount === 1 ? "" : "s"}`);
|
|
18672
|
-
}
|
|
18673
|
-
sessionLog(sessionId, `note-nudge: delivering nudge for ${parts.join(" and ")}`);
|
|
18674
|
-
return `You have ${parts.join(" and ")}. Review with ctx_note read \u2014 some may be actionable now.`;
|
|
18675
|
-
}
|
|
18676
|
-
function markNoteNudgeDelivered(db, sessionId, text, messageId) {
|
|
18677
|
-
setPersistedDeliveredNoteNudge(db, sessionId, messageId ? text : "", messageId ?? "");
|
|
18678
|
-
recordNoteNudgeDeliveryTime(sessionId);
|
|
18679
|
-
sessionLog(sessionId, messageId ? `note-nudge: marked delivered, sticky anchor=${messageId}` : "note-nudge: marked delivered without anchor");
|
|
18680
|
-
}
|
|
18681
|
-
function getStickyNoteNudge(db, sessionId) {
|
|
18682
|
-
const state = getPersistedNoteNudge(db, sessionId);
|
|
18683
|
-
if (!state.stickyText || !state.stickyMessageId)
|
|
18684
|
-
return null;
|
|
18685
|
-
return { text: state.stickyText, messageId: state.stickyMessageId };
|
|
18686
|
-
}
|
|
18687
|
-
function clearNoteNudgeState(db, sessionId) {
|
|
18688
|
-
clearPersistedNoteNudge(db, sessionId);
|
|
18689
|
-
}
|
|
18690
|
-
|
|
18691
18917
|
// src/hooks/magic-context/strip-content.ts
|
|
18692
18918
|
var DROPPED_PLACEHOLDER_PATTERN = /^\[dropped \u00A7\d+\u00A7\]$/;
|
|
18693
18919
|
var TAG_PREFIX_PATTERN = /^\u00A7\d+\u00A7\s*/;
|
|
@@ -20928,6 +21154,16 @@ function collectRelevantSourceTagIds(messages, assignments) {
|
|
|
20928
21154
|
}
|
|
20929
21155
|
return Array.from(relevantTagIds);
|
|
20930
21156
|
}
|
|
21157
|
+
function getReasoningByteSize(parts) {
|
|
21158
|
+
let reasoningBytes = 0;
|
|
21159
|
+
for (const part of parts) {
|
|
21160
|
+
const content = part.thinking ?? part.text ?? "";
|
|
21161
|
+
if (content && content !== "[cleared]") {
|
|
21162
|
+
reasoningBytes += byteSize(content);
|
|
21163
|
+
}
|
|
21164
|
+
}
|
|
21165
|
+
return reasoningBytes;
|
|
21166
|
+
}
|
|
20931
21167
|
function tagMessages(sessionId, messages, tagger, db) {
|
|
20932
21168
|
const targets = new Map;
|
|
20933
21169
|
const reasoningByMessage = new Map;
|
|
@@ -20988,7 +21224,8 @@ function tagMessages(sessionId, messages, tagger, db) {
|
|
|
20988
21224
|
const thinkingParts = messageThinkingParts;
|
|
20989
21225
|
const contentId = `${messageId}:p${partIndex}`;
|
|
20990
21226
|
const existingTagId = resolver.resolve(messageId, "message", contentId, textOrdinal);
|
|
20991
|
-
const
|
|
21227
|
+
const reasoningBytes = textOrdinal === 0 ? getReasoningByteSize(thinkingParts) : 0;
|
|
21228
|
+
const tagId = tagger.assignTag(sessionId, contentId, "message", byteSize(textPart.text), db, reasoningBytes);
|
|
20992
21229
|
if (existingTagId === undefined) {
|
|
20993
21230
|
const sourceContent = stripTagPrefix(textPart.text);
|
|
20994
21231
|
if (sourceContent.trim().length > 0) {
|
|
@@ -21024,7 +21261,8 @@ function tagMessages(sessionId, messages, tagger, db) {
|
|
|
21024
21261
|
if (isToolPartWithOutput(part)) {
|
|
21025
21262
|
const toolPart = part;
|
|
21026
21263
|
const thinkingParts = precedingThinkingParts;
|
|
21027
|
-
const
|
|
21264
|
+
const reasoningBytes = getReasoningByteSize(thinkingParts);
|
|
21265
|
+
const tagId = tagger.assignTag(sessionId, toolPart.callID, "tool", byteSize(toolPart.state.output), db, reasoningBytes);
|
|
21028
21266
|
messageTagNumbers.set(message, Math.max(messageTagNumbers.get(message) ?? 0, tagId));
|
|
21029
21267
|
toolPart.state.output = prependTag(tagId, toolPart.state.output);
|
|
21030
21268
|
toolTagByCallId.set(toolPart.callID, tagId);
|
|
@@ -21676,10 +21914,10 @@ function createNudgePlacementStore(db) {
|
|
|
21676
21914
|
store.set(sessionId, persisted);
|
|
21677
21915
|
return persisted;
|
|
21678
21916
|
},
|
|
21679
|
-
clear(sessionId) {
|
|
21917
|
+
clear(sessionId, options) {
|
|
21680
21918
|
store.delete(sessionId);
|
|
21681
21919
|
missingSessions.add(sessionId);
|
|
21682
|
-
if (db) {
|
|
21920
|
+
if (db && options?.persist !== false) {
|
|
21683
21921
|
clearPersistedNudgePlacement(db, sessionId);
|
|
21684
21922
|
}
|
|
21685
21923
|
}
|
|
@@ -21906,6 +22144,9 @@ function createEventHook(args) {
|
|
|
21906
22144
|
args.commitSeenLastPass?.delete(sessionId);
|
|
21907
22145
|
clearNoteNudgeState(args.db, sessionId);
|
|
21908
22146
|
}
|
|
22147
|
+
if (input.event.type === "message.removed") {
|
|
22148
|
+
return;
|
|
22149
|
+
}
|
|
21909
22150
|
const entry = args.contextUsageMap.get(sessionId);
|
|
21910
22151
|
if (!entry)
|
|
21911
22152
|
return;
|
|
@@ -21998,6 +22239,13 @@ Use \`ctx_note\` for deferred intentions \u2014 things to tackle later, not righ
|
|
|
21998
22239
|
Use \`ctx_memory\` to manage cross-session project memories. Write new memories or delete stale ones. Memories persist across sessions and are automatically injected into new sessions.
|
|
21999
22240
|
Use \`ctx_search\` to search across project memories, session facts, and conversation history from one query.
|
|
22000
22241
|
Use \`ctx_expand\` to decompress a compartment range to see the original conversation transcript. Use \`start\`/\`end\` from \`<compartment start=N end=M>\` attributes. Returns the compacted U:/A: transcript for that message range, capped at ~15K tokens.
|
|
22242
|
+
**Search before asking the user**: If you can't remember or don't know something that might have been discussed before or stored in project memory, use \`ctx_search\` before asking the user. Examples:
|
|
22243
|
+
- Can't remember where a related codebase or dependency lives \u2192 \`ctx_search(query="opencode source code path")\`
|
|
22244
|
+
- Forgot a prior architectural decision or constraint \u2192 \`ctx_search(query="why did we choose SQLite over postgres")\`
|
|
22245
|
+
- Need a config value, API key location, or environment detail \u2192 \`ctx_search(query="embedding provider configuration")\`
|
|
22246
|
+
- Looking for how something was implemented previously \u2192 \`ctx_search(query="how does the dreamer lease work")\`
|
|
22247
|
+
- Want to recall what was decided in an earlier conversation \u2192 \`ctx_search(query="dashboard release signing setup")\`
|
|
22248
|
+
\`ctx_search\` returns ranked results from memories, session facts, and raw message history. Use message ordinals from results with \`ctx_expand\` to retrieve surrounding conversation context.
|
|
22001
22249
|
NEVER drop large ranges blindly (e.g., "1-50"). Review each tag before deciding.
|
|
22002
22250
|
NEVER drop user messages \u2014 they are short and will be summarized by compartmentalization automatically. Dropping them loses context the historian needs.
|
|
22003
22251
|
NEVER drop assistant text messages unless they are exceptionally large. Your conversation messages are lightweight; only large tool outputs are worth dropping.
|
|
@@ -22006,7 +22254,14 @@ var BASE_INTRO_NO_REDUCE = `Messages and tool outputs are tagged with \xA7N\xA7
|
|
|
22006
22254
|
Use \`ctx_note\` for deferred intentions \u2014 things to tackle later, not right now. NOT for task tracking (use todos). Notes survive context compression and you'll be reminded at natural work boundaries (after commits, historian runs, todo completion).
|
|
22007
22255
|
Use \`ctx_memory\` to manage cross-session project memories. Write new memories or delete stale ones. Memories persist across sessions and are automatically injected into new sessions.
|
|
22008
22256
|
Use \`ctx_search\` to search across project memories, session facts, and conversation history from one query.
|
|
22009
|
-
Use \`ctx_expand\` to decompress a compartment range to see the original conversation transcript. Use \`start\`/\`end\` from \`<compartment start=N end=M>\` attributes. Returns the compacted U:/A: transcript for that message range, capped at ~15K tokens
|
|
22257
|
+
Use \`ctx_expand\` to decompress a compartment range to see the original conversation transcript. Use \`start\`/\`end\` from \`<compartment start=N end=M>\` attributes. Returns the compacted U:/A: transcript for that message range, capped at ~15K tokens.
|
|
22258
|
+
**Search before asking the user**: If you can't remember or don't know something that might have been discussed before or stored in project memory, use \`ctx_search\` before asking the user. Examples:
|
|
22259
|
+
- Can't remember where a related codebase or dependency lives \u2192 \`ctx_search(query="opencode source code path")\`
|
|
22260
|
+
- Forgot a prior architectural decision or constraint \u2192 \`ctx_search(query="why did we choose SQLite over postgres")\`
|
|
22261
|
+
- Need a config value, API key location, or environment detail \u2192 \`ctx_search(query="embedding provider configuration")\`
|
|
22262
|
+
- Looking for how something was implemented previously \u2192 \`ctx_search(query="how does the dreamer lease work")\`
|
|
22263
|
+
- Want to recall what was decided in an earlier conversation \u2192 \`ctx_search(query="dashboard release signing setup")\`
|
|
22264
|
+
\`ctx_search\` returns ranked results from memories, session facts, and raw message history. Use message ordinals from results with \`ctx_expand\` to retrieve surrounding conversation context.`;
|
|
22010
22265
|
var SISYPHUS_SECTION = `
|
|
22011
22266
|
### Reduction Triggers
|
|
22012
22267
|
- After collecting background agent results (explore/librarian) \u2014 drop raw outputs once you extracted what you need.
|
|
@@ -23210,7 +23465,8 @@ async function getSemanticScores(args) {
|
|
|
23210
23465
|
function getFtsMatches(args) {
|
|
23211
23466
|
try {
|
|
23212
23467
|
return searchMemoriesFTS(args.db, args.projectPath, args.query, args.limit);
|
|
23213
|
-
} catch {
|
|
23468
|
+
} catch (error48) {
|
|
23469
|
+
log(`[search] FTS query failed for "${args.query}": ${error48 instanceof Error ? error48.message : String(error48)}`);
|
|
23214
23470
|
return [];
|
|
23215
23471
|
}
|
|
23216
23472
|
}
|