@cortexkit/opencode-magic-context 0.31.4 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/magic-context-prompt.d.ts.map +1 -1
- package/dist/config/project-security.d.ts +2 -0
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/config/schema/magic-context.d.ts +9 -0
- package/dist/config/schema/magic-context.d.ts.map +1 -1
- package/dist/features/magic-context/message-index-async.d.ts +2 -1
- package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
- package/dist/features/magic-context/storage-clone.d.ts +34 -0
- package/dist/features/magic-context/storage-clone.d.ts.map +1 -0
- package/dist/features/magic-context/storage-tags.d.ts +11 -0
- package/dist/features/magic-context/storage-tags.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +2 -1
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/tagger.d.ts +10 -0
- package/dist/features/magic-context/tagger.d.ts.map +1 -1
- package/dist/hooks/magic-context/apply-operations.d.ts +24 -0
- package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-trigger.d.ts +7 -1
- package/dist/hooks/magic-context/compartment-trigger.d.ts.map +1 -1
- package/dist/hooks/magic-context/decay-render.d.ts +2 -0
- package/dist/hooks/magic-context/decay-render.d.ts.map +1 -1
- package/dist/hooks/magic-context/hook.d.ts +3 -0
- package/dist/hooks/magic-context/hook.d.ts.map +1 -1
- package/dist/hooks/magic-context/inject-compartments.d.ts +7 -9
- package/dist/hooks/magic-context/inject-compartments.d.ts.map +1 -1
- package/dist/hooks/magic-context/m0-token-breakdown.d.ts +5 -4
- package/dist/hooks/magic-context/m0-token-breakdown.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-chunk.d.ts +2 -0
- package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-db.d.ts +2 -2
- package/dist/hooks/magic-context/read-session-raw.d.ts +8 -0
- package/dist/hooks/magic-context/read-session-raw.d.ts.map +1 -1
- package/dist/hooks/magic-context/shadow-sender.d.ts +262 -0
- package/dist/hooks/magic-context/shadow-sender.d.ts.map +1 -0
- package/dist/hooks/magic-context/tag-content-primitives.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts +8 -0
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/hooks/magic-context/temporal-awareness.d.ts +3 -3
- package/dist/hooks/magic-context/transform-operations.d.ts +1 -1
- package/dist/hooks/magic-context/transform-operations.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +5 -0
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts +4 -1
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/index.js +1665 -140
- package/dist/plugin/hooks/create-session-hooks.d.ts +48 -175
- package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/shared/tag-transcript.d.ts +18 -0
- package/dist/shared/tag-transcript.d.ts.map +1 -1
- package/dist/tools/ctx-expand/constants.d.ts +1 -1
- package/dist/tools/ctx-expand/constants.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/shared/rpc-server.ts +8 -0
- package/src/shared/tag-transcript.test.ts +253 -7
- package/src/shared/tag-transcript.ts +422 -114
package/dist/index.js
CHANGED
|
@@ -15406,8 +15406,11 @@ var init_magic_context = __esm(() => {
|
|
|
15406
15406
|
provider: "local",
|
|
15407
15407
|
model: DEFAULT_LOCAL_EMBEDDING_MODEL
|
|
15408
15408
|
}).describe("Embedding provider configuration"),
|
|
15409
|
-
temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add
|
|
15409
|
+
temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add compact date ranges to compartment headings. Gives the agent a sense of session pacing and "how long ago" across multi-day sessions. Graduated from experimental.temporal_awareness; default: true (set false to opt out).'),
|
|
15410
15410
|
keep_subagents: exports_external.boolean().default(false).describe("Debug: keep the child sessions Magic Context spawns for its own subagents (historian, dreamer, sidekick, memory-migration) instead of deleting them on success. Useful for short-term inspection/data collection — their full transcript (prompt, tool calls, token usage, output) stays in the host session store. Kept sessions accumulate until manually cleared; leave false for normal use. Requires a restart to take effect."),
|
|
15411
|
+
shadow_transform: exports_external.object({
|
|
15412
|
+
enabled: exports_external.boolean().default(false).describe("Dev flag: mirror every transform pass to the Magic Context Rust module over subc and byte-compare outputs. No behavior impact; default off.")
|
|
15413
|
+
}).default({ enabled: false }).describe("Developer-only shadow transform lane. When enabled, the plugin fire-and-forgets a copy of each finalized transform to the Rust module for comparison; the live output is unchanged."),
|
|
15411
15414
|
todowrite: exports_external.object({
|
|
15412
15415
|
enabled: exports_external.boolean().default(true).describe("Pi only: register Magic Context's todowrite task-list tool. Disable if you use your own todo extension. OpenCode ships its own built-in todowrite; this setting has no effect there."),
|
|
15413
15416
|
overlay: exports_external.boolean().default(true).describe("Pi only: show the persistent todo overlay above the editor while tasks are active.")
|
|
@@ -150066,6 +150069,12 @@ function parseJsonRecord(value) {
|
|
|
150066
150069
|
return null;
|
|
150067
150070
|
}
|
|
150068
150071
|
}
|
|
150072
|
+
function isRawCompactionSummaryInfo(info) {
|
|
150073
|
+
if (info === null || typeof info !== "object" || Array.isArray(info))
|
|
150074
|
+
return false;
|
|
150075
|
+
const candidate = info;
|
|
150076
|
+
return candidate.summary === true && candidate.finish === "stop";
|
|
150077
|
+
}
|
|
150069
150078
|
function parseJsonUnknown(value) {
|
|
150070
150079
|
try {
|
|
150071
150080
|
return JSON.parse(value);
|
|
@@ -150088,7 +150097,7 @@ function attachRawPartVersion(value, timeUpdated) {
|
|
|
150088
150097
|
return value;
|
|
150089
150098
|
}
|
|
150090
150099
|
function readRawSessionMessagesFromDb(db, sessionId) {
|
|
150091
|
-
const messageRows = db.prepare("SELECT id, data, time_updated FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId).filter(isRawMessageRow);
|
|
150100
|
+
const messageRows = db.prepare("SELECT id, data, time_created, time_updated FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId).filter(isRawMessageRow);
|
|
150092
150101
|
const partRows = db.prepare("SELECT message_id, data, time_updated FROM part WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId).filter(isRawPartRow);
|
|
150093
150102
|
const partsByMessageId = new Map;
|
|
150094
150103
|
for (const part of partRows) {
|
|
@@ -150096,10 +150105,7 @@ function readRawSessionMessagesFromDb(db, sessionId) {
|
|
|
150096
150105
|
list.push(attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated));
|
|
150097
150106
|
partsByMessageId.set(part.message_id, list);
|
|
150098
150107
|
}
|
|
150099
|
-
const filtered = messageRows.filter((row) =>
|
|
150100
|
-
const info = parseJsonRecord(row.data);
|
|
150101
|
-
return !(info?.summary === true && info?.finish === "stop");
|
|
150102
|
-
});
|
|
150108
|
+
const filtered = messageRows.filter((row) => !isRawCompactionSummaryInfo(parseJsonRecord(row.data)));
|
|
150103
150109
|
return filtered.flatMap((row, index) => {
|
|
150104
150110
|
const info = parseJsonRecord(row.data);
|
|
150105
150111
|
if (!info)
|
|
@@ -150110,10 +150116,21 @@ function readRawSessionMessagesFromDb(db, sessionId) {
|
|
|
150110
150116
|
id: row.id,
|
|
150111
150117
|
role,
|
|
150112
150118
|
parts: partsByMessageId.get(row.id) ?? [],
|
|
150119
|
+
createdAt: row.time_created ?? null,
|
|
150113
150120
|
version: row.time_updated ?? null
|
|
150114
150121
|
};
|
|
150115
150122
|
});
|
|
150116
150123
|
}
|
|
150124
|
+
function readRawSessionMessageIdOrdinalsFromDb(db, sessionId) {
|
|
150125
|
+
const messageRows = db.prepare("SELECT id, data FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId).filter(isRawMessageRow);
|
|
150126
|
+
const filtered = messageRows.filter((row) => !isRawCompactionSummaryInfo(parseJsonRecord(row.data)));
|
|
150127
|
+
const ordinalById = new Map;
|
|
150128
|
+
filtered.forEach((row, index) => {
|
|
150129
|
+
if (parseJsonRecord(row.data))
|
|
150130
|
+
ordinalById.set(row.id, index + 1);
|
|
150131
|
+
});
|
|
150132
|
+
return ordinalById;
|
|
150133
|
+
}
|
|
150117
150134
|
function isAnchorRow(row) {
|
|
150118
150135
|
return row !== null && typeof row === "object" && typeof row.time_created === "number" && typeof row.id === "string";
|
|
150119
150136
|
}
|
|
@@ -150124,7 +150141,7 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
|
|
|
150124
150141
|
const anchorInfo = parseJsonRecord(anchorRow.data ?? "");
|
|
150125
150142
|
if (anchorInfo?.summary === true && anchorInfo?.finish === "stop")
|
|
150126
150143
|
return null;
|
|
150127
|
-
const messageRows = db.prepare(`SELECT id, data, time_updated FROM message
|
|
150144
|
+
const messageRows = db.prepare(`SELECT id, data, time_created, time_updated FROM message
|
|
150128
150145
|
WHERE session_id = ?
|
|
150129
150146
|
AND (time_created > ? OR (time_created = ? AND id >= ?))
|
|
150130
150147
|
ORDER BY time_created ASC, id ASC`).all(sessionId, anchorRow.time_created, anchorRow.time_created, anchorRow.id).filter(isRawMessageRow);
|
|
@@ -150160,6 +150177,7 @@ function readRawSessionTailFromDb(db, sessionId, baseOrdinal, anchorMessageId) {
|
|
|
150160
150177
|
id: row.id,
|
|
150161
150178
|
role: typeof info.role === "string" ? info.role : "unknown",
|
|
150162
150179
|
parts: partsByMessageId.get(row.id) ?? [],
|
|
150180
|
+
createdAt: row.time_created ?? null,
|
|
150163
150181
|
version: row.time_updated ?? null
|
|
150164
150182
|
});
|
|
150165
150183
|
ord += 1;
|
|
@@ -150223,7 +150241,7 @@ function readRawSessionMessageByIdFromDb(db, sessionId, messageId) {
|
|
|
150223
150241
|
return null;
|
|
150224
150242
|
}
|
|
150225
150243
|
const info = parseJsonRecord(row.data);
|
|
150226
|
-
if (!info || info
|
|
150244
|
+
if (!info || isRawCompactionSummaryInfo(info)) {
|
|
150227
150245
|
return null;
|
|
150228
150246
|
}
|
|
150229
150247
|
const ordinalRow = db.prepare(`SELECT COUNT(*) AS ordinal FROM message
|
|
@@ -150242,6 +150260,7 @@ function readRawSessionMessageByIdFromDb(db, sessionId, messageId) {
|
|
|
150242
150260
|
id: row.id,
|
|
150243
150261
|
role,
|
|
150244
150262
|
parts: partRows.map((part) => attachRawPartVersion(parseJsonUnknown(part.data), part.time_updated)),
|
|
150263
|
+
createdAt: row.time_created,
|
|
150245
150264
|
version: row.time_updated ?? null
|
|
150246
150265
|
};
|
|
150247
150266
|
}
|
|
@@ -150758,6 +150777,17 @@ function primeInMemoryTailRawMessageCache(args) {
|
|
|
150758
150777
|
activeAbsoluteCountCache?.set(sessionId, absoluteMessageCount);
|
|
150759
150778
|
return true;
|
|
150760
150779
|
}
|
|
150780
|
+
function readRawSessionMessageIdOrdinals(sessionId) {
|
|
150781
|
+
const provider = sessionProviders.get(sessionId);
|
|
150782
|
+
if (provider?.readMessageIdOrdinals)
|
|
150783
|
+
return provider.readMessageIdOrdinals();
|
|
150784
|
+
if (provider) {
|
|
150785
|
+
return new Map(provider.readMessages().map((message) => [message.id, message.ordinal]));
|
|
150786
|
+
}
|
|
150787
|
+
if (!openCodeDbExists())
|
|
150788
|
+
return new Map;
|
|
150789
|
+
return withReadOnlySessionDb((db) => readRawSessionMessageIdOrdinalsFromDb(db, sessionId));
|
|
150790
|
+
}
|
|
150761
150791
|
function readRawSessionMessageById(sessionId, messageId) {
|
|
150762
150792
|
const provider = sessionProviders.get(sessionId);
|
|
150763
150793
|
if (provider?.readMessageById) {
|
|
@@ -153165,6 +153195,9 @@ var init_project_docs_hash = __esm(() => {
|
|
|
153165
153195
|
docsCache = new Map;
|
|
153166
153196
|
});
|
|
153167
153197
|
|
|
153198
|
+
// src/features/magic-context/storage-clone.ts
|
|
153199
|
+
var init_storage_clone = () => {};
|
|
153200
|
+
|
|
153168
153201
|
// src/shared/stable-json.ts
|
|
153169
153202
|
function stableStringify(value, seen = new WeakSet) {
|
|
153170
153203
|
if (value === undefined)
|
|
@@ -157047,7 +157080,7 @@ function deleteToolTagsByOwner(db, sessionId, ownerMsgId) {
|
|
|
157047
157080
|
const result = getDeleteToolTagsByOwnerStatement(db).run(sessionId, ownerMsgId);
|
|
157048
157081
|
return result.changes ?? 0;
|
|
157049
157082
|
}
|
|
157050
|
-
var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements, CONTENT_ID_SUFFIX, RECLAIM_HINT_EXCLUDED_TOOLS, RECLAIM_HINT_MIN_TOKENS = 250, RECLAIM_HINT_EXCLUDED_LIST, updateTagTokenCountStatements, updateTagInputTokenCountStatements, getOwnerScopedToolTagNumbersStatements, getMinMessageTagNumberForRawIdStatements, TAGGER_FLOOR_SCAN_MESSAGES = 8, TAGGER_FLOOR_MAX_PROBES = 64, TAGGER_FLOOR_SAFETY_MARGIN = 256, TAGGER_FLOOR_PER_SKIP_MARGIN = 64, TAG_SELECT_COLUMNS = "id, message_id, type, status, drop_mode, tool_name, input_byte_size, byte_size, reasoning_byte_size, session_id, tag_number, caveman_depth, tool_owner_message_id", getActiveTagsBySessionStatements, getMaxDroppedTagNumberStatements, getToolTagNumberByOwnerStatements, getNullOwnerToolTagStatements, adoptNullOwnerToolTagStatements, deleteToolTagsByOwnerStatements;
|
|
157083
|
+
var insertTagStatements, updateTagStatusStatements, updateTagDropModeStatements, updateTagMessageIdStatements, getTagNumbersByMessageIdStatements, deleteTagsByMessageIdStatements, getMaxTagNumberBySessionStatements, getTagNumberByMessageIdStatements, hasPiFallbackMessageTagStatements, updateTagByteSizeStatements, updateTagInputByteSizeStatements, CONTENT_ID_SUFFIX, RECLAIM_HINT_EXCLUDED_TOOLS, RECLAIM_HINT_MIN_TOKENS = 250, RECLAIM_HINT_EXCLUDED_LIST, updateTagTokenCountStatements, updateTagInputTokenCountStatements, getOwnerScopedToolTagNumbersStatements, getMinMessageTagNumberForRawIdStatements, TAGGER_FLOOR_SCAN_MESSAGES = 8, TAGGER_FLOOR_MAX_PROBES = 64, TAGGER_FLOOR_SAFETY_MARGIN = 256, TAGGER_FLOOR_PER_SKIP_MARGIN = 64, TAG_SELECT_COLUMNS = "id, message_id, type, status, drop_mode, tool_name, input_byte_size, byte_size, reasoning_byte_size, session_id, tag_number, caveman_depth, tool_owner_message_id", getActiveTagsBySessionStatements, getMaxDroppedTagNumberStatements, getToolTagNumberByOwnerStatements, getNullOwnerToolTagStatements, adoptNullOwnerToolTagStatements, deleteToolTagsByOwnerStatements;
|
|
157051
157084
|
var init_storage_tags = __esm(() => {
|
|
157052
157085
|
insertTagStatements = new WeakMap;
|
|
157053
157086
|
updateTagStatusStatements = new WeakMap;
|
|
@@ -157057,6 +157090,7 @@ var init_storage_tags = __esm(() => {
|
|
|
157057
157090
|
deleteTagsByMessageIdStatements = new WeakMap;
|
|
157058
157091
|
getMaxTagNumberBySessionStatements = new WeakMap;
|
|
157059
157092
|
getTagNumberByMessageIdStatements = new WeakMap;
|
|
157093
|
+
hasPiFallbackMessageTagStatements = new WeakMap;
|
|
157060
157094
|
updateTagByteSizeStatements = new WeakMap;
|
|
157061
157095
|
updateTagInputByteSizeStatements = new WeakMap;
|
|
157062
157096
|
CONTENT_ID_SUFFIX = /:(?:p|file)\d+$/;
|
|
@@ -157093,6 +157127,7 @@ var init_storage = __esm(async () => {
|
|
|
157093
157127
|
init_migrations();
|
|
157094
157128
|
init_project_docs_hash();
|
|
157095
157129
|
init_project_identity2();
|
|
157130
|
+
init_storage_clone();
|
|
157096
157131
|
init_storage_m0_mutation_log();
|
|
157097
157132
|
init_storage_memory_mutation_log();
|
|
157098
157133
|
init_storage_meta_shared();
|
|
@@ -178822,12 +178857,26 @@ var init_decay_curve = __esm(() => {
|
|
|
178822
178857
|
});
|
|
178823
178858
|
|
|
178824
178859
|
// src/hooks/magic-context/decay-render.ts
|
|
178825
|
-
function escapeXmlAttr2(s) {
|
|
178826
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
178827
|
-
}
|
|
178828
178860
|
function escapeXmlContent2(s) {
|
|
178829
178861
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
178830
178862
|
}
|
|
178863
|
+
function formatDateRange(startDate, endDate) {
|
|
178864
|
+
if (!startDate || !endDate)
|
|
178865
|
+
return "";
|
|
178866
|
+
if (startDate === endDate)
|
|
178867
|
+
return startDate;
|
|
178868
|
+
if (startDate.slice(0, 7) === endDate.slice(0, 7))
|
|
178869
|
+
return `${startDate}→${endDate.slice(8)}`;
|
|
178870
|
+
return `${startDate}→${endDate}`;
|
|
178871
|
+
}
|
|
178872
|
+
function compartmentHeading(c) {
|
|
178873
|
+
const dateRange = formatDateRange(c.startDate, c.endDate);
|
|
178874
|
+
const dateSegment = dateRange ? ` · ${dateRange}` : "";
|
|
178875
|
+
return `## ${c.startMessage}-${c.endMessage}${dateSegment} · ${c.title}`;
|
|
178876
|
+
}
|
|
178877
|
+
function guardCompartmentBody(body) {
|
|
178878
|
+
return body.replace(/^## /gm, " ## ");
|
|
178879
|
+
}
|
|
178831
178880
|
function isTieredRow(c) {
|
|
178832
178881
|
return typeof c.p1 === "string" && c.p1.length > 0;
|
|
178833
178882
|
}
|
|
@@ -178857,25 +178906,22 @@ function renderCompartmentAtTier(c, tier2) {
|
|
|
178857
178906
|
return renderOneCompartment(c, tier2);
|
|
178858
178907
|
}
|
|
178859
178908
|
function renderOneCompartment(c, tier2) {
|
|
178860
|
-
const baseAttrs = `start="${c.startMessage}" end="${c.endMessage}" title="${escapeXmlAttr2(c.title)}"`;
|
|
178861
178909
|
if (tier2 >= 5)
|
|
178862
178910
|
return "";
|
|
178911
|
+
const heading = compartmentHeading(c);
|
|
178863
178912
|
if (c.legacy === 1 || !isTieredRow(c)) {
|
|
178864
178913
|
const flat = (c.content ?? "").trim();
|
|
178865
178914
|
if (tier2 >= 4 || flat.length === 0)
|
|
178866
|
-
return
|
|
178867
|
-
|
|
178868
|
-
|
|
178869
|
-
|
|
178870
|
-
"</compartment>"
|
|
178871
|
-
].join(`
|
|
178872
|
-
`);
|
|
178915
|
+
return heading;
|
|
178916
|
+
const body2 = guardCompartmentBody(escapeXmlContent2(legacyBodyForTier(flat, tier2)));
|
|
178917
|
+
return `${heading}
|
|
178918
|
+
${body2}`;
|
|
178873
178919
|
}
|
|
178874
178920
|
const body = tierBody(c, tier2);
|
|
178875
178921
|
if (body.length === 0)
|
|
178876
|
-
return
|
|
178877
|
-
return
|
|
178878
|
-
|
|
178922
|
+
return heading;
|
|
178923
|
+
return `${heading}
|
|
178924
|
+
${guardCompartmentBody(escapeXmlContent2(body))}`;
|
|
178879
178925
|
}
|
|
178880
178926
|
function computeTiers(compartments, historyBudgetTokens) {
|
|
178881
178927
|
const v2Compartments = compartments.map((c, originalIndex) => ({ c, originalIndex })).filter(({ c }) => c.legacy !== 1);
|
|
@@ -178934,6 +178980,7 @@ function extractM0Block(m0Text, tag) {
|
|
|
178934
178980
|
const m = m0Text.match(new RegExp(`<${tag}>[\\s\\S]*?</${tag}>`));
|
|
178935
178981
|
return m ? m[0] : null;
|
|
178936
178982
|
}
|
|
178983
|
+
var DEFAULT_HISTORY_BUDGET_TOKENS = 60000;
|
|
178937
178984
|
var init_decay_render = __esm(() => {
|
|
178938
178985
|
init_decay_curve();
|
|
178939
178986
|
init_read_session_formatting();
|
|
@@ -179057,30 +179104,7 @@ function getVisibleMemoryIds(db, sessionId) {
|
|
|
179057
179104
|
}
|
|
179058
179105
|
}
|
|
179059
179106
|
function renderMemoryBlock(memories) {
|
|
179060
|
-
|
|
179061
|
-
for (const m of memories) {
|
|
179062
|
-
const existing = byCategory.get(m.category);
|
|
179063
|
-
if (existing) {
|
|
179064
|
-
existing.push(m);
|
|
179065
|
-
} else {
|
|
179066
|
-
byCategory.set(m.category, [m]);
|
|
179067
|
-
}
|
|
179068
|
-
}
|
|
179069
|
-
const sections = [];
|
|
179070
|
-
for (const category of CATEGORY_PRIORITY) {
|
|
179071
|
-
const categoryMemories = byCategory.get(category);
|
|
179072
|
-
if (!categoryMemories || categoryMemories.length === 0) {
|
|
179073
|
-
continue;
|
|
179074
|
-
}
|
|
179075
|
-
sections.push(`<${category}>`, ...categoryMemories.map((m) => `- ${escapeXmlContent(m.content)}`), `</${category}>`);
|
|
179076
|
-
}
|
|
179077
|
-
if (sections.length === 0) {
|
|
179078
|
-
return null;
|
|
179079
|
-
}
|
|
179080
|
-
return `<project-memory>
|
|
179081
|
-
${sections.join(`
|
|
179082
|
-
`)}
|
|
179083
|
-
</project-memory>`;
|
|
179107
|
+
return renderMemoryBlockV2(memories) || null;
|
|
179084
179108
|
}
|
|
179085
179109
|
function utilityTier(m) {
|
|
179086
179110
|
if (m.retrievalCount > 0)
|
|
@@ -179107,14 +179131,12 @@ function trimMemoriesToBudget(sessionId, memories, budgetTokens) {
|
|
|
179107
179131
|
return a.id - b.id;
|
|
179108
179132
|
});
|
|
179109
179133
|
const result = [];
|
|
179110
|
-
let usedTokens = 0;
|
|
179111
179134
|
for (const memory of sorted) {
|
|
179112
|
-
const
|
|
179113
|
-
if (
|
|
179135
|
+
const candidate = [...result, memory];
|
|
179136
|
+
if (estimateTokens(renderMemoryBlockV2(candidate)) > budgetTokens) {
|
|
179114
179137
|
break;
|
|
179115
179138
|
}
|
|
179116
179139
|
result.push(memory);
|
|
179117
|
-
usedTokens += memoryTokens;
|
|
179118
179140
|
}
|
|
179119
179141
|
if (result.length < memories.length) {
|
|
179120
179142
|
sessionLog(sessionId, `trimmed memories from ${memories.length} to ${result.length} to fit injection budget of ${budgetTokens} tokens`);
|
|
@@ -179386,11 +179408,18 @@ function memorySelectionOrder(left, right) {
|
|
|
179386
179408
|
return left.id - right.id;
|
|
179387
179409
|
}
|
|
179388
179410
|
function memoryRenderOrder(left, right) {
|
|
179389
|
-
const
|
|
179390
|
-
const
|
|
179391
|
-
|
|
179392
|
-
|
|
179393
|
-
|
|
179411
|
+
const leftPriority = V2_MEMORY_CATEGORIES.indexOf(left.category);
|
|
179412
|
+
const rightPriority = V2_MEMORY_CATEGORIES.indexOf(right.category);
|
|
179413
|
+
if (leftPriority >= 0 || rightPriority >= 0) {
|
|
179414
|
+
if (leftPriority < 0)
|
|
179415
|
+
return 1;
|
|
179416
|
+
if (rightPriority < 0)
|
|
179417
|
+
return -1;
|
|
179418
|
+
if (leftPriority !== rightPriority)
|
|
179419
|
+
return leftPriority - rightPriority;
|
|
179420
|
+
} else if (left.category !== right.category) {
|
|
179421
|
+
return left.category < right.category ? -1 : 1;
|
|
179422
|
+
}
|
|
179394
179423
|
return left.id - right.id;
|
|
179395
179424
|
}
|
|
179396
179425
|
function cachedStatement(cache, db, sql) {
|
|
@@ -179534,13 +179563,13 @@ function mustMaterialize(args) {
|
|
|
179534
179563
|
function trimMemoriesToBudgetV2(sessionId, memories, budgetTokens, renderOptions = {}) {
|
|
179535
179564
|
const selectionOrder = [...memories].sort(memorySelectionOrder);
|
|
179536
179565
|
const selected = [];
|
|
179537
|
-
|
|
179566
|
+
const accounting = createMemoryBlockAccounting(renderOptions);
|
|
179538
179567
|
for (const memory of selectionOrder) {
|
|
179539
|
-
const
|
|
179540
|
-
if (usedTokens +
|
|
179568
|
+
const cost = accounting.candidateCost(memory);
|
|
179569
|
+
if (accounting.usedTokens + cost > budgetTokens)
|
|
179541
179570
|
continue;
|
|
179571
|
+
accounting.admit(memory, cost);
|
|
179542
179572
|
selected.push(memory);
|
|
179543
|
-
usedTokens += memoryTokens;
|
|
179544
179573
|
}
|
|
179545
179574
|
if (selected.length < memories.length) {
|
|
179546
179575
|
sessionLog(sessionId, `v2 trimmed memories from ${memories.length} to ${selected.length} to fit injection budget of ${budgetTokens} tokens`);
|
|
@@ -179554,23 +179583,22 @@ function trimWorkspaceMemoriesToBudgetV2(sessionId, memories, budgetTokens, work
|
|
|
179554
179583
|
}
|
|
179555
179584
|
const selected = [];
|
|
179556
179585
|
const selectedIds = new Set;
|
|
179557
|
-
|
|
179558
|
-
const tokenCost = (memory) => estimateTokens(renderMemoryLineV2(memory, renderOptions.sourceNameByMemoryId?.get(memory.id)));
|
|
179586
|
+
const accounting = createMemoryBlockAccounting(renderOptions);
|
|
179559
179587
|
const trySelect = (memory) => {
|
|
179560
179588
|
if (selectedIds.has(memory.id))
|
|
179561
179589
|
return false;
|
|
179562
|
-
const
|
|
179563
|
-
if (usedTokens +
|
|
179590
|
+
const cost = accounting.candidateCost(memory);
|
|
179591
|
+
if (accounting.usedTokens + cost > budgetTokens)
|
|
179564
179592
|
return false;
|
|
179565
179593
|
selected.push(memory);
|
|
179566
179594
|
selectedIds.add(memory.id);
|
|
179567
|
-
|
|
179595
|
+
accounting.admit(memory, cost);
|
|
179568
179596
|
return true;
|
|
179569
179597
|
};
|
|
179570
179598
|
for (const memory of memories.filter((candidate) => candidate.status === "permanent").sort(memorySelectionOrder)) {
|
|
179571
179599
|
trySelect(memory);
|
|
179572
179600
|
}
|
|
179573
|
-
const remainingAfterPermanent = Math.max(0, budgetTokens - usedTokens);
|
|
179601
|
+
const remainingAfterPermanent = Math.max(0, budgetTokens - accounting.usedTokens);
|
|
179574
179602
|
const floorTokens = remainingAfterPermanent / Math.max(1, workspace.identities.length);
|
|
179575
179603
|
const byIdentity = new Map;
|
|
179576
179604
|
for (const memory of memories) {
|
|
@@ -179589,15 +179617,15 @@ function trimWorkspaceMemoriesToBudgetV2(sessionId, memories, budgetTokens, work
|
|
|
179589
179617
|
for (const memory of candidates) {
|
|
179590
179618
|
if (selectedIds.has(memory.id))
|
|
179591
179619
|
continue;
|
|
179592
|
-
const
|
|
179593
|
-
if (memberTokens +
|
|
179620
|
+
const cost = accounting.candidateCost(memory);
|
|
179621
|
+
if (memberTokens + cost > floorTokens)
|
|
179594
179622
|
continue;
|
|
179595
|
-
if (usedTokens +
|
|
179623
|
+
if (accounting.usedTokens + cost > budgetTokens)
|
|
179596
179624
|
continue;
|
|
179597
179625
|
selected.push(memory);
|
|
179598
179626
|
selectedIds.add(memory.id);
|
|
179599
|
-
|
|
179600
|
-
memberTokens +=
|
|
179627
|
+
accounting.admit(memory, cost);
|
|
179628
|
+
memberTokens += cost;
|
|
179601
179629
|
}
|
|
179602
179630
|
}
|
|
179603
179631
|
const remaining = memories.filter((memory) => !selectedIds.has(memory.id)).sort(memorySelectionOrder);
|
|
@@ -179642,6 +179670,29 @@ function readM0Compartments(db, sessionId) {
|
|
|
179642
179670
|
function nullableString(value) {
|
|
179643
179671
|
return typeof value === "string" ? value : null;
|
|
179644
179672
|
}
|
|
179673
|
+
function withCompartmentDates(sessionId, compartments, temporalAwareness) {
|
|
179674
|
+
if (!temporalAwareness || compartments.length === 0)
|
|
179675
|
+
return compartments;
|
|
179676
|
+
const messageIds = new Set;
|
|
179677
|
+
for (const compartment of compartments) {
|
|
179678
|
+
if (compartment.startMessageId)
|
|
179679
|
+
messageIds.add(compartment.startMessageId);
|
|
179680
|
+
if (compartment.endMessageId)
|
|
179681
|
+
messageIds.add(compartment.endMessageId);
|
|
179682
|
+
}
|
|
179683
|
+
const times = getMessageTimesFromOpenCodeDb(sessionId, Array.from(messageIds));
|
|
179684
|
+
return compartments.map((compartment) => {
|
|
179685
|
+
const startMs = times.get(compartment.startMessageId);
|
|
179686
|
+
const endMs = times.get(compartment.endMessageId);
|
|
179687
|
+
if (startMs === undefined || endMs === undefined)
|
|
179688
|
+
return compartment;
|
|
179689
|
+
return {
|
|
179690
|
+
...compartment,
|
|
179691
|
+
startDate: formatDate(startMs),
|
|
179692
|
+
endDate: formatDate(endMs)
|
|
179693
|
+
};
|
|
179694
|
+
});
|
|
179695
|
+
}
|
|
179645
179696
|
function rowToM0Compartment(row) {
|
|
179646
179697
|
return {
|
|
179647
179698
|
id: Number(row.id ?? 0),
|
|
@@ -179684,17 +179735,55 @@ function readNewMemoriesForM1(db, projectPath, afterId, expiryCutoff) {
|
|
|
179684
179735
|
ORDER BY ${MEMORY_CATEGORY_ORDER_SQL}, id ASC`).all(projectPath, afterId, expiryCutoff).filter(isMemoryRow);
|
|
179685
179736
|
return rows.map((row) => ({ ...row }));
|
|
179686
179737
|
}
|
|
179738
|
+
function createMemoryBlockAccounting(renderOptions) {
|
|
179739
|
+
const seenCategories = new Set;
|
|
179740
|
+
const categoryCost = new Map;
|
|
179741
|
+
return {
|
|
179742
|
+
usedTokens: estimateTokens(`<project-memory>
|
|
179743
|
+
</project-memory>`),
|
|
179744
|
+
candidateCost(memory) {
|
|
179745
|
+
const line = renderMemoryLineV2(memory, renderOptions.sourceNameByMemoryId?.get(memory.id));
|
|
179746
|
+
let cost = estimateTokens(`${line}
|
|
179747
|
+
`);
|
|
179748
|
+
if (!seenCategories.has(memory.category)) {
|
|
179749
|
+
let tags = categoryCost.get(memory.category);
|
|
179750
|
+
if (tags === undefined) {
|
|
179751
|
+
tags = estimateTokens(`<${escapeXmlAttr(memory.category)}>
|
|
179752
|
+
</${escapeXmlAttr(memory.category)}>
|
|
179753
|
+
`);
|
|
179754
|
+
categoryCost.set(memory.category, tags);
|
|
179755
|
+
}
|
|
179756
|
+
cost += tags;
|
|
179757
|
+
}
|
|
179758
|
+
return cost;
|
|
179759
|
+
},
|
|
179760
|
+
admit(memory, cost) {
|
|
179761
|
+
this.usedTokens += cost;
|
|
179762
|
+
seenCategories.add(memory.category);
|
|
179763
|
+
}
|
|
179764
|
+
};
|
|
179765
|
+
}
|
|
179687
179766
|
function renderMemoryLineV2(memory, sourceName) {
|
|
179688
|
-
const
|
|
179689
|
-
return
|
|
179767
|
+
const source = sourceName ? ` [${escapeXmlContent(sourceName)}]` : "";
|
|
179768
|
+
return `#${memory.id}${source}: ${escapeXmlContent(memory.content)}`;
|
|
179690
179769
|
}
|
|
179691
179770
|
function renderMemoryBlockV2(memories, wrapper = "project-memory", renderOptions = {}) {
|
|
179692
179771
|
if (memories.length === 0)
|
|
179693
179772
|
return "";
|
|
179773
|
+
const ordered = [...memories].sort(memoryRenderOrder);
|
|
179694
179774
|
const lines = [`<${wrapper}>`];
|
|
179695
|
-
|
|
179775
|
+
let openCategory;
|
|
179776
|
+
for (const memory of ordered) {
|
|
179777
|
+
if (memory.category !== openCategory) {
|
|
179778
|
+
if (openCategory !== undefined)
|
|
179779
|
+
lines.push(`</${escapeXmlAttr(openCategory)}>`);
|
|
179780
|
+
openCategory = memory.category;
|
|
179781
|
+
lines.push(`<${escapeXmlAttr(openCategory)}>`);
|
|
179782
|
+
}
|
|
179696
179783
|
lines.push(renderMemoryLineV2(memory, renderOptions.sourceNameByMemoryId?.get(memory.id)));
|
|
179697
179784
|
}
|
|
179785
|
+
if (openCategory !== undefined)
|
|
179786
|
+
lines.push(`</${escapeXmlAttr(openCategory)}>`);
|
|
179698
179787
|
lines.push(`</${wrapper}>`);
|
|
179699
179788
|
return lines.join(`
|
|
179700
179789
|
`);
|
|
@@ -179723,7 +179812,7 @@ function renderM0(args) {
|
|
|
179723
179812
|
const userProfile = renderUserProfileBlock(trimUserMemoriesToBudget(args.userProfileBaseline, args.userProfileBudgetTokens ?? DEFAULT_USER_PROFILE_BUDGET_TOKENS));
|
|
179724
179813
|
if (userProfile)
|
|
179725
179814
|
sections.push(userProfile);
|
|
179726
|
-
const baseBudget = args.historyBudgetTokens ??
|
|
179815
|
+
const baseBudget = args.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS2;
|
|
179727
179816
|
const effectiveBudget = baseBudget / Math.max(1, args.decayPressureMultiplier ?? 1);
|
|
179728
179817
|
const sessionHistory = renderSessionHistoryWithDecay({
|
|
179729
179818
|
compartments: args.compartments,
|
|
@@ -179813,6 +179902,7 @@ function materializeM0(options) {
|
|
|
179813
179902
|
} catch {}
|
|
179814
179903
|
throw error51;
|
|
179815
179904
|
}
|
|
179905
|
+
compartments = withCompartmentDates(options.sessionId, compartments, options.temporalAwareness);
|
|
179816
179906
|
const memoryBudget = options.memoryInjectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS;
|
|
179817
179907
|
const memoryRenderOptions = {
|
|
179818
179908
|
sourceNameByMemoryId: sourceNamesForMemories({
|
|
@@ -179830,12 +179920,12 @@ function materializeM0(options) {
|
|
|
179830
179920
|
memories: trimmed.renderOrder,
|
|
179831
179921
|
facts,
|
|
179832
179922
|
memoryRenderOptions,
|
|
179833
|
-
historyBudgetTokens: options.historyBudgetTokens ??
|
|
179923
|
+
historyBudgetTokens: options.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS2,
|
|
179834
179924
|
userProfileBudgetTokens: options.userProfileBudgetTokens,
|
|
179835
179925
|
decayPressureMultiplier
|
|
179836
179926
|
});
|
|
179837
179927
|
let attempts = 0;
|
|
179838
|
-
const budget = options.historyBudgetTokens ??
|
|
179928
|
+
const budget = options.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS2;
|
|
179839
179929
|
while (budget > 0 && historySliceTokens(m0Text) > budget * 1.05 && attempts < 3) {
|
|
179840
179930
|
decayPressureMultiplier *= 1.15;
|
|
179841
179931
|
m0Text = renderM0({
|
|
@@ -179995,7 +180085,7 @@ function renderM1WithMetadata(options, markers, renderedMemoryIds) {
|
|
|
179995
180085
|
});
|
|
179996
180086
|
if (memoryUpdates.block)
|
|
179997
180087
|
blocks.push(memoryUpdates.block);
|
|
179998
|
-
const newCompartments = readNewCompartments(options.db, options.sessionId, markers.maxCompartmentSeq);
|
|
180088
|
+
const newCompartments = withCompartmentDates(options.sessionId, readNewCompartments(options.db, options.sessionId, markers.maxCompartmentSeq), options.temporalAwareness);
|
|
179999
180089
|
if (newCompartments.length > 0) {
|
|
180000
180090
|
blocks.push(`<new-compartments>
|
|
180001
180091
|
${newCompartments.map((compartment) => renderCompartmentAtTier(compartment, 1)).join(`
|
|
@@ -180211,7 +180301,7 @@ function renderFreshM0NonPersisted(options) {
|
|
|
180211
180301
|
const docs = readProjectDocsForM0(projectDirectory ?? "", options.injectDocs);
|
|
180212
180302
|
snapshotMarkers.projectDocsHash = docs.canonicalHash;
|
|
180213
180303
|
snapshotMarkers.materializedAt = options.state.cachedM0MaterializedAt ?? 0;
|
|
180214
|
-
const compartments = readM0Compartments(options.db, options.sessionId);
|
|
180304
|
+
const compartments = withCompartmentDates(options.sessionId, readM0Compartments(options.db, options.sessionId), options.temporalAwareness);
|
|
180215
180305
|
const memories = projectPath ? workspace.isWorkspaced ? getMemoriesByProjects(options.db, workspace.expandedIdentities, ["active", "permanent"], snapshotMarkers.materializedAt, workspace.ownIdentities, workspace.shareCategories) : getMemoriesByProject(options.db, projectPath, ["active", "permanent"], snapshotMarkers.materializedAt) : [];
|
|
180216
180306
|
const userMemories = safeGetActiveUserMemories(options.db);
|
|
180217
180307
|
const memoryBudget = options.memoryInjectionBudgetTokens ?? DEFAULT_MEMORY_BUDGET_TOKENS;
|
|
@@ -180223,7 +180313,7 @@ function renderFreshM0NonPersisted(options) {
|
|
|
180223
180313
|
})
|
|
180224
180314
|
};
|
|
180225
180315
|
const trimmed = workspace.isWorkspaced ? trimWorkspaceMemoriesToBudgetV2(options.sessionId, memories, memoryBudget, workspace, memoryRenderOptions) : trimMemoriesToBudgetV2(options.sessionId, memories, memoryBudget);
|
|
180226
|
-
const budget = options.historyBudgetTokens ??
|
|
180316
|
+
const budget = options.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS2;
|
|
180227
180317
|
let decayPressureMultiplier = 1;
|
|
180228
180318
|
let m0Text = renderM0({
|
|
180229
180319
|
projectDocs: docs.renderedBlock,
|
|
@@ -180345,7 +180435,7 @@ function injectM0M1(options) {
|
|
|
180345
180435
|
const M0_DRIFT_RATIO_FLOOR_TOKENS = 500;
|
|
180346
180436
|
const M1_DRIFT_RATIO = 0.15;
|
|
180347
180437
|
const M1_ABSOLUTE_CAP_RATIO = 0.2;
|
|
180348
|
-
const m1AbsoluteBudget = (options.historyBudgetTokens ??
|
|
180438
|
+
const m1AbsoluteBudget = (options.historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS2) * M1_ABSOLUTE_CAP_RATIO;
|
|
180349
180439
|
const m1HasContent = m1Text !== M1_EMPTY_PLACEHOLDER;
|
|
180350
180440
|
const m1Tokens = m1HasContent ? estimateTokens(m1Text) : 0;
|
|
180351
180441
|
const m0Tokens = estimateTokens(m0Text);
|
|
@@ -180374,7 +180464,7 @@ function injectM0M1(options) {
|
|
|
180374
180464
|
m1Text
|
|
180375
180465
|
};
|
|
180376
180466
|
}
|
|
180377
|
-
var INJECTION_CACHE_MAX = 100, injectionCache, CONSTRAINT_KEYWORDS, EMPTY_HARD_SIGNALS, MaterializeContentionError, RenderM1InvalidMarkersError,
|
|
180467
|
+
var INJECTION_CACHE_MAX = 100, injectionCache, CONSTRAINT_KEYWORDS, EMPTY_HARD_SIGNALS, MaterializeContentionError, RenderM1InvalidMarkersError, DEFAULT_HISTORY_BUDGET_TOKENS2 = 60000, DEFAULT_MEMORY_BUDGET_TOKENS = 8000, DEFAULT_USER_PROFILE_BUDGET_TOKENS = 4000, M0_EMPTY_BODY = "<session-history></session-history>", M1_EMPTY_PLACEHOLDER = "<session-history-since>(no new content since last materialization)</session-history-since>", EMPTY_PROJECT_DOCS, maxCompartmentSeqStatements, maxMemoryIdStatements, legacyCompartmentCountStatements, m0CompartmentStatements, newCompartmentStatements;
|
|
180378
180468
|
var init_inject_compartments = __esm(async () => {
|
|
180379
180469
|
init_compartment_storage();
|
|
180380
180470
|
init_constants();
|
|
@@ -186699,6 +186789,10 @@ function stripUnsafeProjectConfigFields(projectRaw) {
|
|
|
186699
186789
|
delete projectRaw.sqlite;
|
|
186700
186790
|
warnings.push("Ignoring sqlite.* from project config (security: SQLite cache/mmap PRAGMAs apply to the " + "process-global shared database handle; only user-level config may set them).");
|
|
186701
186791
|
}
|
|
186792
|
+
if ("shadow_transform" in projectRaw) {
|
|
186793
|
+
delete projectRaw.shadow_transform;
|
|
186794
|
+
warnings.push("Ignoring shadow_transform from project config (security: this developer-only mirror lane is user-level only).");
|
|
186795
|
+
}
|
|
186702
186796
|
const embedding = projectRaw.embedding;
|
|
186703
186797
|
if (isPlainObject2(embedding)) {
|
|
186704
186798
|
const removed = [];
|
|
@@ -195133,8 +195227,8 @@ function makeToolCompositeKey(ownerMsgId, callId) {
|
|
|
195133
195227
|
return `${ownerMsgId}${TOOL_COMPOSITE_KEY_SEP}${callId}`;
|
|
195134
195228
|
}
|
|
195135
195229
|
var GET_COUNTER_SQL = `SELECT counter FROM session_meta WHERE session_id = ?`;
|
|
195136
|
-
var GET_ASSIGNMENTS_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id FROM tags WHERE session_id = ? ORDER BY tag_number ASC";
|
|
195137
|
-
var GET_ASSIGNMENTS_SCOPED_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id FROM tags WHERE session_id = ? AND tag_number >= ? ORDER BY tag_number ASC";
|
|
195230
|
+
var GET_ASSIGNMENTS_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id, byte_size, token_count, input_byte_size, input_token_count FROM tags WHERE session_id = ? ORDER BY tag_number ASC";
|
|
195231
|
+
var GET_ASSIGNMENTS_SCOPED_SQL = "SELECT message_id, tag_number, type, tool_owner_message_id, byte_size, token_count, input_byte_size, input_token_count FROM tags WHERE session_id = ? AND tag_number >= ? ORDER BY tag_number ASC";
|
|
195138
195232
|
var PROBE_DATA_VERSION_SQL = "PRAGMA main.data_version";
|
|
195139
195233
|
var probeDataVersionStatements = new WeakMap;
|
|
195140
195234
|
function getProbeDataVersionStatement(db) {
|
|
@@ -195158,6 +195252,14 @@ function isAssignmentRow(row) {
|
|
|
195158
195252
|
return false;
|
|
195159
195253
|
if (candidate.tool_owner_message_id !== null && typeof candidate.tool_owner_message_id !== "string")
|
|
195160
195254
|
return false;
|
|
195255
|
+
if (typeof candidate.byte_size !== "number")
|
|
195256
|
+
return false;
|
|
195257
|
+
if (candidate.token_count !== null && typeof candidate.token_count !== "number")
|
|
195258
|
+
return false;
|
|
195259
|
+
if (typeof candidate.input_byte_size !== "number")
|
|
195260
|
+
return false;
|
|
195261
|
+
if (candidate.input_token_count !== null && typeof candidate.input_token_count !== "number")
|
|
195262
|
+
return false;
|
|
195161
195263
|
return true;
|
|
195162
195264
|
}
|
|
195163
195265
|
var UPSERT_COUNTER_SQL = `
|
|
@@ -195192,6 +195294,7 @@ var MAX_TAG_ALLOC_RETRIES = 5;
|
|
|
195192
195294
|
function createTagger() {
|
|
195193
195295
|
const counters = new Map;
|
|
195194
195296
|
const assignments = new Map;
|
|
195297
|
+
const toolAccountingBySession = new Map;
|
|
195195
195298
|
const loadSignatures = new Map;
|
|
195196
195299
|
function getSessionAssignments(sessionId) {
|
|
195197
195300
|
let map2 = assignments.get(sessionId);
|
|
@@ -195201,6 +195304,14 @@ function createTagger() {
|
|
|
195201
195304
|
}
|
|
195202
195305
|
return map2;
|
|
195203
195306
|
}
|
|
195307
|
+
function getSessionToolAccounting(sessionId) {
|
|
195308
|
+
let map2 = toolAccountingBySession.get(sessionId);
|
|
195309
|
+
if (!map2) {
|
|
195310
|
+
map2 = new Map;
|
|
195311
|
+
toolAccountingBySession.set(sessionId, map2);
|
|
195312
|
+
}
|
|
195313
|
+
return map2;
|
|
195314
|
+
}
|
|
195204
195315
|
function isUniqueConstraintError2(error51) {
|
|
195205
195316
|
return error51 instanceof Error && "code" in error51 && error51.code === "SQLITE_CONSTRAINT_UNIQUE";
|
|
195206
195317
|
}
|
|
@@ -195254,6 +195365,14 @@ function createTagger() {
|
|
|
195254
195365
|
}
|
|
195255
195366
|
counters.set(sessionId, next);
|
|
195256
195367
|
sessionAssignments.set(mapKey, next);
|
|
195368
|
+
if (type === "tool") {
|
|
195369
|
+
getSessionToolAccounting(sessionId).set(next, {
|
|
195370
|
+
byteSize: byteSize2,
|
|
195371
|
+
tokenCount: tokenCounts?.tokenCount ?? null,
|
|
195372
|
+
inputByteSize,
|
|
195373
|
+
inputTokenCount: tokenCounts?.inputTokenCount ?? null
|
|
195374
|
+
});
|
|
195375
|
+
}
|
|
195257
195376
|
return next;
|
|
195258
195377
|
}
|
|
195259
195378
|
throw new Error(`tagger.allocateTag: failed to allocate tag for session=${sessionId} key=${mapKey} after ${MAX_TAG_ALLOC_RETRIES} retries`);
|
|
@@ -195314,6 +195433,13 @@ function createTagger() {
|
|
|
195314
195433
|
function getToolTag(sessionId, callId, ownerMsgId) {
|
|
195315
195434
|
return assignments.get(sessionId)?.get(makeToolCompositeKey(ownerMsgId, callId));
|
|
195316
195435
|
}
|
|
195436
|
+
function getToolTagAccounting(sessionId, callId, ownerMsgId) {
|
|
195437
|
+
const tagNumber = getToolTag(sessionId, callId, ownerMsgId);
|
|
195438
|
+
return tagNumber === undefined ? undefined : toolAccountingBySession.get(sessionId)?.get(tagNumber);
|
|
195439
|
+
}
|
|
195440
|
+
function setToolTagAccounting(sessionId, tagNumber, accounting) {
|
|
195441
|
+
getSessionToolAccounting(sessionId).set(tagNumber, accounting);
|
|
195442
|
+
}
|
|
195317
195443
|
function bindTag(sessionId, messageId, tagNumber) {
|
|
195318
195444
|
getSessionAssignments(sessionId).set(messageId, tagNumber);
|
|
195319
195445
|
}
|
|
@@ -195332,6 +195458,7 @@ function createTagger() {
|
|
|
195332
195458
|
function resetCounter(sessionId, db) {
|
|
195333
195459
|
counters.set(sessionId, 0);
|
|
195334
195460
|
assignments.delete(sessionId);
|
|
195461
|
+
toolAccountingBySession.delete(sessionId);
|
|
195335
195462
|
loadSignatures.delete(sessionId);
|
|
195336
195463
|
getResetCounterStatement(db).run(sessionId, getHarness());
|
|
195337
195464
|
}
|
|
@@ -195354,11 +195481,19 @@ function createTagger() {
|
|
|
195354
195481
|
const assignmentRows = (floor > 0 ? db.prepare(GET_ASSIGNMENTS_SCOPED_SQL).all(sessionId, floor) : db.prepare(GET_ASSIGNMENTS_SQL).all(sessionId)).filter(isAssignmentRow);
|
|
195355
195482
|
const sessionAssignments = getSessionAssignments(sessionId);
|
|
195356
195483
|
sessionAssignments.clear();
|
|
195484
|
+
const sessionToolAccounting = getSessionToolAccounting(sessionId);
|
|
195485
|
+
sessionToolAccounting.clear();
|
|
195357
195486
|
let maxTagNumber = 0;
|
|
195358
195487
|
for (const assignment of assignmentRows) {
|
|
195359
195488
|
if (assignment.type === "tool") {
|
|
195360
195489
|
if (assignment.tool_owner_message_id !== null) {
|
|
195361
195490
|
sessionAssignments.set(makeToolCompositeKey(assignment.tool_owner_message_id, assignment.message_id), assignment.tag_number);
|
|
195491
|
+
sessionToolAccounting.set(assignment.tag_number, {
|
|
195492
|
+
byteSize: assignment.byte_size,
|
|
195493
|
+
tokenCount: assignment.token_count,
|
|
195494
|
+
inputByteSize: assignment.input_byte_size,
|
|
195495
|
+
inputTokenCount: assignment.input_token_count
|
|
195496
|
+
});
|
|
195362
195497
|
}
|
|
195363
195498
|
} else {
|
|
195364
195499
|
sessionAssignments.set(assignment.message_id, assignment.tag_number);
|
|
@@ -195378,6 +195513,7 @@ function createTagger() {
|
|
|
195378
195513
|
function cleanup(sessionId) {
|
|
195379
195514
|
counters.delete(sessionId);
|
|
195380
195515
|
assignments.delete(sessionId);
|
|
195516
|
+
toolAccountingBySession.delete(sessionId);
|
|
195381
195517
|
loadSignatures.delete(sessionId);
|
|
195382
195518
|
}
|
|
195383
195519
|
return {
|
|
@@ -195385,6 +195521,8 @@ function createTagger() {
|
|
|
195385
195521
|
assignToolTag,
|
|
195386
195522
|
getTag,
|
|
195387
195523
|
getToolTag,
|
|
195524
|
+
getToolTagAccounting,
|
|
195525
|
+
setToolTagAccounting,
|
|
195388
195526
|
bindTag,
|
|
195389
195527
|
unbindTag,
|
|
195390
195528
|
bindToolTag,
|
|
@@ -195639,26 +195777,28 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
|
|
|
195639
195777
|
sessionLog(sessionId, `compartment trigger: skipped — historian already in progress (usage=${usage.percentage.toFixed(1)}%)`);
|
|
195640
195778
|
return { shouldFire: false };
|
|
195641
195779
|
}
|
|
195642
|
-
|
|
195780
|
+
const lazyInMemoryTail = typeof inMemoryTail === "function" ? inMemoryTail : undefined;
|
|
195781
|
+
let resolvedInMemoryTail = typeof inMemoryTail === "function" ? undefined : inMemoryTail;
|
|
195782
|
+
if (resolvedInMemoryTail) {
|
|
195643
195783
|
try {
|
|
195644
195784
|
const policyVersion = loadProtectedTailMeta(db, sessionId).protectedTailPolicyVersion;
|
|
195645
195785
|
if (policyVersion < 3)
|
|
195646
|
-
|
|
195786
|
+
resolvedInMemoryTail = undefined;
|
|
195647
195787
|
} catch {
|
|
195648
|
-
|
|
195788
|
+
resolvedInMemoryTail = undefined;
|
|
195649
195789
|
}
|
|
195650
195790
|
}
|
|
195651
|
-
const taggerFloor = taggerFloorOverride !== undefined && taggerFloorOverride > 0 ? taggerFloorOverride :
|
|
195791
|
+
const taggerFloor = taggerFloorOverride !== undefined && taggerFloorOverride > 0 ? taggerFloorOverride : resolvedInMemoryTail ? deriveTagLoadFloor(db, sessionId, resolvedInMemoryTail.messages.map((m) => m.id)) : 0;
|
|
195652
195792
|
const proactiveFloorForGate = getProactiveCompartmentTriggerPercentage(executeThresholdPercentage);
|
|
195653
195793
|
if (usage.percentage < proactiveFloorForGate) {
|
|
195654
195794
|
try {
|
|
195655
|
-
const boundFloor =
|
|
195795
|
+
const boundFloor = resolvedInMemoryTail || lazyInMemoryTail ? taggerFloor : 0;
|
|
195656
195796
|
const { bound: persistedBound, nullCount } = getTriggerTagTokenUpperBound(db, sessionId, boundFloor);
|
|
195657
195797
|
if (nullCount === 0) {
|
|
195658
|
-
const untaggedUpperBound =
|
|
195798
|
+
const untaggedUpperBound = resolvedInMemoryTail ? estimateUntaggedInMemoryTailUpperBound(db, sessionId, resolvedInMemoryTail, taggerFloor) : 0;
|
|
195659
195799
|
const eligibleUpperBound = persistedBound + untaggedUpperBound;
|
|
195660
195800
|
if (eligibleUpperBound < triggerBudget) {
|
|
195661
|
-
const memorySuffix =
|
|
195801
|
+
const memorySuffix = resolvedInMemoryTail ? ` (persisted=${persistedBound}, untagged-memory≤${untaggedUpperBound})` : "";
|
|
195662
195802
|
sessionLog(sessionId, `compartment trigger: cheap-skip at ${usage.percentage.toFixed(1)}% (below proactive floor ${proactiveFloorForGate}%) — live-tail upper bound ${eligibleUpperBound}${memorySuffix} < triggerBudget ${triggerBudget}; no size trigger possible, skipped full raw read`);
|
|
195663
195803
|
return { shouldFire: false };
|
|
195664
195804
|
}
|
|
@@ -195667,7 +195807,16 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
|
|
|
195667
195807
|
sessionLog(sessionId, `compartment trigger: cheap-gate skipped (falling through to full read): ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
195668
195808
|
}
|
|
195669
195809
|
}
|
|
195670
|
-
|
|
195810
|
+
if (lazyInMemoryTail) {
|
|
195811
|
+
try {
|
|
195812
|
+
const policyVersion = loadProtectedTailMeta(db, sessionId).protectedTailPolicyVersion;
|
|
195813
|
+
if (policyVersion >= 3)
|
|
195814
|
+
resolvedInMemoryTail = lazyInMemoryTail();
|
|
195815
|
+
} catch {
|
|
195816
|
+
resolvedInMemoryTail = undefined;
|
|
195817
|
+
}
|
|
195818
|
+
}
|
|
195819
|
+
const tailInfo = getUnsummarizedTailInfo(db, sessionId, triggerBudget, usage, executeThresholdPercentage, contextLimit, resolvedInMemoryTail, taggerFloor);
|
|
195671
195820
|
if (!tailInfo.hasNewRawHistory) {
|
|
195672
195821
|
try {
|
|
195673
195822
|
const lastCompartmentEnd = getLastCompartmentEndMessage(db, sessionId);
|
|
@@ -195694,11 +195843,11 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
|
|
|
195694
195843
|
}
|
|
195695
195844
|
const scale = usage.percentage >= BLOCK_UNTIL_DONE_PERCENTAGE ? 0.25 : 0.5;
|
|
195696
195845
|
const scaledBoundary = withRawSessionMessageCache(() => {
|
|
195697
|
-
if (
|
|
195846
|
+
if (resolvedInMemoryTail) {
|
|
195698
195847
|
primeInMemoryTailRawMessageCache({
|
|
195699
195848
|
sessionId,
|
|
195700
|
-
messages:
|
|
195701
|
-
absoluteMessageCount:
|
|
195849
|
+
messages: resolvedInMemoryTail.messages,
|
|
195850
|
+
absoluteMessageCount: resolvedInMemoryTail.absoluteMessageCount
|
|
195702
195851
|
});
|
|
195703
195852
|
}
|
|
195704
195853
|
return resolveOpenCodeProtectedTailBoundary({
|
|
@@ -195840,9 +195989,8 @@ function executeStatus(db, sessionId, protectedTags, executeThresholdPercentageC
|
|
|
195840
195989
|
const compartments = getCompartments(db, sessionId);
|
|
195841
195990
|
let historyBlockTokens = 0;
|
|
195842
195991
|
for (const c of compartments) {
|
|
195843
|
-
historyBlockTokens += estimateTokens(
|
|
195992
|
+
historyBlockTokens += estimateTokens(`## ${c.startMessage}-${c.endMessage} · ${c.title}
|
|
195844
195993
|
${c.content}
|
|
195845
|
-
</compartment>
|
|
195846
195994
|
`);
|
|
195847
195995
|
}
|
|
195848
195996
|
const budgetTokens = historyBudgetPercentage && displayContextLimit > 0 ? Math.floor(displayContextLimit * (Math.min(executeThresholdPercentage, 80) / 100) * historyBudgetPercentage) : null;
|
|
@@ -196345,6 +196493,7 @@ var reconciliationScheduledSessions = new Set;
|
|
|
196345
196493
|
var sessionLocks = new Map;
|
|
196346
196494
|
var incrementalTimers = new Map;
|
|
196347
196495
|
var pendingIncrementalKeys = new Set;
|
|
196496
|
+
var completedIncrementalKeys = new Set;
|
|
196348
196497
|
function defer(fn) {
|
|
196349
196498
|
const immediate = globalThis.setImmediate;
|
|
196350
196499
|
if (typeof immediate === "function") {
|
|
@@ -196412,9 +196561,9 @@ function scheduleReconciliation(db, sessionId, readMessages) {
|
|
|
196412
196561
|
});
|
|
196413
196562
|
});
|
|
196414
196563
|
}
|
|
196415
|
-
function scheduleIncrementalIndex(db, sessionId, messageId,
|
|
196564
|
+
function scheduleIncrementalIndex(db, sessionId, messageId, messageSource) {
|
|
196416
196565
|
const key = `${sessionId}\x00${messageId}`;
|
|
196417
|
-
if (incrementalTimers.has(key) || pendingIncrementalKeys.has(key)) {
|
|
196566
|
+
if (completedIncrementalKeys.has(key) || incrementalTimers.has(key) || pendingIncrementalKeys.has(key)) {
|
|
196418
196567
|
return;
|
|
196419
196568
|
}
|
|
196420
196569
|
const timer = setTimeout(() => {
|
|
@@ -196422,12 +196571,12 @@ function scheduleIncrementalIndex(db, sessionId, messageId, readSingleMessage) {
|
|
|
196422
196571
|
pendingIncrementalKeys.add(key);
|
|
196423
196572
|
let attemptedOrdinal = null;
|
|
196424
196573
|
runWithSessionLock(sessionId, () => {
|
|
196425
|
-
const message =
|
|
196426
|
-
if (!message)
|
|
196574
|
+
const message = typeof messageSource === "function" ? messageSource(sessionId, messageId) : messageSource;
|
|
196575
|
+
if (!message)
|
|
196427
196576
|
return;
|
|
196428
|
-
}
|
|
196429
196577
|
attemptedOrdinal = message.ordinal;
|
|
196430
196578
|
indexSingleMessage(db, sessionId, message);
|
|
196579
|
+
completedIncrementalKeys.add(key);
|
|
196431
196580
|
}).catch((error51) => {
|
|
196432
196581
|
markMessageIndexDirty(db, sessionId, attemptedOrdinal ?? getLastIndexedOrdinal(db, sessionId) + 1);
|
|
196433
196582
|
reconciledSessions.delete(sessionId);
|
|
@@ -196441,6 +196590,11 @@ function scheduleIncrementalIndex(db, sessionId, messageId, readSingleMessage) {
|
|
|
196441
196590
|
function scheduleClearAndReindex(db, sessionId, readMessages) {
|
|
196442
196591
|
reconciledSessions.delete(sessionId);
|
|
196443
196592
|
reconciliationScheduledSessions.delete(sessionId);
|
|
196593
|
+
const prefix = `${sessionId}\x00`;
|
|
196594
|
+
for (const key of completedIncrementalKeys) {
|
|
196595
|
+
if (key.startsWith(prefix))
|
|
196596
|
+
completedIncrementalKeys.delete(key);
|
|
196597
|
+
}
|
|
196444
196598
|
defer(() => {
|
|
196445
196599
|
runWithSessionLock(sessionId, () => {
|
|
196446
196600
|
clearIndexedMessages(db, sessionId);
|
|
@@ -196468,6 +196622,10 @@ function clearSessionTracking(sessionId) {
|
|
|
196468
196622
|
pendingIncrementalKeys.delete(key);
|
|
196469
196623
|
}
|
|
196470
196624
|
}
|
|
196625
|
+
for (const key of completedIncrementalKeys) {
|
|
196626
|
+
if (key.startsWith(prefix))
|
|
196627
|
+
completedIncrementalKeys.delete(key);
|
|
196628
|
+
}
|
|
196471
196629
|
}
|
|
196472
196630
|
|
|
196473
196631
|
// src/hooks/magic-context/event-handler.ts
|
|
@@ -197437,6 +197595,7 @@ function resolveCtxReduceAvailability(sessionId) {
|
|
|
197437
197595
|
}
|
|
197438
197596
|
|
|
197439
197597
|
// src/hooks/magic-context/transform.ts
|
|
197598
|
+
init_decay_render();
|
|
197440
197599
|
init_derive_budgets();
|
|
197441
197600
|
|
|
197442
197601
|
// src/hooks/magic-context/image-token-estimate.ts
|
|
@@ -197657,6 +197816,1295 @@ function replaySentinelByMessageIds(messages, ids, providerID) {
|
|
|
197657
197816
|
missingIds.push(id);
|
|
197658
197817
|
return { replayed, missingIds };
|
|
197659
197818
|
}
|
|
197819
|
+
|
|
197820
|
+
// src/hooks/magic-context/shadow-sender.ts
|
|
197821
|
+
init_compartment_storage();
|
|
197822
|
+
init_storage_memory();
|
|
197823
|
+
init_storage_memory_mutation_log();
|
|
197824
|
+
init_storage_meta_persisted();
|
|
197825
|
+
init_workspaces();
|
|
197826
|
+
init_data_path();
|
|
197827
|
+
init_logger();
|
|
197828
|
+
init_temporal_awareness();
|
|
197829
|
+
await __promiseAll([
|
|
197830
|
+
init_storage(),
|
|
197831
|
+
init_read_session_chunk()
|
|
197832
|
+
]);
|
|
197833
|
+
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
197834
|
+
import { existsSync as existsSync21, readFileSync as readFileSync13, statSync as statSync8 } from "node:fs";
|
|
197835
|
+
import net from "node:net";
|
|
197836
|
+
import { join as join19 } from "node:path";
|
|
197837
|
+
var DEFAULT_MODULE_ID = "magic-context";
|
|
197838
|
+
function getDefaultConnectionFile() {
|
|
197839
|
+
return join19(getDataDir(), "cortexkit", "run", "subc-connection.json");
|
|
197840
|
+
}
|
|
197841
|
+
var SUBC_HEADER_LEN = 17;
|
|
197842
|
+
var SUBC_PROTOCOL_VERSION = 1;
|
|
197843
|
+
var FRAME_REQUEST = 0;
|
|
197844
|
+
var FRAME_RESPONSE = 1;
|
|
197845
|
+
var FRAME_ERROR = 5;
|
|
197846
|
+
var PRIORITY_BACKGROUND_FLAGS = 2 << 1;
|
|
197847
|
+
var AUTH_CLIENT_DOMAIN = "subc-client-v1";
|
|
197848
|
+
var AUTH_SERVER_DOMAIN = "subc-server-v1";
|
|
197849
|
+
var NONCE_LEN = 32;
|
|
197850
|
+
var PROOF_LEN = 32;
|
|
197851
|
+
var CONNECT_BACKOFF_INITIAL_MS = 1000;
|
|
197852
|
+
var CONNECT_BACKOFF_MAX_MS = 30000;
|
|
197853
|
+
var REQUEST_TIMEOUT_MS = 5000;
|
|
197854
|
+
var HANDSHAKE_TIMEOUT_MS = 2000;
|
|
197855
|
+
var RESEED_COOLDOWN_MS = 30 * 60 * 1000;
|
|
197856
|
+
var RESEED_ATTEMPT_CAP = 5;
|
|
197857
|
+
var declaredTrimBySession = new Map;
|
|
197858
|
+
function emptyCounters() {
|
|
197859
|
+
return {
|
|
197860
|
+
enqueued: 0,
|
|
197861
|
+
dropped_oldest: 0,
|
|
197862
|
+
send_failures: 0,
|
|
197863
|
+
connection_skips: 0,
|
|
197864
|
+
ordinal_unresolved: 0,
|
|
197865
|
+
ordinal_mismatch: 0,
|
|
197866
|
+
state_sync_rejects: 0,
|
|
197867
|
+
generation_rejects: 0,
|
|
197868
|
+
resets_sent: 0,
|
|
197869
|
+
transforms_sent: 0
|
|
197870
|
+
};
|
|
197871
|
+
}
|
|
197872
|
+
function createSessionQueueState() {
|
|
197873
|
+
return {
|
|
197874
|
+
queue: [],
|
|
197875
|
+
running: false,
|
|
197876
|
+
initialized: false,
|
|
197877
|
+
shadowGeneration: 0,
|
|
197878
|
+
lastAckedSeq: 0,
|
|
197879
|
+
lastAckedWatermarks: null,
|
|
197880
|
+
idOrdinalMemoGeneration: 0,
|
|
197881
|
+
idOrdinalMemo: new Map,
|
|
197882
|
+
requireResetReason: "cold_start",
|
|
197883
|
+
blockedUntilReset: false,
|
|
197884
|
+
seedPassPending: false,
|
|
197885
|
+
skipped: false,
|
|
197886
|
+
reseedAttempts: 0,
|
|
197887
|
+
lastReseedAttemptMs: null,
|
|
197888
|
+
reseedAwaitingSuccess: false,
|
|
197889
|
+
counters: emptyCounters()
|
|
197890
|
+
};
|
|
197891
|
+
}
|
|
197892
|
+
function cloneJson(value) {
|
|
197893
|
+
return JSON.parse(JSON.stringify(value));
|
|
197894
|
+
}
|
|
197895
|
+
function stableHash(value) {
|
|
197896
|
+
return createHmac("sha256", "magic-context-shadow-watermark").update(value).digest("hex");
|
|
197897
|
+
}
|
|
197898
|
+
function getMessageId(message) {
|
|
197899
|
+
return typeof message.info.id === "string" && message.info.id.length > 0 ? message.info.id : null;
|
|
197900
|
+
}
|
|
197901
|
+
function readStringField(part, field) {
|
|
197902
|
+
if (!isRecord(part))
|
|
197903
|
+
return null;
|
|
197904
|
+
if (field === "text")
|
|
197905
|
+
return typeof part.text === "string" ? part.text : null;
|
|
197906
|
+
if (field === "tool_state_output") {
|
|
197907
|
+
return isRecord(part.state) && typeof part.state.output === "string" ? part.state.output : null;
|
|
197908
|
+
}
|
|
197909
|
+
return typeof part.content === "string" ? part.content : null;
|
|
197910
|
+
}
|
|
197911
|
+
function writeStringField(part, field, value) {
|
|
197912
|
+
if (!isRecord(part))
|
|
197913
|
+
return;
|
|
197914
|
+
if (field === "text") {
|
|
197915
|
+
part.text = value;
|
|
197916
|
+
return;
|
|
197917
|
+
}
|
|
197918
|
+
if (field === "tool_state_output") {
|
|
197919
|
+
if (isRecord(part.state))
|
|
197920
|
+
part.state.output = value;
|
|
197921
|
+
return;
|
|
197922
|
+
}
|
|
197923
|
+
part.content = value;
|
|
197924
|
+
}
|
|
197925
|
+
function removeExactSuffix(value, suffix) {
|
|
197926
|
+
if (!suffix || !value.endsWith(suffix))
|
|
197927
|
+
return null;
|
|
197928
|
+
return value.slice(0, value.length - suffix.length);
|
|
197929
|
+
}
|
|
197930
|
+
function denormalizeShadowOutput(args) {
|
|
197931
|
+
const clonedOutput = cloneJson(args.outputMessages);
|
|
197932
|
+
const normalizations = [];
|
|
197933
|
+
const messageIndex = new Map;
|
|
197934
|
+
args.outputMessages.forEach((message, index) => {
|
|
197935
|
+
messageIndex.set(message, index);
|
|
197936
|
+
});
|
|
197937
|
+
for (const target of args.normalizationTargets) {
|
|
197938
|
+
const msgIndex = messageIndex.get(target.message);
|
|
197939
|
+
if (msgIndex === undefined)
|
|
197940
|
+
continue;
|
|
197941
|
+
let partIndex = -1;
|
|
197942
|
+
for (let index = 0;index < target.message.parts.length; index += 1) {
|
|
197943
|
+
if (target.message.parts[index] === target.part) {
|
|
197944
|
+
partIndex = index;
|
|
197945
|
+
break;
|
|
197946
|
+
}
|
|
197947
|
+
}
|
|
197948
|
+
if (partIndex < 0)
|
|
197949
|
+
continue;
|
|
197950
|
+
const cloneMessage = clonedOutput[msgIndex];
|
|
197951
|
+
const clonePart = cloneMessage?.parts?.[partIndex];
|
|
197952
|
+
const current = readStringField(clonePart, target.field);
|
|
197953
|
+
if (current === null)
|
|
197954
|
+
continue;
|
|
197955
|
+
const prefix = `§${target.tagNumber}§ `;
|
|
197956
|
+
if (!current.startsWith(prefix))
|
|
197957
|
+
continue;
|
|
197958
|
+
writeStringField(clonePart, target.field, current.slice(prefix.length));
|
|
197959
|
+
normalizations.push({
|
|
197960
|
+
kind: "tag_prefix",
|
|
197961
|
+
message_id: getMessageId(target.message),
|
|
197962
|
+
part_index: partIndex,
|
|
197963
|
+
field: target.field,
|
|
197964
|
+
tag_number: target.tagNumber,
|
|
197965
|
+
removed: prefix
|
|
197966
|
+
});
|
|
197967
|
+
}
|
|
197968
|
+
const hintDecisions = getAutoSearchHintDecisions(args.db, args.sessionId).filter((decision) => decision.decision === "hint" && typeof decision.text === "string");
|
|
197969
|
+
if (hintDecisions.length > 0) {
|
|
197970
|
+
const hintByMessage = new Map;
|
|
197971
|
+
for (const decision of hintDecisions) {
|
|
197972
|
+
if (decision.decision === "hint")
|
|
197973
|
+
hintByMessage.set(decision.messageId, decision.text);
|
|
197974
|
+
}
|
|
197975
|
+
args.outputMessages.forEach((message, msgIndex) => {
|
|
197976
|
+
const messageId = getMessageId(message);
|
|
197977
|
+
if (!messageId)
|
|
197978
|
+
return;
|
|
197979
|
+
const hint = hintByMessage.get(messageId);
|
|
197980
|
+
if (!hint)
|
|
197981
|
+
return;
|
|
197982
|
+
const cloneMessage = clonedOutput[msgIndex];
|
|
197983
|
+
for (let partIndex = 0;partIndex < message.parts.length; partIndex += 1) {
|
|
197984
|
+
const originalPart = message.parts[partIndex];
|
|
197985
|
+
if (!isRecord(originalPart) || originalPart.type !== "text")
|
|
197986
|
+
continue;
|
|
197987
|
+
const clonePart = cloneMessage?.parts?.[partIndex];
|
|
197988
|
+
const current = readStringField(clonePart, "text");
|
|
197989
|
+
if (current === null)
|
|
197990
|
+
continue;
|
|
197991
|
+
const stripped = removeExactSuffix(current, hint);
|
|
197992
|
+
if (stripped === null)
|
|
197993
|
+
continue;
|
|
197994
|
+
writeStringField(clonePart, "text", stripped);
|
|
197995
|
+
normalizations.push({
|
|
197996
|
+
kind: "ctx_search_hint",
|
|
197997
|
+
message_id: messageId,
|
|
197998
|
+
part_index: partIndex,
|
|
197999
|
+
field: "text",
|
|
198000
|
+
removed: hint
|
|
198001
|
+
});
|
|
198002
|
+
break;
|
|
198003
|
+
}
|
|
198004
|
+
});
|
|
198005
|
+
}
|
|
198006
|
+
const tsOutput = clonedOutput.filter((message, index) => {
|
|
198007
|
+
if (!isRawCompactionSummaryInfo(args.outputMessages[index]?.info))
|
|
198008
|
+
return true;
|
|
198009
|
+
normalizations.push({
|
|
198010
|
+
kind: "summary_message",
|
|
198011
|
+
message_id: getMessageId(args.outputMessages[index]),
|
|
198012
|
+
part_index: -1,
|
|
198013
|
+
field: "ts_output",
|
|
198014
|
+
removed: JSON.stringify(message)
|
|
198015
|
+
});
|
|
198016
|
+
return false;
|
|
198017
|
+
});
|
|
198018
|
+
return { ts_output: tsOutput, normalizations };
|
|
198019
|
+
}
|
|
198020
|
+
function resolveOrdinalsForShadow(args) {
|
|
198021
|
+
const memo = args.memo;
|
|
198022
|
+
if (args.memoGeneration !== args.generation) {
|
|
198023
|
+
memo.clear();
|
|
198024
|
+
}
|
|
198025
|
+
const ordinalById = readRawSessionMessageIdOrdinals(args.sessionId);
|
|
198026
|
+
const normalizations = [];
|
|
198027
|
+
const visibleMessages = args.messages.filter((message) => {
|
|
198028
|
+
if (!isRawCompactionSummaryInfo(message.info))
|
|
198029
|
+
return true;
|
|
198030
|
+
normalizations.push({
|
|
198031
|
+
kind: "summary_message",
|
|
198032
|
+
message_id: getMessageId(message),
|
|
198033
|
+
part_index: -1,
|
|
198034
|
+
field: "input",
|
|
198035
|
+
removed: JSON.stringify(message)
|
|
198036
|
+
});
|
|
198037
|
+
return false;
|
|
198038
|
+
});
|
|
198039
|
+
const annotated = visibleMessages;
|
|
198040
|
+
const resolved = new Array(annotated.length);
|
|
198041
|
+
let firstUnresolvedId;
|
|
198042
|
+
for (let index = 0;index < annotated.length; index += 1) {
|
|
198043
|
+
const messageId = getMessageId(visibleMessages[index]);
|
|
198044
|
+
if (!messageId)
|
|
198045
|
+
return { ok: false, reason: "unresolved" };
|
|
198046
|
+
const ordinal = ordinalById.get(messageId);
|
|
198047
|
+
if (ordinal === undefined && firstUnresolvedId === undefined) {
|
|
198048
|
+
firstUnresolvedId = messageId;
|
|
198049
|
+
}
|
|
198050
|
+
resolved[index] = ordinal;
|
|
198051
|
+
}
|
|
198052
|
+
let suffixStart = annotated.length;
|
|
198053
|
+
while (suffixStart > 0 && resolved[suffixStart - 1] === undefined) {
|
|
198054
|
+
suffixStart -= 1;
|
|
198055
|
+
}
|
|
198056
|
+
for (let index = 0;index < suffixStart; index += 1) {
|
|
198057
|
+
if (resolved[index] === undefined) {
|
|
198058
|
+
return { ok: false, reason: "unresolved", messageId: firstUnresolvedId };
|
|
198059
|
+
}
|
|
198060
|
+
}
|
|
198061
|
+
if (suffixStart < annotated.length) {
|
|
198062
|
+
const base = suffixStart > 0 ? resolved[suffixStart - 1] : -1;
|
|
198063
|
+
for (let index = suffixStart;index < annotated.length; index += 1) {
|
|
198064
|
+
resolved[index] = base + (index - suffixStart) + 1;
|
|
198065
|
+
}
|
|
198066
|
+
}
|
|
198067
|
+
for (let index = 0;index < annotated.length; index += 1) {
|
|
198068
|
+
const messageId = getMessageId(visibleMessages[index]);
|
|
198069
|
+
const ordinal = resolved[index];
|
|
198070
|
+
const prior = memo.get(messageId);
|
|
198071
|
+
if (prior !== undefined && prior !== ordinal) {
|
|
198072
|
+
return { ok: false, reason: "mismatch", messageId };
|
|
198073
|
+
}
|
|
198074
|
+
memo.set(messageId, ordinal);
|
|
198075
|
+
annotated[index].absolute_ordinal = ordinal;
|
|
198076
|
+
}
|
|
198077
|
+
return {
|
|
198078
|
+
ok: true,
|
|
198079
|
+
annotatedInput: annotated,
|
|
198080
|
+
memoGeneration: args.generation,
|
|
198081
|
+
normalizations
|
|
198082
|
+
};
|
|
198083
|
+
}
|
|
198084
|
+
function ordinalForMessageId(args) {
|
|
198085
|
+
if (args.state.idOrdinalMemoGeneration !== args.generation) {
|
|
198086
|
+
args.state.idOrdinalMemo.clear();
|
|
198087
|
+
args.state.idOrdinalMemoGeneration = args.generation;
|
|
198088
|
+
}
|
|
198089
|
+
if (!args.raw)
|
|
198090
|
+
return null;
|
|
198091
|
+
const prior = args.state.idOrdinalMemo.get(args.messageId);
|
|
198092
|
+
if (prior !== undefined && prior !== args.raw.ordinal)
|
|
198093
|
+
return "mismatch";
|
|
198094
|
+
args.state.idOrdinalMemo.set(args.messageId, args.raw.ordinal);
|
|
198095
|
+
return args.raw.ordinal;
|
|
198096
|
+
}
|
|
198097
|
+
function flatBlockCountForRawMessage(message) {
|
|
198098
|
+
if (!message)
|
|
198099
|
+
return 1;
|
|
198100
|
+
let count = 0;
|
|
198101
|
+
for (const part of message.parts) {
|
|
198102
|
+
if (!isRecord(part)) {
|
|
198103
|
+
count += 1;
|
|
198104
|
+
continue;
|
|
198105
|
+
}
|
|
198106
|
+
const type = typeof part.type === "string" ? part.type : "unknown";
|
|
198107
|
+
switch (type) {
|
|
198108
|
+
case "text":
|
|
198109
|
+
if (part.ignored !== true)
|
|
198110
|
+
count += 1;
|
|
198111
|
+
break;
|
|
198112
|
+
case "reasoning":
|
|
198113
|
+
case "file":
|
|
198114
|
+
case "image":
|
|
198115
|
+
case "step-start":
|
|
198116
|
+
case "subtask":
|
|
198117
|
+
count += 1;
|
|
198118
|
+
break;
|
|
198119
|
+
case "tool": {
|
|
198120
|
+
count += 1;
|
|
198121
|
+
const status = isRecord(part.state) ? part.state.status : undefined;
|
|
198122
|
+
const hasCompletedStatus = status === "completed" || status === "error";
|
|
198123
|
+
const hasOutput = isRecord(part.state) ? typeof part.state.output === "string" || typeof part.state.error === "string" : typeof part.output === "string" || typeof part.error === "string";
|
|
198124
|
+
if (hasCompletedStatus || hasOutput)
|
|
198125
|
+
count += 1;
|
|
198126
|
+
break;
|
|
198127
|
+
}
|
|
198128
|
+
case "compaction":
|
|
198129
|
+
case "step-finish":
|
|
198130
|
+
case "snapshot":
|
|
198131
|
+
case "patch":
|
|
198132
|
+
case "agent":
|
|
198133
|
+
case "retry":
|
|
198134
|
+
break;
|
|
198135
|
+
default:
|
|
198136
|
+
count += 1;
|
|
198137
|
+
break;
|
|
198138
|
+
}
|
|
198139
|
+
}
|
|
198140
|
+
return Math.max(1, count);
|
|
198141
|
+
}
|
|
198142
|
+
function flatBlockIdForRawMessage(messageId, raw, edge) {
|
|
198143
|
+
const blockIndex = edge === "start" ? 0 : flatBlockCountForRawMessage(raw) - 1;
|
|
198144
|
+
return `${messageId}#${blockIndex}`;
|
|
198145
|
+
}
|
|
198146
|
+
function resolveDeclaredTrimForShadow(args) {
|
|
198147
|
+
const marker = getPersistedCompactionMarkerState(args.db, args.sessionId);
|
|
198148
|
+
if (!marker || marker.boundaryOrdinal < 1)
|
|
198149
|
+
return null;
|
|
198150
|
+
const targetEndMessageId = marker.targetEndMessageId ?? marker.boundaryMessageId;
|
|
198151
|
+
if (!targetEndMessageId)
|
|
198152
|
+
return null;
|
|
198153
|
+
const markerKey = `${marker.boundaryOrdinal}:${targetEndMessageId}`;
|
|
198154
|
+
const cached2 = declaredTrimBySession.get(args.sessionId);
|
|
198155
|
+
if (cached2?.markerKey === markerKey)
|
|
198156
|
+
return cached2.trim;
|
|
198157
|
+
const boundaryRaw = readRawSessionMessageById(args.sessionId, targetEndMessageId);
|
|
198158
|
+
if (!boundaryRaw)
|
|
198159
|
+
return null;
|
|
198160
|
+
const compartments = getCompartmentsByEndMessageId(args.db, args.sessionId, targetEndMessageId);
|
|
198161
|
+
const boundaryCompartment = compartments.find((compartment) => compartment.endMessage === marker.boundaryOrdinal);
|
|
198162
|
+
if (!boundaryCompartment)
|
|
198163
|
+
return null;
|
|
198164
|
+
const trim = {
|
|
198165
|
+
flat_boundary_id: flatBlockIdForRawMessage(targetEndMessageId, boundaryRaw, "end"),
|
|
198166
|
+
boundary_bare_message_id: targetEndMessageId,
|
|
198167
|
+
boundary_absolute_ordinal: marker.boundaryOrdinal,
|
|
198168
|
+
next_absolute_ordinal: marker.boundaryOrdinal + 1
|
|
198169
|
+
};
|
|
198170
|
+
declaredTrimBySession.set(args.sessionId, { markerKey, trim });
|
|
198171
|
+
return trim;
|
|
198172
|
+
}
|
|
198173
|
+
function clearDeclaredTrimForSession(sessionId) {
|
|
198174
|
+
declaredTrimBySession.delete(sessionId);
|
|
198175
|
+
}
|
|
198176
|
+
function didDeclaredTrimAdvance(before, after) {
|
|
198177
|
+
if (!after)
|
|
198178
|
+
return false;
|
|
198179
|
+
if (!before)
|
|
198180
|
+
return true;
|
|
198181
|
+
return before.flat_boundary_id !== after.flat_boundary_id;
|
|
198182
|
+
}
|
|
198183
|
+
function resolveShadowWorkspaceContext(db, projectPath) {
|
|
198184
|
+
if (!projectPath) {
|
|
198185
|
+
return {
|
|
198186
|
+
workspace: null,
|
|
198187
|
+
expandedIdentities: [],
|
|
198188
|
+
ownIdentities: [],
|
|
198189
|
+
shareCategories: null
|
|
198190
|
+
};
|
|
198191
|
+
}
|
|
198192
|
+
const identitySet = resolveWorkspaceIdentitySet(db, projectPath);
|
|
198193
|
+
if (identitySet.identities.length <= 1) {
|
|
198194
|
+
return {
|
|
198195
|
+
workspace: null,
|
|
198196
|
+
expandedIdentities: [projectPath],
|
|
198197
|
+
ownIdentities: [projectPath],
|
|
198198
|
+
shareCategories: null
|
|
198199
|
+
};
|
|
198200
|
+
}
|
|
198201
|
+
const expanded = expandWorkspaceIdentitySetWithAliases(db, identitySet.identities);
|
|
198202
|
+
const ownIdentities = expanded.expandedIdentities.filter((identity) => expanded.canonicalIdentityByStoredPath.get(identity) === projectPath);
|
|
198203
|
+
if (ownIdentities.length === 0)
|
|
198204
|
+
ownIdentities.push(projectPath);
|
|
198205
|
+
const shareCategories = resolveWorkspaceShareCategories(db, projectPath) ?? [];
|
|
198206
|
+
const members = [
|
|
198207
|
+
projectPath,
|
|
198208
|
+
...expanded.expandedIdentities.filter((identity) => identity !== projectPath).sort((left, right) => left.localeCompare(right))
|
|
198209
|
+
];
|
|
198210
|
+
return {
|
|
198211
|
+
workspace: {
|
|
198212
|
+
fingerprint: computeWorkspaceEpochFingerprint(db, identitySet.identities),
|
|
198213
|
+
members: members.map((member) => ({
|
|
198214
|
+
project_path: member,
|
|
198215
|
+
share_categories: [...shareCategories]
|
|
198216
|
+
}))
|
|
198217
|
+
},
|
|
198218
|
+
expandedIdentities: members,
|
|
198219
|
+
ownIdentities,
|
|
198220
|
+
shareCategories
|
|
198221
|
+
};
|
|
198222
|
+
}
|
|
198223
|
+
function loadWatermarks(args) {
|
|
198224
|
+
const sessionMeta = getOrCreateSessionMeta(args.db, args.sessionId);
|
|
198225
|
+
const compartmentRow = args.db.prepare("SELECT COALESCE(MAX(sequence), -1) AS max_sequence FROM compartments WHERE session_id = ?").get(args.sessionId);
|
|
198226
|
+
const memoryId = args.projectPath ? getMaxMemoryIdForProjects(args.db, args.workspace.expandedIdentities, args.workspace.ownIdentities, args.workspace.shareCategories) : 0;
|
|
198227
|
+
const m0Row = args.db.prepare("SELECT COALESCE(MAX(id), 0) AS max_id FROM m0_mutation_log WHERE session_id = ?").get(args.sessionId);
|
|
198228
|
+
const memoryMutationId = args.projectPath ? getMaxMemoryMutationIdForProjects(args.db, args.workspace.expandedIdentities) ?? 0 : 0;
|
|
198229
|
+
return {
|
|
198230
|
+
compartment_sequence: compartmentRow?.max_sequence ?? -1,
|
|
198231
|
+
memory_id: memoryId,
|
|
198232
|
+
m0_mutation_id: m0Row?.max_id ?? 0,
|
|
198233
|
+
memory_mutation_id: memoryMutationId,
|
|
198234
|
+
last_todo_state_hash: stableHash(sessionMeta.lastTodoState ?? "")
|
|
198235
|
+
};
|
|
198236
|
+
}
|
|
198237
|
+
function watermarksEqual(left, right) {
|
|
198238
|
+
return left !== null && left.compartment_sequence === right.compartment_sequence && left.memory_id === right.memory_id && left.m0_mutation_id === right.m0_mutation_id && left.memory_mutation_id === right.memory_mutation_id && left.last_todo_state_hash === right.last_todo_state_hash;
|
|
198239
|
+
}
|
|
198240
|
+
function serializeCompartment(args) {
|
|
198241
|
+
const startRaw = args.readRawById(args.compartment.startMessageId);
|
|
198242
|
+
const endRaw = args.readRawById(args.compartment.endMessageId);
|
|
198243
|
+
const startOrdinal = ordinalForMessageId({
|
|
198244
|
+
raw: startRaw,
|
|
198245
|
+
messageId: args.compartment.startMessageId,
|
|
198246
|
+
generation: args.state.shadowGeneration,
|
|
198247
|
+
state: args.state
|
|
198248
|
+
});
|
|
198249
|
+
const endOrdinal = ordinalForMessageId({
|
|
198250
|
+
raw: endRaw,
|
|
198251
|
+
messageId: args.compartment.endMessageId,
|
|
198252
|
+
generation: args.state.shadowGeneration,
|
|
198253
|
+
state: args.state
|
|
198254
|
+
});
|
|
198255
|
+
if (startOrdinal === "mismatch" || endOrdinal === "mismatch")
|
|
198256
|
+
return "mismatch";
|
|
198257
|
+
if (startOrdinal === null || endOrdinal === null)
|
|
198258
|
+
return null;
|
|
198259
|
+
const startCreatedAt = startRaw?.createdAt;
|
|
198260
|
+
const endCreatedAt = endRaw?.createdAt;
|
|
198261
|
+
const dateRange = typeof startCreatedAt === "number" && typeof endCreatedAt === "number" ? {
|
|
198262
|
+
start_date: formatDate(startCreatedAt),
|
|
198263
|
+
end_date: formatDate(endCreatedAt)
|
|
198264
|
+
} : {};
|
|
198265
|
+
return {
|
|
198266
|
+
sequence: args.compartment.sequence,
|
|
198267
|
+
start_message: startOrdinal,
|
|
198268
|
+
end_message: endOrdinal,
|
|
198269
|
+
start_message_id: flatBlockIdForRawMessage(args.compartment.startMessageId, startRaw ?? undefined, "start"),
|
|
198270
|
+
end_message_id: flatBlockIdForRawMessage(args.compartment.endMessageId, endRaw ?? undefined, "end"),
|
|
198271
|
+
...dateRange,
|
|
198272
|
+
title: args.compartment.title,
|
|
198273
|
+
content: args.compartment.content,
|
|
198274
|
+
p1: args.compartment.p1,
|
|
198275
|
+
p2: args.compartment.p2,
|
|
198276
|
+
p3: args.compartment.p3,
|
|
198277
|
+
p4: args.compartment.p4,
|
|
198278
|
+
importance: args.compartment.importance,
|
|
198279
|
+
episode_type: args.compartment.episodeType,
|
|
198280
|
+
legacy: args.compartment.legacy,
|
|
198281
|
+
created_at: args.compartment.createdAt
|
|
198282
|
+
};
|
|
198283
|
+
}
|
|
198284
|
+
function buildStateSyncPayload(args) {
|
|
198285
|
+
const workspace = resolveShadowWorkspaceContext(args.pass.db, args.pass.projectPath);
|
|
198286
|
+
const currentWatermarks = loadWatermarks({
|
|
198287
|
+
db: args.pass.db,
|
|
198288
|
+
sessionId: args.pass.sessionId,
|
|
198289
|
+
projectPath: args.pass.projectPath,
|
|
198290
|
+
workspace
|
|
198291
|
+
});
|
|
198292
|
+
if (!args.force && args.state.lastAckedWatermarks && currentWatermarks.m0_mutation_id > args.state.lastAckedWatermarks.m0_mutation_id) {
|
|
198293
|
+
return "m0_mutation";
|
|
198294
|
+
}
|
|
198295
|
+
if (!args.force && watermarksEqual(args.state.lastAckedWatermarks, currentWatermarks)) {
|
|
198296
|
+
return null;
|
|
198297
|
+
}
|
|
198298
|
+
const acked = args.force ? {
|
|
198299
|
+
compartment_sequence: -1,
|
|
198300
|
+
memory_id: 0,
|
|
198301
|
+
m0_mutation_id: 0,
|
|
198302
|
+
memory_mutation_id: 0,
|
|
198303
|
+
last_todo_state_hash: ""
|
|
198304
|
+
} : args.state.lastAckedWatermarks ?? {
|
|
198305
|
+
compartment_sequence: -1,
|
|
198306
|
+
memory_id: 0,
|
|
198307
|
+
m0_mutation_id: 0,
|
|
198308
|
+
memory_mutation_id: 0,
|
|
198309
|
+
last_todo_state_hash: ""
|
|
198310
|
+
};
|
|
198311
|
+
const rawById = new Map;
|
|
198312
|
+
const readRawById = (messageId) => {
|
|
198313
|
+
if (!rawById.has(messageId)) {
|
|
198314
|
+
rawById.set(messageId, readRawSessionMessageById(args.pass.sessionId, messageId));
|
|
198315
|
+
}
|
|
198316
|
+
return rawById.get(messageId) ?? null;
|
|
198317
|
+
};
|
|
198318
|
+
const compartments = [];
|
|
198319
|
+
for (const compartment of getCompartments(args.pass.db, args.pass.sessionId)) {
|
|
198320
|
+
if (compartment.sequence <= acked.compartment_sequence)
|
|
198321
|
+
continue;
|
|
198322
|
+
const serialized = serializeCompartment({
|
|
198323
|
+
compartment,
|
|
198324
|
+
readRawById,
|
|
198325
|
+
state: args.state
|
|
198326
|
+
});
|
|
198327
|
+
if (serialized === "mismatch")
|
|
198328
|
+
return "mismatch";
|
|
198329
|
+
if (serialized === null)
|
|
198330
|
+
return "unresolved";
|
|
198331
|
+
compartments.push(serialized);
|
|
198332
|
+
}
|
|
198333
|
+
const allMemories = args.pass.projectPath ? workspace.workspace ? getMemoriesByProjects(args.pass.db, workspace.expandedIdentities, ["active", "permanent"], args.pass.passInputs.now_ms, workspace.ownIdentities, workspace.shareCategories) : getMemoriesByProject(args.pass.db, args.pass.projectPath, ["active", "permanent"], args.pass.passInputs.now_ms) : [];
|
|
198334
|
+
const memories = allMemories.filter((memory) => memory.id > acked.memory_id).map((memory) => ({
|
|
198335
|
+
id: memory.id,
|
|
198336
|
+
project_path: memory.projectPath,
|
|
198337
|
+
category: memory.category,
|
|
198338
|
+
content: memory.content,
|
|
198339
|
+
normalized_hash: memory.normalizedHash,
|
|
198340
|
+
importance: memory.importance,
|
|
198341
|
+
scope: memory.scope,
|
|
198342
|
+
shareable: memory.shareable,
|
|
198343
|
+
source_session_id: memory.sourceSessionId,
|
|
198344
|
+
source_type: memory.sourceType,
|
|
198345
|
+
seen_count: memory.seenCount,
|
|
198346
|
+
retrieval_count: memory.retrievalCount,
|
|
198347
|
+
first_seen_at: memory.firstSeenAt,
|
|
198348
|
+
created_at: memory.createdAt,
|
|
198349
|
+
updated_at: memory.updatedAt,
|
|
198350
|
+
last_seen_at: memory.lastSeenAt,
|
|
198351
|
+
last_retrieved_at: memory.lastRetrievedAt,
|
|
198352
|
+
status: memory.status,
|
|
198353
|
+
expires_at: memory.expiresAt,
|
|
198354
|
+
verification_status: memory.verificationStatus,
|
|
198355
|
+
verified_at: memory.verifiedAt,
|
|
198356
|
+
superseded_by_memory_id: memory.supersededByMemoryId,
|
|
198357
|
+
merged_from: memory.mergedFrom,
|
|
198358
|
+
metadata_json: memory.metadataJson
|
|
198359
|
+
}));
|
|
198360
|
+
const renderedMemoryIds = allMemories.map((memory) => memory.id);
|
|
198361
|
+
const memoryMutations = args.pass.projectPath ? getMemoryMutationsForRenderByProjects(args.pass.db, workspace.expandedIdentities, acked.memory_mutation_id, renderedMemoryIds).map((row) => ({
|
|
198362
|
+
id: row.id,
|
|
198363
|
+
project_path: row.projectPath,
|
|
198364
|
+
mutation_type: row.mutationType,
|
|
198365
|
+
target_memory_id: row.targetMemoryId,
|
|
198366
|
+
superseded_by_id: row.supersededById,
|
|
198367
|
+
category: row.category,
|
|
198368
|
+
new_content: row.newContent,
|
|
198369
|
+
queued_at: row.queuedAt
|
|
198370
|
+
})) : [];
|
|
198371
|
+
const sessionMeta = getOrCreateSessionMeta(args.pass.db, args.pass.sessionId);
|
|
198372
|
+
return {
|
|
198373
|
+
method: "state_sync",
|
|
198374
|
+
params: {
|
|
198375
|
+
shadow_generation: args.state.shadowGeneration,
|
|
198376
|
+
expected_shadow_seq: args.state.lastAckedSeq,
|
|
198377
|
+
seed_boundary_id: args.state.seedPassPending && compartments.length > 0 ? args.pass.declaredTrim?.flat_boundary_id ?? null : null,
|
|
198378
|
+
compartments,
|
|
198379
|
+
memories,
|
|
198380
|
+
memory_mutations: memoryMutations,
|
|
198381
|
+
workspace: workspace.workspace,
|
|
198382
|
+
last_todo_state: sessionMeta.lastTodoState ?? ""
|
|
198383
|
+
},
|
|
198384
|
+
watermarks: currentWatermarks
|
|
198385
|
+
};
|
|
198386
|
+
}
|
|
198387
|
+
function extractAckValue(response) {
|
|
198388
|
+
if (isRecord(response) && isRecord(response.result))
|
|
198389
|
+
return response.result;
|
|
198390
|
+
return isRecord(response) ? response : {};
|
|
198391
|
+
}
|
|
198392
|
+
function numericAck(response, keys, fallback) {
|
|
198393
|
+
const value = extractAckValue(response);
|
|
198394
|
+
for (const key of keys) {
|
|
198395
|
+
const candidate = value[key];
|
|
198396
|
+
if (typeof candidate === "number" && Number.isFinite(candidate))
|
|
198397
|
+
return candidate;
|
|
198398
|
+
}
|
|
198399
|
+
return fallback;
|
|
198400
|
+
}
|
|
198401
|
+
function errorCode(error51) {
|
|
198402
|
+
if (isRecord(error51) && typeof error51.code === "string")
|
|
198403
|
+
return error51.code;
|
|
198404
|
+
return null;
|
|
198405
|
+
}
|
|
198406
|
+
function isPeerReject(error51) {
|
|
198407
|
+
const code = errorCode(error51);
|
|
198408
|
+
if (code === "stale_generation" || code === "shadow_generation_mismatch" || code === "shadow_seq_mismatch" || code === "seq_mismatch" || code === "cas_mismatch" || code === "state_cas_reject") {
|
|
198409
|
+
return true;
|
|
198410
|
+
}
|
|
198411
|
+
const text = error51 instanceof Error ? error51.message : String(error51);
|
|
198412
|
+
return text.includes("generation") || text.includes("shadow_seq") || text.includes("seq mismatch") || text.includes("CAS");
|
|
198413
|
+
}
|
|
198414
|
+
function isSeedBoundaryReject(error51) {
|
|
198415
|
+
return errorCode(error51) === "shadow_seed_boundary_mismatch";
|
|
198416
|
+
}
|
|
198417
|
+
function isConnectionFailure(error51) {
|
|
198418
|
+
const code = errorCode(error51);
|
|
198419
|
+
if (code === "ENOENT" || code === "ECONNREFUSED" || code === "ECONNRESET")
|
|
198420
|
+
return true;
|
|
198421
|
+
const text = error51 instanceof Error ? error51.message : String(error51);
|
|
198422
|
+
return text.includes("backoff") || text.includes("connection") || text.includes("ECONN");
|
|
198423
|
+
}
|
|
198424
|
+
function toFlatWireBody(payload) {
|
|
198425
|
+
return { method: payload.method, ...payload.params };
|
|
198426
|
+
}
|
|
198427
|
+
function buildShadowResetBody(args) {
|
|
198428
|
+
return {
|
|
198429
|
+
method: "shadow_reset",
|
|
198430
|
+
params: {
|
|
198431
|
+
shadow_generation: args.state.shadowGeneration,
|
|
198432
|
+
reason: args.reason
|
|
198433
|
+
}
|
|
198434
|
+
};
|
|
198435
|
+
}
|
|
198436
|
+
function buildShadowTransformBody(args) {
|
|
198437
|
+
const denormalized = args.pass.shadowTsOutput !== undefined && args.pass.shadowNormalizations !== undefined ? {
|
|
198438
|
+
ts_output: args.pass.shadowTsOutput,
|
|
198439
|
+
normalizations: args.pass.shadowNormalizations
|
|
198440
|
+
} : denormalizeShadowOutput({
|
|
198441
|
+
db: args.pass.db,
|
|
198442
|
+
sessionId: args.pass.sessionId,
|
|
198443
|
+
outputMessages: args.pass.outputMessages,
|
|
198444
|
+
normalizationTargets: args.pass.normalizationTargets
|
|
198445
|
+
});
|
|
198446
|
+
return {
|
|
198447
|
+
method: "shadow_transform",
|
|
198448
|
+
params: {
|
|
198449
|
+
shadow_generation: args.state.shadowGeneration,
|
|
198450
|
+
seed_pass: args.state.seedPassPending,
|
|
198451
|
+
input: args.pass.annotatedInput,
|
|
198452
|
+
ts_output: denormalized.ts_output,
|
|
198453
|
+
normalizations: denormalized.normalizations,
|
|
198454
|
+
pass_inputs: args.pass.passInputs,
|
|
198455
|
+
ts_decision: args.pass.tsDecision,
|
|
198456
|
+
declared_trim: args.pass.declaredTrim
|
|
198457
|
+
}
|
|
198458
|
+
};
|
|
198459
|
+
}
|
|
198460
|
+
function createShadowSender(options = {}) {
|
|
198461
|
+
const transport = options.transport ?? new SubcShadowTransport;
|
|
198462
|
+
const now = options.now ?? Date.now;
|
|
198463
|
+
const reseedCooldownMs = options.reseedCooldownMs ?? RESEED_COOLDOWN_MS;
|
|
198464
|
+
const reseedAttemptCap = options.reseedAttemptCap ?? RESEED_ATTEMPT_CAP;
|
|
198465
|
+
const sessions = new Map;
|
|
198466
|
+
const subagentSessions = new Set;
|
|
198467
|
+
const getState = (sessionId) => {
|
|
198468
|
+
let state = sessions.get(sessionId);
|
|
198469
|
+
if (!state) {
|
|
198470
|
+
state = createSessionQueueState();
|
|
198471
|
+
sessions.set(sessionId, state);
|
|
198472
|
+
}
|
|
198473
|
+
return state;
|
|
198474
|
+
};
|
|
198475
|
+
const schedule = (sessionId) => {
|
|
198476
|
+
const state = getState(sessionId);
|
|
198477
|
+
if (state.running)
|
|
198478
|
+
return;
|
|
198479
|
+
state.running = true;
|
|
198480
|
+
runQueue(sessionId, state).finally(() => {
|
|
198481
|
+
state.running = false;
|
|
198482
|
+
if (state.queue.length > 0)
|
|
198483
|
+
schedule(sessionId);
|
|
198484
|
+
});
|
|
198485
|
+
};
|
|
198486
|
+
const pushWork = (sessionId, work) => {
|
|
198487
|
+
const state = getState(sessionId);
|
|
198488
|
+
if (work.kind === "pass") {
|
|
198489
|
+
state.counters.enqueued += 1;
|
|
198490
|
+
let dropped = 0;
|
|
198491
|
+
for (let index = state.queue.length - 1;index >= 0; index -= 1) {
|
|
198492
|
+
if (state.queue[index]?.kind !== "pass")
|
|
198493
|
+
continue;
|
|
198494
|
+
state.queue.splice(index, 1);
|
|
198495
|
+
dropped += 1;
|
|
198496
|
+
}
|
|
198497
|
+
if (dropped > 0) {
|
|
198498
|
+
state.counters.dropped_oldest += dropped;
|
|
198499
|
+
sessionLog(sessionId, `shadow: dropped ${dropped} stale queued pass${dropped === 1 ? "" : "es"}`);
|
|
198500
|
+
}
|
|
198501
|
+
}
|
|
198502
|
+
state.queue.push(work);
|
|
198503
|
+
schedule(sessionId);
|
|
198504
|
+
};
|
|
198505
|
+
const runQueue = async (sessionId, state) => {
|
|
198506
|
+
while (!state.skipped && state.queue.length > 0) {
|
|
198507
|
+
const item = state.queue.shift();
|
|
198508
|
+
if (!item)
|
|
198509
|
+
continue;
|
|
198510
|
+
if (item.kind === "reset") {
|
|
198511
|
+
try {
|
|
198512
|
+
await performReset({
|
|
198513
|
+
sessionId,
|
|
198514
|
+
state,
|
|
198515
|
+
reason: item.reason,
|
|
198516
|
+
projectRoot: item.projectRoot
|
|
198517
|
+
});
|
|
198518
|
+
} catch (error51) {
|
|
198519
|
+
state.counters.send_failures += 1;
|
|
198520
|
+
state.initialized = false;
|
|
198521
|
+
state.blockedUntilReset = true;
|
|
198522
|
+
state.requireResetReason ??= item.reason || "reset_retry";
|
|
198523
|
+
sessionLog(sessionId, "shadow: reset failed (ignored):", error51);
|
|
198524
|
+
}
|
|
198525
|
+
continue;
|
|
198526
|
+
}
|
|
198527
|
+
try {
|
|
198528
|
+
await processPass(state, item.pass);
|
|
198529
|
+
} catch (error51) {
|
|
198530
|
+
state.counters.send_failures += 1;
|
|
198531
|
+
if (isPeerReject(error51)) {
|
|
198532
|
+
const code = errorCode(error51);
|
|
198533
|
+
if (code?.includes("generation"))
|
|
198534
|
+
state.counters.generation_rejects += 1;
|
|
198535
|
+
else
|
|
198536
|
+
state.counters.state_sync_rejects += 1;
|
|
198537
|
+
state.requireResetReason = code ?? "peer_reject";
|
|
198538
|
+
state.blockedUntilReset = true;
|
|
198539
|
+
state.lastAckedWatermarks = null;
|
|
198540
|
+
} else if (isConnectionFailure(error51)) {
|
|
198541
|
+
state.counters.connection_skips += 1;
|
|
198542
|
+
state.initialized = false;
|
|
198543
|
+
state.requireResetReason = "route_reopen";
|
|
198544
|
+
}
|
|
198545
|
+
sessionLog(sessionId, "shadow: send failed (ignored):", error51);
|
|
198546
|
+
}
|
|
198547
|
+
}
|
|
198548
|
+
};
|
|
198549
|
+
const performReset = async (args) => {
|
|
198550
|
+
const projectRoot = args.projectRoot ?? process.cwd();
|
|
198551
|
+
const body = toFlatWireBody(buildShadowResetBody(args));
|
|
198552
|
+
const response = await transport.call({
|
|
198553
|
+
sessionId: args.sessionId,
|
|
198554
|
+
projectRoot,
|
|
198555
|
+
method: "shadow_reset",
|
|
198556
|
+
body
|
|
198557
|
+
});
|
|
198558
|
+
args.state.shadowGeneration = numericAck(response, ["shadow_generation", "generation"], args.state.shadowGeneration + 1);
|
|
198559
|
+
args.state.lastAckedSeq = numericAck(response, ["shadow_seq", "seq"], 0);
|
|
198560
|
+
args.state.lastAckedWatermarks = null;
|
|
198561
|
+
args.state.idOrdinalMemo.clear();
|
|
198562
|
+
args.state.idOrdinalMemoGeneration = args.state.shadowGeneration;
|
|
198563
|
+
clearDeclaredTrimForSession(args.sessionId);
|
|
198564
|
+
args.state.initialized = true;
|
|
198565
|
+
args.state.blockedUntilReset = false;
|
|
198566
|
+
args.state.requireResetReason = null;
|
|
198567
|
+
args.state.seedPassPending = true;
|
|
198568
|
+
args.state.counters.resets_sent += 1;
|
|
198569
|
+
sessionLog(args.sessionId, `shadow: reset acknowledged (generation=${args.state.shadowGeneration})`);
|
|
198570
|
+
};
|
|
198571
|
+
const beginReseed = (state) => {
|
|
198572
|
+
const attemptAt = now();
|
|
198573
|
+
if (state.reseedAttempts >= reseedAttemptCap)
|
|
198574
|
+
return false;
|
|
198575
|
+
if (state.lastReseedAttemptMs !== null && attemptAt - state.lastReseedAttemptMs < reseedCooldownMs) {
|
|
198576
|
+
return false;
|
|
198577
|
+
}
|
|
198578
|
+
state.reseedAttempts += 1;
|
|
198579
|
+
state.lastReseedAttemptMs = attemptAt;
|
|
198580
|
+
state.reseedAwaitingSuccess = true;
|
|
198581
|
+
return true;
|
|
198582
|
+
};
|
|
198583
|
+
const processPass = async (state, pass) => {
|
|
198584
|
+
if (state.skipped)
|
|
198585
|
+
return;
|
|
198586
|
+
if (state.blockedUntilReset && !state.requireResetReason) {
|
|
198587
|
+
state.requireResetReason = "unknown_block";
|
|
198588
|
+
}
|
|
198589
|
+
if (!state.initialized || state.requireResetReason) {
|
|
198590
|
+
await performReset({
|
|
198591
|
+
sessionId: pass.sessionId,
|
|
198592
|
+
state,
|
|
198593
|
+
reason: state.requireResetReason ?? "cold_start",
|
|
198594
|
+
projectRoot: pass.projectRoot
|
|
198595
|
+
});
|
|
198596
|
+
}
|
|
198597
|
+
if (state.skipped || state.blockedUntilReset)
|
|
198598
|
+
return;
|
|
198599
|
+
let resolved;
|
|
198600
|
+
try {
|
|
198601
|
+
resolved = resolveOrdinalsForShadow({
|
|
198602
|
+
sessionId: pass.sessionId,
|
|
198603
|
+
messages: pass.inputMessages,
|
|
198604
|
+
generation: state.shadowGeneration,
|
|
198605
|
+
memoGeneration: state.idOrdinalMemoGeneration,
|
|
198606
|
+
memo: state.idOrdinalMemo
|
|
198607
|
+
});
|
|
198608
|
+
} catch (error51) {
|
|
198609
|
+
sessionLog(pass.sessionId, "shadow: capture failed (ignored):", error51);
|
|
198610
|
+
return;
|
|
198611
|
+
}
|
|
198612
|
+
if (!resolved.ok) {
|
|
198613
|
+
if (resolved.reason === "mismatch") {
|
|
198614
|
+
state.counters.ordinal_mismatch += 1;
|
|
198615
|
+
state.requireResetReason = "ordinal_mismatch";
|
|
198616
|
+
state.blockedUntilReset = true;
|
|
198617
|
+
state.idOrdinalMemo.clear();
|
|
198618
|
+
await performReset({
|
|
198619
|
+
sessionId: pass.sessionId,
|
|
198620
|
+
state,
|
|
198621
|
+
reason: "ordinal_mismatch",
|
|
198622
|
+
projectRoot: pass.projectRoot
|
|
198623
|
+
});
|
|
198624
|
+
} else {
|
|
198625
|
+
state.counters.ordinal_unresolved += 1;
|
|
198626
|
+
sessionLog(pass.sessionId, `shadow: pass skipped; unresolved ordinal for ${resolved.messageId ?? "unknown"}`);
|
|
198627
|
+
}
|
|
198628
|
+
return;
|
|
198629
|
+
}
|
|
198630
|
+
state.idOrdinalMemoGeneration = resolved.memoGeneration;
|
|
198631
|
+
let preparedPass;
|
|
198632
|
+
let syncPayload;
|
|
198633
|
+
try {
|
|
198634
|
+
const declaredTrim = resolveDeclaredTrimForShadow({
|
|
198635
|
+
db: pass.db,
|
|
198636
|
+
sessionId: pass.sessionId
|
|
198637
|
+
});
|
|
198638
|
+
const denormalized = denormalizeShadowOutput({
|
|
198639
|
+
db: pass.db,
|
|
198640
|
+
sessionId: pass.sessionId,
|
|
198641
|
+
outputMessages: pass.outputMessages,
|
|
198642
|
+
normalizationTargets: pass.normalizationTargets
|
|
198643
|
+
});
|
|
198644
|
+
preparedPass = {
|
|
198645
|
+
...pass,
|
|
198646
|
+
annotatedInput: resolved.annotatedInput,
|
|
198647
|
+
shadowTsOutput: denormalized.ts_output,
|
|
198648
|
+
shadowNormalizations: [...resolved.normalizations, ...denormalized.normalizations],
|
|
198649
|
+
declaredTrim,
|
|
198650
|
+
tsDecision: {
|
|
198651
|
+
...pass.tsDecision,
|
|
198652
|
+
marker_state: {
|
|
198653
|
+
marker_message_id: declaredTrim?.boundary_bare_message_id,
|
|
198654
|
+
advanced_this_pass: didDeclaredTrimAdvance(pass.declaredTrimBefore, declaredTrim)
|
|
198655
|
+
}
|
|
198656
|
+
}
|
|
198657
|
+
};
|
|
198658
|
+
syncPayload = buildStateSyncPayload({
|
|
198659
|
+
state,
|
|
198660
|
+
pass: preparedPass,
|
|
198661
|
+
force: state.lastAckedWatermarks === null
|
|
198662
|
+
});
|
|
198663
|
+
} catch (error51) {
|
|
198664
|
+
sessionLog(pass.sessionId, "shadow: capture failed (ignored):", error51);
|
|
198665
|
+
return;
|
|
198666
|
+
}
|
|
198667
|
+
if (syncPayload === "m0_mutation") {
|
|
198668
|
+
await performReset({
|
|
198669
|
+
sessionId: pass.sessionId,
|
|
198670
|
+
state,
|
|
198671
|
+
reason: "m0_mutation",
|
|
198672
|
+
projectRoot: pass.projectRoot
|
|
198673
|
+
});
|
|
198674
|
+
try {
|
|
198675
|
+
const fullSync = buildStateSyncPayload({ state, pass: preparedPass, force: true });
|
|
198676
|
+
if (fullSync === "m0_mutation") {
|
|
198677
|
+
throw new Error("forced state sync unexpectedly requested another m0 reset");
|
|
198678
|
+
}
|
|
198679
|
+
syncPayload = fullSync;
|
|
198680
|
+
} catch (error51) {
|
|
198681
|
+
sessionLog(pass.sessionId, "shadow: capture failed (ignored):", error51);
|
|
198682
|
+
return;
|
|
198683
|
+
}
|
|
198684
|
+
}
|
|
198685
|
+
if (syncPayload === "mismatch") {
|
|
198686
|
+
state.counters.ordinal_mismatch += 1;
|
|
198687
|
+
state.requireResetReason = "ordinal_mismatch";
|
|
198688
|
+
await performReset({
|
|
198689
|
+
sessionId: pass.sessionId,
|
|
198690
|
+
state,
|
|
198691
|
+
reason: "ordinal_mismatch",
|
|
198692
|
+
projectRoot: pass.projectRoot
|
|
198693
|
+
});
|
|
198694
|
+
return;
|
|
198695
|
+
}
|
|
198696
|
+
if (syncPayload === "unresolved") {
|
|
198697
|
+
state.counters.ordinal_unresolved += 1;
|
|
198698
|
+
sessionLog(pass.sessionId, "shadow: state sync skipped; compartment ordinal unresolved");
|
|
198699
|
+
return;
|
|
198700
|
+
}
|
|
198701
|
+
if (syncPayload !== null) {
|
|
198702
|
+
let response2;
|
|
198703
|
+
try {
|
|
198704
|
+
response2 = await transport.call({
|
|
198705
|
+
sessionId: pass.sessionId,
|
|
198706
|
+
projectRoot: pass.projectRoot,
|
|
198707
|
+
method: "state_sync",
|
|
198708
|
+
body: toFlatWireBody(syncPayload)
|
|
198709
|
+
});
|
|
198710
|
+
} catch (error51) {
|
|
198711
|
+
if (isSeedBoundaryReject(error51) && beginReseed(state)) {
|
|
198712
|
+
await performReset({
|
|
198713
|
+
sessionId: pass.sessionId,
|
|
198714
|
+
state,
|
|
198715
|
+
reason: "seed_boundary_reseed",
|
|
198716
|
+
projectRoot: pass.projectRoot
|
|
198717
|
+
});
|
|
198718
|
+
if (!state.skipped)
|
|
198719
|
+
await processPass(state, pass);
|
|
198720
|
+
return;
|
|
198721
|
+
}
|
|
198722
|
+
throw error51;
|
|
198723
|
+
}
|
|
198724
|
+
if (state.skipped)
|
|
198725
|
+
return;
|
|
198726
|
+
state.lastAckedSeq = numericAck(response2, ["shadow_seq", "seq"], syncPayload.params.expected_shadow_seq + 1);
|
|
198727
|
+
state.lastAckedWatermarks = syncPayload.watermarks;
|
|
198728
|
+
}
|
|
198729
|
+
const transformBody = toFlatWireBody(buildShadowTransformBody({ pass: preparedPass, state }));
|
|
198730
|
+
const response = await transport.call({
|
|
198731
|
+
sessionId: pass.sessionId,
|
|
198732
|
+
projectRoot: pass.projectRoot,
|
|
198733
|
+
method: "shadow_transform",
|
|
198734
|
+
body: transformBody
|
|
198735
|
+
});
|
|
198736
|
+
if (state.skipped)
|
|
198737
|
+
return;
|
|
198738
|
+
const ack = extractAckValue(response);
|
|
198739
|
+
state.seedPassPending = false;
|
|
198740
|
+
state.counters.transforms_sent += 1;
|
|
198741
|
+
if (ack.divergence || ack.divergence_class || ack.hard_divergence) {
|
|
198742
|
+
sessionLog(pass.sessionId, "shadow: divergence report", ack);
|
|
198743
|
+
}
|
|
198744
|
+
if (ack.quarantined === true && beginReseed(state)) {
|
|
198745
|
+
await performReset({
|
|
198746
|
+
sessionId: pass.sessionId,
|
|
198747
|
+
state,
|
|
198748
|
+
reason: "quarantine_reseed",
|
|
198749
|
+
projectRoot: pass.projectRoot
|
|
198750
|
+
});
|
|
198751
|
+
if (!state.skipped)
|
|
198752
|
+
await processPass(state, pass);
|
|
198753
|
+
} else if (ack.quarantined !== true && state.reseedAwaitingSuccess) {
|
|
198754
|
+
state.reseedAttempts = 0;
|
|
198755
|
+
state.lastReseedAttemptMs = null;
|
|
198756
|
+
state.reseedAwaitingSuccess = false;
|
|
198757
|
+
}
|
|
198758
|
+
};
|
|
198759
|
+
return {
|
|
198760
|
+
enqueue(pass) {
|
|
198761
|
+
if (pass.isSubagent) {
|
|
198762
|
+
const state = sessions.get(pass.sessionId);
|
|
198763
|
+
if (state) {
|
|
198764
|
+
state.skipped = true;
|
|
198765
|
+
state.queue.length = 0;
|
|
198766
|
+
sessions.delete(pass.sessionId);
|
|
198767
|
+
}
|
|
198768
|
+
clearDeclaredTrimForSession(pass.sessionId);
|
|
198769
|
+
transport.closeSession?.(pass.sessionId);
|
|
198770
|
+
if (!subagentSessions.has(pass.sessionId)) {
|
|
198771
|
+
subagentSessions.add(pass.sessionId);
|
|
198772
|
+
sessionLog(pass.sessionId, "shadow: skipped (subagent session)");
|
|
198773
|
+
}
|
|
198774
|
+
return;
|
|
198775
|
+
}
|
|
198776
|
+
pushWork(pass.sessionId, { kind: "pass", pass });
|
|
198777
|
+
},
|
|
198778
|
+
resetSession(sessionId, reason) {
|
|
198779
|
+
if (subagentSessions.has(sessionId))
|
|
198780
|
+
return;
|
|
198781
|
+
const state = getState(sessionId);
|
|
198782
|
+
state.queue.length = 0;
|
|
198783
|
+
state.requireResetReason = reason;
|
|
198784
|
+
state.blockedUntilReset = true;
|
|
198785
|
+
transport.closeSession?.(sessionId);
|
|
198786
|
+
pushWork(sessionId, { kind: "reset", sessionId, reason });
|
|
198787
|
+
},
|
|
198788
|
+
clearSession(sessionId) {
|
|
198789
|
+
const state = sessions.get(sessionId);
|
|
198790
|
+
if (state)
|
|
198791
|
+
state.skipped = true;
|
|
198792
|
+
sessions.delete(sessionId);
|
|
198793
|
+
subagentSessions.delete(sessionId);
|
|
198794
|
+
clearDeclaredTrimForSession(sessionId);
|
|
198795
|
+
transport.closeSession?.(sessionId);
|
|
198796
|
+
},
|
|
198797
|
+
getStats(sessionId) {
|
|
198798
|
+
return { ...getState(sessionId).counters };
|
|
198799
|
+
}
|
|
198800
|
+
};
|
|
198801
|
+
}
|
|
198802
|
+
|
|
198803
|
+
class SubcShadowTransport {
|
|
198804
|
+
connectionFile;
|
|
198805
|
+
moduleId;
|
|
198806
|
+
socket = null;
|
|
198807
|
+
reader = null;
|
|
198808
|
+
nextCorr = 1;
|
|
198809
|
+
routes = new Map;
|
|
198810
|
+
pending = Promise.resolve();
|
|
198811
|
+
nextProbeMs = 0;
|
|
198812
|
+
backoffMs = CONNECT_BACKOFF_INITIAL_MS;
|
|
198813
|
+
requestTimeoutMs;
|
|
198814
|
+
constructor(connectionFile, moduleId = DEFAULT_MODULE_ID, requestTimeoutMs = REQUEST_TIMEOUT_MS) {
|
|
198815
|
+
this.connectionFile = connectionFile ?? getDefaultConnectionFile();
|
|
198816
|
+
this.moduleId = moduleId;
|
|
198817
|
+
this.requestTimeoutMs = requestTimeoutMs;
|
|
198818
|
+
}
|
|
198819
|
+
async call(args) {
|
|
198820
|
+
const run = async () => {
|
|
198821
|
+
const route = await this.ensureRoute(args.sessionId, args.projectRoot);
|
|
198822
|
+
return await this.unaryJson(route, args.body);
|
|
198823
|
+
};
|
|
198824
|
+
const next = this.pending.then(run, run);
|
|
198825
|
+
this.pending = next.then(() => {
|
|
198826
|
+
return;
|
|
198827
|
+
}, () => {
|
|
198828
|
+
return;
|
|
198829
|
+
});
|
|
198830
|
+
return next;
|
|
198831
|
+
}
|
|
198832
|
+
closeSession(sessionId) {
|
|
198833
|
+
this.routes.delete(sessionId);
|
|
198834
|
+
}
|
|
198835
|
+
async ensureRoute(sessionId, projectRoot) {
|
|
198836
|
+
const existing = this.routes.get(sessionId);
|
|
198837
|
+
if (existing !== undefined)
|
|
198838
|
+
return existing;
|
|
198839
|
+
await this.ensureConnected();
|
|
198840
|
+
const body = {
|
|
198841
|
+
op: "route.open",
|
|
198842
|
+
target: { kind: "tool_provider", module_id: this.moduleId },
|
|
198843
|
+
identity: {
|
|
198844
|
+
project_root: projectRoot,
|
|
198845
|
+
harness: getHarness(),
|
|
198846
|
+
session: `shadow:${sessionId}`
|
|
198847
|
+
}
|
|
198848
|
+
};
|
|
198849
|
+
const moduleId = process.env.SUBC_MODULE_ID;
|
|
198850
|
+
const launchNonce = process.env.SUBC_LAUNCH_NONCE;
|
|
198851
|
+
if (moduleId && launchNonce) {
|
|
198852
|
+
body.consumer_identity = { module_id: moduleId, launch_nonce: launchNonce };
|
|
198853
|
+
}
|
|
198854
|
+
const response = await this.unaryJson(0, body);
|
|
198855
|
+
const ack = extractAckValue(response);
|
|
198856
|
+
const route = ack.route_channel;
|
|
198857
|
+
if (typeof route !== "number")
|
|
198858
|
+
throw new Error("connection route.open missing route_channel");
|
|
198859
|
+
this.routes.set(sessionId, route);
|
|
198860
|
+
return route;
|
|
198861
|
+
}
|
|
198862
|
+
async ensureConnected() {
|
|
198863
|
+
if (this.socket && !this.socket.destroyed && this.reader)
|
|
198864
|
+
return;
|
|
198865
|
+
const now = Date.now();
|
|
198866
|
+
if (now < this.nextProbeMs) {
|
|
198867
|
+
throw new Error(`connection backoff active until ${this.nextProbeMs}`);
|
|
198868
|
+
}
|
|
198869
|
+
let candidate = null;
|
|
198870
|
+
try {
|
|
198871
|
+
const conn = readConnectionInfo(this.connectionFile);
|
|
198872
|
+
const endpoint = conn.endpoints[0];
|
|
198873
|
+
if (!endpoint)
|
|
198874
|
+
throw new Error("connection file has no endpoint");
|
|
198875
|
+
candidate = await connectTcp(endpoint.host, endpoint.port, HANDSHAKE_TIMEOUT_MS);
|
|
198876
|
+
const reader = new SocketReader(candidate);
|
|
198877
|
+
await authenticateSubcClient(candidate, reader, conn, HANDSHAKE_TIMEOUT_MS);
|
|
198878
|
+
const socket = candidate;
|
|
198879
|
+
this.socket = socket;
|
|
198880
|
+
this.reader = reader;
|
|
198881
|
+
this.routes.clear();
|
|
198882
|
+
this.backoffMs = CONNECT_BACKOFF_INITIAL_MS;
|
|
198883
|
+
this.nextProbeMs = 0;
|
|
198884
|
+
socket.once("close", () => {
|
|
198885
|
+
if (this.socket === socket)
|
|
198886
|
+
this.invalidateConnection(socket);
|
|
198887
|
+
});
|
|
198888
|
+
} catch (error51) {
|
|
198889
|
+
candidate?.destroy();
|
|
198890
|
+
this.invalidateConnection();
|
|
198891
|
+
this.nextProbeMs = Date.now() + this.backoffMs;
|
|
198892
|
+
this.backoffMs = Math.min(this.backoffMs * 2, CONNECT_BACKOFF_MAX_MS);
|
|
198893
|
+
throw error51;
|
|
198894
|
+
}
|
|
198895
|
+
}
|
|
198896
|
+
invalidateConnection(socket = this.socket) {
|
|
198897
|
+
if (socket && this.socket && socket !== this.socket)
|
|
198898
|
+
return;
|
|
198899
|
+
this.socket = null;
|
|
198900
|
+
this.reader = null;
|
|
198901
|
+
this.routes.clear();
|
|
198902
|
+
socket?.destroy();
|
|
198903
|
+
}
|
|
198904
|
+
async unaryJson(channel, body) {
|
|
198905
|
+
await this.ensureConnected();
|
|
198906
|
+
const socket = this.socket;
|
|
198907
|
+
const reader = this.reader;
|
|
198908
|
+
if (!socket || !reader)
|
|
198909
|
+
throw new Error("connection unavailable");
|
|
198910
|
+
const corr = this.nextCorr++;
|
|
198911
|
+
try {
|
|
198912
|
+
await writeFrame(socket, {
|
|
198913
|
+
type: FRAME_REQUEST,
|
|
198914
|
+
channel,
|
|
198915
|
+
corr,
|
|
198916
|
+
body: Buffer.from(JSON.stringify(body))
|
|
198917
|
+
});
|
|
198918
|
+
const frame = await readTerminalFor(reader, channel, corr, this.requestTimeoutMs);
|
|
198919
|
+
if (frame.type === FRAME_ERROR) {
|
|
198920
|
+
throw parseErrorBody(frame.body);
|
|
198921
|
+
}
|
|
198922
|
+
if (frame.type !== FRAME_RESPONSE) {
|
|
198923
|
+
throw new Error(`unexpected subc frame type ${frame.type}`);
|
|
198924
|
+
}
|
|
198925
|
+
if (frame.body.length === 0)
|
|
198926
|
+
return null;
|
|
198927
|
+
return JSON.parse(frame.body.toString("utf8"));
|
|
198928
|
+
} catch (error51) {
|
|
198929
|
+
this.invalidateConnection(socket);
|
|
198930
|
+
throw error51;
|
|
198931
|
+
}
|
|
198932
|
+
}
|
|
198933
|
+
}
|
|
198934
|
+
function readConnectionInfo(path10) {
|
|
198935
|
+
if (!existsSync21(path10))
|
|
198936
|
+
throw new Error(`connection file not found: ${path10}`);
|
|
198937
|
+
try {
|
|
198938
|
+
const mode = statSync8(path10).mode & 511;
|
|
198939
|
+
if ((mode & 63) !== 0)
|
|
198940
|
+
throw new Error(`connection file has insecure mode ${mode.toString(8)}`);
|
|
198941
|
+
} catch (error51) {
|
|
198942
|
+
if (error51 instanceof Error && error51.message.includes("insecure"))
|
|
198943
|
+
throw error51;
|
|
198944
|
+
}
|
|
198945
|
+
const parsed = JSON.parse(readFileSync13(path10, "utf8"));
|
|
198946
|
+
if (parsed.schema !== 1)
|
|
198947
|
+
throw new Error(`unsupported connection schema ${parsed.schema}`);
|
|
198948
|
+
if (!Array.isArray(parsed.endpoints) || parsed.endpoints.length === 0) {
|
|
198949
|
+
throw new Error("connection file has no endpoints");
|
|
198950
|
+
}
|
|
198951
|
+
if (!Array.isArray(parsed.key) || parsed.key.length < 32)
|
|
198952
|
+
throw new Error("connection key too short");
|
|
198953
|
+
return parsed;
|
|
198954
|
+
}
|
|
198955
|
+
function connectTcp(host, port, timeoutMs) {
|
|
198956
|
+
return new Promise((resolve4, reject) => {
|
|
198957
|
+
const socket = net.createConnection({ host, port });
|
|
198958
|
+
const timer = setTimeout(() => {
|
|
198959
|
+
socket.destroy();
|
|
198960
|
+
reject(new Error("connection timeout"));
|
|
198961
|
+
}, timeoutMs);
|
|
198962
|
+
socket.once("connect", () => {
|
|
198963
|
+
clearTimeout(timer);
|
|
198964
|
+
resolve4(socket);
|
|
198965
|
+
});
|
|
198966
|
+
socket.once("error", (error51) => {
|
|
198967
|
+
clearTimeout(timer);
|
|
198968
|
+
reject(error51);
|
|
198969
|
+
});
|
|
198970
|
+
});
|
|
198971
|
+
}
|
|
198972
|
+
|
|
198973
|
+
class SocketReader {
|
|
198974
|
+
chunks = [];
|
|
198975
|
+
buffered = 0;
|
|
198976
|
+
waiters = [];
|
|
198977
|
+
closed = false;
|
|
198978
|
+
constructor(socket) {
|
|
198979
|
+
socket.on("data", (chunk) => {
|
|
198980
|
+
this.chunks.push(Buffer.from(chunk));
|
|
198981
|
+
this.buffered += chunk.length;
|
|
198982
|
+
this.flushWaiters();
|
|
198983
|
+
});
|
|
198984
|
+
socket.on("close", () => {
|
|
198985
|
+
this.closed = true;
|
|
198986
|
+
this.flushWaiters();
|
|
198987
|
+
});
|
|
198988
|
+
socket.on("error", () => {
|
|
198989
|
+
this.closed = true;
|
|
198990
|
+
this.flushWaiters();
|
|
198991
|
+
});
|
|
198992
|
+
}
|
|
198993
|
+
async readExact(length, timeoutMs) {
|
|
198994
|
+
const deadline = Date.now() + timeoutMs;
|
|
198995
|
+
while (this.buffered < length) {
|
|
198996
|
+
if (this.closed)
|
|
198997
|
+
throw new Error("connection closed");
|
|
198998
|
+
const remaining = deadline - Date.now();
|
|
198999
|
+
if (remaining <= 0)
|
|
199000
|
+
throw new Error("read timeout");
|
|
199001
|
+
await new Promise((resolve4, reject) => {
|
|
199002
|
+
const timer = setTimeout(() => {
|
|
199003
|
+
this.waiters = this.waiters.filter((waiter) => waiter !== onReady);
|
|
199004
|
+
reject(new Error("read timeout"));
|
|
199005
|
+
}, remaining);
|
|
199006
|
+
const onReady = () => {
|
|
199007
|
+
clearTimeout(timer);
|
|
199008
|
+
resolve4();
|
|
199009
|
+
};
|
|
199010
|
+
this.waiters.push(onReady);
|
|
199011
|
+
});
|
|
199012
|
+
}
|
|
199013
|
+
const out = Buffer.allocUnsafe(length);
|
|
199014
|
+
let offset = 0;
|
|
199015
|
+
while (offset < length) {
|
|
199016
|
+
const first = this.chunks[0];
|
|
199017
|
+
const take = Math.min(first.length, length - offset);
|
|
199018
|
+
first.copy(out, offset, 0, take);
|
|
199019
|
+
offset += take;
|
|
199020
|
+
if (take === first.length)
|
|
199021
|
+
this.chunks.shift();
|
|
199022
|
+
else
|
|
199023
|
+
this.chunks[0] = first.subarray(take);
|
|
199024
|
+
this.buffered -= take;
|
|
199025
|
+
}
|
|
199026
|
+
return out;
|
|
199027
|
+
}
|
|
199028
|
+
flushWaiters() {
|
|
199029
|
+
const waiters = this.waiters.splice(0);
|
|
199030
|
+
for (const waiter of waiters)
|
|
199031
|
+
waiter();
|
|
199032
|
+
}
|
|
199033
|
+
}
|
|
199034
|
+
async function writeAuthMessage(socket, value) {
|
|
199035
|
+
const json2 = Buffer.from(JSON.stringify(value));
|
|
199036
|
+
const len = Buffer.allocUnsafe(4);
|
|
199037
|
+
len.writeUInt32LE(json2.length, 0);
|
|
199038
|
+
socket.write(Buffer.concat([len, json2]));
|
|
199039
|
+
}
|
|
199040
|
+
async function readAuthMessage(reader, timeoutMs) {
|
|
199041
|
+
const lenBytes = await reader.readExact(4, timeoutMs);
|
|
199042
|
+
const len = lenBytes.readUInt32LE(0);
|
|
199043
|
+
const body = await reader.readExact(len, timeoutMs);
|
|
199044
|
+
return JSON.parse(body.toString("utf8"));
|
|
199045
|
+
}
|
|
199046
|
+
function proof(key, domain2, clientNonce, serverNonce, daemonId) {
|
|
199047
|
+
return createHmac("sha256", key).update(domain2).update(clientNonce).update(serverNonce).update(daemonId).digest();
|
|
199048
|
+
}
|
|
199049
|
+
async function authenticateSubcClient(socket, reader, conn, timeoutMs) {
|
|
199050
|
+
const key = Buffer.from(conn.key);
|
|
199051
|
+
const clientNonce = randomBytes(NONCE_LEN);
|
|
199052
|
+
await writeAuthMessage(socket, { client_nonce: [...clientNonce], role: "client" });
|
|
199053
|
+
const serverProof = await readAuthMessage(reader, timeoutMs);
|
|
199054
|
+
const serverNonce = Buffer.from(serverProof.server_nonce ?? []);
|
|
199055
|
+
const daemonId = Buffer.from(serverProof.daemon_id ?? []);
|
|
199056
|
+
const serverProofBytes = Buffer.from(serverProof.server_proof ?? []);
|
|
199057
|
+
const expected = proof(key, AUTH_SERVER_DOMAIN, clientNonce, serverNonce, daemonId);
|
|
199058
|
+
if (daemonId.length !== conn.daemon_id.length || !timingSafeEqual(daemonId, Buffer.from(conn.daemon_id)) || serverProofBytes.length !== PROOF_LEN || !timingSafeEqual(serverProofBytes, expected)) {
|
|
199059
|
+
throw new Error("invalid subc server proof");
|
|
199060
|
+
}
|
|
199061
|
+
const clientAuth = proof(key, AUTH_CLIENT_DOMAIN, clientNonce, serverNonce, daemonId);
|
|
199062
|
+
await writeAuthMessage(socket, { client_auth: [...clientAuth] });
|
|
199063
|
+
}
|
|
199064
|
+
async function writeFrame(socket, frame) {
|
|
199065
|
+
const header = Buffer.allocUnsafe(SUBC_HEADER_LEN);
|
|
199066
|
+
header.writeUInt32LE(frame.body.length, 0);
|
|
199067
|
+
header.writeUInt8(SUBC_PROTOCOL_VERSION, 4);
|
|
199068
|
+
header.writeUInt8(frame.type, 5);
|
|
199069
|
+
header.writeUInt8(PRIORITY_BACKGROUND_FLAGS, 6);
|
|
199070
|
+
header.writeUInt16LE(frame.channel, 7);
|
|
199071
|
+
header.writeBigUInt64LE(BigInt(frame.corr), 9);
|
|
199072
|
+
socket.write(Buffer.concat([header, frame.body]));
|
|
199073
|
+
}
|
|
199074
|
+
async function readFrame(reader, timeoutMs) {
|
|
199075
|
+
const header = await reader.readExact(SUBC_HEADER_LEN, timeoutMs);
|
|
199076
|
+
const len = header.readUInt32LE(0);
|
|
199077
|
+
const version2 = header.readUInt8(4);
|
|
199078
|
+
if (version2 !== SUBC_PROTOCOL_VERSION)
|
|
199079
|
+
throw new Error(`unsupported subc frame version ${version2}`);
|
|
199080
|
+
const type = header.readUInt8(5);
|
|
199081
|
+
const channel = header.readUInt16LE(7);
|
|
199082
|
+
const corr = Number(header.readBigUInt64LE(9));
|
|
199083
|
+
const body = len > 0 ? await reader.readExact(len, timeoutMs) : Buffer.alloc(0);
|
|
199084
|
+
return { type, channel, corr, body };
|
|
199085
|
+
}
|
|
199086
|
+
async function readTerminalFor(reader, channel, corr, timeoutMs) {
|
|
199087
|
+
const deadline = Date.now() + timeoutMs;
|
|
199088
|
+
for (;; ) {
|
|
199089
|
+
const remaining = deadline - Date.now();
|
|
199090
|
+
if (remaining <= 0)
|
|
199091
|
+
throw new Error("subc request timeout");
|
|
199092
|
+
const frame = await readFrame(reader, remaining);
|
|
199093
|
+
if (frame.channel === channel && frame.corr === corr)
|
|
199094
|
+
return frame;
|
|
199095
|
+
}
|
|
199096
|
+
}
|
|
199097
|
+
function parseErrorBody(body) {
|
|
199098
|
+
try {
|
|
199099
|
+
const parsed = JSON.parse(body.toString("utf8"));
|
|
199100
|
+
const error51 = new Error(`${parsed.code ?? "subc_error"}: ${parsed.message ?? "unknown"}`);
|
|
199101
|
+
if (typeof parsed.code === "string")
|
|
199102
|
+
error51.code = parsed.code;
|
|
199103
|
+
return error51;
|
|
199104
|
+
} catch {
|
|
199105
|
+
return new Error(body.toString("utf8") || "subc_error");
|
|
199106
|
+
}
|
|
199107
|
+
}
|
|
197660
199108
|
// src/hooks/magic-context/strip-content.ts
|
|
197661
199109
|
var DROPPED_PLACEHOLDER_PATTERN = /^\[dropped §\d+§\]$/;
|
|
197662
199110
|
var TAG_PREFIX_PATTERN = /^§\d+§\s*/;
|
|
@@ -198817,6 +200265,7 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
|
|
|
198817
200265
|
const skipPrefixInjection = options.skipPrefixInjection === true;
|
|
198818
200266
|
const onToolOwnerFallbackLookup = options.onToolOwnerFallbackLookup;
|
|
198819
200267
|
const targets = new Map;
|
|
200268
|
+
const normalizationTargets = [];
|
|
198820
200269
|
const reasoningByMessage = new Map;
|
|
198821
200270
|
const messageTagNumbers = new Map;
|
|
198822
200271
|
const toolTagByCallId = new Map;
|
|
@@ -198937,6 +200386,12 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
|
|
|
198937
200386
|
messageTagNumbers.set(message, Math.max(messageTagNumbers.get(message) ?? 0, tagId));
|
|
198938
200387
|
if (!skipPrefixInjection) {
|
|
198939
200388
|
textPart.text = prependTag(tagId, textPart.text);
|
|
200389
|
+
normalizationTargets.push({
|
|
200390
|
+
tagNumber: tagId,
|
|
200391
|
+
message,
|
|
200392
|
+
part: textPart,
|
|
200393
|
+
field: "text"
|
|
200394
|
+
});
|
|
198940
200395
|
}
|
|
198941
200396
|
targets.set(tagId, {
|
|
198942
200397
|
message,
|
|
@@ -198977,6 +200432,12 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
|
|
|
198977
200432
|
messageTagNumbers.set(message, Math.max(messageTagNumbers.get(message) ?? 0, tagId));
|
|
198978
200433
|
if (!skipPrefixInjection) {
|
|
198979
200434
|
toolPart.state.output = prependTag(tagId, toolPart.state.output);
|
|
200435
|
+
normalizationTargets.push({
|
|
200436
|
+
tagNumber: tagId,
|
|
200437
|
+
message,
|
|
200438
|
+
part: toolPart,
|
|
200439
|
+
field: "tool_state_output"
|
|
200440
|
+
});
|
|
198980
200441
|
}
|
|
198981
200442
|
toolTagByCallId.set(compositeKey, tagId);
|
|
198982
200443
|
if (thinkingParts.length > 0 && !toolThinkingByCallId.has(compositeKey)) {
|
|
@@ -199054,7 +200515,8 @@ function tagMessages(sessionId, messages, tagger, db, options = {}) {
|
|
|
199054
200515
|
toolCallIndex,
|
|
199055
200516
|
batch,
|
|
199056
200517
|
hasRecentReduceCall,
|
|
199057
|
-
hasRecentCommit: commitDetected
|
|
200518
|
+
hasRecentCommit: commitDetected,
|
|
200519
|
+
normalizationTargets
|
|
199058
200520
|
};
|
|
199059
200521
|
}
|
|
199060
200522
|
// src/hooks/magic-context/transform-postprocess-phase.ts
|
|
@@ -200973,6 +202435,11 @@ function clearPendingCompactionMarkerAfterSuccessfulDrain(args) {
|
|
|
200973
202435
|
sessionLog(args.sessionId, "compaction-marker drain: CAS-clear failed but no pending blob remains; another drain already cleared it");
|
|
200974
202436
|
return "cas-lost-already-cleared";
|
|
200975
202437
|
}
|
|
202438
|
+
function finalizeMessageRepresentation(messages, resolvedProviderID) {
|
|
202439
|
+
const clearedParts = modelAcceptsEmptyContent(resolvedProviderID) ? stripClearedReasoning(messages) : 0;
|
|
202440
|
+
const mergedReasoningParts = stripReasoningFromMergedAssistants(messages, resolvedProviderID);
|
|
202441
|
+
return { clearedParts, mergedReasoningParts };
|
|
202442
|
+
}
|
|
200976
202443
|
async function runPostTransformPhase(args) {
|
|
200977
202444
|
const pendingMaterializationAtPassStart = args.pendingMaterializationSessions.has(args.sessionId);
|
|
200978
202445
|
const deferredMaterializationAtPassStart = args.deferredMaterializationSessions.has(args.sessionId);
|
|
@@ -201221,6 +202688,7 @@ async function runPostTransformPhase(args) {
|
|
|
201221
202688
|
injectDocs: args.m0M1.injectDocs,
|
|
201222
202689
|
memoryInjectionBudgetTokens: args.m0M1.memoryInjectionBudgetTokens,
|
|
201223
202690
|
historyBudgetTokens: args.m0M1.historyBudgetTokens,
|
|
202691
|
+
temporalAwareness: args.m0M1.temporalAwareness,
|
|
201224
202692
|
isCacheBustingPass,
|
|
201225
202693
|
hardSignals: args.m0M1.hardSignals
|
|
201226
202694
|
});
|
|
@@ -201451,6 +202919,10 @@ async function runPostTransformPhase(args) {
|
|
|
201451
202919
|
const materializeReason = m0MaterializeReason ?? (explicitMaterializedSuccessfully ? "explicit_flush" : null);
|
|
201452
202920
|
const materialized = m0RematerializedThisPass || explicitMaterializedSuccessfully || deferredMaterializedSuccessfully;
|
|
201453
202921
|
const bustedThisPass = args.didMutateFromFlushedStatuses || pendingOpsDidMutate || heuristicOrReasoningDidMutate || autoReclaimDidMutateThisPass || m0RematerializedThisPass || m0M1InjectedThisPass && historyWasConsumedThisPass || historyWasConsumedThisPass;
|
|
202922
|
+
const tFinalRepresentation = performance.now();
|
|
202923
|
+
const finalRepresentation = finalizeMessageRepresentation(args.messages, args.resolvedProviderID);
|
|
202924
|
+
sessionLog(args.sessionId, `final representation: clearedParts=${finalRepresentation.clearedParts} mergedReasoningParts=${finalRepresentation.mergedReasoningParts}`);
|
|
202925
|
+
logTransformTiming(args.sessionId, "finalizeMessageRepresentation", tFinalRepresentation, `clearedParts=${finalRepresentation.clearedParts} mergedReasoningParts=${finalRepresentation.mergedReasoningParts}`);
|
|
201454
202926
|
return {
|
|
201455
202927
|
explicitMaterializedSuccessfully,
|
|
201456
202928
|
deferredMaterializedSuccessfully,
|
|
@@ -201505,6 +202977,16 @@ function clearMessageTokensCache(sessionId, messageId) {
|
|
|
201505
202977
|
cache.delete(messageId);
|
|
201506
202978
|
}
|
|
201507
202979
|
var recordedSessionProjectIdentity = new BoundedSessionMap(MESSAGE_TOKENS_CACHE_MAX);
|
|
202980
|
+
function cloneForShadow(value) {
|
|
202981
|
+
return JSON.parse(JSON.stringify(value));
|
|
202982
|
+
}
|
|
202983
|
+
function classifyShadowDecision(args) {
|
|
202984
|
+
if (args.materialized)
|
|
202985
|
+
return "hard";
|
|
202986
|
+
if (args.schedulerDecision === "execute" || args.bustedThisPass)
|
|
202987
|
+
return "soft";
|
|
202988
|
+
return "defer";
|
|
202989
|
+
}
|
|
201508
202990
|
function deriveTaggerLoadFloor(messages, sessionId, db) {
|
|
201509
202991
|
return deriveTagLoadFloor(db, sessionId, function* () {
|
|
201510
202992
|
for (const message of messages)
|
|
@@ -201548,6 +203030,19 @@ function createTransform(deps) {
|
|
|
201548
203030
|
clearOpenCodePendingTransformDecision(sessionId);
|
|
201549
203031
|
logTransformTiming(sessionId, "findSessionId", startTime, `messages=${messages.length}`);
|
|
201550
203032
|
const db = deps.db;
|
|
203033
|
+
const shadowSender = deps.shadowSender;
|
|
203034
|
+
let shadowCapture;
|
|
203035
|
+
if (shadowSender) {
|
|
203036
|
+
try {
|
|
203037
|
+
shadowCapture = {
|
|
203038
|
+
nowMs: Date.now(),
|
|
203039
|
+
inputMessages: cloneForShadow(messages),
|
|
203040
|
+
declaredTrimBefore: resolveDeclaredTrimForShadow({ db, sessionId })
|
|
203041
|
+
};
|
|
203042
|
+
} catch (error51) {
|
|
203043
|
+
sessionLog(sessionId, "shadow: capture failed (ignored):", error51);
|
|
203044
|
+
}
|
|
203045
|
+
}
|
|
201551
203046
|
if (deps.client !== undefined) {
|
|
201552
203047
|
scheduleReconciliation(db, sessionId, readRawSessionMessages);
|
|
201553
203048
|
}
|
|
@@ -201902,6 +203397,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
|
|
|
201902
203397
|
let targets = new Map;
|
|
201903
203398
|
let reasoningByMessage = new Map;
|
|
201904
203399
|
let messageTagNumbers = new Map;
|
|
203400
|
+
let tagNormalizationTargets = [];
|
|
201905
203401
|
let batch = null;
|
|
201906
203402
|
let hasRecentReduceCall = false;
|
|
201907
203403
|
if (deps.experimentalTemporalAwareness) {
|
|
@@ -201925,6 +203421,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
|
|
|
201925
203421
|
targets = result.targets;
|
|
201926
203422
|
reasoningByMessage = result.reasoningByMessage;
|
|
201927
203423
|
messageTagNumbers = result.messageTagNumbers;
|
|
203424
|
+
tagNormalizationTargets = result.normalizationTargets;
|
|
201928
203425
|
batch = result.batch;
|
|
201929
203426
|
hasRecentReduceCall = result.hasRecentReduceCall;
|
|
201930
203427
|
const hadPriorCommitState = deps.commitSeenLastPass?.has(sessionId) ?? false;
|
|
@@ -201986,12 +203483,6 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
|
|
|
201986
203483
|
const t4 = performance.now();
|
|
201987
203484
|
const strippedClearedReasoning = canUseEmptySentinels ? stripClearedReasoning(messages) : 0;
|
|
201988
203485
|
logTransformTiming(sessionId, "stripClearedReasoning", t4, `strippedParts=${strippedClearedReasoning}`);
|
|
201989
|
-
const tMergeStrip = performance.now();
|
|
201990
|
-
const strippedMergedReasoning = stripReasoningFromMergedAssistants(messages, resolvedProviderID);
|
|
201991
|
-
if (strippedMergedReasoning > 0) {
|
|
201992
|
-
sessionLog(sessionId, `stripped ${strippedMergedReasoning} reasoning parts from merged assistants (anthropic groupIntoBlocks workaround)`);
|
|
201993
|
-
}
|
|
201994
|
-
logTransformTiming(sessionId, "stripReasoningFromMergedAssistants", tMergeStrip, `strippedParts=${strippedMergedReasoning}`);
|
|
201995
203486
|
const watermark = getMaxDroppedTagNumber(db, sessionId);
|
|
201996
203487
|
const contextUsage = contextUsageEarly;
|
|
201997
203488
|
const schedulerDecision = midTurnAdjustedSchedulerDecision;
|
|
@@ -202115,6 +203606,7 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
|
|
|
202115
203606
|
injectDocs: deps.injectDocs,
|
|
202116
203607
|
memoryInjectionBudgetTokens: deps.memoryConfig?.injectionBudgetTokens,
|
|
202117
203608
|
historyBudgetTokens,
|
|
203609
|
+
temporalAwareness: deps.experimentalTemporalAwareness,
|
|
202118
203610
|
hardSignals: m0HardSignals
|
|
202119
203611
|
}
|
|
202120
203612
|
});
|
|
@@ -202317,6 +203809,45 @@ Historian previously failed ${historianFailureState.failureCount} time(s), so Ma
|
|
|
202317
203809
|
}
|
|
202318
203810
|
const elapsed = (performance.now() - startTime).toFixed(1);
|
|
202319
203811
|
sessionLog(sessionId, `transform completed in ${elapsed}ms (${messages.length} messages, ${targets.size} targets, watermark: ${watermark})`);
|
|
203812
|
+
if (shadowSender && shadowCapture) {
|
|
203813
|
+
try {
|
|
203814
|
+
const shadowDecision = {
|
|
203815
|
+
class: classifyShadowDecision({
|
|
203816
|
+
schedulerDecision,
|
|
203817
|
+
materialized: postTransformResult.materialized,
|
|
203818
|
+
bustedThisPass: postTransformResult.bustedThisPass
|
|
203819
|
+
}),
|
|
203820
|
+
marker_state: { advanced_this_pass: false },
|
|
203821
|
+
materialize_reason: postTransformResult.materializeReason,
|
|
203822
|
+
emergency: postTransformResult.emergency
|
|
203823
|
+
};
|
|
203824
|
+
shadowSender.enqueue({
|
|
203825
|
+
sessionId,
|
|
203826
|
+
isSubagent: sessionMeta.isSubagent,
|
|
203827
|
+
db,
|
|
203828
|
+
inputMessages: shadowCapture.inputMessages,
|
|
203829
|
+
outputMessages: messages,
|
|
203830
|
+
normalizationTargets: tagNormalizationTargets,
|
|
203831
|
+
projectRoot: sessionDirectory ?? deps.directory ?? process.cwd(),
|
|
203832
|
+
projectPath: sessionProjectIdentity,
|
|
203833
|
+
passInputs: {
|
|
203834
|
+
now_ms: shadowCapture.nowMs,
|
|
203835
|
+
model_key: deps.getModelKey?.(sessionId) ?? hardModelKey ?? null,
|
|
203836
|
+
usage: {
|
|
203837
|
+
input_tokens: contextUsage.inputTokens,
|
|
203838
|
+
limit: resolvedContextLimit ?? boundaryContextLimit
|
|
203839
|
+
},
|
|
203840
|
+
effective_execute_threshold: boundaryExecuteThreshold,
|
|
203841
|
+
history_budget_tokens: historyBudgetTokens ?? DEFAULT_HISTORY_BUDGET_TOKENS,
|
|
203842
|
+
cache_ttl: sessionMeta.cacheTtl
|
|
203843
|
+
},
|
|
203844
|
+
tsDecision: shadowDecision,
|
|
203845
|
+
declaredTrimBefore: shadowCapture.declaredTrimBefore
|
|
203846
|
+
});
|
|
203847
|
+
} catch (error51) {
|
|
203848
|
+
sessionLog(sessionId, "shadow: capture failed (ignored):", error51);
|
|
203849
|
+
}
|
|
203850
|
+
}
|
|
202320
203851
|
deps.maybeAutoEmbedSession?.(sessionId);
|
|
202321
203852
|
};
|
|
202322
203853
|
}
|
|
@@ -203390,6 +204921,7 @@ Context is managed for you entirely automatically — there's nothing to prune a
|
|
|
203390
204921
|
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).`;
|
|
203391
204922
|
var TOOL_HISTORY_GUIDANCE = `Compressed history intentionally omits tool calls and their outputs — summaries like "I edited file X" are historian records, not patterns to replicate. In the live conversation, older tool calls and their results are cleaned up to save context — you may see your own past messages referencing actions without the corresponding tool call or result visible. This is normal context management. ALWAYS use real tool calls; never simulate, fabricate, or inline tool outputs in your text. If there is no tool result message, the action did not happen. NEVER simulate, hallucinate or claim tool calls, command output, search results, file edits, or diffs in plain text as if they actually occurred.`;
|
|
203392
204923
|
var MEMORY_GUIDANCE = `Use \`ctx_memory\` for durable project knowledge: write what future sessions must know, update/archive/merge the memories you see in \`<project-memory>\` when they drift. Memories persist across sessions and every new session starts with them.
|
|
204924
|
+
Memories are grouped by category as \`#id: fact\` lines; pass the numeric id to \`ctx_memory\` actions.
|
|
203393
204925
|
**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:
|
|
203394
204926
|
- Found a project's source code path after searching → \`ctx_memory(action="write", category="CONFIG_VALUES", content="OpenCode source is at ~/Work/OSS/opencode")\`
|
|
203395
204927
|
- Discovered a non-obvious build/test command → \`ctx_memory(action="write", category="PROJECT_RULES", content="Always use scripts/release.sh for releases")\`
|
|
@@ -203403,7 +204935,7 @@ Use \`ctx_reduce\` to mark spent tagged content as discardable and reclaim space
|
|
|
203403
204935
|
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.
|
|
203404
204936
|
${CTX_NOTE_GUIDANCE}
|
|
203405
204937
|
${memoryGuidanceBlock(memoryEnabled)}Use \`ctx_search\` to search across project memories, indexed git commits, and this session's full conversation history (including compacted parts) from one query.
|
|
203406
|
-
Use \`ctx_expand\` to recover the raw conversation behind a
|
|
204938
|
+
Use \`ctx_expand\` to recover the raw conversation behind a summary under a \`## start-end · date · title\` heading inside \`<session-history>\` — pass the heading's start/end range when the summary is not enough (exact wording, values, error text).
|
|
203407
204939
|
**Search before asking the user**: If you can't remember or don't know something that might have been discussed before or stored in project memory, use \`ctx_search\` before asking the user. Examples:
|
|
203408
204940
|
- Can't remember where a related codebase or dependency lives → \`ctx_search(query="opencode source code path")\`
|
|
203409
204941
|
- Forgot a prior architectural decision or constraint → \`ctx_search(query="why did we choose SQLite over postgres")\`
|
|
@@ -203418,7 +204950,7 @@ NEVER drop assistant text messages unless they are exceptionally large. Your con
|
|
|
203418
204950
|
Before your turn finishes, consider using \`ctx_reduce\` to drop large tool outputs you no longer need.`;
|
|
203419
204951
|
var BASE_INTRO_NO_REDUCE = (memoryEnabled) => `${CTX_NOTE_GUIDANCE}
|
|
203420
204952
|
${memoryGuidanceBlock(memoryEnabled)}Use \`ctx_search\` to search across project memories, indexed git commits, and this session's full conversation history (including compacted parts) from one query.
|
|
203421
|
-
Use \`ctx_expand\` to recover the raw conversation behind a
|
|
204953
|
+
Use \`ctx_expand\` to recover the raw conversation behind a summary under a \`## start-end · date · title\` heading inside \`<session-history>\` — pass the heading's start/end range when the summary is not enough (exact wording, values, error text).
|
|
203422
204954
|
**Search before asking the user**: If you can't remember or don't know something that might have been discussed before or stored in project memory, use \`ctx_search\` before asking the user. Examples:
|
|
203423
204955
|
- Can't remember where a related codebase or dependency lives → \`ctx_search(query="opencode source code path")\`
|
|
203424
204956
|
- Forgot a prior architectural decision or constraint → \`ctx_search(query="why did we choose SQLite over postgres")\`
|
|
@@ -204004,6 +205536,7 @@ function createMagicContextHook(deps) {
|
|
|
204004
205536
|
};
|
|
204005
205537
|
const sidekickRunnable = isSidekickRunnable(deps.config);
|
|
204006
205538
|
const sidekickConfig = sidekickRunnable ? deps.config.sidekick : undefined;
|
|
205539
|
+
const shadowSender = deps.config.shadow_transform?.enabled === true ? createShadowSender() : undefined;
|
|
204007
205540
|
const transform2 = createTransform({
|
|
204008
205541
|
tagger: deps.tagger,
|
|
204009
205542
|
scheduler: deps.scheduler,
|
|
@@ -204063,7 +205596,8 @@ function createMagicContextHook(deps) {
|
|
|
204063
205596
|
enabled: true,
|
|
204064
205597
|
minChars: deps.config.caveman_text_compression.min_chars ?? 500
|
|
204065
205598
|
} : undefined,
|
|
204066
|
-
maybeAutoEmbedSession
|
|
205599
|
+
maybeAutoEmbedSession,
|
|
205600
|
+
shadowSender
|
|
204067
205601
|
});
|
|
204068
205602
|
const eventHandler = createEventHandler2({
|
|
204069
205603
|
contextUsageMap,
|
|
@@ -204090,6 +205624,7 @@ function createMagicContextHook(deps) {
|
|
|
204090
205624
|
recompProgressBySession.delete(sessionId);
|
|
204091
205625
|
internalChildSessions.delete(sessionId);
|
|
204092
205626
|
channel1StateBySession.delete(sessionId);
|
|
205627
|
+
shadowSender?.clearSession(sessionId);
|
|
204093
205628
|
clearEmbedSessionState(sessionId);
|
|
204094
205629
|
}
|
|
204095
205630
|
});
|
|
@@ -204269,22 +205804,9 @@ function createMagicContextHook(deps) {
|
|
|
204269
205804
|
// src/plugin/hooks/create-session-hooks.ts
|
|
204270
205805
|
function buildMagicContextHookConfig(pluginConfig) {
|
|
204271
205806
|
return {
|
|
205807
|
+
...pluginConfig,
|
|
204272
205808
|
protected_tags: pluginConfig.protected_tags ?? DEFAULT_PROTECTED_TAGS,
|
|
204273
|
-
|
|
204274
|
-
clear_reasoning_age: pluginConfig.clear_reasoning_age,
|
|
204275
|
-
toast_duration_ms: pluginConfig.toast_duration_ms,
|
|
204276
|
-
execute_threshold_percentage: pluginConfig.execute_threshold_percentage ?? DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE,
|
|
204277
|
-
execute_threshold_tokens: pluginConfig.execute_threshold_tokens,
|
|
204278
|
-
historian: pluginConfig.historian,
|
|
204279
|
-
history_budget_percentage: pluginConfig.history_budget_percentage,
|
|
204280
|
-
historian_timeout_ms: pluginConfig.historian_timeout_ms,
|
|
204281
|
-
memory: pluginConfig.memory,
|
|
204282
|
-
sidekick: pluginConfig.sidekick,
|
|
204283
|
-
dreamer: pluginConfig.dreamer,
|
|
204284
|
-
commit_cluster_trigger: pluginConfig.commit_cluster_trigger,
|
|
204285
|
-
system_prompt_injection: pluginConfig.system_prompt_injection,
|
|
204286
|
-
temporal_awareness: pluginConfig.temporal_awareness,
|
|
204287
|
-
caveman_text_compression: pluginConfig.caveman_text_compression
|
|
205809
|
+
execute_threshold_percentage: pluginConfig.execute_threshold_percentage ?? DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE
|
|
204288
205810
|
};
|
|
204289
205811
|
}
|
|
204290
205812
|
function createSessionHooks(args) {
|
|
@@ -204507,9 +206029,8 @@ function computeM0BlockTokens(db, sessionId, args) {
|
|
|
204507
206029
|
try {
|
|
204508
206030
|
const compRows = db.prepare("SELECT content, title, start_message, end_message FROM compartments WHERE session_id = ?").all(sessionId);
|
|
204509
206031
|
for (const c of compRows) {
|
|
204510
|
-
compartmentTokens += estimateTokens(
|
|
206032
|
+
compartmentTokens += estimateTokens(`## ${c.start_message}-${c.end_message} · ${c.title}
|
|
204511
206033
|
${c.content}
|
|
204512
|
-
</compartment>
|
|
204513
206034
|
`);
|
|
204514
206035
|
}
|
|
204515
206036
|
} catch {}
|
|
@@ -205260,9 +206781,9 @@ import { tool } from "@opencode-ai/plugin";
|
|
|
205260
206781
|
// src/tools/ctx-expand/constants.ts
|
|
205261
206782
|
var CTX_EXPAND_DESCRIPTION = `Recover the original conversation from your compacted history.
|
|
205262
206783
|
|
|
205263
|
-
Older parts of this session are summarized
|
|
206784
|
+
Older parts of this session are summarized under \`## start-end · date · title\` headings inside <session-history> — e.g. \`## 120-245 · … · Fixed tagger collision\`. Each heading replaces the raw messages in that ordinal range with a summary. When the summary isn't enough — you need exact wording, a specific value, an error message, or the reasoning behind a decision — expand the range:
|
|
205264
206785
|
|
|
205265
|
-
ctx_expand(start=120, end=245) ← the
|
|
206786
|
+
ctx_expand(start=120, end=245) ← the heading's start/end range
|
|
205266
206787
|
|
|
205267
206788
|
Returns the raw transcript as [N] U:/A: lines, capped at ~15K tokens; an oversized range returns the head and tells you where to continue. Also works with ordinals from ctx_search message results — expand a window around a hit (e.g. start=N-10, end=N+5). Ranges after the last compartment are your live tail — already visible in context, not expandable.
|
|
205268
206789
|
|
|
@@ -206652,12 +208173,12 @@ init_models_dev_cache();
|
|
|
206652
208173
|
// src/shared/rpc-server.ts
|
|
206653
208174
|
init_logger();
|
|
206654
208175
|
init_rpc_notifications();
|
|
206655
|
-
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
208176
|
+
import { randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
206656
208177
|
import {
|
|
206657
208178
|
chmodSync as chmodSync3,
|
|
206658
208179
|
mkdirSync as mkdirSync11,
|
|
206659
208180
|
readdirSync,
|
|
206660
|
-
readFileSync as
|
|
208181
|
+
readFileSync as readFileSync15,
|
|
206661
208182
|
renameSync as renameSync5,
|
|
206662
208183
|
rmSync as rmSync3,
|
|
206663
208184
|
unlinkSync as unlinkSync4,
|
|
@@ -206667,17 +208188,17 @@ import { dirname as dirname9 } from "node:path";
|
|
|
206667
208188
|
|
|
206668
208189
|
// src/shared/rpc-utils.ts
|
|
206669
208190
|
import { createHash as createHash14 } from "node:crypto";
|
|
206670
|
-
import { join as
|
|
208191
|
+
import { join as join21 } from "node:path";
|
|
206671
208192
|
function projectHash(directory) {
|
|
206672
208193
|
const normalized = directory.replace(/\/+$/, "");
|
|
206673
208194
|
return createHash14("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
206674
208195
|
}
|
|
206675
208196
|
function rpcPortDir(storageDir, directory) {
|
|
206676
|
-
return
|
|
208197
|
+
return join21(storageDir, "rpc", projectHash(directory));
|
|
206677
208198
|
}
|
|
206678
208199
|
function rpcPortFilePath(storageDir, directory, pid = process.pid, instanceId) {
|
|
206679
208200
|
const suffix = instanceId ? `-${instanceId}` : "";
|
|
206680
|
-
return
|
|
208201
|
+
return join21(rpcPortDir(storageDir, directory), `port-${pid}${suffix}.json`);
|
|
206681
208202
|
}
|
|
206682
208203
|
function isPidAlive(pid) {
|
|
206683
208204
|
if (!Number.isInteger(pid) || pid <= 0)
|
|
@@ -206730,7 +208251,7 @@ function tokensMatch(presented, expected) {
|
|
|
206730
208251
|
const b = Buffer.from(expected, "utf8");
|
|
206731
208252
|
if (a.length !== b.length)
|
|
206732
208253
|
return false;
|
|
206733
|
-
return
|
|
208254
|
+
return timingSafeEqual2(a, b);
|
|
206734
208255
|
}
|
|
206735
208256
|
function bearerToken(req) {
|
|
206736
208257
|
const auth = req.headers.get("authorization");
|
|
@@ -206747,9 +208268,9 @@ class MagicContextRpcServer {
|
|
|
206747
208268
|
portFilePath;
|
|
206748
208269
|
portDir;
|
|
206749
208270
|
startedAt = Date.now();
|
|
206750
|
-
instanceId =
|
|
208271
|
+
instanceId = randomBytes2(8).toString("hex");
|
|
206751
208272
|
sockets = new Set;
|
|
206752
|
-
token =
|
|
208273
|
+
token = randomBytes2(32).toString("hex");
|
|
206753
208274
|
constructor(storageDir, directory) {
|
|
206754
208275
|
this.portFilePath = rpcPortFilePath(storageDir, directory, process.pid, this.instanceId);
|
|
206755
208276
|
this.portDir = rpcPortDir(storageDir, directory);
|
|
@@ -206758,6 +208279,10 @@ class MagicContextRpcServer {
|
|
|
206758
208279
|
this.handlers.set(method, handler);
|
|
206759
208280
|
}
|
|
206760
208281
|
async start() {
|
|
208282
|
+
if (typeof Bun === "undefined") {
|
|
208283
|
+
log("rpc server skipped: Bun runtime not available (no TUI consumer)");
|
|
208284
|
+
return 0;
|
|
208285
|
+
}
|
|
206761
208286
|
this.startedAt = Date.now();
|
|
206762
208287
|
const self2 = this;
|
|
206763
208288
|
const server = Bun.serve({
|
|
@@ -206837,7 +208362,7 @@ class MagicContextRpcServer {
|
|
|
206837
208362
|
for (const entry of readdirSync(this.portDir)) {
|
|
206838
208363
|
if (!entry.startsWith("port-") || !entry.endsWith(".json"))
|
|
206839
208364
|
continue;
|
|
206840
|
-
const record2 = parseRpcPortFile(
|
|
208365
|
+
const record2 = parseRpcPortFile(readFileSync15(`${this.portDir}/${entry}`, "utf-8"));
|
|
206841
208366
|
if (!record2 || record2.pid === process.pid || !isPidAlive(record2.pid))
|
|
206842
208367
|
continue;
|
|
206843
208368
|
log(`[rpc] another Magic Context RPC server is active for this project (pid ${record2.pid}, port ${record2.port}); starting separate instance on a new port`);
|