@cortexkit/pi-magic-context 0.49.6 → 0.49.7
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/auto-search-worker.js +10 -7
- package/dist/{compaction-marker-candidates-9w5g66at.js → compaction-marker-candidates-tqcq5n00.js} +2 -2
- package/dist/compaction-marker-candidates-worker.js +2 -2
- package/dist/{embedding-backfill-809pgwvb.js → embedding-backfill-vygvr2x4.js} +6 -6
- package/dist/{index-fx0r5485.js → index-0d8zqccx.js} +3 -3
- package/dist/{index-9mn34fnx.js → index-0eg59y8d.js} +2 -2
- package/dist/{index-5z1h0qsa.js → index-0ww9cgfd.js} +4 -4
- package/dist/{index-wf7mwdnf.js → index-34zacc0h.js} +1 -1
- package/dist/{index-57mvvbbh.js → index-6q5mx0cz.js} +8 -7
- package/dist/{index-fcacf11g.js → index-6vjj0555.js} +1 -1
- package/dist/{index-5rc9kvrk.js → index-7mm27xdc.js} +1 -1
- package/dist/{index-8vbh4tzw.js → index-7ptxvfc5.js} +34 -13
- package/dist/{index-hfnjfk4d.js → index-8jpqq2yk.js} +64 -64
- package/dist/{index-cd6bjrj4.js → index-8ye22z7k.js} +1 -1
- package/dist/{index-p4matwj0.js → index-a7fj661j.js} +1 -1
- package/dist/{index-kxxdyjfs.js → index-dj784nyr.js} +1 -1
- package/dist/{index-swab4319.js → index-dj8hpkz7.js} +1 -1
- package/dist/{index-j7pa3t4s.js → index-fhv6ydkq.js} +98 -19
- package/dist/{index-kamhcj15.js → index-gqsfhksw.js} +1 -1
- package/dist/{index-0p9gemc6.js → index-mrsarsbh.js} +160 -25
- package/dist/index.js +979 -321
- package/dist/{message-index-m6snq0fh.js → message-index-8rxj3cxh.js} +7 -7
- package/dist/{message-index-bounded-8a94k72f.js → message-index-bounded-e5bv8gzm.js} +7 -7
- package/dist/{message-time-backfill-xqwg3hg1.js → message-time-backfill-hmekd2jj.js} +1 -1
- package/dist/migration-worker.js +5 -5
- package/dist/{project-embedding-registry-3x6nmszv.js → project-embedding-registry-z5m4eh3d.js} +5 -5
- package/dist/raw-ordinal-warmup-worker.js +17 -15
- package/dist/{read-session-chunk-49e1ayzs.js → read-session-chunk-sp5adk01.js} +6 -4
- package/dist/{read-session-raw-545mgtjz.js → read-session-raw-s14c7nf3.js} +2 -2
- package/dist/{search-q76dp7w9.js → search-hgfaqytc.js} +13 -13
- package/dist/{sqlite-wp7wddw3.js → sqlite-f8kh2n10.js} +1 -1
- package/dist/{storage-memory-7417pbm9.js → storage-memory-yf7r98vz.js} +2 -2
- package/dist/{storage-tags-cvjsrbqh.js → storage-tags-0xk95neb.js} +2 -2
- package/dist/subagent-entry.js +16 -16
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -168,6 +168,7 @@ import {
|
|
|
168
168
|
readPiSessionMessagePage,
|
|
169
169
|
findRestartModelSeedFromBranch,
|
|
170
170
|
iterateEntriesToRawMessageRange,
|
|
171
|
+
iterateAsyncEntriesToRawMessageRange,
|
|
171
172
|
convertEntriesToRawMessages,
|
|
172
173
|
convertPiAssistantEntryById,
|
|
173
174
|
convertEntriesToRawMessagePage,
|
|
@@ -249,7 +250,7 @@ import {
|
|
|
249
250
|
registerTodoStateLifecycle,
|
|
250
251
|
syncCtxMemoryToolEnabled,
|
|
251
252
|
registerMagicContextTools
|
|
252
|
-
} from "./index-
|
|
253
|
+
} from "./index-mrsarsbh.js";
|
|
253
254
|
import {
|
|
254
255
|
canonicalModelIdentity,
|
|
255
256
|
piModelRefToCanonical,
|
|
@@ -289,7 +290,7 @@ import {
|
|
|
289
290
|
openCurrentDatabase,
|
|
290
291
|
openDatabaseAsync,
|
|
291
292
|
closeDatabase
|
|
292
|
-
} from "./index-
|
|
293
|
+
} from "./index-6q5mx0cz.js";
|
|
293
294
|
import {
|
|
294
295
|
setHomeProjectPermission,
|
|
295
296
|
ProjectIdentityError,
|
|
@@ -300,7 +301,7 @@ import {
|
|
|
300
301
|
resolveProjectIdentityForSession,
|
|
301
302
|
beginBootQuietPeriod,
|
|
302
303
|
scheduleAfterBootQuiet
|
|
303
|
-
} from "./index-
|
|
304
|
+
} from "./index-gqsfhksw.js";
|
|
304
305
|
import {
|
|
305
306
|
detectThinkingBindingMismatch,
|
|
306
307
|
detectLatestTurnThinkingMismatch,
|
|
@@ -398,6 +399,7 @@ import {
|
|
|
398
399
|
getPendingPiCompactionMarkerState,
|
|
399
400
|
setPendingPiCompactionMarkerState,
|
|
400
401
|
clearPendingPiCompactionMarkerStateIf,
|
|
402
|
+
logPiCompactionMarkerClear,
|
|
401
403
|
getSessionsWithPendingPiMarker,
|
|
402
404
|
setSessionWorkMetrics,
|
|
403
405
|
getSessionWorkMetrics,
|
|
@@ -445,6 +447,7 @@ import {
|
|
|
445
447
|
readRawSessionMessages2,
|
|
446
448
|
getRawSessionMessageOrdinalCount2,
|
|
447
449
|
visitRawSessionMessages2,
|
|
450
|
+
visitRawSessionMessagesAsync2,
|
|
448
451
|
readRawSessionMessageRange2,
|
|
449
452
|
prepareRawSessionOrdinals2,
|
|
450
453
|
primeTailRawMessageCache2,
|
|
@@ -455,7 +458,7 @@ import {
|
|
|
455
458
|
getRawSessionTagKeysThrough2,
|
|
456
459
|
getLegacyProtectedTailStartOrdinal2,
|
|
457
460
|
readSessionChunk2
|
|
458
|
-
} from "./index-
|
|
461
|
+
} from "./index-fhv6ydkq.js";
|
|
459
462
|
import {
|
|
460
463
|
UNKNOWN_FIT_RATIO,
|
|
461
464
|
hasMeasuredDecisionCalibration,
|
|
@@ -538,7 +541,7 @@ import {
|
|
|
538
541
|
getToolTagNumberByOwner2,
|
|
539
542
|
getNullOwnerToolTag2,
|
|
540
543
|
adoptNullOwnerToolTag2
|
|
541
|
-
} from "./index-
|
|
544
|
+
} from "./index-6vjj0555.js";
|
|
542
545
|
import {
|
|
543
546
|
textMentionsRecentCommit,
|
|
544
547
|
hasMeaningfulUserText2,
|
|
@@ -562,7 +565,7 @@ import {
|
|
|
562
565
|
isTransientSqliteError2,
|
|
563
566
|
withAsyncPrivilegedWriter2,
|
|
564
567
|
indexCoordinationInput2
|
|
565
|
-
} from "./index-
|
|
568
|
+
} from "./index-7ptxvfc5.js";
|
|
566
569
|
import {
|
|
567
570
|
stableStringify,
|
|
568
571
|
closeQuietly,
|
|
@@ -587,7 +590,7 @@ import"./index-399p688g.js";
|
|
|
587
590
|
import {
|
|
588
591
|
runSessionWarmupWorker,
|
|
589
592
|
RAW_SUMMARY_TEXT_MAX_CHARS2
|
|
590
|
-
} from "./index-
|
|
593
|
+
} from "./index-7mm27xdc.js";
|
|
591
594
|
import {
|
|
592
595
|
piHarnessKindFromExecutable
|
|
593
596
|
} from "./index-0j0zqb4t.js";
|
|
@@ -613,13 +616,13 @@ import {
|
|
|
613
616
|
indexSingleMessage2,
|
|
614
617
|
indexMessagesAfterOrdinal2,
|
|
615
618
|
sweepOrphanedOpenCodeMessageIndexes2
|
|
616
|
-
} from "./index-
|
|
619
|
+
} from "./index-0ww9cgfd.js";
|
|
617
620
|
import {
|
|
618
621
|
formatSynapseLaneDescriptor
|
|
619
622
|
} from "./index-21v1kfs7.js";
|
|
620
623
|
import {
|
|
621
624
|
sameProjectDirectory
|
|
622
|
-
} from "./index-
|
|
625
|
+
} from "./index-34zacc0h.js";
|
|
623
626
|
import {
|
|
624
627
|
buildCanonicalChunkTextFromFts,
|
|
625
628
|
buildCompartmentSummaryFallbackText,
|
|
@@ -629,7 +632,7 @@ import {
|
|
|
629
632
|
replaceCompartmentChunkEmbeddings,
|
|
630
633
|
deleteChunkEmbedBackoffForSession,
|
|
631
634
|
chunkWindowSourceKey
|
|
632
|
-
} from "./index-
|
|
635
|
+
} from "./index-8ye22z7k.js";
|
|
633
636
|
import {
|
|
634
637
|
recordSessionProjectIdentity,
|
|
635
638
|
GIT_SWEEP_LEASE_RENEWAL_MS,
|
|
@@ -654,7 +657,7 @@ import {
|
|
|
654
657
|
embedSessionCompartmentChunks2,
|
|
655
658
|
getEmbeddingCoverageStatus2,
|
|
656
659
|
getAutoEmbeddingSessionCoverage2
|
|
657
|
-
} from "./index-
|
|
660
|
+
} from "./index-0d8zqccx.js";
|
|
658
661
|
import {
|
|
659
662
|
V2_MEMORY_CATEGORIES
|
|
660
663
|
} from "./index-135e8hvg.js";
|
|
@@ -666,7 +669,7 @@ import {
|
|
|
666
669
|
expandWorkspaceIdentitySetWithAliases,
|
|
667
670
|
sourceNameForMemory,
|
|
668
671
|
computeWorkspaceEpochFingerprint
|
|
669
|
-
} from "./index-
|
|
672
|
+
} from "./index-0eg59y8d.js";
|
|
670
673
|
import {
|
|
671
674
|
configureContextDatabasePragmas2
|
|
672
675
|
} from "./index-fapenpch.js";
|
|
@@ -716,7 +719,7 @@ import {
|
|
|
716
719
|
normalizeVerificationFiles,
|
|
717
720
|
indexCommitsForProject,
|
|
718
721
|
embedUnembeddedCommits
|
|
719
|
-
} from "./index-
|
|
722
|
+
} from "./index-8jpqq2yk.js";
|
|
720
723
|
import {
|
|
721
724
|
setEmbeddingSessionBusy2,
|
|
722
725
|
isEmbeddingHostBusy2
|
|
@@ -740,7 +743,7 @@ import {
|
|
|
740
743
|
setMemoryClassification2,
|
|
741
744
|
archiveMemory2,
|
|
742
745
|
getMemoryCountsByStatus2
|
|
743
|
-
} from "./index-
|
|
746
|
+
} from "./index-dj784nyr.js";
|
|
744
747
|
import {
|
|
745
748
|
invalidateMemory2
|
|
746
749
|
} from "./index-yhzhdp3m.js";
|
|
@@ -750,8 +753,8 @@ import {
|
|
|
750
753
|
import"./index-gc2749rx.js";
|
|
751
754
|
import {
|
|
752
755
|
cosineSimilarity
|
|
753
|
-
} from "./index-
|
|
754
|
-
import"./index-
|
|
756
|
+
} from "./index-a7fj661j.js";
|
|
757
|
+
import"./index-dj8hpkz7.js";
|
|
755
758
|
import {
|
|
756
759
|
buildHiddenAgentRegistrations,
|
|
757
760
|
logTemperatureDecision,
|
|
@@ -2367,7 +2370,7 @@ function observeNoteNudgeServe(args) {
|
|
|
2367
2370
|
sessions.set(args.sessionId, seen);
|
|
2368
2371
|
return {
|
|
2369
2372
|
isFirstServe: warm && args.isLiveTail && firstServe,
|
|
2370
|
-
|
|
2373
|
+
prefixRewriteCoversAnchor: args.anchorMessageId !== null && args.prefixRewriteCoversAnchor
|
|
2371
2374
|
};
|
|
2372
2375
|
}
|
|
2373
2376
|
var NOTE_NUDGE_COOLDOWN_MS = 15 * 60 * 1000;
|
|
@@ -2406,7 +2409,7 @@ function peekNoteNudgeText(db, sessionId, currentUserMessageId, projectIdentity,
|
|
|
2406
2409
|
const state = getPersistedNoteNudge(db, sessionId);
|
|
2407
2410
|
if (!state.triggerPending)
|
|
2408
2411
|
return null;
|
|
2409
|
-
if (eligibility && !eligibility.isFirstServe && !eligibility.
|
|
2412
|
+
if (eligibility && !eligibility.isFirstServe && !eligibility.prefixRewriteCoversAnchor) {
|
|
2410
2413
|
sessionLog2(sessionId, "note-nudge: deferring — user already served or first serve unproven");
|
|
2411
2414
|
return null;
|
|
2412
2415
|
}
|
|
@@ -3421,7 +3424,7 @@ function startHostStallProfilerSwitch() {
|
|
|
3421
3424
|
// package.json
|
|
3422
3425
|
var package_default = {
|
|
3423
3426
|
name: "@cortexkit/pi-magic-context",
|
|
3424
|
-
version: "0.49.
|
|
3427
|
+
version: "0.49.7",
|
|
3425
3428
|
type: "module",
|
|
3426
3429
|
description: "Pi and OMP coding agent extension for Magic Context — cross-session memory and context management",
|
|
3427
3430
|
main: "dist/index.js",
|
|
@@ -10320,6 +10323,25 @@ async function runHiddenSingleShotPrompt(args) {
|
|
|
10320
10323
|
});
|
|
10321
10324
|
promptSettled = true;
|
|
10322
10325
|
return { validated: run.validated, completion: run.output, childSessionId: handle.id };
|
|
10326
|
+
} catch (error) {
|
|
10327
|
+
if (args.onFailureOutput) {
|
|
10328
|
+
let timer;
|
|
10329
|
+
try {
|
|
10330
|
+
const completion = await Promise.race([
|
|
10331
|
+
args.executor.collect(handle, 200),
|
|
10332
|
+
new Promise((resolve) => {
|
|
10333
|
+
timer = setTimeout(() => resolve(null), 5000);
|
|
10334
|
+
})
|
|
10335
|
+
]);
|
|
10336
|
+
if (completion)
|
|
10337
|
+
args.onFailureOutput(completion);
|
|
10338
|
+
} catch (readError) {
|
|
10339
|
+
log2(`${args.callContext}: failed to collect interrupted output: ${String(readError)}`);
|
|
10340
|
+
} finally {
|
|
10341
|
+
clearTimeout(timer);
|
|
10342
|
+
}
|
|
10343
|
+
}
|
|
10344
|
+
throw error;
|
|
10323
10345
|
} finally {
|
|
10324
10346
|
await args.executor.close(handle, {
|
|
10325
10347
|
promptSettled,
|
|
@@ -13906,31 +13928,33 @@ function snapshotMaintainDocsFiles(docsDir) {
|
|
|
13906
13928
|
import { createHash as createHash8 } from "node:crypto";
|
|
13907
13929
|
|
|
13908
13930
|
// ../plugin/src/features/magic-context/dreamer/map-memories-prompt.ts
|
|
13931
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
13909
13932
|
import { existsSync as existsSync8, statSync as statSync2 } from "node:fs";
|
|
13910
13933
|
import path10 from "node:path";
|
|
13934
|
+
import { promisify as promisify2 } from "node:util";
|
|
13911
13935
|
var MAP_MEMORIES_SYSTEM_PROMPT = `You are a memory mapper for the magic-context system. You map project memories to the repository files that back them.
|
|
13912
13936
|
|
|
13913
13937
|
A memory's BACKING FILES are the file(s) whose code the memory makes a claim about — the files you would open to check whether the memory is accurate. You do NOT judge accuracy, rewrite, or remove anything. You only LOCATE backing files.
|
|
13914
13938
|
|
|
13915
|
-
Tools (read-only): read, grep, glob.
|
|
13939
|
+
Tools (read-only): read, grep, glob. Use at most 3 tool calls per memory, including searches and reads. Never read the same file twice in a batch; reuse what you already learned. Never repeat an identical search or use broad searches such as "fn " or ".*". Request one useful line range, not repeated slices. Each memory may come with "Likely files" from paths it names or a literal identifier search, confirmed to exist — confirm those FIRST (cheap) instead of searching. Use search/grep to FIND code only when no likely files are given. Do not guess — confirm a file exists and genuinely backs the memory before listing it. You are locating code, not exhaustively verifying the claim. If the call budget runs out before you can decide, leave that id unmapped and move on; uncertainty is not evidence of file-independence.
|
|
13916
13940
|
|
|
13917
13941
|
For each memory decide ONE of:
|
|
13918
|
-
- Backing files found → the
|
|
13942
|
+
- Backing files found → the specific repo-relative paths whose code the memory is about; do not chase every caller or related implementation.
|
|
13919
13943
|
- File-independent → the memory describes EXTERNAL behavior (a provider / API / platform / protocol limit, e.g. "Anthropic returns 400 on empty content"), or a pure process / workflow / philosophy rule, with NO specific local file that backs it. A BEHAVIORAL claim (when to act, how to work, who decides, or tool-usage discipline) is file-independent even when it cites file paths or commands as examples: a named file is not the file backing the rule.
|
|
13920
13944
|
|
|
13921
|
-
Output ONE XML manifest
|
|
13945
|
+
Output ONE XML manifest and NOTHING else — no narration, no per-memory commentary, no reasoning. Open <mappings> before investigating. Emit each <memory> element as soon as you decide its mapping, before investigating the next memory, so an interruption cannot lose completed work. Close </mappings> when finished:
|
|
13922
13946
|
<mappings>
|
|
13923
13947
|
<memory id="N" files="path/a.ts,path/b.ts"/>
|
|
13924
13948
|
<memory id="M" independent="true"/>
|
|
13925
13949
|
</mappings>
|
|
13926
13950
|
|
|
13927
13951
|
Rules:
|
|
13928
|
-
- Every input memory id MUST appear exactly once.
|
|
13952
|
+
- Every decided input memory id MUST appear exactly once. Omit uncertain ids rather than inventing backing files or declaring them independent.
|
|
13929
13953
|
- files: repo-relative, comma-separated, no spaces inside a path. Only files that actually exist and genuinely back the memory.
|
|
13930
13954
|
- A BACKING FILE is CODE that implements or handles the claim — not a file that merely mentions it. A path named inside a process directive is an action target or example, not evidence that the file backs the directive. A markdown doc (.md), a PARITY/notes file, or a test that only DESCRIBES an external fact is NOT a backing file. If the only place a memory's fact appears is prose/docs/a test (no code implements or handles it), mark it independent="true".
|
|
13931
13955
|
- Many CONSTRAINTS are HYBRID: "external system does X, and OUR code handles it here." Map those to the HANDLING code (you can verify the handling, even though you can't verify the external behavior). Only mark independent when there is NO local code that implements or handles the fact.
|
|
13932
13956
|
- Prefer the most specific file(s); do not pad with tangential files. Most memories map to one file; some to a few.
|
|
13933
|
-
-
|
|
13957
|
+
- Mark independent="true" only for the file-independent cases above; an investigation that runs out of calls stays unmapped.`;
|
|
13934
13958
|
var MAX_SEED_PATHS_PER_MEMORY = 3;
|
|
13935
13959
|
var PATH_PATTERN = "`?((?:[\\w.-]+\\/)+[\\w.-]+\\.(?:ts|tsx|js|jsx|mjs|cjs|rs|go|py|json|jsonc|sql|toml|sh))`?";
|
|
13936
13960
|
function extractMemoryCandidatePaths(content, repoDir) {
|
|
@@ -13952,10 +13976,65 @@ function extractMemoryCandidatePaths(content, repoDir) {
|
|
|
13952
13976
|
}
|
|
13953
13977
|
return [...found];
|
|
13954
13978
|
}
|
|
13979
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
13980
|
+
async function seedIdentifierCandidates(memories, repoDir) {
|
|
13981
|
+
const identifiers = new Map(memories.filter((memory) => memory.candidates.length === 0).map((memory) => [
|
|
13982
|
+
memory.id,
|
|
13983
|
+
[
|
|
13984
|
+
...new Set(memory.content.match(/\b(?:[A-Z][A-Za-z0-9_]{3,}|[a-z][a-z0-9]*[A-Z][A-Za-z0-9_]*|[a-z][a-z0-9]*_[A-Za-z0-9_]{2,})\b/g) ?? [])
|
|
13985
|
+
].slice(0, 3)
|
|
13986
|
+
]));
|
|
13987
|
+
const terms = [...new Set([...identifiers.values()].flat())];
|
|
13988
|
+
if (terms.length === 0)
|
|
13989
|
+
return memories;
|
|
13990
|
+
let stdout;
|
|
13991
|
+
try {
|
|
13992
|
+
const result = await execFileAsync2("git", [
|
|
13993
|
+
"-c",
|
|
13994
|
+
"core.quotePath=false",
|
|
13995
|
+
"grep",
|
|
13996
|
+
"--no-color",
|
|
13997
|
+
"-n",
|
|
13998
|
+
"-I",
|
|
13999
|
+
"-F",
|
|
14000
|
+
...terms.flatMap((term) => ["-e", term]),
|
|
14001
|
+
"--",
|
|
14002
|
+
...["ts", "tsx", "js", "jsx", "mjs", "cjs", "rs", "go", "py", "sql", "sh"].map((extension) => `*.${extension}`)
|
|
14003
|
+
], {
|
|
14004
|
+
cwd: repoDir,
|
|
14005
|
+
encoding: "utf8",
|
|
14006
|
+
timeout: 2000,
|
|
14007
|
+
maxBuffer: 256 * 1024,
|
|
14008
|
+
windowsHide: true,
|
|
14009
|
+
killSignal: "SIGKILL"
|
|
14010
|
+
});
|
|
14011
|
+
stdout = result.stdout;
|
|
14012
|
+
} catch {
|
|
14013
|
+
return memories;
|
|
14014
|
+
}
|
|
14015
|
+
const matches = stdout.split(`
|
|
14016
|
+
`).flatMap((line) => {
|
|
14017
|
+
const match = line.match(/^([^:]+):\d+:(.*)$/);
|
|
14018
|
+
return match ? [{ file: match[1], text: match[2] }] : [];
|
|
14019
|
+
});
|
|
14020
|
+
return memories.map((memory) => {
|
|
14021
|
+
const names = identifiers.get(memory.id);
|
|
14022
|
+
if (!names?.length)
|
|
14023
|
+
return memory;
|
|
14024
|
+
const files = new Map;
|
|
14025
|
+
for (const match of matches) {
|
|
14026
|
+
const score = names.filter((name) => match.text.includes(name)).length;
|
|
14027
|
+
if (score)
|
|
14028
|
+
files.set(match.file, Math.max(files.get(match.file) ?? 0, score));
|
|
14029
|
+
}
|
|
14030
|
+
const candidates = [...files].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_SEED_PATHS_PER_MEMORY).map(([file]) => file);
|
|
14031
|
+
return candidates.length ? { ...memory, candidates } : memory;
|
|
14032
|
+
});
|
|
14033
|
+
}
|
|
13955
14034
|
function buildMapMemoriesPrompt(projectPath, memories) {
|
|
13956
14035
|
const list = memories.map((m) => {
|
|
13957
14036
|
const seed = m.candidates.length ? `
|
|
13958
|
-
Likely files (
|
|
14037
|
+
Likely files (candidate paths, confirmed to exist): ${m.candidates.join(", ")}` : "";
|
|
13959
14038
|
return `[${m.id}] ${m.category}
|
|
13960
14039
|
${m.content}${seed}`;
|
|
13961
14040
|
}).join(`
|
|
@@ -13965,13 +14044,13 @@ ${m.content}${seed}`;
|
|
|
13965
14044
|
|
|
13966
14045
|
Project: ${projectPath}
|
|
13967
14046
|
|
|
13968
|
-
For each memory below, find the repo file(s) it makes a claim about, or mark it file-independent. Behavioral process/workflow directives stay file-independent even when they name files. When "Likely files" are listed, those paths
|
|
14047
|
+
For each memory below, find the repo file(s) it makes a claim about, or mark it file-independent. Behavioral process/workflow directives stay file-independent even when they name files. When "Likely files" are listed, those paths either occur in the memory or match its identifiers in a bounded literal source search — START there only for code claims: confirm each actually backs the claim (a quick read/outline), drop any that don't, add others only if genuinely needed. Search from scratch only when no likely files are given. Use at most 3 tool calls per memory, reuse prior reads, and emit each decided mapping immediately in ONE <mappings> manifest. Leave uncertain ids unmapped and move on.
|
|
13969
14048
|
|
|
13970
14049
|
<memories>
|
|
13971
14050
|
${list}
|
|
13972
14051
|
</memories>`;
|
|
13973
14052
|
}
|
|
13974
|
-
var MEMORY_ELEMENT_PATTERN = "<memory\\b([
|
|
14053
|
+
var MEMORY_ELEMENT_PATTERN = "<memory\\b([^<>]*)(?:\\/>|>([\\s\\S]*?)<\\/memory>)";
|
|
13975
14054
|
var NESTED_FILE_PATTERN = "<file\\b([^>]*)\\/?>";
|
|
13976
14055
|
function extractNestedFilePaths(inner) {
|
|
13977
14056
|
const files = [];
|
|
@@ -14029,12 +14108,78 @@ function validateMapMemoriesManifest(text, expectedIds) {
|
|
|
14029
14108
|
assertNoDuplicateManifestIds(parsed.filter((entry) => expectedIds.has(entry.id)).map((entry) => entry.id), "mappings");
|
|
14030
14109
|
return parsed;
|
|
14031
14110
|
}
|
|
14111
|
+
function parsePartialMapMemoriesManifest(text, expectedIds) {
|
|
14112
|
+
const start = text.indexOf("<mappings>");
|
|
14113
|
+
if (start < 0)
|
|
14114
|
+
return [];
|
|
14115
|
+
const entries = new Map;
|
|
14116
|
+
const conflicts = new Set;
|
|
14117
|
+
for (const match of text.slice(start).matchAll(new RegExp(MEMORY_ELEMENT_PATTERN, "gi"))) {
|
|
14118
|
+
try {
|
|
14119
|
+
const entry = parseMapMemoriesManifest(`<mappings>${match[0]}</mappings>`)[0];
|
|
14120
|
+
if (!entry || !expectedIds.has(entry.id) || conflicts.has(entry.id))
|
|
14121
|
+
continue;
|
|
14122
|
+
const previous = entries.get(entry.id);
|
|
14123
|
+
if (previous && JSON.stringify(previous) !== JSON.stringify(entry)) {
|
|
14124
|
+
entries.delete(entry.id);
|
|
14125
|
+
conflicts.add(entry.id);
|
|
14126
|
+
} else {
|
|
14127
|
+
entries.set(entry.id, entry);
|
|
14128
|
+
}
|
|
14129
|
+
} catch {}
|
|
14130
|
+
}
|
|
14131
|
+
return [...entries.values()];
|
|
14132
|
+
}
|
|
14032
14133
|
|
|
14033
14134
|
// ../plugin/src/features/magic-context/dreamer/map-memories.ts
|
|
14034
|
-
var MAP_BATCH_SIZE =
|
|
14135
|
+
var MAP_BATCH_SIZE = 5;
|
|
14035
14136
|
var MAP_BATCH_FLOOR_MS = 240000;
|
|
14036
14137
|
var CONSECUTIVE_TIMEOUT_LIMIT2 = 2;
|
|
14037
14138
|
var MAX_INDEPENDENT_REQUEUE_PER_RUN = MAP_BATCH_SIZE;
|
|
14139
|
+
function retryKey(project) {
|
|
14140
|
+
return `map-memories:retry:${project}`;
|
|
14141
|
+
}
|
|
14142
|
+
function readRetries(db, project) {
|
|
14143
|
+
try {
|
|
14144
|
+
const parsed = JSON.parse(getDreamState(db, retryKey(project)) ?? "{}");
|
|
14145
|
+
if (!isRecord(parsed))
|
|
14146
|
+
return {};
|
|
14147
|
+
const retries = {};
|
|
14148
|
+
for (const [id, value] of Object.entries(parsed)) {
|
|
14149
|
+
if (isRecord(value) && typeof value.content === "string" && typeof value.failures === "number" && typeof value.deferUntil === "number") {
|
|
14150
|
+
retries[id] = {
|
|
14151
|
+
content: value.content,
|
|
14152
|
+
failures: value.failures,
|
|
14153
|
+
deferUntil: value.deferUntil
|
|
14154
|
+
};
|
|
14155
|
+
}
|
|
14156
|
+
}
|
|
14157
|
+
return retries;
|
|
14158
|
+
} catch {
|
|
14159
|
+
return {};
|
|
14160
|
+
}
|
|
14161
|
+
}
|
|
14162
|
+
function contentKey(memory) {
|
|
14163
|
+
return createHash8("sha256").update(`${memory.category}
|
|
14164
|
+
${memory.content}`).digest("hex");
|
|
14165
|
+
}
|
|
14166
|
+
function isDeferred(memory, retries) {
|
|
14167
|
+
const retry = retries[memory.id];
|
|
14168
|
+
return !!retry && retry.content === contentKey(memory) && retry.deferUntil > Date.now();
|
|
14169
|
+
}
|
|
14170
|
+
function assistantText(messages) {
|
|
14171
|
+
return messages.filter((message) => isRecord(message) && (message.role === "assistant" || isRecord(message.info) && message.info.role === "assistant")).map((message) => extractLatestAssistantText([message]) ?? (isRecord(message) && Array.isArray(message.content) ? message.content.filter(isRecord).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join(`
|
|
14172
|
+
`) : "")).join(`
|
|
14173
|
+
`);
|
|
14174
|
+
}
|
|
14175
|
+
function currentManifest(text) {
|
|
14176
|
+
const start = text.lastIndexOf("<mappings>");
|
|
14177
|
+
return start < 0 ? text : text.slice(start);
|
|
14178
|
+
}
|
|
14179
|
+
function logBatchUsage(messages, committed, startedAt) {
|
|
14180
|
+
const steps = messages.filter((message) => isRecord(message) && (message.role === "assistant" || isRecord(message.info) && message.info.role === "assistant")).length;
|
|
14181
|
+
log2(`[dreamer] map-memories batch: steps=${steps} committed=${committed} steps_per_memory=${committed ? (steps / committed).toFixed(2) : "n/a"} elapsed_ms=${Date.now() - startedAt}`);
|
|
14182
|
+
}
|
|
14038
14183
|
function computeMapBatchSliceMs(remainingMs, batchesRemaining) {
|
|
14039
14184
|
return Math.min(remainingMs, Math.max(MAP_BATCH_FLOOR_MS, Math.floor(remainingMs / batchesRemaining)));
|
|
14040
14185
|
}
|
|
@@ -14056,7 +14201,7 @@ function toMapInput(memory, repoDir) {
|
|
|
14056
14201
|
candidates: extractMemoryCandidatePaths(memory.content, repoDir)
|
|
14057
14202
|
};
|
|
14058
14203
|
}
|
|
14059
|
-
function selectMapMemoryInputs(db, projectIdentity, repoDir) {
|
|
14204
|
+
function selectMapMemoryInputs(db, projectIdentity, repoDir, includeDeferred = false) {
|
|
14060
14205
|
const active = getMemoriesByProject2(db, projectIdentity);
|
|
14061
14206
|
const activeIds = active.map((m) => m.id);
|
|
14062
14207
|
const unmapped = new Set(getUnmappedMemoryIds(db, activeIds));
|
|
@@ -14075,7 +14220,8 @@ function selectMapMemoryInputs(db, projectIdentity, repoDir) {
|
|
|
14075
14220
|
if (requeue.length >= MAX_INDEPENDENT_REQUEUE_PER_RUN)
|
|
14076
14221
|
break;
|
|
14077
14222
|
}
|
|
14078
|
-
|
|
14223
|
+
const retries = readRetries(db, projectIdentity);
|
|
14224
|
+
return [...unmappedInputs, ...requeue].filter((memory) => includeDeferred || !isDeferred(memory, retries));
|
|
14079
14225
|
}
|
|
14080
14226
|
async function mapMemories(args) {
|
|
14081
14227
|
const result = {
|
|
@@ -14085,14 +14231,21 @@ async function mapMemories(args) {
|
|
|
14085
14231
|
remaining: 0,
|
|
14086
14232
|
complete: true
|
|
14087
14233
|
};
|
|
14088
|
-
const
|
|
14234
|
+
const allInputs = selectMapMemoryInputs(args.db, args.projectIdentity, args.sessionDirectory, true);
|
|
14235
|
+
const retries = readRetries(args.db, args.projectIdentity);
|
|
14236
|
+
const activeIds = new Set(allInputs.map((memory) => String(memory.id)));
|
|
14237
|
+
for (const id of Object.keys(retries))
|
|
14238
|
+
if (!activeIds.has(id))
|
|
14239
|
+
delete retries[id];
|
|
14240
|
+
const inputs = allInputs.filter((memory) => !isDeferred(memory, retries));
|
|
14241
|
+
result.remaining = allInputs.length;
|
|
14242
|
+
result.complete = result.remaining === 0;
|
|
14089
14243
|
if (inputs.length === 0)
|
|
14090
14244
|
return result;
|
|
14091
14245
|
const batches = [];
|
|
14092
14246
|
for (let i = 0;i < inputs.length; i += MAP_BATCH_SIZE) {
|
|
14093
14247
|
batches.push({ inputs: inputs.slice(i, i + MAP_BATCH_SIZE), isOmissionRetry: false });
|
|
14094
14248
|
}
|
|
14095
|
-
result.remaining = inputs.length;
|
|
14096
14249
|
const abortController = new AbortController;
|
|
14097
14250
|
const heartbeat = startLeaseHeartbeat(args.db, args.holderId, args.leaseKey, (reason) => abortController.abort(new Error(reason)), args.leaseAcquisition);
|
|
14098
14251
|
try {
|
|
@@ -14115,6 +14268,24 @@ async function mapMemories(args) {
|
|
|
14115
14268
|
break;
|
|
14116
14269
|
const outcome = await mapOneBatch(args, batch.inputs, sliceMs, abortController.signal);
|
|
14117
14270
|
const committed = outcome.mapped + outcome.independent;
|
|
14271
|
+
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
14272
|
+
const committedIds = new Set(outcome.committedIds);
|
|
14273
|
+
for (const memory of batch.inputs) {
|
|
14274
|
+
if (committedIds.has(memory.id)) {
|
|
14275
|
+
delete retries[memory.id];
|
|
14276
|
+
continue;
|
|
14277
|
+
}
|
|
14278
|
+
const content = contentKey(memory);
|
|
14279
|
+
const prior = retries[memory.id];
|
|
14280
|
+
const failures = (prior?.content === content ? prior.failures : 0) + 1;
|
|
14281
|
+
retries[memory.id] = {
|
|
14282
|
+
content,
|
|
14283
|
+
failures,
|
|
14284
|
+
deferUntil: failures < 2 ? 0 : Date.now() + Math.min(24, 2 ** (failures - 2)) * 3600000
|
|
14285
|
+
};
|
|
14286
|
+
}
|
|
14287
|
+
setDreamState(args.db, retryKey(args.projectIdentity), JSON.stringify(retries));
|
|
14288
|
+
});
|
|
14118
14289
|
result.mapped += outcome.mapped;
|
|
14119
14290
|
result.independent += outcome.independent;
|
|
14120
14291
|
result.remaining -= committed;
|
|
@@ -14124,8 +14295,8 @@ async function mapMemories(args) {
|
|
|
14124
14295
|
}
|
|
14125
14296
|
if (outcome.requeue?.length) {
|
|
14126
14297
|
if (!batch.isOmissionRetry) {
|
|
14127
|
-
batches.
|
|
14128
|
-
inputs: outcome.requeue,
|
|
14298
|
+
batches.push({
|
|
14299
|
+
inputs: outcome.requeue.filter((memory) => !isDeferred(memory, retries)),
|
|
14129
14300
|
isOmissionRetry: true
|
|
14130
14301
|
});
|
|
14131
14302
|
log2(`[dreamer] map-memories: committed ${committed}/${batch.inputs.length} mapping(s); requeueing ${outcome.requeue.length} omitted id(s) in a targeted retry`);
|
|
@@ -14133,12 +14304,12 @@ async function mapMemories(args) {
|
|
|
14133
14304
|
log2(`[dreamer] map-memories: targeted retry still omitted ${outcome.requeue.length} id(s); leaving them unmapped for the next run`);
|
|
14134
14305
|
}
|
|
14135
14306
|
}
|
|
14136
|
-
if (outcome.failure?.class === "timeout") {
|
|
14307
|
+
if (outcome.failure?.class === "timeout" && committed === 0) {
|
|
14137
14308
|
consecutiveTimeouts += 1;
|
|
14138
14309
|
timeoutStreakElapsedMs.push(outcome.failure.elapsedMs);
|
|
14139
14310
|
if (consecutiveTimeouts >= CONSECUTIVE_TIMEOUT_LIMIT2) {
|
|
14140
14311
|
result.stopReason = "timeout-circuit-breaker";
|
|
14141
|
-
log2(`[dreamer] map-memories starvation: circuit breaker tripped after ${consecutiveTimeouts} consecutive batch timeouts
|
|
14312
|
+
log2(`[dreamer] map-memories starvation: circuit breaker tripped after ${consecutiveTimeouts} consecutive batch timeouts with no committed mappings (investigation made no durable progress); per-batch elapsed [${timeoutStreakElapsedMs.join("ms, ")}ms] vs ${sliceMs}ms slice; stopping with ${result.remaining} mapping(s) remaining`);
|
|
14142
14313
|
break;
|
|
14143
14314
|
}
|
|
14144
14315
|
} else {
|
|
@@ -14158,12 +14329,13 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
14158
14329
|
let promptSettled = false;
|
|
14159
14330
|
const startedAt = Date.now();
|
|
14160
14331
|
let budgetFinalized = false;
|
|
14332
|
+
let failedCompletion;
|
|
14161
14333
|
const onBudgetUpdate = (state) => {
|
|
14162
14334
|
budgetFinalized ||= state.finalizeFired;
|
|
14163
14335
|
args.onBudgetUpdate?.(state);
|
|
14164
14336
|
};
|
|
14165
14337
|
try {
|
|
14166
|
-
const prompt = buildMapMemoriesPrompt(args.projectIdentity, batch);
|
|
14338
|
+
const prompt = buildMapMemoriesPrompt(args.projectIdentity, await seedIdentifierCandidates(batch, args.sessionDirectory));
|
|
14167
14339
|
if (args.hiddenCompletionExecutor) {
|
|
14168
14340
|
const run = await runHiddenSingleShotPrompt({
|
|
14169
14341
|
executor: args.hiddenCompletionExecutor,
|
|
@@ -14179,14 +14351,18 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
14179
14351
|
fallbackModels: args.fallbackModels,
|
|
14180
14352
|
timeoutMs: sliceMs,
|
|
14181
14353
|
signal,
|
|
14182
|
-
|
|
14354
|
+
onFailureOutput: (completion) => {
|
|
14355
|
+
failedCompletion = completion;
|
|
14356
|
+
},
|
|
14357
|
+
parse: (text, completion) => validateMapMemoriesManifest(currentManifest(assistantText(completion.messages ?? []) || text), new Set(batch.map((memory) => memory.id)))
|
|
14183
14358
|
});
|
|
14184
14359
|
recordInvocation2(args, startedAt, {
|
|
14185
14360
|
status: "completed",
|
|
14186
14361
|
messages: run.completion.messages ?? []
|
|
14187
14362
|
});
|
|
14188
|
-
const outcome = await applyParsedBatchMappings(args, batch, run.validated, budgetFinalized);
|
|
14189
|
-
|
|
14363
|
+
const outcome = await applyParsedBatchMappings(args, batch, run.validated, budgetFinalized, true);
|
|
14364
|
+
logBatchUsage(run.completion.messages ?? [], outcome.mapped + outcome.independent, startedAt);
|
|
14365
|
+
const returnedIds = new Set(outcome.committedIds);
|
|
14190
14366
|
return {
|
|
14191
14367
|
...outcome,
|
|
14192
14368
|
requeue: budgetFinalized ? [] : batch.filter((memory) => !returnedIds.has(memory.id))
|
|
@@ -14229,7 +14405,7 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
14229
14405
|
fetchOutput: async () => {
|
|
14230
14406
|
const messagesResponse = await client.session.messages({
|
|
14231
14407
|
path: { id: agentSessionId },
|
|
14232
|
-
query: { directory: args.sessionDirectory, limit:
|
|
14408
|
+
query: { directory: args.sessionDirectory, limit: 200 }
|
|
14233
14409
|
});
|
|
14234
14410
|
return normalizeSDKResponse(messagesResponse, [], {
|
|
14235
14411
|
preferResponseOnMissingData: true
|
|
@@ -14239,7 +14415,7 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
14239
14415
|
if (hasLengthCappedOutput(messages)) {
|
|
14240
14416
|
throw new Error("map-memories returned length-capped output");
|
|
14241
14417
|
}
|
|
14242
|
-
const text =
|
|
14418
|
+
const text = currentManifest(assistantText(messages));
|
|
14243
14419
|
if (!text)
|
|
14244
14420
|
throw new Error("map-memories returned no output");
|
|
14245
14421
|
return validateMapMemoriesManifest(text, new Set(batch.map((memory) => memory.id)));
|
|
@@ -14247,8 +14423,9 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
14247
14423
|
});
|
|
14248
14424
|
promptSettled = true;
|
|
14249
14425
|
recordInvocation2(args, startedAt, { status: "completed", messages: run.output });
|
|
14250
|
-
const outcome = await applyParsedBatchMappings(args, batch, run.validated, budgetFinalized);
|
|
14251
|
-
|
|
14426
|
+
const outcome = await applyParsedBatchMappings(args, batch, run.validated, budgetFinalized, true);
|
|
14427
|
+
logBatchUsage(run.output, outcome.mapped + outcome.independent, startedAt);
|
|
14428
|
+
const returnedIds = new Set(outcome.committedIds);
|
|
14252
14429
|
return {
|
|
14253
14430
|
...outcome,
|
|
14254
14431
|
requeue: budgetFinalized ? [] : batch.filter((memory) => !returnedIds.has(memory.id))
|
|
@@ -14256,18 +14433,23 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
14256
14433
|
} catch (error) {
|
|
14257
14434
|
const desc = describeError(error);
|
|
14258
14435
|
log2(`[dreamer] map-memories batch failed: ${desc.brief}`, desc.stackHead ? { stackHead: desc.stackHead } : undefined);
|
|
14436
|
+
const messages = failedCompletion?.messages ?? await readFailedChildMessages(args.client, agentSessionId, args.sessionDirectory) ?? [];
|
|
14259
14437
|
recordInvocation2(args, startedAt, {
|
|
14260
14438
|
status: failedInvocationStatus(error),
|
|
14261
14439
|
error,
|
|
14262
|
-
messages
|
|
14440
|
+
messages
|
|
14263
14441
|
});
|
|
14264
|
-
if (error instanceof DreamerModuleFailureError
|
|
14442
|
+
if (error instanceof DreamerModuleFailureError)
|
|
14265
14443
|
throw error;
|
|
14266
14444
|
if (signal.aborted)
|
|
14267
14445
|
throw error;
|
|
14446
|
+
const recovered = parsePartialMapMemoriesManifest(assistantText(messages) || failedCompletion?.text || "", new Set(batch.map((memory) => memory.id)));
|
|
14447
|
+
const outcome = await applyParsedBatchMappings(args, batch, recovered, budgetFinalized, true);
|
|
14448
|
+
logBatchUsage(messages, outcome.mapped + outcome.independent, startedAt);
|
|
14449
|
+
if (error instanceof DreamTokenBudgetExceeded)
|
|
14450
|
+
throw error;
|
|
14268
14451
|
return {
|
|
14269
|
-
|
|
14270
|
-
independent: 0,
|
|
14452
|
+
...outcome,
|
|
14271
14453
|
failure: {
|
|
14272
14454
|
class: isTimeoutClassError2(error) ? "timeout" : "other",
|
|
14273
14455
|
elapsedMs: Date.now() - startedAt
|
|
@@ -14286,7 +14468,7 @@ async function mapOneBatch(args, batch, sliceMs, signal) {
|
|
|
14286
14468
|
});
|
|
14287
14469
|
}
|
|
14288
14470
|
}
|
|
14289
|
-
async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = false) {
|
|
14471
|
+
async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = false, allowPartial = false) {
|
|
14290
14472
|
const batchIds = new Set(batch.map((memory) => memory.id));
|
|
14291
14473
|
const valid = parsed.filter((entry) => batchIds.has(entry.id));
|
|
14292
14474
|
const unknown = parsed.filter((entry) => !batchIds.has(entry.id));
|
|
@@ -14294,14 +14476,14 @@ async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = f
|
|
|
14294
14476
|
log2(`[dreamer] map-memories warning: dropping ${unknown.length} unknown mapping entr${unknown.length === 1 ? "y" : "ies"} outside the current batch (${unknown.map((entry) => entry.id).join(", ")})`);
|
|
14295
14477
|
}
|
|
14296
14478
|
assertNoDuplicateManifestIds(valid.map((entry) => entry.id), "mappings");
|
|
14297
|
-
if (!budgetFinalized && valid.length * 2 < batch.length) {
|
|
14479
|
+
if (!allowPartial && !budgetFinalized && valid.length * 2 < batch.length) {
|
|
14298
14480
|
throw new Error(`mappings manifest covers ${valid.length}/${batch.length} batch ids after filtering unknown entries; rejecting mostly-wrong manifest`);
|
|
14299
14481
|
}
|
|
14300
14482
|
if (budgetFinalized && valid.length < batch.length) {
|
|
14301
14483
|
log2(`[dreamer] map-memories: accepted partial manifest after token budget: ${valid.length}/${batch.length}`);
|
|
14302
14484
|
}
|
|
14303
14485
|
if (valid.length === 0)
|
|
14304
|
-
return { mapped: 0, independent: 0 };
|
|
14486
|
+
return { mapped: 0, independent: 0, committedIds: [] };
|
|
14305
14487
|
const planned = [];
|
|
14306
14488
|
const batchById = new Map(batch.map((memory) => [memory.id, memory]));
|
|
14307
14489
|
for (const p of valid) {
|
|
@@ -14350,9 +14532,10 @@ async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = f
|
|
|
14350
14532
|
});
|
|
14351
14533
|
}
|
|
14352
14534
|
if (planned.length === 0)
|
|
14353
|
-
return { mapped: 0, independent: 0 };
|
|
14535
|
+
return { mapped: 0, independent: 0, committedIds: [] };
|
|
14354
14536
|
let mapped = 0;
|
|
14355
14537
|
let independent = 0;
|
|
14538
|
+
const committedIds = [];
|
|
14356
14539
|
if (args.moduleRoute) {
|
|
14357
14540
|
const identities = getModuleMemoryIdentities(args.db, args.projectIdentity, planned.map((item) => item.id));
|
|
14358
14541
|
const rows = planned.map((item) => {
|
|
@@ -14390,19 +14573,22 @@ async function applyParsedBatchMappings(args, batch, parsed, budgetFinalized = f
|
|
|
14390
14573
|
const accepted = new Set(result.accepted.filter((id) => typeof id === "number"));
|
|
14391
14574
|
for (const item of planned) {
|
|
14392
14575
|
const identity = identities.get(item.id);
|
|
14393
|
-
if (identity && accepted.has(identity.moduleId))
|
|
14576
|
+
if (identity && accepted.has(identity.moduleId)) {
|
|
14394
14577
|
item.independent ? independent += 1 : mapped += 1;
|
|
14578
|
+
committedIds.push(item.id);
|
|
14579
|
+
}
|
|
14395
14580
|
}
|
|
14396
|
-
return { mapped, independent };
|
|
14581
|
+
return { mapped, independent, committedIds };
|
|
14397
14582
|
}
|
|
14398
14583
|
const now = Date.now();
|
|
14399
14584
|
runLeaseGuardedWrite(args.db, args.holderId, args.leaseKey, () => {
|
|
14400
14585
|
for (const item of planned) {
|
|
14401
14586
|
recordMemoryMapping(args.db, item.id, item.files, now, item.mappingOrigin);
|
|
14402
14587
|
item.independent ? independent += 1 : mapped += 1;
|
|
14588
|
+
committedIds.push(item.id);
|
|
14403
14589
|
}
|
|
14404
14590
|
});
|
|
14405
|
-
return { mapped, independent };
|
|
14591
|
+
return { mapped, independent, committedIds };
|
|
14406
14592
|
}
|
|
14407
14593
|
function recordInvocation2(args, startedAt, params) {
|
|
14408
14594
|
if (!args.parentSessionId)
|
|
@@ -14670,32 +14856,41 @@ async function promotePrimers(args) {
|
|
|
14670
14856
|
// ../plugin/src/features/magic-context/dreamer/primer-seed.ts
|
|
14671
14857
|
var PRIMER_SEED_CAP_TOKENS = 4000;
|
|
14672
14858
|
function renderUserAndToolOrientation(sessionId, startOrdinal, endOrdinal, capTokens) {
|
|
14859
|
+
const renderer = orientationRenderer(capTokens);
|
|
14860
|
+
visitRawSessionMessages2(sessionId, startOrdinal, endOrdinal, renderer.visit, {
|
|
14861
|
+
summary: true
|
|
14862
|
+
});
|
|
14863
|
+
return renderer.result();
|
|
14864
|
+
}
|
|
14865
|
+
function orientationRenderer(capTokens) {
|
|
14673
14866
|
const lines = [];
|
|
14674
14867
|
let tokens = 0;
|
|
14675
14868
|
let sawMessage = false;
|
|
14676
|
-
|
|
14677
|
-
|
|
14678
|
-
|
|
14679
|
-
|
|
14680
|
-
|
|
14681
|
-
|
|
14682
|
-
|
|
14683
|
-
|
|
14684
|
-
|
|
14685
|
-
|
|
14686
|
-
|
|
14687
|
-
const
|
|
14688
|
-
|
|
14689
|
-
|
|
14690
|
-
|
|
14869
|
+
return {
|
|
14870
|
+
visit: (msg) => {
|
|
14871
|
+
sawMessage = true;
|
|
14872
|
+
const out = [];
|
|
14873
|
+
if (msg.role === "user" && hasMeaningfulUserText2(msg.parts)) {
|
|
14874
|
+
const text = extractTexts2(msg.parts).map((t) => cleanUserText2(t)).map(normalizeText).filter((t) => t.length > 0).join(" / ");
|
|
14875
|
+
if (text)
|
|
14876
|
+
out.push(`U: ${text}`);
|
|
14877
|
+
}
|
|
14878
|
+
for (const tc of extractToolCallSummaries(msg.parts))
|
|
14879
|
+
out.push(tc);
|
|
14880
|
+
for (const line of out) {
|
|
14881
|
+
const lineTokens = estimateTokens(line);
|
|
14882
|
+
if (tokens + lineTokens > capTokens && lines.length > 0) {
|
|
14883
|
+
lines.push("… (orientation truncated; investigate the current source directly)");
|
|
14884
|
+
return false;
|
|
14885
|
+
}
|
|
14886
|
+
lines.push(line);
|
|
14887
|
+
tokens += lineTokens;
|
|
14691
14888
|
}
|
|
14692
|
-
|
|
14693
|
-
|
|
14694
|
-
|
|
14695
|
-
|
|
14696
|
-
}
|
|
14697
|
-
return sawMessage ? lines.join(`
|
|
14698
|
-
`) : null;
|
|
14889
|
+
return true;
|
|
14890
|
+
},
|
|
14891
|
+
result: () => sawMessage ? lines.join(`
|
|
14892
|
+
`) : null
|
|
14893
|
+
};
|
|
14699
14894
|
}
|
|
14700
14895
|
function selectPrimerOriginCandidate(db, primer) {
|
|
14701
14896
|
return getPrimerCandidatesByIds(db, primer.sourceCandidateIds).filter((candidate) => candidate.projectPath === primer.projectPath).sort((a, b) => b.sourceMessageTime - a.sourceMessageTime || b.id - a.id)[0];
|
|
@@ -14729,16 +14924,37 @@ function closedBookOriginP1(db, sessionId, originStartMessage) {
|
|
|
14729
14924
|
return { orientation, sessionId };
|
|
14730
14925
|
}
|
|
14731
14926
|
function buildPrimerSeed(db, primer) {
|
|
14927
|
+
return buildPrimerSeedUsing(db, selectPrimerOriginCandidate(db, primer), renderUserAndToolOrientation);
|
|
14928
|
+
}
|
|
14929
|
+
async function buildPrimerSeedAsync(db, primer) {
|
|
14732
14930
|
const mostRecent = selectPrimerOriginCandidate(db, primer);
|
|
14931
|
+
let orientation = null;
|
|
14932
|
+
if (mostRecent && typeof mostRecent.sourceCompartmentStart === "number" && typeof mostRecent.sourceCompartmentEnd === "number") {
|
|
14933
|
+
try {
|
|
14934
|
+
const renderer = orientationRenderer(PRIMER_SEED_CAP_TOKENS);
|
|
14935
|
+
await visitRawSessionMessagesAsync2(mostRecent.sessionId, mostRecent.sourceCompartmentStart, mostRecent.sourceCompartmentEnd, renderer.visit, { summary: true });
|
|
14936
|
+
orientation = renderer.result();
|
|
14937
|
+
} catch {
|
|
14938
|
+
orientation = null;
|
|
14939
|
+
}
|
|
14940
|
+
}
|
|
14941
|
+
return buildPrimerSeedUsing(db, mostRecent, () => orientation);
|
|
14942
|
+
}
|
|
14943
|
+
function buildPrimerSeedUsing(db, mostRecent, render) {
|
|
14733
14944
|
if (!mostRecent || typeof mostRecent.sourceCompartmentStart !== "number" || typeof mostRecent.sourceCompartmentEnd !== "number") {
|
|
14734
|
-
return {
|
|
14945
|
+
return {
|
|
14946
|
+
kind: "closed-book",
|
|
14947
|
+
orientation: "",
|
|
14948
|
+
prePost: "",
|
|
14949
|
+
sessionId: null
|
|
14950
|
+
};
|
|
14735
14951
|
}
|
|
14736
14952
|
const sessionId = mostRecent.sessionId;
|
|
14737
14953
|
const start = mostRecent.sourceCompartmentStart;
|
|
14738
14954
|
const end = mostRecent.sourceCompartmentEnd;
|
|
14739
14955
|
let orientation = null;
|
|
14740
14956
|
try {
|
|
14741
|
-
orientation =
|
|
14957
|
+
orientation = render(sessionId, start, end, PRIMER_SEED_CAP_TOKENS);
|
|
14742
14958
|
} catch {
|
|
14743
14959
|
orientation = null;
|
|
14744
14960
|
}
|
|
@@ -14869,14 +15085,14 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
|
|
|
14869
15085
|
provider = null;
|
|
14870
15086
|
}
|
|
14871
15087
|
}
|
|
14872
|
-
const seed =
|
|
15088
|
+
const seed = await (async () => {
|
|
14873
15089
|
const unregister = provider && originSessionId ? setRawMessageProvider2(originSessionId, provider) : null;
|
|
14874
15090
|
try {
|
|
14875
|
-
return buildPrimerSeed(args.db, primer);
|
|
15091
|
+
return provider?.iterateMessageRangeAsync ? await buildPrimerSeedAsync(args.db, primer) : withRawSessionMessageCache2(() => buildPrimerSeed(args.db, primer));
|
|
14876
15092
|
} finally {
|
|
14877
15093
|
unregister?.();
|
|
14878
15094
|
}
|
|
14879
|
-
});
|
|
15095
|
+
})();
|
|
14880
15096
|
let agentSessionId = null;
|
|
14881
15097
|
let promptSettled = false;
|
|
14882
15098
|
const startedAt = Date.now();
|
|
@@ -14892,7 +15108,10 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
|
|
|
14892
15108
|
prompt,
|
|
14893
15109
|
title: "magic-context-dream-refresh-primers",
|
|
14894
15110
|
callContext: "dreamer:refresh-primers",
|
|
14895
|
-
metadata: {
|
|
15111
|
+
metadata: {
|
|
15112
|
+
tokenBudget: args.tokenBudget,
|
|
15113
|
+
onBudgetUpdate: args.onBudgetUpdate
|
|
15114
|
+
},
|
|
14896
15115
|
model: args.model,
|
|
14897
15116
|
fallbackModels: args.fallbackModels,
|
|
14898
15117
|
language: args.language,
|
|
@@ -14958,7 +15177,10 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
|
|
|
14958
15177
|
validateOutput: (messages) => parseAnswer(messages, primer.answer)
|
|
14959
15178
|
});
|
|
14960
15179
|
promptSettled = true;
|
|
14961
|
-
recordInvocation3(args, startedAt, {
|
|
15180
|
+
recordInvocation3(args, startedAt, {
|
|
15181
|
+
status: "completed",
|
|
15182
|
+
messages: run.output
|
|
15183
|
+
});
|
|
14962
15184
|
const answer = run.validated.trim();
|
|
14963
15185
|
if (!answer)
|
|
14964
15186
|
return false;
|
|
@@ -14973,7 +15195,10 @@ async function refreshOnePrimer(args, primer, sliceMs, signal) {
|
|
|
14973
15195
|
} catch (error) {
|
|
14974
15196
|
const desc = describeError(error);
|
|
14975
15197
|
log2(`[dreamer] refresh-primers failed (primer #${primer.id}): ${desc.brief}`, desc.stackHead ? { stackHead: desc.stackHead } : undefined);
|
|
14976
|
-
recordInvocation3(args, startedAt, {
|
|
15198
|
+
recordInvocation3(args, startedAt, {
|
|
15199
|
+
status: failedInvocationStatus(error),
|
|
15200
|
+
error
|
|
15201
|
+
});
|
|
14977
15202
|
throw error;
|
|
14978
15203
|
} finally {
|
|
14979
15204
|
if (agentSessionId && args.client)
|
|
@@ -15587,9 +15812,9 @@ function insertDreamRun(db, run) {
|
|
|
15587
15812
|
import { createHash as createHash9 } from "node:crypto";
|
|
15588
15813
|
|
|
15589
15814
|
// ../plugin/src/features/magic-context/dreamer/verify-diff.ts
|
|
15590
|
-
import { execFile as
|
|
15591
|
-
import { promisify as
|
|
15592
|
-
var exec =
|
|
15815
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
15816
|
+
import { promisify as promisify3 } from "node:util";
|
|
15817
|
+
var exec = promisify3(execFile3);
|
|
15593
15818
|
var VERIFY_DIFF_BUDGET_TOKENS = 12000;
|
|
15594
15819
|
var MAX_DIFF_BYTES = 4 * 1024 * 1024;
|
|
15595
15820
|
async function git2(root, args) {
|
|
@@ -19906,11 +20131,19 @@ class PiSubagentRunner {
|
|
|
19906
20131
|
});
|
|
19907
20132
|
return new Promise((resolve) => {
|
|
19908
20133
|
let accountingMessages = [];
|
|
20134
|
+
let mappingMessages = [];
|
|
20135
|
+
const isMemoryMapper = options.agent.replace(/^magic-context-/, "") === "dreamer-memory-mapper";
|
|
19909
20136
|
let settled = false;
|
|
19910
20137
|
const settle = (result) => {
|
|
19911
20138
|
if (settled)
|
|
19912
20139
|
return;
|
|
19913
20140
|
settled = true;
|
|
20141
|
+
if (isMemoryMapper) {
|
|
20142
|
+
result = {
|
|
20143
|
+
...result,
|
|
20144
|
+
meta: { ...result.meta, mappingMessages: [...mappingMessages] }
|
|
20145
|
+
};
|
|
20146
|
+
}
|
|
19914
20147
|
if (accountingMessages.length > 0 && /historian|dreamer|classify/i.test(options.agent)) {
|
|
19915
20148
|
const last = [...accountingMessages].reverse().find((message) => message !== null && typeof message === "object" && message.role === "assistant");
|
|
19916
20149
|
const responseChars = result.ok ? result.assistantText.length : last?.content?.filter((part) => typeof part === "object" && part !== null && part.type === "text" && typeof part.text === "string").reduce((sum, part) => sum + part.text.length, 0) ?? 0;
|
|
@@ -20103,6 +20336,12 @@ class PiSubagentRunner {
|
|
|
20103
20336
|
if (typeof event !== "object" || event === null)
|
|
20104
20337
|
return;
|
|
20105
20338
|
const e = event;
|
|
20339
|
+
if (isMemoryMapper) {
|
|
20340
|
+
if (e.type === "message_end" && e.message)
|
|
20341
|
+
mappingMessages.push(e.message);
|
|
20342
|
+
if (e.type === "agent_end" && Array.isArray(e.messages))
|
|
20343
|
+
mappingMessages = e.messages;
|
|
20344
|
+
}
|
|
20106
20345
|
if (rpcBudget && e.type === "response" && event.success === false) {
|
|
20107
20346
|
const reply = event;
|
|
20108
20347
|
if (reply.command === "prompt" || reply.command === "steer" && !sawAgentEnd) {
|
|
@@ -20722,7 +20961,9 @@ import { join as join13 } from "node:path";
|
|
|
20722
20961
|
|
|
20723
20962
|
// src/dreamer/bounded-session-reader.ts
|
|
20724
20963
|
import { closeSync, opendirSync, openSync, readSync } from "node:fs";
|
|
20964
|
+
import { open as open4, opendir } from "node:fs/promises";
|
|
20725
20965
|
import { join as join11 } from "node:path";
|
|
20966
|
+
import { setImmediate as yieldToHost } from "node:timers/promises";
|
|
20726
20967
|
var MAX_ENTRY_BYTES = 1024 * 1024;
|
|
20727
20968
|
var OVERSIZED_HEAD_BYTES = 64 * 1024;
|
|
20728
20969
|
var OVERSIZED_TAIL_BYTES = 4 * 1024;
|
|
@@ -20793,86 +21034,107 @@ function lastMatch(text, pattern) {
|
|
|
20793
21034
|
last = match[1];
|
|
20794
21035
|
return last;
|
|
20795
21036
|
}
|
|
21037
|
+
|
|
21038
|
+
class LineDecoder {
|
|
21039
|
+
pending = Buffer.alloc(0);
|
|
21040
|
+
oversized = null;
|
|
21041
|
+
*append(buffer) {
|
|
21042
|
+
const bytes = buffer.length;
|
|
21043
|
+
let start = 0;
|
|
21044
|
+
for (let i = 0;i < bytes; i++) {
|
|
21045
|
+
if (buffer[i] !== 10)
|
|
21046
|
+
continue;
|
|
21047
|
+
const piece = buffer.subarray(start, i);
|
|
21048
|
+
if (this.oversized) {
|
|
21049
|
+
yield this.oversized.append(piece).stub();
|
|
21050
|
+
this.oversized = null;
|
|
21051
|
+
} else if (this.pending.length + piece.length > MAX_ENTRY_BYTES) {
|
|
21052
|
+
yield new OversizedLine().append(this.pending).append(piece).stub();
|
|
21053
|
+
} else {
|
|
21054
|
+
yield Buffer.concat([this.pending, piece]).toString("utf8");
|
|
21055
|
+
}
|
|
21056
|
+
this.pending = Buffer.alloc(0);
|
|
21057
|
+
start = i + 1;
|
|
21058
|
+
}
|
|
21059
|
+
const rest = buffer.subarray(start, bytes);
|
|
21060
|
+
if (this.oversized) {
|
|
21061
|
+
this.oversized.append(rest);
|
|
21062
|
+
} else if (this.pending.length + rest.length > MAX_ENTRY_BYTES) {
|
|
21063
|
+
this.oversized = new OversizedLine().append(this.pending).append(rest);
|
|
21064
|
+
this.pending = Buffer.alloc(0);
|
|
21065
|
+
} else {
|
|
21066
|
+
this.pending = Buffer.concat([this.pending, rest]);
|
|
21067
|
+
}
|
|
21068
|
+
}
|
|
21069
|
+
*finish() {
|
|
21070
|
+
if (this.oversized)
|
|
21071
|
+
yield this.oversized.stub();
|
|
21072
|
+
else if (this.pending.length)
|
|
21073
|
+
yield this.pending.toString("utf8");
|
|
21074
|
+
}
|
|
21075
|
+
}
|
|
20796
21076
|
function* lines(path) {
|
|
20797
21077
|
const fd = openSync(path, "r");
|
|
20798
21078
|
const buffer = Buffer.alloc(64 * 1024);
|
|
20799
|
-
|
|
20800
|
-
let oversized = null;
|
|
21079
|
+
const decoder = new LineDecoder;
|
|
20801
21080
|
try {
|
|
20802
21081
|
while (true) {
|
|
20803
21082
|
const bytes = readSync(fd, buffer, 0, buffer.length, null);
|
|
20804
21083
|
if (bytes === 0)
|
|
20805
21084
|
break;
|
|
20806
|
-
|
|
20807
|
-
for (let i = 0;i < bytes; i++) {
|
|
20808
|
-
if (buffer[i] !== 10)
|
|
20809
|
-
continue;
|
|
20810
|
-
const piece = buffer.subarray(start, i);
|
|
20811
|
-
if (oversized) {
|
|
20812
|
-
yield oversized.append(piece).stub();
|
|
20813
|
-
oversized = null;
|
|
20814
|
-
} else if (pending.length + piece.length > MAX_ENTRY_BYTES) {
|
|
20815
|
-
yield new OversizedLine().append(pending).append(piece).stub();
|
|
20816
|
-
} else {
|
|
20817
|
-
yield Buffer.concat([pending, piece]).toString("utf8");
|
|
20818
|
-
}
|
|
20819
|
-
pending = Buffer.alloc(0);
|
|
20820
|
-
start = i + 1;
|
|
20821
|
-
}
|
|
20822
|
-
const rest = buffer.subarray(start, bytes);
|
|
20823
|
-
if (oversized) {
|
|
20824
|
-
oversized.append(rest);
|
|
20825
|
-
} else if (pending.length + rest.length > MAX_ENTRY_BYTES) {
|
|
20826
|
-
oversized = new OversizedLine().append(pending).append(rest);
|
|
20827
|
-
pending = Buffer.alloc(0);
|
|
20828
|
-
} else {
|
|
20829
|
-
pending = Buffer.concat([pending, rest]);
|
|
20830
|
-
}
|
|
21085
|
+
yield* decoder.append(buffer.subarray(0, bytes));
|
|
20831
21086
|
}
|
|
20832
|
-
|
|
20833
|
-
yield oversized.stub();
|
|
20834
|
-
else if (pending.length)
|
|
20835
|
-
yield pending.toString("utf8");
|
|
21087
|
+
yield* decoder.finish();
|
|
20836
21088
|
} finally {
|
|
20837
21089
|
closeSync(fd);
|
|
20838
21090
|
}
|
|
20839
21091
|
}
|
|
21092
|
+
async function* asyncLines(path) {
|
|
21093
|
+
const file = await open4(path, "r");
|
|
21094
|
+
const buffer = Buffer.alloc(64 * 1024);
|
|
21095
|
+
const decoder = new LineDecoder;
|
|
21096
|
+
try {
|
|
21097
|
+
while (true) {
|
|
21098
|
+
const { bytesRead } = await file.read(buffer, 0, buffer.length, null);
|
|
21099
|
+
if (bytesRead === 0)
|
|
21100
|
+
break;
|
|
21101
|
+
yield* decoder.append(buffer.subarray(0, bytesRead));
|
|
21102
|
+
await yieldToHost();
|
|
21103
|
+
}
|
|
21104
|
+
yield* decoder.finish();
|
|
21105
|
+
} finally {
|
|
21106
|
+
await file.close();
|
|
21107
|
+
}
|
|
21108
|
+
}
|
|
20840
21109
|
function record(value) {
|
|
20841
21110
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
20842
21111
|
}
|
|
20843
|
-
function*
|
|
21112
|
+
async function* asyncSessionEntries(path) {
|
|
21113
|
+
for await (const line of asyncLines(path)) {
|
|
21114
|
+
try {
|
|
21115
|
+
yield JSON.parse(line);
|
|
21116
|
+
} catch {}
|
|
21117
|
+
}
|
|
21118
|
+
}
|
|
21119
|
+
async function* asyncSessionFiles(directory, nested) {
|
|
20844
21120
|
let dir;
|
|
20845
21121
|
try {
|
|
20846
|
-
dir =
|
|
21122
|
+
dir = await opendir(directory);
|
|
20847
21123
|
} catch {
|
|
20848
21124
|
return;
|
|
20849
21125
|
}
|
|
20850
|
-
|
|
20851
|
-
|
|
20852
|
-
|
|
20853
|
-
|
|
20854
|
-
|
|
20855
|
-
|
|
20856
|
-
if (entry.isFile() && entry.name.endsWith(".jsonl"))
|
|
20857
|
-
yield path;
|
|
20858
|
-
else if (nested && entry.isDirectory())
|
|
20859
|
-
yield* sessionFiles(path, false);
|
|
20860
|
-
}
|
|
20861
|
-
} finally {
|
|
20862
|
-
dir.closeSync();
|
|
20863
|
-
}
|
|
20864
|
-
}
|
|
20865
|
-
function findSession(directory, nested, sessionId) {
|
|
20866
|
-
for (const header of sessionHeaders(directory, nested)) {
|
|
20867
|
-
if (header.id === sessionId)
|
|
20868
|
-
return header.path;
|
|
21126
|
+
for await (const entry of dir) {
|
|
21127
|
+
const path = join11(directory, entry.name);
|
|
21128
|
+
if (entry.isFile() && entry.name.endsWith(".jsonl"))
|
|
21129
|
+
yield path;
|
|
21130
|
+
else if (nested && entry.isDirectory())
|
|
21131
|
+
yield* asyncSessionFiles(path, false);
|
|
20869
21132
|
}
|
|
20870
|
-
return null;
|
|
20871
21133
|
}
|
|
20872
|
-
function*
|
|
20873
|
-
for (const path of
|
|
21134
|
+
async function* asyncSessionHeaders(directory, nested) {
|
|
21135
|
+
for await (const path of asyncSessionFiles(directory, nested)) {
|
|
20874
21136
|
try {
|
|
20875
|
-
for (const line of
|
|
21137
|
+
for await (const line of asyncLines(path)) {
|
|
20876
21138
|
const header = record(JSON.parse(line));
|
|
20877
21139
|
if (header.type === "session")
|
|
20878
21140
|
yield { ...header, path };
|
|
@@ -20881,13 +21143,6 @@ function* sessionHeaders(directory, nested) {
|
|
|
20881
21143
|
} catch {}
|
|
20882
21144
|
}
|
|
20883
21145
|
}
|
|
20884
|
-
function* sessionEntries(path) {
|
|
20885
|
-
for (const line of lines(path)) {
|
|
20886
|
-
try {
|
|
20887
|
-
yield JSON.parse(line);
|
|
20888
|
-
} catch {}
|
|
20889
|
-
}
|
|
20890
|
-
}
|
|
20891
21146
|
|
|
20892
21147
|
// src/dreamer/pi-session-api.ts
|
|
20893
21148
|
import { existsSync as existsSync11, readFileSync as readFileSync10, realpathSync as realpathSync4, statSync as statSync5 } from "node:fs";
|
|
@@ -21154,15 +21409,9 @@ function projectEntry(value) {
|
|
|
21154
21409
|
}
|
|
21155
21410
|
};
|
|
21156
21411
|
}
|
|
21157
|
-
function
|
|
21412
|
+
function summaryProjector() {
|
|
21158
21413
|
let pendingResults = 0;
|
|
21159
|
-
|
|
21160
|
-
let parsed;
|
|
21161
|
-
try {
|
|
21162
|
-
parsed = JSON.parse(line);
|
|
21163
|
-
} catch {
|
|
21164
|
-
continue;
|
|
21165
|
-
}
|
|
21414
|
+
return (parsed) => {
|
|
21166
21415
|
const entry = projectEntry(parsed);
|
|
21167
21416
|
const e = record2(entry);
|
|
21168
21417
|
if (e.type === "message") {
|
|
@@ -21173,7 +21422,30 @@ function* summaryEntries(path) {
|
|
|
21173
21422
|
} else if (m.role === "user" || m.role === "assistant")
|
|
21174
21423
|
pendingResults = 0;
|
|
21175
21424
|
}
|
|
21176
|
-
|
|
21425
|
+
return entry;
|
|
21426
|
+
};
|
|
21427
|
+
}
|
|
21428
|
+
function* summaryEntries(path) {
|
|
21429
|
+
const project = summaryProjector();
|
|
21430
|
+
for (const line of lines(path)) {
|
|
21431
|
+
let entry;
|
|
21432
|
+
try {
|
|
21433
|
+
entry = JSON.parse(line);
|
|
21434
|
+
} catch {}
|
|
21435
|
+
if (entry !== undefined)
|
|
21436
|
+
yield project(entry);
|
|
21437
|
+
}
|
|
21438
|
+
}
|
|
21439
|
+
async function* asyncSummaryEntries(path) {
|
|
21440
|
+
const project = summaryProjector();
|
|
21441
|
+
for await (const line of asyncLines(path)) {
|
|
21442
|
+
let entry;
|
|
21443
|
+
try {
|
|
21444
|
+
entry = JSON.parse(line);
|
|
21445
|
+
} catch {
|
|
21446
|
+
continue;
|
|
21447
|
+
}
|
|
21448
|
+
yield project(entry);
|
|
21177
21449
|
}
|
|
21178
21450
|
}
|
|
21179
21451
|
function createPiPrimerRawProviderFactory(deps = {}) {
|
|
@@ -21186,13 +21458,23 @@ function createPiPrimerRawProviderFactory(deps = {}) {
|
|
|
21186
21458
|
return null;
|
|
21187
21459
|
directory = join13(mod.getAgentDir(), "sessions");
|
|
21188
21460
|
}
|
|
21189
|
-
|
|
21461
|
+
let path = null;
|
|
21462
|
+
for await (const header of asyncSessionHeaders(directory, !deps.sessionDir)) {
|
|
21463
|
+
if (header.id === sessionId) {
|
|
21464
|
+
path = header.path;
|
|
21465
|
+
break;
|
|
21466
|
+
}
|
|
21467
|
+
}
|
|
21190
21468
|
if (!path)
|
|
21191
21469
|
return null;
|
|
21470
|
+
const sessionPath = path;
|
|
21192
21471
|
return {
|
|
21193
21472
|
readMessages() {
|
|
21194
21473
|
throw new Error("Pi primer history requires bounded pages");
|
|
21195
21474
|
},
|
|
21475
|
+
iterateMessageRangeAsync(from, to) {
|
|
21476
|
+
return iterateAsyncEntriesToRawMessageRange(asyncSummaryEntries(sessionPath), from - 1, Number.MAX_SAFE_INTEGER, to);
|
|
21477
|
+
},
|
|
21196
21478
|
iterateMessageRange(from, to) {
|
|
21197
21479
|
return iterateEntriesToRawMessageRange(summaryEntries(path), from - 1, Number.MAX_SAFE_INTEGER, to);
|
|
21198
21480
|
},
|
|
@@ -21207,6 +21489,7 @@ function createPiPrimerRawProviderFactory(deps = {}) {
|
|
|
21207
21489
|
}
|
|
21208
21490
|
|
|
21209
21491
|
// src/dreamer/retrospective-raw-provider-pi.ts
|
|
21492
|
+
import { stat as stat2 } from "node:fs/promises";
|
|
21210
21493
|
import { join as join14 } from "node:path";
|
|
21211
21494
|
class PiRetrospectiveRawProvider {
|
|
21212
21495
|
deps;
|
|
@@ -21222,11 +21505,11 @@ class PiRetrospectiveRawProvider {
|
|
|
21222
21505
|
return [];
|
|
21223
21506
|
directory = join14(mod.getAgentDir(), "sessions");
|
|
21224
21507
|
}
|
|
21225
|
-
const sessions = this.deps.listSessions ? await this.deps.listSessions(directory) : directory ?
|
|
21508
|
+
const sessions = this.deps.listSessions ? await this.deps.listSessions(directory) : directory ? asyncSessionHeaders(directory, !this.deps.sessionDir) : [];
|
|
21226
21509
|
const projectCwd = this.deps.projectCwd;
|
|
21227
21510
|
const result = [];
|
|
21228
21511
|
this.sessionPathById.clear();
|
|
21229
|
-
for (const raw of sessions) {
|
|
21512
|
+
for await (const raw of sessions) {
|
|
21230
21513
|
const info = raw;
|
|
21231
21514
|
if (!info || typeof info !== "object")
|
|
21232
21515
|
continue;
|
|
@@ -21248,7 +21531,7 @@ class PiRetrospectiveRawProvider {
|
|
|
21248
21531
|
}
|
|
21249
21532
|
async readUserMessagesSince(sessionId, sinceMs, capPerSession) {
|
|
21250
21533
|
const limit = Math.max(1, Math.floor(capPerSession));
|
|
21251
|
-
const messages = await this.selectUserEntries(sessionId, (ts) => ts > sinceMs, limit + 1, false);
|
|
21534
|
+
const messages = await this.selectUserEntries(sessionId, (ts) => ts > sinceMs, limit + 1, false, sinceMs);
|
|
21252
21535
|
return {
|
|
21253
21536
|
messages: messages.slice(0, limit),
|
|
21254
21537
|
truncated: messages.length > limit
|
|
@@ -21257,7 +21540,7 @@ class PiRetrospectiveRawProvider {
|
|
|
21257
21540
|
async readOldestMessageTimesSince(sessionIds, sinceMs) {
|
|
21258
21541
|
const out = new Map;
|
|
21259
21542
|
for (const sessionId of sessionIds) {
|
|
21260
|
-
const [oldest] = await this.selectUserEntries(sessionId, (ts) => ts > sinceMs, 1, false);
|
|
21543
|
+
const [oldest] = await this.selectUserEntries(sessionId, (ts) => ts > sinceMs, 1, false, sinceMs);
|
|
21261
21544
|
if (oldest)
|
|
21262
21545
|
out.set(sessionId, oldest.ts);
|
|
21263
21546
|
}
|
|
@@ -21266,15 +21549,19 @@ class PiRetrospectiveRawProvider {
|
|
|
21266
21549
|
async readUserMessagesBefore(sessionId, beforeMs, count) {
|
|
21267
21550
|
return this.selectUserEntries(sessionId, (ts) => ts <= beforeMs, Math.max(1, Math.floor(count)), true);
|
|
21268
21551
|
}
|
|
21269
|
-
async selectUserEntries(sessionId, eligible, limit, newest) {
|
|
21552
|
+
async selectUserEntries(sessionId, eligible, limit, newest, sinceMs) {
|
|
21270
21553
|
const filePath = this.sessionPathById.get(sessionId);
|
|
21271
21554
|
if (!filePath)
|
|
21272
21555
|
return [];
|
|
21273
21556
|
const kept = [];
|
|
21274
21557
|
try {
|
|
21275
|
-
|
|
21558
|
+
if (sinceMs !== undefined && !this.deps.loadEntriesFromFile) {
|
|
21559
|
+
if ((await stat2(filePath)).mtimeMs < sinceMs)
|
|
21560
|
+
return [];
|
|
21561
|
+
}
|
|
21562
|
+
const entries = this.deps.loadEntriesFromFile ? await this.deps.loadEntriesFromFile(filePath) : asyncSessionEntries(filePath);
|
|
21276
21563
|
let ordinal = 0;
|
|
21277
|
-
for (const entry of entries) {
|
|
21564
|
+
for await (const entry of entries) {
|
|
21278
21565
|
const row = normalizePiUserEntry(entry, sessionId, ++ordinal);
|
|
21279
21566
|
if (!row || !eligible(row.ts))
|
|
21280
21567
|
continue;
|
|
@@ -21622,6 +21909,10 @@ function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRe
|
|
|
21622
21909
|
});
|
|
21623
21910
|
}
|
|
21624
21911
|
assertRegistrationOwnerActive();
|
|
21912
|
+
const mappingMessages = result.meta?.mappingMessages;
|
|
21913
|
+
if (budgetTask === "map-memories" && Array.isArray(mappingMessages)) {
|
|
21914
|
+
dreamSession.messages = mappingMessages.filter(isRecord).filter((message) => message.role === "assistant").map((message) => makeMessage("assistant", Array.isArray(message.content) ? message.content.filter(isRecord).flatMap((part) => part.type === "text" && typeof part.text === "string" ? [{ type: "text", text: part.text }] : part.type === "toolCall" ? syntheticToolParts(1) : []) : []));
|
|
21915
|
+
}
|
|
21625
21916
|
if (!result.ok) {
|
|
21626
21917
|
if (result.reason === "token_budget") {
|
|
21627
21918
|
throw new DreamTokenBudgetExceeded(dreamSession.id, Number(result.meta?.tokenBudget?.spent ?? 0));
|
|
@@ -21635,13 +21926,14 @@ function createPiDreamerClient(opts, onAdjunctsRefreshNeeded = opts.onAdjunctsRe
|
|
|
21635
21926
|
}
|
|
21636
21927
|
throw error;
|
|
21637
21928
|
}
|
|
21638
|
-
|
|
21639
|
-
|
|
21640
|
-
|
|
21641
|
-
|
|
21642
|
-
|
|
21643
|
-
|
|
21644
|
-
|
|
21929
|
+
if (!(budgetTask === "map-memories" && Array.isArray(mappingMessages)))
|
|
21930
|
+
dreamSession.messages = [
|
|
21931
|
+
makeMessage("user", [{ type: "text", text: userMessage }]),
|
|
21932
|
+
makeMessage("assistant", [
|
|
21933
|
+
...syntheticToolParts(result.toolCallCount ?? 0, result.completedToolCalls),
|
|
21934
|
+
...result.assistantText ? [{ type: "text", text: result.assistantText }] : []
|
|
21935
|
+
])
|
|
21936
|
+
];
|
|
21645
21937
|
onAdjunctsRefreshNeeded?.(opts.projectIdentity);
|
|
21646
21938
|
} finally {
|
|
21647
21939
|
inFlightDreams.delete(runPromise);
|
|
@@ -22389,7 +22681,7 @@ function reconcilePiCompactionMode(args) {
|
|
|
22389
22681
|
return NO_TRANSITION;
|
|
22390
22682
|
let clearedSomething = false;
|
|
22391
22683
|
if (getPendingPiCompactionMarkerState(args.db, args.sessionId) !== null) {
|
|
22392
|
-
setPendingPiCompactionMarkerState(args.db, args.sessionId, null);
|
|
22684
|
+
setPendingPiCompactionMarkerState(args.db, args.sessionId, null, "compaction-disabled");
|
|
22393
22685
|
clearedSomething = true;
|
|
22394
22686
|
}
|
|
22395
22687
|
if (getOverflowState(args.db, args.sessionId).needsEmergencyRecovery) {
|
|
@@ -23286,21 +23578,40 @@ function buildReplacementContent(tagId) {
|
|
|
23286
23578
|
return `[dropped §${tagId}§]`;
|
|
23287
23579
|
}
|
|
23288
23580
|
function applyNewToolDrop(target, options) {
|
|
23289
|
-
if (
|
|
23581
|
+
if (options.persist && (target?.prepareDrop?.() ?? target?.canDrop?.()) === false) {
|
|
23582
|
+
return { result: "incomplete", mode: "full" };
|
|
23583
|
+
}
|
|
23584
|
+
if (!target) {
|
|
23585
|
+
options.persist?.("full");
|
|
23290
23586
|
return { result: "absent", mode: "full" };
|
|
23291
|
-
|
|
23587
|
+
}
|
|
23588
|
+
if (options.inWindow && hasSmallToolInput(target) || options.keepSkeleton === true || target.cannotRemove?.() === true || target.wouldStrandConversationEnd?.() === true) {
|
|
23292
23589
|
const strip = target.hasAttachments?.() === true;
|
|
23590
|
+
options.persist?.(strip ? "skeleton_stripped" : "skeleton_real");
|
|
23293
23591
|
return {
|
|
23294
23592
|
result: (strip ? target.skeletonStripped?.() : target.skeletonReal?.()) ?? "absent",
|
|
23295
23593
|
mode: strip ? "skeleton_stripped" : "skeleton_real"
|
|
23296
23594
|
};
|
|
23297
23595
|
}
|
|
23596
|
+
options.persist?.("full");
|
|
23298
23597
|
const result = target.drop?.() ?? "absent";
|
|
23299
23598
|
if (result === "truncated" && target.hasAttachments?.() === true) {
|
|
23300
23599
|
return { result: target.skeletonStripped?.() ?? "absent", mode: "skeleton_stripped" };
|
|
23301
23600
|
}
|
|
23302
23601
|
return { result, mode: result === "truncated" ? "skeleton_real" : "full" };
|
|
23303
23602
|
}
|
|
23603
|
+
function applyPersistedToolDrop(db, sessionId, tagNumber, target, options, afterWrite) {
|
|
23604
|
+
return applyNewToolDrop(target, {
|
|
23605
|
+
...options,
|
|
23606
|
+
persist: (mode) => {
|
|
23607
|
+
db.transaction(() => {
|
|
23608
|
+
updateTagDropMode2(db, sessionId, tagNumber, mode);
|
|
23609
|
+
updateTagStatus2(db, sessionId, tagNumber, "dropped");
|
|
23610
|
+
afterWrite?.();
|
|
23611
|
+
}).immediate();
|
|
23612
|
+
}
|
|
23613
|
+
});
|
|
23614
|
+
}
|
|
23304
23615
|
function hasSmallToolInput(target) {
|
|
23305
23616
|
const bytes = target?.inputStringBytes?.() ?? null;
|
|
23306
23617
|
return bytes !== null && bytes <= SKELETON_REAL_INPUT_MAX_BYTES;
|
|
@@ -23375,11 +23686,11 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
|
|
|
23375
23686
|
diagnostics.reasons[reason] = (diagnostics.reasons[reason] ?? 0) + 1;
|
|
23376
23687
|
};
|
|
23377
23688
|
let admitted = false;
|
|
23378
|
-
let startedAt = 0;
|
|
23379
23689
|
try {
|
|
23380
23690
|
db.transaction(() => {
|
|
23381
23691
|
admitted = true;
|
|
23382
|
-
|
|
23692
|
+
}).immediate();
|
|
23693
|
+
{
|
|
23383
23694
|
const pendingOps = preloadedPendingOps ?? getPendingOps(db, sessionId);
|
|
23384
23695
|
const tags = preloadedTags ?? getTagsByNumbers2(db, sessionId, [
|
|
23385
23696
|
...new Set([
|
|
@@ -23431,10 +23742,31 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
|
|
|
23431
23742
|
continue;
|
|
23432
23743
|
}
|
|
23433
23744
|
}
|
|
23745
|
+
const persistDrop = (mode) => {
|
|
23746
|
+
let startedAt = 0;
|
|
23747
|
+
try {
|
|
23748
|
+
db.transaction(() => {
|
|
23749
|
+
startedAt = performance.now();
|
|
23750
|
+
if (mode)
|
|
23751
|
+
updateTagDropMode2(db, sessionId, pendingOp.tagId, mode);
|
|
23752
|
+
updateTagStatus2(db, sessionId, pendingOp.tagId, "dropped");
|
|
23753
|
+
if (!synthetic)
|
|
23754
|
+
removePendingOp(db, sessionId, pendingOp.tagId);
|
|
23755
|
+
}).immediate();
|
|
23756
|
+
} finally {
|
|
23757
|
+
if (startedAt)
|
|
23758
|
+
logSlowWriteTransaction("apply_pending_operation", startedAt);
|
|
23759
|
+
}
|
|
23760
|
+
};
|
|
23434
23761
|
let shouldPersistDrop = false;
|
|
23435
23762
|
if (isToolTag) {
|
|
23436
23763
|
if (editMarkerTagIds.has(pendingOp.tagId)) {
|
|
23437
23764
|
const strip = target?.hasAttachments?.() === true;
|
|
23765
|
+
if (target?.canDrop?.() === false || target?.prepareDrop?.() === false || !target) {
|
|
23766
|
+
reject("edit_marker_incomplete");
|
|
23767
|
+
continue;
|
|
23768
|
+
}
|
|
23769
|
+
persistDrop(strip ? "edit_marker_stripped" : "edit_marker");
|
|
23438
23770
|
const markResult = (strip ? target?.editMarkerStripped?.() : target?.editMarker?.()) ?? "absent";
|
|
23439
23771
|
if (markResult === "incomplete" || markResult === "absent") {
|
|
23440
23772
|
reject(`edit_marker_${markResult}`);
|
|
@@ -23443,11 +23775,11 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
|
|
|
23443
23775
|
didMutateMessage = true;
|
|
23444
23776
|
operationMutated = true;
|
|
23445
23777
|
onTagReduced?.({ tagNumber: pendingOp.tagId, mode: "edit_marker" });
|
|
23446
|
-
updateTagDropMode2(db, sessionId, pendingOp.tagId, strip ? "edit_marker_stripped" : "edit_marker");
|
|
23447
23778
|
shouldPersistDrop = true;
|
|
23448
23779
|
} else {
|
|
23449
23780
|
const { result: dropResult, mode: appliedMode } = applyNewToolDrop(target, {
|
|
23450
|
-
inWindow: skeletonWindow.has(pendingOp.tagId)
|
|
23781
|
+
inWindow: skeletonWindow.has(pendingOp.tagId),
|
|
23782
|
+
persist: persistDrop
|
|
23451
23783
|
});
|
|
23452
23784
|
if (dropResult === "incomplete" || synthetic && dropResult !== "removed" && dropResult !== "truncated") {
|
|
23453
23785
|
reject(`drop_${dropResult}`);
|
|
@@ -23463,12 +23795,12 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
|
|
|
23463
23795
|
} else {
|
|
23464
23796
|
reject(`drop_${dropResult}`);
|
|
23465
23797
|
}
|
|
23466
|
-
updateTagDropMode2(db, sessionId, pendingOp.tagId, appliedMode);
|
|
23467
23798
|
shouldPersistDrop = true;
|
|
23468
23799
|
}
|
|
23469
23800
|
} else if (target) {
|
|
23470
23801
|
const replacement = buildReplacementContent(pendingOp.tagId);
|
|
23471
23802
|
const priorContent = target.getContent?.();
|
|
23803
|
+
persistDrop();
|
|
23472
23804
|
const changed = target.setContent(replacement);
|
|
23473
23805
|
if (changed) {
|
|
23474
23806
|
didMutateMessage = true;
|
|
@@ -23485,30 +23817,25 @@ function applyPendingOperations(sessionId, db, targets, protectedTagIds, preload
|
|
|
23485
23817
|
shouldPersistDrop = true;
|
|
23486
23818
|
} else if (!synthetic) {
|
|
23487
23819
|
reject("target_absent");
|
|
23820
|
+
persistDrop();
|
|
23488
23821
|
shouldPersistDrop = true;
|
|
23489
23822
|
}
|
|
23490
23823
|
if (!shouldPersistDrop) {
|
|
23491
23824
|
reject("not_persisted");
|
|
23492
23825
|
continue;
|
|
23493
23826
|
}
|
|
23494
|
-
updateTagStatus2(db, sessionId, pendingOp.tagId, "dropped");
|
|
23495
|
-
if (!synthetic)
|
|
23496
|
-
removePendingOp(db, sessionId, pendingOp.tagId);
|
|
23497
23827
|
if (operationMutated)
|
|
23498
23828
|
diagnostics.mutated += 1;
|
|
23499
23829
|
else
|
|
23500
23830
|
diagnostics.persistedWithoutMutation += 1;
|
|
23501
23831
|
}
|
|
23502
|
-
}
|
|
23832
|
+
}
|
|
23503
23833
|
} catch (error) {
|
|
23504
23834
|
if (!admitted && /database (?:table )?is locked|sqlite_(busy|locked)/i.test(String(error))) {
|
|
23505
23835
|
sessionLog2(sessionId, "pending operations write admission busy; retaining prior bytes for retry");
|
|
23506
23836
|
return false;
|
|
23507
23837
|
}
|
|
23508
23838
|
throw error;
|
|
23509
|
-
} finally {
|
|
23510
|
-
if (admitted)
|
|
23511
|
-
logSlowWriteTransaction("apply_pending_operations", startedAt);
|
|
23512
23839
|
}
|
|
23513
23840
|
if (diagnostics) {
|
|
23514
23841
|
onBatchComplete?.(diagnostics);
|
|
@@ -24880,6 +25207,24 @@ function checkCompartmentTrigger(db, sessionId, sessionMeta, usage, _previousPer
|
|
|
24880
25207
|
};
|
|
24881
25208
|
}
|
|
24882
25209
|
|
|
25210
|
+
// ../plugin/src/hooks/magic-context/degraded-pass-refusal.ts
|
|
25211
|
+
class DegradedPassRefusalError extends Error {
|
|
25212
|
+
site;
|
|
25213
|
+
code = "DEGRADED_PASS_REFUSAL";
|
|
25214
|
+
recoverable = true;
|
|
25215
|
+
estimatedTokens;
|
|
25216
|
+
contextLimitTokens;
|
|
25217
|
+
constructor(site, options) {
|
|
25218
|
+
super(renderUserFacingFailure("transform_pass_degraded", "plain"), {
|
|
25219
|
+
cause: options?.cause
|
|
25220
|
+
});
|
|
25221
|
+
this.site = site;
|
|
25222
|
+
this.name = "DegradedPassRefusalError";
|
|
25223
|
+
this.estimatedTokens = options?.estimatedTokens;
|
|
25224
|
+
this.contextLimitTokens = options?.contextLimitTokens;
|
|
25225
|
+
}
|
|
25226
|
+
}
|
|
25227
|
+
|
|
24883
25228
|
// ../plugin/src/hooks/magic-context/dropped-token-estimate.ts
|
|
24884
25229
|
var TAG_QUERY_CHUNK_SIZE = 900;
|
|
24885
25230
|
var ESTIMATED_CHARACTERS_PER_TOKEN = 3.5;
|
|
@@ -25500,8 +25845,110 @@ function advanceToolReclaimWatermarkToCurrentMax(db, sessionId) {
|
|
|
25500
25845
|
return maxTagNumber;
|
|
25501
25846
|
}
|
|
25502
25847
|
|
|
25848
|
+
// ../plugin/src/shared/recovery-reasoning-prefix.ts
|
|
25849
|
+
function recoveryReasoningPrefixes(messages, partsKey) {
|
|
25850
|
+
const prefixes = [];
|
|
25851
|
+
for (let index = 0;index < messages.length; index++) {
|
|
25852
|
+
const message = messages[index];
|
|
25853
|
+
const parts = message?.[partsKey];
|
|
25854
|
+
if (!Array.isArray(parts))
|
|
25855
|
+
continue;
|
|
25856
|
+
for (let p = 0;p < parts.length; p++) {
|
|
25857
|
+
const type = parts[p]?.type;
|
|
25858
|
+
if (type !== "thinking" && type !== "reasoning" && type !== "redactedThinking" && type !== "redacted_thinking")
|
|
25859
|
+
continue;
|
|
25860
|
+
prefixes.push(JSON.stringify([
|
|
25861
|
+
...messages.slice(0, index),
|
|
25862
|
+
{ ...message, [partsKey]: parts.slice(0, p + 1) }
|
|
25863
|
+
]));
|
|
25864
|
+
}
|
|
25865
|
+
}
|
|
25866
|
+
return prefixes;
|
|
25867
|
+
}
|
|
25868
|
+
|
|
25503
25869
|
// ../plugin/src/shared/tag-transcript.ts
|
|
25504
25870
|
import { createHash as createHash11 } from "node:crypto";
|
|
25871
|
+
function buildPersistedDropTargets(transcript, tags, options = {}) {
|
|
25872
|
+
const targets = new Map;
|
|
25873
|
+
const tools = new Map(tags.filter((tag) => tag.type === "tool").map((tag) => [makeToolCompositeKey(tag.toolOwnerMessageId ?? "", tag.messageId), tag]));
|
|
25874
|
+
const textTags = new Map(tags.filter((tag) => tag.type !== "tool").map((tag) => [tag.messageId, tag]));
|
|
25875
|
+
const byNumber = new Map(tags.map((tag) => [tag.tagNumber, tag]));
|
|
25876
|
+
const aggregates = new Map;
|
|
25877
|
+
const pending = new Map;
|
|
25878
|
+
const resolved = new Map;
|
|
25879
|
+
const ambiguousCalls = new Set;
|
|
25880
|
+
const end = new ConversationEndGuard(transcript.messages);
|
|
25881
|
+
for (const message of transcript.messages) {
|
|
25882
|
+
let ordinal = 0;
|
|
25883
|
+
let resultRun;
|
|
25884
|
+
const parts = message.parts;
|
|
25885
|
+
const reasoning = message.requiresToolArcSkeleton ?? (message.info.role === "assistant" && parts.some((part) => part.kind === "thinking"));
|
|
25886
|
+
for (const part of parts) {
|
|
25887
|
+
if (part.kind !== "tool_result")
|
|
25888
|
+
resultRun = undefined;
|
|
25889
|
+
if (part.kind === "text") {
|
|
25890
|
+
const prefix = /^§(\d+)§\s/.exec(part.getText() ?? "");
|
|
25891
|
+
const tag = (prefix ? byNumber.get(Number(prefix[1])) : undefined) ?? textTags.get(`${message.info.id}:p${ordinal}`);
|
|
25892
|
+
ordinal++;
|
|
25893
|
+
if (tag && tag.type !== "tool")
|
|
25894
|
+
targets.set(tag.tagNumber, buildTextTarget(part, message, ""));
|
|
25895
|
+
} else if ((part.kind === "tool_use" || part.kind === "tool_result") && part.id) {
|
|
25896
|
+
const open = pending.get(part.id) ?? [];
|
|
25897
|
+
if (open.length > 1)
|
|
25898
|
+
ambiguousCalls.add(part.id);
|
|
25899
|
+
const key = (part.kind === "tool_result" ? resultRun?.callId === part.id ? resultRun.key : open.at(-1) ?? resolved.get(part.id) : undefined) ?? makeToolCompositeKey(message.info.id ?? "", part.id);
|
|
25900
|
+
if (part.kind === "tool_use")
|
|
25901
|
+
open.push(key);
|
|
25902
|
+
else {
|
|
25903
|
+
const position = open.lastIndexOf(key);
|
|
25904
|
+
if (position >= 0)
|
|
25905
|
+
open.splice(position, 1);
|
|
25906
|
+
resultRun = { callId: part.id, key };
|
|
25907
|
+
resolved.set(part.id, key);
|
|
25908
|
+
}
|
|
25909
|
+
pending.set(part.id, open);
|
|
25910
|
+
let aggregate = aggregates.get(key);
|
|
25911
|
+
if (!aggregate) {
|
|
25912
|
+
aggregate = {
|
|
25913
|
+
tag: tools.get(key),
|
|
25914
|
+
occurrences: [],
|
|
25915
|
+
reasoning: false
|
|
25916
|
+
};
|
|
25917
|
+
aggregates.set(key, aggregate);
|
|
25918
|
+
}
|
|
25919
|
+
aggregate.occurrences.push({ message, part, kind: part.kind });
|
|
25920
|
+
aggregate.reasoning ||= reasoning;
|
|
25921
|
+
}
|
|
25922
|
+
}
|
|
25923
|
+
}
|
|
25924
|
+
const arcsByCall = new Map;
|
|
25925
|
+
for (const { occurrences } of aggregates.values()) {
|
|
25926
|
+
const callId = occurrences[0]?.part.id ?? "";
|
|
25927
|
+
arcsByCall.set(callId, (arcsByCall.get(callId) ?? 0) + 1);
|
|
25928
|
+
}
|
|
25929
|
+
for (const aggregate of aggregates.values()) {
|
|
25930
|
+
const { occurrences, reasoning } = aggregate;
|
|
25931
|
+
const callId = occurrences[0]?.part.id ?? "";
|
|
25932
|
+
if (ambiguousCalls.has(callId))
|
|
25933
|
+
continue;
|
|
25934
|
+
const tag = aggregate.tag ?? (arcsByCall.get(callId) === 1 ? tools.get(makeToolCompositeKey("", callId)) : undefined);
|
|
25935
|
+
if (tag)
|
|
25936
|
+
targets.set(tag.tagNumber, buildAggregateTarget(tag.tagNumber, occurrences, reasoning, end));
|
|
25937
|
+
}
|
|
25938
|
+
if (!options.allowReasoningPrefixRewrite) {
|
|
25939
|
+
let lastThinking = -1;
|
|
25940
|
+
transcript.messages.forEach((message, index) => {
|
|
25941
|
+
if (message.parts.some((part) => part.kind === "thinking"))
|
|
25942
|
+
lastThinking = index;
|
|
25943
|
+
});
|
|
25944
|
+
const protectedIds = new Set(transcript.messages.slice(0, lastThinking + 1).map((message) => message.info.id));
|
|
25945
|
+
for (const [number, target] of targets) {
|
|
25946
|
+
if (target.mutationParts?.some(({ message }) => protectedIds.has(message.info.id)))
|
|
25947
|
+
targets.delete(number);
|
|
25948
|
+
}
|
|
25949
|
+
}
|
|
25950
|
+
return targets;
|
|
25951
|
+
}
|
|
25505
25952
|
function textIdentityDigest(value) {
|
|
25506
25953
|
return createHash11("sha256").update(value).digest("hex");
|
|
25507
25954
|
}
|
|
@@ -26196,6 +26643,7 @@ function buildAggregateTarget(tagId, occurrences, requiresToolArcSkeleton, conve
|
|
|
26196
26643
|
}
|
|
26197
26644
|
return any ? "truncated" : "absent";
|
|
26198
26645
|
},
|
|
26646
|
+
prepareDrop: () => !occurrences.some((occ) => occ.part.canRemove?.() === "defer"),
|
|
26199
26647
|
canDrop: () => complete() && !occurrences.some((occ) => occ.part.hasUserAnswer?.()),
|
|
26200
26648
|
requiresToolArcSkeleton,
|
|
26201
26649
|
readInput() {
|
|
@@ -28225,18 +28673,27 @@ function buildPiCompactionSummary(compartments) {
|
|
|
28225
28673
|
return `Magic Context compacted ${titles.length} segments: ${shown}; …and ${titles.length - MAX_SUMMARY_TITLES} more`;
|
|
28226
28674
|
}
|
|
28227
28675
|
function findFirstKeptEntryId(entries, lastCompactedOrdinal, ordinalOffset = 0) {
|
|
28676
|
+
const boundary = resolvePiFirstKeptEntry(entries, lastCompactedOrdinal, ordinalOffset);
|
|
28677
|
+
return boundary.kind === "resolved" ? boundary.entryId : null;
|
|
28678
|
+
}
|
|
28679
|
+
function resolvePiFirstKeptEntry(entries, lastCompactedOrdinal, ordinalOffset = 0) {
|
|
28228
28680
|
const target = lastCompactedOrdinal + 1;
|
|
28229
28681
|
const afterOrdinal = Number.isNaN(lastCompactedOrdinal) ? 0 : Math.max(0, lastCompactedOrdinal);
|
|
28682
|
+
let syntheticKeptId;
|
|
28230
28683
|
for (const message of iterateEntriesToRawMessageRange(entries, afterOrdinal, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, ordinalOffset)) {
|
|
28231
28684
|
if (message.ordinal < target || isPiSystemEntry(message))
|
|
28232
28685
|
continue;
|
|
28233
|
-
if (
|
|
28234
|
-
return
|
|
28686
|
+
if (syntheticKeptId)
|
|
28687
|
+
return { kind: "unplaceable", rawId: syntheticKeptId };
|
|
28688
|
+
if (message.id.startsWith(SYNTH_USER_ID_PREFIX)) {
|
|
28689
|
+
syntheticKeptId = message.id;
|
|
28690
|
+
continue;
|
|
28691
|
+
}
|
|
28235
28692
|
if (message.id.length === 0)
|
|
28236
28693
|
continue;
|
|
28237
|
-
return message.id;
|
|
28694
|
+
return { kind: "resolved", entryId: message.id };
|
|
28238
28695
|
}
|
|
28239
|
-
return
|
|
28696
|
+
return { kind: "waiting" };
|
|
28240
28697
|
}
|
|
28241
28698
|
|
|
28242
28699
|
// src/compaction-marker-manager-pi.ts
|
|
@@ -28257,6 +28714,7 @@ function logPiCompactionMarkerDrainGateOnce(sessionId, pending, gate, fields = {
|
|
|
28257
28714
|
`endMessageId=${pending.endMessageId}`,
|
|
28258
28715
|
`ordinal=${pending.ordinal}`,
|
|
28259
28716
|
`firstKeptEntryId=${firstKeptEntryId ?? "<none>"}`,
|
|
28717
|
+
`boundaryKind=${pending.endMessageId.startsWith(SYNTH_USER_ID_PREFIX) ? "folded-tool-result" : "session-entry"}`,
|
|
28260
28718
|
`renderedCoverageOrdinal=${fields.renderedCoverageOrdinal ?? "<none>"}`
|
|
28261
28719
|
];
|
|
28262
28720
|
if (fields.renderedCoverageEndMessageId !== undefined)
|
|
@@ -28269,6 +28727,17 @@ function logPiCompactionMarkerDrainGateOnce(sessionId, pending, gate, fields = {
|
|
|
28269
28727
|
parts.push(`matchCount=${fields.matchCount}`);
|
|
28270
28728
|
if (fields.alignment !== undefined)
|
|
28271
28729
|
parts.push(`alignment=${fields.alignment}`);
|
|
28730
|
+
if (fields.alignmentReason !== undefined)
|
|
28731
|
+
parts.push(`alignmentReason=${fields.alignmentReason}`);
|
|
28732
|
+
for (const key of [
|
|
28733
|
+
"historyConsumed",
|
|
28734
|
+
"materializationSatisfied",
|
|
28735
|
+
"drainSuppressed",
|
|
28736
|
+
"casLost"
|
|
28737
|
+
]) {
|
|
28738
|
+
if (fields[key] !== undefined)
|
|
28739
|
+
parts.push(`${key}=${fields[key]}`);
|
|
28740
|
+
}
|
|
28272
28741
|
if (fields.foldingOnlyTools !== undefined)
|
|
28273
28742
|
parts.push(`foldingOnlyTools=${JSON.stringify(fields.foldingOnlyTools)}`);
|
|
28274
28743
|
if (fields.persistedOnlyTools !== undefined)
|
|
@@ -28318,10 +28787,20 @@ function applyDeferredPiCompactionMarker(deps, sessionId, pending) {
|
|
|
28318
28787
|
if (!firstKeptEntryId) {
|
|
28319
28788
|
const alignment = resolvePiOrdinalAlignment(deps.db, sessionId, branchEntries);
|
|
28320
28789
|
if (isPiOrdinalAlignmentUnanchored(alignment)) {
|
|
28321
|
-
|
|
28790
|
+
if (alignment.kind === "unanchored" && alignment.reason === "anchor-missing" && hasMergedSyntheticBoundary(branchEntries, pending.endMessageId)) {
|
|
28791
|
+
return { kind: "stale-skip", reason: "synthetic-boundary-merged" };
|
|
28792
|
+
}
|
|
28793
|
+
logPiCompactionMarkerDrainGateOnce(sessionId, pending, "ordinal-alignment-unavailable", {
|
|
28794
|
+
alignment: alignment.kind,
|
|
28795
|
+
...alignment.kind === "unanchored" ? { alignmentReason: alignment.reason } : {}
|
|
28796
|
+
});
|
|
28322
28797
|
return { kind: "waiting-for-entry" };
|
|
28323
28798
|
}
|
|
28324
|
-
|
|
28799
|
+
const boundary = resolvePiFirstKeptEntry(branchEntries, pending.ordinal, alignment.offset);
|
|
28800
|
+
if (boundary.kind === "unplaceable") {
|
|
28801
|
+
return { kind: "stale-skip", reason: "synthetic-kept-entry" };
|
|
28802
|
+
}
|
|
28803
|
+
firstKeptEntryId = boundary.kind === "resolved" ? boundary.entryId : null;
|
|
28325
28804
|
}
|
|
28326
28805
|
if (!firstKeptEntryId) {
|
|
28327
28806
|
logPiCompactionMarkerDrainGateOnce(sessionId, pending, "no-replay-safe-kept-entry", { firstKeptEntryId: null });
|
|
@@ -28376,6 +28855,27 @@ function applyDeferredPiCompactionMarker(deps, sessionId, pending) {
|
|
|
28376
28855
|
return { kind: "retryable-failure", error };
|
|
28377
28856
|
}
|
|
28378
28857
|
}
|
|
28858
|
+
function hasMergedSyntheticBoundary(entries, endMessageId) {
|
|
28859
|
+
if (!endMessageId.startsWith(SYNTH_USER_ID_PREFIX))
|
|
28860
|
+
return false;
|
|
28861
|
+
const resultId = endMessageId.slice(SYNTH_USER_ID_PREFIX.length);
|
|
28862
|
+
let foundResult = false;
|
|
28863
|
+
for (const entry of entries) {
|
|
28864
|
+
const row = entry;
|
|
28865
|
+
if (row?.type !== "message")
|
|
28866
|
+
continue;
|
|
28867
|
+
const message = row.message;
|
|
28868
|
+
if (!foundResult) {
|
|
28869
|
+
foundResult = row.id === resultId && message?.role === "toolResult" && typeof message.toolCallId === "string" && message.toolCallId.length > 0;
|
|
28870
|
+
continue;
|
|
28871
|
+
}
|
|
28872
|
+
if (message?.role === "user")
|
|
28873
|
+
return true;
|
|
28874
|
+
if (message?.role === "assistant")
|
|
28875
|
+
return false;
|
|
28876
|
+
}
|
|
28877
|
+
return false;
|
|
28878
|
+
}
|
|
28379
28879
|
function findLatestCompaction(branchEntries) {
|
|
28380
28880
|
for (let i = branchEntries.length - 1;i >= 0; i--) {
|
|
28381
28881
|
const entry = branchEntries[i];
|
|
@@ -29729,30 +30229,26 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
|
|
|
29729
30229
|
const tPersist = performance.now();
|
|
29730
30230
|
const toDrop = new Set(plan.tagNumbers);
|
|
29731
30231
|
const newestEmergencyTags = recentTags;
|
|
29732
|
-
|
|
29733
|
-
|
|
29734
|
-
|
|
29735
|
-
|
|
29736
|
-
|
|
29737
|
-
|
|
29738
|
-
|
|
29739
|
-
|
|
29740
|
-
|
|
29741
|
-
|
|
29742
|
-
|
|
30232
|
+
for (const tag of tags) {
|
|
30233
|
+
if (!toDrop.has(tag.tagNumber))
|
|
30234
|
+
continue;
|
|
30235
|
+
if (tag.status !== "active" || tag.type !== "tool")
|
|
30236
|
+
continue;
|
|
30237
|
+
const target = targets.get(tag.tagNumber);
|
|
30238
|
+
const recent = (emergency.usagePercentage ?? 0) < 95 && newestEmergencyTags.has(tag.tagNumber);
|
|
30239
|
+
const { result, mode } = applyPersistedToolDrop(db, sessionId, tag.tagNumber, target, {
|
|
30240
|
+
inWindow: recent,
|
|
30241
|
+
keepSkeleton: target?.requiresToolArcSkeleton === true
|
|
30242
|
+
});
|
|
30243
|
+
if (result === "removed" || result === "truncated") {
|
|
30244
|
+
droppedTools++;
|
|
30245
|
+
emergencyDroppedTools++;
|
|
30246
|
+
droppedTokenReductions.push({
|
|
30247
|
+
tagNumber: tag.tagNumber,
|
|
30248
|
+
mode: mode === "full" ? "full" : "truncated"
|
|
29743
30249
|
});
|
|
29744
|
-
if (result === "removed" || result === "truncated") {
|
|
29745
|
-
updateTagStatus2(db, sessionId, tag.tagNumber, "dropped");
|
|
29746
|
-
updateTagDropMode2(db, sessionId, tag.tagNumber, mode);
|
|
29747
|
-
droppedTools++;
|
|
29748
|
-
emergencyDroppedTools++;
|
|
29749
|
-
droppedTokenReductions.push({
|
|
29750
|
-
tagNumber: tag.tagNumber,
|
|
29751
|
-
mode: mode === "full" ? "full" : "truncated"
|
|
29752
|
-
});
|
|
29753
|
-
}
|
|
29754
30250
|
}
|
|
29755
|
-
}
|
|
30251
|
+
}
|
|
29756
30252
|
logStep("applyEmergencyDrops", tPersist, `dropped=${emergencyDroppedTools}`);
|
|
29757
30253
|
sessionLog2(sessionId, `emergency tiered drop: ${plan.reason}`);
|
|
29758
30254
|
} else {
|
|
@@ -29761,33 +30257,29 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
|
|
|
29761
30257
|
}
|
|
29762
30258
|
const staleReduce = routine && config.staleReduceStripEnabled ? collectStaleReduceCallIds(piMessages, buildMessageIdToMaxTagFromTargets(targets), buildCtxReduceTagNumbers(tags), toolAgeCutoff, resolveStableId) : { composite: new Set, bareCallIds: new Set };
|
|
29763
30259
|
if (routine && config.staleReduceStripEnabled && (staleReduce.composite.size > 0 || staleReduce.bareCallIds.size > 0)) {
|
|
29764
|
-
|
|
29765
|
-
|
|
29766
|
-
|
|
29767
|
-
|
|
29768
|
-
|
|
29769
|
-
|
|
29770
|
-
|
|
29771
|
-
|
|
29772
|
-
|
|
29773
|
-
|
|
29774
|
-
|
|
29775
|
-
|
|
29776
|
-
|
|
29777
|
-
|
|
29778
|
-
|
|
29779
|
-
|
|
29780
|
-
|
|
29781
|
-
|
|
29782
|
-
|
|
29783
|
-
|
|
29784
|
-
|
|
29785
|
-
if (result === "removed" || result === "truncated") {
|
|
29786
|
-
droppedStaleReduceCalls++;
|
|
29787
|
-
config.onStaleReduceEdit?.(tag);
|
|
29788
|
-
}
|
|
30260
|
+
for (const tag of tags) {
|
|
30261
|
+
if (tag.status !== "active")
|
|
30262
|
+
continue;
|
|
30263
|
+
if (tag.type !== "tool")
|
|
30264
|
+
continue;
|
|
30265
|
+
if (protectedTools.has(tag.tagNumber))
|
|
30266
|
+
continue;
|
|
30267
|
+
if (!tag.messageId)
|
|
30268
|
+
continue;
|
|
30269
|
+
const matched = tag.toolOwnerMessageId ? staleReduce.composite.has(`${tag.toolOwnerMessageId}\x00${tag.messageId}`) : staleReduce.bareCallIds.has(tag.messageId);
|
|
30270
|
+
if (!matched)
|
|
30271
|
+
continue;
|
|
30272
|
+
const target = targets.get(tag.tagNumber);
|
|
30273
|
+
if (target?.canDrop?.() === false)
|
|
30274
|
+
continue;
|
|
30275
|
+
const { result } = applyPersistedToolDrop(db, sessionId, tag.tagNumber, target, { inWindow: false });
|
|
30276
|
+
if (result === "incomplete")
|
|
30277
|
+
continue;
|
|
30278
|
+
if (result === "removed" || result === "truncated") {
|
|
30279
|
+
droppedStaleReduceCalls++;
|
|
30280
|
+
config.onStaleReduceEdit?.(tag);
|
|
29789
30281
|
}
|
|
29790
|
-
}
|
|
30282
|
+
}
|
|
29791
30283
|
}
|
|
29792
30284
|
if (routine) {
|
|
29793
30285
|
db.transaction(() => {
|
|
@@ -29856,37 +30348,33 @@ function applyPiHeuristicCleanup(sessionId, db, targets, piMessages, config, pre
|
|
|
29856
30348
|
group.push(tag);
|
|
29857
30349
|
fingerprintGroups.set(fingerprint, group);
|
|
29858
30350
|
}
|
|
29859
|
-
|
|
29860
|
-
|
|
29861
|
-
|
|
30351
|
+
for (const [, group] of fingerprintGroups) {
|
|
30352
|
+
if (group.length <= 1)
|
|
30353
|
+
continue;
|
|
30354
|
+
group.sort((a, b) => a.tagNumber - b.tagNumber);
|
|
30355
|
+
for (let i = 0;i < group.length - 1; i++) {
|
|
30356
|
+
const tag = group[i];
|
|
30357
|
+
if (tag.tagNumber > protectedCutoff)
|
|
29862
30358
|
continue;
|
|
29863
|
-
|
|
29864
|
-
|
|
29865
|
-
|
|
29866
|
-
|
|
29867
|
-
|
|
29868
|
-
|
|
29869
|
-
|
|
29870
|
-
|
|
29871
|
-
|
|
29872
|
-
|
|
29873
|
-
|
|
29874
|
-
|
|
30359
|
+
const target = targets.get(tag.tagNumber);
|
|
30360
|
+
if (protectedTools.has(tag.tagNumber))
|
|
30361
|
+
continue;
|
|
30362
|
+
if (target?.canDrop?.() === false)
|
|
30363
|
+
continue;
|
|
30364
|
+
const { result, mode } = applyPersistedToolDrop(db, sessionId, tag.tagNumber, target, {
|
|
30365
|
+
inWindow: false
|
|
30366
|
+
});
|
|
30367
|
+
if (result === "incomplete")
|
|
30368
|
+
continue;
|
|
30369
|
+
if (result === "removed" || result === "truncated") {
|
|
30370
|
+
deduplicatedTools++;
|
|
30371
|
+
droppedTokenReductions.push({
|
|
30372
|
+
tagNumber: tag.tagNumber,
|
|
30373
|
+
mode: mode === "full" ? "full" : "truncated"
|
|
29875
30374
|
});
|
|
29876
|
-
if (result === "incomplete")
|
|
29877
|
-
continue;
|
|
29878
|
-
updateTagDropMode2(db, sessionId, tag.tagNumber, mode);
|
|
29879
|
-
updateTagStatus2(db, sessionId, tag.tagNumber, "dropped");
|
|
29880
|
-
if (result === "removed" || result === "truncated") {
|
|
29881
|
-
deduplicatedTools++;
|
|
29882
|
-
droppedTokenReductions.push({
|
|
29883
|
-
tagNumber: tag.tagNumber,
|
|
29884
|
-
mode: mode === "full" ? "full" : "truncated"
|
|
29885
|
-
});
|
|
29886
|
-
}
|
|
29887
30375
|
}
|
|
29888
30376
|
}
|
|
29889
|
-
}
|
|
30377
|
+
}
|
|
29890
30378
|
}
|
|
29891
30379
|
if (droppedTools > 0 || deduplicatedTools > 0 || droppedInjections > 0 || droppedStaleReduceCalls > 0) {
|
|
29892
30380
|
sessionLog2(sessionId, `heuristic cleanup: dropped ${droppedTools} tool tags, stale ctx_reduce=${droppedStaleReduceCalls}, deduplicated ${deduplicatedTools} tool calls, dropped ${droppedInjections} system injections`);
|
|
@@ -30933,6 +31421,7 @@ function forgetCachedPrefix(sessionId) {
|
|
|
30933
31421
|
function dropCompartmentsAfter(db, sessionId, anchor, holderId, activeBranch = false, onRetire) {
|
|
30934
31422
|
db.exec("BEGIN IMMEDIATE");
|
|
30935
31423
|
let finished = false;
|
|
31424
|
+
let clearedMarker = null;
|
|
30936
31425
|
try {
|
|
30937
31426
|
if (!isCompartmentLeaseHeld(db, sessionId, holderId)) {
|
|
30938
31427
|
db.exec("ROLLBACK");
|
|
@@ -30949,14 +31438,16 @@ function dropCompartmentsAfter(db, sessionId, anchor, holderId, activeBranch = f
|
|
|
30949
31438
|
if (activeBranch) {
|
|
30950
31439
|
db.prepare("UPDATE session_meta SET prior_boundary_ordinal = ? WHERE session_id = ?").run((anchor?.endMessage ?? 0) + 1, sessionId);
|
|
30951
31440
|
const marker = getPendingPiCompactionMarkerState(db, sessionId);
|
|
30952
|
-
if (marker)
|
|
30953
|
-
|
|
31441
|
+
if (marker && clearPendingPiCompactionMarkerStateIf(db, sessionId, marker, null))
|
|
31442
|
+
clearedMarker = marker;
|
|
30954
31443
|
}
|
|
30955
31444
|
deleteChunkEmbedBackoffForSession(db, sessionId);
|
|
30956
31445
|
queueM0Mutation(db, { sessionId, mutationType: "recomp_boundary_change" });
|
|
30957
31446
|
clearCachedM0M1(db, sessionId);
|
|
30958
31447
|
db.exec("COMMIT");
|
|
30959
31448
|
finished = true;
|
|
31449
|
+
if (clearedMarker)
|
|
31450
|
+
logPiCompactionMarkerClear(sessionId, clearedMarker, "active-branch-retired");
|
|
30960
31451
|
return dropped.map((row) => row.sequence);
|
|
30961
31452
|
} finally {
|
|
30962
31453
|
if (!finished) {
|
|
@@ -32575,6 +33066,54 @@ function rewriteNativeToolInput(message, toolCallId, input) {
|
|
|
32575
33066
|
items[match.index] = { ...match.item, [field]: nextValue };
|
|
32576
33067
|
envelope.message.providerPayload = { ...envelope.payload, items };
|
|
32577
33068
|
}
|
|
33069
|
+
function textIdentity(part) {
|
|
33070
|
+
if (!isRecord(part) || part.type !== "text" || typeof part.textSignature !== "string")
|
|
33071
|
+
return;
|
|
33072
|
+
try {
|
|
33073
|
+
const signature = JSON.parse(part.textSignature);
|
|
33074
|
+
if (isRecord(signature) && signature.v === 1 && typeof signature.id === "string" && signature.id.length > 0 && (signature.phase === undefined || signature.phase === "commentary" || signature.phase === "final_answer")) {
|
|
33075
|
+
return { id: signature.id, phase: signature.phase };
|
|
33076
|
+
}
|
|
33077
|
+
} catch {}
|
|
33078
|
+
return;
|
|
33079
|
+
}
|
|
33080
|
+
function rewriteNativeAssistantTexts(message, previous, sessionId) {
|
|
33081
|
+
const envelope = getNativeEnvelope(message);
|
|
33082
|
+
if (envelope?.message.role !== "assistant" || !isRecord(previous) || !Array.isArray(previous.content) || !Array.isArray(envelope.message.content))
|
|
33083
|
+
return;
|
|
33084
|
+
const content = envelope.message.content;
|
|
33085
|
+
for (let index = 0;index < content.length; index++) {
|
|
33086
|
+
const part = content[index];
|
|
33087
|
+
const before = previous.content[index];
|
|
33088
|
+
if (!isRecord(part) || part.type !== "text" || typeof part.text !== "string" || !isRecord(before) || before.type !== "text" || typeof before.text !== "string")
|
|
33089
|
+
continue;
|
|
33090
|
+
if (part.text.replace(/^§\d+§ /u, "") === before.text.replace(/^§\d+§ /u, ""))
|
|
33091
|
+
continue;
|
|
33092
|
+
const identity = textIdentity(part);
|
|
33093
|
+
const sameIdentity = (value) => {
|
|
33094
|
+
const other = textIdentity(value);
|
|
33095
|
+
return identity !== undefined && other?.id === identity.id && other.phase === identity.phase;
|
|
33096
|
+
};
|
|
33097
|
+
const matches = envelope.items.flatMap((item, itemIndex) => isRecord(item) && item.type === "message" && item.role === "assistant" && identity !== undefined && item.id === identity.id && item.phase === identity.phase ? [{ item, itemIndex }] : []);
|
|
33098
|
+
const match = matches[0];
|
|
33099
|
+
const outputs = match && Array.isArray(match.item.content) ? match.item.content.flatMap((output, outputIndex) => isRecord(output) && output.type === "output_text" && typeof output.text === "string" ? [{ output, outputIndex }] : []) : [];
|
|
33100
|
+
if (!identity || content.filter(sameIdentity).length !== 1 || previous.content.filter(sameIdentity).length !== 1 || matches.length !== 1 || outputs.length !== 1) {
|
|
33101
|
+
if (sessionId)
|
|
33102
|
+
sessionLog2(sessionId, `native assistant text replay skipped: missing or ambiguous id/phase match at part ${index} (items=${matches.length}, outputs=${outputs.length}); retaining native history for dispatch validation`);
|
|
33103
|
+
continue;
|
|
33104
|
+
}
|
|
33105
|
+
const { output, outputIndex } = outputs[0];
|
|
33106
|
+
if (output.text === part.text)
|
|
33107
|
+
continue;
|
|
33108
|
+
const nextContent = match.item.content.slice();
|
|
33109
|
+
nextContent[outputIndex] = { ...output, text: part.text };
|
|
33110
|
+
const items = envelope.items.slice();
|
|
33111
|
+
items[match.itemIndex] = { ...match.item, content: nextContent };
|
|
33112
|
+
envelope.items = items;
|
|
33113
|
+
envelope.payload = { ...envelope.payload, items };
|
|
33114
|
+
envelope.message.providerPayload = envelope.payload;
|
|
33115
|
+
}
|
|
33116
|
+
}
|
|
32578
33117
|
function hasRedactedThinkingContent(message) {
|
|
32579
33118
|
if (!Array.isArray(message.content))
|
|
32580
33119
|
return false;
|
|
@@ -33670,7 +34209,7 @@ function object4(value) {
|
|
|
33670
34209
|
function wellFormed(text) {
|
|
33671
34210
|
return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "�");
|
|
33672
34211
|
}
|
|
33673
|
-
function
|
|
34212
|
+
function textIdentity2(text, phase) {
|
|
33674
34213
|
return JSON.stringify([
|
|
33675
34214
|
text,
|
|
33676
34215
|
phase === "commentary" || phase === "final_answer" ? phase : null
|
|
@@ -33728,7 +34267,7 @@ function piResponsesAssistantTexts(value, model) {
|
|
|
33728
34267
|
units += text.length;
|
|
33729
34268
|
if (units > MAX_TEXT_UNITS)
|
|
33730
34269
|
return;
|
|
33731
|
-
texts.push(
|
|
34270
|
+
texts.push(textIdentity2(text, undefined));
|
|
33732
34271
|
continue;
|
|
33733
34272
|
}
|
|
33734
34273
|
if (block?.type !== "text" || typeof block.text !== "string")
|
|
@@ -33736,7 +34275,7 @@ function piResponsesAssistantTexts(value, model) {
|
|
|
33736
34275
|
units += block.text.length;
|
|
33737
34276
|
if (units > MAX_TEXT_UNITS)
|
|
33738
34277
|
return;
|
|
33739
|
-
texts.push(
|
|
34278
|
+
texts.push(textIdentity2(wellFormed(block.text), sameModel ? signaturePhase(block) : undefined));
|
|
33740
34279
|
blocks.push(block);
|
|
33741
34280
|
}
|
|
33742
34281
|
const payload = object4(message.providerPayload);
|
|
@@ -33761,7 +34300,7 @@ function piResponsesAssistantTexts(value, model) {
|
|
|
33761
34300
|
}
|
|
33762
34301
|
if (signature?.v !== 1 || typeof signature.id !== "string" || item.id !== signature.id || item.phase !== signature.phase || part.text !== block.text.replace(/^§\d+§ /u, ""))
|
|
33763
34302
|
return texts;
|
|
33764
|
-
replay.push(
|
|
34303
|
+
replay.push(textIdentity2(wellFormed(part.text), item.phase));
|
|
33765
34304
|
}
|
|
33766
34305
|
}
|
|
33767
34306
|
return replay.length === texts.length ? replay : texts;
|
|
@@ -33770,7 +34309,7 @@ function piResponsesWireTexts(value, phase) {
|
|
|
33770
34309
|
if (phase !== undefined && phase !== null && phase !== "commentary" && phase !== "final_answer")
|
|
33771
34310
|
return;
|
|
33772
34311
|
if (typeof value === "string")
|
|
33773
|
-
return [
|
|
34312
|
+
return [textIdentity2(value, phase)];
|
|
33774
34313
|
if (!Array.isArray(value))
|
|
33775
34314
|
return;
|
|
33776
34315
|
const texts = [];
|
|
@@ -33782,7 +34321,7 @@ function piResponsesWireTexts(value, phase) {
|
|
|
33782
34321
|
units += part.text.length;
|
|
33783
34322
|
if (units > MAX_TEXT_UNITS)
|
|
33784
34323
|
return;
|
|
33785
|
-
texts.push(
|
|
34324
|
+
texts.push(textIdentity2(part.text, phase));
|
|
33786
34325
|
}
|
|
33787
34326
|
return texts;
|
|
33788
34327
|
}
|
|
@@ -35775,17 +36314,26 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
|
|
|
35775
36314
|
return { rebuildReason: "lkg_recovery_input_rebuild" };
|
|
35776
36315
|
return { prefix };
|
|
35777
36316
|
};
|
|
35778
|
-
const
|
|
35779
|
-
const state = stateFor(snapshot.sessionId);
|
|
36317
|
+
const recoveryEntryIds = (snapshot) => {
|
|
35780
36318
|
const anchor = snapshot.replayAnchorInputIndex;
|
|
35781
36319
|
const slot = getSlot(snapshot.sessionId);
|
|
35782
36320
|
if (snapshot.preparationFailure || anchor === null || !slot?.piOutputEntryIds)
|
|
35783
36321
|
throw new PiLkgUncapturable("lkg_output_mapping_unavailable", "Cannot retain recovered output ownership");
|
|
35784
36322
|
const surviving = new Set(snapshot.inputs.map((input) => input.id));
|
|
35785
|
-
|
|
36323
|
+
return [
|
|
35786
36324
|
...slot.piOutputEntryIds.filter((id) => id === null || surviving.has(id)),
|
|
35787
36325
|
...snapshot.inputs.slice(anchor + 1).map((input) => input.id)
|
|
35788
36326
|
];
|
|
36327
|
+
};
|
|
36328
|
+
const noteRecovery = (snapshot, messages, entryIds) => {
|
|
36329
|
+
const state = stateFor(snapshot.sessionId);
|
|
36330
|
+
const slot = getSlot(snapshot.sessionId);
|
|
36331
|
+
if (!slot)
|
|
36332
|
+
throw new PiLkgUncapturable("lkg_miss", "Cannot retain recovery without a saved request");
|
|
36333
|
+
const anchor = snapshot.replayAnchorInputIndex;
|
|
36334
|
+
if (anchor === null)
|
|
36335
|
+
throw new PiLkgUncapturable("lkg_miss", "Cannot retain recovery without an input anchor");
|
|
36336
|
+
const outputEntryIds = entryIds ?? recoveryEntryIds(snapshot);
|
|
35789
36337
|
if (outputEntryIds.length !== messages.length)
|
|
35790
36338
|
throw new PiLkgUncapturable("lkg_output_mapping_unavailable", "Recovered output ownership does not match the served array");
|
|
35791
36339
|
state.recoveryPrefix = {
|
|
@@ -36202,6 +36750,7 @@ function createPiLkgCoordinator(db, scheduleCapture = (capture) => setImmediate(
|
|
|
36202
36750
|
};
|
|
36203
36751
|
return {
|
|
36204
36752
|
noteRecovery,
|
|
36753
|
+
recoveryEntryIds,
|
|
36205
36754
|
recoveryFor,
|
|
36206
36755
|
beginPass(args) {
|
|
36207
36756
|
return {
|
|
@@ -38557,31 +39106,61 @@ function isLargeImagePart(value) {
|
|
|
38557
39106
|
const part = value;
|
|
38558
39107
|
return part.type === "image" && typeof part.mimeType === "string" && part.mimeType.startsWith("image/") && typeof part.data === "string" && part.data.length > 200;
|
|
38559
39108
|
}
|
|
38560
|
-
function
|
|
38561
|
-
|
|
38562
|
-
|
|
38563
|
-
|
|
38564
|
-
|
|
38565
|
-
|
|
38566
|
-
|
|
38567
|
-
|
|
38568
|
-
|
|
38569
|
-
|
|
38570
|
-
|
|
38571
|
-
|
|
38572
|
-
|
|
39109
|
+
function planPiProcessedImageIds(args, frozenIds) {
|
|
39110
|
+
if (!args.detect || args.watermark <= 0)
|
|
39111
|
+
return [];
|
|
39112
|
+
const ids = args.messages.map(args.stableId);
|
|
39113
|
+
const ownTags = ids.map((id) => id ? args.messageIdToMaxTag.get(id) ?? 0 : 0);
|
|
39114
|
+
const completeToolResults = new Set;
|
|
39115
|
+
const openCalls = new Map;
|
|
39116
|
+
for (let i = 0;i < args.messages.length; i++) {
|
|
39117
|
+
const message = args.messages[i];
|
|
39118
|
+
if (!message || typeof message !== "object")
|
|
39119
|
+
continue;
|
|
39120
|
+
if (message.role === "assistant" && Array.isArray(message.content)) {
|
|
39121
|
+
for (const part of message.content) {
|
|
39122
|
+
if (part?.type === "toolCall" && typeof part.id === "string") {
|
|
39123
|
+
openCalls.set(part.id, `${ids[i] ?? ""}\x00${part.id}`);
|
|
39124
|
+
}
|
|
38573
39125
|
}
|
|
38574
|
-
|
|
38575
|
-
|
|
39126
|
+
} else if (message.role === "toolResult" && message.toolCallId) {
|
|
39127
|
+
const key = openCalls.get(message.toolCallId);
|
|
39128
|
+
if (key !== undefined && !args.protectedToolKeys?.has(key) && !args.protectedToolKeys?.has(message.toolCallId)) {
|
|
39129
|
+
completeToolResults.add(i);
|
|
38576
39130
|
}
|
|
38577
|
-
|
|
38578
|
-
|
|
38579
|
-
|
|
38580
|
-
|
|
38581
|
-
|
|
38582
|
-
|
|
39131
|
+
openCalls.delete(message.toolCallId);
|
|
39132
|
+
}
|
|
39133
|
+
}
|
|
39134
|
+
const selected = [];
|
|
39135
|
+
let hasAssistantResponse = false;
|
|
39136
|
+
let nextTag;
|
|
39137
|
+
for (let i = args.messages.length - 1;i >= 0; i--) {
|
|
39138
|
+
const ownTag = ownTags[i];
|
|
39139
|
+
const positionTag = ownTag > 0 ? ownTag : nextTag;
|
|
39140
|
+
if (ownTag > 0)
|
|
39141
|
+
nextTag = ownTag;
|
|
39142
|
+
const raw = args.messages[i];
|
|
39143
|
+
if (!raw || typeof raw !== "object")
|
|
39144
|
+
continue;
|
|
39145
|
+
const message = raw;
|
|
39146
|
+
if (message.role === "assistant") {
|
|
39147
|
+
hasAssistantResponse = true;
|
|
39148
|
+
continue;
|
|
38583
39149
|
}
|
|
39150
|
+
if (message.role !== "user" && message.role !== "toolResult" || message.role === "toolResult" && !completeToolResults.has(i) || !hasAssistantResponse || !Array.isArray(message.content) || !message.content.some(isLargeImagePart))
|
|
39151
|
+
continue;
|
|
39152
|
+
const id = ids[i];
|
|
39153
|
+
if (!id || frozenIds.has(id) || positionTag === undefined || positionTag > args.watermark)
|
|
39154
|
+
continue;
|
|
39155
|
+
if ((args.usagePercentage ?? 0) < 95 && positionTag >= (args.protectedCutoff ?? Infinity))
|
|
39156
|
+
continue;
|
|
39157
|
+
selected.push(id);
|
|
38584
39158
|
}
|
|
39159
|
+
return selected;
|
|
39160
|
+
}
|
|
39161
|
+
function stripPiProcessedImages(args) {
|
|
39162
|
+
const frozenIds = getProcessedImageStrippedIds(args.db, args.sessionId);
|
|
39163
|
+
const newlyStrippedIds = planPiProcessedImageIds(args, frozenIds);
|
|
38585
39164
|
if (newlyStrippedIds.length > 0) {
|
|
38586
39165
|
const persisted = (args.addIds ?? addProcessedImageStrippedIds)(args.db, args.sessionId, newlyStrippedIds);
|
|
38587
39166
|
if (persisted) {
|
|
@@ -38833,6 +39412,9 @@ function createPiTranscript(source, sessionId, entryIds, options = {}) {
|
|
|
38833
39412
|
if (committed)
|
|
38834
39413
|
return;
|
|
38835
39414
|
committed = true;
|
|
39415
|
+
for (const index of dirtyMessages) {
|
|
39416
|
+
rewriteNativeAssistantTexts(working[index], source[index], sessionId);
|
|
39417
|
+
}
|
|
38836
39418
|
for (const [index, parts] of removals) {
|
|
38837
39419
|
const message = working[index];
|
|
38838
39420
|
if (message.role !== "assistant" && message.role !== "toolResult" || !Array.isArray(message.content))
|
|
@@ -39225,6 +39807,8 @@ function createPiAssistantPart(working, messageIndex, partIndex, markDirty) {
|
|
|
39225
39807
|
...existing,
|
|
39226
39808
|
arguments: { dropped: sentinelText }
|
|
39227
39809
|
};
|
|
39810
|
+
} else if (existing?.type === "text") {
|
|
39811
|
+
newContent[partIndex] = { ...existing, text: sentinelText };
|
|
39228
39812
|
} else {
|
|
39229
39813
|
newContent[partIndex] = { type: "text", text: sentinelText };
|
|
39230
39814
|
}
|
|
@@ -41053,6 +41637,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
41053
41637
|
let sessionIdForError;
|
|
41054
41638
|
let lkgPassSnapshot;
|
|
41055
41639
|
let lkgCompactionOff = baseOptions.compactionOff === true;
|
|
41640
|
+
let lkgOptions = baseOptions;
|
|
41056
41641
|
let lkgEmergencyRecoveryArmed = false;
|
|
41057
41642
|
const captureServedEvidence = (...args) => {
|
|
41058
41643
|
try {
|
|
@@ -41083,6 +41668,7 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
41083
41668
|
rawMessageCount = event.messages.length;
|
|
41084
41669
|
const fullWireMessageCount = rawMessageCount;
|
|
41085
41670
|
const options = baseOptions.resolveForProject?.(projectDirectory) ?? baseOptions;
|
|
41671
|
+
lkgOptions = options;
|
|
41086
41672
|
const deliveryModelKey = resolvePiContextModelKey(ctx);
|
|
41087
41673
|
if (!budget.sideTurn && deliveryModelKey) {
|
|
41088
41674
|
const parentId = ctx.sessionManager.getLeafId?.();
|
|
@@ -42301,14 +42887,38 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
42301
42887
|
reason: lkgPassSnapshot.replayFailure ?? "lkg_recovery_unproven"
|
|
42302
42888
|
} : lkgCoordinator.replay(lkgPassSnapshot, (id) => ctx.sessionManager.getEntry?.(id)?.parentId);
|
|
42303
42889
|
if (replay.ok) {
|
|
42890
|
+
const reasoningPrefixes = recoveryReasoningPrefixes(replay.messages, "content");
|
|
42891
|
+
const entryIds = lkgCoordinator.recoveryEntryIds(lkgPassSnapshot);
|
|
42892
|
+
const recoverySessionId = sessionIdForError;
|
|
42893
|
+
const recoveryTranscript = createPiTranscript(replay.messages, sessionIdForError, entryIds.map((id) => id ?? undefined), {
|
|
42894
|
+
preserveReasoningToolArcs: ctx.model?.api !== "openai-codex-responses" && ctx.model?.api !== "openai-responses",
|
|
42895
|
+
authorizeToolRemoval: (callId) => authorizePiToolRemoval({
|
|
42896
|
+
db: lkgOptions.db,
|
|
42897
|
+
sessionId: recoverySessionId,
|
|
42898
|
+
callId,
|
|
42899
|
+
saved: getNativeReplayState(lkgOptions.db, recoverySessionId).toolInputs,
|
|
42900
|
+
canApply: false
|
|
42901
|
+
})
|
|
42902
|
+
});
|
|
42903
|
+
const drops = getTagsBySession2(lkgOptions.db, sessionIdForError).filter((tag) => tag.status === "dropped");
|
|
42904
|
+
const changed = applyFlushedStatuses(sessionIdForError, lkgOptions.db, buildPersistedDropTargets(recoveryTranscript, drops, {
|
|
42905
|
+
allowReasoningPrefixRewrite: true
|
|
42906
|
+
}), drops);
|
|
42907
|
+
const working = recoveryTranscript.getWorkingMessages();
|
|
42908
|
+
recoveryTranscript.commit();
|
|
42909
|
+
const idsByOutput = new Map(working.map((message, index) => [message, entryIds[index]]));
|
|
42910
|
+
recoveryTranscript.finalizeToolRemovals();
|
|
42911
|
+
if (JSON.stringify(recoveryReasoningPrefixes(replay.messages, "content")) !== JSON.stringify(reasoningPrefixes))
|
|
42912
|
+
throw new DegradedPassRefusalError("lkg-reasoning-prefix-changed", { cause: err });
|
|
42913
|
+
const recoveryIds = replay.messages.map((message) => idsByOutput.get(message) ?? null);
|
|
42304
42914
|
assertPiRawFallbackFits(replay.messages, rawFallbackLimit, (line) => {
|
|
42305
42915
|
if (sessionIdForError)
|
|
42306
42916
|
logPiLkgRecovery(sessionIdForError, line);
|
|
42307
|
-
}, err, readPiLkgFitEnvelope(ctx, pi, resolvePiContextModelKey(ctx), sessionDecisionCalibration(baseOptions.db, sessionIdForError), toolWireSchema), replay.measuredPrefix);
|
|
42917
|
+
}, err, readPiLkgFitEnvelope(ctx, pi, resolvePiContextModelKey(ctx), sessionDecisionCalibration(baseOptions.db, sessionIdForError), toolWireSchema), changed ? undefined : replay.measuredPrefix);
|
|
42308
42918
|
const reason = piStorageErrorReason(err);
|
|
42309
42919
|
logPiLkgRecovery(sessionIdForError, `${failureLabel} ${reason}: LKG replay served ${replay.messages.length} messages instead of raw ${rawMessageCount}`);
|
|
42310
42920
|
budget.assertOutcome();
|
|
42311
|
-
lkgCoordinator.noteRecovery(lkgPassSnapshot, replay.messages);
|
|
42921
|
+
lkgCoordinator.noteRecovery(lkgPassSnapshot, replay.messages, recoveryIds);
|
|
42312
42922
|
if (!budget.sideTurn)
|
|
42313
42923
|
captureServedEvidence(sessionIdForError, replay.messages, {
|
|
42314
42924
|
recoveryWithoutTagState: true,
|
|
@@ -42321,6 +42931,8 @@ function registerPiContextHandler(pi, baseOptions) {
|
|
|
42321
42931
|
logPiLkgRecovery(sessionIdForError, `${failureLabel} ${piStorageErrorReason(err)}: LKG unavailable (${replay.reason}); refusing unreduced ${rawMessageCount}-message input`);
|
|
42322
42932
|
} catch (replayError) {
|
|
42323
42933
|
budget.assertOutcome();
|
|
42934
|
+
if (replayError instanceof DegradedPassRefusalError)
|
|
42935
|
+
throw replayError;
|
|
42324
42936
|
if (replayError instanceof PiStorageBusyError)
|
|
42325
42937
|
throw replayError;
|
|
42326
42938
|
logPiLkgRecovery(sessionIdForError, `${failureLabel} ${piStorageErrorReason(err)}: LKG replay unavailable (${replayError instanceof Error ? replayError.message : String(replayError)}); refusing unreduced ${rawMessageCount}-message input`);
|
|
@@ -43638,7 +44250,7 @@ async function runPipeline(args) {
|
|
|
43638
44250
|
protectedToolTags,
|
|
43639
44251
|
protectedCutoff: usesTokenProtection ? protectionWindowForPass.cutoff : undefined,
|
|
43640
44252
|
routine: routineCleanupApplied,
|
|
43641
|
-
staleReduceStripEnabled:
|
|
44253
|
+
staleReduceStripEnabled: true,
|
|
43642
44254
|
onStaleReduceEdit: recordStaleReduceEdit,
|
|
43643
44255
|
emergency: args.forceMaterialization === true && args.emergencyCeilingTokens !== undefined && args.emergencyCeilingTokens > 0 ? {
|
|
43644
44256
|
currentTotalInputTokens: args.contextUsage.inputTokens,
|
|
@@ -43654,7 +44266,7 @@ async function runPipeline(args) {
|
|
|
43654
44266
|
protectedToolTags,
|
|
43655
44267
|
protectedCutoff: usesTokenProtection ? protectionWindowForPass.cutoff : undefined,
|
|
43656
44268
|
routine: true,
|
|
43657
|
-
staleReduceStripEnabled:
|
|
44269
|
+
staleReduceStripEnabled: true,
|
|
43658
44270
|
onStaleReduceEdit: recordStaleReduceEdit,
|
|
43659
44271
|
caveman: args.isSubagent ? undefined : args.heuristics.caveman
|
|
43660
44272
|
}, getActiveTagsBySession2(args.db, args.sessionId), stableIdResolver);
|
|
@@ -43840,16 +44452,50 @@ async function runPipeline(args) {
|
|
|
43840
44452
|
}
|
|
43841
44453
|
}
|
|
43842
44454
|
}
|
|
43843
|
-
|
|
44455
|
+
{
|
|
43844
44456
|
const tProcessedImages = performance.now();
|
|
43845
44457
|
try {
|
|
44458
|
+
const imagePositionTags = new Map;
|
|
44459
|
+
const noteImagePosition = (id, tag) => {
|
|
44460
|
+
if (id && tag > (imagePositionTags.get(id) ?? 0))
|
|
44461
|
+
imagePositionTags.set(id, tag);
|
|
44462
|
+
};
|
|
44463
|
+
for (const [tagNumber, target] of targets) {
|
|
44464
|
+
for (const { message, part } of target.mutationParts ?? []) {
|
|
44465
|
+
if (typeof part === "object" && part !== null && "kind" in part && part.kind === "text")
|
|
44466
|
+
noteImagePosition(message.info.id, tagNumber);
|
|
44467
|
+
}
|
|
44468
|
+
}
|
|
44469
|
+
const imageToolTags = new Map(allTagsForPass.filter((tag) => tag.type === "tool").map((tag) => [
|
|
44470
|
+
`${tag.toolOwnerMessageId ?? ""}\x00${tag.messageId}`,
|
|
44471
|
+
tag.tagNumber
|
|
44472
|
+
]));
|
|
44473
|
+
const imageToolOwners = new Map;
|
|
44474
|
+
for (let index = 0;index < workingMessages.length; index++) {
|
|
44475
|
+
const message = workingMessages[index];
|
|
44476
|
+
const id = stableIdResolver(message, index);
|
|
44477
|
+
if (message.role === "assistant" && Array.isArray(message.content)) {
|
|
44478
|
+
for (const part of message.content) {
|
|
44479
|
+
if (part.type !== "toolCall")
|
|
44480
|
+
continue;
|
|
44481
|
+
imageToolOwners.set(part.id, id ?? "");
|
|
44482
|
+
noteImagePosition(id, imageToolTags.get(`${id ?? ""}\x00${part.id}`) ?? 0);
|
|
44483
|
+
}
|
|
44484
|
+
} else if (message.role === "toolResult") {
|
|
44485
|
+
noteImagePosition(id, imageToolTags.get(`${imageToolOwners.get(message.toolCallId) ?? ""}\x00${message.toolCallId}`) ?? 0);
|
|
44486
|
+
}
|
|
44487
|
+
}
|
|
44488
|
+
const protectedImageToolKeys = new Set(activeTags.filter((tag) => tag.type === "tool" && protectedToolTags.has(tag.tagNumber)).map((tag) => tag.toolOwnerMessageId ? `${tag.toolOwnerMessageId}\x00${tag.messageId}` : tag.messageId));
|
|
43846
44489
|
const imageResult = stripPiProcessedImages({
|
|
43847
44490
|
db: args.db,
|
|
43848
44491
|
sessionId: args.sessionId,
|
|
43849
44492
|
messages: workingMessages,
|
|
43850
44493
|
detect: isCacheBustingPass && !protectedSignedPrefix,
|
|
43851
44494
|
watermark: getMaxDroppedTagNumber2(args.db, args.sessionId),
|
|
43852
|
-
messageIdToMaxTag,
|
|
44495
|
+
messageIdToMaxTag: imagePositionTags,
|
|
44496
|
+
protectedCutoff: usesTokenProtection ? protectionWindowForPass.cutoff : protectedTagNumbersForPass.size > 0 ? Math.min(...protectedTagNumbersForPass) : null,
|
|
44497
|
+
usagePercentage: args.contextUsage.percentage,
|
|
44498
|
+
protectedToolKeys: protectedImageToolKeys,
|
|
43853
44499
|
stableId: stableIdResolver,
|
|
43854
44500
|
onFirstApplication: recordFirstApplicationAt
|
|
43855
44501
|
});
|
|
@@ -43999,6 +44645,18 @@ async function runPipeline(args) {
|
|
|
43999
44645
|
const deferredHistoryDrainEligible = historyWasConsumedThisPass && materializationSatisfiedThisPass && (deferredHistoryWasPendingAtPassStart || hasPendingMaterializeSignal) && !suppressDeferredHistoryDrain && !casLost;
|
|
44000
44646
|
let preserveDeferredMaterializationForMarkerDrain = false;
|
|
44001
44647
|
let pendingForDrain = null;
|
|
44648
|
+
if (!deferredHistoryDrainEligible && (deferredHistoryWasPendingAtPassStart || hasPendingMaterializeSignal)) {
|
|
44649
|
+
try {
|
|
44650
|
+
const pending = getPendingPiCompactionMarkerState(args.db, args.sessionId);
|
|
44651
|
+
if (pending)
|
|
44652
|
+
logPiCompactionMarkerDrainGateOnce(args.sessionId, pending, "drain-not-eligible", {
|
|
44653
|
+
historyConsumed: historyWasConsumedThisPass,
|
|
44654
|
+
materializationSatisfied: materializationSatisfiedThisPass,
|
|
44655
|
+
drainSuppressed: suppressDeferredHistoryDrain,
|
|
44656
|
+
casLost
|
|
44657
|
+
});
|
|
44658
|
+
} catch {}
|
|
44659
|
+
}
|
|
44002
44660
|
if (deferredHistoryDrainEligible) {
|
|
44003
44661
|
try {
|
|
44004
44662
|
args.assertCurrentPass?.();
|
|
@@ -44073,7 +44731,7 @@ async function runPipeline(args) {
|
|
|
44073
44731
|
if (outcome.kind === "waiting-for-entry") {
|
|
44074
44732
|
suppressDeferredHistoryDrain = true;
|
|
44075
44733
|
preserveDeferredMaterializationForMarkerDrain = true;
|
|
44076
|
-
} else if (outcome.kind === "retryable-failure") {} else if (markerEquivalent && clearPendingPiCompactionMarkerStateIf(args.db, args.sessionId, pending)) {
|
|
44734
|
+
} else if (outcome.kind === "retryable-failure") {} else if (markerEquivalent && clearPendingPiCompactionMarkerStateIf(args.db, args.sessionId, pending, outcome.kind === "stale-skip" ? outcome.reason : outcome.kind)) {
|
|
44077
44735
|
consumeDeferredHistoryRefresh(args.sessionId);
|
|
44078
44736
|
} else if (markerEquivalent) {
|
|
44079
44737
|
casLost = true;
|
|
@@ -44291,12 +44949,6 @@ function applyNoteNudges(args) {
|
|
|
44291
44949
|
for (const anchor of noteAnchors) {
|
|
44292
44950
|
appendReminderToUserMessageByIdPi(messages, replayMessageIdByIndex, anchor.messageId, anchor.text);
|
|
44293
44951
|
}
|
|
44294
|
-
const autoSearchDecisions = args.postTransformSnapshot?.autoSearchDecisions ?? getAutoSearchHintDecisions(db, sessionId);
|
|
44295
|
-
for (const decision of autoSearchDecisions) {
|
|
44296
|
-
if (decision.decision === "hint" && getUnpublishedAutoSearchNoHint(sessionId, decision.messageId) === undefined) {
|
|
44297
|
-
appendReminderToUserMessageByIdPi(messages, replayMessageIdByIndex, decision.messageId, decision.text);
|
|
44298
|
-
}
|
|
44299
|
-
}
|
|
44300
44952
|
logTransformTiming(sessionId, "stickyReplayDecisions", tStickyReplay);
|
|
44301
44953
|
const latestUser = findLatestUserMessageIdPi(messages, messageIdByIndex);
|
|
44302
44954
|
const latestUserId = latestUser?.messageId ?? null;
|
|
@@ -44306,7 +44958,7 @@ function applyNoteNudges(args) {
|
|
|
44306
44958
|
userMessageIds: [...messageIdByIndex].filter(([index]) => messages[index]?.role === "user").map(([, id]) => id),
|
|
44307
44959
|
anchorMessageId: latestUserId,
|
|
44308
44960
|
isLiveTail: latestUser?.index === messages.length - 1,
|
|
44309
|
-
|
|
44961
|
+
prefixRewriteCoversAnchor: false
|
|
44310
44962
|
});
|
|
44311
44963
|
const noteReadStillVisible = hasVisibleNoteReadCallPi(messages);
|
|
44312
44964
|
const deferredNoteText = args.postTransformSnapshot?.noteTriggerPending === false ? null : peekNoteNudgeText(db, sessionId, latestUserId, projectIdentity, noteReadStillVisible, eligibility);
|
|
@@ -44330,6 +44982,12 @@ function applyNoteNudges(args) {
|
|
|
44330
44982
|
sessionLog2(sessionId, `Pi note-nudge delivery skipped wire append: ${outcome.kind}`);
|
|
44331
44983
|
}
|
|
44332
44984
|
}
|
|
44985
|
+
const autoSearchDecisions = args.postTransformSnapshot?.autoSearchDecisions ?? getAutoSearchHintDecisions(db, sessionId);
|
|
44986
|
+
for (const decision of autoSearchDecisions) {
|
|
44987
|
+
if (decision.decision === "hint" && getUnpublishedAutoSearchNoHint(sessionId, decision.messageId) === undefined) {
|
|
44988
|
+
appendReminderToUserMessageByIdPi(messages, replayMessageIdByIndex, decision.messageId, decision.text);
|
|
44989
|
+
}
|
|
44990
|
+
}
|
|
44333
44991
|
if (args.isCacheBusting) {
|
|
44334
44992
|
const visibleIds = new Set(messageIdByIndex.values());
|
|
44335
44993
|
const realMessageCount = Math.max(0, messages.length - (args.syntheticLeadingCount ?? 0));
|
|
@@ -47403,9 +48061,9 @@ function readPiWrapupBranchEntries(ctx) {
|
|
|
47403
48061
|
function observePiMessageActivity(db, sessionId, timestamp) {
|
|
47404
48062
|
observeSessionActivity(db, sessionId, timestamp);
|
|
47405
48063
|
}
|
|
47406
|
-
function latestPiMessageTime(path) {
|
|
48064
|
+
async function latestPiMessageTime(path) {
|
|
47407
48065
|
let latest = 0;
|
|
47408
|
-
for (const entry of
|
|
48066
|
+
for await (const entry of asyncSessionEntries(path)) {
|
|
47409
48067
|
const row = entry;
|
|
47410
48068
|
if (row.type === "message" && typeof row.message?.timestamp === "number")
|
|
47411
48069
|
latest = Math.max(latest, row.message.timestamp);
|
|
@@ -47413,12 +48071,12 @@ function latestPiMessageTime(path) {
|
|
|
47413
48071
|
return latest || undefined;
|
|
47414
48072
|
}
|
|
47415
48073
|
async function backfillPiSessionActivity(db, harness, paths) {
|
|
47416
|
-
await backfillSessionActivity(db, harness, (sessionId) => {
|
|
48074
|
+
await backfillSessionActivity(db, harness, async (sessionId) => {
|
|
47417
48075
|
const path = paths.get(sessionId);
|
|
47418
48076
|
if (!path)
|
|
47419
48077
|
return;
|
|
47420
48078
|
try {
|
|
47421
|
-
return latestPiMessageTime(path);
|
|
48079
|
+
return await latestPiMessageTime(path);
|
|
47422
48080
|
} catch {
|
|
47423
48081
|
return;
|
|
47424
48082
|
}
|