@cortexkit/opencode-magic-context 0.22.4 → 0.22.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/agents/magic-context-prompt.d.ts.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/project-security.d.ts +25 -0
- package/dist/config/project-security.d.ts.map +1 -0
- package/dist/config/prune-config-leaf.d.ts +27 -0
- package/dist/config/prune-config-leaf.d.ts.map +1 -0
- package/dist/config/variable.d.ts.map +1 -1
- package/dist/features/magic-context/compartment-storage.d.ts +9 -6
- package/dist/features/magic-context/compartment-storage.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/runner.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/scheduler.d.ts.map +1 -1
- package/dist/features/magic-context/key-files/read-stats.d.ts +0 -2
- package/dist/features/magic-context/key-files/read-stats.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-openai.d.ts.map +1 -1
- package/dist/features/magic-context/memory/embedding-ssrf.d.ts +29 -0
- package/dist/features/magic-context/memory/embedding-ssrf.d.ts.map +1 -0
- package/dist/features/magic-context/memory/project-identity.d.ts +10 -0
- package/dist/features/magic-context/memory/project-identity.d.ts.map +1 -1
- package/dist/features/magic-context/project-docs-hash.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/range-parser.d.ts +6 -0
- package/dist/features/magic-context/range-parser.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 +41 -1
- 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.d.ts +2 -2
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/hooks/auto-update-checker/cache.d.ts.map +1 -1
- package/dist/hooks/auto-update-checker/checker.d.ts.map +1 -1
- package/dist/hooks/auto-update-checker/semver.d.ts +17 -0
- package/dist/hooks/auto-update-checker/semver.d.ts.map +1 -0
- package/dist/hooks/magic-context/command-handler.d.ts +1 -6
- package/dist/hooks/magic-context/command-handler.d.ts.map +1 -1
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts +1 -1
- package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner-validation.d.ts +25 -0
- package/dist/hooks/magic-context/compartment-runner-validation.d.ts.map +1 -1
- package/dist/hooks/magic-context/decay-render.d.ts.map +1 -1
- package/dist/hooks/magic-context/drop-stale-reduce-calls.d.ts +36 -1
- package/dist/hooks/magic-context/drop-stale-reduce-calls.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/reference-retrieval.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/index.js +552 -189
- package/dist/plugin/embedding-bootstrap-helpers.d.ts.map +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
- package/dist/tools/ctx-search/tools.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/shared/rpc-server.ts +18 -2
- package/src/tui/index.tsx +9 -5
package/dist/index.js
CHANGED
|
@@ -287,7 +287,7 @@ __export(exports_util, {
|
|
|
287
287
|
jsonStringifyReplacer: () => jsonStringifyReplacer,
|
|
288
288
|
joinValues: () => joinValues,
|
|
289
289
|
issue: () => issue,
|
|
290
|
-
isPlainObject: () =>
|
|
290
|
+
isPlainObject: () => isPlainObject3,
|
|
291
291
|
isObject: () => isObject,
|
|
292
292
|
hexToUint8Array: () => hexToUint8Array,
|
|
293
293
|
getSizableOrigin: () => getSizableOrigin,
|
|
@@ -452,7 +452,7 @@ function slugify(input) {
|
|
|
452
452
|
function isObject(data) {
|
|
453
453
|
return typeof data === "object" && data !== null && !Array.isArray(data);
|
|
454
454
|
}
|
|
455
|
-
function
|
|
455
|
+
function isPlainObject3(o) {
|
|
456
456
|
if (isObject(o) === false)
|
|
457
457
|
return false;
|
|
458
458
|
const ctor = o.constructor;
|
|
@@ -469,7 +469,7 @@ function isPlainObject(o) {
|
|
|
469
469
|
return true;
|
|
470
470
|
}
|
|
471
471
|
function shallowClone(o) {
|
|
472
|
-
if (
|
|
472
|
+
if (isPlainObject3(o))
|
|
473
473
|
return { ...o };
|
|
474
474
|
if (Array.isArray(o))
|
|
475
475
|
return [...o];
|
|
@@ -609,7 +609,7 @@ function omit(schema, mask) {
|
|
|
609
609
|
return clone(schema, def);
|
|
610
610
|
}
|
|
611
611
|
function extend(schema, shape) {
|
|
612
|
-
if (!
|
|
612
|
+
if (!isPlainObject3(shape)) {
|
|
613
613
|
throw new Error("Invalid input to extend: expected a plain object");
|
|
614
614
|
}
|
|
615
615
|
const checks = schema._zod.def.checks;
|
|
@@ -632,7 +632,7 @@ function extend(schema, shape) {
|
|
|
632
632
|
return clone(schema, def);
|
|
633
633
|
}
|
|
634
634
|
function safeExtend(schema, shape) {
|
|
635
|
-
if (!
|
|
635
|
+
if (!isPlainObject3(shape)) {
|
|
636
636
|
throw new Error("Invalid input to safeExtend: expected a plain object");
|
|
637
637
|
}
|
|
638
638
|
const def = mergeDefs(schema._zod.def, {
|
|
@@ -2115,7 +2115,7 @@ function mergeValues(a, b) {
|
|
|
2115
2115
|
if (a instanceof Date && b instanceof Date && +a === +b) {
|
|
2116
2116
|
return { valid: true, data: a };
|
|
2117
2117
|
}
|
|
2118
|
-
if (
|
|
2118
|
+
if (isPlainObject3(a) && isPlainObject3(b)) {
|
|
2119
2119
|
const bKeys = Object.keys(b);
|
|
2120
2120
|
const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
|
|
2121
2121
|
const newObj = { ...a, ...b };
|
|
@@ -3372,7 +3372,7 @@ var init_schemas = __esm(() => {
|
|
|
3372
3372
|
$ZodType.init(inst, def);
|
|
3373
3373
|
inst._zod.parse = (payload, ctx) => {
|
|
3374
3374
|
const input = payload.value;
|
|
3375
|
-
if (!
|
|
3375
|
+
if (!isPlainObject3(input)) {
|
|
3376
3376
|
payload.issues.push({
|
|
3377
3377
|
expected: "record",
|
|
3378
3378
|
code: "invalid_type",
|
|
@@ -15162,6 +15162,9 @@ function normalizeStoredProjectPath(rawOrStored) {
|
|
|
15162
15162
|
return directoryFallback(rawOrStored);
|
|
15163
15163
|
}
|
|
15164
15164
|
}
|
|
15165
|
+
function storedPathBelongsToIdentity(storedProjectPath, projectIdentity) {
|
|
15166
|
+
return storedProjectPath === projectIdentity || normalizeStoredProjectPath(storedProjectPath) === projectIdentity;
|
|
15167
|
+
}
|
|
15165
15168
|
var GIT_TIMEOUT_MS = 5000, identityCache, directoryFallbackCache, ProjectIdentityError;
|
|
15166
15169
|
var init_project_identity = __esm(() => {
|
|
15167
15170
|
identityCache = new Map;
|
|
@@ -16118,12 +16121,6 @@ function isCompartmentRow(row) {
|
|
|
16118
16121
|
const candidate = row;
|
|
16119
16122
|
return typeof candidate.id === "number" && typeof candidate.session_id === "string" && typeof candidate.sequence === "number" && typeof candidate.start_message === "number" && typeof candidate.end_message === "number" && typeof candidate.start_message_id === "string" && typeof candidate.end_message_id === "string" && typeof candidate.title === "string" && typeof candidate.content === "string" && isStringOrNullish(candidate.p1) && isStringOrNullish(candidate.p2) && isStringOrNullish(candidate.p3) && isStringOrNullish(candidate.p4) && isNumberOrNullish(candidate.importance) && isStringOrNullish(candidate.episode_type) && isNumberOrNullish(candidate.legacy) && typeof candidate.created_at === "number";
|
|
16120
16123
|
}
|
|
16121
|
-
function isSessionFactRow(row) {
|
|
16122
|
-
if (row === null || typeof row !== "object")
|
|
16123
|
-
return false;
|
|
16124
|
-
const candidate = row;
|
|
16125
|
-
return typeof candidate.id === "number" && typeof candidate.session_id === "string" && typeof candidate.category === "string" && typeof candidate.content === "string" && typeof candidate.created_at === "number" && typeof candidate.updated_at === "number";
|
|
16126
|
-
}
|
|
16127
16124
|
function insertCompartmentRows(db, sessionId, compartments, now) {
|
|
16128
16125
|
const stmt = getInsertCompartmentStatement(db);
|
|
16129
16126
|
for (const compartment of compartments) {
|
|
@@ -16152,16 +16149,6 @@ function toCompartment(row) {
|
|
|
16152
16149
|
createdAt: row.created_at
|
|
16153
16150
|
};
|
|
16154
16151
|
}
|
|
16155
|
-
function toSessionFact(row) {
|
|
16156
|
-
return {
|
|
16157
|
-
id: row.id,
|
|
16158
|
-
sessionId: row.session_id,
|
|
16159
|
-
category: row.category,
|
|
16160
|
-
content: row.content,
|
|
16161
|
-
createdAt: row.created_at,
|
|
16162
|
-
updatedAt: row.updated_at
|
|
16163
|
-
};
|
|
16164
|
-
}
|
|
16165
16152
|
function getCompartments(db, sessionId) {
|
|
16166
16153
|
const rows = db.prepare("SELECT * FROM compartments WHERE session_id = ? ORDER BY sequence ASC").all(sessionId).filter(isCompartmentRow);
|
|
16167
16154
|
return rows.map(toCompartment);
|
|
@@ -16170,6 +16157,11 @@ function getLastCompartmentEndMessage(db, sessionId) {
|
|
|
16170
16157
|
const row = db.prepare("SELECT MAX(end_message) as max_end FROM compartments WHERE session_id = ?").get(sessionId);
|
|
16171
16158
|
return row?.max_end ?? -1;
|
|
16172
16159
|
}
|
|
16160
|
+
function getLastCompartmentEndMessageId(db, sessionId) {
|
|
16161
|
+
const row = db.prepare("SELECT end_message_id FROM compartments WHERE session_id = ? ORDER BY sequence DESC LIMIT 1").get(sessionId);
|
|
16162
|
+
const id = row?.end_message_id;
|
|
16163
|
+
return id && id.length > 0 ? id : null;
|
|
16164
|
+
}
|
|
16173
16165
|
function getCompartmentsByEndMessageId(db, sessionId, endMessageId) {
|
|
16174
16166
|
const rows = db.prepare("SELECT * FROM compartments WHERE session_id = ? AND end_message_id = ? ORDER BY sequence ASC").all(sessionId, endMessageId).filter(isCompartmentRow);
|
|
16175
16167
|
return rows.map(toCompartment);
|
|
@@ -16182,10 +16174,6 @@ function appendCompartments(db, sessionId, compartments) {
|
|
|
16182
16174
|
insertCompartmentRows(db, sessionId, compartments, now);
|
|
16183
16175
|
})();
|
|
16184
16176
|
}
|
|
16185
|
-
function getSessionFacts(db, sessionId) {
|
|
16186
|
-
const rows = db.prepare("SELECT * FROM session_facts WHERE session_id = ? ORDER BY category ASC, id ASC").all(sessionId).filter(isSessionFactRow);
|
|
16187
|
-
return rows.map(toSessionFact);
|
|
16188
|
-
}
|
|
16189
16177
|
function buildCompartmentBlock(compartments, facts, memoryBlock, dateRanges) {
|
|
16190
16178
|
const lines = [];
|
|
16191
16179
|
if (memoryBlock) {
|
|
@@ -151079,6 +151067,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
151079
151067
|
ensureColumn(db, "session_meta", "system_prompt_hash", "TEXT DEFAULT ''");
|
|
151080
151068
|
ensureColumn(db, "session_meta", "cleared_reasoning_through_tag", "INTEGER DEFAULT 0");
|
|
151081
151069
|
ensureColumn(db, "session_meta", "stripped_placeholder_ids", "TEXT DEFAULT ''");
|
|
151070
|
+
ensureColumn(db, "session_meta", "stale_reduce_stripped_ids", "TEXT DEFAULT ''");
|
|
151082
151071
|
ensureColumn(db, "compartments", "start_message_id", "TEXT DEFAULT ''");
|
|
151083
151072
|
ensureColumn(db, "compartments", "end_message_id", "TEXT DEFAULT ''");
|
|
151084
151073
|
ensureColumn(db, "memory_embeddings", "model_id", "TEXT");
|
|
@@ -151229,6 +151218,7 @@ function healNullTextColumns(db) {
|
|
|
151229
151218
|
["todo_synthetic_state_json", ""],
|
|
151230
151219
|
["system_prompt_hash", ""],
|
|
151231
151220
|
["stripped_placeholder_ids", ""],
|
|
151221
|
+
["stale_reduce_stripped_ids", ""],
|
|
151232
151222
|
["memory_block_cache", ""],
|
|
151233
151223
|
["memory_block_ids", ""],
|
|
151234
151224
|
["compaction_marker_state", ""],
|
|
@@ -152199,7 +152189,7 @@ var init_migrations = __esm(async () => {
|
|
|
152199
152189
|
|
|
152200
152190
|
// src/features/magic-context/project-docs-hash.ts
|
|
152201
152191
|
import { createHash as createHash4 } from "node:crypto";
|
|
152202
|
-
import { readFileSync as readFileSync5, statSync as statSync2 } from "node:fs";
|
|
152192
|
+
import { lstatSync, readFileSync as readFileSync5, statSync as statSync2 } from "node:fs";
|
|
152203
152193
|
import path4 from "node:path";
|
|
152204
152194
|
function canonicalizeDocContent(raw) {
|
|
152205
152195
|
return raw.replace(/^\uFEFF/, "").replace(/\r\n/g, `
|
|
@@ -152209,9 +152199,10 @@ function canonicalizeDocContent(raw) {
|
|
|
152209
152199
|
}
|
|
152210
152200
|
function fingerprintFile(filePath) {
|
|
152211
152201
|
try {
|
|
152212
|
-
const stat =
|
|
152202
|
+
const stat = lstatSync(filePath);
|
|
152203
|
+
const isReadableDoc = stat.isFile() && stat.size <= MAX_PROJECT_DOC_BYTES;
|
|
152213
152204
|
return {
|
|
152214
|
-
exists:
|
|
152205
|
+
exists: isReadableDoc,
|
|
152215
152206
|
mtimeMs: stat.mtimeMs,
|
|
152216
152207
|
size: stat.size
|
|
152217
152208
|
};
|
|
@@ -152254,6 +152245,16 @@ function readCanonicalPieces(projectDirectory, files) {
|
|
|
152254
152245
|
if (!fingerprint?.exists) {
|
|
152255
152246
|
continue;
|
|
152256
152247
|
}
|
|
152248
|
+
let safeToRead = false;
|
|
152249
|
+
try {
|
|
152250
|
+
const st = lstatSync(filePath);
|
|
152251
|
+
safeToRead = st.isFile() && st.size <= MAX_PROJECT_DOC_BYTES;
|
|
152252
|
+
} catch {
|
|
152253
|
+
safeToRead = false;
|
|
152254
|
+
}
|
|
152255
|
+
if (!safeToRead) {
|
|
152256
|
+
continue;
|
|
152257
|
+
}
|
|
152257
152258
|
const canonicalContent = canonicalizeDocContent(readFileSync5(filePath, "utf8"));
|
|
152258
152259
|
hashPieces.push(`file:${filename}
|
|
152259
152260
|
${canonicalContent}`);
|
|
@@ -152308,10 +152309,11 @@ var PROJECT_DOC_FILES, PROJECT_DOCS_DELIMITER = `
|
|
|
152308
152309
|
|
|
152309
152310
|
---
|
|
152310
152311
|
|
|
152311
|
-
`, docsCache;
|
|
152312
|
+
`, MAX_PROJECT_DOC_BYTES, docsCache;
|
|
152312
152313
|
var init_project_docs_hash = __esm(() => {
|
|
152313
152314
|
init_compartment_storage();
|
|
152314
152315
|
PROJECT_DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
|
|
152316
|
+
MAX_PROJECT_DOC_BYTES = 256 * 1024;
|
|
152315
152317
|
docsCache = new Map;
|
|
152316
152318
|
});
|
|
152317
152319
|
|
|
@@ -152648,7 +152650,8 @@ function casUpdateJsonArrayColumn(db, sessionId, column, validator, mutate, opti
|
|
|
152648
152650
|
}
|
|
152649
152651
|
for (let attempt = 0;attempt < CAS_RETRY_LIMIT; attempt += 1) {
|
|
152650
152652
|
const row = db.prepare(`SELECT ${column} FROM session_meta WHERE session_id = ?`).get(sessionId);
|
|
152651
|
-
const
|
|
152653
|
+
const rawCurrent = row?.[column] ?? null;
|
|
152654
|
+
const currentBlob = rawCurrent ?? "[]";
|
|
152652
152655
|
const current = parseJsonArray(currentBlob, validator);
|
|
152653
152656
|
const next = mutate(current);
|
|
152654
152657
|
if (next === null)
|
|
@@ -152656,7 +152659,7 @@ function casUpdateJsonArrayColumn(db, sessionId, column, validator, mutate, opti
|
|
|
152656
152659
|
const nextBlob = stableStringify(next);
|
|
152657
152660
|
if (nextBlob === currentBlob)
|
|
152658
152661
|
return true;
|
|
152659
|
-
const result = db.prepare(`UPDATE session_meta SET ${column} = ? WHERE session_id = ? AND ${column}
|
|
152662
|
+
const result = db.prepare(`UPDATE session_meta SET ${column} = ? WHERE session_id = ? AND ${column} IS ?`).run(nextBlob, sessionId, rawCurrent);
|
|
152660
152663
|
if (result.changes > 0)
|
|
152661
152664
|
return true;
|
|
152662
152665
|
}
|
|
@@ -152803,14 +152806,16 @@ function getHistorianFailureState(db, sessionId) {
|
|
|
152803
152806
|
};
|
|
152804
152807
|
}
|
|
152805
152808
|
function incrementHistorianFailure(db, sessionId, error51) {
|
|
152809
|
+
let nextCount = 1;
|
|
152806
152810
|
db.transaction(() => {
|
|
152807
152811
|
ensureSessionMetaRow(db, sessionId);
|
|
152808
152812
|
const current = getHistorianFailureState(db, sessionId);
|
|
152809
|
-
|
|
152813
|
+
nextCount = current.failureCount + 1;
|
|
152810
152814
|
db.prepare("UPDATE session_meta SET historian_failure_count = ?, historian_last_error = ?, historian_last_failure_at = ? WHERE session_id = ?").run(nextCount, error51, Date.now(), sessionId);
|
|
152811
152815
|
const reason = error51.replace(/\s+/g, " ").trim().slice(0, 300);
|
|
152812
152816
|
sessionLog(sessionId, `historian failure recorded: count=${nextCount} reason="${reason}"`);
|
|
152813
152817
|
})();
|
|
152818
|
+
return nextCount;
|
|
152814
152819
|
}
|
|
152815
152820
|
function clearHistorianFailureState(db, sessionId) {
|
|
152816
152821
|
db.transaction(() => {
|
|
@@ -152887,19 +152892,78 @@ function getStrippedPlaceholderIds(db, sessionId) {
|
|
|
152887
152892
|
} catch {}
|
|
152888
152893
|
return new Set;
|
|
152889
152894
|
}
|
|
152890
|
-
function
|
|
152895
|
+
function applyStrippedPlaceholderDelta(db, sessionId, delta) {
|
|
152896
|
+
const add = delta.add ? [...delta.add] : [];
|
|
152897
|
+
const remove = delta.remove ? [...delta.remove] : [];
|
|
152898
|
+
if (add.length === 0 && remove.length === 0)
|
|
152899
|
+
return true;
|
|
152891
152900
|
ensureSessionMetaRow(db, sessionId);
|
|
152892
|
-
|
|
152893
|
-
|
|
152901
|
+
for (let attempt = 0;attempt < CAS_RETRY_LIMIT; attempt += 1) {
|
|
152902
|
+
const row = db.prepare("SELECT stripped_placeholder_ids FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
152903
|
+
const rawStored = row ? row.stripped_placeholder_ids ?? null : null;
|
|
152904
|
+
const current = new Set(parseStrippedBlob(rawStored));
|
|
152905
|
+
for (const id of add)
|
|
152906
|
+
current.add(id);
|
|
152907
|
+
for (const id of remove)
|
|
152908
|
+
current.delete(id);
|
|
152909
|
+
const nextBlob = current.size > 0 ? JSON.stringify([...current]) : "";
|
|
152910
|
+
if (nextBlob === (rawStored ?? ""))
|
|
152911
|
+
return true;
|
|
152912
|
+
const result = db.prepare("UPDATE session_meta SET stripped_placeholder_ids = ? WHERE session_id = ? AND stripped_placeholder_ids IS ?").run(nextBlob, sessionId, rawStored);
|
|
152913
|
+
if (result.changes > 0)
|
|
152914
|
+
return true;
|
|
152915
|
+
}
|
|
152916
|
+
sessionLog(sessionId, `stripped_placeholder_ids CAS: ${CAS_RETRY_LIMIT} retries exhausted`);
|
|
152917
|
+
return false;
|
|
152918
|
+
}
|
|
152919
|
+
function parseStrippedBlob(raw) {
|
|
152920
|
+
if (!raw || raw.length === 0)
|
|
152921
|
+
return [];
|
|
152922
|
+
try {
|
|
152923
|
+
const parsed = JSON.parse(raw);
|
|
152924
|
+
if (Array.isArray(parsed))
|
|
152925
|
+
return parsed.filter((v) => typeof v === "string");
|
|
152926
|
+
} catch {}
|
|
152927
|
+
return [];
|
|
152894
152928
|
}
|
|
152895
152929
|
function removeStrippedPlaceholderId(db, sessionId, messageId) {
|
|
152896
|
-
const
|
|
152897
|
-
if (!
|
|
152930
|
+
const before = getStrippedPlaceholderIds(db, sessionId);
|
|
152931
|
+
if (!before.has(messageId)) {
|
|
152898
152932
|
return false;
|
|
152899
152933
|
}
|
|
152900
|
-
|
|
152934
|
+
applyStrippedPlaceholderDelta(db, sessionId, { remove: [messageId] });
|
|
152901
152935
|
return true;
|
|
152902
152936
|
}
|
|
152937
|
+
function getStaleReduceStrippedIds(db, sessionId) {
|
|
152938
|
+
const row = db.prepare("SELECT stale_reduce_stripped_ids FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
152939
|
+
return new Set(parseStrippedBlob(row?.stale_reduce_stripped_ids));
|
|
152940
|
+
}
|
|
152941
|
+
function addStaleReduceStrippedIds(db, sessionId, ids) {
|
|
152942
|
+
const add = [...ids];
|
|
152943
|
+
if (add.length === 0)
|
|
152944
|
+
return true;
|
|
152945
|
+
ensureSessionMetaRow(db, sessionId);
|
|
152946
|
+
for (let attempt = 0;attempt < CAS_RETRY_LIMIT; attempt += 1) {
|
|
152947
|
+
const row = db.prepare("SELECT stale_reduce_stripped_ids FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
152948
|
+
const rawStored = row ? row.stale_reduce_stripped_ids ?? null : null;
|
|
152949
|
+
const current = new Set(parseStrippedBlob(rawStored));
|
|
152950
|
+
let changed = false;
|
|
152951
|
+
for (const id of add) {
|
|
152952
|
+
if (!current.has(id)) {
|
|
152953
|
+
current.add(id);
|
|
152954
|
+
changed = true;
|
|
152955
|
+
}
|
|
152956
|
+
}
|
|
152957
|
+
if (!changed)
|
|
152958
|
+
return true;
|
|
152959
|
+
const nextBlob = JSON.stringify([...current]);
|
|
152960
|
+
const result = db.prepare("UPDATE session_meta SET stale_reduce_stripped_ids = ? WHERE session_id = ? AND stale_reduce_stripped_ids IS ?").run(nextBlob, sessionId, rawStored);
|
|
152961
|
+
if (result.changes > 0)
|
|
152962
|
+
return true;
|
|
152963
|
+
}
|
|
152964
|
+
sessionLog(sessionId, `stale_reduce_stripped_ids CAS: ${CAS_RETRY_LIMIT} retries exhausted`);
|
|
152965
|
+
return false;
|
|
152966
|
+
}
|
|
152903
152967
|
function isPendingCompactionMarker(value) {
|
|
152904
152968
|
return typeof value === "object" && value !== null && typeof value.ordinal === "number" && typeof value.endMessageId === "string" && typeof value.publishedAt === "number";
|
|
152905
152969
|
}
|
|
@@ -163811,7 +163875,7 @@ function cosineSimilarity(a, b) {
|
|
|
163811
163875
|
}
|
|
163812
163876
|
|
|
163813
163877
|
// src/features/magic-context/memory/embedding-identity.ts
|
|
163814
|
-
function
|
|
163878
|
+
function normalizeEndpoint2(endpoint) {
|
|
163815
163879
|
return endpoint?.trim().replace(/\/+$/, "") ?? "";
|
|
163816
163880
|
}
|
|
163817
163881
|
function getEmbeddingProviderIdentity(config2) {
|
|
@@ -163821,7 +163885,7 @@ function getEmbeddingProviderIdentity(config2) {
|
|
|
163821
163885
|
const identityInput = config2.provider === "openai-compatible" ? {
|
|
163822
163886
|
provider: "openai-compatible",
|
|
163823
163887
|
model: config2.model.trim(),
|
|
163824
|
-
endpoint:
|
|
163888
|
+
endpoint: normalizeEndpoint2(config2.endpoint),
|
|
163825
163889
|
apiKeyPresent: Boolean(config2.api_key?.trim()),
|
|
163826
163890
|
inputType: config2.input_type?.trim() || ""
|
|
163827
163891
|
} : {
|
|
@@ -164182,8 +164246,64 @@ var init_embedding_local = __esm(() => {
|
|
|
164182
164246
|
MAX_LOCK_WAIT_MS = 5 * 60000;
|
|
164183
164247
|
});
|
|
164184
164248
|
|
|
164249
|
+
// src/features/magic-context/memory/embedding-ssrf.ts
|
|
164250
|
+
function isLinkLocalIpv4(host) {
|
|
164251
|
+
return /^169\.254\.\d{1,3}\.\d{1,3}$/.test(host);
|
|
164252
|
+
}
|
|
164253
|
+
function ipv4FromMappedIpv6(host) {
|
|
164254
|
+
const m = /^::ffff:(.+)$/.exec(host);
|
|
164255
|
+
if (!m)
|
|
164256
|
+
return null;
|
|
164257
|
+
const tail = m[1];
|
|
164258
|
+
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(tail))
|
|
164259
|
+
return tail;
|
|
164260
|
+
const hex3 = /^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(tail);
|
|
164261
|
+
if (hex3) {
|
|
164262
|
+
const hi = Number.parseInt(hex3[1], 16);
|
|
164263
|
+
const lo = Number.parseInt(hex3[2], 16);
|
|
164264
|
+
if (Number.isNaN(hi) || Number.isNaN(lo))
|
|
164265
|
+
return null;
|
|
164266
|
+
return `${hi >> 8 & 255}.${hi & 255}.${lo >> 8 & 255}.${lo & 255}`;
|
|
164267
|
+
}
|
|
164268
|
+
return null;
|
|
164269
|
+
}
|
|
164270
|
+
function blockedEmbeddingEndpointReason(endpoint) {
|
|
164271
|
+
const trimmed = endpoint.trim();
|
|
164272
|
+
if (trimmed.length === 0)
|
|
164273
|
+
return null;
|
|
164274
|
+
let url2;
|
|
164275
|
+
try {
|
|
164276
|
+
url2 = new URL(trimmed);
|
|
164277
|
+
} catch {
|
|
164278
|
+
return `embedding endpoint is not a valid URL: ${trimmed}`;
|
|
164279
|
+
}
|
|
164280
|
+
const host = url2.hostname.toLowerCase().replace(/^\[/, "").replace(/\]$/, "");
|
|
164281
|
+
if (METADATA_HOSTNAMES.has(host)) {
|
|
164282
|
+
return `embedding endpoint host ${host} is a cloud metadata service (blocked)`;
|
|
164283
|
+
}
|
|
164284
|
+
if (IPV6_METADATA_HOSTS.has(host)) {
|
|
164285
|
+
return `embedding endpoint host ${host} is the AWS IPv6 metadata service (blocked)`;
|
|
164286
|
+
}
|
|
164287
|
+
if (isLinkLocalIpv4(host)) {
|
|
164288
|
+
return `embedding endpoint host ${host} is link-local / cloud metadata (blocked)`;
|
|
164289
|
+
}
|
|
164290
|
+
const mappedV4 = ipv4FromMappedIpv6(host);
|
|
164291
|
+
if (mappedV4 && isLinkLocalIpv4(mappedV4)) {
|
|
164292
|
+
return `embedding endpoint host ${host} (IPv4-mapped ${mappedV4}) is link-local / cloud metadata (blocked)`;
|
|
164293
|
+
}
|
|
164294
|
+
if (host.startsWith("fe80:")) {
|
|
164295
|
+
return `embedding endpoint host ${host} is link-local / cloud metadata (blocked)`;
|
|
164296
|
+
}
|
|
164297
|
+
return null;
|
|
164298
|
+
}
|
|
164299
|
+
var METADATA_HOSTNAMES, IPV6_METADATA_HOSTS;
|
|
164300
|
+
var init_embedding_ssrf = __esm(() => {
|
|
164301
|
+
METADATA_HOSTNAMES = new Set(["metadata.google.internal", "metadata.goog"]);
|
|
164302
|
+
IPV6_METADATA_HOSTS = new Set(["fd00:ec2::254"]);
|
|
164303
|
+
});
|
|
164304
|
+
|
|
164185
164305
|
// src/features/magic-context/memory/embedding-openai.ts
|
|
164186
|
-
function
|
|
164306
|
+
function normalizeEndpoint3(endpoint) {
|
|
164187
164307
|
return endpoint?.trim().replace(/\/+$/, "") ?? "";
|
|
164188
164308
|
}
|
|
164189
164309
|
|
|
@@ -164200,7 +164320,7 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
164200
164320
|
openLogged = false;
|
|
164201
164321
|
halfOpenProbeInFlight = false;
|
|
164202
164322
|
constructor(options) {
|
|
164203
|
-
this.endpoint =
|
|
164323
|
+
this.endpoint = normalizeEndpoint3(options.endpoint);
|
|
164204
164324
|
this.model = options.model?.trim() ?? "";
|
|
164205
164325
|
this.apiKey = options.apiKey?.trim() ?? "";
|
|
164206
164326
|
this.inputType = options.inputType?.trim() ?? "";
|
|
@@ -164220,6 +164340,12 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
164220
164340
|
this.initialized = false;
|
|
164221
164341
|
return false;
|
|
164222
164342
|
}
|
|
164343
|
+
const blockedReason = blockedEmbeddingEndpointReason(this.endpoint);
|
|
164344
|
+
if (blockedReason) {
|
|
164345
|
+
log(`[magic-context] embedding endpoint blocked: ${blockedReason}`);
|
|
164346
|
+
this.initialized = false;
|
|
164347
|
+
return false;
|
|
164348
|
+
}
|
|
164223
164349
|
this.initialized = true;
|
|
164224
164350
|
return true;
|
|
164225
164351
|
}
|
|
@@ -164265,6 +164391,7 @@ class OpenAICompatibleEmbeddingProvider {
|
|
|
164265
164391
|
...this.inputType ? { input_type: this.inputType } : {},
|
|
164266
164392
|
...this.truncate ? { truncate: this.truncate } : {}
|
|
164267
164393
|
}),
|
|
164394
|
+
redirect: "error",
|
|
164268
164395
|
signal: internalController.signal
|
|
164269
164396
|
});
|
|
164270
164397
|
if (!response.ok) {
|
|
@@ -164395,6 +164522,7 @@ var FAILURE_THRESHOLD = 3, FAILURE_WINDOW_MS = 60000, OPEN_DURATION_MS, FETCH_TI
|
|
|
164395
164522
|
var init_embedding_openai = __esm(() => {
|
|
164396
164523
|
init_logger();
|
|
164397
164524
|
init_embedding_identity();
|
|
164525
|
+
init_embedding_ssrf();
|
|
164398
164526
|
OPEN_DURATION_MS = 5 * 60000;
|
|
164399
164527
|
});
|
|
164400
164528
|
|
|
@@ -164635,11 +164763,15 @@ function resolveEmbeddingConfig(config2) {
|
|
|
164635
164763
|
}
|
|
164636
164764
|
if (config2.provider === "openai-compatible") {
|
|
164637
164765
|
const apiKey = config2.api_key?.trim();
|
|
164766
|
+
const inputType = config2.input_type?.trim();
|
|
164767
|
+
const truncate = config2.truncate?.trim();
|
|
164638
164768
|
return {
|
|
164639
164769
|
provider: "openai-compatible",
|
|
164640
164770
|
model: config2.model.trim(),
|
|
164641
164771
|
endpoint: config2.endpoint.trim(),
|
|
164642
|
-
...apiKey ? { api_key: apiKey } : {}
|
|
164772
|
+
...apiKey ? { api_key: apiKey } : {},
|
|
164773
|
+
...inputType ? { input_type: inputType } : {},
|
|
164774
|
+
...truncate ? { truncate } : {}
|
|
164643
164775
|
};
|
|
164644
164776
|
}
|
|
164645
164777
|
return { provider: "off" };
|
|
@@ -164655,7 +164787,9 @@ function createProvider(config2) {
|
|
|
164655
164787
|
return new OpenAICompatibleEmbeddingProvider({
|
|
164656
164788
|
endpoint: config2.endpoint,
|
|
164657
164789
|
model: config2.model,
|
|
164658
|
-
apiKey: config2.api_key
|
|
164790
|
+
apiKey: config2.api_key,
|
|
164791
|
+
inputType: config2.input_type,
|
|
164792
|
+
truncate: config2.truncate
|
|
164659
164793
|
});
|
|
164660
164794
|
}
|
|
164661
164795
|
return new LocalEmbeddingProvider(config2.model);
|
|
@@ -165433,15 +165567,15 @@ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEn
|
|
|
165433
165567
|
const existing = getPersistedCompactionMarkerState(db, sessionId);
|
|
165434
165568
|
if (existing) {
|
|
165435
165569
|
if (existing.boundaryOrdinal === lastCompartmentEnd) {
|
|
165436
|
-
return;
|
|
165570
|
+
return true;
|
|
165437
165571
|
}
|
|
165438
|
-
|
|
165439
|
-
|
|
165440
|
-
|
|
165441
|
-
|
|
165442
|
-
} catch (error51) {
|
|
165443
|
-
sessionLog(sessionId, `compaction-marker: failed to remove old boundary at ordinal ${existing.boundaryOrdinal}, proceeding with new injection:`, error51);
|
|
165572
|
+
const removed = removeCompactionMarker(existing);
|
|
165573
|
+
if (!removed) {
|
|
165574
|
+
sessionLog(sessionId, `compaction-marker: failed to remove old boundary at ordinal ${existing.boundaryOrdinal}; preserving persisted state for retry (not injecting new marker this pass)`);
|
|
165575
|
+
return false;
|
|
165444
165576
|
}
|
|
165577
|
+
setPersistedCompactionMarkerState(db, sessionId, null);
|
|
165578
|
+
sessionLog(sessionId, `compaction-marker: removed old boundary at ordinal ${existing.boundaryOrdinal}, moving to ${lastCompartmentEnd}`);
|
|
165445
165579
|
}
|
|
165446
165580
|
const result = injectCompactionMarker({
|
|
165447
165581
|
sessionId,
|
|
@@ -165455,7 +165589,9 @@ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEn
|
|
|
165455
165589
|
boundaryOrdinal: lastCompartmentEnd
|
|
165456
165590
|
});
|
|
165457
165591
|
sessionLog(sessionId, `compaction-marker: injected at ordinal ${lastCompartmentEnd}, boundary user msg ${result.boundaryMessageId}`);
|
|
165592
|
+
return true;
|
|
165458
165593
|
}
|
|
165594
|
+
return false;
|
|
165459
165595
|
}
|
|
165460
165596
|
function removeCompactionMarkerForSession(db, sessionId) {
|
|
165461
165597
|
const existing = getPersistedCompactionMarkerState(db, sessionId);
|
|
@@ -165490,10 +165626,10 @@ function checkCompactionMarkerConsistency(db) {
|
|
|
165490
165626
|
const state = getPersistedCompactionMarkerState(db, row.session_id);
|
|
165491
165627
|
if (!state)
|
|
165492
165628
|
continue;
|
|
165493
|
-
const boundaryExists = checkMessage.get(state.boundaryMessageId)
|
|
165494
|
-
const summaryMessageExists = checkMessage.get(state.summaryMessageId)
|
|
165495
|
-
const compactionPartExists = checkPart.get(state.compactionPartId)
|
|
165496
|
-
const summaryPartExists = checkPart.get(state.summaryPartId)
|
|
165629
|
+
const boundaryExists = checkMessage.get(state.boundaryMessageId) != null;
|
|
165630
|
+
const summaryMessageExists = checkMessage.get(state.summaryMessageId) != null;
|
|
165631
|
+
const compactionPartExists = checkPart.get(state.compactionPartId) != null;
|
|
165632
|
+
const summaryPartExists = checkPart.get(state.summaryPartId) != null;
|
|
165497
165633
|
const allPresent = boundaryExists && summaryMessageExists && compactionPartExists && summaryPartExists;
|
|
165498
165634
|
if (allPresent)
|
|
165499
165635
|
continue;
|
|
@@ -165753,6 +165889,26 @@ function validateHistorianOutput(text, _sessionId, chunk, _priorCompartments, se
|
|
|
165753
165889
|
events: parsed.events.length > 0 ? parsed.events : undefined
|
|
165754
165890
|
};
|
|
165755
165891
|
}
|
|
165892
|
+
function buildHistorianFailureNotice(failureCount, lastError) {
|
|
165893
|
+
if (failureCount >= HISTORIAN_PERSISTENT_FAILURE_THRESHOLD) {
|
|
165894
|
+
return [
|
|
165895
|
+
"## Magic Context — history comparting needs attention",
|
|
165896
|
+
"",
|
|
165897
|
+
`Magic Context has been unable to compart this session's history ${failureCount} times in a row. This usually means the configured historian model is misconfigured or unreachable (Magic Context already retried every fallback model automatically).`,
|
|
165898
|
+
"",
|
|
165899
|
+
`Last error: ${lastError}`,
|
|
165900
|
+
"",
|
|
165901
|
+
"Check your historian model in magic-context.jsonc, then restart. Your conversation keeps working normally in the meantime — this only affects how older history is summarized."
|
|
165902
|
+
].join(`
|
|
165903
|
+
`);
|
|
165904
|
+
}
|
|
165905
|
+
return [
|
|
165906
|
+
"## Magic Context",
|
|
165907
|
+
"",
|
|
165908
|
+
"Hit a transient issue comparting history this turn — Magic Context will retry automatically on the next turn. Nothing is lost and your conversation continues normally. You'll only be alerted again if this keeps happening."
|
|
165909
|
+
].join(`
|
|
165910
|
+
`);
|
|
165911
|
+
}
|
|
165756
165912
|
function buildHistorianRepairPrompt(originalPrompt, previousOutput, validationError) {
|
|
165757
165913
|
return [
|
|
165758
165914
|
originalPrompt,
|
|
@@ -165841,7 +165997,7 @@ function getReducedRecompTokenBudget(currentBudget) {
|
|
|
165841
165997
|
const reducedBudget = Math.max(MIN_RECOMP_CHUNK_TOKEN_BUDGET, Math.floor(currentBudget / 2));
|
|
165842
165998
|
return reducedBudget < currentBudget ? reducedBudget : null;
|
|
165843
165999
|
}
|
|
165844
|
-
var MIN_RECOMP_CHUNK_TOKEN_BUDGET = 20;
|
|
166000
|
+
var MIN_RECOMP_CHUNK_TOKEN_BUDGET = 20, HISTORIAN_PERSISTENT_FAILURE_THRESHOLD = 3;
|
|
165845
166001
|
var init_compartment_runner_validation = __esm(async () => {
|
|
165846
166002
|
init_compartment_parser();
|
|
165847
166003
|
await init_compartment_runner_mapping();
|
|
@@ -166544,6 +166700,9 @@ function escapeXmlAttr2(s) {
|
|
|
166544
166700
|
function escapeXmlContent2(s) {
|
|
166545
166701
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
166546
166702
|
}
|
|
166703
|
+
function isTieredRow(c) {
|
|
166704
|
+
return typeof c.p1 === "string" && c.p1.length > 0;
|
|
166705
|
+
}
|
|
166547
166706
|
function tierBody(c, tier2) {
|
|
166548
166707
|
const tiers = [c.p1, c.p2, c.p3, c.p4];
|
|
166549
166708
|
const requested = tiers[tier2 - 1];
|
|
@@ -166573,12 +166732,13 @@ function renderOneCompartment(c, tier2) {
|
|
|
166573
166732
|
const baseAttrs = `start="${c.startMessage}" end="${c.endMessage}" title="${escapeXmlAttr2(c.title)}"`;
|
|
166574
166733
|
if (tier2 >= 5)
|
|
166575
166734
|
return "";
|
|
166576
|
-
if (c.legacy === 1) {
|
|
166577
|
-
|
|
166735
|
+
if (c.legacy === 1 || !isTieredRow(c)) {
|
|
166736
|
+
const flat = (c.content ?? "").trim();
|
|
166737
|
+
if (tier2 >= 4 || flat.length === 0)
|
|
166578
166738
|
return `<compartment ${baseAttrs} />`;
|
|
166579
166739
|
return [
|
|
166580
166740
|
`<compartment ${baseAttrs}>`,
|
|
166581
|
-
escapeXmlContent2(legacyBodyForTier(
|
|
166741
|
+
escapeXmlContent2(legacyBodyForTier(flat, tier2)),
|
|
166582
166742
|
"</compartment>"
|
|
166583
166743
|
].join(`
|
|
166584
166744
|
`);
|
|
@@ -167041,7 +167201,18 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
|
|
|
167041
167201
|
const lastCompartment = compartments[compartments.length - 1];
|
|
167042
167202
|
const lastEnd = lastCompartment.endMessage;
|
|
167043
167203
|
const lastEndMessageId = lastCompartment.endMessageId;
|
|
167044
|
-
|
|
167204
|
+
let trimEndMessageId = lastEndMessageId;
|
|
167205
|
+
if (!isCacheBusting) {
|
|
167206
|
+
const baseline = readCachedBaselineState(db, sessionId);
|
|
167207
|
+
if (baseline.hasCachedM0) {
|
|
167208
|
+
if (baseline.boundary) {
|
|
167209
|
+
trimEndMessageId = baseline.boundary;
|
|
167210
|
+
} else {
|
|
167211
|
+
trimEndMessageId = "";
|
|
167212
|
+
}
|
|
167213
|
+
}
|
|
167214
|
+
}
|
|
167215
|
+
if (trimEndMessageId.length === 0) {
|
|
167045
167216
|
sessionLog(sessionId, "injecting legacy compartments without visible-prefix trimming because latest stored compartment has no end_message_id", {
|
|
167046
167217
|
compartmentCount: compartments.length,
|
|
167047
167218
|
compartmentEndMessage: lastEnd
|
|
@@ -167060,18 +167231,18 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
|
|
|
167060
167231
|
return result2;
|
|
167061
167232
|
}
|
|
167062
167233
|
let skippedVisibleMessages = 0;
|
|
167063
|
-
const cutoffIndex = messages.findIndex((message) => message.info.id ===
|
|
167234
|
+
const cutoffIndex = messages.findIndex((message) => message.info.id === trimEndMessageId);
|
|
167064
167235
|
if (cutoffIndex >= 0) {
|
|
167065
167236
|
skippedVisibleMessages = cutoffIndex + 1;
|
|
167066
167237
|
const remaining = messages.slice(cutoffIndex + 1);
|
|
167067
167238
|
messages.splice(0, messages.length, ...remaining);
|
|
167068
167239
|
} else {
|
|
167069
|
-
sessionLog(sessionId, `compartment injection entering degraded mode: boundary ${
|
|
167240
|
+
sessionLog(sessionId, `compartment injection entering degraded mode: boundary ${trimEndMessageId} not in visible messages`);
|
|
167070
167241
|
}
|
|
167071
167242
|
const result = {
|
|
167072
167243
|
block,
|
|
167073
167244
|
compartmentEndMessage: lastEnd,
|
|
167074
|
-
compartmentEndMessageId: cutoffIndex >= 0 ?
|
|
167245
|
+
compartmentEndMessageId: cutoffIndex >= 0 ? trimEndMessageId : null,
|
|
167075
167246
|
compartmentCount: compartments.length,
|
|
167076
167247
|
skippedVisibleMessages,
|
|
167077
167248
|
factCount: facts.length,
|
|
@@ -167081,6 +167252,14 @@ function prepareCompartmentInjection(db, sessionId, messages, isCacheBusting, pr
|
|
|
167081
167252
|
injectionCache.set(sessionId, { kind: "populated", injection: result });
|
|
167082
167253
|
return result;
|
|
167083
167254
|
}
|
|
167255
|
+
function readCachedBaselineState(db, sessionId) {
|
|
167256
|
+
const row = db.prepare("SELECT cached_m0_bytes AS m0, cached_m0_last_baseline_end_message_id AS boundary FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
167257
|
+
const boundary = row?.boundary;
|
|
167258
|
+
return {
|
|
167259
|
+
hasCachedM0: row?.m0 != null,
|
|
167260
|
+
boundary: boundary && boundary.length > 0 ? boundary : null
|
|
167261
|
+
};
|
|
167262
|
+
}
|
|
167084
167263
|
function renderCompartmentInjection(sessionId, messages, prepared) {
|
|
167085
167264
|
const historyBlock = `<session-history>
|
|
167086
167265
|
${prepared.block}
|
|
@@ -167124,6 +167303,10 @@ function findFirstTextPart(parts) {
|
|
|
167124
167303
|
function isDroppedPlaceholder(text) {
|
|
167125
167304
|
return /^\[dropped §\d+§\]$/.test(text.trim());
|
|
167126
167305
|
}
|
|
167306
|
+
function lastCompartmentBoundaryId(compartments) {
|
|
167307
|
+
const last = compartments.at(-1);
|
|
167308
|
+
return last?.endMessageId && last.endMessageId.length > 0 ? last.endMessageId : null;
|
|
167309
|
+
}
|
|
167127
167310
|
function cachedStatement(cache, db, sql) {
|
|
167128
167311
|
let stmt = cache.get(db);
|
|
167129
167312
|
if (!stmt) {
|
|
@@ -167549,6 +167732,8 @@ function materializeM0(options) {
|
|
|
167549
167732
|
modelKey: snapshotMarkers.modelKey
|
|
167550
167733
|
});
|
|
167551
167734
|
options.db.prepare("UPDATE session_meta SET memory_block_count = ?, memory_block_ids = ? WHERE session_id = ?").run(renderedMemoryIds.length, JSON.stringify(renderedMemoryIds), options.sessionId);
|
|
167735
|
+
const baselineEndMessageId = lastCompartmentBoundaryId(compartments);
|
|
167736
|
+
options.db.prepare("UPDATE session_meta SET cached_m0_last_baseline_end_message_id = ? WHERE session_id = ?").run(baselineEndMessageId, options.sessionId);
|
|
167552
167737
|
options.db.exec("COMMIT");
|
|
167553
167738
|
} catch (error51) {
|
|
167554
167739
|
try {
|
|
@@ -167795,7 +167980,8 @@ function softRefreshCachedM1(options) {
|
|
|
167795
167980
|
const renderedMemoryIds = parseMemoryBlockIds(row.memory_block_ids);
|
|
167796
167981
|
const rendered = renderM1WithMetadata({ ...options, preRenderedKeyFilesBlock }, markers, renderedMemoryIds);
|
|
167797
167982
|
const m1Bytes = Buffer4.from(rendered.text, "utf8");
|
|
167798
|
-
|
|
167983
|
+
const baselineEndMessageId = getLastCompartmentEndMessageId(options.db, options.sessionId);
|
|
167984
|
+
options.db.prepare("UPDATE session_meta SET cached_m1_bytes = ?, cached_m0_last_baseline_end_message_id = ? WHERE session_id = ?").run(m1Bytes, baselineEndMessageId, options.sessionId);
|
|
167799
167985
|
options.db.exec("COMMIT");
|
|
167800
167986
|
options.state.cachedM1Bytes = m1Bytes;
|
|
167801
167987
|
options.state.snapshotMarkers = markers;
|
|
@@ -167942,11 +168128,15 @@ function injectM0M1(options) {
|
|
|
167942
168128
|
} else {
|
|
167943
168129
|
m1Text = replayCachedM1(options.state);
|
|
167944
168130
|
}
|
|
167945
|
-
const
|
|
168131
|
+
const M0_DRIFT_RATIO_FLOOR_TOKENS = 500;
|
|
168132
|
+
const M1_DRIFT_RATIO = 0.15;
|
|
167946
168133
|
const M1_ABSOLUTE_CAP_RATIO = 0.2;
|
|
167947
168134
|
const m1AbsoluteBudget = (options.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS) * M1_ABSOLUTE_CAP_RATIO;
|
|
167948
|
-
const
|
|
167949
|
-
|
|
168135
|
+
const m1HasContent = m1Text !== M1_EMPTY_PLACEHOLDER;
|
|
168136
|
+
const m1Tokens = m1HasContent ? estimateTokens(m1Text) : 0;
|
|
168137
|
+
const m0Tokens = estimateTokens(m0Text);
|
|
168138
|
+
const m1OverAbsoluteCap = m1HasContent && m1Tokens > m1AbsoluteBudget;
|
|
168139
|
+
if (!rematerialized && !contentionExhausted && m1Recomputed && options.isCacheBustingPass && (memoryUpdateCount > 40 || m1OverAbsoluteCap || m1HasContent && m0Tokens >= M0_DRIFT_RATIO_FLOOR_TOKENS && m1Tokens > m0Tokens * M1_DRIFT_RATIO)) {
|
|
167950
168140
|
try {
|
|
167951
168141
|
const refolded = materializeWithRetry(options);
|
|
167952
168142
|
applyMarkersToState(options.state, refolded.m0Bytes, refolded.snapshotMarkers, refolded.m1Bytes);
|
|
@@ -169857,7 +170047,7 @@ ${body}
|
|
|
169857
170047
|
function renderSessionRefCompartment(c) {
|
|
169858
170048
|
const importance = c.importance ?? 50;
|
|
169859
170049
|
const attrs = `start="${c.startMessage}" end="${c.endMessage}" title="${escapeXmlAttr(c.title)}"` + (c.episodeType ? ` episode_type="${escapeXmlAttr(c.episodeType)}"` : "") + ` importance="${importance}"`;
|
|
169860
|
-
if (c.p1
|
|
170050
|
+
if (typeof c.p1 === "string" && c.p1.length > 0) {
|
|
169861
170051
|
const p4 = c.p4 && c.p4.length > 0 ? `<p4>
|
|
169862
170052
|
${escapeXmlContent(c.p4)}
|
|
169863
170053
|
</p4>` : "<p4/>";
|
|
@@ -170108,13 +170298,9 @@ async function runCompartmentAgent(deps) {
|
|
|
170108
170298
|
const existingValidationError = validateStoredCompartments(priorCompartments);
|
|
170109
170299
|
if (existingValidationError) {
|
|
170110
170300
|
sessionLog(sessionId, `historian failure: source=existing-validation reason="${existingValidationError}"`);
|
|
170111
|
-
incrementHistorianFailure(db, sessionId, existingValidationError);
|
|
170301
|
+
const failCount = incrementHistorianFailure(db, sessionId, existingValidationError);
|
|
170112
170302
|
telemetry.failureReason = `existing-validation: ${existingValidationError}`;
|
|
170113
|
-
await notifyHistorianIssue(
|
|
170114
|
-
|
|
170115
|
-
Historian skipped this session because existing stored compartments are invalid: ${existingValidationError}
|
|
170116
|
-
|
|
170117
|
-
No new compartments or facts were written. Rebuild or clear the broken compartments before continuing.`);
|
|
170303
|
+
await notifyHistorianIssue(buildHistorianFailureNotice(failCount, existingValidationError));
|
|
170118
170304
|
return;
|
|
170119
170305
|
}
|
|
170120
170306
|
const offset = priorCompartments.length > 0 ? priorCompartments[priorCompartments.length - 1].endMessage + 1 : 1;
|
|
@@ -170140,12 +170326,8 @@ No new compartments or facts were written. Rebuild or clear the broken compartme
|
|
|
170140
170326
|
if (chunkCoverageError) {
|
|
170141
170327
|
telemetry.failureReason = `chunk-coverage: ${chunkCoverageError}`;
|
|
170142
170328
|
sessionLog(sessionId, `historian failure: source=chunk-coverage reason="${chunkCoverageError}" chunkRange=${chunk.startIndex}-${chunk.endIndex}`);
|
|
170143
|
-
incrementHistorianFailure(db, sessionId, chunkCoverageError);
|
|
170144
|
-
await notifyHistorianIssue(
|
|
170145
|
-
|
|
170146
|
-
Historian skipped this session because the raw chunk could not be represented safely: ${chunkCoverageError}
|
|
170147
|
-
|
|
170148
|
-
No new compartments or facts were written.`);
|
|
170329
|
+
const failCount = incrementHistorianFailure(db, sessionId, chunkCoverageError);
|
|
170330
|
+
await notifyHistorianIssue(buildHistorianFailureNotice(failCount, chunkCoverageError));
|
|
170149
170331
|
return;
|
|
170150
170332
|
}
|
|
170151
170333
|
const projectPath = resolveProjectIdentity(directory ?? process.cwd());
|
|
@@ -170186,13 +170368,9 @@ ${chunk.text}`,
|
|
|
170186
170368
|
});
|
|
170187
170369
|
if (!validatedPass.ok) {
|
|
170188
170370
|
sessionLog(sessionId, `historian failure: source=validation reason="${validatedPass.error}" chunkRange=${chunk.startIndex}-${chunk.endIndex} fallbackModel=${deps.fallbackModelId ?? "<none>"} twoPass=${deps.historianTwoPass ? "true" : "false"}`);
|
|
170189
|
-
incrementHistorianFailure(db, sessionId, validatedPass.error);
|
|
170371
|
+
const failCount = incrementHistorianFailure(db, sessionId, validatedPass.error);
|
|
170190
170372
|
telemetry.failureReason = `validation: ${validatedPass.error}`;
|
|
170191
|
-
await notifyHistorianIssue(
|
|
170192
|
-
|
|
170193
|
-
${validatedPass.error}
|
|
170194
|
-
|
|
170195
|
-
No new compartments or facts were written. Check the historian model/output and try again.`);
|
|
170373
|
+
await notifyHistorianIssue(buildHistorianFailureNotice(failCount, validatedPass.error));
|
|
170196
170374
|
return;
|
|
170197
170375
|
}
|
|
170198
170376
|
const emittedCompartments = validatedPass.compartments;
|
|
@@ -170213,12 +170391,8 @@ No new compartments or facts were written. Check the historian model/output and
|
|
|
170213
170391
|
if (lastNewEnd + 1 <= offset) {
|
|
170214
170392
|
telemetry.failureReason = `no forward progress beyond raw message ${offset - 1}`;
|
|
170215
170393
|
sessionLog(sessionId, `historian failure: source=no-progress reason="historian returned compartments that did not advance past raw message ${offset - 1}" newCompartmentCount=${newCompartments.length} lastNewEnd=${lastNewEnd} priorEnd=${offset - 1}`);
|
|
170216
|
-
incrementHistorianFailure(db, sessionId, `no forward progress beyond raw message ${offset - 1}`);
|
|
170217
|
-
await notifyHistorianIssue(
|
|
170218
|
-
|
|
170219
|
-
Historian returned compartments that made no forward progress beyond raw message ${offset - 1}.
|
|
170220
|
-
|
|
170221
|
-
No new compartments or facts were written. Check the historian model/output and try again.`);
|
|
170394
|
+
const failCount = incrementHistorianFailure(db, sessionId, `no forward progress beyond raw message ${offset - 1}`);
|
|
170395
|
+
await notifyHistorianIssue(buildHistorianFailureNotice(failCount, `historian made no forward progress beyond raw message ${offset - 1}`));
|
|
170222
170396
|
return;
|
|
170223
170397
|
}
|
|
170224
170398
|
const deferMarkerApplication = deps.preserveInjectionCacheUntilConsumed === true;
|
|
@@ -170259,7 +170433,6 @@ No new compartments or facts were written. Check the historian model/output and
|
|
|
170259
170433
|
if (deps.preserveInjectionCacheUntilConsumed !== true) {
|
|
170260
170434
|
clearInjectionCache(sessionId);
|
|
170261
170435
|
}
|
|
170262
|
-
deps.onCompartmentStatePublished?.(sessionId);
|
|
170263
170436
|
const promotionDirectory = sessionDirectory || deps.directory;
|
|
170264
170437
|
const discardedLast = persistedCompartments.length < emittedCompartments.length;
|
|
170265
170438
|
const embeddingActive = !!promotionDirectory && deps.memoryEnabled !== false;
|
|
@@ -170286,6 +170459,7 @@ No new compartments or facts were written. Check the historian model/output and
|
|
|
170286
170459
|
embedAndStoreCompartments(db, sessionId, projectIdentity, toEmbed);
|
|
170287
170460
|
}
|
|
170288
170461
|
queueDropsForCompartmentalizedMessages(db, sessionId, lastCompartmentEnd);
|
|
170462
|
+
deps.onCompartmentStatePublished?.(sessionId);
|
|
170289
170463
|
if (deferMarkerApplication) {
|
|
170290
170464
|
deps.onDeferredMarkerPending?.(sessionId);
|
|
170291
170465
|
} else {
|
|
@@ -170330,12 +170504,8 @@ No new compartments or facts were written. Check the historian model/output and
|
|
|
170330
170504
|
telemetry.failureReason = `exception: ${desc.brief}`;
|
|
170331
170505
|
sessionLog(sessionId, `historian failure: source=exception ${desc.brief}${desc.stackHead ? ` stackHead="${desc.stackHead}"` : ""}`);
|
|
170332
170506
|
if (!issueNotified) {
|
|
170333
|
-
incrementHistorianFailure(db, sessionId, desc.brief);
|
|
170334
|
-
await notifyHistorianIssue(
|
|
170335
|
-
|
|
170336
|
-
Historian failed unexpectedly: ${desc.brief}
|
|
170337
|
-
|
|
170338
|
-
No new compartments or facts were written. Check the historian model/output and try again.`);
|
|
170507
|
+
const failCount = incrementHistorianFailure(db, sessionId, desc.brief);
|
|
170508
|
+
await notifyHistorianIssue(buildHistorianFailureNotice(failCount, desc.brief));
|
|
170339
170509
|
}
|
|
170340
170510
|
} finally {
|
|
170341
170511
|
if (!completedSuccessfully) {
|
|
@@ -170499,7 +170669,6 @@ Found ${existingStaging.compartments.length} staged compartment(s) from ${existi
|
|
|
170499
170669
|
if (deps.preserveInjectionCacheUntilConsumed !== true) {
|
|
170500
170670
|
clearInjectionCache(sessionId);
|
|
170501
170671
|
}
|
|
170502
|
-
deps.onCompartmentStatePublished?.(sessionId);
|
|
170503
170672
|
promoted2.facts;
|
|
170504
170673
|
if (deps.memoryEnabled !== false) {
|
|
170505
170674
|
const projectIdentity = resolveProjectIdentity(sessionDirectory);
|
|
@@ -170512,11 +170681,14 @@ Found ${existingStaging.compartments.length} staged compartment(s) from ${existi
|
|
|
170512
170681
|
if (lastCompartmentEnd2 > 0) {
|
|
170513
170682
|
queueDropsForCompartmentalizedMessages(db, sessionId, lastCompartmentEnd2);
|
|
170514
170683
|
}
|
|
170684
|
+
deps.onCompartmentStatePublished?.(sessionId);
|
|
170515
170685
|
if (lastCompartmentEnd2 > 0) {
|
|
170516
|
-
updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd2, deps.directory);
|
|
170517
|
-
|
|
170518
|
-
|
|
170519
|
-
|
|
170686
|
+
const markerUpdated = updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd2, deps.directory);
|
|
170687
|
+
if (markerUpdated) {
|
|
170688
|
+
const stalePending = getPendingCompactionMarkerState(db, sessionId);
|
|
170689
|
+
if (stalePending) {
|
|
170690
|
+
clearPendingCompactionMarkerStateIf(db, sessionId, stalePending);
|
|
170691
|
+
}
|
|
170520
170692
|
}
|
|
170521
170693
|
}
|
|
170522
170694
|
return [
|
|
@@ -170705,13 +170877,13 @@ Another process acquired the compartment-state lease before recomp could publish
|
|
|
170705
170877
|
if (deps.preserveInjectionCacheUntilConsumed !== true) {
|
|
170706
170878
|
clearInjectionCache(sessionId);
|
|
170707
170879
|
}
|
|
170708
|
-
deps.onCompartmentStatePublished?.(sessionId);
|
|
170709
170880
|
const finalCompartments = promoted?.compartments ?? candidateCompartments;
|
|
170710
170881
|
const finalFacts = promoted?.facts ?? candidateFacts;
|
|
170711
170882
|
const lastCompartmentEnd = finalCompartments[finalCompartments.length - 1]?.endMessage ?? 0;
|
|
170712
170883
|
if (lastCompartmentEnd > 0) {
|
|
170713
170884
|
queueDropsForCompartmentalizedMessages(db, sessionId, lastCompartmentEnd);
|
|
170714
170885
|
}
|
|
170886
|
+
deps.onCompartmentStatePublished?.(sessionId);
|
|
170715
170887
|
if (deps.memoryEnabled !== false) {
|
|
170716
170888
|
const projectIdentity = resolveProjectIdentity(sessionDirectory);
|
|
170717
170889
|
await deps.ensureProjectRegistered?.(sessionDirectory, db);
|
|
@@ -170720,10 +170892,12 @@ Another process acquired the compartment-state lease before recomp could publish
|
|
|
170720
170892
|
embedAndStoreCompartments(db, sessionId, projectIdentity, toEmbed);
|
|
170721
170893
|
}
|
|
170722
170894
|
if (lastCompartmentEnd > 0) {
|
|
170723
|
-
updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, deps.directory);
|
|
170724
|
-
|
|
170725
|
-
|
|
170726
|
-
|
|
170895
|
+
const markerUpdated = updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEnd, deps.directory);
|
|
170896
|
+
if (markerUpdated) {
|
|
170897
|
+
const stalePending = getPendingCompactionMarkerState(db, sessionId);
|
|
170898
|
+
if (stalePending) {
|
|
170899
|
+
clearPendingCompactionMarkerStateIf(db, sessionId, stalePending);
|
|
170900
|
+
}
|
|
170727
170901
|
}
|
|
170728
170902
|
}
|
|
170729
170903
|
return [
|
|
@@ -170880,7 +171054,7 @@ Could not acquire the compartment-state lease for this session.`;
|
|
|
170880
171054
|
log(`[magic-context] partial recomp merged validation failed: ${mergedError}`);
|
|
170881
171055
|
return null;
|
|
170882
171056
|
}
|
|
170883
|
-
saveRecompStagingPass(db, sessionId, passCount + 1, merged,
|
|
171057
|
+
saveRecompStagingPass(db, sessionId, passCount + 1, merged, stagedFacts);
|
|
170884
171058
|
const promoted = promoteRecompStagingWithM0Mutation(db, sessionId, leaseHolderId);
|
|
170885
171059
|
if (!promoted) {
|
|
170886
171060
|
log("[magic-context] partial recomp promote returned null");
|
|
@@ -170900,10 +171074,12 @@ Could not acquire the compartment-state lease for this session.`;
|
|
|
170900
171074
|
}
|
|
170901
171075
|
const lastEnd = merged[merged.length - 1]?.endMessage ?? snapEnd;
|
|
170902
171076
|
if (lastEnd > 0) {
|
|
170903
|
-
updateCompactionMarkerAfterPublication(db, sessionId, lastEnd, deps.directory);
|
|
170904
|
-
|
|
170905
|
-
|
|
170906
|
-
|
|
171077
|
+
const markerUpdated = updateCompactionMarkerAfterPublication(db, sessionId, lastEnd, deps.directory);
|
|
171078
|
+
if (markerUpdated) {
|
|
171079
|
+
const stalePending = getPendingCompactionMarkerState(db, sessionId);
|
|
171080
|
+
if (stalePending) {
|
|
171081
|
+
clearPendingCompactionMarkerStateIf(db, sessionId, stalePending);
|
|
171082
|
+
}
|
|
170907
171083
|
}
|
|
170908
171084
|
}
|
|
170909
171085
|
return { compartmentCount: merged.length, lastEndMessage: lastEnd };
|
|
@@ -170945,10 +171121,7 @@ Snapped range ${snapStart}-${snapEnd} would cross into the protected tail (start
|
|
|
170945
171121
|
].join(`
|
|
170946
171122
|
`);
|
|
170947
171123
|
}
|
|
170948
|
-
const
|
|
170949
|
-
category: f.category,
|
|
170950
|
-
content: f.content
|
|
170951
|
-
}));
|
|
171124
|
+
const stagedFacts = [];
|
|
170952
171125
|
const parentSessionResponse = await client.session.get({ path: { id: sessionId } }).catch(() => null);
|
|
170953
171126
|
const parentSession = normalizeSDKResponse(parentSessionResponse, null, { preferResponseOnMissingData: true });
|
|
170954
171127
|
const sessionDirectory = parentSession?.directory ?? directory;
|
|
@@ -170965,7 +171138,7 @@ Snapped range ${snapStart}-${snapEnd} would cross into the protected tail (start
|
|
|
170965
171138
|
candidateCompartments = priorCompartments.map((c, idx) => compartmentToInput(c, idx));
|
|
170966
171139
|
passCount = 0;
|
|
170967
171140
|
offset = snapStart;
|
|
170968
|
-
saveRecompStagingPass(db, sessionId, 0, candidateCompartments,
|
|
171141
|
+
saveRecompStagingPass(db, sessionId, 0, candidateCompartments, stagedFacts);
|
|
170969
171142
|
setRecompPartialRange(db, sessionId, { start: snapStart, end: snapEnd });
|
|
170970
171143
|
}
|
|
170971
171144
|
let currentTokenBudget = historianChunkTokens;
|
|
@@ -171058,7 +171231,7 @@ Original state preserved (staging kept for retry).`;
|
|
|
171058
171231
|
passCount += 1;
|
|
171059
171232
|
currentTokenBudget = historianChunkTokens;
|
|
171060
171233
|
passAttempt = 1;
|
|
171061
|
-
saveRecompStagingPass(db, sessionId, passCount, candidateCompartments,
|
|
171234
|
+
saveRecompStagingPass(db, sessionId, passCount, candidateCompartments, stagedFacts);
|
|
171062
171235
|
const nextOffset = (validatedPass.compartments?.[validatedPass.compartments.length - 1]?.endMessage ?? chunk.endIndex) + 1;
|
|
171063
171236
|
if (nextOffset <= offset) {
|
|
171064
171237
|
return `## Magic Recomp — Failed
|
|
@@ -171079,7 +171252,6 @@ Partial recomp completed historian passes but the final compartment set failed v
|
|
|
171079
171252
|
...resumed ? ["Resumed from previous interrupted partial run."] : [],
|
|
171080
171253
|
`Rebuilt compartments covering messages ${snapStart}-${snapEnd} using ${passCount} historian pass${passCount === 1 ? "" : "es"}.`,
|
|
171081
171254
|
`Preserved ${priorCompartments.length} prior compartment(s) and ${tailCompartments.length} tail compartment(s) unchanged.`,
|
|
171082
|
-
`Facts unchanged (${currentFacts.length} entr${currentFacts.length === 1 ? "y" : "ies"}).`,
|
|
171083
171255
|
`Total compartments: ${finalResult.compartmentCount}.`
|
|
171084
171256
|
].join(`
|
|
171085
171257
|
`);
|
|
@@ -172073,6 +172245,95 @@ function migrateLegacyExperimental(rawConfig, warnings) {
|
|
|
172073
172245
|
return patched;
|
|
172074
172246
|
}
|
|
172075
172247
|
|
|
172248
|
+
// src/config/project-security.ts
|
|
172249
|
+
var HIDDEN_AGENT_KEYS = ["historian", "dreamer", "sidekick"];
|
|
172250
|
+
var AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools", "system_prompt"];
|
|
172251
|
+
function isPlainObject(value) {
|
|
172252
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
172253
|
+
}
|
|
172254
|
+
function stripUnsafeProjectConfigFields(projectRaw) {
|
|
172255
|
+
const warnings = [];
|
|
172256
|
+
if ("auto_update" in projectRaw) {
|
|
172257
|
+
delete projectRaw.auto_update;
|
|
172258
|
+
warnings.push("Ignoring auto_update from project config (security: this setting only honors user-level config).");
|
|
172259
|
+
}
|
|
172260
|
+
for (const agentKey of HIDDEN_AGENT_KEYS) {
|
|
172261
|
+
const block = projectRaw[agentKey];
|
|
172262
|
+
if (!isPlainObject(block))
|
|
172263
|
+
continue;
|
|
172264
|
+
const removed = [];
|
|
172265
|
+
for (const field of AGENT_ESCALATION_FIELDS) {
|
|
172266
|
+
if (field in block) {
|
|
172267
|
+
delete block[field];
|
|
172268
|
+
removed.push(field);
|
|
172269
|
+
}
|
|
172270
|
+
}
|
|
172271
|
+
if (removed.length > 0) {
|
|
172272
|
+
warnings.push(`Ignoring ${agentKey}.${removed.join("/")} from project config ` + "(security: a repository cannot reprogram or re-permission hidden agents).");
|
|
172273
|
+
}
|
|
172274
|
+
}
|
|
172275
|
+
return warnings;
|
|
172276
|
+
}
|
|
172277
|
+
function normalizeEndpoint(value) {
|
|
172278
|
+
if (typeof value !== "string")
|
|
172279
|
+
return;
|
|
172280
|
+
const trimmed = value.trim().replace(/\/+$/, "");
|
|
172281
|
+
return trimmed.length > 0 ? trimmed.toLowerCase() : undefined;
|
|
172282
|
+
}
|
|
172283
|
+
function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
|
|
172284
|
+
const projectEmbedding = projectRaw.embedding;
|
|
172285
|
+
if (!isPlainObject(projectEmbedding))
|
|
172286
|
+
return [];
|
|
172287
|
+
const redirectsEndpoint = "endpoint" in projectEmbedding;
|
|
172288
|
+
if (!redirectsEndpoint)
|
|
172289
|
+
return [];
|
|
172290
|
+
const userEmbedding = userRaw?.embedding;
|
|
172291
|
+
if (isPlainObject(userEmbedding)) {
|
|
172292
|
+
const projectEndpoint = normalizeEndpoint(projectEmbedding.endpoint);
|
|
172293
|
+
const userEndpoint = normalizeEndpoint(userEmbedding.endpoint);
|
|
172294
|
+
if (projectEndpoint !== undefined && projectEndpoint === userEndpoint) {
|
|
172295
|
+
return [];
|
|
172296
|
+
}
|
|
172297
|
+
}
|
|
172298
|
+
const providesOwnKey = typeof projectEmbedding.api_key === "string" && projectEmbedding.api_key.length > 0;
|
|
172299
|
+
if (providesOwnKey)
|
|
172300
|
+
return [];
|
|
172301
|
+
const mergedEmbedding = mergedRaw.embedding;
|
|
172302
|
+
if (!isPlainObject(mergedEmbedding))
|
|
172303
|
+
return [];
|
|
172304
|
+
if (!("api_key" in mergedEmbedding))
|
|
172305
|
+
return [];
|
|
172306
|
+
delete mergedEmbedding.api_key;
|
|
172307
|
+
return [
|
|
172308
|
+
"Dropped inherited user embedding api_key because project config redirected " + "embedding.endpoint without supplying its own key (security: prevents key " + "exfiltration to a repository-chosen endpoint)."
|
|
172309
|
+
];
|
|
172310
|
+
}
|
|
172311
|
+
|
|
172312
|
+
// src/config/prune-config-leaf.ts
|
|
172313
|
+
function isPlainObject2(value) {
|
|
172314
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
172315
|
+
}
|
|
172316
|
+
function pruneNestedConfigLeaf(block, relativePath) {
|
|
172317
|
+
if (relativePath.length === 0)
|
|
172318
|
+
return null;
|
|
172319
|
+
const result = { ...block };
|
|
172320
|
+
let cursor = result;
|
|
172321
|
+
for (let i = 0;i < relativePath.length - 1; i++) {
|
|
172322
|
+
const seg = String(relativePath[i]);
|
|
172323
|
+
const child = cursor[seg];
|
|
172324
|
+
if (!isPlainObject2(child))
|
|
172325
|
+
return null;
|
|
172326
|
+
const clonedChild = { ...child };
|
|
172327
|
+
cursor[seg] = clonedChild;
|
|
172328
|
+
cursor = clonedChild;
|
|
172329
|
+
}
|
|
172330
|
+
const leaf = String(relativePath[relativePath.length - 1]);
|
|
172331
|
+
if (!(leaf in cursor))
|
|
172332
|
+
return null;
|
|
172333
|
+
delete cursor[leaf];
|
|
172334
|
+
return { block: result, removed: relativePath.map(String).join(".") };
|
|
172335
|
+
}
|
|
172336
|
+
|
|
172076
172337
|
// src/config/index.ts
|
|
172077
172338
|
init_magic_context();
|
|
172078
172339
|
|
|
@@ -172083,6 +172344,21 @@ import { homedir } from "node:os";
|
|
|
172083
172344
|
import { dirname, isAbsolute, resolve } from "node:path";
|
|
172084
172345
|
var ENV_PATTERN = /\{env:([^}]+)\}/g;
|
|
172085
172346
|
var FILE_PATTERN = /\{file:([^}]+)\}/g;
|
|
172347
|
+
function sensitiveFilePathReason(resolvedPath) {
|
|
172348
|
+
const home = homedir();
|
|
172349
|
+
const sensitiveDirs = [
|
|
172350
|
+
{ dir: resolve(home, ".ssh"), label: "SSH keys" },
|
|
172351
|
+
{ dir: resolve(home, ".aws"), label: "AWS credentials" },
|
|
172352
|
+
{ dir: resolve(home, ".gnupg"), label: "GnuPG keyring" },
|
|
172353
|
+
{ dir: resolve(home, ".config", "gh"), label: "GitHub CLI auth" }
|
|
172354
|
+
];
|
|
172355
|
+
for (const { dir, label } of sensitiveDirs) {
|
|
172356
|
+
if (resolvedPath === dir || resolvedPath.startsWith(`${dir}/`)) {
|
|
172357
|
+
return label;
|
|
172358
|
+
}
|
|
172359
|
+
}
|
|
172360
|
+
return null;
|
|
172361
|
+
}
|
|
172086
172362
|
function substituteConfigVariables(input) {
|
|
172087
172363
|
const warnings = [];
|
|
172088
172364
|
let text = input.text;
|
|
@@ -172136,6 +172412,10 @@ function substituteConfigVariables(input) {
|
|
|
172136
172412
|
} else if (!isAbsolute(filePath)) {
|
|
172137
172413
|
filePath = resolve(configDir, filePath);
|
|
172138
172414
|
}
|
|
172415
|
+
const sensitiveReason = sensitiveFilePathReason(filePath);
|
|
172416
|
+
if (sensitiveReason) {
|
|
172417
|
+
warnings.push(`${token} resolves to a sensitive path (${sensitiveReason}: ${filePath}); ` + "inlining its contents into config — make sure this is intentional.");
|
|
172418
|
+
}
|
|
172139
172419
|
if (!existsSync2(filePath)) {
|
|
172140
172420
|
warnings.push(`File not found for ${token} (resolved to ${filePath}); using empty string`);
|
|
172141
172421
|
continue;
|
|
@@ -172238,9 +172518,6 @@ function deepMergeRawConfig(base, override) {
|
|
|
172238
172518
|
}
|
|
172239
172519
|
return result;
|
|
172240
172520
|
}
|
|
172241
|
-
function getProjectUserOnlyFields(config2) {
|
|
172242
|
-
return "auto_update" in config2 ? ["auto_update"] : [];
|
|
172243
|
-
}
|
|
172244
172521
|
function redactConfigValue(value) {
|
|
172245
172522
|
if (value === undefined)
|
|
172246
172523
|
return "<missing>";
|
|
@@ -172279,12 +172556,16 @@ function parsePluginConfig(rawConfig, recoveredTopLevelKeys = []) {
|
|
|
172279
172556
|
const warnings = [];
|
|
172280
172557
|
const errorPaths = new Set;
|
|
172281
172558
|
const customMessagesByKey = new Map;
|
|
172559
|
+
const issuePathsByKey = new Map;
|
|
172282
172560
|
const GENERIC_ZOD_PREFIXES = ["Too big", "Too small", "Invalid input", "Invalid", "Expected"];
|
|
172283
172561
|
for (const issue2 of parsed.error.issues) {
|
|
172284
172562
|
const topKey = issue2.path[0];
|
|
172285
172563
|
if (topKey !== undefined) {
|
|
172286
172564
|
const key = String(topKey);
|
|
172287
172565
|
errorPaths.add(key);
|
|
172566
|
+
const paths = issuePathsByKey.get(key) ?? [];
|
|
172567
|
+
paths.push([...issue2.path]);
|
|
172568
|
+
issuePathsByKey.set(key, paths);
|
|
172288
172569
|
const msg = issue2.message;
|
|
172289
172570
|
if (msg && !GENERIC_ZOD_PREFIXES.some((p) => msg.startsWith(p))) {
|
|
172290
172571
|
if (!customMessagesByKey.has(key)) {
|
|
@@ -172300,12 +172581,33 @@ function parsePluginConfig(rawConfig, recoveredTopLevelKeys = []) {
|
|
|
172300
172581
|
if (isAgentConfig) {
|
|
172301
172582
|
delete patched[key];
|
|
172302
172583
|
warnings.push(`"${key}": invalid agent configuration, ignoring. Check your magic-context.jsonc.`);
|
|
172303
|
-
|
|
172304
|
-
|
|
172305
|
-
|
|
172306
|
-
|
|
172307
|
-
|
|
172584
|
+
continue;
|
|
172585
|
+
}
|
|
172586
|
+
const issuePaths = issuePathsByKey.get(key) ?? [];
|
|
172587
|
+
const rawValue = rawConfig[key];
|
|
172588
|
+
const allNested = issuePaths.length > 0 && issuePaths.every((p) => p.length >= 2) && typeof rawValue === "object" && rawValue !== null && !Array.isArray(rawValue);
|
|
172589
|
+
if (allNested) {
|
|
172590
|
+
let prunedBlock = {
|
|
172591
|
+
...rawValue
|
|
172592
|
+
};
|
|
172593
|
+
const prunedLeaves = [];
|
|
172594
|
+
for (const p of issuePaths) {
|
|
172595
|
+
const relative = p.slice(1);
|
|
172596
|
+
const result = pruneNestedConfigLeaf(prunedBlock, relative);
|
|
172597
|
+
if (result) {
|
|
172598
|
+
prunedBlock = result.block;
|
|
172599
|
+
prunedLeaves.push(result.removed);
|
|
172600
|
+
}
|
|
172601
|
+
}
|
|
172602
|
+
patched[key] = prunedBlock;
|
|
172603
|
+
const reason2 = customMessagesByKey.get(key);
|
|
172604
|
+
warnings.push(`"${key}": invalid nested field(s) ${prunedLeaves.map((l) => `"${l}"`).join(", ")}, using defaults for those.${reason2 ? ` ${reason2}` : ""}`);
|
|
172605
|
+
continue;
|
|
172308
172606
|
}
|
|
172607
|
+
delete patched[key];
|
|
172608
|
+
const defaultVal = defaults[key];
|
|
172609
|
+
const reason = customMessagesByKey.get(key);
|
|
172610
|
+
warnings.push(`"${key}": invalid value (${redactConfigValue(rawConfig[key])}), using default ${JSON.stringify(defaultVal)}.${reason ? ` ${reason}` : ""}`);
|
|
172309
172611
|
}
|
|
172310
172612
|
const retryMigrated = migrateLegacyExperimental(patched, preMigrationWarnings);
|
|
172311
172613
|
const retryParsed = MagicContextConfigSchema.safeParse(retryMigrated);
|
|
@@ -172341,14 +172643,13 @@ function loadPluginConfig(directory) {
|
|
|
172341
172643
|
if (projectLoaded) {
|
|
172342
172644
|
allWarnings.push(...projectLoaded.warnings.map((w) => `[project config] ${w}`));
|
|
172343
172645
|
const projectRaw = { ...projectLoaded.config };
|
|
172344
|
-
const
|
|
172345
|
-
|
|
172346
|
-
for (const key of strippedUserOnlyFields) {
|
|
172347
|
-
delete projectRaw[key];
|
|
172348
|
-
}
|
|
172349
|
-
allWarnings.push(`[project config] Ignoring ${strippedUserOnlyFields.join(", ")} from project config (security: these settings only honor user-level config)`);
|
|
172646
|
+
for (const warning of stripUnsafeProjectConfigFields(projectRaw)) {
|
|
172647
|
+
allWarnings.push(`[project config] ${warning}`);
|
|
172350
172648
|
}
|
|
172351
172649
|
mergedRaw = deepMergeRawConfig(mergedRaw, projectRaw);
|
|
172650
|
+
for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userLoaded?.config)) {
|
|
172651
|
+
allWarnings.push(`[project config] ${warning}`);
|
|
172652
|
+
}
|
|
172352
172653
|
}
|
|
172353
172654
|
const config2 = parsePluginConfig(mergedRaw);
|
|
172354
172655
|
if (config2.configWarnings?.length) {
|
|
@@ -172422,14 +172723,13 @@ function loadPluginConfigDetailed(directory) {
|
|
|
172422
172723
|
if (projectLoaded) {
|
|
172423
172724
|
allWarnings.push(...projectLoaded.warnings.map((w) => `[project config] ${w}`));
|
|
172424
172725
|
const projectRaw = { ...projectLoaded.config };
|
|
172425
|
-
const
|
|
172426
|
-
|
|
172427
|
-
for (const key of strippedUserOnlyFields) {
|
|
172428
|
-
delete projectRaw[key];
|
|
172429
|
-
}
|
|
172430
|
-
allWarnings.push(`[project config] Ignoring ${strippedUserOnlyFields.join(", ")} from project config (security: these settings only honor user-level config)`);
|
|
172726
|
+
for (const warning of stripUnsafeProjectConfigFields(projectRaw)) {
|
|
172727
|
+
allWarnings.push(`[project config] ${warning}`);
|
|
172431
172728
|
}
|
|
172432
172729
|
mergedRaw = deepMergeRawConfig(mergedRaw, projectRaw);
|
|
172730
|
+
for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userLoaded?.config)) {
|
|
172731
|
+
allWarnings.push(`[project config] ${warning}`);
|
|
172732
|
+
}
|
|
172433
172733
|
}
|
|
172434
172734
|
const recoveredTopLevelKeys = [];
|
|
172435
172735
|
const config2 = parsePluginConfig(mergedRaw, recoveredTopLevelKeys);
|
|
@@ -173244,6 +173544,11 @@ var CACHE_DIR = join7(getOpenCodeCacheRoot(), "packages");
|
|
|
173244
173544
|
var USER_OPENCODE_CONFIG = join7(getOpenCodeConfigRoot(), "opencode.json");
|
|
173245
173545
|
var USER_OPENCODE_CONFIG_JSONC = join7(getOpenCodeConfigRoot(), "opencode.jsonc");
|
|
173246
173546
|
|
|
173547
|
+
// src/hooks/auto-update-checker/semver.ts
|
|
173548
|
+
function isValidSemver(version2) {
|
|
173549
|
+
return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(version2);
|
|
173550
|
+
}
|
|
173551
|
+
|
|
173247
173552
|
// src/hooks/auto-update-checker/types.ts
|
|
173248
173553
|
init_zod();
|
|
173249
173554
|
var NpmPackageEnvelopeSchema = exports_external.object({
|
|
@@ -173579,6 +173884,10 @@ function resolveInstallContext(runtimePackageJsonPath = getCurrentRuntimePackage
|
|
|
173579
173884
|
}
|
|
173580
173885
|
function preparePackageUpdate(version2, packageName = PACKAGE_NAME, runtimePackageJsonPath = getCurrentRuntimePackageJsonPath()) {
|
|
173581
173886
|
try {
|
|
173887
|
+
if (!isValidSemver(version2)) {
|
|
173888
|
+
warn2(`[auto-update-checker] Refusing to prepare update for invalid version "${version2}"`);
|
|
173889
|
+
return null;
|
|
173890
|
+
}
|
|
173582
173891
|
const installContext = resolveInstallContext(runtimePackageJsonPath);
|
|
173583
173892
|
if (!installContext) {
|
|
173584
173893
|
warn2("[auto-update-checker] No install context found for auto-update");
|
|
@@ -174864,7 +175173,7 @@ async function runDream(args) {
|
|
|
174864
175173
|
}
|
|
174865
175174
|
}
|
|
174866
175175
|
const deadline = startedAt + args.maxRuntimeMinutes * 60 * 1000;
|
|
174867
|
-
const lastDreamAt = getDreamState(args.db, `last_dream_at:${args.projectIdentity}`)
|
|
175176
|
+
const lastDreamAt = getDreamState(args.db, `last_dream_at:${args.projectIdentity}`);
|
|
174868
175177
|
log(`[dreamer] last dream at: ${lastDreamAt ?? "never"} (project=${args.projectIdentity})`);
|
|
174869
175178
|
let lastErrorSignature = null;
|
|
174870
175179
|
let consecutiveSameErrorFailures = 0;
|
|
@@ -175246,7 +175555,6 @@ async function runDream(args) {
|
|
|
175246
175555
|
const hasSuccessfulTask = result.tasks.some((t) => !t.error && !POST_TASK_NAMES.has(t.name));
|
|
175247
175556
|
if (hasSuccessfulTask && !lostLease) {
|
|
175248
175557
|
setDreamState(args.db, `last_dream_at:${args.projectIdentity}`, String(result.finishedAt));
|
|
175249
|
-
setDreamState(args.db, "last_dream_at", String(result.finishedAt));
|
|
175250
175558
|
}
|
|
175251
175559
|
const totalDuration = ((result.finishedAt - startedAt) / 1000).toFixed(1);
|
|
175252
175560
|
const succeeded = result.tasks.filter((t) => !t.error).length;
|
|
@@ -175523,8 +175831,7 @@ function findProjectsNeedingDream(db) {
|
|
|
175523
175831
|
const now = new Date;
|
|
175524
175832
|
for (const row of projectRows) {
|
|
175525
175833
|
const lastDreamAtStr = getDreamState(db, `last_dream_at:${row.project_path}`);
|
|
175526
|
-
const
|
|
175527
|
-
const lastDreamAt = Number(lastDreamAtStr ?? fallbackStr ?? "0") || 0;
|
|
175834
|
+
const lastDreamAt = Number(lastDreamAtStr ?? "0") || 0;
|
|
175528
175835
|
if (lastDreamAt > 0 && isDreamFromCurrentWindow(lastDreamAt, now)) {
|
|
175529
175836
|
continue;
|
|
175530
175837
|
}
|
|
@@ -176174,10 +176481,20 @@ var EMBEDDING_AFFECTING_KEYS = new Set([
|
|
|
176174
176481
|
"embedding.api_key",
|
|
176175
176482
|
"embedding.endpoint",
|
|
176176
176483
|
"embedding.model",
|
|
176177
|
-
"embedding.provider"
|
|
176484
|
+
"embedding.provider",
|
|
176485
|
+
"embedding.input_type",
|
|
176486
|
+
"embedding.truncate"
|
|
176178
176487
|
]);
|
|
176179
176488
|
var EMBEDDING_AFFECTING_TOP_LEVEL_KEYS = new Set(["embedding", "memory", "experimental"]);
|
|
176180
|
-
var EMBEDDING_WARNING_TERMS = [
|
|
176489
|
+
var EMBEDDING_WARNING_TERMS = [
|
|
176490
|
+
"api_key",
|
|
176491
|
+
"endpoint",
|
|
176492
|
+
"model",
|
|
176493
|
+
"provider",
|
|
176494
|
+
"embedding",
|
|
176495
|
+
"input_type",
|
|
176496
|
+
"truncate"
|
|
176497
|
+
];
|
|
176181
176498
|
var loggedFailureSignatures = new Map;
|
|
176182
176499
|
function sha256Prefix2(value, length = 16) {
|
|
176183
176500
|
return createHash8("sha256").update(value).digest("hex").slice(0, length);
|
|
@@ -179192,32 +179509,53 @@ function hasAnyMeaningfulPart(parts) {
|
|
|
179192
179509
|
}
|
|
179193
179510
|
return false;
|
|
179194
179511
|
}
|
|
179195
|
-
function
|
|
179196
|
-
|
|
179512
|
+
function messageHasReducePart(message) {
|
|
179513
|
+
for (const part of message.parts) {
|
|
179514
|
+
if (isSentinel(part))
|
|
179515
|
+
continue;
|
|
179516
|
+
if (isReduceToolPart(part))
|
|
179517
|
+
return true;
|
|
179518
|
+
}
|
|
179519
|
+
return false;
|
|
179520
|
+
}
|
|
179521
|
+
function sentinelizeReduceParts(message) {
|
|
179522
|
+
let touched = false;
|
|
179523
|
+
for (let j = 0;j < message.parts.length; j++) {
|
|
179524
|
+
const part = message.parts[j];
|
|
179525
|
+
if (isSentinel(part))
|
|
179526
|
+
continue;
|
|
179527
|
+
if (isReduceToolPart(part)) {
|
|
179528
|
+
message.parts[j] = makeSentinel(part);
|
|
179529
|
+
touched = true;
|
|
179530
|
+
}
|
|
179531
|
+
}
|
|
179532
|
+
if (touched && !hasAnyMeaningfulPart(message.parts)) {
|
|
179533
|
+
message.parts.length = 0;
|
|
179534
|
+
message.parts.push(makeSentinel(undefined));
|
|
179535
|
+
}
|
|
179536
|
+
return touched;
|
|
179537
|
+
}
|
|
179538
|
+
function dropStaleReduceCalls(messages, frozenIds, options = {}) {
|
|
179539
|
+
const detect = options.detect ?? false;
|
|
179540
|
+
const protectedCount = options.protectedCount ?? 0;
|
|
179197
179541
|
const protectedStart = messages.length - protectedCount;
|
|
179542
|
+
const newlyStrippedIds = [];
|
|
179543
|
+
let didDrop = false;
|
|
179198
179544
|
for (let i = 0;i < messages.length; i++) {
|
|
179199
|
-
if (i >= protectedStart)
|
|
179200
|
-
break;
|
|
179201
179545
|
const message = messages[i];
|
|
179202
|
-
|
|
179203
|
-
|
|
179204
|
-
|
|
179205
|
-
|
|
179206
|
-
|
|
179207
|
-
|
|
179208
|
-
message.parts[j] = makeSentinel(part);
|
|
179209
|
-
touched = true;
|
|
179210
|
-
}
|
|
179211
|
-
}
|
|
179546
|
+
const id = typeof message.info.id === "string" ? message.info.id : undefined;
|
|
179547
|
+
const inFrozen = id !== undefined && frozenIds.has(id);
|
|
179548
|
+
const isNewDetection = !inFrozen && detect && i < protectedStart && id !== undefined && messageHasReducePart(message);
|
|
179549
|
+
if (!inFrozen && !isNewDetection)
|
|
179550
|
+
continue;
|
|
179551
|
+
const touched = sentinelizeReduceParts(message);
|
|
179212
179552
|
if (touched) {
|
|
179213
179553
|
didDrop = true;
|
|
179214
|
-
if (
|
|
179215
|
-
|
|
179216
|
-
message.parts.push(makeSentinel(undefined));
|
|
179217
|
-
}
|
|
179554
|
+
if (isNewDetection && id !== undefined)
|
|
179555
|
+
newlyStrippedIds.push(id);
|
|
179218
179556
|
}
|
|
179219
179557
|
}
|
|
179220
|
-
return didDrop;
|
|
179558
|
+
return { didDrop, newlyStrippedIds };
|
|
179221
179559
|
}
|
|
179222
179560
|
|
|
179223
179561
|
// src/hooks/magic-context/tag-messages.ts
|
|
@@ -180803,7 +181141,7 @@ function isTodoItem(value) {
|
|
|
180803
181141
|
|
|
180804
181142
|
// src/hooks/magic-context/transform-postprocess-phase.ts
|
|
180805
181143
|
var DEGRADE_CACHE_WARNING_THRESHOLD = 10;
|
|
180806
|
-
var degradedCacheCountBySession = new
|
|
181144
|
+
var degradedCacheCountBySession = new BoundedSessionMap(100);
|
|
180807
181145
|
function resetDegradedCacheCount(sessionId) {
|
|
180808
181146
|
degradedCacheCountBySession.delete(sessionId);
|
|
180809
181147
|
}
|
|
@@ -180938,14 +181276,19 @@ async function runPostTransformPhase(args) {
|
|
|
180938
181276
|
if (didMutateFromPendingOperations && isCacheBustingPass) {
|
|
180939
181277
|
args.nudgePlacements.clear(args.sessionId);
|
|
180940
181278
|
}
|
|
180941
|
-
|
|
180942
|
-
|
|
180943
|
-
|
|
180944
|
-
|
|
180945
|
-
|
|
180946
|
-
|
|
180947
|
-
|
|
181279
|
+
try {
|
|
181280
|
+
const t8 = performance.now();
|
|
181281
|
+
const frozenStaleReduceIds = getStaleReduceStrippedIds(args.db, args.sessionId);
|
|
181282
|
+
const staleReduceResult = dropStaleReduceCalls(args.messages, frozenStaleReduceIds, {
|
|
181283
|
+
detect: isCacheBustingPass,
|
|
181284
|
+
protectedCount: args.protectedTags
|
|
181285
|
+
});
|
|
181286
|
+
if (isCacheBustingPass && staleReduceResult.newlyStrippedIds.length > 0) {
|
|
181287
|
+
addStaleReduceStrippedIds(args.db, args.sessionId, staleReduceResult.newlyStrippedIds);
|
|
180948
181288
|
}
|
|
181289
|
+
logTransformTiming(args.sessionId, "dropStaleReduceCalls", t8);
|
|
181290
|
+
} catch (error51) {
|
|
181291
|
+
sessionLog(args.sessionId, "transform failed dropping stale ctx_reduce calls:", error51);
|
|
180949
181292
|
}
|
|
180950
181293
|
const m0M1Enabled = args.fullFeatureMode && args.m0M1 !== undefined && (!!args.m0M1.projectPath || !!args.m0M1.projectDirectory);
|
|
180951
181294
|
if (m0M1Enabled && args.m0M1) {
|
|
@@ -180977,6 +181320,7 @@ async function runPostTransformPhase(args) {
|
|
|
180977
181320
|
sessionLog(args.sessionId, "transform: legacy fallback injection also failed:", getErrorMessage(fallbackError));
|
|
180978
181321
|
}
|
|
180979
181322
|
}
|
|
181323
|
+
clearInjectionCache(args.sessionId);
|
|
180980
181324
|
}
|
|
180981
181325
|
logTransformTiming(args.sessionId, "pp.injectM0M1", tInjectM0M1);
|
|
180982
181326
|
} else if (args.fullFeatureMode && args.pendingCompartmentInjection) {
|
|
@@ -181000,7 +181344,7 @@ async function runPostTransformPhase(args) {
|
|
|
181000
181344
|
if (missingIds.length > 0) {
|
|
181001
181345
|
for (const id of missingIds)
|
|
181002
181346
|
persistedIds.delete(id);
|
|
181003
|
-
|
|
181347
|
+
applyStrippedPlaceholderDelta(args.db, args.sessionId, { remove: missingIds });
|
|
181004
181348
|
}
|
|
181005
181349
|
}
|
|
181006
181350
|
if (isCacheBustingPass) {
|
|
@@ -181009,11 +181353,13 @@ async function runPostTransformPhase(args) {
|
|
|
181009
181353
|
const systemInjectedResult = stripSystemInjectedMessages(args.messages, protectedTailStart, args.liveProviderID);
|
|
181010
181354
|
const newlyNeutralized = droppedResult.sentineledIds.length + systemInjectedResult.sentineledIds.length;
|
|
181011
181355
|
if (newlyNeutralized > 0) {
|
|
181012
|
-
|
|
181356
|
+
const addedIds = [
|
|
181357
|
+
...droppedResult.sentineledIds,
|
|
181358
|
+
...systemInjectedResult.sentineledIds
|
|
181359
|
+
];
|
|
181360
|
+
for (const id of addedIds)
|
|
181013
181361
|
persistedIds.add(id);
|
|
181014
|
-
|
|
181015
|
-
persistedIds.add(id);
|
|
181016
|
-
setStrippedPlaceholderIds(args.db, args.sessionId, persistedIds);
|
|
181362
|
+
applyStrippedPlaceholderDelta(args.db, args.sessionId, { add: addedIds });
|
|
181017
181363
|
sessionLog(args.sessionId, `neutralized ${droppedResult.stripped} dropped + ${systemInjectedResult.stripped} system-injected messages (${newlyNeutralized} new, ${persistedIds.size} total persisted)`);
|
|
181018
181364
|
}
|
|
181019
181365
|
}
|
|
@@ -181532,7 +181878,11 @@ function createTransform(deps) {
|
|
|
181532
181878
|
lastEmergencyNotificationCount.set(sessionId, historianFailureState.failureCount);
|
|
181533
181879
|
sendIgnoredMessage(deps.client, sessionId, `⚠️ Context Emergency — Context is at ${emergencyPercentage}% and historian has failed ${historianFailureState.failureCount} times (last error: ${truncateHistorianEmergencyError(historianFailureState.lastError)}). Aborting this message to prevent context overflow. Historian will retry automatically. If this persists, change your historian model in magic-context.jsonc and restart OpenCode.`, notificationParams);
|
|
181534
181880
|
}
|
|
181535
|
-
startRecoveryRun();
|
|
181881
|
+
const recoveryStarted = startRecoveryRun();
|
|
181882
|
+
if (!recoveryStarted && !getEligibleHistoryForCompartment()) {
|
|
181883
|
+
clearEmergencyRecovery(db, sessionId);
|
|
181884
|
+
sessionLog(sessionId, "transform: disarming emergency recovery — no eligible pre-tail history to compact (would otherwise loop at 95%)");
|
|
181885
|
+
}
|
|
181536
181886
|
sessionLog(sessionId, `EMERGENCY: aborting session at ${emergencyPercentage}%, historian failures: ${historianFailureState.failureCount}`);
|
|
181537
181887
|
} else if (fullFeatureMode && isFirstTransformPassForSession && historianFailureState.failureCount > 0 && getEligibleHistoryForCompartment() && startRecoveryRun()) {
|
|
181538
181888
|
sessionLog(sessionId, `transform: historian recovery triggered on session load after ${historianFailureState.failureCount} failure(s)`);
|
|
@@ -182575,6 +182925,8 @@ function createEventHook(args) {
|
|
|
182575
182925
|
sessionLog(assistantInfo.sessionID, `model changed (${previous.providerID}/${previous.modelID} -> ${assistantInfo.providerID}/${assistantInfo.modelID}), clearing historian failure state and reasoning watermark`);
|
|
182576
182926
|
clearHistorianFailureState(args.db, assistantInfo.sessionID);
|
|
182577
182927
|
clearPersistedReasoningWatermark(args.db, assistantInfo.sessionID);
|
|
182928
|
+
clearDetectedContextLimit(args.db, assistantInfo.sessionID);
|
|
182929
|
+
clearEmergencyRecovery(args.db, assistantInfo.sessionID);
|
|
182578
182930
|
updateSessionMeta(args.db, assistantInfo.sessionID, {
|
|
182579
182931
|
clearedReasoningThroughTag: 0,
|
|
182580
182932
|
observedSafeInputTokens: 0,
|
|
@@ -182678,6 +183030,7 @@ var PARTNER_FRAME_CLOSER_REDUCE = `
|
|
|
182678
183030
|
Reduction prompts are routine housekeeping to keep the session fast and cheap — act on them as light maintenance, never as scarcity warnings. Keep individual operations efficient, but never let context size change *what* work you take on or *how thoroughly* you do it.`;
|
|
182679
183031
|
var PARTNER_FRAME_CLOSER_NO_REDUCE = `
|
|
182680
183032
|
Context is managed for you entirely automatically — there's nothing to prune and no warnings to act on. Stay reasonably concise per operation, and never let context size change *what* work you take on or *how thoroughly* you do it.`;
|
|
183033
|
+
var CTX_NOTE_GUIDANCE = `Use \`ctx_note\` ONLY for genuinely future concerns — something to revisit much later, not work coming up in the next few turns (that's already in your active context) and not active multi-step work (use todos for that). Magic Context preserves your full context across both compaction and restarts, so an upcoming restart or "let's come back to this later" is never a reason to take a note — nothing is lost either way. Notes you do take survive compression and resurface at natural work boundaries (after commits, historian runs, todo completion).`;
|
|
182681
183034
|
function getToolHistoryGuidance(dropToolStructure) {
|
|
182682
183035
|
if (dropToolStructure) {
|
|
182683
183036
|
return `Compressed history intentionally omits tool calls and their outputs — summaries like "I edited file X" are historian records, not patterns to replicate. In the live conversation, older tool calls and their results are cleaned up to save context — you may see your own past messages referencing actions without the corresponding tool call or result visible. This is normal context management. ALWAYS use real tool calls; never simulate, fabricate, or inline tool outputs in your text. If there is no tool result message, the action did not happen. NEVER simulate, hallucinate or claim tool calls, command output, search results, file edits, or diffs in plain text as if they actually occurred.`;
|
|
@@ -182688,7 +183041,8 @@ var BASE_INTRO = (protectedTags, dropToolStructure) => `Messages and tool output
|
|
|
182688
183041
|
Use \`ctx_reduce\` to manage context size. It supports one operation:
|
|
182689
183042
|
- \`drop\`: Remove entirely (best for tool outputs you already acted on).
|
|
182690
183043
|
Syntax: "3-5", "1,2,9", or "1-5,8,12-15". Last ${protectedTags} tags are protected.
|
|
182691
|
-
|
|
183044
|
+
Do not announce or narrate \`ctx_reduce\` drops — just call the tool silently. Saying "I'll drop these outputs" wastes tokens the user does not care about.
|
|
183045
|
+
${CTX_NOTE_GUIDANCE}
|
|
182692
183046
|
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.
|
|
182693
183047
|
**Save to memory proactively**: If you spent multiple turns finding something (a file path, a DB location, a config pattern, a workaround), save it with \`ctx_memory\` so future sessions don't repeat the search. Examples:
|
|
182694
183048
|
- Found a project's source code path after searching → \`ctx_memory(action="write", category="ENVIRONMENT", content="OpenCode source is at ~/Work/OSS/opencode")\`
|
|
@@ -182708,7 +183062,7 @@ NEVER drop large ranges blindly (e.g., "1-50"). Review each tag before deciding.
|
|
|
182708
183062
|
NEVER drop user messages — they are short and will be summarized by compartmentalization automatically. Dropping them loses context the historian needs.
|
|
182709
183063
|
NEVER drop assistant text messages unless they are exceptionally large. Your conversation messages are lightweight; only large tool outputs are worth dropping.
|
|
182710
183064
|
Before your turn finishes, consider using \`ctx_reduce\` to drop large tool outputs you no longer need.`;
|
|
182711
|
-
var BASE_INTRO_NO_REDUCE = (dropToolStructure) =>
|
|
183065
|
+
var BASE_INTRO_NO_REDUCE = (dropToolStructure) => `${CTX_NOTE_GUIDANCE}
|
|
182712
183066
|
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.
|
|
182713
183067
|
**Save to memory proactively**: If you spent multiple turns finding something (a file path, a DB location, a config pattern, a workaround), save it with \`ctx_memory\` so future sessions don't repeat the search. Examples:
|
|
182714
183068
|
- Found a project's source code path after searching → \`ctx_memory(action="write", category="ENVIRONMENT", content="OpenCode source is at ~/Work/OSS/opencode")\`
|
|
@@ -184552,7 +184906,7 @@ function projectIdentityForStoredPath(rawProjectPath) {
|
|
|
184552
184906
|
return normalizeStoredProjectPath(rawProjectPath);
|
|
184553
184907
|
}
|
|
184554
184908
|
function memoryBelongsToProject(memory, projectPath) {
|
|
184555
|
-
return memory.projectPath
|
|
184909
|
+
return storedPathBelongsToIdentity(memory.projectPath, projectPath);
|
|
184556
184910
|
}
|
|
184557
184911
|
function updateMemoryContentInCurrentTransaction(db, memory, content, normalizedHash) {
|
|
184558
184912
|
db.prepare("UPDATE memories SET content = ?, normalized_hash = ?, updated_at = ? WHERE id = ?").run(content, normalizedHash, Date.now(), memory.id);
|
|
@@ -185045,7 +185399,7 @@ import { tool as tool4 } from "@opencode-ai/plugin";
|
|
|
185045
185399
|
// src/features/magic-context/range-parser.ts
|
|
185046
185400
|
function parseRangeString(input) {
|
|
185047
185401
|
const maxRangeElements = 1000;
|
|
185048
|
-
const trimmed = input.trim();
|
|
185402
|
+
const trimmed = input.replace(/§/g, "").trim();
|
|
185049
185403
|
if (trimmed === "") {
|
|
185050
185404
|
throw new Error("Range string must not be empty");
|
|
185051
185405
|
}
|
|
@@ -185291,6 +185645,7 @@ function createCtxSearchTool(deps) {
|
|
|
185291
185645
|
return "Error: 'query' is required.";
|
|
185292
185646
|
}
|
|
185293
185647
|
const lastCompartmentEnd = getLastCompartmentEndMessage(deps.db, toolContext.sessionID);
|
|
185648
|
+
const messageOrdinalCutoff = lastCompartmentEnd >= 0 ? lastCompartmentEnd : 0;
|
|
185294
185649
|
const visibleMemoryIds = getVisibleMemoryIds(deps.db, toolContext.sessionID);
|
|
185295
185650
|
const projectPath = deps.resolveProjectPath(toolContext.directory);
|
|
185296
185651
|
await deps.ensureProjectRegistered?.(toolContext.directory, deps.db);
|
|
@@ -185308,7 +185663,7 @@ function createCtxSearchTool(deps) {
|
|
|
185308
185663
|
},
|
|
185309
185664
|
isEmbeddingRuntimeEnabled: () => embeddingEnabled === true,
|
|
185310
185665
|
readMessages: deps.readMessages,
|
|
185311
|
-
maxMessageOrdinal:
|
|
185666
|
+
maxMessageOrdinal: messageOrdinalCutoff,
|
|
185312
185667
|
gitCommitsEnabled,
|
|
185313
185668
|
sources: normalizeSources(args.sources),
|
|
185314
185669
|
visibleMemoryIds,
|
|
@@ -185411,7 +185766,7 @@ init_models_dev_cache();
|
|
|
185411
185766
|
|
|
185412
185767
|
// src/shared/rpc-server.ts
|
|
185413
185768
|
init_logger();
|
|
185414
|
-
import { randomBytes } from "node:crypto";
|
|
185769
|
+
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
185415
185770
|
import {
|
|
185416
185771
|
mkdirSync as mkdirSync9,
|
|
185417
185772
|
readdirSync,
|
|
@@ -185478,6 +185833,14 @@ function isValidPort(port) {
|
|
|
185478
185833
|
}
|
|
185479
185834
|
|
|
185480
185835
|
// src/shared/rpc-server.ts
|
|
185836
|
+
function tokensMatch(presented, expected) {
|
|
185837
|
+
const a = Buffer.from(presented, "utf8");
|
|
185838
|
+
const b = Buffer.from(expected, "utf8");
|
|
185839
|
+
if (a.length !== b.length)
|
|
185840
|
+
return false;
|
|
185841
|
+
return timingSafeEqual(a, b);
|
|
185842
|
+
}
|
|
185843
|
+
|
|
185481
185844
|
class MagicContextRpcServer {
|
|
185482
185845
|
server = null;
|
|
185483
185846
|
port = 0;
|
|
@@ -185565,7 +185928,7 @@ class MagicContextRpcServer {
|
|
|
185565
185928
|
}
|
|
185566
185929
|
const auth = req.headers.authorization;
|
|
185567
185930
|
const presented = typeof auth === "string" ? auth.replace(/^Bearer\s+/i, "") : "";
|
|
185568
|
-
if (presented
|
|
185931
|
+
if (!tokensMatch(presented, this.token)) {
|
|
185569
185932
|
res.writeHead(401, { "Content-Type": "application/json" });
|
|
185570
185933
|
res.end(JSON.stringify({ error: "Unauthorized" }));
|
|
185571
185934
|
req.resume();
|