@cortexkit/opencode-magic-context 0.31.2 → 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/config/schema/magic-context.d.ts +13 -0
- package/dist/config/schema/magic-context.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/memory/verification-paths.d.ts +16 -0
- package/dist/features/magic-context/memory/verification-paths.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 +1046 -333
- 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 +7 -4
- 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 +36 -0
- package/src/tui-compiled/badge-contrast.ts +84 -0
- package/src/tui-compiled/data/context-db.ts +353 -0
- package/src/tui-compiled/data/notification-socket.ts +294 -0
- package/src/tui-compiled/index.tsx +1204 -0
- package/src/tui-compiled/slots/sidebar-content.tsx +1052 -0
- package/src/tui-compiled/types/opencode-plugin-tui.d.ts +232 -0
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",
|
|
@@ -15408,6 +15408,10 @@ var init_magic_context = __esm(() => {
|
|
|
15408
15408
|
}).describe("Embedding provider configuration"),
|
|
15409
15409
|
temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add start/end date attributes on compartments. Gives the agent a sense of session pacing and "how long ago" across multi-day sessions. Graduated from experimental.temporal_awareness; default: true (set false to opt out).'),
|
|
15410
15410
|
keep_subagents: exports_external.boolean().default(false).describe("Debug: keep the child sessions Magic Context spawns for its own subagents (historian, dreamer, sidekick, memory-migration) instead of deleting them on success. Useful for short-term inspection/data collection — their full transcript (prompt, tool calls, token usage, output) stays in the host session store. Kept sessions accumulate until manually cleared; leave false for normal use. Requires a restart to take effect."),
|
|
15411
|
+
todowrite: exports_external.object({
|
|
15412
|
+
enabled: exports_external.boolean().default(true).describe("Pi only: register Magic Context's todowrite task-list tool. Disable if you use your own todo extension. OpenCode ships its own built-in todowrite; this setting has no effect there."),
|
|
15413
|
+
overlay: exports_external.boolean().default(true).describe("Pi only: show the persistent todo overlay above the editor while tasks are active.")
|
|
15414
|
+
}).default({ enabled: true, overlay: true }).describe("Pi-only todowrite tool and overlay controls. Pi registers tools and widgets at extension boot, so changing this after /cd requires /reload or restart."),
|
|
15411
15415
|
smart_drops: exports_external.boolean().default(false).describe("Content-aware reclaim of provably-superseded tool output, layered on the existing execute-pass auto-drop. When on: superseded todowrite (keep newest 1), spent ctx_reduce (keep newest 5), and zero-value meta (bash_status, bash_kill, ctx_note read/dismiss) outputs are dropped; older edits to a file are compressed to a filePath-preserving marker while the newest edit per file stays full. Only acts on passes already busting the cache, so it never originates a cache bust. Honors the protected-tag reserve. Experimental: opt-in, default off until cache stability is proven; when off the wire is byte-identical to the positional-only reclaim. Requires a restart."),
|
|
15412
15416
|
caveman_text_compression: exports_external.object({
|
|
15413
15417
|
enabled: exports_external.boolean().default(false).describe("Apply deterministic caveman-style text compression to old conversation text. Active for primary sessions when enabled; never for subagents. Compresses user/assistant text in oldest-first tiers: ultra (oldest 20%), full, lite, untouched (newest 40%)."),
|
|
@@ -16658,6 +16662,22 @@ function extractLatestAssistantText(messages) {
|
|
|
16658
16662
|
return getTextParts(latest).map((part) => part.text).join(`
|
|
16659
16663
|
`) || null;
|
|
16660
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
|
+
}
|
|
16661
16681
|
var init_assistant_message_extractor = () => {};
|
|
16662
16682
|
|
|
16663
16683
|
// src/shared/keep-subagents.ts
|
|
@@ -17326,7 +17346,8 @@ function isMidTurn(_deps, sessionId) {
|
|
|
17326
17346
|
}
|
|
17327
17347
|
function isMidTurnFromOpenCodeDb(db, sessionId) {
|
|
17328
17348
|
const latestAssistant = db.prepare(`SELECT id,
|
|
17329
|
-
json_extract(data, '$.finish') as finish
|
|
17349
|
+
json_extract(data, '$.finish') as finish,
|
|
17350
|
+
time_created as timeCreated
|
|
17330
17351
|
FROM message
|
|
17331
17352
|
WHERE session_id = ?
|
|
17332
17353
|
AND json_extract(data, '$.role') = 'assistant'
|
|
@@ -17334,6 +17355,8 @@ function isMidTurnFromOpenCodeDb(db, sessionId) {
|
|
|
17334
17355
|
LIMIT 1`).get(sessionId);
|
|
17335
17356
|
if (typeof latestAssistant?.id !== "string")
|
|
17336
17357
|
return false;
|
|
17358
|
+
if (hasNewerRealUserMessage(db, sessionId, latestAssistant.timeCreated))
|
|
17359
|
+
return false;
|
|
17337
17360
|
if (latestAssistant.finish === "tool-calls")
|
|
17338
17361
|
return true;
|
|
17339
17362
|
const partRows = db.prepare("SELECT data FROM part WHERE session_id = ? AND message_id = ?").all(sessionId, latestAssistant.id);
|
|
@@ -17348,6 +17371,18 @@ function isMidTurnFromOpenCodeDb(db, sessionId) {
|
|
|
17348
17371
|
}
|
|
17349
17372
|
});
|
|
17350
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
|
+
}
|
|
17351
17386
|
function getMessageTimesFromOpenCodeDb(sessionId, messageIds) {
|
|
17352
17387
|
const result = new Map;
|
|
17353
17388
|
if (messageIds.length === 0)
|
|
@@ -150816,12 +150851,19 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
150816
150851
|
let totalTokens = 0;
|
|
150817
150852
|
let messagesProcessed = 0;
|
|
150818
150853
|
let lastOrdinal = startOrdinal - 1;
|
|
150854
|
+
let highestScannedOrdinal = startOrdinal - 1;
|
|
150819
150855
|
let lastMessageId = "";
|
|
150820
150856
|
let firstMessageId = "";
|
|
150821
150857
|
let currentBlock = null;
|
|
150822
150858
|
let pendingNoiseMeta = [];
|
|
150823
150859
|
let commitClusters = 0;
|
|
150824
150860
|
let lastFlushedRole = "";
|
|
150861
|
+
function recordFilteredNoise(meta3) {
|
|
150862
|
+
pendingNoiseMeta.push(meta3);
|
|
150863
|
+
if (!currentBlock) {
|
|
150864
|
+
highestScannedOrdinal = Math.max(highestScannedOrdinal, meta3.ordinal);
|
|
150865
|
+
}
|
|
150866
|
+
}
|
|
150825
150867
|
function flushCurrentBlock() {
|
|
150826
150868
|
if (!currentBlock)
|
|
150827
150869
|
return true;
|
|
@@ -150837,6 +150879,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
150837
150879
|
if (!firstMessageId)
|
|
150838
150880
|
firstMessageId = currentBlock.meta[0]?.messageId ?? "";
|
|
150839
150881
|
lastOrdinal = currentBlock.meta[currentBlock.meta.length - 1]?.ordinal ?? currentBlock.endOrdinal;
|
|
150882
|
+
highestScannedOrdinal = Math.max(highestScannedOrdinal, lastOrdinal);
|
|
150840
150883
|
lastMessageId = currentBlock.meta[currentBlock.meta.length - 1]?.messageId ?? "";
|
|
150841
150884
|
messagesProcessed += currentBlock.meta.length;
|
|
150842
150885
|
lines.push(blockText);
|
|
@@ -150860,7 +150903,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
150860
150903
|
if (msg.role === "user" && !hasMeaningfulUserText(msg.parts)) {
|
|
150861
150904
|
const tcSummaries = extractToolCallSummaries(msg.parts);
|
|
150862
150905
|
if (tcSummaries.length === 0) {
|
|
150863
|
-
|
|
150906
|
+
recordFilteredNoise(meta3);
|
|
150864
150907
|
continue;
|
|
150865
150908
|
}
|
|
150866
150909
|
const tcText = tcSummaries.join(" / ");
|
|
@@ -150892,7 +150935,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
150892
150935
|
const compacted = compactTextForSummary(allParts.join(" / "), msg.role);
|
|
150893
150936
|
const text = compacted.text;
|
|
150894
150937
|
if (!text) {
|
|
150895
|
-
|
|
150938
|
+
recordFilteredNoise(meta3);
|
|
150896
150939
|
continue;
|
|
150897
150940
|
}
|
|
150898
150941
|
const msgHasNarrative = textParts.length > 0;
|
|
@@ -150919,7 +150962,9 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
150919
150962
|
};
|
|
150920
150963
|
pendingNoiseMeta = [];
|
|
150921
150964
|
}
|
|
150922
|
-
flushCurrentBlock()
|
|
150965
|
+
if (flushCurrentBlock() && pendingNoiseMeta.length > 0) {
|
|
150966
|
+
highestScannedOrdinal = Math.max(highestScannedOrdinal, pendingNoiseMeta[pendingNoiseMeta.length - 1]?.ordinal ?? highestScannedOrdinal);
|
|
150967
|
+
}
|
|
150923
150968
|
const toolOnlyRanges = [];
|
|
150924
150969
|
for (const range of flushedToolOnlyBlocks) {
|
|
150925
150970
|
const last = toolOnlyRanges[toolOnlyRanges.length - 1];
|
|
@@ -150936,7 +150981,7 @@ function readSessionChunk(sessionId, tokenBudget, offset = 1, eligibleEndOrdinal
|
|
|
150936
150981
|
endMessageId: lastMessageId,
|
|
150937
150982
|
messageCount: messagesProcessed,
|
|
150938
150983
|
tokenEstimate: totalTokens,
|
|
150939
|
-
hasMore: lastOrdinal < (eligibleEndOrdinal !== undefined ? Math.min(eligibleEndOrdinal - 1, totalMessageCount) : totalMessageCount),
|
|
150984
|
+
hasMore: Math.max(lastOrdinal, highestScannedOrdinal) < (eligibleEndOrdinal !== undefined ? Math.min(eligibleEndOrdinal - 1, totalMessageCount) : totalMessageCount),
|
|
150940
150985
|
text: lines.join(`
|
|
150941
150986
|
`),
|
|
150942
150987
|
lines: lineMeta,
|
|
@@ -150962,7 +151007,7 @@ function normalizeIndexText(text) {
|
|
|
150962
151007
|
function getLastIndexedStatement(db) {
|
|
150963
151008
|
let stmt = lastIndexedStatements.get(db);
|
|
150964
151009
|
if (!stmt) {
|
|
150965
|
-
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 = ?");
|
|
150966
151011
|
lastIndexedStatements.set(db, stmt);
|
|
150967
151012
|
}
|
|
150968
151013
|
return stmt;
|
|
@@ -150983,6 +151028,22 @@ function getUpsertIndexStatement(db) {
|
|
|
150983
151028
|
}
|
|
150984
151029
|
return stmt;
|
|
150985
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
|
+
}
|
|
150986
151047
|
function getDeleteFtsStatement(db) {
|
|
150987
151048
|
let stmt = deleteFtsStatements.get(db);
|
|
150988
151049
|
if (!stmt) {
|
|
@@ -150991,6 +151052,14 @@ function getDeleteFtsStatement(db) {
|
|
|
150991
151052
|
}
|
|
150992
151053
|
return stmt;
|
|
150993
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
|
+
}
|
|
150994
151063
|
function getDeleteIndexStatement(db) {
|
|
150995
151064
|
let stmt = deleteIndexStatements.get(db);
|
|
150996
151065
|
if (!stmt) {
|
|
@@ -151011,6 +151080,14 @@ function getLastIndexedOrdinal(db, sessionId) {
|
|
|
151011
151080
|
const row = getLastIndexedStatement(db).get(sessionId);
|
|
151012
151081
|
return typeof row?.last_indexed_ordinal === "number" ? row.last_indexed_ordinal : 0;
|
|
151013
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
|
+
}
|
|
151014
151091
|
function isMessageAlreadyIndexed(db, sessionId, messageId) {
|
|
151015
151092
|
const row = getCountIndexedMessageStatement(db).get(sessionId, messageId);
|
|
151016
151093
|
return (typeof row?.count === "number" ? row.count : 0) > 0;
|
|
@@ -151084,7 +151161,15 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal,
|
|
|
151084
151161
|
db.exec("BEGIN IMMEDIATE");
|
|
151085
151162
|
let committed = false;
|
|
151086
151163
|
try {
|
|
151087
|
-
|
|
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
|
+
}
|
|
151088
151173
|
const insertMessage = getInsertMessageStatement(db);
|
|
151089
151174
|
for (const message of messages) {
|
|
151090
151175
|
if (message.ordinal <= effectiveWatermark) {
|
|
@@ -151101,7 +151186,7 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal,
|
|
|
151101
151186
|
inserted++;
|
|
151102
151187
|
}
|
|
151103
151188
|
const newWatermark = Math.max(effectiveWatermark, finalWatermark);
|
|
151104
|
-
|
|
151189
|
+
getUpsertCleanIndexStatement(db).run(sessionId, newWatermark, now, getHarness());
|
|
151105
151190
|
db.exec("COMMIT");
|
|
151106
151191
|
committed = true;
|
|
151107
151192
|
} finally {
|
|
@@ -151113,14 +151198,17 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, lastIndexedOrdinal,
|
|
|
151113
151198
|
}
|
|
151114
151199
|
return inserted;
|
|
151115
151200
|
}
|
|
151116
|
-
var lastIndexedStatements, insertMessageStatements, upsertIndexStatements, deleteFtsStatements, deleteIndexStatements, countIndexedMessageStatements;
|
|
151201
|
+
var lastIndexedStatements, insertMessageStatements, upsertIndexStatements, upsertCleanIndexStatements, upsertDirtyFloorStatements, deleteFtsStatements, deleteFtsFromOrdinalStatements, deleteIndexStatements, countIndexedMessageStatements;
|
|
151117
151202
|
var init_message_index = __esm(async () => {
|
|
151118
151203
|
init_compression_depth_storage();
|
|
151119
151204
|
await init_read_session_chunk();
|
|
151120
151205
|
lastIndexedStatements = new WeakMap;
|
|
151121
151206
|
insertMessageStatements = new WeakMap;
|
|
151122
151207
|
upsertIndexStatements = new WeakMap;
|
|
151208
|
+
upsertCleanIndexStatements = new WeakMap;
|
|
151209
|
+
upsertDirtyFloorStatements = new WeakMap;
|
|
151123
151210
|
deleteFtsStatements = new WeakMap;
|
|
151211
|
+
deleteFtsFromOrdinalStatements = new WeakMap;
|
|
151124
151212
|
deleteIndexStatements = new WeakMap;
|
|
151125
151213
|
countIndexedMessageStatements = new WeakMap;
|
|
151126
151214
|
});
|
|
@@ -151159,6 +151247,7 @@ function healNullTextColumns(db) {
|
|
|
151159
151247
|
["cache_ttl", ""],
|
|
151160
151248
|
["last_nudge_band", ""],
|
|
151161
151249
|
["last_nudge_level", ""],
|
|
151250
|
+
["channel2_nudge_claim_token", ""],
|
|
151162
151251
|
["last_transform_error", ""],
|
|
151163
151252
|
["nudge_anchor_message_id", ""],
|
|
151164
151253
|
["nudge_anchor_text", ""],
|
|
@@ -151291,23 +151380,39 @@ function uniqueSorted(values) {
|
|
|
151291
151380
|
function placeholders(values) {
|
|
151292
151381
|
return values.map(() => "?").join(", ");
|
|
151293
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
|
+
}
|
|
151294
151392
|
function normalizeShareCategories(raw) {
|
|
151295
|
-
if (raw === null || raw === undefined)
|
|
151296
|
-
return
|
|
151297
|
-
|
|
151298
|
-
|
|
151393
|
+
if (raw === null || raw === undefined) {
|
|
151394
|
+
return defaultWorkspaceShareCategories();
|
|
151395
|
+
}
|
|
151396
|
+
if (typeof raw !== "string") {
|
|
151397
|
+
warnInvalidShareCategories("not a string", raw);
|
|
151398
|
+
return [];
|
|
151399
|
+
}
|
|
151299
151400
|
let parsed;
|
|
151300
151401
|
try {
|
|
151301
151402
|
parsed = JSON.parse(raw);
|
|
151302
151403
|
} catch {
|
|
151303
|
-
|
|
151404
|
+
warnInvalidShareCategories("malformed JSON", raw);
|
|
151405
|
+
return [];
|
|
151406
|
+
}
|
|
151407
|
+
if (!Array.isArray(parsed)) {
|
|
151408
|
+
warnInvalidShareCategories("not a JSON array", raw);
|
|
151409
|
+
return [];
|
|
151304
151410
|
}
|
|
151305
|
-
if (!Array.isArray(parsed))
|
|
151306
|
-
return null;
|
|
151307
151411
|
const categories = [];
|
|
151308
151412
|
for (const value of parsed) {
|
|
151309
151413
|
if (typeof value !== "string" || !VALID_SHARE_CATEGORIES.has(value)) {
|
|
151310
|
-
|
|
151414
|
+
warnInvalidShareCategories("unknown category", raw);
|
|
151415
|
+
return [];
|
|
151311
151416
|
}
|
|
151312
151417
|
if (!categories.includes(value))
|
|
151313
151418
|
categories.push(value);
|
|
@@ -151316,16 +151421,33 @@ function normalizeShareCategories(raw) {
|
|
|
151316
151421
|
}
|
|
151317
151422
|
function selectWorkspaceShareCategories(db, identities) {
|
|
151318
151423
|
const candidates = uniqueSorted(identities.filter((identity) => identity.length > 0));
|
|
151319
|
-
if (candidates.length === 0 || !tableExists(db, "workspace_members")
|
|
151424
|
+
if (candidates.length === 0 || !tableExists(db, "workspace_members")) {
|
|
151320
151425
|
return null;
|
|
151321
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
|
+
}
|
|
151322
151440
|
const row = db.prepare(`SELECT workspace.share_categories AS shareCategories
|
|
151323
151441
|
FROM workspace_members AS member
|
|
151324
151442
|
JOIN workspaces AS workspace ON workspace.id = member.workspace_id
|
|
151325
151443
|
WHERE member.project_path IN (${placeholders(candidates)})
|
|
151326
151444
|
ORDER BY workspace.id ASC
|
|
151327
151445
|
LIMIT 1`).get(...candidates);
|
|
151328
|
-
|
|
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);
|
|
151329
151451
|
}
|
|
151330
151452
|
function resolveWorkspaceShareCategories(db, projectIdentity) {
|
|
151331
151453
|
return selectWorkspaceShareCategories(db, [projectIdentity]);
|
|
@@ -151430,7 +151552,7 @@ function computeWorkspaceEpochFingerprint(db, identities) {
|
|
|
151430
151552
|
const hash2 = createHash2("sha256");
|
|
151431
151553
|
hash2.update("share_categories", "utf8");
|
|
151432
151554
|
hash2.update("\x00");
|
|
151433
|
-
hash2.update(shareCategories === null ? "
|
|
151555
|
+
hash2.update(shareCategories === null ? "NO_WORKSPACE" : JSON.stringify(shareCategories), "utf8");
|
|
151434
151556
|
hash2.update(`
|
|
151435
151557
|
`);
|
|
151436
151558
|
for (const identity of canonical) {
|
|
@@ -151502,11 +151624,13 @@ function bumpEpochsForWorkspaceMemberSet(db, identities, now = Date.now()) {
|
|
|
151502
151624
|
throw error51;
|
|
151503
151625
|
}
|
|
151504
151626
|
}
|
|
151505
|
-
var VALID_SHARE_CATEGORIES;
|
|
151627
|
+
var VALID_SHARE_CATEGORIES, DEFAULT_WORKSPACE_SHARE_CATEGORIES;
|
|
151506
151628
|
var init_workspaces = __esm(() => {
|
|
151629
|
+
init_logger();
|
|
151507
151630
|
init_constants();
|
|
151508
151631
|
init_project_identity2();
|
|
151509
151632
|
VALID_SHARE_CATEGORIES = new Set(V2_MEMORY_CATEGORIES);
|
|
151633
|
+
DEFAULT_WORKSPACE_SHARE_CATEGORIES = ["CONSTRAINTS"];
|
|
151510
151634
|
});
|
|
151511
151635
|
|
|
151512
151636
|
// src/features/magic-context/migrations.ts
|
|
@@ -153991,6 +154115,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
153991
154115
|
CREATE TABLE IF NOT EXISTS message_history_index (
|
|
153992
154116
|
session_id TEXT PRIMARY KEY,
|
|
153993
154117
|
last_indexed_ordinal INTEGER NOT NULL DEFAULT 0,
|
|
154118
|
+
dirty_floor_ordinal INTEGER NOT NULL DEFAULT 0,
|
|
153994
154119
|
updated_at INTEGER NOT NULL,
|
|
153995
154120
|
harness TEXT NOT NULL DEFAULT 'opencode'
|
|
153996
154121
|
);
|
|
@@ -154020,6 +154145,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
154020
154145
|
last_nudge_level TEXT DEFAULT '',
|
|
154021
154146
|
channel2_nudge_state TEXT DEFAULT '',
|
|
154022
154147
|
channel2_nudge_claimed_at INTEGER DEFAULT 0,
|
|
154148
|
+
channel2_nudge_claim_token TEXT DEFAULT '',
|
|
154023
154149
|
last_emergency_input_sample INTEGER DEFAULT 0,
|
|
154024
154150
|
last_transform_error TEXT DEFAULT '',
|
|
154025
154151
|
nudge_anchor_message_id TEXT DEFAULT '',
|
|
@@ -154266,6 +154392,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
154266
154392
|
ensureColumn(db, "session_meta", "last_nudge_level", "TEXT DEFAULT ''");
|
|
154267
154393
|
ensureColumn(db, "session_meta", "channel2_nudge_state", "TEXT DEFAULT ''");
|
|
154268
154394
|
ensureColumn(db, "session_meta", "channel2_nudge_claimed_at", "INTEGER DEFAULT 0");
|
|
154395
|
+
ensureColumn(db, "session_meta", "channel2_nudge_claim_token", "TEXT DEFAULT ''");
|
|
154269
154396
|
ensureColumn(db, "session_meta", "last_emergency_input_sample", "INTEGER DEFAULT 0");
|
|
154270
154397
|
ensureColumn(db, "session_meta", "last_transform_error", "TEXT DEFAULT ''");
|
|
154271
154398
|
ensureColumn(db, "session_meta", "nudge_anchor_message_id", "TEXT DEFAULT ''");
|
|
@@ -154478,6 +154605,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
154478
154605
|
ON transform_decisions(session_id, harness);
|
|
154479
154606
|
`);
|
|
154480
154607
|
ensureColumn(db, "tags", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
154608
|
+
ensureColumn(db, "message_history_index", "dirty_floor_ordinal", "INTEGER NOT NULL DEFAULT 0");
|
|
154481
154609
|
ensureColumn(db, "pending_ops", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
154482
154610
|
ensureColumn(db, "source_contents", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
154483
154611
|
ensureColumn(db, "compartments", "harness", "TEXT NOT NULL DEFAULT 'opencode'");
|
|
@@ -154492,7 +154620,7 @@ CREATE INDEX IF NOT EXISTS idx_dream_queue_pending ON dream_queue(started_at, en
|
|
|
154492
154620
|
function healWedgedChannel2Claims(db) {
|
|
154493
154621
|
try {
|
|
154494
154622
|
const staleBefore = Date.now() - CHANNEL2_CLAIM_TTL_MS;
|
|
154495
|
-
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);
|
|
154496
154624
|
} catch {}
|
|
154497
154625
|
}
|
|
154498
154626
|
function openDatabase(dbPathOrOptions) {
|
|
@@ -155226,19 +155354,42 @@ function getChannel2NudgeState(db, sessionId) {
|
|
|
155226
155354
|
const raw = result.channel2_nudge_state;
|
|
155227
155355
|
return raw === "pending" || raw === "claimed" || raw === "delivered" ? raw : "";
|
|
155228
155356
|
}
|
|
155229
|
-
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;
|
|
155230
155369
|
db.transaction(() => {
|
|
155231
155370
|
ensureSessionMetaRow(db, sessionId);
|
|
155232
|
-
const claimedAt =
|
|
155233
|
-
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;
|
|
155234
155374
|
})();
|
|
155375
|
+
return changed;
|
|
155235
155376
|
}
|
|
155236
|
-
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) {
|
|
155237
155387
|
let changed = false;
|
|
155238
155388
|
db.transaction(() => {
|
|
155239
155389
|
ensureSessionMetaRow(db, sessionId);
|
|
155240
155390
|
const claimedAt = to === "claimed" ? Date.now() : 0;
|
|
155241
|
-
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);
|
|
155242
155393
|
changed = (result.changes ?? 0) > 0;
|
|
155243
155394
|
})();
|
|
155244
155395
|
return changed;
|
|
@@ -166114,12 +166265,41 @@ function pushNotification(type, payload, sessionId) {
|
|
|
166114
166265
|
queue = kept.sort((a, b) => a.id - b.id);
|
|
166115
166266
|
}
|
|
166116
166267
|
}
|
|
166117
|
-
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
|
+
}
|
|
166118
166298
|
const matchesClient = (notification) => sessionId === undefined || notification.sessionId === undefined || notification.sessionId === sessionId;
|
|
166119
|
-
if (
|
|
166120
|
-
queue = queue.filter((notification) => !(notification.id <=
|
|
166299
|
+
if (sessionCursor > 0) {
|
|
166300
|
+
queue = queue.filter((notification) => !(notification.id <= sessionCursor && matchesClient(notification)));
|
|
166121
166301
|
}
|
|
166122
|
-
return queue.filter((notification) => notification.id >
|
|
166302
|
+
return queue.filter((notification) => notification.id > sessionCursor && matchesClient(notification));
|
|
166123
166303
|
}
|
|
166124
166304
|
function isTuiConnected(sessionId) {
|
|
166125
166305
|
if (sinks.size === 0)
|
|
@@ -167958,6 +168138,14 @@ function getSaveEmbeddingStatement(db) {
|
|
|
167958
168138
|
}
|
|
167959
168139
|
return stmt;
|
|
167960
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
|
+
}
|
|
167961
168149
|
function getLoadAllEmbeddingsStatement(db) {
|
|
167962
168150
|
let stmt = loadAllEmbeddingsStatements.get(db);
|
|
167963
168151
|
if (!stmt) {
|
|
@@ -167986,6 +168174,10 @@ function saveEmbedding(db, memoryId, embedding, modelId) {
|
|
|
167986
168174
|
const blob = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
|
|
167987
168175
|
getSaveEmbeddingStatement(db).run(memoryId, blob, modelId);
|
|
167988
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
|
+
}
|
|
167989
168181
|
function loadAllEmbeddings(db, projectPath, modelId) {
|
|
167990
168182
|
const rows = getLoadAllEmbeddingsStatement(db).all(projectPath, modelId).filter(isEmbeddingRow);
|
|
167991
168183
|
const embeddings = new Map;
|
|
@@ -168017,9 +168209,10 @@ function getMemoryEmbedCoverage(db, projectPath, modelId) {
|
|
|
168017
168209
|
embedded: typeof row?.embedded === "number" ? row.embedded : 0
|
|
168018
168210
|
};
|
|
168019
168211
|
}
|
|
168020
|
-
var saveEmbeddingStatements, loadAllEmbeddingsStatements, deleteEmbeddingStatements, getStoredModelIdStatements, clearAllEmbeddingsStatements, clearModelEmbeddingsStatements, getDistinctStoredModelIdsStatements;
|
|
168212
|
+
var saveEmbeddingStatements, saveEmbeddingIfHashMatchesStatements, loadAllEmbeddingsStatements, deleteEmbeddingStatements, getStoredModelIdStatements, clearAllEmbeddingsStatements, clearModelEmbeddingsStatements, getDistinctStoredModelIdsStatements;
|
|
168021
168213
|
var init_storage_memory_embeddings = __esm(() => {
|
|
168022
168214
|
saveEmbeddingStatements = new WeakMap;
|
|
168215
|
+
saveEmbeddingIfHashMatchesStatements = new WeakMap;
|
|
168023
168216
|
loadAllEmbeddingsStatements = new WeakMap;
|
|
168024
168217
|
deleteEmbeddingStatements = new WeakMap;
|
|
168025
168218
|
getStoredModelIdStatements = new WeakMap;
|
|
@@ -168844,12 +169037,12 @@ function isUnembeddedMemoryRow(row) {
|
|
|
168844
169037
|
if (row === null || typeof row !== "object")
|
|
168845
169038
|
return false;
|
|
168846
169039
|
const candidate = row;
|
|
168847
|
-
return typeof candidate.id === "number" && typeof candidate.content === "string";
|
|
169040
|
+
return typeof candidate.id === "number" && typeof candidate.content === "string" && typeof candidate.normalized_hash === "string";
|
|
168848
169041
|
}
|
|
168849
169042
|
function getLoadUnembeddedMemoriesStatement(db) {
|
|
168850
169043
|
let stmt = loadUnembeddedMemoriesStatements.get(db);
|
|
168851
169044
|
if (!stmt) {
|
|
168852
|
-
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 ?");
|
|
168853
169046
|
loadUnembeddedMemoriesStatements.set(db, stmt);
|
|
168854
169047
|
}
|
|
168855
169048
|
return stmt;
|
|
@@ -168872,8 +169065,9 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
168872
169065
|
const embedding = result.vectors[index];
|
|
168873
169066
|
if (!embedding)
|
|
168874
169067
|
continue;
|
|
168875
|
-
|
|
168876
|
-
|
|
169068
|
+
if (saveEmbeddingIfHashMatches(db, memory.id, embedding, result.modelId, memory.normalized_hash)) {
|
|
169069
|
+
embeddedCount += 1;
|
|
169070
|
+
}
|
|
168877
169071
|
}
|
|
168878
169072
|
})();
|
|
168879
169073
|
return embeddedCount;
|
|
@@ -169339,6 +169533,9 @@ function isMemorySourceType(value) {
|
|
|
169339
169533
|
function isVerificationStatus(value) {
|
|
169340
169534
|
return typeof value === "string" && value in VERIFICATION_STATUS_LOOKUP;
|
|
169341
169535
|
}
|
|
169536
|
+
function isUniqueConstraintError(error51) {
|
|
169537
|
+
return error51 instanceof Error && "code" in error51 && error51.code === "SQLITE_CONSTRAINT_UNIQUE";
|
|
169538
|
+
}
|
|
169342
169539
|
function isNullableString(value) {
|
|
169343
169540
|
return value === null || typeof value === "string";
|
|
169344
169541
|
}
|
|
@@ -169504,28 +169701,55 @@ function getMemoryCountsByStatusStatement(db) {
|
|
|
169504
169701
|
}
|
|
169505
169702
|
return stmt;
|
|
169506
169703
|
}
|
|
169507
|
-
function
|
|
169508
|
-
const now = Date.now();
|
|
169509
|
-
const normalizedHash = computeNormalizedHash(input.content);
|
|
169704
|
+
function buildInsertMemoryValues(input, normalizedHash, now, includeImportance) {
|
|
169510
169705
|
const insertValues = [
|
|
169511
169706
|
input.projectPath,
|
|
169512
169707
|
input.category,
|
|
169513
169708
|
input.content,
|
|
169514
169709
|
normalizedHash
|
|
169515
169710
|
];
|
|
169516
|
-
if (
|
|
169711
|
+
if (includeImportance) {
|
|
169517
169712
|
insertValues.push(input.importance ?? 50);
|
|
169518
169713
|
}
|
|
169519
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);
|
|
169520
|
-
|
|
169521
|
-
|
|
169522
|
-
|
|
169715
|
+
return insertValues;
|
|
169716
|
+
}
|
|
169717
|
+
function loadInsertedMemory(db, rowid) {
|
|
169718
|
+
const inserted = getMemoryById(db, Number(rowid));
|
|
169523
169719
|
if (!inserted) {
|
|
169524
169720
|
throw new Error("Failed to load inserted memory row");
|
|
169525
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);
|
|
169526
169731
|
invalidateProject(input.projectPath);
|
|
169527
169732
|
return inserted;
|
|
169528
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
|
+
}
|
|
169529
169753
|
function getMemoryByHash(db, projectPath, category, normalizedHash) {
|
|
169530
169754
|
const result = getMemoryByHashStatement(db).get(projectPath, category, normalizedHash);
|
|
169531
169755
|
if (!isMemoryRow(result)) {
|
|
@@ -170078,7 +170302,7 @@ import path6 from "node:path";
|
|
|
170078
170302
|
import { promisify } from "node:util";
|
|
170079
170303
|
async function runGit(cwd, args) {
|
|
170080
170304
|
try {
|
|
170081
|
-
const result = await
|
|
170305
|
+
const result = await execFileForVerificationPaths("git", args, {
|
|
170082
170306
|
cwd,
|
|
170083
170307
|
timeout: GIT_TIMEOUT_MS2,
|
|
170084
170308
|
maxBuffer: 16 * 1024 * 1024,
|
|
@@ -170258,9 +170482,10 @@ async function normalizeVerificationFiles(args) {
|
|
|
170258
170482
|
gitRoot
|
|
170259
170483
|
};
|
|
170260
170484
|
}
|
|
170261
|
-
var execFileAsync, GIT_TIMEOUT_MS2 = 1e4;
|
|
170485
|
+
var execFileAsync, GIT_TIMEOUT_MS2 = 1e4, defaultExecFileForVerificationPaths = async (file2, args, options) => await execFileAsync(file2, [...args], options), execFileForVerificationPaths;
|
|
170262
170486
|
var init_verification_paths = __esm(() => {
|
|
170263
170487
|
execFileAsync = promisify(execFile);
|
|
170488
|
+
execFileForVerificationPaths = defaultExecFileForVerificationPaths;
|
|
170264
170489
|
});
|
|
170265
170490
|
|
|
170266
170491
|
// src/features/magic-context/memory/index.ts
|
|
@@ -180430,6 +180655,23 @@ function recursiveByteLength(value) {
|
|
|
180430
180655
|
}
|
|
180431
180656
|
return String(value).length;
|
|
180432
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
|
+
}
|
|
180433
180675
|
function rawPartVersion(part) {
|
|
180434
180676
|
return part.__magicContextPartUpdatedAt ?? part.updated_at ?? part.updatedAt ?? part.version ?? part.revision ?? "";
|
|
180435
180677
|
}
|
|
@@ -180777,13 +181019,12 @@ function buildTrueRawTokenIndex(sessionId, messages, options) {
|
|
|
180777
181019
|
function partContentFingerprint(part) {
|
|
180778
181020
|
if (!isRecord(part))
|
|
180779
181021
|
return `${typeof part}:${recursiveByteLength(part)}`;
|
|
180780
|
-
const type = partType(part);
|
|
180781
181022
|
const tool = toolSignalFromPart(part);
|
|
180782
181023
|
if (tool) {
|
|
180783
|
-
return
|
|
181024
|
+
return contentStringsHash([tool.inputText, tool.outputText]);
|
|
180784
181025
|
}
|
|
180785
181026
|
const text = firstStringField(part, ["text", "thinking", "reasoning", "content", "url"]) ?? "";
|
|
180786
|
-
return
|
|
181027
|
+
return contentStringsHash([text]);
|
|
180787
181028
|
}
|
|
180788
181029
|
function computeRawRangeFingerprint(messages, startInclusive, endExclusive) {
|
|
180789
181030
|
const pieces = [];
|
|
@@ -180808,7 +181049,7 @@ function invalidateTrueRawTokenCache(args) {
|
|
|
180808
181049
|
}
|
|
180809
181050
|
args.reason;
|
|
180810
181051
|
}
|
|
180811
|
-
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;
|
|
180812
181053
|
var init_read_session_true_raw_tokens = __esm(() => {
|
|
180813
181054
|
init_read_session_formatting();
|
|
180814
181055
|
MAX_MESSAGE_CACHE_KEY_BYTES = 64 * 1024 * 1024;
|
|
@@ -184819,6 +185060,15 @@ async function executeContextRecompWithResult(deps, options = {}) {
|
|
|
184819
185060
|
published: false
|
|
184820
185061
|
};
|
|
184821
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
|
+
}
|
|
184822
185072
|
const renewal = startLeaseRenewal(deps, holderId);
|
|
184823
185073
|
const runnerDeps = withPublishedCallback({ ...deps, compartmentLeaseHolderId: holderId });
|
|
184824
185074
|
const promise2 = options.range ? executePartialRecompInternal(runnerDeps, options.range) : executeContextRecompInternal(runnerDeps);
|
|
@@ -186333,12 +186583,108 @@ function migrateLegacyExperimental(rawConfig, warnings) {
|
|
|
186333
186583
|
}
|
|
186334
186584
|
|
|
186335
186585
|
// src/config/project-security.ts
|
|
186586
|
+
init_magic_context();
|
|
186336
186587
|
var HIDDEN_AGENT_KEYS = ["historian", "dreamer", "sidekick"];
|
|
186588
|
+
var HISTORIAN_USER_ONLY_FIELDS = ["model", "fallback_models"];
|
|
186337
186589
|
var AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools", "system_prompt"];
|
|
186338
186590
|
var EMBEDDING_DESTINATION_FIELDS = ["endpoint", "provider"];
|
|
186339
|
-
|
|
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) {
|
|
186340
186595
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
186341
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
|
+
}
|
|
186342
186688
|
function stripUnsafeProjectConfigFields(projectRaw) {
|
|
186343
186689
|
const warnings = [];
|
|
186344
186690
|
if ("auto_update" in projectRaw) {
|
|
@@ -186354,7 +186700,7 @@ function stripUnsafeProjectConfigFields(projectRaw) {
|
|
|
186354
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).");
|
|
186355
186701
|
}
|
|
186356
186702
|
const embedding = projectRaw.embedding;
|
|
186357
|
-
if (
|
|
186703
|
+
if (isPlainObject2(embedding)) {
|
|
186358
186704
|
const removed = [];
|
|
186359
186705
|
for (const field of EMBEDDING_DESTINATION_FIELDS) {
|
|
186360
186706
|
if (field in embedding) {
|
|
@@ -186366,9 +186712,22 @@ function stripUnsafeProjectConfigFields(projectRaw) {
|
|
|
186366
186712
|
warnings.push(`Ignoring embedding.${removed.join("/")} from project config ` + "(security: a repository cannot choose where private text is embedded).");
|
|
186367
186713
|
}
|
|
186368
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
|
+
}
|
|
186369
186728
|
for (const agentKey of HIDDEN_AGENT_KEYS) {
|
|
186370
186729
|
const block = projectRaw[agentKey];
|
|
186371
|
-
if (!
|
|
186730
|
+
if (!isPlainObject2(block))
|
|
186372
186731
|
continue;
|
|
186373
186732
|
const removed = [];
|
|
186374
186733
|
for (const field of AGENT_ESCALATION_FIELDS) {
|
|
@@ -186383,6 +186742,101 @@ function stripUnsafeProjectConfigFields(projectRaw) {
|
|
|
186383
186742
|
}
|
|
186384
186743
|
return warnings;
|
|
186385
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
|
+
}
|
|
186386
186840
|
function normalizeEndpoint(value) {
|
|
186387
186841
|
if (typeof value !== "string")
|
|
186388
186842
|
return;
|
|
@@ -186391,13 +186845,13 @@ function normalizeEndpoint(value) {
|
|
|
186391
186845
|
}
|
|
186392
186846
|
function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
|
|
186393
186847
|
const projectEmbedding = projectRaw.embedding;
|
|
186394
|
-
if (!
|
|
186848
|
+
if (!isPlainObject2(projectEmbedding))
|
|
186395
186849
|
return [];
|
|
186396
186850
|
const redirectsEndpoint = "endpoint" in projectEmbedding;
|
|
186397
186851
|
if (!redirectsEndpoint)
|
|
186398
186852
|
return [];
|
|
186399
186853
|
const userEmbedding = userRaw?.embedding;
|
|
186400
|
-
if (
|
|
186854
|
+
if (isPlainObject2(userEmbedding)) {
|
|
186401
186855
|
const projectEndpoint = normalizeEndpoint(projectEmbedding.endpoint);
|
|
186402
186856
|
const userEndpoint = normalizeEndpoint(userEmbedding.endpoint);
|
|
186403
186857
|
if (projectEndpoint !== undefined && projectEndpoint === userEndpoint) {
|
|
@@ -186408,7 +186862,7 @@ function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
|
|
|
186408
186862
|
if (providesOwnKey)
|
|
186409
186863
|
return [];
|
|
186410
186864
|
const mergedEmbedding = mergedRaw.embedding;
|
|
186411
|
-
if (!
|
|
186865
|
+
if (!isPlainObject2(mergedEmbedding))
|
|
186412
186866
|
return [];
|
|
186413
186867
|
if (!("api_key" in mergedEmbedding))
|
|
186414
186868
|
return [];
|
|
@@ -186419,7 +186873,7 @@ function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
|
|
|
186419
186873
|
}
|
|
186420
186874
|
|
|
186421
186875
|
// src/config/prune-config-leaf.ts
|
|
186422
|
-
function
|
|
186876
|
+
function isPlainObject3(value) {
|
|
186423
186877
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
186424
186878
|
}
|
|
186425
186879
|
function pruneNestedConfigLeaf(block, relativePath) {
|
|
@@ -186430,7 +186884,7 @@ function pruneNestedConfigLeaf(block, relativePath) {
|
|
|
186430
186884
|
for (let i = 0;i < relativePath.length - 1; i++) {
|
|
186431
186885
|
const seg = String(relativePath[i]);
|
|
186432
186886
|
const child = cursor[seg];
|
|
186433
|
-
if (!
|
|
186887
|
+
if (!isPlainObject3(child)) {
|
|
186434
186888
|
if (!(seg in cursor))
|
|
186435
186889
|
return null;
|
|
186436
186890
|
delete cursor[seg];
|
|
@@ -186782,6 +187236,7 @@ function loadPluginConfigDetailed(directory) {
|
|
|
186782
187236
|
const projectLoaded = projectDetected.format !== "none" ? loadConfigFileDetailed(projectDetected.path, "project") : projectLegacyFallback.source ? loadConfigFileDetailed(projectLegacyFallback.source.path, "project") : null;
|
|
186783
187237
|
const allWarnings = [];
|
|
186784
187238
|
let mergedRaw = {};
|
|
187239
|
+
const trustedBaseConfig = parsePluginConfig(userLoaded?.config ?? {});
|
|
186785
187240
|
if (userLegacyFallback.source) {
|
|
186786
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.`);
|
|
186787
187242
|
} else if (legacyUserUnmigrated) {
|
|
@@ -186806,6 +187261,13 @@ function loadPluginConfigDetailed(directory) {
|
|
|
186806
187261
|
for (const warning of dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userLoaded?.config)) {
|
|
186807
187262
|
allWarnings.push(`[project config] ${warning}`);
|
|
186808
187263
|
}
|
|
187264
|
+
for (const warning of constrainProjectThresholdOverrides({
|
|
187265
|
+
mergedRaw,
|
|
187266
|
+
projectRaw,
|
|
187267
|
+
trustedBaseConfig
|
|
187268
|
+
})) {
|
|
187269
|
+
allWarnings.push(`[project config] ${warning}`);
|
|
187270
|
+
}
|
|
186809
187271
|
}
|
|
186810
187272
|
const recoveredTopLevelKeys = [];
|
|
186811
187273
|
const config2 = parsePluginConfig(mergedRaw, recoveredTopLevelKeys);
|
|
@@ -188529,6 +188991,14 @@ function renewLease(db, holderId, leaseKey = DREAMING_LEASE_KEY) {
|
|
|
188529
188991
|
return true;
|
|
188530
188992
|
});
|
|
188531
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
|
+
}
|
|
188532
189002
|
var LEASE_HEARTBEAT_INTERVAL_MS = 60 * 1000;
|
|
188533
189003
|
function startLeaseHeartbeat(db, holderId, leaseKey, onLost, intervalMs = LEASE_HEARTBEAT_INTERVAL_MS) {
|
|
188534
189004
|
let lost = false;
|
|
@@ -188687,8 +189157,7 @@ class OpenCodeRetrospectiveRawProvider {
|
|
|
188687
189157
|
LEFT JOIN session_meta m ON m.session_id = sp.session_id
|
|
188688
189158
|
WHERE sp.project_path = ? AND sp.harness = 'opencode'
|
|
188689
189159
|
AND COALESCE(m.is_subagent, 0) = 0
|
|
188690
|
-
ORDER BY sp.updated_at
|
|
188691
|
-
LIMIT ?`).all(projectIdentity, RETROSPECTIVE_MAX_SESSIONS_PER_RUN);
|
|
189160
|
+
ORDER BY sp.updated_at ASC, sp.session_id ASC`).all(projectIdentity);
|
|
188692
189161
|
return rows.map((row) => ({
|
|
188693
189162
|
sessionId: row.session_id,
|
|
188694
189163
|
updatedAt: typeof row.updated_at === "number" ? row.updated_at : undefined
|
|
@@ -188713,6 +189182,12 @@ class OpenCodeRetrospectiveRawProvider {
|
|
|
188713
189182
|
return { messages: [], truncated: false };
|
|
188714
189183
|
}
|
|
188715
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
|
+
}
|
|
188716
189191
|
readUserMessagesBefore(sessionId, beforeMs, count) {
|
|
188717
189192
|
const db = this.resolveDb();
|
|
188718
189193
|
if (!db)
|
|
@@ -188734,13 +189209,28 @@ class OpenCodeRetrospectiveRawProvider {
|
|
|
188734
189209
|
async function readRetrospectiveScanWindow(provider, projectIdentity, watermarkMs, overlapUserCount, options) {
|
|
188735
189210
|
const maxMessages = options?.maxMessagesPerRun ?? RETROSPECTIVE_MAX_MESSAGES_PER_RUN;
|
|
188736
189211
|
const capPerSession = options?.capPerSession ?? RETROSPECTIVE_MAX_MESSAGES_PER_SESSION;
|
|
188737
|
-
const
|
|
189212
|
+
const sessionLimit = Math.max(1, Math.floor(options?.maxSessionsPerRun ?? RETROSPECTIVE_MAX_SESSIONS_PER_RUN));
|
|
188738
189213
|
try {
|
|
188739
|
-
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;
|
|
188740
189231
|
const sinceReads = [];
|
|
188741
|
-
|
|
188742
|
-
|
|
188743
|
-
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))));
|
|
188744
189234
|
}
|
|
188745
189235
|
let saturatedFrontier = Number.POSITIVE_INFINITY;
|
|
188746
189236
|
for (const read of sinceReads) {
|
|
@@ -188762,9 +189252,14 @@ async function readRetrospectiveScanWindow(provider, projectIdentity, watermarkM
|
|
|
188762
189252
|
if (firstDropped) {
|
|
188763
189253
|
frontier = Math.min(frontier, firstDropped.ts - 1);
|
|
188764
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
|
+
}
|
|
188765
189260
|
maxScannedTs = Math.max(watermarkMs, Math.min(maxScannedTs, frontier));
|
|
188766
189261
|
const keptSessionIds = new Set(keptSince.map((message) => message.sessionId));
|
|
188767
|
-
const overlapSessions =
|
|
189262
|
+
const overlapSessions = sessionsToRead.filter((session) => keptSessionIds.has(session.sessionId));
|
|
188768
189263
|
const overlapBatches = overlapUserCount > 0 && watermarkMs > 0 ? await Promise.all(overlapSessions.map((session) => provider.readUserMessagesBefore(session.sessionId, watermarkMs, overlapUserCount))) : [];
|
|
188769
189264
|
const seen = new Set;
|
|
188770
189265
|
const merged = [];
|
|
@@ -188792,6 +189287,24 @@ function readOpenCodeMessagesSince(db, sessionId, sinceMs, capPerSession) {
|
|
|
188792
189287
|
const kept = truncated ? rows.slice(0, limit) : rows;
|
|
188793
189288
|
return { messages: normalizeOpenCodeRows(db, sessionId, kept), truncated };
|
|
188794
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
|
+
}
|
|
188795
189308
|
function readOpenCodeUserMessagesBefore(db, sessionId, beforeMs, count) {
|
|
188796
189309
|
const want = Math.max(1, Math.floor(count));
|
|
188797
189310
|
const window = Math.max(want * 5, 32);
|
|
@@ -189410,6 +189923,39 @@ function hasShareabilitySensitiveText(text) {
|
|
|
189410
189923
|
init_memory();
|
|
189411
189924
|
init_subagent_token_capture();
|
|
189412
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
|
+
|
|
189413
189959
|
// src/features/magic-context/dreamer/classify-prompt.ts
|
|
189414
189960
|
var SCORING_GUIDANCE = `### How to score importance (1-100)
|
|
189415
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.
|
|
@@ -189476,14 +190022,15 @@ ${renderPool(args.memories)}`;
|
|
|
189476
190022
|
var SCOPES = new Set(["project", "ecosystem", "universe"]);
|
|
189477
190023
|
function parseClassifyManifest(text) {
|
|
189478
190024
|
const out = [];
|
|
189479
|
-
|
|
190025
|
+
const body = extractCompleteManifestBody(text, "classify");
|
|
190026
|
+
for (const m of body.matchAll(/<memory\b([^>]*)\/?>/g)) {
|
|
189480
190027
|
const attrs = m[1];
|
|
189481
190028
|
const idMatch = attrs.match(/\bid\s*=\s*"(\d+)"/);
|
|
189482
190029
|
if (!idMatch)
|
|
189483
|
-
|
|
190030
|
+
throw new Error("classify manifest entry missing numeric id");
|
|
189484
190031
|
const id = Number.parseInt(idMatch[1], 10);
|
|
189485
190032
|
if (!Number.isInteger(id))
|
|
189486
|
-
|
|
190033
|
+
throw new Error("classify manifest entry missing numeric id");
|
|
189487
190034
|
const entry = { id };
|
|
189488
190035
|
const impMatch = attrs.match(/\bimportance\s*=\s*"(\d+)"/);
|
|
189489
190036
|
if (impMatch) {
|
|
@@ -189492,18 +190039,23 @@ function parseClassifyManifest(text) {
|
|
|
189492
190039
|
entry.importance = Math.max(1, Math.min(100, imp));
|
|
189493
190040
|
}
|
|
189494
190041
|
const scopeMatch = attrs.match(/\bscope\s*=\s*"([a-z]+)"/i);
|
|
189495
|
-
if (scopeMatch
|
|
189496
|
-
|
|
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;
|
|
189497
190047
|
}
|
|
189498
190048
|
const shareMatch = attrs.match(/\bshareable\s*=\s*"(true|false|1|0)"/i);
|
|
189499
190049
|
if (shareMatch) {
|
|
189500
190050
|
const v = shareMatch[1].toLowerCase();
|
|
189501
190051
|
entry.shareable = v === "true" || v === "1";
|
|
189502
190052
|
}
|
|
189503
|
-
if (entry.importance
|
|
189504
|
-
|
|
190053
|
+
if (entry.importance === undefined && !entry.scope && entry.shareable === undefined) {
|
|
190054
|
+
throw new Error(`classify manifest entry ${id} missing classification fields`);
|
|
189505
190055
|
}
|
|
190056
|
+
out.push(entry);
|
|
189506
190057
|
}
|
|
190058
|
+
assertNoDuplicateManifestIds(out.map((entry) => entry.id), "classify");
|
|
189507
190059
|
return out;
|
|
189508
190060
|
}
|
|
189509
190061
|
|
|
@@ -189638,9 +190190,13 @@ async function classifyOneChunk(args, chunk, anchors, sliceMs, signal) {
|
|
|
189638
190190
|
});
|
|
189639
190191
|
},
|
|
189640
190192
|
validateOutput: (messages) => {
|
|
190193
|
+
if (hasLengthCappedOutput(messages)) {
|
|
190194
|
+
throw new Error("classify returned length-capped output");
|
|
190195
|
+
}
|
|
189641
190196
|
const text = extractLatestAssistantText(messages);
|
|
189642
190197
|
if (!text)
|
|
189643
190198
|
throw new Error("classify returned no output");
|
|
190199
|
+
parseClassifyManifest(text);
|
|
189644
190200
|
return text;
|
|
189645
190201
|
}
|
|
189646
190202
|
});
|
|
@@ -189666,17 +190222,13 @@ async function classifyOneChunk(args, chunk, anchors, sliceMs, signal) {
|
|
|
189666
190222
|
}
|
|
189667
190223
|
function applyClassifications(args, chunk, manifestText) {
|
|
189668
190224
|
const byId = new Map(chunk.map((m) => [m.id, m]));
|
|
189669
|
-
const parsed = parseClassifyManifest(manifestText)
|
|
190225
|
+
const parsed = parseClassifyManifest(manifestText);
|
|
190226
|
+
assertManifestCoversExactly(parsed.map((entry) => entry.id), new Set(byId.keys()), "classify");
|
|
189670
190227
|
if (parsed.length === 0)
|
|
189671
190228
|
return { classified: 0, changed: 0 };
|
|
189672
190229
|
let classified = 0;
|
|
189673
190230
|
let changed = 0;
|
|
189674
|
-
|
|
189675
|
-
args.db.transaction(() => {
|
|
189676
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
189677
|
-
leaseLost = true;
|
|
189678
|
-
return;
|
|
189679
|
-
}
|
|
190231
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
189680
190232
|
for (const p of parsed) {
|
|
189681
190233
|
const memory = byId.get(p.id);
|
|
189682
190234
|
if (!memory)
|
|
@@ -189691,9 +190243,7 @@ function applyClassifications(args, chunk, manifestText) {
|
|
|
189691
190243
|
if (didChange)
|
|
189692
190244
|
changed += 1;
|
|
189693
190245
|
}
|
|
189694
|
-
})
|
|
189695
|
-
if (leaseLost)
|
|
189696
|
-
throw new Error("Dream lease lost during classify commit");
|
|
190246
|
+
});
|
|
189697
190247
|
return { classified, changed };
|
|
189698
190248
|
}
|
|
189699
190249
|
function recordInvocation(args, startedAt, params) {
|
|
@@ -189743,9 +190293,11 @@ var SMART_NOTE_CHECK_LIVENESS_RECHECK_MS = 24 * 60 * 60 * 1000;
|
|
|
189743
190293
|
|
|
189744
190294
|
class SmartNoteNetworkError extends Error {
|
|
189745
190295
|
isSmartNoteNetworkError = true;
|
|
189746
|
-
|
|
190296
|
+
terminal;
|
|
190297
|
+
constructor(message, options = {}) {
|
|
189747
190298
|
super(message);
|
|
189748
190299
|
this.name = "SmartNoteNetworkError";
|
|
190300
|
+
this.terminal = options.terminal ?? false;
|
|
189749
190301
|
}
|
|
189750
190302
|
}
|
|
189751
190303
|
|
|
@@ -189759,6 +190311,9 @@ class SmartNoteSecurityError extends Error {
|
|
|
189759
190311
|
function isSmartNoteNetworkError(error51) {
|
|
189760
190312
|
return error51 instanceof SmartNoteNetworkError || error51 instanceof Error && (error51.name === "SmartNoteNetworkError" || error51.message.includes("SmartNoteNetworkError") || error51.message.includes("SMART_NOTE_NETWORK"));
|
|
189761
190313
|
}
|
|
190314
|
+
function isTerminalSmartNoteNetworkError(error51) {
|
|
190315
|
+
return error51 instanceof SmartNoteNetworkError && error51.terminal;
|
|
190316
|
+
}
|
|
189762
190317
|
function parseSmartNoteManifest(json2) {
|
|
189763
190318
|
if (!json2)
|
|
189764
190319
|
return { capabilities: [] };
|
|
@@ -189788,6 +190343,7 @@ function stringArray(value) {
|
|
|
189788
190343
|
var DNS_TIMEOUT_MS = 3000;
|
|
189789
190344
|
var DEFAULT_HTTP_TIMEOUT_MS = 5000;
|
|
189790
190345
|
var DEFAULT_HTTP_BODY_LIMIT_BYTES = 64 * 1024;
|
|
190346
|
+
var MAX_HTTP_ADDRESS_CANDIDATES = 4;
|
|
189791
190347
|
var defaultResolver = {
|
|
189792
190348
|
async lookup(hostname3, signal) {
|
|
189793
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 })));
|
|
@@ -189824,18 +190380,21 @@ async function guardedSmartNoteHttpGet(input, options) {
|
|
|
189824
190380
|
});
|
|
189825
190381
|
const timeoutMs = options.timeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;
|
|
189826
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);
|
|
189827
190385
|
let lastError;
|
|
189828
|
-
for (const candidate of
|
|
190386
|
+
for (const candidate of candidates) {
|
|
189829
190387
|
try {
|
|
189830
|
-
return await
|
|
190388
|
+
return await requestAddress(validation, candidate, {
|
|
189831
190389
|
signal: options.signal,
|
|
189832
190390
|
timeoutMs,
|
|
189833
190391
|
bodyLimitBytes
|
|
189834
190392
|
});
|
|
189835
190393
|
} catch (error51) {
|
|
189836
190394
|
lastError = error51;
|
|
189837
|
-
if (error51 instanceof SmartNoteSecurityError || options.signal.aborted)
|
|
190395
|
+
if (error51 instanceof SmartNoteSecurityError || options.signal.aborted || isTerminalSmartNoteNetworkError(error51)) {
|
|
189838
190396
|
throw error51;
|
|
190397
|
+
}
|
|
189839
190398
|
}
|
|
189840
190399
|
}
|
|
189841
190400
|
throw toNetworkError(lastError, "all validated addresses failed");
|
|
@@ -189905,7 +190464,9 @@ function requestValidatedAddress(validation, candidate, options) {
|
|
|
189905
190464
|
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
189906
190465
|
bytes += buf.byteLength;
|
|
189907
190466
|
if (bytes > options.bodyLimitBytes) {
|
|
189908
|
-
|
|
190467
|
+
reject(new SmartNoteNetworkError("SMART_NOTE_NETWORK: response body too large", { terminal: true }));
|
|
190468
|
+
response.destroy();
|
|
190469
|
+
request2.destroy();
|
|
189909
190470
|
return;
|
|
189910
190471
|
}
|
|
189911
190472
|
chunks.push(buf);
|
|
@@ -189922,10 +190483,16 @@ function requestValidatedAddress(validation, candidate, options) {
|
|
|
189922
190483
|
resolve4({ status, body: Buffer.concat(chunks).toString("utf8") });
|
|
189923
190484
|
});
|
|
189924
190485
|
});
|
|
189925
|
-
const onAbort = () =>
|
|
190486
|
+
const onAbort = () => {
|
|
190487
|
+
reject(new SmartNoteNetworkError("SMART_NOTE_NETWORK: aborted"));
|
|
190488
|
+
request2.destroy();
|
|
190489
|
+
};
|
|
189926
190490
|
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
189927
190491
|
request2.on("timeout", () => {
|
|
189928
|
-
|
|
190492
|
+
reject(new SmartNoteNetworkError("SMART_NOTE_NETWORK: request timed out", {
|
|
190493
|
+
terminal: true
|
|
190494
|
+
}));
|
|
190495
|
+
request2.destroy();
|
|
189929
190496
|
});
|
|
189930
190497
|
request2.on("error", (error51) => {
|
|
189931
190498
|
options.signal.removeEventListener("abort", onAbort);
|
|
@@ -190246,6 +190813,20 @@ function withSandboxLock(fn) {
|
|
|
190246
190813
|
var DEFAULT_TIMEOUT_MS = 2000;
|
|
190247
190814
|
var DEFAULT_HEAP_LIMIT_BYTES = 8 * 1024 * 1024;
|
|
190248
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
|
+
}
|
|
190249
190830
|
async function runCompiledSmartNoteCheck(options) {
|
|
190250
190831
|
return withSandboxLock(() => runCompiledSmartNoteCheckLocked(options));
|
|
190251
190832
|
}
|
|
@@ -190253,17 +190834,24 @@ async function runCompiledSmartNoteCheckLocked(options) {
|
|
|
190253
190834
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
190254
190835
|
const controller = new AbortController;
|
|
190255
190836
|
const externalAbort = () => controller.abort(options.signal?.reason);
|
|
190256
|
-
options.signal?.
|
|
190837
|
+
if (options.signal?.aborted) {
|
|
190838
|
+
externalAbort();
|
|
190839
|
+
} else {
|
|
190840
|
+
options.signal?.addEventListener("abort", externalAbort, { once: true });
|
|
190841
|
+
}
|
|
190257
190842
|
const timer = setTimeout(() => controller.abort(new Error("smart-note check timed out")), timeoutMs);
|
|
190258
190843
|
try {
|
|
190844
|
+
throwIfRunAborted(controller.signal);
|
|
190845
|
+
const capabilities = resolveCapabilitiesForRun(options, controller.signal);
|
|
190259
190846
|
const deadline = Date.now() + timeoutMs;
|
|
190260
190847
|
const quickjs = await getAsyncModule();
|
|
190848
|
+
throwIfRunAborted(controller.signal);
|
|
190261
190849
|
const context = quickjs.newContext();
|
|
190262
190850
|
try {
|
|
190263
190851
|
context.runtime.setMemoryLimit(options.heapLimitBytes ?? DEFAULT_HEAP_LIMIT_BYTES);
|
|
190264
190852
|
context.runtime.setMaxStackSize(options.stackLimitBytes ?? DEFAULT_STACK_LIMIT_BYTES);
|
|
190265
190853
|
context.runtime.setInterruptHandler(() => controller.signal.aborted || Date.now() > deadline);
|
|
190266
|
-
installCapabilityObject(context,
|
|
190854
|
+
installCapabilityObject(context, capabilities);
|
|
190267
190855
|
disableAmbientDynamicCode(context);
|
|
190268
190856
|
const result = await evalCheck(context, options.compiledCheck);
|
|
190269
190857
|
const checkResult = result;
|
|
@@ -190416,6 +191004,7 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
190416
191004
|
}
|
|
190417
191005
|
}, {
|
|
190418
191006
|
timeoutMs: remainingMs,
|
|
191007
|
+
signal: args.signal,
|
|
190419
191008
|
fallbackModels: args.fallbackModels,
|
|
190420
191009
|
callContext: "dreamer:smart-note-compiler",
|
|
190421
191010
|
fetchOutput: async () => {
|
|
@@ -190440,7 +191029,8 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
190440
191029
|
}
|
|
190441
191030
|
const dryRun = await runCompiledSmartNoteCheck({
|
|
190442
191031
|
compiledCheck,
|
|
190443
|
-
|
|
191032
|
+
capabilityFactory: args.capabilityFactory,
|
|
191033
|
+
signal: args.signal,
|
|
190444
191034
|
timeoutMs: 2000
|
|
190445
191035
|
});
|
|
190446
191036
|
if (!dryRun.ok) {
|
|
@@ -190593,7 +191183,9 @@ function nextSmartNoteCheckDueAt(cron, options = {}) {
|
|
|
190593
191183
|
const rawNext = cron?.trim() ? nextDueAtMs(cron, now) : null;
|
|
190594
191184
|
const rawDelta = rawNext ? rawNext - now : SMART_NOTE_CHECK_DEFAULT_INTERVAL_MS;
|
|
190595
191185
|
const clamped = Math.min(ceilingMs, Math.max(floorMs, rawDelta));
|
|
190596
|
-
|
|
191186
|
+
const jittered = clamped + deterministicJitterMs(clamped, options.noteId, options.hash);
|
|
191187
|
+
const bounded = Math.min(ceilingMs, Math.max(floorMs, jittered));
|
|
191188
|
+
return now + bounded;
|
|
190597
191189
|
}
|
|
190598
191190
|
function deterministicJitterMs(intervalMs, noteId, hash2) {
|
|
190599
191191
|
const max = Math.min(60000, Math.floor(intervalMs * 0.1));
|
|
@@ -190763,9 +191355,9 @@ async function runDueCompiledSmartNoteChecks(args) {
|
|
|
190763
191355
|
try {
|
|
190764
191356
|
const result = await runCompiledSmartNoteCheck({
|
|
190765
191357
|
compiledCheck: note.compiledCheck,
|
|
190766
|
-
|
|
191358
|
+
capabilityFactory: (signal) => createSmartNoteCapabilities({
|
|
190767
191359
|
projectRoot: args.projectRoot,
|
|
190768
|
-
signal
|
|
191360
|
+
signal
|
|
190769
191361
|
}),
|
|
190770
191362
|
signal: controller.signal,
|
|
190771
191363
|
timeoutMs: Math.min(2000, remaining)
|
|
@@ -190830,6 +191422,24 @@ var MAX_COMPILE_PER_RUN = 5;
|
|
|
190830
191422
|
var MAX_FALLBACK_PER_RUN = 3;
|
|
190831
191423
|
var MAX_COMPILATION_FAILURES = 3;
|
|
190832
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
|
+
}
|
|
190833
191443
|
async function evaluateSmartNotes(args) {
|
|
190834
191444
|
const projectRoot = args.sessionDirectory ?? args.projectIdentity;
|
|
190835
191445
|
const pendingAtStart = getPendingSmartNotes(args.db, args.projectIdentity).length;
|
|
@@ -190838,6 +191448,7 @@ async function evaluateSmartNotes(args) {
|
|
|
190838
191448
|
return { surfaced: 0, pending: 0, ran: false };
|
|
190839
191449
|
}
|
|
190840
191450
|
let leaseLost = false;
|
|
191451
|
+
const leaseAbortController = new AbortController;
|
|
190841
191452
|
const leaseHeld = () => !leaseLost && peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey);
|
|
190842
191453
|
const assertLeaseHeld = (phase) => {
|
|
190843
191454
|
if (!leaseHeld()) {
|
|
@@ -190847,6 +191458,7 @@ async function evaluateSmartNotes(args) {
|
|
|
190847
191458
|
};
|
|
190848
191459
|
const heartbeat = startLeaseHeartbeat(args.db, args.holderId, args.leaseKey, () => {
|
|
190849
191460
|
leaseLost = true;
|
|
191461
|
+
leaseAbortController.abort(new Error("Dream lease lost during smart notes"));
|
|
190850
191462
|
log("[dreamer] smart notes: lease lost — aborting");
|
|
190851
191463
|
args.onLeaseLost?.("smart notes");
|
|
190852
191464
|
});
|
|
@@ -190869,7 +191481,7 @@ async function evaluateSmartNotes(args) {
|
|
|
190869
191481
|
break;
|
|
190870
191482
|
assertLeaseHeld("compile start");
|
|
190871
191483
|
didWork = true;
|
|
190872
|
-
const compiled = await compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld);
|
|
191484
|
+
const compiled = await compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld, leaseAbortController.signal);
|
|
190873
191485
|
if (compiled)
|
|
190874
191486
|
surfaced += 1;
|
|
190875
191487
|
}
|
|
@@ -190889,7 +191501,7 @@ async function evaluateSmartNotes(args) {
|
|
|
190889
191501
|
break;
|
|
190890
191502
|
assertLeaseHeld("fallback start");
|
|
190891
191503
|
didWork = true;
|
|
190892
|
-
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);
|
|
190893
191505
|
const now = Date.now();
|
|
190894
191506
|
assertLeaseHeld("fallback commit");
|
|
190895
191507
|
commitSmartNoteState(args.db, {
|
|
@@ -190915,9 +191527,8 @@ async function evaluateSmartNotes(args) {
|
|
|
190915
191527
|
heartbeat.stop();
|
|
190916
191528
|
}
|
|
190917
191529
|
}
|
|
190918
|
-
async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld) {
|
|
190919
|
-
const
|
|
190920
|
-
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");
|
|
190921
191532
|
try {
|
|
190922
191533
|
const result = await compileSmartNoteCheck({
|
|
190923
191534
|
client: args.client,
|
|
@@ -190926,7 +191537,8 @@ async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld)
|
|
|
190926
191537
|
sessionDirectory: args.sessionDirectory,
|
|
190927
191538
|
projectIdentity: args.projectIdentity,
|
|
190928
191539
|
note,
|
|
190929
|
-
|
|
191540
|
+
capabilityFactory: (signal) => createSmartNoteCapabilities({ projectRoot, signal }),
|
|
191541
|
+
signal: promptSignal.signal,
|
|
190930
191542
|
deadline: args.deadline,
|
|
190931
191543
|
model: args.model,
|
|
190932
191544
|
fallbackModels: args.fallbackModels
|
|
@@ -190971,7 +191583,7 @@ async function compileNote(args, note, projectRoot, assertLeaseHeld, leaseHeld)
|
|
|
190971
191583
|
});
|
|
190972
191584
|
return result.dryRun.met;
|
|
190973
191585
|
} finally {
|
|
190974
|
-
|
|
191586
|
+
promptSignal.cleanup();
|
|
190975
191587
|
}
|
|
190976
191588
|
}
|
|
190977
191589
|
async function runLivenessCheck(args, note, projectRoot, assertLeaseHeld, leaseHeld) {
|
|
@@ -190982,7 +191594,7 @@ async function runLivenessCheck(args, note, projectRoot, assertLeaseHeld, leaseH
|
|
|
190982
191594
|
try {
|
|
190983
191595
|
const result = await runCompiledSmartNoteCheck({
|
|
190984
191596
|
compiledCheck: note.compiledCheck,
|
|
190985
|
-
|
|
191597
|
+
capabilityFactory: (signal) => createSmartNoteCapabilities({ projectRoot, signal }),
|
|
190986
191598
|
signal: controller.signal,
|
|
190987
191599
|
timeoutMs: 2000
|
|
190988
191600
|
});
|
|
@@ -191011,7 +191623,7 @@ async function runLivenessCheck(args, note, projectRoot, assertLeaseHeld, leaseH
|
|
|
191011
191623
|
clearTimeout(timer);
|
|
191012
191624
|
}
|
|
191013
191625
|
}
|
|
191014
|
-
async function confirmReadOnly(args, noteId, content, surfaceCondition) {
|
|
191626
|
+
async function confirmReadOnly(args, noteId, content, surfaceCondition, leaseSignal) {
|
|
191015
191627
|
let childSessionId = null;
|
|
191016
191628
|
const startedAt = Date.now();
|
|
191017
191629
|
let invocationRecorded = false;
|
|
@@ -191054,42 +191666,49 @@ Note content: ${JSON.stringify(content)}
|
|
|
191054
191666
|
Surface condition: ${JSON.stringify(surfaceCondition ?? "")}
|
|
191055
191667
|
|
|
191056
191668
|
Output exactly JSON: {"met": false}`;
|
|
191057
|
-
const
|
|
191058
|
-
|
|
191059
|
-
|
|
191060
|
-
|
|
191061
|
-
|
|
191062
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
191093
191712
|
recordInvocation2({ status: "completed", messages: run.output });
|
|
191094
191713
|
return run.validated;
|
|
191095
191714
|
} catch (error51) {
|
|
@@ -191308,19 +191927,21 @@ ${list}
|
|
|
191308
191927
|
}
|
|
191309
191928
|
function parseMapMemoriesManifest(text) {
|
|
191310
191929
|
const out = [];
|
|
191311
|
-
|
|
191930
|
+
const body = extractCompleteManifestBody(text, "mappings");
|
|
191931
|
+
for (const m of body.matchAll(/<memory\b([^>]*)\/?>/g)) {
|
|
191312
191932
|
const attrs = m[1];
|
|
191313
191933
|
const idMatch = attrs.match(/\bid\s*=\s*"(\d+)"/);
|
|
191314
191934
|
if (!idMatch)
|
|
191315
|
-
|
|
191935
|
+
throw new Error("mappings manifest entry missing numeric id");
|
|
191316
191936
|
const id = Number.parseInt(idMatch[1], 10);
|
|
191317
191937
|
if (!Number.isInteger(id))
|
|
191318
|
-
|
|
191938
|
+
throw new Error("mappings manifest entry missing numeric id");
|
|
191319
191939
|
const independent = /\bindependent\s*=\s*"(?:true|1)"/i.test(attrs);
|
|
191320
191940
|
const filesMatch = attrs.match(/\bfiles\s*=\s*"([^"]*)"/);
|
|
191321
191941
|
const files = filesMatch ? filesMatch[1].split(",").map((f) => f.trim()).filter(Boolean) : [];
|
|
191322
191942
|
out.push({ id, files, independent: independent || files.length === 0 });
|
|
191323
191943
|
}
|
|
191944
|
+
assertNoDuplicateManifestIds(out.map((entry) => entry.id), "mappings");
|
|
191324
191945
|
return out;
|
|
191325
191946
|
}
|
|
191326
191947
|
|
|
@@ -191409,9 +192030,13 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
191409
192030
|
});
|
|
191410
192031
|
},
|
|
191411
192032
|
validateOutput: (messages) => {
|
|
192033
|
+
if (hasLengthCappedOutput(messages)) {
|
|
192034
|
+
throw new Error("map-memories returned length-capped output");
|
|
192035
|
+
}
|
|
191412
192036
|
const text = extractLatestAssistantText(messages);
|
|
191413
192037
|
if (!text)
|
|
191414
192038
|
throw new Error("map-memories returned no output");
|
|
192039
|
+
parseMapMemoriesManifest(text);
|
|
191415
192040
|
return text;
|
|
191416
192041
|
}
|
|
191417
192042
|
});
|
|
@@ -191437,7 +192062,8 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
191437
192062
|
}
|
|
191438
192063
|
async function applyBatchMappings(args, batch, manifestText) {
|
|
191439
192064
|
const batchIds = new Set(batch.map((m) => m.id));
|
|
191440
|
-
const parsed = parseMapMemoriesManifest(manifestText)
|
|
192065
|
+
const parsed = parseMapMemoriesManifest(manifestText);
|
|
192066
|
+
assertManifestCoversExactly(parsed.map((entry) => entry.id), batchIds, "mappings");
|
|
191441
192067
|
if (parsed.length === 0)
|
|
191442
192068
|
return { mapped: 0, independent: 0 };
|
|
191443
192069
|
const planned = [];
|
|
@@ -191459,12 +192085,7 @@ async function applyBatchMappings(args, batch, manifestText) {
|
|
|
191459
192085
|
const now = Date.now();
|
|
191460
192086
|
let mapped = 0;
|
|
191461
192087
|
let independent = 0;
|
|
191462
|
-
|
|
191463
|
-
args.db.transaction(() => {
|
|
191464
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
191465
|
-
leaseLost = true;
|
|
191466
|
-
return;
|
|
191467
|
-
}
|
|
192088
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
191468
192089
|
for (const item of planned) {
|
|
191469
192090
|
recordMemoryMapping(args.db, item.id, item.files, now);
|
|
191470
192091
|
if (item.independent)
|
|
@@ -191472,9 +192093,7 @@ async function applyBatchMappings(args, batch, manifestText) {
|
|
|
191472
192093
|
else
|
|
191473
192094
|
mapped += 1;
|
|
191474
192095
|
}
|
|
191475
|
-
})
|
|
191476
|
-
if (leaseLost)
|
|
191477
|
-
throw new Error("Dream lease lost during map-memories commit");
|
|
192096
|
+
});
|
|
191478
192097
|
return { mapped, independent };
|
|
191479
192098
|
}
|
|
191480
192099
|
function recordInvocation2(args, startedAt, params) {
|
|
@@ -191705,11 +192324,7 @@ async function promotePrimers(args) {
|
|
|
191705
192324
|
activePrimers: primers,
|
|
191706
192325
|
threshold: PRIMER_CLUSTER_THRESHOLD
|
|
191707
192326
|
});
|
|
191708
|
-
args.db.
|
|
191709
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
191710
|
-
leaseLost = true;
|
|
191711
|
-
return;
|
|
191712
|
-
}
|
|
192327
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
191713
192328
|
for (const cluster of clusters) {
|
|
191714
192329
|
if (cluster.candidates.length === 0)
|
|
191715
192330
|
continue;
|
|
@@ -191740,7 +192355,7 @@ async function promotePrimers(args) {
|
|
|
191740
192355
|
});
|
|
191741
192356
|
result.promoted += 1;
|
|
191742
192357
|
}
|
|
191743
|
-
})
|
|
192358
|
+
});
|
|
191744
192359
|
if (leaseLost)
|
|
191745
192360
|
throw new Error("Dream lease lost during promote-primers commit");
|
|
191746
192361
|
log(`[dreamer] primers: candidates=${result.candidates} promoted=${result.promoted} updated=${result.updated}`);
|
|
@@ -192018,16 +192633,9 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
|
|
|
192018
192633
|
log(`[dreamer] refresh-primers: primer #${primer.id} answer not committed (no investigation tool calls)`);
|
|
192019
192634
|
return false;
|
|
192020
192635
|
}
|
|
192021
|
-
|
|
192022
|
-
args.db.transaction(() => {
|
|
192023
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
192024
|
-
leaseLost = true;
|
|
192025
|
-
return;
|
|
192026
|
-
}
|
|
192636
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
192027
192637
|
updatePrimerAnswer(args.db, primer.id, answer);
|
|
192028
|
-
})
|
|
192029
|
-
if (leaseLost)
|
|
192030
|
-
throw new Error("Dream lease lost during refresh-primers commit");
|
|
192638
|
+
});
|
|
192031
192639
|
return true;
|
|
192032
192640
|
} catch (error51) {
|
|
192033
192641
|
const desc = describeError(error51);
|
|
@@ -192296,7 +192904,7 @@ async function partitionVerifyScope(args) {
|
|
|
192296
192904
|
continue;
|
|
192297
192905
|
}
|
|
192298
192906
|
const files = v?.files ?? [];
|
|
192299
|
-
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);
|
|
192300
192908
|
if (needs)
|
|
192301
192909
|
inScope.push(toPrompt(m));
|
|
192302
192910
|
else
|
|
@@ -192362,22 +192970,26 @@ function filesOf(s) {
|
|
|
192362
192970
|
}
|
|
192363
192971
|
function parseVerifyManifest(text) {
|
|
192364
192972
|
const out = { verified: [], updated: [], archived: [] };
|
|
192365
|
-
|
|
192973
|
+
const body = extractCompleteManifestBody(text, "verify");
|
|
192974
|
+
for (const m of body.matchAll(/<verified\b([^>]*)\/?>/g)) {
|
|
192366
192975
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
192367
|
-
if (Number.isInteger(id))
|
|
192368
|
-
|
|
192976
|
+
if (!Number.isInteger(id))
|
|
192977
|
+
throw new Error("verify manifest entry missing numeric id");
|
|
192978
|
+
out.verified.push({ id, files: filesOf(m[1]) });
|
|
192369
192979
|
}
|
|
192370
|
-
for (const m of
|
|
192980
|
+
for (const m of body.matchAll(/<update\b([^>]*?)(?:\/>|>([\s\S]*?)<\/update>)/g)) {
|
|
192371
192981
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
192372
|
-
if (Number.isInteger(id))
|
|
192373
|
-
|
|
192374
|
-
}
|
|
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() });
|
|
192375
192985
|
}
|
|
192376
|
-
for (const m of
|
|
192986
|
+
for (const m of body.matchAll(/<archive\b([^>]*)\/?>/g)) {
|
|
192377
192987
|
const id = Number.parseInt(attrOf(m[1], "id") ?? "", 10);
|
|
192378
|
-
if (Number.isInteger(id))
|
|
192379
|
-
|
|
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") ?? "" });
|
|
192380
192991
|
}
|
|
192992
|
+
assertNoDuplicateManifestIds([...out.verified, ...out.updated, ...out.archived].map((entry) => entry.id), "verify");
|
|
192381
192993
|
return out;
|
|
192382
192994
|
}
|
|
192383
192995
|
|
|
@@ -192470,9 +193082,13 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
192470
193082
|
});
|
|
192471
193083
|
},
|
|
192472
193084
|
validateOutput: (messages) => {
|
|
193085
|
+
if (hasLengthCappedOutput(messages)) {
|
|
193086
|
+
throw new Error("verify returned length-capped output");
|
|
193087
|
+
}
|
|
192473
193088
|
const text = extractLatestAssistantText(messages);
|
|
192474
193089
|
if (!text)
|
|
192475
193090
|
throw new Error("verify returned no output");
|
|
193091
|
+
parseVerifyManifest(text);
|
|
192476
193092
|
return text;
|
|
192477
193093
|
}
|
|
192478
193094
|
});
|
|
@@ -192499,24 +193115,14 @@ async function verifyOneBatch(args, batch, sliceMs, signal) {
|
|
|
192499
193115
|
async function applyVerifyManifest(args, batch, manifestText) {
|
|
192500
193116
|
const batchIds = new Set(batch.map((m) => m.id));
|
|
192501
193117
|
const parsed = parseVerifyManifest(manifestText);
|
|
193118
|
+
assertManifestCoversExactly([...parsed.verified, ...parsed.updated, ...parsed.archived].map((entry) => entry.id), batchIds, "verify");
|
|
192502
193119
|
const now = Date.now();
|
|
192503
193120
|
const writes = [];
|
|
192504
|
-
const verdictCounts = new Map;
|
|
192505
|
-
for (const verdict of [...parsed.verified, ...parsed.updated, ...parsed.archived]) {
|
|
192506
|
-
if (!batchIds.has(verdict.id))
|
|
192507
|
-
continue;
|
|
192508
|
-
verdictCounts.set(verdict.id, (verdictCounts.get(verdict.id) ?? 0) + 1);
|
|
192509
|
-
}
|
|
192510
|
-
const conflictingIds = new Set(Array.from(verdictCounts.entries()).filter(([, count]) => count !== 1).map(([id]) => id));
|
|
192511
193121
|
for (const v of parsed.verified) {
|
|
192512
|
-
if (!batchIds.has(v.id) || conflictingIds.has(v.id))
|
|
192513
|
-
continue;
|
|
192514
193122
|
const files = await normalizeFiles(args, v.files);
|
|
192515
193123
|
writes.push({ kind: "verify", id: v.id, files });
|
|
192516
193124
|
}
|
|
192517
193125
|
for (const u of parsed.updated) {
|
|
192518
|
-
if (!batchIds.has(u.id) || conflictingIds.has(u.id))
|
|
192519
|
-
continue;
|
|
192520
193126
|
const content = u.content.trim();
|
|
192521
193127
|
if (!content || content.length > 20000) {
|
|
192522
193128
|
const files2 = await normalizeFiles(args, u.files);
|
|
@@ -192533,8 +193139,6 @@ async function applyVerifyManifest(args, batch, manifestText) {
|
|
|
192533
193139
|
});
|
|
192534
193140
|
}
|
|
192535
193141
|
for (const a of parsed.archived) {
|
|
192536
|
-
if (!batchIds.has(a.id) || conflictingIds.has(a.id))
|
|
192537
|
-
continue;
|
|
192538
193142
|
writes.push({ kind: "archive", id: a.id, reason: a.reason });
|
|
192539
193143
|
}
|
|
192540
193144
|
if (writes.length === 0)
|
|
@@ -192542,12 +193146,7 @@ async function applyVerifyManifest(args, batch, manifestText) {
|
|
|
192542
193146
|
let verified = 0;
|
|
192543
193147
|
let updated = 0;
|
|
192544
193148
|
let archived = 0;
|
|
192545
|
-
|
|
192546
|
-
args.db.transaction(() => {
|
|
192547
|
-
if (!peekLeaseHolderAndExpiry(args.db, args.holderId, args.leaseKey)) {
|
|
192548
|
-
leaseLost = true;
|
|
192549
|
-
return;
|
|
192550
|
-
}
|
|
193149
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
192551
193150
|
for (const w of writes) {
|
|
192552
193151
|
const memory = getMemoryById(args.db, w.id);
|
|
192553
193152
|
if (!isPrimaryMutable(memory))
|
|
@@ -192575,9 +193174,7 @@ async function applyVerifyManifest(args, batch, manifestText) {
|
|
|
192575
193174
|
archived += 1;
|
|
192576
193175
|
}
|
|
192577
193176
|
}
|
|
192578
|
-
})
|
|
192579
|
-
if (leaseLost)
|
|
192580
|
-
throw new Error("Dream lease lost during verify commit");
|
|
193177
|
+
});
|
|
192581
193178
|
return { verified, updated, archived };
|
|
192582
193179
|
}
|
|
192583
193180
|
async function normalizeFiles(args, rawFiles) {
|
|
@@ -193071,11 +193668,7 @@ async function runRetrospectiveTask(config2, ctx, helpers) {
|
|
|
193071
193668
|
throw new Error("Dream lease lost during retrospective");
|
|
193072
193669
|
const sourceSessionId = flagged[0]?.sessionId ?? userMessages[0]?.sessionId ?? "retrospective";
|
|
193073
193670
|
const learnings = parseRetrospectiveLearnings(deepenRun.validated);
|
|
193074
|
-
const applied = db
|
|
193075
|
-
if (!peekLeaseHolderAndExpiry(db, holderId, leaseKey)) {
|
|
193076
|
-
leaseLost = true;
|
|
193077
|
-
return null;
|
|
193078
|
-
}
|
|
193671
|
+
const applied = runLeaseGuardedWrite(db, holderId, leaseKey, () => {
|
|
193079
193672
|
const result = applyRetrospectiveLearnings({
|
|
193080
193673
|
db,
|
|
193081
193674
|
projectIdentity,
|
|
@@ -193086,7 +193679,7 @@ async function runRetrospectiveTask(config2, ctx, helpers) {
|
|
|
193086
193679
|
});
|
|
193087
193680
|
recordRetrospectiveWindowProcessed(db, projectIdentity, windowKey);
|
|
193088
193681
|
return result;
|
|
193089
|
-
})
|
|
193682
|
+
});
|
|
193090
193683
|
if (leaseLost || !applied)
|
|
193091
193684
|
throw new Error("Dream lease lost during retrospective commit");
|
|
193092
193685
|
log(`[dreamer] retrospective: flagged=${flagged.length} learnings=${learnings.length} memory=${applied.memoryWritten} observations=${applied.observationsInserted} dropped=${applied.observationsDropped} rejected=${applied.rejected.length}`);
|
|
@@ -194608,7 +195201,7 @@ function createTagger() {
|
|
|
194608
195201
|
}
|
|
194609
195202
|
return map2;
|
|
194610
195203
|
}
|
|
194611
|
-
function
|
|
195204
|
+
function isUniqueConstraintError2(error51) {
|
|
194612
195205
|
return error51 instanceof Error && "code" in error51 && error51.code === "SQLITE_CONSTRAINT_UNIQUE";
|
|
194613
195206
|
}
|
|
194614
195207
|
function syncCounterAtLeast(sessionId, db, value) {
|
|
@@ -194646,7 +195239,7 @@ function createTagger() {
|
|
|
194646
195239
|
getUpsertCounterStatement(db).run(sessionId, next, getHarness());
|
|
194647
195240
|
})();
|
|
194648
195241
|
} catch (error51) {
|
|
194649
|
-
if (!
|
|
195242
|
+
if (!isUniqueConstraintError2(error51)) {
|
|
194650
195243
|
throw error51;
|
|
194651
195244
|
}
|
|
194652
195245
|
const racedRow = dbExistingLookup();
|
|
@@ -195813,8 +196406,9 @@ function scheduleReconciliation(db, sessionId, readMessages) {
|
|
|
195813
196406
|
reconciliationScheduledSessions.add(sessionId);
|
|
195814
196407
|
defer(() => {
|
|
195815
196408
|
reconcileSessionIndex(db, sessionId, readMessages).catch((error51) => {
|
|
195816
|
-
reconciliationScheduledSessions.delete(sessionId);
|
|
195817
196409
|
logIndexingError(sessionId, "reconciliation", error51);
|
|
196410
|
+
}).finally(() => {
|
|
196411
|
+
reconciliationScheduledSessions.delete(sessionId);
|
|
195818
196412
|
});
|
|
195819
196413
|
});
|
|
195820
196414
|
}
|
|
@@ -195826,13 +196420,17 @@ function scheduleIncrementalIndex(db, sessionId, messageId, readSingleMessage) {
|
|
|
195826
196420
|
const timer = setTimeout(() => {
|
|
195827
196421
|
incrementalTimers.delete(key);
|
|
195828
196422
|
pendingIncrementalKeys.add(key);
|
|
196423
|
+
let attemptedOrdinal = null;
|
|
195829
196424
|
runWithSessionLock(sessionId, () => {
|
|
195830
196425
|
const message = readSingleMessage(sessionId, messageId);
|
|
195831
196426
|
if (!message) {
|
|
195832
196427
|
return;
|
|
195833
196428
|
}
|
|
196429
|
+
attemptedOrdinal = message.ordinal;
|
|
195834
196430
|
indexSingleMessage(db, sessionId, message);
|
|
195835
196431
|
}).catch((error51) => {
|
|
196432
|
+
markMessageIndexDirty(db, sessionId, attemptedOrdinal ?? getLastIndexedOrdinal(db, sessionId) + 1);
|
|
196433
|
+
reconciledSessions.delete(sessionId);
|
|
195836
196434
|
logIndexingError(sessionId, `incremental index for ${messageId}`, error51);
|
|
195837
196435
|
}).finally(() => {
|
|
195838
196436
|
pendingIncrementalKeys.delete(key);
|
|
@@ -196026,6 +196624,7 @@ init_models_dev_cache();
|
|
|
196026
196624
|
// src/hooks/magic-context/channel2-delivery.ts
|
|
196027
196625
|
init_storage_meta_persisted();
|
|
196028
196626
|
init_logger();
|
|
196627
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
196029
196628
|
|
|
196030
196629
|
// src/hooks/magic-context/ctx-reduce-nudge.ts
|
|
196031
196630
|
init_tag_content_primitives();
|
|
@@ -196206,19 +196805,6 @@ ${body}${hintText}
|
|
|
196206
196805
|
}
|
|
196207
196806
|
|
|
196208
196807
|
// src/hooks/magic-context/channel2-delivery.ts
|
|
196209
|
-
function sealDeliveredAfterUnconfirmedSend(db, sessionId) {
|
|
196210
|
-
try {
|
|
196211
|
-
if (getChannel2NudgeState(db, sessionId) === "delivered") {
|
|
196212
|
-
return "already-delivered";
|
|
196213
|
-
}
|
|
196214
|
-
} catch {}
|
|
196215
|
-
try {
|
|
196216
|
-
setChannel2NudgeState(db, sessionId, "delivered");
|
|
196217
|
-
return "sealed";
|
|
196218
|
-
} catch {
|
|
196219
|
-
return "stuck-claimed";
|
|
196220
|
-
}
|
|
196221
|
-
}
|
|
196222
196808
|
async function maybeDeliverChannel2(sessionId, deps) {
|
|
196223
196809
|
let state;
|
|
196224
196810
|
try {
|
|
@@ -196244,7 +196830,8 @@ async function maybeDeliverChannel2(sessionId, deps) {
|
|
|
196244
196830
|
const client = deps.client;
|
|
196245
196831
|
if (!client)
|
|
196246
196832
|
return false;
|
|
196247
|
-
|
|
196833
|
+
const claimToken = randomUUID2();
|
|
196834
|
+
if (!claimChannel2NudgeState(deps.db, sessionId, claimToken)) {
|
|
196248
196835
|
return false;
|
|
196249
196836
|
}
|
|
196250
196837
|
try {
|
|
@@ -196268,40 +196855,36 @@ async function maybeDeliverChannel2(sessionId, deps) {
|
|
|
196268
196855
|
if (typeof session?.promptAsync !== "function") {
|
|
196269
196856
|
throw new Error("client has no session.promptAsync");
|
|
196270
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
|
+
}
|
|
196271
196863
|
await session.promptAsync({ path: { id: sessionId }, body });
|
|
196272
196864
|
} catch (error51) {
|
|
196273
196865
|
try {
|
|
196274
|
-
|
|
196275
|
-
|
|
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
|
+
}
|
|
196276
196872
|
} catch (revertError) {
|
|
196277
196873
|
sessionLog(sessionId, "channel2 ceiling nudge delivery failed; pending restore was busy so the stale claim will heal later:", { deliveryError: error51, revertError });
|
|
196278
196874
|
}
|
|
196279
196875
|
return false;
|
|
196280
196876
|
}
|
|
196281
196877
|
try {
|
|
196282
|
-
const confirmed =
|
|
196878
|
+
const confirmed = casChannel2NudgeClaim(deps.db, sessionId, "delivered", claimToken);
|
|
196283
196879
|
if (confirmed) {
|
|
196284
196880
|
sessionLog(sessionId, "channel2 ceiling nudge delivered");
|
|
196285
196881
|
return true;
|
|
196286
196882
|
}
|
|
196287
|
-
const
|
|
196288
|
-
|
|
196289
|
-
sessionLog(sessionId, "channel2 ceiling nudge duplicate window: our send returned after a sibling reclaimed the stale lease and already delivered");
|
|
196290
|
-
} else if (outcome === "sealed") {
|
|
196291
|
-
sessionLog(sessionId, "channel2 ceiling nudge sent but claim confirmation was lost; sealed delivered without an authoritative confirm");
|
|
196292
|
-
} else {
|
|
196293
|
-
sessionLog(sessionId, "channel2 ceiling nudge sent but claim confirmation was lost; lease stayed claimed and will heal later");
|
|
196294
|
-
}
|
|
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`);
|
|
196295
196885
|
return false;
|
|
196296
196886
|
} catch (error51) {
|
|
196297
|
-
|
|
196298
|
-
if (outcome === "already-delivered") {
|
|
196299
|
-
sessionLog(sessionId, "channel2 ceiling nudge duplicate window: our send returned after a sibling reclaimed the stale lease and already delivered");
|
|
196300
|
-
} else if (outcome === "sealed") {
|
|
196301
|
-
sessionLog(sessionId, "channel2 ceiling nudge sent but confirm failed:", error51);
|
|
196302
|
-
} else {
|
|
196303
|
-
sessionLog(sessionId, "channel2 ceiling nudge sent but confirm failed; lease stayed claimed and will heal later:", error51);
|
|
196304
|
-
}
|
|
196887
|
+
sessionLog(sessionId, "channel2 ceiling nudge sent but token-confirm failed; lease state left unchanged:", error51);
|
|
196305
196888
|
return false;
|
|
196306
196889
|
}
|
|
196307
196890
|
}
|
|
@@ -197731,6 +198314,8 @@ function injectToolPartIntoLatestAssistant(messages, part) {
|
|
|
197731
198314
|
continue;
|
|
197732
198315
|
if (typeof message.info.id !== "string")
|
|
197733
198316
|
continue;
|
|
198317
|
+
if (!isReplayableAssistantAnchor(message))
|
|
198318
|
+
continue;
|
|
197734
198319
|
if (hasToolPartWithCallId(message, part.callID)) {
|
|
197735
198320
|
return message.info.id;
|
|
197736
198321
|
}
|
|
@@ -197745,6 +198330,8 @@ function injectToolPartIntoAssistantById(messages, messageId, part) {
|
|
|
197745
198330
|
continue;
|
|
197746
198331
|
if (message.info.role !== "assistant")
|
|
197747
198332
|
continue;
|
|
198333
|
+
if (!isReplayableAssistantAnchor(message))
|
|
198334
|
+
return false;
|
|
197748
198335
|
if (hasToolPartWithCallId(message, part.callID))
|
|
197749
198336
|
return true;
|
|
197750
198337
|
message.parts.push(part);
|
|
@@ -197764,6 +198351,9 @@ function hasToolPartWithCallId(message, callId) {
|
|
|
197764
198351
|
}
|
|
197765
198352
|
return false;
|
|
197766
198353
|
}
|
|
198354
|
+
function isReplayableAssistantAnchor(message) {
|
|
198355
|
+
return message.info.error === undefined || message.info.error === null;
|
|
198356
|
+
}
|
|
197767
198357
|
function appendReminderToUserMessage(message, reminder) {
|
|
197768
198358
|
for (const part of message.parts) {
|
|
197769
198359
|
if (!isTextPart(part)) {
|
|
@@ -198620,24 +199210,46 @@ function getMessageSearchStatementWithCutoff(db) {
|
|
|
198620
199210
|
return stmt;
|
|
198621
199211
|
}
|
|
198622
199212
|
var ftsRowCountStatements = new WeakMap;
|
|
199213
|
+
var ftsRowCountStatementsWithCutoff = new WeakMap;
|
|
198623
199214
|
var ftsMatchCountStatements = new WeakMap;
|
|
198624
|
-
|
|
198625
|
-
|
|
198626
|
-
|
|
198627
|
-
|
|
198628
|
-
|
|
198629
|
-
|
|
198630
|
-
|
|
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);
|
|
198631
199233
|
return typeof row?.n === "number" ? row.n : 0;
|
|
198632
199234
|
}
|
|
198633
|
-
function countSessionFtsMatches(db, sessionId, ftsQuery) {
|
|
198634
|
-
|
|
198635
|
-
|
|
198636
|
-
|
|
198637
|
-
|
|
198638
|
-
|
|
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
|
+
})();
|
|
198639
199251
|
try {
|
|
198640
|
-
const row = stmt.get(sessionId, ftsQuery);
|
|
199252
|
+
const row = cutoff === null ? stmt.get(sessionId, ftsQuery) : stmt.get(sessionId, ftsQuery, cutoff);
|
|
198641
199253
|
return typeof row?.n === "number" ? row.n : 0;
|
|
198642
199254
|
} catch {
|
|
198643
199255
|
return 0;
|
|
@@ -198887,7 +199499,7 @@ function searchMessages(args) {
|
|
|
198887
199499
|
role: row.role
|
|
198888
199500
|
}));
|
|
198889
199501
|
}
|
|
198890
|
-
const corpusSize = getSessionFtsRowCount(args.db, args.sessionId);
|
|
199502
|
+
const corpusSize = getSessionFtsRowCount(args.db, args.sessionId, cutoff);
|
|
198891
199503
|
const queryLists = [];
|
|
198892
199504
|
if (baseQuery.length > 0) {
|
|
198893
199505
|
queryLists.push({
|
|
@@ -198900,7 +199512,7 @@ function searchMessages(args) {
|
|
|
198900
199512
|
const probeQuery = sanitizeFtsQuery(probe);
|
|
198901
199513
|
if (probeQuery.length === 0)
|
|
198902
199514
|
continue;
|
|
198903
|
-
const df = countSessionFtsMatches(args.db, args.sessionId, probeQuery);
|
|
199515
|
+
const df = countSessionFtsMatches(args.db, args.sessionId, probeQuery, cutoff);
|
|
198904
199516
|
const weight = probeDiscriminationWeight(df, corpusSize);
|
|
198905
199517
|
probeWeights.set(probe, weight);
|
|
198906
199518
|
queryLists.push({
|
|
@@ -200194,8 +200806,31 @@ function readFilePath(target) {
|
|
|
200194
200806
|
|
|
200195
200807
|
// src/hooks/magic-context/todo-view.ts
|
|
200196
200808
|
import { createHash as createHash12 } from "node:crypto";
|
|
200197
|
-
var
|
|
200198
|
-
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]);
|
|
200199
200834
|
var SYNTHETIC_CALL_ID_PREFIX = "mc_synthetic_todo_";
|
|
200200
200835
|
function normalizeTodoStateJson(todos) {
|
|
200201
200836
|
if (!Array.isArray(todos))
|
|
@@ -200207,7 +200842,7 @@ function normalizeTodoStateJson(todos) {
|
|
|
200207
200842
|
normalized.push({
|
|
200208
200843
|
content: todo.content,
|
|
200209
200844
|
status: todo.status,
|
|
200210
|
-
priority: todo.priority ??
|
|
200845
|
+
priority: todo.priority ?? TODO_PRIORITY_MEDIUM
|
|
200211
200846
|
});
|
|
200212
200847
|
}
|
|
200213
200848
|
return JSON.stringify(normalized);
|
|
@@ -200251,11 +200886,11 @@ function parseTodoState(stateJson) {
|
|
|
200251
200886
|
const result = [];
|
|
200252
200887
|
for (const item of parsed) {
|
|
200253
200888
|
if (!isTodoItem(item))
|
|
200254
|
-
|
|
200889
|
+
return null;
|
|
200255
200890
|
result.push({
|
|
200256
200891
|
content: item.content,
|
|
200257
200892
|
status: item.status,
|
|
200258
|
-
priority: item.priority ??
|
|
200893
|
+
priority: item.priority ?? TODO_PRIORITY_MEDIUM
|
|
200259
200894
|
});
|
|
200260
200895
|
}
|
|
200261
200896
|
return result;
|
|
@@ -200263,11 +200898,17 @@ function parseTodoState(stateJson) {
|
|
|
200263
200898
|
return null;
|
|
200264
200899
|
}
|
|
200265
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
|
+
}
|
|
200266
200907
|
function isTodoItem(value) {
|
|
200267
200908
|
if (value === null || typeof value !== "object")
|
|
200268
200909
|
return false;
|
|
200269
200910
|
const todo = value;
|
|
200270
|
-
return typeof todo.content === "string" &&
|
|
200911
|
+
return typeof todo.content === "string" && isTodoStatus(todo.status) && (todo.priority === undefined || isTodoPriority(todo.priority));
|
|
200271
200912
|
}
|
|
200272
200913
|
|
|
200273
200914
|
// src/hooks/magic-context/tool-reclaim.ts
|
|
@@ -200457,7 +201098,7 @@ async function runPostTransformPhase(args) {
|
|
|
200457
201098
|
if (args.schedulerDecision === "execute" && !materializationRequested) {
|
|
200458
201099
|
updateSessionMeta(args.db, args.sessionId, { lastResponseTime: Date.now() });
|
|
200459
201100
|
}
|
|
200460
|
-
const toolReclaimExecutePass = args.schedulerDecision === "execute";
|
|
201101
|
+
const toolReclaimExecutePass = args.schedulerDecision === "execute" || m0HardFoldThisPass;
|
|
200461
201102
|
const alreadyMutatingThisPass = pendingOpsDidMutate || heuristicOrReasoningDidMutate;
|
|
200462
201103
|
let autoReclaimTargetCount = 0;
|
|
200463
201104
|
let autoReclaimDidMutate = false;
|
|
@@ -200549,12 +201190,12 @@ async function runPostTransformPhase(args) {
|
|
|
200549
201190
|
sessionLog(args.sessionId, "transform failed dropping stale ctx_reduce calls:", error51);
|
|
200550
201191
|
}
|
|
200551
201192
|
}
|
|
200552
|
-
if (canUseEmptySentinels
|
|
201193
|
+
if (canUseEmptySentinels) {
|
|
200553
201194
|
try {
|
|
200554
201195
|
const tImg = performance.now();
|
|
200555
201196
|
const frozenImageIds = getProcessedImageStrippedIds(args.db, args.sessionId);
|
|
200556
201197
|
const imageResult = stripProcessedImages(args.messages, frozenImageIds, {
|
|
200557
|
-
detect: isCacheBustingPass,
|
|
201198
|
+
detect: isCacheBustingPass && args.watermark > 0,
|
|
200558
201199
|
watermark: args.watermark,
|
|
200559
201200
|
messageTagNumbers: args.messageTagNumbers
|
|
200560
201201
|
});
|
|
@@ -202120,12 +202761,25 @@ await __promiseAll([
|
|
|
202120
202761
|
]);
|
|
202121
202762
|
import * as crypto3 from "node:crypto";
|
|
202122
202763
|
var WAIT_FOR_LEASE_MS = 1000;
|
|
202764
|
+
var MAX_WRAPUP_LEASE_WAIT_MS = 10 * 60 * 1000;
|
|
202123
202765
|
function sleep2(ms) {
|
|
202124
202766
|
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
202125
202767
|
}
|
|
202126
202768
|
function plural(value, word) {
|
|
202127
202769
|
return `${value} ${word}${value === 1 ? "" : "s"}`;
|
|
202128
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
|
+
}
|
|
202129
202783
|
function formatAlreadyRunningMessage(state) {
|
|
202130
202784
|
if (!state)
|
|
202131
202785
|
return "/ctx-wrapup is already running for this session.";
|
|
@@ -202168,44 +202822,55 @@ function emitWrapupProgress(ctx, sessionId, progress) {
|
|
|
202168
202822
|
note: progress.note
|
|
202169
202823
|
});
|
|
202170
202824
|
}
|
|
202171
|
-
async function waitForExistingIncrementalRun(sessionId) {
|
|
202825
|
+
async function waitForExistingIncrementalRun(sessionId, maxWaitMs) {
|
|
202172
202826
|
const active = getActiveCompartmentRun(sessionId);
|
|
202173
202827
|
if (!active)
|
|
202174
202828
|
return "ok";
|
|
202175
202829
|
if (active.kind === "recomp" || active.kind === "wrapup")
|
|
202176
202830
|
return "busy";
|
|
202177
|
-
|
|
202178
|
-
|
|
202179
|
-
|
|
202831
|
+
const outcome = await waitForActiveRunWithin(active.promise, maxWaitMs);
|
|
202832
|
+
if (outcome === "timeout")
|
|
202833
|
+
return "timeout";
|
|
202180
202834
|
return "ok";
|
|
202181
202835
|
}
|
|
202182
202836
|
async function acquireCompartmentLeaseForWrapup(ctx, sessionId, renewWrapupMarker) {
|
|
202183
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());
|
|
202184
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
|
+
}
|
|
202185
202846
|
const active = getActiveCompartmentRun(sessionId);
|
|
202186
|
-
if (active?.kind === "recomp" || active?.kind === "wrapup")
|
|
202187
|
-
return
|
|
202847
|
+
if (active?.kind === "recomp" || active?.kind === "wrapup") {
|
|
202848
|
+
return { ok: false, reason: "busy" };
|
|
202849
|
+
}
|
|
202188
202850
|
if (active) {
|
|
202189
202851
|
emitWrapupProgress(ctx, sessionId, { note: "Waiting for the active historian run…" });
|
|
202190
|
-
|
|
202191
|
-
|
|
202192
|
-
|
|
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
|
+
}
|
|
202193
202857
|
continue;
|
|
202194
202858
|
}
|
|
202195
202859
|
const lease = acquireCompartmentLease(ctx.db, sessionId, holderId);
|
|
202196
202860
|
if (lease)
|
|
202197
|
-
return holderId;
|
|
202861
|
+
return { ok: true, holderId };
|
|
202198
202862
|
emitWrapupProgress(ctx, sessionId, { note: "Waiting for the compartment-state lease…" });
|
|
202199
202863
|
if (!renewWrapupMarker({}))
|
|
202200
|
-
return
|
|
202201
|
-
await sleep2(WAIT_FOR_LEASE_MS);
|
|
202864
|
+
return { ok: false, reason: "ownership_lost" };
|
|
202865
|
+
await sleep2(Math.min(WAIT_FOR_LEASE_MS, remainingMs()));
|
|
202202
202866
|
}
|
|
202203
202867
|
}
|
|
202204
202868
|
async function runOneWrapupIteration(args) {
|
|
202205
202869
|
const { ctx, sessionId, plan, messagesToKeep, anchorRawMessageCount } = args;
|
|
202206
|
-
const
|
|
202207
|
-
if (!
|
|
202208
|
-
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;
|
|
202209
202874
|
const renewal = setInterval(() => {
|
|
202210
202875
|
try {
|
|
202211
202876
|
if (!renewCompartmentLease(ctx.db, sessionId, leaseHolderId)) {
|
|
@@ -202250,7 +202915,7 @@ async function runOneWrapupIteration(args) {
|
|
|
202250
202915
|
registerActiveCompartmentRun(sessionId, runnerPromise, "wrapup");
|
|
202251
202916
|
try {
|
|
202252
202917
|
await runnerPromise;
|
|
202253
|
-
return true;
|
|
202918
|
+
return { ran: true };
|
|
202254
202919
|
} finally {
|
|
202255
202920
|
clearInterval(renewal);
|
|
202256
202921
|
releaseCompartmentLease(ctx.db, sessionId, leaseHolderId);
|
|
@@ -202325,7 +202990,7 @@ ${message2}`;
|
|
|
202325
202990
|
}, 60000);
|
|
202326
202991
|
markerRenewal.unref?.();
|
|
202327
202992
|
try {
|
|
202328
|
-
const activeAtStart = await waitForExistingIncrementalRun(sessionId);
|
|
202993
|
+
const activeAtStart = await waitForExistingIncrementalRun(sessionId, resolveWrapupLeaseWaitTimeout(ctx));
|
|
202329
202994
|
if (activeAtStart === "busy") {
|
|
202330
202995
|
const message2 = "Another Magic Context rebuild is already running for this session.";
|
|
202331
202996
|
setRecompTerminal(ctx.liveSessionState, sessionId, "skipped", message2);
|
|
@@ -202333,16 +202998,22 @@ ${message2}`;
|
|
|
202333
202998
|
|
|
202334
202999
|
${message2}`;
|
|
202335
203000
|
}
|
|
202336
|
-
|
|
202337
|
-
|
|
202338
|
-
|
|
202339
|
-
|
|
202340
|
-
|
|
202341
|
-
|
|
202342
|
-
|
|
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) {
|
|
202343
203014
|
stoppedForFailure = true;
|
|
202344
203015
|
stoppedReason = ownershipLostReason;
|
|
202345
|
-
} else {
|
|
203016
|
+
} else if (!stoppedForFailure) {
|
|
202346
203017
|
emitWrapupProgress(ctx, sessionId, {
|
|
202347
203018
|
processedMessages: Math.max(0, lastEnd),
|
|
202348
203019
|
totalMessages: Math.max(0, initialPlan.targetEligibleEndOrdinal - 1),
|
|
@@ -202389,7 +203060,7 @@ ${message2}`;
|
|
|
202389
203060
|
}
|
|
202390
203061
|
const beforeEnd = lastEnd;
|
|
202391
203062
|
const beforeFailures = getHistorianFailureState(ctx.db, sessionId).failureCount;
|
|
202392
|
-
const
|
|
203063
|
+
const iteration = await runOneWrapupIteration({
|
|
202393
203064
|
ctx,
|
|
202394
203065
|
sessionId,
|
|
202395
203066
|
plan,
|
|
@@ -202397,9 +203068,9 @@ ${message2}`;
|
|
|
202397
203068
|
anchorRawMessageCount: initialPlan.anchorRawMessageCount,
|
|
202398
203069
|
renewWrapupMarker
|
|
202399
203070
|
});
|
|
202400
|
-
if (!ran) {
|
|
203071
|
+
if (!iteration.ran) {
|
|
202401
203072
|
stoppedForFailure = true;
|
|
202402
|
-
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.";
|
|
202403
203074
|
break;
|
|
202404
203075
|
}
|
|
202405
203076
|
const afterEnd = getLastCompartmentEndMessage(ctx.db, sessionId);
|
|
@@ -204957,13 +205628,18 @@ function queueMemoryEmbedding(args) {
|
|
|
204957
205628
|
if (!snapshot?.enabled) {
|
|
204958
205629
|
return;
|
|
204959
205630
|
}
|
|
205631
|
+
const normalizedHash = computeNormalizedHash(args.content);
|
|
204960
205632
|
(async () => {
|
|
204961
205633
|
const result = await embedTextForProject(args.projectPath, args.content);
|
|
204962
205634
|
if (!result) {
|
|
204963
205635
|
sessionLog(args.sessionId, `memory embedding skipped for memory ${args.memoryId}: provider unavailable or embedding generation failed.`);
|
|
204964
205636
|
return;
|
|
204965
205637
|
}
|
|
204966
|
-
|
|
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
|
+
}
|
|
204967
205643
|
sessionLog(args.sessionId, `proactively embedded memory ${args.memoryId}.`);
|
|
204968
205644
|
})().catch((error51) => {
|
|
204969
205645
|
sessionLog(args.sessionId, `memory embedding failed for memory ${args.memoryId}:`, error51);
|
|
@@ -205048,8 +205724,9 @@ function createCtxMemoryTool(deps) {
|
|
|
205048
205724
|
const isOwn = targetIdentityForStoredPath(memory.projectPath) === projectPath;
|
|
205049
205725
|
if (isOwn)
|
|
205050
205726
|
return true;
|
|
205051
|
-
return toolShareCategories
|
|
205727
|
+
return toolShareCategories?.includes(memory.category) ?? false;
|
|
205052
205728
|
};
|
|
205729
|
+
const memoryOwnedByTool = (memory) => workspaceIdentitySet.identities.length > 1 ? targetIdentityForStoredPath(memory.projectPath) === projectPath : memoryBelongsToProject(memory, projectPath);
|
|
205053
205730
|
const embeddingSnapshot = getProjectEmbeddingSnapshot(projectPath);
|
|
205054
205731
|
if (embeddingSnapshot ? !embeddingSnapshot.features.memoryEnabled : deps.memoryEnabled === false) {
|
|
205055
205732
|
return getDisabledMessage();
|
|
@@ -205072,21 +205749,24 @@ function createCtxMemoryTool(deps) {
|
|
|
205072
205749
|
updateMemorySeenCount(deps.db, existingMemory.id);
|
|
205073
205750
|
return `Memory already exists [ID: ${existingMemory.id}] in ${category} (seen count incremented).`;
|
|
205074
205751
|
}
|
|
205075
|
-
const
|
|
205752
|
+
const insertResult = insertMemoryIdempotent(deps.db, {
|
|
205076
205753
|
projectPath,
|
|
205077
205754
|
category,
|
|
205078
205755
|
content,
|
|
205079
205756
|
sourceSessionId: toolContext.sessionID,
|
|
205080
205757
|
sourceType: toolContext.agent === DREAMER_AGENT ? "dreamer" : getSourceType(deps)
|
|
205081
205758
|
});
|
|
205759
|
+
if (!insertResult.inserted) {
|
|
205760
|
+
return `Memory already exists [ID: ${insertResult.memory.id}] in ${category} (seen count incremented).`;
|
|
205761
|
+
}
|
|
205082
205762
|
queueMemoryEmbedding({
|
|
205083
205763
|
deps,
|
|
205084
205764
|
sessionId: toolContext.sessionID,
|
|
205085
205765
|
projectPath,
|
|
205086
|
-
memoryId: memory.id,
|
|
205766
|
+
memoryId: insertResult.memory.id,
|
|
205087
205767
|
content
|
|
205088
205768
|
});
|
|
205089
|
-
return `Saved memory [ID: ${memory.id}] in ${category}.`;
|
|
205769
|
+
return `Saved memory [ID: ${insertResult.memory.id}] in ${category}.`;
|
|
205090
205770
|
}
|
|
205091
205771
|
if (args.action === "list") {
|
|
205092
205772
|
const limit = normalizeLimit2(args.limit);
|
|
@@ -205106,7 +205786,8 @@ function createCtxMemoryTool(deps) {
|
|
|
205106
205786
|
}
|
|
205107
205787
|
const rawProjectPath = projectPathForMemoryId(deps.db, updateId);
|
|
205108
205788
|
const memory = getMemoryById(deps.db, updateId);
|
|
205109
|
-
|
|
205789
|
+
const updateAllowed = memory ? toolContext.agent === DREAMER_AGENT ? memoryVisibleToTool(memory) : memoryOwnedByTool(memory) : false;
|
|
205790
|
+
if (!memory || !rawProjectPath || !updateAllowed) {
|
|
205110
205791
|
return `Error: Memory with ID ${updateId} was not found.`;
|
|
205111
205792
|
}
|
|
205112
205793
|
if (toolContext.agent !== DREAMER_AGENT && !isPrimaryMutableMemory(memory)) {
|
|
@@ -205154,7 +205835,7 @@ function createCtxMemoryTool(deps) {
|
|
|
205154
205835
|
return "Error: One or more source memories were not found.";
|
|
205155
205836
|
}
|
|
205156
205837
|
if (toolContext.agent !== DREAMER_AGENT) {
|
|
205157
|
-
const foreign = sourceMemories.find((memory) => !
|
|
205838
|
+
const foreign = sourceMemories.find((memory) => !memoryOwnedByTool(memory));
|
|
205158
205839
|
if (foreign) {
|
|
205159
205840
|
return `Error: Memory with ID ${foreign.id} was not found.`;
|
|
205160
205841
|
}
|
|
@@ -205177,11 +205858,6 @@ function createCtxMemoryTool(deps) {
|
|
|
205177
205858
|
return "Error: A valid category is required when action is 'merge'.";
|
|
205178
205859
|
}
|
|
205179
205860
|
const normalizedHash = computeNormalizedHash(content);
|
|
205180
|
-
const duplicate = getMemoryByHash(deps.db, projectPath, category, normalizedHash);
|
|
205181
|
-
const canonicalExisting = duplicate && ids.includes(duplicate.id) ? duplicate : null;
|
|
205182
|
-
if (duplicate && !canonicalExisting) {
|
|
205183
|
-
return `Error: Memory content already exists as ID ${duplicate.id}; update or archive existing duplicates instead.`;
|
|
205184
|
-
}
|
|
205185
205861
|
const mergedFrom = JSON.stringify(Array.from(new Set(sourceMemories.flatMap((memory) => {
|
|
205186
205862
|
let parsed;
|
|
205187
205863
|
try {
|
|
@@ -205197,14 +205873,21 @@ function createCtxMemoryTool(deps) {
|
|
|
205197
205873
|
const mergedSeenCount = sourceMemories.reduce((sum, memory) => sum + memory.seenCount, 0);
|
|
205198
205874
|
const mergedRetrievalCount = sourceMemories.reduce((sum, memory) => sum + memory.retrievalCount, 0);
|
|
205199
205875
|
const mergedStatus = sourceMemories.some((memory) => memory.status === "permanent") ? "permanent" : "active";
|
|
205200
|
-
|
|
205201
|
-
|
|
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, {
|
|
205202
205885
|
projectPath,
|
|
205203
205886
|
category,
|
|
205204
205887
|
content,
|
|
205205
205888
|
sourceSessionId: toolContext.sessionID,
|
|
205206
205889
|
sourceType: toolContext.agent === DREAMER_AGENT ? "dreamer" : getSourceType(deps)
|
|
205207
|
-
});
|
|
205890
|
+
}).memory;
|
|
205208
205891
|
const canonicalContentChanged = nextCanonical.content !== content || nextCanonical.normalizedHash !== normalizedHash;
|
|
205209
205892
|
if (canonicalContentChanged) {
|
|
205210
205893
|
updateMemoryContentInCurrentTransaction(deps.db, nextCanonical, content, normalizedHash);
|
|
@@ -205232,7 +205915,10 @@ function createCtxMemoryTool(deps) {
|
|
|
205232
205915
|
});
|
|
205233
205916
|
}
|
|
205234
205917
|
return nextCanonical;
|
|
205235
|
-
})
|
|
205918
|
+
});
|
|
205919
|
+
if (mergeConflict || !canonicalMemory) {
|
|
205920
|
+
return mergeConflict ?? "Error: Failed to merge memories.";
|
|
205921
|
+
}
|
|
205236
205922
|
queueMemoryEmbedding({
|
|
205237
205923
|
deps,
|
|
205238
205924
|
sessionId: toolContext.sessionID,
|
|
@@ -205253,7 +205939,8 @@ function createCtxMemoryTool(deps) {
|
|
|
205253
205939
|
for (const memoryId of archiveIds) {
|
|
205254
205940
|
const rawProjectPath = projectPathForMemoryId(deps.db, memoryId);
|
|
205255
205941
|
const memory = getMemoryById(deps.db, memoryId);
|
|
205256
|
-
|
|
205942
|
+
const archiveAllowed = memory ? toolContext.agent === DREAMER_AGENT ? memoryVisibleToTool(memory) : memoryOwnedByTool(memory) : false;
|
|
205943
|
+
if (!memory || !rawProjectPath || !archiveAllowed) {
|
|
205257
205944
|
return `Error: Memory with ID ${memoryId} was not found.`;
|
|
205258
205945
|
}
|
|
205259
205946
|
if (toolContext.agent !== DREAMER_AGENT && !isPrimaryMutableMemory(memory)) {
|
|
@@ -205708,7 +206395,7 @@ function normalizeLimit3(limit) {
|
|
|
205708
206395
|
return Math.max(1, Math.floor(limit));
|
|
205709
206396
|
}
|
|
205710
206397
|
function normalizeSources(sources) {
|
|
205711
|
-
if (
|
|
206398
|
+
if (sources === undefined)
|
|
205712
206399
|
return;
|
|
205713
206400
|
const result = [];
|
|
205714
206401
|
const seen = new Set;
|
|
@@ -205721,7 +206408,7 @@ function normalizeSources(sources) {
|
|
|
205721
206408
|
}
|
|
205722
206409
|
}
|
|
205723
206410
|
}
|
|
205724
|
-
return result
|
|
206411
|
+
return result;
|
|
205725
206412
|
}
|
|
205726
206413
|
function formatAge2(committedAtMs) {
|
|
205727
206414
|
const ageMs = Date.now() - committedAtMs;
|
|
@@ -205813,7 +206500,7 @@ function createCtxSearchTool(deps) {
|
|
|
205813
206500
|
args: {
|
|
205814
206501
|
query: tool5.schema.string().describe("Search query. Matches against memory content, Primers, git commit messages, and raw user/assistant message text."),
|
|
205815
206502
|
limit: tool5.schema.number().optional().describe("Maximum results to return (default: 10)"),
|
|
205816
|
-
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.')
|
|
205817
206504
|
},
|
|
205818
206505
|
async execute(args, toolContext) {
|
|
205819
206506
|
const query = args.query?.trim();
|
|
@@ -205988,8 +206675,9 @@ function projectHash(directory) {
|
|
|
205988
206675
|
function rpcPortDir(storageDir, directory) {
|
|
205989
206676
|
return join20(storageDir, "rpc", projectHash(directory));
|
|
205990
206677
|
}
|
|
205991
|
-
function rpcPortFilePath(storageDir, directory, pid = process.pid) {
|
|
205992
|
-
|
|
206678
|
+
function rpcPortFilePath(storageDir, directory, pid = process.pid, instanceId) {
|
|
206679
|
+
const suffix = instanceId ? `-${instanceId}` : "";
|
|
206680
|
+
return join20(rpcPortDir(storageDir, directory), `port-${pid}${suffix}.json`);
|
|
205993
206681
|
}
|
|
205994
206682
|
function isPidAlive(pid) {
|
|
205995
206683
|
if (!Number.isInteger(pid) || pid <= 0)
|
|
@@ -206017,7 +206705,8 @@ function parseRpcPortFile(content, fallbackPid = 0) {
|
|
|
206017
206705
|
port: port2,
|
|
206018
206706
|
pid,
|
|
206019
206707
|
started_at: Number.isFinite(startedAt) ? startedAt : 0,
|
|
206020
|
-
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
|
|
206021
206710
|
};
|
|
206022
206711
|
} catch {
|
|
206023
206712
|
return null;
|
|
@@ -206043,6 +206732,13 @@ function tokensMatch(presented, expected) {
|
|
|
206043
206732
|
return false;
|
|
206044
206733
|
return timingSafeEqual(a, b);
|
|
206045
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
|
+
}
|
|
206046
206742
|
|
|
206047
206743
|
class MagicContextRpcServer {
|
|
206048
206744
|
server = null;
|
|
@@ -206051,16 +206747,18 @@ class MagicContextRpcServer {
|
|
|
206051
206747
|
portFilePath;
|
|
206052
206748
|
portDir;
|
|
206053
206749
|
startedAt = Date.now();
|
|
206750
|
+
instanceId = randomBytes(8).toString("hex");
|
|
206054
206751
|
sockets = new Set;
|
|
206055
206752
|
token = randomBytes(32).toString("hex");
|
|
206056
206753
|
constructor(storageDir, directory) {
|
|
206057
|
-
this.portFilePath = rpcPortFilePath(storageDir, directory);
|
|
206754
|
+
this.portFilePath = rpcPortFilePath(storageDir, directory, process.pid, this.instanceId);
|
|
206058
206755
|
this.portDir = rpcPortDir(storageDir, directory);
|
|
206059
206756
|
}
|
|
206060
206757
|
handle(method, handler) {
|
|
206061
206758
|
this.handlers.set(method, handler);
|
|
206062
206759
|
}
|
|
206063
206760
|
async start() {
|
|
206761
|
+
this.startedAt = Date.now();
|
|
206064
206762
|
const self2 = this;
|
|
206065
206763
|
const server = Bun.serve({
|
|
206066
206764
|
port: 0,
|
|
@@ -206103,7 +206801,8 @@ class MagicContextRpcServer {
|
|
|
206103
206801
|
port: this.port,
|
|
206104
206802
|
pid: process.pid,
|
|
206105
206803
|
started_at: this.startedAt,
|
|
206106
|
-
token: this.token
|
|
206804
|
+
token: this.token,
|
|
206805
|
+
instance_id: this.instanceId
|
|
206107
206806
|
}), { encoding: "utf-8", mode: 384 });
|
|
206108
206807
|
renameSync5(tmpPath, this.portFilePath);
|
|
206109
206808
|
try {
|
|
@@ -206149,6 +206848,9 @@ class MagicContextRpcServer {
|
|
|
206149
206848
|
async handleFetch(req, srv) {
|
|
206150
206849
|
const url2 = new URL(req.url);
|
|
206151
206850
|
if (url2.pathname === "/ws") {
|
|
206851
|
+
if (!tokensMatch(websocketToken(req, url2), this.token)) {
|
|
206852
|
+
return new Response("Unauthorized", { status: 401 });
|
|
206853
|
+
}
|
|
206152
206854
|
const ok = srv.upgrade(req, { data: { authed: false } });
|
|
206153
206855
|
if (ok)
|
|
206154
206856
|
return;
|
|
@@ -206160,9 +206862,7 @@ class MagicContextRpcServer {
|
|
|
206160
206862
|
if (req.method !== "POST" || !url2.pathname.startsWith("/rpc/")) {
|
|
206161
206863
|
return new Response("Not Found", { status: 404 });
|
|
206162
206864
|
}
|
|
206163
|
-
|
|
206164
|
-
const presented = typeof auth === "string" ? auth.replace(/^Bearer\s+/i, "") : "";
|
|
206165
|
-
if (!tokensMatch(presented, this.token)) {
|
|
206865
|
+
if (!tokensMatch(bearerToken(req), this.token)) {
|
|
206166
206866
|
return json2({ error: "Unauthorized" }, 401);
|
|
206167
206867
|
}
|
|
206168
206868
|
const method = url2.pathname.slice(5);
|
|
@@ -206197,6 +206897,8 @@ class MagicContextRpcServer {
|
|
|
206197
206897
|
} catch {
|
|
206198
206898
|
return;
|
|
206199
206899
|
}
|
|
206900
|
+
if (msg.type !== "hello" && !ws.data.authed)
|
|
206901
|
+
return;
|
|
206200
206902
|
if (msg.type === "hello") {
|
|
206201
206903
|
if (!tokensMatch(typeof msg.token === "string" ? msg.token : "", this.token)) {
|
|
206202
206904
|
ws.send(JSON.stringify({ type: "error", error: "unauthorized" }));
|
|
@@ -206209,6 +206911,8 @@ class MagicContextRpcServer {
|
|
|
206209
206911
|
}
|
|
206210
206912
|
ws.data.authed = true;
|
|
206211
206913
|
ws.data.sessionId = typeof msg.sessionId === "string" && msg.sessionId.length > 0 ? msg.sessionId : undefined;
|
|
206914
|
+
ws.data.unregister?.();
|
|
206915
|
+
ws.data.unregister = undefined;
|
|
206212
206916
|
const sink = {
|
|
206213
206917
|
sessionId: ws.data.sessionId,
|
|
206214
206918
|
send: (notification) => {
|
|
@@ -206218,7 +206922,10 @@ class MagicContextRpcServer {
|
|
|
206218
206922
|
ws.data.unregister = registerNotificationSink(sink);
|
|
206219
206923
|
this.sockets.add(ws);
|
|
206220
206924
|
const lastReceivedId = Number(msg.lastReceivedId ?? 0);
|
|
206221
|
-
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);
|
|
206222
206929
|
for (const notification of backlog) {
|
|
206223
206930
|
ws.send(JSON.stringify({ type: "notification", notification }));
|
|
206224
206931
|
}
|
|
@@ -206228,7 +206935,13 @@ class MagicContextRpcServer {
|
|
|
206228
206935
|
if (msg.type === "ack") {
|
|
206229
206936
|
const lastReceivedId = Number(msg.lastReceivedId ?? 0);
|
|
206230
206937
|
if (Number.isFinite(lastReceivedId) && lastReceivedId > 0) {
|
|
206231
|
-
|
|
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
|
+
}
|
|
206232
206945
|
}
|
|
206233
206946
|
}
|
|
206234
206947
|
}
|