@cortexkit/opencode-magic-context 0.31.3 → 0.31.4
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/config/index.d.ts.map +1 -1
- package/dist/config/project-security.d.ts +16 -12
- package/dist/config/project-security.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/classify-prompt.d.ts +2 -2
- package/dist/features/magic-context/dreamer/classify-prompt.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/classify.d.ts +8 -0
- package/dist/features/magic-context/dreamer/classify.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/evaluate-smart-notes.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/lease.d.ts +1 -0
- package/dist/features/magic-context/dreamer/lease.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/manifest-parser.d.ts +7 -0
- package/dist/features/magic-context/dreamer/manifest-parser.d.ts.map +1 -0
- package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts +2 -3
- package/dist/features/magic-context/dreamer/map-memories-prompt.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/map-memories.d.ts +8 -0
- package/dist/features/magic-context/dreamer/map-memories.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/promote-primers.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/retrospective-raw-provider.d.ts +5 -0
- package/dist/features/magic-context/dreamer/retrospective-raw-provider.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify-prompt.d.ts +2 -2
- package/dist/features/magic-context/dreamer/verify-prompt.d.ts.map +1 -1
- package/dist/features/magic-context/dreamer/verify.d.ts.map +1 -1
- package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts +4 -0
- package/dist/features/magic-context/memory/storage-memory-embeddings.d.ts.map +1 -1
- package/dist/features/magic-context/memory/storage-memory.d.ts +12 -0
- package/dist/features/magic-context/memory/storage-memory.d.ts.map +1 -1
- package/dist/features/magic-context/message-index-async.d.ts.map +1 -1
- package/dist/features/magic-context/message-index.d.ts +6 -0
- package/dist/features/magic-context/message-index.d.ts.map +1 -1
- package/dist/features/magic-context/project-embedding-registry.d.ts.map +1 -1
- package/dist/features/magic-context/search.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/capabilities.d.ts +1 -0
- package/dist/features/magic-context/smart-notes/capabilities.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/compiler.d.ts +3 -2
- package/dist/features/magic-context/smart-notes/compiler.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/runner.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts +3 -2
- package/dist/features/magic-context/smart-notes/sandbox-runner.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/schedule.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/ssrf-guard.d.ts +17 -6
- package/dist/features/magic-context/smart-notes/ssrf-guard.d.ts.map +1 -1
- package/dist/features/magic-context/smart-notes/types.d.ts +6 -1
- package/dist/features/magic-context/smart-notes/types.d.ts.map +1 -1
- package/dist/features/magic-context/storage-db.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta-persisted.d.ts +8 -0
- package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
- package/dist/features/magic-context/storage-meta.d.ts +1 -1
- package/dist/features/magic-context/storage-meta.d.ts.map +1 -1
- package/dist/features/magic-context/storage.d.ts +1 -1
- package/dist/features/magic-context/storage.d.ts.map +1 -1
- package/dist/features/magic-context/workspaces.d.ts +8 -0
- package/dist/features/magic-context/workspaces.d.ts.map +1 -1
- package/dist/hooks/magic-context/channel2-delivery.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-runner.d.ts +2 -0
- package/dist/hooks/magic-context/compartment-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-db.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-true-raw-tokens.d.ts.map +1 -1
- package/dist/hooks/magic-context/tag-messages.d.ts +1 -0
- package/dist/hooks/magic-context/tag-messages.d.ts.map +1 -1
- package/dist/hooks/magic-context/todo-view.d.ts +27 -2
- package/dist/hooks/magic-context/todo-view.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-message-helpers.d.ts +6 -3
- package/dist/hooks/magic-context/transform-message-helpers.d.ts.map +1 -1
- package/dist/hooks/magic-context/wrapup-orchestrator.d.ts +1 -0
- package/dist/hooks/magic-context/wrapup-orchestrator.d.ts.map +1 -1
- package/dist/index.js +1039 -331
- package/dist/shared/assistant-message-extractor.d.ts +1 -0
- package/dist/shared/assistant-message-extractor.d.ts.map +1 -1
- package/dist/shared/redaction.d.ts.map +1 -1
- package/dist/shared/rpc-notifications.d.ts +21 -16
- package/dist/shared/rpc-notifications.d.ts.map +1 -1
- package/dist/shared/rpc-server.d.ts +1 -0
- package/dist/shared/rpc-server.d.ts.map +1 -1
- package/dist/shared/rpc-utils.d.ts +3 -1
- package/dist/shared/rpc-utils.d.ts.map +1 -1
- package/dist/tools/ctx-memory/tools.d.ts.map +1 -1
- package/dist/tools/ctx-search/tools.d.ts.map +1 -1
- package/dist/tools/ctx-search/types.d.ts +1 -1
- package/dist/tools/ctx-search/types.d.ts.map +1 -1
- package/dist/tui/data/notification-socket.d.ts +1 -0
- package/dist/tui/data/notification-socket.d.ts.map +1 -1
- package/dist/tui-compiled/badge-contrast.d.ts +46 -0
- package/dist/tui-compiled/badge-contrast.d.ts.map +1 -0
- package/dist/tui-compiled/data/context-db.d.ts +45 -0
- package/dist/tui-compiled/data/context-db.d.ts.map +1 -0
- package/dist/tui-compiled/data/notification-socket.d.ts +40 -0
- package/dist/tui-compiled/data/notification-socket.d.ts.map +1 -0
- package/package.json +3 -2
- package/src/shared/assistant-message-extractor.ts +20 -0
- package/src/shared/redaction.ts +1 -0
- package/src/shared/rpc-client.test.ts +164 -5
- package/src/shared/rpc-notifications.ts +73 -21
- package/src/shared/rpc-server.ts +69 -18
- package/src/shared/rpc-utils.ts +12 -2
- package/src/tui/data/notification-socket.test.ts +131 -0
- package/src/tui/data/notification-socket.ts +81 -16
- package/src/tui/entry.mjs +23 -3
- package/src/tui-compiled/data/notification-socket.ts +81 -16
package/dist/index.js
CHANGED
|
@@ -490,7 +490,7 @@ __export(exports_util, {
|
|
|
490
490
|
jsonStringifyReplacer: () => jsonStringifyReplacer,
|
|
491
491
|
joinValues: () => joinValues,
|
|
492
492
|
issue: () => issue,
|
|
493
|
-
isPlainObject: () =>
|
|
493
|
+
isPlainObject: () => isPlainObject,
|
|
494
494
|
isObject: () => isObject,
|
|
495
495
|
hexToUint8Array: () => hexToUint8Array,
|
|
496
496
|
getSizableOrigin: () => getSizableOrigin,
|
|
@@ -655,7 +655,7 @@ function slugify(input) {
|
|
|
655
655
|
function isObject(data) {
|
|
656
656
|
return typeof data === "object" && data !== null && !Array.isArray(data);
|
|
657
657
|
}
|
|
658
|
-
function
|
|
658
|
+
function isPlainObject(o) {
|
|
659
659
|
if (isObject(o) === false)
|
|
660
660
|
return false;
|
|
661
661
|
const ctor = o.constructor;
|
|
@@ -672,7 +672,7 @@ function isPlainObject3(o) {
|
|
|
672
672
|
return true;
|
|
673
673
|
}
|
|
674
674
|
function shallowClone(o) {
|
|
675
|
-
if (
|
|
675
|
+
if (isPlainObject(o))
|
|
676
676
|
return { ...o };
|
|
677
677
|
if (Array.isArray(o))
|
|
678
678
|
return [...o];
|
|
@@ -812,7 +812,7 @@ function omit(schema, mask) {
|
|
|
812
812
|
return clone(schema, def);
|
|
813
813
|
}
|
|
814
814
|
function extend(schema, shape) {
|
|
815
|
-
if (!
|
|
815
|
+
if (!isPlainObject(shape)) {
|
|
816
816
|
throw new Error("Invalid input to extend: expected a plain object");
|
|
817
817
|
}
|
|
818
818
|
const checks = schema._zod.def.checks;
|
|
@@ -835,7 +835,7 @@ function extend(schema, shape) {
|
|
|
835
835
|
return clone(schema, def);
|
|
836
836
|
}
|
|
837
837
|
function safeExtend(schema, shape) {
|
|
838
|
-
if (!
|
|
838
|
+
if (!isPlainObject(shape)) {
|
|
839
839
|
throw new Error("Invalid input to safeExtend: expected a plain object");
|
|
840
840
|
}
|
|
841
841
|
const def = mergeDefs(schema._zod.def, {
|
|
@@ -2318,7 +2318,7 @@ function mergeValues(a, b) {
|
|
|
2318
2318
|
if (a instanceof Date && b instanceof Date && +a === +b) {
|
|
2319
2319
|
return { valid: true, data: a };
|
|
2320
2320
|
}
|
|
2321
|
-
if (
|
|
2321
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
2322
2322
|
const bKeys = Object.keys(b);
|
|
2323
2323
|
const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
|
|
2324
2324
|
const newObj = { ...a, ...b };
|
|
@@ -3575,7 +3575,7 @@ var init_schemas = __esm(() => {
|
|
|
3575
3575
|
$ZodType.init(inst, def);
|
|
3576
3576
|
inst._zod.parse = (payload, ctx) => {
|
|
3577
3577
|
const input = payload.value;
|
|
3578
|
-
if (!
|
|
3578
|
+
if (!isPlainObject(input)) {
|
|
3579
3579
|
payload.issues.push({
|
|
3580
3580
|
expected: "record",
|
|
3581
3581
|
code: "invalid_type",
|
|
@@ -16662,6 +16662,22 @@ function extractLatestAssistantText(messages) {
|
|
|
16662
16662
|
return getTextParts(latest).map((part) => part.text).join(`
|
|
16663
16663
|
`) || null;
|
|
16664
16664
|
}
|
|
16665
|
+
function hasLengthCappedOutput(value) {
|
|
16666
|
+
if (Array.isArray(value))
|
|
16667
|
+
return value.some((item) => hasLengthCappedOutput(item));
|
|
16668
|
+
if (!isRecord(value))
|
|
16669
|
+
return false;
|
|
16670
|
+
if (value.length_capped === true || value.lengthCapped === true)
|
|
16671
|
+
return true;
|
|
16672
|
+
const finishReason = value.finish_reason ?? value.finishReason;
|
|
16673
|
+
if (typeof finishReason === "string") {
|
|
16674
|
+
const normalized = finishReason.toLowerCase();
|
|
16675
|
+
if (normalized === "length" || normalized === "max_tokens" || normalized === "max_output_tokens") {
|
|
16676
|
+
return true;
|
|
16677
|
+
}
|
|
16678
|
+
}
|
|
16679
|
+
return Object.values(value).some((item) => hasLengthCappedOutput(item));
|
|
16680
|
+
}
|
|
16665
16681
|
var init_assistant_message_extractor = () => {};
|
|
16666
16682
|
|
|
16667
16683
|
// src/shared/keep-subagents.ts
|
|
@@ -17330,7 +17346,8 @@ function isMidTurn(_deps, sessionId) {
|
|
|
17330
17346
|
}
|
|
17331
17347
|
function isMidTurnFromOpenCodeDb(db, sessionId) {
|
|
17332
17348
|
const latestAssistant = db.prepare(`SELECT id,
|
|
17333
|
-
json_extract(data, '$.finish') as finish
|
|
17349
|
+
json_extract(data, '$.finish') as finish,
|
|
17350
|
+
time_created as timeCreated
|
|
17334
17351
|
FROM message
|
|
17335
17352
|
WHERE session_id = ?
|
|
17336
17353
|
AND json_extract(data, '$.role') = 'assistant'
|
|
@@ -17338,6 +17355,8 @@ function isMidTurnFromOpenCodeDb(db, sessionId) {
|
|
|
17338
17355
|
LIMIT 1`).get(sessionId);
|
|
17339
17356
|
if (typeof latestAssistant?.id !== "string")
|
|
17340
17357
|
return false;
|
|
17358
|
+
if (hasNewerRealUserMessage(db, sessionId, latestAssistant.timeCreated))
|
|
17359
|
+
return false;
|
|
17341
17360
|
if (latestAssistant.finish === "tool-calls")
|
|
17342
17361
|
return true;
|
|
17343
17362
|
const partRows = db.prepare("SELECT data FROM part WHERE session_id = ? AND message_id = ?").all(sessionId, latestAssistant.id);
|
|
@@ -17352,6 +17371,18 @@ function isMidTurnFromOpenCodeDb(db, sessionId) {
|
|
|
17352
17371
|
}
|
|
17353
17372
|
});
|
|
17354
17373
|
}
|
|
17374
|
+
function hasNewerRealUserMessage(db, sessionId, latestAssistantTimeCreated) {
|
|
17375
|
+
if (typeof latestAssistantTimeCreated !== "number")
|
|
17376
|
+
return false;
|
|
17377
|
+
const row = db.prepare(`SELECT 1 as one
|
|
17378
|
+
FROM message
|
|
17379
|
+
WHERE session_id = ?
|
|
17380
|
+
AND time_created > ?
|
|
17381
|
+
AND json_extract(data, '$.role') = 'user'
|
|
17382
|
+
AND COALESCE(json_extract(data, '$.synthetic'), 0) NOT IN (1, 'true')
|
|
17383
|
+
LIMIT 1`).get(sessionId, latestAssistantTimeCreated);
|
|
17384
|
+
return row?.one === 1;
|
|
17385
|
+
}
|
|
17355
17386
|
function getMessageTimesFromOpenCodeDb(sessionId, messageIds) {
|
|
17356
17387
|
const result = new Map;
|
|
17357
17388
|
if (messageIds.length === 0)
|
|
@@ -150820,12 +150851,19 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
150820
150851
|
let totalTokens = 0;
|
|
150821
150852
|
let messagesProcessed = 0;
|
|
150822
150853
|
let lastOrdinal = startOrdinal - 1;
|
|
150854
|
+
let highestScannedOrdinal = startOrdinal - 1;
|
|
150823
150855
|
let lastMessageId = "";
|
|
150824
150856
|
let firstMessageId = "";
|
|
150825
150857
|
let currentBlock = null;
|
|
150826
150858
|
let pendingNoiseMeta = [];
|
|
150827
150859
|
let commitClusters = 0;
|
|
150828
150860
|
let lastFlushedRole = "";
|
|
150861
|
+
function recordFilteredNoise(meta3) {
|
|
150862
|
+
pendingNoiseMeta.push(meta3);
|
|
150863
|
+
if (!currentBlock) {
|
|
150864
|
+
highestScannedOrdinal = Math.max(highestScannedOrdinal, meta3.ordinal);
|
|
150865
|
+
}
|
|
150866
|
+
}
|
|
150829
150867
|
function flushCurrentBlock() {
|
|
150830
150868
|
if (!currentBlock)
|
|
150831
150869
|
return true;
|
|
@@ -150841,6 +150879,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
150841
150879
|
if (!firstMessageId)
|
|
150842
150880
|
firstMessageId = currentBlock.meta[0]?.messageId ?? "";
|
|
150843
150881
|
lastOrdinal = currentBlock.meta[currentBlock.meta.length - 1]?.ordinal ?? currentBlock.endOrdinal;
|
|
150882
|
+
highestScannedOrdinal = Math.max(highestScannedOrdinal, lastOrdinal);
|
|
150844
150883
|
lastMessageId = currentBlock.meta[currentBlock.meta.length - 1]?.messageId ?? "";
|
|
150845
150884
|
messagesProcessed += currentBlock.meta.length;
|
|
150846
150885
|
lines.push(blockText);
|
|
@@ -150864,7 +150903,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
150864
150903
|
if (msg.role === "user" && !hasMeaningfulUserText(msg.parts)) {
|
|
150865
150904
|
const tcSummaries = extractToolCallSummaries(msg.parts);
|
|
150866
150905
|
if (tcSummaries.length === 0) {
|
|
150867
|
-
|
|
150906
|
+
recordFilteredNoise(meta3);
|
|
150868
150907
|
continue;
|
|
150869
150908
|
}
|
|
150870
150909
|
const tcText = tcSummaries.join(" / ");
|
|
@@ -150896,7 +150935,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
150896
150935
|
const compacted = compactTextForSummary(allParts.join(" / "), msg.role);
|
|
150897
150936
|
const text = compacted.text;
|
|
150898
150937
|
if (!text) {
|
|
150899
|
-
|
|
150938
|
+
recordFilteredNoise(meta3);
|
|
150900
150939
|
continue;
|
|
150901
150940
|
}
|
|
150902
150941
|
const msgHasNarrative = textParts.length > 0;
|
|
@@ -150923,7 +150962,9 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
150923
150962
|
};
|
|
150924
150963
|
pendingNoiseMeta = [];
|
|
150925
150964
|
}
|
|
150926
|
-
flushCurrentBlock()
|
|
150965
|
+
if (flushCurrentBlock() && pendingNoiseMeta.length > 0) {
|
|
150966
|
+
highestScannedOrdinal = Math.max(highestScannedOrdinal, pendingNoiseMeta[pendingNoiseMeta.length - 1]?.ordinal ?? highestScannedOrdinal);
|
|
150967
|
+
}
|
|
150927
150968
|
const toolOnlyRanges = [];
|
|
150928
150969
|
for (const range of flushedToolOnlyBlocks) {
|
|
150929
150970
|
const last = toolOnlyRanges[toolOnlyRanges.length - 1];
|
|
@@ -150940,7 +150981,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
150940
150981
|
endMessageId: lastMessageId,
|
|
150941
150982
|
messageCount: messagesProcessed,
|
|
150942
150983
|
tokenEstimate: totalTokens,
|
|
150943
|
-
hasMore: lastOrdinal < (eligibleEndOrdinal !== undefined ? Math.min(eligibleEndOrdinal - 1, totalMessageCount) : totalMessageCount),
|
|
150984
|
+
hasMore: Math.max(lastOrdinal, highestScannedOrdinal) < (eligibleEndOrdinal !== undefined ? Math.min(eligibleEndOrdinal - 1, totalMessageCount) : totalMessageCount),
|
|
150944
150985
|
text: lines.join(`
|
|
150945
150986
|
`),
|
|
150946
150987
|
lines: lineMeta,
|
|
@@ -150966,7 +151007,7 @@ function normalizeIndexText(text) {
|
|
|
150966
151007
|
function getLastIndexedStatement(db) {
|
|
150967
151008
|
let stmt = lastIndexedStatements.get(db);
|
|
150968
151009
|
if (!stmt) {
|
|
150969
|
-
stmt = db.prepare("SELECT last_indexed_ordinal FROM message_history_index WHERE session_id = ?");
|
|
151010
|
+
stmt = db.prepare("SELECT last_indexed_ordinal, dirty_floor_ordinal FROM message_history_index WHERE session_id = ?");
|
|
150970
151011
|
lastIndexedStatements.set(db, stmt);
|
|
150971
151012
|
}
|
|
150972
151013
|
return stmt;
|
|
@@ -150987,6 +151028,22 @@ function getUpsertIndexStatement(db) {
|
|
|
150987
151028
|
}
|
|
150988
151029
|
return stmt;
|
|
150989
151030
|
}
|
|
151031
|
+
function getUpsertCleanIndexStatement(db) {
|
|
151032
|
+
let stmt = upsertCleanIndexStatements.get(db);
|
|
151033
|
+
if (!stmt) {
|
|
151034
|
+
stmt = db.prepare("INSERT INTO message_history_index (session_id, last_indexed_ordinal, dirty_floor_ordinal, updated_at, harness) VALUES (?, ?, 0, ?, ?) ON CONFLICT(session_id) DO UPDATE SET last_indexed_ordinal = excluded.last_indexed_ordinal, dirty_floor_ordinal = 0, updated_at = excluded.updated_at");
|
|
151035
|
+
upsertCleanIndexStatements.set(db, stmt);
|
|
151036
|
+
}
|
|
151037
|
+
return stmt;
|
|
151038
|
+
}
|
|
151039
|
+
function getUpsertDirtyFloorStatement(db) {
|
|
151040
|
+
let stmt = upsertDirtyFloorStatements.get(db);
|
|
151041
|
+
if (!stmt) {
|
|
151042
|
+
stmt = db.prepare("INSERT INTO message_history_index (session_id, last_indexed_ordinal, dirty_floor_ordinal, updated_at, harness) VALUES (?, ?, ?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET last_indexed_ordinal = MAX(message_history_index.last_indexed_ordinal, excluded.last_indexed_ordinal), dirty_floor_ordinal = CASE WHEN message_history_index.dirty_floor_ordinal <= 0 THEN excluded.dirty_floor_ordinal WHEN excluded.dirty_floor_ordinal <= 0 THEN message_history_index.dirty_floor_ordinal ELSE MIN(message_history_index.dirty_floor_ordinal, excluded.dirty_floor_ordinal) END, updated_at = excluded.updated_at");
|
|
151043
|
+
upsertDirtyFloorStatements.set(db, stmt);
|
|
151044
|
+
}
|
|
151045
|
+
return stmt;
|
|
151046
|
+
}
|
|
150990
151047
|
function getDeleteFtsStatement(db) {
|
|
150991
151048
|
let stmt = deleteFtsStatements.get(db);
|
|
150992
151049
|
if (!stmt) {
|
|
@@ -150995,6 +151052,14 @@ function getDeleteFtsStatement(db) {
|
|
|
150995
151052
|
}
|
|
150996
151053
|
return stmt;
|
|
150997
151054
|
}
|
|
151055
|
+
function getDeleteFtsFromOrdinalStatement(db) {
|
|
151056
|
+
let stmt = deleteFtsFromOrdinalStatements.get(db);
|
|
151057
|
+
if (!stmt) {
|
|
151058
|
+
stmt = db.prepare("DELETE FROM message_history_fts WHERE session_id = ? AND CAST(message_ordinal AS INTEGER) >= ?");
|
|
151059
|
+
deleteFtsFromOrdinalStatements.set(db, stmt);
|
|
151060
|
+
}
|
|
151061
|
+
return stmt;
|
|
151062
|
+
}
|
|
150998
151063
|
function getDeleteIndexStatement(db) {
|
|
150999
151064
|
let stmt = deleteIndexStatements.get(db);
|
|
151000
151065
|
if (!stmt) {
|
|
@@ -151015,6 +151080,14 @@ function getLastIndexedOrdinal(db, sessionId) {
|
|
|
151015
151080
|
const row = getLastIndexedStatement(db).get(sessionId);
|
|
151016
151081
|
return typeof row?.last_indexed_ordinal === "number" ? row.last_indexed_ordinal : 0;
|
|
151017
151082
|
}
|
|
151083
|
+
function getDirtyIndexFloor(db, sessionId) {
|
|
151084
|
+
const row = getLastIndexedStatement(db).get(sessionId);
|
|
151085
|
+
return typeof row?.dirty_floor_ordinal === "number" && row.dirty_floor_ordinal > 0 ? row.dirty_floor_ordinal : null;
|
|
151086
|
+
}
|
|
151087
|
+
function markMessageIndexDirty(db, sessionId, floorOrdinal) {
|
|
151088
|
+
const dirtyFloor = Math.max(1, Math.floor(floorOrdinal));
|
|
151089
|
+
getUpsertDirtyFloorStatement(db).run(sessionId, getLastIndexedOrdinal(db, sessionId), dirtyFloor, Date.now(), getHarness());
|
|
151090
|
+
}
|
|
151018
151091
|
function isMessageAlreadyIndexed(db, sessionId, messageId) {
|
|
151019
151092
|
const row = getCountIndexedMessageStatement(db).get(sessionId, messageId);
|
|
151020
151093
|
return (typeof row?.count === "number" ? row.count : 0) > 0;
|
|
@@ -151088,7 +151161,15 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal,
|
|
|
151088
151161
|
db.exec("BEGIN IMMEDIATE");
|
|
151089
151162
|
let committed = false;
|
|
151090
151163
|
try {
|
|
151091
|
-
|
|
151164
|
+
let effectiveWatermark = Math.max(lastIndexedOrdinal, getLastIndexedOrdinal(db, sessionId));
|
|
151165
|
+
const dirtyFloor = getDirtyIndexFloor(db, sessionId);
|
|
151166
|
+
if (dirtyFloor !== null) {
|
|
151167
|
+
const rewindOrdinal = Math.max(1, Math.min(dirtyFloor, finalWatermark + 1));
|
|
151168
|
+
if (rewindOrdinal <= finalWatermark) {
|
|
151169
|
+
getDeleteFtsFromOrdinalStatement(db).run(sessionId, rewindOrdinal);
|
|
151170
|
+
}
|
|
151171
|
+
effectiveWatermark = Math.min(effectiveWatermark, rewindOrdinal - 1);
|
|
151172
|
+
}
|
|
151092
151173
|
const insertMessage = getInsertMessageStatement(db);
|
|
151093
151174
|
for (const message of messages) {
|
|
151094
151175
|
if (message.ordinal <= effectiveWatermark) {
|
|
@@ -151105,7 +151186,7 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal,
|
|
|
151105
151186
|
inserted++;
|
|
151106
151187
|
}
|
|
151107
151188
|
const newWatermark = Math.max(effectiveWatermark, finalWatermark);
|
|
151108
|
-
|
|
151189
|
+
getUpsertCleanIndexStatement(db).run(sessionId, newWatermark, now, getHarness());
|
|
151109
151190
|
db.exec("COMMIT");
|
|
151110
151191
|
committed = true;
|
|
151111
151192
|
} finally {
|
|
@@ -151117,14 +151198,17 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal,
|
|
|
151117
151198
|
}
|
|
151118
151199
|
return inserted;
|
|
151119
151200
|
}
|
|
151120
|
-
var lastIndexedStatements, insertMessageStatements, upsertIndexStatements, deleteFtsStatements, deleteIndexStatements, countIndexedMessageStatements;
|
|
151201
|
+
var lastIndexedStatements, insertMessageStatements, upsertIndexStatements, upsertCleanIndexStatements, upsertDirtyFloorStatements, deleteFtsStatements, deleteFtsFromOrdinalStatements, deleteIndexStatements, countIndexedMessageStatements;
|
|
151121
151202
|
var init_message_index = __esm(async () => {
|
|
151122
151203
|
init_compression_depth_storage();
|
|
151123
151204
|
await init_read_session_chunk();
|
|
151124
151205
|
lastIndexedStatements = new WeakMap;
|
|
151125
151206
|
insertMessageStatements = new WeakMap;
|
|
151126
151207
|
upsertIndexStatements = new WeakMap;
|
|
151208
|
+
upsertCleanIndexStatements = new WeakMap;
|
|
151209
|
+
upsertDirtyFloorStatements = new WeakMap;
|
|
151127
151210
|
deleteFtsStatements = new WeakMap;
|
|
151211
|
+
deleteFtsFromOrdinalStatements = new WeakMap;
|
|
151128
151212
|
deleteIndexStatements = new WeakMap;
|
|
151129
151213
|
countIndexedMessageStatements = new WeakMap;
|
|
151130
151214
|
});
|
|
@@ -151163,6 +151247,7 @@ function healNullTextColumns(db) {
|
|
|
151163
151247
|
["cache_ttl", ""],
|
|
151164
151248
|
["last_nudge_band", ""],
|
|
151165
151249
|
["last_nudge_level", ""],
|
|
151250
|
+
["channel2_nudge_claim_token", ""],
|
|
151166
151251
|
["last_transform_error", ""],
|
|
151167
151252
|
["nudge_anchor_message_id", ""],
|
|
151168
151253
|
["nudge_anchor_text", ""],
|
|
@@ -151295,23 +151380,39 @@ function uniqueSorted(values) {
|
|
|
151295
151380
|
function placeholders(values) {
|
|
151296
151381
|
return values.map(() => "?").join(", ");
|
|
151297
151382
|
}
|
|
151383
|
+
function defaultWorkspaceShareCategories() {
|
|
151384
|
+
return [...DEFAULT_WORKSPACE_SHARE_CATEGORIES];
|
|
151385
|
+
}
|
|
151386
|
+
function warnInvalidShareCategories(reason, raw) {
|
|
151387
|
+
log("[magic-context] WARN: invalid workspace share_categories; sharing no foreign memory categories", {
|
|
151388
|
+
reason,
|
|
151389
|
+
raw
|
|
151390
|
+
});
|
|
151391
|
+
}
|
|
151298
151392
|
function normalizeShareCategories(raw) {
|
|
151299
|
-
if (raw === null || raw === undefined)
|
|
151300
|
-
return
|
|
151301
|
-
|
|
151302
|
-
|
|
151393
|
+
if (raw === null || raw === undefined) {
|
|
151394
|
+
return defaultWorkspaceShareCategories();
|
|
151395
|
+
}
|
|
151396
|
+
if (typeof raw !== "string") {
|
|
151397
|
+
warnInvalidShareCategories("not a string", raw);
|
|
151398
|
+
return [];
|
|
151399
|
+
}
|
|
151303
151400
|
let parsed;
|
|
151304
151401
|
try {
|
|
151305
151402
|
parsed = JSON.parse(raw);
|
|
151306
151403
|
} catch {
|
|
151307
|
-
|
|
151404
|
+
warnInvalidShareCategories("malformed JSON", raw);
|
|
151405
|
+
return [];
|
|
151406
|
+
}
|
|
151407
|
+
if (!Array.isArray(parsed)) {
|
|
151408
|
+
warnInvalidShareCategories("not a JSON array", raw);
|
|
151409
|
+
return [];
|
|
151308
151410
|
}
|
|
151309
|
-
if (!Array.isArray(parsed))
|
|
151310
|
-
return null;
|
|
151311
151411
|
const categories = [];
|
|
151312
151412
|
for (const value of parsed) {
|
|
151313
151413
|
if (typeof value !== "string" || !VALID_SHARE_CATEGORIES.has(value)) {
|
|
151314
|
-
|
|
151414
|
+
warnInvalidShareCategories("unknown category", raw);
|
|
151415
|
+
return [];
|
|
151315
151416
|
}
|
|
151316
151417
|
if (!categories.includes(value))
|
|
151317
151418
|
categories.push(value);
|
|
@@ -151320,16 +151421,33 @@ function normalizeShareCategories(raw) {
|
|
|
151320
151421
|
}
|
|
151321
151422
|
function selectWorkspaceShareCategories(db, identities) {
|
|
151322
151423
|
const candidates = uniqueSorted(identities.filter((identity) => identity.length > 0));
|
|
151323
|
-
if (candidates.length === 0 || !tableExists(db, "workspace_members")
|
|
151424
|
+
if (candidates.length === 0 || !tableExists(db, "workspace_members")) {
|
|
151324
151425
|
return null;
|
|
151325
151426
|
}
|
|
151427
|
+
const hasMembership = Boolean(db.prepare(`SELECT 1
|
|
151428
|
+
FROM workspace_members
|
|
151429
|
+
WHERE project_path IN (${placeholders(candidates)})
|
|
151430
|
+
LIMIT 1`).get(...candidates));
|
|
151431
|
+
if (!hasMembership)
|
|
151432
|
+
return null;
|
|
151433
|
+
if (!tableExists(db, "workspaces")) {
|
|
151434
|
+
log("[magic-context] WARN: workspace member has no workspaces table; sharing no foreign memory categories");
|
|
151435
|
+
return [];
|
|
151436
|
+
}
|
|
151437
|
+
if (!columnExists(db, "workspaces", "share_categories")) {
|
|
151438
|
+
return defaultWorkspaceShareCategories();
|
|
151439
|
+
}
|
|
151326
151440
|
const row = db.prepare(`SELECT workspace.share_categories AS shareCategories
|
|
151327
151441
|
FROM workspace_members AS member
|
|
151328
151442
|
JOIN workspaces AS workspace ON workspace.id = member.workspace_id
|
|
151329
151443
|
WHERE member.project_path IN (${placeholders(candidates)})
|
|
151330
151444
|
ORDER BY workspace.id ASC
|
|
151331
151445
|
LIMIT 1`).get(...candidates);
|
|
151332
|
-
|
|
151446
|
+
if (!row) {
|
|
151447
|
+
log("[magic-context] WARN: workspace member has no workspace share_categories row; sharing no foreign memory categories");
|
|
151448
|
+
return [];
|
|
151449
|
+
}
|
|
151450
|
+
return normalizeShareCategories(row.shareCategories);
|
|
151333
151451
|
}
|
|
151334
151452
|
function resolveWorkspaceShareCategories(db, projectIdentity) {
|
|
151335
151453
|
return selectWorkspaceShareCategories(db, [projectIdentity]);
|
|
@@ -151434,7 +151552,7 @@ function computeWorkspaceEpochFingerprint(db, identities) {
|
|
|
151434
151552
|
const hash2 = createHash2("sha256");
|
|
151435
151553
|
hash2.update("share_categories", "utf8");
|
|
151436
151554
|
hash2.update("\x00");
|
|
151437
|
-
hash2.update(shareCategories === null ? "
|
|
151555
|
+
hash2.update(shareCategories === null ? "NO_WORKSPACE" : JSON.stringify(shareCategories), "utf8");
|
|
151438
151556
|
hash2.update(`
|
|
151439
151557
|
`);
|
|
151440
151558
|
for (const identity of canonical) {
|
|
@@ -151506,11 +151624,13 @@ function bumpEpochsForWorkspaceMemberSet(db, identities, now = Date.now()) {
|
|
|
151506
151624
|
throw error51;
|
|
151507
151625
|
}
|
|
151508
151626
|
}
|
|
151509
|
-
var VALID_SHARE_CATEGORIES;
|
|
151627
|
+
var VALID_SHARE_CATEGORIES, DEFAULT_WORKSPACE_SHARE_CATEGORIES;
|
|
151510
151628
|
var init_workspaces = __esm(() => {
|
|
151629
|
+
init_logger();
|
|
151511
151630
|
init_constants();
|
|
151512
151631
|
init_project_identity2();
|
|
151513
151632
|
VALID_SHARE_CATEGORIES = new Set(V2_MEMORY_CATEGORIES);
|
|
151633
|
+
DEFAULT_WORKSPACE_SHARE_CATEGORIES = ["CONSTRAINTS"];
|
|
151514
151634
|
});
|
|
151515
151635
|
|
|
151516
151636
|
// src/features/magic-context/migrations.ts
|
|
@@ -153995,6 +154115,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
153995
154115
|
CREATE TABLE IF NOT EXISTS message_history_index (
|
|
153996
154116
|
session_id TEXT PRIMARY KEY,
|
|
153997
154117
|
last_indexed_ordinal INTEGER NOT NULL DEFAULT 0,
|
|
154118
|
+
dirty_floor_ordinal INTEGER NOT NULL DEFAULT 0,
|
|
153998
154119
|
updated_at INTEGER NOT NULL,
|
|
153999
154120
|
harness TEXT NOT NULL DEFAULT 'opencode'
|
|
154000
154121
|
);
|
|
@@ -154024,6 +154145,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
154024
154145
|
last_nudge_level TEXT DEFAULT '',
|
|
154025
154146
|
channel2_nudge_state TEXT DEFAULT '',
|
|
154026
154147
|
channel2_nudge_claimed_at INTEGER DEFAULT 0,
|
|
154148
|
+
channel2_nudge_claim_token TEXT DEFAULT '',
|
|
154027
154149
|
last_emergency_input_sample INTEGER DEFAULT 0,
|
|
154028
154150
|
last_transform_error TEXT DEFAULT '',
|
|
154029
154151
|
nudge_anchor_message_id TEXT DEFAULT '',
|
|
@@ -154270,6 +154392,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
154270
154392
|
ensureColumn(db, "session_meta", "last_nudge_level", "TEXT DEFAULT ''");
|
|
154271
154393
|
ensureColumn(db, "session_meta", "channel2_nudge_state", "TEXT DEFAULT ''");
|
|
154272
154394
|
ensureColumn(db, "session_meta", "channel2_nudge_claimed_at", "INTEGER DEFAULT 0");
|
|
154395
|
+
ensureColumn(db, "session_meta", "channel2_nudge_claim_token", "TEXT DEFAULT ''");
|
|
154273
154396
|
ensureColumn(db, "session_meta", "last_emergency_input_sample", "INTEGER DEFAULT 0");
|
|
154274
154397
|
ensureColumn(db, "session_meta", "last_transform_error", "TEXT DEFAULT ''");
|
|
154275
154398
|
ensureColumn(db, "session_meta", "nudge_anchor_message_id", "TEXT DEFAULT ''");
|
|
@@ -154482,6 +154605,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
154482
154605
|
ON transform_decisions(session_id, harness);
|
|
154483
154606
|
`);
|
|
154484
154607
|
ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
154608
|
+
ensureColumn(db, "message_history_index", "dirty_floor_ordinal", "INTEGER NOT NULL DEFAULT 0");
|
|
154485
154609
|
ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
154486
154610
|
ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
154487
154611
|
ensureColumn(db, "compartments", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
@@ -154496,7 +154620,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
154496
154620
|
function healWedgedChannel2Claims(db) {
|
|
154497
154621
|
try {
|
|
154498
154622
|
const staleBefore = Date.now() - CHANNEL2_CLAIM_TTL_MS;
|
|
154499
|
-
db.prepare("UPDATE session_meta SET channel2_nudge_state = 'pending', channel2_nudge_claimed_at = 0 WHERE channel2_nudge_state = 'claimed' AND (channel2_nudge_claimed_at IS NULL OR channel2_nudge_claimed_at = 0 OR channel2_nudge_claimed_at <= ?)").run(staleBefore);
|
|
154623
|
+
db.prepare("UPDATE session_meta SET channel2_nudge_state = 'pending', channel2_nudge_claimed_at = 0, channel2_nudge_claim_token = '' WHERE channel2_nudge_state = 'claimed' AND (channel2_nudge_claimed_at IS NULL OR channel2_nudge_claimed_at = 0 OR channel2_nudge_claimed_at <= ?)").run(staleBefore);
|
|
154500
154624
|
} catch {}
|
|
154501
154625
|
}
|
|
154502
154626
|
function openDatabase(dbPathOrOptions) {
|
|
@@ -155230,19 +155354,42 @@ function getChannel2NudgeState(db, sessionId) {
|
|
|
155230
155354
|
const raw = result.channel2_nudge_state;
|
|
155231
155355
|
return raw === "pending" || raw === "claimed" || raw === "delivered" ? raw : "";
|
|
155232
155356
|
}
|
|
155233
|
-
function
|
|
155357
|
+
function getChannel2NudgeClaim(db, sessionId) {
|
|
155358
|
+
const result = db.prepare("SELECT channel2_nudge_state, channel2_nudge_claimed_at, channel2_nudge_claim_token FROM session_meta WHERE session_id = ?").get(sessionId);
|
|
155359
|
+
const rawState = typeof result?.channel2_nudge_state === "string" ? result.channel2_nudge_state : "";
|
|
155360
|
+
const state = rawState === "pending" || rawState === "claimed" || rawState === "delivered" ? rawState : "";
|
|
155361
|
+
return {
|
|
155362
|
+
state,
|
|
155363
|
+
claimedAt: typeof result?.channel2_nudge_claimed_at === "number" ? result.channel2_nudge_claimed_at : 0,
|
|
155364
|
+
claimToken: typeof result?.channel2_nudge_claim_token === "string" ? result.channel2_nudge_claim_token : ""
|
|
155365
|
+
};
|
|
155366
|
+
}
|
|
155367
|
+
function casChannel2NudgeState(db, sessionId, from, to) {
|
|
155368
|
+
let changed = false;
|
|
155234
155369
|
db.transaction(() => {
|
|
155235
155370
|
ensureSessionMetaRow(db, sessionId);
|
|
155236
|
-
const claimedAt =
|
|
155237
|
-
db.prepare("UPDATE session_meta SET channel2_nudge_state = ?, channel2_nudge_claimed_at =
|
|
155371
|
+
const claimedAt = to === "claimed" ? Date.now() : 0;
|
|
155372
|
+
const result = db.prepare("UPDATE session_meta SET channel2_nudge_state = ?, channel2_nudge_claimed_at = ?, channel2_nudge_claim_token = '' WHERE session_id = ? AND channel2_nudge_state = ?").run(to, claimedAt, sessionId, from);
|
|
155373
|
+
changed = (result.changes ?? 0) > 0;
|
|
155238
155374
|
})();
|
|
155375
|
+
return changed;
|
|
155239
155376
|
}
|
|
155240
|
-
function
|
|
155377
|
+
function claimChannel2NudgeState(db, sessionId, claimToken) {
|
|
155378
|
+
let changed = false;
|
|
155379
|
+
db.transaction(() => {
|
|
155380
|
+
ensureSessionMetaRow(db, sessionId);
|
|
155381
|
+
const result = db.prepare("UPDATE session_meta SET channel2_nudge_state = 'claimed', channel2_nudge_claimed_at = ?, channel2_nudge_claim_token = ? WHERE session_id = ? AND channel2_nudge_state = 'pending'").run(Date.now(), claimToken, sessionId);
|
|
155382
|
+
changed = (result.changes ?? 0) > 0;
|
|
155383
|
+
})();
|
|
155384
|
+
return changed;
|
|
155385
|
+
}
|
|
155386
|
+
function casChannel2NudgeClaim(db, sessionId, to, claimToken) {
|
|
155241
155387
|
let changed = false;
|
|
155242
155388
|
db.transaction(() => {
|
|
155243
155389
|
ensureSessionMetaRow(db, sessionId);
|
|
155244
155390
|
const claimedAt = to === "claimed" ? Date.now() : 0;
|
|
155245
|
-
const
|
|
155391
|
+
const nextClaimToken = to === "claimed" ? claimToken : "";
|
|
155392
|
+
const result = db.prepare("UPDATE session_meta SET channel2_nudge_state = ?, channel2_nudge_claimed_at = ?, channel2_nudge_claim_token = ? WHERE session_id = ? AND channel2_nudge_state = 'claimed' AND channel2_nudge_claim_token = ?").run(to, claimedAt, nextClaimToken, sessionId, claimToken);
|
|
155246
155393
|
changed = (result.changes ?? 0) > 0;
|
|
155247
155394
|
})();
|
|
155248
155395
|
return changed;
|
|
@@ -166118,12 +166265,41 @@ function pushNotification(type, payload, sessionId) {
|
|
|
166118
166265
|
queue = kept.sort((a, b) => a.id - b.id);
|
|
166119
166266
|
}
|
|
166120
166267
|
}
|
|
166121
|
-
function
|
|
166268
|
+
function cursor(value) {
|
|
166269
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
166270
|
+
}
|
|
166271
|
+
function drainNotifications(lastReceivedId = 0, sessionId, options = {}) {
|
|
166272
|
+
const sessionCursor = cursor(lastReceivedId);
|
|
166273
|
+
if (options.globalOnly) {
|
|
166274
|
+
queue = queue.filter((notification) => notification.sessionId !== undefined || notification.id > sessionCursor);
|
|
166275
|
+
return queue.filter((notification) => notification.sessionId === undefined && notification.id > sessionCursor);
|
|
166276
|
+
}
|
|
166277
|
+
if (options.sessionOnly) {
|
|
166278
|
+
if (sessionId === undefined)
|
|
166279
|
+
return [];
|
|
166280
|
+
queue = queue.filter((notification) => notification.sessionId !== sessionId || notification.id > sessionCursor);
|
|
166281
|
+
return queue.filter((notification) => notification.sessionId === sessionId && notification.id > sessionCursor);
|
|
166282
|
+
}
|
|
166283
|
+
if (sessionId !== undefined && options.globalLastReceivedId !== undefined) {
|
|
166284
|
+
const globalCursor = cursor(options.globalLastReceivedId);
|
|
166285
|
+
queue = queue.filter((notification) => {
|
|
166286
|
+
if (notification.sessionId === undefined)
|
|
166287
|
+
return notification.id > globalCursor;
|
|
166288
|
+
if (notification.sessionId === sessionId)
|
|
166289
|
+
return notification.id > sessionCursor;
|
|
166290
|
+
return true;
|
|
166291
|
+
});
|
|
166292
|
+
return queue.filter((notification) => {
|
|
166293
|
+
if (notification.sessionId === undefined)
|
|
166294
|
+
return notification.id > globalCursor;
|
|
166295
|
+
return notification.sessionId === sessionId && notification.id > sessionCursor;
|
|
166296
|
+
});
|
|
166297
|
+
}
|
|
166122
166298
|
const matchesClient = (notification) => sessionId === undefined || notification.sessionId === undefined || notification.sessionId === sessionId;
|
|
166123
|
-
if (
|
|
166124
|
-
queue = queue.filter((notification) => !(notification.id <=
|
|
166299
|
+
if (sessionCursor > 0) {
|
|
166300
|
+
queue = queue.filter((notification) => !(notification.id <= sessionCursor && matchesClient(notification)));
|
|
166125
166301
|
}
|
|
166126
|
-
return queue.filter((notification) => notification.id >
|
|
166302
|
+
return queue.filter((notification) => notification.id > sessionCursor && matchesClient(notification));
|
|
166127
166303
|
}
|
|
166128
166304
|
function isTuiConnected(sessionId) {
|
|
166129
166305
|
if (sinks.size === 0)
|
|
@@ -167962,6 +168138,14 @@ function getSaveEmbeddingStatement(db) {
|
|
|
167962
168138
|
}
|
|
167963
168139
|
return stmt;
|
|
167964
168140
|
}
|
|
168141
|
+
function getSaveEmbeddingIfHashMatchesStatement(db) {
|
|
168142
|
+
let stmt = saveEmbeddingIfHashMatchesStatements.get(db);
|
|
168143
|
+
if (!stmt) {
|
|
168144
|
+
stmt = db.prepare("INSERT INTO memory_embeddings (memory_id, embedding, model_id) SELECT ?, ?, ? FROM memories WHERE id = ? AND normalized_hash = ? ON CONFLICT(memory_id, model_id) DO UPDATE SET embedding = excluded.embedding");
|
|
168145
|
+
saveEmbeddingIfHashMatchesStatements.set(db, stmt);
|
|
168146
|
+
}
|
|
168147
|
+
return stmt;
|
|
168148
|
+
}
|
|
167965
168149
|
function getLoadAllEmbeddingsStatement(db) {
|
|
167966
168150
|
let stmt = loadAllEmbeddingsStatements.get(db);
|
|
167967
168151
|
if (!stmt) {
|
|
@@ -167990,6 +168174,10 @@ function saveEmbedding(db, memoryId, embedding, modelId) {
|
|
|
167990
168174
|
const blob = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
|
|
167991
168175
|
getSaveEmbeddingStatement(db).run(memoryId, blob, modelId);
|
|
167992
168176
|
}
|
|
168177
|
+
function saveEmbeddingIfHashMatches(db, memoryId, embedding, modelId, normalizedHash) {
|
|
168178
|
+
const blob = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
|
|
168179
|
+
return getSaveEmbeddingIfHashMatchesStatement(db).run(memoryId, blob, modelId, memoryId, normalizedHash).changes > 0;
|
|
168180
|
+
}
|
|
167993
168181
|
function loadAllEmbeddings(db, projectPath, modelId) {
|
|
167994
168182
|
const rows = getLoadAllEmbeddingsStatement(db).all(projectPath, modelId).filter(isEmbeddingRow);
|
|
167995
168183
|
const embeddings = new Map;
|
|
@@ -168021,9 +168209,10 @@ function getMemoryEmbedCoverage(db, projectPath, modelId) {
|
|
|
168021
168209
|
embedded: typeof row?.embedded === "number" ? row.embedded : 0
|
|
168022
168210
|
};
|
|
168023
168211
|
}
|
|
168024
|
-
var saveEmbeddingStatements, loadAllEmbeddingsStatements, deleteEmbeddingStatements, getStoredModelIdStatements, clearAllEmbeddingsStatements, clearModelEmbeddingsStatements, getDistinctStoredModelIdsStatements;
|
|
168212
|
+
var saveEmbeddingStatements, saveEmbeddingIfHashMatchesStatements, loadAllEmbeddingsStatements, deleteEmbeddingStatements, getStoredModelIdStatements, clearAllEmbeddingsStatements, clearModelEmbeddingsStatements, getDistinctStoredModelIdsStatements;
|
|
168025
168213
|
var init_storage_memory_embeddings = __esm(() => {
|
|
168026
168214
|
saveEmbeddingStatements = new WeakMap;
|
|
168215
|
+
saveEmbeddingIfHashMatchesStatements = new WeakMap;
|
|
168027
168216
|
loadAllEmbeddingsStatements = new WeakMap;
|
|
168028
168217
|
deleteEmbeddingStatements = new WeakMap;
|
|
168029
168218
|
getStoredModelIdStatements = new WeakMap;
|
|
@@ -168848,12 +169037,12 @@ function isUnembeddedMemoryRow(row) {
|
|
|
168848
169037
|
if (row === null || typeof row !== "object")
|
|
168849
169038
|
return false;
|
|
168850
169039
|
const candidate = row;
|
|
168851
|
-
return typeof candidate.id === "number" && typeof candidate.content === "string";
|
|
169040
|
+
return typeof candidate.id === "number" && typeof candidate.content === "string" && typeof candidate.normalized_hash === "string";
|
|
168852
169041
|
}
|
|
168853
169042
|
function getLoadUnembeddedMemoriesStatement(db) {
|
|
168854
169043
|
let stmt = loadUnembeddedMemoriesStatements.get(db);
|
|
168855
169044
|
if (!stmt) {
|
|
168856
|
-
stmt = db.prepare("SELECT m.id AS id, m.content AS content FROM memories m LEFT JOIN memory_embeddings me ON m.id = me.memory_id AND me.model_id = ? WHERE m.project_path = ? AND m.status = 'active' AND me.memory_id IS NULL LIMIT ?");
|
|
169045
|
+
stmt = db.prepare("SELECT m.id AS id, m.content AS content, m.normalized_hash AS normalized_hash FROM memories m LEFT JOIN memory_embeddings me ON m.id = me.memory_id AND me.model_id = ? WHERE m.project_path = ? AND m.status = 'active' AND me.memory_id IS NULL LIMIT ?");
|
|
168857
169046
|
loadUnembeddedMemoriesStatements.set(db, stmt);
|
|
168858
169047
|
}
|
|
168859
169048
|
return stmt;
|
|
@@ -168876,8 +169065,9 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
168876
169065
|
const embedding = result.vectors[index];
|
|
168877
169066
|
if (!embedding)
|
|
168878
169067
|
continue;
|
|
168879
|
-
|
|
168880
|
-
|
|
169068
|
+
if (saveEmbeddingIfHashMatches(db, memory.id, embedding, result.modelId, memory.normalized_hash)) {
|
|
169069
|
+
embeddedCount += 1;
|
|
169070
|
+
}
|
|
168881
169071
|
}
|
|
168882
169072
|
})();
|
|
168883
169073
|
return embeddedCount;
|
|
@@ -169343,6 +169533,9 @@ function isMemorySourceType(value) {
|
|
|
169343
169533
|
function isVerificationStatus(value) {
|
|
169344
169534
|
return typeof value === "string" && value in VERIFICATION_STATUS_LOOKUP;
|
|
169345
169535
|
}
|
|
169536
|
+
function isUniqueConstraintError(error51) {
|
|
169537
|
+
return error51 instanceof Error && "code" in error51 && error51.code === "SQLITE_CONSTRAINT_UNIQUE";
|
|
169538
|
+
}
|
|
169346
169539
|
function isNullableString(value) {
|
|
169347
169540
|
return value === null || typeof value === "string";
|
|
169348
169541
|
}
|
|
@@ -169508,28 +169701,55 @@ function getMemoryCountsByStatusStatement(db) {
|
|
|
169508
169701
|
}
|
|
169509
169702
|
return stmt;
|
|
169510
169703
|
}
|
|
169511
|
-
function
|
|
169512
|
-
const now = Date.now();
|
|
169513
|
-
const normalizedHash = computeNormalizedHash(input.content);
|
|
169704
|
+
function buildInsertMemoryValues(input, normalizedHash, now, includeImportance) {
|
|
169514
169705
|
const insertValues = [
|
|
169515
169706
|
input.projectPath,
|
|
169516
169707
|
input.category,
|
|
169517
169708
|
input.content,
|
|
169518
169709
|
normalizedHash
|
|
169519
169710
|
];
|
|
169520
|
-
if (
|
|
169711
|
+
if (includeImportance) {
|
|
169521
169712
|
insertValues.push(input.importance ?? 50);
|
|
169522
169713
|
}
|
|
169523
169714
|
insertValues.push(input.sourceSessionId ?? null, input.sourceType ?? "historian", 1, 0, now, now, now, now, null, "active", input.expiresAt ?? null, "unverified", null, null, null, input.metadataJson ?? null);
|
|
169524
|
-
|
|
169525
|
-
|
|
169526
|
-
|
|
169715
|
+
return insertValues;
|
|
169716
|
+
}
|
|
169717
|
+
function loadInsertedMemory(db, rowid) {
|
|
169718
|
+
const inserted = getMemoryById(db, Number(rowid));
|
|
169527
169719
|
if (!inserted) {
|
|
169528
169720
|
throw new Error("Failed to load inserted memory row");
|
|
169529
169721
|
}
|
|
169722
|
+
return inserted;
|
|
169723
|
+
}
|
|
169724
|
+
function insertMemory(db, input) {
|
|
169725
|
+
const now = Date.now();
|
|
169726
|
+
const normalizedHash = computeNormalizedHash(input.content);
|
|
169727
|
+
const insertValues = buildInsertMemoryValues(input, normalizedHash, now, hasMemoryImportanceColumn(db));
|
|
169728
|
+
const result = getInsertMemoryStatement(db).run(...insertValues);
|
|
169729
|
+
const insertedResult = result;
|
|
169730
|
+
const inserted = loadInsertedMemory(db, insertedResult.lastInsertRowid);
|
|
169530
169731
|
invalidateProject(input.projectPath);
|
|
169531
169732
|
return inserted;
|
|
169532
169733
|
}
|
|
169734
|
+
function insertMemoryIdempotent(db, input) {
|
|
169735
|
+
try {
|
|
169736
|
+
return { memory: insertMemory(db, input), inserted: true };
|
|
169737
|
+
} catch (error51) {
|
|
169738
|
+
if (!isUniqueConstraintError(error51)) {
|
|
169739
|
+
throw error51;
|
|
169740
|
+
}
|
|
169741
|
+
const normalizedHash = computeNormalizedHash(input.content);
|
|
169742
|
+
const existing = getMemoryByHash(db, input.projectPath, input.category, normalizedHash);
|
|
169743
|
+
if (!existing) {
|
|
169744
|
+
throw error51;
|
|
169745
|
+
}
|
|
169746
|
+
updateMemorySeenCount(db, existing.id);
|
|
169747
|
+
return {
|
|
169748
|
+
memory: getMemoryById(db, existing.id) ?? existing,
|
|
169749
|
+
inserted: false
|
|
169750
|
+
};
|
|
169751
|
+
}
|
|
169752
|
+
}
|
|
169533
169753
|
function getMemoryByHash(db, projectPath, category, normalizedHash) {
|
|
169534
169754
|
const result = getMemoryByHashStatement(db).get(projectPath, category, normalizedHash);
|
|
169535
169755
|
if (!isMemoryRow(result)) {
|
|
@@ -180435,6 +180655,23 @@ function recursiveByteLength(value) {
|
|
|
180435
180655
|
}
|
|
180436
180656
|
return String(value).length;
|
|
180437
180657
|
}
|
|
180658
|
+
function updateFnv1a32(hash2, text) {
|
|
180659
|
+
let next = hash2;
|
|
180660
|
+
for (let index = 0;index < text.length; index += 1) {
|
|
180661
|
+
next ^= text.charCodeAt(index);
|
|
180662
|
+
next = Math.imul(next, FNV1A_32_PRIME) >>> 0;
|
|
180663
|
+
}
|
|
180664
|
+
return next;
|
|
180665
|
+
}
|
|
180666
|
+
function contentStringsHash(fields) {
|
|
180667
|
+
let hash2 = FNV1A_32_OFFSET;
|
|
180668
|
+
for (const field of fields) {
|
|
180669
|
+
hash2 = updateFnv1a32(hash2, `${field.length}:`);
|
|
180670
|
+
hash2 = updateFnv1a32(hash2, field);
|
|
180671
|
+
hash2 = updateFnv1a32(hash2, "\x00");
|
|
180672
|
+
}
|
|
180673
|
+
return hash2.toString(16).padStart(8, "0");
|
|
180674
|
+
}
|
|
180438
180675
|
function rawPartVersion(part) {
|
|
180439
180676
|
return part.__magicContextPartUpdatedAt ?? part.updated_at ?? part.updatedAt ?? part.version ?? part.revision ?? "";
|
|
180440
180677
|
}
|
|
@@ -180782,13 +181019,12 @@ function buildTrueRawTokenIndex(sessionId, messages, options) {
|
|
|
180782
181019
|
function partContentFingerprint(part) {
|
|
180783
181020
|
if (!isRecord(part))
|
|
180784
181021
|
return `${typeof part}:${recursiveByteLength(part)}`;
|
|
180785
|
-
const type = partType(part);
|
|
180786
181022
|
const tool = toolSignalFromPart(part);
|
|
180787
181023
|
if (tool) {
|
|
180788
|
-
return
|
|
181024
|
+
return contentStringsHash([tool.inputText, tool.outputText]);
|
|
180789
181025
|
}
|
|
180790
181026
|
const text = firstStringField(part, ["text", "thinking", "reasoning", "content", "url"]) ?? "";
|
|
180791
|
-
return
|
|
181027
|
+
return contentStringsHash([text]);
|
|
180792
181028
|
}
|
|
180793
181029
|
function computeRawRangeFingerprint(messages, startInclusive, endExclusive) {
|
|
180794
181030
|
const pieces = [];
|
|
@@ -180813,7 +181049,7 @@ function invalidateTrueRawTokenCache(args) {
|
|
|
180813
181049
|
}
|
|
180814
181050
|
args.reason;
|
|
180815
181051
|
}
|
|
180816
|
-
var MAX_MESSAGE_CACHE_ENTRIES = 1e5, MAX_MESSAGE_CACHE_KEY_BYTES, messageEstimateCache, messageEstimateCacheBytes = 0, EMPTY_BREAKDOWN;
|
|
181052
|
+
var MAX_MESSAGE_CACHE_ENTRIES = 1e5, MAX_MESSAGE_CACHE_KEY_BYTES, FNV1A_32_OFFSET = 2166136261, FNV1A_32_PRIME = 16777619, messageEstimateCache, messageEstimateCacheBytes = 0, EMPTY_BREAKDOWN;
|
|
180817
181053
|
var init_read_session_true_raw_tokens = __esm(() => {
|
|
180818
181054
|
init_read_session_formatting();
|
|
180819
181055
|
MAX_MESSAGE_CACHE_KEY_BYTES = 64 * 1024 * 1024;
|
|
@@ -184824,6 +185060,15 @@ async function executeContextRecompWithResult(deps, options = {}) {
|
|
|
184824
185060
|
published: false
|
|
184825
185061
|
};
|
|
184826
185062
|
}
|
|
185063
|
+
options.onLeaseAcquired?.();
|
|
185064
|
+
if (isWrapupInProgress(deps.db, sessionId)) {
|
|
185065
|
+
sessionLog(sessionId, "recomp skipped: /ctx-wrapup became active");
|
|
185066
|
+
releaseCompartmentLease(deps.db, sessionId, holderId);
|
|
185067
|
+
return {
|
|
185068
|
+
message: "## Magic Recomp — Skipped\n\n/ctx-wrapup is already compacting this session. Wait for it to finish, then try `/ctx-recomp` again.",
|
|
185069
|
+
published: false
|
|
185070
|
+
};
|
|
185071
|
+
}
|
|
184827
185072
|
const renewal = startLeaseRenewal(deps, holderId);
|
|
184828
185073
|
const runnerDeps = withPublishedCallback({ ...deps, compartmentLeaseHolderId: holderId });
|
|
184829
185074
|
const promise2 = options.range ? executePartialRecompInternal(runnerDeps, options.range) : executeContextRecompInternal(runnerDeps);
|
|
@@ -186338,12 +186583,108 @@ function migrateLegacyExperimental(rawConfig, warnings) {
|
|
|
186338
186583
|
}
|
|
186339
186584
|
|
|
186340
186585
|
// src/config/project-security.ts
|
|
186586
|
+
init_magic_context();
|
|
186341
186587
|
var HIDDEN_AGENT_KEYS = ["historian", "dreamer", "sidekick"];
|
|
186588
|
+
var HISTORIAN_USER_ONLY_FIELDS = ["model", "fallback_models"];
|
|
186342
186589
|
var AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools", "system_prompt"];
|
|
186343
186590
|
var EMBEDDING_DESTINATION_FIELDS = ["endpoint", "provider"];
|
|
186344
|
-
|
|
186591
|
+
var PERCENTAGE_THRESHOLD_REASON = "security: a repository may only raise compaction thresholds above the user's effective value; it cannot force earlier historian work or cloned-repo cost escalation.";
|
|
186592
|
+
var TOKEN_THRESHOLD_REASON = "security: a repository may only raise execute_threshold_tokens above the user's trusted token threshold; it cannot force earlier historian work or cloned-repo cost escalation.";
|
|
186593
|
+
var TOKEN_THRESHOLD_INTRODUCTION_REASON = "security: a repository cannot introduce a new execute_threshold_tokens override when the user has no trusted token threshold for that key; that could force earlier historian work or cloned-repo cost escalation.";
|
|
186594
|
+
function isPlainObject2(value) {
|
|
186345
186595
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
186346
186596
|
}
|
|
186597
|
+
function isValidPercentageThreshold(value) {
|
|
186598
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 20 && value <= 80;
|
|
186599
|
+
}
|
|
186600
|
+
function isValidTokenThreshold(value) {
|
|
186601
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 5000 && value <= 2000000;
|
|
186602
|
+
}
|
|
186603
|
+
function normalizeTrustedPercentageThresholds(value) {
|
|
186604
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
186605
|
+
return { defaultValue: value, overrides: new Map };
|
|
186606
|
+
}
|
|
186607
|
+
if (isPlainObject2(value) && typeof value.default === "number" && Number.isFinite(value.default)) {
|
|
186608
|
+
const overrides = new Map;
|
|
186609
|
+
for (const [key, child] of Object.entries(value)) {
|
|
186610
|
+
if (key === "default")
|
|
186611
|
+
continue;
|
|
186612
|
+
if (typeof child === "number" && Number.isFinite(child)) {
|
|
186613
|
+
overrides.set(key, child);
|
|
186614
|
+
}
|
|
186615
|
+
}
|
|
186616
|
+
return { defaultValue: value.default, overrides };
|
|
186617
|
+
}
|
|
186618
|
+
return { defaultValue: DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE, overrides: new Map };
|
|
186619
|
+
}
|
|
186620
|
+
function normalizeTrustedTokenThresholds(value) {
|
|
186621
|
+
if (!isPlainObject2(value)) {
|
|
186622
|
+
return { defaultValue: undefined, overrides: new Map };
|
|
186623
|
+
}
|
|
186624
|
+
const overrides = new Map;
|
|
186625
|
+
for (const [key, child] of Object.entries(value)) {
|
|
186626
|
+
if (key === "default")
|
|
186627
|
+
continue;
|
|
186628
|
+
if (typeof child === "number" && Number.isFinite(child)) {
|
|
186629
|
+
overrides.set(key, child);
|
|
186630
|
+
}
|
|
186631
|
+
}
|
|
186632
|
+
return {
|
|
186633
|
+
defaultValue: typeof value.default === "number" && Number.isFinite(value.default) ? value.default : undefined,
|
|
186634
|
+
overrides
|
|
186635
|
+
};
|
|
186636
|
+
}
|
|
186637
|
+
function clonePercentageThresholds(value) {
|
|
186638
|
+
return {
|
|
186639
|
+
defaultValue: value.defaultValue,
|
|
186640
|
+
overrides: new Map(value.overrides)
|
|
186641
|
+
};
|
|
186642
|
+
}
|
|
186643
|
+
function cloneTokenThresholds(value) {
|
|
186644
|
+
return {
|
|
186645
|
+
defaultValue: value.defaultValue,
|
|
186646
|
+
overrides: new Map(value.overrides)
|
|
186647
|
+
};
|
|
186648
|
+
}
|
|
186649
|
+
function percentageThresholdsEqual(left, right) {
|
|
186650
|
+
if (left.defaultValue !== right.defaultValue)
|
|
186651
|
+
return false;
|
|
186652
|
+
if (left.overrides.size !== right.overrides.size)
|
|
186653
|
+
return false;
|
|
186654
|
+
for (const [key, value] of left.overrides) {
|
|
186655
|
+
if (right.overrides.get(key) !== value)
|
|
186656
|
+
return false;
|
|
186657
|
+
}
|
|
186658
|
+
return true;
|
|
186659
|
+
}
|
|
186660
|
+
function setMergedPercentageThreshold(mergedRaw, value) {
|
|
186661
|
+
if (value.overrides.size === 0) {
|
|
186662
|
+
mergedRaw.execute_threshold_percentage = value.defaultValue;
|
|
186663
|
+
return;
|
|
186664
|
+
}
|
|
186665
|
+
const serialized = { default: value.defaultValue };
|
|
186666
|
+
for (const [key, threshold] of value.overrides) {
|
|
186667
|
+
serialized[key] = threshold;
|
|
186668
|
+
}
|
|
186669
|
+
mergedRaw.execute_threshold_percentage = serialized;
|
|
186670
|
+
}
|
|
186671
|
+
function setMergedTokenThreshold(mergedRaw, value) {
|
|
186672
|
+
if (value.defaultValue === undefined && value.overrides.size === 0) {
|
|
186673
|
+
delete mergedRaw.execute_threshold_tokens;
|
|
186674
|
+
return;
|
|
186675
|
+
}
|
|
186676
|
+
const serialized = {};
|
|
186677
|
+
if (value.defaultValue !== undefined) {
|
|
186678
|
+
serialized.default = value.defaultValue;
|
|
186679
|
+
}
|
|
186680
|
+
for (const [key, threshold] of value.overrides) {
|
|
186681
|
+
serialized[key] = threshold;
|
|
186682
|
+
}
|
|
186683
|
+
mergedRaw.execute_threshold_tokens = serialized;
|
|
186684
|
+
}
|
|
186685
|
+
function makeProjectThresholdWarning(field, reason) {
|
|
186686
|
+
return `Ignoring ${field} from project config (${reason})`;
|
|
186687
|
+
}
|
|
186347
186688
|
function stripUnsafeProjectConfigFields(projectRaw) {
|
|
186348
186689
|
const warnings = [];
|
|
186349
186690
|
if ("auto_update" in projectRaw) {
|
|
@@ -186359,7 +186700,7 @@ function stripUnsafeProjectConfigFields(projectRaw) {
|
|
|
186359
186700
|
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).");
|
|
186360
186701
|
}
|
|
186361
186702
|
const embedding = projectRaw.embedding;
|
|
186362
|
-
if (
|
|
186703
|
+
if (isPlainObject2(embedding)) {
|
|
186363
186704
|
const removed = [];
|
|
186364
186705
|
for (const field of EMBEDDING_DESTINATION_FIELDS) {
|
|
186365
186706
|
if (field in embedding) {
|
|
@@ -186371,9 +186712,22 @@ function stripUnsafeProjectConfigFields(projectRaw) {
|
|
|
186371
186712
|
warnings.push(`Ignoring embedding.${removed.join("/")} from project config ` + "(security: a repository cannot choose where private text is embedded).");
|
|
186372
186713
|
}
|
|
186373
186714
|
}
|
|
186715
|
+
const historian = projectRaw.historian;
|
|
186716
|
+
if (isPlainObject2(historian)) {
|
|
186717
|
+
const removed = [];
|
|
186718
|
+
for (const field of HISTORIAN_USER_ONLY_FIELDS) {
|
|
186719
|
+
if (field in historian) {
|
|
186720
|
+
delete historian[field];
|
|
186721
|
+
removed.push(field);
|
|
186722
|
+
}
|
|
186723
|
+
}
|
|
186724
|
+
if (removed.length > 0) {
|
|
186725
|
+
warnings.push(`Ignoring historian.${removed.join("/")} from project config ` + "(security: historian model selection is user-level only; a repository cannot force extra compaction cost).");
|
|
186726
|
+
}
|
|
186727
|
+
}
|
|
186374
186728
|
for (const agentKey of HIDDEN_AGENT_KEYS) {
|
|
186375
186729
|
const block = projectRaw[agentKey];
|
|
186376
|
-
if (!
|
|
186730
|
+
if (!isPlainObject2(block))
|
|
186377
186731
|
continue;
|
|
186378
186732
|
const removed = [];
|
|
186379
186733
|
for (const field of AGENT_ESCALATION_FIELDS) {
|
|
@@ -186388,6 +186742,101 @@ function stripUnsafeProjectConfigFields(projectRaw) {
|
|
|
186388
186742
|
}
|
|
186389
186743
|
return warnings;
|
|
186390
186744
|
}
|
|
186745
|
+
function constrainProjectThresholdOverrides(args) {
|
|
186746
|
+
const warnings = [];
|
|
186747
|
+
const basePercentage = normalizeTrustedPercentageThresholds(args.trustedBaseConfig.execute_threshold_percentage);
|
|
186748
|
+
const baseTokens = normalizeTrustedTokenThresholds(args.trustedBaseConfig.execute_threshold_tokens);
|
|
186749
|
+
if ("execute_threshold_percentage" in args.projectRaw) {
|
|
186750
|
+
const projectValue = args.projectRaw.execute_threshold_percentage;
|
|
186751
|
+
if (isValidPercentageThreshold(projectValue)) {
|
|
186752
|
+
const constrained = clonePercentageThresholds(basePercentage);
|
|
186753
|
+
constrained.defaultValue = Math.max(basePercentage.defaultValue, projectValue);
|
|
186754
|
+
for (const [modelKey, threshold] of basePercentage.overrides) {
|
|
186755
|
+
const raisedThreshold = Math.max(threshold, projectValue);
|
|
186756
|
+
if (raisedThreshold === constrained.defaultValue) {
|
|
186757
|
+
constrained.overrides.delete(modelKey);
|
|
186758
|
+
} else {
|
|
186759
|
+
constrained.overrides.set(modelKey, raisedThreshold);
|
|
186760
|
+
}
|
|
186761
|
+
}
|
|
186762
|
+
setMergedPercentageThreshold(args.mergedRaw, constrained);
|
|
186763
|
+
if (percentageThresholdsEqual(constrained, basePercentage)) {
|
|
186764
|
+
warnings.push(makeProjectThresholdWarning("execute_threshold_percentage", PERCENTAGE_THRESHOLD_REASON));
|
|
186765
|
+
}
|
|
186766
|
+
} else if (isPlainObject2(projectValue)) {
|
|
186767
|
+
const constrained = clonePercentageThresholds(basePercentage);
|
|
186768
|
+
let touchedValidEntry = false;
|
|
186769
|
+
if (isValidPercentageThreshold(projectValue.default)) {
|
|
186770
|
+
touchedValidEntry = true;
|
|
186771
|
+
if (projectValue.default > basePercentage.defaultValue) {
|
|
186772
|
+
constrained.defaultValue = projectValue.default;
|
|
186773
|
+
} else {
|
|
186774
|
+
warnings.push(makeProjectThresholdWarning("execute_threshold_percentage.default", PERCENTAGE_THRESHOLD_REASON));
|
|
186775
|
+
}
|
|
186776
|
+
}
|
|
186777
|
+
for (const [modelKey, rawValue] of Object.entries(projectValue)) {
|
|
186778
|
+
if (modelKey === "default")
|
|
186779
|
+
continue;
|
|
186780
|
+
if (!isValidPercentageThreshold(rawValue))
|
|
186781
|
+
continue;
|
|
186782
|
+
touchedValidEntry = true;
|
|
186783
|
+
const baseValue = basePercentage.overrides.get(modelKey) ?? basePercentage.defaultValue;
|
|
186784
|
+
if (rawValue > baseValue) {
|
|
186785
|
+
if (rawValue === constrained.defaultValue) {
|
|
186786
|
+
constrained.overrides.delete(modelKey);
|
|
186787
|
+
} else {
|
|
186788
|
+
constrained.overrides.set(modelKey, rawValue);
|
|
186789
|
+
}
|
|
186790
|
+
} else {
|
|
186791
|
+
warnings.push(makeProjectThresholdWarning(`execute_threshold_percentage.${modelKey}`, PERCENTAGE_THRESHOLD_REASON));
|
|
186792
|
+
}
|
|
186793
|
+
}
|
|
186794
|
+
if (touchedValidEntry) {
|
|
186795
|
+
setMergedPercentageThreshold(args.mergedRaw, constrained);
|
|
186796
|
+
}
|
|
186797
|
+
}
|
|
186798
|
+
}
|
|
186799
|
+
if ("execute_threshold_tokens" in args.projectRaw && isPlainObject2(args.projectRaw.execute_threshold_tokens)) {
|
|
186800
|
+
const projectValue = args.projectRaw.execute_threshold_tokens;
|
|
186801
|
+
const constrained = cloneTokenThresholds(baseTokens);
|
|
186802
|
+
let touchedValidEntry = false;
|
|
186803
|
+
if (isValidTokenThreshold(projectValue.default)) {
|
|
186804
|
+
touchedValidEntry = true;
|
|
186805
|
+
if (baseTokens.defaultValue === undefined) {
|
|
186806
|
+
warnings.push(makeProjectThresholdWarning("execute_threshold_tokens.default", TOKEN_THRESHOLD_INTRODUCTION_REASON));
|
|
186807
|
+
} else if (projectValue.default > baseTokens.defaultValue) {
|
|
186808
|
+
constrained.defaultValue = projectValue.default;
|
|
186809
|
+
} else {
|
|
186810
|
+
warnings.push(makeProjectThresholdWarning("execute_threshold_tokens.default", TOKEN_THRESHOLD_REASON));
|
|
186811
|
+
}
|
|
186812
|
+
}
|
|
186813
|
+
for (const [modelKey, rawValue] of Object.entries(projectValue)) {
|
|
186814
|
+
if (modelKey === "default")
|
|
186815
|
+
continue;
|
|
186816
|
+
if (!isValidTokenThreshold(rawValue))
|
|
186817
|
+
continue;
|
|
186818
|
+
touchedValidEntry = true;
|
|
186819
|
+
const baseValue = baseTokens.overrides.get(modelKey) ?? baseTokens.defaultValue;
|
|
186820
|
+
if (baseValue === undefined) {
|
|
186821
|
+
warnings.push(makeProjectThresholdWarning(`execute_threshold_tokens.${modelKey}`, TOKEN_THRESHOLD_INTRODUCTION_REASON));
|
|
186822
|
+
continue;
|
|
186823
|
+
}
|
|
186824
|
+
if (rawValue > baseValue) {
|
|
186825
|
+
if (rawValue === constrained.defaultValue) {
|
|
186826
|
+
constrained.overrides.delete(modelKey);
|
|
186827
|
+
} else {
|
|
186828
|
+
constrained.overrides.set(modelKey, rawValue);
|
|
186829
|
+
}
|
|
186830
|
+
} else {
|
|
186831
|
+
warnings.push(makeProjectThresholdWarning(`execute_threshold_tokens.${modelKey}`, TOKEN_THRESHOLD_REASON));
|
|
186832
|
+
}
|
|
186833
|
+
}
|
|
186834
|
+
if (touchedValidEntry) {
|
|
186835
|
+
setMergedTokenThreshold(args.mergedRaw, constrained);
|
|
186836
|
+
}
|
|
186837
|
+
}
|
|
186838
|
+
return warnings;
|
|
186839
|
+
}
|
|
186391
186840
|
function normalizeEndpoint(value) {
|
|
186392
186841
|
if (typeof value !== "string")
|
|
186393
186842
|
return;
|
|
@@ -186396,13 +186845,13 @@ function normalizeEndpoint(value) {
|
|
|
186396
186845
|
}
|
|
186397
186846
|
function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
|
|
186398
186847
|
const projectEmbedding = projectRaw.embedding;
|
|
186399
|
-
if (!
|
|
186848
|
+
if (!isPlainObject2(projectEmbedding))
|
|
186400
186849
|
return [];
|
|
186401
186850
|
const redirectsEndpoint = "endpoint" in projectEmbedding;
|
|
186402
186851
|
if (!redirectsEndpoint)
|
|
186403
186852
|
return [];
|
|
186404
186853
|
const userEmbedding = userRaw?.embedding;
|
|
186405
|
-
if (
|
|
186854
|
+
if (isPlainObject2(userEmbedding)) {
|
|
186406
186855
|
const projectEndpoint = normalizeEndpoint(projectEmbedding.endpoint);
|
|
186407
186856
|
const userEndpoint = normalizeEndpoint(userEmbedding.endpoint);
|
|
186408
186857
|
if (projectEndpoint !== undefined && projectEndpoint === userEndpoint) {
|
|
@@ -186413,7 +186862,7 @@ function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
|
|
|
186413
186862
|
if (providesOwnKey)
|
|
186414
186863
|
return [];
|
|
186415
186864
|
const mergedEmbedding = mergedRaw.embedding;
|
|
186416
|
-
if (!
|
|
186865
|
+
if (!isPlainObject2(mergedEmbedding))
|
|
186417
186866
|
return [];
|
|
186418
186867
|
if (!("api_key" in mergedEmbedding))
|
|
186419
186868
|
return [];
|
|
@@ -186424,7 +186873,7 @@ function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
|
|
|
186424
186873
|
}
|
|
186425
186874
|
|
|
186426
186875
|
// src/config/prune-config-leaf.ts
|
|
186427
|
-
function
|
|
186876
|
+
function isPlainObject3(value) {
|
|
186428
186877
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
186429
186878
|
}
|
|
186430
186879
|
function pruneNestedConfigLeaf(block, relativePath) {
|
|
@@ -186435,7 +186884,7 @@ function pruneNestedConfigLeaf(block, relativePath) {
|
|
|
186435
186884
|
for (let i = 0;i < relativePath.length - 1; i++) {
|
|
186436
186885
|
const seg = String(relativePath[i]);
|
|
186437
186886
|
const child = cursor[seg];
|
|
186438
|
-
if (!
|
|
186887
|
+
if (!isPlainObject3(child)) {
|
|
186439
186888
|
if (!(seg in cursor))
|
|
186440
186889
|
return null;
|
|
186441
186890
|
delete cursor[seg];
|
|
@@ -186787,6 +187236,7 @@ function loadPluginConfigDetailed(directory) {
|
|
|
186787
187236
|
const projectLoaded = projectDetected.format !== "none" ? loadConfigFileDetailed(projectDetected.path, "project") : projectLegacyFallback.source ? loadConfigFileDetailed(projectLegacyFallback.source.path, "project") : null;
|
|
186788
187237
|
const allWarnings = [];
|
|
186789
187238
|
let mergedRaw = {};
|
|
187239
|
+
const trustedBaseConfig = parsePluginConfig(userLoaded?.config ?? {});
|
|
186790
187240
|
if (userLegacyFallback.source) {
|
|
186791
187241
|
allWarnings.push(`[user config] reading legacy config from ${userLegacyFallback.source.path} until migration completes; run \`npx @cortexkit/magic-context doctor\` to consolidate into the shared CortexKit location.`);
|
|
186792
187242
|
} else if (legacyUserUnmigrated) {
|
|
@@ -186811,6 +187261,13 @@ function loadPluginConfigDetailed(directory) {
|
|
|
186811
187261
|
for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userLoaded?.config)) {
|
|
186812
187262
|
allWarnings.push(`[project config] ${warning}`);
|
|
186813
187263
|
}
|
|
187264
|
+
for (const warning of constrainProjectThresholdOverrides({
|
|
187265
|
+
mergedRaw,
|
|
187266
|
+
projectRaw,
|
|
187267
|
+
trustedBaseConfig
|
|
187268
|
+
})) {
|
|
187269
|
+
allWarnings.push(`[project config] ${warning}`);
|
|
187270
|
+
}
|
|
186814
187271
|
}
|
|
186815
187272
|
const recoveredTopLevelKeys = [];
|
|
186816
187273
|
const config2 = parsePluginConfig(mergedRaw, recoveredTopLevelKeys);
|
|
@@ -188534,6 +188991,14 @@ function renewLease(db, holderId, leaseKey = DREAMING_LEASE_KEY) {
|
|
|
188534
188991
|
return true;
|
|
188535
188992
|
});
|
|
188536
188993
|
}
|
|
188994
|
+
function runLeaseGuardedWrite(db, holderId, leaseKey, fn) {
|
|
188995
|
+
return runImmediate(db, () => {
|
|
188996
|
+
if (!peekLeaseHolderAndExpiry(db, holderId, leaseKey)) {
|
|
188997
|
+
throw new Error("Dream lease lost before guarded write");
|
|
188998
|
+
}
|
|
188999
|
+
return fn();
|
|
189000
|
+
});
|
|
189001
|
+
}
|
|
188537
189002
|
var LEASE_HEARTBEAT_INTERVAL_MS = 60 * 1000;
|
|
188538
189003
|
function startLeaseHeartbeat(db, holderId, leaseKey, onLost, intervalMs = LEASE_HEARTBEAT_INTERVAL_MS) {
|
|
188539
189004
|
let lost = false;
|
|
@@ -188692,8 +189157,7 @@ class OpenCodeRetrospectiveRawProvider {
|
|
|
188692
189157
|
LEFT JOIN session_meta m ON m.session_id = sp.session_id
|
|
188693
189158
|
WHERE sp.project_path = ? AND sp.harness = 'opencode'
|
|
188694
189159
|
AND COALESCE(m.is_subagent, 0) = 0
|
|
188695
|
-
ORDER BY sp.updated_at
|
|
188696
|
-
LIMIT ?`).all(projectIdentity, RETROSPECTIVE_MAX_SESSIONS_PER_RUN);
|
|
189160
|
+
ORDER BY sp.updated_at ASC, sp.session_id ASC`).all(projectIdentity);
|
|
188697
189161
|
return rows.map((row) => ({
|
|
188698
189162
|
sessionId: row.session_id,
|
|
188699
189163
|
updatedAt: typeof row.updated_at === "number" ? row.updated_at : undefined
|
|
@@ -188718,6 +189182,12 @@ class OpenCodeRetrospectiveRawProvider {
|
|
|
188718
189182
|
return { messages: [], truncated: false };
|
|
188719
189183
|
}
|
|
188720
189184
|
}
|
|
189185
|
+
readOldestMessageTimesSince(sessionIds, sinceMs) {
|
|
189186
|
+
const db = this.resolveDb();
|
|
189187
|
+
if (!db || sessionIds.length === 0)
|
|
189188
|
+
return new Map;
|
|
189189
|
+
return readOpenCodeOldestMessageTimesSince(db, sessionIds, sinceMs);
|
|
189190
|
+
}
|
|
188721
189191
|
readUserMessagesBefore(sessionId, beforeMs, count) {
|
|
188722
189192
|
const db = this.resolveDb();
|
|
188723
189193
|
if (!db)
|
|
@@ -188739,13 +189209,28 @@ class OpenCodeRetrospectiveRawProvider {
|
|
|
188739
189209
|
async function readRetrospectiveScanWindow(provider, projectIdentity, watermarkMs, overlapUserCount, options) {
|
|
188740
189210
|
const maxMessages = options?.maxMessagesPerRun ?? RETROSPECTIVE_MAX_MESSAGES_PER_RUN;
|
|
188741
189211
|
const capPerSession = options?.capPerSession ?? RETROSPECTIVE_MAX_MESSAGES_PER_SESSION;
|
|
188742
|
-
const
|
|
189212
|
+
const sessionLimit = Math.max(1, Math.floor(options?.maxSessionsPerRun ?? RETROSPECTIVE_MAX_SESSIONS_PER_RUN));
|
|
188743
189213
|
try {
|
|
188744
|
-
const
|
|
189214
|
+
const allSessions = await provider.listProjectSessions(projectIdentity);
|
|
189215
|
+
const eligibleSessions = allSessions.map((session, index) => ({ session, index })).filter(({ session }) => (session.updatedAt ?? Number.POSITIVE_INFINITY) > watermarkMs);
|
|
189216
|
+
const oldestBySession = provider.readOldestMessageTimesSince ? await provider.readOldestMessageTimesSince(eligibleSessions.map(({ session }) => session.sessionId), watermarkMs) : null;
|
|
189217
|
+
const sessions = (oldestBySession ? eligibleSessions.filter(({ session }) => oldestBySession.has(session.sessionId)) : eligibleSessions).sort((a, b) => {
|
|
189218
|
+
const aFrontier = oldestBySession?.get(a.session.sessionId);
|
|
189219
|
+
const bFrontier = oldestBySession?.get(b.session.sessionId);
|
|
189220
|
+
if (aFrontier !== undefined || bFrontier !== undefined) {
|
|
189221
|
+
return (aFrontier ?? Number.POSITIVE_INFINITY) - (bFrontier ?? Number.POSITIVE_INFINITY) || a.index - b.index;
|
|
189222
|
+
}
|
|
189223
|
+
const aUpdated = a.session.updatedAt ?? Number.POSITIVE_INFINITY;
|
|
189224
|
+
const bUpdated = b.session.updatedAt ?? Number.POSITIVE_INFINITY;
|
|
189225
|
+
const byUpdated = aUpdated - bUpdated;
|
|
189226
|
+
return byUpdated || a.index - b.index;
|
|
189227
|
+
});
|
|
189228
|
+
const sessionsToRead = sessions.slice(0, sessionLimit).map(({ session }) => session);
|
|
189229
|
+
const firstExcludedSession = sessions[sessionLimit]?.session;
|
|
189230
|
+
const firstExcludedPendingTs = firstExcludedSession ? oldestBySession?.get(firstExcludedSession.sessionId) : undefined;
|
|
188745
189231
|
const sinceReads = [];
|
|
188746
|
-
|
|
188747
|
-
|
|
188748
|
-
sinceReads.push(...await Promise.all(page.map((session) => provider.readUserMessagesSince(session.sessionId, watermarkMs, capPerSession))));
|
|
189232
|
+
if (sessionsToRead.length > 0) {
|
|
189233
|
+
sinceReads.push(...await Promise.all(sessionsToRead.map((session) => provider.readUserMessagesSince(session.sessionId, watermarkMs, capPerSession))));
|
|
188749
189234
|
}
|
|
188750
189235
|
let saturatedFrontier = Number.POSITIVE_INFINITY;
|
|
188751
189236
|
for (const read of sinceReads) {
|
|
@@ -188767,9 +189252,14 @@ async function readRetrospectiveScanWindow(provider, projectIdentity, watermarkM
|
|
|
188767
189252
|
if (firstDropped) {
|
|
188768
189253
|
frontier = Math.min(frontier, firstDropped.ts - 1);
|
|
188769
189254
|
}
|
|
189255
|
+
if (typeof firstExcludedPendingTs === "number") {
|
|
189256
|
+
frontier = Math.min(frontier, firstExcludedPendingTs - 1);
|
|
189257
|
+
} else if (typeof firstExcludedSession?.updatedAt === "number") {
|
|
189258
|
+
frontier = Math.min(frontier, firstExcludedSession.updatedAt - 1);
|
|
189259
|
+
}
|
|
188770
189260
|
maxScannedTs = Math.max(watermarkMs, Math.min(maxScannedTs, frontier));
|
|
188771
189261
|
const keptSessionIds = new Set(keptSince.map((message) => message.sessionId));
|
|
188772
|
-
const overlapSessions =
|
|
189262
|
+
const overlapSessions = sessionsToRead.filter((session) => keptSessionIds.has(session.sessionId));
|
|
188773
189263
|
const overlapBatches = overlapUserCount > 0 && watermarkMs > 0 ? await Promise.all(overlapSessions.map((session) => provider.readUserMessagesBefore(session.sessionId, watermarkMs, overlapUserCount))) : [];
|
|
188774
189264
|
const seen = new Set;
|
|
188775
189265
|
const merged = [];
|
|
@@ -188797,6 +189287,24 @@ function readOpenCodeMessagesSince(db, sessionId, sinceMs, capPerSession) {
|
|
|
188797
189287
|
const kept = truncated ? rows.slice(0, limit) : rows;
|
|
188798
189288
|
return { messages: normalizeOpenCodeRows(db, sessionId, kept), truncated };
|
|
188799
189289
|
}
|
|
189290
|
+
function readOpenCodeOldestMessageTimesSince(db, sessionIds, sinceMs) {
|
|
189291
|
+
const result = new Map;
|
|
189292
|
+
const uniqueIds = Array.from(new Set(sessionIds.filter((id) => id.length > 0)));
|
|
189293
|
+
const chunkSize = 500;
|
|
189294
|
+
for (let i = 0;i < uniqueIds.length; i += chunkSize) {
|
|
189295
|
+
const chunk = uniqueIds.slice(i, i + chunkSize);
|
|
189296
|
+
const placeholders3 = chunk.map(() => "?").join(", ");
|
|
189297
|
+
const rows = db.prepare(`SELECT session_id, MIN(time_created) AS oldest_ts
|
|
189298
|
+
FROM message
|
|
189299
|
+
WHERE time_created > ? AND session_id IN (${placeholders3})
|
|
189300
|
+
GROUP BY session_id`).all(sinceMs, ...chunk);
|
|
189301
|
+
for (const row of rows) {
|
|
189302
|
+
if (typeof row.oldest_ts === "number")
|
|
189303
|
+
result.set(row.session_id, row.oldest_ts);
|
|
189304
|
+
}
|
|
189305
|
+
}
|
|
189306
|
+
return result;
|
|
189307
|
+
}
|
|
188800
189308
|
function readOpenCodeUserMessagesBefore(db, sessionId, beforeMs, count) {
|
|
188801
189309
|
const want = Math.max(1, Math.floor(count));
|
|
188802
189310
|
const window = Math.max(want * 5, 32);
|
|
@@ -189415,6 +189923,39 @@ function hasShareabilitySensitiveText(text) {
|
|
|
189415
189923
|
init_memory();
|
|
189416
189924
|
init_subagent_token_capture();
|
|
189417
189925
|
|
|
189926
|
+
// src/features/magic-context/dreamer/manifest-parser.ts
|
|
189927
|
+
function extractCompleteManifestBody(text, rootName) {
|
|
189928
|
+
const escapedRoot = rootName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
189929
|
+
const rootMatch = new RegExp(`<${escapedRoot}\\b[^>]*>([\\s\\S]*?)<\\/${escapedRoot}>`, "i").exec(text);
|
|
189930
|
+
if (rootMatch)
|
|
189931
|
+
return rootMatch[1];
|
|
189932
|
+
const hasOpenRoot = new RegExp(`<${escapedRoot}\\b`, "i").test(text);
|
|
189933
|
+
const hasCloseRoot = new RegExp(`<\\/${escapedRoot}>`, "i").test(text);
|
|
189934
|
+
if (hasOpenRoot && !hasCloseRoot) {
|
|
189935
|
+
throw new Error(`${rootName} manifest missing closing root tag`);
|
|
189936
|
+
}
|
|
189937
|
+
throw new Error(`${rootName} manifest missing complete root element`);
|
|
189938
|
+
}
|
|
189939
|
+
function assertNoDuplicateManifestIds(ids, rootName) {
|
|
189940
|
+
const seen = new Set;
|
|
189941
|
+
for (const id of ids) {
|
|
189942
|
+
if (seen.has(id))
|
|
189943
|
+
throw new Error(`${rootName} manifest contains duplicate id ${id}`);
|
|
189944
|
+
seen.add(id);
|
|
189945
|
+
}
|
|
189946
|
+
}
|
|
189947
|
+
function assertManifestCoversExactly(ids, expectedIds, rootName) {
|
|
189948
|
+
assertNoDuplicateManifestIds(ids, rootName);
|
|
189949
|
+
for (const id of ids) {
|
|
189950
|
+
if (!expectedIds.has(id))
|
|
189951
|
+
throw new Error(`${rootName} manifest contains unknown id ${id}`);
|
|
189952
|
+
}
|
|
189953
|
+
for (const id of expectedIds) {
|
|
189954
|
+
if (!ids.includes(id))
|
|
189955
|
+
throw new Error(`${rootName} manifest missing id ${id}`);
|
|
189956
|
+
}
|
|
189957
|
+
}
|
|
189958
|
+
|
|
189418
189959
|
// src/features/magic-context/dreamer/classify-prompt.ts
|
|
189419
189960
|
var SCORING_GUIDANCE = `### How to score importance (1-100)
|
|
189420
189961
|
Importance decides which memories survive when the injected memory block is over budget: high scores stay in context, low scores drop first. So the score is only useful if it **discriminates** — if most memories land in the same band, you have not classified them, you have just labelled them.
|
|
@@ -189481,14 +190022,15 @@ ${renderPool(args.memories)}`;
|
|
|
189481
190022
|
var SCOPES = new Set(["project", "ecosystem", "universe"]);
|
|
189482
190023
|
function parseClassifyManifest(text) {
|
|
189483
190024
|
const out = [];
|
|
189484
|
-
|
|
190025
|
+
const body = extractCompleteManifestBody(text, "classify");
|
|
190026
|
+
for (const m of body.matchAll(/<memory\b([^>]*)\/?>/g)) {
|
|
189485
190027
|
const attrs = m[1];
|
|
189486
190028
|
const idMatch = attrs.match(/\bid\s*=\s*"(\d+)"/);
|
|
189487
190029
|
if (!idMatch)
|
|
189488
|
-
|
|
190030
|
+
throw new Error("classify manifest entry missing numeric id");
|
|
189489
190031
|
const id = Number.parseInt(idMatch[1], 10);
|
|
189490
190032
|
if (!Number.isInteger(id))
|
|
189491
|
-
|
|
190033
|
+
throw new Error("classify manifest entry missing numeric id");
|
|
189492
190034
|
const entry = { id };
|
|
189493
190035
|
const impMatch = attrs.match(/\bimportance\s*=\s*"(\d+)"/);
|
|
189494
190036
|
if (impMatch) {
|
|
@@ -189497,18 +190039,23 @@ function parseClassifyManifest(text) {
|
|
|
189497
190039
|
entry.importance = Math.max(1, Math.min(100, imp));
|
|
189498
190040
|
}
|
|
189499
190041
|
const scopeMatch = attrs.match(/\bscope\s*=\s*"([a-z]+)"/i);
|
|
189500
|
-
if (scopeMatch
|
|
189501
|
-
|
|
190042
|
+
if (scopeMatch) {
|
|
190043
|
+
const scope = scopeMatch[1].toLowerCase();
|
|
190044
|
+
if (!SCOPES.has(scope))
|
|
190045
|
+
throw new Error(`classify manifest invalid scope ${scope}`);
|
|
190046
|
+
entry.scope = scope;
|
|
189502
190047
|
}
|
|
189503
190048
|
const shareMatch = attrs.match(/\bshareable\s*=\s*"(true|false|1|0)"/i);
|
|
189504
190049
|
if (shareMatch) {
|
|
189505
190050
|
const v = shareMatch[1].toLowerCase();
|
|
189506
190051
|
entry.shareable = v === "true" || v === "1";
|
|
189507
190052
|
}
|
|
189508
|
-
if (entry.importance
|
|
189509
|
-
|
|
190053
|
+
if (entry.importance === undefined && !entry.scope && entry.shareable === undefined) {
|
|
190054
|
+
throw new Error(`classify manifest entry ${id} missing classification fields`);
|
|
189510
190055
|
}
|
|
190056
|
+
out.push(entry);
|
|
189511
190057
|
}
|
|
190058
|
+
assertNoDuplicateManifestIds(out.map((entry) => entry.id), "classify");
|
|
189512
190059
|
return out;
|
|
189513
190060
|
}
|
|
189514
190061
|
|
|
@@ -189643,9 +190190,13 @@ async function classifyOneChunk(args, chunk, anchors, sliceMs, signal) {
|
|
|
189643
190190
|
});
|
|
189644
190191
|
},
|
|
189645
190192
|
validateOutput: (messages) => {
|
|
190193
|
+
if (hasLengthCappedOutput(messages)) {
|
|
190194
|
+
throw new Error("classify returned length-capped output");
|
|
190195
|
+
}
|
|
189646
190196
|
const text = extractLatestAssistantText(messages);
|
|
189647
190197
|
if (!text)
|
|
189648
190198
|
throw new Error("classify returned no output");
|
|
190199
|
+
parseClassifyManifest(text);
|
|
189649
190200
|
return text;
|
|
189650
190201
|
}
|
|
189651
190202
|
});
|
|
@@ -189671,17 +190222,13 @@ async function classifyOneChunk(args, chunk, anchors, sliceMs, signal) {
|
|
|
189671
190222
|
}
|
|
189672
190223
|
function applyClassifications(args, chunk, manifestText) {
|
|
189673
190224
|
const byId = new Map(chunk.map((m) => [m.id, m]));
|
|
189674
|
-
const parsed = parseClassifyManifest(manifestText)
|
|
190225
|
+
const parsed = parseClassifyManifest(manifestText);
|
|
190226
|
+
assertManifestCoversExactly(parsed.map((entry) => entry.id), new Set(byId.keys()), "classify");
|
|
189675
190227
|
if (parsed.length === 0)
|
|
189676
190228
|
return { classified: 0, changed: 0 };
|
|
189677
190229
|
let classified = 0;
|
|
189678
190230
|
let changed = 0;
|
|
189679
|
-
|
|
189680
|
-
args.db.transaction(() => {
|
|
189681
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
189682
|
-
leaseLost = true;
|
|
189683
|
-
return;
|
|
189684
|
-
}
|
|
190231
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
189685
190232
|
for (const p of parsed) {
|
|
189686
190233
|
const memory = byId.get(p.id);
|
|
189687
190234
|
if (!memory)
|
|
@@ -189696,9 +190243,7 @@ function applyClassifications(args, chunk, manifestText) {
|
|
|
189696
190243
|
if (didChange)
|
|
189697
190244
|
changed += 1;
|
|
189698
190245
|
}
|
|
189699
|
-
})
|
|
189700
|
-
if (leaseLost)
|
|
189701
|
-
throw new Error("Dream lease lost during classify commit");
|
|
190246
|
+
});
|
|
189702
190247
|
return { classified, changed };
|
|
189703
190248
|
}
|
|
189704
190249
|
function recordInvocation(args, startedAt, params) {
|
|
@@ -189748,9 +190293,11 @@ var SMART_NOTE_CHECK_LIVENESS_RECHECK_MS = 24 * 60 * 60 * 1000;
|
|
|
189748
190293
|
|
|
189749
190294
|
class SmartNoteNetworkError extends Error {
|
|
189750
190295
|
isSmartNoteNetworkError = true;
|
|
189751
|
-
|
|
190296
|
+
terminal;
|
|
190297
|
+
constructor(message, options = {}) {
|
|
189752
190298
|
super(message);
|
|
189753
190299
|
this.name = "SmartNoteNetworkError";
|
|
190300
|
+
this.terminal = options.terminal ?? false;
|
|
189754
190301
|
}
|
|
189755
190302
|
}
|
|
189756
190303
|
|
|
@@ -189764,6 +190311,9 @@ class SmartNoteSecurityError extends Error {
|
|
|
189764
190311
|
function isSmartNoteNetworkError(error51) {
|
|
189765
190312
|
return error51 instanceof SmartNoteNetworkError || error51 instanceof Error && (error51.name === "SmartNoteNetworkError" || error51.message.includes("SmartNoteNetworkError") || error51.message.includes("SMART_NOTE_NETWORK"));
|
|
189766
190313
|
}
|
|
190314
|
+
function isTerminalSmartNoteNetworkError(error51) {
|
|
190315
|
+
return error51 instanceof SmartNoteNetworkError && error51.terminal;
|
|
190316
|
+
}
|
|
189767
190317
|
function parseSmartNoteManifest(json2) {
|
|
189768
190318
|
if (!json2)
|
|
189769
190319
|
return { capabilities: [] };
|
|
@@ -189793,6 +190343,7 @@ function stringArray(value) {
|
|
|
189793
190343
|
var DNS_TIMEOUT_MS = 3000;
|
|
189794
190344
|
var DEFAULT_HTTP_TIMEOUT_MS = 5000;
|
|
189795
190345
|
var DEFAULT_HTTP_BODY_LIMIT_BYTES = 64 * 1024;
|
|
190346
|
+
var MAX_HTTP_ADDRESS_CANDIDATES = 4;
|
|
189796
190347
|
var defaultResolver = {
|
|
189797
190348
|
async lookup(hostname3, signal) {
|
|
189798
190349
|
return await withAbortAndTimeout(dnsLookup(hostname3, { all: true, verbatim: true }), signal, DNS_TIMEOUT_MS, "DNS lookup timed out").then((rows) => rows.filter((row) => row.family === 4 || row.family === 6).map((row) => ({ address: row.address, family: row.family })));
|
|
@@ -189829,18 +190380,21 @@ async function guardedSmartNoteHttpGet(input, options) {
|
|
|
189829
190380
|
});
|
|
189830
190381
|
const timeoutMs = options.timeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;
|
|
189831
190382
|
const bodyLimitBytes = options.bodyLimitBytes ?? DEFAULT_HTTP_BODY_LIMIT_BYTES;
|
|
190383
|
+
const requestAddress = options.requestAddress ?? requestValidatedAddress;
|
|
190384
|
+
const candidates = validation.addresses.slice(0, MAX_HTTP_ADDRESS_CANDIDATES);
|
|
189832
190385
|
let lastError;
|
|
189833
|
-
for (const candidate of
|
|
190386
|
+
for (const candidate of candidates) {
|
|
189834
190387
|
try {
|
|
189835
|
-
return await
|
|
190388
|
+
return await requestAddress(validation, candidate, {
|
|
189836
190389
|
signal: options.signal,
|
|
189837
190390
|
timeoutMs,
|
|
189838
190391
|
bodyLimitBytes
|
|
189839
190392
|
});
|
|
189840
190393
|
} catch (error51) {
|
|
189841
190394
|
lastError = error51;
|
|
189842
|
-
if (error51 instanceof SmartNoteSecurityError || options.signal.aborted)
|
|
190395
|
+
if (error51 instanceof SmartNoteSecurityError || options.signal.aborted || isTerminalSmartNoteNetworkError(error51)) {
|
|
189843
190396
|
throw error51;
|
|
190397
|
+
}
|
|
189844
190398
|
}
|
|
189845
190399
|
}
|
|
189846
190400
|
throw toNetworkError(lastError, "all validated addresses failed");
|
|
@@ -189910,7 +190464,9 @@ function requestValidatedAddress(validation, candidate, options) {
|
|
|
189910
190464
|
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
189911
190465
|
bytes += buf.byteLength;
|
|
189912
190466
|
if (bytes > options.bodyLimitBytes) {
|
|
189913
|
-
|
|
190467
|
+
reject(new SmartNoteNetworkError("SMART_NOTE_NETWORK: response body too large", { terminal: true }));
|
|
190468
|
+
response.destroy();
|
|
190469
|
+
request2.destroy();
|
|
189914
190470
|
return;
|
|
189915
190471
|
}
|
|
189916
190472
|
chunks.push(buf);
|
|
@@ -189927,10 +190483,16 @@ function requestValidatedAddress(validation, candidate, options) {
|
|
|
189927
190483
|
resolve4({ status, body: Buffer.concat(chunks).toString("utf8") });
|
|
189928
190484
|
});
|
|
189929
190485
|
});
|
|
189930
|
-
const onAbort = () =>
|
|
190486
|
+
const onAbort = () => {
|
|
190487
|
+
reject(new SmartNoteNetworkError("SMART_NOTE_NETWORK: aborted"));
|
|
190488
|
+
request2.destroy();
|
|
190489
|
+
};
|
|
189931
190490
|
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
189932
190491
|
request2.on("timeout", () => {
|
|
189933
|
-
|
|
190492
|
+
reject(new SmartNoteNetworkError("SMART_NOTE_NETWORK: request timed out", {
|
|
190493
|
+
terminal: true
|
|
190494
|
+
}));
|
|
190495
|
+
request2.destroy();
|
|
189934
190496
|
});
|
|
189935
190497
|
request2.on("error", (error51) => {
|
|
189936
190498
|
options.signal.removeEventListener("abort", onAbort);
|
|
@@ -190251,6 +190813,20 @@ function withSandboxLock(fn) {
|
|
|
190251
190813
|
var DEFAULT_TIMEOUT_MS = 2000;
|
|
190252
190814
|
var DEFAULT_HEAP_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
190253
190815
|
var DEFAULT_STACK_LIMIT_BYTES = 512 * 1024;
|
|
190816
|
+
function resolveCapabilitiesForRun(options, signal) {
|
|
190817
|
+
if (options.capabilityFactory) {
|
|
190818
|
+
return options.capabilityFactory(signal);
|
|
190819
|
+
}
|
|
190820
|
+
if (options.capabilities) {
|
|
190821
|
+
return options.capabilities;
|
|
190822
|
+
}
|
|
190823
|
+
throw new Error("smart-note check requires capabilities");
|
|
190824
|
+
}
|
|
190825
|
+
function throwIfRunAborted(signal) {
|
|
190826
|
+
if (signal.aborted) {
|
|
190827
|
+
throw signal.reason ?? new Error("smart-note check aborted");
|
|
190828
|
+
}
|
|
190829
|
+
}
|
|
190254
190830
|
async function runCompiledSmartNoteCheck(options) {
|
|
190255
190831
|
return withSandboxLock(() => runCompiledSmartNoteCheckLocked(options));
|
|
190256
190832
|
}
|
|
@@ -190258,17 +190834,24 @@ async function runCompiledSmartNoteCheckLocked(options) {
|
|
|
190258
190834
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
190259
190835
|
const controller = new AbortController;
|
|
190260
190836
|
const externalAbort = () => controller.abort(options.signal?.reason);
|
|
190261
|
-
options.signal?.
|
|
190837
|
+
if (options.signal?.aborted) {
|
|
190838
|
+
externalAbort();
|
|
190839
|
+
} else {
|
|
190840
|
+
options.signal?.addEventListener("abort", externalAbort, { once: true });
|
|
190841
|
+
}
|
|
190262
190842
|
const timer = setTimeout(() => controller.abort(new Error("smart-note check timed out")), timeoutMs);
|
|
190263
190843
|
try {
|
|
190844
|
+
throwIfRunAborted(controller.signal);
|
|
190845
|
+
const capabilities = resolveCapabilitiesForRun(options, controller.signal);
|
|
190264
190846
|
const deadline = Date.now() + timeoutMs;
|
|
190265
190847
|
const quickjs = await getAsyncModule();
|
|
190848
|
+
throwIfRunAborted(controller.signal);
|
|
190266
190849
|
const context = quickjs.newContext();
|
|
190267
190850
|
try {
|
|
190268
190851
|
context.runtime.setMemoryLimit(options.heapLimitBytes ?? DEFAULT_HEAP_LIMIT_BYTES);
|
|
190269
190852
|
context.runtime.setMaxStackSize(options.stackLimitBytes ?? DEFAULT_STACK_LIMIT_BYTES);
|
|
190270
190853
|
context.runtime.setInterruptHandler(() => controller.signal.aborted || Date.now() > deadline);
|
|
190271
|
-
installCapabilityObject(context,
|
|
190854
|
+
installCapabilityObject(context, capabilities);
|
|
190272
190855
|
disableAmbientDynamicCode(context);
|
|
190273
190856
|
const result = await evalCheck(context, options.compiledCheck);
|
|
190274
190857
|
const checkResult = result;
|
|
@@ -190421,6 +191004,7 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
190421
191004
|
}
|
|
190422
191005
|
}, {
|
|
190423
191006
|
timeoutMs: remainingMs,
|
|
191007
|
+
signal: args.signal,
|
|
190424
191008
|
fallbackModels: args.fallbackModels,
|
|
190425
191009
|
callContext: "dreamer:smart-note-compiler",
|
|
190426
191010
|
fetchOutput: async () => {
|
|
@@ -190445,7 +191029,8 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
190445
191029
|
}
|
|
190446
191030
|
const dryRun = await runCompiledSmartNoteCheck({
|
|
190447
191031
|
compiledCheck,
|
|
190448
|
-
|
|
191032
|
+
capabilityFactory: args.capabilityFactory,
|
|
191033
|
+
signal: args.signal,
|
|
190449
191034
|
timeoutMs: 2000
|
|
190450
191035
|
});
|
|
190451
191036
|
if (!dryRun.ok) {
|
|
@@ -190598,7 +191183,9 @@ function nextSmartNoteCheckDueAt(cron, options = {}) {
|
|
|
190598
191183
|
const rawNext = cron?.trim() ? nextDueAtMs(cron, now) : null;
|
|
190599
191184
|
const rawDelta = rawNext ? rawNext - now : SMART_NOTE_CHECK_DEFAULT_INTERVAL_MS;
|
|
190600
191185
|
const clamped = Math.min(ceilingMs, Math.max(floorMs, rawDelta));
|
|
190601
|
-
|
|
191186
|
+
const jittered = clamped + deterministicJitterMs(clamped, options.noteId, options.hash);
|
|
191187
|
+
const bounded = Math.min(ceilingMs, Math.max(floorMs, jittered));
|
|
191188
|
+
return now + bounded;
|
|
190602
191189
|
}
|
|
190603
191190
|
function deterministicJitterMs(intervalMs, noteId, hash2) {
|
|
190604
191191
|
const max = Math.min(60000, Math.floor(intervalMs * 0.1));
|
|
@@ -190768,9 +191355,9 @@ async function runDueCompiledSmartNoteChecks(args) {
|
|
|
190768
191355
|
try {
|
|
190769
191356
|
const result = await runCompiledSmartNoteCheck({
|
|
190770
191357
|
compiledCheck: note.compiledCheck,
|
|
190771
|
-
|
|
191358
|
+
capabilityFactory: (signal) => createSmartNoteCapabilities({
|
|
190772
191359
|
projectRoot: args.projectRoot,
|
|
190773
|
-
signal
|
|
191360
|
+
signal
|
|
190774
191361
|
}),
|
|
190775
191362
|
signal: controller.signal,
|
|
190776
191363
|
timeoutMs: Math.min(2000, remaining)
|
|
@@ -190835,6 +191422,24 @@ var MAX_COMPILE_PER_RUN = 5;
|
|
|
190835
191422
|
var MAX_FALLBACK_PER_RUN = 3;
|
|
190836
191423
|
var MAX_COMPILATION_FAILURES = 3;
|
|
190837
191424
|
var SMART_NOTE_CONFIRMATION_SYSTEM_PROMPT = 'You are a no-tool smart-note confirmation evaluator. Output only JSON shaped as {"met": boolean}. Return true only when the supplied text alone proves the condition is satisfied.';
|
|
191425
|
+
function createPromptAbortSignal(parent, timeoutMs, timeoutMessage) {
|
|
191426
|
+
const controller = new AbortController;
|
|
191427
|
+
const abortFromParent = () => {
|
|
191428
|
+
controller.abort(parent.reason ?? new Error("smart-note prompt aborted by lease loss"));
|
|
191429
|
+
};
|
|
191430
|
+
if (parent.aborted)
|
|
191431
|
+
abortFromParent();
|
|
191432
|
+
else
|
|
191433
|
+
parent.addEventListener("abort", abortFromParent, { once: true });
|
|
191434
|
+
const timer = setTimeout(() => controller.abort(new Error(timeoutMessage)), timeoutMs);
|
|
191435
|
+
return {
|
|
191436
|
+
signal: controller.signal,
|
|
191437
|
+
cleanup: () => {
|
|
191438
|
+
clearTimeout(timer);
|
|
191439
|
+
parent.removeEventListener("abort", abortFromParent);
|
|
191440
|
+
}
|
|
191441
|
+
};
|
|
191442
|
+
}
|
|
190838
191443
|
async function evaluateSmartNotes(args) {
|
|
190839
191444
|
const projectRoot = args.sessionDirectory ?? args.projectIdentity;
|
|
190840
191445
|
const pendingAtStart = getPendingSmartNotes(args.db, args.projectIdentity).length;
|
|
@@ -190843,6 +191448,7 @@ async function evaluateSmartNotes(args) {
|
|
|
190843
191448
|
return { surfaced: 0, pending: 0, ran: false };
|
|
190844
191449
|
}
|
|
190845
191450
|
let leaseLost = false;
|
|
191451
|
+
const leaseAbortController = new AbortController;
|
|
190846
191452
|
const leaseHeld = () => !leaseLost && peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey);
|
|
190847
191453
|
const assertLeaseHeld = (phase) => {
|
|
190848
191454
|
if (!leaseHeld()) {
|
|
@@ -190852,6 +191458,7 @@ async function evaluateSmartNotes(args) {
|
|
|
190852
191458
|
};
|
|
190853
191459
|
const heartbeat = startLeaseHeartbeat(args.db, args.holderId, args.leaseKey, () => {
|
|
190854
191460
|
leaseLost = true;
|
|
191461
|
+
leaseAbortController.abort(new Error("Dream lease lost during smart notes"));
|
|
190855
191462
|
log("[dreamer] smart notes: lease lost — aborting");
|
|
190856
191463
|
args.onLeaseLost?.("smart notes");
|
|
190857
191464
|
});
|
|
@@ -190874,7 +191481,7 @@ async function evaluateSmartNotes(args) {
|
|
|
190874
191481
|
break;
|
|
190875
191482
|
assertLeaseHeld("compile start");
|
|
190876
191483
|
didWork = true;
|
|
190877
|
-
const compiled = await compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld);
|
|
191484
|
+
const compiled = await compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld, leaseAbortController.signal);
|
|
190878
191485
|
if (compiled)
|
|
190879
191486
|
surfaced += 1;
|
|
190880
191487
|
}
|
|
@@ -190894,7 +191501,7 @@ async function evaluateSmartNotes(args) {
|
|
|
190894
191501
|
break;
|
|
190895
191502
|
assertLeaseHeld("fallback start");
|
|
190896
191503
|
didWork = true;
|
|
190897
|
-
const met = await confirmReadOnly(args, note.id, note.content, note.surfaceCondition);
|
|
191504
|
+
const met = await confirmReadOnly(args, note.id, note.content, note.surfaceCondition, leaseAbortController.signal);
|
|
190898
191505
|
const now = Date.now();
|
|
190899
191506
|
assertLeaseHeld("fallback commit");
|
|
190900
191507
|
commitSmartNoteState(args.db, {
|
|
@@ -190920,9 +191527,8 @@ async function evaluateSmartNotes(args) {
|
|
|
190920
191527
|
heartbeat.stop();
|
|
190921
191528
|
}
|
|
190922
191529
|
}
|
|
190923
|
-
async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld) {
|
|
190924
|
-
const
|
|
190925
|
-
const timer = setTimeout(() => controller.abort(new Error("smart-note compile deadline")), Math.max(1000, args.deadline - Date.now()));
|
|
191530
|
+
async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld, leaseSignal) {
|
|
191531
|
+
const promptSignal = createPromptAbortSignal(leaseSignal, Math.max(1000, args.deadline - Date.now()), "smart-note compile deadline");
|
|
190926
191532
|
try {
|
|
190927
191533
|
const result = await compileSmartNoteCheck({
|
|
190928
191534
|
client: args.client,
|
|
@@ -190931,7 +191537,8 @@ async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld)
|
|
|
190931
191537
|
sessionDirectory: args.sessionDirectory,
|
|
190932
191538
|
projectIdentity: args.projectIdentity,
|
|
190933
191539
|
note,
|
|
190934
|
-
|
|
191540
|
+
capabilityFactory: (signal) => createSmartNoteCapabilities({ projectRoot, signal }),
|
|
191541
|
+
signal: promptSignal.signal,
|
|
190935
191542
|
deadline: args.deadline,
|
|
190936
191543
|
model: args.model,
|
|
190937
191544
|
fallbackModels: args.fallbackModels
|
|
@@ -190976,7 +191583,7 @@ async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld)
|
|
|
190976
191583
|
});
|
|
190977
191584
|
return result.dryRun.met;
|
|
190978
191585
|
} finally {
|
|
190979
|
-
|
|
191586
|
+
promptSignal.cleanup();
|
|
190980
191587
|
}
|
|
190981
191588
|
}
|
|
190982
191589
|
async function runLivenessCheck(args, note, projectRoot, assertLeaseHeld, leaseHeld) {
|
|
@@ -190987,7 +191594,7 @@ async function runLivenessCheck(args, note, projectRoot, assertLeaseHeld, leaseH
|
|
|
190987
191594
|
try {
|
|
190988
191595
|
const result = await runCompiledSmartNoteCheck({
|
|
190989
191596
|
compiledCheck: note.compiledCheck,
|
|
190990
|
-
|
|
191597
|
+
capabilityFactory: (signal) => createSmartNoteCapabilities({ projectRoot, signal }),
|
|
190991
191598
|
signal: controller.signal,
|
|
190992
191599
|
timeoutMs: 2000
|
|
190993
191600
|
});
|
|
@@ -191016,7 +191623,7 @@ async function runLivenessCheck(args, note, projectRoot, assertLeaseHeld, leaseH
|
|
|
191016
191623
|
clearTimeout(timer);
|
|
191017
191624
|
}
|
|
191018
191625
|
}
|
|
191019
|
-
async function confirmReadOnly(args, noteId, content, surfaceCondition) {
|
|
191626
|
+
async function confirmReadOnly(args, noteId, content, surfaceCondition, leaseSignal) {
|
|
191020
191627
|
let childSessionId = null;
|
|
191021
191628
|
const startedAt = Date.now();
|
|
191022
191629
|
let invocationRecorded = false;
|
|
@@ -191059,42 +191666,49 @@ Note content: ${JSON.stringify(content)}
|
|
|
191059
191666
|
Surface condition: ${JSON.stringify(surfaceCondition ?? "")}
|
|
191060
191667
|
|
|
191061
191668
|
Output exactly JSON: {"met": false}`;
|
|
191062
|
-
const
|
|
191063
|
-
|
|
191064
|
-
|
|
191065
|
-
|
|
191066
|
-
|
|
191067
|
-
|
|
191068
|
-
|
|
191069
|
-
|
|
191070
|
-
|
|
191071
|
-
|
|
191072
|
-
|
|
191073
|
-
|
|
191074
|
-
|
|
191075
|
-
|
|
191076
|
-
|
|
191077
|
-
|
|
191078
|
-
|
|
191079
|
-
|
|
191080
|
-
|
|
191081
|
-
|
|
191082
|
-
|
|
191083
|
-
|
|
191084
|
-
|
|
191085
|
-
|
|
191086
|
-
|
|
191087
|
-
|
|
191088
|
-
|
|
191089
|
-
|
|
191090
|
-
|
|
191091
|
-
|
|
191092
|
-
|
|
191093
|
-
|
|
191094
|
-
|
|
191095
|
-
|
|
191096
|
-
|
|
191097
|
-
|
|
191669
|
+
const promptSignal = createPromptAbortSignal(leaseSignal, Math.max(1000, args.deadline - Date.now()), "smart-note confirmation deadline");
|
|
191670
|
+
let run;
|
|
191671
|
+
try {
|
|
191672
|
+
run = await promptSyncWithValidatedOutputRetry(args.client, {
|
|
191673
|
+
path: { id: childSessionId },
|
|
191674
|
+
query: { directory: args.sessionDirectory ?? args.projectIdentity },
|
|
191675
|
+
body: {
|
|
191676
|
+
agent: SMART_NOTE_COMPILER_AGENT,
|
|
191677
|
+
system: SMART_NOTE_CONFIRMATION_SYSTEM_PROMPT,
|
|
191678
|
+
...modelBodyField(args.model),
|
|
191679
|
+
parts: [{ type: "text", text: prompt, synthetic: true }]
|
|
191680
|
+
}
|
|
191681
|
+
}, {
|
|
191682
|
+
timeoutMs: Math.max(1000, args.deadline - Date.now()),
|
|
191683
|
+
signal: promptSignal.signal,
|
|
191684
|
+
fallbackModels: args.fallbackModels,
|
|
191685
|
+
callContext: "dreamer:smart-note-read-only-confirm",
|
|
191686
|
+
fetchOutput: async () => {
|
|
191687
|
+
const messagesResponse = await args.client.session.messages({
|
|
191688
|
+
path: { id: childSessionId },
|
|
191689
|
+
query: {
|
|
191690
|
+
directory: args.sessionDirectory ?? args.projectIdentity,
|
|
191691
|
+
limit: 20
|
|
191692
|
+
}
|
|
191693
|
+
});
|
|
191694
|
+
return normalizeSDKResponse(messagesResponse, [], {
|
|
191695
|
+
preferResponseOnMissingData: true
|
|
191696
|
+
});
|
|
191697
|
+
},
|
|
191698
|
+
validateOutput: (messages) => {
|
|
191699
|
+
const text = extractLatestAssistantText(messages) ?? "";
|
|
191700
|
+
const match = text.match(/\{[\s\S]*\}/);
|
|
191701
|
+
if (!match)
|
|
191702
|
+
throw new Error("confirmation evaluator returned no JSON");
|
|
191703
|
+
const parsed = JSON.parse(match[0]);
|
|
191704
|
+
if (typeof parsed.met !== "boolean")
|
|
191705
|
+
throw new Error("confirmation met missing");
|
|
191706
|
+
return parsed.met;
|
|
191707
|
+
}
|
|
191708
|
+
});
|
|
191709
|
+
} finally {
|
|
191710
|
+
promptSignal.cleanup();
|
|
191711
|
+
}
|
|
191098
191712
|
recordInvocation2({ status: "completed", messages: run.output });
|
|
191099
191713
|
return run.validated;
|
|
191100
191714
|
} catch (error51) {
|
|
@@ -191313,19 +191927,21 @@ ${list}
|
|
|
191313
191927
|
}
|
|
191314
191928
|
function parseMapMemoriesManifest(text) {
|
|
191315
191929
|
const out = [];
|
|
191316
|
-
|
|
191930
|
+
const body = extractCompleteManifestBody(text, "mappings");
|
|
191931
|
+
for (const m of body.matchAll(/<memory\b([^>]*)\/?>/g)) {
|
|
191317
191932
|
const attrs = m[1];
|
|
191318
191933
|
const idMatch = attrs.match(/\bid\s*=\s*"(\d+)"/);
|
|
191319
191934
|
if (!idMatch)
|
|
191320
|
-
|
|
191935
|
+
throw new Error("mappings manifest entry missing numeric id");
|
|
191321
191936
|
const id = Number.parseInt(idMatch[1], 10);
|
|
191322
191937
|
if (!Number.isInteger(id))
|
|
191323
|
-
|
|
191938
|
+
throw new Error("mappings manifest entry missing numeric id");
|
|
191324
191939
|
const independent = /\bindependent\s*=\s*"(?:true|1)"/i.test(attrs);
|
|
191325
191940
|
const filesMatch = attrs.match(/\bfiles\s*=\s*"([^"]*)"/);
|
|
191326
191941
|
const files = filesMatch ? filesMatch[1].split(",").map((f) => f.trim()).filter(Boolean) : [];
|
|
191327
191942
|
out.push({ id, files, independent: independent || files.length === 0 });
|
|
191328
191943
|
}
|
|
191944
|
+
assertNoDuplicateManifestIds(out.map((entry) => entry.id), "mappings");
|
|
191329
191945
|
return out;
|
|
191330
191946
|
}
|
|
191331
191947
|
|
|
@@ -191414,9 +192030,13 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
191414
192030
|
});
|
|
191415
192031
|
},
|
|
191416
192032
|
validateOutput: (messages) => {
|
|
192033
|
+
if (hasLengthCappedOutput(messages)) {
|
|
192034
|
+
throw new Error("map-memories returned length-capped output");
|
|
192035
|
+
}
|
|
191417
192036
|
const text = extractLatestAssistantText(messages);
|
|
191418
192037
|
if (!text)
|
|
191419
192038
|
throw new Error("map-memories returned no output");
|
|
192039
|
+
parseMapMemoriesManifest(text);
|
|
191420
192040
|
return text;
|
|
191421
192041
|
}
|
|
191422
192042
|
});
|
|
@@ -191442,7 +192062,8 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
191442
192062
|
}
|
|
191443
192063
|
async function applyBatchMappings(args, batch, manifestText) {
|
|
191444
192064
|
const batchIds = new Set(batch.map((m) => m.id));
|
|
191445
|
-
const parsed = parseMapMemoriesManifest(manifestText)
|
|
192065
|
+
const parsed = parseMapMemoriesManifest(manifestText);
|
|
192066
|
+
assertManifestCoversExactly(parsed.map((entry) => entry.id), batchIds, "mappings");
|
|
191446
192067
|
if (parsed.length === 0)
|
|
191447
192068
|
return { mapped: 0, independent: 0 };
|
|
191448
192069
|
const planned = [];
|
|
@@ -191464,12 +192085,7 @@ async function applyBatchMappings(args, batch, manifestText) {
|
|
|
191464
192085
|
const now = Date.now();
|
|
191465
192086
|
let mapped = 0;
|
|
191466
192087
|
let independent = 0;
|
|
191467
|
-
|
|
191468
|
-
args.db.transaction(() => {
|
|
191469
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
191470
|
-
leaseLost = true;
|
|
191471
|
-
return;
|
|
191472
|
-
}
|
|
192088
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
191473
192089
|
for (const item of planned) {
|
|
191474
192090
|
recordMemoryMapping(args.db, item.id, item.files, now);
|
|
191475
192091
|
if (item.independent)
|
|
@@ -191477,9 +192093,7 @@ async function applyBatchMappings(args, batch, manifestText) {
|
|
|
191477
192093
|
else
|
|
191478
192094
|
mapped += 1;
|
|
191479
192095
|
}
|
|
191480
|
-
})
|
|
191481
|
-
if (leaseLost)
|
|
191482
|
-
throw new Error("Dream lease lost during map-memories commit");
|
|
192096
|
+
});
|
|
191483
192097
|
return { mapped, independent };
|
|
191484
192098
|
}
|
|
191485
192099
|
function recordInvocation2(args, startedAt, params) {
|
|
@@ -191710,11 +192324,7 @@ async function promotePrimers(args) {
|
|
|
191710
192324
|
activePrimers: primers,
|
|
191711
192325
|
threshold: PRIMER_CLUSTER_THRESHOLD
|
|
191712
192326
|
});
|
|
191713
|
-
args.db.
|
|
191714
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
191715
|
-
leaseLost = true;
|
|
191716
|
-
return;
|
|
191717
|
-
}
|
|
192327
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
191718
192328
|
for (const cluster of clusters) {
|
|
191719
192329
|
if (cluster.candidates.length === 0)
|
|
191720
192330
|
continue;
|
|
@@ -191745,7 +192355,7 @@ async function promotePrimers(args) {
|
|
|
191745
192355
|
});
|
|
191746
192356
|
result.promoted += 1;
|
|
191747
192357
|
}
|
|
191748
|
-
})
|
|
192358
|
+
});
|
|
191749
192359
|
if (leaseLost)
|
|
191750
192360
|
throw new Error("Dream lease lost during promote-primers commit");
|
|
191751
192361
|
log(`[dreamer] primers: candidates=${result.candidates} promoted=${result.promoted} updated=${result.updated}`);
|
|
@@ -192023,16 +192633,9 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
|
|
|
192023
192633
|
log(`[dreamer] refresh-primers: primer #${primer.id} answer not committed (no investigation tool calls)`);
|
|
192024
192634
|
return false;
|
|
192025
192635
|
}
|
|
192026
|
-
|
|
192027
|
-
args.db.transaction(() => {
|
|
192028
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
192029
|
-
leaseLost = true;
|
|
192030
|
-
return;
|
|
192031
|
-
}
|
|
192636
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
192032
192637
|
updatePrimerAnswer(args.db, primer.id, answer);
|
|
192033
|
-
})
|
|
192034
|
-
if (leaseLost)
|
|
192035
|
-
throw new Error("Dream lease lost during refresh-primers commit");
|
|
192638
|
+
});
|
|
192036
192639
|
return true;
|
|
192037
192640
|
} catch (error51) {
|
|
192038
192641
|
const desc = describeError(error51);
|
|
@@ -192301,7 +192904,7 @@ async function partitionVerifyScope(args) {
|
|
|
192301
192904
|
continue;
|
|
192302
192905
|
}
|
|
192303
192906
|
const files = v?.files ?? [];
|
|
192304
|
-
const needs = files.some((file2) => !verificationFileExists(gitRoot, file2) || uncommitted.has(file2) || (changeTimes.get(file2) ?? 0)
|
|
192907
|
+
const needs = files.some((file2) => !verificationFileExists(gitRoot, file2) || uncommitted.has(file2) || (changeTimes.get(file2) ?? 0) >= verifiedAt - 1000);
|
|
192305
192908
|
if (needs)
|
|
192306
192909
|
inScope.push(toPrompt(m));
|
|
192307
192910
|
else
|
|
@@ -192367,22 +192970,26 @@ function filesOf(s) {
|
|
|
192367
192970
|
}
|
|
192368
192971
|
function parseVerifyManifest(text) {
|
|
192369
192972
|
const out = { verified: [], updated: [], archived: [] };
|
|
192370
|
-
|
|
192973
|
+
const body = extractCompleteManifestBody(text, "verify");
|
|
192974
|
+
for (const m of body.matchAll(/<verified\b([^>]*)\/?>/g)) {
|
|
192371
192975
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
192372
|
-
if (Number.isInteger(id))
|
|
192373
|
-
|
|
192976
|
+
if (!Number.isInteger(id))
|
|
192977
|
+
throw new Error("verify manifest entry missing numeric id");
|
|
192978
|
+
out.verified.push({ id, files: filesOf(m[1]) });
|
|
192374
192979
|
}
|
|
192375
|
-
for (const m of
|
|
192980
|
+
for (const m of body.matchAll(/<update\b([^>]*?)(?:\/>|>([\s\S]*?)<\/update>)/g)) {
|
|
192376
192981
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
192377
|
-
if (Number.isInteger(id))
|
|
192378
|
-
|
|
192379
|
-
}
|
|
192982
|
+
if (!Number.isInteger(id))
|
|
192983
|
+
throw new Error("verify manifest entry missing numeric id");
|
|
192984
|
+
out.updated.push({ id, files: filesOf(m[1]), content: (m[2] ?? "").trim() });
|
|
192380
192985
|
}
|
|
192381
|
-
for (const m of
|
|
192986
|
+
for (const m of body.matchAll(/<archive\b([^>]*)\/?>/g)) {
|
|
192382
192987
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
192383
|
-
if (Number.isInteger(id))
|
|
192384
|
-
|
|
192988
|
+
if (!Number.isInteger(id))
|
|
192989
|
+
throw new Error("verify manifest entry missing numeric id");
|
|
192990
|
+
out.archived.push({ id, reason: attrOf(m[1], "reason") ?? "" });
|
|
192385
192991
|
}
|
|
192992
|
+
assertNoDuplicateManifestIds([...out.verified, ...out.updated, ...out.archived].map((entry) => entry.id), "verify");
|
|
192386
192993
|
return out;
|
|
192387
192994
|
}
|
|
192388
192995
|
|
|
@@ -192475,9 +193082,13 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
192475
193082
|
});
|
|
192476
193083
|
},
|
|
192477
193084
|
validateOutput: (messages) => {
|
|
193085
|
+
if (hasLengthCappedOutput(messages)) {
|
|
193086
|
+
throw new Error("verify returned length-capped output");
|
|
193087
|
+
}
|
|
192478
193088
|
const text = extractLatestAssistantText(messages);
|
|
192479
193089
|
if (!text)
|
|
192480
193090
|
throw new Error("verify returned no output");
|
|
193091
|
+
parseVerifyManifest(text);
|
|
192481
193092
|
return text;
|
|
192482
193093
|
}
|
|
192483
193094
|
});
|
|
@@ -192504,24 +193115,14 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
192504
193115
|
async function applyVerifyManifest(args, batch, manifestText) {
|
|
192505
193116
|
const batchIds = new Set(batch.map((m) => m.id));
|
|
192506
193117
|
const parsed = parseVerifyManifest(manifestText);
|
|
193118
|
+
assertManifestCoversExactly([...parsed.verified, ...parsed.updated, ...parsed.archived].map((entry) => entry.id), batchIds, "verify");
|
|
192507
193119
|
const now = Date.now();
|
|
192508
193120
|
const writes = [];
|
|
192509
|
-
const verdictCounts = new Map;
|
|
192510
|
-
for (const verdict of [...parsed.verified, ...parsed.updated, ...parsed.archived]) {
|
|
192511
|
-
if (!batchIds.has(verdict.id))
|
|
192512
|
-
continue;
|
|
192513
|
-
verdictCounts.set(verdict.id, (verdictCounts.get(verdict.id) ?? 0) + 1);
|
|
192514
|
-
}
|
|
192515
|
-
const conflictingIds = new Set(Array.from(verdictCounts.entries()).filter(([, count]) => count !== 1).map(([id]) => id));
|
|
192516
193121
|
for (const v of parsed.verified) {
|
|
192517
|
-
if (!batchIds.has(v.id) || conflictingIds.has(v.id))
|
|
192518
|
-
continue;
|
|
192519
193122
|
const files = await normalizeFiles(args, v.files);
|
|
192520
193123
|
writes.push({ kind: "verify", id: v.id, files });
|
|
192521
193124
|
}
|
|
192522
193125
|
for (const u of parsed.updated) {
|
|
192523
|
-
if (!batchIds.has(u.id) || conflictingIds.has(u.id))
|
|
192524
|
-
continue;
|
|
192525
193126
|
const content = u.content.trim();
|
|
192526
193127
|
if (!content || content.length > 20000) {
|
|
192527
193128
|
const files2 = await normalizeFiles(args, u.files);
|
|
@@ -192538,8 +193139,6 @@ async function applyVerifyManifest(args, batch, manifestText) {
|
|
|
192538
193139
|
});
|
|
192539
193140
|
}
|
|
192540
193141
|
for (const a of parsed.archived) {
|
|
192541
|
-
if (!batchIds.has(a.id) || conflictingIds.has(a.id))
|
|
192542
|
-
continue;
|
|
192543
193142
|
writes.push({ kind: "archive", id: a.id, reason: a.reason });
|
|
192544
193143
|
}
|
|
192545
193144
|
if (writes.length === 0)
|
|
@@ -192547,12 +193146,7 @@ async function applyVerifyManifest(args, batch, manifestText) {
|
|
|
192547
193146
|
let verified = 0;
|
|
192548
193147
|
let updated = 0;
|
|
192549
193148
|
let archived = 0;
|
|
192550
|
-
|
|
192551
|
-
args.db.transaction(() => {
|
|
192552
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
192553
|
-
leaseLost = true;
|
|
192554
|
-
return;
|
|
192555
|
-
}
|
|
193149
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
192556
193150
|
for (const w of writes) {
|
|
192557
193151
|
const memory = getMemoryById(args.db, w.id);
|
|
192558
193152
|
if (!isPrimaryMutable(memory))
|
|
@@ -192580,9 +193174,7 @@ async function applyVerifyManifest(args, batch, manifestText) {
|
|
|
192580
193174
|
archived += 1;
|
|
192581
193175
|
}
|
|
192582
193176
|
}
|
|
192583
|
-
})
|
|
192584
|
-
if (leaseLost)
|
|
192585
|
-
throw new Error("Dream lease lost during verify commit");
|
|
193177
|
+
});
|
|
192586
193178
|
return { verified, updated, archived };
|
|
192587
193179
|
}
|
|
192588
193180
|
async function normalizeFiles(args, rawFiles) {
|
|
@@ -193076,11 +193668,7 @@ async function runRetrospectiveTask(config2, ctx, helpers) {
|
|
|
193076
193668
|
throw new Error("Dream lease lost during retrospective");
|
|
193077
193669
|
const sourceSessionId = flagged[0]?.sessionId ?? userMessages[0]?.sessionId ?? "retrospective";
|
|
193078
193670
|
const learnings = parseRetrospectiveLearnings(deepenRun.validated);
|
|
193079
|
-
const applied = db
|
|
193080
|
-
if (!peekLeaseHolderAndExpiry(db, holderId, leaseKey)) {
|
|
193081
|
-
leaseLost = true;
|
|
193082
|
-
return null;
|
|
193083
|
-
}
|
|
193671
|
+
const applied = runLeaseGuardedWrite(db, holderId, leaseKey, () => {
|
|
193084
193672
|
const result = applyRetrospectiveLearnings({
|
|
193085
193673
|
db,
|
|
193086
193674
|
projectIdentity,
|
|
@@ -193091,7 +193679,7 @@ async function runRetrospectiveTask(config2, ctx, helpers) {
|
|
|
193091
193679
|
});
|
|
193092
193680
|
recordRetrospectiveWindowProcessed(db, projectIdentity, windowKey);
|
|
193093
193681
|
return result;
|
|
193094
|
-
})
|
|
193682
|
+
});
|
|
193095
193683
|
if (leaseLost || !applied)
|
|
193096
193684
|
throw new Error("Dream lease lost during retrospective commit");
|
|
193097
193685
|
log(`[dreamer] retrospective: flagged=${flagged.length} learnings=${learnings.length} memory=${applied.memoryWritten} observations=${applied.observationsInserted} dropped=${applied.observationsDropped} rejected=${applied.rejected.length}`);
|
|
@@ -194613,7 +195201,7 @@ function createTagger() {
|
|
|
194613
195201
|
}
|
|
194614
195202
|
return map2;
|
|
194615
195203
|
}
|
|
194616
|
-
function
|
|
195204
|
+
function isUniqueConstraintError2(error51) {
|
|
194617
195205
|
return error51 instanceof Error && "code" in error51 && error51.code === "SQLITE_CONSTRAINT_UNIQUE";
|
|
194618
195206
|
}
|
|
194619
195207
|
function syncCounterAtLeast(sessionId, db, value) {
|
|
@@ -194651,7 +195239,7 @@ function createTagger() {
|
|
|
194651
195239
|
getUpsertCounterStatement(db).run(sessionId, next, getHarness());
|
|
194652
195240
|
})();
|
|
194653
195241
|
} catch (error51) {
|
|
194654
|
-
if (!
|
|
195242
|
+
if (!isUniqueConstraintError2(error51)) {
|
|
194655
195243
|
throw error51;
|
|
194656
195244
|
}
|
|
194657
195245
|
const racedRow = dbExistingLookup();
|
|
@@ -195818,8 +196406,9 @@ function scheduleReconciliation(db, sessionId, readMessages) {
|
|
|
195818
196406
|
reconciliationScheduledSessions.add(sessionId);
|
|
195819
196407
|
defer(() => {
|
|
195820
196408
|
reconcileSessionIndex(db, sessionId, readMessages).catch((error51) => {
|
|
195821
|
-
reconciliationScheduledSessions.delete(sessionId);
|
|
195822
196409
|
logIndexingError(sessionId, "reconciliation", error51);
|
|
196410
|
+
}).finally(() => {
|
|
196411
|
+
reconciliationScheduledSessions.delete(sessionId);
|
|
195823
196412
|
});
|
|
195824
196413
|
});
|
|
195825
196414
|
}
|
|
@@ -195831,13 +196420,17 @@ function scheduleIncrementalIndex(db, sessionId, messageId, readSingleMessage) {
|
|
|
195831
196420
|
const timer = setTimeout(() => {
|
|
195832
196421
|
incrementalTimers.delete(key);
|
|
195833
196422
|
pendingIncrementalKeys.add(key);
|
|
196423
|
+
let attemptedOrdinal = null;
|
|
195834
196424
|
runWithSessionLock(sessionId, () => {
|
|
195835
196425
|
const message = readSingleMessage(sessionId, messageId);
|
|
195836
196426
|
if (!message) {
|
|
195837
196427
|
return;
|
|
195838
196428
|
}
|
|
196429
|
+
attemptedOrdinal = message.ordinal;
|
|
195839
196430
|
indexSingleMessage(db, sessionId, message);
|
|
195840
196431
|
}).catch((error51) => {
|
|
196432
|
+
markMessageIndexDirty(db, sessionId, attemptedOrdinal ?? getLastIndexedOrdinal(db, sessionId) + 1);
|
|
196433
|
+
reconciledSessions.delete(sessionId);
|
|
195841
196434
|
logIndexingError(sessionId, `incremental index for ${messageId}`, error51);
|
|
195842
196435
|
}).finally(() => {
|
|
195843
196436
|
pendingIncrementalKeys.delete(key);
|
|
@@ -196031,6 +196624,7 @@ init_models_dev_cache();
|
|
|
196031
196624
|
// src/hooks/magic-context/channel2-delivery.ts
|
|
196032
196625
|
init_storage_meta_persisted();
|
|
196033
196626
|
init_logger();
|
|
196627
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
196034
196628
|
|
|
196035
196629
|
// src/hooks/magic-context/ctx-reduce-nudge.ts
|
|
196036
196630
|
init_tag_content_primitives();
|
|
@@ -196211,19 +196805,6 @@ ${body}${hintText}
|
|
|
196211
196805
|
}
|
|
196212
196806
|
|
|
196213
196807
|
// src/hooks/magic-context/channel2-delivery.ts
|
|
196214
|
-
function sealDeliveredAfterUnconfirmedSend(db, sessionId) {
|
|
196215
|
-
try {
|
|
196216
|
-
if (getChannel2NudgeState(db, sessionId) === "delivered") {
|
|
196217
|
-
return "already-delivered";
|
|
196218
|
-
}
|
|
196219
|
-
} catch {}
|
|
196220
|
-
try {
|
|
196221
|
-
setChannel2NudgeState(db, sessionId, "delivered");
|
|
196222
|
-
return "sealed";
|
|
196223
|
-
} catch {
|
|
196224
|
-
return "stuck-claimed";
|
|
196225
|
-
}
|
|
196226
|
-
}
|
|
196227
196808
|
async function maybeDeliverChannel2(sessionId, deps) {
|
|
196228
196809
|
let state;
|
|
196229
196810
|
try {
|
|
@@ -196249,7 +196830,8 @@ async function maybeDeliverChannel2(sessionId, deps) {
|
|
|
196249
196830
|
const client = deps.client;
|
|
196250
196831
|
if (!client)
|
|
196251
196832
|
return false;
|
|
196252
|
-
|
|
196833
|
+
const claimToken = randomUUID2();
|
|
196834
|
+
if (!claimChannel2NudgeState(deps.db, sessionId, claimToken)) {
|
|
196253
196835
|
return false;
|
|
196254
196836
|
}
|
|
196255
196837
|
try {
|
|
@@ -196273,40 +196855,36 @@ async function maybeDeliverChannel2(sessionId, deps) {
|
|
|
196273
196855
|
if (typeof session?.promptAsync !== "function") {
|
|
196274
196856
|
throw new Error("client has no session.promptAsync");
|
|
196275
196857
|
}
|
|
196858
|
+
const claim = getChannel2NudgeClaim(deps.db, sessionId);
|
|
196859
|
+
if (claim.state !== "claimed" || claim.claimToken !== claimToken) {
|
|
196860
|
+
sessionLog(sessionId, `channel2 ceiling nudge delivery skipped: claim no longer owned before send (state=${claim.state || "empty"})`);
|
|
196861
|
+
return false;
|
|
196862
|
+
}
|
|
196276
196863
|
await session.promptAsync({ path: { id: sessionId }, body });
|
|
196277
196864
|
} catch (error51) {
|
|
196278
196865
|
try {
|
|
196279
|
-
|
|
196280
|
-
|
|
196866
|
+
const restored = casChannel2NudgeClaim(deps.db, sessionId, "pending", claimToken);
|
|
196867
|
+
if (restored) {
|
|
196868
|
+
sessionLog(sessionId, "channel2 ceiling nudge delivery failed (will retry):", error51);
|
|
196869
|
+
} else {
|
|
196870
|
+
sessionLog(sessionId, "channel2 ceiling nudge delivery failed after its claim was no longer owned; lease state left unchanged:", error51);
|
|
196871
|
+
}
|
|
196281
196872
|
} catch (revertError) {
|
|
196282
196873
|
sessionLog(sessionId, "channel2 ceiling nudge delivery failed; pending restore was busy so the stale claim will heal later:", { deliveryError: error51, revertError });
|
|
196283
196874
|
}
|
|
196284
196875
|
return false;
|
|
196285
196876
|
}
|
|
196286
196877
|
try {
|
|
196287
|
-
const confirmed =
|
|
196878
|
+
const confirmed = casChannel2NudgeClaim(deps.db, sessionId, "delivered", claimToken);
|
|
196288
196879
|
if (confirmed) {
|
|
196289
196880
|
sessionLog(sessionId, "channel2 ceiling nudge delivered");
|
|
196290
196881
|
return true;
|
|
196291
196882
|
}
|
|
196292
|
-
const
|
|
196293
|
-
|
|
196294
|
-
sessionLog(sessionId, "channel2 ceiling nudge duplicate window: our send returned after a sibling reclaimed the stale lease and already delivered");
|
|
196295
|
-
} else if (outcome === "sealed") {
|
|
196296
|
-
sessionLog(sessionId, "channel2 ceiling nudge sent but claim confirmation was lost; sealed delivered without an authoritative confirm");
|
|
196297
|
-
} else {
|
|
196298
|
-
sessionLog(sessionId, "channel2 ceiling nudge sent but claim confirmation was lost; lease stayed claimed and will heal later");
|
|
196299
|
-
}
|
|
196883
|
+
const claim = getChannel2NudgeClaim(deps.db, sessionId);
|
|
196884
|
+
sessionLog(sessionId, `channel2 ceiling nudge sent but claim confirmation was not ours (state=${claim.state || "empty"}); leaving existing lease state unchanged`);
|
|
196300
196885
|
return false;
|
|
196301
196886
|
} catch (error51) {
|
|
196302
|
-
|
|
196303
|
-
if (outcome === "already-delivered") {
|
|
196304
|
-
sessionLog(sessionId, "channel2 ceiling nudge duplicate window: our send returned after a sibling reclaimed the stale lease and already delivered");
|
|
196305
|
-
} else if (outcome === "sealed") {
|
|
196306
|
-
sessionLog(sessionId, "channel2 ceiling nudge sent but confirm failed:", error51);
|
|
196307
|
-
} else {
|
|
196308
|
-
sessionLog(sessionId, "channel2 ceiling nudge sent but confirm failed; lease stayed claimed and will heal later:", error51);
|
|
196309
|
-
}
|
|
196887
|
+
sessionLog(sessionId, "channel2 ceiling nudge sent but token-confirm failed; lease state left unchanged:", error51);
|
|
196310
196888
|
return false;
|
|
196311
196889
|
}
|
|
196312
196890
|
}
|
|
@@ -197736,6 +198314,8 @@ function injectToolPartIntoLatestAssistant(messages, part) {
|
|
|
197736
198314
|
continue;
|
|
197737
198315
|
if (typeof message.info.id !== "string")
|
|
197738
198316
|
continue;
|
|
198317
|
+
if (!isReplayableAssistantAnchor(message))
|
|
198318
|
+
continue;
|
|
197739
198319
|
if (hasToolPartWithCallId(message, part.callID)) {
|
|
197740
198320
|
return message.info.id;
|
|
197741
198321
|
}
|
|
@@ -197750,6 +198330,8 @@ function injectToolPartIntoAssistantById(messages, messageId, part) {
|
|
|
197750
198330
|
continue;
|
|
197751
198331
|
if (message.info.role !== "assistant")
|
|
197752
198332
|
continue;
|
|
198333
|
+
if (!isReplayableAssistantAnchor(message))
|
|
198334
|
+
return false;
|
|
197753
198335
|
if (hasToolPartWithCallId(message, part.callID))
|
|
197754
198336
|
return true;
|
|
197755
198337
|
message.parts.push(part);
|
|
@@ -197769,6 +198351,9 @@ function hasToolPartWithCallId(message, callId) {
|
|
|
197769
198351
|
}
|
|
197770
198352
|
return false;
|
|
197771
198353
|
}
|
|
198354
|
+
function isReplayableAssistantAnchor(message) {
|
|
198355
|
+
return message.info.error === undefined || message.info.error === null;
|
|
198356
|
+
}
|
|
197772
198357
|
function appendReminderToUserMessage(message, reminder) {
|
|
197773
198358
|
for (const part of message.parts) {
|
|
197774
198359
|
if (!isTextPart(part)) {
|
|
@@ -198625,24 +199210,46 @@ function getMessageSearchStatementWithCutoff(db) {
|
|
|
198625
199210
|
return stmt;
|
|
198626
199211
|
}
|
|
198627
199212
|
var ftsRowCountStatements = new WeakMap;
|
|
199213
|
+
var ftsRowCountStatementsWithCutoff = new WeakMap;
|
|
198628
199214
|
var ftsMatchCountStatements = new WeakMap;
|
|
198629
|
-
|
|
198630
|
-
|
|
198631
|
-
|
|
198632
|
-
|
|
198633
|
-
|
|
198634
|
-
|
|
198635
|
-
|
|
199215
|
+
var ftsMatchCountStatementsWithCutoff = new WeakMap;
|
|
199216
|
+
function getSessionFtsRowCount(db, sessionId, cutoff) {
|
|
199217
|
+
const stmt = cutoff === null ? (() => {
|
|
199218
|
+
let cached2 = ftsRowCountStatements.get(db);
|
|
199219
|
+
if (!cached2) {
|
|
199220
|
+
cached2 = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ?");
|
|
199221
|
+
ftsRowCountStatements.set(db, cached2);
|
|
199222
|
+
}
|
|
199223
|
+
return cached2;
|
|
199224
|
+
})() : (() => {
|
|
199225
|
+
let cached2 = ftsRowCountStatementsWithCutoff.get(db);
|
|
199226
|
+
if (!cached2) {
|
|
199227
|
+
cached2 = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ? AND CAST(message_ordinal AS INTEGER) <= ?");
|
|
199228
|
+
ftsRowCountStatementsWithCutoff.set(db, cached2);
|
|
199229
|
+
}
|
|
199230
|
+
return cached2;
|
|
199231
|
+
})();
|
|
199232
|
+
const row = cutoff === null ? stmt.get(sessionId) : stmt.get(sessionId, cutoff);
|
|
198636
199233
|
return typeof row?.n === "number" ? row.n : 0;
|
|
198637
199234
|
}
|
|
198638
|
-
function countSessionFtsMatches(db, sessionId, ftsQuery) {
|
|
198639
|
-
|
|
198640
|
-
|
|
198641
|
-
|
|
198642
|
-
|
|
198643
|
-
|
|
199235
|
+
function countSessionFtsMatches(db, sessionId, ftsQuery, cutoff) {
|
|
199236
|
+
const stmt = cutoff === null ? (() => {
|
|
199237
|
+
let cached2 = ftsMatchCountStatements.get(db);
|
|
199238
|
+
if (!cached2) {
|
|
199239
|
+
cached2 = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ? AND message_history_fts MATCH ?");
|
|
199240
|
+
ftsMatchCountStatements.set(db, cached2);
|
|
199241
|
+
}
|
|
199242
|
+
return cached2;
|
|
199243
|
+
})() : (() => {
|
|
199244
|
+
let cached2 = ftsMatchCountStatementsWithCutoff.get(db);
|
|
199245
|
+
if (!cached2) {
|
|
199246
|
+
cached2 = db.prepare("SELECT COUNT(*) AS n FROM message_history_fts WHERE session_id = ? AND message_history_fts MATCH ? AND CAST(message_ordinal AS INTEGER) <= ?");
|
|
199247
|
+
ftsMatchCountStatementsWithCutoff.set(db, cached2);
|
|
199248
|
+
}
|
|
199249
|
+
return cached2;
|
|
199250
|
+
})();
|
|
198644
199251
|
try {
|
|
198645
|
-
const row = stmt.get(sessionId, ftsQuery);
|
|
199252
|
+
const row = cutoff === null ? stmt.get(sessionId, ftsQuery) : stmt.get(sessionId, ftsQuery, cutoff);
|
|
198646
199253
|
return typeof row?.n === "number" ? row.n : 0;
|
|
198647
199254
|
} catch {
|
|
198648
199255
|
return 0;
|
|
@@ -198892,7 +199499,7 @@ function searchMessages(args) {
|
|
|
198892
199499
|
role: row.role
|
|
198893
199500
|
}));
|
|
198894
199501
|
}
|
|
198895
|
-
const corpusSize = getSessionFtsRowCount(args.db, args.sessionId);
|
|
199502
|
+
const corpusSize = getSessionFtsRowCount(args.db, args.sessionId, cutoff);
|
|
198896
199503
|
const queryLists = [];
|
|
198897
199504
|
if (baseQuery.length > 0) {
|
|
198898
199505
|
queryLists.push({
|
|
@@ -198905,7 +199512,7 @@ function searchMessages(args) {
|
|
|
198905
199512
|
const probeQuery = sanitizeFtsQuery(probe);
|
|
198906
199513
|
if (probeQuery.length === 0)
|
|
198907
199514
|
continue;
|
|
198908
|
-
const df = countSessionFtsMatches(args.db, args.sessionId, probeQuery);
|
|
199515
|
+
const df = countSessionFtsMatches(args.db, args.sessionId, probeQuery, cutoff);
|
|
198909
199516
|
const weight = probeDiscriminationWeight(df, corpusSize);
|
|
198910
199517
|
probeWeights.set(probe, weight);
|
|
198911
199518
|
queryLists.push({
|
|
@@ -200199,8 +200806,31 @@ function readFilePath(target) {
|
|
|
200199
200806
|
|
|
200200
200807
|
// src/hooks/magic-context/todo-view.ts
|
|
200201
200808
|
import { createHash as createHash12 } from "node:crypto";
|
|
200202
|
-
var
|
|
200203
|
-
var
|
|
200809
|
+
var TODO_STATUS_PENDING = "pending";
|
|
200810
|
+
var TODO_STATUS_IN_PROGRESS = "in_progress";
|
|
200811
|
+
var TODO_STATUS_COMPLETED = "completed";
|
|
200812
|
+
var TODO_STATUS_CANCELLED = "cancelled";
|
|
200813
|
+
var TODO_PRIORITY_HIGH = "high";
|
|
200814
|
+
var TODO_PRIORITY_MEDIUM = "medium";
|
|
200815
|
+
var TODO_PRIORITY_LOW = "low";
|
|
200816
|
+
var TODO_STATUSES = [
|
|
200817
|
+
TODO_STATUS_PENDING,
|
|
200818
|
+
TODO_STATUS_IN_PROGRESS,
|
|
200819
|
+
TODO_STATUS_COMPLETED,
|
|
200820
|
+
TODO_STATUS_CANCELLED
|
|
200821
|
+
];
|
|
200822
|
+
var TODO_PRIORITIES = [
|
|
200823
|
+
TODO_PRIORITY_HIGH,
|
|
200824
|
+
TODO_PRIORITY_MEDIUM,
|
|
200825
|
+
TODO_PRIORITY_LOW
|
|
200826
|
+
];
|
|
200827
|
+
var TODO_STATUS_SET = new Set(TODO_STATUSES);
|
|
200828
|
+
var TODO_PRIORITY_SET = new Set(TODO_PRIORITIES);
|
|
200829
|
+
var TERMINAL_STATUSES = new Set([
|
|
200830
|
+
TODO_STATUS_COMPLETED,
|
|
200831
|
+
TODO_STATUS_CANCELLED
|
|
200832
|
+
]);
|
|
200833
|
+
var TITLE_DONE_STATUSES = new Set([TODO_STATUS_COMPLETED]);
|
|
200204
200834
|
var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
|
|
200205
200835
|
function normalizeTodoStateJson(todos) {
|
|
200206
200836
|
if (!Array.isArray(todos))
|
|
@@ -200212,7 +200842,7 @@ function normalizeTodoStateJson(todos) {
|
|
|
200212
200842
|
normalized.push({
|
|
200213
200843
|
content: todo.content,
|
|
200214
200844
|
status: todo.status,
|
|
200215
|
-
priority: todo.priority ??
|
|
200845
|
+
priority: todo.priority ?? TODO_PRIORITY_MEDIUM
|
|
200216
200846
|
});
|
|
200217
200847
|
}
|
|
200218
200848
|
return JSON.stringify(normalized);
|
|
@@ -200256,11 +200886,11 @@ function parseTodoState(stateJson) {
|
|
|
200256
200886
|
const result = [];
|
|
200257
200887
|
for (const item of parsed) {
|
|
200258
200888
|
if (!isTodoItem(item))
|
|
200259
|
-
|
|
200889
|
+
return null;
|
|
200260
200890
|
result.push({
|
|
200261
200891
|
content: item.content,
|
|
200262
200892
|
status: item.status,
|
|
200263
|
-
priority: item.priority ??
|
|
200893
|
+
priority: item.priority ?? TODO_PRIORITY_MEDIUM
|
|
200264
200894
|
});
|
|
200265
200895
|
}
|
|
200266
200896
|
return result;
|
|
@@ -200268,11 +200898,17 @@ function parseTodoState(stateJson) {
|
|
|
200268
200898
|
return null;
|
|
200269
200899
|
}
|
|
200270
200900
|
}
|
|
200901
|
+
function isTodoStatus(value) {
|
|
200902
|
+
return typeof value === "string" && TODO_STATUS_SET.has(value);
|
|
200903
|
+
}
|
|
200904
|
+
function isTodoPriority(value) {
|
|
200905
|
+
return typeof value === "string" && TODO_PRIORITY_SET.has(value);
|
|
200906
|
+
}
|
|
200271
200907
|
function isTodoItem(value) {
|
|
200272
200908
|
if (value === null || typeof value !== "object")
|
|
200273
200909
|
return false;
|
|
200274
200910
|
const todo = value;
|
|
200275
|
-
return typeof todo.content === "string" &&
|
|
200911
|
+
return typeof todo.content === "string" && isTodoStatus(todo.status) && (todo.priority === undefined || isTodoPriority(todo.priority));
|
|
200276
200912
|
}
|
|
200277
200913
|
|
|
200278
200914
|
// src/hooks/magic-context/tool-reclaim.ts
|
|
@@ -200462,7 +201098,7 @@ async function runPostTransformPhase(args) {
|
|
|
200462
201098
|
if (args.schedulerDecision === "execute" && !materializationRequested) {
|
|
200463
201099
|
updateSessionMeta(args.db, args.sessionId, { lastResponseTime: Date.now() });
|
|
200464
201100
|
}
|
|
200465
|
-
const toolReclaimExecutePass = args.schedulerDecision === "execute";
|
|
201101
|
+
const toolReclaimExecutePass = args.schedulerDecision === "execute" || m0HardFoldThisPass;
|
|
200466
201102
|
const alreadyMutatingThisPass = pendingOpsDidMutate || heuristicOrReasoningDidMutate;
|
|
200467
201103
|
let autoReclaimTargetCount = 0;
|
|
200468
201104
|
let autoReclaimDidMutate = false;
|
|
@@ -200554,12 +201190,12 @@ async function runPostTransformPhase(args) {
|
|
|
200554
201190
|
sessionLog(args.sessionId, "transform failed dropping stale ctx_reduce calls:", error51);
|
|
200555
201191
|
}
|
|
200556
201192
|
}
|
|
200557
|
-
if (canUseEmptySentinels
|
|
201193
|
+
if (canUseEmptySentinels) {
|
|
200558
201194
|
try {
|
|
200559
201195
|
const tImg = performance.now();
|
|
200560
201196
|
const frozenImageIds = getProcessedImageStrippedIds(args.db, args.sessionId);
|
|
200561
201197
|
const imageResult = stripProcessedImages(args.messages, frozenImageIds, {
|
|
200562
|
-
detect: isCacheBustingPass,
|
|
201198
|
+
detect: isCacheBustingPass && args.watermark > 0,
|
|
200563
201199
|
watermark: args.watermark,
|
|
200564
201200
|
messageTagNumbers: args.messageTagNumbers
|
|
200565
201201
|
});
|
|
@@ -202125,12 +202761,25 @@ await __promiseAll([
|
|
|
202125
202761
|
]);
|
|
202126
202762
|
import * as crypto3 from "node:crypto";
|
|
202127
202763
|
var WAIT_FOR_LEASE_MS = 1000;
|
|
202764
|
+
var MAX_WRAPUP_LEASE_WAIT_MS = 10 * 60 * 1000;
|
|
202128
202765
|
function sleep2(ms) {
|
|
202129
202766
|
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
202130
202767
|
}
|
|
202131
202768
|
function plural(value, word) {
|
|
202132
202769
|
return `${value} ${word}${value === 1 ? "" : "s"}`;
|
|
202133
202770
|
}
|
|
202771
|
+
function resolveWrapupLeaseWaitTimeout(ctx) {
|
|
202772
|
+
const configured = ctx.wrapupLeaseWaitTimeoutMs ?? MAX_WRAPUP_LEASE_WAIT_MS;
|
|
202773
|
+
return Number.isFinite(configured) && configured >= 0 ? configured : MAX_WRAPUP_LEASE_WAIT_MS;
|
|
202774
|
+
}
|
|
202775
|
+
async function waitForActiveRunWithin(promise2, timeoutMs) {
|
|
202776
|
+
if (timeoutMs <= 0)
|
|
202777
|
+
return "timeout";
|
|
202778
|
+
return Promise.race([
|
|
202779
|
+
promise2.then(() => "settled", () => "settled"),
|
|
202780
|
+
sleep2(timeoutMs).then(() => "timeout")
|
|
202781
|
+
]);
|
|
202782
|
+
}
|
|
202134
202783
|
function formatAlreadyRunningMessage(state) {
|
|
202135
202784
|
if (!state)
|
|
202136
202785
|
return "/ctx-wrapup is already running for this session.";
|
|
@@ -202173,44 +202822,55 @@ function emitWrapupProgress(ctx, sessionId, progress) {
|
|
|
202173
202822
|
note: progress.note
|
|
202174
202823
|
});
|
|
202175
202824
|
}
|
|
202176
|
-
async function waitForExistingIncrementalRun(sessionId) {
|
|
202825
|
+
async function waitForExistingIncrementalRun(sessionId, maxWaitMs) {
|
|
202177
202826
|
const active = getActiveCompartmentRun(sessionId);
|
|
202178
202827
|
if (!active)
|
|
202179
202828
|
return "ok";
|
|
202180
202829
|
if (active.kind === "recomp" || active.kind === "wrapup")
|
|
202181
202830
|
return "busy";
|
|
202182
|
-
|
|
202183
|
-
|
|
202184
|
-
|
|
202831
|
+
const outcome = await waitForActiveRunWithin(active.promise, maxWaitMs);
|
|
202832
|
+
if (outcome === "timeout")
|
|
202833
|
+
return "timeout";
|
|
202185
202834
|
return "ok";
|
|
202186
202835
|
}
|
|
202187
202836
|
async function acquireCompartmentLeaseForWrapup(ctx, sessionId, renewWrapupMarker) {
|
|
202188
202837
|
const holderId = crypto3.randomUUID();
|
|
202838
|
+
const waitStartedAt = Date.now();
|
|
202839
|
+
const maxWaitMs = resolveWrapupLeaseWaitTimeout(ctx);
|
|
202840
|
+
const remainingMs = () => Math.max(0, waitStartedAt + maxWaitMs - Date.now());
|
|
202189
202841
|
for (;; ) {
|
|
202842
|
+
if (remainingMs() <= 0) {
|
|
202843
|
+
sessionLog(sessionId, "wrapup: timed out waiting for the compartment-state lease");
|
|
202844
|
+
return { ok: false, reason: "timeout" };
|
|
202845
|
+
}
|
|
202190
202846
|
const active = getActiveCompartmentRun(sessionId);
|
|
202191
|
-
if (active?.kind === "recomp" || active?.kind === "wrapup")
|
|
202192
|
-
return
|
|
202847
|
+
if (active?.kind === "recomp" || active?.kind === "wrapup") {
|
|
202848
|
+
return { ok: false, reason: "busy" };
|
|
202849
|
+
}
|
|
202193
202850
|
if (active) {
|
|
202194
202851
|
emitWrapupProgress(ctx, sessionId, { note: "Waiting for the active historian run…" });
|
|
202195
|
-
|
|
202196
|
-
|
|
202197
|
-
|
|
202852
|
+
const outcome = await waitForActiveRunWithin(active.promise, remainingMs());
|
|
202853
|
+
if (outcome === "timeout") {
|
|
202854
|
+
sessionLog(sessionId, "wrapup: timed out waiting for the active historian run");
|
|
202855
|
+
return { ok: false, reason: "timeout" };
|
|
202856
|
+
}
|
|
202198
202857
|
continue;
|
|
202199
202858
|
}
|
|
202200
202859
|
const lease = acquireCompartmentLease(ctx.db, sessionId, holderId);
|
|
202201
202860
|
if (lease)
|
|
202202
|
-
return holderId;
|
|
202861
|
+
return { ok: true, holderId };
|
|
202203
202862
|
emitWrapupProgress(ctx, sessionId, { note: "Waiting for the compartment-state lease…" });
|
|
202204
202863
|
if (!renewWrapupMarker({}))
|
|
202205
|
-
return
|
|
202206
|
-
await sleep2(WAIT_FOR_LEASE_MS);
|
|
202864
|
+
return { ok: false, reason: "ownership_lost" };
|
|
202865
|
+
await sleep2(Math.min(WAIT_FOR_LEASE_MS, remainingMs()));
|
|
202207
202866
|
}
|
|
202208
202867
|
}
|
|
202209
202868
|
async function runOneWrapupIteration(args) {
|
|
202210
202869
|
const { ctx, sessionId, plan, messagesToKeep, anchorRawMessageCount } = args;
|
|
202211
|
-
const
|
|
202212
|
-
if (!
|
|
202213
|
-
return false;
|
|
202870
|
+
const acquired = await acquireCompartmentLeaseForWrapup(ctx, sessionId, args.renewWrapupMarker);
|
|
202871
|
+
if (!acquired.ok)
|
|
202872
|
+
return { ran: false, reason: acquired.reason };
|
|
202873
|
+
const leaseHolderId = acquired.holderId;
|
|
202214
202874
|
const renewal = setInterval(() => {
|
|
202215
202875
|
try {
|
|
202216
202876
|
if (!renewCompartmentLease(ctx.db, sessionId, leaseHolderId)) {
|
|
@@ -202255,7 +202915,7 @@ async function runOneWrapupIteration(args) {
|
|
|
202255
202915
|
registerActiveCompartmentRun(sessionId, runnerPromise, "wrapup");
|
|
202256
202916
|
try {
|
|
202257
202917
|
await runnerPromise;
|
|
202258
|
-
return true;
|
|
202918
|
+
return { ran: true };
|
|
202259
202919
|
} finally {
|
|
202260
202920
|
clearInterval(renewal);
|
|
202261
202921
|
releaseCompartmentLease(ctx.db, sessionId, leaseHolderId);
|
|
@@ -202330,7 +202990,7 @@ ${message2}`;
|
|
|
202330
202990
|
}, 60000);
|
|
202331
202991
|
markerRenewal.unref?.();
|
|
202332
202992
|
try {
|
|
202333
|
-
const activeAtStart = await waitForExistingIncrementalRun(sessionId);
|
|
202993
|
+
const activeAtStart = await waitForExistingIncrementalRun(sessionId, resolveWrapupLeaseWaitTimeout(ctx));
|
|
202334
202994
|
if (activeAtStart === "busy") {
|
|
202335
202995
|
const message2 = "Another Magic Context rebuild is already running for this session.";
|
|
202336
202996
|
setRecompTerminal(ctx.liveSessionState, sessionId, "skipped", message2);
|
|
@@ -202338,16 +202998,22 @@ ${message2}`;
|
|
|
202338
202998
|
|
|
202339
202999
|
${message2}`;
|
|
202340
203000
|
}
|
|
202341
|
-
|
|
202342
|
-
|
|
202343
|
-
|
|
202344
|
-
|
|
202345
|
-
|
|
202346
|
-
|
|
202347
|
-
|
|
203001
|
+
if (activeAtStart === "timeout") {
|
|
203002
|
+
stoppedForFailure = true;
|
|
203003
|
+
stoppedReason = "Timed out waiting for the active historian run.";
|
|
203004
|
+
}
|
|
203005
|
+
if (!stoppedForFailure) {
|
|
203006
|
+
try {
|
|
203007
|
+
sendIgnoredMessage(ctx.client, sessionId, `Magic Wrapup started — compacting about ${plural(expectedChunks, "chunk")} of history. This can take a few minutes; the result posts here when done.`, ctx.getNotificationParams(sessionId));
|
|
203008
|
+
} catch {}
|
|
203009
|
+
try {
|
|
203010
|
+
pushNotification("action", { action: "wrapup-progress-kick" }, sessionId);
|
|
203011
|
+
} catch {}
|
|
203012
|
+
}
|
|
203013
|
+
if (!stoppedForFailure && ownershipLost) {
|
|
202348
203014
|
stoppedForFailure = true;
|
|
202349
203015
|
stoppedReason = ownershipLostReason;
|
|
202350
|
-
} else {
|
|
203016
|
+
} else if (!stoppedForFailure) {
|
|
202351
203017
|
emitWrapupProgress(ctx, sessionId, {
|
|
202352
203018
|
processedMessages: Math.max(0, lastEnd),
|
|
202353
203019
|
totalMessages: Math.max(0, initialPlan.targetEligibleEndOrdinal - 1),
|
|
@@ -202394,7 +203060,7 @@ ${message2}`;
|
|
|
202394
203060
|
}
|
|
202395
203061
|
const beforeEnd = lastEnd;
|
|
202396
203062
|
const beforeFailures = getHistorianFailureState(ctx.db, sessionId).failureCount;
|
|
202397
|
-
const
|
|
203063
|
+
const iteration = await runOneWrapupIteration({
|
|
202398
203064
|
ctx,
|
|
202399
203065
|
sessionId,
|
|
202400
203066
|
plan,
|
|
@@ -202402,9 +203068,9 @@ ${message2}`;
|
|
|
202402
203068
|
anchorRawMessageCount: initialPlan.anchorRawMessageCount,
|
|
202403
203069
|
renewWrapupMarker
|
|
202404
203070
|
});
|
|
202405
|
-
if (!ran) {
|
|
203071
|
+
if (!iteration.ran) {
|
|
202406
203072
|
stoppedForFailure = true;
|
|
202407
|
-
stoppedReason = ownershipLost ? ownershipLostReason : "Another Magic Context rebuild started while wrapup was waiting.";
|
|
203073
|
+
stoppedReason = ownershipLost || iteration.reason === "ownership_lost" ? ownershipLostReason : iteration.reason === "timeout" ? "Timed out waiting for another process to release the compartment-state lease." : "Another Magic Context rebuild started while wrapup was waiting.";
|
|
202408
203074
|
break;
|
|
202409
203075
|
}
|
|
202410
203076
|
const afterEnd = getLastCompartmentEndMessage(ctx.db, sessionId);
|
|
@@ -204962,13 +205628,18 @@ function queueMemoryEmbedding(args) {
|
|
|
204962
205628
|
if (!snapshot?.enabled) {
|
|
204963
205629
|
return;
|
|
204964
205630
|
}
|
|
205631
|
+
const normalizedHash = computeNormalizedHash(args.content);
|
|
204965
205632
|
(async () => {
|
|
204966
205633
|
const result = await embedTextForProject(args.projectPath, args.content);
|
|
204967
205634
|
if (!result) {
|
|
204968
205635
|
sessionLog(args.sessionId, `memory embedding skipped for memory ${args.memoryId}: provider unavailable or embedding generation failed.`);
|
|
204969
205636
|
return;
|
|
204970
205637
|
}
|
|
204971
|
-
|
|
205638
|
+
const saved = saveEmbeddingIfHashMatches(args.deps.db, args.memoryId, result.vector, result.modelId, normalizedHash);
|
|
205639
|
+
if (!saved) {
|
|
205640
|
+
sessionLog(args.sessionId, `memory embedding skipped for memory ${args.memoryId}: content changed before the embedding finished.`);
|
|
205641
|
+
return;
|
|
205642
|
+
}
|
|
204972
205643
|
sessionLog(args.sessionId, `proactively embedded memory ${args.memoryId}.`);
|
|
204973
205644
|
})().catch((error51) => {
|
|
204974
205645
|
sessionLog(args.sessionId, `memory embedding failed for memory ${args.memoryId}:`, error51);
|
|
@@ -205053,8 +205724,9 @@ function createCtxMemoryTool(deps) {
|
|
|
205053
205724
|
const isOwn = targetIdentityForStoredPath(memory.projectPath) === projectPath;
|
|
205054
205725
|
if (isOwn)
|
|
205055
205726
|
return true;
|
|
205056
|
-
return toolShareCategories
|
|
205727
|
+
return toolShareCategories?.includes(memory.category) ?? false;
|
|
205057
205728
|
};
|
|
205729
|
+
const memoryOwnedByTool = (memory) => workspaceIdentitySet.identities.length > 1 ? targetIdentityForStoredPath(memory.projectPath) === projectPath : memoryBelongsToProject(memory, projectPath);
|
|
205058
205730
|
const embeddingSnapshot = getProjectEmbeddingSnapshot(projectPath);
|
|
205059
205731
|
if (embeddingSnapshot ? !embeddingSnapshot.features.memoryEnabled : deps.memoryEnabled === false) {
|
|
205060
205732
|
return getDisabledMessage();
|
|
@@ -205077,21 +205749,24 @@ function createCtxMemoryTool(deps) {
|
|
|
205077
205749
|
updateMemorySeenCount(deps.db, existingMemory.id);
|
|
205078
205750
|
return `Memory already exists [ID: ${existingMemory.id}] in ${category} (seen count incremented).`;
|
|
205079
205751
|
}
|
|
205080
|
-
const
|
|
205752
|
+
const insertResult = insertMemoryIdempotent(deps.db, {
|
|
205081
205753
|
projectPath,
|
|
205082
205754
|
category,
|
|
205083
205755
|
content,
|
|
205084
205756
|
sourceSessionId: toolContext.sessionID,
|
|
205085
205757
|
sourceType: toolContext.agent === DREAMER_AGENT ? "dreamer" : getSourceType(deps)
|
|
205086
205758
|
});
|
|
205759
|
+
if (!insertResult.inserted) {
|
|
205760
|
+
return `Memory already exists [ID: ${insertResult.memory.id}] in ${category} (seen count incremented).`;
|
|
205761
|
+
}
|
|
205087
205762
|
queueMemoryEmbedding({
|
|
205088
205763
|
deps,
|
|
205089
205764
|
sessionId: toolContext.sessionID,
|
|
205090
205765
|
projectPath,
|
|
205091
|
-
memoryId: memory.id,
|
|
205766
|
+
memoryId: insertResult.memory.id,
|
|
205092
205767
|
content
|
|
205093
205768
|
});
|
|
205094
|
-
return `Saved memory [ID: ${memory.id}] in ${category}.`;
|
|
205769
|
+
return `Saved memory [ID: ${insertResult.memory.id}] in ${category}.`;
|
|
205095
205770
|
}
|
|
205096
205771
|
if (args.action === "list") {
|
|
205097
205772
|
const limit = normalizeLimit2(args.limit);
|
|
@@ -205111,7 +205786,8 @@ function createCtxMemoryTool(deps) {
|
|
|
205111
205786
|
}
|
|
205112
205787
|
const rawProjectPath = projectPathForMemoryId(deps.db, updateId);
|
|
205113
205788
|
const memory = getMemoryById(deps.db, updateId);
|
|
205114
|
-
|
|
205789
|
+
const updateAllowed = memory ? toolContext.agent === DREAMER_AGENT ? memoryVisibleToTool(memory) : memoryOwnedByTool(memory) : false;
|
|
205790
|
+
if (!memory || !rawProjectPath || !updateAllowed) {
|
|
205115
205791
|
return `Error: Memory with ID ${updateId} was not found.`;
|
|
205116
205792
|
}
|
|
205117
205793
|
if (toolContext.agent !== DREAMER_AGENT && !isPrimaryMutableMemory(memory)) {
|
|
@@ -205159,7 +205835,7 @@ function createCtxMemoryTool(deps) {
|
|
|
205159
205835
|
return "Error: One or more source memories were not found.";
|
|
205160
205836
|
}
|
|
205161
205837
|
if (toolContext.agent !== DREAMER_AGENT) {
|
|
205162
|
-
const foreign = sourceMemories.find((memory) => !
|
|
205838
|
+
const foreign = sourceMemories.find((memory) => !memoryOwnedByTool(memory));
|
|
205163
205839
|
if (foreign) {
|
|
205164
205840
|
return `Error: Memory with ID ${foreign.id} was not found.`;
|
|
205165
205841
|
}
|
|
@@ -205182,11 +205858,6 @@ function createCtxMemoryTool(deps) {
|
|
|
205182
205858
|
return "Error: A valid category is required when action is 'merge'.";
|
|
205183
205859
|
}
|
|
205184
205860
|
const normalizedHash = computeNormalizedHash(content);
|
|
205185
|
-
const duplicate = getMemoryByHash(deps.db, projectPath, category, normalizedHash);
|
|
205186
|
-
const canonicalExisting = duplicate && ids.includes(duplicate.id) ? duplicate : null;
|
|
205187
|
-
if (duplicate && !canonicalExisting) {
|
|
205188
|
-
return `Error: Memory content already exists as ID ${duplicate.id}; update or archive existing duplicates instead.`;
|
|
205189
|
-
}
|
|
205190
205861
|
const mergedFrom = JSON.stringify(Array.from(new Set(sourceMemories.flatMap((memory) => {
|
|
205191
205862
|
let parsed;
|
|
205192
205863
|
try {
|
|
@@ -205202,14 +205873,21 @@ function createCtxMemoryTool(deps) {
|
|
|
205202
205873
|
const mergedSeenCount = sourceMemories.reduce((sum, memory) => sum + memory.seenCount, 0);
|
|
205203
205874
|
const mergedRetrievalCount = sourceMemories.reduce((sum, memory) => sum + memory.retrievalCount, 0);
|
|
205204
205875
|
const mergedStatus = sourceMemories.some((memory) => memory.status === "permanent") ? "permanent" : "active";
|
|
205205
|
-
|
|
205206
|
-
|
|
205876
|
+
let mergeConflict = null;
|
|
205877
|
+
const canonicalMemory = runImmediateTransaction(deps.db, () => {
|
|
205878
|
+
const lockedDuplicate = getMemoryByHash(deps.db, projectPath, category, normalizedHash);
|
|
205879
|
+
const canonicalExisting = lockedDuplicate && ids.includes(lockedDuplicate.id) ? lockedDuplicate : null;
|
|
205880
|
+
if (lockedDuplicate && !canonicalExisting) {
|
|
205881
|
+
mergeConflict = `Error: Memory content already exists as ID ${lockedDuplicate.id}; update or archive existing duplicates instead.`;
|
|
205882
|
+
return null;
|
|
205883
|
+
}
|
|
205884
|
+
const nextCanonical = canonicalExisting?.id != null ? canonicalExisting : insertMemoryIdempotent(deps.db, {
|
|
205207
205885
|
projectPath,
|
|
205208
205886
|
category,
|
|
205209
205887
|
content,
|
|
205210
205888
|
sourceSessionId: toolContext.sessionID,
|
|
205211
205889
|
sourceType: toolContext.agent === DREAMER_AGENT ? "dreamer" : getSourceType(deps)
|
|
205212
|
-
});
|
|
205890
|
+
}).memory;
|
|
205213
205891
|
const canonicalContentChanged = nextCanonical.content !== content || nextCanonical.normalizedHash !== normalizedHash;
|
|
205214
205892
|
if (canonicalContentChanged) {
|
|
205215
205893
|
updateMemoryContentInCurrentTransaction(deps.db, nextCanonical, content, normalizedHash);
|
|
@@ -205237,7 +205915,10 @@ function createCtxMemoryTool(deps) {
|
|
|
205237
205915
|
});
|
|
205238
205916
|
}
|
|
205239
205917
|
return nextCanonical;
|
|
205240
|
-
})
|
|
205918
|
+
});
|
|
205919
|
+
if (mergeConflict || !canonicalMemory) {
|
|
205920
|
+
return mergeConflict ?? "Error: Failed to merge memories.";
|
|
205921
|
+
}
|
|
205241
205922
|
queueMemoryEmbedding({
|
|
205242
205923
|
deps,
|
|
205243
205924
|
sessionId: toolContext.sessionID,
|
|
@@ -205258,7 +205939,8 @@ function createCtxMemoryTool(deps) {
|
|
|
205258
205939
|
for (const memoryId of archiveIds) {
|
|
205259
205940
|
const rawProjectPath = projectPathForMemoryId(deps.db, memoryId);
|
|
205260
205941
|
const memory = getMemoryById(deps.db, memoryId);
|
|
205261
|
-
|
|
205942
|
+
const archiveAllowed = memory ? toolContext.agent === DREAMER_AGENT ? memoryVisibleToTool(memory) : memoryOwnedByTool(memory) : false;
|
|
205943
|
+
if (!memory || !rawProjectPath || !archiveAllowed) {
|
|
205262
205944
|
return `Error: Memory with ID ${memoryId} was not found.`;
|
|
205263
205945
|
}
|
|
205264
205946
|
if (toolContext.agent !== DREAMER_AGENT && !isPrimaryMutableMemory(memory)) {
|
|
@@ -205713,7 +206395,7 @@ function normalizeLimit3(limit) {
|
|
|
205713
206395
|
return Math.max(1, Math.floor(limit));
|
|
205714
206396
|
}
|
|
205715
206397
|
function normalizeSources(sources) {
|
|
205716
|
-
if (
|
|
206398
|
+
if (sources === undefined)
|
|
205717
206399
|
return;
|
|
205718
206400
|
const result = [];
|
|
205719
206401
|
const seen = new Set;
|
|
@@ -205726,7 +206408,7 @@ function normalizeSources(sources) {
|
|
|
205726
206408
|
}
|
|
205727
206409
|
}
|
|
205728
206410
|
}
|
|
205729
|
-
return result
|
|
206411
|
+
return result;
|
|
205730
206412
|
}
|
|
205731
206413
|
function formatAge2(committedAtMs) {
|
|
205732
206414
|
const ageMs = Date.now() - committedAtMs;
|
|
@@ -205818,7 +206500,7 @@ function createCtxSearchTool(deps) {
|
|
|
205818
206500
|
args: {
|
|
205819
206501
|
query: tool5.schema.string().describe("Search query. Matches against memory content, Primers, git commit messages, and raw user/assistant message text."),
|
|
205820
206502
|
limit: tool5.schema.number().optional().describe("Maximum results to return (default: 10)"),
|
|
205821
|
-
sources: tool5.schema.array(tool5.schema.enum(["memory", "message", "git_commit", "primer", "note"])).optional().describe('Optional. Restrict to specific sources. Examples: ["primer"] for standing project explanations, ["git_commit"] for "when did we change X", ["memory"] for naming conventions, ["message"] for "did we discuss this earlier", ["note"] for parked decisions or follow-ups, ["git_commit","message"] for regression hunts. Omit for a broad search across all enabled sources.')
|
|
206503
|
+
sources: tool5.schema.array(tool5.schema.enum(["memory", "message", "git_commit", "primer", "note"])).optional().describe('Optional. Restrict to specific sources. Examples: ["primer"] for standing project explanations, ["git_commit"] for "when did we change X", ["memory"] for naming conventions, ["message"] for "did we discuss this earlier", ["note"] for parked decisions or follow-ups, ["git_commit","message"] for regression hunts. Omit for a broad search across all enabled sources; pass [] to search no sources.')
|
|
205822
206504
|
},
|
|
205823
206505
|
async execute(args, toolContext) {
|
|
205824
206506
|
const query = args.query?.trim();
|
|
@@ -205993,8 +206675,9 @@ function projectHash(directory) {
|
|
|
205993
206675
|
function rpcPortDir(storageDir, directory) {
|
|
205994
206676
|
return join20(storageDir, "rpc", projectHash(directory));
|
|
205995
206677
|
}
|
|
205996
|
-
function rpcPortFilePath(storageDir, directory, pid = process.pid) {
|
|
205997
|
-
|
|
206678
|
+
function rpcPortFilePath(storageDir, directory, pid = process.pid, instanceId) {
|
|
206679
|
+
const suffix = instanceId ? `-${instanceId}` : "";
|
|
206680
|
+
return join20(rpcPortDir(storageDir, directory), `port-${pid}${suffix}.json`);
|
|
205998
206681
|
}
|
|
205999
206682
|
function isPidAlive(pid) {
|
|
206000
206683
|
if (!Number.isInteger(pid) || pid <= 0)
|
|
@@ -206022,7 +206705,8 @@ function parseRpcPortFile(content, fallbackPid = 0) {
|
|
|
206022
206705
|
port: port2,
|
|
206023
206706
|
pid,
|
|
206024
206707
|
started_at: Number.isFinite(startedAt) ? startedAt : 0,
|
|
206025
|
-
token: typeof parsed.token === "string" ? parsed.token : undefined
|
|
206708
|
+
token: typeof parsed.token === "string" ? parsed.token : undefined,
|
|
206709
|
+
instance_id: typeof parsed.instance_id === "string" ? parsed.instance_id : undefined
|
|
206026
206710
|
};
|
|
206027
206711
|
} catch {
|
|
206028
206712
|
return null;
|
|
@@ -206048,6 +206732,13 @@ function tokensMatch(presented, expected) {
|
|
|
206048
206732
|
return false;
|
|
206049
206733
|
return timingSafeEqual(a, b);
|
|
206050
206734
|
}
|
|
206735
|
+
function bearerToken(req) {
|
|
206736
|
+
const auth = req.headers.get("authorization");
|
|
206737
|
+
return typeof auth === "string" ? auth.replace(/^Bearer\s+/i, "") : "";
|
|
206738
|
+
}
|
|
206739
|
+
function websocketToken(req, url2) {
|
|
206740
|
+
return bearerToken(req) || url2.searchParams.get("token") || "";
|
|
206741
|
+
}
|
|
206051
206742
|
|
|
206052
206743
|
class MagicContextRpcServer {
|
|
206053
206744
|
server = null;
|
|
@@ -206056,16 +206747,18 @@ class MagicContextRpcServer {
|
|
|
206056
206747
|
portFilePath;
|
|
206057
206748
|
portDir;
|
|
206058
206749
|
startedAt = Date.now();
|
|
206750
|
+
instanceId = randomBytes(8).toString("hex");
|
|
206059
206751
|
sockets = new Set;
|
|
206060
206752
|
token = randomBytes(32).toString("hex");
|
|
206061
206753
|
constructor(storageDir, directory) {
|
|
206062
|
-
this.portFilePath = rpcPortFilePath(storageDir, directory);
|
|
206754
|
+
this.portFilePath = rpcPortFilePath(storageDir, directory, process.pid, this.instanceId);
|
|
206063
206755
|
this.portDir = rpcPortDir(storageDir, directory);
|
|
206064
206756
|
}
|
|
206065
206757
|
handle(method, handler) {
|
|
206066
206758
|
this.handlers.set(method, handler);
|
|
206067
206759
|
}
|
|
206068
206760
|
async start() {
|
|
206761
|
+
this.startedAt = Date.now();
|
|
206069
206762
|
const self2 = this;
|
|
206070
206763
|
const server = Bun.serve({
|
|
206071
206764
|
port: 0,
|
|
@@ -206108,7 +206801,8 @@ class MagicContextRpcServer {
|
|
|
206108
206801
|
port: this.port,
|
|
206109
206802
|
pid: process.pid,
|
|
206110
206803
|
started_at: this.startedAt,
|
|
206111
|
-
token: this.token
|
|
206804
|
+
token: this.token,
|
|
206805
|
+
instance_id: this.instanceId
|
|
206112
206806
|
}), { encoding: "utf-8", mode: 384 });
|
|
206113
206807
|
renameSync5(tmpPath, this.portFilePath);
|
|
206114
206808
|
try {
|
|
@@ -206154,6 +206848,9 @@ class MagicContextRpcServer {
|
|
|
206154
206848
|
async handleFetch(req, srv) {
|
|
206155
206849
|
const url2 = new URL(req.url);
|
|
206156
206850
|
if (url2.pathname === "/ws") {
|
|
206851
|
+
if (!tokensMatch(websocketToken(req, url2), this.token)) {
|
|
206852
|
+
return new Response("Unauthorized", { status: 401 });
|
|
206853
|
+
}
|
|
206157
206854
|
const ok = srv.upgrade(req, { data: { authed: false } });
|
|
206158
206855
|
if (ok)
|
|
206159
206856
|
return;
|
|
@@ -206165,9 +206862,7 @@ class MagicContextRpcServer {
|
|
|
206165
206862
|
if (req.method !== "POST" || !url2.pathname.startsWith("/rpc/")) {
|
|
206166
206863
|
return new Response("Not Found", { status: 404 });
|
|
206167
206864
|
}
|
|
206168
|
-
|
|
206169
|
-
const presented = typeof auth === "string" ? auth.replace(/^Bearer\s+/i, "") : "";
|
|
206170
|
-
if (!tokensMatch(presented, this.token)) {
|
|
206865
|
+
if (!tokensMatch(bearerToken(req), this.token)) {
|
|
206171
206866
|
return json2({ error: "Unauthorized" }, 401);
|
|
206172
206867
|
}
|
|
206173
206868
|
const method = url2.pathname.slice(5);
|
|
@@ -206202,6 +206897,8 @@ class MagicContextRpcServer {
|
|
|
206202
206897
|
} catch {
|
|
206203
206898
|
return;
|
|
206204
206899
|
}
|
|
206900
|
+
if (msg.type !== "hello" && !ws.data.authed)
|
|
206901
|
+
return;
|
|
206205
206902
|
if (msg.type === "hello") {
|
|
206206
206903
|
if (!tokensMatch(typeof msg.token === "string" ? msg.token : "", this.token)) {
|
|
206207
206904
|
ws.send(JSON.stringify({ type: "error", error: "unauthorized" }));
|
|
@@ -206214,6 +206911,8 @@ class MagicContextRpcServer {
|
|
|
206214
206911
|
}
|
|
206215
206912
|
ws.data.authed = true;
|
|
206216
206913
|
ws.data.sessionId = typeof msg.sessionId === "string" && msg.sessionId.length > 0 ? msg.sessionId : undefined;
|
|
206914
|
+
ws.data.unregister?.();
|
|
206915
|
+
ws.data.unregister = undefined;
|
|
206217
206916
|
const sink = {
|
|
206218
206917
|
sessionId: ws.data.sessionId,
|
|
206219
206918
|
send: (notification) => {
|
|
@@ -206223,7 +206922,10 @@ class MagicContextRpcServer {
|
|
|
206223
206922
|
ws.data.unregister = registerNotificationSink(sink);
|
|
206224
206923
|
this.sockets.add(ws);
|
|
206225
206924
|
const lastReceivedId = Number(msg.lastReceivedId ?? 0);
|
|
206226
|
-
const
|
|
206925
|
+
const sessionCursor = Number.isFinite(lastReceivedId) ? lastReceivedId : 0;
|
|
206926
|
+
const hasGlobalCursor = typeof msg.globalLastReceivedId === "number";
|
|
206927
|
+
const globalLastReceivedId = hasGlobalCursor ? Number.isFinite(msg.globalLastReceivedId) ? msg.globalLastReceivedId : 0 : 0;
|
|
206928
|
+
const backlog = ws.data.sessionId === undefined && hasGlobalCursor ? drainNotifications(globalLastReceivedId, undefined, { globalOnly: true }) : drainNotifications(sessionCursor, ws.data.sessionId, hasGlobalCursor ? { globalLastReceivedId } : undefined);
|
|
206227
206929
|
for (const notification of backlog) {
|
|
206228
206930
|
ws.send(JSON.stringify({ type: "notification", notification }));
|
|
206229
206931
|
}
|
|
@@ -206233,7 +206935,13 @@ class MagicContextRpcServer {
|
|
|
206233
206935
|
if (msg.type === "ack") {
|
|
206234
206936
|
const lastReceivedId = Number(msg.lastReceivedId ?? 0);
|
|
206235
206937
|
if (Number.isFinite(lastReceivedId) && lastReceivedId > 0) {
|
|
206236
|
-
|
|
206938
|
+
if (msg.ackScope === "global") {
|
|
206939
|
+
drainNotifications(lastReceivedId, undefined, { globalOnly: true });
|
|
206940
|
+
} else if (typeof msg.sessionId === "string" && msg.sessionId.length > 0) {
|
|
206941
|
+
drainNotifications(lastReceivedId, msg.sessionId, { sessionOnly: true });
|
|
206942
|
+
} else {
|
|
206943
|
+
drainNotifications(lastReceivedId, ws.data.sessionId);
|
|
206944
|
+
}
|
|
206237
206945
|
}
|
|
206238
206946
|
}
|
|
206239
206947
|
}
|